-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocs.json
More file actions
44 lines (44 loc) · 42.1 KB
/
docs.json
File metadata and controls
44 lines (44 loc) · 42.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
{
"project": "fabric",
"version": "0.2.0",
"generatedBy": "Dewey 0.3.1",
"generatedAt": "2026-03-09T13:21:21.688Z",
"sections": [
{
"id": "overview",
"title": "Overview",
"description": "Lightweight sandboxes for agentic workloads. One interface, any runtime.",
"content": "# Overview\n\nFabric is a lightweight sandbox toolkit that lets you run agentic workloads across local containers and cloud runtimes through a unified interface. Start local, scale anywhere.\n\n## What Fabric Does\n\nFabric provides a single `Sandbox` interface that works identically across four execution environments:\n\n- **Local** — Apple `container` CLI on your Mac. No Docker, no API keys, free.\n- **Daytona** — Enterprise cloud sandboxes with network policies.\n- **E2B** — Sub-200ms code interpreter sandboxes.\n- **exe.dev** — Persistent VMs with SSH and pre-installed agents.\n\nEvery sandbox supports the same operations: `exec()`, `runCode()`, `writeFile()`, `readFile()`, `snapshot()`, `restore()`, and `delegate()`.\n\n## Key Concepts\n\n**Sandbox**: An isolated execution environment. Create one, run code in it, tear it down. The interface is identical regardless of where it runs.\n\n**`.fabric` Config**: Per-project configuration file. Defines image, mounts, env vars, and composable profiles (minimal, node, python, bun).\n\n**Snapshot**: Serializable capture of workspace state (files + metadata). Enables checkpointing and cross-runtime handoff.\n\n**Handoff**: Move work between runtimes without losing state. Snapshot locally, restore in the cloud.\n\n**Runtime**: The execution backend. Each runtime adapter implements the same interface but talks to a different infrastructure provider.\n\n## Architecture\n\n```\npackages/\n├── cli/ # CLI — fabric setup, shell, exec, init\n├── core/ # Sandbox, Runtime, Task interfaces\n├── runtime-local/ # Apple container CLI + subprocess\n├── runtime-daytona/ # Daytona cloud sandbox adapter\n├── runtime-e2b/ # E2B cloud sandbox adapter\n├── runtime-exe/ # exe.dev persistent VM adapter\n├── server/ # HTTP API\n└── landing/ # Website and docs\n```\n\nThe local container runtime uses Apple's `container` CLI (Virtualization.framework) to run lightweight Linux VMs. No Docker daemon, no Swift binary — just a Homebrew package.\n\n## Quick Example\n\n```typescript\nimport { LocalContainerSandboxFactory } from \"@fabric/runtime-local\"\n\nconst factory = new LocalContainerSandboxFactory()\nconst sandbox = await factory.create({ image: \"alpine:latest\" })\n\nconst result = await sandbox.exec(\"uname -a\")\nconsole.log(result.stdout) // Linux ... aarch64\n\nconst snapshot = await sandbox.snapshot()\nawait sandbox.stop()\n\n// Restore in cloud\nconst cloudSandbox = await e2bFactory.create({})\nawait cloudSandbox.restore(snapshot)\n```\n\n## Next Steps\n\n- [Getting Started](./getting-started.md) — Install Fabric and create your first sandbox\n- [Local Container Runtime](./local-container.md) — Apple container CLI deep dive\n- [Daytona](./daytona.md) — Enterprise cloud sandboxes\n- [E2B](./e2b.md) — Fast code interpreter sandboxes\n- [exe.dev](./exe.md) — Persistent VMs with SSH"
},
{
"id": "getting-started",
"title": "Getting Started",
"description": "Get a sandbox running in under 2 minutes. Local containers, cloud providers, one interface.",
"content": "# Getting Started\n\nFabric is a lightweight sandbox toolkit for running code and AI agents across local and cloud environments. Start local, scale to cloud, preserve context everywhere.\n\n## Quick Start\n\nGet a sandbox running in under 2 minutes:\n\n```bash\nnpm install -g fabric-ai\n\nexport DAYTONA_API_KEY=your_key\n\nfabric create --provider daytona\nfabric exec \"echo 'Hello from Fabric!'\"\n```\n\nOr use the SDK directly:\n\n```typescript\nimport { DaytonaSandboxFactory } from \"fabric-ai-daytona\"\n\nconst factory = new DaytonaSandboxFactory({\n apiKey: process.env.DAYTONA_API_KEY,\n defaultLanguage: \"typescript\",\n})\n\nconst sandbox = await factory.create({})\nconst result = await sandbox.exec(\"echo 'Hello!'\")\nconsole.log(result.stdout)\nawait sandbox.stop()\n```\n\n## Installation\n\n### CLI (Recommended)\n\n```bash\ngit clone https://github.com/arach/fabric.git && cd fabric\nbun run packages/cli/src/cli.ts setup\n```\n\n`fabric setup` handles everything: installs the Apple `container` CLI, downloads the Linux kernel, and pulls base images. Requires macOS 26+ on Apple Silicon.\n\n### SDK Packages\n\n```bash\nnpm install fabric-ai-core\nnpm install fabric-ai-daytona\nnpm install fabric-ai-e2b\nnpm install fabric-ai-exe\n```\n\n## Provider Setup\n\nFabric supports multiple sandbox providers. Each has different strengths — choose based on your workflow.\n\n| Provider | Startup | Auth | Best for |\n|----------|---------|------|----------|\n| **Local** | ~1s | None | Development, offline, free |\n| **Daytona** | ~2-3s | API Key | Enterprise, TypeScript, network policies |\n| **E2B** | <200ms | API Key | Data science, Python, Jupyter |\n| **exe.dev** | ~2s | SSH Key | Persistent VMs, full root access |\n\n### Local (default — no API key needed)\n\n```bash\nfabric create --provider local\nfabric exec \"echo Hello from a local container!\"\n```\n\n### Cloud providers\n\n```bash\n# Daytona\nexport DAYTONA_API_KEY=your_key # from app.daytona.io\nfabric create --provider daytona\n\n# E2B\nexport E2B_API_KEY=your_key # from e2b.dev/dashboard\nfabric create --provider e2b\n\n# exe.dev\nssh exe.dev # registers your SSH key\nfabric create --provider exe\n```\n\n## Your First Sandbox\n\n```typescript\nimport { DaytonaSandboxFactory } from \"fabric-ai-daytona\"\n\nasync function main() {\n const factory = new DaytonaSandboxFactory({\n apiKey: process.env.DAYTONA_API_KEY,\n defaultLanguage: \"typescript\",\n })\n\n const sandbox = await factory.create({})\n console.log(`Sandbox ID: ${sandbox.id}`)\n\n const result = await sandbox.exec(\"echo 'Hello, Fabric!'\")\n console.log(result.stdout)\n\n await sandbox.stop()\n}\n\nmain()\n```\n\nOr locally, no API key needed:\n\n```typescript\nimport { LocalContainerSandboxFactory } from \"@fabric/runtime-local\"\n\nconst factory = new LocalContainerSandboxFactory()\nconst sandbox = await factory.create({ image: \"alpine:latest\" })\n\nconst result = await sandbox.exec(\"uname -a\")\nconsole.log(result.stdout) // Linux ... aarch64\n\nawait sandbox.stop()\n```\n\n## Running Code\n\n### Shell Commands\n\n```typescript\nconst result = await sandbox.exec(\"ls -la /workspace\")\nconsole.log(result.stdout)\n\nif (result.exitCode !== 0) {\n console.error(result.stderr)\n}\n```\n\nOr from the CLI:\n\n```bash\nfabric exec \"ls -la /workspace\"\nfabric exec \"node --version\"\nfabric exec \"python3 -c 'print(2 + 2)'\"\n```\n\n### Code Execution\n\n```typescript\nconst result = await sandbox.runCode(`\n const greeting = \"Hello from TypeScript!\"\n console.log(greeting)\n`, \"typescript\")\nconsole.log(result.output)\n```\n\nFrom the CLI:\n\n```bash\nfabric run --language typescript \"console.log('Hello!')\"\nfabric run --language python \"print(2 + 2)\"\n```\n\n### Interactive Shell\n\nDrop into a live Linux environment:\n\n```bash\nfabric shell # Ubuntu (default)\nfabric shell --image alpine # Alpine\nfabric shell --image omarchy # Arch Linux\nfabric shell --image python # Python 3.12\nfabric shell --image nginx:latest # Any OCI image\n```\n\nExit with `exit` or Ctrl+D — the container is cleaned up automatically.\n\n## File Operations\n\n### Writing Files\n\n```typescript\nawait sandbox.writeFile(\"/workspace/hello.ts\", `\nexport function greet(name: string): string {\n return \\`Hello, \\${name}!\\`\n}\n`)\n```\n\n### Reading Files\n\n```typescript\nconst content = await sandbox.readFile(\"/workspace/hello.ts\")\nconsole.log(content)\n\nconst files = await sandbox.listFiles(\"/workspace\")\nconsole.log(files) // [\"hello.ts\", ...]\n```\n\n## Handoffs\n\nMove work between runtimes without losing state. Snapshot locally, restore in the cloud:\n\n```typescript\nimport { LocalContainerSandboxFactory } from \"@fabric/runtime-local\"\nimport { E2BSandboxFactory } from \"fabric-ai-e2b\"\n\n// Start local\nconst localFactory = new LocalContainerSandboxFactory()\nconst local = await localFactory.create({ image: \"node:22\" })\nawait local.exec(\"npm install\")\nawait local.writeFile(\"/workspace/data.json\", '{\"key\": \"value\"}')\n\n// Snapshot and move to cloud\nconst snapshot = await local.snapshot()\nawait local.stop()\n\nconst cloudFactory = new E2BSandboxFactory(process.env.E2B_API_KEY)\nconst cloud = await cloudFactory.create({})\nawait cloud.restore(snapshot)\n\n// Files and state are preserved\nconst result = await cloud.exec(\"cat /workspace/data.json\")\nconsole.log(result.stdout) // {\"key\": \"value\"}\n\nawait cloud.stop()\n```\n\n## Project Configuration\n\nAdd a `.fabric` file to your project root to configure sandbox defaults:\n\n```bash\nfabric init # Create default .fabric config\nfabric init node # Create config with node profile\n```\n\nExample `.fabric` file:\n\n```bash\n# Fabric sandbox config\nprofile: node\n\n# Mount host directories into the container\nmount: ./src:/workspace/src:ro\nmount: ./data:/workspace/data\n\n# Environment variables\nenv: NODE_ENV=development\n\n# Override defaults\n# image: node:22\n# provider: local\n# network: true\n```\n\n### Profiles\n\nPresets that configure image and default mounts for common workflows:\n\n| Profile | Image | Default Mounts |\n|---------|-------|----------------|\n| `minimal` | alpine:latest | `.:/workspace:ro` |\n| `node` | node:22 | `./src`, `./package.json` |\n| `python` | python:3.12 | `./src`, `./requirements.txt` |\n| `bun` | oven/bun:latest | `./src`, `./package.json` |\n\nProfiles are composable — your `.fabric` config extends the profile. Additional mounts and env vars are merged on top of profile defaults. The `.fabric` file is discovered by walking up from the current directory, so it works from any subdirectory.\n\n## Running Claude Code\n\n[Claude Code](https://docs.anthropic.com/en/docs/claude-code) is Anthropic's CLI for AI-assisted coding. Run it inside Fabric sandboxes for autonomous development tasks:\n\n```typescript\nimport { Sandbox } from \"@e2b/code-interpreter\"\n\nconst sandbox = await Sandbox.create(\"anthropic-claude-code\", {\n apiKey: process.env.E2B_API_KEY,\n envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },\n})\n\nconst result = await sandbox.commands.run(\n `echo 'Create a fibonacci function' | claude -p`,\n { timeoutMs: 120_000 }\n)\n\nconsole.log(result.stdout)\nawait sandbox.kill()\n```\n\nOr locally:\n\n```bash\nfabric create --provider local\nfabric exec \"npm install -g @anthropic-ai/claude-code\"\nfabric exec \"ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY echo 'Build a REST API' | claude -p\"\n```\n\n## Error Handling\n\nAlways clean up sandboxes, even when errors occur:\n\n```typescript\nconst sandbox = await factory.create({})\ntry {\n await sandbox.exec(\"some-command\")\n await sandbox.exec(\"another-command\")\n} finally {\n await sandbox.stop() // Always runs\n}\n```\n\nFor transient failures, retry with backoff:\n\n```typescript\nasync function execWithRetry(sandbox, cmd, maxAttempts = 3) {\n for (let i = 0; i < maxAttempts; i++) {\n try {\n return await sandbox.exec(cmd)\n } catch (e) {\n if (i === maxAttempts - 1) throw e\n await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i)))\n }\n }\n}\n```\n\n## Available Images\n\n| Name | Image | Description |\n|------|-------|-------------|\n| ubuntu | ubuntu:latest | Ubuntu Linux (default) |\n| alpine | alpine:latest | Alpine Linux (minimal) |\n| omarchy / arch | lopsided/archlinux | Arch Linux (arm64) |\n| debian | debian:latest | Debian |\n| fedora | fedora:latest | Fedora |\n| bun | oven/bun:latest | Bun runtime |\n| node | node:22 | Node.js 22 |\n| python | python:3.12 | Python 3.12 |\n\nAny OCI-compatible arm64 image works: `fabric shell --image myregistry/myimage:tag`\n\n## CLI Reference\n\n| Command | Description |\n|---------|-------------|\n| `fabric setup` | Install everything (container CLI, kernel, images) |\n| `fabric init` | Create a `.fabric` config for this project |\n| `fabric shell` | Interactive Linux shell |\n| `fabric create` | Create a sandbox |\n| `fabric exec` | Run a command in a sandbox |\n| `fabric run` | Execute code in a sandbox |\n| `fabric list` | List active sandboxes |\n| `fabric stop` | Stop a sandbox |\n\n## Pricing\n\n**Fabric is free.** You bring your own API keys for cloud providers.\n\n| Component | Cost |\n|-----------|------|\n| Fabric CLI & SDK | Free |\n| Local containers | Free (runs on your Mac) |\n| Daytona sandboxes | [Daytona pricing](https://daytona.io/pricing) |\n| E2B sandboxes | [E2B pricing](https://e2b.dev/pricing) |\n| exe.dev VMs | [exe.dev pricing](https://exe.dev) |\n\nThe core framework will always be free and open source.\n\n## Next Steps\n\n- [Local Containers](./local-container.md) — How the container runtime works\n- [Architecture](./architecture.md) — Project structure and runtime adapter pattern\n- [API Reference](./api.md) — TypeScript interfaces\n- [Project Config](./skill.md) — `.fabric` file reference and profiles\n- [Daytona](./daytona.md) — Enterprise features and network policies\n- [E2B](./e2b.md) — Jupyter integration and Claude Code template\n- [exe.dev](./exe.md) — Persistent VMs and pre-installed agents"
},
{
"id": "local-container",
"title": "Local Container Runtime",
"description": "Run isolated Linux containers on macOS using Apple's container CLI — no Docker, no API keys.",
"content": "# Local Container Runtime\n\nThe local container runtime runs isolated Linux containers on your Mac using Apple's `container` CLI and Virtualization.framework. No Docker required — just native Apple Silicon performance.\n\n## Requirements\n\n- macOS 26+ (Tahoe)\n- Apple Silicon (M1 or later)\n- Apple `container` CLI (installed by `fabric setup`)\n\n## Setup\n\n```bash\nfabric setup\n```\n\nThis installs everything: the Apple `container` CLI (via Homebrew), the Linux kernel, and base images.\n\n## Usage\n\n### Interactive Shell\n\n```bash\nfabric shell # Ubuntu (default)\nfabric shell --image alpine # Alpine\nfabric shell --image omarchy # Arch Linux\nfabric shell --image python # Python 3.12\nfabric shell --image nginx:latest # Any OCI image\n```\n\n### Programmatic (SDK)\n\n```typescript\nimport { LocalContainerSandboxFactory } from \"@fabric/runtime-local\"\n\nconst factory = new LocalContainerSandboxFactory()\nconst sandbox = await factory.create({ image: \"ubuntu:latest\" })\n\nconst result = await sandbox.exec(\"uname -a\")\nconsole.log(result.stdout) // Linux ... aarch64\n\nawait sandbox.writeFile(\"hello.ts\", \"console.log('hi')\")\nawait sandbox.exec(\"bun hello.ts\")\n\nawait sandbox.stop()\n```\n\n## Architecture\n\n```\n┌──────────────────────────────────────┐\n│ Your Mac (Host) │\n├──────────────────────────────────────┤\n│ Apple container CLI │\n│ └─ Virtualization.framework │\n│ └─ Lightweight Linux VM │\n│ └─ Your Container (OCI image) │\n│ Mounted: ~/project ↔ /workspace\n└──────────────────────────────────────┘\n```\n\nThe `container` CLI manages everything through a privileged system daemon — networking, image pulls, VM lifecycle. No entitlements or code signing needed.\n\n### TypeScript Classes\n\n| Class | Description |\n|-------|-------------|\n| `SubprocessRuntime` | Direct host execution, no isolation |\n| `ContainerRuntime` | Shells out to `container` CLI for one-shot commands |\n| `LocalContainerSandbox` | Long-running container with `exec`, `writeFile`, `snapshot` |\n| `LocalContainerSandboxFactory` | Creates and manages sandboxes |\n\n## Container Images\n\n| Name | Image | Description |\n|------|-------|-------------|\n| ubuntu | ubuntu:latest | Ubuntu 24.04 LTS |\n| omarchy / arch | lopsided/archlinux | Arch Linux (arm64) |\n| alpine | alpine:latest | Alpine Linux (minimal) |\n| debian | debian:latest | Debian |\n| fedora | fedora:latest | Fedora |\n| bun | oven/bun:latest | Bun runtime |\n| node | node:22 | Node.js 22 |\n| python | python:3.12 | Python 3.12 |\n\nAny OCI-compatible arm64 image works: `fabric shell --image myregistry/myimage:tag`\n\n## File Operations\n\nFiles are shared via mounted volumes. The workspace directory on the host is mounted to `/workspace` inside the container.\n\n```typescript\nawait sandbox.writeFile(\"hello.ts\", `\nexport function greet(name: string) {\n return \\`Hello, \\${name}!\\`\n}\n`)\n\nconst content = await sandbox.readFile(\"hello.ts\")\nconst files = await sandbox.listFiles(\".\")\n```\n\n## Snapshots and Handoff\n\nCapture and restore workspace state for handoff between runtimes:\n\n```typescript\n// Capture snapshot\nconst snapshot = await sandbox.snapshot()\nawait sandbox.stop()\n\n// Restore to cloud sandbox\nconst cloudSandbox = await e2bFactory.create({})\nawait cloudSandbox.restore(snapshot)\n```\n\n## Known Issues\n\n- **Arch Linux**: The official `archlinux:latest` image has broken platform metadata for the Apple container CLI. Fabric uses `lopsided/archlinux` instead.\n- **pacman sandboxing**: Newer pacman versions use Landlock (kernel sandboxing) which the VM kernel doesn't support. Fix: `echo \"DisableSandbox\" >> /etc/pacman.conf`\n\n## Comparison with Cloud Providers\n\n| Feature | Local Container | Daytona | E2B | exe.dev |\n|---------|----------------|---------|-----|---------|\n| Startup | ~1s | ~2-3s | <200ms | ~5-10s |\n| API Key | None | Required | Required | SSH key |\n| Internet | Host network | Tier-based | Full | Full |\n| Cost | Free | Pay per use | Pay per use | Pay per use |\n| Platform | macOS + Apple Silicon | Any | Any | Any |\n| Isolation | VM (Virtualization.framework) | Container | Container | VM |"
},
{
"id": "daytona",
"title": "Daytona Runtime",
"description": "Enterprise cloud sandboxes with secure network policies, multi-language support, and tier-based access control.",
"content": "# Daytona Runtime\n\nDaytona provides enterprise-grade cloud sandboxes for running Claude Code agents with secure network policies and multi-language support.\n\n## Installation\n\n```bash\nnpm install @fabric/core @fabric/runtime-daytona\n```\n\n## Configuration\n\nSet these environment variables:\n\n```bash\n# Required\nDAYTONA_API_KEY=your_daytona_api_key\n\n# Required for Claude Code\nANTHROPIC_API_KEY=your_anthropic_api_key\n```\n\nGet your Daytona API key from [app.daytona.io](https://app.daytona.io).\n\n## Basic Usage\n\n```typescript\nimport { DaytonaSandboxFactory } from \"@fabric/runtime-daytona\"\n\nconst factory = new DaytonaSandboxFactory({\n apiKey: process.env.DAYTONA_API_KEY!,\n defaultLanguage: \"typescript\"\n})\n\n// Create a sandbox\nconst sandbox = await factory.create({})\nconsole.log(`Sandbox ID: ${sandbox.id}`)\n\n// Execute shell commands\nconst result = await sandbox.exec(\"ls -la\")\nconsole.log(result.stdout)\n\n// Run TypeScript code\nconst codeResult = await sandbox.runCode(`\n const greeting = \"Hello from Daytona!\"\n console.log(greeting)\n console.log(\"Current time:\", new Date().toISOString())\n`)\nconsole.log(codeResult.output)\n\n// Clean up\nawait sandbox.stop()\n```\n\n## Language Support\n\nDaytona supports multiple languages out of the box:\n\n```typescript\n// TypeScript (default)\nconst tsSandbox = await factory.create({ language: \"typescript\" })\n\n// Python\nconst pySandbox = await factory.create({ language: \"python\" })\n\n// Go\nconst goSandbox = await factory.create({ language: \"go\" })\n\n// Rust\nconst rustSandbox = await factory.create({ language: \"rust\" })\n\n// JavaScript\nconst jsSandbox = await factory.create({ language: \"javascript\" })\n```\n\n## File Operations\n\n```typescript\n// Write a file\nawait sandbox.writeFile(\"/home/daytona/hello.ts\", `\nexport function greet(name: string) {\n return \\`Hello, \\${name}!\\`\n}\n`)\n\n// Read a file\nconst content = await sandbox.readFile(\"/home/daytona/hello.ts\")\nconsole.log(content)\n\n// List files in a directory\nconst files = await sandbox.listFiles(\"/home/daytona\")\nconsole.log(\"Files:\", files)\n```\n\n## Running Claude Code\n\nClaude Code works in Daytona with a direct Anthropic API key:\n\n```typescript\nimport { Daytona } from \"@daytonaio/sdk\"\n\nconst daytona = new Daytona({ apiKey: process.env.DAYTONA_API_KEY })\n\nconst sandbox = await daytona.create({ language: \"typescript\" })\n\n// Install Claude Code\nawait sandbox.process.executeCommand(\n \"npm install -g @anthropic-ai/claude-code\",\n undefined, undefined, 120\n)\n\n// Run Claude Code\nconst mission = \"Create a fibonacci function in TypeScript\"\nconst result = await sandbox.process.executeCommand(\n `export ANTHROPIC_API_KEY=${process.env.ANTHROPIC_API_KEY} && echo '${mission}' | claude -p --dangerously-skip-permissions`,\n undefined, undefined, 180\n)\n\nconsole.log(result.result)\n\nawait sandbox.delete()\n```\n\n## Network Access\n\nDaytona uses tier-based network policies for security.\n\n### Essential Services (All Tiers)\n\nThese services are always accessible:\n\n**AI APIs:**\n- api.anthropic.com\n- api.openai.com\n- api.perplexity.ai\n- api.deepseek.com\n- api.groq.com\n- openrouter.ai\n\n**Package Registries:**\n- registry.npmjs.org\n- pypi.org\n- repo1.maven.org\n\n**Git Hosting:**\n- github.com\n- gitlab.com\n- bitbucket.org\n\n**Container Registries:**\n- docker.io\n- gcr.io\n- ghcr.io\n\n### Network Restrictions\n\n| Tier | Network Access |\n|------|----------------|\n| Tier 1/2 | Essential services only |\n| Tier 3/4 | Full internet + custom allowlist |\n\n### Custom Allowlist (Tier 3/4)\n\n```typescript\nconst sandbox = await daytona.create({\n networkAllowList: \"208.80.154.232/32,199.16.156.103/32\"\n})\n```\n\n## Snapshots\n\nCapture and restore sandbox state:\n\n```typescript\n// Capture snapshot\nconst snapshot = await sandbox.snapshot()\nconsole.log(`Snapshot ID: ${snapshot.id}`)\nconsole.log(`Files captured: ${snapshot.files.length}`)\n\n// Save snapshot to file\nimport { writeFileSync } from \"fs\"\nwriteFileSync(\"snapshot.json\", JSON.stringify(snapshot, null, 2))\n\n// Later, restore from snapshot\nconst newSandbox = await factory.create({})\nawait newSandbox.restore(snapshot)\n```\n\n## Environment Variables\n\nPass environment variables to the sandbox:\n\n```typescript\nconst sandbox = await factory.create({\n envVars: {\n NODE_ENV: \"production\",\n DEBUG: \"true\",\n API_URL: \"https://api.example.com\"\n }\n})\n```\n\n## Error Handling\n\n```typescript\ntry {\n const sandbox = await factory.create({})\n\n const result = await sandbox.exec(\"some-command\")\n\n if (result.exitCode !== 0) {\n console.error(\"Command failed:\", result.stderr)\n }\n\n} catch (error) {\n if (error.message.includes(\"timeout\")) {\n console.error(\"Operation timed out\")\n } else {\n console.error(\"Error:\", error.message)\n }\n}\n```\n\n## Sandbox Lifecycle\n\n```typescript\nconst sandbox = await factory.create({})\n\nconsole.log(sandbox.status) // \"running\"\n\n// Do work...\n\nawait sandbox.stop()\n\nconsole.log(sandbox.status) // \"stopped\"\n```\n\n## Best Practices\n\n1. **Always clean up sandboxes** - Call `sandbox.stop()` or `sandbox.delete()` when done\n\n2. **Use appropriate timeouts** - Long-running commands need longer timeouts:\n ```typescript\n await sandbox.process.executeCommand(cmd, undefined, undefined, 300) // 5 min\n ```\n\n3. **Check exit codes** - Always verify command success:\n ```typescript\n const result = await sandbox.exec(\"npm install\")\n if (result.exitCode !== 0) {\n throw new Error(`Install failed: ${result.stderr}`)\n }\n ```\n\n4. **Use snapshots for reproducibility** - Capture state before risky operations\n\n5. **Set environment variables at creation** - More secure than inline exports\n\n## Comparison with E2B\n\n| Feature | Daytona | E2B |\n|---------|---------|-----|\n| Default Language | TypeScript | Python |\n| Multi-Language | TS, Python, Go, Rust, JS | Python, JS |\n| Network | Secure allowlist | Full access |\n| Claude Template | npm install | Pre-built |\n| Jupyter Kernel | No | Yes |\n| Best For | Enterprise, TypeScript | Data science |\n\n## Resources\n\n- [Daytona Documentation](https://www.daytona.io/docs)\n- [Daytona Dashboard](https://app.daytona.io)\n- [Network Limits](https://www.daytona.io/docs/en/network-limits/)\n- [SDK Reference](https://www.daytona.io/docs/en/typescript-sdk/)"
},
{
"id": "e2b",
"title": "E2B Runtime",
"description": "Fast-starting code interpreter sandboxes with sub-200ms startup, Jupyter support, and a pre-built Claude Code template.",
"content": "# E2B Runtime\n\nE2B provides fast-starting code interpreter sandboxes with full internet access and a pre-built Claude Code template.\n\n## Installation\n\n```bash\nnpm install @fabric/core @fabric/runtime-e2b\n```\n\n## Configuration\n\nSet these environment variables:\n\n```bash\n# Required\nE2B_API_KEY=your_e2b_api_key\n\n# Required for Claude Code\nANTHROPIC_API_KEY=your_anthropic_api_key\n```\n\nGet your E2B API key from [e2b.dev/dashboard](https://e2b.dev/dashboard).\n\n## Basic Usage\n\n```typescript\nimport { E2BSandboxFactory } from \"@fabric/runtime-e2b\"\n\nconst factory = new E2BSandboxFactory(process.env.E2B_API_KEY)\n\n// Create a sandbox\nconst sandbox = await factory.create({})\nconsole.log(`Sandbox ID: ${sandbox.id}`)\n\n// Execute shell commands\nconst result = await sandbox.exec(\"ls -la\")\nconsole.log(result.stdout)\n\n// Run Python code (E2B default)\nconst codeResult = await sandbox.runCode(`\nimport datetime\nprint(\"Hello from E2B!\")\nprint(\"Current time:\", datetime.datetime.now().isoformat())\n`)\nconsole.log(codeResult.output)\n\n// Clean up\nawait sandbox.stop()\n```\n\n## Claude Code Template\n\nE2B provides a pre-built template with Claude Code installed:\n\n```typescript\nimport { Sandbox } from \"@e2b/code-interpreter\"\n\n// Create sandbox with Claude Code template\nconst sandbox = await Sandbox.create(\"anthropic-claude-code\", {\n apiKey: process.env.E2B_API_KEY,\n envs: {\n ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY\n }\n})\n\nconsole.log(`Sandbox: ${sandbox.sandboxId}`)\n\n// Run Claude Code with a mission\nconst mission = \"Create a Python script that generates the first 20 Fibonacci numbers\"\n\nconst result = await sandbox.commands.run(\n `echo '${mission}' | claude -p --dangerously-skip-permissions`,\n { timeoutMs: 120_000 }\n)\n\nconsole.log(\"Claude's response:\")\nconsole.log(result.stdout)\n\n// Check what Claude created\nconst files = await sandbox.files.list(\"/home/user\")\nconsole.log(\"Files:\", files.map(f => f.name))\n\n// Read a file Claude created\nif (files.some(f => f.name === \"fibonacci.py\")) {\n const content = await sandbox.files.read(\"/home/user/fibonacci.py\")\n console.log(\"\\nfibonacci.py:\")\n console.log(content)\n}\n\nawait sandbox.kill()\n```\n\n## Code Execution\n\nE2B's code interpreter supports Python and JavaScript:\n\n```typescript\n// Python (default)\nconst pyResult = await sandbox.runCode(`\nimport math\nprint(\"Pi =\", math.pi)\nprint(\"E =\", math.e)\n`)\n\n// Using the E2B SDK directly for more control\nimport { Sandbox } from \"@e2b/code-interpreter\"\n\nconst sbx = await Sandbox.create({ apiKey: process.env.E2B_API_KEY })\n\nconst execution = await sbx.runCode(`\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nx = np.linspace(0, 10, 100)\nplt.plot(x, np.sin(x))\nplt.title(\"Sine Wave\")\nplt.savefig(\"sine.png\")\nprint(\"Chart saved!\")\n`)\n\nconsole.log(execution.logs.stdout)\n\n// Access generated charts\nfor (const result of execution.results) {\n if (result.png) {\n console.log(\"Chart generated:\", result.png.substring(0, 50) + \"...\")\n }\n}\n\nawait sbx.kill()\n```\n\n## File Operations\n\n```typescript\n// Write a file\nawait sandbox.writeFile(\"/home/user/hello.py\", `\ndef greet(name):\n return f\"Hello, {name}!\"\n\nif __name__ == \"__main__\":\n print(greet(\"World\"))\n`)\n\n// Read a file\nconst content = await sandbox.readFile(\"/home/user/hello.py\")\nconsole.log(content)\n\n// List files in a directory\nconst files = await sandbox.listFiles(\"/home/user\")\nconsole.log(\"Files:\", files)\n```\n\n## Using the E2B SDK Directly\n\nFor advanced use cases, use the E2B SDK directly:\n\n```typescript\nimport { Sandbox } from \"@e2b/code-interpreter\"\n\nconst sandbox = await Sandbox.create({\n apiKey: process.env.E2B_API_KEY,\n metadata: { project: \"my-project\" }\n})\n\n// Commands API\nconst cmdResult = await sandbox.commands.run(\"pip install requests\")\nconsole.log(\"Exit code:\", cmdResult.exitCode)\n\n// Files API\nawait sandbox.files.write(\"/home/user/data.json\", JSON.stringify({ key: \"value\" }))\nconst data = await sandbox.files.read(\"/home/user/data.json\")\n\n// Run code with full execution info\nconst execution = await sandbox.runCode(`\nx = [1, 2, 3, 4, 5]\nprint(\"Sum:\", sum(x))\nprint(\"Average:\", sum(x) / len(x))\n`)\n\nconsole.log(\"Stdout:\", execution.logs.stdout)\nconsole.log(\"Stderr:\", execution.logs.stderr)\n\nif (execution.error) {\n console.error(\"Error:\", execution.error)\n}\n\nawait sandbox.kill()\n```\n\n## Snapshots\n\nCapture and restore sandbox state:\n\n```typescript\n// Capture snapshot\nconst snapshot = await sandbox.snapshot()\nconsole.log(`Snapshot ID: ${snapshot.id}`)\nconsole.log(`Files captured: ${snapshot.files.length}`)\n\n// Save snapshot\nimport { writeFileSync } from \"fs\"\nwriteFileSync(\"e2b-snapshot.json\", JSON.stringify(snapshot, null, 2))\n\n// Later, restore from snapshot\nconst newSandbox = await factory.create({})\nawait newSandbox.restore(snapshot)\n```\n\n## Network Access\n\nE2B sandboxes have **full internet access** by default:\n\n```typescript\nconst result = await sandbox.exec(`\ncurl -s https://api.github.com/users/anthropics\n`)\nconsole.log(result.stdout)\n```\n\nThis makes E2B ideal for:\n- Fetching external data\n- Calling third-party APIs\n- Installing packages from any source\n- Web scraping\n\n## Timeouts\n\nE2B sandboxes have configurable timeouts:\n\n```typescript\nimport { Sandbox } from \"@e2b/code-interpreter\"\n\n// Set sandbox timeout (default: 5 minutes)\nconst sandbox = await Sandbox.create({\n apiKey: process.env.E2B_API_KEY,\n timeoutMs: 600_000 // 10 minutes\n})\n\n// Set command timeout\nconst result = await sandbox.commands.run(\"long-running-command\", {\n timeoutMs: 300_000 // 5 minutes\n})\n```\n\n## Error Handling\n\n```typescript\ntry {\n const sandbox = await factory.create({})\n\n const result = await sandbox.exec(\"python script.py\")\n\n if (result.exitCode !== 0) {\n console.error(\"Script failed:\")\n console.error(result.stderr)\n }\n\n} catch (error) {\n if (error.message.includes(\"timeout\")) {\n console.error(\"Operation timed out\")\n } else if (error.message.includes(\"E2B_API_KEY\")) {\n console.error(\"API key not configured\")\n } else {\n console.error(\"Error:\", error.message)\n }\n}\n```\n\n## Jupyter Kernel Support\n\nE2B sandboxes include Jupyter kernel support:\n\n```typescript\nimport { Sandbox } from \"@e2b/code-interpreter\"\n\nconst sandbox = await Sandbox.create({ apiKey: process.env.E2B_API_KEY })\n\n// Run code that maintains state\nawait sandbox.runCode(\"x = 10\")\nawait sandbox.runCode(\"y = 20\")\nconst result = await sandbox.runCode(\"print(x + y)\") // Outputs: 30\n\n// Generate visualizations\nconst chartExecution = await sandbox.runCode(`\nimport matplotlib.pyplot as plt\nplt.figure(figsize=(10, 6))\nplt.bar(['A', 'B', 'C'], [10, 20, 15])\nplt.title('Sample Chart')\nplt.show()\n`)\n\n// Charts are returned as base64 PNG\nfor (const result of chartExecution.results) {\n if (result.png) {\n // Save or display the chart\n const buffer = Buffer.from(result.png, 'base64')\n writeFileSync('chart.png', buffer)\n }\n}\n\nawait sandbox.kill()\n```\n\n## Best Practices\n\n1. **Use the Claude Code template** - For Claude agents, use `anthropic-claude-code` template:\n ```typescript\n const sandbox = await Sandbox.create(\"anthropic-claude-code\", { ... })\n ```\n\n2. **Set appropriate timeouts** - Claude Code operations need longer timeouts:\n ```typescript\n await sandbox.commands.run(cmd, { timeoutMs: 120_000 })\n ```\n\n3. **Clean up sandboxes** - Always call `kill()` or `stop()`:\n ```typescript\n try {\n // ... do work\n } finally {\n await sandbox.kill()\n }\n ```\n\n4. **Check execution errors** - The `runCode` method returns an error field:\n ```typescript\n const result = await sandbox.runCode(code)\n if (result.error) {\n console.error(\"Execution error:\", result.error)\n }\n ```\n\n5. **Use metadata** - Tag sandboxes for easier management:\n ```typescript\n await Sandbox.create({\n apiKey: process.env.E2B_API_KEY,\n metadata: { userId: \"123\", project: \"my-app\" }\n })\n ```\n\n## Comparison with Daytona\n\n| Feature | E2B | Daytona |\n|---------|-----|---------|\n| Default Language | Python | TypeScript |\n| Startup Time | <200ms | ~2-3s |\n| Network | Full access | Allowlist |\n| Claude Template | Pre-built | npm install |\n| Jupyter Kernel | Yes | No |\n| Multi-Language | Python, JS | TS, Python, Go, Rust, JS |\n| Best For | Data science | Enterprise |\n\n## Resources\n\n- [E2B Documentation](https://e2b.dev/docs)\n- [E2B Dashboard](https://e2b.dev/dashboard)\n- [Code Interpreter SDK](https://github.com/e2b-dev/code-interpreter)\n- [Claude Code Template](https://e2b.dev/docs/templates/claude-code)"
},
{
"id": "exe",
"title": "exe.dev Runtime",
"description": "Persistent VMs with SSH access, full root, and pre-installed Claude Code, Codex, and Shelley agents.",
"content": "# exe.dev Runtime\n\nexe.dev provides persistent VMs with SSH access for running Claude Code agents. Unlike ephemeral sandboxes, exe.dev VMs persist between sessions with full root access.\n\n## Installation\n\n```bash\nnpm install fabric-ai-core fabric-ai-exe\n```\n\n## Configuration\n\nexe.dev uses SSH authentication - no API key required. Your SSH key is used automatically:\n\n```bash\n# Ensure you have an SSH key\nls ~/.ssh/id_ed25519 || ls ~/.ssh/id_rsa\n\n# If not, generate one\nssh-keygen -t ed25519\n\n# Sign up and authenticate with exe.dev\nssh exe.dev\n```\n\nFor Claude Code execution, set your Anthropic API key:\n\n```bash\nANTHROPIC_API_KEY=your_anthropic_api_key\n```\n\n## Basic Usage\n\n```typescript\nimport { ExeSandboxFactory } from \"fabric-ai-exe\"\n\nconst factory = new ExeSandboxFactory()\n\n// Create a VM\nconst sandbox = await factory.create({ name: \"my-agent\" })\nconsole.log(`VM: ${sandbox.id}.exe.xyz`)\n\n// Execute shell commands via SSH\nconst result = await sandbox.exec(\"ls -la\")\nconsole.log(result.stdout)\n\n// Run Python code\nconst codeResult = await sandbox.runCode(`\nprint(\"Hello from exe.dev!\")\nprint(\"2 + 2 =\", 2 + 2)\n`, \"python\")\nconsole.log(codeResult.output)\n\n// Clean up\nawait sandbox.stop()\n```\n\n## SSH Configuration\n\nThe adapter automatically loads SSH keys from standard locations:\n\n```typescript\n// Default behavior - uses ~/.ssh/id_ed25519 or ~/.ssh/id_rsa\nconst factory = new ExeSandboxFactory()\n\n// Custom SSH key path\nconst factory = new ExeSandboxFactory({\n privateKeyPath: \"/path/to/your/key\"\n})\n\n// Or provide key content directly\nconst factory = new ExeSandboxFactory({\n privateKey: Buffer.from(\"-----BEGIN OPENSSH PRIVATE KEY-----...\")\n})\n```\n\n## Language Support\n\nexe.dev VMs are full Ubuntu machines. The runtime adapter handles code execution:\n\n```typescript\n// Python\nconst pyResult = await sandbox.runCode(`\nimport math\nprint(f\"Pi is {math.pi}\")\n`, \"python\")\n\n// JavaScript/Node.js\nconst jsResult = await sandbox.runCode(`\nconsole.log(\"Hello from Node.js\")\nconsole.log(process.version)\n`, \"javascript\")\n\n// TypeScript (via bun or tsx)\nconst tsResult = await sandbox.runCode(`\nconst greeting: string = \"Hello TypeScript\"\nconsole.log(greeting)\n`, \"typescript\")\n\n// Bash/Shell (default)\nconst shResult = await sandbox.runCode(`\necho \"Current directory: $(pwd)\"\nls -la\n`)\n```\n\n## File Operations via SFTP\n\n```typescript\n// Write a file\nawait sandbox.writeFile(\"/home/user/hello.py\", `\ndef greet(name):\n return f\"Hello, {name}!\"\n\nprint(greet(\"World\"))\n`)\n\n// Read a file\nconst content = await sandbox.readFile(\"/home/user/hello.py\")\nconsole.log(content)\n\n// List files in a directory\nconst files = await sandbox.listFiles(\"/home/user\")\nconsole.log(\"Files:\", files)\n```\n\n## Pre-installed Agents\n\nexe.dev VMs come with coding agents pre-installed:\n\n```typescript\nconst sandbox = await factory.create({ name: \"agent-box\" })\n\n// Check available agents\nconst result = await sandbox.exec(\"which claude codex\")\nconsole.log(result.stdout)\n// /usr/local/bin/claude\n// /usr/local/bin/codex\n\n// Run Claude Code\nawait sandbox.exec(`\n export ANTHROPIC_API_KEY=${process.env.ANTHROPIC_API_KEY}\n echo 'Build a REST API in Python' | claude -p --dangerously-skip-permissions\n`)\n```\n\n### Shelley Agent\n\nexe.dev includes Shelley, a web-based agent accessible at port 9999:\n\n```typescript\n// Shelley is available at:\n// https://my-agent.exe.xyz:9999/\n\n// Configure Shelley via AGENTS.md\nawait sandbox.writeFile(\"/home/user/.config/shelley/AGENTS.md\", `\n# Agent Instructions\n\nYou are a helpful coding assistant.\n- Write clean, documented code\n- Follow best practices\n- Test your changes\n`)\n```\n\n## VM Management\n\n```typescript\n// Create with custom name\nconst sandbox = await factory.create({ name: \"my-project\" })\n\n// List all your VMs\nconst vms = await factory.list()\nconsole.log(\"Your VMs:\", vms)\n// [{ id: \"my-project\", status: \"running\" }, ...]\n\n// Resume an existing VM\nconst existing = await factory.resume(\"my-project\")\nif (existing) {\n const result = await existing.exec(\"echo 'Still here!'\")\n console.log(result.stdout)\n}\n\n// Stop and delete\nawait sandbox.stop()\n```\n\n## Direct SSH Access\n\nYou can also SSH into your VMs directly:\n\n```bash\n# Create VM via CLI\nssh exe.dev new my-project\n\n# SSH into the VM\nssh my-project.exe.xyz\n\n# Or use the Fabric CLI\nfabric create --provider exe --name my-project\nfabric exec --provider exe \"echo hello\"\n```\n\n## Snapshots\n\nCapture and restore workspace state:\n\n```typescript\n// Capture snapshot\nconst snapshot = await sandbox.snapshot()\nconsole.log(`Snapshot ID: ${snapshot.id}`)\nconsole.log(`Files captured: ${snapshot.files.length}`)\n\n// Save snapshot to file\nimport { writeFileSync } from \"fs\"\nwriteFileSync(\"snapshot.json\", JSON.stringify(snapshot, null, 2))\n\n// Later, restore from snapshot\nconst newSandbox = await factory.create({ name: \"restored\" })\nawait newSandbox.restore(snapshot)\n```\n\n## Network Access\n\nexe.dev VMs have full internet access by default - no restrictions or allowlists.\n\n```typescript\n// Access any API\nconst result = await sandbox.exec(\"curl https://api.github.com\")\nconsole.log(result.stdout)\n\n// Install packages from anywhere\nawait sandbox.exec(\"pip install requests pandas numpy\")\nawait sandbox.exec(\"npm install express axios\")\n```\n\n## Persistent Disk\n\nUnlike ephemeral sandboxes, exe.dev VMs have persistent storage:\n\n```typescript\n// Files persist between sessions\nawait sandbox.writeFile(\"/home/user/data.json\", '{\"count\": 1}')\n\n// Later, reconnect to the same VM\nconst existing = await factory.resume(\"my-project\")\nif (existing) {\n const data = await existing.readFile(\"/home/user/data.json\")\n console.log(\"Data:\", JSON.parse(data)) // {\"count\": 1}\n}\n```\n\n## Root Access\n\nYou have full sudo access on exe.dev VMs:\n\n```typescript\n// Install system packages\nawait sandbox.exec(\"sudo apt-get update && sudo apt-get install -y ffmpeg\")\n\n// Modify system configuration\nawait sandbox.exec(\"sudo systemctl enable nginx\")\n\n// Access everything\nawait sandbox.exec(\"sudo cat /etc/passwd\")\n```\n\n## Error Handling\n\n```typescript\ntry {\n const sandbox = await factory.create({ name: \"test\" })\n\n const result = await sandbox.exec(\"some-command\")\n\n if (result.exitCode !== 0) {\n console.error(\"Command failed:\", result.stderr)\n }\n\n} catch (error) {\n if (error.message.includes(\"SSH connection failed\")) {\n console.error(\"Could not connect to exe.dev\")\n console.error(\"Make sure you have authenticated: ssh exe.dev\")\n } else {\n console.error(\"Error:\", error.message)\n }\n}\n```\n\n## Comparison with Other Providers\n\n| Feature | exe.dev | E2B | Daytona |\n|---------|---------|-----|---------|\n| Architecture | Persistent VMs | Ephemeral Sandboxes | Ephemeral Sandboxes |\n| Network Access | Full Internet | Full Internet | Allowlist |\n| Pre-installed Agents | Claude, Codex, Shelley | Claude Template | npm install |\n| Root Access | Yes (sudo) | Limited | No |\n| Persistent Disk | Yes | Snapshot | Snapshot |\n| Access Protocol | SSH/SFTP | REST API | REST API |\n| Setup | SSH key | API key | API key |\n\n## Best Practices\n\n1. **Name your VMs descriptively** - Makes it easier to manage multiple projects:\n ```typescript\n await factory.create({ name: \"project-frontend\" })\n await factory.create({ name: \"project-api\" })\n ```\n\n2. **Use resume for existing VMs** - Don't create duplicates:\n ```typescript\n let sandbox = await factory.resume(\"my-project\")\n if (!sandbox) {\n sandbox = await factory.create({ name: \"my-project\" })\n }\n ```\n\n3. **Clean up when done** - VMs consume resources:\n ```typescript\n await sandbox.stop()\n ```\n\n4. **Use snapshots before risky operations**:\n ```typescript\n const backup = await sandbox.snapshot()\n // Try something risky...\n if (somethingWentWrong) {\n await sandbox.restore(backup)\n }\n ```\n\n5. **Keep SSH keys secure** - Your key authenticates all exe.dev operations\n\n## Resources\n\n- [exe.dev Website](https://exe.dev)\n- [exe.dev Documentation](https://exe.dev/docs)\n- [exe.dev Blog](https://blog.exe.dev)\n- [Shelley Agent Docs](https://exe.dev/docs/shelley)"
}
]
}