chore: fix bad merge

This commit is contained in:
Nathan Flurry 2026-02-11 07:52:48 -08:00
parent 1dd45908a3
commit 94353f7696
205 changed files with 19244 additions and 14866 deletions

View file

@ -1,21 +1,19 @@
---
title: "Cloudflare"
description: "Deploy the daemon inside a Cloudflare Sandbox."
description: "Deploy Sandbox Agent inside a Cloudflare Sandbox."
---
## Prerequisites
- Cloudflare account with Workers Paid plan
- Docker running locally for `wrangler dev`
- `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` for the coding agents
- Cloudflare account with Workers paid plan
- Docker for local `wrangler dev`
- `ANTHROPIC_API_KEY` or `OPENAI_API_KEY`
<Note>
Cloudflare Sandbox SDK is in beta. See [Sandbox SDK docs](https://developers.cloudflare.com/sandbox/) for details.
Cloudflare Sandbox SDK is beta. See [Sandbox SDK docs](https://developers.cloudflare.com/sandbox/).
</Note>
## Quick Start
Create a new Sandbox SDK project:
## Quick start
```bash
npm create cloudflare@latest -- my-sandbox --template=cloudflare/sandbox-sdk/examples/minimal
@ -24,64 +22,16 @@ cd my-sandbox
## Dockerfile
Create a `Dockerfile` with sandbox-agent and agents pre-installed:
```dockerfile
FROM cloudflare/sandbox:0.7.0
# Install sandbox-agent
RUN curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh
RUN curl -fsSL https://releases.rivet.dev/sandbox-agent/0.2.x/install.sh | sh
RUN sandbox-agent install-agent claude && sandbox-agent install-agent codex
# Pre-install agents
RUN sandbox-agent install-agent claude && \
sandbox-agent install-agent codex
# Required for local development with wrangler dev
EXPOSE 8000
```
<Note>
The `EXPOSE 8000` directive is required for `wrangler dev` to proxy requests to the container. Port 3000 is reserved for the Cloudflare control plane.
</Note>
## Wrangler Configuration
Update `wrangler.jsonc` to use your Dockerfile:
```jsonc
{
"name": "my-sandbox-agent",
"main": "src/index.ts",
"compatibility_date": "2025-01-01",
"compatibility_flags": ["nodejs_compat"],
"containers": [
{
"class_name": "Sandbox",
"image": "./Dockerfile",
"instance_type": "lite",
"max_instances": 1
}
],
"durable_objects": {
"bindings": [
{
"class_name": "Sandbox",
"name": "Sandbox"
}
]
},
"migrations": [
{
"new_sqlite_classes": ["Sandbox"],
"tag": "v1"
}
]
}
```
## TypeScript Example
This example proxies requests to sandbox-agent via `containerFetch`, which works reliably in both local development and production:
## TypeScript proxy example
```typescript
import { getSandbox, type Sandbox } from "@cloudflare/sandbox";
@ -95,158 +45,87 @@ type Env = {
const PORT = 8000;
/** Check if sandbox-agent is already running */
async function isServerRunning(sandbox: Sandbox): Promise<boolean> {
try {
const result = await sandbox.exec(`curl -sf http://localhost:${PORT}/v2/health`);
const result = await sandbox.exec(`curl -sf http://localhost:${PORT}/v1/health`);
return result.success;
} catch {
return false;
}
}
/** Ensure sandbox-agent is running in the container */
async function ensureRunning(sandbox: Sandbox, env: Env): Promise<void> {
if (await isServerRunning(sandbox)) return;
// Set environment variables for agents
const envVars: Record<string, string> = {};
if (env.ANTHROPIC_API_KEY) envVars.ANTHROPIC_API_KEY = env.ANTHROPIC_API_KEY;
if (env.OPENAI_API_KEY) envVars.OPENAI_API_KEY = env.OPENAI_API_KEY;
await sandbox.setEnvVars(envVars);
// Start sandbox-agent server
await sandbox.startProcess(
`sandbox-agent server --no-token --host 0.0.0.0 --port ${PORT}`
);
await sandbox.startProcess(`sandbox-agent server --no-token --host 0.0.0.0 --port ${PORT}`);
// Poll health endpoint until server is ready
for (let i = 0; i < 30; i++) {
if (await isServerRunning(sandbox)) return;
await new Promise((r) => setTimeout(r, 200));
}
throw new Error("sandbox-agent failed to start");
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
// Proxy requests: /sandbox/:name/v2/...
const match = url.pathname.match(/^\/sandbox\/([^/]+)(\/.*)?$/);
if (match) {
const [, name, path = "/"] = match;
const sandbox = getSandbox(env.Sandbox, name);
await ensureRunning(sandbox, env);
// Proxy request to container
return sandbox.containerFetch(
new Request(`http://localhost${path}${url.search}`, request),
PORT
);
if (!match) {
return new Response("Not found", { status: 404 });
}
return new Response("Not found", { status: 404 });
const [, name, path = "/"] = match;
const sandbox = getSandbox(env.Sandbox, name);
await ensureRunning(sandbox, env);
return sandbox.containerFetch(
new Request(`http://localhost${path}${url.search}`, request),
PORT,
);
},
};
```
## Connect from Client
## Connect from a client
```typescript
import { SandboxAgentClient } from "sandbox-agent";
import { SandboxAgent } from "sandbox-agent";
// Connect via the proxy endpoint
const client = new SandboxAgentClient({
const sdk = await SandboxAgent.connect({
baseUrl: "http://localhost:8787/sandbox/my-sandbox",
agent: "mock",
});
// Wait for server to be ready
for (let i = 0; i < 30; i++) {
try {
await client.getHealth();
break;
} catch {
await new Promise((r) => setTimeout(r, 1000));
}
}
const session = await sdk.createSession({ agent: "claude" });
// Create a session and start coding
await client.createSession("my-session", { agent: "claude" });
await client.postMessage("my-session", {
message: "Summarize this repository",
const off = session.onEvent((event) => {
console.log(event.sender, event.payload);
});
for await (const event of client.streamEvents("my-session")) {
// Auto-approve permissions
if (event.type === "permission.requested") {
await client.replyPermission("my-session", event.data.permission_id, {
reply: "once",
});
}
// Handle text output
if (event.type === "item.delta" && event.data?.delta) {
process.stdout.write(event.data.delta);
}
}
await session.prompt([{ type: "text", text: "Summarize this repository" }]);
off();
```
## Environment Variables
Use `.dev.vars` for local development:
```bash
echo "ANTHROPIC_API_KEY=your-api-key" > .dev.vars
```
<Warning>
Use plain `KEY=value` format in `.dev.vars`. Do not use `export KEY=value` - wrangler won't parse the bash syntax.
</Warning>
<Note>
The `.dev.vars` file is automatically gitignored and only used during local development with `npm run dev`.
</Note>
For production, set secrets via wrangler:
```bash
wrangler secret put ANTHROPIC_API_KEY
```
## Local Development
Start the development server:
## Local development
```bash
npm run dev
```
<Note>
First run builds the Docker container (2-3 minutes). Subsequent runs are much faster.
</Note>
Test with curl:
Test health:
```bash
curl http://localhost:8787/sandbox/demo/v2/health
curl http://localhost:8787/sandbox/demo/v1/health
```
<Tip>
Containers cache environment variables. If you change `.dev.vars`, either use a new sandbox name or clear existing containers:
```bash
docker ps -a | grep sandbox | awk '{print $1}' | xargs -r docker rm -f
```
</Tip>
## Production Deployment
Deploy to Cloudflare:
## Production deployment
```bash
wrangler deploy
```
For production with preview URLs (direct container access), you'll need a custom domain with wildcard DNS routing. See [Cloudflare Production Deployment](https://developers.cloudflare.com/sandbox/guides/production-deployment/) for setup instructions.

View file

@ -1,63 +1,52 @@
---
title: "Daytona"
description: "Run the daemon in a Daytona workspace."
description: "Run Sandbox Agent in a Daytona workspace."
---
<Warning>
Daytona Tier 3+ is required to access api.anthropic.com and api.openai.com. Tier 1/2 sandboxes have restricted network access that will cause agent failures. See [Daytona network limits](https://www.daytona.io/docs/en/network-limits/) for details.
Daytona Tier 3+ is required for access to common model provider endpoints.
See [Daytona network limits](https://www.daytona.io/docs/en/network-limits/).
</Warning>
## Prerequisites
- `DAYTONA_API_KEY` environment variable
- `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` for the coding agents
- `DAYTONA_API_KEY`
- `ANTHROPIC_API_KEY` or `OPENAI_API_KEY`
## TypeScript Example
## TypeScript example
```typescript
import { Daytona } from "@daytonaio/sdk";
import { SandboxAgentClient } from "sandbox-agent";
import { SandboxAgent } from "sandbox-agent";
const daytona = new Daytona();
// Pass API keys to the sandbox
const envVars: Record<string, string> = {};
if (process.env.ANTHROPIC_API_KEY) envVars.ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
if (process.env.OPENAI_API_KEY) envVars.OPENAI_API_KEY = process.env.OPENAI_API_KEY;
const sandbox = await daytona.create({ envVars });
// Install sandbox-agent
await sandbox.process.executeCommand(
"curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh"
"curl -fsSL https://releases.rivet.dev/sandbox-agent/0.2.x/install.sh | sh"
);
// Start the server in the background
await sandbox.process.executeCommand(
"nohup sandbox-agent server --no-token --host 0.0.0.0 --port 3000 >/tmp/sandbox-agent.log 2>&1 &"
);
// Wait for server to be ready
await new Promise((r) => setTimeout(r, 2000));
// Get the public URL
const baseUrl = (await sandbox.getSignedPreviewUrl(3000, 4 * 60 * 60)).url;
const sdk = await SandboxAgent.connect({ baseUrl });
// Connect and use the SDK
const client = new SandboxAgentClient({ baseUrl, agent: "mock" });
const session = await sdk.createSession({ agent: "claude" });
await session.prompt([{ type: "text", text: "Summarize this repository" }]);
await client.createSession("my-session", {
agent: "claude",
permissionMode: "default",
});
// Cleanup when done
await sandbox.delete();
```
## Using Snapshots for Faster Startup
For production, use snapshots with pre-installed binaries:
## Using snapshots for faster startup
```typescript
import { Daytona, Image } from "@daytonaio/sdk";
@ -65,7 +54,6 @@ import { Daytona, Image } from "@daytonaio/sdk";
const daytona = new Daytona();
const SNAPSHOT = "sandbox-agent-ready";
// Create snapshot once (takes 2-3 minutes)
const hasSnapshot = await daytona.snapshot.get(SNAPSHOT).then(() => true, () => false);
if (!hasSnapshot) {
@ -73,18 +61,10 @@ if (!hasSnapshot) {
name: SNAPSHOT,
image: Image.base("ubuntu:22.04").runCommands(
"apt-get update && apt-get install -y curl ca-certificates",
"curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh",
"curl -fsSL https://releases.rivet.dev/sandbox-agent/0.2.x/install.sh | sh",
"sandbox-agent install-agent claude",
"sandbox-agent install-agent codex",
),
});
}
// Now sandboxes start instantly
const sandbox = await daytona.create({
snapshot: SNAPSHOT,
envVars,
});
```
See [Daytona Snapshots](https://daytona.io/docs/snapshots) for details.

View file

@ -1,15 +1,15 @@
---
title: "Docker"
description: "Build and run the daemon in a Docker container."
description: "Build and run Sandbox Agent in a Docker container."
---
<Warning>
Docker is not recommended for production. Standard Docker containers don't provide sufficient isolation for running untrusted code. Use a dedicated sandbox provider like E2B or Daytona for production workloads.
Docker is not recommended for production isolation of untrusted workloads. Use dedicated sandbox providers (E2B, Daytona, etc.) for stronger isolation.
</Warning>
## Quick Start
## Quick start
Run sandbox-agent in a container with agents pre-installed:
Run Sandbox Agent with agents pre-installed:
```bash
docker run --rm -p 3000:3000 \
@ -17,23 +17,21 @@ docker run --rm -p 3000:3000 \
-e OPENAI_API_KEY="$OPENAI_API_KEY" \
alpine:latest sh -c "\
apk add --no-cache curl ca-certificates libstdc++ libgcc bash && \
curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh && \
curl -fsSL https://releases.rivet.dev/sandbox-agent/0.2.x/install.sh | sh && \
sandbox-agent install-agent claude && \
sandbox-agent install-agent codex && \
sandbox-agent server --no-token --host 0.0.0.0 --port 3000"
```
<Note>
Alpine is required because Claude Code is built for musl libc. Debian/Ubuntu images use glibc and won't work.
Alpine is required for some agent binaries that target musl libc.
</Note>
Access the API at `http://localhost:3000`.
## TypeScript with dockerode
```typescript
import Docker from "dockerode";
import { SandboxAgentClient } from "sandbox-agent";
import { SandboxAgent } from "sandbox-agent";
const docker = new Docker();
const PORT = 3000;
@ -42,7 +40,7 @@ const container = await docker.createContainer({
Image: "alpine:latest",
Cmd: ["sh", "-c", [
"apk add --no-cache curl ca-certificates libstdc++ libgcc bash",
"curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh",
"curl -fsSL https://releases.rivet.dev/sandbox-agent/0.2.x/install.sh | sh",
"sandbox-agent install-agent claude",
"sandbox-agent install-agent codex",
`sandbox-agent server --no-token --host 0.0.0.0 --port ${PORT}`,
@ -60,24 +58,18 @@ const container = await docker.createContainer({
await container.start();
// Wait for server and connect
const baseUrl = `http://127.0.0.1:${PORT}`;
const client = new SandboxAgentClient({ baseUrl, agent: "mock" });
const sdk = await SandboxAgent.connect({ baseUrl });
// Use the client...
await client.createSession("my-session", {
agent: "claude",
permissionMode: "default",
});
const session = await sdk.createSession({ agent: "claude" });
await session.prompt([{ type: "text", text: "Summarize this repository." }]);
```
## Building from Source
To build a static binary for use in minimal containers:
## Building from source
```bash
docker build -f docker/release/linux-x86_64.Dockerfile -t sandbox-agent-build .
docker run --rm -v "$PWD/artifacts:/artifacts" sandbox-agent-build
```
The binary will be at `./artifacts/sandbox-agent-x86_64-unknown-linux-musl`.
Binary output: `./artifacts/sandbox-agent-x86_64-unknown-linux-musl`.

View file

@ -1,79 +1,52 @@
---
title: "E2B"
description: "Deploy the daemon inside an E2B sandbox."
description: "Deploy Sandbox Agent inside an E2B sandbox."
---
## Prerequisites
- `E2B_API_KEY` environment variable
- `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` for the coding agents
- `E2B_API_KEY`
- `ANTHROPIC_API_KEY` or `OPENAI_API_KEY`
## TypeScript Example
## TypeScript example
```typescript
import { Sandbox } from "@e2b/code-interpreter";
import { SandboxAgentClient } from "sandbox-agent";
import { SandboxAgent } from "sandbox-agent";
// Pass API keys to the sandbox
const envs: Record<string, string> = {};
if (process.env.ANTHROPIC_API_KEY) envs.ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
if (process.env.OPENAI_API_KEY) envs.OPENAI_API_KEY = process.env.OPENAI_API_KEY;
const sandbox = await Sandbox.create({ allowInternetAccess: true, envs });
// Install sandbox-agent
await sandbox.commands.run(
"curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh"
"curl -fsSL https://releases.rivet.dev/sandbox-agent/0.2.x/install.sh | sh"
);
// Install agents before starting the server
await sandbox.commands.run("sandbox-agent install-agent claude");
await sandbox.commands.run("sandbox-agent install-agent codex");
// Start the server in the background
await sandbox.commands.run(
"sandbox-agent server --no-token --host 0.0.0.0 --port 3000",
{ background: true }
);
// Connect to the server
const baseUrl = `https://${sandbox.getHost(3000)}`;
const client = new SandboxAgentClient({ baseUrl, agent: "mock" });
const sdk = await SandboxAgent.connect({ baseUrl });
// Wait for server to be ready
for (let i = 0; i < 30; i++) {
try {
await client.getHealth();
break;
} catch {
await new Promise((r) => setTimeout(r, 1000));
}
}
// Create a session and start coding
await client.createSession("my-session", {
agent: "claude",
permissionMode: "default",
const session = await sdk.createSession({ agent: "claude" });
const off = session.onEvent((event) => {
console.log(event.sender, event.payload);
});
await client.postMessage("my-session", {
message: "Summarize this repository",
});
await session.prompt([{ type: "text", text: "Summarize this repository" }]);
off();
for await (const event of client.streamEvents("my-session")) {
console.log(event.type, event.data);
}
// Cleanup
await sandbox.kill();
```
## Faster Cold Starts
## Faster cold starts
For faster startup, create a custom E2B template with sandbox-agent and agents pre-installed:
1. Create a template with the install script baked in
2. Pre-install agents: `sandbox-agent install-agent claude codex`
3. Use the template ID when creating sandboxes
See [E2B Custom Templates](https://e2b.dev/docs/sandbox-template) for details.
For faster startup, create a custom E2B template with Sandbox Agent and target agents pre-installed.
See [E2B Custom Templates](https://e2b.dev/docs/sandbox-template).

View file

@ -1,52 +1,53 @@
---
title: "Local"
description: "Run the daemon locally for development."
description: "Run Sandbox Agent locally for development."
---
For local development, you can run the daemon directly on your machine.
For local development, run Sandbox Agent directly on your machine.
## With the CLI
```bash
# Install
curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh
curl -fsSL https://releases.rivet.dev/sandbox-agent/0.2.x/install.sh | sh
# Run
sandbox-agent server --no-token --host 127.0.0.1 --port 2468
```
Or with npm or Bun:
Or with npm/Bun:
<Tabs>
<Tab title="npx">
```bash
npx sandbox-agent server --no-token --host 127.0.0.1 --port 2468
npx @sandbox-agent/cli@0.2.x server --no-token --host 127.0.0.1 --port 2468
```
</Tab>
<Tab title="bunx">
```bash
bunx sandbox-agent server --no-token --host 127.0.0.1 --port 2468
bunx @sandbox-agent/cli@0.2.x server --no-token --host 127.0.0.1 --port 2468
```
</Tab>
</Tabs>
## With the TypeScript SDK
The SDK can automatically spawn and manage the server as a subprocess:
The SDK can spawn and manage the server as a subprocess:
```typescript
import { SandboxAgent } from "sandbox-agent";
// Spawns sandbox-agent server as a subprocess
const client = await SandboxAgent.start();
const sdk = await SandboxAgent.start();
await client.createSession("my-session", {
const session = await sdk.createSession({
agent: "claude",
permissionMode: "default",
});
// When done
await client.dispose();
await session.prompt([
{ type: "text", text: "Summarize this repository." },
]);
await sdk.dispose();
```
This installs the binary (if needed) and starts the server on a random available port. No manual setup required.
This starts the server on an available local port and connects automatically.

View file

@ -1,47 +1,39 @@
---
title: "Vercel"
description: "Deploy the daemon inside a Vercel Sandbox."
description: "Deploy Sandbox Agent inside a Vercel Sandbox."
---
## Prerequisites
- `VERCEL_OIDC_TOKEN` or `VERCEL_ACCESS_TOKEN` environment variable
- `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` for the coding agents
- `VERCEL_OIDC_TOKEN` or `VERCEL_ACCESS_TOKEN`
- `ANTHROPIC_API_KEY` or `OPENAI_API_KEY`
## TypeScript Example
## TypeScript example
```typescript
import { Sandbox } from "@vercel/sandbox";
import { SandboxAgentClient } from "sandbox-agent";
import { SandboxAgent } from "sandbox-agent";
// Pass API keys to the sandbox
const envs: Record<string, string> = {};
if (process.env.ANTHROPIC_API_KEY) envs.ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
if (process.env.OPENAI_API_KEY) envs.OPENAI_API_KEY = process.env.OPENAI_API_KEY;
// Create sandbox with port 3000 exposed
const sandbox = await Sandbox.create({
runtime: "node24",
ports: [3000],
});
// Helper to run commands
const run = async (cmd: string, args: string[] = []) => {
const result = await sandbox.runCommand({ cmd, args, env: envs });
if (result.exitCode !== 0) {
throw new Error(`Command failed: ${cmd} ${args.join(" ")}`);
}
return result;
};
// Install sandbox-agent
await run("sh", ["-c", "curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh"]);
// Install agents before starting the server
await run("sh", ["-c", "curl -fsSL https://releases.rivet.dev/sandbox-agent/0.2.x/install.sh | sh"]);
await run("sandbox-agent", ["install-agent", "claude"]);
await run("sandbox-agent", ["install-agent", "codex"]);
// Start the server in the background
await sandbox.runCommand({
cmd: "sandbox-agent",
args: ["server", "--no-token", "--host", "0.0.0.0", "--port", "3000"],
@ -49,43 +41,22 @@ await sandbox.runCommand({
detached: true,
});
// Connect to the server
const baseUrl = sandbox.domain(3000);
const client = new SandboxAgentClient({ baseUrl, agent: "mock" });
const sdk = await SandboxAgent.connect({ baseUrl });
// Wait for server to be ready
for (let i = 0; i < 30; i++) {
try {
await client.getHealth();
break;
} catch {
await new Promise((r) => setTimeout(r, 1000));
}
}
const session = await sdk.createSession({ agent: "claude" });
// Create a session and start coding
await client.createSession("my-session", {
agent: "claude",
permissionMode: "default",
const off = session.onEvent((event) => {
console.log(event.sender, event.payload);
});
await client.postMessage("my-session", {
message: "Summarize this repository",
});
await session.prompt([{ type: "text", text: "Summarize this repository" }]);
off();
for await (const event of client.streamEvents("my-session")) {
console.log(event.type, event.data);
}
// Cleanup
await sandbox.stop();
```
## Authentication
Vercel Sandboxes support two authentication methods:
- **OIDC Token**: Set `VERCEL_OIDC_TOKEN` (recommended for CI/CD)
- **Access Token**: Set `VERCEL_ACCESS_TOKEN` (for local development, run `vercel env pull`)
See [Vercel Sandbox docs](https://vercel.com/docs/functions/sandbox) for details.
Vercel Sandboxes support OIDC token auth (recommended) and access-token auth.
See [Vercel Sandbox docs](https://vercel.com/docs/functions/sandbox).