tensorlake 0.5.34 → 0.5.36

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.34";
38
+ SDK_VERSION = "0.5.36";
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";
@@ -155,6 +155,56 @@ var init_errors = __esm({
155
155
  }
156
156
  });
157
157
 
158
+ // src/sdk-timings.ts
159
+ function sdkTimingsEnabled() {
160
+ const value = process.env[SDK_TIMINGS_ENV_VAR];
161
+ return value === "1" || value === "true" || value === "yes";
162
+ }
163
+ function sdkTimingPayloadsEnabled() {
164
+ const value = process.env[SDK_TIMING_PAYLOADS_ENV_VAR];
165
+ return value === "1" || value === "true" || value === "yes";
166
+ }
167
+ function nowMs() {
168
+ return performance.now();
169
+ }
170
+ function logSdkTiming(op, phase, startedAt, fields = {}) {
171
+ if (!sdkTimingsEnabled()) return;
172
+ logSdkTimingEvent(op, phase, {
173
+ ...fields,
174
+ duration_ms: Math.round((nowMs() - startedAt) * 100) / 100
175
+ });
176
+ }
177
+ function logSdkTimingEvent(op, phase, fields = {}) {
178
+ if (!sdkTimingsEnabled()) return;
179
+ const parts = [
180
+ "[tensorlake:sdk-timing]",
181
+ `op=${op}`,
182
+ `phase=${phase}`
183
+ ];
184
+ for (const [key, value] of Object.entries(fields)) {
185
+ if (value == null) continue;
186
+ parts.push(`${key}=${formatSdkTimingValue(value)}`);
187
+ }
188
+ console.error(parts.join(" "));
189
+ }
190
+ function formatSdkTimingValue(value) {
191
+ if (typeof value === "number" || typeof value === "boolean") {
192
+ return String(value);
193
+ }
194
+ if (/^[A-Za-z0-9_./:@-]+$/.test(value)) {
195
+ return value;
196
+ }
197
+ return JSON.stringify(value);
198
+ }
199
+ var SDK_TIMINGS_ENV_VAR, SDK_TIMING_PAYLOADS_ENV_VAR;
200
+ var init_sdk_timings = __esm({
201
+ "src/sdk-timings.ts"() {
202
+ "use strict";
203
+ SDK_TIMINGS_ENV_VAR = "TENSORLAKE_SDK_TIMINGS";
204
+ SDK_TIMING_PAYLOADS_ENV_VAR = "TENSORLAKE_SDK_TIMING_PAYLOADS";
205
+ }
206
+ });
207
+
158
208
  // src/http.ts
