
Runtime sicuro per eseguire in sandbox i compiti degli agenti AI. Esegui codice non fidato in ambienti WebAssembly isolati.
Capsule è un runtime per eseguire codice non fidato in ambienti isolati. Ogni task viene eseguito all'interno del proprio sandbox WebAssembly, fornendo:
Basta annotare le funzioni Python con il decoratore @task:
from capsule import task
@task(name="analyze_data", compute="MEDIUM", ram="512MB", timeout="30s", max_retries=1)
def analyze_data(dataset: list) -> dict:
"""Elabora i dati in un ambiente isolato e con controllo delle risorse."""
# Il tuo codice viene eseguito in modo sicuro in un sandbox Wasm
return {"processed": len(dataset), "status": "complete"}
Usa la funzione wrapper task() con accesso completo all'ecosistema npm:
import { task } from "@capsule-run/sdk";
export const analyzeData = task({
name: "analyze_data",
compute: "MEDIUM",
ram: "512MB",
timeout: "30s",
maxRetries: 1
}, (dataset: number[]): object => {
// Il tuo codice viene eseguito in modo sicuro in un sandbox Wasm
return { processed: dataset.length, status: "complete" };
});
[!NOTE] Il runtime richiede un task chiamato
"main"come punto di ingresso. Python ne creerà automaticamente uno se non ne viene definito, ma si consiglia di impostarlo esplicitamente.
Quando esegui capsule run main.py (o main.ts), il tuo codice viene compilato in un modulo WebAssembly ed eseguito in sandbox isolati.
Ogni task opera all'interno del proprio sandbox con limiti di risorse configurabili, garantendo che i fallimenti siano contenuti e non si propaghino ad altre parti del tuo flusso di lavoro. Il sistema host controlla ogni aspetto dell'esecuzione, dall'allocazione della CPU tramite la misurazione del carburante Wasm ai vincoli di memoria e all'applicazione dei timeout.
pip install capsule-run
Crea hello.py:
from capsule import task
@task(name="main", compute="LOW", ram="64MB")
def main() -> str:
return "Hello from Capsule!"
Eseguilo:
capsule run hello.py
npm install -g @capsule-run/cli
npm install @capsule-run/sdk
Crea hello.ts:
import { task } from "@capsule-run/sdk";
export const main = task({
name: "main",
compute: "LOW",
ram: "64MB"
}, (): string => {
return "Hello from Capsule!";
});
Eseguilo:
capsule run hello.ts
[!TIP] Aggiungi
--verboseper vedere i dettagli dell'esecuzione del task in tempo reale.
La funzione run() ti consente di eseguire task in modo programmatico dal tuo codice invece di usare la CLI. Gli args vengono automaticamente inoltrati come parametri al task main.
from capsule import run
result = await run(
file="./sandbox.py",
args=["codice da eseguire"]
)
Crea sandbox.py:
from capsule import task
@task(name="main", compute="LOW", ram="64MB")
def main(code: str) -> str:
return eval(code)
[!IMPORTANT] Devi avere
@capsule-run/clinelle tue dipendenze per usare le funzioni runner in TypeScript.
import { run } from '@capsule-run/sdk/runner';
const result = await run({
file: './sandbox.ts',
args: ['codice da eseguire']
});
Crea sandbox.ts:
import { task } from "@capsule-run/sdk";
export const main = task({
name: "main",
compute: "LOW",
ram: "64MB"
}, (code: string): string => {
return eval(code);
});
[!TIP] Se stai cercando una soluzione preconfigurata e pronta all'uso, dai un'occhiata all'adattatore Python o all'adattatore TypeScript.
Configura i tuoi task con questi parametri:
Capsule controlla l'uso della CPU tramite il meccanismo del carburante di WebAssembly, che misura l'esecuzione delle istruzioni. Il livello di compute determina quanto carburante riceve il tuo task.
compute="1000000") per un controllo preciso sui limiti di esecuzione.Ogni task restituisce un involucro JSON strutturato contenente sia il risultato che i metadati di esecuzione:
{
"success": true,
"result": "Hello from Capsule!",
"error": null,
"execution": {
"task_name": "data_processor",
"duration_ms": 1523,
"retries": 0,
"fuel_consumed": 45000,
"ram_used": 1200000,
"host_requests": [{...}]
}
}
Campi della risposta:
success — Booleano che indica se il task è stato completato con successoresult — Il valore di ritorno effettivo del tuo task (json, stringa, null in caso di fallimento, ecc.)error — Dettagli dell'errore se il task è fallito ({ error_type: string, message: string })execution — Metriche di performance:
task_name — Nome del task eseguitoduration_ms — Tempo di esecuzione in millisecondiretries — Numero di tentativi di ripetizione effettuatifuel_consumed — Risorse CPU utilizzate (vedi Livelli di Compute)ram_used — Picco di memoria utilizzato in bytehost_requests — Elenco delle richieste host effettuate dal taskI task possono effettuare richieste HTTP verso i domini specificati in allowed_hosts. Di default, non sono consentite richieste in uscita ([]). Fornisci una lista di domini consentiti per concedere l'accesso, oppure usa ["*"] per consentire tutti i domini.
import json
from capsule import task
from urllib.request import urlopen
@task(name="main", allowed_hosts=["api.openai.com", "*.anthropic.com"])
def main() -> dict:
with urlopen("https://api.openai.com/v1/models") as response:
return json.loads(response.read().decode("utf-8"))
import { task } from "@capsule-run/sdk";
export const main = task({
name: "main",
allowedHosts: ["api.openai.com", "*.anthropic.com"]
}, async () => {
const response = await fetch("https://api.openai.com/v1/models");
return response.json();
});
I task possono leggere e scrivere file all'interno delle directory specificate in allowed_files. Qualsiasi tentativo di accedere a file al di fuori di queste directory non è possibile.
[!NOTE]
allowed_filessupporta solo percorsi di directory, non singoli file.
Ogni voce può essere un semplice percorso (lettura-scrittura di default) o un oggetto strutturato con una mode esplicita:
"read-only" (o "ro")"read-write" (o "rw")Le operazioni standard sui file di Python funzionano normalmente. Usa open(), os, pathlib o qualsiasi libreria di manipolazione dei file.
from capsule import task
@task(name="main", allowed_files=[
{"path": "./data", "mode": "read-only"},
{"path": "./output", "mode": "read-write"},
])
def main() -> str:
with open("./data/input.txt") as f:
content = f.read()
with open("./output/result.txt", "w") as f:
f.write(content)
return content
Le stringhe semplici sono ancora accettate: allowed_files=["./output"] imposta lettura-scrittura di default.
I built-in comuni di Node.js sono disponibili. Usa il modulo standard fs:
import { task } from "@capsule-run/sdk";
import fs from "fs/promises";
export const main = task({
name: "main",
allowedFiles: [
{ path: "./data", mode: "read-only" },
{ path: "./output", mode: "read-write" },
]
}, async () => {
const content = await fs.readFile("./data/input.txt", "utf8");
await fs.writeFile("./output/result.txt", content);
return content;
});
Le stringhe semplici sono ancora accettate: allowedFiles: ["./output"] imposta lettura-scrittura di default.
--mount)Il flag --mount (CLI) o il parametro mounts (SDK) montano una directory host nel sandbox sotto un alias. I mount si propagano ai sotto-task e aggiungono accesso a nuovi percorsi; non cambiano la modalità di accesso dei percorsi già dichiarati in allowed_files.
Formato: PERCORSO_HOST[::PERCORSO_OSPITE][:ro|:rw]
CLI
# Monta un workspace di sessione e lo espone come "workspace" all'interno del task
capsule run main.py --mount sessions/abc123_workspace::workspace
# Multiple directory
capsule run main.py \
--mount sessions/abc123_workspace::workspace \
--mount sessions/bce456_workspace::workspace:ro
SDK Python
from capsule import run
result = await run(
file="main.py",
mounts=[".capsule/sessions/abc123_workspace::workspace"],
)
SDK TypeScript / JavaScript
import { run } from "@capsule-run/sdk";
const result = await run({
file: "main.py",
mounts: [".capsule/sessions/abc123_workspace::workspace"],
});
All'interno del task, la directory è accessibile tramite il percorso ospite:
# il task la vede in "workspace/", non nel percorso completo della sessione
with open("workspace/output.txt", "w") as f:
f.write("done")
[!NOTE] I percorsi
--mountdevono essere relativi e non devono uscire dalla root del progetto. I percorsi assoluti vengono rifiutati.
I task possono accedere alle variabili d'ambiente per leggere configurazioni, chiavi API o altre impostazioni di runtime.
Usa il modulo standard os.environ di Python per accedere alle variabili d'ambiente:
from capsule import task
import os
@task(name="main", env_variables=["API_KEY"])
def main() -> dict:
api_key = os.environ.get("API_KEY")
return {"api_key": api_key}
Usa il process.env standard per accedere alle variabili d'ambiente:
import { task } from "@capsule-run/sdk";
export const main = task({
name: "main",
envVariables: ["API_KEY"]
}, () => {
const apiKey = process.env.API_KEY;
return { apiKeySet: apiKey !== undefined };
});
Puoi creare un file capsule.toml nella root del progetto per impostare opzioni predefinite per tutti i task e definire metadati del flusso di lavoro:
# capsule.toml
[workflow]
name = "Il Mio Workflow"
version = "1.0.0"
entrypoint = "src/main.py" # File predefinito quando si esegue `capsule run`
[tasks]
default_compute = "MEDIUM"
default_ram = "256MB"
default_timeout = "30s"
default_max_retries = 2
Con un entrypoint definito, puoi semplicemente eseguire:
capsule run
Le opzioni a livello di task sovrascrivono sempre questi valori predefiniti quando specificate.
Quando esegui il tuo codice, Capsule crea una cartella .capsule nella root del progetto. Questa è la cache di compilazione. Memorizza gli artefatti compilati in modo che le esecuzioni successive siano rapide (da secondi a pochi millisecondi).
[!TIP]
.capsuledovrebbe essere aggiunto a.gitignore. La cache è specifica per il tuo ambiente e verrà rigenerata automaticamente.
.capsule/
├── wasm/
│ ├── main_a1b2c3d4.wasm # Modulo WebAssembly compilato
│ └── main_a1b2c3d4.cwasm # Cache nativa precompilata
├── wit/ # Definizioni delle interfacce
└── trace.db # Log di esecuzione
Usa capsule build per precompilare in anticipo e saltare il costo di compilazione alla prima esecuzione:
capsule build main.ts # o `main.py`
Eseguire il codice sorgente direttamente (come .py o .ts) valuta e compila il tuo file in fase di esecuzione. Sebbene sia ottimo per lo sviluppo, questo passaggio di compilazione aggiunge alcuni secondi di latenza alla prima chiamata. Per casi d'uso in cui la latenza sub-secondo è critica, dovresti compilare i tuoi task in anticipo.
# Genera un file hello.wasm ottimizzato
capsule build hello.py --export
# Esegui l'artefatto compilato direttamente
capsule exec hello.wasm
[!NOTE] O dal tuo codice esistente:
from capsule import run result = await run( file="./hello.wasm", # o `hello.py` args=[] ) print(f"Task completato: {result['result']}")
Eseguire un file .wasm bypassa completamente il compilatore, riducendo il tempo di inizializzazione a millisecondi, utilizzando al contempo un formato nativamente ottimizzato (.cwasm) dietro le quinte.
[!NOTE] TypeScript/JavaScript ha una compatibilità più ampia rispetto a Python poiché non si basa su binding nativi.
Python: La maggior parte delle librerie standard Python funzionano perfettamente. I pacchetti che utilizzano estensioni C richiedono una wheel compilata per wasm32-wasi. Molti pacchetti popolari come numpy e pandas non ne forniscono ancora una, quindi non funzioneranno all'interno del sandbox. Tuttavia, il tuo codice host (che utilizza run()) ha accesso all'intero ecosistema Python, inclusi tutti i pacchetti pip e le estensioni native. Vedi utilizzo dal codice
TypeScript/JavaScript: I pacchetti npm e i moduli ES funzionano. I built-in comuni di Node.js sono disponibili. Se hai problemi con un built-in, non esitare ad aprire un issue.
I contributi sono benvenuti!
Prerequisiti: Rust (ultima versione stabile), Python 3.13+, Node.js 22+
git clone https://github.com/capsulerun/capsule.git
cd capsule
# Compila e installa la CLI
cargo install --path crates/capsule-cli
# SDK Python (installazione modificabile)
pip install -e crates/capsule-sdk/python
# SDK TypeScript (link per sviluppo locale)
cd crates/capsule-sdk/javascript
npm install && npm run build && npm link
# Poi nel tuo progetto: npm link @capsule-run/sdk
git checkout -b feature/funzionalità-straordinariacargo test (solo se modifichi crates/capsule-cli o crates/capsule-core)Hai bisogno di aiuto? Apri un issue
Capsule si basa su questi progetti open source:
Questo progetto è concesso in licenza con Apache License 2.0 - vedi il file LICENSE per i dettagli.
| Parametro | Descrizione | Tipo | Default | Esempio |
|---|
name | Identificatore del task | str | nome della funzione (Python) / richiesto (TS) | "process_data" |
compute | Livello di allocazione CPU: "LOW", "MEDIUM" o "HIGH" | str | "MEDIUM" | "HIGH" |
ram | Limite di memoria per il task | str | illimitato | "512MB", "2GB" |
timeout | Tempo massimo di esecuzione | str | illimitato | "30s", "5m", "1h" |
max_retries / maxRetries | Numero di tentativi di ripetizione in caso di fallimento | int | 0 | 3 |
allowed_files / allowedFiles | Cartelle accessibili all'interno del sandbox (con modalità di accesso opzionale) | list | [] | ["./data"], [{"path": "./data", "mode": "ro"}] |
allowed_hosts / allowedHosts | Domini accessibili all'interno del sandbox | list | [] | ["api.openai.com", "*.anthropic.com"] |
env_variables / envVariables | Variabili d'ambiente accessibili all'interno del sandbox | list | [] | ["API_KEY"] |
| Parte | Obbligatorio | Descrizione |
|---|
PERCORSO_HOST | sì | Percorso sulla macchina host (relativo a cwd, deve rimanere all'interno della root del progetto) |
::PERCORSO_OSPITE | no | Percorso che il task vede all'interno del sandbox. Default: PERCORSO_HOST |
:ro / :rw | no | Modalità di accesso. Default: lettura-scrittura |