@tangle-network/sandbox 0.0.0-develop.20260514223840.b2abd84
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/LICENSE +11 -0
- package/README.md +736 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.js +1 -0
- package/dist/client-CO5cnRwn.js +1 -0
- package/dist/collaboration/index.d.ts +2 -0
- package/dist/collaboration/index.js +1 -0
- package/dist/collaboration-CRyb5e8F.js +1 -0
- package/dist/core.d.ts +3 -0
- package/dist/core.js +1 -0
- package/dist/errors-CljiGR__.js +1 -0
- package/dist/errors-Cy5W2uYq.d.ts +1116 -0
- package/dist/index-CCsA3S0D.d.ts +136 -0
- package/dist/index-Cka6RuZB.d.ts +371 -0
- package/dist/index-Dpj1oB5i.d.ts +220 -0
- package/dist/index.d.ts +555 -0
- package/dist/index.js +1 -0
- package/dist/openai/index.d.ts +642 -0
- package/dist/openai/index.js +1 -0
- package/dist/platform-integrations.d.ts +2 -0
- package/dist/platform-integrations.js +1 -0
- package/dist/sandbox-193cSjQB.js +1 -0
- package/dist/sandbox-CTe9fN5m.d.ts +4857 -0
- package/dist/session-gateway/index.d.ts +448 -0
- package/dist/session-gateway/index.js +1 -0
- package/dist/tangle/index.d.ts +2 -0
- package/dist/tangle/index.js +1 -0
- package/dist/tangle-B4_UoyBS.js +1 -0
- package/package.json +105 -0
package/README.md
ADDED
|
@@ -0,0 +1,736 @@
|
|
|
1
|
+
# @tangle-network/sandbox
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for the Tangle Sandbox platform. Create isolated dev containers, run AI agents, and build automation workflows.
|
|
4
|
+
|
|
5
|
+
A separate CLI is published as `@tangle-network/sandbox-cli`.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @tangle-network/sandbox
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @tangle-network/sandbox
|
|
13
|
+
# or
|
|
14
|
+
yarn add @tangle-network/sandbox
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { Sandbox } from "@tangle-network/sandbox";
|
|
21
|
+
|
|
22
|
+
// Initialize the client
|
|
23
|
+
const client = new Sandbox({
|
|
24
|
+
apiKey: "sk_sandbox_...",
|
|
25
|
+
baseUrl: "https://your-sandbox-api.example.com",
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Create a sandbox
|
|
29
|
+
const box = await client.create({
|
|
30
|
+
name: "my-project",
|
|
31
|
+
image: "node:20",
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Execute commands
|
|
35
|
+
const result = await box.exec("npm install && npm test");
|
|
36
|
+
console.log(result.stdout);
|
|
37
|
+
|
|
38
|
+
// Run an AI agent task
|
|
39
|
+
const task = await box.task("Fix any failing tests and commit the changes");
|
|
40
|
+
console.log(task.response);
|
|
41
|
+
|
|
42
|
+
// Clean up
|
|
43
|
+
await box.delete();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
- **Sandbox Management** - Create, list, stop, resume, and delete sandboxes
|
|
49
|
+
- **Command Execution** - Run shell commands in isolated containers
|
|
50
|
+
- **AI Agent Tasks** - Multi-turn agent execution with automatic tool use
|
|
51
|
+
- **Snapshots** - Save and restore sandbox state
|
|
52
|
+
- **BYOS3** - Bring your own S3 storage for snapshots
|
|
53
|
+
- **Batch Execution** - Run tasks across multiple sandboxes in parallel
|
|
54
|
+
- **Event Streaming** - Real-time SSE streams for agent events
|
|
55
|
+
- **Collaboration Foundations** - Token issuance and document identity helpers for collaborative editing
|
|
56
|
+
- **Trace Intelligence** - Export raw sandbox and fleet traces, derived insights, timing metrics, and OTEL JSON to customer-owned observability systems
|
|
57
|
+
|
|
58
|
+
## Collaboration Foundations
|
|
59
|
+
|
|
60
|
+
The SDK now includes the first collaboration primitives for product backends:
|
|
61
|
+
|
|
62
|
+
- collaboration token issuance from `@tangle-network/sandbox/auth`
|
|
63
|
+
- stable document identity helpers from `@tangle-network/sandbox/collaboration`
|
|
64
|
+
- a collaboration API client for bootstrap / token refresh / snapshot calls
|
|
65
|
+
- a headless file bridge for syncing document adapters with sandbox files
|
|
66
|
+
|
|
67
|
+
Example:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import {
|
|
71
|
+
buildCollaborationDocumentId,
|
|
72
|
+
} from "@tangle-network/sandbox/collaboration";
|
|
73
|
+
import { ProductTokenIssuer } from "@tangle-network/sandbox/auth";
|
|
74
|
+
|
|
75
|
+
const issuer = new ProductTokenIssuer({
|
|
76
|
+
productId: "gtm-agent",
|
|
77
|
+
signingSecret: process.env.SANDBOX_SIGNING_SECRET!,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const documentId = buildCollaborationDocumentId({
|
|
81
|
+
workspaceId: "ws_123",
|
|
82
|
+
relativePath: "system/operating-system.md",
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const { token, expiresAt } = issuer.issueCollaboration({
|
|
86
|
+
userId: "user_123",
|
|
87
|
+
sessionId: "sess_456",
|
|
88
|
+
projectId: "ws_123",
|
|
89
|
+
documentId,
|
|
90
|
+
access: "write",
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Bootstrap and snapshot helpers:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { CollaborationClient } from "@tangle-network/sandbox/collaboration";
|
|
98
|
+
|
|
99
|
+
const collab = new CollaborationClient({
|
|
100
|
+
baseUrl: "https://app.example.com",
|
|
101
|
+
headers: () => ({
|
|
102
|
+
Authorization: `Bearer ${sessionToken}`,
|
|
103
|
+
}),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const bootstrap = await collab.bootstrap({
|
|
107
|
+
workspaceId: "ws_123",
|
|
108
|
+
relativePath: "system/operating-system.md",
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The bridge and client are SDK-side primitives. Product/backend endpoints and CRDT runtime integration still need to be wired by the application.
|
|
113
|
+
|
|
114
|
+
## Trace Intelligence
|
|
115
|
+
|
|
116
|
+
Sandboxes and fleet runs return raw trace evidence by default. The derived intelligence envelope is opt-in unless you call the explicit intelligence helper. The raw trace is the source of truth; the intelligence envelope is the platform's current analysis over that evidence.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const boxBundle = await box.trace(); // { trace }
|
|
120
|
+
const boxInsights = await box.intelligence(); // intelligence only
|
|
121
|
+
const boxBundleWithInsights = await box.trace({ includeIntelligence: true });
|
|
122
|
+
|
|
123
|
+
const run = await fleet.dispatchExecWithInsights("pytest -q", {
|
|
124
|
+
machines: ["worker-1", "worker-2"],
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
console.log(run.trace.events);
|
|
128
|
+
console.log(run.intelligence.signals);
|
|
129
|
+
console.log(run.intelligence.recommendedActions);
|
|
130
|
+
|
|
131
|
+
const bundle = await fleet.trace(); // { trace }
|
|
132
|
+
const insights = await fleet.intelligence(); // intelligence only
|
|
133
|
+
const bundleWithInsights = await fleet.trace({ includeIntelligence: true });
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Sandbox traces cover lifecycle, runtime, usage, timing, and current health snapshots. Fleet traces add machine lifecycle, workspace state, dispatch results, fanout timings, and critical path. The intelligence envelope tells you what to inspect next: reliability, parallelism efficiency for fleets, dispatch failure classes, resource attribution, timing bottlenecks, and recommended actions.
|
|
137
|
+
|
|
138
|
+
The built-in intelligence in this SDK is deterministic platform analysis. It reports `billing.billable: false` and `billing.costUsd: 0`; customers are not charged for generating these envelopes. Products opt into generated intelligence per trace request with `includeIntelligence: true` or by calling `intelligence()`.
|
|
139
|
+
|
|
140
|
+
Customers can keep the complete raw payload or forward it to their own observability stack. Use `format: "tangle"` to preserve the native envelope, or `format: "otel-json"` for OpenTelemetry-style collectors and platforms that accept OTLP JSON.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
await box.exportTrace({
|
|
144
|
+
url: "https://collector.example.com/traces",
|
|
145
|
+
headers: { Authorization: `Bearer ${process.env.OBSERVABILITY_TOKEN}` },
|
|
146
|
+
format: "otel-json",
|
|
147
|
+
serviceName: "research-agent",
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
await fleet.exportTrace({
|
|
151
|
+
url: "https://collector.example.com/traces",
|
|
152
|
+
headers: { Authorization: `Bearer ${process.env.OBSERVABILITY_TOKEN}` },
|
|
153
|
+
format: "otel-json",
|
|
154
|
+
serviceName: "research-agent",
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
For Braintrust, Lemma, Raindrop, Langfuse, Datadog, or a custom warehouse, keep this as a customer-owned sink or webhook. Tangle does not need their vendor credentials: fetch `box.trace()` or `fleet.trace()` and send the raw bundle through their SDK/API, or point `exportTrace()` at a small ingest endpoint that transforms it into the vendor's preferred schema.
|
|
159
|
+
|
|
160
|
+
Agent tools should expose both `trace` and `intelligence` actions on `manageSandboxes`. `trace` returns the full raw bundle for downstream analysis; `intelligence` returns the compact agent-readable next-step summary.
|
|
161
|
+
|
|
162
|
+
## Core Concepts
|
|
163
|
+
|
|
164
|
+
### Sandboxes
|
|
165
|
+
|
|
166
|
+
A sandbox is an isolated dev container with:
|
|
167
|
+
- A programmatic runtime API
|
|
168
|
+
- Optional SSH access
|
|
169
|
+
- Optional web terminal
|
|
170
|
+
- Persistent storage with snapshots
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
const box = await client.create({
|
|
174
|
+
name: "my-sandbox",
|
|
175
|
+
image: "python:3.12",
|
|
176
|
+
env: { DEBUG: "true" },
|
|
177
|
+
sshEnabled: true,
|
|
178
|
+
maxLifetimeSeconds: 7200, // 2 hours
|
|
179
|
+
idleTimeoutSeconds: 1800, // 30 min idle timeout
|
|
180
|
+
resources: {
|
|
181
|
+
cpuCores: 2,
|
|
182
|
+
memoryMB: 4096,
|
|
183
|
+
diskGB: 20,
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Status Lifecycle
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
pending -> provisioning -> running -> stopped -> deleted
|
|
192
|
+
|
|
|
193
|
+
v
|
|
194
|
+
failed
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## API Reference
|
|
198
|
+
|
|
199
|
+
### Client
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { Sandbox } from "@tangle-network/sandbox";
|
|
203
|
+
|
|
204
|
+
const client = new Sandbox({
|
|
205
|
+
apiKey: "sk_sandbox_...",
|
|
206
|
+
baseUrl: "https://your-sandbox-api.example.com", // required
|
|
207
|
+
timeoutMs: 30000, // optional
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### `client.create(options?)`
|
|
212
|
+
|
|
213
|
+
Create a new sandbox.
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
const box = await client.create({
|
|
217
|
+
name: "my-project",
|
|
218
|
+
image: "node:20", // or "typescript" for pre-built image
|
|
219
|
+
agentIdentifier: "my-agent", // agent to run
|
|
220
|
+
env: { NODE_ENV: "development" },
|
|
221
|
+
sshEnabled: true,
|
|
222
|
+
sshPublicKey: "ssh-ed25519 AAAA...",
|
|
223
|
+
webTerminalEnabled: true,
|
|
224
|
+
maxLifetimeSeconds: 3600,
|
|
225
|
+
idleTimeoutSeconds: 900,
|
|
226
|
+
resources: {
|
|
227
|
+
cpuCores: 2,
|
|
228
|
+
memoryMB: 4096,
|
|
229
|
+
diskGB: 20,
|
|
230
|
+
},
|
|
231
|
+
metadata: { team: "platform" },
|
|
232
|
+
// BYOS3: Customer-provided storage
|
|
233
|
+
storage: {
|
|
234
|
+
type: "s3",
|
|
235
|
+
bucket: "my-snapshots",
|
|
236
|
+
region: "us-east-1",
|
|
237
|
+
credentials: {
|
|
238
|
+
accessKeyId: "AKIA...",
|
|
239
|
+
secretAccessKey: "...",
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
fromSnapshot: "snap_abc123", // restore from snapshot
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
#### `client.list(options?)`
|
|
247
|
+
|
|
248
|
+
List all sandboxes.
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
const sandboxes = await client.list({
|
|
252
|
+
status: "running", // filter by status
|
|
253
|
+
limit: 10,
|
|
254
|
+
offset: 0,
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
#### `client.get(id)`
|
|
259
|
+
|
|
260
|
+
Get a sandbox by ID.
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
const box = await client.get("sandbox_abc123");
|
|
264
|
+
if (box) {
|
|
265
|
+
console.log(box.status);
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
#### `client.usage()`
|
|
270
|
+
|
|
271
|
+
Get account usage information.
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
const usage = await client.usage();
|
|
275
|
+
console.log(`Active: ${usage.activeSandboxes}`);
|
|
276
|
+
console.log(`Compute: ${usage.computeMinutes} minutes`);
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
#### `client.runBatch(tasks, options?)`
|
|
280
|
+
|
|
281
|
+
Run tasks across multiple sandboxes in parallel.
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
const result = await client.runBatch([
|
|
285
|
+
{ id: "task-1", message: "Analyze code quality" },
|
|
286
|
+
{ id: "task-2", message: "Run security scan" },
|
|
287
|
+
{ id: "task-3", message: "Generate documentation" },
|
|
288
|
+
], {
|
|
289
|
+
timeoutMs: 300000,
|
|
290
|
+
scalingMode: "balanced", // "fastest" | "balanced" | "cheapest"
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
console.log(`Success rate: ${result.successRate}%`);
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Sandbox Instance
|
|
297
|
+
|
|
298
|
+
After creating or retrieving a sandbox, you get a `SandboxInstance` with these methods:
|
|
299
|
+
|
|
300
|
+
#### `box.exec(command, options?)`
|
|
301
|
+
|
|
302
|
+
Execute a shell command.
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
const result = await box.exec("npm install", {
|
|
306
|
+
cwd: "/workspace",
|
|
307
|
+
env: { CI: "true" },
|
|
308
|
+
timeoutMs: 60000,
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
console.log(result.exitCode); // 0
|
|
312
|
+
console.log(result.stdout);
|
|
313
|
+
console.log(result.stderr);
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
#### `box.prompt(message, options?)`
|
|
317
|
+
|
|
318
|
+
Send a single prompt to the AI agent.
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
const result = await box.prompt("What files are in this project?", {
|
|
322
|
+
sessionId: "session_123", // for conversation continuity
|
|
323
|
+
model: "anthropic/claude-sonnet-4-20250514",
|
|
324
|
+
timeoutMs: 120000,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
console.log(result.response);
|
|
328
|
+
console.log(result.usage); // { inputTokens, outputTokens }
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
#### Backend Selection
|
|
332
|
+
|
|
333
|
+
Each sandbox runs one AI backend. Pass `backend.type` to choose it:
|
|
334
|
+
|
|
335
|
+
| Type | Runtime | When to use |
|
|
336
|
+
|------|---------|-------------|
|
|
337
|
+
| `opencode` | [OpenCode](https://github.com/anomalyco/opencode) | Default. Multi-provider, profile system, MCP support |
|
|
338
|
+
| `claude-code` | [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | Anthropic-native. Needs ANTHROPIC_API_KEY |
|
|
339
|
+
| `codex` | [Codex CLI](https://github.com/openai/codex) | OpenAI-native. Needs OPENAI_API_KEY |
|
|
340
|
+
| `cursor` | [Cursor Agent SDK](https://cursor.com/changelog/sdk-release) | Cursor-native local/cloud agent. Needs CURSOR_API_KEY |
|
|
341
|
+
| `amp` | [AMP](https://sourcegraph.com/amp) | Sourcegraph AMP agent |
|
|
342
|
+
| `factory-droids` | [Factory](https://factory.ai) | Factory Droid agent |
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// Use Claude Code backend
|
|
346
|
+
await box.prompt("Fix the auth bug", {
|
|
347
|
+
backend: { type: "claude-code" },
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Use Codex with a named profile
|
|
351
|
+
await box.prompt("Audit this repo", {
|
|
352
|
+
backend: { type: "codex", profile: "browser-codex-fast" },
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// Use Cursor Agent SDK
|
|
356
|
+
await box.prompt("Implement this change", {
|
|
357
|
+
backend: {
|
|
358
|
+
type: "cursor",
|
|
359
|
+
model: {
|
|
360
|
+
model: "composer-2",
|
|
361
|
+
apiKey: process.env.CURSOR_API_KEY,
|
|
362
|
+
},
|
|
363
|
+
profile: {
|
|
364
|
+
name: "cursor-release-agent",
|
|
365
|
+
prompt: {
|
|
366
|
+
systemPrompt:
|
|
367
|
+
"Use repo rules, configured MCP servers, skills, and subagents when relevant.",
|
|
368
|
+
},
|
|
369
|
+
mcp: {
|
|
370
|
+
docs: { transport: "sse", url: "https://docs.example.com/sse" },
|
|
371
|
+
},
|
|
372
|
+
subagents: {
|
|
373
|
+
reviewer: {
|
|
374
|
+
description: "Reviews changes for correctness and missing tests.",
|
|
375
|
+
prompt: "Review the current diff. Return only blocking findings.",
|
|
376
|
+
model: "composer-2",
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
resources: {
|
|
380
|
+
instructions: "Run focused tests before reporting completion.",
|
|
381
|
+
skills: [
|
|
382
|
+
{
|
|
383
|
+
kind: "inline",
|
|
384
|
+
name: "release-check",
|
|
385
|
+
content:
|
|
386
|
+
"---\nname: release-check\ndescription: Validate release readiness.\n---\nRun typecheck and focused tests.",
|
|
387
|
+
},
|
|
388
|
+
],
|
|
389
|
+
},
|
|
390
|
+
extensions: {
|
|
391
|
+
cursor: {
|
|
392
|
+
runtime: "local",
|
|
393
|
+
local: { settingSources: ["project", "user"] },
|
|
394
|
+
force: true,
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Use OpenCode with an inline profile
|
|
402
|
+
await box.prompt("Audit this repo", {
|
|
403
|
+
backend: {
|
|
404
|
+
type: "opencode",
|
|
405
|
+
profile: {
|
|
406
|
+
name: "security-auditor",
|
|
407
|
+
prompt: {
|
|
408
|
+
systemPrompt: "Focus on authorization and sandbox boundary mistakes.",
|
|
409
|
+
},
|
|
410
|
+
tools: { bash: true },
|
|
411
|
+
permissions: { bash: "allow" },
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
// BYOK (Bring Your Own Key)
|
|
417
|
+
await box.prompt("Analyze this", {
|
|
418
|
+
backend: {
|
|
419
|
+
type: "opencode",
|
|
420
|
+
model: {
|
|
421
|
+
provider: "anthropic",
|
|
422
|
+
model: "claude-sonnet-4-20250514",
|
|
423
|
+
apiKey: process.env.MY_ANTHROPIC_KEY,
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
The SDK serializes `backend.profile` into the required wire format automatically.
|
|
430
|
+
|
|
431
|
+
Cursor profiles map portable MCP, resources, skills, subagents, hooks, permissions,
|
|
432
|
+
and Cursor-native `extensions.cursor` fields into the Cursor Agent SDK. Local
|
|
433
|
+
Cursor runs materialize `.cursor/*` project files inside the sandbox workspace.
|
|
434
|
+
Cloud Cursor runs fail closed when given uncommitted local resources that cannot
|
|
435
|
+
be delivered to the remote Cursor workspace.
|
|
436
|
+
|
|
437
|
+
Provider-native metadata is available through `box.backend` when the backend
|
|
438
|
+
SDK exposes it:
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
const account = await box.backend.account();
|
|
442
|
+
const models = await box.backend.models();
|
|
443
|
+
const repositories = await box.backend.repositories();
|
|
444
|
+
const agents = await box.backend.agents({ limit: 20 });
|
|
445
|
+
const agent = await box.backend.agent(agents.items[0].agentId);
|
|
446
|
+
const runs = await box.backend.runs(agent.agentId, { limit: 20 });
|
|
447
|
+
const run = await box.backend.run(runs.items[0].id, {
|
|
448
|
+
agentId: agent.agentId,
|
|
449
|
+
});
|
|
450
|
+
const messages = await box.backend.agentMessages(agent.agentId, { limit: 50 });
|
|
451
|
+
const artifacts = await box.backend.artifacts("active-session-id");
|
|
452
|
+
const bytes = await box.backend.downloadArtifact(
|
|
453
|
+
"active-session-id",
|
|
454
|
+
artifacts[0].path,
|
|
455
|
+
);
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
Unsupported provider-control methods return the backend error; the SDK does not
|
|
459
|
+
fabricate catalog, run, or artifact data for backends that do not expose it.
|
|
460
|
+
|
|
461
|
+
#### `box.task(message, options?)`
|
|
462
|
+
|
|
463
|
+
Run a multi-turn agent task. The agent keeps working until completion.
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
const result = await box.task("Set up a REST API with authentication", {
|
|
467
|
+
maxTurns: 20, // limit turns (0 = unlimited)
|
|
468
|
+
sessionId: "...", // continue previous session
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
console.log(result.turnsUsed);
|
|
472
|
+
console.log(result.response);
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
#### `box.streamPrompt(message, options?)`
|
|
476
|
+
|
|
477
|
+
Stream agent events in real-time.
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
for await (const event of box.streamPrompt("Explain this codebase")) {
|
|
481
|
+
switch (event.type) {
|
|
482
|
+
case "message.part.updated": {
|
|
483
|
+
const part = event.data.part as { type?: string; text?: string };
|
|
484
|
+
if (part.type === "text" && event.data.delta) {
|
|
485
|
+
process.stdout.write(String(event.data.delta));
|
|
486
|
+
}
|
|
487
|
+
if (part.type === "reasoning" && event.data.delta) {
|
|
488
|
+
// Reasoning/thinking tokens from extended thinking models
|
|
489
|
+
process.stderr.write(`[thinking] ${String(event.data.delta)}`);
|
|
490
|
+
}
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
case "result":
|
|
494
|
+
console.log("\nFinal:", event.data.finalText);
|
|
495
|
+
break;
|
|
496
|
+
case "done":
|
|
497
|
+
console.log("\nComplete!");
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
#### `box.streamTask(message, options?)`
|
|
504
|
+
|
|
505
|
+
Stream a multi-turn task with real-time events.
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
for await (const event of box.streamTask("Build a CLI tool")) {
|
|
509
|
+
// Handle events...
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
#### `box.direct()`
|
|
514
|
+
|
|
515
|
+
Create an explicit advanced direct-runtime view of the sandbox.
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
const directBox = box.direct();
|
|
519
|
+
const result = await directBox.exec("npm test");
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
#### `box.events(options?)`
|
|
523
|
+
|
|
524
|
+
Subscribe to sandbox lifecycle events.
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
for await (const event of box.events({ signal: controller.signal })) {
|
|
528
|
+
console.log(`Event: ${event.type}`, event.data);
|
|
529
|
+
}
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### Snapshots
|
|
533
|
+
|
|
534
|
+
#### `box.snapshot(options?)`
|
|
535
|
+
|
|
536
|
+
Create a snapshot of the sandbox state.
|
|
537
|
+
|
|
538
|
+
```typescript
|
|
539
|
+
const snapshot = await box.snapshot({
|
|
540
|
+
tags: ["v1.0", "stable"],
|
|
541
|
+
paths: ["/workspace"], // specific paths (default: all)
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
console.log(snapshot.snapshotId);
|
|
545
|
+
console.log(snapshot.sizeBytes);
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
#### `box.listSnapshots()`
|
|
549
|
+
|
|
550
|
+
List all snapshots for this sandbox.
|
|
551
|
+
|
|
552
|
+
```typescript
|
|
553
|
+
const snapshots = await box.listSnapshots();
|
|
554
|
+
for (const snap of snapshots) {
|
|
555
|
+
console.log(`${snap.snapshotId}: ${snap.createdAt}`);
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### BYOS3 (Bring Your Own S3)
|
|
560
|
+
|
|
561
|
+
Store snapshots in your own S3-compatible storage. Supports AWS S3, Google Cloud Storage, and Cloudflare R2.
|
|
562
|
+
|
|
563
|
+
#### Creating a sandbox with BYOS3
|
|
564
|
+
|
|
565
|
+
```typescript
|
|
566
|
+
const box = await client.create({
|
|
567
|
+
name: "my-sandbox",
|
|
568
|
+
storage: {
|
|
569
|
+
type: "s3", // "s3" | "gcs" | "r2"
|
|
570
|
+
bucket: "my-snapshots",
|
|
571
|
+
region: "us-east-1",
|
|
572
|
+
endpoint: "https://s3.us-east-1.amazonaws.com", // optional
|
|
573
|
+
credentials: {
|
|
574
|
+
accessKeyId: "AKIA...",
|
|
575
|
+
secretAccessKey: "...",
|
|
576
|
+
},
|
|
577
|
+
prefix: "sandbox-snapshots/", // optional path prefix
|
|
578
|
+
},
|
|
579
|
+
fromSnapshot: "snap_abc123", // restore from your storage
|
|
580
|
+
});
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
#### Snapshots with BYOS3
|
|
584
|
+
|
|
585
|
+
When storage is configured, snapshots are written directly to your bucket:
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
// Create snapshot to your S3
|
|
589
|
+
const snap = await box.snapshot({
|
|
590
|
+
tags: ["production"],
|
|
591
|
+
storage: {
|
|
592
|
+
type: "s3",
|
|
593
|
+
bucket: "my-snapshots",
|
|
594
|
+
credentials: { accessKeyId: "...", secretAccessKey: "..." },
|
|
595
|
+
},
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
// List snapshots from your S3
|
|
599
|
+
const snapshots = await box.listSnapshots({
|
|
600
|
+
type: "s3",
|
|
601
|
+
bucket: "my-snapshots",
|
|
602
|
+
credentials: { ... },
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
// Restore from your S3
|
|
606
|
+
await box.restoreFromStorage({
|
|
607
|
+
type: "s3",
|
|
608
|
+
bucket: "my-snapshots",
|
|
609
|
+
credentials: { ... },
|
|
610
|
+
});
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
### Runtime Routing
|
|
614
|
+
|
|
615
|
+
By default, SDK runtime calls like `exec()`, `prompt()`, `task()`, file ops, git, and process management go through the Sandbox API, which dispatches them to the correct running sandbox for the authenticated user.
|
|
616
|
+
|
|
617
|
+
Runtime agent calls can pass a named backend profile or an inline provider-neutral profile object:
|
|
618
|
+
|
|
619
|
+
```typescript
|
|
620
|
+
const result = await box.prompt("Audit the authentication flow", {
|
|
621
|
+
backend: {
|
|
622
|
+
profile: {
|
|
623
|
+
name: "security-auditor",
|
|
624
|
+
prompt: {
|
|
625
|
+
systemPrompt: "You are a senior application security auditor.",
|
|
626
|
+
instructions: ["Prioritize authz, tenancy, and secret handling."],
|
|
627
|
+
},
|
|
628
|
+
tools: { bash: true },
|
|
629
|
+
permissions: { bash: "allow" },
|
|
630
|
+
},
|
|
631
|
+
},
|
|
632
|
+
});
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
### Direct Runtime Access
|
|
636
|
+
|
|
637
|
+
For advanced use cases, use `box.direct()` to make runtime calls directly against the sandbox runtime while keeping normal lifecycle methods on the API client:
|
|
638
|
+
|
|
639
|
+
```typescript
|
|
640
|
+
const directBox = box.direct();
|
|
641
|
+
const result = await directBox.exec("npm test");
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
This is the recommended advanced path for power users who want runtime-level access without re-implementing auth/header plumbing themselves.
|
|
645
|
+
|
|
646
|
+
### Lifecycle Methods
|
|
647
|
+
|
|
648
|
+
```typescript
|
|
649
|
+
// Stop (preserves state)
|
|
650
|
+
await box.stop();
|
|
651
|
+
|
|
652
|
+
// Resume
|
|
653
|
+
await box.resume();
|
|
654
|
+
|
|
655
|
+
// Delete (destroys everything)
|
|
656
|
+
await box.delete();
|
|
657
|
+
|
|
658
|
+
// Refresh status from API
|
|
659
|
+
await box.refresh();
|
|
660
|
+
|
|
661
|
+
// Wait for specific status
|
|
662
|
+
await box.waitFor("running", { timeoutMs: 60000 });
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
### Properties
|
|
666
|
+
|
|
667
|
+
```typescript
|
|
668
|
+
box.id // Unique identifier
|
|
669
|
+
box.name // Human-readable name
|
|
670
|
+
box.status // "pending" | "provisioning" | "running" | "stopped" | "failed"
|
|
671
|
+
box.connection // raw direct connection info for advanced use
|
|
672
|
+
box.metadata // Custom metadata
|
|
673
|
+
box.createdAt // Date
|
|
674
|
+
box.startedAt // Date | undefined
|
|
675
|
+
box.lastActivityAt // Date | undefined
|
|
676
|
+
box.expiresAt // Date | undefined
|
|
677
|
+
box.error // Error message if failed
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
## Error Handling
|
|
681
|
+
|
|
682
|
+
```typescript
|
|
683
|
+
import {
|
|
684
|
+
AuthError,
|
|
685
|
+
NetworkError,
|
|
686
|
+
NotFoundError,
|
|
687
|
+
QuotaError,
|
|
688
|
+
StateError,
|
|
689
|
+
TimeoutError,
|
|
690
|
+
ValidationError,
|
|
691
|
+
} from "@tangle-network/sandbox";
|
|
692
|
+
|
|
693
|
+
try {
|
|
694
|
+
await box.exec("npm test");
|
|
695
|
+
} catch (err) {
|
|
696
|
+
if (err instanceof TimeoutError) {
|
|
697
|
+
console.log("Command timed out");
|
|
698
|
+
} else if (err instanceof StateError) {
|
|
699
|
+
console.log(`Invalid state: ${err.currentState}`);
|
|
700
|
+
} else if (err instanceof NetworkError) {
|
|
701
|
+
console.log("Connection failed");
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
## TypeScript
|
|
707
|
+
|
|
708
|
+
Full TypeScript support with exported types:
|
|
709
|
+
|
|
710
|
+
```typescript
|
|
711
|
+
import type {
|
|
712
|
+
SandboxClientConfig,
|
|
713
|
+
CreateSandboxOptions,
|
|
714
|
+
SandboxInfo,
|
|
715
|
+
SandboxStatus,
|
|
716
|
+
SandboxConnection,
|
|
717
|
+
ExecResult,
|
|
718
|
+
ExecOptions,
|
|
719
|
+
PromptResult,
|
|
720
|
+
PromptOptions,
|
|
721
|
+
TaskResult,
|
|
722
|
+
TaskOptions,
|
|
723
|
+
SnapshotResult,
|
|
724
|
+
SnapshotOptions,
|
|
725
|
+
SnapshotInfo,
|
|
726
|
+
StorageConfig,
|
|
727
|
+
BatchTask,
|
|
728
|
+
BatchResult,
|
|
729
|
+
BatchOptions,
|
|
730
|
+
UsageInfo,
|
|
731
|
+
} from "@tangle-network/sandbox";
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
## License
|
|
735
|
+
|
|
736
|
+
MIT
|