mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-15 20:03:11 +00:00
feat: acp http adapter
This commit is contained in:
parent
2ba630c180
commit
b4c8564cb2
217 changed files with 18785 additions and 17400 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue