tensorlake 0.5.30 → 0.5.31

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
@@ -24,7 +24,8 @@ declare enum SandboxStatus {
24
24
  SNAPSHOTTING = "snapshotting",
25
25
  SUSPENDING = "suspending",
26
26
  SUSPENDED = "suspended",
27
- TERMINATED = "terminated"
27
+ TERMINATED = "terminated",
28
+ TIMEOUT = "timeout"
28
29
  }
29
30
  declare enum SnapshotStatus {
30
31
  IN_PROGRESS = "in_progress",
@@ -332,6 +333,10 @@ interface SandboxClientOptions {
332
333
  namespace?: string;
333
334
  maxRetries?: number;
334
335
  retryBackoffMs?: number;
336
+ /** Total HTTP request timeout in seconds. Default: 300. */
337
+ requestTimeout?: number;
338
+ /** @deprecated Use requestTimeout. Total HTTP request timeout in milliseconds. */
339
+ timeoutMs?: number;
335
340
  }
336
341
  interface SandboxOptions {
337
342
  sandboxId: string;
@@ -343,10 +348,17 @@ interface SandboxOptions {
343
348
  resolveProxyInfo?: (identifier: string) => Promise<SandboxInfo & {
344
349
  readonly traceId: string;
345
350
  }>;
351
+ /** Optional total HTTP request timeout in seconds for sandbox proxy operations. Omit for no total proxy timeout. */
352
+ requestTimeout?: number;
353
+ /** @deprecated Use requestTimeout. Optional total HTTP request timeout in milliseconds for sandbox proxy operations. */
354
+ timeoutMs?: number;
346
355
  }
347
356
  interface CreateAndConnectOptions extends CreateSandboxOptions {
348
357
  poolId?: string;
349
358
  proxyUrl?: string;
359
+ /** Total HTTP request timeout in seconds for sandbox startup. Default: client requestTimeout. */
360
+ requestTimeout?: number;
361
+ /** @deprecated Use requestTimeout. */
350
362
  startupTimeout?: number;
351
363
  }
352
364
  interface SuspendResumeOptions {
@@ -694,6 +706,9 @@ declare class SandboxClient {
694
706
  private readonly projectId;
695
707
  private readonly namespace;
696
708
  private readonly local;
709
+ private readonly maxRetries;
710
+ private readonly retryBackoffMs;
711
+ private readonly requestTimeoutMs;
697
712
  /** @internal Pass `true` to suppress the deprecation warning when used by `Sandbox.create()` / `Sandbox.connect()`. */
698
713
  constructor(options?: SandboxClientOptions, _internal?: boolean);
699
714
  /** Create a client for the TensorLake cloud platform. */
@@ -702,13 +717,18 @@ declare class SandboxClient {
702
717
  organizationId?: string;
703
718
  projectId?: string;
704
719
  apiUrl?: string;
720
+ requestTimeout?: number;
721
+ timeoutMs?: number;
705
722
  }): SandboxClient;
706
723
  /** Create a client for a local Indexify server. */
707
724
  static forLocalhost(options?: {
708
725
  apiUrl?: string;
709
726
  namespace?: string;
727
+ requestTimeout?: number;
728
+ timeoutMs?: number;
710
729
  }): SandboxClient;
711
730
  close(): void;
731
+ private withRequestTimeout;
712
732
  private path;
713
733
  /** Create a new sandbox. Returns immediately; the sandbox may still be starting. Use `createAndConnect()` for a blocking, ready-to-use handle. */
714
734
  create(options?: CreateSandboxOptions): Promise<Traced<CreateSandboxResponse>>;
@@ -813,14 +833,15 @@ declare class SandboxClient {
813
833
  /** Delete a sandbox pool. Fails if the pool has active containers. */
814
834
  deletePool(poolId: string): Promise<void>;
815
835
  /** Return a `Sandbox` handle for an existing running sandbox without verifying it exists. */
816
- connect(identifier: string, proxyUrl?: string, routingHint?: string): Sandbox;
836
+ connect(identifier: string, proxyUrl?: string, routingHint?: string, requestTimeout?: number): Sandbox;
817
837
  /**
818
838
  * Create a sandbox, wait for it to reach `Running`, and return a connected handle.
819
839
  *
820
- * Blocks until the sandbox is ready or `startupTimeout` elapses. The returned
840
+ * Blocks until the sandbox is ready or `requestTimeout` elapses. The returned
821
841
  * `Sandbox` auto-terminates when `terminate()` is called.
822
842
  *
823
- * @param options.startupTimeout - Max seconds to wait for `Running` status (default 60).
843
+ * @param options.requestTimeout - Max seconds to wait for `Running` status (default: client requestTimeout).
844
+ * @param options.startupTimeout - Deprecated alias for `requestTimeout`.
824
845
  * @throws {SandboxError} If the sandbox terminates during startup or the timeout elapses.
825
846
  */
826
847
  createAndConnect(options?: CreateAndConnectOptions): Promise<Sandbox>;
package/dist/index.d.ts CHANGED
@@ -24,7 +24,8 @@ declare enum SandboxStatus {
24
24
  SNAPSHOTTING = "snapshotting",
25
25
  SUSPENDING = "suspending",
26
26
  SUSPENDED = "suspended",
27
- TERMINATED = "terminated"
27
+ TERMINATED = "terminated",
28
+ TIMEOUT = "timeout"
28
29
  }
29
30
  declare enum SnapshotStatus {
30
31
  IN_PROGRESS = "in_progress",
@@ -332,6 +333,10 @@ interface SandboxClientOptions {
332
333
  namespace?: string;
333
334
  maxRetries?: number;
334
335
  retryBackoffMs?: number;
336
+ /** Total HTTP request timeout in seconds. Default: 300. */
337
+ requestTimeout?: number;
338
+ /** @deprecated Use requestTimeout. Total HTTP request timeout in milliseconds. */
339
+ timeoutMs?: number;
335
340
  }
336
341
  interface SandboxOptions {
337
342
  sandboxId: string;
@@ -343,10 +348,17 @@ interface SandboxOptions {
343
348
  resolveProxyInfo?: (identifier: string) => Promise<SandboxInfo & {
344
349
  readonly traceId: string;
345
350
  }>;
351
+ /** Optional total HTTP request timeout in seconds for sandbox proxy operations. Omit for no total proxy timeout. */
352
+ requestTimeout?: number;
353
+ /** @deprecated Use requestTimeout. Optional total HTTP request timeout in milliseconds for sandbox proxy operations. */
354
+ timeoutMs?: number;
346
355
  }
347
356
  interface CreateAndConnectOptions extends CreateSandboxOptions {
348
357
  poolId?: string;
349
358
  proxyUrl?: string;
359
+ /** Total HTTP request timeout in seconds for sandbox startup. Default: client requestTimeout. */
360
+ requestTimeout?: number;
361
+ /** @deprecated Use requestTimeout. */
350
362
  startupTimeout?: number;
351
363
  }
352
364
  interface SuspendResumeOptions {
@@ -694,6 +706,9 @@ declare class SandboxClient {
694
706
  private readonly projectId;
695
707
  private readonly namespace;
696
708
  private readonly local;
709
+ private readonly maxRetries;
710
+ private readonly retryBackoffMs;
711
+ private readonly requestTimeoutMs;
697
712
  /** @internal Pass `true` to suppress the deprecation warning when used by `Sandbox.create()` / `Sandbox.connect()`. */
698
713
  constructor(options?: SandboxClientOptions, _internal?: boolean);
699
714
  /** Create a client for the TensorLake cloud platform. */
@@ -702,13 +717,18 @@ declare class SandboxClient {
702
717
  organizationId?: string;
703
718
  projectId?: string;
704
719
  apiUrl?: string;
720
+ requestTimeout?: number;
721
+ timeoutMs?: number;
705
722
  }): SandboxClient;
706
723
  /** Create a client for a local Indexify server. */
707
724
  static forLocalhost(options?: {
708
725
  apiUrl?: string;
709
726
  namespace?: string;
727
+ requestTimeout?: number;
728
+ timeoutMs?: number;
710
729
  }): SandboxClient;
711
730
  close(): void;
731
+ private withRequestTimeout;
712
732
  private path;
713
733
  /** Create a new sandbox. Returns immediately; the sandbox may still be starting. Use `createAndConnect()` for a blocking, ready-to-use handle. */
714
734
  create(options?: CreateSandboxOptions): Promise<Traced<CreateSandboxResponse>>;
@@ -813,14 +833,15 @@ declare class SandboxClient {
813
833
  /** Delete a sandbox pool. Fails if the pool has active containers. */
814
834
  deletePool(poolId: string): Promise<void>;
815
835
  /** Return a `Sandbox` handle for an existing running sandbox without verifying it exists. */
816
- connect(identifier: string, proxyUrl?: string, routingHint?: string): Sandbox;
836
+ connect(identifier: string, proxyUrl?: string, routingHint?: string, requestTimeout?: number): Sandbox;
817
837
  /**
818
838
  * Create a sandbox, wait for it to reach `Running`, and return a connected handle.
819
839
  *
820
- * Blocks until the sandbox is ready or `startupTimeout` elapses. The returned
840
+ * Blocks until the sandbox is ready or `requestTimeout` elapses. The returned
821
841
  * `Sandbox` auto-terminates when `terminate()` is called.
822
842
  *
823
- * @param options.startupTimeout - Max seconds to wait for `Running` status (default 60).
843
+ * @param options.requestTimeout - Max seconds to wait for `Running` status (default: client requestTimeout).
844
+ * @param options.startupTimeout - Deprecated alias for `requestTimeout`.
824
845
  * @throws {SandboxError} If the sandbox terminates during startup or the timeout elapses.
825
846
  */
826
847
  createAndConnect(options?: CreateAndConnectOptions): Promise<Sandbox>;
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ var SDK_VERSION, API_URL, API_KEY, NAMESPACE, SANDBOX_PROXY_URL, DEFAULT_HTTP_TI
19
19
  var init_defaults = __esm({
20
20
  "src/defaults.ts"() {
21
21
  "use strict";
22
- SDK_VERSION = "0.5.30";
22
+ SDK_VERSION = "0.5.31";
23
23
  API_URL = process.env.TENSORLAKE_API_URL ?? "https://api.tensorlake.ai";
24
24
  API_KEY = process.env.TENSORLAKE_API_KEY ?? void 0;
25
25
  NAMESPACE = process.env.INDEXIFY_NAMESPACE ?? "default";
@@ -184,12 +184,13 @@ function anySignal(signals) {
184
184
  function sleep(ms) {
185
185
  return new Promise((resolve) => setTimeout(resolve, ms));
186
186
  }
187
- var HttpClient;
187
+ var REQUEST_TIMEOUT_HEADER, HttpClient;
188
188
  var init_http = __esm({
189
189
  "src/http.ts"() {
190
190
  "use strict";
191
191
  init_defaults();
192
192
  init_errors();
193
+ REQUEST_TIMEOUT_HEADER = "X-Tensorlake-Request-Timeout-Ms";
193
194
  setGlobalDispatcher(
194
195
  new Agent({
195
196
  keepAliveTimeout: 6e4,
@@ -208,10 +209,13 @@ var init_http = __esm({
208
209
  this.baseUrl = url.endsWith("/") ? url.slice(0, -1) : url;
209
210
  this.maxRetries = options.maxRetries ?? MAX_RETRIES;
210
211
  this.retryBackoffMs = options.retryBackoffMs ?? RETRY_BACKOFF_MS;
211
- this.timeoutMs = options.timeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;
212
+ this.timeoutMs = options.timeoutMs === void 0 ? DEFAULT_HTTP_TIMEOUT_MS : options.timeoutMs;
212
213
  this.headers = {
213
214
  "User-Agent": `tensorlake-typescript-sdk/${SDK_VERSION}`
214
215
  };
216
+ if (this.timeoutMs != null) {
217
+ this.headers[REQUEST_TIMEOUT_HEADER] = String(this.timeoutMs);
218
+ }
215
219
  if (options.apiKey) {
216
220
  this.headers["Authorization"] = `Bearer ${options.apiKey}`;
217
221
  }
@@ -240,7 +244,10 @@ var init_http = __esm({
240
244
  const response = await this.requestResponse(method, path2, {
241
245
  json: options?.body,
242
246
  headers: options?.headers,
243
- signal: options?.signal
247
+ signal: options?.signal,
248
+ retryableStatusCodes: options?.retryableStatusCodes,
249
+ allowedErrorStatusCodes: options?.allowedErrorStatusCodes,
250
+ allowHttpErrors: options?.allowHttpErrors
244
251
  });
245
252
  const text = await response.text();
246
253
  const data = text ? JSON.parse(text) : void 0;
@@ -292,7 +299,9 @@ var init_http = __esm({
292
299
  body,
293
300
  headers,
294
301
  options?.signal,
295
- options?.allowHttpErrors ?? false
302
+ options?.allowHttpErrors ?? false,
303
+ options?.retryableStatusCodes ?? RETRYABLE_STATUS_CODES,
304
+ options?.allowedErrorStatusCodes ?? /* @__PURE__ */ new Set()
296
305
  );
297
306
  return withTraceId(response, traceId);
298
307
  }
@@ -302,7 +311,7 @@ var init_http = __esm({
302
311
  const spanId = randomHex(8);
303
312
  return { traceparent: `00-${traceId}-${spanId}-01`, traceId };
304
313
  }
305
- async doFetch(method, path2, body, headers, signal, allowHttpErrors = false) {
314
+ async doFetch(method, path2, body, headers, signal, allowHttpErrors = false, retryableStatusCodes = RETRYABLE_STATUS_CODES, allowedErrorStatusCodes = /* @__PURE__ */ new Set()) {
306
315
  const url = `${this.baseUrl}${path2}`;
307
316
  const { traceparent, traceId } = _HttpClient.makeTraceparent();
308
317
  headers["traceparent"] = traceparent;
@@ -313,10 +322,7 @@ var init_http = __esm({
313
322
  await sleep(delay);
314
323
  }
315
324
  this.abortController = new AbortController();
316
- const timeoutId = setTimeout(
317
- () => this.abortController?.abort(),
318
- this.timeoutMs
319
- );
325
+ const timeoutId = this.timeoutMs == null ? null : setTimeout(() => this.abortController?.abort(), this.timeoutMs);
320
326
  const combinedSignal = signal ? anySignal([signal, this.abortController.signal]) : this.abortController.signal;
321
327
  try {
322
328
  const response = await undiciFetch(url, {
@@ -325,22 +331,22 @@ var init_http = __esm({
325
331
  body,
326
332
  signal: combinedSignal
327
333
  });
328
- clearTimeout(timeoutId);
334
+ if (timeoutId != null) clearTimeout(timeoutId);
329
335
  if (response.ok) return { response, traceId };
330
- if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < this.maxRetries) {
336
+ if (retryableStatusCodes.has(response.status) && attempt < this.maxRetries) {
331
337
  lastError = new RemoteAPIError(
332
338
  response.status,
333
339
  await response.text().catch(() => "")
334
340
  );
335
341
  continue;
336
342
  }
337
- if (allowHttpErrors) {
343
+ if (allowHttpErrors || allowedErrorStatusCodes.has(response.status)) {
338
344
  return { response, traceId };
339
345
  }
340
346
  const errorBody = await response.text().catch(() => "");
341
347
  throwMappedError(response.status, errorBody, path2);
342
348
  } catch (err) {
343
- clearTimeout(timeoutId);
349
+ if (timeoutId != null) clearTimeout(timeoutId);
344
350
  if (err instanceof RemoteAPIError || err instanceof SandboxNotFoundError || err instanceof PoolNotFoundError || err instanceof PoolInUseError) {
345
351
  throw err;
346
352
  }
@@ -412,6 +418,7 @@ var init_models = __esm({
412
418
  SandboxStatus3["SUSPENDING"] = "suspending";
413
419
  SandboxStatus3["SUSPENDED"] = "suspended";
414
420
  SandboxStatus3["TERMINATED"] = "terminated";
421
+ SandboxStatus3["TIMEOUT"] = "timeout";
415
422
  return SandboxStatus3;
416
423
  })(SandboxStatus || {});
417
424
  SnapshotStatus = /* @__PURE__ */ ((SnapshotStatus2) => {
@@ -3024,6 +3031,12 @@ function sendPtyFrame(socket, frame) {
3024
3031
  socket.send(frame, (error) => error ? reject(error) : resolve());
3025
3032
  });
3026
3033
  }
3034
+ function secondsToMillis3(seconds) {
3035
+ if (!Number.isFinite(seconds) || seconds <= 0) {
3036
+ throw new SandboxError("requestTimeout must be a positive number of seconds");
3037
+ }
3038
+ return Math.ceil(seconds * 1e3);
3039
+ }
3027
3040
  var DEFAULT_PROCESS_USER, SandboxProxyConnection, PTY_OP_DATA, PTY_OP_RESIZE, PTY_OP_READY, PTY_OP_EXIT, Pty, Sandbox;
3028
3041
  var init_sandbox = __esm({
3029
3042
  "src/sandbox.ts"() {
@@ -3125,7 +3138,8 @@ var init_sandbox = __esm({
3125
3138
  projectId: this.options.projectId,
3126
3139
  hostHeader,
3127
3140
  sandboxIdHeader,
3128
- routingHint
3141
+ routingHint,
3142
+ timeoutMs: this.options.requestTimeout != null ? secondsToMillis3(this.options.requestTimeout) : this.options.timeoutMs ?? null
3129
3143
  });
3130
3144
  }
3131
3145
  };
@@ -3346,9 +3360,7 @@ var init_sandbox = __esm({
3346
3360
  /* _internal */
3347
3361
  true
3348
3362
  );
3349
- const sandbox = await client.createAndConnect(options);
3350
- sandbox.lifecycleClient = client;
3351
- return sandbox;
3363
+ return client.createAndConnect(options);
3352
3364
  }
3353
3365
  /**
3354
3366
  * Attach to an existing sandbox and return a connected handle.
@@ -3367,7 +3379,8 @@ var init_sandbox = __esm({
3367
3379
  const sandbox = client.connect(
3368
3380
  options.sandboxId,
3369
3381
  options.proxyUrl,
3370
- options.routingHint
3382
+ options.routingHint,
3383
+ options.requestTimeout
3371
3384
  );
3372
3385
  sandbox.lifecycleClient = client;
3373
3386
  return sandbox;
@@ -3892,6 +3905,27 @@ var client_exports = {};
3892
3905
  __export(client_exports, {
3893
3906
  SandboxClient: () => SandboxClient
3894
3907
  });
3908
+ function resolveRequestTimeoutMs(options) {
3909
+ if (options?.requestTimeout != null) {
3910
+ return secondsToMillis4(options.requestTimeout);
3911
+ }
3912
+ if (options?.timeoutMs != null) {
3913
+ validateTimeoutMs(options.timeoutMs);
3914
+ return options.timeoutMs;
3915
+ }
3916
+ return DEFAULT_HTTP_TIMEOUT_MS;
3917
+ }
3918
+ function secondsToMillis4(seconds) {
3919
+ if (!Number.isFinite(seconds) || seconds <= 0) {
3920
+ throw new SandboxError("requestTimeout must be a positive number of seconds");
3921
+ }
3922
+ return Math.ceil(seconds * 1e3);
3923
+ }
3924
+ function validateTimeoutMs(timeoutMs) {
3925
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
3926
+ throw new SandboxError("timeoutMs must be a positive number of milliseconds");
3927
+ }
3928
+ }
3895
3929
  function sleep2(ms) {
3896
3930
  return new Promise((resolve) => setTimeout(resolve, ms));
3897
3931
  }
@@ -3948,7 +3982,7 @@ function validateUserPort(port) {
3948
3982
  function dedupeAndSortPorts(ports) {
3949
3983
  return [...new Set(ports)].sort((a, b) => a - b);
3950
3984
  }
3951
- var SandboxClient, RESERVED_SANDBOX_MANAGEMENT_PORT;
3985
+ var CREATE_SANDBOX_RETRYABLE_STATUS_CODES, CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES, SandboxClient, RESERVED_SANDBOX_MANAGEMENT_PORT;
3952
3986
  var init_client = __esm({
3953
3987
  "src/client.ts"() {
3954
3988
  "use strict";
@@ -3958,6 +3992,8 @@ var init_client = __esm({
3958
3992
  init_models();
3959
3993
  init_sandbox();
3960
3994
  init_url();
3995
+ CREATE_SANDBOX_RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([502, 503]);
3996
+ CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES = /* @__PURE__ */ new Set([504]);
3961
3997
  SandboxClient = class _SandboxClient {
3962
3998
  http;
3963
3999
  apiUrl;
@@ -3966,6 +4002,9 @@ var init_client = __esm({
3966
4002
  projectId;
3967
4003
  namespace;
3968
4004
  local;
4005
+ maxRetries;
4006
+ retryBackoffMs;
4007
+ requestTimeoutMs;
3969
4008
  /** @internal Pass `true` to suppress the deprecation warning when used by `Sandbox.create()` / `Sandbox.connect()`. */
3970
4009
  constructor(options, _internal = false) {
3971
4010
  if (!_internal) {
@@ -3979,13 +4018,17 @@ var init_client = __esm({
3979
4018
  this.projectId = options?.projectId;
3980
4019
  this.namespace = options?.namespace ?? NAMESPACE;
3981
4020
  this.local = isLocalhost(this.apiUrl);
4021
+ this.maxRetries = options?.maxRetries ?? MAX_RETRIES;
4022
+ this.retryBackoffMs = options?.retryBackoffMs ?? RETRY_BACKOFF_MS;
4023
+ this.requestTimeoutMs = resolveRequestTimeoutMs(options);
3982
4024
  this.http = new HttpClient({
3983
4025
  baseUrl: resolveSandboxLifecycleUrl(this.apiUrl),
3984
4026
  apiKey: this.apiKey,
3985
4027
  organizationId: this.organizationId,
3986
4028
  projectId: this.projectId,
3987
- maxRetries: options?.maxRetries ?? MAX_RETRIES,
3988
- retryBackoffMs: options?.retryBackoffMs ?? RETRY_BACKOFF_MS
4029
+ maxRetries: this.maxRetries,
4030
+ retryBackoffMs: this.retryBackoffMs,
4031
+ timeoutMs: this.requestTimeoutMs
3989
4032
  });
3990
4033
  }
3991
4034
  /** Create a client for the TensorLake cloud platform. */
@@ -3994,19 +4037,46 @@ var init_client = __esm({
3994
4037
  apiUrl: options?.apiUrl ?? "https://api.tensorlake.ai",
3995
4038
  apiKey: options?.apiKey,
3996
4039
  organizationId: options?.organizationId,
3997
- projectId: options?.projectId
4040
+ projectId: options?.projectId,
4041
+ requestTimeout: options?.requestTimeout,
4042
+ timeoutMs: options?.timeoutMs
3998
4043
  });
3999
4044
  }
4000
4045
  /** Create a client for a local Indexify server. */
4001
4046
  static forLocalhost(options) {
4002
4047
  return new _SandboxClient({
4003
4048
  apiUrl: options?.apiUrl ?? "http://localhost:8900",
4004
- namespace: options?.namespace ?? "default"
4049
+ namespace: options?.namespace ?? "default",
4050
+ requestTimeout: options?.requestTimeout,
4051
+ timeoutMs: options?.timeoutMs
4005
4052
  });
4006
4053
  }
4007
4054
  close() {
4008
4055
  this.http.close();
4009
4056
  }
4057
+ withRequestTimeout(requestTimeout) {
4058
+ if (requestTimeout == null) {
4059
+ return this;
4060
+ }
4061
+ const timeoutMs = secondsToMillis4(requestTimeout);
4062
+ if (timeoutMs === this.requestTimeoutMs) {
4063
+ return this;
4064
+ }
4065
+ return new _SandboxClient(
4066
+ {
4067
+ apiUrl: this.apiUrl,
4068
+ apiKey: this.apiKey,
4069
+ organizationId: this.organizationId,
4070
+ projectId: this.projectId,
4071
+ namespace: this.namespace,
4072
+ maxRetries: this.maxRetries,
4073
+ retryBackoffMs: this.retryBackoffMs,
4074
+ timeoutMs
4075
+ },
4076
+ /* _internal */
4077
+ true
4078
+ );
4079
+ }
4010
4080
  // --- Path helper ---
4011
4081
  path(subpath) {
4012
4082
  return lifecyclePath(subpath, this.local, this.namespace);
@@ -4037,7 +4107,11 @@ var init_client = __esm({
4037
4107
  const raw = await this.http.requestJson(
4038
4108
  "POST",
4039
4109
  this.path("sandboxes"),
4040
- { body }
4110
+ {
4111
+ body,
4112
+ retryableStatusCodes: CREATE_SANDBOX_RETRYABLE_STATUS_CODES,
4113
+ allowedErrorStatusCodes: CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES
4114
+ }
4041
4115
  );
4042
4116
  const result = fromSnakeKeys(raw, "sandboxId");
4043
4117
  return Object.assign(result, { traceId: raw.traceId });
@@ -4231,7 +4305,11 @@ var init_client = __esm({
4231
4305
  async claim(poolId) {
4232
4306
  const raw = await this.http.requestJson(
4233
4307
  "POST",
4234
- this.path(`sandbox-pools/${poolId}/sandboxes`)
4308
+ this.path(`sandbox-pools/${poolId}/sandboxes`),
4309
+ {
4310
+ retryableStatusCodes: CREATE_SANDBOX_RETRYABLE_STATUS_CODES,
4311
+ allowedErrorStatusCodes: CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES
4312
+ }
4235
4313
  );
4236
4314
  const result = fromSnakeKeys(raw, "sandboxId");
4237
4315
  return Object.assign(result, { traceId: raw.traceId });
@@ -4393,7 +4471,7 @@ var init_client = __esm({
4393
4471
  }
4394
4472
  // --- Connect ---
4395
4473
  /** Return a `Sandbox` handle for an existing running sandbox without verifying it exists. */
4396
- connect(identifier, proxyUrl, routingHint) {
4474
+ connect(identifier, proxyUrl, routingHint, requestTimeout) {
4397
4475
  const resolvedProxy = proxyUrl ?? resolveProxyUrl(this.apiUrl);
4398
4476
  return new Sandbox({
4399
4477
  sandboxId: identifier,
@@ -4402,29 +4480,33 @@ var init_client = __esm({
4402
4480
  organizationId: this.organizationId,
4403
4481
  projectId: this.projectId,
4404
4482
  routingHint,
4405
- resolveProxyInfo: proxyUrl == null ? async (currentIdentifier) => this.get(currentIdentifier) : void 0
4483
+ resolveProxyInfo: proxyUrl == null ? async (currentIdentifier) => this.get(currentIdentifier) : void 0,
4484
+ requestTimeout
4406
4485
  });
4407
4486
  }
4408
4487
  /**
4409
4488
  * Create a sandbox, wait for it to reach `Running`, and return a connected handle.
4410
4489
  *
4411
- * Blocks until the sandbox is ready or `startupTimeout` elapses. The returned
4490
+ * Blocks until the sandbox is ready or `requestTimeout` elapses. The returned
4412
4491
  * `Sandbox` auto-terminates when `terminate()` is called.
4413
4492
  *
4414
- * @param options.startupTimeout - Max seconds to wait for `Running` status (default 60).
4493
+ * @param options.requestTimeout - Max seconds to wait for `Running` status (default: client requestTimeout).
4494
+ * @param options.startupTimeout - Deprecated alias for `requestTimeout`.
4415
4495
  * @throws {SandboxError} If the sandbox terminates during startup or the timeout elapses.
4416
4496
  */
4417
4497
  async createAndConnect(options) {
4418
- const startupTimeout = options?.startupTimeout ?? 60;
4419
- const result = options?.poolId != null ? await this.claim(options.poolId) : await this.create(options);
4498
+ const requestTimeout = options?.requestTimeout ?? options?.startupTimeout ?? this.requestTimeoutMs / 1e3;
4499
+ const requestClient = this.withRequestTimeout(requestTimeout);
4500
+ const result = options?.poolId != null ? await requestClient.claim(options.poolId) : await requestClient.create(options);
4420
4501
  const requestedName = options?.poolId != null ? null : options?.name ?? null;
4421
4502
  const finishConnect = (routingHint, name, ingressEndpoint) => {
4422
- const sandbox = this.connect(
4503
+ const sandbox = requestClient.connect(
4423
4504
  result.sandboxId,
4424
4505
  options?.proxyUrl ?? ingressEndpoint,
4425
- routingHint
4506
+ routingHint,
4507
+ requestTimeout
4426
4508
  );
4427
- sandbox._setOwner(this);
4509
+ sandbox._setOwner(requestClient);
4428
4510
  sandbox.traceId = result.traceId;
4429
4511
  sandbox._setLifecycleIdentifier(result.sandboxId);
4430
4512
  sandbox._setName(name ?? requestedName);
@@ -4441,9 +4523,18 @@ var init_client = __esm({
4441
4523
  })
4442
4524
  );
4443
4525
  }
4444
- const deadline = Date.now() + startupTimeout * 1e3;
4526
+ if (result.status === "timeout" /* TIMEOUT */) {
4527
+ try {
4528
+ await requestClient.delete(result.sandboxId);
4529
+ } catch {
4530
+ }
4531
+ throw new SandboxError(
4532
+ `Sandbox ${result.sandboxId} did not start within ${requestTimeout}s`
4533
+ );
4534
+ }
4535
+ const deadline = Date.now() + secondsToMillis4(requestTimeout);
4445
4536
  while (Date.now() < deadline) {
4446
- const info = await this.get(result.sandboxId);
4537
+ const info = await requestClient.get(result.sandboxId);
4447
4538
  if (info.status === "running" /* RUNNING */) {
4448
4539
  return finishConnect(info.routingHint, info.name, info.ingressEndpoint);
4449
4540
  }
@@ -4458,11 +4549,11 @@ var init_client = __esm({
4458
4549
  await sleep2(500);
4459
4550
  }
4460
4551
  try {
4461
- await this.delete(result.sandboxId);
4552
+ await requestClient.delete(result.sandboxId);
4462
4553
  } catch {
4463
4554
  }
4464
4555
  throw new SandboxError(
4465
- `Sandbox ${result.sandboxId} did not start within ${startupTimeout}s`
4556
+ `Sandbox ${result.sandboxId} did not start within ${requestTimeout}s`
4466
4557
  );
4467
4558
  }
4468
4559
  };