
Безопасная среда выполнения для изоляции задач AI-агентов. Запускайте недоверенный код в изолированных средах WebAssembly.
Capsule — это среда выполнения для запуска непроверенного кода в изолированных окружениях. Каждая задача выполняется в собственной песочнице WebAssembly, что обеспечивает:
Просто добавьте декоратор @task к вашим функциям:
from capsule import task
@task(name="analyze_data", compute="MEDIUM", ram="512MB", timeout="30s", max_retries=1)
def analyze_data(dataset: list) -> dict:
"""Обработка данных в изолированном окружении с контролем ресурсов."""
# Ваш код безопасно выполняется в песочнице Wasm
return {"processed": len(dataset), "status": "complete"}
Используйте функцию-обёртку task() с полным доступом к экосистеме 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 => {
// Ваш код безопасно выполняется в песочнице Wasm
return { processed: dataset.length, status: "complete" };
});
[!NOTE] Среда выполнения требует задачу с именем
"main"в качестве точки входа. Python создаст её автоматически, если она не определена, но рекомендуется задавать её явно.
При запуске capsule run main.py (или main.ts) ваш код компилируется в модуль WebAssembly и выполняется в изолированных песочницах.
Каждая задача работает в собственной песочнице с настраиваемыми ограничениями ресурсов. Это гарантирует, что сбои изолированы и не влияют на другие части рабочего процесса. Хост-система контролирует каждый аспект выполнения: от распределения CPU (через топливный механизм Wasm) до ограничений памяти и времени выполнения.
pip install capsule-run
Создайте hello.py:
from capsule import task
@task(name="main", compute="LOW", ram="64MB")
def main() -> str:
return "Hello from Capsule!"
Запустите:
capsule run hello.py
npm install -g @capsule-run/cli
npm install @capsule-run/sdk
Создайте hello.ts:
import { task } from "@capsule-run/sdk";
export const main = task({
name: "main",
compute: "LOW",
ram: "64MB"
}, (): string => {
return "Hello from Capsule!";
});
Запустите:
capsule run hello.ts
[!TIP] Добавьте
--verbose, чтобы видеть детали выполнения задач в реальном времени.
Функция run() позволяет выполнять задачи программно, не используя CLI. Аргументы args автоматически передаются как параметры задаче main.
from capsule import run
result = await run(
file="./sandbox.py",
args=["code to execute"]
)
Создайте sandbox.py:
from capsule import task
@task(name="main", compute="LOW", ram="64MB")
def main(code: str) -> str:
return eval(code)
[!IMPORTANT] Для использования функций запуска в TypeScript требуется
@capsule-run/cliв зависимостях.
import { run } from '@capsule-run/sdk/runner';
const result = await run({
file: './sandbox.ts',
args: ['code to execute']
});
Создайте 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] Если вам нужно готовое решение, посмотрите адаптер Python или адаптер TypeScript.
Настройте свои задачи с помощью этих параметров:
Capsule управляет использованием CPU через топливный механизм WebAssembly, который дозирует выполнение инструкций. Уровень compute определяет, сколько топлива получает задача.
compute="1000000") для точного контроля пределов выполнения.Каждая задача возвращает структурированный JSON-конверт, содержащий как результат, так и метаданные выполнения:
{
"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": [{...}]
}
}
Поля ответа:
success — логическое значение, указывающее на успешное выполнение задачиresult — фактическое возвращаемое значение задачи (json, строка, null при ошибке и т.д.)error — детали ошибки, если задача завершилась сбоем ({ error_type: string, message: string })execution — метрики производительности:
task_name — имя выполненной задачиduration_ms — время выполнения в миллисекундахretries — количество произошедших повторовfuel_consumed — использованные ресурсы CPU (см. Уровни вычислительных ресурсов)ram_used — пиковое использование памяти в байтахhost_requests — список запросов к хосту, сделанных задачейЗадачи могут выполнять HTTP-запросы к доменам, указанным в allowed_hosts. По умолчанию исходящие запросы запрещены ([]). Укажите белый список доменов для предоставления доступа или используйте ["*"], чтобы разрешить все домены.
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();
});
Задачи могут читать и записывать файлы в каталогах, указанных в allowed_files. Любые попытки доступа к файлам за пределами этих каталогов невозможны.
[!NOTE]
allowed_filesподдерживает только пути к каталогам, не к отдельным файлам.
Каждая запись может быть простым путём (по умолчанию чтение-запись) или структурированным объектом с явным mode:
"read-only" (или "ro")"read-write" (или "rw")Стандартные файловые операции Python работают обычным образом. Используйте open(), os, pathlib или любую библиотеку для работы с файлами.
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
Простые строки также принимаются: allowed_files=["./output"] по умолчанию даёт режим чтения-записи.
Доступны стандартные встроенные модули Node.js. Используйте модуль 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;
});
Простые строки также принимаются: allowedFiles: ["./output"] по умолчанию даёт режим чтения-записи.
--mount)Флаг --mount (CLI) или параметр mounts (SDK) монтирует хост-каталог в песочницу под псевдонимом. Монтирования распространяются на подзадачи и добавляют доступ к новым путям; они не меняют режим доступа к уже объявленным путям в allowed_files.
Формат: HOST_PATH[::GUEST_PATH][:ro|:rw]
CLI
# Монтировать сессионную рабочую область и предоставить её как "workspace" внутри задачи
capsule run main.py --mount sessions/abc123_workspace::workspace
# Несколько каталогов
capsule run main.py \
--mount sessions/abc123_workspace::workspace \
--mount sessions/bce456_workspace::workspace:ro
Python SDK
from capsule import run
result = await run(
file="main.py",
mounts=[".capsule/sessions/abc123_workspace::workspace"],
)
TypeScript / JavaScript SDK
import { run } from "@capsule-run/sdk";
const result = await run({
file: "main.py",
mounts: [".capsule/sessions/abc123_workspace::workspace"],
});
Внутри задачи каталог доступен по гостевому пути:
# задача видит его как "workspace/", а не полный путь сессии
with open("workspace/output.txt", "w") as f:
f.write("done")
[!NOTE] Пути
--mountдолжны быть относительными и не должны выходить за пределы корня проекта. Абсолютные пути отклоняются.
Задачи могут получать доступ к переменным окружения для чтения конфигурации, ключей API или других настроек времени выполнения.
Используйте стандартный os.environ для доступа к переменным окружения:
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}
Используйте стандартный process.env для доступа к переменным окружения:
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 };
});
Вы можете создать файл capsule.toml в корне проекта, чтобы задать параметры по умолчанию для всех задач и определить метаданные рабочего процесса:
# capsule.toml
[workflow]
name = "My Workflow"
version = "1.0.0"
entrypoint = "src/main.py" # Файл по умолчанию при запуске `capsule run`
[tasks]
default_compute = "MEDIUM"
default_ram = "256MB"
default_timeout = "30s"
default_max_retries = 2
Если определена точка входа, можно просто запустить:
capsule run
Параметры на уровне задачи всегда переопределяют эти значения по умолчанию.
При запуске кода Capsule создаёт папку .capsule в корне вашего проекта. Это кеш сборки. В нём хранятся скомпилированные артефакты, поэтому последующие запуски выполняются быстро (от секунд до нескольких миллисекунд).
[!TIP]
.capsuleследует добавить в.gitignore. Кеш специфичен для вашего окружения и будет автоматически восстановлен.
.capsule/
├── wasm/
│ ├── main_a1b2c3d4.wasm # Скомпилированный модуль WebAssembly
│ └── main_a1b2c3d4.cwasm # Нативный предварительно скомпилированный кеш
├── wit/ # Определения интерфейсов
└── trace.db # Логи выполнения
Используйте capsule build для предварительной компиляции, чтобы избежать затрат на компиляцию при первом запуске:
capsule build main.ts # или `main.py`
Запуск исходного кода напрямую (например, .py или .ts) оценивает и компилирует файл во время выполнения. Хотя это удобно для разработки, этап компиляции добавляет несколько секунд задержки при первом вызове. В случаях, где критична задержка менее секунды, следует собирать задачи заранее.
# Создаёт оптимизированный файл hello.wasm
capsule build hello.py --export
# Запустить скомпилированный артефакт напрямую
capsule exec hello.wasm
[!NOTE] Или из вашего существующего кода:
from capsule import run result = await run( file="./hello.wasm", # или `hello.py` args=[] ) print(f"Задача завершена: {result['result']}")
Выполнение файла .wasm полностью обходит компилятор, сокращая время инициализации до миллисекунд, при этом за кулисами используется нативно оптимизированный формат (.cwasm).
[!NOTE] TypeScript/JavaScript имеет более широкую совместимость, чем Python, поскольку не зависит от нативных привязок.
Python: Большинство стандартных библиотек Python работают отлично. Пакеты, использующие C-расширения, требуют скомпилированного колеса wasm32-wasi. Многие популярные пакеты (например, numpy, pandas) пока не предоставляют такого колеса, поэтому они не будут работать внутри песочницы. Однако ваш хост-код (использующий run()) имеет доступ ко всей экосистеме Python, включая любые пакеты pip и нативные расширения. См. использование из кода
TypeScript/JavaScript: Работают пакеты npm и модули ES. Доступны стандартные встроенные модули Node.js. Если у вас возникли проблемы с каким-либо встроенным модулем, не стесняйтесь открыть issue.
Вклад приветствуется!
Необходимо: Rust (последняя стабильная версия), Python 3.13+, Node.js 22+
git clone https://github.com/capsulerun/capsule.git
cd capsule
# Сборка и установка CLI
cargo install --path crates/capsule-cli
# Python SDK (редактируемая установка)
pip install -e crates/capsule-sdk/python
# TypeScript SDK (линковка для локальной разработки)
cd crates/capsule-sdk/javascript
npm install && npm run build && npm link
# Затем в вашем проекте: npm link @capsule-run/sdk
git checkout -b feature/amazing-featurecargo test (только если изменяете crates/capsule-cli или crates/capsule-core)Нужна помощь? Создайте issue
Capsule построен на основе этих проектов с открытым исходным кодом:
Этот проект лицензирован по Apache License 2.0 — см. файл LICENSE для подробностей.
| Параметр | Описание | Тип | По умолчанию | Пример |
|---|
name | Идентификатор задачи | str | имя функции (Python) / обязательный (TS) | "process_data" |
compute | Уровень выделения CPU: "LOW", "MEDIUM" или "HIGH" | str | "MEDIUM" | "HIGH" |
ram | Лимит памяти для задачи | str | без ограничений | "512MB", "2GB" |
timeout | Максимальное время выполнения | str | без ограничений | "30s", "5m", "1h" |
max_retries / maxRetries | Количество попыток повтора при сбое | int | 0 | 3 |
allowed_files / allowedFiles | Папки, доступные в песочнице (с опциональным режимом доступа) | list | [] | ["./data"], [{"path": "./data", "mode": "ro"}] |
allowed_hosts / allowedHosts | Домены, доступные в песочнице | list | [] | ["api.openai.com", "*.anthropic.com"] |
env_variables / envVariables | Переменные окружения, доступные в песочнице | list | [] | ["API_KEY"] |
| Часть | Обязательно | Описание |
|---|
HOST_PATH | да | Путь на хост-машине (относительно cwd, должен оставаться внутри корня проекта) |
::GUEST_PATH | нет | Путь, который видит задача внутри песочницы. По умолчанию равен HOST_PATH |
:ro / :rw | нет | Режим доступа. По умолчанию чтение-запись |