SDK Python e TypeScript
Os dois SDKs falam com a mesma API (/api/v1/orgs/{orgId}/projects/{projectId}/sandboxes) e passam pelos mesmos testes de
contrato. Autentique com um token de conta de serviço com os escopos sandboxes:read,
sandboxes:write e sandboxes:exec.
Variáveis lidas por padrão: CODA_API_URL, CODA_TOKEN, CODA_ORG_ID e CODA_PROJECT_ID. Os nomes antigos
ATLAS_* continuam aceitos como reserva, assim como os aliases AtlasClient e AtlasError.
Python
pip install coda-sandbox
from coda_sandbox import CodaClient
with CodaClient() as coda:
with coda.sandboxes.create(image="python-3.13", network_profile="pypi-only", ttl_seconds=1800) as sbx:
sbx.exec("pip install --user requests") # modo sync
exit = sbx.run(["python", "-c", "import requests; print(requests.__version__)"],
on_stdout=lambda b: print(b.decode(), end="")) # modo stream (SSE)
print("saída:", exit["exitCode"])
sbx.files.write("app/main.py", "print('olá')")
print(sbx.files.read_text("app/main.py"))
sbx.spawn(["python", "-m", "http.server", "8000"]) # modo background
port = sbx.expose_port(8000) # visibilidade TOKEN
print(port["url"], port["authUrl"])
# o with encerra a sandbox
TypeScript
npm install @coda-cloud/sandbox
import { CodaClient } from "@coda-cloud/sandbox";
const coda = new CodaClient();
await using sbx = await coda.sandboxes.create({ image: "node-24", networkProfile: "npm-only" });
await sbx.exec("npm install --prefix /workspace left-pad");
const exit = await sbx.run(["node", "-e", "console.log(42)"], {
onStdout: (chunk) => process.stdout.write(chunk),
onStderr: (chunk) => process.stderr.write(chunk),
});
console.log("saída", exit.exitCode);
await sbx.files.write("index.js", 'console.log("olá")');
const tarGz = await sbx.files.download(".");
await using encerra a sandbox no fim do bloco (TypeScript 5.2+); sem ele, chame sbx.terminate().
Modos de execução
| Modo | Método | Quando usar |
|---|---|---|
stream | run | ver a saída enquanto o comando roda; o fim chega no evento exit |
sync | exec | comandos curtos; até 1 MiB de cada saída, com stdoutTruncated |
background | spawn + command, output, cancel | servidores e tarefas longas; a saída é lida por offset |
Um comando aceita cwd, env, tempo limite (padrão 300 s) e stdin. Uma string vira /bin/sh -c; uma lista é o argv direto
(funciona em imagens sem shell).
Erros
Toda resposta de erro vira uma exceção tipada:
| Exceção | Quando |
|---|---|
PlanLimitError | 400 plan.limit_exceeded: um campo acima do teto do plano (params.key diz qual) |
FeatureUnavailableError | 403 plan.feature_unavailable: pausa, porta pública, egress aberto fora do plano |
ForbiddenError | 403: papel ou escopo insuficiente (scopes traz o escopo pedido) |
QuotaExceededError | 422 quota.exceeded: sandboxes simultâneas, portas ou vCPU·s do mês |
NotFoundError | 404: a sandbox ou o arquivo não existe |
ConflictError | 409: sandbox pausada ou encerrada, ou microVM indisponível na região |
RateLimitedError | 429: retry_after_seconds (Python) ou retryAfterSeconds (TS) diz quando tentar de novo |
RestrictedError | 423: organização restrita |
UnavailableError | 503: kill switch, sandboxes desligadas ou runtime indisponível |
Idempotência
create(..., idempotency_key="pedido-123") (Python) ou create({ idempotencyKey: 'pedido-123' }) (TS) envia o cabeçalho
Idempotency-Key: repetir a chamada com a mesma chave devolve a mesma sandbox, útil quando o agente refaz um passo.