Skip to content

Commit 0129e2a

Browse files
tombeckenhamclaude
andcommitted
feat(examples): switch sandbox-web's fixed stack to Claude Code on docker
Swap the durable-runs demo from Grok Build (grok-4.5) to Claude Code (claude-opus-4-8): claudeCodeText adapter, ANTHROPIC_API_KEY auth, npm CLI install in the container, and session resume via the claude-code.session-id event. Claude Code's one spawn path is the journaling NDJSON stream, so the streaming-json protocol override (and the ACP port) go away. Docs that described sandbox-web as Grok-on-Docker are corrected to match. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 6039377 commit 0129e2a

12 files changed

Lines changed: 58 additions & 62 deletions

File tree

docs/adapters/claude-code.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ The Claude Code adapter runs [Claude Code](https://docs.anthropic.com/en/docs/cl
2424
npm install @tanstack/ai-claude-code
2525
```
2626

27-
A runnable demo lives at [`examples/sandbox-cloudflare`](https://github.com/TanStack/ai/tree/main/examples/sandbox-cloudflare) — pick Claude Code, Codex, or Grok Build in the UI, with session resume, the harness tool timeline, and tool bridging, wired into a TanStack Start app on Workers. For the same wiring on plain Node with durable, refresh-surviving runs (Grok Build on Docker), see [`examples/sandbox-web`](https://github.com/TanStack/ai/tree/main/examples/sandbox-web) — swapping in this adapter is a one-line change (`src/sandbox-agent.ts`).
27+
A runnable demo lives at [`examples/sandbox-cloudflare`](https://github.com/TanStack/ai/tree/main/examples/sandbox-cloudflare) — pick Claude Code, Codex, or Grok Build in the UI, with session resume, the harness tool timeline, and tool bridging, wired into a TanStack Start app on Workers. For the same wiring on plain Node with durable, refresh-surviving runs (this adapter on Docker), see [`examples/sandbox-web`](https://github.com/TanStack/ai/tree/main/examples/sandbox-web).
2828

2929
## Authentication
3030

docs/adapters/codex.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ The Codex adapter runs [OpenAI Codex](https://developers.openai.com/codex) (via
2424
npm install @tanstack/ai-codex
2525
```
2626

27-
A runnable demo lives at [`examples/sandbox-cloudflare`](https://github.com/TanStack/ai/tree/main/examples/sandbox-cloudflare) — pick Claude Code, Codex, or Grok Build in the UI, with session resume, the harness tool timeline, and tool bridging, wired into a TanStack Start app on Workers. For the same wiring on plain Node with durable, refresh-surviving runs (Grok Build on Docker), see [`examples/sandbox-web`](https://github.com/TanStack/ai/tree/main/examples/sandbox-web) — swapping in this adapter is a one-line change (`src/sandbox-agent.ts`).
27+
A runnable demo lives at [`examples/sandbox-cloudflare`](https://github.com/TanStack/ai/tree/main/examples/sandbox-cloudflare) — pick Claude Code, Codex, or Grok Build in the UI, with session resume, the harness tool timeline, and tool bridging, wired into a TanStack Start app on Workers. For the same wiring on plain Node with durable, refresh-surviving runs (Claude Code on Docker), see [`examples/sandbox-web`](https://github.com/TanStack/ai/tree/main/examples/sandbox-web) — swapping in this adapter is a one-line change (`src/sandbox-agent.ts`).
2828

2929
## Authentication
3030

docs/adapters/opencode.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ npm install -g opencode-ai
3030
opencode auth login
3131
```
3232

33-
A runnable demo lives at [`examples/sandbox-cloudflare`](https://github.com/TanStack/ai/tree/main/examples/sandbox-cloudflare) — pick Claude Code, Codex, or Grok Build in the UI, with session resume, the harness tool timeline, and tool bridging, wired into a TanStack Start app on Workers. For the same wiring on plain Node with durable, refresh-surviving runs (Grok Build on Docker), see [`examples/sandbox-web`](https://github.com/TanStack/ai/tree/main/examples/sandbox-web) — swapping in this adapter is a one-line change (`src/sandbox-agent.ts`).
33+
A runnable demo lives at [`examples/sandbox-cloudflare`](https://github.com/TanStack/ai/tree/main/examples/sandbox-cloudflare) — pick Claude Code, Codex, or Grok Build in the UI, with session resume, the harness tool timeline, and tool bridging, wired into a TanStack Start app on Workers. For the same wiring on plain Node with durable, refresh-surviving runs (Claude Code on Docker), see [`examples/sandbox-web`](https://github.com/TanStack/ai/tree/main/examples/sandbox-web) — swapping in this adapter is a one-line change (`src/sandbox-agent.ts`).
3434

3535
## Models
3636

docs/sandbox/overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ from the UI — see
133133

134134
For a **web** chat where the agent builds and runs an app inside a sandbox and
135135
hands back a live preview URL — with durable, refresh-surviving runs — see
136-
`examples/sandbox-web` (Grok Build on Docker; the stack is fixed in one file
136+
`examples/sandbox-web` (Claude Code on Docker; the stack is fixed in one file
137137
and swappable).
138138

139139
> **Durable instance resume:** bookkeeping defaults to in-memory (single-process).

docs/sandbox/quick-start.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,11 @@ Because local-process inherits your host environment, you can drop the
156156

157157
A complete, runnable app ships at
158158
[`examples/sandbox-web`](https://github.com/TanStack/ai/tree/main/examples/sandbox-web)
159-
— a "build me an app" agent where you pick the harness (Claude Code, Codex,
160-
OpenCode, Grok Build) and provider (Docker, local-process, Vercel, Daytona) per
161-
run from the UI; it scaffolds an app in the sandbox, runs the dev server, and
162-
streams back a live preview URL and the diff. For a coding agent running at the
163-
edge, see
159+
— a "build me an app" agent (Claude Code on a Docker sandbox) with durable,
160+
refresh-surviving runs; it scaffolds an app in the sandbox, runs the dev
161+
server, and streams back a live preview URL. For a coding agent running at the
162+
edge — with the harness (Claude Code, Codex, Grok Build) picked per run from
163+
the UI — see
164164
[`examples/sandbox-cloudflare`](https://github.com/TanStack/ai/tree/main/examples/sandbox-cloudflare).
165165

166166
From here:

examples/sandbox-web/.env.example

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
# Grok BuildxAI API key (GROK_API_KEY accepted as an alias). The key is
2-
# injected into the sandbox as a secret so the in-sandbox `grok` CLI can
3-
# authenticate headlessly. https://console.x.ai/
4-
XAI_API_KEY=
1+
# Claude CodeAnthropic API key. The key is injected into the sandbox as a
2+
# secret so the in-sandbox `claude` CLI can authenticate headlessly.
3+
# https://console.anthropic.com/
4+
ANTHROPIC_API_KEY=
55

66
# Optional: override the Docker base image (must ship node + npm + git).
77
# SANDBOX_IMAGE=node:22

examples/sandbox-web/README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ A web chat where an AI coding agent builds and runs a real app **inside a
44
sandbox**, then hands back a live preview URL — and the run is **durable**: it
55
survives a refresh, a closed tab, and a lost connection.
66

7-
The stack is deliberately fixed — **Grok Build** (the `grok` CLI, model
8-
`grok-4.5`) in a **Docker** sandbox — because this app is the runnable demo of
7+
The stack is deliberately fixed — **Claude Code** (the `claude` CLI, model
8+
`claude-opus-4-8`) in a **Docker** sandbox — because this app is the runnable demo of
99
the [durable runs](../../docs/sandbox/durable-runs.md) journal-only tier, and
1010
durability's one hard requirement is that a run be reconstructible from its
1111
`runId` alone (the takeover route and the reaper have nothing else). One
@@ -21,7 +21,7 @@ Durable-Object sibling, running Claude Code, Codex, or Grok Build).
2121
2. The route runs `chat({ adapter, middleware: [withPersistence, withLocks,
2222
withSandbox(sandbox, { runs, durability })], … })` — see
2323
[`src/run-durable.ts`](./src/run-durable.ts) for the one shared assembly.
24-
3. `withSandbox` resumes-or-creates the thread's sandbox; the `grok` CLI runs
24+
3. `withSandbox` resumes-or-creates the thread's sandbox; the `claude` CLI runs
2525
inside it and streams its events back out through the journal.
2626
4. The agent scaffolds a self-contained TanStack Start app, runs its dev server
2727
on port **5173**, and mints the preview URL via the bridged `exposePreview`
@@ -62,7 +62,7 @@ Object) plus durable `RunStore`/`LockStore` backends.
6262
## Prerequisites
6363

6464
- a running **Docker daemon**
65-
- `XAI_API_KEY` (or `GROK_API_KEY`) — see [`.env.example`](./.env.example)
65+
- `ANTHROPIC_API_KEY` — see [`.env.example`](./.env.example)
6666

6767
## Run
6868

@@ -72,7 +72,7 @@ pnpm install
7272
pnpm build
7373

7474
cd examples/sandbox-web
75-
cp .env.example .env # set XAI_API_KEY
75+
cp .env.example .env # set ANTHROPIC_API_KEY
7676
pnpm dev # http://localhost:3002
7777
```
7878

@@ -81,15 +81,15 @@ drag-and-drop and localStorage, then give me the preview URL."_ — and refresh
8181
the page mid-build.
8282

8383
> The first message per thread is slow: it pulls `node:22` (once) and installs
84-
> the `grok` CLI in the fresh container. Pre-bake an image with the CLI and set
85-
> `SANDBOX_IMAGE=<your-image>` to skip that.
84+
> the `claude` CLI in the fresh container. Pre-bake an image with the CLI and
85+
> set `SANDBOX_IMAGE=<your-image>` to skip that.
8686
8787
## Swapping the stack
8888

8989
The fixed choices live in one file, [`src/sandbox-agent.ts`](./src/sandbox-agent.ts):
9090
`buildAdapter()` (the harness), `buildSandbox()` (the provider + CLI install +
91-
secrets), and `missingEnv()` (the auth check). Swap `grokBuildText` for
92-
`claudeCodeText` / `codexText` and `dockerSandbox` for another provider and the
91+
secrets), and `missingEnv()` (the auth check). Swap `claudeCodeText` for
92+
`codexText` / `grokBuildText` and `dockerSandbox` for another provider and the
9393
durable wiring is unchanged — it only ever sees the definitions. If you make
9494
the stack a **per-request browser choice** instead, every route that arrives
9595
with only a `runId` (takeover, reaper) needs that choice stored server-side —

examples/sandbox-web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
},
1313
"dependencies": {
1414
"@tanstack/ai": "workspace:*",
15-
"@tanstack/ai-grok-build": "workspace:*",
15+
"@tanstack/ai-claude-code": "workspace:*",
1616
"@tanstack/ai-persistence": "workspace:*",
1717
"@tanstack/ai-react": "workspace:*",
1818
"@tanstack/ai-sandbox": "workspace:*",

examples/sandbox-web/src/routes/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ import {
2222
import type { StreamChunk } from '@tanstack/ai'
2323
import type { UIMessage } from '@tanstack/ai-react'
2424

25-
/** CUSTOM event Grok Build emits so follow-up runs can resume its session. */
26-
const GROK_SESSION_ID_EVENT = 'grok-build.session-id'
25+
/** CUSTOM event Claude Code emits so follow-up runs can resume its session. */
26+
const CLAUDE_CODE_SESSION_ID_EVENT = 'claude-code.session-id'
2727

2828
function readSessionId(chunk: StreamChunk): string | undefined {
2929
if (chunk.type !== EventType.CUSTOM) return undefined
30-
if (chunk.name !== GROK_SESSION_ID_EVENT) return undefined
30+
if (chunk.name !== CLAUDE_CODE_SESSION_ID_EVENT) return undefined
3131
const value = chunk.value
3232
if (value === null || typeof value !== 'object' || !('sessionId' in value)) {
3333
return undefined
@@ -405,7 +405,7 @@ function SandboxAgentChat({ initialThreadId }: { initialThreadId: string }) {
405405
</span>
406406
</div>
407407
<div className="ml-auto flex items-center gap-2 text-sm">
408-
<span className="text-xs text-gray-500">grok-4.5 on docker</span>
408+
<span className="text-xs text-gray-500">opus-4.8 on docker</span>
409409
<button
410410
onClick={newThread}
411411
disabled={isLoading}

examples/sandbox-web/src/run-durable.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* - `ensureReaper`, the scheduled sweep durable runs require (a detached run
1313
* nobody rejoins must not keep its sandbox alive forever).
1414
*
15-
* The whole module is small because the stack is FIXED (Grok Build on Docker):
15+
* The whole module is small because the stack is FIXED (Claude Code on Docker):
1616
* takeover requires rebuilding a run's chat() from its `runId` alone, and with
1717
* one adapter and one provider the rebuild is just `threadId` → sandbox +
1818
* transcript. Make the stack a per-request browser choice and every route that
@@ -109,7 +109,7 @@ export function buildRunStream(input: {
109109
abortController: AbortController
110110
attach: boolean
111111
durability: StreamDurability
112-
/** Grok session to resume for a follow-up turn (fresh runs only). */
112+
/** Claude Code session to resume for a follow-up turn (fresh runs only). */
113113
sessionId?: string
114114
}): AsyncIterable<StreamChunk> {
115115
const { threadId, runId, abortController, attach, durability, sessionId } =

0 commit comments

Comments
 (0)