mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-15 10:05:18 +00:00
122 lines
3.2 KiB
Text
122 lines
3.2 KiB
Text
---
|
|
title: "Cloudflare"
|
|
description: "Deploy Sandbox Agent inside a Cloudflare Sandbox."
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
- Cloudflare account with Workers paid plan
|
|
- Docker for local `wrangler dev`
|
|
- `ANTHROPIC_API_KEY` or `OPENAI_API_KEY`
|
|
|
|
<Note>
|
|
Cloudflare Sandbox SDK is beta. See [Sandbox SDK docs](https://developers.cloudflare.com/sandbox/).
|
|
</Note>
|
|
|
|
## Quick start
|
|
|
|
```bash
|
|
npm create cloudflare@latest -- my-sandbox --template=cloudflare/sandbox-sdk/examples/minimal
|
|
cd my-sandbox
|
|
```
|
|
|
|
## Dockerfile
|
|
|
|
```dockerfile
|
|
FROM cloudflare/sandbox:0.7.0
|
|
|
|
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
|
|
|
|
EXPOSE 8000
|
|
```
|
|
|
|
## TypeScript example
|
|
|
|
```typescript
|
|
import { getSandbox, type Sandbox } from "@cloudflare/sandbox";
|
|
import { Hono } from "hono";
|
|
import { SandboxAgent } from "sandbox-agent";
|
|
|
|
export { Sandbox } from "@cloudflare/sandbox";
|
|
|
|
type Bindings = {
|
|
Sandbox: DurableObjectNamespace<Sandbox>;
|
|
ASSETS: Fetcher;
|
|
ANTHROPIC_API_KEY?: string;
|
|
OPENAI_API_KEY?: string;
|
|
};
|
|
|
|
const app = new Hono<{ Bindings: Bindings }>();
|
|
const PORT = 8000;
|
|
|
|
async function isServerRunning(sandbox: Sandbox): Promise<boolean> {
|
|
try {
|
|
const result = await sandbox.exec(`curl -sf http://localhost:${PORT}/v1/health`);
|
|
return result.success;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function getReadySandbox(name: string, env: Bindings): Promise<Sandbox> {
|
|
const sandbox = getSandbox(env.Sandbox, name);
|
|
if (!(await isServerRunning(sandbox))) {
|
|
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);
|
|
await sandbox.startProcess(`sandbox-agent server --no-token --host 0.0.0.0 --port ${PORT}`);
|
|
}
|
|
return sandbox;
|
|
}
|
|
|
|
app.post("/sandbox/:name/prompt", async (c) => {
|
|
const sandbox = await getReadySandbox(c.req.param("name"), c.env);
|
|
|
|
const sdk = await SandboxAgent.connect({
|
|
fetch: (input, init) => sandbox.containerFetch(input as Request | string | URL, init, PORT),
|
|
});
|
|
|
|
const session = await sdk.createSession({ agent: "codex" });
|
|
const response = await session.prompt([{ type: "text", text: "Summarize this repository" }]);
|
|
await sdk.destroySession(session.id);
|
|
await sdk.dispose();
|
|
|
|
return c.json(response);
|
|
});
|
|
|
|
app.all("/sandbox/:name/proxy/*", async (c) => {
|
|
const sandbox = await getReadySandbox(c.req.param("name"), c.env);
|
|
const wildcard = c.req.param("*");
|
|
const path = wildcard ? `/${wildcard}` : "/";
|
|
const query = new URL(c.req.raw.url).search;
|
|
|
|
return sandbox.containerFetch(new Request(`http://localhost${path}${query}`, c.req.raw), PORT);
|
|
});
|
|
|
|
app.all("*", (c) => c.env.ASSETS.fetch(c.req.raw));
|
|
|
|
export default app;
|
|
```
|
|
|
|
Create the SDK client inside the Worker using custom `fetch` backed by `sandbox.containerFetch(...)`.
|
|
This keeps all Sandbox Agent calls inside the Cloudflare sandbox routing path and does not require a `baseUrl`.
|
|
|
|
## Local development
|
|
|
|
```bash
|
|
npm run dev
|
|
```
|
|
|
|
Test health:
|
|
|
|
```bash
|
|
curl http://localhost:8787/sandbox/demo/proxy/v1/health
|
|
```
|
|
|
|
## Production deployment
|
|
|
|
```bash
|
|
wrangler deploy
|
|
```
|