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.
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.33";
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";
@@ -32,6 +32,28 @@ var init_defaults = __esm({
32
32
  });
33
33
 
34
34
  // src/errors.ts
35
+ function describeError(err) {
36
+ const parts = [];
37
+ const seen = /* @__PURE__ */ new Set();
38
+ let current = err;
39
+ for (let depth = 0; depth < 6; depth++) {
40
+ if (current == null || typeof current !== "object" || seen.has(current)) {
41
+ break;
42
+ }
43
+ seen.add(current);
44
+ const e = current;
45
+ const message = typeof e.message === "string" ? e.message.trim() : "";
46
+ const code = typeof e.code === "string" ? e.code : void 0;
47
+ let segment = message;
48
+ if (code && (!message || !message.includes(code))) {
49
+ segment = segment ? `${segment} (${code})` : code;
50
+ }
51
+ if (segment && !parts.includes(segment)) parts.push(segment);
52
+ current = e.cause;
53
+ }
54
+ if (parts.length > 0) return parts.join(": ");
55
+ return err instanceof Error ? err.message : String(err);
56
+ }
35
57
  var SandboxException, SandboxError, SandboxConnectionError, SandboxNotFoundError, PoolNotFoundError, PoolInUseError, RemoteAPIError, RequestNotFinishedError, RequestFailedError, RequestExecutionError;
