feat(cloudflare): add React frontend and improve deployment docs

- Add React + Vite frontend for Cloudflare example with sandbox-agent SDK
- Update ensureRunning to poll health endpoint instead of fixed wait
- Fix SDK fetch binding issue (globalThis.fetch.bind)
- Update docs with .dev.vars format warning and container caching tip
- Use containerFetch proxy pattern for reliable local dev
This commit is contained in:
Nathan Flurry 2026-02-03 02:10:45 -08:00
parent 44382d2c12
commit 3576b7fcca
12 changed files with 619 additions and 425 deletions

View file

@ -22,10 +22,69 @@ npm create cloudflare@latest -- my-sandbox --template=cloudflare/sandbox-sdk/exa
cd my-sandbox 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
# 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 ## TypeScript Example
This example proxies requests to sandbox-agent via `containerFetch`, which works reliably in both local development and production:
```typescript ```typescript
import { getSandbox, proxyToSandbox, type Sandbox } from "@cloudflare/sandbox"; import { getSandbox, type Sandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox"; export { Sandbox } from "@cloudflare/sandbox";
type Env = { type Env = {
@ -34,62 +93,60 @@ type Env = {
OPENAI_API_KEY?: string; OPENAI_API_KEY?: string;
}; };
/** Check if sandbox-agent is already running by probing its health endpoint */ const PORT = 8000;
/** Check if sandbox-agent is already running */
async function isServerRunning(sandbox: Sandbox): Promise<boolean> { async function isServerRunning(sandbox: Sandbox): Promise<boolean> {
try { try {
const result = await sandbox.exec("curl -sf http://localhost:8000/v1/health"); const result = await sandbox.exec(`curl -sf http://localhost:${PORT}/v1/health`);
return result.success; return result.success;
} catch { } catch {
return false; 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}`
);
// 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));
}
}
export default { export default {
async fetch(request: Request, env: Env): Promise<Response> { async fetch(request: Request, env: Env): Promise<Response> {
// Proxy requests to exposed ports first const url = new URL(request.url);
const proxyResponse = await proxyToSandbox(request, env);
if (proxyResponse) return proxyResponse;
const { hostname } = new URL(request.url); // Proxy requests: /sandbox/:name/v1/...
const sandbox = getSandbox(env.Sandbox, "sandbox-agent"); const match = url.pathname.match(/^\/sandbox\/([^/]+)(\/.*)?$/);
if (match) {
const [, name, path = "/"] = match;
const sandbox = getSandbox(env.Sandbox, name);
// Check if server is already running to avoid port conflicts await ensureRunning(sandbox, env);
const alreadyRunning = await isServerRunning(sandbox);
if (!alreadyRunning) { // Proxy request to container
// Install sandbox-agent return sandbox.containerFetch(
await sandbox.exec( new Request(`http://localhost${path}${url.search}`, request),
"curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh" PORT
); );
// Install 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);
// Start sandbox-agent server as background process
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 return new Response("Not found", { status: 404 });
const exposed = await sandbox.exposePort(8000, { hostname });
return Response.json({
endpoint: exposed.url,
message: alreadyRunning
? "sandbox-agent server was already running"
: "sandbox-agent server started",
});
}, },
}; };
``` ```
@ -99,11 +156,10 @@ export default {
```typescript ```typescript
import { SandboxAgent } from "sandbox-agent"; import { SandboxAgent } from "sandbox-agent";
// Get the endpoint from the Worker // Connect via the proxy endpoint
const { endpoint } = await fetch("http://localhost:8787").then((r) => r.json()); const client = await SandboxAgent.connect({
baseUrl: "http://localhost:8787/sandbox/my-sandbox",
// Connect to sandbox-agent });
const client = await SandboxAgent.connect({ baseUrl: endpoint });
// Wait for server to be ready // Wait for server to be ready
for (let i = 0; i < 30; i++) { for (let i = 0; i < 30; i++) {
@ -116,42 +172,49 @@ for (let i = 0; i < 30; i++) {
} }
// Create a session and start coding // Create a session and start coding
await client.createSession("my-session", { await client.createSession("my-session", { agent: "claude" });
agent: "claude",
permissionMode: "default",
});
await client.postMessage("my-session", { await client.postMessage("my-session", {
message: "Summarize this repository", message: "Summarize this repository",
}); });
for await (const event of client.streamEvents("my-session")) { for await (const event of client.streamEvents("my-session")) {
console.log(event.type, event.data); // Auto-approve permissions
} if (event.type === "permission.requested") {
``` await client.replyPermission("my-session", event.data.permission_id, {
reply: "once",
});
}
## Configuration // Handle text output
if (event.type === "item.delta" && event.data?.delta) {
Update `wrangler.jsonc` to add environment variables: process.stdout.write(event.data.delta);
```jsonc
{
"vars": {
"ANTHROPIC_API_KEY": "your-api-key"
} }
} }
``` ```
Or use `.dev.vars` for local development: ## Environment Variables
Use `.dev.vars` for local development:
```bash ```bash
echo "ANTHROPIC_API_KEY=your-api-key" > .dev.vars 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> <Note>
The `.dev.vars` file is automatically gitignored and only used during local development with `npm run dev`. The `.dev.vars` file is automatically gitignored and only used during local development with `npm run dev`.
</Note> </Note>
For production, set secrets via wrangler:
```bash
wrangler secret put ANTHROPIC_API_KEY
```
## Local Development ## Local Development
Start the development server: Start the development server:
@ -167,40 +230,22 @@ First run builds the Docker container (2-3 minutes). Subsequent runs are much fa
Test with curl: Test with curl:
```bash ```bash
curl http://localhost:8787 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 ## Production Deployment
For production, preview URLs require a custom domain with wildcard DNS routing. Deploy to Cloudflare:
See [Cloudflare Production Deployment](https://developers.cloudflare.com/sandbox/guides/production-deployment/) for setup instructions. ```bash
wrangler deploy
## Faster Cold Starts
To speed up cold starts, you can:
1. Create a custom Dockerfile with sandbox-agent pre-installed
2. Pre-install agents in the Docker image
Example `Dockerfile`:
```dockerfile
FROM docker.io/cloudflare/sandbox:0.7.0
RUN curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh && \
sandbox-agent install-agent claude && \
sandbox-agent install-agent codex
``` ```
Then update `wrangler.jsonc`: 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.
```jsonc
{
"containers": {
"sandbox": {
"image": "./Dockerfile"
}
}
}
```

View file

@ -49,9 +49,9 @@
"deploy/index", "deploy/index",
"deploy/local", "deploy/local",
"deploy/e2b", "deploy/e2b",
"deploy/daytona",
"deploy/vercel", "deploy/vercel",
"deploy/cloudflare", "deploy/cloudflare",
"deploy/daytona",
"deploy/docker" "deploy/docker"
] ]
}, },

View file

@ -0,0 +1,11 @@
FROM cloudflare/sandbox:0.7.0
# Install sandbox-agent
RUN curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh
# Pre-install agents
RUN sandbox-agent install-agent claude && \
sandbox-agent install-agent codex
# Expose port for local dev (wrangler dev requires EXPOSE directives)
EXPOSE 8000

View file

@ -0,0 +1,272 @@
import { useState, useRef, useEffect, useCallback } from "react";
import { SandboxAgent } from "sandbox-agent";
import type { PermissionEventData, QuestionEventData } from "sandbox-agent";
export function App() {
const [sandboxName, setSandboxName] = useState("demo");
const [prompt, setPrompt] = useState("");
const [output, setOutput] = useState("");
const [status, setStatus] = useState<"idle" | "connecting" | "ready" | "thinking">("idle");
const [error, setError] = useState<string | null>(null);
const clientRef = useRef<SandboxAgent | null>(null);
const sessionIdRef = useRef<string>(`session-${Date.now()}`);
const abortRef = useRef<AbortController | null>(null);
const isThinkingRef = useRef(false);
const log = useCallback((msg: string) => {
setOutput((prev) => prev + msg + "\n");
}, []);
const connect = useCallback(async () => {
setStatus("connecting");
setError(null);
setOutput("");
try {
// Connect via proxy endpoint (need full URL for SDK)
const baseUrl = `${window.location.origin}/sandbox/${encodeURIComponent(sandboxName)}`;
log(`Connecting to sandbox: ${sandboxName}`);
const client = await SandboxAgent.connect({ baseUrl });
clientRef.current = client;
// Wait for health (this also ensures the container is started)
log("Waiting for sandbox-agent to be ready...");
for (let i = 0; i < 30; i++) {
try {
await client.getHealth();
break;
} catch {
if (i === 29) throw new Error("Timeout waiting for sandbox-agent");
await new Promise((r) => setTimeout(r, 1000));
}
}
// Create session
await client.createSession(sessionIdRef.current, { agent: "claude" });
log("Session created. Ready to chat.\n");
setStatus("ready");
// Start listening for events
startEventStream(client);
} catch (err) {
setError(err instanceof Error ? err.message : String(err));
setStatus("idle");
}
}, [sandboxName, log]);
const startEventStream = useCallback(
async (client: SandboxAgent) => {
abortRef.current?.abort();
const controller = new AbortController();
abortRef.current = controller;
try {
for await (const event of client.streamEvents(sessionIdRef.current, undefined, controller.signal)) {
console.log("Event:", event.type, event.data);
// Auto-approve permissions
if (event.type === "permission.requested") {
const data = event.data as PermissionEventData;
log(`[Auto-approved] ${data.action}`);
await client.replyPermission(sessionIdRef.current, data.permission_id, { reply: "once" });
}
// Reject questions (don't support interactive input)
if (event.type === "question.requested") {
const data = event.data as QuestionEventData;
log(`[Question rejected] ${data.prompt}`);
await client.rejectQuestion(sessionIdRef.current, data.question_id);
}
// Track when assistant starts thinking
if (event.type === "item.started") {
const item = (event.data as any)?.item;
if (item?.role === "assistant") {
isThinkingRef.current = true;
}
}
// Show deltas while assistant is thinking
if (event.type === "item.delta" && isThinkingRef.current) {
const delta = (event.data as any)?.delta;
if (delta) {
const text = typeof delta === "string" ? delta : delta.type === "text" ? delta.text || "" : "";
if (text) {
setOutput((prev) => prev + text);
}
}
}
// Track assistant turn completion
if (event.type === "item.completed") {
const item = (event.data as any)?.item;
if (item?.role === "assistant") {
isThinkingRef.current = false;
setOutput((prev) => prev + "\n\n");
setStatus("ready");
}
}
// Handle errors
if (event.type === "error") {
const data = event.data as any;
log(`Error: ${data?.message || JSON.stringify(data)}`);
}
// Handle session end
if (event.type === "session.ended") {
const data = event.data as any;
log(`Session ended: ${data?.reason || "unknown"}`);
setStatus("idle");
}
}
} catch (err) {
if (controller.signal.aborted) return;
console.error("Event stream error:", err);
}
},
[log]
);
const send = useCallback(async () => {
if (!clientRef.current || !prompt.trim() || status !== "ready") return;
const message = prompt.trim();
setPrompt("");
setOutput((prev) => prev + `user: ${message}\n\nassistant: `);
setStatus("thinking");
try {
await clientRef.current.postMessage(sessionIdRef.current, { message });
} catch (err) {
setError(err instanceof Error ? err.message : String(err));
setStatus("ready");
}
}, [prompt, status]);
// Cleanup on unmount
useEffect(() => {
return () => {
abortRef.current?.abort();
};
}, []);
return (
<div style={styles.container}>
<h1 style={styles.title}>Sandbox Agent</h1>
{status === "idle" && (
<div style={styles.connectForm}>
<label style={styles.label}>
Sandbox name:
<input
style={styles.input}
value={sandboxName}
onChange={(e) => setSandboxName(e.target.value)}
placeholder="demo"
/>
</label>
<button style={styles.button} onClick={connect}>
Connect
</button>
</div>
)}
{status === "connecting" && <div style={styles.status}>Connecting to sandbox...</div>}
{error && <div style={styles.error}>{error}</div>}
{(status === "ready" || status === "thinking") && (
<>
<div style={styles.output}>{output}</div>
<div style={styles.inputRow}>
<input
style={styles.promptInput}
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && send()}
placeholder="Enter prompt..."
disabled={status === "thinking"}
/>
<button style={styles.button} onClick={send} disabled={status === "thinking"}>
{status === "thinking" ? "..." : "Send"}
</button>
</div>
</>
)}
</div>
);
}
const styles: Record<string, React.CSSProperties> = {
container: {
fontFamily: "system-ui, sans-serif",
maxWidth: 800,
margin: "2rem auto",
padding: "1rem",
},
title: {
marginBottom: "1rem",
},
connectForm: {
display: "flex",
gap: "1rem",
alignItems: "flex-end",
},
label: {
display: "flex",
flexDirection: "column",
gap: "0.25rem",
fontSize: "0.875rem",
color: "#666",
},
input: {
padding: "0.5rem",
fontSize: "1rem",
width: 200,
},
button: {
padding: "0.5rem 1rem",
fontSize: "1rem",
cursor: "pointer",
backgroundColor: "#0066cc",
color: "white",
border: "none",
borderRadius: 4,
},
status: {
color: "#666",
fontStyle: "italic",
},
error: {
color: "#cc0000",
padding: "0.5rem",
backgroundColor: "#fff0f0",
borderRadius: 4,
marginBottom: "1rem",
},
output: {
whiteSpace: "pre-wrap",
background: "#1e1e1e",
color: "#d4d4d4",
padding: "1rem",
minHeight: 300,
fontFamily: "monospace",
fontSize: 14,
overflow: "auto",
borderRadius: 4,
},
inputRow: {
display: "flex",
gap: "0.5rem",
marginTop: "1rem",
},
promptInput: {
flex: 1,
padding: "0.5rem",
fontSize: "1rem",
},
};

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sandbox Agent</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/main.tsx"></script>
</body>
</html>

View file

@ -0,0 +1,9 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { App } from "./App";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>
);

View file

@ -3,17 +3,25 @@
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "wrangler dev", "dev": "vite build --watch & wrangler dev",
"deploy": "wrangler deploy", "build": "vite build",
"deploy": "vite build && wrangler deploy",
"typecheck": "tsc --noEmit" "typecheck": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@cloudflare/sandbox": "latest" "@cloudflare/sandbox": "latest",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"sandbox-agent": "workspace:*"
}, },
"devDependencies": { "devDependencies": {
"@cloudflare/workers-types": "latest", "@cloudflare/workers-types": "latest",
"@types/node": "latest", "@types/node": "latest",
"@types/react": "^19.1.0",
"@types/react-dom": "^19.1.0",
"@vitejs/plugin-react": "^4.5.0",
"typescript": "latest", "typescript": "latest",
"vite": "^6.2.0",
"vitest": "^3.0.0", "vitest": "^3.0.0",
"wrangler": "latest" "wrangler": "latest"
} }

View file

@ -1,69 +1,76 @@
import { getSandbox, proxyToSandbox, type Sandbox } from "@cloudflare/sandbox"; import { getSandbox, type Sandbox } from "@cloudflare/sandbox";
export { Sandbox } from "@cloudflare/sandbox"; export { Sandbox } from "@cloudflare/sandbox";
type Env = { type Env = {
Sandbox: DurableObjectNamespace<Sandbox>; Bindings: {
ANTHROPIC_API_KEY?: string; Sandbox: DurableObjectNamespace<Sandbox>;
OPENAI_API_KEY?: string; ASSETS: Fetcher;
ANTHROPIC_API_KEY?: string;
OPENAI_API_KEY?: string;
};
}; };
const PORT = 8000;
/** Check if sandbox-agent is already running by probing its health endpoint */ /** Check if sandbox-agent is already running by probing its health endpoint */
async function isServerRunning(sandbox: Sandbox): Promise<boolean> { async function isServerRunning(sandbox: Sandbox): Promise<boolean> {
try { try {
const result = await sandbox.exec("curl -sf http://localhost:8000/v1/health"); const result = await sandbox.exec(`curl -sf http://localhost:${PORT}/v1/health`);
return result.success; return result.success;
} catch { } catch {
return false; return false;
} }
} }
/** Ensure sandbox-agent is running in the container */
async function ensureRunning(sandbox: Sandbox, env: Env["Bindings"]): 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 as background process
await sandbox.startProcess(`sandbox-agent server --no-token --host 0.0.0.0 --port ${PORT}`);
// Poll health endpoint until server is ready (max ~6 seconds)
for (let i = 0; i < 30; i++) {
if (await isServerRunning(sandbox)) return;
await new Promise((r) => setTimeout(r, 200));
}
}
export default { export default {
async fetch(request: Request, env: Env): Promise<Response> { async fetch(request: Request, env: Env["Bindings"]): Promise<Response> {
// Proxy requests to exposed ports first const url = new URL(request.url);
const proxyResponse = await proxyToSandbox(request, env);
if (proxyResponse) return proxyResponse;
const { hostname } = new URL(request.url); // Proxy requests to sandbox-agent: /sandbox/:name/v1/...
const sandbox = getSandbox(env.Sandbox, "sandbox-agent"); const match = url.pathname.match(/^\/sandbox\/([^/]+)(\/.*)?$/);
if (match) {
if (!env.ANTHROPIC_API_KEY && !env.OPENAI_API_KEY) {
return Response.json(
{ error: "ANTHROPIC_API_KEY or OPENAI_API_KEY must be set" },
{ status: 500 }
);
}
// Check if server is already running to avoid port conflicts const name = match[1];
const alreadyRunning = await isServerRunning(sandbox); const path = match[2] || "/";
const sandbox = getSandbox(env.Sandbox, name);
if (!alreadyRunning) { await ensureRunning(sandbox, env);
console.log("Installing sandbox-agent...");
await sandbox.exec( // Proxy request to container
"curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh" return sandbox.containerFetch(
new Request(`http://localhost${path}${url.search}`, request),
PORT
); );
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 // Serve frontend assets
const exposed = await sandbox.exposePort(8000, { hostname }); return env.ASSETS.fetch(request);
console.log("Server accessible at:", exposed.url);
return Response.json({
endpoint: exposed.url,
message: alreadyRunning
? "sandbox-agent server was already running"
: "sandbox-agent server started",
});
}, },
}; } satisfies ExportedHandler<Env["Bindings"]>;

View file

@ -0,0 +1,11 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
root: "frontend",
build: {
outDir: "../dist",
emptyOutDir: true,
},
});

