@devicai/sandboxtsc
v0.1.3
Published
A unified TypeScript SDK for cloud sandbox providers (Vercel, Cloudflare, Daytona, E2B and self-hosted containers)
Downloads
417
Maintainers
Readme
@devicai/sandboxtsc
A unified TypeScript SDK for cloud sandbox providers. Write your code once against one ergonomic interface — running commands, moving files, stateful sessions, snapshots, preview URLs — and switch providers with a single line.
Unlike most abstractions, SandboxTsc treats snapshots, preview URLs, and stateful sessions as first-class, cross-provider primitives.
Supported providers: Vercel Sandbox, Cloudflare Sandbox, Daytona, E2B, devic-sandbox and any generic self-hosted container sandbox HTTP API.
Install
npm install @devicai/sandboxtsc
# provider peers — install only what you use:
npm install @vercel/sandbox # vercel
npm install @cloudflare/sandbox # cloudflare (Workers-only)
npm install @daytonaio/sdk ws # daytona (ws is required in Node but undeclared)
npm install e2b # e2b (Node >= 20.19 for the CJS build)
npm install ws # devic-sandbox/container, only on Node < 22The container and devic-sandbox providers have zero extra dependencies.
Quickstart
import { createSandboxClient } from '@devicai/sandboxtsc';
import { e2b } from '@devicai/sandboxtsc/e2b';
const client = createSandboxClient({
provider: e2b({ apiKey: process.env.E2B_API_KEY }),
});
const sandbox = await client.create({ runtime: 'node24', timeoutMs: 5 * 60_000 });
const { exitCode, stdout, stderr } = await sandbox.runCommand('node --version');
await sandbox.writeFile('app/index.js', 'console.log("hi")');
const snap = await sandbox.snapshot();
const url = await sandbox.getPreviewUrl(3000);
await sandbox.destroy();Swap the provider — the rest of the code does not change:
import { vercel } from '@devicai/sandboxtsc/vercel'; // needs @vercel/sandbox
import { cloudflare } from '@devicai/sandboxtsc/cloudflare'; // inside a Worker
import { daytona } from '@devicai/sandboxtsc/daytona'; // needs @daytonaio/sdk
import { e2b } from '@devicai/sandboxtsc/e2b'; // needs e2b
import { devicSandbox } from '@devicai/sandboxtsc/devic-sandbox';
import { container } from '@devicai/sandboxtsc/container'; // generic self-hosted APIThe unified interface
client.create(opts?): Promise<Sandbox> // runtime, source, resources, timeoutMs,
// ports, env, exposedPort, providerOptions
client.connect(sandboxId): Promise<Sandbox>
client.list(opts?): Promise<Page<SandboxInfo>>
client.snapshots.list / .restore(id, opts?) / .delete(id)
sandbox.runCommand(cmd, opts?) // → { exitCode, stdout, stderr, cwd? } — eager strings
sandbox.createSession(opts?) // persistent cwd (and env where native) across run()s
session.run(cmd) / session.runStream(cmd)
sandbox.writeFile / readFile / readText / mkdir / listFiles
sandbox.uploadFile / downloadFile
sandbox.snapshot(opts?) / stop() / destroy() / extendTimeout(additionalMs)
sandbox.getInfo() / getPreviewUrl(port) / exposePort(port)
sandbox.capabilities // introspect before relying on a capabilityRules that hold on every provider:
- All durations are milliseconds.
- Command output is eager (real strings), never lazy thunks.
- Non-zero exit codes do not throw; inspect
result.exitCodeor pass{ throwOnNonZero: true }. - Sessions thread
cwdacross runs everywhere (native shell or emulated marker protocol). - Every capability is declared
native|emulated(equivalent behavior via shell) |unsupported(throwsUnsupportedCapabilityErrorwith an actionable message — never silent under-delivery).
Snapshots
const snap = await sandbox.snapshot({ name: 'after-install' });
const clone = await client.snapshots.restore(snap.id, { timeoutMs: 10 * 60_000 });
await client.snapshots.delete(snap.id);Scope is filesystem (whole FS — /usr/local/bin installs survive) on vercel,
container, devic-sandbox, daytona and e2b; workdir (one-directory backup) on
cloudflare. Requesting a scope the provider cannot deliver throws instead of
capturing less.
Preview URLs
const sandbox = await client.create({ ports: [3000], exposedPort: 3000 });
const url = await sandbox.getPreviewUrl(3000); // all six providers
if (sandbox.capabilities.exposePortDynamic !== 'unsupported') {
const live = await sandbox.exposePort(8080); // cloudflare, daytona, e2b
}Documentation
Full capability matrix, per-provider gotchas and the agent-ready markdown reference live in the repository: github.com/devicai/sandbox-tsc.
License
Apache-2.0
