Skip to content

Commit 9b84765

Browse files
authored
Merge pull request #28 from Gui-Yue/support_e2b
feat(sandbox): add E2B cloud sandbox support
2 parents 9c52547 + 8f5c964 commit 9b84765

53 files changed

Lines changed: 3022 additions & 40 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.test.example

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,16 @@ POSTGRES_PORT=5433
5050
POSTGRES_DB=kode_test
5151
POSTGRES_USER=postgres
5252
POSTGRES_PASSWORD=testpass123
53+
54+
# =============================================================================
55+
# E2B Cloud Sandbox (for E2B integration/e2e tests)
56+
# =============================================================================
57+
# E2B API Key - 从 https://e2b.dev/dashboard 获取
58+
# 如果不配置,E2B 相关的 E2E 测试将自动跳过
59+
E2B_API_KEY=replace-with-your-e2b-api-key
60+
61+
# E2B 沙箱模板(可选,默认使用 'base')
62+
E2B_TEMPLATE=base
63+
64+
# E2B 沙箱超时时间(毫秒,可选,默认 300000)
65+
E2B_TIMEOUT_MS=300000

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ test-workspace/
1414
.DS_Store
1515
test-cli.ts
1616
.env.test
17+
.data
1718

1819
# Ignore all markdown files except README.md and files in doc/docs folders
1920
*.md
2021
!README.md
2122
!README.zh-CN.md
23+
!README.en.md
2224
!ROADMAP.md
2325
!doc/**/*.md
2426
!docs/**/*.md

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- **Long-Running & Resumable** - Seven-stage checkpoints with Safe-Fork-Point for crash recovery
1111
- **Multi-Agent Collaboration** - AgentPool, Room messaging, and task delegation
1212
- **Enterprise Persistence** - SQLite/PostgreSQL support with unified WAL
13+
- **Cloud Sandbox** - [E2B](https://e2b.dev) integration for isolated remote code execution
1314
- **Extensible Ecosystem** - MCP tools, custom Providers, Skills system
1415

1516
## Quick Start

README.zh-CN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- **长时运行与恢复** - 七段断点机制,支持 Safe-Fork-Point 崩溃恢复
1111
- **多 Agent 协作** - AgentPool、Room 消息、任务委派
1212
- **企业级持久化** - 支持 SQLite/PostgreSQL,统一 WAL 日志
13+
- **云端沙箱** - 集成 [E2B](https://e2b.dev),提供隔离的远程代码执行环境
1314
- **可扩展生态** - MCP 工具、自定义 Provider、Skills 系统
1415

1516
## 快速开始

docs/en/guides/e2b-sandbox.md

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# E2B Cloud Sandbox Guide
2+
3+
KODE SDK supports [E2B](https://e2b.dev) as a cloud sandbox backend, providing fully isolated remote Linux environments for AI Agent code execution.
4+
5+
---
6+
7+
## Overview
8+
9+
| Feature | Description |
10+
|---------|-------------|
11+
| **Isolation** | Each sandbox runs in an isolated micro-VM |
12+
| **Startup** | ~150ms to create a full Linux environment |
13+
| **Pre-installed** | Python, Node.js, common system tools |
14+
| **Scalable** | Each Agent gets its own sandbox instance |
15+
16+
### When to Use E2B vs Local Sandbox
17+
18+
| Scenario | Recommended |
19+
|----------|-------------|
20+
| Development/Testing | Local Sandbox |
21+
| Production with untrusted code | E2B |
22+
| Multi-user concurrent agents | E2B |
23+
| Need specific environment (GPU, packages) | E2B (Custom Template) |
24+
| Offline / No internet | Local Sandbox |
25+
26+
---
27+
28+
## Prerequisites
29+
30+
1. Sign up at [e2b.dev](https://e2b.dev)
31+
2. Get your API Key from the Dashboard
32+
3. Set environment variable:
33+
34+
```bash
35+
export E2B_API_KEY=your-api-key
36+
```
37+
38+
---
39+
40+
## Quick Start
41+
42+
### Create and Use a Sandbox
43+
44+
```typescript
45+
import { E2BSandbox } from '@shareai-lab/kode-sdk';
46+
47+
const sandbox = new E2BSandbox({
48+
apiKey: process.env.E2B_API_KEY,
49+
template: 'base',
50+
timeoutMs: 300_000, // 5 minutes
51+
});
52+
await sandbox.init();
53+
54+
// Execute commands
55+
const result = await sandbox.exec('python3 -c "print(1+1)"');
56+
console.log(result.stdout); // "2\n"
57+
58+
// File operations
59+
await sandbox.fs.write('script.py', 'print("hello")');
60+
const content = await sandbox.fs.read('script.py');
61+
62+
// Cleanup
63+
await sandbox.dispose();
64+
```
65+
66+
---
67+
68+
## Configuration
69+
70+
### E2BSandboxOptions
71+
72+
```typescript
73+
interface E2BSandboxOptions {
74+
apiKey?: string; // E2B API Key (or use E2B_API_KEY env)
75+
template?: string; // Template ID/alias, default 'base'
76+
timeoutMs?: number; // Sandbox lifetime, default 300_000 (5min)
77+
workDir?: string; // Working directory, default '/home/user'
78+
envs?: Record<string, string>; // Environment variables
79+
metadata?: Record<string, string>; // Custom metadata
80+
allowInternetAccess?: boolean; // Allow internet, default true
81+
execTimeoutMs?: number; // Command timeout, default 120_000
82+
sandboxId?: string; // Connect to existing sandbox (resume)
83+
domain?: string; // E2B API domain
84+
}
85+
```
86+
87+
### Environment Variables
88+
89+
| Variable | Description |
90+
|----------|-------------|
91+
| `E2B_API_KEY` | Your E2B API key (used if `apiKey` not provided) |
92+
93+
---
94+
95+
## Agent Integration
96+
97+
### Using E2B Sandbox with Agent
98+
99+
```typescript
100+
import { Agent, E2BSandbox } from '@shareai-lab/kode-sdk';
101+
import { createRuntime } from './shared/runtime';
102+
103+
const sandbox = new E2BSandbox({
104+
template: 'base',
105+
timeoutMs: 600_000,
106+
});
107+
await sandbox.init();
108+
109+
const deps = createRuntime(({ templates, registerBuiltin }) => {
110+
registerBuiltin('fs', 'bash', 'todo');
111+
templates.register({
112+
id: 'coder',
113+
systemPrompt: 'You are a coding assistant.',
114+
tools: ['bash_run', 'fs_read', 'fs_write', 'todo_read', 'todo_write'],
115+
});
116+
});
117+
118+
const agent = await Agent.create({ templateId: 'coder', sandbox }, deps);
119+
await agent.send('Write and run a Python fibonacci script');
120+
```
121+
122+
### Sandbox Lifecycle Binding
123+
124+
- **Agent Start**: Call `sandbox.init()` before `Agent.create()`
125+
- **Agent Pause**: Sandbox persists (use `sandboxId` to reconnect)
126+
- **Agent Resume**: Pass `sandboxId` to reconnect to existing sandbox
127+
- **Agent Destroy**: Call `sandbox.dispose()` to terminate
128+
129+
### Resume / Fork with Persistent sandboxId
130+
131+
```typescript
132+
// First run - create
133+
const sandbox = new E2BSandbox({ template: 'base' });
134+
await sandbox.init();
135+
const id = sandbox.getSandboxId(); // persist this
136+
137+
// Later - resume
138+
const restored = new E2BSandbox({ sandboxId: id });
139+
await restored.init();
140+
// Same sandbox environment is available
141+
```
142+
143+
---
144+
145+
## Custom Templates
146+
147+
### Using E2BTemplateBuilder
148+
149+
```typescript
150+
import { E2BTemplateBuilder } from '@shareai-lab/kode-sdk';
151+
152+
await E2BTemplateBuilder.build({
153+
alias: 'data-analysis',
154+
base: 'python',
155+
baseVersion: '3.11',
156+
aptPackages: ['graphviz'],
157+
pipPackages: ['pandas', 'numpy', 'matplotlib'],
158+
workDir: '/workspace',
159+
cpuCount: 4,
160+
memoryMB: 2048,
161+
});
162+
```
163+
164+
### Available Base Images
165+
166+
| Base | Description |
167+
|------|-------------|
168+
| `python` | Python with pip |
169+
| `node` | Node.js with npm |
170+
| `debian` | Debian base |
171+
| `ubuntu` | Ubuntu base |
172+
| `custom` | Custom Dockerfile |
173+
174+
---
175+
176+
## Network & Ports
177+
178+
### Exposing Ports
179+
180+
```typescript
181+
const sandbox = new E2BSandbox({ template: 'node' });
182+
await sandbox.init();
183+
184+
await sandbox.exec('npx serve -l 3000 &');
185+
const url = sandbox.getHostUrl(3000);
186+
console.log(`Preview: ${url}`);
187+
// https://3000-<sandboxId>.e2b.app
188+
```
189+
190+
---
191+
192+
## Best Practices
193+
194+
1. **Timeout Management**: Set appropriate `timeoutMs` to control costs. Call `dispose()` when done.
195+
2. **Error Handling**: E2B operations are remote calls; handle network errors gracefully.
196+
3. **Data Export**: Sandbox data is lost after `dispose()`. Export important results first.
197+
4. **Concurrency**: E2B has account-level sandbox limits. Plan accordingly with AgentPool.
198+
5. **Template Reuse**: Build templates once, reuse across sandboxes for faster startup.

docs/en/reference/api.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,64 @@ const agentId = generateAgentId(); // e.g., 'agt-abc123xyz'
684684

685685
---
686686

687+
## E2BSandbox
688+
689+
Cloud sandbox powered by [E2B](https://e2b.dev) for isolated code execution.
690+
691+
### Constructor
692+
693+
```typescript
694+
new E2BSandbox(options?: E2BSandboxOptions)
695+
```
696+
697+
### Methods
698+
699+
| Method | Signature | Description |
700+
|--------|-----------|-------------|
701+
| `init()` | `async init(): Promise<void>` | Initialize (create or connect) sandbox |
702+
| `exec(cmd, opts?)` | `async exec(cmd: string, opts?: { timeoutMs?: number }): Promise<SandboxExecResult>` | Execute a command |
703+
| `dispose()` | `async dispose(): Promise<void>` | Kill sandbox and cleanup |
704+
| `getSandboxId()` | `getSandboxId(): string` | Get sandbox ID for persistence |
705+
| `getHostUrl(port)` | `getHostUrl(port: number): string` | Get accessible URL for a port |
706+
| `setTimeout(ms)` | `async setTimeout(timeoutMs: number): Promise<void>` | Extend sandbox lifetime |
707+
| `isRunning()` | `async isRunning(): Promise<boolean>` | Check if sandbox is alive |
708+
| `watchFiles(paths, listener)` | `async watchFiles(...): Promise<string>` | Watch file changes |
709+
| `unwatchFiles(id)` | `unwatchFiles(id: string): void` | Stop watching |
710+
| `getE2BInstance()` | `getE2BInstance(): E2BSdk` | Access underlying E2B SDK |
711+
712+
### Properties
713+
714+
| Property | Type | Description |
715+
|----------|------|-------------|
716+
| `kind` | `'e2b'` | Sandbox type identifier |
717+
| `workDir` | `string` | Working directory path |
718+
| `fs` | `SandboxFS` | File system operations |
719+
720+
---
721+
722+
## E2BTemplateBuilder
723+
724+
Static utility for building custom E2B sandbox templates.
725+
726+
### Static Methods
727+
728+
#### `E2BTemplateBuilder.build(config, opts?)`
729+
730+
```typescript
731+
static async build(
732+
config: E2BTemplateConfig,
733+
opts?: { apiKey?: string; onLog?: (log: string) => void }
734+
): Promise<{ templateId: string; alias: string }>
735+
```
736+
737+
#### `E2BTemplateBuilder.exists(alias, opts?)`
738+
739+
```typescript
740+
static async exists(alias: string, opts?: { apiKey?: string }): Promise<boolean>
741+
```
742+
743+
---
744+
687745
## References
688746

689747
- [Types Reference](./types.md)

docs/en/reference/types.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,45 @@ interface ReminderOptions {
477477

478478
---
479479

480+
## E2B Types
481+
482+
### E2BSandboxOptions
483+
484+
```typescript
485+
interface E2BSandboxOptions {
486+
apiKey?: string;
487+
template?: string;
488+
timeoutMs?: number;
489+
workDir?: string;
490+
envs?: Record<string, string>;
491+
metadata?: Record<string, string>;
492+
allowInternetAccess?: boolean;
493+
execTimeoutMs?: number;
494+
sandboxId?: string;
495+
domain?: string;
496+
}
497+
```
498+
499+
### E2BTemplateConfig
500+
501+
```typescript
502+
interface E2BTemplateConfig {
503+
alias: string;
504+
base: 'python' | 'node' | 'debian' | 'ubuntu' | 'custom';
505+
baseVersion?: string;
506+
dockerfile?: string;
507+
aptPackages?: string[];
508+
pipPackages?: string[];
509+
npmPackages?: string[];
510+
buildCommands?: string[];
511+
workDir?: string;
512+
cpuCount?: number;
513+
memoryMB?: number;
514+
}
515+
```
516+
517+
---
518+
480519
## References
481520

482521
- [API Reference](./api.md)

0 commit comments

Comments
 (0)