View file

@ -4,10 +4,14 @@
"main": "src/cloudflare.ts", "main": "src/cloudflare.ts",
"compatibility_date": "2025-01-01", "compatibility_date": "2025-01-01",
"compatibility_flags": ["nodejs_compat"], "compatibility_flags": ["nodejs_compat"],
"assets": {
"directory": "./dist",
"binding": "ASSETS"
},
"containers": [ "containers": [
{ {
"class_name": "Sandbox", "class_name": "Sandbox",
"image": "docker.io/cloudflare/sandbox:0.7.0", "image": "./Dockerfile",
"instance_type": "lite", "instance_type": "lite",
"max_instances": 1 "max_instances": 1
} }

375
pnpm-lock.yaml generated
View file

@ -13,13 +13,22 @@ importers:
version: 2.7.6 version: 2.7.6
vitest: vitest:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.0) version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
examples/cloudflare: examples/cloudflare:
dependencies: dependencies:
'@cloudflare/sandbox': '@cloudflare/sandbox':
specifier: latest specifier: latest
version: 0.7.0 version: 0.7.0
react:
specifier: ^19.1.0
version: 19.2.4
react-dom:
specifier: ^19.1.0
version: 19.2.4(react@19.2.4)
sandbox-agent:
specifier: workspace:*
version: link:../../sdks/typescript
devDependencies: devDependencies:
'@cloudflare/workers-types': '@cloudflare/workers-types':
specifier: latest specifier: latest
@ -27,12 +36,24 @@ importers:
'@types/node': '@types/node':
specifier: latest specifier: latest
version: 25.2.0 version: 25.2.0
'@types/react':
specifier: ^19.1.0
version: 19.2.10
'@types/react-dom':
specifier: ^19.1.0
version: 19.2.3(@types/react@19.2.10)
'@vitejs/plugin-react':
specifier: ^4.5.0
version: 4.7.0(vite@6.4.1(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2))
typescript: typescript:
specifier: latest specifier: latest
version: 5.9.3 version: 5.9.3
vite:
specifier: ^6.2.0
version: 6.4.1(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
vitest: vitest:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.0) version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
wrangler: wrangler:
specifier: latest specifier: latest
version: 4.61.1(@cloudflare/workers-types@4.20260131.0) version: 4.61.1(@cloudflare/workers-types@4.20260131.0)
@ -41,14 +62,14 @@ importers:
dependencies: dependencies:
'@daytonaio/sdk': '@daytonaio/sdk':
specifier: latest specifier: latest
version: 0.135.0(ws@8.19.0) version: 0.138.0(ws@8.19.0)
'@sandbox-agent/example-shared': '@sandbox-agent/example-shared':
specifier: workspace:* specifier: workspace:*
version: link:../shared version: link:../shared
devDependencies: devDependencies:
'@types/node': '@types/node':
specifier: latest specifier: latest
version: 25.0.10 version: 25.2.0
tsx: tsx:
specifier: latest specifier: latest
version: 4.21.0 version: 4.21.0
@ -70,7 +91,7 @@ importers:
version: 4.0.1 version: 4.0.1
'@types/node': '@types/node':
specifier: latest specifier: latest
version: 25.0.10 version: 25.2.0
tsx: tsx:
specifier: latest specifier: latest
version: 4.21.0 version: 4.21.0
@ -79,7 +100,7 @@ importers:
version: 5.9.3 version: 5.9.3
vitest: vitest:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.2.4(@types/debug@4.1.12)(@types/node@25.0.10) version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
examples/e2b: examples/e2b:
dependencies: dependencies:
@ -95,7 +116,7 @@ importers:
devDependencies: devDependencies:
'@types/node': '@types/node':
specifier: latest specifier: latest
version: 25.0.10 version: 25.2.0
tsx: tsx:
specifier: latest specifier: latest
version: 4.21.0 version: 4.21.0
@ -104,7 +125,7 @@ importers:
version: 5.9.3 version: 5.9.3
vitest: vitest:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.2.4(@types/debug@4.1.12)(@types/node@25.0.10) version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
examples/shared: examples/shared:
dependencies: dependencies:
@ -114,7 +135,7 @@ importers:
devDependencies: devDependencies:
'@types/node': '@types/node':
specifier: latest specifier: latest
version: 25.0.10 version: 25.2.0
typescript: typescript:
specifier: latest specifier: latest
version: 5.9.3 version: 5.9.3
@ -133,7 +154,7 @@ importers:
devDependencies: devDependencies:
'@types/node': '@types/node':
specifier: latest specifier: latest
version: 25.1.0 version: 25.2.0
tsx: tsx:
specifier: latest specifier: latest
version: 4.21.0 version: 4.21.0
@ -142,7 +163,7 @@ importers:
version: 5.9.3 version: 5.9.3
vitest: vitest:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.2.4(@types/debug@4.1.12)(@types/node@25.1.0) version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
frontend/packages/inspector: frontend/packages/inspector:
dependencies: dependencies:
@ -216,10 +237,10 @@ importers:
dependencies: dependencies:
'@anthropic-ai/claude-code': '@anthropic-ai/claude-code':
specifier: latest specifier: latest
version: 2.1.22 version: 2.1.29
'@openai/codex': '@openai/codex':
specifier: latest specifier: latest
version: 0.92.0 version: 0.94.0
cheerio: cheerio:
specifier: ^1.0.0 specifier: ^1.0.0
version: 1.2.0 version: 1.2.0
@ -294,14 +315,14 @@ importers:
dependencies: dependencies:
'@daytonaio/sdk': '@daytonaio/sdk':
specifier: latest specifier: latest
version: 0.135.0(ws@8.19.0) version: 0.138.0(ws@8.19.0)
'@e2b/code-interpreter': '@e2b/code-interpreter':
specifier: latest specifier: latest
version: 2.3.3 version: 2.3.3
devDependencies: devDependencies:
'@types/node': '@types/node':
specifier: latest specifier: latest
version: 25.0.10 version: 25.2.0
tsx: tsx:
specifier: latest specifier: latest
version: 4.21.0 version: 4.21.0
@ -326,7 +347,7 @@ importers:
devDependencies: devDependencies:
vitest: vitest:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.0) version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
sdks/cli/platforms/darwin-arm64: {} sdks/cli/platforms/darwin-arm64: {}
@ -352,7 +373,7 @@ importers:
version: 5.9.3 version: 5.9.3
vitest: vitest:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.7) version: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.7)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
packages: packages:
@ -360,8 +381,8 @@ packages:
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'} engines: {node: '>=10'}
'@anthropic-ai/claude-code@2.1.22': '@anthropic-ai/claude-code@2.1.29':
resolution: {integrity: sha512-WMwUUC/Ux87LqBDBC4KI/uYE0L/jcro3XcBzyd4a/YCkGVRyruyhypeeyHqAW7bUxm72xxWaPoy0keBkxpgIpQ==} resolution: {integrity: sha512-vMHTOXrYdnreGtKUsWdd3Bwx5fKprTyNG7shrvbx3L2/jU9jexkOJrEKmN5loeR5jrE54LSB38QpaIj8pVM6eQ==}
engines: {node: '>=18.0.0'} engines: {node: '>=18.0.0'}
hasBin: true hasBin: true
@ -754,14 +775,14 @@ packages:
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'} engines: {node: '>=12'}
'@daytonaio/api-client@0.135.0': '@daytonaio/api-client@0.138.0':
resolution: {integrity: sha512-7/gY3FimUXtgQvyUzEnxlr6ztET7G7bG0whdc4HRmeoWIZhNX+Fr2L2IpOH55UvFkb0CY7p60Ecubx2IltlMJA==} resolution: {integrity: sha512-mKO3Aqk2aCnOw4ej+UxvKE+Z1ixmo9OKTAFElkvRb6UOwb5zioudqTyqEfijkA2tXUXO8yPGhQDPaICLgpPopA==}
'@daytonaio/sdk@0.135.0': '@daytonaio/sdk@0.138.0':
resolution: {integrity: sha512-bJBBpLvFAfpcGlI2rg5xe2lKf0P5RWmt2OSL73mRyWJBQ1m2NRPijnmnE//sf62YoOOZpwL2Ykq3DkT8fczrew==} resolution: {integrity: sha512-cnbsflZYJ1NA4pQ2uX2lLN4w4ZQsO/xqdGDnpmwSu/LIW5F+O5gA8z4mfuWdIRcFFT4UhIpTzMuh3zRwxH7dIw==}
'@daytonaio/toolbox-api-client@0.135.0': '@daytonaio/toolbox-api-client@0.138.0':
resolution: {integrity: sha512-XkaFm3nKF9PlOJi/qZS8pXpKjDn00X/lFvtpwUwCzIZSe0sC68uJZfp7/+DNgIVU1927kBOKgiyb6OMp4MCLDw==} resolution: {integrity: sha512-unM9e7MOQiyDXdY8hCW1uTctYbxpo/TGZ6L71ZXyS/j2Cnz9/ud4VWBLcQP2VzlC+lrBP2YMrhT90zSSvcNfmA==}
'@e2b/code-interpreter@2.3.3': '@e2b/code-interpreter@2.3.3':
resolution: {integrity: sha512-WOpSwc1WpvxyOijf6WMbR76BUuvd2O9ddXgCHHi65lkuy6YgQGq7oyd8PNsT331O9Tqbccjy6uF4xanSdLX1UA==} resolution: {integrity: sha512-WOpSwc1WpvxyOijf6WMbR76BUuvd2O9ddXgCHHi65lkuy6YgQGq7oyd8PNsT331O9Tqbccjy6uF4xanSdLX1UA==}
@ -1667,8 +1688,8 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
'@openai/codex@0.92.0': '@openai/codex@0.94.0':
resolution: {integrity: sha512-DR9A2QlJDtEpMwqUGMIztTCzzCYTVrM7rqG3XuMVURnQ4b7XrScmY5RnSUuUZ/ga7wDTqw0BTmVzPurm4NX3Tw==} resolution: {integrity: sha512-GKOU2ty3NXls2aeiFSCnSSB6zQBtENqC5OnPa8s79Z576YP1r2DIfUrhQZzVDKmFei852E1SG4TNljFL/081gg==}
engines: {node: '>=16'} engines: {node: '>=16'}
hasBin: true hasBin: true
@ -2153,12 +2174,6 @@ packages:
'@types/node@24.10.9': '@types/node@24.10.9':
resolution: {integrity: sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==} resolution: {integrity: sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==}
'@types/node@25.0.10':
resolution: {integrity: sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==}
'@types/node@25.1.0':
resolution: {integrity: sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==}
'@types/node@25.2.0': '@types/node@25.2.0':
resolution: {integrity: sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==} resolution: {integrity: sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==}
@ -2467,10 +2482,6 @@ packages:
chownr@1.1.4: chownr@1.1.4:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
chownr@2.0.0:
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
engines: {node: '>=10'}
chownr@3.0.0: chownr@3.0.0:
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -2868,10 +2879,6 @@ packages:
fs-constants@1.0.0: fs-constants@1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
fs-minipass@2.1.0:
resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
engines: {node: '>= 8'}
fsevents@2.3.3: fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@ -3332,22 +3339,10 @@ packages:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'} engines: {node: '>=16 || 14 >=14.17'}
minipass@3.3.6:
resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
engines: {node: '>=8'}
minipass@5.0.0:
resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
engines: {node: '>=8'}
minipass@7.1.2: minipass@7.1.2:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'} engines: {node: '>=16 || 14 >=14.17'}
minizlib@2.1.2:
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
engines: {node: '>= 8'}
minizlib@3.1.0: minizlib@3.1.0:
resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==}
engines: {node: '>= 18'} engines: {node: '>= 18'}
@ -3355,11 +3350,6 @@ packages:
mkdirp-classic@0.5.3: mkdirp-classic@0.5.3:
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
mkdirp@1.0.4:
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
engines: {node: '>=10'}
hasBin: true
mlly@1.8.0: mlly@1.8.0:
resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
@ -3923,11 +3913,6 @@ packages:
tar-stream@3.1.7: tar-stream@3.1.7:
resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==}
tar@6.2.1:
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
engines: {node: '>=10'}
deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me
tar@7.5.6: tar@7.5.6:
resolution: {integrity: sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==} resolution: {integrity: sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -4447,9 +4432,6 @@ packages:
yallist@3.1.1: yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
yallist@5.0.0: yallist@5.0.0:
resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -4509,7 +4491,7 @@ snapshots:
'@alloc/quick-lru@5.2.0': {} '@alloc/quick-lru@5.2.0': {}
'@anthropic-ai/claude-code@2.1.22': '@anthropic-ai/claude-code@2.1.29':
optionalDependencies: optionalDependencies:
'@img/sharp-darwin-arm64': 0.33.5 '@img/sharp-darwin-arm64': 0.33.5
'@img/sharp-darwin-x64': 0.33.5 '@img/sharp-darwin-x64': 0.33.5
@ -5307,18 +5289,18 @@ snapshots:
dependencies: dependencies:
'@jridgewell/trace-mapping': 0.3.9 '@jridgewell/trace-mapping': 0.3.9
'@daytonaio/api-client@0.135.0': '@daytonaio/api-client@0.138.0':
dependencies: dependencies:
axios: 1.13.4 axios: 1.13.4
transitivePeerDependencies: transitivePeerDependencies:
- debug - debug
'@daytonaio/sdk@0.135.0(ws@8.19.0)': '@daytonaio/sdk@0.138.0(ws@8.19.0)':
dependencies: dependencies:
'@aws-sdk/client-s3': 3.975.0 '@aws-sdk/client-s3': 3.975.0
'@aws-sdk/lib-storage': 3.975.0(@aws-sdk/client-s3@3.975.0) '@aws-sdk/lib-storage': 3.975.0(@aws-sdk/client-s3@3.975.0)
'@daytonaio/api-client': 0.135.0 '@daytonaio/api-client': 0.138.0
'@daytonaio/toolbox-api-client': 0.135.0 '@daytonaio/toolbox-api-client': 0.138.0
'@iarna/toml': 2.2.5 '@iarna/toml': 2.2.5
axios: 1.13.4 axios: 1.13.4
busboy: 1.6.0 busboy: 1.6.0
@ -5329,13 +5311,13 @@ snapshots:
isomorphic-ws: 5.0.0(ws@8.19.0) isomorphic-ws: 5.0.0(ws@8.19.0)
pathe: 2.0.3 pathe: 2.0.3
shell-quote: 1.8.3 shell-quote: 1.8.3
tar: 6.2.1 tar: 7.5.6
transitivePeerDependencies: transitivePeerDependencies:
- aws-crt - aws-crt
- debug - debug
- ws - ws
'@daytonaio/toolbox-api-client@0.135.0': '@daytonaio/toolbox-api-client@0.138.0':
dependencies: dependencies:
axios: 1.13.4 axios: 1.13.4
transitivePeerDependencies: transitivePeerDependencies:
@ -5888,7 +5870,7 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5 '@nodelib/fs.scandir': 2.1.5
fastq: 1.20.1 fastq: 1.20.1
'@openai/codex@0.92.0': {} '@openai/codex@0.94.0': {}
'@oslojs/encoding@1.1.0': {} '@oslojs/encoding@1.1.0': {}
@ -6424,13 +6406,13 @@ snapshots:
'@types/docker-modem@3.0.6': '@types/docker-modem@3.0.6':
dependencies: dependencies:
'@types/node': 24.10.9 '@types/node': 25.2.0
'@types/ssh2': 1.15.5 '@types/ssh2': 1.15.5
'@types/dockerode@4.0.1': '@types/dockerode@4.0.1':
dependencies: dependencies:
'@types/docker-modem': 3.0.6 '@types/docker-modem': 3.0.6
'@types/node': 24.10.9 '@types/node': 25.2.0
'@types/ssh2': 1.15.5 '@types/ssh2': 1.15.5
'@types/estree@1.0.8': {} '@types/estree@1.0.8': {}
@ -6463,14 +6445,6 @@ snapshots:
dependencies: dependencies:
undici-types: 7.16.0 undici-types: 7.16.0
'@types/node@25.0.10':
dependencies:
undici-types: 7.16.0
'@types/node@25.1.0':
dependencies:
undici-types: 7.16.0
'@types/node@25.2.0': '@types/node@25.2.0':
dependencies: dependencies:
undici-types: 7.16.0 undici-types: 7.16.0
@ -6553,30 +6527,6 @@ snapshots:
chai: 5.3.3 chai: 5.3.3
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
'@vitest/mocker@3.2.4(vite@5.4.21(@types/node@22.19.7))':
dependencies:
'@vitest/spy': 3.2.4
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
vite: 5.4.21(@types/node@22.19.7)
'@vitest/mocker@3.2.4(vite@5.4.21(@types/node@25.0.10))':
dependencies:
'@vitest/spy': 3.2.4
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
vite: 5.4.21(@types/node@25.0.10)
'@vitest/mocker@3.2.4(vite@5.4.21(@types/node@25.1.0))':
dependencies:
'@vitest/spy': 3.2.4
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
vite: 5.4.21(@types/node@25.1.0)
'@vitest/mocker@3.2.4(vite@5.4.21(@types/node@25.2.0))': '@vitest/mocker@3.2.4(vite@5.4.21(@types/node@25.2.0))':
dependencies: dependencies:
'@vitest/spy': 3.2.4 '@vitest/spy': 3.2.4
@ -6936,8 +6886,6 @@ snapshots:
chownr@1.1.4: {} chownr@1.1.4: {}
chownr@2.0.0: {}
chownr@3.0.0: {} chownr@3.0.0: {}
ci-info@4.3.1: {} ci-info@4.3.1: {}
@ -7400,10 +7348,6 @@ snapshots:
fs-constants@1.0.0: {} fs-constants@1.0.0: {}
fs-minipass@2.1.0:
dependencies:
minipass: 3.3.6
fsevents@2.3.3: fsevents@2.3.3:
optional: true optional: true
@ -8079,27 +8023,14 @@ snapshots:
dependencies: dependencies:
brace-expansion: 2.0.2 brace-expansion: 2.0.2
minipass@3.3.6:
dependencies:
yallist: 4.0.0
minipass@5.0.0: {}
minipass@7.1.2: {} minipass@7.1.2: {}
minizlib@2.1.2:
dependencies:
minipass: 3.3.6
yallist: 4.0.0
minizlib@3.1.0: minizlib@3.1.0:
dependencies: dependencies:
minipass: 7.1.2 minipass: 7.1.2
mkdirp-classic@0.5.3: {} mkdirp-classic@0.5.3: {}
mkdirp@1.0.4: {}
mlly@1.8.0: mlly@1.8.0:
dependencies: dependencies:
acorn: 8.15.0 acorn: 8.15.0
@ -8343,7 +8274,7 @@ snapshots:
'@protobufjs/path': 1.1.2 '@protobufjs/path': 1.1.2
'@protobufjs/pool': 1.1.0 '@protobufjs/pool': 1.1.0
'@protobufjs/utf8': 1.1.0 '@protobufjs/utf8': 1.1.0
'@types/node': 24.10.9 '@types/node': 25.2.0
long: 5.3.2 long: 5.3.2
proxy-from-env@1.1.0: {} proxy-from-env@1.1.0: {}
@ -8779,15 +8710,6 @@ snapshots:
- bare-abort-controller - bare-abort-controller
- react-native-b4a - react-native-b4a
tar@6.2.1:
dependencies:
chownr: 2.0.0
fs-minipass: 2.1.0
minipass: 5.0.0
minizlib: 2.1.2
mkdirp: 1.0.4
yallist: 4.0.0
tar@7.5.6: tar@7.5.6:
dependencies: dependencies:
'@isaacs/fs-minipass': 4.0.1 '@isaacs/fs-minipass': 4.0.1
@ -9044,15 +8966,16 @@ snapshots:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
vfile-message: 4.0.3 vfile-message: 4.0.3
vite-node@3.2.4(@types/node@22.19.7): vite-node@3.2.4(@types/node@22.19.7)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2):
dependencies: dependencies:
cac: 6.7.14 cac: 6.7.14
debug: 4.4.3 debug: 4.4.3
es-module-lexer: 1.7.0 es-module-lexer: 1.7.0
pathe: 2.0.3 pathe: 2.0.3
vite: 5.4.21(@types/node@22.19.7) vite: 6.4.1(@types/node@22.19.7)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- jiti
- less - less
- lightningcss - lightningcss
- sass - sass
@ -9061,52 +8984,19 @@ snapshots:
- sugarss - sugarss
- supports-color - supports-color
- terser - terser
- tsx
- yaml
vite-node@3.2.4(@types/node@25.0.10): vite-node@3.2.4(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2):
dependencies: dependencies:
cac: 6.7.14 cac: 6.7.14
debug: 4.4.3 debug: 4.4.3
es-module-lexer: 1.7.0 es-module-lexer: 1.7.0
pathe: 2.0.3 pathe: 2.0.3
vite: 5.4.21(@types/node@25.0.10) vite: 6.4.1(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
transitivePeerDependencies:
- '@types/node'
- less
- lightningcss
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
vite-node@3.2.4(@types/node@25.1.0):
dependencies:
cac: 6.7.14
debug: 4.4.3
es-module-lexer: 1.7.0
pathe: 2.0.3
vite: 5.4.21(@types/node@25.1.0)
transitivePeerDependencies:
- '@types/node'
- less
- lightningcss
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
vite-node@3.2.4(@types/node@25.2.0):
dependencies:
cac: 6.7.14
debug: 4.4.3
es-module-lexer: 1.7.0
pathe: 2.0.3
vite: 5.4.21(@types/node@25.2.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- jiti
- less - less
- lightningcss - lightningcss
- sass - sass
@ -9115,6 +9005,8 @@ snapshots:
- sugarss - sugarss
- supports-color - supports-color
- terser - terser
- tsx
- yaml
vite@5.4.21(@types/node@22.19.7): vite@5.4.21(@types/node@22.19.7):
dependencies: dependencies:
@ -9125,24 +9017,6 @@ snapshots:
'@types/node': 22.19.7 '@types/node': 22.19.7
fsevents: 2.3.3 fsevents: 2.3.3
vite@5.4.21(@types/node@25.0.10):
dependencies:
esbuild: 0.21.5
postcss: 8.5.6
rollup: 4.56.0
optionalDependencies:
'@types/node': 25.0.10
fsevents: 2.3.3
vite@5.4.21(@types/node@25.1.0):
dependencies:
esbuild: 0.21.5
postcss: 8.5.6
rollup: 4.56.0
optionalDependencies:
'@types/node': 25.1.0
fsevents: 2.3.3
vite@5.4.21(@types/node@25.2.0): vite@5.4.21(@types/node@25.2.0):
dependencies: dependencies:
esbuild: 0.21.5 esbuild: 0.21.5
@ -9152,6 +9026,21 @@ snapshots:
'@types/node': 25.2.0 '@types/node': 25.2.0
fsevents: 2.3.3 fsevents: 2.3.3
vite@6.4.1(@types/node@22.19.7)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2):
dependencies:
esbuild: 0.25.12
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.6
rollup: 4.56.0
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 22.19.7
fsevents: 2.3.3
jiti: 1.21.7
tsx: 4.21.0
yaml: 2.8.2
vite@6.4.1(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2): vite@6.4.1(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2):
dependencies: dependencies:
esbuild: 0.25.12 esbuild: 0.25.12
@ -9171,11 +9060,11 @@ snapshots:
optionalDependencies: optionalDependencies:
vite: 6.4.1(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) vite: 6.4.1(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.19.7): vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.19.7)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2):
dependencies: dependencies:
'@types/chai': 5.2.3 '@types/chai': 5.2.3
'@vitest/expect': 3.2.4 '@vitest/expect': 3.2.4
'@vitest/mocker': 3.2.4(vite@5.4.21(@types/node@22.19.7)) '@vitest/mocker': 3.2.4(vite@5.4.21(@types/node@25.2.0))
'@vitest/pretty-format': 3.2.4 '@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4 '@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4 '@vitest/snapshot': 3.2.4
@ -9194,12 +9083,13 @@ snapshots:
tinypool: 1.1.1 tinypool: 1.1.1
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
vite: 5.4.21(@types/node@22.19.7) vite: 5.4.21(@types/node@22.19.7)
vite-node: 3.2.4(@types/node@22.19.7) vite-node: 3.2.4(@types/node@22.19.7)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@types/debug': 4.1.12 '@types/debug': 4.1.12
'@types/node': 22.19.7 '@types/node': 22.19.7
transitivePeerDependencies: transitivePeerDependencies:
- jiti
- less - less
- lightningcss - lightningcss
- msw - msw
@ -9209,86 +9099,10 @@ snapshots:
- sugarss - sugarss
- supports-color - supports-color
- terser - terser
- tsx
- yaml
vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.0.10): vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2):
dependencies:
'@types/chai': 5.2.3
'@vitest/expect': 3.2.4
'@vitest/mocker': 3.2.4(vite@5.4.21(@types/node@25.0.10))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
'@vitest/spy': 3.2.4
'@vitest/utils': 3.2.4
chai: 5.3.3
debug: 4.4.3
expect-type: 1.3.0
magic-string: 0.30.21
pathe: 2.0.3
picomatch: 4.0.3
std-env: 3.10.0
tinybench: 2.9.0
tinyexec: 0.3.2
tinyglobby: 0.2.15
tinypool: 1.1.1
tinyrainbow: 2.0.0
vite: 5.4.21(@types/node@25.0.10)
vite-node: 3.2.4(@types/node@25.0.10)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/debug': 4.1.12
'@types/node': 25.0.10
transitivePeerDependencies:
- less
- lightningcss
- msw
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.1.0):
dependencies:
'@types/chai': 5.2.3
'@vitest/expect': 3.2.4
'@vitest/mocker': 3.2.4(vite@5.4.21(@types/node@25.1.0))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
'@vitest/spy': 3.2.4
'@vitest/utils': 3.2.4
chai: 5.3.3
debug: 4.4.3
expect-type: 1.3.0
magic-string: 0.30.21
pathe: 2.0.3
picomatch: 4.0.3
std-env: 3.10.0
tinybench: 2.9.0
tinyexec: 0.3.2
tinyglobby: 0.2.15
tinypool: 1.1.1
tinyrainbow: 2.0.0
vite: 5.4.21(@types/node@25.1.0)
vite-node: 3.2.4(@types/node@25.1.0)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/debug': 4.1.12
'@types/node': 25.1.0
transitivePeerDependencies:
- less
- lightningcss
- msw
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.0):
dependencies: dependencies:
'@types/chai': 5.2.3 '@types/chai': 5.2.3
'@vitest/expect': 3.2.4 '@vitest/expect': 3.2.4
@ -9311,12 +9125,13 @@ snapshots:
tinypool: 1.1.1 tinypool: 1.1.1
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
vite: 5.4.21(@types/node@25.2.0) vite: 5.4.21(@types/node@25.2.0)
vite-node: 3.2.4(@types/node@25.2.0) vite-node: 3.2.4(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2)
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@types/debug': 4.1.12 '@types/debug': 4.1.12
'@types/node': 25.2.0 '@types/node': 25.2.0
transitivePeerDependencies: transitivePeerDependencies:
- jiti
- less - less
- lightningcss - lightningcss
- msw - msw
@ -9326,6 +9141,8 @@ snapshots:
- sugarss - sugarss
- supports-color - supports-color
- terser - terser
- tsx
- yaml
vscode-languageserver-textdocument@1.0.12: {} vscode-languageserver-textdocument@1.0.12: {}
@ -9417,8 +9234,6 @@ snapshots:
yallist@3.1.1: {} yallist@3.1.1: {}
yallist@4.0.0: {}
yallist@5.0.0: {} yallist@5.0.0: {}
yaml@2.8.2: {} yaml@2.8.2: {}

View file

@ -66,7 +66,7 @@ export class SandboxAgent {
private constructor(options: SandboxAgentConnectOptions) { private constructor(options: SandboxAgentConnectOptions) {
this.baseUrl = options.baseUrl.replace(/\/$/, ""); this.baseUrl = options.baseUrl.replace(/\/$/, "");
this.token = options.token; this.token = options.token;
this.fetcher = options.fetch ?? globalThis.fetch; this.fetcher = options.fetch ?? globalThis.fetch.bind(globalThis);
this.defaultHeaders = options.headers; this.defaultHeaders = options.headers;
if (!this.fetcher) { if (!this.fetcher) {