SandboxesReferencia del SDK

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

TypeScriptPython
projectId / projectproject_id / project
timeoutMstimeout_ms
regionregion
volumeName / volumeIdvolume_name / volume_id
sizesize
snapshotIdsnapshot_id
envsenvs
metadatametadata
lizardTokenlizard_token
apiKeyapi_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.
  • size es '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 plantillas base, claude, codex e interpreter, 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 lanza AuthenticationError con HTTP 403. size no se aplica a un sandbox restaurado desde un snapshot; se ejecuta con el tamaño de su origen.
  • timeoutMs es 300000 por defecto, cinco minutos. 0 desactiva 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 region cuando adjuntes un volumen: el sandbox se ejecuta donde está el volumen.
  • metadata son tus propias etiquetas; getInfo() las devuelve, list() no.
  • lizardToken toma una de tus claves API liz_ e inicia sesión en lizard dentro 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í que lizard ahí solo puede actuar sobre ese proyecto. La plantilla base no incluye lizard.

Métodos

TypeScriptPythonQué 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.

ErrorSignificado
PaymentRequiredErrorHTTP 402: la cuenta debe empezar Pro o pagar una factura antes de poder crear nada. url es la página que hay que abrir.
AuthenticationErrorHTTP 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.
NotFoundErrorHTTP 404: el sandbox, el archivo o el snapshot no existe o ha vencido.
ConflictErrorHTTP 409: estado en conflicto, como hacer fork de un sandbox con volumen.
LimitExceededErrorHTTP 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.
TimeoutErrorHTTP 408 o 504. La espera propia de 60 segundos del SDK de TypeScript para process.exec lanza en su lugar una DOMException llamada TimeoutError.
LizardErrorOtros 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.

LlamadaSolicitud
CrearPOST /api/sandboxes con template, projectId, size, timeoutMs, envs, metadata, region, volumeName; devuelve 201 con sandboxId
Obtener el estadoGET /api/sandboxes/{id}
ListarGET /api/sandboxes, o GET /api/projects/{projectId}/sandboxes para un proyecto
Ejecutar un comandoPOST /api/sandboxes/{id}/exec con cmd, envs, workdir, user, timeoutMs; devuelve stdout, stderr, exitCode
Cambiar la duraciónPOST /api/sandboxes/{id}/timeout con timeoutMs
Pausar o reanudarPOST /api/sandboxes/{id}/pause, POST /api/sandboxes/{id}/resume
Publicar un puertoPOST /api/sandboxes/{id}/expose/{port}; DELETE en la misma ruta lo retira
DetenerDELETE /api/sandboxes/{id}
Consultar los límitesGET /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