tensorlake 0.5.30 → 0.5.33

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.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/dist/index.cjs CHANGED
@@ -35,7 +35,7 @@ var SDK_VERSION, API_URL, API_KEY, NAMESPACE, SANDBOX_PROXY_URL, DEFAULT_HTTP_TI
35
35
  var init_defaults = __esm({
36
36
  "src/defaults.ts"() {
37
37
  "use strict";
38
- SDK_VERSION = "0.5.30";
38
+ SDK_VERSION = "0.5.33";
39
39
  API_URL = process.env.TENSORLAKE_API_URL ?? "https://api.tensorlake.ai";
40
40
  API_KEY = process.env.TENSORLAKE_API_KEY ?? void 0;
41
41
  NAMESPACE = process.env.INDEXIFY_NAMESPACE ?? "default";
@@ -48,6 +48,28 @@ var init_defaults = __esm({
48
48
  });
49
49
 
50
50
  // src/errors.ts
51
+ function describeError(err) {
52
+ const parts = [];
53
+ const seen = /* @__PURE__ */ new Set();
54
+ let current = err;
55
+ for (let depth = 0; depth < 6; depth++) {
56
+ if (current == null || typeof current !== "object" || seen.has(current)) {
57
+ break;
58
+ }
59
+ seen.add(current);
60
+ const e = current;
61
+ const message = typeof e.message === "string" ? e.message.trim() : "";
62
+ const code = typeof e.code === "string" ? e.code : void 0;
63
+ let segment = message;
64
+ if (code && (!message || !message.includes(code))) {
65
+ segment = segment ? `${segment} (${code})` : code;
66
+ }
67
+ if (segment && !parts.includes(segment)) parts.push(segment);
68
+ current = e.cause;
69
+ }
70
+ if (parts.length > 0) return parts.join(": ");
71
+ return err instanceof Error ? err.message : String(err);
72
+ }
51
73
  var SandboxException, SandboxError, SandboxConnectionError, SandboxNotFoundError, PoolNotFoundError, PoolInUseError, RemoteAPIError, RequestNotFinishedError, RequestFailedError, RequestExecutionError;
52
74
  var init_errors = __esm({
53
75
  "src/errors.ts"() {
@@ -65,9 +87,10 @@ var init_errors = __esm({
65
87
  }
66
88
  };
67
89
  SandboxConnectionError = class extends SandboxError {
68
- constructor(message) {
90
+ constructor(message, options) {
69
91
  super(`Connection error: ${message}`);
70
92
  this.name = "SandboxConnectionError";
93
+ if (options?.cause !== void 0) this.cause = options.cause;
71
94
  }
72
95
  };
73
96
  SandboxNotFoundError = class extends SandboxError {
@@ -195,13 +218,14 @@ function anySignal(signals) {
195
218
  function sleep(ms) {
196
219
  return new Promise((resolve) => setTimeout(resolve, ms));
197
220
  }
198
- var import_undici, HttpClient;
221
+ var import_undici, REQUEST_TIMEOUT_HEADER, HttpClient;
199
222
  var init_http = __esm({
200
223
  "src/http.ts"() {
201
224
  "use strict";
202
225
  import_undici = require("undici");
203
226
  init_defaults();
204
227
  init_errors();
228
+ REQUEST_TIMEOUT_HEADER = "X-Tensorlake-Request-Timeout-Ms";
205
229
  (0, import_undici.setGlobalDispatcher)(
206
230
  new import_undici.Agent({
207
231
  keepAliveTimeout: 6e4,
@@ -220,10 +244,13 @@ var init_http = __esm({
220
244
  this.baseUrl = url.endsWith("/") ? url.slice(0, -1) : url;
221
245
  this.maxRetries = options.maxRetries ?? MAX_RETRIES;
222
246
  this.retryBackoffMs = options.retryBackoffMs ?? RETRY_BACKOFF_MS;
223
- this.timeoutMs = options.timeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;
247
+ this.timeoutMs = options.timeoutMs === void 0 ? DEFAULT_HTTP_TIMEOUT_MS : options.timeoutMs;
224
248
  this.headers = {
225
249
  "User-Agent": `tensorlake-typescript-sdk/${SDK_VERSION}`
226
250
  };
251
+ if (this.timeoutMs != null) {
252
+ this.headers[REQUEST_TIMEOUT_HEADER] = String(this.timeoutMs);
253
+ }
227
254
  if (options.apiKey) {
228
255
  this.headers["Authorization"] = `Bearer ${options.apiKey}`;
229
256
  }
@@ -252,7 +279,10 @@ var init_http = __esm({
252
279
  const response = await this.requestResponse(method, path2, {
253
280
  json: options?.body,
254
281
  headers: options?.headers,
255
- signal: options?.signal
282
+ signal: options?.signal,
283
+ retryableStatusCodes: options?.retryableStatusCodes,
284
+ allowedErrorStatusCodes: options?.allowedErrorStatusCodes,
285
+ allowHttpErrors: options?.allowHttpErrors
256
286
  });
257
287
  const text = await response.text();
258
288
  const data = text ? JSON.parse(text) : void 0;
@@ -304,7 +334,9 @@ var init_http = __esm({
304
334
  body,
305
335
  headers,
306
336
  options?.signal,
307
- options?.allowHttpErrors ?? false
337
+ options?.allowHttpErrors ?? false,
338
+ options?.retryableStatusCodes ?? RETRYABLE_STATUS_CODES,
339
+ options?.allowedErrorStatusCodes ?? /* @__PURE__ */ new Set()
308
340
  );
309
341
  return withTraceId(response, traceId);
310
342
  }
@@ -314,7 +346,7 @@ var init_http = __esm({
314
346
  const spanId = randomHex(8);
315
347
  return { traceparent: `00-${traceId}-${spanId}-01`, traceId };
316
348
  }
317
- async doFetch(method, path2, body, headers, signal, allowHttpErrors = false) {
349
+ async doFetch(method, path2, body, headers, signal, allowHttpErrors = false, retryableStatusCodes = RETRYABLE_STATUS_CODES, allowedErrorStatusCodes = /* @__PURE__ */ new Set()) {
318
350
  const url = `${this.baseUrl}${path2}`;
319
351
  const { traceparent, traceId } = _HttpClient.makeTraceparent();
320
352
  headers["traceparent"] = traceparent;
@@ -325,10 +357,11 @@ var init_http = __esm({
325
357
  await sleep(delay);
326
358
  }
327
359
  this.abortController = new AbortController();
328
- const timeoutId = setTimeout(
329
- () => this.abortController?.abort(),
330
- this.timeoutMs
331
- );
360
+ let timedOut = false;
361
+ const timeoutId = this.timeoutMs == null ? null : setTimeout(() => {
362
+ timedOut = true;
363
+ this.abortController?.abort();
364
+ }, this.timeoutMs);
332
365
  const combinedSignal = signal ? anySignal([signal, this.abortController.signal]) : this.abortController.signal;
333
366
  try {
334
367
  const response = await (0, import_undici.fetch)(url, {
@@ -337,35 +370,41 @@ var init_http = __esm({
337
370
  body,
338
371
  signal: combinedSignal
339
372
  });
340
- clearTimeout(timeoutId);
373
+ if (timeoutId != null) clearTimeout(timeoutId);
341
374
  if (response.ok) return { response, traceId };
342
- if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < this.maxRetries) {
375
+ if (retryableStatusCodes.has(response.status) && attempt < this.maxRetries) {
343
376
  lastError = new RemoteAPIError(
344
377
  response.status,
345
378
  await response.text().catch(() => "")
346
379
  );
347
380
  continue;
348
381
  }
349
- if (allowHttpErrors) {
382
+ if (allowHttpErrors || allowedErrorStatusCodes.has(response.status)) {
350
383
  return { response, traceId };
351
384
  }
352
385
  const errorBody = await response.text().catch(() => "");
353
386
  throwMappedError(response.status, errorBody, path2);
354
387
  } catch (err) {
355
- clearTimeout(timeoutId);
388
+ if (timeoutId != null) clearTimeout(timeoutId);
356
389
  if (err instanceof RemoteAPIError || err instanceof SandboxNotFoundError || err instanceof PoolNotFoundError || err instanceof PoolInUseError) {
357
390
  throw err;
358
391
  }
359
- if (signal?.aborted) {
360
- throw new SandboxConnectionError("Request aborted");
392
+ if (signal?.aborted && !timedOut) {
393
+ throw new SandboxConnectionError("request aborted by caller", {
394
+ cause: err
395
+ });
361
396
  }
362
397
  lastError = err instanceof Error ? err : new Error(String(err));
363
398
  if (attempt >= this.maxRetries) {
364
- throw new SandboxConnectionError(lastError.message);
399
+ const detail = timedOut ? `request timed out after ${this.timeoutMs}ms` : describeError(err);
400
+ throw new SandboxConnectionError(detail, { cause: err });
365
401
  }
366
402
  }
367
403
  }
368
- throw new SandboxConnectionError(lastError?.message ?? "Request failed");
404
+ throw new SandboxConnectionError(
405
+ lastError ? describeError(lastError) : "request failed",
406
+ { cause: lastError }
407
+ );
369
408
  }
370
409
  };
371
410
  }
@@ -424,6 +463,7 @@ var init_models = __esm({
424
463
  SandboxStatus3["SUSPENDING"] = "suspending";
425
464
  SandboxStatus3["SUSPENDED"] = "suspended";
426
465
  SandboxStatus3["TERMINATED"] = "terminated";
466
+ SandboxStatus3["TIMEOUT"] = "timeout";
427
467
  return SandboxStatus3;
428
468
  })(SandboxStatus || {});
429
469
  SnapshotStatus = /* @__PURE__ */ ((SnapshotStatus2) => {
@@ -3035,6 +3075,12 @@ function sendPtyFrame(socket, frame) {
3035
3075
  socket.send(frame, (error) => error ? reject(error) : resolve());
3036
3076
  });
3037
3077
  }
3078
+ function secondsToMillis3(seconds) {
3079
+ if (!Number.isFinite(seconds) || seconds <= 0) {
3080
+ throw new SandboxError("requestTimeout must be a positive number of seconds");
3081
+ }
3082
+ return Math.ceil(seconds * 1e3);
3083
+ }
3038
3084
  var import_ws2, DEFAULT_PROCESS_USER, SandboxProxyConnection, PTY_OP_DATA, PTY_OP_RESIZE, PTY_OP_READY, PTY_OP_EXIT, Pty, Sandbox;
3039
3085
  var init_sandbox = __esm({
3040
3086
  "src/sandbox.ts"() {
@@ -3137,7 +3183,8 @@ var init_sandbox = __esm({
3137
3183
  projectId: this.options.projectId,
3138
3184
  hostHeader,
3139
3185
  sandboxIdHeader,
3140
- routingHint
3186
+ routingHint,
3187
+ timeoutMs: this.options.requestTimeout != null ? secondsToMillis3(this.options.requestTimeout) : this.options.timeoutMs ?? null
3141
3188
  });
3142
3189
  }
3143
3190
  };
@@ -3358,9 +3405,7 @@ var init_sandbox = __esm({
3358
3405
  /* _internal */
3359
3406
  true
3360
3407
  );
3361
- const sandbox = await client.createAndConnect(options);
3362
- sandbox.lifecycleClient = client;
3363
- return sandbox;
3408
+ return client.createAndConnect(options);
3364
3409
  }
3365
3410
  /**
3366
3411
  * Attach to an existing sandbox and return a connected handle.
@@ -3379,7 +3424,8 @@ var init_sandbox = __esm({
3379
3424
  const sandbox = client.connect(
3380
3425
  options.sandboxId,
3381
3426
  options.proxyUrl,
3382
- options.routingHint
3427
+ options.routingHint,
3428
+ options.requestTimeout
3383
3429
  );
3384
3430
  sandbox.lifecycleClient = client;
3385
3431
  return sandbox;
@@ -3488,6 +3534,11 @@ var init_sandbox = __esm({
3488
3534
  const client = this.requireLifecycleClient("resume");
3489
3535
  await client.resume(this.lifecycleIdentifier, options);
3490
3536
  }
3537
+ /** Live-copy this running sandbox. */
3538
+ async copy(options) {
3539
+ const client = this.requireLifecycleClient("copy");
3540
+ return client.copy(this.lifecycleIdentifier, options);
3541
+ }
3491
3542
  /**
3492
3543
  * Create a checkpoint of this sandbox and wait for it to be locally ready.
3493
3544
  *
@@ -3904,6 +3955,27 @@ var client_exports = {};
3904
3955
  __export(client_exports, {
3905
3956
  SandboxClient: () => SandboxClient
3906
3957
  });
3958
+ function resolveRequestTimeoutMs(options) {
3959
+ if (options?.requestTimeout != null) {
3960
+ return secondsToMillis4(options.requestTimeout);
3961
+ }
3962
+ if (options?.timeoutMs != null) {
3963
+ validateTimeoutMs(options.timeoutMs);
3964
+ return options.timeoutMs;
3965
+ }
3966
+ return DEFAULT_HTTP_TIMEOUT_MS;
3967
+ }
3968
+ function secondsToMillis4(seconds) {
3969
+ if (!Number.isFinite(seconds) || seconds <= 0) {
3970
+ throw new SandboxError("requestTimeout must be a positive number of seconds");
3971
+ }
3972
+ return Math.ceil(seconds * 1e3);
3973
+ }
3974
+ function validateTimeoutMs(timeoutMs) {
3975
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
3976
+ throw new SandboxError("timeoutMs must be a positive number of milliseconds");
3977
+ }
3978
+ }
3907
3979
  function sleep2(ms) {
3908
3980
  return new Promise((resolve) => setTimeout(resolve, ms));
3909
3981
  }
@@ -3960,7 +4032,7 @@ function validateUserPort(port) {
3960
4032
  function dedupeAndSortPorts(ports) {
3961
4033
  return [...new Set(ports)].sort((a, b) => a - b);
3962
4034
  }
3963
- var SandboxClient, RESERVED_SANDBOX_MANAGEMENT_PORT;
4035
+ var CREATE_SANDBOX_RETRYABLE_STATUS_CODES, CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES, COPY_SANDBOX_ALLOWED_ERROR_STATUS_CODES, SandboxClient, RESERVED_SANDBOX_MANAGEMENT_PORT;
3964
4036
  var init_client = __esm({
3965
4037
  "src/client.ts"() {
3966
4038
  "use strict";
@@ -3970,6 +4042,9 @@ var init_client = __esm({
3970
4042
  init_models();
3971
4043
  init_sandbox();
3972
4044
  init_url();
4045
+ CREATE_SANDBOX_RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([502, 503]);
4046
+ CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES = /* @__PURE__ */ new Set([504]);
4047
+ COPY_SANDBOX_ALLOWED_ERROR_STATUS_CODES = /* @__PURE__ */ new Set([422, 504]);
3973
4048
  SandboxClient = class _SandboxClient {
3974
4049
  http;
3975
4050
  apiUrl;
@@ -3978,6 +4053,9 @@ var init_client = __esm({
3978
4053
  projectId;
3979
4054
  namespace;
3980
4055
  local;
4056
+ maxRetries;
4057
+ retryBackoffMs;
4058
+ requestTimeoutMs;
3981
4059
  /** @internal Pass `true` to suppress the deprecation warning when used by `Sandbox.create()` / `Sandbox.connect()`. */
3982
4060
  constructor(options, _internal = false) {
3983
4061
  if (!_internal) {
@@ -3991,13 +4069,17 @@ var init_client = __esm({
3991
4069
  this.projectId = options?.projectId;
3992
4070
  this.namespace = options?.namespace ?? NAMESPACE;
3993
4071
  this.local = isLocalhost(this.apiUrl);
4072
+ this.maxRetries = options?.maxRetries ?? MAX_RETRIES;
4073
+ this.retryBackoffMs = options?.retryBackoffMs ?? RETRY_BACKOFF_MS;
4074
+ this.requestTimeoutMs = resolveRequestTimeoutMs(options);
3994
4075
  this.http = new HttpClient({
3995
4076
  baseUrl: resolveSandboxLifecycleUrl(this.apiUrl),
3996
4077
  apiKey: this.apiKey,
3997
4078
  organizationId: this.organizationId,
3998
4079
  projectId: this.projectId,
3999
- maxRetries: options?.maxRetries ?? MAX_RETRIES,
4000
- retryBackoffMs: options?.retryBackoffMs ?? RETRY_BACKOFF_MS
4080
+ maxRetries: this.maxRetries,
4081
+ retryBackoffMs: this.retryBackoffMs,
4082
+ timeoutMs: this.requestTimeoutMs
4001
4083
  });
4002
4084
  }
4003
4085
  /** Create a client for the TensorLake cloud platform. */
@@ -4006,19 +4088,46 @@ var init_client = __esm({
4006
4088
  apiUrl: options?.apiUrl ?? "https://api.tensorlake.ai",
4007
4089
  apiKey: options?.apiKey,
4008
4090
  organizationId: options?.organizationId,
4009
- projectId: options?.projectId
4091
+ projectId: options?.projectId,
4092
+ requestTimeout: options?.requestTimeout,
4093
+ timeoutMs: options?.timeoutMs
4010
4094
  });
4011
4095
  }
4012
4096
  /** Create a client for a local Indexify server. */
4013
4097
  static forLocalhost(options) {
4014
4098
  return new _SandboxClient({
4015
4099
  apiUrl: options?.apiUrl ?? "http://localhost:8900",
4016
- namespace: options?.namespace ?? "default"
4100
+ namespace: options?.namespace ?? "default",
4101
+ requestTimeout: options?.requestTimeout,
4102
+ timeoutMs: options?.timeoutMs
4017
4103
  });
4018
4104
  }
4019
4105
  close() {
4020
4106
  this.http.close();
4021
4107
  }
4108
+ withRequestTimeout(requestTimeout) {
4109
+ if (requestTimeout == null) {
4110
+ return this;
4111
+ }
4112
+ const timeoutMs = secondsToMillis4(requestTimeout);
4113
+ if (timeoutMs === this.requestTimeoutMs) {
4114
+ return this;
4115
+ }
4116
+ return new _SandboxClient(
4117
+ {
4118
+ apiUrl: this.apiUrl,
4119
+ apiKey: this.apiKey,
4120
+ organizationId: this.organizationId,
4121
+ projectId: this.projectId,
4122
+ namespace: this.namespace,
4123
+ maxRetries: this.maxRetries,
4124
+ retryBackoffMs: this.retryBackoffMs,
4125
+ timeoutMs
4126
+ },
4127
+ /* _internal */
4128
+ true
4129
+ );
4130
+ }
4022
4131
  // --- Path helper ---
4023
4132
  path(subpath) {
4024
4133
  return lifecyclePath(subpath, this.local, this.namespace);
@@ -4034,7 +4143,6 @@ var init_client = __esm({
4034
4143
  }
4035
4144
  };
4036
4145
  if (options?.image != null) body.image = options.image;
4037
- if (options?.secretNames != null) body.secret_names = options.secretNames;
4038
4146
  if (options?.timeoutSecs != null) body.timeout_secs = options.timeoutSecs;
4039
4147
  if (options?.entrypoint != null) body.entrypoint = options.entrypoint;
4040
4148
  if (options?.snapshotId != null) body.snapshot_id = options.snapshotId;
@@ -4049,7 +4157,11 @@ var init_client = __esm({
4049
4157
  const raw = await this.http.requestJson(
4050
4158
  "POST",
4051
4159
  this.path("sandboxes"),
4052
- { body }
4160
+ {
4161
+ body,
4162
+ retryableStatusCodes: CREATE_SANDBOX_RETRYABLE_STATUS_CODES,
4163
+ allowedErrorStatusCodes: CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES
4164
+ }
4053
4165
  );
4054
4166
  const result = fromSnakeKeys(raw, "sandboxId");
4055
4167
  return Object.assign(result, { traceId: raw.traceId });
@@ -4243,7 +4355,32 @@ var init_client = __esm({
4243
4355
  async claim(poolId) {
4244
4356
  const raw = await this.http.requestJson(
4245
4357
  "POST",
4246
- this.path(`sandbox-pools/${poolId}/sandboxes`)
4358
+ this.path(`sandbox-pools/${poolId}/sandboxes`),
4359
+ {
4360
+ retryableStatusCodes: CREATE_SANDBOX_RETRYABLE_STATUS_CODES,
4361
+ allowedErrorStatusCodes: CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES
4362
+ }
4363
+ );
4364
+ const result = fromSnakeKeys(raw, "sandboxId");
4365
+ return Object.assign(result, { traceId: raw.traceId });
4366
+ }
4367
+ /**
4368
+ * Live-copy a running sandbox.
4369
+ *
4370
+ * The server creates `times` running copies from the source sandbox. Partial
4371
+ * responses can include failed copies; inspect each returned sandbox's
4372
+ * `status` and `reason`.
4373
+ */
4374
+ async copy(sandboxId, options) {
4375
+ const times = options?.times ?? 1;
4376
+ if (!Number.isInteger(times) || times < 1) {
4377
+ throw new SandboxError("times must be a positive integer");
4378
+ }
4379
+ const client = this.withRequestTimeout(options?.requestTimeout);
4380
+ const raw = await client.http.requestJson(
4381
+ "POST",
4382
+ client.path(`sandbox/${sandboxId}/copy?times=${encodeURIComponent(String(times))}`),
4383
+ { allowedErrorStatusCodes: COPY_SANDBOX_ALLOWED_ERROR_STATUS_CODES }
4247
4384
  );
4248
4385
  const result = fromSnakeKeys(raw, "sandboxId");
4249
4386
  return Object.assign(result, { traceId: raw.traceId });
@@ -4344,7 +4481,6 @@ var init_client = __esm({
4344
4481
  },
4345
4482
  timeout_secs: options.timeoutSecs ?? 0
4346
4483
  };
4347
- if (options.secretNames != null) body.secret_names = options.secretNames;
4348
4484
  if (options.entrypoint != null) body.entrypoint = options.entrypoint;
4349
4485
  if (options.maxContainers != null) body.max_containers = options.maxContainers;
4350
4486
  if (options.warmContainers != null) body.warm_containers = options.warmContainers;
@@ -4385,7 +4521,6 @@ var init_client = __esm({
4385
4521
  },
4386
4522
  timeout_secs: options.timeoutSecs ?? 0
4387
4523
  };
4388
- if (options.secretNames != null) body.secret_names = options.secretNames;
4389
4524
  if (options.entrypoint != null) body.entrypoint = options.entrypoint;
4390
4525
  if (options.maxContainers != null) body.max_containers = options.maxContainers;
4391
4526
  if (options.warmContainers != null) body.warm_containers = options.warmContainers;
@@ -4405,7 +4540,7 @@ var init_client = __esm({
4405
4540
  }
4406
4541
  // --- Connect ---
4407
4542
  /** Return a `Sandbox` handle for an existing running sandbox without verifying it exists. */
4408
- connect(identifier, proxyUrl, routingHint) {
4543
+ connect(identifier, proxyUrl, routingHint, requestTimeout) {
4409
4544
  const resolvedProxy = proxyUrl ?? resolveProxyUrl(this.apiUrl);
4410
4545
  return new Sandbox({
4411
4546
  sandboxId: identifier,
@@ -4414,29 +4549,33 @@ var init_client = __esm({
4414
4549
  organizationId: this.organizationId,
4415
4550
  projectId: this.projectId,
4416
4551
  routingHint,
4417
- resolveProxyInfo: proxyUrl == null ? async (currentIdentifier) => this.get(currentIdentifier) : void 0
4552
+ resolveProxyInfo: proxyUrl == null ? async (currentIdentifier) => this.get(currentIdentifier) : void 0,
4553
+ requestTimeout
4418
4554
  });
4419
4555
  }
4420
4556
  /**
4421
4557
  * Create a sandbox, wait for it to reach `Running`, and return a connected handle.
4422
4558
  *
4423
- * Blocks until the sandbox is ready or `startupTimeout` elapses. The returned
4559
+ * Blocks until the sandbox is ready or `requestTimeout` elapses. The returned
4424
4560
  * `Sandbox` auto-terminates when `terminate()` is called.
4425
4561
  *
4426
- * @param options.startupTimeout - Max seconds to wait for `Running` status (default 60).
4562
+ * @param options.requestTimeout - Max seconds to wait for `Running` status (default: client requestTimeout).
4563
+ * @param options.startupTimeout - Deprecated alias for `requestTimeout`.
4427
4564
  * @throws {SandboxError} If the sandbox terminates during startup or the timeout elapses.
4428
4565
  */
4429
4566
  async createAndConnect(options) {
4430
- const startupTimeout = options?.startupTimeout ?? 60;
4431
- const result = options?.poolId != null ? await this.claim(options.poolId) : await this.create(options);
4567
+ const requestTimeout = options?.requestTimeout ?? options?.startupTimeout ?? this.requestTimeoutMs / 1e3;
4568
+ const requestClient = this.withRequestTimeout(requestTimeout);
4569
+ const result = options?.poolId != null ? await requestClient.claim(options.poolId) : await requestClient.create(options);
4432
4570
  const requestedName = options?.poolId != null ? null : options?.name ?? null;
4433
4571
  const finishConnect = (routingHint, name, ingressEndpoint) => {
4434
- const sandbox = this.connect(
4572
+ const sandbox = requestClient.connect(
4435
4573
  result.sandboxId,
4436
4574
  options?.proxyUrl ?? ingressEndpoint,
4437
- routingHint
4575
+ routingHint,
4576
+ requestTimeout
4438
4577
  );
4439
- sandbox._setOwner(this);
4578
+ sandbox._setOwner(requestClient);
4440
4579
  sandbox.traceId = result.traceId;
4441
4580
  sandbox._setLifecycleIdentifier(result.sandboxId);
4442
4581
  sandbox._setName(name ?? requestedName);
@@ -4453,9 +4592,18 @@ var init_client = __esm({
4453
4592
  })
4454
4593
  );
4455
4594
  }
4456
- const deadline = Date.now() + startupTimeout * 1e3;
4595
+ if (result.status === "timeout" /* TIMEOUT */) {
4596
+ try {
4597
+ await requestClient.delete(result.sandboxId);
4598
+ } catch {
4599
+ }
4600
+ throw new SandboxError(
4601
+ `Sandbox ${result.sandboxId} did not start within ${requestTimeout}s`
4602
+ );
4603
+ }
4604
+ const deadline = Date.now() + secondsToMillis4(requestTimeout);
4457
4605
  while (Date.now() < deadline) {
4458
- const info = await this.get(result.sandboxId);
4606
+ const info = await requestClient.get(result.sandboxId);
4459
4607
  if (info.status === "running" /* RUNNING */) {
4460
4608
  return finishConnect(info.routingHint, info.name, info.ingressEndpoint);
4461
4609
  }
@@ -4470,11 +4618,11 @@ var init_client = __esm({
4470
4618
  await sleep2(500);
4471
4619
  }
4472
4620
  try {
4473
- await this.delete(result.sandboxId);
4621
+ await requestClient.delete(result.sandboxId);
4474
4622
  } catch {
4475
4623
  }
4476
4624
  throw new SandboxError(
4477
- `Sandbox ${result.sandboxId} did not start within ${startupTimeout}s`
4625
+ `Sandbox ${result.sandboxId} did not start within ${requestTimeout}s`
4478
4626
  );
4479
4627
  }
4480
4628
  };