Official TypeScript SDK for Isorun. Create an isolated Linux VM from any container image, run commands in it, read and write files, and tear it down — billed by the second.
npm install isorunRequires Node.js 22.19 or newer.
import { Isorun } from 'isorun'
const isorun = new Isorun() // reads ISORUN_API_KEY from env
const sandbox = await isorun.create({ image: 'node:22' })
const { stdout } = await sandbox.exec('node -v')
console.log(stdout) // v22.x.x
await sandbox.destroy()Get an API key at app.isorun.ai.
const sandbox = await isorun.create({ image: 'python:3.12' })
try {
await sandbox.writeFile('/tmp/code.py', sourceFromLLM)
const { stdout } = await sandbox.exec('python /tmp/code.py', 30)
return stdout
} finally {
await sandbox.destroy()
}const parent = await isorun.create({ image: 'node:22' })
await parent.writeFile('/app/worker.js', workerCode)
const workers = await parent.fork(10) // 10 independent clones
const results = await Promise.all(
workers.map((w) => w.exec('node /app/worker.js')),
)const base = await isorun.create({ image: 'python:3.12' })
await base.exec('pip install pandas numpy')
const { id } = await base.snapshot()
await base.destroy()
// Later, restore a fresh sandbox from the snapshot
const restored = await isorun.restore(id)
await restored.exec('python -c "import pandas; print(pandas.__version__)"')await sandbox.writeFile('/app/config.json', JSON.stringify({ env: 'prod' }))
const contents = await sandbox.readFile('/app/config.json')
const entries = await sandbox.readdir('/app')const url = sandbox.url(3000) // URL for the guest's port 3000
// Send requests with an `Authorization: Bearer ${apiKey}` header.Use a sandbox as the code-execution tool in an agent stack. Each helper returns ready-to-use tools plus a close() to tear the sandbox down. The relevant peer dependency is installed only for the entry point you use.
import { isorunTools } from 'isorun/openai-agents'
const { tools, close } = await isorunTools()
// add `tools` to your Agent, then call close() when the run is doneimport { isorunTools } from 'isorun/langchain'
const { tools, close } = await isorunTools()
// pass `tools` to a LangChain / LangGraph agent; close() when doneExpose the Isorun tools over stdio to any MCP client:
ISORUN_API_KEY=isorun_live_... npx isorun-mcpimport { Isorun } from 'isorun'
import { runOrchestrator } from 'isorun/claude-agents/orchestrator'
await runOrchestrator({
isorun: new Isorun(),
environmentId: 'env_...', // from the Anthropic Console
environmentKey: 'sk-ant-oat01-...', // self-hosted environment key
image: 'docker.io/isorun/claude-agents:0.4.3',
vcpus: 2,
memMiB: 4096,
})See the Claude Managed Agents guide for setup.
const isorun = new Isorun({ apiKey: 'isorun_live_...' })| Option | Type | Default | Description |
|---|---|---|---|
apiKey |
string |
ISORUN_API_KEY env |
Your API key. |
apiUrl |
string |
derived from key region | Override the runner endpoint. |
The runner URL is derived from the region encoded in the API key. ISORUN_API_URL also overrides it.
await isorun.create(options?) // → Sandbox
await isorun.get(id) // → Sandbox | null
await isorun.list() // → Sandbox[]
await isorun.restore(snapshotId) // → Sandbox
await isorun.listSnapshots() // → Snapshot[]
await isorun.deleteSnapshot(id) // → void
await isorun.networkProfiles() // → NetworkProfile[]
await isorun.usage() // → UsageSummary
await isorun.history() // → SandboxHistoryEntry[]
await isorun.connect() // optionally pre-open connections before issuing requests// Execute
await sandbox.exec(command, timeoutSec?) // → { exitCode, stdout, stderr }
// Files
await sandbox.writeFile(path, content) // string | Uint8Array
await sandbox.readFile(path) // → string
await sandbox.readdir(path) // → FileEntry[]
// URL builder for guest services
sandbox.url(port, path?) // → string
// Lifecycle
await sandbox.info() // → SandboxInfo
await sandbox.snapshot() // → Snapshot
await sandbox.fork(count?) // → Sandbox[]
await sandbox.hibernate() // pause + snapshot to disk
await sandbox.resume() // resume a hibernated sandbox
await sandbox.setTimeout(seconds) // keep-alive; 0 disables auto-destroy
await sandbox.auditLog() // → AuditEntry[]
await sandbox.destroy() // → DestroyResultAll methods are fully typed; see the bundled .d.ts or docs.isorun.ai.
| Field | Type | Default | Notes |
|---|---|---|---|
image |
string |
node:22 |
Container image. |
vcpus |
number |
1 |
Virtual CPUs. |
memMiB |
number |
1024 |
Memory in MiB. |
diskMiB |
number |
4096 |
Scratch disk; wiped on destroy. |
timeoutSec |
number |
300 |
Auto-destroy timer; reset via setTimeout. |
network |
{ allow?: string[]; deny?: string[] } |
— | Egress allow/deny lists (CIDRs, hostnames, wildcards). |
networkProfile |
string |
— | Named egress profile (see networkProfiles()). |
credentials |
Record<string, string> |
— | Credentials injected into the sandbox. |
vcpus and memMiB together select a sandbox size.
| Variable | Purpose |
|---|---|
ISORUN_API_KEY |
API key, used when not passed to the constructor. |
ISORUN_API_URL |
Override the runner endpoint. |
Every method throws an IsorunError on a non-2xx response. It carries the HTTP status and a truncated body, so you can branch by code:
import { Isorun, IsorunError } from 'isorun'
try {
await sandbox.exec('...')
} catch (e) {
if (e instanceof IsorunError && e.status === 429) backoff()
else throw e
}Full guides and the complete API reference are at docs.isorun.ai.