@tangle-network/sandbox 0.10.4 → 0.10.5-develop.20260715115519.fee3d86

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/README.md CHANGED
@@ -206,9 +206,54 @@ client.connect();
206
206
  detection, replay-on-reconnect, and `lastEventId` persistence. None of this requires a
207
207
  Durable Object.
208
208
 
209
+ Server-originated product events can arrive before the browser connects:
210
+
211
+ ```typescript
212
+ await client.broadcastSessionAgentEvent(sessionId, {
213
+ type: "project.provision_progress",
214
+ properties: { message: "Creating workspace" },
215
+ });
216
+ ```
217
+
218
+ The public API binds that pending queue to the authenticated product and Sandbox account and commits it to Redis before returning `success: true`.
219
+ A later connection by the same owner leases and acknowledges the ordered queue; another owner receives none of it.
220
+ A gateway crash before acknowledgement redelivers the same event IDs, which the default `SessionGatewayClient` deduplicates before invoking handlers.
221
+
209
222
  See `examples/cf-worker-chat.ts`, `examples/browser-streaming-resume.ts`, and
210
223
  `examples/reconnect-from-last-event-id.ts` for end-to-end patterns.
211
224
 
225
+ ### Browser-safe scoped runtime client
226
+
227
+ `@tangle-network/sandbox/runtime` is a browser- and Worker-safe entry point
228
+ (no `node:` imports) for talking directly to one sandbox's runtime with a
229
+ short-lived scoped token — never the account-level Sandbox API key — minted
230
+ server-side via `box.mintScopedToken({ scope: "session-runtime", sessionId })`
231
+ or `box.mintScopedToken({ scope: "read-only" })`.
232
+
233
+ ```typescript
234
+ import { createSandboxRuntimeClient } from "@tangle-network/sandbox/runtime";
235
+
236
+ const runtime = createSandboxRuntimeClient({
237
+ baseUrl: scopedToken.sidecarProxyUrl, // browser-safe proxy base from box.mintScopedToken()
238
+ token: scopedToken.token,
239
+ });
240
+
241
+ await runtime.writeFile("/workspace/notes.md", "# from the browser");
242
+
243
+ // Binary-safe, chunked upload for payloads over ~1 MiB — same engine and
244
+ // size contract as SandboxInstance.fs.uploadData, wired to the scoped token
245
+ const blob = await fileInput.files[0].arrayBuffer();
246
+ await runtime.uploadData("/workspace/uploads/file.bin", blob, {
247
+ onProgress: (p) => console.log(`${p.percentage.toFixed(1)}%`),
248
+ });
249
+ ```
250
+
251
+ Every method on the scoped client — `writeFile`, `readFiles`, `uploadData`,
252
+ terminals, sessions — carries the same `Authorization: Bearer <token>` header
253
+ and base URL prefix, so the request conventions match whether the token
254
+ scope allows the call or not; a denied call fails with the typed
255
+ `SandboxError`, not a silent no-op.
256
+
212
257
  ## Features
213
258
 
214
259
  - **Sandbox Management** - Create, list, stop, resume, and delete sandboxes
@@ -714,6 +759,70 @@ for await (const event of box.events({ signal: controller.signal })) {
714
759
  }
