npm package: @sandflare/sdk
Description: Sandflare SDK — Firecracker microVM sandboxes for AI agents
Latest version: 2.1.10

# Sandflare JavaScript / TypeScript SDK

The official JS/TS SDK for Sandflare — Firecracker microVM sandboxes for AI agents.

## Install

```bash
npm install @sandflare/sdk
```

## Quick start

```ts
import { Sandbox } from '@sandflare/sdk'

const sb = await Sandbox.create('agent', { size: 'nano' })
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

```ts
// Basic
const sb = await Sandbox.create('agent')

// With size
const sb = await Sandbox.create('agent', { size: 'nano' })    // 512 MB RAM
const sb = await Sandbox.create('agent', { size: 'small' })   // 1 GB RAM
const sb = await Sandbox.create('agent', { size: 'medium' })  // 2 GB RAM

// With options
const sb = await Sandbox.create('agent', {
  size: 'small',
  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>' })
```

`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` |
| `tier` | `string?` | `nano`, `small`, `medium`, `large`, `xl` |
| `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 |
| `vcpus` | `number` | vCPU count |

### Get & List

```ts
// 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

```ts
// 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

```ts
// 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

```ts
// 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

```ts
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

```ts
// 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

```ts
await sb.pause()
// ... later ...
const sb2 = await Sandbox.get(sb.id)  // auto-resumes, file state preserved
```

---

## AI Memory (mem0-backed)

Sandbox-scoped and user-scoped AI memory — semantically searchable across sessions.

```ts
// 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

```ts
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

```ts
await sb.enableAutoSnapshot(15)   // every 15 minutes
await sb.disableAutoSnapshot()
```

---

## Metrics & processes

```ts
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

```ts
const result = await sb.gitClone('https://github.com/org/repo', {
  path: '/home/agent/repo',
  branch: 'main',
  depth: 1,
})
console.log(result.output)
```

---

## Terminal (WebSocket)

```ts
const ws = sb.openTerminal({ cols: 220, rows: 50 })
ws.addEventListener('message', e => process.stdout.write(e.data))
```

---

## Local development

```bash
cd sdk/javascript
npm install
npm run build      # outputs to dist/
npm run typecheck  # tsc --noEmit
```

## Publish

```bash
npm run build && npm publish --access public
```