Claude with a real Linux sandbox — a Sprite (Fly.io) — in one chat() call.
A chat app that gives Claude a real Linux sandbox — a Sprite (Fly.io) — through a single run_bash tool. Ask it to inspect the machine, write and run code, install packages, or drive a git repo, and it does the work inside an isolated, disposable Sprite and streams the results back.
- Chat —
@tanstack/ai+@tanstack/ai-anthropic(anthropicText). - Execution —
@tanstack/ai-sandbox-sprites(SpritesClient): therun_bashtool runs commands inside a Sprite and streams stdout/stderr/exit back to the model. - UI — React + Vite + Tailwind v4 with shadcn (Base UI) chat components:
MessageScroller(auto-scrolls the live edge),Message,Bubble,Marker(eachrun_bashstep renders as an inline marker with a spinner + shimmer while it runs), plus markdown-rendered responses.
When the model needs to run something, chat()'s agent loop calls run_bash; the tool execs the command in a Sprite (created lazily on first use and reused) and the model reads the real output.
Prerequisites: Node.js ≥ 22 and pnpm (npm i -g pnpm), plus two credentials:
| Credential | Where to get it |
|---|---|
ANTHROPIC_API_KEY |
console.anthropic.com — the chat model |
SPRITES_API_KEY |
sprites.dev — the sandbox (org/projectNumber/tokenId/secret) |
1. Clone & install
git clone https://github.com/fly-apps/tanstack-sprite-agent
cd tanstack-sprite-agent
pnpm install2. Add your keys — copy the template and fill it in:
cp .env.example .env
# then edit .env and set ANTHROPIC_API_KEY and SPRITES_API_KEY3. Run — builds the UI, then serves it + the API on port 8080:
pnpm serve.env is loaded automatically (node --env-file-if-exists=.env). Open http://localhost:8080 and try:
- "What OS, kernel, and node version is this sandbox?"
- "Write fib.py that prints the first 10 Fibonacci numbers, then run it."
- "Create a git repo, add a README, and show me the log."
The first request provisions a fresh Sprite and can take ~1–3 min; after that it's fast.
Run the API and the Vite dev server side by side (the Vite dev server proxies /api → :8081, giving you UI hot-reload):
PORT=8081 pnpm start # API on :8081 (loads .env)
pnpm dev # Vite UI with hot reloadserver.mjs Node http server: serves the built UI + POST /api/chat (SSE)
src/App.tsx the chat UI (shadcn Base UI components + markdown)
src/lib/use-sandbox-chat.ts streaming hook: parses the AG-UI SSE into messages + tool steps
src/components/ui/ shadcn Base UI components (message-scroller, message, bubble, marker, spinner, …)
The browser POSTs the conversation to /api/chat; the server runs chat() with the run_bash tool and streams AG-UI Server-Sent Events back (text deltas + TOOL_CALL_*), which the client renders as chat bubbles and tool markers.
- Model. Defaults to
claude-sonnet-5; setANTHROPIC_MODELto any Anthropic model id (e.g.claude-opus-4-8). - One shared sandbox. The demo creates a single Sprite on first tool use and reuses it. A production app would key a sandbox per session (see
@tanstack/ai-sandbox'sdefineSandbox/withSandbox) and tear it down when done. - The Anthropic key stays on the server — nothing sensitive is written into the Sprite; the Sprite only ever runs the commands the model requests.
- Cold start. The first
run_bashin a fresh Sprite waits for it to provision (~1–3 min); after that calls are fast, and an idle Sprite resumes on the next exec.
The app is a plain Node server, so it runs anywhere. To host it on a Sprite and expose it at the Sprite's public URL, run it as a service bound to the proxied HTTP port:
sprite-env services create tanstack-sprite-agent \
--cmd /.sprite/bin/node --args server.mjs \
--dir "$PWD" \
--env "ANTHROPIC_API_KEY=…,SPRITES_API_KEY=…,PORT=8080" \
--http-port 8080MIT
