@tensorlakeai/tensorlake 0.5.157 → 0.5.159

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.d.cts CHANGED
@@ -67,6 +67,8 @@ interface NativeSandboxProxyClient {
67
67
  }
68
68
  interface NativeSandboxClient {
69
69
  createSandbox(requestJson: string): Promise<TracedJson>;
70
+ /** Blocking create for createAndConnect: a `running`, `failed` or `timeout` record. */
71
+ createSandboxAndWait(requestJson: string): Promise<TracedJson>;
70
72
  /** Wait-free create (`wait: false`, ADR 0086). */
71
73
  createSandboxNoWait(requestJson: string): Promise<TracedJson>;
72
74
  /** Poll the sandbox until it leaves `pending` or the budget runs out; resolves with the last `SandboxInfo`. */
@@ -84,6 +86,8 @@ interface NativeSandboxClient {
84
86
  deleteSandbox(sandboxId: string): Promise<string>;
85
87
  suspendSandbox(sandboxId: string): Promise<string>;
86
88
  resumeSandbox(sandboxId: string): Promise<string>;
89
+ suspendSandboxAndWait(sandboxId: string, waitSec: number): Promise<boolean>;
90
+ resumeSandboxAndWait(sandboxId: string, waitSec: number): Promise<boolean>;
87
91
  attachFileSystem(sandboxId: string, fileSystemId: string, mountPath: string, readOnly?: boolean | null, prefetch?: boolean | null, snapshotId?: string | null, owner?: string | null): Promise<TracedJson>;
88
92
  detachFileSystem(sandboxId: string, mountPath: string): Promise<TracedJson>;
89
93
  createSnapshot(sandboxId: string, snapshotType?: string | null): Promise<TracedJson>;
@@ -306,6 +310,8 @@ interface ClaimSandboxOptions {
306
310
  fileSystems?: FileSystemMount[];
307
311
  }
308
312
  interface CreateSandboxOptions {
313
+ /** Allow proxy traffic to wake a suspended sandbox (default true). False requires a named sandbox. */
314
+ autoResume?: boolean;
309
315
  /** Optional sandbox image name. GPU sandboxes require a CAS image; when omitted with a GPU request, Tensorlake uses the server's configured GPU default. */
310
316
  image?: string;
311
317
  /** CPU count. Defaults to 1 for fresh creates; snapshot restores inherit it when omitted. */
@@ -326,6 +332,10 @@ interface CreateSandboxOptions {
326
332
  allowOut?: string[];
327
333
  denyOut?: string[];
328
334
  snapshotId?: string;
335
+ /** Whether exposed user ports are reachable without Tensorlake auth. Defaults to false for fresh creates; snapshot restores inherit it when omitted. */
336
+ allowUnauthenticatedAccess?: boolean;
337
+ /** User ports routable through the sandbox proxy. Port 9501 is reserved. Snapshot restores inherit ports when omitted; [] clears them. Does not enable unauthenticated access. */
338
+ exposedPorts?: number[];
329
339
  /** Optional name for the sandbox. Named sandboxes support suspend/resume. When absent the sandbox is ephemeral. */
330
340
  name?: string;
331
341
  /** File systems to mount on fresh creation or warm-pool claim, each at its own absolute, unique guest mount path. */
@@ -375,6 +385,8 @@ interface SandboxResourceResizeInfo {
375
385
  }
376
386
  /** Wait options apply only to resource updates and are ignored for other properties. */
377
387
  interface UpdateSandboxOptions extends ResourceResizeWaitOptions {
388
+ /** Change wake-on-traffic policy without waking or suspending the sandbox. Omit to keep it unchanged; false requires a named sandbox. Cannot be combined with resource resizing. */
389
+ autoResume?: boolean;
378
390
  /** Whole live vCPU target; omitted retains the current allocation. */
379
391
  cpus?: number;
380
392
  /** Memory target in MiB, with the same units as create; never rounded by the SDK. */
@@ -470,6 +482,8 @@ interface CopySandboxResponse {
470
482
  sandboxes: CopiedSandboxResponse[];
471
483
  }
472
484
  interface SandboxInfo {
485
+ /** Whether proxy traffic may wake this sandbox; defaults to true on older servers. */
486
+ autoResume: boolean;
473
487
  /**
474
488
  * The runtime the server chose for this sandbox: `firecracker`,
475
489
  * `cloud-hypervisor` or `gvisor`. Informational only; it is never sent and
@@ -897,7 +911,10 @@ interface CreateAndConnectOptions extends CreateSandboxOptions {
897
911
  * false: throw `SandboxPending` and leave the sandbox queued.
898
912
  */
899
913
  cancelOnTimeout?: boolean;
900
- /** Seconds between readiness polls. Default: 2. */
914
+ /**
915
+ * Seconds between readiness polls once the server's own wait has run out.
916
+ * Default: 2.
917
+ */
901
918
  pollInterval?: number;
902
919
  }
903
920
  /**
@@ -1115,8 +1132,8 @@ declare class Sandbox {
1115
1132
  *
1116
1133
  * Covers both fresh sandbox creation and restore-from-snapshot (set
1117
1134
  * `snapshotId`). With `wait` unset or `true` it blocks until the sandbox
1118
- * is `Running`: the create is sent with `wait: false` and the sandbox is
1119
- * polled every two seconds, with `requestTimeout` as the wait budget.
1135
+ * is `Running`: the server waits and answers as soon as it runs, with
1136
+ * `requestTimeout` as the wait budget.
1120
1137
  *
1121
1138
  * With `wait: false` it returns a {@link PendingSandbox} at once:
1122
1139
  * `pending.ready()` waits for it, `pending.status()` looks at it, and
@@ -1576,6 +1593,10 @@ declare class SandboxClient {
1576
1593
  * cannot. By default blocks until the sandbox is fully `Suspended`. Pass
1577
1594
  * `{ wait: false }` to return immediately after the request is sent
1578
1595
  * (fire-and-return); the server processes the suspend asynchronously.
1596
+ *
1597
+ * The server answers as soon as the suspend completes. When its wait runs
1598
+ * out first, or the server does not support waiting, the rest of `timeout`
1599
+ * is spent polling every `pollInterval` seconds.
1579
1600
  */
1580
1601
  suspend(sandboxId: string, options?: SuspendResumeOptions): Promise<void>;
1581
1602
  /**
@@ -1584,8 +1605,18 @@ declare class SandboxClient {
1584
1605
  * By default blocks until the sandbox is `Running` and routable. Pass
1585
1606
  * `{ wait: false }` to return immediately after the request is sent
1586
1607
  * (fire-and-return); the server processes the resume asynchronously.
1608
+ *
1609
+ * The server answers as soon as the sandbox runs. When its wait runs out
1610
+ * first, or the server does not support waiting, the rest of `timeout` is
1611
+ * spent polling every `pollInterval` seconds.
1587
1612
  */
1588
1613
  resume(sandboxId: string, options?: SuspendResumeOptions): Promise<void>;
1614
+ /**
1615
+ * Send a suspend or resume. With `wait`, the server waits for `target` and
1616
+ * answers whether it was reached; when its wait runs out first, the rest of
1617
+ * the timeout is spent polling.
1618
+ */
1619
+ private changeLifecycle;
1589
1620
  /**
1590
1621
  * Attach a registered file system to a running sandbox at `mountPath`.
1591
1622
  *
@@ -1669,10 +1700,9 @@ declare class SandboxClient {
1669
1700
  /**
1670
1701
  * Create a sandbox, wait for it to reach `Running`, and return a connected handle.
1671
1702
  *
1672
- * This is `create({ wait: false })` followed by `PendingSandbox.ready()`
1673
- * polling every `pollInterval` seconds, with `requestTimeout` as the wait
1674
- * budget, in one call. The returned `Sandbox` auto-terminates when
1675
- * `terminate()` is called.
1703
+ * The create waits on the server, which answers as soon as the sandbox
1704
+ * runs, with `requestTimeout` as the wait budget. The returned `Sandbox`
1705
+ * auto-terminates when `terminate()` is called.
1676
1706
  *
1677
1707
  * **When the wait runs out the sandbox is not deleted.** A `SandboxPending`
1678
1708
  * is thrown with the sandbox id, and the sandbox keeps its place in the
@@ -2794,6 +2824,8 @@ declare class RemoteAPIError extends SandboxError {
2794
2824
  readonly statusCode: number;
2795
2825
  /** Original response body, retained for compatibility and diagnostics. */
2796
2826
  readonly responseMessage: string;
2827
+ /** Machine-readable API error code, including proxy policy failures. */
2828
+ readonly code?: string;
2797
2829
  readonly sandboxId?: string;
2798
2830
  /** Server reason, including unknown future reasons. */
2799
2831
  readonly reason?: string;
package/dist/index.d.ts CHANGED
@@ -67,6 +67,8 @@ interface NativeSandboxProxyClient {
67
67
  }
68
68
  interface NativeSandboxClient {
69
69
  createSandbox(requestJson: string): Promise<TracedJson>;
70
+ /** Blocking create for createAndConnect: a `running`, `failed` or `timeout` record. */
71
+ createSandboxAndWait(requestJson: string): Promise<TracedJson>;
70
72
  /** Wait-free create (`wait: false`, ADR 0086). */
71
73
  createSandboxNoWait(requestJson: string): Promise<TracedJson>;
72
74
  /** Poll the sandbox until it leaves `pending` or the budget runs out; resolves with the last `SandboxInfo`. */
@@ -84,6 +86,8 @@ interface NativeSandboxClient {
84
86
  deleteSandbox(sandboxId: string): Promise<string>;
85
87
  suspendSandbox(sandboxId: string): Promise<string>;
86
88
  resumeSandbox(sandboxId: string): Promise<string>;
89
+ suspendSandboxAndWait(sandboxId: string, waitSec: number): Promise<boolean>;
90
+ resumeSandboxAndWait(sandboxId: string, waitSec: number): Promise<boolean>;
87
91
  attachFileSystem(sandboxId: string, fileSystemId: string, mountPath: string, readOnly?: boolean | null, prefetch?: boolean | null, snapshotId?: string | null, owner?: string | null): Promise<TracedJson>;
88
92
  detachFileSystem(sandboxId: string, mountPath: string): Promise<TracedJson>;
89
93
  createSnapshot(sandboxId: string, snapshotType?: string | null): Promise<TracedJson>;
@@ -306,6 +310,8 @@ interface ClaimSandboxOptions {
306
310
  fileSystems?: FileSystemMount[];
307
311
  }
308
312
  interface CreateSandboxOptions {
313
+ /** Allow proxy traffic to wake a suspended sandbox (default true). False requires a named sandbox. */
314
+ autoResume?: boolean;
309
315
  /** Optional sandbox image name. GPU sandboxes require a CAS image; when omitted with a GPU request, Tensorlake uses the server's configured GPU default. */
310
316
  image?: string;
311
317
  /** CPU count. Defaults to 1 for fresh creates; snapshot restores inherit it when omitted. */
@@ -326,6 +332,10 @@ interface CreateSandboxOptions {
326
332
  allowOut?: string[];
327
333
  denyOut?: string[];
328
334
  snapshotId?: string;
335
+ /** Whether exposed user ports are reachable without Tensorlake auth. Defaults to false for fresh creates; snapshot restores inherit it when omitted. */
336
+ allowUnauthenticatedAccess?: boolean;
337
+ /** User ports routable through the sandbox proxy. Port 9501 is reserved. Snapshot restores inherit ports when omitted; [] clears them. Does not enable unauthenticated access. */
338
+ exposedPorts?: number[];
329
339
  /** Optional name for the sandbox. Named sandboxes support suspend/resume. When absent the sandbox is ephemeral. */
330
340
  name?: string;
331
341
  /** File systems to mount on fresh creation or warm-pool claim, each at its own absolute, unique guest mount path. */
@@ -375,6 +385,8 @@ interface SandboxResourceResizeInfo {
375
385
  }
376
386
  /** Wait options apply only to resource updates and are ignored for other properties. */
377
387
  interface UpdateSandboxOptions extends ResourceResizeWaitOptions {
388
+ /** Change wake-on-traffic policy without waking or suspending the sandbox. Omit to keep it unchanged; false requires a named sandbox. Cannot be combined with resource resizing. */
389
+ autoResume?: boolean;
378
390
  /** Whole live vCPU target; omitted retains the current allocation. */
379
391
  cpus?: number;
380
392
  /** Memory target in MiB, with the same units as create; never rounded by the SDK. */
@@ -470,6 +482,8 @@ interface CopySandboxResponse {
470
482
  sandboxes: CopiedSandboxResponse[];
471
483
  }
472
484
  interface SandboxInfo {
485
+ /** Whether proxy traffic may wake this sandbox; defaults to true on older servers. */
486
+ autoResume: boolean;
473
487
  /**
474
488
  * The runtime the server chose for this sandbox: `firecracker`,
475
489
  * `cloud-hypervisor` or `gvisor`. Informational only; it is never sent and
@@ -897,7 +911,10 @@ interface CreateAndConnectOptions extends CreateSandboxOptions {
897
911
  * false: throw `SandboxPending` and leave the sandbox queued.
898
912
  */
899
913
  cancelOnTimeout?: boolean;
900
- /** Seconds between readiness polls. Default: 2. */
914
+ /**
915
+ * Seconds between readiness polls once the server's own wait has run out.
916
+ * Default: 2.
917
+ */
901
918
  pollInterval?: number;
902
919
  }
903
920
  /**
@@ -1115,8 +1132,8 @@ declare class Sandbox {
1115
1132
  *
1116
1133
  * Covers both fresh sandbox creation and restore-from-snapshot (set
1117
1134
  * `snapshotId`). With `wait` unset or `true` it blocks until the sandbox
1118
- * is `Running`: the create is sent with `wait: false` and the sandbox is
1119
- * polled every two seconds, with `requestTimeout` as the wait budget.
1135
+ * is `Running`: the server waits and answers as soon as it runs, with
1136
+ * `requestTimeout` as the wait budget.
1120
1137
  *
1121
1138
  * With `wait: false` it returns a {@link PendingSandbox} at once:
1122
1139
  * `pending.ready()` waits for it, `pending.status()` looks at it, and
@@ -1576,6 +1593,10 @@ declare class SandboxClient {
1576
1593
  * cannot. By default blocks until the sandbox is fully `Suspended`. Pass
1577
1594
  * `{ wait: false }` to return immediately after the request is sent
1578
1595
  * (fire-and-return); the server processes the suspend asynchronously.
1596
+ *
1597
+ * The server answers as soon as the suspend completes. When its wait runs
1598
+ * out first, or the server does not support waiting, the rest of `timeout`
1599
+ * is spent polling every `pollInterval` seconds.
1579
1600
  */
1580
1601
  suspend(sandboxId: string, options?: SuspendResumeOptions): Promise<void>;
1581
1602
  /**
@@ -1584,8 +1605,18 @@ declare class SandboxClient {
1584
1605
  * By default blocks until the sandbox is `Running` and routable. Pass
1585
1606
  * `{ wait: false }` to return immediately after the request is sent
1586
1607
  * (fire-and-return); the server processes the resume asynchronously.
1608
+ *
1609
+ * The server answers as soon as the sandbox runs. When its wait runs out
1610
+ * first, or the server does not support waiting, the rest of `timeout` is
1611
+ * spent polling every `pollInterval` seconds.
1587
1612
  */
1588
1613
  resume(sandboxId: string, options?: SuspendResumeOptions): Promise<void>;
1614
+ /**
1615
+ * Send a suspend or resume. With `wait`, the server waits for `target` and
1616
+ * answers whether it was reached; when its wait runs out first, the rest of
1617
+ * the timeout is spent polling.
1618
+ */
1619
+ private changeLifecycle;
1589
1620
  /**
1590
1621
  * Attach a registered file system to a running sandbox at `mountPath`.
1591
1622
  *
@@ -1669,10 +1700,9 @@ declare class SandboxClient {
1669
1700
  /**
1670
1701
  * Create a sandbox, wait for it to reach `Running`, and return a connected handle.
1671
1702
  *
1672
- * This is `create({ wait: false })` followed by `PendingSandbox.ready()`
1673
- * polling every `pollInterval` seconds, with `requestTimeout` as the wait
1674
- * budget, in one call. The returned `Sandbox` auto-terminates when
1675
- * `terminate()` is called.
1703
+ * The create waits on the server, which answers as soon as the sandbox
1704
+ * runs, with `requestTimeout` as the wait budget. The returned `Sandbox`
1705
+ * auto-terminates when `terminate()` is called.
1676
1706
  *
1677
1707
  * **When the wait runs out the sandbox is not deleted.** A `SandboxPending`
1678
1708
  * is thrown with the sandbox id, and the sandbox keeps its place in the
@@ -2794,6 +2824,8 @@ declare class RemoteAPIError extends SandboxError {
2794
2824
  readonly statusCode: number;
2795
2825
  /** Original response body, retained for compatibility and diagnostics. */
2796
2826
  readonly responseMessage: string;
2827
+ /** Machine-readable API error code, including proxy policy failures. */
2828
+ readonly code?: string;
2797
2829
  readonly sandboxId?: string;
2798
2830
  /** Server reason, including unknown future reasons. */
2799
2831
  readonly reason?: string;
package/dist/index.js CHANGED
@@ -46,7 +46,7 @@ var init_defaults = __esm({
46
46
  "src/defaults.ts"() {
47
47
  "use strict";
48
48
  init_esm_shims();
49
- SDK_VERSION = "0.5.157";
49
+ SDK_VERSION = "0.5.159";
50
50
  API_URL = process.env.TENSORLAKE_API_URL ?? "https://api.tensorlake.ai";
51
51
  API_KEY = process.env.TENSORLAKE_API_KEY ?? void 0;
52
52
  NAMESPACE = process.env.INDEXIFY_NAMESPACE ?? "default";
@@ -181,13 +181,17 @@ var init_errors = __esm({
181
181
  statusCode;
182
182
  /** Original response body, retained for compatibility and diagnostics. */
183
183
  responseMessage;
184
+ /** Machine-readable API error code, including proxy policy failures. */
185
+ code;
184
186
  errorDetails;
185
187
  constructor(statusCode, message) {
186
188
  let failure;
189
+ let code;
187
190
  try {
188
191
  const payload = JSON.parse(message);
189
192
  if (payload && typeof payload === "object" && !Array.isArray(payload)) {
190
193
  const record = payload;
194
+ if (typeof record.code === "string") code = record.code;
191
195
  if (typeof record.sandbox_id === "string" && record.sandbox_id.trim() && (record.status === "failed" || record.status === "terminated"))
192
196
  failure = record;
193
197
  }
@@ -213,6 +217,7 @@ var init_errors = __esm({
213
217
  this.name = "RemoteAPIError";
214
218
  this.statusCode = statusCode;
215
219
  this.responseMessage = message;
220
+ this.code = code;
216
221
  }
217
222
  };
218
223
  RequestNotFinishedError = class extends Error {
@@ -509,7 +514,7 @@ var init_native_worker_client = __esm({
509
514
  }
510
515
  };
511
516
  runtimeKey = /* @__PURE__ */ Symbol.for(
512
- `tensorlake.native.worker.${false ? "source" : "0.5.157"}.v1`
517
+ `tensorlake.native.worker.${false ? "source" : "0.5.159"}.v1`
513
518
  );
514
519
  runtimes = globalThis;
515
520
  }
@@ -4930,6 +4935,12 @@ var init_client = __esm({
4930
4935
  if (options?.timeoutSecs != null) body.timeout_secs = options.timeoutSecs;
4931
4936
  if (options?.entrypoint != null) body.entrypoint = options.entrypoint;
4932
4937
  if (options?.snapshotId != null) body.snapshot_id = options.snapshotId;
4938
+ if (options?.allowUnauthenticatedAccess != null) {
4939
+ body.allow_unauthenticated_access = options.allowUnauthenticatedAccess;
4940
+ }
4941
+ if (options?.exposedPorts != null) {
4942
+ body.exposed_ports = normalizeUserPorts(options.exposedPorts);
4943
+ }
4933
4944
  if (options?.name != null) body.name = options.name;
4934
4945
  if (options?.fileSystems != null && options.fileSystems.length > 0) {
4935
4946
  body.file_systems = options.fileSystems.map(fileSystemMountToWire);
@@ -4941,6 +4952,7 @@ var init_client = __esm({
4941
4952
  deny_out: options?.denyOut ?? []
4942
4953
  };
4943
4954
  }
4955
+ if (options?.autoResume != null) body.auto_resume = options.autoResume;
4944
4956
  if (options?.maxPendingSecs != null) {
4945
4957
  if (!Number.isInteger(options.maxPendingSecs) || options.maxPendingSecs < 0) {
4946
4958
  throw new SandboxError("maxPendingSecs must be a non-negative integer");
@@ -5057,13 +5069,14 @@ var init_client = __esm({
5057
5069
  const resizing = Object.keys(resources).length > 0;
5058
5070
  if (resizing) {
5059
5071
  validateResizeWait(options);
5060
- if (options.name != null || options.allowUnauthenticatedAccess != null || options.exposedPorts != null || options.network !== void 0) {
5072
+ if (options.name != null || options.allowUnauthenticatedAccess != null || options.exposedPorts != null || options.autoResume != null || options.network !== void 0) {
5061
5073
  throw new SandboxError(
5062
5074
  "resources cannot be combined with other sandbox update fields"
5063
5075
  );
5064
5076
  }
5065
5077
  body.resources = resources;
5066
5078
  }
5079
+ if (options.autoResume != null) body.auto_resume = options.autoResume;
5067
5080
  if (options.name != null) body.name = options.name;
5068
5081
  if (options.allowUnauthenticatedAccess != null) {
5069
5082
  body.allow_unauthenticated_access = options.allowUnauthenticatedAccess;
@@ -5158,28 +5171,19 @@ var init_client = __esm({
5158
5171
  * cannot. By default blocks until the sandbox is fully `Suspended`. Pass
5159
5172
  * `{ wait: false }` to return immediately after the request is sent
5160
5173
  * (fire-and-return); the server processes the suspend asynchronously.
5174
+ *
5175
+ * The server answers as soon as the suspend completes. When its wait runs
5176
+ * out first, or the server does not support waiting, the rest of `timeout`
5177
+ * is spent polling every `pollInterval` seconds.
5161
5178
  */
5162
5179
  async suspend(sandboxId, options) {
5163
- await callNative(() => this.native.suspendSandbox(sandboxId), {
5180
+ await this.changeLifecycle(
5164
5181
  sandboxId,
5165
- notFoundKind: "sandbox"
5166
- });
5167
- if (options?.wait === false) return;
5168
- const timeout = options?.timeout ?? 300;
5169
- const pollInterval = options?.pollInterval ?? 1;
5170
- const deadline = Date.now() + timeout * 1e3;
5171
- while (Date.now() < deadline) {
5172
- const info = await this.get(sandboxId);
5173
- if (info.status === "suspended" /* SUSPENDED */) return;
5174
- if (info.status === "terminated" /* TERMINATED */) {
5175
- throw new SandboxError(
5176
- `Sandbox ${sandboxId} terminated while waiting for suspend`
5177
- );
5178
- }
5179
- await sleep2(pollInterval * 1e3);
5180
- }
5181
- throw new SandboxError(
5182
- `Sandbox ${sandboxId} did not suspend within ${timeout}s`
5182
+ "suspend",
5183
+ "suspended" /* SUSPENDED */,
5184
+ () => this.native.suspendSandbox(sandboxId),
5185
+ (waitSec) => this.native.suspendSandboxAndWait(sandboxId, waitSec),
5186
+ options
5183
5187
  );
5184
5188
  }
5185
5189
  /**
@@ -5188,28 +5192,58 @@ var init_client = __esm({
5188
5192
  * By default blocks until the sandbox is `Running` and routable. Pass
5189
5193
  * `{ wait: false }` to return immediately after the request is sent
5190
5194
  * (fire-and-return); the server processes the resume asynchronously.
5195
+ *
5196
+ * The server answers as soon as the sandbox runs. When its wait runs out
5197
+ * first, or the server does not support waiting, the rest of `timeout` is
5198
+ * spent polling every `pollInterval` seconds.
5191
5199
  */
5192
5200
  async resume(sandboxId, options) {
5193
- await callNative(() => this.native.resumeSandbox(sandboxId), {
5201
+ await this.changeLifecycle(
5194
5202
  sandboxId,
5195
- notFoundKind: "sandbox"
5196
- });
5197
- if (options?.wait === false) return;
5203
+ "resume",
5204
+ "running" /* RUNNING */,
5205
+ () => this.native.resumeSandbox(sandboxId),
5206
+ (waitSec) => this.native.resumeSandboxAndWait(sandboxId, waitSec),
5207
+ options
5208
+ );
5209
+ }
5210
+ /**
5211
+ * Send a suspend or resume. With `wait`, the server waits for `target` and
5212
+ * answers whether it was reached; when its wait runs out first, the rest of
5213
+ * the timeout is spent polling.
5214
+ */
5215
+ async changeLifecycle(sandboxId, action, target, send, sendAndWait, options) {
5216
+ const context = { sandboxId, notFoundKind: "sandbox" };
5217
+ if (options?.wait === false) {
5218
+ await callNative(send, context);
5219
+ return;
5220
+ }
5198
5221
  const timeout = options?.timeout ?? 300;
5199
5222
  const pollInterval = options?.pollInterval ?? 1;
5200
5223
  const deadline = Date.now() + timeout * 1e3;
5224
+ let settled;
5225
+ try {
5226
+ settled = await callNative(() => sendAndWait(timeout), context);
5227
+ } catch (error) {
5228
+ if (!(error instanceof RemoteAPIError) || error.statusCode !== 409) {
5229
+ throw error;
5230
+ }
5231
+ if ((await this.get(sandboxId)).status === target) return;
5232
+ throw error;
5233
+ }
5234
+ if (settled) return;
5201
5235
  while (Date.now() < deadline) {
5202
5236
  const info = await this.get(sandboxId);
5203
- if (info.status === "running" /* RUNNING */) return;
5237
+ if (info.status === target) return;
5204
5238
  if (info.status === "terminated" /* TERMINATED */) {
5205
5239
  throw new SandboxError(
5206
- `Sandbox ${sandboxId} terminated while waiting for resume`
5240
+ `Sandbox ${sandboxId} terminated while waiting for ${action}`
5207
5241
  );
5208
5242
  }
5209
5243
  await sleep2(pollInterval * 1e3);
5210
5244
  }
5211
5245
  throw new SandboxError(
5212
- `Sandbox ${sandboxId} did not resume within ${timeout}s`
5246
+ `Sandbox ${sandboxId} did not ${action} within ${timeout}s`
5213
5247
  );
5214
5248
  }
5215
5249
  /**
@@ -5472,10 +5506,9 @@ var init_client = __esm({
5472
5506
  /**
5473
5507
  * Create a sandbox, wait for it to reach `Running`, and return a connected handle.
5474
5508
  *
5475
- * This is `create({ wait: false })` followed by `PendingSandbox.ready()`
5476
- * polling every `pollInterval` seconds, with `requestTimeout` as the wait
5477
- * budget, in one call. The returned `Sandbox` auto-terminates when
5478
- * `terminate()` is called.
5509
+ * The create waits on the server, which answers as soon as the sandbox
5510
+ * runs, with `requestTimeout` as the wait budget. The returned `Sandbox`
5511
+ * auto-terminates when `terminate()` is called.
5479
5512
  *
5480
5513
  * **When the wait runs out the sandbox is not deleted.** A `SandboxPending`
5481
5514
  * is thrown with the sandbox id, and the sandbox keeps its place in the
@@ -5487,6 +5520,11 @@ var init_client = __esm({
5487
5520
  * claim that is still starting is then waited on the same way.
5488
5521
  */
5489
5522
  async createAndConnect(options) {
5523
+ if (options?.poolId != null && (options.allowUnauthenticatedAccess != null || options.exposedPorts != null)) {
5524
+ throw new SandboxError(
5525
+ "allowUnauthenticatedAccess and exposedPorts cannot be combined with poolId: pool claims inherit access settings from the pool."
5526
+ );
5527
+ }
5490
5528
  const opStart = nowMs();
5491
5529
  const requestTimeout = options?.requestTimeout ?? options?.startupTimeout ?? this.requestTimeoutMs / 1e3;
5492
5530
  const requestClient = this.withRequestTimeout(requestTimeout);
@@ -5497,11 +5535,13 @@ var init_client = __esm({
5497
5535
  pool_id: options?.poolId
5498
5536
  });
5499
5537
  const requestedName = options?.poolId != null ? null : options?.name ?? null;
5500
- let sandboxId;
5501
- let traceId;
5502
5538
  const createStart = nowMs();
5539
+ let result;
5540
+ if (options?.poolId != null && options.autoResume != null) {
5541
+ throw new SandboxError("autoResume cannot be set when claiming from a pool");
5542
+ }
5503
5543
  if (options?.poolId != null) {
5504
- const result = await requestClient.claim(options.poolId, {
5544
+ result = await requestClient.claim(options.poolId, {
5505
5545
  fileSystems: options.fileSystems
5506
5546
  });
5507
5547
  logSdkTiming("sandbox.create", "claim_response", createStart, {
@@ -5509,84 +5549,55 @@ var init_client = __esm({
5509
5549
  status: result.status,
5510
5550
  server_trace_id: result.traceId
5511
5551
  });
5512
- if (result.status === "running" /* RUNNING */) {
5513
- const explicitProxyUrl = options?.proxyUrl ?? explicitProxyUrlOverride() ?? void 0;
5514
- const selectedProxyUrl = await this.native.selectSandboxProxyUrl(
5515
- result.sandboxId,
5516
- result.sandboxUrl ?? null,
5517
- result.ingressEndpoint ?? null,
5518
- explicitProxyUrl ?? null
5519
- );
5520
- const sandbox2 = requestClient.connect(
5521
- result.sandboxId,
5522
- selectedProxyUrl,
5523
- result.routingHint,
5524
- requestTimeout,
5525
- explicitProxyUrl
5526
- );
5527
- sandbox2._setOwner(requestClient);
5528
- sandbox2.traceId = result.traceId;
5529
- sandbox2._setLifecycleIdentifier(result.sandboxId);
5530
- sandbox2._setName(result.name ?? requestedName);
5531
- logSdkTiming("sandbox.create", "complete", opStart, {
5532
- sandbox_id: result.sandboxId,
5533
- status: "running" /* RUNNING */,
5534
- server_trace_id: result.traceId
5535
- });
5536
- return sandbox2;
5537
- }
5538
- if (result.status === "suspended" /* SUSPENDED */ || result.status === "terminated" /* TERMINATED */ || result.status === "failed" /* FAILED */) {
5539
- throw startupFailure(result.sandboxId, result.status, {
5540
- errorDetails: result.errorDetails,
5541
- terminationReason: result.terminationReason ?? result.reason
5542
- });
5543
- }
5544
- sandboxId = result.sandboxId;
5545
- traceId = result.traceId;
5546
5552
  } else {
5547
- const pending = await requestClient.requestPending(options, {
5548
- ownsSandbox: true
5553
+ const body = _SandboxClient.buildCreateRequestBody({
5554
+ ...options,
5555
+ wait: void 0
5549
5556
  });
5557
+ result = await requestClient.tracedJson(
5558
+ () => requestClient.native.createSandboxAndWait(JSON.stringify(body)),
5559
+ "sandboxId"
5560
+ );
5550
5561
  logSdkTiming("sandbox.create", "create_response", createStart, {
5551
- sandbox_id: pending.sandboxId,
5552
- status: pending.state,
5553
- server_trace_id: pending.traceId
5562
+ sandbox_id: result.sandboxId,
5563
+ status: result.status,
5564
+ server_trace_id: result.traceId
5565
+ });
5566
+ }
5567
+ if (result.status === "running" /* RUNNING */) {
5568
+ const explicitProxyUrl = options?.proxyUrl ?? explicitProxyUrlOverride() ?? void 0;
5569
+ const selectedProxyUrl = await this.native.selectSandboxProxyUrl(
5570
+ result.sandboxId,
5571
+ result.sandboxUrl ?? null,
5572
+ result.ingressEndpoint ?? null,
5573
+ explicitProxyUrl ?? null
5574
+ );
5575
+ const sandbox2 = requestClient.connect(
5576
+ result.sandboxId,
5577
+ selectedProxyUrl,
5578
+ result.routingHint,
5579
+ requestTimeout,
5580
+ explicitProxyUrl
5581
+ );
5582
+ sandbox2._setOwner(requestClient);
5583
+ sandbox2.traceId = result.traceId;
5584
+ sandbox2._setLifecycleIdentifier(result.sandboxId);
5585
+ sandbox2._setName(result.name ?? requestedName);
5586
+ logSdkTiming("sandbox.create", "complete", opStart, {
5587
+ sandbox_id: result.sandboxId,
5588
+ status: "running" /* RUNNING */,
5589
+ server_trace_id: result.traceId
5590
+ });
5591
+ return sandbox2;
5592
+ }
5593
+ if (result.status === "suspended" /* SUSPENDED */ || result.status === "terminated" /* TERMINATED */ || result.status === "failed" /* FAILED */) {
5594
+ throw startupFailure(result.sandboxId, result.status, {
5595
+ errorDetails: result.errorDetails,
5596
+ terminationReason: result.terminationReason ?? result.reason
5554
5597
  });
5555
- sandboxId = pending.sandboxId;
5556
- traceId = pending.traceId;
5557
- if (pending.state === "running" /* RUNNING */) {
5558
- const explicitProxyUrl = options?.proxyUrl ?? explicitProxyUrlOverride() ?? void 0;
5559
- const selectedProxyUrl = await this.native.selectSandboxProxyUrl(
5560
- sandboxId,
5561
- pending.sandboxUrl ?? null,
5562
- pending.ingressEndpoint ?? null,
5563
- explicitProxyUrl ?? null
5564
- );
5565
- const sandbox2 = requestClient.connect(
5566
- sandboxId,
5567
- selectedProxyUrl,
5568
- pending.routingHint,
5569
- requestTimeout,
5570
- explicitProxyUrl
5571
- );
5572
- sandbox2._setOwner(requestClient);
5573
- sandbox2.traceId = traceId;
5574
- sandbox2._setLifecycleIdentifier(sandboxId);
5575
- sandbox2._setName(pending.name ?? requestedName);
5576
- logSdkTiming("sandbox.create", "complete", opStart, {
5577
- sandbox_id: sandboxId,
5578
- status: "running" /* RUNNING */,
5579
- server_trace_id: traceId
5580
- });
5581
- return sandbox2;
5582
- }
5583
- if (pending.state === "terminated" /* TERMINATED */ || pending.state === "failed" /* FAILED */) {
5584
- throw startupFailure(sandboxId, pending.state, {
5585
- errorDetails: pending.errorDetails,
5586
- terminationReason: pending.terminationReason ?? pending.reason
5587
- });
5588
- }
5589
5598
  }
5599
+ const sandboxId = result.sandboxId;
5600
+ const traceId = result.traceId;
5590
5601
  const remaining = Math.max(0, (deadline - Date.now()) / 1e3);
5591
5602
  const observed = await requestClient.waitForSandbox(
5592
5603
  sandboxId,