From 3576b7fcca2aeaa8b00c7f6574b0447817d8097f Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Tue, 3 Feb 2026 02:10:45 -0800 Subject: [PATCH] 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 --- docs/deploy/cloudflare.mdx | 229 +++++++++------ docs/docs.json | 2 +- examples/cloudflare/Dockerfile | 11 + examples/cloudflare/frontend/App.tsx | 272 +++++++++++++++++ examples/cloudflare/frontend/index.html | 12 + examples/cloudflare/frontend/main.tsx | 9 + examples/cloudflare/package.json | 14 +- examples/cloudflare/src/cloudflare.ts | 101 ++++--- examples/cloudflare/vite.config.ts | 11 + examples/cloudflare/wrangler.jsonc | 6 +- pnpm-lock.yaml | 375 ++++++------------------ sdks/typescript/src/client.ts | 2 +- 12 files changed, 619 insertions(+), 425 deletions(-) create mode 100644 examples/cloudflare/Dockerfile create mode 100644 examples/cloudflare/frontend/App.tsx create mode 100644 examples/cloudflare/frontend/index.html create mode 100644 examples/cloudflare/frontend/main.tsx create mode 100644 examples/cloudflare/vite.config.ts diff --git a/docs/deploy/cloudflare.mdx b/docs/deploy/cloudflare.mdx index 69f4f80..204870b 100644 --- a/docs/deploy/cloudflare.mdx +++ b/docs/deploy/cloudflare.mdx @@ -22,10 +22,69 @@ npm create cloudflare@latest -- my-sandbox --template=cloudflare/sandbox-sdk/exa 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 +``` + + +The `EXPOSE 8000` directive is required for `wrangler dev` to proxy requests to the container. Port 3000 is reserved for the Cloudflare control plane. + + +## 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 -import { getSandbox, proxyToSandbox, type Sandbox } from "@cloudflare/sandbox"; +import { getSandbox, type Sandbox } from "@cloudflare/sandbox"; export { Sandbox } from "@cloudflare/sandbox"; type Env = { @@ -34,62 +93,60 @@ type Env = { 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 { 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; } catch { return false; } } +/** Ensure sandbox-agent is running in the container */ +async function ensureRunning(sandbox: Sandbox, env: Env): Promise { + if (await isServerRunning(sandbox)) return; + + // Set environment variables for agents + const envVars: Record = {}; + 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 { async fetch(request: Request, env: Env): Promise { - // Proxy requests to exposed ports first - const proxyResponse = await proxyToSandbox(request, env); - if (proxyResponse) return proxyResponse; + const url = new URL(request.url); - const { hostname } = new URL(request.url); - const sandbox = getSandbox(env.Sandbox, "sandbox-agent"); + // Proxy requests: /sandbox/:name/v1/... + 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 - const alreadyRunning = await isServerRunning(sandbox); + await ensureRunning(sandbox, env); - if (!alreadyRunning) { - // Install sandbox-agent - await sandbox.exec( - "curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh" + // Proxy request to container + return sandbox.containerFetch( + new Request(`http://localhost${path}${url.search}`, request), + 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 = {}; - 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 - 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", - }); + return new Response("Not found", { status: 404 }); }, }; ``` @@ -99,11 +156,10 @@ export default { ```typescript import { SandboxAgent } from "sandbox-agent"; -// Get the endpoint from the Worker -const { endpoint } = await fetch("http://localhost:8787").then((r) => r.json()); - -// Connect to sandbox-agent -const client = await SandboxAgent.connect({ baseUrl: endpoint }); +// Connect via the proxy endpoint +const client = await SandboxAgent.connect({ + baseUrl: "http://localhost:8787/sandbox/my-sandbox", +}); // Wait for server to be ready for (let i = 0; i < 30; i++) { @@ -116,42 +172,49 @@ for (let i = 0; i < 30; i++) { } // Create a session and start coding -await client.createSession("my-session", { - agent: "claude", - permissionMode: "default", -}); +await client.createSession("my-session", { agent: "claude" }); await client.postMessage("my-session", { message: "Summarize this repository", }); 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 - -Update `wrangler.jsonc` to add environment variables: - -```jsonc -{ - "vars": { - "ANTHROPIC_API_KEY": "your-api-key" + // Handle text output + if (event.type === "item.delta" && event.data?.delta) { + process.stdout.write(event.data.delta); } } ``` -Or use `.dev.vars` for local development: +## Environment Variables + +Use `.dev.vars` for local development: ```bash echo "ANTHROPIC_API_KEY=your-api-key" > .dev.vars ``` + +Use plain `KEY=value` format in `.dev.vars`. Do not use `export KEY=value` - wrangler won't parse the bash syntax. + + The `.dev.vars` file is automatically gitignored and only used during local development with `npm run dev`. +For production, set secrets via wrangler: + +```bash +wrangler secret put ANTHROPIC_API_KEY +``` + ## Local Development 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: ```bash -curl http://localhost:8787 +curl http://localhost:8787/sandbox/demo/v1/health ``` + +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 +``` + + ## 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. - -## 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 +```bash +wrangler deploy ``` -Then update `wrangler.jsonc`: - -```jsonc -{ - "containers": { - "sandbox": { - "image": "./Dockerfile" - } - } -} -``` +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. diff --git a/docs/docs.json b/docs/docs.json index 7f09168..43eb67b 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -49,9 +49,9 @@ "deploy/index", "deploy/local", "deploy/e2b", + "deploy/daytona", "deploy/vercel", "deploy/cloudflare", - "deploy/daytona", "deploy/docker" ] }, diff --git a/examples/cloudflare/Dockerfile b/examples/cloudflare/Dockerfile new file mode 100644 index 0000000..88b96f2 --- /dev/null +++ b/examples/cloudflare/Dockerfile @@ -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 diff --git a/examples/cloudflare/frontend/App.tsx b/examples/cloudflare/frontend/App.tsx new file mode 100644 index 0000000..2f7f0c5 --- /dev/null +++ b/examples/cloudflare/frontend/App.tsx @@ -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(null); + + const clientRef = useRef(null); + const sessionIdRef = useRef(`session-${Date.now()}`); + const abortRef = useRef(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 ( +
+

