@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.
@@ -1,4 +1,5 @@
1
- import { f as ValidationError, l as ServerError, p as parseErrorResponse } from "./errors-DSz87Rkk.js";
1
+ import { c as SandboxError, d as TimeoutError, f as ValidationError, i as NetworkError, l as ServerError, p as parseErrorResponse, s as QuotaError } from "./errors-wd266B9Q.js";
2
+ import { FILE_UPLOAD_SESSION_MAX_BYTES, isFileTooLargeErrorBody, isPayloadTooLargeErrorBody } from "@tangle-network/runtime-contracts";
2
3
  //#region src/backend-config.ts
3
4
  const LEGACY_QUESTION_ALIAS_KEY = ["question", "Channel"].join("");
4
5
  function parseModelString(model) {
@@ -153,6 +154,238 @@ function serializeForSidecar(profile) {
153
154
  return toBackendProfile(profile);
154
155
  }
155
156
  //#endregion
157
+ //#region src/lib/chunked-upload.ts
158
+ /**
159
+ * Chunked upload — the sanctioned SDK path for delivering payloads larger
160
+ * than the sandbox API's single-request body caps. Splits the payload into
161
+ * server-sized parts against a `/fs/upload-session` session, PUTs each part
162
+ * with transient retry, then commits with a whole-payload SHA-256 so the
163
+ * runtime can verify nothing was dropped or reordered in transit.
164
+ *
165
+ * Browser/Worker-safe: no `node:` imports, no `Buffer`. Input normalizes to
166
+ * a `Uint8Array` and hashes via Web Crypto (`crypto.subtle`), available in
167
+ * browsers, Workers, and Node 18+ — the same environments this module must
168
+ * run in unmodified.
169
+ */
170
+ const DEFAULT_MAX_RETRIES = 4;
171
+ const RETRY_BASE_MS = 250;
172
+ const RETRY_MAX_MS = 2e3;
173
+ const delay = (ms, signal) => new Promise((resolve, reject) => {
174
+ if (signal?.aborted) {
175
+ reject(signal.reason ?? /* @__PURE__ */ new Error("aborted"));
176
+ return;
177
+ }
178
+ const timer = setTimeout(() => {
179
+ signal?.removeEventListener("abort", onAbort);
180
+ resolve();
181
+ }, ms);
182
+ function onAbort() {
183
+ clearTimeout(timer);
184
+ reject(signal?.reason ?? /* @__PURE__ */ new Error("aborted"));
185
+ }
186
+ signal?.addEventListener("abort", onAbort, { once: true });
187
+ });
188
+ /** Transient part-upload failures worth retrying: rate-limit/budget (429),
189
+ * 5xx, transport (network), and request timeouts — mirrors
190
+ * `isTransientWriteError` in `sandbox.ts`. A non-transient error (bad path,
191
+ * auth, checksum, containment) fails loud immediately. */
192
+ function isTransientUploadError(err) {
193
+ return err instanceof QuotaError || err instanceof ServerError || err instanceof NetworkError || err instanceof TimeoutError;
194
+ }
195
+ function isChecksumMismatch(err) {
196
+ return err instanceof SandboxError && err.code === "CHECKSUM_MISMATCH";
197
+ }
198
+ /** Cheap size probe used for the pre-flight cap check, read directly off
199
+ * the input shape (`Blob.size` / `ArrayBuffer.byteLength` / view
200
+ * `byteLength`) so an oversized `Blob` never has its bytes materialized
201
+ * just to be rejected. Strings have no cheap length in UTF-8, so they're
202
+ * encoded once here (and once more in {@link normalizeToBytes} — an
203
+ * acceptable cost given strings are never the multi-MB case this path
204
+ * exists for). */
205
+ function sizeOfInput(data) {
206
+ if (typeof data === "string") return new TextEncoder().encode(data).byteLength;
207
+ if (data instanceof ArrayBuffer) return data.byteLength;
208
+ if (ArrayBuffer.isView(data)) return data.byteLength;
209
+ return data.size;
210
+ }
211
+ /**
212
+ * Normalizes to a `Uint8Array` backed by a plain (never shared)
213
+ * `ArrayBuffer`. A caller-supplied view may be backed by a
214
+ * `SharedArrayBuffer`, which `crypto.subtle.digest` and `fetch`'s
215
+ * `BodyInit` both reject at the type level (and some engines reject at
216
+ * runtime) — copying here keeps every downstream call on one concrete,
217
+ * always-accepted buffer type.
218
+ */
219
+ async function normalizeToBytes(data) {
220
+ if (typeof data === "string") return new TextEncoder().encode(data);
221
+ if (data instanceof ArrayBuffer) return new Uint8Array(data.slice(0));
222
+ if (ArrayBuffer.isView(data)) {
223
+ const copy = new Uint8Array(data.byteLength);
224
+ copy.set(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
225
+ return copy;
226
+ }
227
+ return new Uint8Array(await data.arrayBuffer());
228
+ }
229
+ function webCryptoSubtle() {
230
+ const subtle = globalThis.crypto?.subtle;
231
+ if (!subtle) throw new Error("chunked upload requires Web Crypto (crypto.subtle) for SHA-256 hashing — available in browsers, Workers, and Node 18+");
232
+ return subtle;
233
+ }
234
+ async function sha256Hex(bytes) {
235
+ const digest = await webCryptoSubtle().digest("SHA-256", bytes);
236
+ return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
237
+ }
238
+ /**
239
+ * Canonical size-cap bodies (413 `PAYLOAD_TOO_LARGE` / `FILE_TOO_LARGE`)
240
+ * carry `surface`/`limitBytes`/`actualBytes` that the generic error parser
241
+ * drops; fold them into the message before delegating to
242
+ * `parseErrorResponse` for the rest (status→class mapping, retryAfterMs,
243
+ * origin).
244
+ */
245
+ async function parseUploadError(response) {
246
+ const bodyText = await response.text();
247
+ let parsedBody;
248
+ try {
249
+ parsedBody = JSON.parse(bodyText);
250
+ } catch {
251
+ parsedBody = void 0;
252
+ }
253
+ if (isPayloadTooLargeErrorBody(parsedBody) || isFileTooLargeErrorBody(parsedBody)) {
254
+ const { code, message, surface, limitBytes, actualBytes } = parsedBody.error;
255
+ const detail = typeof actualBytes === "number" ? `${message} (${surface}: ${actualBytes} of ${limitBytes} bytes)` : `${message} (${surface}: limit ${limitBytes} bytes)`;
256
+ return parseErrorResponse(response.status, JSON.stringify({
257
+ code,
258
+ message: detail
259
+ }), void 0, response.headers);
260
+ }
261
+ return parseErrorResponse(response.status, bodyText, void 0, response.headers);
262
+ }
263
+ async function initUploadSession(transport, remotePath, totalBytes, sha256, mode, signal) {
264
+ signal?.throwIfAborted();
265
+ const response = await transport.fetch("/fs/upload-session", {
266
+ method: "POST",
267
+ headers: { "Content-Type": "application/json" },
268
+ body: JSON.stringify({
269
+ path: remotePath,
270
+ mode,
271
+ totalBytes,
272
+ sha256
273
+ }),
274
+ signal
275
+ });
276
+ if (!response.ok) throw await parseUploadError(response);
277
+ return (await response.json()).data;
278
+ }
279
+ async function putPart(transport, uploadId, index, chunk, options) {
280
+ const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
281
+ for (let attempt = 0;; attempt++) {
282
+ options.signal?.throwIfAborted();
283
+ try {
284
+ const response = await transport.fetch(`/fs/upload-session/${encodeURIComponent(uploadId)}/parts/${index}`, {
285
+ method: "PUT",
286
+ headers: { "Content-Type": "application/octet-stream" },
287
+ body: chunk,
288
+ signal: options.signal
289
+ });
290
+ if (!response.ok) throw await parseUploadError(response);
291
+ return;
292
+ } catch (err) {
293
+ if (isTransientUploadError(err) && attempt < maxRetries) {
294
+ const retryAfterMs = err instanceof SandboxError ? err.retryAfterMs : void 0;
295
+ const backoff = Math.min(RETRY_BASE_MS * 2 ** attempt, RETRY_MAX_MS);
296
+ await delay(retryAfterMs ?? backoff, options.signal);
297
+ continue;
298
+ }
299
+ throw err;
300
+ }
301
+ }
302
+ }
303
+ function partCount(totalBytes, partSize) {
304
+ return Math.ceil(totalBytes / partSize);
305
+ }
306
+ async function uploadAllParts(transport, uploadId, bytes, partSize, totalParts, options) {
307
+ const totalBytes = bytes.byteLength;
308
+ const paceMs = options.paceMs ?? 0;
309
+ let uploadedBytes = 0;
310
+ let started = false;
311
+ for (let index = 0; index < totalParts; index++) {
312
+ if (started && paceMs > 0) await delay(paceMs, options.signal);
313
+ started = true;
314
+ const start = index * partSize;
315
+ const end = Math.min(start + partSize, totalBytes);
316
+ await putPart(transport, uploadId, index, bytes.subarray(start, end), options);
317
+ uploadedBytes += end - start;
318
+ options.onProgress?.({
319
+ bytesUploaded: uploadedBytes,
320
+ totalBytes,
321
+ percentage: totalBytes === 0 ? 100 : Math.round(uploadedBytes / totalBytes * 100)
322
+ });
323
+ }
324
+ }
325
+ async function commitSession(transport, uploadId, totalParts, sha256, signal) {
326
+ signal?.throwIfAborted();
327
+ const response = await transport.fetch(`/fs/upload-session/${encodeURIComponent(uploadId)}/commit`, {
328
+ method: "POST",
329
+ headers: { "Content-Type": "application/json" },
330
+ body: JSON.stringify({
331
+ totalParts,
332
+ sha256
333
+ }),
334
+ signal
335
+ });
336
+ if (!response.ok) throw await parseUploadError(response);
337
+ return (await response.json()).data;
338
+ }
339
+ /** Best-effort abort of an in-progress session — never masks the real
340
+ * failure that triggered it, and never itself throws. An orphaned session
341
+ * also self-expires server-side (`expiresAt`), so a failed DELETE here is
342
+ * not a leak, just deferred cleanup. */
343
+ async function abortSession(transport, uploadId) {
344
+ try {
345
+ await transport.fetch(`/fs/upload-session/${encodeURIComponent(uploadId)}`, { method: "DELETE" });
346
+ } catch {}
347
+ }
348
+ /**
349
+ * Upload arbitrary binary or text data to the sandbox via the chunked
350
+ * upload-session protocol, verifying integrity with a whole-payload
351
+ * SHA-256 at commit. Browser/Worker-safe and works identically over the
352
+ * gateway proxy or a direct sidecar connection — see
353
+ * {@link FileSystem.uploadData}.
354
+ *
355
+ * @param transport - Fetch-shaped adapter scoped to the sandbox runtime
356
+ * (paths are relative to it, e.g. `/fs/upload-session`).
357
+ * @param remotePath - Destination path in the sandbox.
358
+ * @param data - Payload to upload. Strings encode as UTF-8.
359
+ * @param options - See {@link ChunkedUploadOptions}.
360
+ * @throws {SandboxError} `PAYLOAD_TOO_LARGE` immediately (zero requests) if
361
+ * the payload exceeds {@link FILE_UPLOAD_SESSION_MAX_BYTES}; otherwise the
362
+ * typed error from whichever step (init/part/commit) failed after retries
363
+ * are exhausted.
364
+ */
365
+ async function uploadChunked(transport, remotePath, data, options = {}) {
366
+ const estimatedSize = sizeOfInput(data);
367
+ if (estimatedSize > FILE_UPLOAD_SESSION_MAX_BYTES) throw new SandboxError(`Payload for ${remotePath} is ${estimatedSize} bytes, over the ${FILE_UPLOAD_SESSION_MAX_BYTES} byte chunked-upload session cap`, "PAYLOAD_TOO_LARGE", 413);
368
+ options.signal?.throwIfAborted();
369
+ const bytes = await normalizeToBytes(data);
370
+ options.signal?.throwIfAborted();
371
+ const sha256 = await sha256Hex(bytes);
372
+ const init = await initUploadSession(transport, remotePath, bytes.byteLength, sha256, options.mode, options.signal);
373
+ const totalParts = partCount(bytes.byteLength, init.partSize);
374
+ try {
375
+ await uploadAllParts(transport, init.uploadId, bytes, init.partSize, totalParts, options);
376
+ try {
377
+ return await commitSession(transport, init.uploadId, totalParts, sha256, options.signal);
378
+ } catch (err) {
379
+ if (!isChecksumMismatch(err)) throw err;
380
+ await uploadAllParts(transport, init.uploadId, bytes, init.partSize, totalParts, options);
381
+ return await commitSession(transport, init.uploadId, totalParts, sha256, options.signal);
382
+ }
383
+ } catch (err) {
384
+ await abortSession(transport, init.uploadId);
385
+ throw err;
386
+ }
387
+ }
388
+ //#endregion
156
389
  //#region src/runtime-api.ts
157
390
  /** Internal typed client for routes served by a sandbox runtime. */
158
391
  var SandboxRuntimeApi = class {
@@ -203,7 +436,7 @@ var SandboxRuntimeApi = class {
203
436
  method: "POST",
204
437
  body: JSON.stringify({
205
438
  paths: chunk,
206
- ...options.encoding ? { encoding: options.encoding } : {}
439
+ encoding: options.encoding ?? "utf8"
207
440
  })
208
441
  });
209
442
  for (const [path, result] of Object.entries(payload.data.files)) if (result.success) files.push({
@@ -249,6 +482,25 @@ var SandboxRuntimeApi = class {
249
482
  })
250
483
  })).data;