715
760
  ```
716
761
 
762
+ ### File I/O
763
+
764
+ | Method | Environment | Payload | Notes |
765
+ |---|---|---|---|
766
+ | `fs.read(path)` / `fs.write(path, content, options?)` | Node + browser | UTF-8 string, or base64 via `options.encoding` | Single request — see the size contract below for the cap |
767
+ | `fs.writeMany(files, options?)` | Node + browser | Same as `write`; per-file `encoding` | Paced, retry-aware batch through `fs.write`'s endpoint; fails loud on the first file that can't be written |
768
+ | `fs.upload(localPath, remotePath, options?)` | Node only | Local file path | Reads the local file with `node:fs`; not available in a browser tab |
769
+ | `fs.uploadData(remotePath, data, options?)` | Node + browser | `Blob \| ArrayBuffer \| ArrayBufferView \| string` | Browser/Worker-safe, binary-safe — the sanctioned path for payloads over ~1 MiB (see below) |
770
+ | `fs.download(remotePath, localPath, options?)` | Node only | — | Writes to a local path with `node:fs` |
771
+ | `fs.uploadDir` / `fs.downloadDir` | Node only | Local directory path | tar-based directory transfer |
772
+ | `fs.list` / `fs.stat` / `fs.mkdir` / `fs.delete` / `fs.rename` / `fs.exists` | Node + browser | — | Directory listing and file metadata operations |
773
+
774
+ ```typescript
775
+ // Text write/read (UTF-8 by default)
776
+ await box.fs.write("/workspace/config.json", JSON.stringify(config));
777
+ const raw = await box.fs.read("/workspace/config.json");
778
+
779
+ // Base64 for binary content through the same single-shot endpoint
780
+ await box.fs.write("/workspace/icon.png", base64Png, { encoding: "base64" });
781
+
782
+ // Batch-write many small files (paced, retried)
783
+ await box.fs.writeMany([
784
+ { path: "skills/review.md", content: reviewSkill },
785
+ { path: "assets/logo.png", content: base64Logo, encoding: "base64" },
786
+ ]);
787
+
788
+ // Node-only: upload a local file
789
+ await box.fs.upload("./model.bin", "/workspace/models/model.bin");
790
+
791
+ // Browser/Worker-safe: upload in-memory data over ~1 MiB (e.g. an
792
+ // <input type="file"> Blob) — chunked and verified end-to-end
793
+ const blob = await fetch("https://example.com/model.bin").then((r) => r.blob());
794
+ await box.fs.uploadData("/workspace/models/model.bin", blob, {
795
+ onProgress: (p) => console.log(`${p.percentage.toFixed(1)}%`),
796
+ });
797
+
798
+ await box.fs.download("/workspace/results.zip", "./results.zip");
799
+ ```
800
+
801
+ `fs.uploadData` is the sanctioned path once a payload clears the single-shot
802
+ write cap: it splits the payload into server-sized parts against a chunked
803
+ upload-session and verifies the whole payload with a SHA-256 at commit, so
804
+ it's safe for arbitrarily-shaped binary content (video, model weights,
805
+ archives) up to the session cap. It works identically whether the sandbox is
806
+ reached through the gateway proxy or a direct sidecar connection
807
+ (`box.direct()`).
808
+
809
+ #### File size contract
810
+
811
+ | Surface | Cap | Constant | Failure |
812
+ |---|---|---|---|
813
+ | Single-shot decoded write (`fs.write`, `fs.writeMany`) | 5 MiB | `FILE_DECODED_WRITE_MAX_BYTES` | `FILE_TOO_LARGE` |
814
+ | Gateway proxy request (wire, any route) | 1 MiB | `SANDBOX_PROXY_REQUEST_MAX_BYTES` | `PAYLOAD_TOO_LARGE` |
815
+ | Direct-runtime write request (wire, `box.direct()` only) | 32 MiB | `SANDBOX_DIRECT_FILE_REQUEST_MAX_BYTES` | `PAYLOAD_TOO_LARGE` |
816
+ | Multipart upload (`fs.upload`) | 5 MiB | `FILE_MULTIPART_UPLOAD_MAX_BYTES` | `FILE_TOO_LARGE` |
817
+ | Chunked upload part (`fs.uploadData`, internal) | 768 KiB | `FILE_UPLOAD_PART_MAX_BYTES` | — (server-sized, transparent to callers) |
818
+ | Chunked upload session (`fs.uploadData`) | 64 MiB | `FILE_UPLOAD_SESSION_MAX_BYTES` | `PAYLOAD_TOO_LARGE` |
819
+
820
+ All constants are exported from `@tangle-network/runtime-contracts`. Use
821
+ `fs.uploadData` for anything above the single-shot caps. `PAYLOAD_TOO_LARGE`
822
+ means split the delivery (use `fs.uploadData` instead of one request);
823
+ `FILE_TOO_LARGE` means the content itself exceeds what that surface accepts,
824
+ no matter how it's delivered.
825
+
717
826
  ### Snapshots
718
827
 
719
828
  #### `box.snapshot(options?)`
@@ -1,6 +1,6 @@
1
- import { D as CodeLanguage, E as CodeExecutionResult, T as CodeExecutionOptions, k as CodeResultPart } from "../types-BvSoEHz8.js";
2
- import { n as SandboxInstance } from "../sandbox-BtNWbfd3.js";
3
- import { i as SandboxClient } from "../client-B8i6qrQr.js";
1
+ import { D as CodeExecutionOptions, O as CodeExecutionResult, j as CodeResultPart, k as CodeLanguage } from "../types-LNLeU9Ub.js";
2
+ import { n as SandboxInstance } from "../sandbox-B9P4tyt4.js";
3
+ import { i as SandboxClient } from "../client-CSaBJ8wD.js";
4
4
  import * as _$_modelcontextprotocol_sdk_server_index_js0 from "@modelcontextprotocol/sdk/server/index.js";
5
5
 
6
6
  //#region src/agent/tools/_specs.d.ts
@@ -1,5 +1,5 @@
1
- import { Bn as SandboxClientConfig, Bt as IntelligenceReport, Cn as PublishPublicTemplateVersionOptions, Di as UsageInfo, Dn as ReconcileSandboxFleetsResult, En as ReconcileSandboxFleetsOptions, F as CreateSandboxFleetOptions, Gn as SandboxFleetArtifactSpec, Hn as SandboxEnvironment, Ht as IntelligenceReportCompareTo, I as CreateSandboxFleetTokenOptions, Jn as SandboxFleetDispatchResponse, Jt as ListSandboxFleetOptions, Kn as SandboxFleetCostEstimate, L as CreateSandboxFleetWithCoordinatorOptions, N as CreateIntelligenceReportOptions, P as CreateRequestOptions, R as CreateSandboxOptions, Sn as PublishPublicTemplateOptions, Sr as SandboxProfileSummary, Tn as ReapExpiredSandboxFleetsResult, Ur as SecretsManager, Vt as IntelligenceReportBudget, Wn as SandboxFleetArtifact, Wt as IntelligenceReportWindow, Yn as SandboxFleetDriverCapability, Yt as ListSandboxOptions, Zn as SandboxFleetInfo, _ as BatchResult, _r as SandboxInfo, _t as FleetExecDispatchResult, a as AttachSandboxFleetMachineOptions, ar as SandboxFleetOperationsSummary, bn as PublicTemplateInfo, bt as FleetPromptDispatchResult, ci as SubscriptionInfo, cr as SandboxFleetTraceBundle, dr as SandboxFleetTraceOptions, et as ExecOptions, fr as SandboxFleetUsage, ft as FleetDispatchCancelResult, g as BatchOptions, gr as SandboxFleetWorkspaceSnapshotResult, gt as FleetExecDispatchOptions, hn as PromptResult, hr as SandboxFleetWorkspaceRestoreResult, ht as FleetDispatchStreamOptions, ii as SshKeysManager, m as BatchEvent, mn as PromptOptions, mr as SandboxFleetWorkspaceReconcileResult, mt as FleetDispatchResultBufferOptions, pn as PromptInputPart, pt as FleetDispatchResultBuffer, rr as SandboxFleetManifest, sr as SandboxFleetToken, tr as SandboxFleetMachineRecord, tt as ExecResult, v as BatchRunOptions, vt as FleetMachineId, wn as ReapExpiredSandboxFleetsOptions, x as BatchTask, xi as TokenRefreshHandler, xn as PublicTemplateVersionInfo, y as BatchRunRequest, yt as FleetPromptDispatchOptions } from "./types-BvSoEHz8.js";
2
- import { a as TraceExportSink, i as TraceExportResult, n as SandboxInstance, t as HttpClient } from "./sandbox-BtNWbfd3.js";
1
+ import { $n as SandboxFleetDriverCapability, An as ReconcileSandboxFleetsResult, B as CreateSandboxOptions, Cn as PublicTemplateInfo, Ct as FleetPromptDispatchResult, Dn as ReapExpiredSandboxFleetsOptions, En as PublishPublicTemplateVersionOptions, Er as SandboxProfileSummary, F as CreateIntelligenceReportOptions, Gt as IntelligenceReportCompareTo, I as CreateRequestOptions, Jn as SandboxFleetArtifact, Kn as SandboxEnvironment, L as CreateSandboxFleetOptions, On as ReapExpiredSandboxFleetsResult, Qn as SandboxFleetDispatchResponse, Qt as ListSandboxOptions, R as CreateSandboxFleetTokenOptions, St as FleetPromptDispatchOptions, Ti as TokenRefreshHandler, Tn as PublishPublicTemplateOptions, Ut as IntelligenceReport, Wn as SandboxClientConfig, Wt as IntelligenceReportBudget, Xn as SandboxFleetCostEstimate, Yn as SandboxFleetArtifactSpec, Zt as ListSandboxFleetOptions, _ as BatchResult, _n as PromptOptions, _t as FleetDispatchResultBufferOptions, a as AttachSandboxFleetMachineOptions, ar as SandboxFleetMachineRecord, br as SandboxFleetWorkspaceSnapshotResult, bt as FleetExecDispatchResult, ci as SshKeysManager, dr as SandboxFleetToken, fi as SubscriptionInfo, fr as SandboxFleetTraceBundle, g as BatchOptions, gn as PromptInputPart, gr as SandboxFleetUsage, gt as FleetDispatchResultBuffer, hr as SandboxFleetTraceOptions, ht as FleetDispatchCancelResult, ji as UsageInfo, kn as ReconcileSandboxFleetsOptions, lr as SandboxFleetOperationsSummary, m as BatchEvent, nt as ExecOptions, qr as SecretsManager, qt as IntelligenceReportWindow, rt as ExecResult, sr as SandboxFleetManifest, tr as SandboxFleetInfo, v as BatchRunOptions, vn as PromptResult, vr as SandboxFleetWorkspaceReconcileResult, vt as FleetDispatchStreamOptions, wn as PublicTemplateVersionInfo, x as BatchTask, xr as SandboxInfo, xt as FleetMachineId, y as BatchRunRequest, yr as SandboxFleetWorkspaceRestoreResult, yt as FleetExecDispatchOptions, z as CreateSandboxFleetWithCoordinatorOptions } from "./types-LNLeU9Ub.js";
2
+ import { a as TraceExportSink, i as TraceExportResult, n as SandboxInstance, t as HttpClient } from "./sandbox-B9P4tyt4.js";
3
3
 
4
4
  //#region src/lib/sse-parser.d.ts
5
5
  /**
@@ -194,7 +194,11 @@ interface SessionBroadcastEvent {
194
194
  data?: unknown;
195
195
  channel?: string;
196
196
  }
197
- /** Result returned after Sandbox accepts or rejects a session event. */
197
+ /**
198
+ * Result returned after Sandbox accepts or rejects a session event.
199
+ * For a session that has not connected yet, `success: true` means the event
200
+ * was durably queued for the authenticated owner.
201
+ */
198
202
  interface SessionBroadcastResult {
199
203
  success: boolean;
200
204
  sessionId: string;