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.
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.31";
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";
@@ -195,13 +195,14 @@ function anySignal(signals) {
195
195
  function sleep(ms) {
196
196
  return new Promise((resolve) => setTimeout(resolve, ms));
197
197
  }
198
- var import_undici, HttpClient;
198
+ var import_undici, REQUEST_TIMEOUT_HEADER, HttpClient;
199
199
  var init_http = __esm({
200
200
  "src/http.ts"() {
201
201
  "use strict";
202
202
  import_undici = require("undici");
203
203
  init_defaults();
204
204
  init_errors();
205
+ REQUEST_TIMEOUT_HEADER = "X-Tensorlake-Request-Timeout-Ms";
205
206
  (0, import_undici.setGlobalDispatcher)(
206
207
  new import_undici.Agent({
207
208
  keepAliveTimeout: 6e4,
@@ -220,10 +221,13 @@ var init_http = __esm({
220
221
  this.baseUrl = url.endsWith("/") ? url.slice(0, -1) : url;
221
222
  this.maxRetries = options.maxRetries ?? MAX_RETRIES;
222
223
  this.retryBackoffMs = options.retryBackoffMs ?? RETRY_BACKOFF_MS;
223
- this.timeoutMs = options.timeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;
224
+ this.timeoutMs = options.timeoutMs === void 0 ? DEFAULT_HTTP_TIMEOUT_MS : options.timeoutMs;
224
225
  this.headers = {
225
226
  "User-Agent": `tensorlake-typescript-sdk/${SDK_VERSION}`
226
227
  };
228
+ if (this.timeoutMs != null) {
229
+ this.headers[REQUEST_TIMEOUT_HEADER] = String(this.timeoutMs);
230
+ }
227
231
  if (options.apiKey) {
228
232
  this.headers["Authorization"] = `Bearer ${options.apiKey}`;
229
233
  }
@@ -252,7 +256,10 @@ var init_http = __esm({
252
256
  const response = await this.requestResponse(method, path2, {
253
257
  json: options?.body,
254
258
  headers: options?.headers,
255
- signal: options?.signal
259
+ signal: options?.signal,
260
+ retryableStatusCodes: options?.retryableStatusCodes,
261
+ allowedErrorStatusCodes: options?.allowedErrorStatusCodes,
262
+ allowHttpErrors: options?.allowHttpErrors
256
263
  });
257
264
  const text = await response.text();
258
265
  const data = text ? JSON.parse(text) : void 0;
@@ -304,7 +311,9 @@ var init_http = __esm({
304
311
  body,
305
312
  headers,
306
313
  options?.signal,
307
- options?.allowHttpErrors ?? false
314
+ options?.allowHttpErrors ?? false,
315
+ options?.retryableStatusCodes ?? RETRYABLE_STATUS_CODES,
316
+ options?.allowedErrorStatusCodes ?? /* @__PURE__ */ new Set()
308
317
  );
309
318
  return withTraceId(response, traceId);
310
319
  }
@@ -314,7 +323,7 @@ var init_http = __esm({
314
323
  const spanId = randomHex(8);
315
324
  return { traceparent: `00-${traceId}-${spanId}-01`, traceId };
316
325
  }
317
- async doFetch(method, path2, body, headers, signal, allowHttpErrors = false) {
326
+ async doFetch(method, path2, body, headers, signal, allowHttpErrors = false, retryableStatusCodes = RETRYABLE_STATUS_CODES, allowedErrorStatusCodes = /* @__PURE__ */ new Set()) {
318
327
  const url = `${this.baseUrl}${path2}`;
319
328
  const { traceparent, traceId } = _HttpClient.makeTraceparent();
320
329
  headers["traceparent"] = traceparent;
@@ -325,10 +334,7 @@ var init_http = __esm({
325
334
  await sleep(delay);
326
335
  }
327
336
  this.abortController = new AbortController();
328
- const timeoutId = setTimeout(
329
- () => this.abortController?.abort(),
330
- this.timeoutMs
331
- );
337
+ const timeoutId = this.timeoutMs == null ? null : setTimeout(() => this.abortController?.abort(), this.timeoutMs);
332
338
  const combinedSignal = signal ? anySignal([signal, this.abortController.signal]) : this.abortController.signal;
333
339
  try {
334
340
  const response = await (0, import_undici.fetch)(url, {
@@ -337,22 +343,22 @@ var init_http = __esm({
337
343
  body,
338
344
  signal: combinedSignal
339
345
  });
340
- clearTimeout(timeoutId);
346
+ if (timeoutId != null) clearTimeout(timeoutId);
341
347
  if (response.ok) return { response, traceId };
342
- if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < this.maxRetries) {
348
+ if (retryableStatusCodes.has(response.status) && attempt < this.maxRetries) {
343
349
  lastError = new RemoteAPIError(
344
350
  response.status,
345
351
  await response.text().catch(() => "")
346
352
  );
347
353
  continue;
348
354
  }
349
- if (allowHttpErrors) {
355
+ if (allowHttpErrors || allowedErrorStatusCodes.has(response.status)) {
350
356
  return { response, traceId };
351
357
  }
352
358
  const errorBody = await response.text().catch(() => "");
353
359
  throwMappedError(response.status, errorBody, path2);
354
360
  } catch (err) {
355
- clearTimeout(timeoutId);
361
+ if (timeoutId != null) clearTimeout(timeoutId);
356
362
  if (err instanceof RemoteAPIError || err instanceof SandboxNotFoundError || err instanceof PoolNotFoundError || err instanceof PoolInUseError) {
357
363
  throw err;
358
364
  }
@@ -424,6 +430,7 @@ var init_models = __esm({
424
430
  SandboxStatus3["SUSPENDING"] = "suspending";
425
431
  SandboxStatus3["SUSPENDED"] = "suspended";
426
432
  SandboxStatus3["TERMINATED"] = "terminated";
433
+ SandboxStatus3["TIMEOUT"] = "timeout";
427
434
  return SandboxStatus3;
428
435
  })(SandboxStatus || {});
429
436
  SnapshotStatus = /* @__PURE__ */ ((SnapshotStatus2) => {
@@ -3035,6 +3042,12 @@ function sendPtyFrame(socket, frame) {
3035
3042
  socket.send(frame, (error) => error ? reject(error) : resolve());
3036
3043
  });
3037
3044
  }
3045
+ function secondsToMillis3(seconds) {
3046
+ if (!Number.isFinite(seconds) || seconds <= 0) {
3047
+ throw new SandboxError("requestTimeout must be a positive number of seconds");
3048
+ }
3049
+ return Math.ceil(seconds * 1e3);
3050
+ }
3038
3051
  var import_ws2, DEFAULT_PROCESS_USER, SandboxProxyConnection, PTY_OP_DATA, PTY_OP_RESIZE, PTY_OP_READY, PTY_OP_EXIT, Pty, Sandbox;
3039
3052
  var init_sandbox = __esm({
3040
3053
  "src/sandbox.ts"() {
@@ -3137,7 +3150,8 @@ var init_sandbox = __esm({
3137
3150
  projectId: this.options.projectId,
3138
3151
  hostHeader,
3139
3152
  sandboxIdHeader,
3140
- routingHint
3153
+ routingHint,
3154
+ timeoutMs: this.options.requestTimeout != null ? secondsToMillis3(this.options.requestTimeout) : this.options.timeoutMs ?? null
3141
3155
  });
3142
3156
  }
3143
3157
  };
@@ -3358,9 +3372,7 @@ var init_sandbox = __esm({
3358
3372
  /* _internal */
3359
3373
  true
3360
3374
  );
3361
- const sandbox = await client.createAndConnect(options);
3362
- sandbox.lifecycleClient = client;
3363
- return sandbox;
3375
+ return client.createAndConnect(options);
3364
3376
  }
3365
3377
  /**
3366
3378
  * Attach to an existing sandbox and return a connected handle.
@@ -3379,7 +3391,8 @@ var init_sandbox = __esm({
3379
3391
  const sandbox = client.connect(
3380
3392
  options.sandboxId,
3381
3393
  options.proxyUrl,
3382
- options.routingHint
3394
+ options.routingHint,
3395
+ options.requestTimeout
3383
3396
  );
3384
3397
  sandbox.lifecycleClient = client;
3385
3398
  return sandbox;
@@ -3904,6 +3917,27 @@ var client_exports = {};
3904
3917
  __export(client_exports, {
3905
3918
  SandboxClient: () => SandboxClient
3906
3919
  });
3920
+ function resolveRequestTimeoutMs(options) {
3921
+ if (options?.requestTimeout != null) {
3922
+ return secondsToMillis4(options.requestTimeout);
3923
+ }
3924
+ if (options?.timeoutMs != null) {
3925
+ validateTimeoutMs(options.timeoutMs);
3926
+ return options.timeoutMs;
3927
+ }
3928
+ return DEFAULT_HTTP_TIMEOUT_MS;
3929
+ }
3930
+ function secondsToMillis4(seconds) {
3931
+ if (!Number.isFinite(seconds) || seconds <= 0) {
3932
+ throw new SandboxError("requestTimeout must be a positive number of seconds");
3933
+ }
3934
+ return Math.ceil(seconds * 1e3);
3935
+ }
3936
+ function validateTimeoutMs(timeoutMs) {
3937
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
3938
+ throw new SandboxError("timeoutMs must be a positive number of milliseconds");
3939
+ }
3940
+ }
3907
3941
  function sleep2(ms) {
3908
3942
  return new Promise((resolve) => setTimeout(resolve, ms));
3909
3943
  }
@@ -3960,7 +3994,7 @@ function validateUserPort(port) {
3960
3994
  function dedupeAndSortPorts(ports) {
3961
3995
  return [...new Set(ports)].sort((a, b) => a - b);
3962
3996
  }
3963
- var SandboxClient, RESERVED_SANDBOX_MANAGEMENT_PORT;
3997
+ var CREATE_SANDBOX_RETRYABLE_STATUS_CODES, CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES, SandboxClient, RESERVED_SANDBOX_MANAGEMENT_PORT;
3964
3998
  var init_client = __esm({
3965
3999
  "src/client.ts"() {
3966
4000
  "use strict";
@@ -3970,6 +4004,8 @@ var init_client = __esm({
3970
4004
  init_models();
3971
4005
  init_sandbox();
3972
4006
  init_url();
4007
+ CREATE_SANDBOX_RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([502, 503]);
4008
+ CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES = /* @__PURE__ */ new Set([504]);
3973
4009
  SandboxClient = class _SandboxClient {
3974
4010
  http;
3975
4011
  apiUrl;
@@ -3978,6 +4014,9 @@ var init_client = __esm({
3978
4014
  projectId;
3979
4015
  namespace;
3980
4016
  local;
4017
+ maxRetries;
4018
+ retryBackoffMs;
4019
+ requestTimeoutMs;
3981
4020
  /** @internal Pass `true` to suppress the deprecation warning when used by `Sandbox.create()` / `Sandbox.connect()`. */
3982
4021
  constructor(options, _internal = false) {
3983
4022
  if (!_internal) {
@@ -3991,13 +4030,17 @@ var init_client = __esm({
3991
4030
  this.projectId = options?.projectId;
3992
4031
  this.namespace = options?.namespace ?? NAMESPACE;
3993
4032
  this.local = isLocalhost(this.apiUrl);
4033
+ this.maxRetries = options?.maxRetries ?? MAX_RETRIES;
4034
+ this.retryBackoffMs = options?.retryBackoffMs ?? RETRY_BACKOFF_MS;
4035
+ this.requestTimeoutMs = resolveRequestTimeoutMs(options);
3994
4036
  this.http = new HttpClient({
3995
4037
  baseUrl: resolveSandboxLifecycleUrl(this.apiUrl),
3996
4038
  apiKey: this.apiKey,
3997
4039
  organizationId: this.organizationId,
3998
4040
  projectId: this.projectId,
3999
- maxRetries: options?.maxRetries ?? MAX_RETRIES,
4000
- retryBackoffMs: options?.retryBackoffMs ?? RETRY_BACKOFF_MS
4041
+ maxRetries: this.maxRetries,
4042
+ retryBackoffMs: this.retryBackoffMs,
4043
+ timeoutMs: this.requestTimeoutMs
4001
4044
  });
4002
4045
  }
4003
4046
  /** Create a client for the TensorLake cloud platform. */
@@ -4006,19 +4049,46 @@ var init_client = __esm({
4006
4049
  apiUrl: options?.apiUrl ?? "https://api.tensorlake.ai",
4007
4050
  apiKey: options?.apiKey,
4008
4051
  organizationId: options?.organizationId,
4009
- projectId: options?.projectId
4052
+ projectId: options?.projectId,
4053
+ requestTimeout: options?.requestTimeout,
4054
+ timeoutMs: options?.timeoutMs
4010
4055
  });
4011
4056
  }
4012
4057
  /** Create a client for a local Indexify server. */
4013
4058
  static forLocalhost(options) {
4014
4059
  return new _SandboxClient({
4015
4060
  apiUrl: options?.apiUrl ?? "http://localhost:8900",
4016
- namespace: options?.namespace ?? "default"
4061
+ namespace: options?.namespace ?? "default",
4062
+ requestTimeout: options?.requestTimeout,
4063
+ timeoutMs: options?.timeoutMs
4017
4064
  });
4018
4065
  }
4019
4066
  close() {
4020
4067
  this.http.close();
4021
4068
  }
4069
+ withRequestTimeout(requestTimeout) {
4070
+ if (requestTimeout == null) {
4071
+ return this;
4072
+ }
4073
+ const timeoutMs = secondsToMillis4(requestTimeout);
4074
+ if (timeoutMs === this.requestTimeoutMs) {
4075
+ return this;
4076
+ }
4077
+ return new _SandboxClient(
4078
+ {
4079
+ apiUrl: this.apiUrl,
4080
+ apiKey: this.apiKey,
4081
+ organizationId: this.organizationId,
4082
+ projectId: this.projectId,
4083
+ namespace: this.namespace,
4084
+ maxRetries: this.maxRetries,
4085
+ retryBackoffMs: this.retryBackoffMs,
4086
+ timeoutMs
4087
+ },
4088
+ /* _internal */
4089
+ true
4090
+ );
4091
+ }
4022
4092
  // --- Path helper ---
4023
4093
  path(subpath) {
4024
4094
  return lifecyclePath(subpath, this.local, this.namespace);
@@ -4049,7 +4119,11 @@ var init_client = __esm({
4049
4119
  const raw = await this.http.requestJson(
4050
4120
  "POST",
4051
4121
  this.path("sandboxes"),
4052
- { body }
4122
+ {
4123
+ body,
4124
+ retryableStatusCodes: CREATE_SANDBOX_RETRYABLE_STATUS_CODES,
4125
+ allowedErrorStatusCodes: CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES
4126
+ }
4053
4127
  );
4054
4128
  const result = fromSnakeKeys(raw, "sandboxId");
4055
4129
  return Object.assign(result, { traceId: raw.traceId });
@@ -4243,7 +4317,11 @@ var init_client = __esm({
4243
4317
  async claim(poolId) {
4244
4318
  const raw = await this.http.requestJson(
4245
4319
  "POST",
4246
- this.path(`sandbox-pools/${poolId}/sandboxes`)
4320
+ this.path(`sandbox-pools/${poolId}/sandboxes`),
4321
+ {
4322
+ retryableStatusCodes: CREATE_SANDBOX_RETRYABLE_STATUS_CODES,
4323
+ allowedErrorStatusCodes: CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES
4324
+ }
4247
4325
  );
4248
4326
  const result = fromSnakeKeys(raw, "sandboxId");
4249
4327
  return Object.assign(result, { traceId: raw.traceId });
@@ -4405,7 +4483,7 @@ var init_client = __esm({
4405
4483
  }
4406
4484
  // --- Connect ---
4407
4485
  /** Return a `Sandbox` handle for an existing running sandbox without verifying it exists. */
4408
- connect(identifier, proxyUrl, routingHint) {
4486
+ connect(identifier, proxyUrl, routingHint, requestTimeout) {
4409
4487
  const resolvedProxy = proxyUrl ?? resolveProxyUrl(this.apiUrl);
4410
4488
  return new Sandbox({
4411
4489
  sandboxId: identifier,
@@ -4414,29 +4492,33 @@ var init_client = __esm({
4414
4492
  organizationId: this.organizationId,
4415
4493
  projectId: this.projectId,
4416
4494
  routingHint,
4417
- resolveProxyInfo: proxyUrl == null ? async (currentIdentifier) => this.get(currentIdentifier) : void 0
4495
+ resolveProxyInfo: proxyUrl == null ? async (currentIdentifier) => this.get(currentIdentifier) : void 0,
4496
+ requestTimeout
4418
4497
  });
4419
4498
  }
4420
4499
  /**
4421
4500
  * Create a sandbox, wait for it to reach `Running`, and return a connected handle.
4422
4501
  *
4423
- * Blocks until the sandbox is ready or `startupTimeout` elapses. The returned
4502
+ * Blocks until the sandbox is ready or `requestTimeout` elapses. The returned
4424
4503
  * `Sandbox` auto-terminates when `terminate()` is called.
4425
4504
  *
4426
- * @param options.startupTimeout - Max seconds to wait for `Running` status (default 60).
4505
+ * @param options.requestTimeout - Max seconds to wait for `Running` status (default: client requestTimeout).
4506
+ * @param options.startupTimeout - Deprecated alias for `requestTimeout`.
4427
4507
  * @throws {SandboxError} If the sandbox terminates during startup or the timeout elapses.
4428
4508
  */
4429
4509
  async createAndConnect(options) {
4430
- const startupTimeout = options?.startupTimeout ?? 60;
4431
- const result = options?.poolId != null ? await this.claim(options.poolId) : await this.create(options);
4510
+ const requestTimeout = options?.requestTimeout ?? options?.startupTimeout ?? this.requestTimeoutMs / 1e3;
4511
+ const requestClient = this.withRequestTimeout(requestTimeout);
4512
+ const result = options?.poolId != null ? await requestClient.claim(options.poolId) : await requestClient.create(options);
4432
4513
  const requestedName = options?.poolId != null ? null : options?.name ?? null;
4433
4514
  const finishConnect = (routingHint, name, ingressEndpoint) => {
4434
- const sandbox = this.connect(
4515
+ const sandbox = requestClient.connect(
4435
4516
  result.sandboxId,
4436
4517
  options?.proxyUrl ?? ingressEndpoint,
4437
- routingHint
4518
+ routingHint,
4519
+ requestTimeout
4438
4520
  );
4439
- sandbox._setOwner(this);
4521
+ sandbox._setOwner(requestClient);
4440
4522
  sandbox.traceId = result.traceId;
4441
4523
  sandbox._setLifecycleIdentifier(result.sandboxId);
4442
4524
  sandbox._setName(name ?? requestedName);
@@ -4453,9 +4535,18 @@ var init_client = __esm({
4453
4535
  })
4454
4536
  );
4455
4537
  }
4456
- const deadline = Date.now() + startupTimeout * 1e3;
4538
+ if (result.status === "timeout" /* TIMEOUT */) {
4539
+ try {
4540
+ await requestClient.delete(result.sandboxId);
4541
+ } catch {
4542
+ }
4543
+ throw new SandboxError(
4544
+ `Sandbox ${result.sandboxId} did not start within ${requestTimeout}s`
4545
+ );
4546
+ }
4547
+ const deadline = Date.now() + secondsToMillis4(requestTimeout);
4457
4548
  while (Date.now() < deadline) {
4458
- const info = await this.get(result.sandboxId);
4549
+ const info = await requestClient.get(result.sandboxId);
4459
4550
  if (info.status === "running" /* RUNNING */) {
4460
4551
  return finishConnect(info.routingHint, info.name, info.ingressEndpoint);
4461
4552
  }
@@ -4470,11 +4561,11 @@ var init_client = __esm({
4470
4561
  await sleep2(500);
4471
4562
  }
4472
4563
  try {
4473
- await this.delete(result.sandboxId);
4564
+ await requestClient.delete(result.sandboxId);
4474
4565
  } catch {
4475
4566
  }
4476
4567
  throw new SandboxError(
4477
- `Sandbox ${result.sandboxId} did not start within ${startupTimeout}s`
4568
+ `Sandbox ${result.sandboxId} did not start within ${requestTimeout}s`
4478
4569
  );
4479
4570
  }
4480
4571
  };