251
484
  }
485
+ /**
486
+ * Upload in-memory binary or text data to the sandbox over the chunked
487
+ * upload-session protocol — the browser/Worker-safe sanctioned path for
488
+ * payloads over ~1 MiB, sized to clear the gateway proxy's per-request cap
489
+ * (parts stay under {@link FILE_UPLOAD_PART_MAX_BYTES}). Binary-safe:
490
+ * accepts a `Blob`, `ArrayBuffer`, `ArrayBufferView`, or UTF-8 `string`.
491
+ * Delegates to the shared `uploadChunked` engine in `lib/chunked-upload.js`,
492
+ * wired to this client's `transport.fetch` so requests carry the same
493
+ * scoped-token `Authorization` header and base URL as every other method
494
+ * on this class (e.g. {@link SandboxRuntimeApi.writeFile}) — works
495
+ * identically over the gateway proxy or a direct sidecar connection.
496
+ *
497
+ * @throws {SandboxError} `PAYLOAD_TOO_LARGE` immediately, before any
498
+ * request, if the payload exceeds the chunked-upload session cap.
499
+ */
500
+ async uploadData(remotePath, data, options) {
501
+ await this.transport.ensureRunning();
502
+ return uploadChunked({ fetch: (path, init) => this.transport.fetch(path, init) }, remotePath, data, options);
503
+ }
252
504
  async deleteFile(path, options = {}) {
253
505
  await this.transport.ensureRunning();
254
506
  const params = new URLSearchParams();
@@ -259,6 +511,19 @@ var SandboxRuntimeApi = class {
259
511
  body: JSON.stringify({ path })
260
512
  });
