docs: add Cloudflare Sandbox SDK deployment guide

- Add docs/deploy/cloudflare.mdx with full deployment guide
- Add examples/cloudflare/ with working Worker code
- Update docs navigation to include Cloudflare option
- Update deploy index page with Cloudflare card

The example shows how to run sandbox-agent inside a Cloudflare Sandbox
with exposed ports for API access.

Co-authored-by: Shelley <shelley@exe.dev>
This commit is contained in:
Shelley 2026-02-02 20:31:48 +00:00
parent cc37ed0458
commit 33ace91cfd
10 changed files with 1021 additions and 34 deletions

3
examples/cloudflare/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
node_modules/
.wrangler/
.dev.vars

View file

@ -0,0 +1,46 @@
# Cloudflare Sandbox Agent Example
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
## Setup
1. Install dependencies:
```bash
pnpm install
```
2. Create `.dev.vars` with your API keys:
```bash
echo "ANTHROPIC_API_KEY=your-api-key" > .dev.vars
```
## Development
Start the development server:
```bash
pnpm run dev
```
Test the endpoint:
```bash
curl http://localhost:8787
```
## Deploy
```bash
pnpm run deploy
```
Note: Production preview URLs require a custom domain with wildcard DNS routing.
See [Cloudflare Production Deployment](https://developers.cloudflare.com/sandbox/guides/production-deployment/) for details.

View file

@ -0,0 +1,20 @@
{
"name": "@sandbox-agent/example-cloudflare",
"private": true,
"type": "module",
"scripts": {
"dev": "wrangler dev",
"deploy": "wrangler deploy",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@cloudflare/sandbox": "latest"
},
"devDependencies": {
"@cloudflare/workers-types": "latest",
"@types/node": "latest",
"typescript": "latest",
"vitest": "^3.0.0",
"wrangler": "latest"
}
}

View file

@ -0,0 +1,52 @@
import { getSandbox, proxyToSandbox, type Sandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox";
type Env = {
Sandbox: DurableObjectNamespace<Sandbox>;
ANTHROPIC_API_KEY?: string;
OPENAI_API_KEY?: string;
};
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Proxy requests to exposed ports first
const proxyResponse = await proxyToSandbox(request, env);
if (proxyResponse) return proxyResponse;
const { hostname } = new URL(request.url);
const sandbox = getSandbox(env.Sandbox, "sandbox-agent");
console.log("Installing sandbox-agent...");
await sandbox.exec(
"curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh"
);
console.log("Installing agents...");
await sandbox.exec("sandbox-agent install-agent claude");
await sandbox.exec("sandbox-agent install-agent codex");
// 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);
console.log("Starting sandbox-agent server...");
await sandbox.startProcess(
"sandbox-agent server --no-token --host 0.0.0.0 --port 8000"
);
// Wait for server to start
await new Promise((r) => setTimeout(r, 2000));
// Expose the port with a preview URL
const exposed = await sandbox.exposePort(8000, { hostname });
console.log("Server accessible at:", exposed.url);
return Response.json({
endpoint: exposed.url,
message: "sandbox-agent server is running",
});
},
};

View file

@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "esnext",
"lib": ["esnext"],
"module": "esnext",
"moduleResolution": "bundler",
"types": ["@cloudflare/workers-types"],
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"noEmit": true
},
"include": ["src/**/*.ts"]
}

View file

@ -0,0 +1,29 @@
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "sandbox-agent-cloudflare",
"main": "src/cloudflare.ts",
"compatibility_date": "2025-01-01",
"compatibility_flags": ["nodejs_compat"],
"containers": [
{
"class_name": "Sandbox",
"image": "docker.io/cloudflare/sandbox:latest",
"instance_type": "lite",
"max_instances": 1
}
],
"durable_objects": {
"bindings": [
{
"class_name": "Sandbox",
"name": "Sandbox"
}
]
},
"migrations": [
{
"new_sqlite_classes": ["Sandbox"],
"tag": "v1"
}
]
}