Referencia de la API y el SDK de Sandbox
Lizard SDK es el cliente de Sandboxes en Lizard (lizard.build): @lizard-build/sdk en npm para TypeScript y JavaScript, y lizard-sdk en PyPI para Python, que se importa como lizard. Ambos llaman a la misma API HTTP. Esta página enumera cada llamada de sandbox por su nombre en TypeScript y en Python, con ejemplos en ambos lenguajes. Para una primera ejecución guiada, usa el inicio rápido; para saber qué son los Sandboxes, lee la descripción general.
Instalar y autenticar
npm install @lizard-build/sdk
pip install lizard-sdk
export LIZARD_API_KEY="<your-api-key>"Crea una clave API en Settings → API keys o con lizard keys create <name>. El SDK lee LIZARD_API_KEY; pasa apiKey (Python: api_key) para usar otra clave. Cada sandbox necesita un proyecto: pasa projectId (Python: project_id) o resuelve su ID, slug o nombre con project.
Opciones de creación
| TypeScript | Python |
|---|---|
projectId / project | project_id / project |
timeoutMs | timeout_ms |
region | region |
volumeName / volumeId | volume_name / volume_id |
size | size |
snapshotId | snapshot_id |
envs | envs |
metadata | metadata |
lizardToken | lizard_token |
apiKey | api_key |
import { Sandbox } from '@lizard-build/sdk';
const sandbox = await Sandbox.create('base', {
projectId: 'proj_123',
size: 'small',
timeoutMs: 10 * 60 * 1000,
envs: { MODE: 'test' },
metadata: { task: 'report-42' },
});from lizard import Sandbox
sandbox = Sandbox.create(
"base",
project_id="proj_123",
size="small",
timeout_ms=10 * 60 * 1000,
envs={"MODE": "test"},
metadata={"task": "report-42"},
)- El primer argumento es una plantilla del catálogo de runtime y plantillas. Sin él, el SDK arranca
base. sizees'mini','small','medium'o'large': 1 vCPU / 2 GB, 2 vCPU / 4 GB, 4 vCPU / 8 GB (el predeterminado) u 8 vCPU / 16 GB; consulta tamaños y precios.'mini'solo arranca desde las plantillasbase,claude,codexeinterpreter, sin un volumen. Durante la prueba,'small'es el predeterminado, solo se ejecuta un'medium'a la vez y solo funcionan las regiones de EE. UU.;'large', un segundo Medium u otra región lanzaAuthenticationErrorcon HTTP 403.sizeno se aplica a un sandbox restaurado desde un snapshot; se ejecuta con el tamaño de su origen.timeoutMses 300000 por defecto, cinco minutos.0desactiva la caducidad. Consulta recursos y duración.- Indica la región de forma explícita si hace falta; que aparezca en el catálogo no garantiza capacidad libre. No indiques
regioncuando adjuntes un volumen: el sandbox se ejecuta donde está el volumen. metadatason tus propias etiquetas;getInfo()las devuelve,list()no.lizardTokentoma una de tus claves APIliz_e inicia sesión enlizarddentro del sandbox. Tu clave solo autoriza la solicitud: el sandbox recibe una clave nueva propia, limitada al proyecto del sandbox y que se elimina con el sandbox, así quelizardahí solo puede actuar sobre ese proyecto. La plantillabaseno incluyelizard.
Métodos
| TypeScript | Python | Qué hace |
|---|---|---|
Sandbox.create(template, options) | Sandbox.create(template, ...) | Crear en un proyecto desde una plantilla del catálogo. |
Sandbox.connect(sandboxId) | Sandbox.connect(sandbox_id) | Conectar a un sandbox existente; no reanuda. |
Sandbox.list({ projectId }) | Sandbox.list(project_id=...) | Listar Sandboxes accesibles; comprobar su estado. |
sandbox.getInfo() | sandbox.get_info() | Obtener estado, runtime y metadatos. |
sandbox.process.exec(cmd, opts) | sandbox.process.exec_(cmd, ...) | Ejecutar un comando de shell o un array argv; devolver stdout, stderr y el código de salida. |
sandbox.process.list() | sandbox.process.list() | Listar los procesos iniciados con exec. |
sandbox.process.kill(pid, signal) | sandbox.process.kill(pid, signal) | Enviar una señal a un proceso y a sus hijos; SIGTERM por defecto. |
sandbox.fs.write(path, data) | sandbox.fs.write(path, data) | Escribir texto o bytes; crear directorios superiores. |
sandbox.fs.read(path) | sandbox.fs.read(path) | Leer texto UTF-8. |
sandbox.fs.readBytes(path) | sandbox.fs.read_bytes(path) | Leer los bytes de un archivo. |
sandbox.fs.list(path) | sandbox.fs.list(path) | Listar un directorio. |
sandbox.fs.stat(path) | sandbox.fs.stat(path) | Devolver tipo, tamaño, modo, propietario y fecha de modificación. |
sandbox.fs.makeDir(path) | sandbox.fs.make_dir(path) | Crear un directorio y los superiores que falten. |
sandbox.fs.move(from, to) | sandbox.fs.move(src, dst) | Mover un archivo o directorio. |
sandbox.fs.remove(path) | sandbox.fs.remove(path) | Eliminar un archivo o directorio. |
sandbox.exposePort(port) | sandbox.expose_port(port) | Publicar un puerto; devolver url, hostname y el token de acceso. |
sandbox.getHost(port) | sandbox.get_host(port) | Publicar un puerto y devolver su hostname; las solicitudes necesitan sandbox.accessToken. |
sandbox.unexpose(port) | sandbox.unexpose(port) | Retirar un puerto publicado. |
sandbox.setTimeout(ms) | sandbox.set_timeout(ms) | Fijar la duración restante, mínimo 1000 ms. |
sandbox.pause() / sandbox.resume() | sandbox.pause() / sandbox.resume() | Poner en cola una pausa o reanudación y responder al instante. |
sandbox.pause({ fast: true }) | sandbox.pause(fast=True) | Pausa conservando solo los archivos, en alrededor de un segundo con cualquier tamaño de memoria; los procesos terminan y la reanudación arranca el sandbox de nuevo. |
sandbox.waitForStatus(status) | sandbox.wait_for_status(status) | Esperar a paused o running; lanzar un error si falla la pausa. |
sandbox.fork({ count }) | sandbox.fork(count=...) | Copiar un sandbox en marcha de 1 a 100 veces, incluidos la memoria y los procesos; cada copia cuenta para tu límite de sandboxes en marcha. |
sandbox.snapshot(name) | sandbox.snapshot(name) | Guardar la memoria, los procesos y todos los archivos en un snapshot. Solo se restaura en el proyecto y la región de este sandbox. |
Sandbox.waitForSnapshot(snapshotId) | Sandbox.wait_for_snapshot(snapshot_id) | Esperar a que el snapshot esté listo. |
Sandbox.restore(snapshotId, { projectId, volumeId, timeoutMs }) | Sandbox.restore(snapshot_id, project_id=..., volume_id=..., timeout_ms=...) | Iniciar un sandbox nuevo desde un snapshot, en el proyecto y la región del snapshot. Cualquier otro projectId lanza AuthenticationError con HTTP 403 y code igual a SNAPSHOT_SCOPE. |
Sandbox.snapshots(projectId) | Sandbox.snapshots(project_id) | Listar los snapshots tomados en un proyecto. |
Sandbox.mySnapshots() | Sandbox.my_snapshots() | Listar todos los snapshots que tomaste, en todos tus workspaces. |
Sandbox.deleteSnapshot(snapshotId) | Sandbox.delete_snapshot(snapshot_id) | Eliminar un snapshot; los sandboxes restaurados desde él siguen en marcha. |
sandbox.kill() | sandbox.kill() | Detener el sandbox y separar su volumen. |
Ejecutar comandos
process.exec (Python: exec_) ejecuta un comando y espera a que termine. Pasa onStdout u onStderr (Python: on_stdout, on_stderr) para recibir la salida mientras se ejecuta el comando, una línea por llamada sin el salto de línea final; la llamada igualmente devuelve el resultado completo al final. Pasa un array argv en lugar de una cadena para evitar la shell, que es la forma segura de pasar texto no confiable como argumento.
const result = await sandbox.process.exec('npm test', {
workdir: '/app',
envs: { NODE_ENV: 'test' },
timeoutMs: 120_000,
onStdout: (line) => console.log(line),
});
if (result.exitCode !== 0) throw new Error(result.stderr);
await sandbox.process.exec(['grep', '-rn', '--', userQuery, '/workspace']);result = sandbox.process.exec_(
"npm test",
workdir="/app",
envs={"NODE_ENV": "test"},
timeout_ms=120_000,
on_stdout=print,
)
if result.exit_code != 0:
raise RuntimeError(result.stderr)
sandbox.process.exec_(["grep", "-rn", "--", user_query, "/workspace"])Los resultados incluyen stdout, stderr y el código de salida. Un código de salida distinto de cero es un resultado que debes comprobar; los fallos de la solicitud lanzan una excepción. timeoutMs (Python: timeout_ms) fija cuánto tiempo puede ejecutarse un comando, de 1 a 600 segundos. Sin él, el servidor detiene el comando a los 150 segundos, y el SDK de TypeScript deja de esperar a los 60 segundos y lanza una DOMException llamada TimeoutError. Pasa timeoutMs para cualquier comando que pueda tardar más de un minuto. process.exec acepta envs, workdir y user; los comandos se ejecutan como root salvo que pases user. Las envs de creación llegan a todos los comandos.
Archivos
fs.write escribe texto o bytes; fs.read lee texto. Para archivos binarios, escribe bytes y vuelve a leerlos con fs.readBytes (Python: read_bytes). Usa rutas absolutas. Una ruta que no existe lanza NotFoundError.
await sandbox.fs.write('/app/data.csv', 'x,y\n1,2\n');
const text = await sandbox.fs.read('/app/data.csv');
const entries = await sandbox.fs.list('/app');
const png = await sandbox.fs.readBytes('/app/chart.png');sandbox.fs.write("/app/data.csv", "x,y\n1,2\n")
text = sandbox.fs.read("/app/data.csv")
entries = sandbox.fs.list("/app")
png = sandbox.fs.read_bytes("/app/chart.png")Una pausa o un snapshot conserva todos los archivos del disco, estén donde estén. Tras una detención (kill o expiración), los archivos fuera de un volumen de Persistent Volumes en /workspace desaparecen, salvo que antes hayas tomado un snapshot.
Puertos
Inicia un servidor en 0.0.0.0 dentro del sandbox y luego publica su puerto. Cada solicitud necesita el token de acceso, en la cabecera X-Lizard-Access-Token o una vez como ?lizard_token= en un navegador. Consulta URLs de vista previa.
const { url, hostname, accessToken } = await sandbox.exposePort(3000);
console.log(url); // open in a browser; it carries the token
await fetch(`https://${hostname}/api`, { headers: { 'X-Lizard-Access-Token': accessToken } });
await sandbox.unexpose(3000);import httpx
exposed = sandbox.expose_port(3000)
print(exposed.url) # open in a browser; it carries the token
httpx.get(f"https://{exposed.hostname}/api", headers={"X-Lizard-Access-Token": exposed.access_token})
sandbox.unexpose(3000)Pausa, snapshot y fork
Estas llamadas conservan la memoria, los procesos en ejecución y los archivos. El inicio rápido explica qué conserva cada una, quién puede restaurar un snapshot, cómo se facturan los restores y qué puede fallar.
await sandbox.pause();
await sandbox.waitForStatus('paused');
await sandbox.resume();
await sandbox.waitForStatus('running');
const saved = await sandbox.snapshot('ready-env');
const copy = await Sandbox.restore(saved.id);
for (const f of await sandbox.fork({ count: 2 })) {
if (f.sandbox) await f.sandbox.process.exec('echo hi');
else console.warn('fork failed:', f.error);
}sandbox.pause()
sandbox.wait_for_status("paused")
sandbox.resume()
sandbox.wait_for_status("running")
saved = sandbox.snapshot("ready-env")
copy = Sandbox.restore(saved["id"])
for f in sandbox.fork(count=2):
if f.sandbox:
f.sandbox.process.exec_("echo hi")
else:
print("fork failed:", f.error)fork devuelve una entrada por copia: el sandbox nuevo o un error, como el límite de sandboxes de la cuenta. Cada copia se factura como su origen.
Ejecutar código con estado
CodeSandbox arranca la plantilla interpreter y ejecuta código con runCode (Python: run_code). En esa plantilla, Python se ejecuta en un kernel de Jupyter persistente por contexto, así que las variables se conservan entre llamadas y los resultados pueden incluir gráficos como image/png. JavaScript y bash se ejecutan como un proceso nuevo en cada llamada. Consulta intérprete de código.
Volúmenes
Adjunta un volumen de Persistent Volumes por su nombre para conservar /workspace entre sandboxes:
import { Sandbox, Volume } from '@lizard-build/sdk';
await Volume.getOrCreate('proj_123', 'agent-workdir', { sizeGb: 10 });
const sandbox = await Sandbox.create('base', { projectId: 'proj_123', volumeName: 'agent-workdir' });from lizard import Sandbox, Volume
Volume.get_or_create("proj_123", "agent-workdir", size_gb=10)
sandbox = Sandbox.create("base", project_id="proj_123", volume_name="agent-workdir")API no compatibles
La vigilancia de archivos aún no está disponible. sandbox.fork() devuelve 409 cuando hay un volumen adjunto. Sandbox.connect no reanuda un sandbox pausado ni uno detenido; para uno pausado, llama a resume(). No hay acceso por el protocolo SSH; lizard sandbox ssh abre una shell interactiva con tus credenciales de la CLI.
Errores
Los fallos de la API lanzan (Python: raise) estos errores. Cada uno incluye el estado HTTP y, cuando la API lo envía, un code.
| Error | Significado |
|---|---|
PaymentRequiredError | HTTP 402: la cuenta debe empezar Pro o pagar una factura antes de poder crear nada. url es la página que hay que abrir. |
AuthenticationError | HTTP 401 o 403: clave no válida o sin acceso. Durante la prueba, un segundo sandbox Medium en ejecución a la vez o una región fuera de EE. UU. también devuelve 403, con code igual a TRIAL_MEDIUM_RUNNING o TRIAL_SANDBOX_REGION; un sandbox Large devuelve 403 con SANDBOX_SIZE_NOT_ALLOWED, tanto al crearlo como al reanudarlo o hacer fork de él. |
NotFoundError | HTTP 404: el sandbox, el archivo o el snapshot no existe o ha vencido. |
ConflictError | HTTP 409: estado en conflicto, como hacer fork de un sandbox con volumen. |
LimitExceededError | HTTP 429: demasiadas vCPU de sandboxes a la vez o demasiados inicios. code indica qué límite, como SANDBOX_LIMIT_REACHED o SANDBOX_RATE_LIMITED. Para SANDBOX_LIMIT_REACHED, limit y running están en vCPU, unit lo indica (vcpu) y needed son las vCPU que necesita el sandbox rechazado. retryAfterS (Python: retry_after_s) indica cuándo volver a intentarlo. |
TimeoutError | HTTP 408 o 504. La espera propia de 60 segundos del SDK de TypeScript para process.exec lanza en su lugar una DOMException llamada TimeoutError. |
LizardError | Otros fallos de la API, incluidas las operaciones no compatibles. |
Los valores que llevan a HTTP 429 están en límites de la cuenta.
API HTTP
El SDK llama a la API REST de Lizard en https://lizard.build. Envía tu clave API como Authorization: Bearer liz_... o en la cabecera X-API-Key.
| Llamada | Solicitud |
|---|---|
| Crear | POST /api/sandboxes con template, projectId, size, timeoutMs, envs, metadata, region, volumeName; devuelve 201 con sandboxId |
| Obtener el estado | GET /api/sandboxes/{id} |
| Listar | GET /api/sandboxes, o GET /api/projects/{projectId}/sandboxes para un proyecto |
| Ejecutar un comando | POST /api/sandboxes/{id}/exec con cmd, envs, workdir, user, timeoutMs; devuelve stdout, stderr, exitCode |
| Cambiar la duración | POST /api/sandboxes/{id}/timeout con timeoutMs |
| Pausar o reanudar | POST /api/sandboxes/{id}/pause, POST /api/sandboxes/{id}/resume |
| Publicar un puerto | POST /api/sandboxes/{id}/expose/{port}; DELETE en la misma ruta lo retira |
| Detener | DELETE /api/sandboxes/{id} |
| Consultar los límites | GET /api/sandboxes/limits; devuelve tier, unit, limits, running y runningVcpu |
curl -X POST https://lizard.build/api/sandboxes \
-H "Authorization: Bearer $LIZARD_API_KEY" \
-H "Content-Type: application/json" \
-d '{"template": "base", "projectId": "proj_123", "timeoutMs": 300000}'La API directa crea un sandbox sin caducidad salvo que pases timeoutMs.
Almacenamiento y facturación
La facturación, el tráfico saliente y lo que sobrevive a una detención se describen en un solo lugar, en almacenamiento y facturación. Los tamaños y precios por hora están en límites.
Actualizado