159
209
  function withTraceId(value, traceId) {
160
210
  if (value == null) {
@@ -175,6 +225,10 @@ function normalizeRequestBody(body) {
175
225
  }
176
226
  return body;
177
227
  }
228
+ function stripPathPayload(path2) {
229
+ const queryStart = path2.search(/[?#]/);
230
+ return queryStart === -1 ? path2 : path2.slice(0, queryStart);
231
+ }
178
232
  function throwMappedError(status, body, path2) {
179
233
  let message = body;
180
234
  try {
@@ -225,6 +279,7 @@ var init_http = __esm({
225
279
  import_undici = require("undici");
226
280
  init_defaults();
227
281
  init_errors();
282
+ init_sdk_timings();
228
283
  REQUEST_TIMEOUT_HEADER = "X-Tensorlake-Request-Timeout-Ms";
229
284
  (0, import_undici.setGlobalDispatcher)(
230
285
  new import_undici.Agent({
@@ -351,9 +406,18 @@ var init_http = __esm({
351
406
  const { traceparent, traceId } = _HttpClient.makeTraceparent();
352
407
  headers["traceparent"] = traceparent;
353
408
  let lastError;
409
+ const operationStart = nowMs();
410
+ const timingPath = sdkTimingPayloadsEnabled() ? path2 : stripPathPayload(path2);
354
411
  for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
355
412
  if (attempt > 0) {
356
413
  const delay = this.retryBackoffMs * Math.pow(2, attempt - 1);
414
+ logSdkTimingEvent("http.request", "retry_sleep_start", {
415
+ method,
416
+ path: timingPath,
417
+ attempt,
418
+ delay_ms: delay,
419
+ server_trace_id: traceId
420
+ });
357
421
  await sleep(delay);
358
422
  }
359
423
  this.abortController = new AbortController();
@@ -363,6 +427,7 @@ var init_http = __esm({
363
427
  this.abortController?.abort();
364
428
  }, this.timeoutMs);
365
429
  const combinedSignal = signal ? anySignal([signal, this.abortController.signal]) : this.abortController.signal;
430
+ const attemptStart = nowMs();
366
431
  try {
367
432
  const response = await (0, import_undici.fetch)(url, {
368
433
  method,
@@ -371,15 +436,45 @@ var init_http = __esm({
371
436
  signal: combinedSignal
372
437
  });
373
438
  if (timeoutId != null) clearTimeout(timeoutId);
374
- if (response.ok) return { response, traceId };
439
+ logSdkTiming("http.request", "headers", attemptStart, {
440
+ method,
441
+ path: timingPath,
442
+ status: response.status,
443
+ attempt,
444
+ server_trace_id: traceId
445
+ });
446
+ if (response.ok) {
447
+ logSdkTiming("http.request", "complete", operationStart, {
448
+ method,
449
+ path: timingPath,
450
+ status: response.status,
451
+ attempts: attempt + 1,
452
+ server_trace_id: traceId
453
+ });
454
+ return { response, traceId };
455
+ }
375
456
  if (retryableStatusCodes.has(response.status) && attempt < this.maxRetries) {
376
457
  lastError = new RemoteAPIError(
377
458
  response.status,
378
459
  await response.text().catch(() => "")
379
460
  );
461
+ logSdkTimingEvent("http.request", "retryable_status", {
462
+ method,
463
+ path: timingPath,
464
+ status: response.status,
465
+ attempt,
466
+ server_trace_id: traceId
467
+ });
380
468
  continue;
381
469
  }
382
470
  if (allowHttpErrors || allowedErrorStatusCodes.has(response.status)) {
471
+ logSdkTiming("http.request", "complete", operationStart, {
472
+ method,
473
+ path: timingPath,
474
+ status: response.status,
475
+ attempts: attempt + 1,
476
+ server_trace_id: traceId
477
+ });
383
478
  return { response, traceId };
384
479
  }
385
480
  const errorBody = await response.text().catch(() => "");
@@ -395,6 +490,13 @@ var init_http = __esm({
395
490
  });
396
491
  }
397
492
  lastError = err instanceof Error ? err : new Error(String(err));
493
+ logSdkTiming("http.request", "error", attemptStart, {
494
+ method,
495
+ path: timingPath,
496
+ attempt,
497
+ server_trace_id: traceId,
498
+ error: timedOut ? `request timed out after ${this.timeoutMs}ms` : describeError(err)
499
+ });
398
500
  if (attempt >= this.maxRetries) {
399
501
  const detail = timedOut ? `request timed out after ${this.timeoutMs}ms` : describeError(err);
400
502
  throw new SandboxConnectionError(detail, { cause: err });
@@ -3108,6 +3210,7 @@ var init_sandbox = __esm({
3108
3210
  init_models();
3109
3211
  init_tunnel();
3110
3212
  init_sse();
3213
+ init_sdk_timings();
3111
3214
  init_url();
3112
3215
  import_ws2 = __toESM(require("ws"), 1);
3113
3216
  DEFAULT_PROCESS_USER = "tl-user";
@@ -3136,7 +3239,12 @@ var init_sandbox = __esm({
3136
3239
  if (this.resolvePromise != null) {
3137
3240
  return this.resolvePromise;
3138
3241
  }
3139
- this.resolvePromise = this.resolveProxyInfo(this.sandbox._getLifecycleIdentifier()).then((info) => {
3242
+ const identifier = this.sandbox._getLifecycleIdentifier();
3243
+ const resolveStart = nowMs();
3244
+ logSdkTimingEvent("sandbox.proxy", "resolve_start", {
3245
+ sandbox_id: identifier
3246
+ });
3247
+ this.resolvePromise = this.resolveProxyInfo(identifier).then((info) => {
3140
3248
  this.resolveProxyInfo = void 0;
3141
3249
  this.sandbox.traceId = info.traceId;
3142
3250
  this.sandbox._setLifecycleIdentifier(info.sandboxId);
@@ -3146,6 +3254,12 @@ var init_sandbox = __esm({
3146
3254
  const nextHttp = this.configureProxy(proxyUrl, info.sandboxId, this.routingHint);
3147
3255
  this.http.close();
3148
3256
  this.http = nextHttp;
3257
+ logSdkTiming("sandbox.proxy", "resolve_complete", resolveStart, {
3258
+ sandbox_id: info.sandboxId,
3259
+ server_trace_id: info.traceId,
3260
+ routing_hint: this.routingHint,
3261
+ ingress_endpoint: info.ingressEndpoint
3262
+ });
3149
3263
  }).finally(() => {
3150
3264
  this.resolvePromise = null;
3151
3265
  });
@@ -3606,6 +3720,7 @@ var init_sandbox = __esm({
3606
3720
  * the process, streams output, and delivers the exit code over one connection.
3607
3721
  */
3608
3722
  async run(command, options) {
3723
+ const opStart = nowMs();
3609
3724
  const body = { command };
3610
3725
  if (options?.args) body.args = options.args;
3611
3726
  if (options?.env) body.env = options.env;
@@ -3613,15 +3728,28 @@ var init_sandbox = __esm({
3613
3728
  if (options?.timeout != null) body.timeout = options.timeout;
3614
3729
  const user = processUserPayload(options?.user);
3615
3730
  body.user = user;
3731
+ logSdkTimingEvent("sandbox.run", "start", {
3732
+ sandbox_id: this.sandboxId,
3733
+ command: sdkTimingPayloadsEnabled() ? command : void 0,
3734
+ command_length: command.length
3735
+ });
3736
+ const requestStart = nowMs();
3616
3737
  const sseStream = await this.http.requestStream(
3617
3738
  "POST",
3618
3739
  "/api/v1/processes/run",
3619
3740
  { json: body }
3620
3741
  );
3621
3742
  const traceId = sseStream.traceId;
3743
+ logSdkTiming("sandbox.run", "stream_headers", requestStart, {
3744
+ sandbox_id: this.sandboxId,
3745
+ server_trace_id: traceId,
3746
+ command: sdkTimingPayloadsEnabled() ? command : void 0,
3747
+ command_length: command.length
3748
+ });
3622
3749
  const stdoutLines = [];
3623
3750
  const stderrLines = [];
3624
3751
  let exitCode = -1;
3752
+ const streamStart = nowMs();
3625
3753
  for await (const raw of parseSSEStream(sseStream)) {
3626
3754
  if (typeof raw.line === "string") {
3627
3755
  if (raw.stream === "stderr") {
@@ -3637,6 +3765,20 @@ var init_sandbox = __esm({
3637
3765
  }
3638
3766
  }
3639
3767
  }
3768
+ logSdkTiming("sandbox.run", "stream_complete", streamStart, {
3769
+ sandbox_id: this.sandboxId,
3770
+ server_trace_id: traceId,
3771
+ command: sdkTimingPayloadsEnabled() ? command : void 0,
3772
+ command_length: command.length,
3773
+ exit_code: exitCode
3774
+ });
3775
+ logSdkTiming("sandbox.run", "complete", opStart, {
3776
+ sandbox_id: this.sandboxId,
3777
+ server_trace_id: traceId,
3778
+ command: sdkTimingPayloadsEnabled() ? command : void 0,
3779
+ command_length: command.length,
3780
+ exit_code: exitCode
3781
+ });
3640
3782
  return Object.assign(
3641
3783
  { exitCode, stdout: stdoutLines.join("\n"), stderr: stderrLines.join("\n") },
3642
3784
  { traceId }
@@ -3984,6 +4126,49 @@ var client_exports = {};
3984
4126
  __export(client_exports, {
3985
4127
  SandboxClient: () => SandboxClient
3986
4128
  });
4129
+ function cloudInitIncludeData(source) {
4130
+ const isWindowsPath = /^[A-Za-z]:[\\/]/.test(source);
4131
+ try {
4132
+ const url = new URL(source);
4133
+ if ((url.protocol === "http:" || url.protocol === "https:") && url.hostname.length > 0) {
4134
+ return Buffer.from(`#include
4135
+ ${url.toString()}
4136
+ `, "utf8");
4137
+ }
4138
+ if (!isWindowsPath) {
4139
+ throw new SandboxError(
4140
+ "cloud-init URL must be an absolute HTTP(S) URL with a host"
4141
+ );
4142
+ }
4143
+ } catch (error) {
4144
+ if (error instanceof SandboxError) throw error;
4145
+ if (!isWindowsPath && /^[A-Za-z][A-Za-z0-9+.-]*:/.test(source)) {
4146
+ throw new SandboxError(
4147
+ "cloud-init URL must be an absolute HTTP(S) URL with a host"
4148
+ );
4149
+ }
4150
+ }
4151
+ return void 0;
4152
+ }
4153
+ async function readCloudInitBase64(cloudInit) {
4154
+ if (cloudInit == null) return void 0;
4155
+ const sourceText = cloudInit instanceof URL ? cloudInit.toString() : cloudInit;
4156
+ const includeData = cloudInitIncludeData(sourceText);
4157
+ const data = includeData ?? await (0, import_promises.readFile)(sourceText).catch((error) => {
4158
+ throw new SandboxError(
4159
+ `failed to read cloud-init file ${JSON.stringify(sourceText)}: ${error}`
4160
+ );
4161
+ });
4162
+ if (data.length === 0) {
4163
+ throw new SandboxError("cloud-init user data must not be empty");
4164
+ }
4165
+ if (data.length > MAX_CLOUD_INIT_USER_DATA_BYTES) {
4166
+ throw new SandboxError(
4167
+ `cloud-init user data exceeds ${MAX_CLOUD_INIT_USER_DATA_BYTES} byte limit`
4168
+ );
4169
+ }
4170
+ return data.toString("base64");
4171
+ }
3987
4172
  function resolveRequestTimeoutMs(options) {
3988
4173
  if (options?.requestTimeout != null) {
3989
4174
  return secondsToMillis4(options.requestTimeout);
@@ -4061,19 +4246,22 @@ function validateUserPort(port) {
4061
4246
  function dedupeAndSortPorts(ports) {
4062
4247
  return [...new Set(ports)].sort((a, b) => a - b);
4063
4248
  }
4064
- var CREATE_SANDBOX_RETRYABLE_STATUS_CODES, CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES, COPY_SANDBOX_ALLOWED_ERROR_STATUS_CODES, SandboxClient, RESERVED_SANDBOX_MANAGEMENT_PORT;
4249
+ var import_promises, CREATE_SANDBOX_RETRYABLE_STATUS_CODES, CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES, COPY_SANDBOX_ALLOWED_ERROR_STATUS_CODES, MAX_CLOUD_INIT_USER_DATA_BYTES, SandboxClient, RESERVED_SANDBOX_MANAGEMENT_PORT;
4065
4250
  var init_client = __esm({
4066
4251
  "src/client.ts"() {
4067
4252
  "use strict";
4253
+ import_promises = require("fs/promises");
4068
4254
  init_defaults();
4069
4255
  init_errors();
4070
4256
  init_http();
4071
4257
  init_models();
4072
4258
  init_sandbox();
4259
+ init_sdk_timings();
4073
4260
  init_url();
4074
4261
  CREATE_SANDBOX_RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([502, 503]);
4075
4262
  CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES = /* @__PURE__ */ new Set([504]);
4076
4263
  COPY_SANDBOX_ALLOWED_ERROR_STATUS_CODES = /* @__PURE__ */ new Set([422, 504]);
4264
+ MAX_CLOUD_INIT_USER_DATA_BYTES = 16 * 1024;
4077
4265
  SandboxClient = class _SandboxClient {
4078
4266
  http;
4079
4267
  apiUrl;
@@ -4164,6 +4352,7 @@ var init_client = __esm({
4164
4352
  // --- Sandbox CRUD ---
4165
4353
  /** Create a new sandbox. Returns immediately; the sandbox may still be starting. Use `createAndConnect()` for a blocking, ready-to-use handle. */
4166
4354
  async create(options) {
4355
+ const cloudInitBase64 = await readCloudInitBase64(options?.cloudInit);
4167
4356
  const body = {
4168
4357
  resources: {
4169
4358
  cpus: options?.cpus ?? 1,
@@ -4176,6 +4365,7 @@ var init_client = __esm({
4176
4365
  if (options?.entrypoint != null) body.entrypoint = options.entrypoint;
4177
4366
  if (options?.snapshotId != null) body.snapshot_id = options.snapshotId;
4178
4367
  if (options?.name != null) body.name = options.name;
4368
+ if (cloudInitBase64 != null) body.cloud_init_base64 = cloudInitBase64;
4179
4369
  if (options?.allowInternetAccess === false || options?.allowOut != null || options?.denyOut != null) {
4180
4370
  body.network = {
4181
4371
  allow_internet_access: options?.allowInternetAccess ?? true,
@@ -4593,9 +4783,24 @@ var init_client = __esm({
4593
4783
  * @throws {SandboxError} If the sandbox terminates during startup or the timeout elapses.
4594
4784
  */
4595
4785
  async createAndConnect(options) {
4786
+ if (options?.poolId != null && options?.cloudInit != null) {
4787
+ throw new SandboxError("cloud-init cannot be used with poolId");
4788
+ }
4789
+ const opStart = nowMs();
4596
4790
  const requestTimeout = options?.requestTimeout ?? options?.startupTimeout ?? this.requestTimeoutMs / 1e3;
4597
4791
  const requestClient = this.withRequestTimeout(requestTimeout);
4792
+ logSdkTimingEvent("sandbox.create", "start", {
4793
+ request_timeout_s: requestTimeout,
4794
+ image: options?.image,
4795
+ pool_id: options?.poolId
4796
+ });
4797
+ const createStart = nowMs();
4598
4798
  const result = options?.poolId != null ? await requestClient.claim(options.poolId) : await requestClient.create(options);
4799
+ logSdkTiming("sandbox.create", options?.poolId != null ? "claim_response" : "create_response", createStart, {
4800
+ sandbox_id: result.sandboxId,
4801
+ status: result.status,
4802
+ server_trace_id: result.traceId
4803
+ });
4599
4804
  const requestedName = options?.poolId != null ? null : options?.name ?? null;
4600
4805
  const finishConnect = (routingHint, name, ingressEndpoint) => {
4601
4806
  const sandbox = requestClient.connect(
@@ -4608,6 +4813,13 @@ var init_client = __esm({
4608
4813
  sandbox.traceId = result.traceId;
4609
4814
  sandbox._setLifecycleIdentifier(result.sandboxId);
4610
4815
  sandbox._setName(name ?? requestedName);
4816
+ logSdkTiming("sandbox.create", "complete", opStart, {
4817
+ sandbox_id: result.sandboxId,
4818
+ status: "running" /* RUNNING */,
4819
+ server_trace_id: result.traceId,
4820
+ routing_hint: routingHint,
4821
+ ingress_endpoint: ingressEndpoint
4822
+ });
4611
4823
  return sandbox;
4612
4824
  };
4613
4825
  if (result.status === "running" /* RUNNING */) {
@@ -4632,7 +4844,13 @@ var init_client = __esm({
4632
4844
  }
4633
4845
  const deadline = Date.now() + secondsToMillis4(requestTimeout);
4634
4846
  while (Date.now() < deadline) {
4847
+ const pollStart = nowMs();
4635
4848
  const info = await requestClient.get(result.sandboxId);
4849
+ logSdkTiming("sandbox.create", "poll_response", pollStart, {
4850
+ sandbox_id: result.sandboxId,
4851
+ status: info.status,
4852
+ server_trace_id: info.traceId
4853
+ });
4636
4854
  if (info.status === "running" /* RUNNING */) {
4637
4855
  return finishConnect(info.routingHint, info.name, info.ingressEndpoint);
4638
4856
  }