@treesap/sandbox 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +107 -0
- package/README.md +495 -0
- package/dist/api-server.d.ts +41 -0
- package/dist/api-server.d.ts.map +1 -0
- package/dist/api-server.js +536 -0
- package/dist/api-server.js.map +1 -0
- package/dist/auth-middleware.d.ts +31 -0
- package/dist/auth-middleware.d.ts.map +1 -0
- package/dist/auth-middleware.js +35 -0
- package/dist/auth-middleware.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +65 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +137 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +412 -0
- package/dist/client.js.map +1 -0
- package/dist/file-service.d.ts +94 -0
- package/dist/file-service.d.ts.map +1 -0
- package/dist/file-service.js +203 -0
- package/dist/file-service.js.map +1 -0
- package/dist/http-exposure-service.d.ts +71 -0
- package/dist/http-exposure-service.d.ts.map +1 -0
- package/dist/http-exposure-service.js +172 -0
- package/dist/http-exposure-service.js.map +1 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/sandbox-manager.d.ts +76 -0
- package/dist/sandbox-manager.d.ts.map +1 -0
- package/dist/sandbox-manager.js +161 -0
- package/dist/sandbox-manager.js.map +1 -0
- package/dist/sandbox.d.ts +118 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +303 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +240 -0
- package/dist/server.js.map +1 -0
- package/dist/stream-service.d.ts +35 -0
- package/dist/stream-service.d.ts.map +1 -0
- package/dist/stream-service.js +136 -0
- package/dist/stream-service.js.map +1 -0
- package/dist/terminal.d.ts +46 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +264 -0
- package/dist/terminal.js.map +1 -0
- package/dist/websocket.d.ts +48 -0
- package/dist/websocket.d.ts.map +1 -0
- package/dist/websocket.js +332 -0
- package/dist/websocket.js.map +1 -0
- package/package.json +59 -0
- package/src/api-server.ts +658 -0
- package/src/auth-middleware.ts +65 -0
- package/src/cli.ts +71 -0
- package/src/client.ts +537 -0
- package/src/file-service.ts +273 -0
- package/src/http-exposure-service.ts +232 -0
- package/src/index.ts +101 -0
- package/src/sandbox-manager.ts +202 -0
- package/src/sandbox.ts +396 -0
- package/src/stream-service.ts +174 -0
- package/tsconfig.json +37 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [1.0.0] - 2025-11-06
|
|
6
|
+
|
|
7
|
+
### 🎉 Major Redesign - Breaking Changes
|
|
8
|
+
|
|
9
|
+
TreeSap Sandbox has been completely redesigned from a terminal-centric WebSocket server to a folder-based sandbox API similar to Cloudflare's Sandbox SDK.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Sandbox Management API**
|
|
14
|
+
- Create isolated sandbox instances with dedicated working directories
|
|
15
|
+
- List all active sandboxes
|
|
16
|
+
- Get sandbox status and metrics
|
|
17
|
+
- Destroy sandboxes with optional cleanup
|
|
18
|
+
|
|
19
|
+
- **File Operations API**
|
|
20
|
+
- `readFile()` - Read file contents
|
|
21
|
+
- `writeFile()` - Write/create files
|
|
22
|
+
- `listFiles()` - List directory contents with optional recursion
|
|
23
|
+
- `deleteFile()` - Remove files and directories
|
|
24
|
+
- Path traversal protection
|
|
25
|
+
- Glob pattern filtering
|
|
26
|
+
|
|
27
|
+
- **Command Execution**
|
|
28
|
+
- `exec()` - Execute commands and return complete results
|
|
29
|
+
- `execStream()` - Stream command output via Server-Sent Events
|
|
30
|
+
- Timeout support
|
|
31
|
+
- Custom working directory and environment variables
|
|
32
|
+
|
|
33
|
+
- **Process Management**
|
|
34
|
+
- `startProcess()` - Start long-running background processes
|
|
35
|
+
- `listProcesses()` - View all running processes
|
|
36
|
+
- `killProcess()` - Terminate specific processes
|
|
37
|
+
- `killAllProcesses()` - Terminate all processes
|
|
38
|
+
- `streamProcessLogs()` - Real-time log streaming
|
|
39
|
+
- `getProcessLogs()` - Get accumulated logs
|
|
40
|
+
|
|
41
|
+
- **TypeScript SDK Client**
|
|
42
|
+
- Full-featured client library (`SandboxClient`)
|
|
43
|
+
- Promise-based API with async/await support
|
|
44
|
+
- Type-safe interfaces
|
|
45
|
+
- Streaming support with SSE parsing utilities
|
|
46
|
+
|
|
47
|
+
- **REST API Server**
|
|
48
|
+
- Built with Hono framework
|
|
49
|
+
- Complete REST endpoints for all operations
|
|
50
|
+
- Server-Sent Events for streaming
|
|
51
|
+
- CORS support
|
|
52
|
+
- Health check endpoints
|
|
53
|
+
|
|
54
|
+
- **CLI Tool**
|
|
55
|
+
- `treesap-sandbox` command-line interface
|
|
56
|
+
- Configurable port, host, and paths
|
|
57
|
+
- Built-in help
|
|
58
|
+
|
|
59
|
+
- **Core Components**
|
|
60
|
+
- `Sandbox` class - Individual sandbox instance
|
|
61
|
+
- `SandboxManager` - Multi-sandbox management
|
|
62
|
+
- `FileService` - File operations service
|
|
63
|
+
- `StreamService` - SSE streaming utilities
|
|
64
|
+
|
|
65
|
+
- **Auto-cleanup**
|
|
66
|
+
- Automatic cleanup of idle sandboxes (30 min default)
|
|
67
|
+
- Configurable cleanup intervals
|
|
68
|
+
- Graceful shutdown handling
|
|
69
|
+
|
|
70
|
+
- **Documentation**
|
|
71
|
+
- Comprehensive README with examples
|
|
72
|
+
- Migration guide from v0.x
|
|
73
|
+
- API reference
|
|
74
|
+
- Use case examples
|
|
75
|
+
|
|
76
|
+
### Removed
|
|
77
|
+
|
|
78
|
+
- `TerminalService` - Replaced by `Sandbox` class
|
|
79
|
+
- `WebSocketTerminalService` - Replaced by REST API + SSE
|
|
80
|
+
- WebSocket support - Replaced by Server-Sent Events
|
|
81
|
+
- `node-pty` dependency - Replaced by `child_process`
|
|
82
|
+
- `ws` dependency - No longer needed
|
|
83
|
+
- Terminal session persistence to disk
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
|
|
87
|
+
- **Package version**: `0.1.0` → `1.0.0`
|
|
88
|
+
- **Main export**: `startSandboxServer()` → `startServer()`
|
|
89
|
+
- **Communication**: WebSocket → REST API + Server-Sent Events
|
|
90
|
+
- **Isolation**: PTY sessions → Folder-based sandboxes
|
|
91
|
+
- **Dependencies**: Removed `node-pty` and `ws`, kept `hono` and `@hono/node-server`
|
|
92
|
+
|
|
93
|
+
### Migration
|
|
94
|
+
|
|
95
|
+
See [MIGRATION.md](./MIGRATION.md) for detailed migration instructions from v0.x to v1.0.
|
|
96
|
+
|
|
97
|
+
## [0.1.0] - Previous Version
|
|
98
|
+
|
|
99
|
+
Initial release with terminal-centric WebSocket server.
|
|
100
|
+
|
|
101
|
+
### Features
|
|
102
|
+
|
|
103
|
+
- PTY-based terminal sessions
|
|
104
|
+
- WebSocket real-time communication
|
|
105
|
+
- REST API for terminal management
|
|
106
|
+
- Session persistence
|
|
107
|
+
- Multi-client support
|
package/README.md
ADDED
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
# 🌳 TreeSap Sandbox
|
|
2
|
+
|
|
3
|
+
A self-hosted sandbox API for isolated code execution and file management. Similar to Cloudflare's Sandbox SDK, but designed for self-hosting on your own infrastructure.
|
|
4
|
+
|
|
5
|
+
Perfect for AI agents, code execution platforms, and automation tools that need secure, isolated environments.
|
|
6
|
+
|
|
7
|
+
> ⚠️ **Warning**: This package is experimental and exposes your machine to the local network. It executes arbitrary code with folder-based isolation only (not containerized). Proceed with caution.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- 🔒 **Folder-based isolation** - Each sandbox gets its own working directory
|
|
12
|
+
- 📁 **File Operations API** - Read, write, list, and delete files
|
|
13
|
+
- ⚡ **Command Execution** - Run commands with streaming output
|
|
14
|
+
- 🔄 **Process Management** - Start, monitor, and kill background processes
|
|
15
|
+
- 🌊 **Real-time Streaming** - Server-Sent Events for live output
|
|
16
|
+
- 🎯 **Simple REST API** - Easy to integrate with any language
|
|
17
|
+
- 📦 **TypeScript SDK** - Full-featured client library included
|
|
18
|
+
- 🚀 **Self-hosted** - Run on your own servers, no cloud dependencies
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @treesap/sandbox
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### Start the Server
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Using CLI
|
|
32
|
+
npx treesap-sandbox --port 3000
|
|
33
|
+
|
|
34
|
+
# Or programmatically
|
|
35
|
+
import { startServer } from '@treesap/sandbox';
|
|
36
|
+
|
|
37
|
+
await startServer({ port: 3000 });
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Use the Client SDK
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { SandboxClient } from '@treesap/sandbox';
|
|
44
|
+
|
|
45
|
+
// Create a new sandbox
|
|
46
|
+
const sandbox = await SandboxClient.create('http://localhost:3000');
|
|
47
|
+
|
|
48
|
+
// Execute commands
|
|
49
|
+
const result = await sandbox.exec('npm install');
|
|
50
|
+
console.log(result.stdout);
|
|
51
|
+
|
|
52
|
+
// File operations
|
|
53
|
+
await sandbox.writeFile('hello.txt', 'Hello, World!');
|
|
54
|
+
const content = await sandbox.readFile('hello.txt');
|
|
55
|
+
const files = await sandbox.listFiles();
|
|
56
|
+
|
|
57
|
+
// Background processes
|
|
58
|
+
const server = await sandbox.startProcess('node server.js');
|
|
59
|
+
const logs = await sandbox.streamProcessLogs(server.id);
|
|
60
|
+
|
|
61
|
+
// Cleanup
|
|
62
|
+
await sandbox.destroy({ cleanup: true });
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## API Reference
|
|
66
|
+
|
|
67
|
+
### Sandbox Management
|
|
68
|
+
|
|
69
|
+
#### Create a Sandbox
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
POST /sandbox
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
const sandbox = await SandboxClient.create('http://localhost:3000', {
|
|
77
|
+
workDir: '/custom/path', // optional
|
|
78
|
+
timeout: 60000, // optional, default timeout for commands
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### Get Sandbox Info
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
GET /sandbox/:id
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const status = await sandbox.getStatus();
|
|
90
|
+
console.log(status);
|
|
91
|
+
// {
|
|
92
|
+
// id: 'abc123',
|
|
93
|
+
// workDir: '/path/to/sandbox',
|
|
94
|
+
// createdAt: 1234567890,
|
|
95
|
+
// processCount: 2,
|
|
96
|
+
// ...
|
|
97
|
+
// }
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### List All Sandboxes
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
GET /sandbox
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### Destroy a Sandbox
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
DELETE /sandbox/:id?cleanup=true
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
await sandbox.destroy({ cleanup: true });
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Command Execution
|
|
117
|
+
|
|
118
|
+
#### Execute Command
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
POST /sandbox/:id/exec
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
const result = await sandbox.exec('ls -la', {
|
|
126
|
+
timeout: 5000,
|
|
127
|
+
cwd: '/custom/dir',
|
|
128
|
+
env: { NODE_ENV: 'production' }
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
console.log(result);
|
|
132
|
+
// {
|
|
133
|
+
// success: true,
|
|
134
|
+
// stdout: '...',
|
|
135
|
+
// stderr: '...',
|
|
136
|
+
// exitCode: 0
|
|
137
|
+
// }
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### Stream Command Output
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
GET /sandbox/:id/exec-stream?command=npm%20install
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { parseSSEStream } from '@treesap/sandbox';
|
|
148
|
+
|
|
149
|
+
const stream = await sandbox.execStream('npm install');
|
|
150
|
+
|
|
151
|
+
for await (const event of parseSSEStream(stream)) {
|
|
152
|
+
switch (event.type) {
|
|
153
|
+
case 'stdout':
|
|
154
|
+
console.log('Output:', event.data);
|
|
155
|
+
break;
|
|
156
|
+
case 'stderr':
|
|
157
|
+
console.error('Error:', event.data);
|
|
158
|
+
break;
|
|
159
|
+
case 'complete':
|
|
160
|
+
console.log('Exit code:', event.exitCode);
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Process Management
|
|
167
|
+
|
|
168
|
+
#### Start a Background Process
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
POST /sandbox/:id/process
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
const process = await sandbox.startProcess('python -m http.server 8000');
|
|
176
|
+
console.log('Started with PID:', process.pid);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### List Processes
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
GET /sandbox/:id/process
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
const processes = await sandbox.listProcesses();
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### Get Process Info
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
GET /sandbox/:id/process/:processId
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
const info = await sandbox.getProcess(processId);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
#### Kill a Process
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
DELETE /sandbox/:id/process/:processId?signal=SIGTERM
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
await sandbox.killProcess(processId, 'SIGTERM');
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
#### Stream Process Logs
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
GET /sandbox/:id/process/:processId/logs
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
const stream = await sandbox.streamProcessLogs(processId);
|
|
217
|
+
|
|
218
|
+
for await (const event of parseSSEStream(stream)) {
|
|
219
|
+
console.log(`[${event.timestamp}] ${event.data}`);
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### File Operations
|
|
224
|
+
|
|
225
|
+
#### List Files
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
GET /sandbox/:id/files?path=.&recursive=true&pattern=*.js
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
const files = await sandbox.listFiles('.', {
|
|
233
|
+
recursive: true,
|
|
234
|
+
pattern: '*.js',
|
|
235
|
+
includeHidden: false
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
files.forEach(file => {
|
|
239
|
+
console.log(file.name, file.type, file.size);
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
#### Read File
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
GET /sandbox/:id/files/path/to/file.txt
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
const content = await sandbox.readFile('package.json');
|
|
251
|
+
console.log(content);
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
#### Write File
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
POST /sandbox/:id/files/path/to/file.txt
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
await sandbox.writeFile('README.md', '# Hello World');
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
#### Delete File
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
DELETE /sandbox/:id/files/path/to/file.txt?recursive=true
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
await sandbox.deleteFile('node_modules', { recursive: true });
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Use Cases
|
|
275
|
+
|
|
276
|
+
### AI Agent Code Execution
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
const sandbox = await SandboxClient.create('http://localhost:3000');
|
|
280
|
+
|
|
281
|
+
// Agent writes code
|
|
282
|
+
await sandbox.writeFile('script.py', `
|
|
283
|
+
import pandas as pd
|
|
284
|
+
print(pd.__version__)
|
|
285
|
+
`);
|
|
286
|
+
|
|
287
|
+
// Agent installs dependencies
|
|
288
|
+
await sandbox.exec('pip install pandas');
|
|
289
|
+
|
|
290
|
+
// Agent runs code
|
|
291
|
+
const result = await sandbox.exec('python script.py');
|
|
292
|
+
console.log(result.stdout);
|
|
293
|
+
|
|
294
|
+
// Cleanup
|
|
295
|
+
await sandbox.destroy({ cleanup: true });
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Running Tests in Isolation
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
const sandbox = await SandboxClient.create('http://localhost:3000');
|
|
302
|
+
|
|
303
|
+
// Clone repo
|
|
304
|
+
await sandbox.exec('git clone https://github.com/user/repo.git');
|
|
305
|
+
await sandbox.exec('cd repo && npm install');
|
|
306
|
+
|
|
307
|
+
// Run tests
|
|
308
|
+
const testResult = await sandbox.exec('cd repo && npm test');
|
|
309
|
+
|
|
310
|
+
console.log('Tests passed:', testResult.success);
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Long-Running Services
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
const sandbox = await SandboxClient.create('http://localhost:3000');
|
|
317
|
+
|
|
318
|
+
// Start a web server
|
|
319
|
+
const server = await sandbox.startProcess('node server.js');
|
|
320
|
+
|
|
321
|
+
// Monitor logs in real-time
|
|
322
|
+
const logStream = await sandbox.streamProcessLogs(server.id);
|
|
323
|
+
|
|
324
|
+
for await (const log of parseSSEStream(logStream)) {
|
|
325
|
+
console.log('[Server]', log.data);
|
|
326
|
+
|
|
327
|
+
if (log.data.includes('Server started')) {
|
|
328
|
+
console.log('Server is ready!');
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Later: stop the server
|
|
334
|
+
await sandbox.killProcess(server.id);
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Server Configuration
|
|
338
|
+
|
|
339
|
+
### CLI Options
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
treesap-sandbox [options]
|
|
343
|
+
|
|
344
|
+
Options:
|
|
345
|
+
-p, --port <port> Port to listen on (default: 3000)
|
|
346
|
+
-h, --host <host> Host to bind to (default: 0.0.0.0)
|
|
347
|
+
-b, --base-path <path> Base path for sandbox folders (default: ./.sandboxes)
|
|
348
|
+
-m, --max-sandboxes <num> Maximum number of sandboxes (default: 100)
|
|
349
|
+
--cors Enable CORS (default: false)
|
|
350
|
+
--help Show this help message
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Programmatic Configuration
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
import { startServer } from '@treesap/sandbox';
|
|
357
|
+
|
|
358
|
+
const { server, manager } = await startServer({
|
|
359
|
+
port: 3000,
|
|
360
|
+
host: '0.0.0.0',
|
|
361
|
+
basePath: './.sandboxes',
|
|
362
|
+
maxSandboxes: 100,
|
|
363
|
+
cors: true,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// Access sandbox manager directly
|
|
367
|
+
const stats = manager.getStats();
|
|
368
|
+
console.log('Active sandboxes:', stats.totalSandboxes);
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Architecture
|
|
372
|
+
|
|
373
|
+
TreeSap Sandbox uses a simple, lightweight architecture:
|
|
374
|
+
|
|
375
|
+
```
|
|
376
|
+
┌─────────────────────────────────────┐
|
|
377
|
+
│ Client SDK / API │
|
|
378
|
+
│ (HTTP REST + Server-Sent Events) │
|
|
379
|
+
└──────────────┬──────────────────────┘
|
|
380
|
+
│
|
|
381
|
+
│ HTTP/REST
|
|
382
|
+
│
|
|
383
|
+
┌──────────────▼──────────────────────┐
|
|
384
|
+
│ API Server (Hono) │
|
|
385
|
+
│ - Sandbox Management │
|
|
386
|
+
│ - Command Execution │
|
|
387
|
+
│ - Process Management │
|
|
388
|
+
│ - File Operations │
|
|
389
|
+
└──────────────┬──────────────────────┘
|
|
390
|
+
│
|
|
391
|
+
│
|
|
392
|
+
┌──────────────▼──────────────────────┐
|
|
393
|
+
│ Sandbox Manager │
|
|
394
|
+
│ - Creates isolated sandboxes │
|
|
395
|
+
│ - Manages lifecycle │
|
|
396
|
+
│ - Auto-cleanup │
|
|
397
|
+
└──────────────┬──────────────────────┘
|
|
398
|
+
│
|
|
399
|
+
│
|
|
400
|
+
┌──────────────▼──────────────────────┐
|
|
401
|
+
│ Individual Sandboxes │
|
|
402
|
+
│ - Isolated working directory │
|
|
403
|
+
│ - Process spawning (child_process) │
|
|
404
|
+
│ - File operations (fs) │
|
|
405
|
+
└─────────────────────────────────────┘
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Security Model
|
|
409
|
+
|
|
410
|
+
- **Folder-based isolation**: Each sandbox operates in its own directory
|
|
411
|
+
- **Path traversal prevention**: File operations are validated to prevent `../` attacks
|
|
412
|
+
- **Process limits**: Configurable max processes per sandbox
|
|
413
|
+
- **Automatic cleanup**: Idle sandboxes are cleaned up after 30 minutes
|
|
414
|
+
- **Resource monitoring**: Optional timeout limits for commands
|
|
415
|
+
|
|
416
|
+
> ⚠️ **Note**: This is folder-based isolation, not container-based. For production use with untrusted code, consider running the server inside a Docker container or VM for an additional security layer.
|
|
417
|
+
|
|
418
|
+
## Comparison with Cloudflare Sandbox SDK
|
|
419
|
+
|
|
420
|
+
| Feature | TreeSap Sandbox | Cloudflare Sandbox |
|
|
421
|
+
|---------|----------------|-------------------|
|
|
422
|
+
| Self-hosted | ✅ Yes | ❌ Cloud only |
|
|
423
|
+
| Folder isolation | ✅ Yes | Container-based |
|
|
424
|
+
| File operations | ✅ Full API | ✅ Full API |
|
|
425
|
+
| Command execution | ✅ Yes | ✅ Yes |
|
|
426
|
+
| Process management | ✅ Yes | ✅ Yes |
|
|
427
|
+
| Streaming output | ✅ SSE | ✅ SSE |
|
|
428
|
+
| Code interpreters | ⚠️ Planned | ✅ Python/JS |
|
|
429
|
+
| Public URLs | ❌ No | ✅ Yes |
|
|
430
|
+
| Platform | Node.js | Cloudflare Workers |
|
|
431
|
+
|
|
432
|
+
## Advanced Usage
|
|
433
|
+
|
|
434
|
+
### Using Core Components Directly
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
import { Sandbox, FileService } from '@treesap/sandbox';
|
|
438
|
+
|
|
439
|
+
// Create a sandbox directly (without server)
|
|
440
|
+
const sandbox = new Sandbox({
|
|
441
|
+
workDir: '/tmp/my-sandbox',
|
|
442
|
+
timeout: 30000,
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
await sandbox.initialize();
|
|
446
|
+
|
|
447
|
+
// Execute commands
|
|
448
|
+
const result = await sandbox.exec('ls -la');
|
|
449
|
+
|
|
450
|
+
// Use file service
|
|
451
|
+
const fileService = new FileService(sandbox.workDir);
|
|
452
|
+
await fileService.writeFile('test.txt', 'Hello!');
|
|
453
|
+
const files = await fileService.listFiles();
|
|
454
|
+
|
|
455
|
+
// Cleanup
|
|
456
|
+
await sandbox.destroy({ cleanup: true });
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Custom Server Integration
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
import { createServer, SandboxManager } from '@treesap/sandbox';
|
|
463
|
+
import { serve } from '@hono/node-server';
|
|
464
|
+
|
|
465
|
+
const { app, manager } = createServer({
|
|
466
|
+
basePath: '/custom/sandboxes',
|
|
467
|
+
maxSandboxes: 50,
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
// Add custom routes
|
|
471
|
+
app.get('/custom-endpoint', (c) => {
|
|
472
|
+
const stats = manager.getStats();
|
|
473
|
+
return c.json({ stats });
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
// Start server
|
|
477
|
+
serve({ fetch: app.fetch, port: 3000 });
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
## Contributing
|
|
481
|
+
|
|
482
|
+
Contributions are welcome! Please check out the [GitHub repository](https://github.com/withtreesap/treesap) for more information.
|
|
483
|
+
|
|
484
|
+
## License
|
|
485
|
+
|
|
486
|
+
MIT
|
|
487
|
+
|
|
488
|
+
## Related Projects
|
|
489
|
+
|
|
490
|
+
- [Cloudflare Sandbox SDK](https://developers.cloudflare.com/sandbox/) - Cloud-based sandbox execution
|
|
491
|
+
- [TreeSap](https://github.com/withtreesap/treesap) - AI agent framework
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
Built with ❤️ by the TreeSap Team
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { SandboxManager } from './sandbox-manager';
|
|
3
|
+
import { HttpExposureService } from './http-exposure-service';
|
|
4
|
+
export interface ServerConfig {
|
|
5
|
+
port?: number;
|
|
6
|
+
host?: string;
|
|
7
|
+
basePath?: string;
|
|
8
|
+
maxSandboxes?: number;
|
|
9
|
+
cors?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* API keys for authentication. If empty or not provided, authentication is disabled.
|
|
12
|
+
* Can also be set via SANDBOX_API_KEYS environment variable (comma-separated).
|
|
13
|
+
*/
|
|
14
|
+
apiKeys?: string[];
|
|
15
|
+
/**
|
|
16
|
+
* HTTP exposure configuration for Caddy integration
|
|
17
|
+
*/
|
|
18
|
+
httpExposure?: {
|
|
19
|
+
caddyAdminUrl?: string;
|
|
20
|
+
baseDomain?: string;
|
|
21
|
+
protocol?: 'http' | 'https';
|
|
22
|
+
upstreamHost?: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Create and configure the API server
|
|
27
|
+
*/
|
|
28
|
+
export declare function createServer(config?: ServerConfig): {
|
|
29
|
+
app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
30
|
+
manager: SandboxManager;
|
|
31
|
+
httpExposure: HttpExposureService;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Start the sandbox server
|
|
35
|
+
*/
|
|
36
|
+
export declare function startServer(config?: ServerConfig): Promise<{
|
|
37
|
+
server: import("@hono/node-server").ServerType;
|
|
38
|
+
app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
39
|
+
manager: SandboxManager;
|
|
40
|
+
}>;
|
|
41
|
+
//# sourceMappingURL=api-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-server.d.ts","sourceRoot":"","sources":["../src/api-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAMnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;OAEG;IACH,YAAY,CAAC,EAAE;QACb,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAE,YAAiB;;;;EAglBrD;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,MAAM,GAAE,YAAiB;;;;GAiB1D"}
|