261
513
  }
514
+ async renameFile(sourcePath, destinationPath, options = {}) {
515
+ await this.transport.ensureRunning();
516
+ const params = new URLSearchParams();
517
+ if (options.sessionId) params.set("sessionId", options.sessionId);
518
+ const query = params.toString();
519
+ return (await this.json(`/files/rename${query ? `?${query}` : ""}`, {
520
+ method: "POST",
521
+ body: JSON.stringify({
522
+ sourcePath,
523
+ destinationPath
524
+ })
525
+ })).data;
526
+ }
262
527
  async fileTree(path, options = {}) {
263
528
  if (options.maxDepth !== void 0 && (!Number.isInteger(options.maxDepth) || options.maxDepth < 1 || options.maxDepth > 20)) throw new ValidationError("maxDepth must be an integer between 1 and 20");
264
529
  await this.transport.ensureRunning();
@@ -272,6 +537,7 @@ var SandboxRuntimeApi = class {
272
537
  const info = normalizeSessionInfo(await this.json("/agents/sessions", {
273
538
  method: "POST",
274
539
  body: JSON.stringify({
540
+ ...options.sessionId !== void 0 ? { sessionId: options.sessionId } : {},
275
541
  ...options.title !== void 0 ? { title: options.title } : {},
276
542
  ...options.parentId !== void 0 ? { parentID: options.parentId } : {},
277
543
  backend: normalizeRuntimeBackendConfig(options.backend)
@@ -431,4 +697,4 @@ function normalizeSessionInfo(raw) {
431
697
  };
432
698
  }
433
699
  //#endregion
434
- export { serializeForSidecar as i, normalizeSessionInfo as n, normalizeRuntimeBackendConfig as r, SandboxRuntimeApi as t };
700
+ export { serializeForSidecar as a, normalizeRuntimeBackendConfig as i, normalizeSessionInfo as n, uploadChunked as r, SandboxRuntimeApi as t };
package/dist/runtime.d.ts CHANGED
@@ -1,6 +1,31 @@
1
- import { A as CommitTaskSessionOptions, Ai as WriteFileOptions, Ar as SandboxTerminalInfo, B as CreateTaskSessionOptions, Dr as SandboxRuntimeProfileList, Gr as SendSessionMessageRequest, Kr as SentSessionMessage, Kt as ListMessagesOptions, Mr as SandboxTerminalRequestOptions, Qr as SessionMessage, Tr as SandboxRuntimeHealth, Wr as SendSessionMessageOptions, Xr as SessionInfo, br as SandboxPortBinding, di as TaskSessionChanges, dt as FileWriteResult, fi as TaskSessionCommitResult, it as FileReadBatchResult, jr as SandboxTerminalManager, kr as SandboxTerminalCreateOptions, lt as FileTreeOptions, mi as TaskSessionInfo, rt as FileReadBatchOptions, ut as FileTreeResult, z as CreateSessionOptions } from "./types-BvSoEHz8.js";
2
- import { a as NotFoundError, c as SandboxError, d as ServerError, f as StateError, i as NetworkError, l as SandboxErrorJson, m as ValidationError, n as CapabilityError, o as PartialFailureError, p as TimeoutError, s as QuotaError, t as AuthError, u as SandboxFailureDetail } from "./errors--P_nbLzM.js";
1
+ import { Ai as UploadProgress, E as ChunkedUploadResult, Fr as SandboxTerminalManager, H as CreateTaskSessionOptions, Ir as SandboxTerminalRequestOptions, Jr as SendSessionMessageOptions, M as CommitTaskSessionOptions, Nr as SandboxTerminalCreateOptions, Pi as WriteFileOptions, Pr as SandboxTerminalInfo, T as ChunkedUploadOptions, V as CreateSessionOptions, Xr as SentSessionMessage, Yr as SendSessionMessageRequest, Yt as ListMessagesOptions, at as FileReadBatchOptions, ei as SessionInfo, ft as FileTreeOptions, gi as TaskSessionCommitResult, hi as TaskSessionChanges, jn as RenameOptions, jr as SandboxRuntimeProfileList, kr as SandboxRuntimeHealth, lt as FileRenameResult, mt as FileWriteResult, ni as SessionMessage, ot as FileReadBatchResult, pt as FileTreeResult, vi as TaskSessionInfo, wr as SandboxPortBinding } from "./types-LNLeU9Ub.js";
2
+ import { a as NotFoundError, c as SandboxError, d as ServerError, f as StateError, i as NetworkError, l as SandboxErrorJson, m as ValidationError, n as CapabilityError, o as PartialFailureError, p as TimeoutError, s as QuotaError, t as AuthError, u as SandboxFailureDetail } from "./errors-CNCz3Ms9.js";
3
3
 
4
+ //#region src/lib/chunked-upload.d.ts
5
+ /** The transport a caller wires this module to — {@link SandboxInstance.runtimeFetch}
6
+ * in the SDK, or any fetch-shaped adapter in a standalone consumer. */
7
+ interface ChunkedUploadTransport {
8
+ fetch(path: string, init?: RequestInit): Promise<Response>;
9
+ }
10
+ /**
11
+ * Upload arbitrary binary or text data to the sandbox via the chunked
12
+ * upload-session protocol, verifying integrity with a whole-payload
13
+ * SHA-256 at commit. Browser/Worker-safe and works identically over the
14
+ * gateway proxy or a direct sidecar connection — see
15
+ * {@link FileSystem.uploadData}.
16
+ *
17
+ * @param transport - Fetch-shaped adapter scoped to the sandbox runtime
18
+ * (paths are relative to it, e.g. `/fs/upload-session`).
19
+ * @param remotePath - Destination path in the sandbox.
20
+ * @param data - Payload to upload. Strings encode as UTF-8.
21
+ * @param options - See {@link ChunkedUploadOptions}.
22
+ * @throws {SandboxError} `PAYLOAD_TOO_LARGE` immediately (zero requests) if
23
+ * the payload exceeds {@link FILE_UPLOAD_SESSION_MAX_BYTES}; otherwise the
24
+ * typed error from whichever step (init/part/commit) failed after retries
25
+ * are exhausted.
26
+ */
27
+ declare function uploadChunked(transport: ChunkedUploadTransport, remotePath: string, data: Blob | ArrayBuffer | ArrayBufferView | string, options?: ChunkedUploadOptions): Promise<ChunkedUploadResult>;
28
+ //#endregion
4
29
  //#region src/runtime-api.d.ts
5
30
  interface RuntimeTransport {
6
31
  ensureRunning(): Promise<void>;
@@ -16,7 +41,24 @@ declare class SandboxRuntimeApi {
16
41
  profiles(): Promise<SandboxRuntimeProfileList>;
17
42
  readFiles(paths: string[], options?: FileReadBatchOptions): Promise<FileReadBatchResult>;
18
43
  writeFile(path: string, content: string, options?: WriteFileOptions): Promise<FileWriteResult>;
44
+ /**
45
+ * Upload in-memory binary or text data to the sandbox over the chunked
46
+ * upload-session protocol — the browser/Worker-safe sanctioned path for
47
+ * payloads over ~1 MiB, sized to clear the gateway proxy's per-request cap
48
+ * (parts stay under {@link FILE_UPLOAD_PART_MAX_BYTES}). Binary-safe:
49
+ * accepts a `Blob`, `ArrayBuffer`, `ArrayBufferView`, or UTF-8 `string`.
50
+ * Delegates to the shared `uploadChunked` engine in `lib/chunked-upload.js`,
51
+ * wired to this client's `transport.fetch` so requests carry the same
52
+ * scoped-token `Authorization` header and base URL as every other method
53
+ * on this class (e.g. {@link SandboxRuntimeApi.writeFile}) — works
54
+ * identically over the gateway proxy or a direct sidecar connection.
55
+ *
56
+ * @throws {SandboxError} `PAYLOAD_TOO_LARGE` immediately, before any
57
+ * request, if the payload exceeds the chunked-upload session cap.
58
+ */
59
+ uploadData(remotePath: string, data: Blob | ArrayBuffer | ArrayBufferView | string, options?: ChunkedUploadOptions): Promise<ChunkedUploadResult>;
19
60
  deleteFile(path: string, options?: Pick<WriteFileOptions, "sessionId">): Promise<void>;
61
+ renameFile(sourcePath: string, destinationPath: string, options?: RenameOptions): Promise<FileRenameResult>;
20
62
  fileTree(path: string, options?: FileTreeOptions): Promise<FileTreeResult>;
21
63
  createSession(options: CreateSessionOptions): Promise<SessionInfo>;
22
64
  messages(id: string, options?: Omit<ListMessagesOptions, "sessionId">): Promise<SessionMessage[]>;
@@ -36,9 +78,9 @@ declare class SandboxRuntimeApi {
36
78
  //#endregion
37
79
  //#region src/runtime-client.d.ts
38
80
  interface SandboxRuntimeClientConfig {
39
- /** Browser-safe proxy base returned by `box.mintScopedToken()`. */
81
+ /** Browser-safe proxy base returned by a runtime-scoped token mint. */
40
82
  baseUrl: string;
41
- /** Short-lived scoped token returned by `box.mintScopedToken()`. */
83
+ /** Short-lived `session-runtime` or `read-only` token. */
42
84
  token: string;
43
85
  /** Request timeout in milliseconds. Defaults to 30 seconds. */
44
86
  timeoutMs?: number;
@@ -49,7 +91,9 @@ interface SandboxRuntimeClientConfig {
49
91
  * Browser- and Worker-safe client for one sandbox runtime.
50
92
  *
51
93
  * Construct it from the scoped token response returned by
52
- * `box.mintScopedToken()`. The full Sandbox API key remains server-side.
94
+ * `box.mintScopedToken({ scope: "session-runtime", sessionId })` or
95
+ * `box.mintScopedToken({ scope: "read-only" })`. The full Sandbox API key
96
+ * remains server-side.
53
97
  */
54
98
  declare class SandboxRuntimeClient extends SandboxRuntimeApi {
55
99
  private readonly auth;
@@ -59,4 +103,4 @@ declare class SandboxRuntimeClient extends SandboxRuntimeApi {
59
103
  }
60
104
  declare function createSandboxRuntimeClient(config: SandboxRuntimeClientConfig): SandboxRuntimeClient;
61
105
  //#endregion
62
- export { AuthError, CapabilityError, type FileReadBatchOptions, type FileReadBatchResult, type FileTreeOptions, type FileTreeResult, type FileWriteResult, type ListMessagesOptions, NetworkError, NotFoundError, PartialFailureError, QuotaError, SandboxError, type SandboxErrorJson, type SandboxFailureDetail, SandboxRuntimeClient, type SandboxRuntimeClientConfig, type SandboxRuntimeHealth, type SandboxTerminalCreateOptions, type SandboxTerminalInfo, type SandboxTerminalManager, type SandboxTerminalRequestOptions, ServerError, type SessionMessage, StateError, TimeoutError, ValidationError, type WriteFileOptions, createSandboxRuntimeClient };
106
+ export { AuthError, CapabilityError, type ChunkedUploadOptions, type ChunkedUploadResult, type ChunkedUploadTransport, type FileReadBatchOptions, type FileReadBatchResult, type FileRenameResult, type FileTreeOptions, type FileTreeResult, type FileWriteResult, type ListMessagesOptions, NetworkError, NotFoundError, PartialFailureError, QuotaError, type RenameOptions, SandboxError, type SandboxErrorJson, type SandboxFailureDetail, SandboxRuntimeClient, type SandboxRuntimeClientConfig, type SandboxRuntimeHealth, type SandboxTerminalCreateOptions, type SandboxTerminalInfo, type SandboxTerminalManager, type SandboxTerminalRequestOptions, ServerError, type SessionMessage, StateError, TimeoutError, type UploadProgress, ValidationError, type WriteFileOptions, createSandboxRuntimeClient, uploadChunked };
package/dist/runtime.js CHANGED
@@ -1,5 +1,5 @@
1
- import { t as SandboxRuntimeApi } from "./runtime-api-CYA2DUQw.js";
2
- import { a as NotFoundError, c as SandboxError, d as TimeoutError, f as ValidationError, i as NetworkError, l as ServerError, n as CapabilityError, o as PartialFailureError, s as QuotaError, t as AuthError, u as StateError } from "./errors-DSz87Rkk.js";
1
+ import { r as uploadChunked, t as SandboxRuntimeApi } from "./runtime-api-BWq4XSrb.js";
2
+ import { a as NotFoundError, c as SandboxError, d as TimeoutError, f as ValidationError, i as NetworkError, l as ServerError, n as CapabilityError, o as PartialFailureError, s as QuotaError, t as AuthError, u as StateError } from "./errors-wd266B9Q.js";
3
3
  //#region src/runtime-client.ts
4
4
  const DEFAULT_RUNTIME_TIMEOUT_MS = 3e4;
5
5
  function requireToken(token) {
@@ -28,7 +28,9 @@ function requireTimeout(timeoutMs) {
28
28
  * Browser- and Worker-safe client for one sandbox runtime.
29
29
  *
30
30
  * Construct it from the scoped token response returned by
31
- * `box.mintScopedToken()`. The full Sandbox API key remains server-side.
31
+ * `box.mintScopedToken({ scope: "session-runtime", sessionId })` or
32
+ * `box.mintScopedToken({ scope: "read-only" })`. The full Sandbox API key
33
+ * remains server-side.
32
34
  */
33
35
  var SandboxRuntimeClient = class extends SandboxRuntimeApi {
34
36
  auth;
@@ -78,4 +80,4 @@ function createSandboxRuntimeClient(config) {
78
80
  return new SandboxRuntimeClient(config);
79
81
  }
80
82
  //#endregion
81
- export { AuthError, CapabilityError, NetworkError, NotFoundError, PartialFailureError, QuotaError, SandboxError, SandboxRuntimeClient, ServerError, StateError, TimeoutError, ValidationError, createSandboxRuntimeClient };
83
+ export { AuthError, CapabilityError, NetworkError, NotFoundError, PartialFailureError, QuotaError, SandboxError, SandboxRuntimeClient, ServerError, StateError, TimeoutError, ValidationError, createSandboxRuntimeClient, uploadChunked };
@@ -1,4 +1,4 @@
1
- import { $ as EventStreamOptions, A as CommitTaskSessionOptions, Ai as WriteFileOptions, An as Rollout, B as CreateTaskSessionOptions, Br as SearchMatch, Bt as IntelligenceReport, Ci as TurnDriveResult, Cr as SandboxResourceUsage, Ct as GitBranch, D as CodeLanguage, Dr as SandboxRuntimeProfileList, Dt as GitStatus, E as CodeExecutionResult, Et as GitDiff, Gr as SendSessionMessageRequest, Gt as JsonValue, Ht as IntelligenceReportCompareTo, Ir as SandboxTraceOptions, Jr as SessionEventStreamOptions, Kr as SentSessionMessage, Kt as ListMessagesOptions, Mi as WriteManyOptions, Mn as RolloutOptions, Nr as SandboxTraceBundle, Nt as GpuLeaseManager, Oi as WaitForOptions, On as RestoreSnapshotOptions, Or as SandboxStatus, Ot as GpuLease, Pn as RolloutStartResult, Qr as SessionMessage, Rn as SSHCommandDescriptor, Rr as ScopedToken, T as CodeExecutionOptions, Tr as SandboxRuntimeHealth, U as DispatchPromptOptions, Un as SandboxEvent, Vn as SandboxConnection, Vr as SearchOptions, Vt as IntelligenceReportBudget, W as DispatchedSession, Wr as SendSessionMessageOptions, Wt as IntelligenceReportWindow, Xr as SessionInfo, Y as DriverInfo, Yr as SessionForkOptions, Z as EgressManager, Zr as SessionListOptions, Zt as MintScopedTokenOptions, _n as ProvisionResult, _r as SandboxInfo, ai as StartupDiagnostics, an as PreviewLinkManager, bi as TeePublicKeyResponse, br as SandboxPortBinding, cr as SandboxFleetTraceBundle, di as TaskSessionChanges, dt as FileWriteResult, en as NetworkManager, et as ExecOptions, fi as TaskSessionCommitResult, gi as TeeAttestationOptions, gn as ProvisionEvent, hn as PromptResult, j as CompletedTurnResult, ji as WriteManyFile, jr as SandboxTerminalManager, ki as WaitForRolloutOptions, kn as ResumeOptions, l as BackendManager, li as TaskOptions, ln as ProcessManager, mi as TaskSessionInfo, mn as PromptOptions, ni as SnapshotOptions, pn as PromptInputPart, q as DriveTurnOptions, ri as SnapshotResult, rn as PermissionsManager, s as BackendConfig, st as FileSystem, ti as SnapshotInfo, tt as ExecResult, ui as TaskResult, vi as TeeAttestationResponse, w as BranchOptions, wt as GitCommit, z as CreateSessionOptions, zn as SSHCredentials, zt as InstalledTool } from "./types-BvSoEHz8.js";
1
+ import { $ as EgressManager, $r as SessionForkOptions, At as GitStatus, Br as SandboxTraceOptions, D as CodeExecutionOptions, Di as TurnDriveResult, Dr as SandboxResourceUsage, Dt as GitCommit, Et as GitBranch, Fi as WriteManyFile, Fr as SandboxTerminalManager, G as DispatchPromptOptions, Gn as SandboxConnection, Gr as SearchOptions, Gt as IntelligenceReportCompareTo, H as CreateTaskSessionOptions, Hn as SSHCommandDescriptor, Hr as ScopedToken, Ht as InstalledTool, Ii as WriteManyOptions, In as RolloutOptions, It as GpuLeaseManager, Jr as SendSessionMessageOptions, Jt as JsonValue, K as DispatchedSession, Lr as SandboxTraceBundle, M as CommitTaskSessionOptions, Mi as WaitForOptions, Mn as RestoreSnapshotOptions, Mr as SandboxStatus, N as CompletedTurnResult, Ni as WaitForRolloutOptions, Nn as ResumeOptions, O as CodeExecutionResult, Pi as WriteFileOptions, Pn as Rollout, Qr as SessionEventStreamOptions, Rn as RolloutStartResult, Si as TeeAttestationResponse, Un as SSHCredentials, Ut as IntelligenceReport, V as CreateSessionOptions, Wr as SearchMatch, Wt as IntelligenceReportBudget, Xr as SentSessionMessage, Y as DriveTurnOptions, Yr as SendSessionMessageRequest, Yt as ListMessagesOptions, Z as DriverInfo, _n as PromptOptions, ai as SnapshotInfo, bi as TeeAttestationOptions, bn as ProvisionResult, cn as PreviewLinkManager, ei as SessionInfo, en as MintScopedTokenOptions, fn as ProcessManager, fr as SandboxFleetTraceBundle, gi as TaskSessionCommitResult, gn as PromptInputPart, hi as TaskSessionChanges, jr as SandboxRuntimeProfileList, jt as GpuLease, k as CodeLanguage, kr as SandboxRuntimeHealth, kt as GitDiff, l as BackendManager, li as StartupDiagnostics, mi as TaskResult, mt as FileWriteResult, ni as SessionMessage, nt as ExecOptions, oi as SnapshotOptions, on as PermissionsManager, pi as TaskOptions, qn as SandboxEvent, qt as IntelligenceReportWindow, rn as NetworkManager, rt as ExecResult, s as BackendConfig, si as SnapshotResult, ti as SessionListOptions, tt as EventStreamOptions, ut as FileSystem, vi as TaskSessionInfo, vn as PromptResult, w as BranchOptions, wi as TeePublicKeyResponse, wr as SandboxPortBinding, xr as SandboxInfo, yn as ProvisionEvent } from "./types-LNLeU9Ub.js";
2
2
 
3
3
  //#region src/mcp.d.ts
4
4
  /**
@@ -894,12 +894,18 @@ declare class SandboxInstance {
894
894
  private fsReadBatch;
895
895
  private fsTree;
896
896
  private fsUpload;
897
+ /** See {@link FileSystem.uploadData}. Delegates to the shared, browser-safe
898
+ * chunked-upload flow in `lib/chunked-upload.ts`, wired to this sandbox's
899
+ * runtime endpoint (works over the gateway proxy or a direct connection —
900
+ * both are transparent through `runtimeFetch`). */
901
+ private fsUploadData;
897
902
  private fsDownload;
898
903
  private fsUploadDir;
899
904
  private fsDownloadDir;
900
905
  private fsList;
901
906
  private fsStat;
902
907
  private fsDelete;
908
+ private fsRename;
903
909
  private fsMkdir;
904
910
  private fsExists;
905
911
  /**
@@ -1498,15 +1504,10 @@ declare class SandboxInstance {
1498
1504
  */
1499
1505
  driveTurn(message: string | PromptInputPart[], opts: DriveTurnOptions): Promise<TurnDriveResult>;
1500
1506
  /**
1501
- * Mint a scoped, time-bounded JWT for direct browser access to this
1502
- * sandbox (Issue #913 Gap 1). Authority is the caller's
1503
- * `TANGLE_API_KEY` (sk-tan-*) the Sandbox API mints the token;
1504
- * signing secrets stay server-side.
1505
- *
1506
- * Use this to give a browser direct read access to the sandbox without
1507
- * leaking the full bearer (`box.connection.authToken`). The returned
1508
- * token verifies against the same sidecar middleware that already
1509
- * gates ProductTokenIssuer-issued JWTs — no new sidecar surface.
1507
+ * Mint a scoped, time-bounded JWT for browser access (Issue #913 Gap 1).
1508
+ * Authority is the caller's `TANGLE_API_KEY` (sk-tan-*); signing secrets
1509
+ * stay server-side. Session/project scopes work with SessionGatewayClient,
1510
+ * while session-runtime/read-only scopes work with SandboxRuntimeClient.
1510
1511
  */
1511
1512
  mintScopedToken(opts: MintScopedTokenOptions): Promise<ScopedToken>;
1512
1513
  /** @internal — invoked by SandboxSession.status(). */