Agents IA
Utilisez les sandboxes Spunto comme machines isolées pour vos agents de code IA — ou confiez-leur des tâches entières.
Spunto offre deux portes d’entrée à un agent :
- Les tâches — vous donnez un prompt à Spunto, qui fait tourner l’agent pour vous (Claude Code ou Codex) dans une sandbox à lui, sur une branche à lui, et s’arrête à la relecture. → Tâches
- Les sandboxes — votre propre boucle d’agent pilote une machine par l’API REST : la créer depuis un projet, y lancer des commandes, s’attacher à un terminal, lire ses ports, la détruire. C’est l’objet de cette page.
- Isolation — chaque exécution d’agent a sa propre machine ; aucun accès à l’hôte
- Préconfigurées — les dépôts sont clonés, les secrets injectés, vos commandes de cycle de vie ont tourné
- Jetables — détruisez-la une fois le travail fini, ou gardez-la arrêtée avec son
/workspace - Pilotées par API — créer, exécuter, lire et détruire, entièrement en HTTP
1. POST /api/orgs/{orgId}/sandboxes → create it from a project
2. GET /api/orgs/{orgId}/sandboxes/{sandboxId} → wait for status "running"
3. POST /api/orgs/{orgId}/sandboxes/{sandboxId}/exec → run commands (or attach a terminal)
4. DELETE /api/orgs/{orgId}/sandboxes/{sandboxId} → destroy itCréez une clé d’API dans l’organisation où l’agent doit opérer — Settings → API keys — avec workers:read + workers:write (ils couvrent les sandboxes) et workers:exec pour lancer des commandes ou ouvrir un terminal ; ajoutez projects:read s’il doit aussi consulter les projets. Pour un agent déployé durablement hors du compte d’une personne, créez une clé appartenant à l’organisation (compte de service) — elle continue de fonctionner si son créateur s’en va.
curl -H "Authorization: Bearer spk_..." \
https://spunto.net/api/orgs/{orgId}/projectsappId vaut toujours spunto-cloud ; size est facultatif (small, default, large, xlarge) :
curl -X POST \
-H "Authorization: Bearer spk_..." \
-H "Content-Type: application/json" \
-d '{ "projectId": "{projectId}", "appId": "spunto-cloud", "size": "default", "branch": "main" }' \
https://spunto.net/api/orgs/{orgId}/sandboxesLa réponse revient tout de suite, avec status: "starting". Interrogez GET .../sandboxes/{sandboxId} jusqu’à status === "running" (ou error, avec statusReason).
curl -X POST \
-H "Authorization: Bearer spk_..." -H "Content-Type: application/json" \
-d '{ "command": "npm test" }' \
https://spunto.net/api/orgs/{orgId}/sandboxes/{sandboxId}/execL’appel attend la fin de la commande et répond avec son code de sortie et sa sortie :
{
"exitCode": 0,
"timedOut": false,
"stdout": "…",
"stderr": "",
"truncated": false,
"durationMs": 5120
}La commande tourne dans le shell de la sandbox (bash -lc), dans /workspace : son PATH, ses versions d’outils et ses secrets s’appliquent. Passez cwd pour l’exécuter ailleurs, env pour ajouter des variables, et timeoutMs (jusqu’à 30 minutes). Seul le propriétaire de la sandbox peut y lancer des commandes.
Pour un vrai shell — envoyer des entrées à un programme, ou une session qu’un humain peut suivre —, le terminal de la sandbox est un PTY complet sur WebSocket. Il accepte le même en-tête Authorization: Bearer <api key> (scope workers:exec) : pas besoin de session navigateur.
wss://spunto.net/api/sandboxes/{sandboxId}/terminal?mode=terminal&cols=220&rows=50&session=main| Paramètre | Défaut | Description |
|---|---|---|
mode | terminal | terminal (PTY interactif) ou logs (les logs de la sandbox, en lecture seule) |
cols / rows | 120 / 30 | Dimensions du PTY — une valeur large évite les retours à la ligne dans la sortie de l’agent |
session | main | Nom de la session persistante — réutilisez-le pour vous rattacher à une session en cours |
Protocole :
- Attendez
{ "type": "ready" }— le PTY est attaché - Envoyez
{ "type": "input", "data": "<base64>" }— les frappes brutes,\rcompris pour valider une ligne - Recevez
{ "type": "output", "data": "<base64>" }— la sortie brute du PTY (ANSI compris) - Envoyez
{ "type": "resize", "cols": N, "rows": M }pour redimensionner le PTY
Se reconnecter avec le même ?session=<name> rattache la session — le shell et ce qui y tourne survivent à une déconnexion. C’est ainsi qu’on lance une longue commande d’agent sans garder la connexion ouverte pendant toute sa durée.
import WebSocket from "ws"
import { Buffer } from "node:buffer"
const ws = new WebSocket(
`wss://spunto.net/api/sandboxes/${sandboxId}/terminal?mode=terminal&cols=220&rows=50`,
{ headers: { Authorization: `Bearer ${apiKey}` } }
)
ws.once("message", (raw) => {
if (JSON.parse(raw.toString()).type !== "ready") return
ws.send(JSON.stringify({ type: "input", data: Buffer.from("npm run build\r").toString("base64") }))
})
ws.on("message", (raw) => {
const msg = JSON.parse(raw.toString())
if (msg.type === "output") process.stdout.write(Buffer.from(msg.data, "base64"))
})curl -H "Authorization: Bearer spk_..." \
https://spunto.net/api/orgs/{orgId}/sandboxes/{sandboxId}/portsRenvoie l’URL de l’IDE et chaque port en écoute dans la sandbox, chacun sur son URL HTTPS à lui.
Les secrets de l’organisation, du projet et du membre sont injectés en variables d’environnement dans chaque sandbox — par exemple un secret personnel :
curl -X POST \
-H "Authorization: Bearer spk_..." \
-H "Content-Type: application/json" \
-d '{ "name": "ANTHROPIC_API_KEY", "value": "sk-ant-..." }' \
https://spunto.net/api/users/me/secretsChiffrés au repos, jamais renvoyés par l’API.