36
58
  var init_errors = __esm({
37
59
  "src/errors.ts"() {
@@ -49,9 +71,10 @@ var init_errors = __esm({
49
71
  }
50
72
  };
51
73
  SandboxConnectionError = class extends SandboxError {
52
- constructor(message) {
74
+ constructor(message, options) {
53
75
  super(`Connection error: ${message}`);
54
76
  this.name = "SandboxConnectionError";
77
+ if (options?.cause !== void 0) this.cause = options.cause;
55
78
  }
56
79
  };
57
80
  SandboxNotFoundError = class extends SandboxError {
@@ -184,12 +207,13 @@ function anySignal(signals) {
184
207
  function sleep(ms) {
185
208
  return new Promise((resolve) => setTimeout(resolve, ms));
186
209
  }
187
- var HttpClient;
210
+ var REQUEST_TIMEOUT_HEADER, HttpClient;
188
211
  var init_http = __esm({
189
212
  "src/http.ts"() {
190
213
  "use strict";
191
214
  init_defaults();
192
215
  init_errors();
216
+ REQUEST_TIMEOUT_HEADER = "X-Tensorlake-Request-Timeout-Ms";
193
217
  setGlobalDispatcher(
194
218
  new Agent({
195
219
  keepAliveTimeout: 6e4,
@@ -208,10 +232,13 @@ var init_http = __esm({
208
232
  this.baseUrl = url.endsWith("/") ? url.slice(0, -1) : url;
209
233
  this.maxRetries = options.maxRetries ?? MAX_RETRIES;
210
234
  this.retryBackoffMs = options.retryBackoffMs ?? RETRY_BACKOFF_MS;
211
- this.timeoutMs = options.timeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;
235
+ this.timeoutMs = options.timeoutMs === void 0 ? DEFAULT_HTTP_TIMEOUT_MS : options.timeoutMs;
212
236
  this.headers = {
213
237
  "User-Agent": `tensorlake-typescript-sdk/${SDK_VERSION}`
214
238
  };
239
+ if (this.timeoutMs != null) {
240
+ this.headers[REQUEST_TIMEOUT_HEADER] = String(this.timeoutMs);
241
+ }
215
242
  if (options.apiKey) {
216
243
  this.headers["Authorization"] = `Bearer ${options.apiKey}`;
217
244
  }
@@ -240,7 +267,10 @@ var init_http = __esm({
240
267
  const response = await this.requestResponse(method, path2, {
241
268
  json: options?.body,
242
269
  headers: options?.headers,
243
- signal: options?.signal
270
+ signal: options?.signal,
271
+ retryableStatusCodes: options?.retryableStatusCodes,
272
+ allowedErrorStatusCodes: options?.allowedErrorStatusCodes,
273
+ allowHttpErrors: options?.allowHttpErrors
244
274
  });
245
275
  const text = await response.text();
246
276
  const data = text ? JSON.parse(text) : void 0;
@@ -292,7 +322,9 @@ var init_http = __esm({
292
322
  body,
293
323
  headers,
294
324
  options?.signal,
295
- options?.allowHttpErrors ?? false
325
+ options?.allowHttpErrors ?? false,
326
+ options?.retryableStatusCodes ?? RETRYABLE_STATUS_CODES,
327
+ options?.allowedErrorStatusCodes ?? /* @__PURE__ */ new Set()
296
328
  );
297
329
  return withTraceId(response, traceId);
298
330
  }
@@ -302,7 +334,7 @@ var init_http = __esm({
302
334
  const spanId = randomHex(8);
303
335
  return { traceparent: `00-${traceId}-${spanId}-01`, traceId };
304
336
  }
305
- async doFetch(method, path2, body, headers, signal, allowHttpErrors = false) {
337
+ async doFetch(method, path2, body, headers, signal, allowHttpErrors = false, retryableStatusCodes = RETRYABLE_STATUS_CODES, allowedErrorStatusCodes = /* @__PURE__ */ new Set()) {
306
338
  const url = `${this.baseUrl}${path2}`;
307
339
  const { traceparent, traceId } = _HttpClient.makeTraceparent();
308
340
  headers["traceparent"] = traceparent;
@@ -313,10 +345,11 @@ var init_http = __esm({
313
345
  await sleep(delay);
314
346
  }
315
347
  this.abortController = new AbortController();
316
- const timeoutId = setTimeout(
317
- () => this.abortController?.abort(),
318
- this.timeoutMs
319
- );
348
+ let timedOut = false;
349
+ const timeoutId = this.timeoutMs == null ? null : setTimeout(() => {
350
+ timedOut = true;
351
+ this.abortController?.abort();
352
+ }, this.timeoutMs);
320
353
  const combinedSignal = signal ? anySignal([signal, this.abortController.signal]) : this.abortController.signal;
321
354
  try {
322
355
  const response = await undiciFetch(url, {
@@ -325,35 +358,41 @@ var init_http = __esm({
325
358
  body,
326
359
  signal: combinedSignal
327
360
  });
328
- clearTimeout(timeoutId);
361
+ if (timeoutId != null) clearTimeout(timeoutId);
329
362
  if (response.ok) return { response, traceId };
330
- if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < this.maxRetries) {
363
+ if (retryableStatusCodes.has(response.status) && attempt < this.maxRetries) {
331
364
  lastError = new RemoteAPIError(
332
365
  response.status,
333
366
  await response.text().catch(() => "")
334
367
  );
335
368
  continue;
336
369
  }
337
- if (allowHttpErrors) {
370
+ if (allowHttpErrors || allowedErrorStatusCodes.has(response.status)) {
338
371
  return { response, traceId };
339
372
  }
340
373
  const errorBody = await response.text().catch(() => "");
341
374
  throwMappedError(response.status, errorBody, path2);
342
375
  } catch (err) {
343
- clearTimeout(timeoutId);
376
+ if (timeoutId != null) clearTimeout(timeoutId);
344
377
  if (err instanceof RemoteAPIError || err instanceof SandboxNotFoundError || err instanceof PoolNotFoundError || err instanceof PoolInUseError) {
345
378
  throw err;
346
379
  }
347
- if (signal?.aborted) {
348
- throw new SandboxConnectionError("Request aborted");
380
+ if (signal?.aborted && !timedOut) {
381
+ throw new SandboxConnectionError("request aborted by caller", {
382
+ cause: err
383
+ });
349
384
  }
350
385
  lastError = err instanceof Error ? err : new Error(String(err));
351
386
  if (attempt >= this.maxRetries) {
352
- throw new SandboxConnectionError(lastError.message);
387
+ const detail = timedOut ? `request timed out after ${this.timeoutMs}ms` : describeError(err);
388
+ throw new SandboxConnectionError(detail, { cause: err });
353
389
  }
354
390
  }
355
391
  }
356
- throw new SandboxConnectionError(lastError?.message ?? "Request failed");
392
+ throw new SandboxConnectionError(
393
+ lastError ? describeError(lastError) : "request failed",
394
+ { cause: lastError }
395
+ );
357
396
  }
358
397
  };
359
398
  }
@@ -412,6 +451,7 @@ var init_models = __esm({
412
451
  SandboxStatus3["SUSPENDING"] = "suspending";
413
452
  SandboxStatus3["SUSPENDED"] = "suspended";
414
453
  SandboxStatus3["TERMINATED"] = "terminated";
454
+ SandboxStatus3["TIMEOUT"] = "timeout";
415
455
  return SandboxStatus3;
416
456
  })(SandboxStatus || {});
417
457
  SnapshotStatus = /* @__PURE__ */ ((SnapshotStatus2) => {
@@ -3024,6 +3064,12 @@ function sendPtyFrame(socket, frame) {
3024
3064
  socket.send(frame, (error) => error ? reject(error) : resolve());
3025
3065
  });
3026
3066
  }
3067
+ function secondsToMillis3(seconds) {
3068
+ if (!Number.isFinite(seconds) || seconds <= 0) {
3069
+ throw new SandboxError("requestTimeout must be a positive number of seconds");
3070
+ }
3071
+ return Math.ceil(seconds * 1e3);
3072
+ }
3027
3073
  var DEFAULT_PROCESS_USER, SandboxProxyConnection, PTY_OP_DATA, PTY_OP_RESIZE, PTY_OP_READY, PTY_OP_EXIT, Pty, Sandbox;
3028
3074
  var init_sandbox = __esm({
3029
3075
  "src/sandbox.ts"() {
@@ -3125,7 +3171,8 @@ var init_sandbox = __esm({
3125
3171
  projectId: this.options.projectId,
3126
3172
  hostHeader,
3127
3173
  sandboxIdHeader,
3128
- routingHint
3174
+ routingHint,
3175
+ timeoutMs: this.options.requestTimeout != null ? secondsToMillis3(this.options.requestTimeout) : this.options.timeoutMs ?? null
3129
3176
  });
3130
3177
  }
3131
3178
  };
@@ -3346,9 +3393,7 @@ var init_sandbox = __esm({
3346
3393
  /* _internal */
3347
3394
  true
3348
3395
  );
3349
- const sandbox = await client.createAndConnect(options);
3350
- sandbox.lifecycleClient = client;
3351
- return sandbox;
3396
+ return client.createAndConnect(options);
3352
3397
  }
3353
3398
  /**
3354
3399
  * Attach to an existing sandbox and return a connected handle.
@@ -3367,7 +3412,8 @@ var init_sandbox = __esm({
3367
3412
  const sandbox = client.connect(
3368
3413
  options.sandboxId,
3369
3414
  options.proxyUrl,
3370
- options.routingHint
3415
+ options.routingHint,
3416
+ options.requestTimeout
3371
3417
  );
3372
3418
  sandbox.lifecycleClient = client;
3373
3419
  return sandbox;
@@ -3476,6 +3522,11 @@ var init_sandbox = __esm({
3476
3522
  const client = this.requireLifecycleClient("resume");
3477
3523
  await client.resume(this.lifecycleIdentifier, options);
3478
3524
  }
3525
+ /** Live-copy this running sandbox. */
3526
+ async copy(options) {
3527
+ const client = this.requireLifecycleClient("copy");
3528
+ return client.copy(this.lifecycleIdentifier, options);
3529
+ }
3479
3530
  /**
3480
3531
  * Create a checkpoint of this sandbox and wait for it to be locally ready.
3481
3532
  *
@@ -3892,6 +3943,27 @@ var client_exports = {};
3892
3943
  __export(client_exports, {
3893
3944
  SandboxClient: () => SandboxClient
3894
3945
  });
3946
+ function resolveRequestTimeoutMs(options) {
3947
+ if (options?.requestTimeout != null) {
3948
+ return secondsToMillis4(options.requestTimeout);
3949
+ }
3950
+ if (options?.timeoutMs != null) {
3951
+ validateTimeoutMs(options.timeoutMs);
3952
+ return options.timeoutMs;
3953
+ }
3954
+ return DEFAULT_HTTP_TIMEOUT_MS;
3955
+ }
3956
+ function secondsToMillis4(seconds) {
3957
+ if (!Number.isFinite(seconds) || seconds <= 0) {
3958
+ throw new SandboxError("requestTimeout must be a positive number of seconds");
3959
+ }
3960
+ return Math.ceil(seconds * 1e3);
3961
+ }
3962
+ function validateTimeoutMs(timeoutMs) {
3963
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
3964
+ throw new SandboxError("timeoutMs must be a positive number of milliseconds");
3965
+ }
3966
+ }
3895
3967
  function sleep2(ms) {
3896
3968
  return new Promise((resolve) => setTimeout(resolve, ms));
3897
3969
  }
@@ -3948,7 +4020,7 @@ function validateUserPort(port) {
3948
4020
  function dedupeAndSortPorts(ports) {
3949
4021
  return [...new Set(ports)].sort((a, b) => a - b);
3950
4022
  }
3951
- var SandboxClient, RESERVED_SANDBOX_MANAGEMENT_PORT;
4023
+ var CREATE_SANDBOX_RETRYABLE_STATUS_CODES, CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES, COPY_SANDBOX_ALLOWED_ERROR_STATUS_CODES, SandboxClient, RESERVED_SANDBOX_MANAGEMENT_PORT;
3952
4024
  var init_client = __esm({
3953
4025
  "src/client.ts"() {
3954
4026
  "use strict";
@@ -3958,6 +4030,9 @@ var init_client = __esm({
3958
4030
  init_models();
3959
4031
  init_sandbox();
3960
4032
  init_url();
4033
+ CREATE_SANDBOX_RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([502, 503]);
4034
+ CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES = /* @__PURE__ */ new Set([504]);
4035
+ COPY_SANDBOX_ALLOWED_ERROR_STATUS_CODES = /* @__PURE__ */ new Set([422, 504]);
3961
4036
  SandboxClient = class _SandboxClient {
3962
4037
  http;
3963
4038
  apiUrl;
@@ -3966,6 +4041,9 @@ var init_client = __esm({
3966
4041
  projectId;
3967
4042
  namespace;
3968
4043
  local;
4044
+ maxRetries;
4045
+ retryBackoffMs;
4046
+ requestTimeoutMs;
3969
4047
  /** @internal Pass `true` to suppress the deprecation warning when used by `Sandbox.create()` / `Sandbox.connect()`. */
3970
4048
  constructor(options, _internal = false) {
3971
4049
  if (!_internal) {
@@ -3979,13 +4057,17 @@ var init_client = __esm({
3979
4057
  this.projectId = options?.projectId;
3980
4058
  this.namespace = options?.namespace ?? NAMESPACE;
3981
4059
  this.local = isLocalhost(this.apiUrl);
4060
+ this.maxRetries = options?.maxRetries ?? MAX_RETRIES;
4061
+ this.retryBackoffMs = options?.retryBackoffMs ?? RETRY_BACKOFF_MS;
4062
+ this.requestTimeoutMs = resolveRequestTimeoutMs(options);
3982
4063
  this.http = new HttpClient({
3983
4064
  baseUrl: resolveSandboxLifecycleUrl(this.apiUrl),
3984
4065
  apiKey: this.apiKey,
3985
4066
  organizationId: this.organizationId,
3986
4067
  projectId: this.projectId,
3987
- maxRetries: options?.maxRetries ?? MAX_RETRIES,
3988
- retryBackoffMs: options?.retryBackoffMs ?? RETRY_BACKOFF_MS
4068
+ maxRetries: this.maxRetries,
4069
+ retryBackoffMs: this.retryBackoffMs,
4070
+ timeoutMs: this.requestTimeoutMs
3989
4071
  });
3990
4072
  }
3991
4073
  /** Create a client for the TensorLake cloud platform. */
@@ -3994,19 +4076,46 @@ var init_client = __esm({
3994
4076
  apiUrl: options?.apiUrl ?? "https://api.tensorlake.ai",
3995
4077
  apiKey: options?.apiKey,
3996
4078
  organizationId: options?.organizationId,
3997
- projectId: options?.projectId
4079
+ projectId: options?.projectId,
4080
+ requestTimeout: options?.requestTimeout,
4081
+ timeoutMs: options?.timeoutMs
3998
4082
  });
3999
4083
  }
4000
4084
  /** Create a client for a local Indexify server. */
4001
4085
  static forLocalhost(options) {
4002
4086
  return new _SandboxClient({
4003
4087
  apiUrl: options?.apiUrl ?? "http://localhost:8900",
4004
- namespace: options?.namespace ?? "default"
4088
+ namespace: options?.namespace ?? "default",
4089
+ requestTimeout: options?.requestTimeout,
4090
+ timeoutMs: options?.timeoutMs
4005
4091
  });
4006
4092
  }
4007
4093
  close() {
4008
4094
  this.http.close();
4009
4095
  }
4096
+ withRequestTimeout(requestTimeout) {
4097
+ if (requestTimeout == null) {
4098
+ return this;
4099
+ }
4100
+ const timeoutMs = secondsToMillis4(requestTimeout);
4101
+ if (timeoutMs === this.requestTimeoutMs) {
4102
+ return this;
4103
+ }
4104
+ return new _SandboxClient(
4105
+ {
4106
+ apiUrl: this.apiUrl,
4107
+ apiKey: this.apiKey,
4108
+ organizationId: this.organizationId,
4109
+ projectId: this.projectId,
4110
+ namespace: this.namespace,
4111
+ maxRetries: this.maxRetries,
4112
+ retryBackoffMs: this.retryBackoffMs,
4113
+ timeoutMs
4114
+ },
4115
+ /* _internal */
4116
+ true
4117
+ );
4118
+ }
4010
4119
  // --- Path helper ---
4011
4120
  path(subpath) {
4012
4121
  return lifecyclePath(subpath, this.local, this.namespace);
@@ -4022,7 +4131,6 @@ var init_client = __esm({
4022
4131
  }
4023
4132
  };
4024
4133
  if (options?.image != null) body.image = options.image;
4025
- if (options?.secretNames != null) body.secret_names = options.secretNames;
4026
4134
  if (options?.timeoutSecs != null) body.timeout_secs = options.timeoutSecs;
4027
4135
  if (options?.entrypoint != null) body.entrypoint = options.entrypoint;
4028
4136
  if (options?.snapshotId != null) body.snapshot_id = options.snapshotId;
@@ -4037,7 +4145,11 @@ var init_client = __esm({
4037
4145
  const raw = await this.http.requestJson(
4038
4146
  "POST",
4039
4147
  this.path("sandboxes"),
4040
- { body }
4148
+ {
4149
+ body,
4150
+ retryableStatusCodes: CREATE_SANDBOX_RETRYABLE_STATUS_CODES,
4151
+ allowedErrorStatusCodes: CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES
4152
+ }
4041
4153
  );
4042
4154
  const result = fromSnakeKeys(raw, "sandboxId");
4043
4155
  return Object.assign(result, { traceId: raw.traceId });
@@ -4231,7 +4343,32 @@ var init_client = __esm({
4231
4343
  async claim(poolId) {
4232
4344
  const raw = await this.http.requestJson(
4233
4345
  "POST",
4234
- this.path(`sandbox-pools/${poolId}/sandboxes`)
4346
+ this.path(`sandbox-pools/${poolId}/sandboxes`),
4347
+ {
4348
+ retryableStatusCodes: CREATE_SANDBOX_RETRYABLE_STATUS_CODES,
4349
+ allowedErrorStatusCodes: CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES
4350
+ }
4351
+ );
4352
+ const result = fromSnakeKeys(raw, "sandboxId");
4353
+ return Object.assign(result, { traceId: raw.traceId });
4354
+ }
4355
+ /**
4356
+ * Live-copy a running sandbox.
4357
+ *
4358
+ * The server creates `times` running copies from the source sandbox. Partial
4359
+ * responses can include failed copies; inspect each returned sandbox's
4360
+ * `status` and `reason`.
4361
+ */
4362
+ async copy(sandboxId, options) {
4363
+ const times = options?.times ?? 1;
4364
+ if (!Number.isInteger(times) || times < 1) {
4365
+ throw new SandboxError("times must be a positive integer");
4366
+ }
4367
+ const client = this.withRequestTimeout(options?.requestTimeout);
4368
+ const raw = await client.http.requestJson(
4369
+ "POST",
4370
+ client.path(`sandbox/${sandboxId}/copy?times=${encodeURIComponent(String(times))}`),
4371
+ { allowedErrorStatusCodes: COPY_SANDBOX_ALLOWED_ERROR_STATUS_CODES }
4235
4372
  );
4236
4373
  const result = fromSnakeKeys(raw, "sandboxId");
4237
4374
  return Object.assign(result, { traceId: raw.traceId });
@@ -4332,7 +4469,6 @@ var init_client = __esm({
4332
4469
  },
4333
4470
  timeout_secs: options.timeoutSecs ?? 0
4334
4471
  };
4335
- if (options.secretNames != null) body.secret_names = options.secretNames;
4336
4472
  if (options.entrypoint != null) body.entrypoint = options.entrypoint;
4337
4473
  if (options.maxContainers != null) body.max_containers = options.maxContainers;
4338
4474
  if (options.warmContainers != null) body.warm_containers = options.warmContainers;
@@ -4373,7 +4509,6 @@ var init_client = __esm({
4373
4509
  },
4374
4510
  timeout_secs: options.timeoutSecs ?? 0
4375
4511
  };
4376
- if (options.secretNames != null) body.secret_names = options.secretNames;
4377
4512
  if (options.entrypoint != null) body.entrypoint = options.entrypoint;
4378
4513
  if (options.maxContainers != null) body.max_containers = options.maxContainers;
4379
4514
  if (options.warmContainers != null) body.warm_containers = options.warmContainers;
@@ -4393,7 +4528,7 @@ var init_client = __esm({
4393
4528
  }
4394
4529
  // --- Connect ---
4395
4530
  /** Return a `Sandbox` handle for an existing running sandbox without verifying it exists. */
4396
- connect(identifier, proxyUrl, routingHint) {
4531
+ connect(identifier, proxyUrl, routingHint, requestTimeout) {
4397
4532
  const resolvedProxy = proxyUrl ?? resolveProxyUrl(this.apiUrl);
4398
4533
  return new Sandbox({
4399
4534
  sandboxId: identifier,
@@ -4402,29 +4537,33 @@ var init_client = __esm({
4402
4537
  organizationId: this.organizationId,
4403
4538
  projectId: this.projectId,
4404
4539
  routingHint,
4405
- resolveProxyInfo: proxyUrl == null ? async (currentIdentifier) => this.get(currentIdentifier) : void 0
4540
+ resolveProxyInfo: proxyUrl == null ? async (currentIdentifier) => this.get(currentIdentifier) : void 0,
4541
+ requestTimeout
4406
4542
  });
4407
4543
  }
4408
4544
  /**
4409
4545
  * Create a sandbox, wait for it to reach `Running`, and return a connected handle.
4410
4546
  *
4411
- * Blocks until the sandbox is ready or `startupTimeout` elapses. The returned
4547
+ * Blocks until the sandbox is ready or `requestTimeout` elapses. The returned
4412
4548
  * `Sandbox` auto-terminates when `terminate()` is called.
4413
4549
  *
4414
- * @param options.startupTimeout - Max seconds to wait for `Running` status (default 60).
4550
+ * @param options.requestTimeout - Max seconds to wait for `Running` status (default: client requestTimeout).
4551
+ * @param options.startupTimeout - Deprecated alias for `requestTimeout`.
4415
4552
  * @throws {SandboxError} If the sandbox terminates during startup or the timeout elapses.
4416
4553
  */
4417
4554
  async createAndConnect(options) {
4418
- const startupTimeout = options?.startupTimeout ?? 60;
4419
- const result = options?.poolId != null ? await this.claim(options.poolId) : await this.create(options);
4555
+ const requestTimeout = options?.requestTimeout ?? options?.startupTimeout ?? this.requestTimeoutMs / 1e3;
4556
+ const requestClient = this.withRequestTimeout(requestTimeout);
4557
+ const result = options?.poolId != null ? await requestClient.claim(options.poolId) : await requestClient.create(options);
4420
4558
  const requestedName = options?.poolId != null ? null : options?.name ?? null;
4421
4559
  const finishConnect = (routingHint, name, ingressEndpoint) => {
4422
- const sandbox = this.connect(
4560
+ const sandbox = requestClient.connect(
4423
4561
  result.sandboxId,
4424
4562
  options?.proxyUrl ?? ingressEndpoint,
4425
- routingHint
4563
+ routingHint,
4564
+ requestTimeout
4426
4565
  );
4427
- sandbox._setOwner(this);
4566
+ sandbox._setOwner(requestClient);
4428
4567
  sandbox.traceId = result.traceId;
4429
4568
  sandbox._setLifecycleIdentifier(result.sandboxId);
4430
4569
  sandbox._setName(name ?? requestedName);
@@ -4441,9 +4580,18 @@ var init_client = __esm({
4441
4580
  })
4442
4581
  );
4443
4582
  }
4444
- const deadline = Date.now() + startupTimeout * 1e3;
4583
+ if (result.status === "timeout" /* TIMEOUT */) {
4584
+ try {
4585
+ await requestClient.delete(result.sandboxId);
4586
+ } catch {
4587
+ }
4588
+ throw new SandboxError(
4589
+ `Sandbox ${result.sandboxId} did not start within ${requestTimeout}s`
4590
+ );
4591
+ }
4592
+ const deadline = Date.now() + secondsToMillis4(requestTimeout);
4445
4593
  while (Date.now() < deadline) {
4446
- const info = await this.get(result.sandboxId);
4594
+ const info = await requestClient.get(result.sandboxId);
4447
4595
  if (info.status === "running" /* RUNNING */) {
4448
4596
  return finishConnect(info.routingHint, info.name, info.ingressEndpoint);
4449
4597
  }
@@ -4458,11 +4606,11 @@ var init_client = __esm({
4458
4606
  await sleep2(500);
4459
4607
  }
4460
4608
  try {
4461
- await this.delete(result.sandboxId);
4609
+ await requestClient.delete(result.sandboxId);
4462
4610
  } catch {
4463
4611
  }
4464
4612
  throw new SandboxError(
4465
- `Sandbox ${result.sandboxId} did not start within ${startupTimeout}s`
4613
+ `Sandbox ${result.sandboxId} did not start within ${requestTimeout}s`
4466
4614
  );
4467
4615
  }
4468
4616
  };