@sandflare/sdk
v2.1.20
Published
Sandflare SDK — Firecracker microVM sandboxes for AI agents
Maintainers
Readme
Sandflare JavaScript / TypeScript SDK
The official JS/TS SDK for Sandflare — Firecracker microVM sandboxes for AI agents.
Install
npm install @sandflare/sdkQuick start
import { Sandbox } from '@sandflare/sdk'
const sb = await Sandbox.create('agent', { templateId: 'codebox' })
const result = await sb.exec('echo hello')
console.log(result.stdout) // "hello\n"
await sb.delete()Environment variables
| Variable | Description |
|---|---|
| SANDFLARE_API_KEY | API key (required) |
| SANDFLARE_API_URL | Optional base URL override (default: https://api.sandflare.io) |
| PANDAAGENT_API_KEY | Legacy alias for SANDFLARE_API_KEY |
| PANDAAGENT_BASE_URL | Legacy alias for SANDFLARE_API_URL |
All requests use X-API-Key header for authentication.
Sandbox
Create
// Basic (base Ubuntu, 512 MB / 1 vCPU)
const sb = await Sandbox.create('agent')
// Pick an environment template
const sb = await Sandbox.create('agent', { templateId: 'codebox' }) // Python data stack
const sb = await Sandbox.create('agent', { templateId: 'ai-agent' }) // LangChain / CrewAI
const sb = await Sandbox.create('agent', { templateId: 'browser-agent' }) // Playwright + Chromium
const sb = await Sandbox.create('agent', { templateId: 'nextjs' }) // Next.js 15
const sb = await Sandbox.create('agent', { templateId: 'vite-react' }) // Vite + React 19
// With options
const sb = await Sandbox.create('agent', {
templateId: 'codebox',
ttlHours: 2,
ephemeral: true,
metadata: { env: 'prod', version: '1.0' },
label: 'production-run',
})
// From a snapshot
const sb = await Sandbox.create('restored', { snapshotId: '<snapshot-uuid>' })Templates and resources:
| templateId | Environment | RAM | vCPU | Rate/s |
|---|---|---|---|---|
| "" (default) | Base Ubuntu | 512 MB | 1 | ~$0.000014 |
| codebox | pandas, numpy, matplotlib, scipy, scikit-learn | 4 GB | 2 | ~$0.000040 |
| ai-agent | LangChain, CrewAI, LlamaIndex | 4 GB | 2 | ~$0.000040 |
| browser-agent | Playwright + Chromium | 2 GB | 2 | ~$0.000032 |
| nextjs | Node 20 + Next.js 15 | 2 GB | 2 | ~$0.000032 |
| vite-react | Node 20 + Vite + React 19 | 2 GB | 2 | ~$0.000032 |
sb.info is a SandboxInfo object with these fields:
| Field | Type | Description |
|---|---|---|
| name | string | Unique sandbox ID (e.g. sb-abc123) |
| status | string | ready, paused, stopped |
| label | string? | Human-readable label |
| previewUrl | string? | https://3000-sb-abc123.sandflare.io |
| subdomainUrl | string? | https://sb-abc123.sandflare.io (port-less) |
| agentUrl | string | Internal agent URL |
| metadata | Record<string,unknown>? | Custom key-value metadata |
| ephemeral | boolean? | True if auto-deleted on close |
| expiresAt | string? | ISO timestamp if TTL set |
| memoryMb | number | RAM in MB (e.g. 512, 1024, 2048, 4096) |
| vcpus | number | vCPU count |
Get & List
// Get by name
const sb = await Sandbox.get('sb-abc123')
// List all
const sandboxes = await Sandbox.list()
// Filter by label
const sandboxes = await Sandbox.list({ label: 'production-run' })Exec
// Run a shell command
const result = await sb.exec('echo hello')
console.log(result.stdout) // "hello\n"
console.log(result.exitCode) // 0
console.log(result.ok) // true
// With env vars
const result = await sb.exec('echo $MY_VAR', { env: { MY_VAR: 'hello_world' } })
// With cwd and timeout
const result = await sb.exec('ls', { cwd: '/tmp', timeout: 10 })
// Stream output in real-time
for await (const chunk of sb.execStream('python3 train.py', { timeout: 120 })) {
if (chunk.event === 'stdout') process.stdout.write(String((chunk.data as any).line ?? ''))
if (chunk.event === 'done') console.log('done')
}
// Typed streaming chunks (type: 'stdout' | 'stderr' | 'done')
for await (const chunk of sb.execStreamIter('python3 train.py')) {
if (chunk.type === 'stdout') process.stdout.write(chunk.data)
if (chunk.type === 'done') console.log(`\nexited ${chunk.exitCode}`)
}File I/O
// Write and read
await sb.writeFile('/tmp/hello.txt', 'hello world')
const content = await sb.readFile('/tmp/hello.txt') // string
// Binary upload
await sb.uploadFile('/tmp/data.bin', Buffer.from([0x00, 0x01, 0x02]))
// Binary download
const bytes = await sb.downloadFile('/tmp/data.bin') // Buffer
// List directory
const entries = await sb.ls('/tmp')
for (const e of entries) {
console.log(e.name, e.size, e.isDir)
}Run code
// Python
const result = await sb.runPython('print(2 + 2)')
console.log(result.stdout) // "4\n"
// Node.js
const result = await sb.runNode('console.log(2 + 2)')
console.log(result.stdout) // "4\n"
// Stateful kernel (variables persist across calls)
await sb.runCode('x = 42')
const r = await sb.runCode('x * 2')
console.log(r.text) // "84"Lifecycle
await sb.pause() // snapshot memory to disk, free compute
await sb.resume() // restore from snapshot
await sb.delete() // destroy permanently
await sb.close() // alias for delete()Snapshots
// Create snapshot (sandbox continues running)
const snap = await sb.snapshot({
name: 'after-data-load',
description: 'Dataset v2 loaded',
tags: ['prod', 'v2'],
})
console.log(snap.snapshotId) // UUID
// List snapshots for this sandbox
const snaps = await sb.listSnapshots()
for (const s of snaps) {
console.log(s.snapshotId, s.name, s.status)
}
// Restore (creates new sandbox from snapshot)
const restored = await Sandbox.create('fork', { snapshotId: snap.snapshotId })Pause → auto-resume
await sb.pause()
// ... later ...
const sb2 = await Sandbox.get(sb.id) // auto-resumes, file state preservedAI Memory (mem0-backed)
Sandbox-scoped and user-scoped AI memory — semantically searchable across sessions.
// Sandbox-scoped memory (via sb.aiMemory)
await sb.aiMemory.add('User prefers async/await and strict TypeScript')
await sb.aiMemory.add('Bug: always flush stream before closing')
const results = await sb.aiMemory.search('TypeScript coding preferences')
for (const r of results) console.log(r.memory)
const all = await sb.aiMemory.list()
// User-level memory (persists across ALL sandboxes)
const userMem = Sandbox.userMemory() // reads SANDFLARE_API_KEY from env
await userMem.add('User speaks English and prefers concise answers')
const results = await userMem.search('communication preferences')
const all = await userMem.list()Custom templates
import { Template } from '@sandflare/sdk'
// Build from Dockerfile
const job = await Template.build('my-env',
'FROM ubuntu:22.04\nRUN apt-get update && apt-get install -y python3'
)
// Poll until ready
const ready = await Template.waitForBuild(job.id, { timeoutMs: 600_000 })
// Use the template
const sb = await Sandbox.create('agent', { templateId: ready.id })
// Manage templates
const jobs = await Template.list()
const status = await Template.getBuildStatus('tmpl-abc123')
await Template.delete('tmpl-abc123')Auto-snapshots
await sb.enableAutoSnapshot(15) // every 15 minutes
await sb.disableAutoSnapshot()Metrics & processes
const m = await sb.metrics()
console.log(`CPU: ${m.cpuUsedPct.toFixed(1)}% RAM: ${m.memUsed}/${m.memTotal}`)
const procs = await sb.getProcesses()
for (const p of procs) console.log(p.pid, p.command)
await sb.killProcess(1234)Git clone
const result = await sb.gitClone('https://github.com/org/repo', {
path: '/home/agent/repo',
branch: 'main',
depth: 1,
})
console.log(result.output)Terminal (WebSocket)
const ws = sb.openTerminal({ cols: 220, rows: 50 })
ws.addEventListener('message', e => process.stdout.write(e.data))Local development
cd sdk/javascript
npm install
npm run build # outputs to dist/
npm run typecheck # tsc --noEmitPublish
npm run build && npm publish --access public