tensorlake 0.5.135 → 0.5.136

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
@@ -49,7 +49,7 @@ var init_defaults = __esm({
49
49
  "src/defaults.ts"() {
50
50
  "use strict";
51
51
  init_cjs_shims();
52
- SDK_VERSION = "0.5.135";
52
+ SDK_VERSION = "0.5.136";
53
53
  API_URL = process.env.TENSORLAKE_API_URL ?? "https://api.tensorlake.ai";
54
54
  API_KEY = process.env.TENSORLAKE_API_KEY ?? void 0;
55
55
  NAMESPACE = process.env.INDEXIFY_NAMESPACE ?? "default";
@@ -82,7 +82,7 @@ function formatErrorDetails(errorDetails) {
82
82
  }
83
83
  return String(errorDetails);
84
84
  }
85
- var SandboxException, SandboxError, SandboxConnectionError, SandboxNotFoundError, PoolNotFoundError, PoolInUseError, RemoteAPIError, RequestNotFinishedError, RequestFailedError, RequestExecutionError;
85
+ var SandboxException, SandboxError, SandboxPending, SandboxConnectionError, SandboxNotFoundError, PoolNotFoundError, PoolInUseError, RemoteAPIError, RequestNotFinishedError, RequestFailedError, RequestExecutionError;
86
86
  var init_errors = __esm({
87
87
  "src/errors.ts"() {
88
88
  "use strict";
@@ -94,9 +94,27 @@ var init_errors = __esm({
94
94
  }
95
95
  };
96
96
  SandboxError = class extends SandboxException {
97
- constructor(message) {
97
+ reason;
98
+ sandboxId;
99
+ constructor(message, options) {
98
100
  super(message);
99
101
  this.name = "SandboxError";
102
+ if (options?.reason !== void 0) this.reason = options.reason;
103
+ if (options?.sandboxId !== void 0) this.sandboxId = options.sandboxId;
104
+ }
105
+ };
106
+ SandboxPending = class extends SandboxError {
107
+ pendingReason;
108
+ timeout;
109
+ constructor(sandboxId, options) {
110
+ let message = `Sandbox ${sandboxId} is still pending`;
111
+ if (options?.timeout !== void 0) message += ` after ${options.timeout}s`;
112
+ if (options?.pendingReason) message += ` (${options.pendingReason})`;
113
+ message += `; it keeps its place in the queue. Wait again with ready() or Sandbox.connect({ sandboxId: '${sandboxId}' }), or delete it to cancel.`;
114
+ super(message, { reason: options?.pendingReason, sandboxId });
115
+ this.name = "SandboxPending";
116
+ this.pendingReason = options?.pendingReason;
117
+ this.timeout = options?.timeout;
100
118
  }
101
119
  };
102
120
  SandboxConnectionError = class extends SandboxError {
@@ -107,11 +125,9 @@ var init_errors = __esm({
107
125
  }
108
126
  };
109
127
  SandboxNotFoundError = class extends SandboxError {
110
- sandboxId;
111
128
  constructor(sandboxId) {
112
- super(`Sandbox not found: ${sandboxId}`);
129
+ super(`Sandbox not found: ${sandboxId}`, { sandboxId });
113
130
  this.name = "SandboxNotFoundError";
114
- this.sandboxId = sandboxId;
115
131
  }
116
132
  };
117
133
  PoolNotFoundError = class extends SandboxError {
@@ -135,9 +151,6 @@ var init_errors = __esm({
135
151
  statusCode;
136
152
  /** Original response body, retained for compatibility and diagnostics. */
137
153
  responseMessage;
138
- sandboxId;
139
- /** Server reason, including unknown future reasons. */
140
- reason;
141
154
  errorDetails;
142
155
  constructor(statusCode, message) {
143
156
  let failure;
@@ -162,9 +175,10 @@ var init_errors = __esm({
162
175
  if (detail) displayMessage += `: ${detail}`;
163
176
  if (!reason && !detail) displayMessage = message;
164
177
  }
165
- super(`API error (status ${statusCode}): ${displayMessage}`);
166
- this.sandboxId = sandboxId;
167
- this.reason = reason;
178
+ super(`API error (status ${statusCode}): ${displayMessage}`, {
179
+ reason,
180
+ sandboxId
181
+ });
168
182
  this.errorDetails = errorDetails;
169
183
  this.name = "RemoteAPIError";
170
184
  this.statusCode = statusCode;
@@ -460,7 +474,7 @@ var init_native_worker_client = __esm({
460
474
  }
461
475
  };
462
476
  runtimeKey = /* @__PURE__ */ Symbol.for(
463
- `tensorlake.native.worker.${false ? "source" : "0.5.135"}.v1`
477
+ `tensorlake.native.worker.${false ? "source" : "0.5.136"}.v1`
464
478
  );
465
479
  runtimes = globalThis;
466
480
  }
@@ -612,6 +626,9 @@ var init_native_sandbox = __esm({
612
626
  });
613
627
 
614
628
  // src/models.ts
629
+ function isSandboxPending(status) {
630
+ return String(status) === "pending" /* PENDING */;
631
+ }
615
632
  function camelToSnake(str) {
616
633
  return str.replace(CAMEL_TO_SNAKE_RE, (ch) => "_" + ch.toLowerCase());
617
634
  }
@@ -668,7 +685,7 @@ function fromSnakeKeys(obj, idField) {
668
685
  }
669
686
  return obj;
670
687
  }
671
- var SandboxStatus, SnapshotStatus, ProcessStatus, StdinMode, OutputMode, ContainerState, CAMEL_TO_SNAKE_RE;
688
+ var SandboxStatus, SandboxPendingReason, TERMINATION_REASON_NO_CAPACITY, TERMINATION_REASON_CANCELLED, SnapshotStatus, ProcessStatus, StdinMode, OutputMode, ContainerState, CAMEL_TO_SNAKE_RE;
672
689
  var init_models = __esm({
673
690
  "src/models.ts"() {
674
691
  "use strict";
@@ -684,6 +701,19 @@ var init_models = __esm({
684
701
  SandboxStatus2["FAILED"] = "failed";
685
702
  return SandboxStatus2;
686
703
  })(SandboxStatus || {});
704
+ SandboxPendingReason = /* @__PURE__ */ ((SandboxPendingReason2) => {
705
+ SandboxPendingReason2["SCHEDULING"] = "scheduling";
706
+ SandboxPendingReason2["WAITING_FOR_CONTAINER"] = "waiting_for_container";
707
+ SandboxPendingReason2["NO_EXECUTORS_AVAILABLE"] = "no_executors_available";
708
+ SandboxPendingReason2["NO_RESOURCES_AVAILABLE"] = "no_resources_available";
709
+ SandboxPendingReason2["POOL_AT_CAPACITY"] = "pool_at_capacity";
710
+ SandboxPendingReason2["MAX_CAPACITY_REACHED"] = "max_capacity_reached";
711
+ SandboxPendingReason2["NO_EXECUTOR_FOR_SNAPSHOT_LOCATION"] = "no_executor_for_snapshot_location";
712
+ SandboxPendingReason2["UNPLACEABLE"] = "unplaceable";
713
+ return SandboxPendingReason2;
714
+ })(SandboxPendingReason || {});
715
+ TERMINATION_REASON_NO_CAPACITY = "no_capacity";
716
+ TERMINATION_REASON_CANCELLED = "cancelled";
687
717
  SnapshotStatus = /* @__PURE__ */ ((SnapshotStatus2) => {
688
718
  SnapshotStatus2["IN_PROGRESS"] = "in_progress";
689
719
  SnapshotStatus2["LOCAL_READY"] = "local_ready";
@@ -717,6 +747,64 @@ var init_models = __esm({
717
747
  }
718
748
  });
719
749
 
750
+ // src/pending-sandbox.ts
751
+ var PendingSandbox;
752
+ var init_pending_sandbox = __esm({
753
+ "src/pending-sandbox.ts"() {
754
+ "use strict";
755
+ init_cjs_shims();
756
+ PendingSandbox = class {
757
+ /** @internal */
758
+ constructor(record, binding) {
759
+ this.binding = binding;
760
+ this.sandboxId = record.sandboxId;
761
+ this.name = record.name;
762
+ this.state = record.state;
763
+ this.pendingReason = record.pendingReason;
764
+ this.routingHint = record.routingHint;
765
+ this.ingressEndpoint = record.ingressEndpoint;
766
+ this.sandboxUrl = record.sandboxUrl;
767
+ this.reason = record.reason;
768
+ this.terminationReason = record.terminationReason;
769
+ this.errorDetails = record.errorDetails;
770
+ this.traceId = binding.traceId;
771
+ }
772
+ sandboxId;
773
+ name;
774
+ state;
775
+ pendingReason;
776
+ routingHint;
777
+ ingressEndpoint;
778
+ sandboxUrl;
779
+ reason;
780
+ terminationReason;
781
+ errorDetails;
782
+ /** W3C trace id of the create request. */
783
+ traceId;
784
+ /** Fetch the sandbox's current state with one `GET`. */
785
+ async status() {
786
+ return this.binding.client.get(this.sandboxId);
787
+ }
788
+ /**
789
+ * Wait for the sandbox to be running and return a connected `Sandbox`.
790
+ *
791
+ * Polls `GET /sandboxes/{id}` every `pollInterval` seconds (default 2).
792
+ * Call it again after a `SandboxPending` to keep waiting.
793
+ *
794
+ * @throws {SandboxPending} `timeout` elapsed while the sandbox was still
795
+ * pending. It keeps its place in the queue; the error carries
796
+ * `sandboxId` and `pendingReason`.
797
+ * @throws {SandboxError} The sandbox settled somewhere else: `terminated`
798
+ * or `failed` (`reason` is the server's, such as `no_capacity` or
799
+ * `cancelled`), or `suspended`.
800
+ */
801
+ async ready(options) {
802
+ return this.binding.client._ready(this.sandboxId, this.binding, options);
803
+ }
804
+ };
805
+ }
806
+ });
807
+
720
808
  // src/tunnel.ts
721
809
  async function openTunnelWebSocket(options) {
722
810
  const wsUrl = buildTunnelWsUrl(options.baseUrl, options.remotePort);
@@ -3638,17 +3726,10 @@ var init_sandbox = __esm({
3638
3726
  this.ownsSandbox = true;
3639
3727
  this.lifecycleClient = client;
3640
3728
  }
3641
- // --- Static factory methods ---
3642
- /**
3643
- * Create a new sandbox and return a connected, running handle.
3644
- *
3645
- * Covers both fresh sandbox creation and restore-from-snapshot (set
3646
- * `snapshotId`). Blocks until the sandbox is `Running`.
3647
- *
3648
- * When `name` is already claimed, the server rejects the create with
3649
- * HTTP 409 (`RemoteAPIError`). To attach to the existing sandbox
3650
- * instead, use {@link Sandbox.getOrCreate}.
3651
- */
3729
+ /** @internal Used by PendingSandbox.ready for a non-owning handle. */
3730
+ _setLifecycleClient(client) {
3731
+ this.lifecycleClient = client;
3732
+ }
3652
3733
  static async create(options) {
3653
3734
  const { SandboxClient: SandboxClient2 } = await Promise.resolve().then(() => (init_client(), client_exports));
3654
3735
  const client = new SandboxClient2(
@@ -3656,6 +3737,19 @@ var init_sandbox = __esm({
3656
3737
  /* _internal */
3657
3738
  true
3658
3739
  );
3740
+ if (options?.wait === false) {
3741
+ if (options.poolId != null) {
3742
+ throw new SandboxError(
3743
+ "wait: false cannot be combined with poolId: a pool claim is answered synchronously"
3744
+ );
3745
+ }
3746
+ const requestTimeout = options.requestTimeout ?? options.startupTimeout ?? void 0;
3747
+ return client.requestPending(options, {
3748
+ proxyUrl: options.proxyUrl,
3749
+ requestTimeout,
3750
+ ownsSandbox: true
3751
+ });
3752
+ }
3659
3753
  return client.createAndConnect(options);
3660
3754
  }
3661
3755
  /**
@@ -3946,6 +4040,7 @@ var init_sandbox = __esm({
3946
4040
  */
3947
4041
  async refreshRoutingWhenRunning(deadline, pollInterval, timeout) {
3948
4042
  const client = this.requireLifecycleClient("refresh proxy routing");
4043
+ let last;
3949
4044
  while (Date.now() < deadline) {
3950
4045
  const info = await client.get(this.lifecycleIdentifier);
3951
4046
  if (info.status === "running" /* RUNNING */ && (info.sandboxUrl != null || this.proxy.hasExplicitProxyUrl())) {
@@ -3954,13 +4049,22 @@ var init_sandbox = __esm({
3954
4049
  }
3955
4050
  if (info.status === "terminated" /* TERMINATED */) {
3956
4051
  throw new SandboxError(
3957
- `Sandbox ${this.lifecycleIdentifier} terminated while refreshing proxy routing`
4052
+ `Sandbox ${this.lifecycleIdentifier} terminated while refreshing proxy routing`,
4053
+ { reason: info.terminationReason, sandboxId: info.sandboxId }
3958
4054
  );
3959
4055
  }
4056
+ last = info;
3960
4057
  await sleep(Math.min(pollInterval * 1e3, Math.max(0, deadline - Date.now())));
3961
4058
  }
4059
+ if (last?.status === "pending" /* PENDING */) {
4060
+ throw new SandboxPending(last.sandboxId, {
4061
+ pendingReason: last.pendingReason,
4062
+ timeout
4063
+ });
4064
+ }
3962
4065
  throw new SandboxError(
3963
- `Sandbox ${this.lifecycleIdentifier} did not provide refreshed proxy routing within ${timeout}s`
4066
+ `Sandbox ${this.lifecycleIdentifier} did not provide refreshed proxy routing within ${timeout}s`,
4067
+ { sandboxId: this.lifecycleIdentifier }
3964
4068
  );
3965
4069
  }
3966
4070
  /** Live-copy this running sandbox. */
@@ -4455,6 +4559,7 @@ var init_sandbox = __esm({
4455
4559
  // src/client.ts
4456
4560
  var client_exports = {};
4457
4561
  __export(client_exports, {
4562
+ DEFAULT_WAIT_POLL_INTERVAL_SEC: () => DEFAULT_WAIT_POLL_INTERVAL_SEC,
4458
4563
  SandboxClient: () => SandboxClient
4459
4564
  });
4460
4565
  function requireReadOnlySnapshotPin(fileSystemId, mountPath, readOnly, snapshotId) {
@@ -4493,6 +4598,12 @@ function gpuRequest(gpu, gpus, gpuModel) {
4493
4598
  }
4494
4599
  return [{ count: gpus, model: gpuModel }];
4495
4600
  }
4601
+ function startupFailure(sandboxId, status, options) {
4602
+ return new SandboxError(formatStartupFailureMessage(sandboxId, status, options), {
4603
+ reason: options.terminationReason,
4604
+ sandboxId
4605
+ });
4606
+ }
4496
4607
  function resolveRequestTimeoutMs(options) {
4497
4608
  if (options?.requestTimeout != null) {
4498
4609
  return secondsToMillis3(options.requestTimeout);
@@ -4553,7 +4664,7 @@ function validateUserPort(port) {
4553
4664
  function dedupeAndSortPorts(ports) {
4554
4665
  return [...new Set(ports)].sort((a, b) => a - b);
4555
4666
  }
4556
- var GPU_MODELS, SandboxClient, RESERVED_SANDBOX_MANAGEMENT_PORT;
4667
+ var DEFAULT_WAIT_POLL_INTERVAL_SEC, GPU_MODELS, SandboxClient, RESERVED_SANDBOX_MANAGEMENT_PORT;
4557
4668
  var init_client = __esm({
4558
4669
  "src/client.ts"() {
4559
4670
  "use strict";
@@ -4563,9 +4674,11 @@ var init_client = __esm({
4563
4674
  init_native_worker_client();
4564
4675
  init_native_sandbox();
4565
4676
  init_models();
4677
+ init_pending_sandbox();
4566
4678
  init_sandbox();
4567
4679
  init_sdk_timings();
4568
4680
  init_url();
4681
+ DEFAULT_WAIT_POLL_INTERVAL_SEC = 2;
4569
4682
  GPU_MODELS = /* @__PURE__ */ new Set([
4570
4683
  "A100-40GB",
4571
4684
  "A100-80GB",
@@ -4668,9 +4781,155 @@ var init_client = __esm({
4668
4781
  const { json } = await callNative(fn, context);
4669
4782
  return fromSnakeKeys(JSON.parse(json), idField);
4670
4783
  }
4671
- // --- Sandbox CRUD ---
4672
- /** Create a new sandbox. Returns immediately; the sandbox may still be starting. Use `createAndConnect()` for a blocking, ready-to-use handle. */
4673
4784
  async create(options) {
4785
+ if (options?.wait === false) {
4786
+ return this.requestPending(options, { ownsSandbox: false });
4787
+ }
4788
+ const body = _SandboxClient.buildCreateRequestBody(options);
4789
+ return this.tracedJson(
4790
+ () => this.native.createSandbox(JSON.stringify(body)),
4791
+ "sandboxId"
4792
+ );
4793
+ }
4794
+ /**
4795
+ * @internal Send the create with `wait: false` and return the pending
4796
+ * handle, bound to this client so `ready()` / `status()` work.
4797
+ */
4798
+ async requestPending(options, binding) {
4799
+ const body = _SandboxClient.buildCreateRequestBody({
4800
+ ...options,
4801
+ wait: false
4802
+ });
4803
+ const record = await this.tracedJson(
4804
+ () => this.native.createSandboxNoWait(JSON.stringify(body))
4805
+ );
4806
+ return new PendingSandbox(record, {
4807
+ client: this,
4808
+ proxyUrl: binding.proxyUrl,
4809
+ requestTimeout: binding.requestTimeout,
4810
+ requestedName: options?.name ?? null,
4811
+ ownsSandbox: binding.ownsSandbox,
4812
+ traceId: record.traceId
4813
+ });
4814
+ }
4815
+ /** @internal Implementation of `PendingSandbox.ready`. */
4816
+ async _ready(sandboxId, binding, options) {
4817
+ const budget = options?.timeout ?? this.requestTimeoutMs / 1e3;
4818
+ const observed = await this.waitForSandbox(
4819
+ sandboxId,
4820
+ budget,
4821
+ options?.pollInterval ?? DEFAULT_WAIT_POLL_INTERVAL_SEC
4822
+ );
4823
+ return this.settleWait(observed, {
4824
+ budget,
4825
+ proxyUrl: binding.proxyUrl,
4826
+ requestTimeout: binding.requestTimeout,
4827
+ cancelOnTimeout: options?.cancelOnTimeout ?? false,
4828
+ requestedName: binding.requestedName,
4829
+ ownsSandbox: binding.ownsSandbox,
4830
+ traceId: binding.traceId
4831
+ });
4832
+ }
4833
+ /**
4834
+ * Poll `get` every `pollInterval` seconds until the sandbox leaves
4835
+ * `pending` or `timeout` elapses, resolving with the last observation.
4836
+ * Never rejects for a timeout and never deletes.
4837
+ */
4838
+ async waitForSandbox(sandboxId, timeout, pollInterval) {
4839
+ if (!Number.isFinite(timeout) || timeout < 0) {
4840
+ throw new SandboxError("timeout must be a non-negative number of seconds");
4841
+ }
4842
+ if (!Number.isFinite(pollInterval) || pollInterval <= 0) {
4843
+ throw new SandboxError("pollInterval must be a positive number of seconds");
4844
+ }
4845
+ return this.tracedJson(
4846
+ () => this.native.waitForSandbox(sandboxId, timeout, pollInterval),
4847
+ "sandboxId",
4848
+ { sandboxId, notFoundKind: "sandbox" }
4849
+ );
4850
+ }
4851
+ /** Turn the last observed sandbox state into a handle or an error. */
4852
+ async settleWait(observed, options) {
4853
+ const { status, sandboxId } = observed;
4854
+ if (isSandboxPending(status)) {
4855
+ if (options.cancelOnTimeout) {
4856
+ try {
4857
+ await this.delete(sandboxId);
4858
+ } catch {
4859
+ }
4860
+ throw new SandboxError(
4861
+ `Sandbox ${sandboxId} did not start within ${options.budget}s`,
4862
+ { reason: observed.pendingReason, sandboxId }
4863
+ );
4864
+ }
4865
+ throw new SandboxPending(sandboxId, {
4866
+ pendingReason: observed.pendingReason,
4867
+ timeout: options.budget
4868
+ });
4869
+ }
4870
+ if (status === "running" /* RUNNING */) {
4871
+ const explicitProxyUrl = options.proxyUrl ?? explicitProxyUrlOverride() ?? void 0;
4872
+ const selectedProxyUrl = await this.native.selectSandboxProxyUrl(
4873
+ sandboxId,
4874
+ observed.sandboxUrl ?? null,
4875
+ observed.ingressEndpoint ?? null,
4876
+ explicitProxyUrl ?? null
4877
+ );
4878
+ const sandbox = this.connect(
4879
+ sandboxId,
4880
+ selectedProxyUrl,
4881
+ observed.routingHint,
4882
+ options.requestTimeout,
4883
+ explicitProxyUrl
4884
+ );
4885
+ if (options.ownsSandbox) sandbox._setOwner(this);
4886
+ else sandbox._setLifecycleClient(this);
4887
+ sandbox.traceId = options.traceId ?? observed.traceId;
4888
+ sandbox._setLifecycleIdentifier(sandboxId);
4889
+ sandbox._setName(observed.name ?? options.requestedName ?? null);
4890
+ return sandbox;
4891
+ }
4892
+ if (status === "terminated" /* TERMINATED */ || status === "failed" /* FAILED */) {
4893
+ throw startupFailure(sandboxId, status, {
4894
+ errorDetails: observed.errorDetails,
4895
+ terminationReason: observed.terminationReason
4896
+ });
4897
+ }
4898
+ throw new SandboxError(
4899
+ `Sandbox ${sandboxId} is ${status}, not running; ` + (status === "suspended" /* SUSPENDED */ ? "resume it to run it again" : "wait for it to settle"),
4900
+ { reason: String(status), sandboxId }
4901
+ );
4902
+ }
4903
+ /**
4904
+ * Routing for a lazy `connect` handle. A sandbox that is still pending is
4905
+ * waited on here rather than failing: readiness is a property of the
4906
+ * sandbox, not of the create call that requested it.
4907
+ */
4908
+ async resolveRoutingInfo(identifier, requestTimeout) {
4909
+ const info = await this.get(identifier);
4910
+ if (info.status !== "pending" /* PENDING */) return info;
4911
+ const budget = requestTimeout ?? this.requestTimeoutMs / 1e3;
4912
+ const observed = await this.waitForSandbox(
4913
+ info.sandboxId,
4914
+ budget,
4915
+ DEFAULT_WAIT_POLL_INTERVAL_SEC
4916
+ );
4917
+ if (isSandboxPending(observed.status)) {
4918
+ throw new SandboxPending(observed.sandboxId, {
4919
+ pendingReason: observed.pendingReason,
4920
+ timeout: budget
4921
+ });
4922
+ }
4923
+ if (observed.status === "terminated" /* TERMINATED */ || observed.status === "failed" /* FAILED */) {
4924
+ throw startupFailure(observed.sandboxId, observed.status, {
4925
+ errorDetails: observed.errorDetails,
4926
+ terminationReason: observed.terminationReason
4927
+ });
4928
+ }
4929
+ return observed;
4930
+ }
4931
+ /** @internal Build the create wire body; shared by `create` and `createAndConnect`. */
4932
+ static buildCreateRequestBody(options) {
4674
4933
  const gpuResources = gpuRequest(
4675
4934
  options?.gpu,
4676
4935
  options?.gpus,
@@ -4700,10 +4959,14 @@ var init_client = __esm({
4700
4959
  deny_out: options?.denyOut ?? []
4701
4960
  };
4702
4961
  }
4703
- return this.tracedJson(
4704
- () => this.native.createSandbox(JSON.stringify(body)),
4705
- "sandboxId"
4706
- );
4962
+ if (options?.maxPendingSecs != null) {
4963
+ if (!Number.isInteger(options.maxPendingSecs) || options.maxPendingSecs < 0) {
4964
+ throw new SandboxError("maxPendingSecs must be a non-negative integer");
4965
+ }
4966
+ body.max_pending_secs = options.maxPendingSecs;
4967
+ }
4968
+ if (options?.wait === false) body.wait = false;
4969
+ return body;
4707
4970
  }
4708
4971
  /** Get current state and metadata for a sandbox by ID. */
4709
4972
  async get(sandboxId) {
@@ -5151,7 +5414,7 @@ var init_client = __esm({
5151
5414
  organizationId: this.organizationId,
5152
5415
  projectId: this.projectId,
5153
5416
  routingHint,
5154
- resolveProxyInfo: async (currentIdentifier) => this.get(currentIdentifier),
5417
+ resolveProxyInfo: async (currentIdentifier) => this.resolveRoutingInfo(currentIdentifier, requestTimeout),
5155
5418
  requestTimeout,
5156
5419
  nativeClient: this.native
5157
5420
  });
@@ -5159,123 +5422,145 @@ var init_client = __esm({
5159
5422
  /**
5160
5423
  * Create a sandbox, wait for it to reach `Running`, and return a connected handle.
5161
5424
  *
5162
- * Blocks until the sandbox is ready or `requestTimeout` elapses. The returned
5163
- * `Sandbox` auto-terminates when `terminate()` is called.
5425
+ * This is `create({ wait: false })` followed by `PendingSandbox.ready()`
5426
+ * polling every `pollInterval` seconds, with `requestTimeout` as the wait
5427
+ * budget, in one call. The returned `Sandbox` auto-terminates when
5428
+ * `terminate()` is called.
5429
+ *
5430
+ * **When the wait runs out the sandbox is not deleted.** A `SandboxPending`
5431
+ * is thrown with the sandbox id, and the sandbox keeps its place in the
5432
+ * queue and starts whenever capacity arrives; `connect` with that id to
5433
+ * collect it, or `delete` it to give up. Pass
5434
+ * `cancelOnTimeout: true` for the previous behaviour (delete, then throw
5435
+ * `SandboxError`), or set `maxPendingSecs` to let the server fail it after
5436
+ * a bound. A pool claim (`poolId`) answers from the server-side wait; a
5437
+ * claim that is still starting is then waited on the same way.
5164
5438
  */
5165
5439
  async createAndConnect(options) {
5166
5440
  const opStart = nowMs();
5167
5441
  const requestTimeout = options?.requestTimeout ?? options?.startupTimeout ?? this.requestTimeoutMs / 1e3;
5168
5442
  const requestClient = this.withRequestTimeout(requestTimeout);
5443
+ const deadline = Date.now() + secondsToMillis3(requestTimeout);
5169
5444
  logSdkTimingEvent("sandbox.create", "start", {
5170
5445
  request_timeout_s: requestTimeout,
5171
5446
  image: options?.image,
5172
5447
  pool_id: options?.poolId
5173
5448
  });
5449
+ const requestedName = options?.poolId != null ? null : options?.name ?? null;
5450
+ let sandboxId;
5451
+ let traceId;
5174
5452
  const createStart = nowMs();
5175
- const result = options?.poolId != null ? await requestClient.claim(options.poolId, {
5176
- fileSystems: options.fileSystems
5177
- }) : await requestClient.create(options);
5178
- logSdkTiming(
5179
- "sandbox.create",
5180
- options?.poolId != null ? "claim_response" : "create_response",
5181
- createStart,
5182
- {
5453
+ if (options?.poolId != null) {
5454
+ const result = await requestClient.claim(options.poolId, {
5455
+ fileSystems: options.fileSystems
5456
+ });
5457
+ logSdkTiming("sandbox.create", "claim_response", createStart, {
5183
5458
  sandbox_id: result.sandboxId,
5184
5459
  status: result.status,
5185
5460
  server_trace_id: result.traceId
5186
- }
5187
- );
5188
- const requestedName = options?.poolId != null ? null : options?.name ?? null;
5189
- const finishConnect = async (sandboxId, routingHint, name, ingressEndpoint, sandboxUrl) => {
5190
- const explicitProxyUrl = options?.proxyUrl ?? explicitProxyUrlOverride() ?? void 0;
5191
- const selectedProxyUrl = await this.native.selectSandboxProxyUrl(
5192
- sandboxId,
5193
- sandboxUrl ?? null,
5194
- ingressEndpoint ?? null,
5195
- explicitProxyUrl ?? null
5196
- );
5197
- const sandbox = requestClient.connect(
5198
- sandboxId,
5199
- selectedProxyUrl,
5200
- routingHint,
5201
- requestTimeout,
5202
- explicitProxyUrl
5203
- );
5204
- sandbox._setOwner(requestClient);
5205
- sandbox.traceId = result.traceId;
5206
- sandbox._setLifecycleIdentifier(sandboxId);
5207
- sandbox._setName(name ?? requestedName);
5208
- logSdkTiming("sandbox.create", "complete", opStart, {
5209
- sandbox_id: sandboxId,
5210
- status: "running" /* RUNNING */,
5211
- server_trace_id: result.traceId,
5212
- routing_hint: routingHint,
5213
- ingress_endpoint: ingressEndpoint,
5214
- sandbox_url: sandboxUrl
5215
5461
  });
5216
- return sandbox;
5217
- };
5218
- if (result.status === "running" /* RUNNING */) {
5219
- return finishConnect(
5220
- result.sandboxId,
5221
- result.routingHint,
5222
- result.name,
5223
- result.ingressEndpoint,
5224
- result.sandboxUrl
5225
- );
5226
- }
5227
- if (result.status === "suspended" /* SUSPENDED */ || result.status === "terminated" /* TERMINATED */ || result.status === "failed" /* FAILED */) {
5228
- throw new SandboxError(
5229
- formatStartupFailureMessage(result.sandboxId, result.status, {
5462
+ if (result.status === "running" /* RUNNING */) {
5463
+ const explicitProxyUrl = options?.proxyUrl ?? explicitProxyUrlOverride() ?? void 0;
5464
+ const selectedProxyUrl = await this.native.selectSandboxProxyUrl(
5465
+ result.sandboxId,
5466
+ result.sandboxUrl ?? null,
5467
+ result.ingressEndpoint ?? null,
5468
+ explicitProxyUrl ?? null
5469
+ );
5470
+ const sandbox2 = requestClient.connect(
5471
+ result.sandboxId,
5472
+ selectedProxyUrl,
5473
+ result.routingHint,
5474
+ requestTimeout,
5475
+ explicitProxyUrl
5476
+ );
5477
+ sandbox2._setOwner(requestClient);
5478
+ sandbox2.traceId = result.traceId;
5479
+ sandbox2._setLifecycleIdentifier(result.sandboxId);
5480
+ sandbox2._setName(result.name ?? requestedName);
5481
+ logSdkTiming("sandbox.create", "complete", opStart, {
5482
+ sandbox_id: result.sandboxId,
5483
+ status: "running" /* RUNNING */,
5484
+ server_trace_id: result.traceId
5485
+ });
5486
+ return sandbox2;
5487
+ }
5488
+ if (result.status === "suspended" /* SUSPENDED */ || result.status === "terminated" /* TERMINATED */ || result.status === "failed" /* FAILED */) {
5489
+ throw startupFailure(result.sandboxId, result.status, {
5230
5490
  errorDetails: result.errorDetails,
5231
5491
  terminationReason: result.terminationReason ?? result.reason
5232
- })
5233
- );
5234
- }
5235
- if (result.status === "timeout" /* TIMEOUT */) {
5236
- try {
5237
- await requestClient.delete(result.sandboxId);
5238
- } catch {
5492
+ });
5239
5493
  }
5240
- throw new SandboxError(
5241
- `Sandbox ${result.sandboxId} did not start within ${requestTimeout}s`
5242
- );
5243
- }
5244
- const deadline = Date.now() + secondsToMillis3(requestTimeout);
5245
- while (Date.now() < deadline) {
5246
- const pollStart = nowMs();
5247
- const info = await requestClient.get(result.sandboxId);
5248
- logSdkTiming("sandbox.create", "poll_response", pollStart, {
5249
- sandbox_id: result.sandboxId,
5250
- status: info.status,
5251
- server_trace_id: info.traceId
5494
+ sandboxId = result.sandboxId;
5495
+ traceId = result.traceId;
5496
+ } else {
5497
+ const pending = await requestClient.requestPending(options, {
5498
+ ownsSandbox: true
5252
5499
  });
5253
- if (info.status === "running" /* RUNNING */) {
5254
- return finishConnect(
5255
- info.sandboxId,
5256
- info.routingHint,
5257
- info.name,
5258
- info.ingressEndpoint,
5259
- info.sandboxUrl
5500
+ logSdkTiming("sandbox.create", "create_response", createStart, {
5501
+ sandbox_id: pending.sandboxId,
5502
+ status: pending.state,
5503
+ server_trace_id: pending.traceId
5504
+ });
5505
+ sandboxId = pending.sandboxId;
5506
+ traceId = pending.traceId;
5507
+ if (pending.state === "running" /* RUNNING */) {
5508
+ const explicitProxyUrl = options?.proxyUrl ?? explicitProxyUrlOverride() ?? void 0;
5509
+ const selectedProxyUrl = await this.native.selectSandboxProxyUrl(
5510
+ sandboxId,
5511
+ pending.sandboxUrl ?? null,
5512
+ pending.ingressEndpoint ?? null,
5513
+ explicitProxyUrl ?? null
5260
5514
  );
5261
- }
5262
- if (info.status === "suspended" /* SUSPENDED */ || info.status === "terminated" /* TERMINATED */) {
5263
- throw new SandboxError(
5264
- formatStartupFailureMessage(result.sandboxId, info.status, {
5265
- errorDetails: info.errorDetails,
5266
- terminationReason: info.terminationReason
5267
- })
5515
+ const sandbox2 = requestClient.connect(
5516
+ sandboxId,
5517
+ selectedProxyUrl,
5518
+ pending.routingHint,
5519
+ requestTimeout,
5520
+ explicitProxyUrl
5268
5521
  );
5522
+ sandbox2._setOwner(requestClient);
5523
+ sandbox2.traceId = traceId;
5524
+ sandbox2._setLifecycleIdentifier(sandboxId);
5525
+ sandbox2._setName(pending.name ?? requestedName);
5526
+ logSdkTiming("sandbox.create", "complete", opStart, {
5527
+ sandbox_id: sandboxId,
5528
+ status: "running" /* RUNNING */,
5529
+ server_trace_id: traceId
5530
+ });
5531
+ return sandbox2;
5532
+ }
5533
+ if (pending.state === "terminated" /* TERMINATED */ || pending.state === "failed" /* FAILED */) {
5534
+ throw startupFailure(sandboxId, pending.state, {
5535
+ errorDetails: pending.errorDetails,
5536
+ terminationReason: pending.terminationReason ?? pending.reason
5537
+ });
5269
5538
  }
5270
- await sleep2(500);
5271
- }
5272
- try {
5273
- await requestClient.delete(result.sandboxId);
5274
- } catch {
5275
5539
  }
5276
- throw new SandboxError(
5277
- `Sandbox ${result.sandboxId} did not start within ${requestTimeout}s`
5540
+ const remaining = Math.max(0, (deadline - Date.now()) / 1e3);
5541
+ const observed = await requestClient.waitForSandbox(
5542
+ sandboxId,
5543
+ remaining,
5544
+ options?.pollInterval ?? DEFAULT_WAIT_POLL_INTERVAL_SEC
5278
5545
  );
5546
+ const sandbox = await requestClient.settleWait(observed, {
5547
+ budget: requestTimeout,
5548
+ proxyUrl: options?.proxyUrl,
5549
+ requestTimeout,
5550
+ cancelOnTimeout: options?.cancelOnTimeout ?? false,
5551
+ requestedName,
5552
+ ownsSandbox: true,
5553
+ traceId
5554
+ });
5555
+ logSdkTiming("sandbox.create", "complete", opStart, {
5556
+ sandbox_id: sandboxId,
5557
+ status: "running" /* RUNNING */,
5558
+ server_trace_id: traceId,
5559
+ routing_hint: observed.routingHint,
5560
+ ingress_endpoint: observed.ingressEndpoint,
5561
+ sandbox_url: observed.sandboxUrl
5562
+ });
5563
+ return sandbox;
5279
5564
  }
5280
5565
  };
5281
5566
  RESERVED_SANDBOX_MANAGEMENT_PORT = 9501;
@@ -6654,6 +6939,7 @@ __export(index_exports, {
6654
6939
  ImageBuildOperationType: () => ImageBuildOperationType,
6655
6940
  MountError: () => MountError,
6656
6941
  OutputMode: () => OutputMode,
6942
+ PendingSandbox: () => PendingSandbox,
6657
6943
  PoolInUseError: () => PoolInUseError,
6658
6944
  PoolNotFoundError: () => PoolNotFoundError,
6659
6945
  ProcessStatus: () => ProcessStatus,
@@ -6672,9 +6958,13 @@ __export(index_exports, {
6672
6958
  SandboxError: () => SandboxError,
6673
6959
  SandboxException: () => SandboxException,
6674
6960
  SandboxNotFoundError: () => SandboxNotFoundError,
6961
+ SandboxPending: () => SandboxPending,
6962
+ SandboxPendingReason: () => SandboxPendingReason,
6675
6963
  SandboxStatus: () => SandboxStatus,
6676
6964
  SnapshotStatus: () => SnapshotStatus,
6677
6965
  StdinMode: () => StdinMode,
6966
+ TERMINATION_REASON_CANCELLED: () => TERMINATION_REASON_CANCELLED,
6967
+ TERMINATION_REASON_NO_CAPACITY: () => TERMINATION_REASON_NO_CAPACITY,
6678
6968
  TcpTunnel: () => TcpTunnel,
6679
6969
  createFileSystem: () => createFileSystem,
6680
6970
  createSandboxImage: () => createSandboxImage,
@@ -6683,6 +6973,7 @@ __export(index_exports, {
6683
6973
  dockerfileContent: () => dockerfileContent,
6684
6974
  findSandboxImageByName: () => findSandboxImageByName,
6685
6975
  importSandboxImage: () => importSandboxImage,
6976
+ isSandboxPending: () => isSandboxPending,
6686
6977
  listFileSystems: () => listFileSystems,
6687
6978
  listSandboxImages: () => listSandboxImages,
6688
6979
  registerApplication: () => registerApplication,
@@ -6698,6 +6989,7 @@ module.exports = __toCommonJS(index_exports);
6698
6989
  init_cjs_shims();
6699
6990
  init_client();
6700
6991
  init_sandbox();
6992
+ init_pending_sandbox();
6701
6993
  init_sandbox();
6702
6994
  init_desktop();
6703
6995
  init_tunnel();
@@ -9714,6 +10006,7 @@ init_models();
9714
10006
  ImageBuildOperationType,
9715
10007
  MountError,
9716
10008
  OutputMode,
10009
+ PendingSandbox,
9717
10010
  PoolInUseError,
9718
10011
  PoolNotFoundError,
9719
10012
  ProcessStatus,
@@ -9732,9 +10025,13 @@ init_models();
9732
10025
  SandboxError,
9733
10026
  SandboxException,
9734
10027
  SandboxNotFoundError,
10028
+ SandboxPending,
10029
+ SandboxPendingReason,
9735
10030
  SandboxStatus,
9736
10031
  SnapshotStatus,
9737
10032
  StdinMode,
10033
+ TERMINATION_REASON_CANCELLED,
10034
+ TERMINATION_REASON_NO_CAPACITY,
9738
10035
  TcpTunnel,
9739
10036
  createFileSystem,
9740
10037
  createSandboxImage,
@@ -9743,6 +10040,7 @@ init_models();
9743
10040
  dockerfileContent,
9744
10041
  findSandboxImageByName,
9745
10042
  importSandboxImage,
10043
+ isSandboxPending,
9746
10044
  listFileSystems,
9747
10045
  listSandboxImages,
9748
10046
  registerApplication,