tensorlake 0.4.50 → 0.5.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/dist/index.cjs CHANGED
@@ -31,10 +31,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
33
  // src/defaults.ts
34
- var API_URL, API_KEY, NAMESPACE, SANDBOX_PROXY_URL, DEFAULT_HTTP_TIMEOUT_MS, MAX_RETRIES, RETRY_BACKOFF_MS, RETRYABLE_STATUS_CODES;
34
+ var SDK_VERSION, API_URL, API_KEY, NAMESPACE, SANDBOX_PROXY_URL, DEFAULT_HTTP_TIMEOUT_MS, MAX_RETRIES, RETRY_BACKOFF_MS, RETRYABLE_STATUS_CODES;
35
35
  var init_defaults = __esm({
36
36
  "src/defaults.ts"() {
37
37
  "use strict";
38
+ SDK_VERSION = "0.4.49";
38
39
  API_URL = process.env.TENSORLAKE_API_URL ?? "https://api.tensorlake.ai";
39
40
  API_KEY = process.env.TENSORLAKE_API_KEY ?? void 0;
40
41
  NAMESPACE = process.env.INDEXIFY_NAMESPACE ?? "default";
@@ -132,6 +133,12 @@ var init_errors = __esm({
132
133
  });
133
134
 
134
135
  // src/http.ts
136
+ function withTraceId(value, traceId) {
137
+ if (value == null) {
138
+ return { traceId };
139
+ }
140
+ return Object.assign(value, { traceId });
141
+ }
135
142
  function hasHeader(headers, name) {
136
143
  const lowered = name.toLowerCase();
137
144
  return Object.keys(headers).some((key) => key.toLowerCase() === lowered);
@@ -201,7 +208,7 @@ var init_http = __esm({
201
208
  allowH2: true
202
209
  })
203
210
  );
204
- HttpClient = class {
211
+ HttpClient = class _HttpClient {
205
212
  baseUrl;
206
213
  headers;
207
214
  maxRetries;
@@ -214,7 +221,9 @@ var init_http = __esm({
214
221
  this.maxRetries = options.maxRetries ?? MAX_RETRIES;
215
222
  this.retryBackoffMs = options.retryBackoffMs ?? RETRY_BACKOFF_MS;
216
223
  this.timeoutMs = options.timeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;
217
- this.headers = {};
224
+ this.headers = {
225
+ "User-Agent": `tensorlake-typescript-sdk/${SDK_VERSION}`
226
+ };
218
227
  if (options.apiKey) {
219
228
  this.headers["Authorization"] = `Bearer ${options.apiKey}`;
220
229
  }
@@ -243,8 +252,8 @@ var init_http = __esm({
243
252
  signal: options?.signal
244
253
  });
245
254
  const text = await response.text();
246
- if (!text) return void 0;
247
- return JSON.parse(text);
255
+ const data = text ? JSON.parse(text) : void 0;
256
+ return withTraceId(data, response.traceId);
248
257
  }
249
258
  /** Make a request returning raw bytes. */
250
259
  async requestBytes(method, path2, options) {
@@ -258,7 +267,7 @@ var init_http = __esm({
258
267
  signal: options?.signal
259
268
  });
260
269
  const buffer = await response.arrayBuffer();
261
- return new Uint8Array(buffer);
270
+ return withTraceId(new Uint8Array(buffer), response.traceId);
262
271
  }
263
272
  /** Make a request and return the response body as an SSE stream. */
264
273
  async requestStream(method, path2, options) {
@@ -273,7 +282,7 @@ var init_http = __esm({
273
282
  "No response body for SSE stream"
274
283
  );
275
284
  }
276
- return response.body;
285
+ return withTraceId(response.body, response.traceId);
277
286
  }
278
287
  /** Make a request and return the raw Response. */
279
288
  async requestResponse(method, path2, options) {
@@ -286,7 +295,7 @@ var init_http = __esm({
286
295
  headers["Content-Type"] = "application/json";
287
296
  }
288
297
  const body = hasJsonBody ? JSON.stringify(options?.json) : normalizeRequestBody(options?.body);
289
- return this.doFetch(
298
+ const { response, traceId } = await this.doFetch(
290
299
  method,
291
300
  path2,
292
301
  body,
@@ -294,9 +303,18 @@ var init_http = __esm({
294
303
  options?.signal,
295
304
  options?.allowHttpErrors ?? false
296
305
  );
306
+ return withTraceId(response, traceId);
307
+ }
308
+ static makeTraceparent() {
309
+ const randomHex = (bytes) => Array.from(crypto.getRandomValues(new Uint8Array(bytes))).map((b) => b.toString(16).padStart(2, "0")).join("");
310
+ const traceId = randomHex(16);
311
+ const spanId = randomHex(8);
312
+ return { traceparent: `00-${traceId}-${spanId}-01`, traceId };
297
313
  }
298
314
  async doFetch(method, path2, body, headers, signal, allowHttpErrors = false) {
299
315
  const url = `${this.baseUrl}${path2}`;
316
+ const { traceparent, traceId } = _HttpClient.makeTraceparent();
317
+ headers["traceparent"] = traceparent;
300
318
  let lastError;
301
319
  for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
302
320
  if (attempt > 0) {
@@ -317,7 +335,7 @@ var init_http = __esm({
317
335
  signal: combinedSignal
318
336
  });
319
337
  clearTimeout(timeoutId);
320
- if (response.ok) return response;
338
+ if (response.ok) return { response, traceId };
321
339
  if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < this.maxRetries) {
322
340
  lastError = new RemoteAPIError(
323
341
  response.status,
@@ -326,7 +344,7 @@ var init_http = __esm({
326
344
  continue;
327
345
  }
328
346
  if (allowHttpErrors) {
329
- return response;
347
+ return { response, traceId };
330
348
  }
331
349
  const errorBody = await response.text().catch(() => "");
332
350
  throwMappedError(response.status, errorBody, path2);
@@ -396,20 +414,20 @@ var SandboxStatus, SnapshotStatus, ProcessStatus, StdinMode, OutputMode, Contain
396
414
  var init_models = __esm({
397
415
  "src/models.ts"() {
398
416
  "use strict";
399
- SandboxStatus = /* @__PURE__ */ ((SandboxStatus2) => {
400
- SandboxStatus2["PENDING"] = "pending";
401
- SandboxStatus2["RUNNING"] = "running";
402
- SandboxStatus2["SNAPSHOTTING"] = "snapshotting";
403
- SandboxStatus2["SUSPENDING"] = "suspending";
404
- SandboxStatus2["SUSPENDED"] = "suspended";
405
- SandboxStatus2["TERMINATED"] = "terminated";
406
- return SandboxStatus2;
417
+ SandboxStatus = /* @__PURE__ */ ((SandboxStatus3) => {
418
+ SandboxStatus3["PENDING"] = "pending";
419
+ SandboxStatus3["RUNNING"] = "running";
420
+ SandboxStatus3["SNAPSHOTTING"] = "snapshotting";
421
+ SandboxStatus3["SUSPENDING"] = "suspending";
422
+ SandboxStatus3["SUSPENDED"] = "suspended";
423
+ SandboxStatus3["TERMINATED"] = "terminated";
424
+ return SandboxStatus3;
407
425
  })(SandboxStatus || {});
408
- SnapshotStatus = /* @__PURE__ */ ((SnapshotStatus2) => {
409
- SnapshotStatus2["IN_PROGRESS"] = "in_progress";
410
- SnapshotStatus2["COMPLETED"] = "completed";
411
- SnapshotStatus2["FAILED"] = "failed";
412
- return SnapshotStatus2;
426
+ SnapshotStatus = /* @__PURE__ */ ((SnapshotStatus3) => {
427
+ SnapshotStatus3["IN_PROGRESS"] = "in_progress";
428
+ SnapshotStatus3["COMPLETED"] = "completed";
429
+ SnapshotStatus3["FAILED"] = "failed";
430
+ return SnapshotStatus3;
413
431
  })(SnapshotStatus || {});
414
432
  ProcessStatus = /* @__PURE__ */ ((ProcessStatus2) => {
415
433
  ProcessStatus2["RUNNING"] = "running";
@@ -3158,6 +3176,7 @@ var init_sandbox = __esm({
3158
3176
  };
3159
3177
  Sandbox = class {
3160
3178
  sandboxId;
3179
+ traceId = null;
3161
3180
  http;
3162
3181
  baseUrl;
3163
3182
  wsHeaders;
@@ -3195,9 +3214,126 @@ var init_sandbox = __esm({
3195
3214
  this.ownsSandbox = true;
3196
3215
  this.lifecycleClient = client;
3197
3216
  }
3217
+ // --- Static factory methods ---
3218
+ /**
3219
+ * Create a new sandbox and return a connected, running handle.
3220
+ *
3221
+ * Covers both fresh sandbox creation and restore-from-snapshot (set
3222
+ * `snapshotId`). Blocks until the sandbox is `Running`.
3223
+ */
3224
+ static async create(options) {
3225
+ const { SandboxClient: SandboxClient2 } = await Promise.resolve().then(() => (init_client(), client_exports));
3226
+ const client = new SandboxClient2(
3227
+ options,
3228
+ /* _internal */
3229
+ true
3230
+ );
3231
+ const sandbox = await client.createAndConnect(options);
3232
+ sandbox.lifecycleClient = client;
3233
+ return sandbox;
3234
+ }
3235
+ /**
3236
+ * Attach to an existing sandbox and return a connected handle.
3237
+ *
3238
+ * Verifies the sandbox exists via a server GET call, then returns a handle
3239
+ * in whatever state the sandbox is in. Does **not** auto-resume a suspended
3240
+ * sandbox — call `sandbox.resume()` explicitly.
3241
+ */
3242
+ static async connect(options) {
3243
+ const { SandboxClient: SandboxClient2 } = await Promise.resolve().then(() => (init_client(), client_exports));
3244
+ const client = new SandboxClient2(
3245
+ options,
3246
+ /* _internal */
3247
+ true
3248
+ );
3249
+ await client.get(options.sandboxId);
3250
+ const sandbox = client.connect(options.sandboxId, options.proxyUrl, options.routingHint);
3251
+ sandbox.lifecycleClient = client;
3252
+ return sandbox;
3253
+ }
3254
+ // --- Static snapshot management ---
3255
+ /** Get information about a snapshot by ID. No sandbox handle needed. */
3256
+ static async getSnapshot(snapshotId, options) {
3257
+ const { SandboxClient: SandboxClient2 } = await Promise.resolve().then(() => (init_client(), client_exports));
3258
+ const client = new SandboxClient2(
3259
+ options,
3260
+ /* _internal */
3261
+ true
3262
+ );
3263
+ return client.getSnapshot(snapshotId);
3264
+ }
3265
+ /** Delete a snapshot by ID. No sandbox handle needed. */
3266
+ static async deleteSnapshot(snapshotId, options) {
3267
+ const { SandboxClient: SandboxClient2 } = await Promise.resolve().then(() => (init_client(), client_exports));
3268
+ const client = new SandboxClient2(
3269
+ options,
3270
+ /* _internal */
3271
+ true
3272
+ );
3273
+ await client.deleteSnapshot(snapshotId);
3274
+ }
3275
+ // --- Instance lifecycle methods ---
3276
+ requireLifecycleClient(operation) {
3277
+ if (!this.lifecycleClient) {
3278
+ throw new SandboxError(
3279
+ `Cannot ${operation}: no lifecycle client available. Use Sandbox.create() or Sandbox.connect() to get a lifecycle-aware handle.`
3280
+ );
3281
+ }
3282
+ return this.lifecycleClient;
3283
+ }
3284
+ /**
3285
+ * Suspend this sandbox.
3286
+ *
3287
+ * By default blocks until the sandbox is fully `Suspended`. Pass
3288
+ * `{ wait: false }` for fire-and-return.
3289
+ */
3290
+ async suspend(options) {
3291
+ const client = this.requireLifecycleClient("suspend");
3292
+ await client.suspend(this.sandboxId, options);
3293
+ }
3294
+ /**
3295
+ * Resume this sandbox.
3296
+ *
3297
+ * By default blocks until the sandbox is `Running` and routable. Pass
3298
+ * `{ wait: false }` for fire-and-return.
3299
+ */
3300
+ async resume(options) {
3301
+ const client = this.requireLifecycleClient("resume");
3302
+ await client.resume(this.sandboxId, options);
3303
+ }
3304
+ /**
3305
+ * Create a snapshot of this sandbox's filesystem and wait for it to
3306
+ * be committed.
3307
+ *
3308
+ * By default blocks until the snapshot artifact is ready and returns
3309
+ * the completed `SnapshotInfo`. Pass `{ wait: false }` to fire-and-return
3310
+ * (returns `undefined`).
3311
+ */
3312
+ async checkpoint(options) {
3313
+ const client = this.requireLifecycleClient("checkpoint");
3314
+ if (options?.wait === false) {
3315
+ await client.snapshot(this.sandboxId, { contentMode: options.contentMode });
3316
+ return void 0;
3317
+ }
3318
+ return client.snapshotAndWait(this.sandboxId, {
3319
+ timeout: options?.timeout,
3320
+ pollInterval: options?.pollInterval,
3321
+ contentMode: options?.contentMode
3322
+ });
3323
+ }
3324
+ /**
3325
+ * List snapshots taken from this sandbox.
3326
+ */
3327
+ async listSnapshots() {
3328
+ const client = this.requireLifecycleClient("listSnapshots");
3329
+ const all = await client.listSnapshots();
3330
+ return all.filter((s) => s.sandboxId === this.sandboxId);
3331
+ }
3332
+ /** Close the HTTP client. The sandbox keeps running. */
3198
3333
  close() {
3199
3334
  this.http.close();
3200
3335
  }
3336
+ /** Terminate the sandbox and release all resources. */
3201
3337
  async terminate() {
3202
3338
  const client = this.lifecycleClient;
3203
3339
  this.ownsSandbox = false;
@@ -3250,6 +3386,14 @@ var init_sandbox = __esm({
3250
3386
  };
3251
3387
  }
3252
3388
  // --- Process management ---
3389
+ /**
3390
+ * Start a process in the sandbox without waiting for it to exit.
3391
+ *
3392
+ * Returns a `ProcessInfo` with the assigned `pid`. Use `getProcess()` to
3393
+ * poll status, or `followStdout()` / `followOutput()` to stream output
3394
+ * until the process exits. Use `run()` instead to block until completion
3395
+ * and get combined output in one call.
3396
+ */
3253
3397
  async startProcess(command, options) {
3254
3398
  const payload = { command };
3255
3399
  if (options?.args != null) payload.args = options.args;
@@ -3271,6 +3415,7 @@ var init_sandbox = __esm({
3271
3415
  );
3272
3416
  return fromSnakeKeys(raw);
3273
3417
  }
3418
+ /** List all processes (running and exited) tracked by the sandbox daemon. */
3274
3419
  async listProcesses() {
3275
3420
  const raw = await this.http.requestJson(
3276
3421
  "GET",
@@ -3278,6 +3423,7 @@ var init_sandbox = __esm({
3278
3423
  );
3279
3424
  return (raw.processes ?? []).map((p) => fromSnakeKeys(p));
3280
3425
  }
3426
+ /** Get current status and metadata for a process by PID. */
3281
3427
  async getProcess(pid) {
3282
3428
  const raw = await this.http.requestJson(
3283
3429
  "GET",
@@ -3285,9 +3431,11 @@ var init_sandbox = __esm({
3285
3431
  );
3286
3432
  return fromSnakeKeys(raw);
3287
3433
  }
3434
+ /** Send SIGKILL to a process. */
3288
3435
  async killProcess(pid) {
3289
3436
  await this.http.requestJson("DELETE", `/api/v1/processes/${pid}`);
3290
3437
  }
3438
+ /** Send an arbitrary signal to a process (e.g. `15` for SIGTERM, `9` for SIGKILL). */
3291
3439
  async sendSignal(pid, signal) {
3292
3440
  const raw = await this.http.requestJson(
3293
3441
  "POST",
@@ -3297,15 +3445,18 @@ var init_sandbox = __esm({
3297
3445
  return fromSnakeKeys(raw);
3298
3446
  }
3299
3447
  // --- Process I/O ---
3448
+ /** Write bytes to a process's stdin. The process must have been started with `stdinMode: StdinMode.PIPE`. */
3300
3449
  async writeStdin(pid, data) {
3301
3450
  await this.http.requestBytes("POST", `/api/v1/processes/${pid}/stdin`, {
3302
3451
  body: data,
3303
3452
  contentType: "application/octet-stream"
3304
3453
  });
3305
3454
  }
3455
+ /** Close a process's stdin pipe, signalling EOF to the process. */
3306
3456
  async closeStdin(pid) {
3307
3457
  await this.http.requestJson("POST", `/api/v1/processes/${pid}/stdin/close`);
3308
3458
  }
3459
+ /** Return all captured stdout lines produced so far by a process. */
3309
3460
  async getStdout(pid) {
3310
3461
  const raw = await this.http.requestJson(
3311
3462
  "GET",
@@ -3313,6 +3464,7 @@ var init_sandbox = __esm({
3313
3464
  );
3314
3465
  return fromSnakeKeys(raw);
3315
3466
  }
3467
+ /** Return all captured stderr lines produced so far by a process. */
3316
3468
  async getStderr(pid) {
3317
3469
  const raw = await this.http.requestJson(
3318
3470
  "GET",
@@ -3320,6 +3472,7 @@ var init_sandbox = __esm({
3320
3472
  );
3321
3473
  return fromSnakeKeys(raw);
3322
3474
  }
3475
+ /** Return all captured stdout+stderr lines produced so far by a process. */
3323
3476
  async getOutput(pid) {
3324
3477
  const raw = await this.http.requestJson(
3325
3478
  "GET",
@@ -3328,6 +3481,7 @@ var init_sandbox = __esm({
3328
3481
  return fromSnakeKeys(raw);
3329
3482
  }
3330
3483
  // --- Streaming (SSE) ---
3484
+ /** Stream stdout events from a process until it exits. Yields one `OutputEvent` per line. */
3331
3485
  async *followStdout(pid, options) {
3332
3486
  const stream = await this.http.requestStream(
3333
3487
  "GET",
@@ -3341,6 +3495,7 @@ var init_sandbox = __esm({
3341
3495
  yield fromSnakeKeys(raw);
3342
3496
  }
3343
3497
  }
3498
+ /** Stream stderr events from a process until it exits. Yields one `OutputEvent` per line. */
3344
3499
  async *followStderr(pid, options) {
3345
3500
  const stream = await this.http.requestStream(
3346
3501
  "GET",
@@ -3354,6 +3509,7 @@ var init_sandbox = __esm({
3354
3509
  yield fromSnakeKeys(raw);
3355
3510
  }
3356
3511
  }
3512
+ /** Stream combined stdout+stderr events from a process until it exits. Yields one `OutputEvent` per line. */
3357
3513
  async *followOutput(pid, options) {
3358
3514
  const stream = await this.http.requestStream(
3359
3515
  "GET",
@@ -3368,12 +3524,14 @@ var init_sandbox = __esm({
3368
3524
  }
3369
3525
  }
3370
3526
  // --- File operations ---
3527
+ /** Read a file from the sandbox and return its raw bytes. */
3371
3528
  async readFile(path2) {
3372
3529
  return this.http.requestBytes(
3373
3530
  "GET",
3374
3531
  `/api/v1/files?path=${encodeURIComponent(path2)}`
3375
3532
  );
3376
3533
  }
3534
+ /** Write raw bytes to a file in the sandbox, creating it if it does not exist. */
3377
3535
  async writeFile(path2, content) {
3378
3536
  await this.http.requestBytes(
3379
3537
  "PUT",
@@ -3381,12 +3539,14 @@ var init_sandbox = __esm({
3381
3539
  { body: content, contentType: "application/octet-stream" }
3382
3540
  );
3383
3541
  }
3542
+ /** Delete a file from the sandbox. */
3384
3543
  async deleteFile(path2) {
3385
3544
  await this.http.requestJson(
3386
3545
  "DELETE",
3387
3546
  `/api/v1/files?path=${encodeURIComponent(path2)}`
3388
3547
  );
3389
3548
  }
3549
+ /** List the contents of a directory in the sandbox. */
3390
3550
  async listDirectory(path2) {
3391
3551
  const raw = await this.http.requestJson(
3392
3552
  "GET",
@@ -3395,6 +3555,7 @@ var init_sandbox = __esm({
3395
3555
  return fromSnakeKeys(raw);
3396
3556
  }
3397
3557
  // --- PTY ---
3558
+ /** Create an interactive PTY session. Returns a `sessionId` and `token` for WebSocket connection via `connectPty()`. */
3398
3559
  async createPtySession(options) {
3399
3560
  const payload = {
3400
3561
  command: options.command,
@@ -3411,6 +3572,7 @@ var init_sandbox = __esm({
3411
3572
  );
3412
3573
  return fromSnakeKeys(raw);
3413
3574
  }
3575
+ /** Create a PTY session and connect to it immediately. Cleans up the session if the WebSocket connection fails. */
3414
3576
  async createPty(options) {
3415
3577
  const { onData, onExit, ...createOptions } = options;
3416
3578
  const session = await this.createPtySession(createOptions);
@@ -3424,6 +3586,7 @@ var init_sandbox = __esm({
3424
3586
  throw error;
3425
3587
  }
3426
3588
  }
3589
+ /** Attach to an existing PTY session by ID and token and return a connected `Pty` handle. */
3427
3590
  async connectPty(sessionId, token, options) {
3428
3591
  const wsUrl = new URL(this.ptyWsUrl(sessionId, token));
3429
3592
  const authToken = wsUrl.searchParams.get("token") ?? token;
@@ -3448,6 +3611,7 @@ var init_sandbox = __esm({
3448
3611
  await pty.connect();
3449
3612
  return pty;
3450
3613
  }
3614
+ /** Open a TCP tunnel to a port inside the sandbox and return the local listener. */
3451
3615
  async createTunnel(remotePort, options) {
3452
3616
  return TcpTunnel.listen({
3453
3617
  baseUrl: this.baseUrl,
@@ -3458,6 +3622,7 @@ var init_sandbox = __esm({
3458
3622
  connectTimeout: options?.connectTimeout
3459
3623
  });
3460
3624
  }
3625
+ /** Connect to a sandbox VNC session for programmatic desktop control. */
3461
3626
  async connectDesktop(options) {
3462
3627
  return Desktop.connect({
3463
3628
  baseUrl: this.baseUrl,
@@ -3480,6 +3645,7 @@ var init_sandbox = __esm({
3480
3645
  return `${wsBase}/api/v1/pty/${sessionId}/ws?token=${token}`;
3481
3646
  }
3482
3647
  // --- Health ---
3648
+ /** Check the sandbox daemon health. */
3483
3649
  async health() {
3484
3650
  const raw = await this.http.requestJson(
3485
3651
  "GET",
@@ -3487,6 +3653,7 @@ var init_sandbox = __esm({
3487
3653
  );
3488
3654
  return fromSnakeKeys(raw);
3489
3655
  }
3656
+ /** Get sandbox daemon info (version, uptime, process counts). */
3490
3657
  async info() {
3491
3658
  const raw = await this.http.requestJson(
3492
3659
  "GET",
@@ -3499,6 +3666,10 @@ var init_sandbox = __esm({
3499
3666
  });
3500
3667
 
3501
3668
  // src/client.ts
3669
+ var client_exports = {};
3670
+ __export(client_exports, {
3671
+ SandboxClient: () => SandboxClient
3672
+ });
3502
3673
  function sleep2(ms) {
3503
3674
  return new Promise((resolve) => setTimeout(resolve, ms));
3504
3675
  }
@@ -3535,7 +3706,13 @@ var init_client = __esm({
3535
3706
  projectId;
3536
3707
  namespace;
3537
3708
  local;
3538
- constructor(options) {
3709
+ /** @internal Pass `true` to suppress the deprecation warning when used by `Sandbox.create()` / `Sandbox.connect()`. */
3710
+ constructor(options, _internal = false) {
3711
+ if (!_internal) {
3712
+ console.warn(
3713
+ "[tensorlake] SandboxClient is deprecated; use Sandbox.create() / Sandbox.connect() instead."
3714
+ );
3715
+ }
3539
3716
  this.apiUrl = options?.apiUrl ?? API_URL;
3540
3717
  this.apiKey = options?.apiKey ?? API_KEY;
3541
3718
  this.organizationId = options?.organizationId;
@@ -3575,6 +3752,7 @@ var init_client = __esm({
3575
3752
  return lifecyclePath(subpath, this.local, this.namespace);
3576
3753
  }
3577
3754
  // --- Sandbox CRUD ---
3755
+ /** Create a new sandbox. Returns immediately; the sandbox may still be starting. Use `createAndConnect()` for a blocking, ready-to-use handle. */
3578
3756
  async create(options) {
3579
3757
  const body = {
3580
3758
  resources: {
@@ -3601,8 +3779,10 @@ var init_client = __esm({
3601
3779
  this.path("sandboxes"),
3602
3780
  { body }
3603
3781
  );
3604
- return fromSnakeKeys(raw, "sandboxId");
3782
+ const result = fromSnakeKeys(raw, "sandboxId");
3783
+ return Object.assign(result, { traceId: raw.traceId });
3605
3784
  }
3785
+ /** Get current state and metadata for a sandbox by ID. */
3606
3786
  async get(sandboxId) {
3607
3787
  const raw = await this.http.requestJson(
3608
3788
  "GET",
@@ -3610,6 +3790,7 @@ var init_client = __esm({
3610
3790
  );
3611
3791
  return fromSnakeKeys(raw, "sandboxId");
3612
3792
  }
3793
+ /** List all sandboxes in the namespace. */
3613
3794
  async list() {
3614
3795
  const raw = await this.http.requestJson(
3615
3796
  "GET",
@@ -3619,6 +3800,7 @@ var init_client = __esm({
3619
3800
  (s) => fromSnakeKeys(s, "sandboxId")
3620
3801
  );
3621
3802
  }
3803
+ /** Update sandbox properties such as name, exposed ports, and proxy auth settings. */
3622
3804
  async update(sandboxId, options) {
3623
3805
  const body = {};
3624
3806
  if (options.name != null) body.name = options.name;
@@ -3638,6 +3820,7 @@ var init_client = __esm({
3638
3820
  );
3639
3821
  return fromSnakeKeys(raw, "sandboxId");
3640
3822
  }
3823
+ /** Get the current proxy port settings for a sandbox. */
3641
3824
  async getPortAccess(sandboxId) {
3642
3825
  const info = await this.get(sandboxId);
3643
3826
  return {
@@ -3646,6 +3829,7 @@ var init_client = __esm({
3646
3829
  sandboxUrl: info.sandboxUrl
3647
3830
  };
3648
3831
  }
3832
+ /** Add one or more user ports to the sandbox proxy allowlist. */
3649
3833
  async exposePorts(sandboxId, ports, options) {
3650
3834
  const requestedPorts = normalizeUserPorts(ports);
3651
3835
  const current = await this.getPortAccess(sandboxId);
@@ -3658,6 +3842,7 @@ var init_client = __esm({
3658
3842
  exposedPorts: desiredPorts
3659
3843
  });
3660
3844
  }
3845
+ /** Remove one or more user ports from the sandbox proxy allowlist. */
3661
3846
  async unexposePorts(sandboxId, ports) {
3662
3847
  const requestedPorts = normalizeUserPorts(ports);
3663
3848
  const current = await this.getPortAccess(sandboxId);
@@ -3668,32 +3853,98 @@ var init_client = __esm({
3668
3853
  exposedPorts: desiredPorts
3669
3854
  });
3670
3855
  }
3856
+ /** Terminate and delete a sandbox. */
3671
3857
  async delete(sandboxId) {
3672
3858
  await this.http.requestJson(
3673
3859
  "DELETE",
3674
3860
  this.path(`sandboxes/${sandboxId}`)
3675
3861
  );
3676
3862
  }
3677
- async suspend(sandboxId) {
3863
+ /**
3864
+ * Suspend a named sandbox, preserving its state for later resume.
3865
+ *
3866
+ * Only sandboxes created with a `name` can be suspended; ephemeral sandboxes
3867
+ * cannot. By default blocks until the sandbox is fully `Suspended`. Pass
3868
+ * `{ wait: false }` to return immediately after the request is sent
3869
+ * (fire-and-return); the server processes the suspend asynchronously.
3870
+ *
3871
+ * @param sandboxId - ID or name of the sandbox.
3872
+ * @param options.wait - If `true` (default), poll until `Suspended`. Pass `false` to fire-and-return.
3873
+ * @param options.timeout - Max seconds to wait when `wait=true` (default 300).
3874
+ * @param options.pollInterval - Seconds between status polls when `wait=true` (default 1).
3875
+ * @throws {SandboxError} If `wait=true` and the sandbox does not reach `Suspended` within `timeout`.
3876
+ */
3877
+ async suspend(sandboxId, options) {
3678
3878
  await this.http.requestResponse(
3679
3879
  "POST",
3680
3880
  this.path(`sandboxes/${sandboxId}/suspend`)
3681
3881
  );
3882
+ if (options?.wait === false) return;
3883
+ const timeout = options?.timeout ?? 300;
3884
+ const pollInterval = options?.pollInterval ?? 1;
3885
+ const deadline = Date.now() + timeout * 1e3;
3886
+ while (Date.now() < deadline) {
3887
+ const info = await this.get(sandboxId);
3888
+ if (info.status === "suspended" /* SUSPENDED */) return;
3889
+ if (info.status === "terminated" /* TERMINATED */) {
3890
+ throw new SandboxError(`Sandbox ${sandboxId} terminated while waiting for suspend`);
3891
+ }
3892
+ await sleep2(pollInterval * 1e3);
3893
+ }
3894
+ throw new SandboxError(`Sandbox ${sandboxId} did not suspend within ${timeout}s`);
3682
3895
  }
3683
- async resume(sandboxId) {
3896
+ /**
3897
+ * Resume a suspended sandbox and bring it back to `Running`.
3898
+ *
3899
+ * By default blocks until the sandbox is `Running` and routable. Pass
3900
+ * `{ wait: false }` to return immediately after the request is sent
3901
+ * (fire-and-return); the server processes the resume asynchronously.
3902
+ *
3903
+ * @param sandboxId - ID or name of the sandbox.
3904
+ * @param options.wait - If `true` (default), poll until `Running`. Pass `false` to fire-and-return.
3905
+ * @param options.timeout - Max seconds to wait when `wait=true` (default 300).
3906
+ * @param options.pollInterval - Seconds between status polls when `wait=true` (default 1).
3907
+ * @throws {SandboxError} If `wait=true` and the sandbox does not reach `Running` within `timeout`.
3908
+ */
3909
+ async resume(sandboxId, options) {
3684
3910
  await this.http.requestResponse(
3685
3911
  "POST",
3686
3912
  this.path(`sandboxes/${sandboxId}/resume`)
3687
3913
  );
3914
+ if (options?.wait === false) return;
3915
+ const timeout = options?.timeout ?? 300;
3916
+ const pollInterval = options?.pollInterval ?? 1;
3917
+ const deadline = Date.now() + timeout * 1e3;
3918
+ while (Date.now() < deadline) {
3919
+ const info = await this.get(sandboxId);
3920
+ if (info.status === "running" /* RUNNING */) return;
3921
+ if (info.status === "terminated" /* TERMINATED */) {
3922
+ throw new SandboxError(`Sandbox ${sandboxId} terminated while waiting for resume`);
3923
+ }
3924
+ await sleep2(pollInterval * 1e3);
3925
+ }
3926
+ throw new SandboxError(`Sandbox ${sandboxId} did not resume within ${timeout}s`);
3688
3927
  }
3928
+ /** Claim a warm sandbox from a pool, creating one if no warm containers are available. */
3689
3929
  async claim(poolId) {
3690
3930
  const raw = await this.http.requestJson(
3691
3931
  "POST",
3692
3932
  this.path(`sandbox-pools/${poolId}/sandboxes`)
3693
3933
  );
3694
- return fromSnakeKeys(raw, "sandboxId");
3934
+ const result = fromSnakeKeys(raw, "sandboxId");
3935
+ return Object.assign(result, { traceId: raw.traceId });
3695
3936
  }
3696
3937
  // --- Snapshots ---
3938
+ /**
3939
+ * Request a snapshot of a running sandbox's filesystem.
3940
+ *
3941
+ * This call **returns immediately** with a `snapshotId` and `in_progress`
3942
+ * status — the snapshot is created asynchronously. Poll `getSnapshot()` until
3943
+ * `completed` or `failed`, or use `snapshotAndWait()` to block automatically.
3944
+ *
3945
+ * @param options.contentMode - `"filesystem_only"` for cold-boot snapshots (e.g. image builds).
3946
+ * Omit to use the server default (full VM snapshot).
3947
+ */
3697
3948
  async snapshot(sandboxId, options) {
3698
3949
  const requestOptions = options?.contentMode != null ? { body: { snapshot_content_mode: options.contentMode } } : void 0;
3699
3950
  const raw = await this.http.requestJson(
@@ -3703,6 +3954,7 @@ var init_client = __esm({
3703
3954
  );
3704
3955
  return fromSnakeKeys(raw, "snapshotId");
3705
3956
  }
3957
+ /** Get current status and metadata for a snapshot by ID. */
3706
3958
  async getSnapshot(snapshotId) {
3707
3959
  const raw = await this.http.requestJson(
3708
3960
  "GET",
@@ -3710,6 +3962,7 @@ var init_client = __esm({
3710
3962
  );
3711
3963
  return fromSnakeKeys(raw, "snapshotId");
3712
3964
  }
3965
+ /** List all snapshots in the namespace. */
3713
3966
  async listSnapshots() {
3714
3967
  const raw = await this.http.requestJson(
3715
3968
  "GET",
@@ -3719,12 +3972,26 @@ var init_client = __esm({
3719
3972
  (s) => fromSnakeKeys(s, "snapshotId")
3720
3973
  );
3721
3974
  }
3975
+ /** Delete a snapshot by ID. */
3722
3976
  async deleteSnapshot(snapshotId) {
3723
3977
  await this.http.requestJson(
3724
3978
  "DELETE",
3725
3979
  this.path(`snapshots/${snapshotId}`)
3726
3980
  );
3727
3981
  }
3982
+ /**
3983
+ * Create a snapshot and block until it is committed.
3984
+ *
3985
+ * Combines `snapshot()` with polling `getSnapshot()` until `completed`.
3986
+ * Prefer `sandbox.checkpoint()` on a `Sandbox` handle for the same behavior
3987
+ * without managing the client separately.
3988
+ *
3989
+ * @param sandboxId - ID of the running sandbox to snapshot.
3990
+ * @param options.timeout - Max seconds to wait (default 300).
3991
+ * @param options.pollInterval - Seconds between status polls (default 1).
3992
+ * @param options.contentMode - Content mode passed through to `snapshot()`.
3993
+ * @throws {SandboxError} If the snapshot fails or `timeout` elapses.
3994
+ */
3728
3995
  async snapshotAndWait(sandboxId, options) {
3729
3996
  const timeout = options?.timeout ?? 300;
3730
3997
  const pollInterval = options?.pollInterval ?? 1;
@@ -3747,6 +4014,7 @@ var init_client = __esm({
3747
4014
  );
3748
4015
  }
3749
4016
  // --- Pools ---
4017
+ /** Create a new sandbox pool with warm pre-booted containers. */
3750
4018
  async createPool(options) {
3751
4019
  const body = {
3752
4020
  image: options.image,
@@ -3768,6 +4036,7 @@ var init_client = __esm({
3768
4036
  );
3769
4037
  return fromSnakeKeys(raw, "poolId");
3770
4038
  }
4039
+ /** Get current state and metadata for a sandbox pool by ID. */
3771
4040
  async getPool(poolId) {
3772
4041
  const raw = await this.http.requestJson(
3773
4042
  "GET",
@@ -3775,6 +4044,7 @@ var init_client = __esm({
3775
4044
  );
3776
4045
  return fromSnakeKeys(raw, "poolId");
3777
4046
  }
4047
+ /** List all sandbox pools in the namespace. */
3778
4048
  async listPools() {
3779
4049
  const raw = await this.http.requestJson(
3780
4050
  "GET",
@@ -3784,6 +4054,7 @@ var init_client = __esm({
3784
4054
  (p) => fromSnakeKeys(p, "poolId")
3785
4055
  );
3786
4056
  }
4057
+ /** Replace the configuration of an existing sandbox pool. */
3787
4058
  async updatePool(poolId, options) {
3788
4059
  const body = {
3789
4060
  image: options.image,
@@ -3805,6 +4076,7 @@ var init_client = __esm({
3805
4076
  );
3806
4077
  return fromSnakeKeys(raw, "poolId");
3807
4078
  }
4079
+ /** Delete a sandbox pool. Fails if the pool has active containers. */
3808
4080
  async deletePool(poolId) {
3809
4081
  await this.http.requestJson(
3810
4082
  "DELETE",
@@ -3812,6 +4084,7 @@ var init_client = __esm({
3812
4084
  );
3813
4085
  }
3814
4086
  // --- Connect ---
4087
+ /** Return a `Sandbox` handle for an existing running sandbox without verifying it exists. */
3815
4088
  connect(identifier, proxyUrl, routingHint) {
3816
4089
  const resolvedProxy = proxyUrl ?? resolveProxyUrl(this.apiUrl);
3817
4090
  return new Sandbox({
@@ -3823,6 +4096,15 @@ var init_client = __esm({
3823
4096
  routingHint
3824
4097
  });
3825
4098
  }
4099
+ /**
4100
+ * Create a sandbox, wait for it to reach `Running`, and return a connected handle.
4101
+ *
4102
+ * Blocks until the sandbox is ready or `startupTimeout` elapses. The returned
4103
+ * `Sandbox` auto-terminates when `terminate()` is called.
4104
+ *
4105
+ * @param options.startupTimeout - Max seconds to wait for `Running` status (default 60).
4106
+ * @throws {SandboxError} If the sandbox terminates during startup or the timeout elapses.
4107
+ */
3826
4108
  async createAndConnect(options) {
3827
4109
  const startupTimeout = options?.startupTimeout ?? 60;
3828
4110
  let result;
@@ -3834,6 +4116,7 @@ var init_client = __esm({
3834
4116
  if (result.status === "running" /* RUNNING */) {
3835
4117
  const sandbox = this.connect(result.sandboxId, options?.proxyUrl, result.routingHint);
3836
4118
  sandbox._setOwner(this);
4119
+ sandbox.traceId = result.traceId;
3837
4120
  return sandbox;
3838
4121
  }
3839
4122
  const deadline = Date.now() + startupTimeout * 1e3;
@@ -3842,6 +4125,7 @@ var init_client = __esm({
3842
4125
  if (info.status === "running" /* RUNNING */) {
3843
4126
  const sandbox = this.connect(result.sandboxId, options?.proxyUrl, info.routingHint);
3844
4127
  sandbox._setOwner(this);
4128
+ sandbox.traceId = result.traceId;
3845
4129
  return sandbox;
3846
4130
  }
3847
4131
  if (info.status === "terminated" /* TERMINATED */) {