Ascii Box
Run Ascii Box cloud VMs through the normalized SDK API.
Ascii Box provides persistent Linux VMs with files, bounded commands, HTTPS previews, desktop access, and stop/resume snapshots.
Installation
bun add @opencoredev/sandbox-sdk ai zod @asciidev/box-sdkAuthentication
Create an API key in the Box dashboard, then set BOX_API_KEY on the server that creates sandboxes.
BOX_API_KEY=box_your_keyPass apiKey to box() only when environment-based configuration is not available.
Run a command
import { createSandbox } from "@opencoredev/sandbox-sdk";
import { box } from "@opencoredev/sandbox-sdk/box";
await using sandbox = await createSandbox({ provider: box() });
await sandbox.files.write("hello.txt", "hello from Box");
console.log((await sandbox.run("cat hello.txt")).stdout);The Box command API separates stdout and stderr. Commands default to 30 seconds and support at most 60 seconds. The API does not expose a streaming or background-process handle, so sandbox.processes.start() returns an unsupported error.
Run an AI SDK agent
This provider works with AI SDK ToolLoopAgent through the normalized sandbox session. Pass the language model from your existing AI SDK provider or AI Gateway setup.
import { ToolLoopAgent, type LanguageModel } from "ai";import { createSandbox } from "@opencoredev/sandbox-sdk";import { createSandboxToolApproval, createSandboxTools, toAISandboxSession,} from "@opencoredev/sandbox-sdk/ai";import { box } from "@opencoredev/sandbox-sdk/box";export async function runSandboxAgent(model: LanguageModel) { await using sandbox = await createSandbox({ provider: box({ ttlSeconds: 900 }), }); const aiSandbox = toAISandboxSession(sandbox); const agent = new ToolLoopAgent({ model, instructions: `Work only in the provided sandbox.\n\n${aiSandbox.description}`, tools: createSandboxTools(), toolApproval: createSandboxToolApproval(), }); return await agent.generate({ prompt: "Inspect the repository, run its tests, and summarize the result.", experimental_sandbox: aiSandbox, });}See the AI SDK guide for approval flows, direct session access, and HarnessAgent alternatives.
Expose a preview
Start the server in the Box, bind it to 0.0.0.0, then expose its port.
const preview = await sandbox.ports.expose(3000);
const response = await preview.request!("/health");The adapter requests a public Box URL by default because that mode passed end-to-end live validation. Pass box({ public: false }) to request Box's native token-gated mode; the adapter removes _token from preview.url and keeps it inside request().
Managed lifecycle
The managed provider surface maps stop() to Box archive/snapshot, resume() to native resume, and destroy() to deletion. Normal sandbox.stop() performs full cleanup: it archives a running Box when required, waits for that transition, then deletes it.
Use sandbox.raw.client for native prompts, events, desktop streaming, forks, and snapshot inspection. The current native Box model is available at sandbox.raw.box.
Options
| Option | Type | Default | Behavior |
|---|---|---|---|
apiKey | string | BOX_API_KEY | Authenticates Box API requests. |
baseUrl | string | BOX_BASE_URL or public v1 URL | Overrides the Box API endpoint. |
ttlSeconds | number | null | Box service default | Sets auto-archive; null disables it. |
noEnv | boolean | false | Withholds account-level secrets from the Box. |
name | string | Generated by Box | Applies a friendly name after provisioning. |
public | boolean | true | Returns ungated hosted URLs when true. |
readyTimeout | number | 600000 | Limits the provisioning wait in milliseconds. |
Per-sandbox values passed to createSandbox({ env }) are sent through Box's native env field. Box accepts at most 100 variables and 64 KB total.
Read next
See Ports, compare exact modes in Compatibility, or open the Box SDK guide.