tensorlake 0.5.37 → 0.5.40

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.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.37";
38
+ SDK_VERSION = "0.5.40";
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,360 +155,155 @@ 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)}`);
158
+ // src/native-sandbox.ts
159
+ function resolveRequire() {
160
+ if (typeof module !== "undefined" && module.exports != null && typeof require !== "undefined") {
161
+ return require;
187
162
  }
188
- console.error(parts.join(" "));
163
+ return (0, import_node_module.createRequire)(import_meta.url);
189
164
  }
190
- function formatSdkTimingValue(value) {
191
- if (typeof value === "number" || typeof value === "boolean") {
192
- return String(value);
165
+ function loadNativeSandboxBinding() {
166
+ if (cachedBinding) return cachedBinding;
167
+ if (cachedBindingError) throw cachedBindingError;
168
+ try {
169
+ const { loadNative } = resolveRequire()("../lib/runtime.cjs");
170
+ const binding = loadNative();
171
+ if (binding == null || typeof binding.NativeSandboxClient !== "function" || typeof binding.NativeSandboxProxyClient !== "function") {
172
+ throw new SandboxError(
173
+ "native binding does not export the sandbox clients; rebuild with 'npm run build:native'"
174
+ );
175
+ }
176
+ cachedBinding = binding;
177
+ return cachedBinding;
178
+ } catch (error) {
179
+ cachedBindingError = error instanceof Error ? error : new Error(String(error));
180
+ throw cachedBindingError;
193
181
  }
194
- if (/^[A-Za-z0-9_./:@-]+$/.test(value)) {
195
- return value;
182
+ }
183
+ function parseNativeError(error) {
184
+ const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
185
+ if (!message) return null;
186
+ try {
187
+ const parsed = JSON.parse(message);
188
+ if (parsed && typeof parsed.category === "string") {
189
+ return {
190
+ category: parsed.category,
191
+ status: typeof parsed.status === "number" ? parsed.status : null,
192
+ message: typeof parsed.message === "string" ? parsed.message : message
193
+ };
194
+ }
195
+ } catch {
196
196
  }
197
- return JSON.stringify(value);
197
+ return null;
198
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";
199
+ function translateNativeError(error, context) {
200
+ const payload = parseNativeError(error);
201
+ if (!payload) {
202
+ if (error instanceof Error) return error;
203
+ return new SandboxError(String(error));
205
204
  }
206
- });
207
-
208
- // src/http.ts
209
- function withTraceId(value, traceId) {
210
- if (value == null) {
211
- return { traceId };
205
+ const { category, status, message } = payload;
206
+ if (category === "connection") {
207
+ return new SandboxConnectionError(message, { cause: error });
212
208
  }
213
- return Object.assign(value, { traceId });
214
- }
215
- function hasHeader(headers, name) {
216
- const lowered = name.toLowerCase();
217
- return Object.keys(headers).some((key) => key.toLowerCase() === lowered);
218
- }
219
- function normalizeRequestBody(body) {
220
- if (body == null) {
221
- return void 0;
209
+ if (status === 404) {
210
+ if (context?.notFoundKind === "pool" && context.poolId) {
211
+ return new PoolNotFoundError(context.poolId);
212
+ }
213
+ if (context?.notFoundKind === "sandbox" && context.sandboxId) {
214
+ return new SandboxNotFoundError(context.sandboxId);
215
+ }
216
+ return new RemoteAPIError(404, message);
222
217
  }
223
- if (body instanceof Uint8Array) {
224
- return Uint8Array.from(body).buffer;
218
+ if (status === 409 && context?.poolId) {
219
+ return new PoolInUseError(context.poolId, message);
225
220
  }
226
- return body;
227
- }
228
- function stripPathPayload(path2) {
229
- const queryStart = path2.search(/[?#]/);
230
- return queryStart === -1 ? path2 : path2.slice(0, queryStart);
221
+ if (typeof status === "number") {
222
+ return new RemoteAPIError(status, message);
223
+ }
224
+ return new SandboxError(message);
231
225
  }
232
- function throwMappedError(status, body, path2) {
233
- let message = body;
226
+ async function callNative(fn, context) {
234
227
  try {
235
- const parsed = JSON.parse(body);
236
- if (parsed.message) message = parsed.message;
237
- else if (parsed.error) message = parsed.error;
238
- } catch {
228
+ return await fn();
229
+ } catch (error) {
230
+ throw translateNativeError(error, context);
239
231
  }
240
- if (status === 404) {
241
- if (path2.includes("sandbox-pools") || path2.includes("pools")) {
242
- const match = path2.match(/sandbox-pools\/([^/]+)/);
243
- if (match) throw new PoolNotFoundError(match[1]);
232
+ }
233
+ async function* nativeEventStream(start, context, signal) {
234
+ const queue = [];
235
+ let wake = null;
236
+ let finished = false;
237
+ let failure = null;
238
+ const notify = () => {
239
+ if (wake) {
240
+ const w = wake;
241
+ wake = null;
242
+ w();
244
243
  }
245
- if (path2.includes("sandboxes")) {
246
- const match = path2.match(/sandboxes\/([^/]+)/);
247
- if (match) throw new SandboxNotFoundError(match[1]);
244
+ };
245
+ const emit = (eventJson) => {
246
+ queue.push(eventJson);
247
+ notify();
248
+ };
249
+ const onAbort = () => notify();
250
+ signal?.addEventListener("abort", onAbort, { once: true });
251
+ const startPromise = start(emit).catch((error) => {
252
+ failure = error;
253
+ }).finally(() => {
254
+ finished = true;
255
+ notify();
256
+ });
257
+ try {
258
+ while (true) {
259
+ if (queue.length > 0) {
260
+ yield JSON.parse(queue.shift());
261
+ continue;
262
+ }
263
+ if (signal?.aborted) break;
264
+ if (finished) break;
265
+ await new Promise((resolve) => {
266
+ wake = resolve;
267
+ });
248
268
  }
249
- throw new RemoteAPIError(404, message);
250
- }
251
- if (status === 409) {
252
- if (path2.includes("sandbox-pools") || path2.includes("pools")) {
253
- const match = path2.match(/sandbox-pools\/([^/]+)/);
254
- if (match) throw new PoolInUseError(match[1], message);
269
+ if (failure != null) {
270
+ throw translateNativeError(failure, context);
255
271
  }
272
+ } finally {
273
+ signal?.removeEventListener("abort", onAbort);
274
+ void startPromise.catch(() => {
275
+ });
256
276
  }
257
- throw new RemoteAPIError(status, message);
258
277
  }
259
- function anySignal(signals) {
260
- const controller = new AbortController();
261
- for (const signal of signals) {
262
- if (signal.aborted) {
263
- controller.abort(signal.reason);
264
- return controller.signal;
278
+ function assembleCommandResult(events) {
279
+ const stdoutLines = [];
280
+ const stderrLines = [];
281
+ let exitCode = -1;
282
+ for (const eventJson of events) {
283
+ const raw = JSON.parse(eventJson);
284
+ if (typeof raw.line === "string") {
285
+ if (raw.stream === "stderr") {
286
+ stderrLines.push(raw.line);
287
+ } else {
288
+ stdoutLines.push(raw.line);
289
+ }
290
+ } else if ("exit_code" in raw || "signal" in raw) {
291
+ if (typeof raw.exit_code === "number") {
292
+ exitCode = raw.exit_code;
293
+ } else if (typeof raw.signal === "number") {
294
+ exitCode = -raw.signal;
295
+ }
265
296
  }
266
- signal.addEventListener("abort", () => controller.abort(signal.reason), {
267
- once: true
268
- });
269
297
  }
270
- return controller.signal;
271
- }
272
- function sleep(ms) {
273
- return new Promise((resolve) => setTimeout(resolve, ms));
298
+ return { exitCode, stdout: stdoutLines.join("\n"), stderr: stderrLines.join("\n") };
274
299
  }
275
- var import_undici, REQUEST_TIMEOUT_HEADER, HttpClient;
276
- var init_http = __esm({
277
- "src/http.ts"() {
300
+ var import_node_module, import_meta, cachedBinding, cachedBindingError;
301
+ var init_native_sandbox = __esm({
302
+ "src/native-sandbox.ts"() {
278
303
  "use strict";
279
- import_undici = require("undici");
280
- init_defaults();
304
+ import_node_module = require("module");
281
305
  init_errors();
282
- init_sdk_timings();
283
- REQUEST_TIMEOUT_HEADER = "X-Tensorlake-Request-Timeout-Ms";
284
- (0, import_undici.setGlobalDispatcher)(
285
- new import_undici.Agent({
286
- keepAliveTimeout: 6e4,
287
- allowH2: true
288
- })
289
- );
290
- HttpClient = class _HttpClient {
291
- baseUrl;
292
- headers;
293
- maxRetries;
294
- retryBackoffMs;
295
- timeoutMs;
296
- abortController = null;
297
- constructor(options) {
298
- const url = options.baseUrl;
299
- this.baseUrl = url.endsWith("/") ? url.slice(0, -1) : url;
300
- this.maxRetries = options.maxRetries ?? MAX_RETRIES;
301
- this.retryBackoffMs = options.retryBackoffMs ?? RETRY_BACKOFF_MS;
302
- this.timeoutMs = options.timeoutMs === void 0 ? DEFAULT_HTTP_TIMEOUT_MS : options.timeoutMs;
303
- this.headers = {
304
- "User-Agent": `tensorlake-typescript-sdk/${SDK_VERSION}`
305
- };
306
- if (this.timeoutMs != null) {
307
- this.headers[REQUEST_TIMEOUT_HEADER] = String(this.timeoutMs);
308
- }
309
- if (options.apiKey) {
310
- this.headers["Authorization"] = `Bearer ${options.apiKey}`;
311
- }
312
- if (options.organizationId) {
313
- this.headers["X-Forwarded-Organization-Id"] = options.organizationId;
314
- }
315
- if (options.projectId) {
316
- this.headers["X-Forwarded-Project-Id"] = options.projectId;
317
- }
318
- if (options.hostHeader) {
319
- this.headers["Host"] = options.hostHeader;
320
- }
321
- if (options.sandboxIdHeader) {
322
- this.headers["X-Tensorlake-Sandbox-Id"] = options.sandboxIdHeader;
323
- }
324
- if (options.routingHint) {
325
- this.headers["X-Tensorlake-Route-Hint"] = options.routingHint;
326
- }
327
- }
328
- close() {
329
- this.abortController?.abort();
330
- this.abortController = null;
331
- }
332
- /** Make a JSON request, returning the parsed response body. */
333
- async requestJson(method, path2, options) {
334
- const response = await this.requestResponse(method, path2, {
335
- json: options?.body,
336
- headers: options?.headers,
337
- signal: options?.signal,
338
- retryableStatusCodes: options?.retryableStatusCodes,
339
- allowedErrorStatusCodes: options?.allowedErrorStatusCodes,
340
- allowHttpErrors: options?.allowHttpErrors
341
- });
342
- const text = await response.text();
343
- const data = text ? JSON.parse(text) : void 0;
344
- return withTraceId(data, response.traceId);
345
- }
346
- /** Make a request returning raw bytes. */
347
- async requestBytes(method, path2, options) {
348
- const headers = { ...options?.headers ?? {} };
349
- if (options?.contentType) {
350
- headers["Content-Type"] = options.contentType;
351
- }
352
- const response = await this.requestResponse(method, path2, {
353
- body: options?.body,
354
- headers,
355
- signal: options?.signal
356
- });
357
- const buffer = await response.arrayBuffer();
358
- return withTraceId(new Uint8Array(buffer), response.traceId);
359
- }
360
- /** Make a request and return the response body as an SSE stream. */
361
- async requestStream(method, path2, options) {
362
- const response = await this.requestResponse(method, path2, {
363
- json: options?.json,
364
- headers: { Accept: "text/event-stream" },
365
- signal: options?.signal
366
- });
367
- if (!response.body) {
368
- throw new RemoteAPIError(
369
- response.status,
370
- "No response body for SSE stream"
371
- );
372
- }
373
- return withTraceId(response.body, response.traceId);
374
- }
375
- /** Make a request and return the raw Response. */
376
- async requestResponse(method, path2, options) {
377
- const headers = {
378
- ...this.headers,
379
- ...options?.headers ?? {}
380
- };
381
- const hasJsonBody = options?.json !== void 0;
382
- if (hasJsonBody && !hasHeader(headers, "Content-Type")) {
383
- headers["Content-Type"] = "application/json";
384
- }
385
- const body = hasJsonBody ? JSON.stringify(options?.json) : normalizeRequestBody(options?.body);
386
- const { response, traceId } = await this.doFetch(
387
- method,
388
- path2,
389
- body,
390
- headers,
391
- options?.signal,
392
- options?.allowHttpErrors ?? false,
393
- options?.retryableStatusCodes ?? RETRYABLE_STATUS_CODES,
394
- options?.allowedErrorStatusCodes ?? /* @__PURE__ */ new Set()
395
- );
396
- return withTraceId(response, traceId);
397
- }
398
- static makeTraceparent() {
399
- const randomHex = (bytes) => Array.from(crypto.getRandomValues(new Uint8Array(bytes))).map((b) => b.toString(16).padStart(2, "0")).join("");
400
- const traceId = randomHex(16);
401
- const spanId = randomHex(8);
402
- return { traceparent: `00-${traceId}-${spanId}-01`, traceId };
403
- }
404
- async doFetch(method, path2, body, headers, signal, allowHttpErrors = false, retryableStatusCodes = RETRYABLE_STATUS_CODES, allowedErrorStatusCodes = /* @__PURE__ */ new Set()) {
405
- const url = `${this.baseUrl}${path2}`;
406
- const { traceparent, traceId } = _HttpClient.makeTraceparent();
407
- headers["traceparent"] = traceparent;
408
- let lastError;
409
- const operationStart = nowMs();
410
- const timingPath = sdkTimingPayloadsEnabled() ? path2 : stripPathPayload(path2);
411
- for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
412
- if (attempt > 0) {
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
- });
421
- await sleep(delay);
422
- }
423
- this.abortController = new AbortController();
424
- let timedOut = false;
425
- const timeoutId = this.timeoutMs == null ? null : setTimeout(() => {
426
- timedOut = true;
427
- this.abortController?.abort();
428
- }, this.timeoutMs);
429
- const combinedSignal = signal ? anySignal([signal, this.abortController.signal]) : this.abortController.signal;
430
- const attemptStart = nowMs();
431
- try {
432
- const response = await (0, import_undici.fetch)(url, {
433
- method,
434
- headers,
435
- body,
436
- signal: combinedSignal
437
- });
438
- if (timeoutId != null) clearTimeout(timeoutId);
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
- }
456
- if (retryableStatusCodes.has(response.status) && attempt < this.maxRetries) {
457
- lastError = new RemoteAPIError(
458
- response.status,
459
- await response.text().catch(() => "")
460
- );
461
- logSdkTimingEvent("http.request", "retryable_status", {
462
- method,
463
- path: timingPath,
464
- status: response.status,
465
- attempt,
466
- server_trace_id: traceId
467
- });
468
- continue;
469
- }
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
- });
478
- return { response, traceId };
479
- }
480
- const errorBody = await response.text().catch(() => "");
481
- throwMappedError(response.status, errorBody, path2);
482
- } catch (err) {
483
- if (timeoutId != null) clearTimeout(timeoutId);
484
- if (err instanceof RemoteAPIError || err instanceof SandboxNotFoundError || err instanceof PoolNotFoundError || err instanceof PoolInUseError) {
485
- throw err;
486
- }
487
- if (signal?.aborted && !timedOut) {
488
- throw new SandboxConnectionError("request aborted by caller", {
489
- cause: err
490
- });
491
- }
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
- });
500
- if (attempt >= this.maxRetries) {
501
- const detail = timedOut ? `request timed out after ${this.timeoutMs}ms` : describeError(err);
502
- throw new SandboxConnectionError(detail, { cause: err });
503
- }
504
- }
505
- }
506
- throw new SandboxConnectionError(
507
- lastError ? describeError(lastError) : "request failed",
508
- { cause: lastError }
509
- );
510
- }
511
- };
306
+ import_meta = {};
512
307
  }
513
308
  });
514
309
 
@@ -3017,75 +2812,67 @@ var init_desktop = __esm({
3017
2812
  }
3018
2813
  });
3019
2814
 
3020
- // src/sse.ts
3021
- async function* parseSSEMessages(stream, signal) {
3022
- const reader = stream.getReader();
3023
- const decoder = new TextDecoder();
3024
- let buffer = "";
3025
- try {
3026
- while (true) {
3027
- if (signal?.aborted) break;
3028
- const { done, value } = await reader.read();
3029
- if (done) break;
3030
- buffer += decoder.decode(value, { stream: true });
3031
- const parts = buffer.split(/\r?\n\r?\n/);
3032
- buffer = parts.pop() ?? "";
3033
- for (const part of parts) {
3034
- const lines = part.split(/\r?\n/);
3035
- const dataLines = [];
3036
- let event;
3037
- let id;
3038
- for (const line of lines) {
3039
- if (!line || line.startsWith(":")) continue;
3040
- const separator = line.indexOf(":");
3041
- const field = separator === -1 ? line : line.slice(0, separator);
3042
- let value2 = separator === -1 ? "" : line.slice(separator + 1);
3043
- if (value2.startsWith(" ")) {
3044
- value2 = value2.slice(1);
3045
- }
3046
- if (field === "data") {
3047
- dataLines.push(value2);
3048
- } else if (field === "event") {
3049
- event = value2;
3050
- } else if (field === "id") {
3051
- id = value2;
3052
- }
3053
- }
3054
- if (dataLines.length > 0 || event || id) {
3055
- yield { data: dataLines.join("\n"), event, id };
3056
- }
3057
- }
3058
- }
3059
- } finally {
3060
- reader.releaseLock();
3061
- }
2815
+ // src/sdk-timings.ts
2816
+ function sdkTimingsEnabled() {
2817
+ const value = process.env[SDK_TIMINGS_ENV_VAR];
2818
+ return value === "1" || value === "true" || value === "yes";
3062
2819
  }
3063
- async function* parseSSEStream(stream, signal) {
3064
- for await (const message of parseSSEMessages(stream, signal)) {
3065
- if (!message.data) continue;
3066
- try {
3067
- yield JSON.parse(message.data);
3068
- } catch {
3069
- }
3070
- }
2820
+ function sdkTimingPayloadsEnabled() {
2821
+ const value = process.env[SDK_TIMING_PAYLOADS_ENV_VAR];
2822
+ return value === "1" || value === "true" || value === "yes";
3071
2823
  }
3072
- var init_sse = __esm({
3073
- "src/sse.ts"() {
3074
- "use strict";
3075
- }
3076
- });
3077
-
3078
- // src/url.ts
3079
- function trimTrailingSlashes(url) {
3080
- return url.endsWith("/") ? url.slice(0, -1) : url;
2824
+ function nowMs() {
2825
+ return performance.now();
3081
2826
  }
3082
- function isLocalhost(apiUrl) {
3083
- try {
3084
- const parsed = new URL(apiUrl);
3085
- return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
3086
- } catch {
3087
- return false;
3088
- }
2827
+ function logSdkTiming(op, phase, startedAt, fields = {}) {
2828
+ if (!sdkTimingsEnabled()) return;
2829
+ logSdkTimingEvent(op, phase, {
2830
+ ...fields,
2831
+ duration_ms: Math.round((nowMs() - startedAt) * 100) / 100
2832
+ });
2833
+ }
2834
+ function logSdkTimingEvent(op, phase, fields = {}) {
2835
+ if (!sdkTimingsEnabled()) return;
2836
+ const parts = [
2837
+ "[tensorlake:sdk-timing]",
2838
+ `op=${op}`,
2839
+ `phase=${phase}`
2840
+ ];
2841
+ for (const [key, value] of Object.entries(fields)) {
2842
+ if (value == null) continue;
2843
+ parts.push(`${key}=${formatSdkTimingValue(value)}`);
2844
+ }
2845
+ console.error(parts.join(" "));
2846
+ }
2847
+ function formatSdkTimingValue(value) {
2848
+ if (typeof value === "number" || typeof value === "boolean") {
2849
+ return String(value);
2850
+ }
2851
+ if (/^[A-Za-z0-9_./:@-]+$/.test(value)) {
2852
+ return value;
2853
+ }
2854
+ return JSON.stringify(value);
2855
+ }
2856
+ var SDK_TIMINGS_ENV_VAR, SDK_TIMING_PAYLOADS_ENV_VAR;
2857
+ var init_sdk_timings = __esm({
2858
+ "src/sdk-timings.ts"() {
2859
+ "use strict";
2860
+ SDK_TIMINGS_ENV_VAR = "TENSORLAKE_SDK_TIMINGS";
2861
+ SDK_TIMING_PAYLOADS_ENV_VAR = "TENSORLAKE_SDK_TIMING_PAYLOADS";
2862
+ }
2863
+ });
2864
+
2865
+ // src/url.ts
2866
+ function trimTrailingSlashes(url) {
2867
+ return url.endsWith("/") ? url.slice(0, -1) : url;
2868
+ }
2869
+ function isLocalhost(apiUrl) {
2870
+ try {
2871
+ const parsed = new URL(apiUrl);
2872
+ return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
2873
+ } catch {
2874
+ return false;
2875
+ }
3089
2876
  }
3090
2877
  function resolveProxyUrl(apiUrl) {
3091
2878
  const explicit = process.env.TENSORLAKE_SANDBOX_PROXY_URL;
@@ -3135,24 +2922,6 @@ function sandboxUrlFromIngressEndpoint(ingressEndpoint, sandboxId, port = 9501)
3135
2922
  const label = port === 9501 ? sandboxId : `${port}-${sandboxId}`;
3136
2923
  return `${parsed.protocol}//${label}.${parsed.host}`;
