0.1.8 • Published yesterday
@brimble/sandbox
Licence
MIT
Version
0.1.8
Deps
0
Size
115 kB
Vulns
0
Weekly
0
@brimble/sandbox
TypeScript SDK for the Brimble Sandbox API.
Install
npm install @brimble/sandbox
Testing
# Unit tests (mocked transport, no API key required)
npm run test
# Live integration test (creates a sandbox, runs a command, then destroys it)
BRIMBLE_SANDBOX_KEY=your_key_here npm run test:integration
# Run both
BRIMBLE_SANDBOX_KEY=your_key_here npm run test:all
Quickstart
import { CodeLanguage, Sandbox } from '@brimble/sandbox';
const client = new Sandbox();
const sandbox = await client.sandboxes.create({
template: 'node-22',
persistent: true,
persistentDiskGB: 20,
mountPath: '/workspace',
});
await sandbox.exec({ cmd: 'node -v', env: { NODE_ENV: 'production' } });
await sandbox.putFile('tmp/notes.txt', Buffer.from('hello sandbox'));
await sandbox.putFiles([
{ path: '/tmp/hello.txt', body: 'hello from batch' },
{ path: '/tmp/config.json', body: JSON.stringify({ mode: 'dev' }) },
]);
const stream = await sandbox.getFile('tmp/notes.txt');
await sandbox.runCode({
language: CodeLanguage.Node,
code: 'console.log(1 + 1)',
env: { FEATURE_FLAG: 'on' },
});
// Handle-first lifecycle on existing sandboxes.
const existingSandbox = await client.sandboxes.get(sandbox.id);
await existingSandbox.destroy();
Set BRIMBLE_SANDBOX_KEY in your environment.
If needed, pass apiKey explicitly in the constructor; explicit value wins over env.
Ergonomic helpers
// 1) Create returns a ready sandbox (~2-3s blocking POST)
const created = await client.sandboxes.create({ template: 'node-22' });
// createReady() is an alias of create() (deprecated)
const alsoReady = await client.sandboxes.createReady({ template: 'node-22' });
// 2) Get + wait in one call (for resume/reconnect)
const loaded = await client.sandboxes.getReady(created.id);
// 3) Create a volume + attach in one call (region resolved for volume only)
const withVolume = await client.sandboxes.withVolume({
sandbox: { template: 'node-22', mountPath: '/var/www/html' },
volume: { name: 'workspace-disk', sizeGB: 20 },
});
// 4) Auto-wait at runtime call sites
await withVolume.exec({ cmd: 'npm -v' }, { waitUntilReady: true });
// Streaming command output (Vercel/Railway-style async iteration)
const output = await withVolume.exec({ cmd: 'for i in 1 2 3; do echo $i; done', stream: true });
for await (const log of output) {
if (log.stream === 'stdout') process.stdout.write(log.data);
}
const streamedResult = await output.result();
// Or stream with callbacks and still get the final ExecResult
const buffered = await withVolume.exec({
cmd: 'npm install',
onStdout: (chunk) => process.stdout.write(chunk),
});
// File downloads
const file = await withVolume.getFile('tmp/notes.txt');
for await (const chunk of file) {
process.stdout.write(chunk);
}
// 5) List templates/regions
const templates = await client.sandboxes.listTemplates();
const nodeTemplate = await client.sandboxes.getTemplate('node-22');
const { regions } = await client.sandboxes.listRegions();
// 6) Async iterators for pagination
for await (const sandbox of client.sandboxes.iterate({ teamId: '<team>' })) {
console.log(sandbox.id, sandbox.status);
}
Handle composition
const sandbox = await client.sandboxes.get('<sandbox-id>');
await sandbox.snapshots.create({ name: 'before-migration' });
Volume attachment is create-time only.
Use client.sandboxes.create({ ..., volumeId }) or client.sandboxes.withVolume(...).
Network egress
Control outbound network access when creating or updating a sandbox.
import { Sandbox, SandboxEgressMode } from '@brimble/sandbox';
const client = new Sandbox();
// Create with restricted egress (allowlist)
const sandbox = await client.sandboxes.createReady({
template: 'node-22',
egress: {
mode: SandboxEgressMode.Restricted,
allow: ['1.1.1.1', 'api.example.com'],
},
});
// Update egress at runtime
const updated = await sandbox.updateEgress({
mode: SandboxEgressMode.DenyAll,
});
console.log(updated.network_updated); // true when Nomad network mode changed
// Legacy shorthand (maps to deny_all)
await client.sandboxes.create({ template: 'node-22', blockOutbound: true });
Modes: open (full internet), restricted (allowlist required), deny_all (no outbound).
Retry, timeouts, and idempotency
const client = new Sandbox({
timeoutMs: 30_000,
retry: {
maxAttempts: 3,
baseDelayMs: 250,
maxDelayMs: 2_000,
},
});
await client.sandboxes.create(
{ template: 'node-22' },
{ idempotencyKey: 'create-sandbox-123' },
);
If region is omitted, the SDK resolves the first available sandbox region automatically.
Resources
client.sandboxescreate,createReady,withVolume,list,iterate,get,getReady,listRegions,listTemplates,getTemplate,destroy,pause,resume,updateEgress,quickstartNode,quickstartPython,use
sandboxhandle (returned fromcreate/get/list)waitUntilReady,refresh,destroy,pause,resume,updateEgress,exec,runCode,putFile,putFiles,getFile,stats,createSnapshot,listSnapshots,snapshots.create,snapshots.list
client.sandboxes.use(id)destroy,exec,runCode,putFile,putFiles,getFile,stats,createSnapshot,listSnapshots
client.snapshotslistAll,iterateAll,delete
client.volumeslist,iterate,create,get,delete
Errors
HTTP errors throw typed errors:
AuthErrorValidationErrorNotFoundErrorRateLimitErrorSandboxApiError
All include:
statusmessageendpointresponseBodyrequestId