zidane/ contexts/ e2b
A sandbox provider backed by E2B micro-VMs, hosted or self-hosted.
import { … } from 'zidane/contexts/e2b'src/contexts/e2b.ts
Use createE2BProvider for isolation off your machine. It returns a SandboxProvider that you wrap with createSandboxContext, needs the optional e2b package, and reads E2B_API_KEY when you pass no key. The other exports cover readiness checks, template and environment resolution, and the setup script that runs before the first turn.
import { createSandboxContext } from 'zidane/contexts/sandbox'
import { createE2BProvider } from 'zidane/contexts/e2b'
const context = createSandboxContext(createE2BProvider({ cwd: '/home/user/project', onDestroy: 'pause' }))
const handle = await context.spawn()
await context.exec(handle, 'bun --version')
await context.destroy(handle)Functions#
createE2BProviderFunction#
Build a SandboxProvider backed by E2B.
The provider owns a registry of live E2BSandbox instances keyed by
sandbox id, so the per-call exec/readFile/… hooks reuse the same warm
connection rather than reconnecting each time.
function createE2BProvider(options?: E2BProviderOptions): SandboxProviderParameters
| Name | Type | Default | Description |
|---|---|---|---|
| options? | E2BProviderOptions | {} | API key, domain, template, sandbox id, working directory, timeout, environment and readiness settings. |
Returns
Example
import { createE2BProvider } from 'zidane/contexts/e2b'
import { createSandboxContext } from 'zidane/contexts/sandbox'
const execution = createSandboxContext(
createE2BProvider({ apiKey: process.env.E2B_API_KEY, timeoutSeconds: 600 }),
)resolveE2BEnvFunction#
Merge the factory-level E2BProviderOptions.env with the per-spawn
SpawnConfig.env, per-spawn winning on key conflicts. Returns undefined
when the merge is empty so callers can skip setting envs on the create
options entirely (rather than handing the SDK an empty object).
function resolveE2BEnv(
optionEnv: Record<string, string> | undefined,
perSpawnEnv: Record<string, string> | undefined,
): Record<string, string> | undefinedParameters
| Name | Type | Description |
|---|---|---|
| optionEnv | Record<string, string> | undefined | Environment from the provider options. |
| perSpawnEnv | Record<string, string> | undefined | Environment from the spawn config. Wins on key conflicts. |
Returns
Record<string, string> | undefinedresolveE2BTemplateFunction#
Resolve the effective E2B template, highest precedence first: the per-spawn
SpawnConfig.sandbox.template, then the factory template option, then the
E2B_TEMPLATE env var. Returns undefined (→ SDK's base default) when
none is set. The E2B SDK reads E2B_API_KEY/E2B_DOMAIN from the env but
NOT E2B_TEMPLATE, so this provider has to honor it itself.
function resolveE2BTemplate(
perSpawnTemplate: string | undefined,
optionTemplate: string | undefined,
env?: Record<string, string | undefined>,
): string | undefinedParameters
| Name | Type | Default | Description |
|---|---|---|---|
| perSpawnTemplate | string | undefined | Template from the spawn config. | |
| optionTemplate | string | undefined | Template from the provider options. | |
| env? | Record<string, string | undefined> | process.env | Environment to read |
Returns
string | undefinedrunE2BPregameFunction#
Upload a setup ("pregame") script into a ready sandbox and execute it.
Writes the script to /tmp/<name>, then chmod +x's and runs it directly
(so a #!/bin/bash / #!/usr/bin/env python3 shebang chooses the
interpreter) in cwd with envs applied — the same env the sandbox was
created with, so the script sees the run's --env / --pass-env. A
generous PREGAME_TIMEOUT_MS ceiling is used so long apt/pip
installs aren't killed by E2B's 60s command default. A non-zero exit
(whether E2B returns it or raises on it) throws, so the caller can tear the
sandbox down rather than prompt against a broken environment.
function runE2BPregame(
sandbox: E2BSandbox,
logger: Logger,
opts: { content: string; cwd?: string; envs?: Record<string, string>; name: string },
): Promise<void>Parameters
| Name | Type | Description |
|---|---|---|
| sandbox | E2BSandbox | Sandbox to run the script in. |
| logger | Logger | Logger for progress and failure lines. |
| opts | { content: string; cwd?: string; envs?: Record<string, string>; name: string } | Script |
Returns
Promise<void>waitForE2BReadyFunction#
Block until an E2B sandbox is ready to run commands, or throw if it never becomes ready within the deadline.
The E2B control plane can hand back a sandbox id before the sandbox's envd
is accepting commands; the first real command then absorbs that cold start
and may time out. This polls a trivial true command on a short backoff,
logging a waiting… line up front and a ready line (with elapsedMs) on
success. On deadline exhaustion it best-effort kills the sandbox and throws.
now/sleep are injectable purely for deterministic tests; in production
they default to Date.now and a real timer.
function waitForE2BReady(
sandbox: E2BSandbox,
logger: Logger,
opts: E2BReadinessOptions,
): Promise<void>Parameters
| Name | Type | Description |
|---|---|---|
| sandbox | E2BSandbox | Sandbox to probe. |
| logger | Logger | Logger for progress lines. |
| opts | E2BReadinessOptions | Deadline and probe settings. |
Returns
Promise<void>Interfaces#
E2BLifecycleInterface#
What E2B does when a sandbox's timeout elapses (e2b SDK SandboxLifecycle).
interface E2BLifecycleProperties
| Name | Type | Description |
|---|---|---|
| autoResume? | boolean | Resume a paused sandbox on the next connect / request (needs |
| onTimeout | 'pause' | 'kill' | Action when the sandbox timeout elapses: |
E2BProviderOptionsInterface#
Options for createE2BProvider.
interface E2BProviderOptionsProperties
| Name | Type | Description |
|---|---|---|
| apiKey? | string | E2B API key. Falls back to the |
| cwd? | string | Default working directory, applied when a spawn doesn't set its own
|
| domain? | string | E2B API domain. Set this to point at a self-hosted / on-prem cluster
(e.g. |
| env? | Record<string, string> | Environment variables baked into every sandbox at create time (passed to
E2B's |
| keepAliveSeconds? | number | Keep the sandbox alive while it's used: at most once a minute, any
command or file call pushes its timeout out to this many seconds from
now ( |
| lifecycle? | E2BLifecycle | What happens when a created sandbox's timeout elapses. |
| logger? | Logger | Logger for provider-level lifecycle lines (notably the readiness wait).
Defaults to a |
| onDestroy? | 'pause' | 'kill' | 'leave' | What to do with an owned sandbox when
Has no effect on sandboxes the provider only connected to (the |
| pregame? | { content: string; name: string } | A setup script to upload and run once the sandbox is ready, before any
prompting begins (see |
| readinessTimeoutSeconds? | number | Total deadline, in seconds, for the post-create readiness probe (see
|
| sandboxId? | string | Connect to a pre-existing sandbox by id instead of creating a fresh one
(E2B's |
| template? | string | Sandbox template id / name to launch. Falls back to the |
| timeoutSeconds? | number | Sandbox lifetime in seconds before E2B auto-kills it. Defaults to the
SDK default (300s). Per-spawn |
E2BReadinessOptionsInterface#
Options for waitForE2BReady.
interface E2BReadinessOptionsProperties
| Name | Type | Description |
|---|---|---|
| killOnTimeout? | boolean | Whether to kill the sandbox if it never becomes ready. Defaults to |
| now? | () => number | Clock source — injectable so tests can drive the deadline deterministically. |
| sleep? | (ms: number) => Promise<void> | Sleep between probes — injectable so tests can advance the clock without waiting. |
| timeoutSeconds | number | Total deadline in seconds. |