3137
2924
  }
3138
- function resolveSandboxLifecycleUrl(apiUrl) {
3139
- if (isLocalhost(apiUrl)) return apiUrl;
3140
- try {
3141
- const parsed = new URL(apiUrl);
3142
- if (parsed.hostname.startsWith("api.")) {
3143
- parsed.hostname = "sandbox." + parsed.hostname.slice(4);
3144
- return parsed.toString().replace(/\/$/, "");
3145
- }
3146
- } catch {
3147
- }
3148
- return apiUrl;
3149
- }
3150
- function lifecyclePath(path2, isLocal, namespace) {
3151
- if (isLocal) {
3152
- return `/v1/namespaces/${namespace}/${path2}`;
3153
- }
3154
- return `/${path2}`;
3155
- }
3156
2925
  var init_url = __esm({
3157
2926
  "src/url.ts"() {
3158
2927
  "use strict";
@@ -3163,7 +2932,7 @@ var init_url = __esm({
3163
2932
  // src/sandbox.ts
3164
2933
  function processUserPayload(user) {
3165
2934
  if (user == null) {
3166
- return DEFAULT_PROCESS_USER;
2935
+ return void 0;
3167
2936
  }
3168
2937
  if (typeof user === "string" && user.trim() === "") {
3169
2938
  throw new SandboxError("process user must not be empty");
@@ -3193,33 +2962,25 @@ function sendPtyFrame(socket, frame) {
3193
2962
  socket.send(frame, (error) => error ? reject(error) : resolve());
3194
2963
  });
3195
2964
  }
3196
- function secondsToMillis3(seconds) {
3197
- if (!Number.isFinite(seconds) || seconds <= 0) {
3198
- throw new SandboxError("requestTimeout must be a positive number of seconds");
3199
- }
3200
- return Math.ceil(seconds * 1e3);
3201
- }
3202
- var import_ws2, DEFAULT_PROCESS_USER, SandboxProxyConnection, PTY_OP_DATA, PTY_OP_RESIZE, PTY_OP_READY, PTY_OP_EXIT, Pty, Sandbox;
2965
+ var import_ws2, SandboxProxyConnection, PTY_OP_DATA, PTY_OP_RESIZE, PTY_OP_READY, PTY_OP_EXIT, Pty, Sandbox;
3203
2966
  var init_sandbox = __esm({
3204
2967
  "src/sandbox.ts"() {
3205
2968
  "use strict";
3206
2969
  init_desktop();
3207
2970
  init_defaults();
3208
2971
  init_errors();
3209
- init_http();
2972
+ init_native_sandbox();
3210
2973
  init_models();
3211
2974
  init_tunnel();
3212
- init_sse();
3213
2975
  init_sdk_timings();
3214
2976
  init_url();
3215
2977
  import_ws2 = __toESM(require("ws"), 1);
3216
- DEFAULT_PROCESS_USER = "tl-user";
3217
2978
  SandboxProxyConnection = class {
3218
2979
  constructor(sandbox, options) {
3219
2980
  this.sandbox = sandbox;
3220
2981
  this.options = options;
3221
2982
  this.routingHint = options.routingHint;
3222
- this.http = this.configureProxy(
2983
+ this.nativeProxy = this.configureProxy(
3223
2984
  options.proxyUrl ?? SANDBOX_PROXY_URL,
3224
2985
  options.sandboxId,
3225
2986
  options.routingHint
@@ -3228,7 +2989,7 @@ var init_sandbox = __esm({
3228
2989
  }
3229
2990
  baseUrl = "";
3230
2991
  wsHeaders = {};
3231
- http;
2992
+ nativeProxy;
3232
2993
  resolveProxyInfo;
3233
2994
  resolvePromise = null;
3234
2995
  routingHint;
@@ -3251,9 +3012,11 @@ var init_sandbox = __esm({
3251
3012
  this.sandbox._setName(info.name ?? null);
3252
3013
  this.routingHint = this.routingHint ?? info.routingHint;
3253
3014
  const proxyUrl = info.ingressEndpoint ?? this.options.proxyUrl ?? SANDBOX_PROXY_URL;
3254
- const nextHttp = this.configureProxy(proxyUrl, info.sandboxId, this.routingHint);
3255
- this.http.close();
3256
- this.http = nextHttp;
3015
+ this.nativeProxy = this.configureProxy(
3016
+ proxyUrl,
3017
+ info.sandboxId,
3018
+ this.routingHint
3019
+ );
3257
3020
  logSdkTiming("sandbox.proxy", "resolve_complete", resolveStart, {
3258
3021
  sandbox_id: info.sandboxId,
3259
3022
  server_trace_id: info.traceId,
@@ -3265,24 +3028,12 @@ var init_sandbox = __esm({
3265
3028
  });
3266
3029
  return this.resolvePromise;
3267
3030
  }
3268
- async requestJson(method, path2, options) {
3269
- await this.ensureResolved();
3270
- return this.http.requestJson(method, path2, options);
3271
- }
3272
- async requestBytes(method, path2, options) {
3031
+ /** Await proxy resolution and return the Rust-backed proxy client. */
3032
+ async client() {
3273
3033
  await this.ensureResolved();
3274
- return this.http.requestBytes(method, path2, options);
3275
- }
3276
- async requestStream(method, path2, options) {
3277
- await this.ensureResolved();
3278
- return this.http.requestStream(method, path2, options);
3279
- }
3280
- async requestResponse(method, path2, options) {
3281
- await this.ensureResolved();
3282
- return this.http.requestResponse(method, path2, options);
3034
+ return this.nativeProxy;
3283
3035
  }
3284
3036
  close() {
3285
- this.http.close();
3286
3037
  }
3287
3038
  configureProxy(proxyUrl, sandboxId, routingHint) {
3288
3039
  const { baseUrl, hostHeader, sandboxIdHeader } = resolveProxyTarget(
@@ -3306,16 +3057,34 @@ var init_sandbox = __esm({
3306
3057
  if (sandboxIdHeader) {
3307
3058
  this.wsHeaders["X-Tensorlake-Sandbox-Id"] = sandboxIdHeader;
3308
3059
  }
3309
- return new HttpClient({
3310
- baseUrl,
3311
- apiKey: this.options.apiKey,
3312
- organizationId: this.options.organizationId,
3313
- projectId: this.options.projectId,
3314
- hostHeader,
3315
- sandboxIdHeader,
3316
- routingHint,
3317
- timeoutMs: this.options.requestTimeout != null ? secondsToMillis3(this.options.requestTimeout) : this.options.timeoutMs ?? null
3318
- });
3060
+ if (this.options.nativeClient) {
3061
+ return this.options.nativeClient.connectProxy(
3062
+ proxyUrl,
3063
+ sandboxId,
3064
+ routingHint ?? null,
3065
+ this.proxyRequestTimeoutSec()
3066
+ );
3067
+ }
3068
+ const binding = loadNativeSandboxBinding();
3069
+ return new binding.NativeSandboxProxyClient(
3070
+ proxyUrl,
3071
+ sandboxId,
3072
+ this.options.apiKey ?? null,
3073
+ this.options.organizationId ?? null,
3074
+ this.options.projectId ?? null,
3075
+ routingHint ?? null,
3076
+ null,
3077
+ this.proxyRequestTimeoutSec()
3078
+ );
3079
+ }
3080
+ proxyRequestTimeoutSec() {
3081
+ if (this.options.requestTimeout != null) {
3082
+ return this.options.requestTimeout;
3083
+ }
3084
+ if (this.options.timeoutMs != null) {
3085
+ return this.options.timeoutMs / 1e3;
3086
+ }
3087
+ return null;
3319
3088
  }
3320
3089
  };
3321
3090
  PTY_OP_DATA = 0;
@@ -3484,7 +3253,6 @@ var init_sandbox = __esm({
3484
3253
  sandboxId;
3485
3254
  traceId = null;
3486
3255
  proxy;
3487
- http;
3488
3256
  ownsSandbox = false;
3489
3257
  lifecycleClient = null;
3490
3258
  lifecycleIdentifier;
@@ -3493,7 +3261,6 @@ var init_sandbox = __esm({
3493
3261
  this.sandboxId = options.sandboxId;
3494
3262
  this.lifecycleIdentifier = options.sandboxId;
3495
3263
  this.proxy = new SandboxProxyConnection(this, options);
3496
- this.http = this.proxy;
3497
3264
  }
3498
3265
  get baseUrl() {
3499
3266
  return this.proxy.baseUrl;
@@ -3698,9 +3465,9 @@ var init_sandbox = __esm({
3698
3465
  const filtered = all.filter((s) => s.sandboxId === this.lifecycleIdentifier);
3699
3466
  return Object.assign(filtered, { traceId: all.traceId });
3700
3467
  }
3701
- /** Close the HTTP client. The sandbox keeps running. */
3468
+ /** Close the proxy connection. The sandbox keeps running. */
3702
3469
  close() {
3703
- this.http.close();
3470
+ this.proxy.close();
3704
3471
  }
3705
3472
  /** Terminate the sandbox and release all resources. */
3706
3473
  async terminate() {
@@ -3727,51 +3494,18 @@ var init_sandbox = __esm({
3727
3494
  if (options?.workingDir) body.working_dir = options.workingDir;
3728
3495
  if (options?.timeout != null) body.timeout = options.timeout;
3729
3496
  const user = processUserPayload(options?.user);
3730
- body.user = user;
3497
+ if (user !== void 0) body.user = user;
3731
3498
  logSdkTimingEvent("sandbox.run", "start", {
3732
3499
  sandbox_id: this.sandboxId,
3733
3500
  command: sdkTimingPayloadsEnabled() ? command : void 0,
3734
3501
  command_length: command.length
3735
3502
  });
3736
- const requestStart = nowMs();
3737
- const sseStream = await this.http.requestStream(
3738
- "POST",
3739
- "/api/v1/processes/run",
3740
- { json: body }
3503
+ const proxy = await this.proxy.client();
3504
+ const { traceId, events } = await callNative(
3505
+ () => proxy.runProcess(JSON.stringify(body)),
3506
+ { sandboxId: this.sandboxId }
3741
3507
  );
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
- });
3749
- const stdoutLines = [];
3750
- const stderrLines = [];
3751
- let exitCode = -1;
3752
- const streamStart = nowMs();
3753
- for await (const raw of parseSSEStream(sseStream)) {
3754
- if (typeof raw.line === "string") {
3755
- if (raw.stream === "stderr") {
3756
- stderrLines.push(raw.line);
3757
- } else {
3758
- stdoutLines.push(raw.line);
3759
- }
3760
- } else if ("exit_code" in raw || "signal" in raw) {
3761
- if (typeof raw.exit_code === "number") {
3762
- exitCode = raw.exit_code;
3763
- } else if (typeof raw.signal === "number") {
3764
- exitCode = -raw.signal;
3765
- }
3766
- }
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
- });
3508
+ const { exitCode, stdout, stderr } = assembleCommandResult(events);
3775
3509
  logSdkTiming("sandbox.run", "complete", opStart, {
3776
3510
  sandbox_id: this.sandboxId,
3777
3511
  server_trace_id: traceId,
@@ -3779,10 +3513,7 @@ var init_sandbox = __esm({
3779
3513
  command_length: command.length,
3780
3514
  exit_code: exitCode
3781
3515
  });
3782
- return Object.assign(
3783
- { exitCode, stdout: stdoutLines.join("\n"), stderr: stderrLines.join("\n") },
3784
- { traceId }
3785
- );
3516
+ return Object.assign({ exitCode, stdout, stderr }, { traceId });
3786
3517
  }
3787
3518
  // --- Process management ---
3788
3519
  /**
@@ -3799,7 +3530,7 @@ var init_sandbox = __esm({
3799
3530
  if (options?.env != null) payload.env = options.env;
3800
3531
  if (options?.workingDir != null) payload.working_dir = options.workingDir;
3801
3532
  const user = processUserPayload(options?.user);
3802
- payload.user = user;
3533
+ if (user !== void 0) payload.user = user;
3803
3534
  if (options?.stdinMode != null && options.stdinMode !== "closed" /* CLOSED */) {
3804
3535
  payload.stdin_mode = options.stdinMode;
3805
3536
  }
@@ -3814,97 +3545,101 @@ var init_sandbox = __esm({
3814
3545
  if (options?.healthCheck != null) {
3815
3546
  payload.health_check = toSnakeKeys(options.healthCheck);
3816
3547
  }
3817
- const raw = await this.http.requestJson(
3818
- "POST",
3819
- "/api/v1/processes",
3820
- { body: payload }
3548
+ const proxy = await this.proxy.client();
3549
+ const { traceId, json } = await callNative(
3550
+ () => proxy.startProcess(JSON.stringify(payload)),
3551
+ { sandboxId: this.sandboxId }
3821
3552
  );
3822
- return Object.assign(fromSnakeKeys(raw), { traceId: raw.traceId });
3553
+ return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
3823
3554
  }
3824
3555
  /** List all processes (running and exited) tracked by the sandbox daemon. */
3825
3556
  async listProcesses() {
3826
- const raw = await this.http.requestJson(
3827
- "GET",
3828
- "/api/v1/processes"
3557
+ const proxy = await this.proxy.client();
3558
+ const { traceId, json } = await callNative(() => proxy.listProcesses(), {
3559
+ sandboxId: this.sandboxId
3560
+ });
3561
+ const parsed = JSON.parse(json);
3562
+ const processes = (parsed.processes ?? []).map(
3563
+ (p) => fromSnakeKeys(p)
3829
3564
  );
3830
- const processes = (raw.processes ?? []).map((p) => fromSnakeKeys(p));
3831
- return Object.assign(processes, { traceId: raw.traceId });
3565
+ return Object.assign(processes, { traceId });
3832
3566
  }
3833
3567
  /** Get current status and metadata for a process by PID. */
3834
3568
  async getProcess(pid) {
3835
- const raw = await this.http.requestJson(
3836
- "GET",
3837
- `/api/v1/processes/${pid}`
3838
- );
3839
- return Object.assign(fromSnakeKeys(raw), { traceId: raw.traceId });
3569
+ const proxy = await this.proxy.client();
3570
+ const { traceId, json } = await callNative(() => proxy.getProcess(pid), {
3571
+ sandboxId: this.sandboxId
3572
+ });
3573
+ return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
3840
3574
  }
3841
3575
  /** Send SIGKILL to a process. */
3842
3576
  async killProcess(pid) {
3843
- await this.http.requestJson("DELETE", `/api/v1/processes/${pid}`);
3577
+ const proxy = await this.proxy.client();
3578
+ await callNative(() => proxy.killProcess(pid), { sandboxId: this.sandboxId });
3844
3579
  }
3845
3580
  /** Restart a managed process by PID. */
3846
3581
  async restartProcess(pid) {
3847
- const raw = await this.http.requestJson(
3848
- "POST",
3849
- `/api/v1/processes/${pid}/restart`
3850
- );
3851
- return Object.assign(fromSnakeKeys(raw), { traceId: raw.traceId });
3582
+ const proxy = await this.proxy.client();
3583
+ const { traceId, json } = await callNative(() => proxy.restartProcess(pid), {
3584
+ sandboxId: this.sandboxId
3585
+ });
3586
+ return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
3852
3587
  }
3853
3588
  /** Send an arbitrary signal to a process (e.g. `15` for SIGTERM, `9` for SIGKILL). */
3854
3589
  async sendSignal(pid, signal) {
3855
- const raw = await this.http.requestJson(
3856
- "POST",
3857
- `/api/v1/processes/${pid}/signal`,
3858
- { body: { signal } }
3590
+ const proxy = await this.proxy.client();
3591
+ const { traceId, json } = await callNative(
3592
+ () => proxy.sendSignal(pid, signal),
3593
+ { sandboxId: this.sandboxId }
3859
3594
  );
3860
- return Object.assign(fromSnakeKeys(raw), { traceId: raw.traceId });
3595
+ return Object.assign(fromSnakeKeys(JSON.parse(json)), {
3596
+ traceId
3597
+ });
3861
3598
  }
3862
3599
  // --- Process I/O ---
3863
3600
  /** Write bytes to a process's stdin. The process must have been started with `stdinMode: StdinMode.PIPE`. */
3864
3601
  async writeStdin(pid, data) {
3865
- await this.http.requestBytes("POST", `/api/v1/processes/${pid}/stdin`, {
3866
- body: data,
3867
- contentType: "application/octet-stream"
3602
+ const proxy = await this.proxy.client();
3603
+ await callNative(() => proxy.writeStdin(pid, Buffer.from(data)), {
3604
+ sandboxId: this.sandboxId
3868
3605
  });
3869
3606
  }
3870
3607
  /** Close a process's stdin pipe, signalling EOF to the process. */
3871
3608
  async closeStdin(pid) {
3872
- await this.http.requestJson("POST", `/api/v1/processes/${pid}/stdin/close`);
3609
+ const proxy = await this.proxy.client();
3610
+ await callNative(() => proxy.closeStdin(pid), { sandboxId: this.sandboxId });
3873
3611
  }
3874
3612
  /** Return all captured stdout lines produced so far by a process. */
3875
3613
  async getStdout(pid) {
3876
- const raw = await this.http.requestJson(
3877
- "GET",
3878
- `/api/v1/processes/${pid}/stdout`
3879
- );
3880
- return Object.assign(fromSnakeKeys(raw), { traceId: raw.traceId });
3614
+ const proxy = await this.proxy.client();
3615
+ const { traceId, json } = await callNative(() => proxy.getStdout(pid), {
3616
+ sandboxId: this.sandboxId
3617
+ });
3618
+ return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
3881
3619
  }
3882
3620
  /** Return all captured stderr lines produced so far by a process. */
3883
3621
  async getStderr(pid) {
3884
- const raw = await this.http.requestJson(
3885
- "GET",
3886
- `/api/v1/processes/${pid}/stderr`
3887
- );
3888
- return Object.assign(fromSnakeKeys(raw), { traceId: raw.traceId });
3622
+ const proxy = await this.proxy.client();
3623
+ const { traceId, json } = await callNative(() => proxy.getStderr(pid), {
3624
+ sandboxId: this.sandboxId
3625
+ });
3626
+ return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
3889
3627
  }
3890
3628
  /** Return all captured stdout+stderr lines produced so far by a process. */
3891
3629
  async getOutput(pid) {
3892
- const raw = await this.http.requestJson(
3893
- "GET",
3894
- `/api/v1/processes/${pid}/output`
3895
- );
3896
- return Object.assign(fromSnakeKeys(raw), { traceId: raw.traceId });
3630
+ const proxy = await this.proxy.client();
3631
+ const { traceId, json } = await callNative(() => proxy.getOutput(pid), {
3632
+ sandboxId: this.sandboxId
3633
+ });
3634
+ return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
3897
3635
  }
3898
3636
  // --- Streaming (SSE) ---
3899
3637
  /** Stream stdout events from a process until it exits. Yields one `OutputEvent` per line. */
3900
3638
  async *followStdout(pid, options) {
3901
- const stream = await this.http.requestStream(
3902
- "GET",
3903
- `/api/v1/processes/${pid}/stdout/follow`,
3904
- options
3905
- );
3906
- for await (const raw of parseSSEStream(
3907
- stream,
3639
+ const proxy = await this.proxy.client();
3640
+ for await (const raw of nativeEventStream(
3641
+ (emit) => proxy.followStdout(pid, emit),
3642
+ { sandboxId: this.sandboxId },
3908
3643
  options?.signal
3909
3644
  )) {
3910
3645
  yield fromSnakeKeys(raw);
@@ -3912,13 +3647,10 @@ var init_sandbox = __esm({
3912
3647
  }
3913
3648
  /** Stream stderr events from a process until it exits. Yields one `OutputEvent` per line. */
3914
3649
  async *followStderr(pid, options) {
3915
- const stream = await this.http.requestStream(
3916
- "GET",
3917
- `/api/v1/processes/${pid}/stderr/follow`,
3918
- options
3919
- );
3920
- for await (const raw of parseSSEStream(
3921
- stream,
3650
+ const proxy = await this.proxy.client();
3651
+ for await (const raw of nativeEventStream(
3652
+ (emit) => proxy.followStderr(pid, emit),
3653
+ { sandboxId: this.sandboxId },
3922
3654
  options?.signal
3923
3655
  )) {
3924
3656
  yield fromSnakeKeys(raw);
@@ -3926,13 +3658,10 @@ var init_sandbox = __esm({
3926
3658
  }
3927
3659
  /** Stream combined stdout+stderr events from a process until it exits. Yields one `OutputEvent` per line. */
3928
3660
  async *followOutput(pid, options) {
3929
- const stream = await this.http.requestStream(
3930
- "GET",
3931
- `/api/v1/processes/${pid}/output/follow`,
3932
- options
3933
- );
3934
- for await (const raw of parseSSEStream(
3935
- stream,
3661
+ const proxy = await this.proxy.client();
3662
+ for await (const raw of nativeEventStream(
3663
+ (emit) => proxy.followOutput(pid, emit),
3664
+ { sandboxId: this.sandboxId },
3936
3665
  options?.signal
3937
3666
  )) {
3938
3667
  yield fromSnakeKeys(raw);
@@ -3941,33 +3670,33 @@ var init_sandbox = __esm({
3941
3670
  // --- File operations ---
3942
3671
  /** Read a file from the sandbox and return its raw bytes. */
3943
3672
  async readFile(path2) {
3944
- return this.http.requestBytes(
3945
- "GET",
3946
- `/api/v1/files?path=${encodeURIComponent(path2)}`
3947
- );
3673
+ const proxy = await this.proxy.client();
3674
+ const { traceId, data } = await callNative(() => proxy.readFile(path2), {
3675
+ sandboxId: this.sandboxId
3676
+ });
3677
+ return Object.assign(Uint8Array.from(data), { traceId });
3948
3678
  }
3949
3679
  /** Write raw bytes to a file in the sandbox, creating it if it does not exist. */
3950
3680
  async writeFile(path2, content) {
3951
- await this.http.requestBytes(
3952
- "PUT",
3953
- `/api/v1/files?path=${encodeURIComponent(path2)}`,
3954
- { body: content, contentType: "application/octet-stream" }
3955
- );
3681
+ const proxy = await this.proxy.client();
3682
+ await callNative(() => proxy.writeFile(path2, Buffer.from(content)), {
3683
+ sandboxId: this.sandboxId
3684
+ });
3956
3685
  }
3957
3686
  /** Delete a file from the sandbox. */
3958
3687
  async deleteFile(path2) {
3959
- await this.http.requestJson(
3960
- "DELETE",
3961
- `/api/v1/files?path=${encodeURIComponent(path2)}`
3962
- );
3688
+ const proxy = await this.proxy.client();
3689
+ await callNative(() => proxy.deleteFile(path2), { sandboxId: this.sandboxId });
3963
3690
  }
3964
3691
  /** List the contents of a directory in the sandbox. */
3965
3692
  async listDirectory(path2) {
3966
- const raw = await this.http.requestJson(
3967
- "GET",
3968
- `/api/v1/files/list?path=${encodeURIComponent(path2)}`
3969
- );
3970
- return Object.assign(fromSnakeKeys(raw), { traceId: raw.traceId });
3693
+ const proxy = await this.proxy.client();
3694
+ const { traceId, json } = await callNative(() => proxy.listDirectory(path2), {
3695
+ sandboxId: this.sandboxId
3696
+ });
3697
+ return Object.assign(fromSnakeKeys(JSON.parse(json)), {
3698
+ traceId
3699
+ });
3971
3700
  }
3972
3701
  // --- PTY ---
3973
3702
  /** Create an interactive PTY session. Returns a `sessionId` and `token` for WebSocket connection via `connectPty()`. */
@@ -3980,12 +3709,12 @@ var init_sandbox = __esm({
3980
3709
  if (options.args != null) payload.args = options.args;
3981
3710
  if (options.env != null) payload.env = options.env;
3982
3711
  if (options.workingDir != null) payload.working_dir = options.workingDir;
3983
- const raw = await this.http.requestJson(
3984
- "POST",
3985
- "/api/v1/pty",
3986
- { body: payload }
3712
+ const proxy = await this.proxy.client();
3713
+ const { traceId, json } = await callNative(
3714
+ () => proxy.createPtySession(JSON.stringify(payload)),
3715
+ { sandboxId: this.sandboxId }
3987
3716
  );
3988
- return Object.assign(fromSnakeKeys(raw), { traceId: raw.traceId });
3717
+ return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
3989
3718
  }
3990
3719
  /** Create a PTY session and connect to it immediately. Cleans up the session if the WebSocket connection fails. */
3991
3720
  async createPty(options) {
@@ -3995,7 +3724,8 @@ var init_sandbox = __esm({
3995
3724
  return await this.connectPty(session.sessionId, session.token, { onData, onExit });
3996
3725
  } catch (error) {
3997
3726
  try {
3998
- await this.http.requestResponse("DELETE", `/api/v1/pty/${session.sessionId}`);
3727
+ const proxy = await this.proxy.client();
3728
+ await proxy.deletePtySession(session.sessionId);
3999
3729
  } catch {
4000
3730
  }
4001
3731
  throw error;
@@ -4015,7 +3745,8 @@ var init_sandbox = __esm({
4015
3745
  "X-PTY-Token": authToken
4016
3746
  },
4017
3747
  killSession: async () => {
4018
- await this.http.requestResponse("DELETE", `/api/v1/pty/${sessionId}`);
3748
+ const proxy = await this.proxy.client();
3749
+ await proxy.deletePtySession(sessionId);
4019
3750
  }
4020
3751
  });
4021
3752
  if (options?.onData) {
@@ -4103,19 +3834,19 @@ var init_sandbox = __esm({
4103
3834
  // --- Health ---
4104
3835
  /** Check the sandbox daemon health. */
4105
3836
  async health() {
4106
- const raw = await this.http.requestJson(
4107
- "GET",
4108
- "/api/v1/health"
4109
- );
4110
- return Object.assign(fromSnakeKeys(raw), { traceId: raw.traceId });
3837
+ const proxy = await this.proxy.client();
3838
+ const { traceId, json } = await callNative(() => proxy.health(), {
3839
+ sandboxId: this.sandboxId
3840
+ });
3841
+ return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
4111
3842
  }
4112
3843
  /** Get sandbox daemon info (version, uptime, process counts). */
4113
3844
  async daemonInfo() {
4114
- const raw = await this.http.requestJson(
4115
- "GET",
4116
- "/api/v1/info"
4117
- );
4118
- return Object.assign(fromSnakeKeys(raw), { traceId: raw.traceId });
3845
+ const proxy = await this.proxy.client();
3846
+ const { traceId, json } = await callNative(() => proxy.info(), {
3847
+ sandboxId: this.sandboxId
3848
+ });
3849
+ return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
4119
3850
  }
4120
3851
  };
4121
3852
  }
@@ -4171,7 +3902,7 @@ async function readCloudInitBase64(cloudInit) {
4171
3902
  }
4172
3903
  function resolveRequestTimeoutMs(options) {
4173
3904
  if (options?.requestTimeout != null) {
4174
- return secondsToMillis4(options.requestTimeout);
3905
+ return secondsToMillis3(options.requestTimeout);
4175
3906
  }
4176
3907
  if (options?.timeoutMs != null) {
4177
3908
  validateTimeoutMs(options.timeoutMs);
@@ -4179,7 +3910,7 @@ function resolveRequestTimeoutMs(options) {
4179
3910
  }
4180
3911
  return DEFAULT_HTTP_TIMEOUT_MS;
4181
3912
  }
4182
- function secondsToMillis4(seconds) {
3913
+ function secondsToMillis3(seconds) {
4183
3914
  if (!Number.isFinite(seconds) || seconds <= 0) {
4184
3915
  throw new SandboxError("requestTimeout must be a positive number of seconds");
4185
3916
  }
@@ -4190,7 +3921,7 @@ function validateTimeoutMs(timeoutMs) {
4190
3921
  throw new SandboxError("timeoutMs must be a positive number of milliseconds");
4191
3922
  }
4192
3923
  }
4193
- function sleep2(ms) {
3924
+ function sleep(ms) {
4194
3925
  return new Promise((resolve) => setTimeout(resolve, ms));
4195
3926
  }
4196
3927
  function snapshotStatusSatisfiesWaitCondition(status, waitUntil) {
@@ -4246,32 +3977,26 @@ function validateUserPort(port) {
4246
3977
  function dedupeAndSortPorts(ports) {
4247
3978
  return [...new Set(ports)].sort((a, b) => a - b);
4248
3979
  }
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;
3980
+ var import_promises, MAX_CLOUD_INIT_USER_DATA_BYTES, SandboxClient, RESERVED_SANDBOX_MANAGEMENT_PORT;
4250
3981
  var init_client = __esm({
4251
3982
  "src/client.ts"() {
4252
3983
  "use strict";
4253
3984
  import_promises = require("fs/promises");
4254
3985
  init_defaults();
4255
3986
  init_errors();
4256
- init_http();
3987
+ init_native_sandbox();
4257
3988
  init_models();
4258
3989
  init_sandbox();
4259
3990
  init_sdk_timings();
4260
3991
  init_url();
4261
- CREATE_SANDBOX_RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([502, 503]);
4262
- CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES = /* @__PURE__ */ new Set([504]);
4263
- COPY_SANDBOX_ALLOWED_ERROR_STATUS_CODES = /* @__PURE__ */ new Set([422, 504]);
4264
3992
  MAX_CLOUD_INIT_USER_DATA_BYTES = 16 * 1024;
4265
3993
  SandboxClient = class _SandboxClient {
4266
- http;
3994
+ native;
4267
3995
  apiUrl;
4268
3996
  apiKey;
4269
3997
  organizationId;
4270
3998
  projectId;
4271
3999
  namespace;
4272
- local;
4273
- maxRetries;
4274
- retryBackoffMs;
4275
4000
  requestTimeoutMs;
4276
4001
  /** @internal Pass `true` to suppress the deprecation warning when used by `Sandbox.create()` / `Sandbox.connect()`. */
4277
4002
  constructor(options, _internal = false) {
@@ -4285,19 +4010,17 @@ var init_client = __esm({
4285
4010
  this.organizationId = options?.organizationId;
4286
4011
  this.projectId = options?.projectId;
4287
4012
  this.namespace = options?.namespace ?? NAMESPACE;
4288
- this.local = isLocalhost(this.apiUrl);
4289
- this.maxRetries = options?.maxRetries ?? MAX_RETRIES;
4290
- this.retryBackoffMs = options?.retryBackoffMs ?? RETRY_BACKOFF_MS;
4291
4013
  this.requestTimeoutMs = resolveRequestTimeoutMs(options);
4292
- this.http = new HttpClient({
4293
- baseUrl: resolveSandboxLifecycleUrl(this.apiUrl),
4294
- apiKey: this.apiKey,
4295
- organizationId: this.organizationId,
4296
- projectId: this.projectId,
4297
- maxRetries: this.maxRetries,
4298
- retryBackoffMs: this.retryBackoffMs,
4299
- timeoutMs: this.requestTimeoutMs
4300
- });
4014
+ const binding = loadNativeSandboxBinding();
4015
+ this.native = new binding.NativeSandboxClient(
4016
+ this.apiUrl,
4017
+ this.apiKey ?? null,
4018
+ this.organizationId ?? null,
4019
+ this.projectId ?? null,
4020
+ this.namespace,
4021
+ null,
4022
+ this.requestTimeoutMs / 1e3
4023
+ );
4301
4024
  }
4302
4025
  /** Create a client for the TensorLake cloud platform. */
4303
4026
  static forCloud(options) {
@@ -4320,13 +4043,12 @@ var init_client = __esm({
4320
4043
  });
4321
4044
  }
4322
4045
  close() {
4323
- this.http.close();
4324
4046
  }
4325
4047
  withRequestTimeout(requestTimeout) {
4326
4048
  if (requestTimeout == null) {
4327
4049
  return this;
4328
4050
  }
4329
- const timeoutMs = secondsToMillis4(requestTimeout);
4051
+ const timeoutMs = secondsToMillis3(requestTimeout);
4330
4052
  if (timeoutMs === this.requestTimeoutMs) {
4331
4053
  return this;
4332
4054
  }
@@ -4337,17 +4059,24 @@ var init_client = __esm({
4337
4059
  organizationId: this.organizationId,
4338
4060
  projectId: this.projectId,
4339
4061
  namespace: this.namespace,
4340
- maxRetries: this.maxRetries,
4341
- retryBackoffMs: this.retryBackoffMs,
4342
4062
  timeoutMs
4343
4063
  },
4344
4064
  /* _internal */
4345
4065
  true
4346
4066
  );
4347
4067
  }
4348
- // --- Path helper ---
4349
- path(subpath) {
4350
- return lifecyclePath(subpath, this.local, this.namespace);
4068
+ // --- Native marshalling helpers ---
4069
+ /** Run a native JSON call and reshape it into a `Traced<T>`. */
4070
+ async tracedJson(fn, idField, context) {
4071
+ const { traceId, json } = await callNative(fn, context);
4072
+ return Object.assign(fromSnakeKeys(JSON.parse(json), idField), {
4073
+ traceId
4074
+ });
4075
+ }
4076
+ /** Run a native JSON call and reshape it into a plain `T` (no trace id). */
4077
+ async plainJson(fn, idField, context) {
4078
+ const { json } = await callNative(fn, context);
4079
+ return fromSnakeKeys(JSON.parse(json), idField);
4351
4080
  }
4352
4081
  // --- Sandbox CRUD ---
4353
4082
  /** Create a new sandbox. Returns immediately; the sandbox may still be starting. Use `createAndConnect()` for a blocking, ready-to-use handle. */
@@ -4373,36 +4102,27 @@ var init_client = __esm({
4373
4102
  deny_out: options?.denyOut ?? []
4374
4103
  };
4375
4104
  }
4376
- const raw = await this.http.requestJson(
4377
- "POST",
4378
- this.path("sandboxes"),
4379
- {
4380
- body,
4381
- retryableStatusCodes: CREATE_SANDBOX_RETRYABLE_STATUS_CODES,
4382
- allowedErrorStatusCodes: CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES
4383
- }
4105
+ return this.tracedJson(
4106
+ () => this.native.createSandbox(JSON.stringify(body)),
4107
+ "sandboxId"
4384
4108
  );
4385
- const result = fromSnakeKeys(raw, "sandboxId");
4386
- return Object.assign(result, { traceId: raw.traceId });
4387
4109
  }
4388
4110
  /** Get current state and metadata for a sandbox by ID. */
4389
4111
  async get(sandboxId) {
4390
- const raw = await this.http.requestJson(
4391
- "GET",
4392
- this.path(`sandboxes/${sandboxId}`)
4112
+ return this.tracedJson(
4113
+ () => this.native.getSandbox(sandboxId),
4114
+ "sandboxId",
4115
+ { sandboxId, notFoundKind: "sandbox" }
4393
4116
  );
4394
- return Object.assign(fromSnakeKeys(raw, "sandboxId"), { traceId: raw.traceId });
4395
4117
  }
4396
4118
  /** List all sandboxes in the namespace. */
4397
4119
  async list() {
4398
- const raw = await this.http.requestJson(
4399
- "GET",
4400
- this.path("sandboxes")
4401
- );
4402
- const sandboxes = (raw.sandboxes ?? []).map(
4120
+ const { traceId, json } = await callNative(() => this.native.listSandboxes());
4121
+ const parsed = JSON.parse(json);
4122
+ const sandboxes = (parsed.sandboxes ?? []).map(
4403
4123
  (s) => fromSnakeKeys(s, "sandboxId")
4404
4124
  );
4405
- return Object.assign(sandboxes, { traceId: raw.traceId });
4125
+ return Object.assign(sandboxes, { traceId });
4406
4126
  }
4407
4127
  /**
4408
4128
  * List archived (terminated) sandboxes in the namespace.
@@ -4411,37 +4131,30 @@ var init_client = __esm({
4411
4131
  * archived sandboxes store until the server-configured TTL expires.
4412
4132
  */
4413
4133
  async listArchived(options) {
4414
- const query = [];
4415
- if (options?.limit != null) {
4416
- query.push(`limit=${encodeURIComponent(String(options.limit))}`);
4417
- }
4418
- if (options?.cursor != null) {
4419
- query.push(`cursor=${encodeURIComponent(options.cursor)}`);
4420
- }
4421
- if (options?.direction != null) {
4422
- query.push(`direction=${encodeURIComponent(options.direction)}`);
4423
- }
4424
- const suffix = query.length ? `?${query.join("&")}` : "";
4425
- const raw = await this.http.requestJson("GET", this.path(`archived-sandboxes${suffix}`));
4426
- const sandboxes = (raw.sandboxes ?? []).map(
4134
+ const { traceId, json } = await callNative(
4135
+ () => this.native.listArchivedSandboxes(
4136
+ options?.limit ?? null,
4137
+ options?.cursor ?? null,
4138
+ options?.direction ?? null
4139
+ )
4140
+ );
4141
+ const parsed = JSON.parse(json);
4142
+ const sandboxes = (parsed.sandboxes ?? []).map(
4427
4143
  (s) => fromSnakeKeys(s, "sandboxId")
4428
4144
  );
4429
4145
  const response = {
4430
4146
  sandboxes,
4431
- prevCursor: raw.prev_cursor,
4432
- nextCursor: raw.next_cursor
4147
+ prevCursor: parsed.prev_cursor,
4148
+ nextCursor: parsed.next_cursor
4433
4149
  };
4434
- return Object.assign(response, { traceId: raw.traceId });
4150
+ return Object.assign(response, { traceId });
4435
4151
  }
4436
4152
  /** Get a single archived sandbox by id. */
4437
4153
  async getArchived(sandboxId) {
4438
- const raw = await this.http.requestJson(
4439
- "GET",
4440
- this.path(`archived-sandboxes/${encodeURIComponent(sandboxId)}`)
4441
- );
4442
- return Object.assign(
4443
- fromSnakeKeys(raw, "sandboxId"),
4444
- { traceId: raw.traceId }
4154
+ return this.tracedJson(
4155
+ () => this.native.getArchivedSandbox(sandboxId),
4156
+ "sandboxId",
4157
+ { sandboxId, notFoundKind: "sandbox" }
4445
4158
  );
4446
4159
  }
4447
4160
  /** Update sandbox properties such as name, exposed ports, and proxy auth settings. */
@@ -4457,12 +4170,11 @@ var init_client = __esm({
4457
4170
  if (Object.keys(body).length === 0) {
4458
4171
  throw new SandboxError("At least one sandbox update field must be provided.");
4459
4172
  }
4460
- const raw = await this.http.requestJson(
4461
- "PATCH",
4462
- this.path(`sandboxes/${sandboxId}`),
4463
- { body }
4173
+ return this.tracedJson(
4174
+ () => this.native.updateSandbox(sandboxId, JSON.stringify(body)),
4175
+ "sandboxId",
4176
+ { sandboxId, notFoundKind: "sandbox" }
4464
4177
  );
4465
- return Object.assign(fromSnakeKeys(raw, "sandboxId"), { traceId: raw.traceId });
4466
4178
  }
4467
4179
  /** Get the current proxy port settings for a sandbox. */
4468
4180
  async getPortAccess(sandboxId) {
@@ -4500,10 +4212,7 @@ var init_client = __esm({
4500
4212
  }
4501
4213
  /** Terminate and delete a sandbox. */
4502
4214
  async delete(sandboxId) {
4503
- await this.http.requestJson(
4504
- "DELETE",
4505
- this.path(`sandboxes/${sandboxId}`)
4506
- );
4215
+ await callNative(() => this.native.deleteSandbox(sandboxId), { sandboxId, notFoundKind: "sandbox" });
4507
4216
  }
4508
4217
  /**
4509
4218
  * Suspend a named sandbox, preserving its state for later resume.
@@ -4512,18 +4221,9 @@ var init_client = __esm({
4512
4221
  * cannot. By default blocks until the sandbox is fully `Suspended`. Pass
4513
4222
  * `{ wait: false }` to return immediately after the request is sent
4514
4223
  * (fire-and-return); the server processes the suspend asynchronously.
4515
- *
4516
- * @param sandboxId - ID or name of the sandbox.
4517
- * @param options.wait - If `true` (default), poll until `Suspended`. Pass `false` to fire-and-return.
4518
- * @param options.timeout - Max seconds to wait when `wait=true` (default 300).
4519
- * @param options.pollInterval - Seconds between status polls when `wait=true` (default 1).
4520
- * @throws {SandboxError} If `wait=true` and the sandbox does not reach `Suspended` within `timeout`.
4521
4224
  */
4522
4225
  async suspend(sandboxId, options) {
4523
- await this.http.requestResponse(
4524
- "POST",
4525
- this.path(`sandboxes/${sandboxId}/suspend`)
4526
- );
4226
+ await callNative(() => this.native.suspendSandbox(sandboxId), { sandboxId, notFoundKind: "sandbox" });
4527
4227
  if (options?.wait === false) return;
4528
4228
  const timeout = options?.timeout ?? 300;
4529
4229
  const pollInterval = options?.pollInterval ?? 1;
@@ -4534,7 +4234,7 @@ var init_client = __esm({
4534
4234
  if (info.status === "terminated" /* TERMINATED */) {
4535
4235
  throw new SandboxError(`Sandbox ${sandboxId} terminated while waiting for suspend`);
4536
4236
  }
4537
- await sleep2(pollInterval * 1e3);
4237
+ await sleep(pollInterval * 1e3);
4538
4238
  }
4539
4239
  throw new SandboxError(`Sandbox ${sandboxId} did not suspend within ${timeout}s`);
4540
4240
  }
@@ -4544,18 +4244,9 @@ var init_client = __esm({
4544
4244
  * By default blocks until the sandbox is `Running` and routable. Pass
4545
4245
  * `{ wait: false }` to return immediately after the request is sent
4546
4246
  * (fire-and-return); the server processes the resume asynchronously.
4547
- *
4548
- * @param sandboxId - ID or name of the sandbox.
4549
- * @param options.wait - If `true` (default), poll until `Running`. Pass `false` to fire-and-return.
4550
- * @param options.timeout - Max seconds to wait when `wait=true` (default 300).
4551
- * @param options.pollInterval - Seconds between status polls when `wait=true` (default 1).
4552
- * @throws {SandboxError} If `wait=true` and the sandbox does not reach `Running` within `timeout`.
4553
4247
  */
4554
4248
  async resume(sandboxId, options) {
4555
- await this.http.requestResponse(
4556
- "POST",
4557
- this.path(`sandboxes/${sandboxId}/resume`)
4558
- );
4249
+ await callNative(() => this.native.resumeSandbox(sandboxId), { sandboxId, notFoundKind: "sandbox" });
4559
4250
  if (options?.wait === false) return;
4560
4251
  const timeout = options?.timeout ?? 300;
4561
4252
  const pollInterval = options?.pollInterval ?? 1;
@@ -4566,22 +4257,17 @@ var init_client = __esm({
4566
4257
  if (info.status === "terminated" /* TERMINATED */) {
4567
4258
  throw new SandboxError(`Sandbox ${sandboxId} terminated while waiting for resume`);
4568
4259
  }
4569
- await sleep2(pollInterval * 1e3);
4260
+ await sleep(pollInterval * 1e3);
4570
4261
  }
4571
4262
  throw new SandboxError(`Sandbox ${sandboxId} did not resume within ${timeout}s`);
4572
4263
  }
4573
4264
  /** Claim a warm sandbox from a pool, creating one if no warm containers are available. */
4574
4265
  async claim(poolId) {
4575
- const raw = await this.http.requestJson(
4576
- "POST",
4577
- this.path(`sandbox-pools/${poolId}/sandboxes`),
4578
- {
4579
- retryableStatusCodes: CREATE_SANDBOX_RETRYABLE_STATUS_CODES,
4580
- allowedErrorStatusCodes: CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES
4581
- }
4266
+ return this.tracedJson(
4267
+ () => this.native.claimSandbox(poolId),
4268
+ "sandboxId",
4269
+ { poolId, notFoundKind: "pool" }
4582
4270
  );
4583
- const result = fromSnakeKeys(raw, "sandboxId");
4584
- return Object.assign(result, { traceId: raw.traceId });
4585
4271
  }
4586
4272
  /**
4587
4273
  * Live-copy a running sandbox.
@@ -4596,13 +4282,11 @@ var init_client = __esm({
4596
4282
  throw new SandboxError("times must be a positive integer");
4597
4283
  }
4598
4284
  const client = this.withRequestTimeout(options?.requestTimeout);
4599
- const raw = await client.http.requestJson(
4600
- "POST",
4601
- client.path(`sandboxes/${sandboxId}/copy?times=${encodeURIComponent(String(times))}`),
4602
- { allowedErrorStatusCodes: COPY_SANDBOX_ALLOWED_ERROR_STATUS_CODES }
4285
+ return client.tracedJson(
4286
+ () => client.native.copySandbox(sandboxId, times),
4287
+ "sandboxId",
4288
+ { sandboxId, notFoundKind: "sandbox" }
4603
4289
  );
4604
- const result = fromSnakeKeys(raw, "sandboxId");
4605
- return Object.assign(result, { traceId: raw.traceId });
4606
4290
  }
4607
4291
  // --- Snapshots ---
4608
4292
  /**
@@ -4612,44 +4296,33 @@ var init_client = __esm({
4612
4296
  * status — the snapshot is created asynchronously. Poll `getSnapshot()` until
4613
4297
  * `local_ready`, `completed`, or `failed`, or use `snapshotAndWait()` to
4614
4298
  * block automatically.
4615
- *
4616
- * @param options.snapshotType - `"filesystem"` for cold-boot snapshots (e.g. image builds).
4617
- * Omit to use the server default (`filesystem`).
4618
4299
  */
4619
4300
  async snapshot(sandboxId, options) {
4620
- const requestOptions = options?.snapshotType != null ? { body: { snapshot_type: options.snapshotType } } : void 0;
4621
- const raw = await this.http.requestJson(
4622
- "POST",
4623
- this.path(`sandboxes/${sandboxId}/snapshot`),
4624
- requestOptions
4301
+ return this.plainJson(
4302
+ () => this.native.createSnapshot(sandboxId, options?.snapshotType ?? null),
4303
+ "snapshotId",
4304
+ { sandboxId, notFoundKind: "sandbox" }
4625
4305
  );
4626
- return fromSnakeKeys(raw, "snapshotId");
4627
4306
  }
4628
4307
  /** Get current status and metadata for a snapshot by ID. */
4629
4308
  async getSnapshot(snapshotId) {
4630
- const raw = await this.http.requestJson(
4631
- "GET",
4632
- this.path(`snapshots/${snapshotId}`)
4309
+ return this.tracedJson(
4310
+ () => this.native.getSnapshot(snapshotId),
4311
+ "snapshotId"
4633
4312
  );
4634
- return Object.assign(fromSnakeKeys(raw, "snapshotId"), { traceId: raw.traceId });
4635
4313
  }
4636
4314
  /** List all snapshots in the namespace. */
4637
4315
  async listSnapshots() {
4638
- const raw = await this.http.requestJson(
4639
- "GET",
4640
- this.path("snapshots")
4641
- );
4642
- const snapshots = (raw.snapshots ?? []).map(
4316
+ const { traceId, json } = await callNative(() => this.native.listSnapshots());
4317
+ const parsed = JSON.parse(json);
4318
+ const snapshots = (parsed.snapshots ?? []).map(
4643
4319
  (s) => fromSnakeKeys(s, "snapshotId")
4644
4320
  );
4645
- return Object.assign(snapshots, { traceId: raw.traceId });
4321
+ return Object.assign(snapshots, { traceId });
4646
4322
  }
4647
4323
  /** Delete a snapshot by ID. */
4648
4324
  async deleteSnapshot(snapshotId) {
4649
- await this.http.requestJson(
4650
- "DELETE",
4651
- this.path(`snapshots/${snapshotId}`)
4652
- );
4325
+ await callNative(() => this.native.deleteSnapshot(snapshotId));
4653
4326
  }
4654
4327
  /**
4655
4328
  * Create a snapshot and block until it is locally ready.
@@ -4657,14 +4330,6 @@ var init_client = __esm({
4657
4330
  * Combines `snapshot()` with polling `getSnapshot()` until `local_ready`
4658
4331
  * or `completed`. Pass `{ waitUntil: "completed" }` when durable
4659
4332
  * `snapshotUri` metadata is required.
4660
- * Prefer `sandbox.checkpoint()` on a `Sandbox` handle for the same behavior
4661
- * without managing the client separately.
4662
- *
4663
- * @param sandboxId - ID of the running sandbox to snapshot.
4664
- * @param options.timeout - Max seconds to wait (default 300).
4665
- * @param options.pollInterval - Seconds between status polls (default 1).
4666
- * @param options.snapshotType - Snapshot type passed through to `snapshot()`.
4667
- * @throws {SandboxError} If the snapshot fails or `timeout` elapses.
4668
4333
  */
4669
4334
  async snapshotAndWait(sandboxId, options) {
4670
4335
  const timeout = options?.timeout ?? 300;
@@ -4682,7 +4347,7 @@ var init_client = __esm({
4682
4347
  `Snapshot ${result.snapshotId} failed: ${info.error}`
4683
4348
  );
4684
4349
  }
4685
- await sleep2(pollInterval * 1e3);
4350
+ await sleep(pollInterval * 1e3);
4686
4351
  }
4687
4352
  throw new SandboxError(
4688
4353
  `Snapshot ${result.snapshotId} did not reach ${waitUntil} within ${timeout}s`
@@ -4703,31 +4368,27 @@ var init_client = __esm({
4703
4368
  if (options.entrypoint != null) body.entrypoint = options.entrypoint;
4704
4369
  if (options.maxContainers != null) body.max_containers = options.maxContainers;
4705
4370
  if (options.warmContainers != null) body.warm_containers = options.warmContainers;
4706
- const raw = await this.http.requestJson(
4707
- "POST",
4708
- this.path("sandbox-pools"),
4709
- { body }
4371
+ return this.plainJson(
4372
+ () => this.native.createPool(JSON.stringify(body)),
4373
+ "poolId"
4710
4374
  );
4711
- return fromSnakeKeys(raw, "poolId");
4712
4375
  }
4713
4376
  /** Get current state and metadata for a sandbox pool by ID. */
4714
4377
  async getPool(poolId) {
4715
- const raw = await this.http.requestJson(
4716
- "GET",
4717
- this.path(`sandbox-pools/${poolId}`)
4378
+ return this.plainJson(
4379
+ () => this.native.getPool(poolId),
4380
+ "poolId",
4381
+ { poolId, notFoundKind: "pool" }
4718
4382
  );
4719
- return fromSnakeKeys(raw, "poolId");
4720
4383
  }
4721
4384
  /** List all sandbox pools in the namespace. */
4722
4385
  async listPools() {
4723
- const raw = await this.http.requestJson(
4724
- "GET",
4725
- this.path("sandbox-pools")
4726
- );
4727
- const pools = (raw.pools ?? []).map(
4386
+ const { traceId, json } = await callNative(() => this.native.listPools());
4387
+ const parsed = JSON.parse(json);
4388
+ const pools = (parsed.pools ?? []).map(
4728
4389
  (p) => fromSnakeKeys(p, "poolId")
4729
4390
  );
4730
- return Object.assign(pools, { traceId: raw.traceId });
4391
+ return Object.assign(pools, { traceId });
4731
4392
  }
4732
4393
  /** Replace the configuration of an existing sandbox pool. */
4733
4394
  async updatePool(poolId, options) {
@@ -4743,19 +4404,15 @@ var init_client = __esm({
4743
4404
  if (options.entrypoint != null) body.entrypoint = options.entrypoint;
4744
4405
  if (options.maxContainers != null) body.max_containers = options.maxContainers;
4745
4406
  if (options.warmContainers != null) body.warm_containers = options.warmContainers;
4746
- const raw = await this.http.requestJson(
4747
- "PUT",
4748
- this.path(`sandbox-pools/${poolId}`),
4749
- { body }
4407
+ return this.plainJson(
4408
+ () => this.native.updatePool(poolId, JSON.stringify(body)),
4409
+ "poolId",
4410
+ { poolId, notFoundKind: "pool" }
4750
4411
  );
4751
- return fromSnakeKeys(raw, "poolId");
4752
4412
  }
4753
4413
  /** Delete a sandbox pool. Fails if the pool has active containers. */
4754
4414
  async deletePool(poolId) {
4755
- await this.http.requestJson(
4756
- "DELETE",
4757
- this.path(`sandbox-pools/${poolId}`)
4758
- );
4415
+ await callNative(() => this.native.deletePool(poolId), { poolId, notFoundKind: "pool" });
4759
4416
  }
4760
4417
  // --- Connect ---
4761
4418
  /** Return a `Sandbox` handle for an existing running sandbox without verifying it exists. */
@@ -4769,7 +4426,8 @@ var init_client = __esm({
4769
4426
  projectId: this.projectId,
4770
4427
  routingHint,
4771
4428
  resolveProxyInfo: proxyUrl == null ? async (currentIdentifier) => this.get(currentIdentifier) : void 0,
4772
- requestTimeout
4429
+ requestTimeout,
4430
+ nativeClient: this.native
4773
4431
  });
4774
4432
  }
4775
4433
  /**
@@ -4777,10 +4435,6 @@ var init_client = __esm({
4777
4435
  *
4778
4436
  * Blocks until the sandbox is ready or `requestTimeout` elapses. The returned
4779
4437
  * `Sandbox` auto-terminates when `terminate()` is called.
4780
- *
4781
- * @param options.requestTimeout - Max seconds to wait for `Running` status (default: client requestTimeout).
4782
- * @param options.startupTimeout - Deprecated alias for `requestTimeout`.
4783
- * @throws {SandboxError} If the sandbox terminates during startup or the timeout elapses.
4784
4438
  */
4785
4439
  async createAndConnect(options) {
4786
4440
  if (options?.poolId != null && options?.cloudInit != null) {
@@ -4842,7 +4496,7 @@ var init_client = __esm({
4842
4496
  `Sandbox ${result.sandboxId} did not start within ${requestTimeout}s`
4843
4497
  );
4844
4498
  }
4845
- const deadline = Date.now() + secondsToMillis4(requestTimeout);
4499
+ const deadline = Date.now() + secondsToMillis3(requestTimeout);
4846
4500
  while (Date.now() < deadline) {
4847
4501
  const pollStart = nowMs();
4848
4502
  const info = await requestClient.get(result.sandboxId);
@@ -4862,7 +4516,7 @@ var init_client = __esm({
4862
4516
  })
4863
4517
  );
4864
4518
  }
4865
- await sleep2(500);
4519
+ await sleep(500);
4866
4520
  }
4867
4521
  try {
4868
4522
  await requestClient.delete(result.sandboxId);
@@ -5008,17 +4662,17 @@ __export(sandbox_image_exports, {
5008
4662
  createSandboxImage: () => createSandboxImage,
5009
4663
  runCreateSandboxImageCli: () => runCreateSandboxImageCli
5010
4664
  });
5011
- function resolveRequire() {
4665
+ function resolveRequire2() {
5012
4666
  if (typeof module !== "undefined" && module.exports != null && typeof require !== "undefined") {
5013
4667
  return require;
5014
4668
  }
5015
- return (0, import_node_module.createRequire)(import_meta.url);
4669
+ return (0, import_node_module2.createRequire)(import_meta2.url);
5016
4670
  }
5017
4671
  function loadNativeBinding() {
5018
4672
  if (cachedNativeBinding) return cachedNativeBinding;
5019
4673
  if (cachedNativeBindingError) throw cachedNativeBindingError;
5020
4674
  try {
5021
- const { loadNative } = resolveRequire()("../lib/runtime.cjs");
4675
+ const { loadNative } = resolveRequire2()("../lib/runtime.cjs");
5022
4676
  cachedNativeBinding = loadNative();
5023
4677
  return cachedNativeBinding;
5024
4678
  } catch (error) {
@@ -5216,16 +4870,16 @@ async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
5216
4870
  { emit: ndjsonStdoutEmit }
5217
4871
  );
5218
4872
  }
5219
- var import_node_module, import_node_path, import_node_fs, import_node_util, import_meta, cachedNativeBinding, cachedNativeBindingError, DEFAULT_IMAGE_NAME2;
4873
+ var import_node_module2, import_node_path, import_node_fs, import_node_util, import_meta2, cachedNativeBinding, cachedNativeBindingError, DEFAULT_IMAGE_NAME2;
5220
4874
  var init_sandbox_image = __esm({
5221
4875
  "src/sandbox-image.ts"() {
5222
4876
  "use strict";
5223
- import_node_module = require("module");
4877
+ import_node_module2 = require("module");
5224
4878
  import_node_path = __toESM(require("path"), 1);
5225
4879
  import_node_fs = require("fs");
5226
4880
  import_node_util = require("util");
5227
4881
  init_image();
5228
- import_meta = {};
4882
+ import_meta2 = {};
5229
4883
  DEFAULT_IMAGE_NAME2 = "default";
5230
4884
  }
5231
4885
  });
@@ -5271,9 +4925,365 @@ init_tunnel();
5271
4925
 
5272
4926
  // src/cloud-client.ts
5273
4927
  init_defaults();
5274
- init_http();
4928
+
4929
+ // src/http.ts
4930
+ var import_undici = require("undici");
4931
+ init_defaults();
4932
+ init_errors();
4933
+ init_sdk_timings();
4934
+ var REQUEST_TIMEOUT_HEADER = "X-Tensorlake-Request-Timeout-Ms";
4935
+ (0, import_undici.setGlobalDispatcher)(
4936
+ new import_undici.Agent({
4937
+ keepAliveTimeout: 6e4,
4938
+ allowH2: true
4939
+ })
4940
+ );
4941
+ function withTraceId(value, traceId) {
4942
+ if (value == null) {
4943
+ return { traceId };
4944
+ }
4945
+ return Object.assign(value, { traceId });
4946
+ }
4947
+ var HttpClient = class _HttpClient {
4948
+ baseUrl;
4949
+ headers;
4950
+ maxRetries;
4951
+ retryBackoffMs;
4952
+ timeoutMs;
4953
+ abortController = null;
4954
+ constructor(options) {
4955
+ const url = options.baseUrl;
4956
+ this.baseUrl = url.endsWith("/") ? url.slice(0, -1) : url;
4957
+ this.maxRetries = options.maxRetries ?? MAX_RETRIES;
4958
+ this.retryBackoffMs = options.retryBackoffMs ?? RETRY_BACKOFF_MS;
4959
+ this.timeoutMs = options.timeoutMs === void 0 ? DEFAULT_HTTP_TIMEOUT_MS : options.timeoutMs;
4960
+ this.headers = {
4961
+ "User-Agent": `tensorlake-typescript-sdk/${SDK_VERSION}`
4962
+ };
4963
+ if (this.timeoutMs != null) {
4964
+ this.headers[REQUEST_TIMEOUT_HEADER] = String(this.timeoutMs);
4965
+ }
4966
+ if (options.apiKey) {
4967
+ this.headers["Authorization"] = `Bearer ${options.apiKey}`;
4968
+ }
4969
+ if (options.organizationId) {
4970
+ this.headers["X-Forwarded-Organization-Id"] = options.organizationId;
4971
+ }
4972
+ if (options.projectId) {
4973
+ this.headers["X-Forwarded-Project-Id"] = options.projectId;
4974
+ }
4975
+ if (options.hostHeader) {
4976
+ this.headers["Host"] = options.hostHeader;
4977
+ }
4978
+ if (options.sandboxIdHeader) {
4979
+ this.headers["X-Tensorlake-Sandbox-Id"] = options.sandboxIdHeader;
4980
+ }
4981
+ if (options.routingHint) {
4982
+ this.headers["X-Tensorlake-Route-Hint"] = options.routingHint;
4983
+ }
4984
+ }
4985
+ close() {
4986
+ this.abortController?.abort();
4987
+ this.abortController = null;
4988
+ }
4989
+ /** Make a JSON request, returning the parsed response body. */
4990
+ async requestJson(method, path2, options) {
4991
+ const response = await this.requestResponse(method, path2, {
4992
+ json: options?.body,
4993
+ headers: options?.headers,
4994
+ signal: options?.signal,
4995
+ retryableStatusCodes: options?.retryableStatusCodes,
4996
+ allowedErrorStatusCodes: options?.allowedErrorStatusCodes,
4997
+ allowHttpErrors: options?.allowHttpErrors
4998
+ });
4999
+ const text = await response.text();
5000
+ const data = text ? JSON.parse(text) : void 0;
5001
+ return withTraceId(data, response.traceId);
5002
+ }
5003
+ /** Make a request returning raw bytes. */
5004
+ async requestBytes(method, path2, options) {
5005
+ const headers = { ...options?.headers ?? {} };
5006
+ if (options?.contentType) {
5007
+ headers["Content-Type"] = options.contentType;
5008
+ }
5009
+ const response = await this.requestResponse(method, path2, {
5010
+ body: options?.body,
5011
+ headers,
5012
+ signal: options?.signal
5013
+ });
5014
+ const buffer = await response.arrayBuffer();
5015
+ return withTraceId(new Uint8Array(buffer), response.traceId);
5016
+ }
5017
+ /** Make a request and return the response body as an SSE stream. */
5018
+ async requestStream(method, path2, options) {
5019
+ const response = await this.requestResponse(method, path2, {
5020
+ json: options?.json,
5021
+ headers: { Accept: "text/event-stream" },
5022
+ signal: options?.signal
5023
+ });
5024
+ if (!response.body) {
5025
+ throw new RemoteAPIError(
5026
+ response.status,
5027
+ "No response body for SSE stream"
5028
+ );
5029
+ }
5030
+ return withTraceId(response.body, response.traceId);
5031
+ }
5032
+ /** Make a request and return the raw Response. */
5033
+ async requestResponse(method, path2, options) {
5034
+ const headers = {
5035
+ ...this.headers,
5036
+ ...options?.headers ?? {}
5037
+ };
5038
+ const hasJsonBody = options?.json !== void 0;
5039
+ if (hasJsonBody && !hasHeader(headers, "Content-Type")) {
5040
+ headers["Content-Type"] = "application/json";
5041
+ }
5042
+ const body = hasJsonBody ? JSON.stringify(options?.json) : normalizeRequestBody(options?.body);
5043
+ const { response, traceId } = await this.doFetch(
5044
+ method,
5045
+ path2,
5046
+ body,
5047
+ headers,
5048
+ options?.signal,
5049
+ options?.allowHttpErrors ?? false,
5050
+ options?.retryableStatusCodes ?? RETRYABLE_STATUS_CODES,
5051
+ options?.allowedErrorStatusCodes ?? /* @__PURE__ */ new Set()
5052
+ );
5053
+ return withTraceId(response, traceId);
5054
+ }
5055
+ static makeTraceparent() {
5056
+ const randomHex = (bytes) => Array.from(crypto.getRandomValues(new Uint8Array(bytes))).map((b) => b.toString(16).padStart(2, "0")).join("");
5057
+ const traceId = randomHex(16);
5058
+ const spanId = randomHex(8);
5059
+ return { traceparent: `00-${traceId}-${spanId}-01`, traceId };
5060
+ }
5061
+ async doFetch(method, path2, body, headers, signal, allowHttpErrors = false, retryableStatusCodes = RETRYABLE_STATUS_CODES, allowedErrorStatusCodes = /* @__PURE__ */ new Set()) {
5062
+ const url = `${this.baseUrl}${path2}`;
5063
+ const { traceparent, traceId } = _HttpClient.makeTraceparent();
5064
+ headers["traceparent"] = traceparent;
5065
+ let lastError;
5066
+ const operationStart = nowMs();
5067
+ const timingPath = sdkTimingPayloadsEnabled() ? path2 : stripPathPayload(path2);
5068
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
5069
+ if (attempt > 0) {
5070
+ const delay = this.retryBackoffMs * Math.pow(2, attempt - 1);
5071
+ logSdkTimingEvent("http.request", "retry_sleep_start", {
5072
+ method,
5073
+ path: timingPath,
5074
+ attempt,
5075
+ delay_ms: delay,
5076
+ server_trace_id: traceId
5077
+ });
5078
+ await sleep2(delay);
5079
+ }
5080
+ this.abortController = new AbortController();
5081
+ let timedOut = false;
5082
+ const timeoutId = this.timeoutMs == null ? null : setTimeout(() => {
5083
+ timedOut = true;
5084
+ this.abortController?.abort();
5085
+ }, this.timeoutMs);
5086
+ const combinedSignal = signal ? anySignal([signal, this.abortController.signal]) : this.abortController.signal;
5087
+ const attemptStart = nowMs();
5088
+ try {
5089
+ const response = await (0, import_undici.fetch)(url, {
5090
+ method,
5091
+ headers,
5092
+ body,
5093
+ signal: combinedSignal
5094
+ });
5095
+ if (timeoutId != null) clearTimeout(timeoutId);
5096
+ logSdkTiming("http.request", "headers", attemptStart, {
5097
+ method,
5098
+ path: timingPath,
5099
+ status: response.status,
5100
+ attempt,
5101
+ server_trace_id: traceId
5102
+ });
5103
+ if (response.ok) {
5104
+ logSdkTiming("http.request", "complete", operationStart, {
5105
+ method,
5106
+ path: timingPath,
5107
+ status: response.status,
5108
+ attempts: attempt + 1,
5109
+ server_trace_id: traceId
5110
+ });
5111
+ return { response, traceId };
5112
+ }
5113
+ if (retryableStatusCodes.has(response.status) && attempt < this.maxRetries) {
5114
+ lastError = new RemoteAPIError(
5115
+ response.status,
5116
+ await response.text().catch(() => "")
5117
+ );
5118
+ logSdkTimingEvent("http.request", "retryable_status", {
5119
+ method,
5120
+ path: timingPath,
5121
+ status: response.status,
5122
+ attempt,
5123
+ server_trace_id: traceId
5124
+ });
5125
+ continue;
5126
+ }
5127
+ if (allowHttpErrors || allowedErrorStatusCodes.has(response.status)) {
5128
+ logSdkTiming("http.request", "complete", operationStart, {
5129
+ method,
5130
+ path: timingPath,
5131
+ status: response.status,
5132
+ attempts: attempt + 1,
5133
+ server_trace_id: traceId
5134
+ });
5135
+ return { response, traceId };
5136
+ }
5137
+ const errorBody = await response.text().catch(() => "");
5138
+ throwMappedError(response.status, errorBody, path2);
5139
+ } catch (err) {
5140
+ if (timeoutId != null) clearTimeout(timeoutId);
5141
+ if (err instanceof RemoteAPIError || err instanceof SandboxNotFoundError || err instanceof PoolNotFoundError || err instanceof PoolInUseError) {
5142
+ throw err;
5143
+ }
5144
+ if (signal?.aborted && !timedOut) {
5145
+ throw new SandboxConnectionError("request aborted by caller", {
5146
+ cause: err
5147
+ });
5148
+ }
5149
+ lastError = err instanceof Error ? err : new Error(String(err));
5150
+ logSdkTiming("http.request", "error", attemptStart, {
5151
+ method,
5152
+ path: timingPath,
5153
+ attempt,
5154
+ server_trace_id: traceId,
5155
+ error: timedOut ? `request timed out after ${this.timeoutMs}ms` : describeError(err)
5156
+ });
5157
+ if (attempt >= this.maxRetries) {
5158
+ const detail = timedOut ? `request timed out after ${this.timeoutMs}ms` : describeError(err);
5159
+ throw new SandboxConnectionError(detail, { cause: err });
5160
+ }
5161
+ }
5162
+ }
5163
+ throw new SandboxConnectionError(
5164
+ lastError ? describeError(lastError) : "request failed",
5165
+ { cause: lastError }
5166
+ );
5167
+ }
5168
+ };
5169
+ function hasHeader(headers, name) {
5170
+ const lowered = name.toLowerCase();
5171
+ return Object.keys(headers).some((key) => key.toLowerCase() === lowered);
5172
+ }
5173
+ function normalizeRequestBody(body) {
5174
+ if (body == null) {
5175
+ return void 0;
5176
+ }
5177
+ if (body instanceof Uint8Array) {
5178
+ return Uint8Array.from(body).buffer;
5179
+ }
5180
+ return body;
5181
+ }
5182
+ function stripPathPayload(path2) {
5183
+ const queryStart = path2.search(/[?#]/);
5184
+ return queryStart === -1 ? path2 : path2.slice(0, queryStart);
5185
+ }
5186
+ function throwMappedError(status, body, path2) {
5187
+ let message = body;
5188
+ try {
5189
+ const parsed = JSON.parse(body);
5190
+ if (parsed.message) message = parsed.message;
5191
+ else if (parsed.error) message = parsed.error;
5192
+ } catch {
5193
+ }
5194
+ if (status === 404) {
5195
+ if (path2.includes("sandbox-pools") || path2.includes("pools")) {
5196
+ const match = path2.match(/sandbox-pools\/([^/]+)/);
5197
+ if (match) throw new PoolNotFoundError(match[1]);
5198
+ }
5199
+ if (path2.includes("sandboxes")) {
5200
+ const match = path2.match(/sandboxes\/([^/]+)/);
5201
+ if (match) throw new SandboxNotFoundError(match[1]);
5202
+ }
5203
+ throw new RemoteAPIError(404, message);
5204
+ }
5205
+ if (status === 409) {
5206
+ if (path2.includes("sandbox-pools") || path2.includes("pools")) {
5207
+ const match = path2.match(/sandbox-pools\/([^/]+)/);
5208
+ if (match) throw new PoolInUseError(match[1], message);
5209
+ }
5210
+ }
5211
+ throw new RemoteAPIError(status, message);
5212
+ }
5213
+ function anySignal(signals) {
5214
+ const controller = new AbortController();
5215
+ for (const signal of signals) {
5216
+ if (signal.aborted) {
5217
+ controller.abort(signal.reason);
5218
+ return controller.signal;
5219
+ }
5220
+ signal.addEventListener("abort", () => controller.abort(signal.reason), {
5221
+ once: true
5222
+ });
5223
+ }
5224
+ return controller.signal;
5225
+ }
5226
+ function sleep2(ms) {
5227
+ return new Promise((resolve) => setTimeout(resolve, ms));
5228
+ }
5229
+
5230
+ // src/cloud-client.ts
5275
5231
  init_models();
5276
- init_sse();
5232
+
5233
+ // src/sse.ts
5234
+ async function* parseSSEMessages(stream, signal) {
5235
+ const reader = stream.getReader();
5236
+ const decoder = new TextDecoder();
5237
+ let buffer = "";
5238
+ try {
5239
+ while (true) {
5240
+ if (signal?.aborted) break;
5241
+ const { done, value } = await reader.read();
5242
+ if (done) break;
5243
+ buffer += decoder.decode(value, { stream: true });
5244
+ const parts = buffer.split(/\r?\n\r?\n/);
5245
+ buffer = parts.pop() ?? "";
5246
+ for (const part of parts) {
5247
+ const lines = part.split(/\r?\n/);
5248
+ const dataLines = [];
5249
+ let event;
5250
+ let id;
5251
+ for (const line of lines) {
5252
+ if (!line || line.startsWith(":")) continue;
5253
+ const separator = line.indexOf(":");
5254
+ const field = separator === -1 ? line : line.slice(0, separator);
5255
+ let value2 = separator === -1 ? "" : line.slice(separator + 1);
5256
+ if (value2.startsWith(" ")) {
5257
+ value2 = value2.slice(1);
5258
+ }
5259
+ if (field === "data") {
5260
+ dataLines.push(value2);
5261
+ } else if (field === "event") {
5262
+ event = value2;
5263
+ } else if (field === "id") {
5264
+ id = value2;
5265
+ }
5266
+ }
5267
+ if (dataLines.length > 0 || event || id) {
5268
+ yield { data: dataLines.join("\n"), event, id };
5269
+ }
5270
+ }
5271
+ }
5272
+ } finally {
5273
+ reader.releaseLock();
5274
+ }
5275
+ }
5276
+ async function* parseSSEStream(stream, signal) {
5277
+ for await (const message of parseSSEMessages(stream, signal)) {
5278
+ if (!message.data) continue;
5279
+ try {
5280
+ yield JSON.parse(message.data);
5281
+ } catch {
5282
+ }
5283
+ }
5284
+ }
5285
+
5286
+ // src/cloud-client.ts
5277
5287
  var CloudClient = class _CloudClient {
5278
5288
  http;
5279
5289
  organizationId;