Sandbox Agent

+ + {status === "idle" && ( +
+ + +
+ )} + + {status === "connecting" &&
Connecting to sandbox...
} + + {error &&
{error}
} + + {(status === "ready" || status === "thinking") && ( + <> +
{output}
+
+ setPrompt(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && send()} + placeholder="Enter prompt..." + disabled={status === "thinking"} + /> + +
+ + )} +
+ ); +} + +const styles: Record = { + 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", + }, +}; diff --git a/examples/cloudflare/frontend/index.html b/examples/cloudflare/frontend/index.html new file mode 100644 index 0000000..2378265 --- /dev/null +++ b/examples/cloudflare/frontend/index.html @@ -0,0 +1,12 @@ + + + + + + Sandbox Agent + + +
+ + + diff --git a/examples/cloudflare/frontend/main.tsx b/examples/cloudflare/frontend/main.tsx new file mode 100644 index 0000000..f258e49 --- /dev/null +++ b/examples/cloudflare/frontend/main.tsx @@ -0,0 +1,9 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { App } from "./App"; + +createRoot(document.getElementById("root")!).render( + + + +); diff --git a/examples/cloudflare/package.json b/examples/cloudflare/package.json index 14a1fab..e052605 100644 --- a/examples/cloudflare/package.json +++ b/examples/cloudflare/package.json @@ -3,17 +3,25 @@ "private": true, "type": "module", "scripts": { - "dev": "wrangler dev", - "deploy": "wrangler deploy", + "dev": "vite build --watch & wrangler dev", + "build": "vite build", + "deploy": "vite build && wrangler deploy", "typecheck": "tsc --noEmit" }, "dependencies": { - "@cloudflare/sandbox": "latest" + "@cloudflare/sandbox": "latest", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "sandbox-agent": "workspace:*" }, "devDependencies": { "@cloudflare/workers-types": "latest", "@types/node": "latest", + "@types/react": "^19.1.0", + "@types/react-dom": "^19.1.0", + "@vitejs/plugin-react": "^4.5.0", "typescript": "latest", + "vite": "^6.2.0", "vitest": "^3.0.0", "wrangler": "latest" } diff --git a/examples/cloudflare/src/cloudflare.ts b/examples/cloudflare/src/cloudflare.ts index 1cc0892..3866727 100644 --- a/examples/cloudflare/src/cloudflare.ts +++ b/examples/cloudflare/src/cloudflare.ts @@ -1,69 +1,76 @@ -import { getSandbox, proxyToSandbox, type Sandbox } from "@cloudflare/sandbox"; +import { getSandbox, type Sandbox } from "@cloudflare/sandbox"; + export { Sandbox } from "@cloudflare/sandbox"; type Env = { - Sandbox: DurableObjectNamespace; - ANTHROPIC_API_KEY?: string; - OPENAI_API_KEY?: string; + Bindings: { + Sandbox: DurableObjectNamespace; + 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 */ async function isServerRunning(sandbox: Sandbox): Promise { 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; } catch { return false; } } +/** Ensure sandbox-agent is running in the container */ +async function ensureRunning(sandbox: Sandbox, env: Env["Bindings"]): Promise { + if (await isServerRunning(sandbox)) return; + + // Set environment variables for agents + const envVars: Record = {}; + 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 { - async fetch(request: Request, env: Env): Promise { - // Proxy requests to exposed ports first - const proxyResponse = await proxyToSandbox(request, env); - if (proxyResponse) return proxyResponse; + async fetch(request: Request, env: Env["Bindings"]): Promise { + const url = new URL(request.url); - const { hostname } = new URL(request.url); - const sandbox = getSandbox(env.Sandbox, "sandbox-agent"); + // Proxy requests to sandbox-agent: /sandbox/:name/v1/... + 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 alreadyRunning = await isServerRunning(sandbox); + const name = match[1]; + const path = match[2] || "/"; + const sandbox = getSandbox(env.Sandbox, name); - if (!alreadyRunning) { - console.log("Installing sandbox-agent..."); - await sandbox.exec( - "curl -fsSL https://releases.rivet.dev/sandbox-agent/latest/install.sh | sh" + await ensureRunning(sandbox, env); + + // Proxy request to container + 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 = {}; - 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: alreadyRunning - ? "sandbox-agent server was already running" - : "sandbox-agent server started", - }); + // Serve frontend assets + return env.ASSETS.fetch(request); }, -}; +} satisfies ExportedHandler; diff --git a/examples/cloudflare/vite.config.ts b/examples/cloudflare/vite.config.ts new file mode 100644 index 0000000..657e846 --- /dev/null +++ b/examples/cloudflare/vite.config.ts @@ -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, + }, +}); diff --git a/examples/cloudflare/wrangler.jsonc b/examples/cloudflare/wrangler.jsonc index f096e6d..a1401c4 100644 --- a/examples/cloudflare/wrangler.jsonc +++ b/examples/cloudflare/wrangler.jsonc @@ -4,10 +4,14 @@ "main": "src/cloudflare.ts", "compatibility_date": "2025-01-01", "compatibility_flags": ["nodejs_compat"], + "assets": { + "directory": "./dist", + "binding": "ASSETS" + }, "containers": [ { "class_name": "Sandbox", - "image": "docker.io/cloudflare/sandbox:0.7.0", + "image": "./Dockerfile", "instance_type": "lite", "max_instances": 1 } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bfb0d9d..1fd0e12 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,13 +13,22 @@ importers: version: 2.7.6 vitest: 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: dependencies: '@cloudflare/sandbox': specifier: latest 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: '@cloudflare/workers-types': specifier: latest @@ -27,12 +36,24 @@ importers: '@types/node': specifier: latest 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: specifier: latest 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: 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: specifier: latest version: 4.61.1(@cloudflare/workers-types@4.20260131.0) @@ -41,14 +62,14 @@ importers: dependencies: '@daytonaio/sdk': specifier: latest - version: 0.135.0(ws@8.19.0) + version: 0.138.0(ws@8.19.0) '@sandbox-agent/example-shared': specifier: workspace:* version: link:../shared devDependencies: '@types/node': specifier: latest - version: 25.0.10 + version: 25.2.0 tsx: specifier: latest version: 4.21.0 @@ -70,7 +91,7 @@ importers: version: 4.0.1 '@types/node': specifier: latest - version: 25.0.10 + version: 25.2.0 tsx: specifier: latest version: 4.21.0 @@ -79,7 +100,7 @@ importers: version: 5.9.3 vitest: 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: dependencies: @@ -95,7 +116,7 @@ importers: devDependencies: '@types/node': specifier: latest - version: 25.0.10 + version: 25.2.0 tsx: specifier: latest version: 4.21.0 @@ -104,7 +125,7 @@ importers: version: 5.9.3 vitest: 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: dependencies: @@ -114,7 +135,7 @@ importers: devDependencies: '@types/node': specifier: latest - version: 25.0.10 + version: 25.2.0 typescript: specifier: latest version: 5.9.3 @@ -133,7 +154,7 @@ importers: devDependencies: '@types/node': specifier: latest - version: 25.1.0 + version: 25.2.0 tsx: specifier: latest version: 4.21.0 @@ -142,7 +163,7 @@ importers: version: 5.9.3 vitest: 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: dependencies: @@ -216,10 +237,10 @@ importers: dependencies: '@anthropic-ai/claude-code': specifier: latest - version: 2.1.22 + version: 2.1.29 '@openai/codex': specifier: latest - version: 0.92.0 + version: 0.94.0 cheerio: specifier: ^1.0.0 version: 1.2.0 @@ -294,14 +315,14 @@ importers: dependencies: '@daytonaio/sdk': specifier: latest - version: 0.135.0(ws@8.19.0) + version: 0.138.0(ws@8.19.0) '@e2b/code-interpreter': specifier: latest version: 2.3.3 devDependencies: '@types/node': specifier: latest - version: 25.0.10 + version: 25.2.0 tsx: specifier: latest version: 4.21.0 @@ -326,7 +347,7 @@ importers: devDependencies: vitest: 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: {} @@ -352,7 +373,7 @@ importers: version: 5.9.3 vitest: 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: @@ -360,8 +381,8 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@anthropic-ai/claude-code@2.1.22': - resolution: {integrity: sha512-WMwUUC/Ux87LqBDBC4KI/uYE0L/jcro3XcBzyd4a/YCkGVRyruyhypeeyHqAW7bUxm72xxWaPoy0keBkxpgIpQ==} + '@anthropic-ai/claude-code@2.1.29': + resolution: {integrity: sha512-vMHTOXrYdnreGtKUsWdd3Bwx5fKprTyNG7shrvbx3L2/jU9jexkOJrEKmN5loeR5jrE54LSB38QpaIj8pVM6eQ==} engines: {node: '>=18.0.0'} hasBin: true @@ -754,14 +775,14 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@daytonaio/api-client@0.135.0': - resolution: {integrity: sha512-7/gY3FimUXtgQvyUzEnxlr6ztET7G7bG0whdc4HRmeoWIZhNX+Fr2L2IpOH55UvFkb0CY7p60Ecubx2IltlMJA==} + '@daytonaio/api-client@0.138.0': + resolution: {integrity: sha512-mKO3Aqk2aCnOw4ej+UxvKE+Z1ixmo9OKTAFElkvRb6UOwb5zioudqTyqEfijkA2tXUXO8yPGhQDPaICLgpPopA==} - '@daytonaio/sdk@0.135.0': - resolution: {integrity: sha512-bJBBpLvFAfpcGlI2rg5xe2lKf0P5RWmt2OSL73mRyWJBQ1m2NRPijnmnE//sf62YoOOZpwL2Ykq3DkT8fczrew==} + '@daytonaio/sdk@0.138.0': + resolution: {integrity: sha512-cnbsflZYJ1NA4pQ2uX2lLN4w4ZQsO/xqdGDnpmwSu/LIW5F+O5gA8z4mfuWdIRcFFT4UhIpTzMuh3zRwxH7dIw==} - '@daytonaio/toolbox-api-client@0.135.0': - resolution: {integrity: sha512-XkaFm3nKF9PlOJi/qZS8pXpKjDn00X/lFvtpwUwCzIZSe0sC68uJZfp7/+DNgIVU1927kBOKgiyb6OMp4MCLDw==} + '@daytonaio/toolbox-api-client@0.138.0': + resolution: {integrity: sha512-unM9e7MOQiyDXdY8hCW1uTctYbxpo/TGZ6L71ZXyS/j2Cnz9/ud4VWBLcQP2VzlC+lrBP2YMrhT90zSSvcNfmA==} '@e2b/code-interpreter@2.3.3': resolution: {integrity: sha512-WOpSwc1WpvxyOijf6WMbR76BUuvd2O9ddXgCHHi65lkuy6YgQGq7oyd8PNsT331O9Tqbccjy6uF4xanSdLX1UA==} @@ -1667,8 +1688,8 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@openai/codex@0.92.0': - resolution: {integrity: sha512-DR9A2QlJDtEpMwqUGMIztTCzzCYTVrM7rqG3XuMVURnQ4b7XrScmY5RnSUuUZ/ga7wDTqw0BTmVzPurm4NX3Tw==} + '@openai/codex@0.94.0': + resolution: {integrity: sha512-GKOU2ty3NXls2aeiFSCnSSB6zQBtENqC5OnPa8s79Z576YP1r2DIfUrhQZzVDKmFei852E1SG4TNljFL/081gg==} engines: {node: '>=16'} hasBin: true @@ -2153,12 +2174,6 @@ packages: '@types/node@24.10.9': 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': resolution: {integrity: sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==} @@ -2467,10 +2482,6 @@ packages: chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - chownr@3.0.0: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} @@ -2868,10 +2879,6 @@ packages: fs-constants@1.0.0: 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: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -3332,22 +3339,10 @@ packages: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 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: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - minizlib@3.1.0: resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} engines: {node: '>= 18'} @@ -3355,11 +3350,6 @@ packages: mkdirp-classic@0.5.3: 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: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} @@ -3923,11 +3913,6 @@ packages: tar-stream@3.1.7: 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: resolution: {integrity: sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==} engines: {node: '>=18'} @@ -4447,9 +4432,6 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - yallist@5.0.0: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} @@ -4509,7 +4491,7 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@anthropic-ai/claude-code@2.1.22': + '@anthropic-ai/claude-code@2.1.29': optionalDependencies: '@img/sharp-darwin-arm64': 0.33.5 '@img/sharp-darwin-x64': 0.33.5 @@ -5307,18 +5289,18 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@daytonaio/api-client@0.135.0': + '@daytonaio/api-client@0.138.0': dependencies: axios: 1.13.4 transitivePeerDependencies: - debug - '@daytonaio/sdk@0.135.0(ws@8.19.0)': + '@daytonaio/sdk@0.138.0(ws@8.19.0)': dependencies: '@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/toolbox-api-client': 0.135.0 + '@daytonaio/api-client': 0.138.0 + '@daytonaio/toolbox-api-client': 0.138.0 '@iarna/toml': 2.2.5 axios: 1.13.4 busboy: 1.6.0 @@ -5329,13 +5311,13 @@ snapshots: isomorphic-ws: 5.0.0(ws@8.19.0) pathe: 2.0.3 shell-quote: 1.8.3 - tar: 6.2.1 + tar: 7.5.6 transitivePeerDependencies: - aws-crt - debug - ws - '@daytonaio/toolbox-api-client@0.135.0': + '@daytonaio/toolbox-api-client@0.138.0': dependencies: axios: 1.13.4 transitivePeerDependencies: @@ -5888,7 +5870,7 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 - '@openai/codex@0.92.0': {} + '@openai/codex@0.94.0': {} '@oslojs/encoding@1.1.0': {} @@ -6424,13 +6406,13 @@ snapshots: '@types/docker-modem@3.0.6': dependencies: - '@types/node': 24.10.9 + '@types/node': 25.2.0 '@types/ssh2': 1.15.5 '@types/dockerode@4.0.1': dependencies: '@types/docker-modem': 3.0.6 - '@types/node': 24.10.9 + '@types/node': 25.2.0 '@types/ssh2': 1.15.5 '@types/estree@1.0.8': {} @@ -6463,14 +6445,6 @@ snapshots: dependencies: 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': dependencies: undici-types: 7.16.0 @@ -6553,30 +6527,6 @@ snapshots: chai: 5.3.3 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))': dependencies: '@vitest/spy': 3.2.4 @@ -6936,8 +6886,6 @@ snapshots: chownr@1.1.4: {} - chownr@2.0.0: {} - chownr@3.0.0: {} ci-info@4.3.1: {} @@ -7400,10 +7348,6 @@ snapshots: fs-constants@1.0.0: {} - fs-minipass@2.1.0: - dependencies: - minipass: 3.3.6 - fsevents@2.3.3: optional: true @@ -8079,27 +8023,14 @@ snapshots: dependencies: brace-expansion: 2.0.2 - minipass@3.3.6: - dependencies: - yallist: 4.0.0 - - minipass@5.0.0: {} - minipass@7.1.2: {} - minizlib@2.1.2: - dependencies: - minipass: 3.3.6 - yallist: 4.0.0 - minizlib@3.1.0: dependencies: minipass: 7.1.2 mkdirp-classic@0.5.3: {} - mkdirp@1.0.4: {} - mlly@1.8.0: dependencies: acorn: 8.15.0 @@ -8343,7 +8274,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 24.10.9 + '@types/node': 25.2.0 long: 5.3.2 proxy-from-env@1.1.0: {} @@ -8779,15 +8710,6 @@ snapshots: - bare-abort-controller - 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: dependencies: '@isaacs/fs-minipass': 4.0.1 @@ -9044,15 +8966,16 @@ snapshots: '@types/unist': 3.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: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 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: - '@types/node' + - jiti - less - lightningcss - sass @@ -9061,52 +8984,19 @@ snapshots: - sugarss - supports-color - 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: 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.0.10) - 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) + vite: 6.4.1(@types/node@25.2.0)(jiti@1.21.7)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' + - jiti - less - lightningcss - sass @@ -9115,6 +9005,8 @@ snapshots: - sugarss - supports-color - terser + - tsx + - yaml vite@5.4.21(@types/node@22.19.7): dependencies: @@ -9125,24 +9017,6 @@ snapshots: '@types/node': 22.19.7 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): dependencies: esbuild: 0.21.5 @@ -9152,6 +9026,21 @@ snapshots: '@types/node': 25.2.0 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): dependencies: esbuild: 0.25.12 @@ -9171,11 +9060,11 @@ snapshots: optionalDependencies: 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: '@types/chai': 5.2.3 '@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/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -9194,12 +9083,13 @@ snapshots: tinypool: 1.1.1 tinyrainbow: 2.0.0 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 optionalDependencies: '@types/debug': 4.1.12 '@types/node': 22.19.7 transitivePeerDependencies: + - jiti - less - lightningcss - msw @@ -9209,86 +9099,10 @@ snapshots: - sugarss - supports-color - terser + - tsx + - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.0.10): - 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): + 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 @@ -9311,12 +9125,13 @@ snapshots: tinypool: 1.1.1 tinyrainbow: 2.0.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 optionalDependencies: '@types/debug': 4.1.12 '@types/node': 25.2.0 transitivePeerDependencies: + - jiti - less - lightningcss - msw @@ -9326,6 +9141,8 @@ snapshots: - sugarss - supports-color - terser + - tsx + - yaml vscode-languageserver-textdocument@1.0.12: {} @@ -9417,8 +9234,6 @@ snapshots: yallist@3.1.1: {} - yallist@4.0.0: {} - yallist@5.0.0: {} yaml@2.8.2: {} diff --git a/sdks/typescript/src/client.ts b/sdks/typescript/src/client.ts index 912a14b..7f9ad95 100644 --- a/sdks/typescript/src/client.ts +++ b/sdks/typescript/src/client.ts @@ -66,7 +66,7 @@ export class SandboxAgent { private constructor(options: SandboxAgentConnectOptions) { this.baseUrl = options.baseUrl.replace(/\/$/, ""); this.token = options.token; - this.fetcher = options.fetch ?? globalThis.fetch; + this.fetcher = options.fetch ?? globalThis.fetch.bind(globalThis); this.defaultHeaders = options.headers; if (!this.fetcher) {