tensorlake 0.5.38 → 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/bin/darwin-arm64/tensorlake +0 -0
- package/dist/bin/darwin-arm64/tl +0 -0
- package/dist/bin/linux-arm64/tensorlake +0 -0
- package/dist/bin/linux-arm64/tl +0 -0
- package/dist/bin/linux-x64/tensorlake +0 -0
- package/dist/bin/linux-x64/tl +0 -0
- package/dist/bin/win32-x64/tensorlake.exe +0 -0
- package/dist/bin/win32-x64/tl.exe +0 -0
- package/dist/index.cjs +808 -798
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +96 -36
- package/dist/index.d.ts +96 -36
- package/dist/index.js +805 -796
- package/dist/index.js.map +1 -1
- package/dist/native/darwin-arm64/tensorlake-node.node +0 -0
- package/dist/native/linux-arm64/tensorlake-node.node +0 -0
- package/dist/native/linux-x64/tensorlake-node.node +0 -0
- package/dist/native/win32-x64/tensorlake-node.node +0 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ var SDK_VERSION, API_URL, API_KEY, NAMESPACE, SANDBOX_PROXY_URL, DEFAULT_HTTP_TI
|
|
|
19
19
|
var init_defaults = __esm({
|
|
20
20
|
"src/defaults.ts"() {
|
|
21
21
|
"use strict";
|
|
22
|
-
SDK_VERSION = "0.5.
|
|
22
|
+
SDK_VERSION = "0.5.40";
|
|
23
23
|
API_URL = process.env.TENSORLAKE_API_URL ?? "https://api.tensorlake.ai";
|
|
24
24
|
API_KEY = process.env.TENSORLAKE_API_KEY ?? void 0;
|
|
25
25
|
NAMESPACE = process.env.INDEXIFY_NAMESPACE ?? "default";
|
|
@@ -139,364 +139,154 @@ var init_errors = __esm({
|
|
|
139
139
|
}
|
|
140
140
|
});
|
|
141
141
|
|
|
142
|
-
// src/
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
function sdkTimingPayloadsEnabled() {
|
|
148
|
-
const value = process.env[SDK_TIMING_PAYLOADS_ENV_VAR];
|
|
149
|
-
return value === "1" || value === "true" || value === "yes";
|
|
150
|
-
}
|
|
151
|
-
function nowMs() {
|
|
152
|
-
return performance.now();
|
|
153
|
-
}
|
|
154
|
-
function logSdkTiming(op, phase, startedAt, fields = {}) {
|
|
155
|
-
if (!sdkTimingsEnabled()) return;
|
|
156
|
-
logSdkTimingEvent(op, phase, {
|
|
157
|
-
...fields,
|
|
158
|
-
duration_ms: Math.round((nowMs() - startedAt) * 100) / 100
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
function logSdkTimingEvent(op, phase, fields = {}) {
|
|
162
|
-
if (!sdkTimingsEnabled()) return;
|
|
163
|
-
const parts = [
|
|
164
|
-
"[tensorlake:sdk-timing]",
|
|
165
|
-
`op=${op}`,
|
|
166
|
-
`phase=${phase}`
|
|
167
|
-
];
|
|
168
|
-
for (const [key, value] of Object.entries(fields)) {
|
|
169
|
-
if (value == null) continue;
|
|
170
|
-
parts.push(`${key}=${formatSdkTimingValue(value)}`);
|
|
142
|
+
// src/native-sandbox.ts
|
|
143
|
+
import { createRequire } from "module";
|
|
144
|
+
function resolveRequire() {
|
|
145
|
+
if (typeof module !== "undefined" && module.exports != null && typeof __require !== "undefined") {
|
|
146
|
+
return __require;
|
|
171
147
|
}
|
|
172
|
-
|
|
148
|
+
return createRequire(import.meta.url);
|
|
173
149
|
}
|
|
174
|
-
function
|
|
175
|
-
if (
|
|
176
|
-
|
|
150
|
+
function loadNativeSandboxBinding() {
|
|
151
|
+
if (cachedBinding) return cachedBinding;
|
|
152
|
+
if (cachedBindingError) throw cachedBindingError;
|
|
153
|
+
try {
|
|
154
|
+
const { loadNative } = resolveRequire()("../lib/runtime.cjs");
|
|
155
|
+
const binding = loadNative();
|
|
156
|
+
if (binding == null || typeof binding.NativeSandboxClient !== "function" || typeof binding.NativeSandboxProxyClient !== "function") {
|
|
157
|
+
throw new SandboxError(
|
|
158
|
+
"native binding does not export the sandbox clients; rebuild with 'npm run build:native'"
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
cachedBinding = binding;
|
|
162
|
+
return cachedBinding;
|
|
163
|
+
} catch (error) {
|
|
164
|
+
cachedBindingError = error instanceof Error ? error : new Error(String(error));
|
|
165
|
+
throw cachedBindingError;
|
|
177
166
|
}
|
|
178
|
-
|
|
179
|
-
|
|
167
|
+
}
|
|
168
|
+
function parseNativeError(error) {
|
|
169
|
+
const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
|
|
170
|
+
if (!message) return null;
|
|
171
|
+
try {
|
|
172
|
+
const parsed = JSON.parse(message);
|
|
173
|
+
if (parsed && typeof parsed.category === "string") {
|
|
174
|
+
return {
|
|
175
|
+
category: parsed.category,
|
|
176
|
+
status: typeof parsed.status === "number" ? parsed.status : null,
|
|
177
|
+
message: typeof parsed.message === "string" ? parsed.message : message
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
} catch {
|
|
180
181
|
}
|
|
181
|
-
return
|
|
182
|
+
return null;
|
|
182
183
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
SDK_TIMING_PAYLOADS_ENV_VAR = "TENSORLAKE_SDK_TIMING_PAYLOADS";
|
|
184
|
+
function translateNativeError(error, context) {
|
|
185
|
+
const payload = parseNativeError(error);
|
|
186
|
+
if (!payload) {
|
|
187
|
+
if (error instanceof Error) return error;
|
|
188
|
+
return new SandboxError(String(error));
|
|
189
189
|
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
import {
|
|
194
|
-
Agent,
|
|
195
|
-
fetch as undiciFetch,
|
|
196
|
-
setGlobalDispatcher
|
|
197
|
-
} from "undici";
|
|
198
|
-
function withTraceId(value, traceId) {
|
|
199
|
-
if (value == null) {
|
|
200
|
-
return { traceId };
|
|
190
|
+
const { category, status, message } = payload;
|
|
191
|
+
if (category === "connection") {
|
|
192
|
+
return new SandboxConnectionError(message, { cause: error });
|
|
201
193
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
return void 0;
|
|
194
|
+
if (status === 404) {
|
|
195
|
+
if (context?.notFoundKind === "pool" && context.poolId) {
|
|
196
|
+
return new PoolNotFoundError(context.poolId);
|
|
197
|
+
}
|
|
198
|
+
if (context?.notFoundKind === "sandbox" && context.sandboxId) {
|
|
199
|
+
return new SandboxNotFoundError(context.sandboxId);
|
|
200
|
+
}
|
|
201
|
+
return new RemoteAPIError(404, message);
|
|
211
202
|
}
|
|
212
|
-
if (
|
|
213
|
-
return
|
|
203
|
+
if (status === 409 && context?.poolId) {
|
|
204
|
+
return new PoolInUseError(context.poolId, message);
|
|
214
205
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
return queryStart === -1 ? path2 : path2.slice(0, queryStart);
|
|
206
|
+
if (typeof status === "number") {
|
|
207
|
+
return new RemoteAPIError(status, message);
|
|
208
|
+
}
|
|
209
|
+
return new SandboxError(message);
|
|
220
210
|
}
|
|
221
|
-
function
|
|
222
|
-
let message = body;
|
|
211
|
+
async function callNative(fn, context) {
|
|
223
212
|
try {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
} catch {
|
|
213
|
+
return await fn();
|
|
214
|
+
} catch (error) {
|
|
215
|
+
throw translateNativeError(error, context);
|
|
228
216
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
217
|
+
}
|
|
218
|
+
async function* nativeEventStream(start, context, signal) {
|
|
219
|
+
const queue = [];
|
|
220
|
+
let wake = null;
|
|
221
|
+
let finished = false;
|
|
222
|
+
let failure = null;
|
|
223
|
+
const notify = () => {
|
|
224
|
+
if (wake) {
|
|
225
|
+
const w = wake;
|
|
226
|
+
wake = null;
|
|
227
|
+
w();
|
|
233
228
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
229
|
+
};
|
|
230
|
+
const emit = (eventJson) => {
|
|
231
|
+
queue.push(eventJson);
|
|
232
|
+
notify();
|
|
233
|
+
};
|
|
234
|
+
const onAbort = () => notify();
|
|
235
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
236
|
+
const startPromise = start(emit).catch((error) => {
|
|
237
|
+
failure = error;
|
|
238
|
+
}).finally(() => {
|
|
239
|
+
finished = true;
|
|
240
|
+
notify();
|
|
241
|
+
});
|
|
242
|
+
try {
|
|
243
|
+
while (true) {
|
|
244
|
+
if (queue.length > 0) {
|
|
245
|
+
yield JSON.parse(queue.shift());
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (signal?.aborted) break;
|
|
249
|
+
if (finished) break;
|
|
250
|
+
await new Promise((resolve) => {
|
|
251
|
+
wake = resolve;
|
|
252
|
+
});
|
|
237
253
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if (status === 409) {
|
|
241
|
-
if (path2.includes("sandbox-pools") || path2.includes("pools")) {
|
|
242
|
-
const match = path2.match(/sandbox-pools\/([^/]+)/);
|
|
243
|
-
if (match) throw new PoolInUseError(match[1], message);
|
|
254
|
+
if (failure != null) {
|
|
255
|
+
throw translateNativeError(failure, context);
|
|
244
256
|
}
|
|
257
|
+
} finally {
|
|
258
|
+
signal?.removeEventListener("abort", onAbort);
|
|
259
|
+
void startPromise.catch(() => {
|
|
260
|
+
});
|
|
245
261
|
}
|
|
246
|
-
throw new RemoteAPIError(status, message);
|
|
247
262
|
}
|
|
248
|
-
function
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
263
|
+
function assembleCommandResult(events) {
|
|
264
|
+
const stdoutLines = [];
|
|
265
|
+
const stderrLines = [];
|
|
266
|
+
let exitCode = -1;
|
|
267
|
+
for (const eventJson of events) {
|
|
268
|
+
const raw = JSON.parse(eventJson);
|
|
269
|
+
if (typeof raw.line === "string") {
|
|
270
|
+
if (raw.stream === "stderr") {
|
|
271
|
+
stderrLines.push(raw.line);
|
|
272
|
+
} else {
|
|
273
|
+
stdoutLines.push(raw.line);
|
|
274
|
+
}
|
|
275
|
+
} else if ("exit_code" in raw || "signal" in raw) {
|
|
276
|
+
if (typeof raw.exit_code === "number") {
|
|
277
|
+
exitCode = raw.exit_code;
|
|
278
|
+
} else if (typeof raw.signal === "number") {
|
|
279
|
+
exitCode = -raw.signal;
|
|
280
|
+
}
|
|
254
281
|
}
|
|
255
|
-
signal.addEventListener("abort", () => controller.abort(signal.reason), {
|
|
256
|
-
once: true
|
|
257
|
-
});
|
|
258
282
|
}
|
|
259
|
-
return
|
|
260
|
-
}
|
|
261
|
-
function sleep(ms) {
|
|
262
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
283
|
+
return { exitCode, stdout: stdoutLines.join("\n"), stderr: stderrLines.join("\n") };
|
|
263
284
|
}
|
|
264
|
-
var
|
|
265
|
-
var
|
|
266
|
-
"src/
|
|
285
|
+
var cachedBinding, cachedBindingError;
|
|
286
|
+
var init_native_sandbox = __esm({
|
|
287
|
+
"src/native-sandbox.ts"() {
|
|
267
288
|
"use strict";
|
|
268
|
-
init_defaults();
|
|
269
289
|
init_errors();
|
|
270
|
-
init_sdk_timings();
|
|
271
|
-
REQUEST_TIMEOUT_HEADER = "X-Tensorlake-Request-Timeout-Ms";
|
|
272
|
-
setGlobalDispatcher(
|
|
273
|
-
new Agent({
|
|
274
|
-
keepAliveTimeout: 6e4,
|
|
275
|
-
allowH2: true
|
|
276
|
-
})
|
|
277
|
-
);
|
|
278
|
-
HttpClient = class _HttpClient {
|
|
279
|
-
baseUrl;
|
|
280
|
-
headers;
|
|
281
|
-
maxRetries;
|
|
282
|
-
retryBackoffMs;
|
|
283
|
-
timeoutMs;
|
|
284
|
-
abortController = null;
|
|
285
|
-
constructor(options) {
|
|
286
|
-
const url = options.baseUrl;
|
|
287
|
-
this.baseUrl = url.endsWith("/") ? url.slice(0, -1) : url;
|
|
288
|
-
this.maxRetries = options.maxRetries ?? MAX_RETRIES;
|
|
289
|
-
this.retryBackoffMs = options.retryBackoffMs ?? RETRY_BACKOFF_MS;
|
|
290
|
-
this.timeoutMs = options.timeoutMs === void 0 ? DEFAULT_HTTP_TIMEOUT_MS : options.timeoutMs;
|
|
291
|
-
this.headers = {
|
|
292
|
-
"User-Agent": `tensorlake-typescript-sdk/${SDK_VERSION}`
|
|
293
|
-
};
|
|
294
|
-
if (this.timeoutMs != null) {
|
|
295
|
-
this.headers[REQUEST_TIMEOUT_HEADER] = String(this.timeoutMs);
|
|
296
|
-
}
|
|
297
|
-
if (options.apiKey) {
|
|
298
|
-
this.headers["Authorization"] = `Bearer ${options.apiKey}`;
|
|
299
|
-
}
|
|
300
|
-
if (options.organizationId) {
|
|
301
|
-
this.headers["X-Forwarded-Organization-Id"] = options.organizationId;
|
|
302
|
-
}
|
|
303
|
-
if (options.projectId) {
|
|
304
|
-
this.headers["X-Forwarded-Project-Id"] = options.projectId;
|
|
305
|
-
}
|
|
306
|
-
if (options.hostHeader) {
|
|
307
|
-
this.headers["Host"] = options.hostHeader;
|
|
308
|
-
}
|
|
309
|
-
if (options.sandboxIdHeader) {
|
|
310
|
-
this.headers["X-Tensorlake-Sandbox-Id"] = options.sandboxIdHeader;
|
|
311
|
-
}
|
|
312
|
-
if (options.routingHint) {
|
|
313
|
-
this.headers["X-Tensorlake-Route-Hint"] = options.routingHint;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
close() {
|
|
317
|
-
this.abortController?.abort();
|
|
318
|
-
this.abortController = null;
|
|
319
|
-
}
|
|
320
|
-
/** Make a JSON request, returning the parsed response body. */
|
|
321
|
-
async requestJson(method, path2, options) {
|
|
322
|
-
const response = await this.requestResponse(method, path2, {
|
|
323
|
-
json: options?.body,
|
|
324
|
-
headers: options?.headers,
|
|
325
|
-
signal: options?.signal,
|
|
326
|
-
retryableStatusCodes: options?.retryableStatusCodes,
|
|
327
|
-
allowedErrorStatusCodes: options?.allowedErrorStatusCodes,
|
|
328
|
-
allowHttpErrors: options?.allowHttpErrors
|
|
329
|
-
});
|
|
330
|
-
const text = await response.text();
|
|
331
|
-
const data = text ? JSON.parse(text) : void 0;
|
|
332
|
-
return withTraceId(data, response.traceId);
|
|
333
|
-
}
|
|
334
|
-
/** Make a request returning raw bytes. */
|
|
335
|
-
async requestBytes(method, path2, options) {
|
|
336
|
-
const headers = { ...options?.headers ?? {} };
|
|
337
|
-
if (options?.contentType) {
|
|
338
|
-
headers["Content-Type"] = options.contentType;
|
|
339
|
-
}
|
|
340
|
-
const response = await this.requestResponse(method, path2, {
|
|
341
|
-
body: options?.body,
|
|
342
|
-
headers,
|
|
343
|
-
signal: options?.signal
|
|
344
|
-
});
|
|
345
|
-
const buffer = await response.arrayBuffer();
|
|
346
|
-
return withTraceId(new Uint8Array(buffer), response.traceId);
|
|
347
|
-
}
|
|
348
|
-
/** Make a request and return the response body as an SSE stream. */
|
|
349
|
-
async requestStream(method, path2, options) {
|
|
350
|
-
const response = await this.requestResponse(method, path2, {
|
|
351
|
-
json: options?.json,
|
|
352
|
-
headers: { Accept: "text/event-stream" },
|
|
353
|
-
signal: options?.signal
|
|
354
|
-
});
|
|
355
|
-
if (!response.body) {
|
|
356
|
-
throw new RemoteAPIError(
|
|
357
|
-
response.status,
|
|
358
|
-
"No response body for SSE stream"
|
|
359
|
-
);
|
|
360
|
-
}
|
|
361
|
-
return withTraceId(response.body, response.traceId);
|
|
362
|
-
}
|
|
363
|
-
/** Make a request and return the raw Response. */
|
|
364
|
-
async requestResponse(method, path2, options) {
|
|
365
|
-
const headers = {
|
|
366
|
-
...this.headers,
|
|
367
|
-
...options?.headers ?? {}
|
|
368
|
-
};
|
|
369
|
-
const hasJsonBody = options?.json !== void 0;
|
|
370
|
-
if (hasJsonBody && !hasHeader(headers, "Content-Type")) {
|
|
371
|
-
headers["Content-Type"] = "application/json";
|
|
372
|
-
}
|
|
373
|
-
const body = hasJsonBody ? JSON.stringify(options?.json) : normalizeRequestBody(options?.body);
|
|
374
|
-
const { response, traceId } = await this.doFetch(
|
|
375
|
-
method,
|
|
376
|
-
path2,
|
|
377
|
-
body,
|
|
378
|
-
headers,
|
|
379
|
-
options?.signal,
|
|
380
|
-
options?.allowHttpErrors ?? false,
|
|
381
|
-
options?.retryableStatusCodes ?? RETRYABLE_STATUS_CODES,
|
|
382
|
-
options?.allowedErrorStatusCodes ?? /* @__PURE__ */ new Set()
|
|
383
|
-
);
|
|
384
|
-
return withTraceId(response, traceId);
|
|
385
|
-
}
|
|
386
|
-
static makeTraceparent() {
|
|
387
|
-
const randomHex = (bytes) => Array.from(crypto.getRandomValues(new Uint8Array(bytes))).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
388
|
-
const traceId = randomHex(16);
|
|
389
|
-
const spanId = randomHex(8);
|
|
390
|
-
return { traceparent: `00-${traceId}-${spanId}-01`, traceId };
|
|
391
|
-
}
|
|
392
|
-
async doFetch(method, path2, body, headers, signal, allowHttpErrors = false, retryableStatusCodes = RETRYABLE_STATUS_CODES, allowedErrorStatusCodes = /* @__PURE__ */ new Set()) {
|
|
393
|
-
const url = `${this.baseUrl}${path2}`;
|
|
394
|
-
const { traceparent, traceId } = _HttpClient.makeTraceparent();
|
|
395
|
-
headers["traceparent"] = traceparent;
|
|
396
|
-
let lastError;
|
|
397
|
-
const operationStart = nowMs();
|
|
398
|
-
const timingPath = sdkTimingPayloadsEnabled() ? path2 : stripPathPayload(path2);
|
|
399
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
400
|
-
if (attempt > 0) {
|
|
401
|
-
const delay = this.retryBackoffMs * Math.pow(2, attempt - 1);
|
|
402
|
-
logSdkTimingEvent("http.request", "retry_sleep_start", {
|
|
403
|
-
method,
|
|
404
|
-
path: timingPath,
|
|
405
|
-
attempt,
|
|
406
|
-
delay_ms: delay,
|
|
407
|
-
server_trace_id: traceId
|
|
408
|
-
});
|
|
409
|
-
await sleep(delay);
|
|
410
|
-
}
|
|
411
|
-
this.abortController = new AbortController();
|
|
412
|
-
let timedOut = false;
|
|
413
|
-
const timeoutId = this.timeoutMs == null ? null : setTimeout(() => {
|
|
414
|
-
timedOut = true;
|
|
415
|
-
this.abortController?.abort();
|
|
416
|
-
}, this.timeoutMs);
|
|
417
|
-
const combinedSignal = signal ? anySignal([signal, this.abortController.signal]) : this.abortController.signal;
|
|
418
|
-
const attemptStart = nowMs();
|
|
419
|
-
try {
|
|
420
|
-
const response = await undiciFetch(url, {
|
|
421
|
-
method,
|
|
422
|
-
headers,
|
|
423
|
-
body,
|
|
424
|
-
signal: combinedSignal
|
|
425
|
-
});
|
|
426
|
-
if (timeoutId != null) clearTimeout(timeoutId);
|
|
427
|
-
logSdkTiming("http.request", "headers", attemptStart, {
|
|
428
|
-
method,
|
|
429
|
-
path: timingPath,
|
|
430
|
-
status: response.status,
|
|
431
|
-
attempt,
|
|
432
|
-
server_trace_id: traceId
|
|
433
|
-
});
|
|
434
|
-
if (response.ok) {
|
|
435
|
-
logSdkTiming("http.request", "complete", operationStart, {
|
|
436
|
-
method,
|
|
437
|
-
path: timingPath,
|
|
438
|
-
status: response.status,
|
|
439
|
-
attempts: attempt + 1,
|
|
440
|
-
server_trace_id: traceId
|
|
441
|
-
});
|
|
442
|
-
return { response, traceId };
|
|
443
|
-
}
|
|
444
|
-
if (retryableStatusCodes.has(response.status) && attempt < this.maxRetries) {
|
|
445
|
-
lastError = new RemoteAPIError(
|
|
446
|
-
response.status,
|
|
447
|
-
await response.text().catch(() => "")
|
|
448
|
-
);
|
|
449
|
-
logSdkTimingEvent("http.request", "retryable_status", {
|
|
450
|
-
method,
|
|
451
|
-
path: timingPath,
|
|
452
|
-
status: response.status,
|
|
453
|
-
attempt,
|
|
454
|
-
server_trace_id: traceId
|
|
455
|
-
});
|
|
456
|
-
continue;
|
|
457
|
-
}
|
|
458
|
-
if (allowHttpErrors || allowedErrorStatusCodes.has(response.status)) {
|
|
459
|
-
logSdkTiming("http.request", "complete", operationStart, {
|
|
460
|
-
method,
|
|
461
|
-
path: timingPath,
|
|
462
|
-
status: response.status,
|
|
463
|
-
attempts: attempt + 1,
|
|
464
|
-
server_trace_id: traceId
|
|
465
|
-
});
|
|
466
|
-
return { response, traceId };
|
|
467
|
-
}
|
|
468
|
-
const errorBody = await response.text().catch(() => "");
|
|
469
|
-
throwMappedError(response.status, errorBody, path2);
|
|
470
|
-
} catch (err) {
|
|
471
|
-
if (timeoutId != null) clearTimeout(timeoutId);
|
|
472
|
-
if (err instanceof RemoteAPIError || err instanceof SandboxNotFoundError || err instanceof PoolNotFoundError || err instanceof PoolInUseError) {
|
|
473
|
-
throw err;
|
|
474
|
-
}
|
|
475
|
-
if (signal?.aborted && !timedOut) {
|
|
476
|
-
throw new SandboxConnectionError("request aborted by caller", {
|
|
477
|
-
cause: err
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
lastError = err instanceof Error ? err : new Error(String(err));
|
|
481
|
-
logSdkTiming("http.request", "error", attemptStart, {
|
|
482
|
-
method,
|
|
483
|
-
path: timingPath,
|
|
484
|
-
attempt,
|
|
485
|
-
server_trace_id: traceId,
|
|
486
|
-
error: timedOut ? `request timed out after ${this.timeoutMs}ms` : describeError(err)
|
|
487
|
-
});
|
|
488
|
-
if (attempt >= this.maxRetries) {
|
|
489
|
-
const detail = timedOut ? `request timed out after ${this.timeoutMs}ms` : describeError(err);
|
|
490
|
-
throw new SandboxConnectionError(detail, { cause: err });
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
throw new SandboxConnectionError(
|
|
495
|
-
lastError ? describeError(lastError) : "request failed",
|
|
496
|
-
{ cause: lastError }
|
|
497
|
-
);
|
|
498
|
-
}
|
|
499
|
-
};
|
|
500
290
|
}
|
|
501
291
|
});
|
|
502
292
|
|
|
@@ -3005,71 +2795,63 @@ var init_desktop = __esm({
|
|
|
3005
2795
|
}
|
|
3006
2796
|
});
|
|
3007
2797
|
|
|
3008
|
-
// src/
|
|
3009
|
-
|
|
3010
|
-
const
|
|
3011
|
-
|
|
3012
|
-
let buffer = "";
|
|
3013
|
-
try {
|
|
3014
|
-
while (true) {
|
|
3015
|
-
if (signal?.aborted) break;
|
|
3016
|
-
const { done, value } = await reader.read();
|
|
3017
|
-
if (done) break;
|
|
3018
|
-
buffer += decoder.decode(value, { stream: true });
|
|
3019
|
-
const parts = buffer.split(/\r?\n\r?\n/);
|
|
3020
|
-
buffer = parts.pop() ?? "";
|
|
3021
|
-
for (const part of parts) {
|
|
3022
|
-
const lines = part.split(/\r?\n/);
|
|
3023
|
-
const dataLines = [];
|
|
3024
|
-
let event;
|
|
3025
|
-
let id;
|
|
3026
|
-
for (const line of lines) {
|
|
3027
|
-
if (!line || line.startsWith(":")) continue;
|
|
3028
|
-
const separator = line.indexOf(":");
|
|
3029
|
-
const field = separator === -1 ? line : line.slice(0, separator);
|
|
3030
|
-
let value2 = separator === -1 ? "" : line.slice(separator + 1);
|
|
3031
|
-
if (value2.startsWith(" ")) {
|
|
3032
|
-
value2 = value2.slice(1);
|
|
3033
|
-
}
|
|
3034
|
-
if (field === "data") {
|
|
3035
|
-
dataLines.push(value2);
|
|
3036
|
-
} else if (field === "event") {
|
|
3037
|
-
event = value2;
|
|
3038
|
-
} else if (field === "id") {
|
|
3039
|
-
id = value2;
|
|
3040
|
-
}
|
|
3041
|
-
}
|
|
3042
|
-
if (dataLines.length > 0 || event || id) {
|
|
3043
|
-
yield { data: dataLines.join("\n"), event, id };
|
|
3044
|
-
}
|
|
3045
|
-
}
|
|
3046
|
-
}
|
|
3047
|
-
} finally {
|
|
3048
|
-
reader.releaseLock();
|
|
3049
|
-
}
|
|
2798
|
+
// src/sdk-timings.ts
|
|
2799
|
+
function sdkTimingsEnabled() {
|
|
2800
|
+
const value = process.env[SDK_TIMINGS_ENV_VAR];
|
|
2801
|
+
return value === "1" || value === "true" || value === "yes";
|
|
3050
2802
|
}
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
try {
|
|
3055
|
-
yield JSON.parse(message.data);
|
|
3056
|
-
} catch {
|
|
3057
|
-
}
|
|
3058
|
-
}
|
|
2803
|
+
function sdkTimingPayloadsEnabled() {
|
|
2804
|
+
const value = process.env[SDK_TIMING_PAYLOADS_ENV_VAR];
|
|
2805
|
+
return value === "1" || value === "true" || value === "yes";
|
|
3059
2806
|
}
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
"use strict";
|
|
3063
|
-
}
|
|
3064
|
-
});
|
|
3065
|
-
|
|
3066
|
-
// src/url.ts
|
|
3067
|
-
function trimTrailingSlashes(url) {
|
|
3068
|
-
return url.endsWith("/") ? url.slice(0, -1) : url;
|
|
2807
|
+
function nowMs() {
|
|
2808
|
+
return performance.now();
|
|
3069
2809
|
}
|
|
3070
|
-
function
|
|
3071
|
-
|
|
3072
|
-
|
|
2810
|
+
function logSdkTiming(op, phase, startedAt, fields = {}) {
|
|
2811
|
+
if (!sdkTimingsEnabled()) return;
|
|
2812
|
+
logSdkTimingEvent(op, phase, {
|
|
2813
|
+
...fields,
|
|
2814
|
+
duration_ms: Math.round((nowMs() - startedAt) * 100) / 100
|
|
2815
|
+
});
|
|
2816
|
+
}
|
|
2817
|
+
function logSdkTimingEvent(op, phase, fields = {}) {
|
|
2818
|
+
if (!sdkTimingsEnabled()) return;
|
|
2819
|
+
const parts = [
|
|
2820
|
+
"[tensorlake:sdk-timing]",
|
|
2821
|
+
`op=${op}`,
|
|
2822
|
+
`phase=${phase}`
|
|
2823
|
+
];
|
|
2824
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
2825
|
+
if (value == null) continue;
|
|
2826
|
+
parts.push(`${key}=${formatSdkTimingValue(value)}`);
|
|
2827
|
+
}
|
|
2828
|
+
console.error(parts.join(" "));
|
|
2829
|
+
}
|
|
2830
|
+
function formatSdkTimingValue(value) {
|
|
2831
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
2832
|
+
return String(value);
|
|
2833
|
+
}
|
|
2834
|
+
if (/^[A-Za-z0-9_./:@-]+$/.test(value)) {
|
|
2835
|
+
return value;
|
|
2836
|
+
}
|
|
2837
|
+
return JSON.stringify(value);
|
|
2838
|
+
}
|
|
2839
|
+
var SDK_TIMINGS_ENV_VAR, SDK_TIMING_PAYLOADS_ENV_VAR;
|
|
2840
|
+
var init_sdk_timings = __esm({
|
|
2841
|
+
"src/sdk-timings.ts"() {
|
|
2842
|
+
"use strict";
|
|
2843
|
+
SDK_TIMINGS_ENV_VAR = "TENSORLAKE_SDK_TIMINGS";
|
|
2844
|
+
SDK_TIMING_PAYLOADS_ENV_VAR = "TENSORLAKE_SDK_TIMING_PAYLOADS";
|
|
2845
|
+
}
|
|
2846
|
+
});
|
|
2847
|
+
|
|
2848
|
+
// src/url.ts
|
|
2849
|
+
function trimTrailingSlashes(url) {
|
|
2850
|
+
return url.endsWith("/") ? url.slice(0, -1) : url;
|
|
2851
|
+
}
|
|
2852
|
+
function isLocalhost(apiUrl) {
|
|
2853
|
+
try {
|
|
2854
|
+
const parsed = new URL(apiUrl);
|
|
3073
2855
|
return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
|
3074
2856
|
} catch {
|
|
3075
2857
|
return false;
|
|
@@ -3123,24 +2905,6 @@ function sandboxUrlFromIngressEndpoint(ingressEndpoint, sandboxId, port = 9501)
|
|
|
3123
2905
|
const label = port === 9501 ? sandboxId : `${port}-${sandboxId}`;
|
|
3124
2906
|
return `${parsed.protocol}//${label}.${parsed.host}`;
|
|
3125
2907
|
}
|
|
3126
|
-
function resolveSandboxLifecycleUrl(apiUrl) {
|
|
3127
|
-
if (isLocalhost(apiUrl)) return apiUrl;
|
|
3128
|
-
try {
|
|
3129
|
-
const parsed = new URL(apiUrl);
|
|
3130
|
-
if (parsed.hostname.startsWith("api.")) {
|
|
3131
|
-
parsed.hostname = "sandbox." + parsed.hostname.slice(4);
|
|
3132
|
-
return parsed.toString().replace(/\/$/, "");
|
|
3133
|
-
}
|
|
3134
|
-
} catch {
|
|
3135
|
-
}
|
|
3136
|
-
return apiUrl;
|
|
3137
|
-
}
|
|
3138
|
-
function lifecyclePath(path2, isLocal, namespace) {
|
|
3139
|
-
if (isLocal) {
|
|
3140
|
-
return `/v1/namespaces/${namespace}/${path2}`;
|
|
3141
|
-
}
|
|
3142
|
-
return `/${path2}`;
|
|
3143
|
-
}
|
|
3144
2908
|
var init_url = __esm({
|
|
3145
2909
|
"src/url.ts"() {
|
|
3146
2910
|
"use strict";
|
|
@@ -3152,7 +2916,7 @@ var init_url = __esm({
|
|
|
3152
2916
|
import WebSocket2 from "ws";
|
|
3153
2917
|
function processUserPayload(user) {
|
|
3154
2918
|
if (user == null) {
|
|
3155
|
-
return
|
|
2919
|
+
return void 0;
|
|
3156
2920
|
}
|
|
3157
2921
|
if (typeof user === "string" && user.trim() === "") {
|
|
3158
2922
|
throw new SandboxError("process user must not be empty");
|
|
@@ -3182,32 +2946,24 @@ function sendPtyFrame(socket, frame) {
|
|
|
3182
2946
|
socket.send(frame, (error) => error ? reject(error) : resolve());
|
|
3183
2947
|
});
|
|
3184
2948
|
}
|
|
3185
|
-
|
|
3186
|
-
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
3187
|
-
throw new SandboxError("requestTimeout must be a positive number of seconds");
|
|
3188
|
-
}
|
|
3189
|
-
return Math.ceil(seconds * 1e3);
|
|
3190
|
-
}
|
|
3191
|
-
var DEFAULT_PROCESS_USER, SandboxProxyConnection, PTY_OP_DATA, PTY_OP_RESIZE, PTY_OP_READY, PTY_OP_EXIT, Pty, Sandbox;
|
|
2949
|
+
var SandboxProxyConnection, PTY_OP_DATA, PTY_OP_RESIZE, PTY_OP_READY, PTY_OP_EXIT, Pty, Sandbox;
|
|
3192
2950
|
var init_sandbox = __esm({
|
|
3193
2951
|
"src/sandbox.ts"() {
|
|
3194
2952
|
"use strict";
|
|
3195
2953
|
init_desktop();
|
|
3196
2954
|
init_defaults();
|
|
3197
2955
|
init_errors();
|
|
3198
|
-
|
|
2956
|
+
init_native_sandbox();
|
|
3199
2957
|
init_models();
|
|
3200
2958
|
init_tunnel();
|
|
3201
|
-
init_sse();
|
|
3202
2959
|
init_sdk_timings();
|
|
3203
2960
|
init_url();
|
|
3204
|
-
DEFAULT_PROCESS_USER = "tl-user";
|
|
3205
2961
|
SandboxProxyConnection = class {
|
|
3206
2962
|
constructor(sandbox, options) {
|
|
3207
2963
|
this.sandbox = sandbox;
|
|
3208
2964
|
this.options = options;
|
|
3209
2965
|
this.routingHint = options.routingHint;
|
|
3210
|
-
this.
|
|
2966
|
+
this.nativeProxy = this.configureProxy(
|
|
3211
2967
|
options.proxyUrl ?? SANDBOX_PROXY_URL,
|
|
3212
2968
|
options.sandboxId,
|
|
3213
2969
|
options.routingHint
|
|
@@ -3216,7 +2972,7 @@ var init_sandbox = __esm({
|
|
|
3216
2972
|
}
|
|
3217
2973
|
baseUrl = "";
|
|
3218
2974
|
wsHeaders = {};
|
|
3219
|
-
|
|
2975
|
+
nativeProxy;
|
|
3220
2976
|
resolveProxyInfo;
|
|
3221
2977
|
resolvePromise = null;
|
|
3222
2978
|
routingHint;
|
|
@@ -3239,9 +2995,11 @@ var init_sandbox = __esm({
|
|
|
3239
2995
|
this.sandbox._setName(info.name ?? null);
|
|
3240
2996
|
this.routingHint = this.routingHint ?? info.routingHint;
|
|
3241
2997
|
const proxyUrl = info.ingressEndpoint ?? this.options.proxyUrl ?? SANDBOX_PROXY_URL;
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
2998
|
+
this.nativeProxy = this.configureProxy(
|
|
2999
|
+
proxyUrl,
|
|
3000
|
+
info.sandboxId,
|
|
3001
|
+
this.routingHint
|
|
3002
|
+
);
|
|
3245
3003
|
logSdkTiming("sandbox.proxy", "resolve_complete", resolveStart, {
|
|
3246
3004
|
sandbox_id: info.sandboxId,
|
|
3247
3005
|
server_trace_id: info.traceId,
|
|
@@ -3253,24 +3011,12 @@ var init_sandbox = __esm({
|
|
|
3253
3011
|
});
|
|
3254
3012
|
return this.resolvePromise;
|
|
3255
3013
|
}
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
return this.http.requestJson(method, path2, options);
|
|
3259
|
-
}
|
|
3260
|
-
async requestBytes(method, path2, options) {
|
|
3261
|
-
await this.ensureResolved();
|
|
3262
|
-
return this.http.requestBytes(method, path2, options);
|
|
3263
|
-
}
|
|
3264
|
-
async requestStream(method, path2, options) {
|
|
3265
|
-
await this.ensureResolved();
|
|
3266
|
-
return this.http.requestStream(method, path2, options);
|
|
3267
|
-
}
|
|
3268
|
-
async requestResponse(method, path2, options) {
|
|
3014
|
+
/** Await proxy resolution and return the Rust-backed proxy client. */
|
|
3015
|
+
async client() {
|
|
3269
3016
|
await this.ensureResolved();
|
|
3270
|
-
return this.
|
|
3017
|
+
return this.nativeProxy;
|
|
3271
3018
|
}
|
|
3272
3019
|
close() {
|
|
3273
|
-
this.http.close();
|
|
3274
3020
|
}
|
|
3275
3021
|
configureProxy(proxyUrl, sandboxId, routingHint) {
|
|
3276
3022
|
const { baseUrl, hostHeader, sandboxIdHeader } = resolveProxyTarget(
|
|
@@ -3294,16 +3040,34 @@ var init_sandbox = __esm({
|
|
|
3294
3040
|
if (sandboxIdHeader) {
|
|
3295
3041
|
this.wsHeaders["X-Tensorlake-Sandbox-Id"] = sandboxIdHeader;
|
|
3296
3042
|
}
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3043
|
+
if (this.options.nativeClient) {
|
|
3044
|
+
return this.options.nativeClient.connectProxy(
|
|
3045
|
+
proxyUrl,
|
|
3046
|
+
sandboxId,
|
|
3047
|
+
routingHint ?? null,
|
|
3048
|
+
this.proxyRequestTimeoutSec()
|
|
3049
|
+
);
|
|
3050
|
+
}
|
|
3051
|
+
const binding = loadNativeSandboxBinding();
|
|
3052
|
+
return new binding.NativeSandboxProxyClient(
|
|
3053
|
+
proxyUrl,
|
|
3054
|
+
sandboxId,
|
|
3055
|
+
this.options.apiKey ?? null,
|
|
3056
|
+
this.options.organizationId ?? null,
|
|
3057
|
+
this.options.projectId ?? null,
|
|
3058
|
+
routingHint ?? null,
|
|
3059
|
+
null,
|
|
3060
|
+
this.proxyRequestTimeoutSec()
|
|
3061
|
+
);
|
|
3062
|
+
}
|
|
3063
|
+
proxyRequestTimeoutSec() {
|
|
3064
|
+
if (this.options.requestTimeout != null) {
|
|
3065
|
+
return this.options.requestTimeout;
|
|
3066
|
+
}
|
|
3067
|
+
if (this.options.timeoutMs != null) {
|
|
3068
|
+
return this.options.timeoutMs / 1e3;
|
|
3069
|
+
}
|
|
3070
|
+
return null;
|
|
3307
3071
|
}
|
|
3308
3072
|
};
|
|
3309
3073
|
PTY_OP_DATA = 0;
|
|
@@ -3472,7 +3236,6 @@ var init_sandbox = __esm({
|
|
|
3472
3236
|
sandboxId;
|
|
3473
3237
|
traceId = null;
|
|
3474
3238
|
proxy;
|
|
3475
|
-
http;
|
|
3476
3239
|
ownsSandbox = false;
|
|
3477
3240
|
lifecycleClient = null;
|
|
3478
3241
|
lifecycleIdentifier;
|
|
@@ -3481,7 +3244,6 @@ var init_sandbox = __esm({
|
|
|
3481
3244
|
this.sandboxId = options.sandboxId;
|
|
3482
3245
|
this.lifecycleIdentifier = options.sandboxId;
|
|
3483
3246
|
this.proxy = new SandboxProxyConnection(this, options);
|
|
3484
|
-
this.http = this.proxy;
|
|
3485
3247
|
}
|
|
3486
3248
|
get baseUrl() {
|
|
3487
3249
|
return this.proxy.baseUrl;
|
|
@@ -3686,9 +3448,9 @@ var init_sandbox = __esm({
|
|
|
3686
3448
|
const filtered = all.filter((s) => s.sandboxId === this.lifecycleIdentifier);
|
|
3687
3449
|
return Object.assign(filtered, { traceId: all.traceId });
|
|
3688
3450
|
}
|
|
3689
|
-
/** Close the
|
|
3451
|
+
/** Close the proxy connection. The sandbox keeps running. */
|
|
3690
3452
|
close() {
|
|
3691
|
-
this.
|
|
3453
|
+
this.proxy.close();
|
|
3692
3454
|
}
|
|
3693
3455
|
/** Terminate the sandbox and release all resources. */
|
|
3694
3456
|
async terminate() {
|
|
@@ -3715,51 +3477,18 @@ var init_sandbox = __esm({
|
|
|
3715
3477
|
if (options?.workingDir) body.working_dir = options.workingDir;
|
|
3716
3478
|
if (options?.timeout != null) body.timeout = options.timeout;
|
|
3717
3479
|
const user = processUserPayload(options?.user);
|
|
3718
|
-
body.user = user;
|
|
3480
|
+
if (user !== void 0) body.user = user;
|
|
3719
3481
|
logSdkTimingEvent("sandbox.run", "start", {
|
|
3720
3482
|
sandbox_id: this.sandboxId,
|
|
3721
3483
|
command: sdkTimingPayloadsEnabled() ? command : void 0,
|
|
3722
3484
|
command_length: command.length
|
|
3723
3485
|
});
|
|
3724
|
-
const
|
|
3725
|
-
const
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
{ json: body }
|
|
3486
|
+
const proxy = await this.proxy.client();
|
|
3487
|
+
const { traceId, events } = await callNative(
|
|
3488
|
+
() => proxy.runProcess(JSON.stringify(body)),
|
|
3489
|
+
{ sandboxId: this.sandboxId }
|
|
3729
3490
|
);
|
|
3730
|
-
const
|
|
3731
|
-
logSdkTiming("sandbox.run", "stream_headers", requestStart, {
|
|
3732
|
-
sandbox_id: this.sandboxId,
|
|
3733
|
-
server_trace_id: traceId,
|
|
3734
|
-
command: sdkTimingPayloadsEnabled() ? command : void 0,
|
|
3735
|
-
command_length: command.length
|
|
3736
|
-
});
|
|
3737
|
-
const stdoutLines = [];
|
|
3738
|
-
const stderrLines = [];
|
|
3739
|
-
let exitCode = -1;
|
|
3740
|
-
const streamStart = nowMs();
|
|
3741
|
-
for await (const raw of parseSSEStream(sseStream)) {
|
|
3742
|
-
if (typeof raw.line === "string") {
|
|
3743
|
-
if (raw.stream === "stderr") {
|
|
3744
|
-
stderrLines.push(raw.line);
|
|
3745
|
-
} else {
|
|
3746
|
-
stdoutLines.push(raw.line);
|
|
3747
|
-
}
|
|
3748
|
-
} else if ("exit_code" in raw || "signal" in raw) {
|
|
3749
|
-
if (typeof raw.exit_code === "number") {
|
|
3750
|
-
exitCode = raw.exit_code;
|
|
3751
|
-
} else if (typeof raw.signal === "number") {
|
|
3752
|
-
exitCode = -raw.signal;
|
|
3753
|
-
}
|
|
3754
|
-
}
|
|
3755
|
-
}
|
|
3756
|
-
logSdkTiming("sandbox.run", "stream_complete", streamStart, {
|
|
3757
|
-
sandbox_id: this.sandboxId,
|
|
3758
|
-
server_trace_id: traceId,
|
|
3759
|
-
command: sdkTimingPayloadsEnabled() ? command : void 0,
|
|
3760
|
-
command_length: command.length,
|
|
3761
|
-
exit_code: exitCode
|
|
3762
|
-
});
|
|
3491
|
+
const { exitCode, stdout, stderr } = assembleCommandResult(events);
|
|
3763
3492
|
logSdkTiming("sandbox.run", "complete", opStart, {
|
|
3764
3493
|
sandbox_id: this.sandboxId,
|
|
3765
3494
|
server_trace_id: traceId,
|
|
@@ -3767,10 +3496,7 @@ var init_sandbox = __esm({
|
|
|
3767
3496
|
command_length: command.length,
|
|
3768
3497
|
exit_code: exitCode
|
|
3769
3498
|
});
|
|
3770
|
-
return Object.assign(
|
|
3771
|
-
{ exitCode, stdout: stdoutLines.join("\n"), stderr: stderrLines.join("\n") },
|
|
3772
|
-
{ traceId }
|
|
3773
|
-
);
|
|
3499
|
+
return Object.assign({ exitCode, stdout, stderr }, { traceId });
|
|
3774
3500
|
}
|
|
3775
3501
|
// --- Process management ---
|
|
3776
3502
|
/**
|
|
@@ -3787,7 +3513,7 @@ var init_sandbox = __esm({
|
|
|
3787
3513
|
if (options?.env != null) payload.env = options.env;
|
|
3788
3514
|
if (options?.workingDir != null) payload.working_dir = options.workingDir;
|
|
3789
3515
|
const user = processUserPayload(options?.user);
|
|
3790
|
-
payload.user = user;
|
|
3516
|
+
if (user !== void 0) payload.user = user;
|
|
3791
3517
|
if (options?.stdinMode != null && options.stdinMode !== "closed" /* CLOSED */) {
|
|
3792
3518
|
payload.stdin_mode = options.stdinMode;
|
|
3793
3519
|
}
|
|
@@ -3802,97 +3528,101 @@ var init_sandbox = __esm({
|
|
|
3802
3528
|
if (options?.healthCheck != null) {
|
|
3803
3529
|
payload.health_check = toSnakeKeys(options.healthCheck);
|
|
3804
3530
|
}
|
|
3805
|
-
const
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
{
|
|
3531
|
+
const proxy = await this.proxy.client();
|
|
3532
|
+
const { traceId, json } = await callNative(
|
|
3533
|
+
() => proxy.startProcess(JSON.stringify(payload)),
|
|
3534
|
+
{ sandboxId: this.sandboxId }
|
|
3809
3535
|
);
|
|
3810
|
-
return Object.assign(fromSnakeKeys(
|
|
3536
|
+
return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
|
|
3811
3537
|
}
|
|
3812
3538
|
/** List all processes (running and exited) tracked by the sandbox daemon. */
|
|
3813
3539
|
async listProcesses() {
|
|
3814
|
-
const
|
|
3815
|
-
|
|
3816
|
-
|
|
3540
|
+
const proxy = await this.proxy.client();
|
|
3541
|
+
const { traceId, json } = await callNative(() => proxy.listProcesses(), {
|
|
3542
|
+
sandboxId: this.sandboxId
|
|
3543
|
+
});
|
|
3544
|
+
const parsed = JSON.parse(json);
|
|
3545
|
+
const processes = (parsed.processes ?? []).map(
|
|
3546
|
+
(p) => fromSnakeKeys(p)
|
|
3817
3547
|
);
|
|
3818
|
-
|
|
3819
|
-
return Object.assign(processes, { traceId: raw.traceId });
|
|
3548
|
+
return Object.assign(processes, { traceId });
|
|
3820
3549
|
}
|
|
3821
3550
|
/** Get current status and metadata for a process by PID. */
|
|
3822
3551
|
async getProcess(pid) {
|
|
3823
|
-
const
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
);
|
|
3827
|
-
return Object.assign(fromSnakeKeys(
|
|
3552
|
+
const proxy = await this.proxy.client();
|
|
3553
|
+
const { traceId, json } = await callNative(() => proxy.getProcess(pid), {
|
|
3554
|
+
sandboxId: this.sandboxId
|
|
3555
|
+
});
|
|
3556
|
+
return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
|
|
3828
3557
|
}
|
|
3829
3558
|
/** Send SIGKILL to a process. */
|
|
3830
3559
|
async killProcess(pid) {
|
|
3831
|
-
await this.
|
|
3560
|
+
const proxy = await this.proxy.client();
|
|
3561
|
+
await callNative(() => proxy.killProcess(pid), { sandboxId: this.sandboxId });
|
|
3832
3562
|
}
|
|
3833
3563
|
/** Restart a managed process by PID. */
|
|
3834
3564
|
async restartProcess(pid) {
|
|
3835
|
-
const
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
);
|
|
3839
|
-
return Object.assign(fromSnakeKeys(
|
|
3565
|
+
const proxy = await this.proxy.client();
|
|
3566
|
+
const { traceId, json } = await callNative(() => proxy.restartProcess(pid), {
|
|
3567
|
+
sandboxId: this.sandboxId
|
|
3568
|
+
});
|
|
3569
|
+
return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
|
|
3840
3570
|
}
|
|
3841
3571
|
/** Send an arbitrary signal to a process (e.g. `15` for SIGTERM, `9` for SIGKILL). */
|
|
3842
3572
|
async sendSignal(pid, signal) {
|
|
3843
|
-
const
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
{
|
|
3573
|
+
const proxy = await this.proxy.client();
|
|
3574
|
+
const { traceId, json } = await callNative(
|
|
3575
|
+
() => proxy.sendSignal(pid, signal),
|
|
3576
|
+
{ sandboxId: this.sandboxId }
|
|
3847
3577
|
);
|
|
3848
|
-
return Object.assign(fromSnakeKeys(
|
|
3578
|
+
return Object.assign(fromSnakeKeys(JSON.parse(json)), {
|
|
3579
|
+
traceId
|
|
3580
|
+
});
|
|
3849
3581
|
}
|
|
3850
3582
|
// --- Process I/O ---
|
|
3851
3583
|
/** Write bytes to a process's stdin. The process must have been started with `stdinMode: StdinMode.PIPE`. */
|
|
3852
3584
|
async writeStdin(pid, data) {
|
|
3853
|
-
await this.
|
|
3854
|
-
|
|
3855
|
-
|
|
3585
|
+
const proxy = await this.proxy.client();
|
|
3586
|
+
await callNative(() => proxy.writeStdin(pid, Buffer.from(data)), {
|
|
3587
|
+
sandboxId: this.sandboxId
|
|
3856
3588
|
});
|
|
3857
3589
|
}
|
|
3858
3590
|
/** Close a process's stdin pipe, signalling EOF to the process. */
|
|
3859
3591
|
async closeStdin(pid) {
|
|
3860
|
-
await this.
|
|
3592
|
+
const proxy = await this.proxy.client();
|
|
3593
|
+
await callNative(() => proxy.closeStdin(pid), { sandboxId: this.sandboxId });
|
|
3861
3594
|
}
|
|
3862
3595
|
/** Return all captured stdout lines produced so far by a process. */
|
|
3863
3596
|
async getStdout(pid) {
|
|
3864
|
-
const
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
);
|
|
3868
|
-
return Object.assign(fromSnakeKeys(
|
|
3597
|
+
const proxy = await this.proxy.client();
|
|
3598
|
+
const { traceId, json } = await callNative(() => proxy.getStdout(pid), {
|
|
3599
|
+
sandboxId: this.sandboxId
|
|
3600
|
+
});
|
|
3601
|
+
return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
|
|
3869
3602
|
}
|
|
3870
3603
|
/** Return all captured stderr lines produced so far by a process. */
|
|
3871
3604
|
async getStderr(pid) {
|
|
3872
|
-
const
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
);
|
|
3876
|
-
return Object.assign(fromSnakeKeys(
|
|
3605
|
+
const proxy = await this.proxy.client();
|
|
3606
|
+
const { traceId, json } = await callNative(() => proxy.getStderr(pid), {
|
|
3607
|
+
sandboxId: this.sandboxId
|
|
3608
|
+
});
|
|
3609
|
+
return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
|
|
3877
3610
|
}
|
|
3878
3611
|
/** Return all captured stdout+stderr lines produced so far by a process. */
|
|
3879
3612
|
async getOutput(pid) {
|
|
3880
|
-
const
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
);
|
|
3884
|
-
return Object.assign(fromSnakeKeys(
|
|
3613
|
+
const proxy = await this.proxy.client();
|
|
3614
|
+
const { traceId, json } = await callNative(() => proxy.getOutput(pid), {
|
|
3615
|
+
sandboxId: this.sandboxId
|
|
3616
|
+
});
|
|
3617
|
+
return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
|
|
3885
3618
|
}
|
|
3886
3619
|
// --- Streaming (SSE) ---
|
|
3887
3620
|
/** Stream stdout events from a process until it exits. Yields one `OutputEvent` per line. */
|
|
3888
3621
|
async *followStdout(pid, options) {
|
|
3889
|
-
const
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
);
|
|
3894
|
-
for await (const raw of parseSSEStream(
|
|
3895
|
-
stream,
|
|
3622
|
+
const proxy = await this.proxy.client();
|
|
3623
|
+
for await (const raw of nativeEventStream(
|
|
3624
|
+
(emit) => proxy.followStdout(pid, emit),
|
|
3625
|
+
{ sandboxId: this.sandboxId },
|
|
3896
3626
|
options?.signal
|
|
3897
3627
|
)) {
|
|
3898
3628
|
yield fromSnakeKeys(raw);
|
|
@@ -3900,13 +3630,10 @@ var init_sandbox = __esm({
|
|
|
3900
3630
|
}
|
|
3901
3631
|
/** Stream stderr events from a process until it exits. Yields one `OutputEvent` per line. */
|
|
3902
3632
|
async *followStderr(pid, options) {
|
|
3903
|
-
const
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
);
|
|
3908
|
-
for await (const raw of parseSSEStream(
|
|
3909
|
-
stream,
|
|
3633
|
+
const proxy = await this.proxy.client();
|
|
3634
|
+
for await (const raw of nativeEventStream(
|
|
3635
|
+
(emit) => proxy.followStderr(pid, emit),
|
|
3636
|
+
{ sandboxId: this.sandboxId },
|
|
3910
3637
|
options?.signal
|
|
3911
3638
|
)) {
|
|
3912
3639
|
yield fromSnakeKeys(raw);
|
|
@@ -3914,13 +3641,10 @@ var init_sandbox = __esm({
|
|
|
3914
3641
|
}
|
|
3915
3642
|
/** Stream combined stdout+stderr events from a process until it exits. Yields one `OutputEvent` per line. */
|
|
3916
3643
|
async *followOutput(pid, options) {
|
|
3917
|
-
const
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
);
|
|
3922
|
-
for await (const raw of parseSSEStream(
|
|
3923
|
-
stream,
|
|
3644
|
+
const proxy = await this.proxy.client();
|
|
3645
|
+
for await (const raw of nativeEventStream(
|
|
3646
|
+
(emit) => proxy.followOutput(pid, emit),
|
|
3647
|
+
{ sandboxId: this.sandboxId },
|
|
3924
3648
|
options?.signal
|
|
3925
3649
|
)) {
|
|
3926
3650
|
yield fromSnakeKeys(raw);
|
|
@@ -3929,33 +3653,33 @@ var init_sandbox = __esm({
|
|
|
3929
3653
|
// --- File operations ---
|
|
3930
3654
|
/** Read a file from the sandbox and return its raw bytes. */
|
|
3931
3655
|
async readFile(path2) {
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
);
|
|
3656
|
+
const proxy = await this.proxy.client();
|
|
3657
|
+
const { traceId, data } = await callNative(() => proxy.readFile(path2), {
|
|
3658
|
+
sandboxId: this.sandboxId
|
|
3659
|
+
});
|
|
3660
|
+
return Object.assign(Uint8Array.from(data), { traceId });
|
|
3936
3661
|
}
|
|
3937
3662
|
/** Write raw bytes to a file in the sandbox, creating it if it does not exist. */
|
|
3938
3663
|
async writeFile(path2, content) {
|
|
3939
|
-
await this.
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
);
|
|
3664
|
+
const proxy = await this.proxy.client();
|
|
3665
|
+
await callNative(() => proxy.writeFile(path2, Buffer.from(content)), {
|
|
3666
|
+
sandboxId: this.sandboxId
|
|
3667
|
+
});
|
|
3944
3668
|
}
|
|
3945
3669
|
/** Delete a file from the sandbox. */
|
|
3946
3670
|
async deleteFile(path2) {
|
|
3947
|
-
await this.
|
|
3948
|
-
|
|
3949
|
-
`/api/v1/files?path=${encodeURIComponent(path2)}`
|
|
3950
|
-
);
|
|
3671
|
+
const proxy = await this.proxy.client();
|
|
3672
|
+
await callNative(() => proxy.deleteFile(path2), { sandboxId: this.sandboxId });
|
|
3951
3673
|
}
|
|
3952
3674
|
/** List the contents of a directory in the sandbox. */
|
|
3953
3675
|
async listDirectory(path2) {
|
|
3954
|
-
const
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
);
|
|
3958
|
-
return Object.assign(fromSnakeKeys(
|
|
3676
|
+
const proxy = await this.proxy.client();
|
|
3677
|
+
const { traceId, json } = await callNative(() => proxy.listDirectory(path2), {
|
|
3678
|
+
sandboxId: this.sandboxId
|
|
3679
|
+
});
|
|
3680
|
+
return Object.assign(fromSnakeKeys(JSON.parse(json)), {
|
|
3681
|
+
traceId
|
|
3682
|
+
});
|
|
3959
3683
|
}
|
|
3960
3684
|
// --- PTY ---
|
|
3961
3685
|
/** Create an interactive PTY session. Returns a `sessionId` and `token` for WebSocket connection via `connectPty()`. */
|
|
@@ -3968,12 +3692,12 @@ var init_sandbox = __esm({
|
|
|
3968
3692
|
if (options.args != null) payload.args = options.args;
|
|
3969
3693
|
if (options.env != null) payload.env = options.env;
|
|
3970
3694
|
if (options.workingDir != null) payload.working_dir = options.workingDir;
|
|
3971
|
-
const
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
{
|
|
3695
|
+
const proxy = await this.proxy.client();
|
|
3696
|
+
const { traceId, json } = await callNative(
|
|
3697
|
+
() => proxy.createPtySession(JSON.stringify(payload)),
|
|
3698
|
+
{ sandboxId: this.sandboxId }
|
|
3975
3699
|
);
|
|
3976
|
-
return Object.assign(fromSnakeKeys(
|
|
3700
|
+
return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
|
|
3977
3701
|
}
|
|
3978
3702
|
/** Create a PTY session and connect to it immediately. Cleans up the session if the WebSocket connection fails. */
|
|
3979
3703
|
async createPty(options) {
|
|
@@ -3983,7 +3707,8 @@ var init_sandbox = __esm({
|
|
|
3983
3707
|
return await this.connectPty(session.sessionId, session.token, { onData, onExit });
|
|
3984
3708
|
} catch (error) {
|
|
3985
3709
|
try {
|
|
3986
|
-
await this.
|
|
3710
|
+
const proxy = await this.proxy.client();
|
|
3711
|
+
await proxy.deletePtySession(session.sessionId);
|
|
3987
3712
|
} catch {
|
|
3988
3713
|
}
|
|
3989
3714
|
throw error;
|
|
@@ -4003,7 +3728,8 @@ var init_sandbox = __esm({
|
|
|
4003
3728
|
"X-PTY-Token": authToken
|
|
4004
3729
|
},
|
|
4005
3730
|
killSession: async () => {
|
|
4006
|
-
await this.
|
|
3731
|
+
const proxy = await this.proxy.client();
|
|
3732
|
+
await proxy.deletePtySession(sessionId);
|
|
4007
3733
|
}
|
|
4008
3734
|
});
|
|
4009
3735
|
if (options?.onData) {
|
|
@@ -4091,19 +3817,19 @@ var init_sandbox = __esm({
|
|
|
4091
3817
|
// --- Health ---
|
|
4092
3818
|
/** Check the sandbox daemon health. */
|
|
4093
3819
|
async health() {
|
|
4094
|
-
const
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
);
|
|
4098
|
-
return Object.assign(fromSnakeKeys(
|
|
3820
|
+
const proxy = await this.proxy.client();
|
|
3821
|
+
const { traceId, json } = await callNative(() => proxy.health(), {
|
|
3822
|
+
sandboxId: this.sandboxId
|
|
3823
|
+
});
|
|
3824
|
+
return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
|
|
4099
3825
|
}
|
|
4100
3826
|
/** Get sandbox daemon info (version, uptime, process counts). */
|
|
4101
3827
|
async daemonInfo() {
|
|
4102
|
-
const
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
);
|
|
4106
|
-
return Object.assign(fromSnakeKeys(
|
|
3828
|
+
const proxy = await this.proxy.client();
|
|
3829
|
+
const { traceId, json } = await callNative(() => proxy.info(), {
|
|
3830
|
+
sandboxId: this.sandboxId
|
|
3831
|
+
});
|
|
3832
|
+
return Object.assign(fromSnakeKeys(JSON.parse(json)), { traceId });
|
|
4107
3833
|
}
|
|
4108
3834
|
};
|
|
4109
3835
|
}
|
|
@@ -4160,7 +3886,7 @@ async function readCloudInitBase64(cloudInit) {
|
|
|
4160
3886
|
}
|
|
4161
3887
|
function resolveRequestTimeoutMs(options) {
|
|
4162
3888
|
if (options?.requestTimeout != null) {
|
|
4163
|
-
return
|
|
3889
|
+
return secondsToMillis3(options.requestTimeout);
|
|
4164
3890
|
}
|
|
4165
3891
|
if (options?.timeoutMs != null) {
|
|
4166
3892
|
validateTimeoutMs(options.timeoutMs);
|
|
@@ -4168,7 +3894,7 @@ function resolveRequestTimeoutMs(options) {
|
|
|
4168
3894
|
}
|
|
4169
3895
|
return DEFAULT_HTTP_TIMEOUT_MS;
|
|
4170
3896
|
}
|
|
4171
|
-
function
|
|
3897
|
+
function secondsToMillis3(seconds) {
|
|
4172
3898
|
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
4173
3899
|
throw new SandboxError("requestTimeout must be a positive number of seconds");
|
|
4174
3900
|
}
|
|
@@ -4179,7 +3905,7 @@ function validateTimeoutMs(timeoutMs) {
|
|
|
4179
3905
|
throw new SandboxError("timeoutMs must be a positive number of milliseconds");
|
|
4180
3906
|
}
|
|
4181
3907
|
}
|
|
4182
|
-
function
|
|
3908
|
+
function sleep(ms) {
|
|
4183
3909
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4184
3910
|
}
|
|
4185
3911
|
function snapshotStatusSatisfiesWaitCondition(status, waitUntil) {
|
|
@@ -4235,31 +3961,25 @@ function validateUserPort(port) {
|
|
|
4235
3961
|
function dedupeAndSortPorts(ports) {
|
|
4236
3962
|
return [...new Set(ports)].sort((a, b) => a - b);
|
|
4237
3963
|
}
|
|
4238
|
-
var
|
|
3964
|
+
var MAX_CLOUD_INIT_USER_DATA_BYTES, SandboxClient, RESERVED_SANDBOX_MANAGEMENT_PORT;
|
|
4239
3965
|
var init_client = __esm({
|
|
4240
3966
|
"src/client.ts"() {
|
|
4241
3967
|
"use strict";
|
|
4242
3968
|
init_defaults();
|
|
4243
3969
|
init_errors();
|
|
4244
|
-
|
|
3970
|
+
init_native_sandbox();
|
|
4245
3971
|
init_models();
|
|
4246
3972
|
init_sandbox();
|
|
4247
3973
|
init_sdk_timings();
|
|
4248
3974
|
init_url();
|
|
4249
|
-
CREATE_SANDBOX_RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([502, 503]);
|
|
4250
|
-
CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES = /* @__PURE__ */ new Set([504]);
|
|
4251
|
-
COPY_SANDBOX_ALLOWED_ERROR_STATUS_CODES = /* @__PURE__ */ new Set([422, 504]);
|
|
4252
3975
|
MAX_CLOUD_INIT_USER_DATA_BYTES = 16 * 1024;
|
|
4253
3976
|
SandboxClient = class _SandboxClient {
|
|
4254
|
-
|
|
3977
|
+
native;
|
|
4255
3978
|
apiUrl;
|
|
4256
3979
|
apiKey;
|
|
4257
3980
|
organizationId;
|
|
4258
3981
|
projectId;
|
|
4259
3982
|
namespace;
|
|
4260
|
-
local;
|
|
4261
|
-
maxRetries;
|
|
4262
|
-
retryBackoffMs;
|
|
4263
3983
|
requestTimeoutMs;
|
|
4264
3984
|
/** @internal Pass `true` to suppress the deprecation warning when used by `Sandbox.create()` / `Sandbox.connect()`. */
|
|
4265
3985
|
constructor(options, _internal = false) {
|
|
@@ -4273,19 +3993,17 @@ var init_client = __esm({
|
|
|
4273
3993
|
this.organizationId = options?.organizationId;
|
|
4274
3994
|
this.projectId = options?.projectId;
|
|
4275
3995
|
this.namespace = options?.namespace ?? NAMESPACE;
|
|
4276
|
-
this.local = isLocalhost(this.apiUrl);
|
|
4277
|
-
this.maxRetries = options?.maxRetries ?? MAX_RETRIES;
|
|
4278
|
-
this.retryBackoffMs = options?.retryBackoffMs ?? RETRY_BACKOFF_MS;
|
|
4279
3996
|
this.requestTimeoutMs = resolveRequestTimeoutMs(options);
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
3997
|
+
const binding = loadNativeSandboxBinding();
|
|
3998
|
+
this.native = new binding.NativeSandboxClient(
|
|
3999
|
+
this.apiUrl,
|
|
4000
|
+
this.apiKey ?? null,
|
|
4001
|
+
this.organizationId ?? null,
|
|
4002
|
+
this.projectId ?? null,
|
|
4003
|
+
this.namespace,
|
|
4004
|
+
null,
|
|
4005
|
+
this.requestTimeoutMs / 1e3
|
|
4006
|
+
);
|
|
4289
4007
|
}
|
|
4290
4008
|
/** Create a client for the TensorLake cloud platform. */
|
|
4291
4009
|
static forCloud(options) {
|
|
@@ -4308,13 +4026,12 @@ var init_client = __esm({
|
|
|
4308
4026
|
});
|
|
4309
4027
|
}
|
|
4310
4028
|
close() {
|
|
4311
|
-
this.http.close();
|
|
4312
4029
|
}
|
|
4313
4030
|
withRequestTimeout(requestTimeout) {
|
|
4314
4031
|
if (requestTimeout == null) {
|
|
4315
4032
|
return this;
|
|
4316
4033
|
}
|
|
4317
|
-
const timeoutMs =
|
|
4034
|
+
const timeoutMs = secondsToMillis3(requestTimeout);
|
|
4318
4035
|
if (timeoutMs === this.requestTimeoutMs) {
|
|
4319
4036
|
return this;
|
|
4320
4037
|
}
|
|
@@ -4325,17 +4042,24 @@ var init_client = __esm({
|
|
|
4325
4042
|
organizationId: this.organizationId,
|
|
4326
4043
|
projectId: this.projectId,
|
|
4327
4044
|
namespace: this.namespace,
|
|
4328
|
-
maxRetries: this.maxRetries,
|
|
4329
|
-
retryBackoffMs: this.retryBackoffMs,
|
|
4330
4045
|
timeoutMs
|
|
4331
4046
|
},
|
|
4332
4047
|
/* _internal */
|
|
4333
4048
|
true
|
|
4334
4049
|
);
|
|
4335
4050
|
}
|
|
4336
|
-
// ---
|
|
4337
|
-
|
|
4338
|
-
|
|
4051
|
+
// --- Native marshalling helpers ---
|
|
4052
|
+
/** Run a native JSON call and reshape it into a `Traced<T>`. */
|
|
4053
|
+
async tracedJson(fn, idField, context) {
|
|
4054
|
+
const { traceId, json } = await callNative(fn, context);
|
|
4055
|
+
return Object.assign(fromSnakeKeys(JSON.parse(json), idField), {
|
|
4056
|
+
traceId
|
|
4057
|
+
});
|
|
4058
|
+
}
|
|
4059
|
+
/** Run a native JSON call and reshape it into a plain `T` (no trace id). */
|
|
4060
|
+
async plainJson(fn, idField, context) {
|
|
4061
|
+
const { json } = await callNative(fn, context);
|
|
4062
|
+
return fromSnakeKeys(JSON.parse(json), idField);
|
|
4339
4063
|
}
|
|
4340
4064
|
// --- Sandbox CRUD ---
|
|
4341
4065
|
/** Create a new sandbox. Returns immediately; the sandbox may still be starting. Use `createAndConnect()` for a blocking, ready-to-use handle. */
|
|
@@ -4361,36 +4085,27 @@ var init_client = __esm({
|
|
|
4361
4085
|
deny_out: options?.denyOut ?? []
|
|
4362
4086
|
};
|
|
4363
4087
|
}
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
{
|
|
4368
|
-
body,
|
|
4369
|
-
retryableStatusCodes: CREATE_SANDBOX_RETRYABLE_STATUS_CODES,
|
|
4370
|
-
allowedErrorStatusCodes: CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES
|
|
4371
|
-
}
|
|
4088
|
+
return this.tracedJson(
|
|
4089
|
+
() => this.native.createSandbox(JSON.stringify(body)),
|
|
4090
|
+
"sandboxId"
|
|
4372
4091
|
);
|
|
4373
|
-
const result = fromSnakeKeys(raw, "sandboxId");
|
|
4374
|
-
return Object.assign(result, { traceId: raw.traceId });
|
|
4375
4092
|
}
|
|
4376
4093
|
/** Get current state and metadata for a sandbox by ID. */
|
|
4377
4094
|
async get(sandboxId) {
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4095
|
+
return this.tracedJson(
|
|
4096
|
+
() => this.native.getSandbox(sandboxId),
|
|
4097
|
+
"sandboxId",
|
|
4098
|
+
{ sandboxId, notFoundKind: "sandbox" }
|
|
4381
4099
|
);
|
|
4382
|
-
return Object.assign(fromSnakeKeys(raw, "sandboxId"), { traceId: raw.traceId });
|
|
4383
4100
|
}
|
|
4384
4101
|
/** List all sandboxes in the namespace. */
|
|
4385
4102
|
async list() {
|
|
4386
|
-
const
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
);
|
|
4390
|
-
const sandboxes = (raw.sandboxes ?? []).map(
|
|
4103
|
+
const { traceId, json } = await callNative(() => this.native.listSandboxes());
|
|
4104
|
+
const parsed = JSON.parse(json);
|
|
4105
|
+
const sandboxes = (parsed.sandboxes ?? []).map(
|
|
4391
4106
|
(s) => fromSnakeKeys(s, "sandboxId")
|
|
4392
4107
|
);
|
|
4393
|
-
return Object.assign(sandboxes, { traceId
|
|
4108
|
+
return Object.assign(sandboxes, { traceId });
|
|
4394
4109
|
}
|
|
4395
4110
|
/**
|
|
4396
4111
|
* List archived (terminated) sandboxes in the namespace.
|
|
@@ -4399,37 +4114,30 @@ var init_client = __esm({
|
|
|
4399
4114
|
* archived sandboxes store until the server-configured TTL expires.
|
|
4400
4115
|
*/
|
|
4401
4116
|
async listArchived(options) {
|
|
4402
|
-
const
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
}
|
|
4412
|
-
const suffix = query.length ? `?${query.join("&")}` : "";
|
|
4413
|
-
const raw = await this.http.requestJson("GET", this.path(`archived-sandboxes${suffix}`));
|
|
4414
|
-
const sandboxes = (raw.sandboxes ?? []).map(
|
|
4117
|
+
const { traceId, json } = await callNative(
|
|
4118
|
+
() => this.native.listArchivedSandboxes(
|
|
4119
|
+
options?.limit ?? null,
|
|
4120
|
+
options?.cursor ?? null,
|
|
4121
|
+
options?.direction ?? null
|
|
4122
|
+
)
|
|
4123
|
+
);
|
|
4124
|
+
const parsed = JSON.parse(json);
|
|
4125
|
+
const sandboxes = (parsed.sandboxes ?? []).map(
|
|
4415
4126
|
(s) => fromSnakeKeys(s, "sandboxId")
|
|
4416
4127
|
);
|
|
4417
4128
|
const response = {
|
|
4418
4129
|
sandboxes,
|
|
4419
|
-
prevCursor:
|
|
4420
|
-
nextCursor:
|
|
4130
|
+
prevCursor: parsed.prev_cursor,
|
|
4131
|
+
nextCursor: parsed.next_cursor
|
|
4421
4132
|
};
|
|
4422
|
-
return Object.assign(response, { traceId
|
|
4133
|
+
return Object.assign(response, { traceId });
|
|
4423
4134
|
}
|
|
4424
4135
|
/** Get a single archived sandbox by id. */
|
|
4425
4136
|
async getArchived(sandboxId) {
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
return Object.assign(
|
|
4431
|
-
fromSnakeKeys(raw, "sandboxId"),
|
|
4432
|
-
{ traceId: raw.traceId }
|
|
4137
|
+
return this.tracedJson(
|
|
4138
|
+
() => this.native.getArchivedSandbox(sandboxId),
|
|
4139
|
+
"sandboxId",
|
|
4140
|
+
{ sandboxId, notFoundKind: "sandbox" }
|
|
4433
4141
|
);
|
|
4434
4142
|
}
|
|
4435
4143
|
/** Update sandbox properties such as name, exposed ports, and proxy auth settings. */
|
|
@@ -4445,12 +4153,11 @@ var init_client = __esm({
|
|
|
4445
4153
|
if (Object.keys(body).length === 0) {
|
|
4446
4154
|
throw new SandboxError("At least one sandbox update field must be provided.");
|
|
4447
4155
|
}
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
{
|
|
4156
|
+
return this.tracedJson(
|
|
4157
|
+
() => this.native.updateSandbox(sandboxId, JSON.stringify(body)),
|
|
4158
|
+
"sandboxId",
|
|
4159
|
+
{ sandboxId, notFoundKind: "sandbox" }
|
|
4452
4160
|
);
|
|
4453
|
-
return Object.assign(fromSnakeKeys(raw, "sandboxId"), { traceId: raw.traceId });
|
|
4454
4161
|
}
|
|
4455
4162
|
/** Get the current proxy port settings for a sandbox. */
|
|
4456
4163
|
async getPortAccess(sandboxId) {
|
|
@@ -4488,10 +4195,7 @@ var init_client = __esm({
|
|
|
4488
4195
|
}
|
|
4489
4196
|
/** Terminate and delete a sandbox. */
|
|
4490
4197
|
async delete(sandboxId) {
|
|
4491
|
-
await this.
|
|
4492
|
-
"DELETE",
|
|
4493
|
-
this.path(`sandboxes/${sandboxId}`)
|
|
4494
|
-
);
|
|
4198
|
+
await callNative(() => this.native.deleteSandbox(sandboxId), { sandboxId, notFoundKind: "sandbox" });
|
|
4495
4199
|
}
|
|
4496
4200
|
/**
|
|
4497
4201
|
* Suspend a named sandbox, preserving its state for later resume.
|
|
@@ -4500,18 +4204,9 @@ var init_client = __esm({
|
|
|
4500
4204
|
* cannot. By default blocks until the sandbox is fully `Suspended`. Pass
|
|
4501
4205
|
* `{ wait: false }` to return immediately after the request is sent
|
|
4502
4206
|
* (fire-and-return); the server processes the suspend asynchronously.
|
|
4503
|
-
*
|
|
4504
|
-
* @param sandboxId - ID or name of the sandbox.
|
|
4505
|
-
* @param options.wait - If `true` (default), poll until `Suspended`. Pass `false` to fire-and-return.
|
|
4506
|
-
* @param options.timeout - Max seconds to wait when `wait=true` (default 300).
|
|
4507
|
-
* @param options.pollInterval - Seconds between status polls when `wait=true` (default 1).
|
|
4508
|
-
* @throws {SandboxError} If `wait=true` and the sandbox does not reach `Suspended` within `timeout`.
|
|
4509
4207
|
*/
|
|
4510
4208
|
async suspend(sandboxId, options) {
|
|
4511
|
-
await this.
|
|
4512
|
-
"POST",
|
|
4513
|
-
this.path(`sandboxes/${sandboxId}/suspend`)
|
|
4514
|
-
);
|
|
4209
|
+
await callNative(() => this.native.suspendSandbox(sandboxId), { sandboxId, notFoundKind: "sandbox" });
|
|
4515
4210
|
if (options?.wait === false) return;
|
|
4516
4211
|
const timeout = options?.timeout ?? 300;
|
|
4517
4212
|
const pollInterval = options?.pollInterval ?? 1;
|
|
@@ -4522,7 +4217,7 @@ var init_client = __esm({
|
|
|
4522
4217
|
if (info.status === "terminated" /* TERMINATED */) {
|
|
4523
4218
|
throw new SandboxError(`Sandbox ${sandboxId} terminated while waiting for suspend`);
|
|
4524
4219
|
}
|
|
4525
|
-
await
|
|
4220
|
+
await sleep(pollInterval * 1e3);
|
|
4526
4221
|
}
|
|
4527
4222
|
throw new SandboxError(`Sandbox ${sandboxId} did not suspend within ${timeout}s`);
|
|
4528
4223
|
}
|
|
@@ -4532,18 +4227,9 @@ var init_client = __esm({
|
|
|
4532
4227
|
* By default blocks until the sandbox is `Running` and routable. Pass
|
|
4533
4228
|
* `{ wait: false }` to return immediately after the request is sent
|
|
4534
4229
|
* (fire-and-return); the server processes the resume asynchronously.
|
|
4535
|
-
*
|
|
4536
|
-
* @param sandboxId - ID or name of the sandbox.
|
|
4537
|
-
* @param options.wait - If `true` (default), poll until `Running`. Pass `false` to fire-and-return.
|
|
4538
|
-
* @param options.timeout - Max seconds to wait when `wait=true` (default 300).
|
|
4539
|
-
* @param options.pollInterval - Seconds between status polls when `wait=true` (default 1).
|
|
4540
|
-
* @throws {SandboxError} If `wait=true` and the sandbox does not reach `Running` within `timeout`.
|
|
4541
4230
|
*/
|
|
4542
4231
|
async resume(sandboxId, options) {
|
|
4543
|
-
await this.
|
|
4544
|
-
"POST",
|
|
4545
|
-
this.path(`sandboxes/${sandboxId}/resume`)
|
|
4546
|
-
);
|
|
4232
|
+
await callNative(() => this.native.resumeSandbox(sandboxId), { sandboxId, notFoundKind: "sandbox" });
|
|
4547
4233
|
if (options?.wait === false) return;
|
|
4548
4234
|
const timeout = options?.timeout ?? 300;
|
|
4549
4235
|
const pollInterval = options?.pollInterval ?? 1;
|
|
@@ -4554,22 +4240,17 @@ var init_client = __esm({
|
|
|
4554
4240
|
if (info.status === "terminated" /* TERMINATED */) {
|
|
4555
4241
|
throw new SandboxError(`Sandbox ${sandboxId} terminated while waiting for resume`);
|
|
4556
4242
|
}
|
|
4557
|
-
await
|
|
4243
|
+
await sleep(pollInterval * 1e3);
|
|
4558
4244
|
}
|
|
4559
4245
|
throw new SandboxError(`Sandbox ${sandboxId} did not resume within ${timeout}s`);
|
|
4560
4246
|
}
|
|
4561
4247
|
/** Claim a warm sandbox from a pool, creating one if no warm containers are available. */
|
|
4562
4248
|
async claim(poolId) {
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
{
|
|
4567
|
-
retryableStatusCodes: CREATE_SANDBOX_RETRYABLE_STATUS_CODES,
|
|
4568
|
-
allowedErrorStatusCodes: CREATE_SANDBOX_ALLOWED_ERROR_STATUS_CODES
|
|
4569
|
-
}
|
|
4249
|
+
return this.tracedJson(
|
|
4250
|
+
() => this.native.claimSandbox(poolId),
|
|
4251
|
+
"sandboxId",
|
|
4252
|
+
{ poolId, notFoundKind: "pool" }
|
|
4570
4253
|
);
|
|
4571
|
-
const result = fromSnakeKeys(raw, "sandboxId");
|
|
4572
|
-
return Object.assign(result, { traceId: raw.traceId });
|
|
4573
4254
|
}
|
|
4574
4255
|
/**
|
|
4575
4256
|
* Live-copy a running sandbox.
|
|
@@ -4584,13 +4265,11 @@ var init_client = __esm({
|
|
|
4584
4265
|
throw new SandboxError("times must be a positive integer");
|
|
4585
4266
|
}
|
|
4586
4267
|
const client = this.withRequestTimeout(options?.requestTimeout);
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
{
|
|
4268
|
+
return client.tracedJson(
|
|
4269
|
+
() => client.native.copySandbox(sandboxId, times),
|
|
4270
|
+
"sandboxId",
|
|
4271
|
+
{ sandboxId, notFoundKind: "sandbox" }
|
|
4591
4272
|
);
|
|
4592
|
-
const result = fromSnakeKeys(raw, "sandboxId");
|
|
4593
|
-
return Object.assign(result, { traceId: raw.traceId });
|
|
4594
4273
|
}
|
|
4595
4274
|
// --- Snapshots ---
|
|
4596
4275
|
/**
|
|
@@ -4600,44 +4279,33 @@ var init_client = __esm({
|
|
|
4600
4279
|
* status — the snapshot is created asynchronously. Poll `getSnapshot()` until
|
|
4601
4280
|
* `local_ready`, `completed`, or `failed`, or use `snapshotAndWait()` to
|
|
4602
4281
|
* block automatically.
|
|
4603
|
-
*
|
|
4604
|
-
* @param options.snapshotType - `"filesystem"` for cold-boot snapshots (e.g. image builds).
|
|
4605
|
-
* Omit to use the server default (`filesystem`).
|
|
4606
4282
|
*/
|
|
4607
4283
|
async snapshot(sandboxId, options) {
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
"
|
|
4611
|
-
|
|
4612
|
-
requestOptions
|
|
4284
|
+
return this.plainJson(
|
|
4285
|
+
() => this.native.createSnapshot(sandboxId, options?.snapshotType ?? null),
|
|
4286
|
+
"snapshotId",
|
|
4287
|
+
{ sandboxId, notFoundKind: "sandbox" }
|
|
4613
4288
|
);
|
|
4614
|
-
return fromSnakeKeys(raw, "snapshotId");
|
|
4615
4289
|
}
|
|
4616
4290
|
/** Get current status and metadata for a snapshot by ID. */
|
|
4617
4291
|
async getSnapshot(snapshotId) {
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4292
|
+
return this.tracedJson(
|
|
4293
|
+
() => this.native.getSnapshot(snapshotId),
|
|
4294
|
+
"snapshotId"
|
|
4621
4295
|
);
|
|
4622
|
-
return Object.assign(fromSnakeKeys(raw, "snapshotId"), { traceId: raw.traceId });
|
|
4623
4296
|
}
|
|
4624
4297
|
/** List all snapshots in the namespace. */
|
|
4625
4298
|
async listSnapshots() {
|
|
4626
|
-
const
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
);
|
|
4630
|
-
const snapshots = (raw.snapshots ?? []).map(
|
|
4299
|
+
const { traceId, json } = await callNative(() => this.native.listSnapshots());
|
|
4300
|
+
const parsed = JSON.parse(json);
|
|
4301
|
+
const snapshots = (parsed.snapshots ?? []).map(
|
|
4631
4302
|
(s) => fromSnakeKeys(s, "snapshotId")
|
|
4632
4303
|
);
|
|
4633
|
-
return Object.assign(snapshots, { traceId
|
|
4304
|
+
return Object.assign(snapshots, { traceId });
|
|
4634
4305
|
}
|
|
4635
4306
|
/** Delete a snapshot by ID. */
|
|
4636
4307
|
async deleteSnapshot(snapshotId) {
|
|
4637
|
-
await this.
|
|
4638
|
-
"DELETE",
|
|
4639
|
-
this.path(`snapshots/${snapshotId}`)
|
|
4640
|
-
);
|
|
4308
|
+
await callNative(() => this.native.deleteSnapshot(snapshotId));
|
|
4641
4309
|
}
|
|
4642
4310
|
/**
|
|
4643
4311
|
* Create a snapshot and block until it is locally ready.
|
|
@@ -4645,14 +4313,6 @@ var init_client = __esm({
|
|
|
4645
4313
|
* Combines `snapshot()` with polling `getSnapshot()` until `local_ready`
|
|
4646
4314
|
* or `completed`. Pass `{ waitUntil: "completed" }` when durable
|
|
4647
4315
|
* `snapshotUri` metadata is required.
|
|
4648
|
-
* Prefer `sandbox.checkpoint()` on a `Sandbox` handle for the same behavior
|
|
4649
|
-
* without managing the client separately.
|
|
4650
|
-
*
|
|
4651
|
-
* @param sandboxId - ID of the running sandbox to snapshot.
|
|
4652
|
-
* @param options.timeout - Max seconds to wait (default 300).
|
|
4653
|
-
* @param options.pollInterval - Seconds between status polls (default 1).
|
|
4654
|
-
* @param options.snapshotType - Snapshot type passed through to `snapshot()`.
|
|
4655
|
-
* @throws {SandboxError} If the snapshot fails or `timeout` elapses.
|
|
4656
4316
|
*/
|
|
4657
4317
|
async snapshotAndWait(sandboxId, options) {
|
|
4658
4318
|
const timeout = options?.timeout ?? 300;
|
|
@@ -4670,7 +4330,7 @@ var init_client = __esm({
|
|
|
4670
4330
|
`Snapshot ${result.snapshotId} failed: ${info.error}`
|
|
4671
4331
|
);
|
|
4672
4332
|
}
|
|
4673
|
-
await
|
|
4333
|
+
await sleep(pollInterval * 1e3);
|
|
4674
4334
|
}
|
|
4675
4335
|
throw new SandboxError(
|
|
4676
4336
|
`Snapshot ${result.snapshotId} did not reach ${waitUntil} within ${timeout}s`
|
|
@@ -4691,31 +4351,27 @@ var init_client = __esm({
|
|
|
4691
4351
|
if (options.entrypoint != null) body.entrypoint = options.entrypoint;
|
|
4692
4352
|
if (options.maxContainers != null) body.max_containers = options.maxContainers;
|
|
4693
4353
|
if (options.warmContainers != null) body.warm_containers = options.warmContainers;
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
{ body }
|
|
4354
|
+
return this.plainJson(
|
|
4355
|
+
() => this.native.createPool(JSON.stringify(body)),
|
|
4356
|
+
"poolId"
|
|
4698
4357
|
);
|
|
4699
|
-
return fromSnakeKeys(raw, "poolId");
|
|
4700
4358
|
}
|
|
4701
4359
|
/** Get current state and metadata for a sandbox pool by ID. */
|
|
4702
4360
|
async getPool(poolId) {
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4361
|
+
return this.plainJson(
|
|
4362
|
+
() => this.native.getPool(poolId),
|
|
4363
|
+
"poolId",
|
|
4364
|
+
{ poolId, notFoundKind: "pool" }
|
|
4706
4365
|
);
|
|
4707
|
-
return fromSnakeKeys(raw, "poolId");
|
|
4708
4366
|
}
|
|
4709
4367
|
/** List all sandbox pools in the namespace. */
|
|
4710
4368
|
async listPools() {
|
|
4711
|
-
const
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
);
|
|
4715
|
-
const pools = (raw.pools ?? []).map(
|
|
4369
|
+
const { traceId, json } = await callNative(() => this.native.listPools());
|
|
4370
|
+
const parsed = JSON.parse(json);
|
|
4371
|
+
const pools = (parsed.pools ?? []).map(
|
|
4716
4372
|
(p) => fromSnakeKeys(p, "poolId")
|
|
4717
4373
|
);
|
|
4718
|
-
return Object.assign(pools, { traceId
|
|
4374
|
+
return Object.assign(pools, { traceId });
|
|
4719
4375
|
}
|
|
4720
4376
|
/** Replace the configuration of an existing sandbox pool. */
|
|
4721
4377
|
async updatePool(poolId, options) {
|
|
@@ -4731,19 +4387,15 @@ var init_client = __esm({
|
|
|
4731
4387
|
if (options.entrypoint != null) body.entrypoint = options.entrypoint;
|
|
4732
4388
|
if (options.maxContainers != null) body.max_containers = options.maxContainers;
|
|
4733
4389
|
if (options.warmContainers != null) body.warm_containers = options.warmContainers;
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
{
|
|
4390
|
+
return this.plainJson(
|
|
4391
|
+
() => this.native.updatePool(poolId, JSON.stringify(body)),
|
|
4392
|
+
"poolId",
|
|
4393
|
+
{ poolId, notFoundKind: "pool" }
|
|
4738
4394
|
);
|
|
4739
|
-
return fromSnakeKeys(raw, "poolId");
|
|
4740
4395
|
}
|
|
4741
4396
|
/** Delete a sandbox pool. Fails if the pool has active containers. */
|
|
4742
4397
|
async deletePool(poolId) {
|
|
4743
|
-
await this.
|
|
4744
|
-
"DELETE",
|
|
4745
|
-
this.path(`sandbox-pools/${poolId}`)
|
|
4746
|
-
);
|
|
4398
|
+
await callNative(() => this.native.deletePool(poolId), { poolId, notFoundKind: "pool" });
|
|
4747
4399
|
}
|
|
4748
4400
|
// --- Connect ---
|
|
4749
4401
|
/** Return a `Sandbox` handle for an existing running sandbox without verifying it exists. */
|
|
@@ -4757,7 +4409,8 @@ var init_client = __esm({
|
|
|
4757
4409
|
projectId: this.projectId,
|
|
4758
4410
|
routingHint,
|
|
4759
4411
|
resolveProxyInfo: proxyUrl == null ? async (currentIdentifier) => this.get(currentIdentifier) : void 0,
|
|
4760
|
-
requestTimeout
|
|
4412
|
+
requestTimeout,
|
|
4413
|
+
nativeClient: this.native
|
|
4761
4414
|
});
|
|
4762
4415
|
}
|
|
4763
4416
|
/**
|
|
@@ -4765,10 +4418,6 @@ var init_client = __esm({
|
|
|
4765
4418
|
*
|
|
4766
4419
|
* Blocks until the sandbox is ready or `requestTimeout` elapses. The returned
|
|
4767
4420
|
* `Sandbox` auto-terminates when `terminate()` is called.
|
|
4768
|
-
*
|
|
4769
|
-
* @param options.requestTimeout - Max seconds to wait for `Running` status (default: client requestTimeout).
|
|
4770
|
-
* @param options.startupTimeout - Deprecated alias for `requestTimeout`.
|
|
4771
|
-
* @throws {SandboxError} If the sandbox terminates during startup or the timeout elapses.
|
|
4772
4421
|
*/
|
|
4773
4422
|
async createAndConnect(options) {
|
|
4774
4423
|
if (options?.poolId != null && options?.cloudInit != null) {
|
|
@@ -4830,7 +4479,7 @@ var init_client = __esm({
|
|
|
4830
4479
|
`Sandbox ${result.sandboxId} did not start within ${requestTimeout}s`
|
|
4831
4480
|
);
|
|
4832
4481
|
}
|
|
4833
|
-
const deadline = Date.now() +
|
|
4482
|
+
const deadline = Date.now() + secondsToMillis3(requestTimeout);
|
|
4834
4483
|
while (Date.now() < deadline) {
|
|
4835
4484
|
const pollStart = nowMs();
|
|
4836
4485
|
const info = await requestClient.get(result.sandboxId);
|
|
@@ -4850,7 +4499,7 @@ var init_client = __esm({
|
|
|
4850
4499
|
})
|
|
4851
4500
|
);
|
|
4852
4501
|
}
|
|
4853
|
-
await
|
|
4502
|
+
await sleep(500);
|
|
4854
4503
|
}
|
|
4855
4504
|
try {
|
|
4856
4505
|
await requestClient.delete(result.sandboxId);
|
|
@@ -4996,21 +4645,21 @@ __export(sandbox_image_exports, {
|
|
|
4996
4645
|
createSandboxImage: () => createSandboxImage,
|
|
4997
4646
|
runCreateSandboxImageCli: () => runCreateSandboxImageCli
|
|
4998
4647
|
});
|
|
4999
|
-
import { createRequire } from "module";
|
|
4648
|
+
import { createRequire as createRequire2 } from "module";
|
|
5000
4649
|
import path from "path";
|
|
5001
4650
|
import { existsSync } from "fs";
|
|
5002
4651
|
import { parseArgs } from "util";
|
|
5003
|
-
function
|
|
4652
|
+
function resolveRequire2() {
|
|
5004
4653
|
if (typeof module !== "undefined" && module.exports != null && typeof __require !== "undefined") {
|
|
5005
4654
|
return __require;
|
|
5006
4655
|
}
|
|
5007
|
-
return
|
|
4656
|
+
return createRequire2(import.meta.url);
|
|
5008
4657
|
}
|
|
5009
4658
|
function loadNativeBinding() {
|
|
5010
4659
|
if (cachedNativeBinding) return cachedNativeBinding;
|
|
5011
4660
|
if (cachedNativeBindingError) throw cachedNativeBindingError;
|
|
5012
4661
|
try {
|
|
5013
|
-
const { loadNative } =
|
|
4662
|
+
const { loadNative } = resolveRequire2()("../lib/runtime.cjs");
|
|
5014
4663
|
cachedNativeBinding = loadNative();
|
|
5015
4664
|
return cachedNativeBinding;
|
|
5016
4665
|
} catch (error) {
|
|
@@ -5226,9 +4875,369 @@ init_tunnel();
|
|
|
5226
4875
|
|
|
5227
4876
|
// src/cloud-client.ts
|
|
5228
4877
|
init_defaults();
|
|
5229
|
-
|
|
4878
|
+
|
|
4879
|
+
// src/http.ts
|
|
4880
|
+
init_defaults();
|
|
4881
|
+
init_errors();
|
|
4882
|
+
init_sdk_timings();
|
|
4883
|
+
import {
|
|
4884
|
+
Agent,
|
|
4885
|
+
fetch as undiciFetch,
|
|
4886
|
+
setGlobalDispatcher
|
|
4887
|
+
} from "undici";
|
|
4888
|
+
var REQUEST_TIMEOUT_HEADER = "X-Tensorlake-Request-Timeout-Ms";
|
|
4889
|
+
setGlobalDispatcher(
|
|
4890
|
+
new Agent({
|
|
4891
|
+
keepAliveTimeout: 6e4,
|
|
4892
|
+
allowH2: true
|
|
4893
|
+
})
|
|
4894
|
+
);
|
|
4895
|
+
function withTraceId(value, traceId) {
|
|
4896
|
+
if (value == null) {
|
|
4897
|
+
return { traceId };
|
|
4898
|
+
}
|
|
4899
|
+
return Object.assign(value, { traceId });
|
|
4900
|
+
}
|
|
4901
|
+
var HttpClient = class _HttpClient {
|
|
4902
|
+
baseUrl;
|
|
4903
|
+
headers;
|
|
4904
|
+
maxRetries;
|
|
4905
|
+
retryBackoffMs;
|
|
4906
|
+
timeoutMs;
|
|
4907
|
+
abortController = null;
|
|
4908
|
+
constructor(options) {
|
|
4909
|
+
const url = options.baseUrl;
|
|
4910
|
+
this.baseUrl = url.endsWith("/") ? url.slice(0, -1) : url;
|
|
4911
|
+
this.maxRetries = options.maxRetries ?? MAX_RETRIES;
|
|
4912
|
+
this.retryBackoffMs = options.retryBackoffMs ?? RETRY_BACKOFF_MS;
|
|
4913
|
+
this.timeoutMs = options.timeoutMs === void 0 ? DEFAULT_HTTP_TIMEOUT_MS : options.timeoutMs;
|
|
4914
|
+
this.headers = {
|
|
4915
|
+
"User-Agent": `tensorlake-typescript-sdk/${SDK_VERSION}`
|
|
4916
|
+
};
|
|
4917
|
+
if (this.timeoutMs != null) {
|
|
4918
|
+
this.headers[REQUEST_TIMEOUT_HEADER] = String(this.timeoutMs);
|
|
4919
|
+
}
|
|
4920
|
+
if (options.apiKey) {
|
|
4921
|
+
this.headers["Authorization"] = `Bearer ${options.apiKey}`;
|
|
4922
|
+
}
|
|
4923
|
+
if (options.organizationId) {
|
|
4924
|
+
this.headers["X-Forwarded-Organization-Id"] = options.organizationId;
|
|
4925
|
+
}
|
|
4926
|
+
if (options.projectId) {
|
|
4927
|
+
this.headers["X-Forwarded-Project-Id"] = options.projectId;
|
|
4928
|
+
}
|
|
4929
|
+
if (options.hostHeader) {
|
|
4930
|
+
this.headers["Host"] = options.hostHeader;
|
|
4931
|
+
}
|
|
4932
|
+
if (options.sandboxIdHeader) {
|
|
4933
|
+
this.headers["X-Tensorlake-Sandbox-Id"] = options.sandboxIdHeader;
|
|
4934
|
+
}
|
|
4935
|
+
if (options.routingHint) {
|
|
4936
|
+
this.headers["X-Tensorlake-Route-Hint"] = options.routingHint;
|
|
4937
|
+
}
|
|
4938
|
+
}
|
|
4939
|
+
close() {
|
|
4940
|
+
this.abortController?.abort();
|
|
4941
|
+
this.abortController = null;
|
|
4942
|
+
}
|
|
4943
|
+
/** Make a JSON request, returning the parsed response body. */
|
|
4944
|
+
async requestJson(method, path2, options) {
|
|
4945
|
+
const response = await this.requestResponse(method, path2, {
|
|
4946
|
+
json: options?.body,
|
|
4947
|
+
headers: options?.headers,
|
|
4948
|
+
signal: options?.signal,
|
|
4949
|
+
retryableStatusCodes: options?.retryableStatusCodes,
|
|
4950
|
+
allowedErrorStatusCodes: options?.allowedErrorStatusCodes,
|
|
4951
|
+
allowHttpErrors: options?.allowHttpErrors
|
|
4952
|
+
});
|
|
4953
|
+
const text = await response.text();
|
|
4954
|
+
const data = text ? JSON.parse(text) : void 0;
|
|
4955
|
+
return withTraceId(data, response.traceId);
|
|
4956
|
+
}
|
|
4957
|
+
/** Make a request returning raw bytes. */
|
|
4958
|
+
async requestBytes(method, path2, options) {
|
|
4959
|
+
const headers = { ...options?.headers ?? {} };
|
|
4960
|
+
if (options?.contentType) {
|
|
4961
|
+
headers["Content-Type"] = options.contentType;
|
|
4962
|
+
}
|
|
4963
|
+
const response = await this.requestResponse(method, path2, {
|
|
4964
|
+
body: options?.body,
|
|
4965
|
+
headers,
|
|
4966
|
+
signal: options?.signal
|
|
4967
|
+
});
|
|
4968
|
+
const buffer = await response.arrayBuffer();
|
|
4969
|
+
return withTraceId(new Uint8Array(buffer), response.traceId);
|
|
4970
|
+
}
|
|
4971
|
+
/** Make a request and return the response body as an SSE stream. */
|
|
4972
|
+
async requestStream(method, path2, options) {
|
|
4973
|
+
const response = await this.requestResponse(method, path2, {
|
|
4974
|
+
json: options?.json,
|
|
4975
|
+
headers: { Accept: "text/event-stream" },
|
|
4976
|
+
signal: options?.signal
|
|
4977
|
+
});
|
|
4978
|
+
if (!response.body) {
|
|
4979
|
+
throw new RemoteAPIError(
|
|
4980
|
+
response.status,
|
|
4981
|
+
"No response body for SSE stream"
|
|
4982
|
+
);
|
|
4983
|
+
}
|
|
4984
|
+
return withTraceId(response.body, response.traceId);
|
|
4985
|
+
}
|
|
4986
|
+
/** Make a request and return the raw Response. */
|
|
4987
|
+
async requestResponse(method, path2, options) {
|
|
4988
|
+
const headers = {
|
|
4989
|
+
...this.headers,
|
|
4990
|
+
...options?.headers ?? {}
|
|
4991
|
+
};
|
|
4992
|
+
const hasJsonBody = options?.json !== void 0;
|
|
4993
|
+
if (hasJsonBody && !hasHeader(headers, "Content-Type")) {
|
|
4994
|
+
headers["Content-Type"] = "application/json";
|
|
4995
|
+
}
|
|
4996
|
+
const body = hasJsonBody ? JSON.stringify(options?.json) : normalizeRequestBody(options?.body);
|
|
4997
|
+
const { response, traceId } = await this.doFetch(
|
|
4998
|
+
method,
|
|
4999
|
+
path2,
|
|
5000
|
+
body,
|
|
5001
|
+
headers,
|
|
5002
|
+
options?.signal,
|
|
5003
|
+
options?.allowHttpErrors ?? false,
|
|
5004
|
+
options?.retryableStatusCodes ?? RETRYABLE_STATUS_CODES,
|
|
5005
|
+
options?.allowedErrorStatusCodes ?? /* @__PURE__ */ new Set()
|
|
5006
|
+
);
|
|
5007
|
+
return withTraceId(response, traceId);
|
|
5008
|
+
}
|
|
5009
|
+
static makeTraceparent() {
|
|
5010
|
+
const randomHex = (bytes) => Array.from(crypto.getRandomValues(new Uint8Array(bytes))).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
5011
|
+
const traceId = randomHex(16);
|
|
5012
|
+
const spanId = randomHex(8);
|
|
5013
|
+
return { traceparent: `00-${traceId}-${spanId}-01`, traceId };
|
|
5014
|
+
}
|
|
5015
|
+
async doFetch(method, path2, body, headers, signal, allowHttpErrors = false, retryableStatusCodes = RETRYABLE_STATUS_CODES, allowedErrorStatusCodes = /* @__PURE__ */ new Set()) {
|
|
5016
|
+
const url = `${this.baseUrl}${path2}`;
|
|
5017
|
+
const { traceparent, traceId } = _HttpClient.makeTraceparent();
|
|
5018
|
+
headers["traceparent"] = traceparent;
|
|
5019
|
+
let lastError;
|
|
5020
|
+
const operationStart = nowMs();
|
|
5021
|
+
const timingPath = sdkTimingPayloadsEnabled() ? path2 : stripPathPayload(path2);
|
|
5022
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
5023
|
+
if (attempt > 0) {
|
|
5024
|
+
const delay = this.retryBackoffMs * Math.pow(2, attempt - 1);
|
|
5025
|
+
logSdkTimingEvent("http.request", "retry_sleep_start", {
|
|
5026
|
+
method,
|
|
5027
|
+
path: timingPath,
|
|
5028
|
+
attempt,
|
|
5029
|
+
delay_ms: delay,
|
|
5030
|
+
server_trace_id: traceId
|
|
5031
|
+
});
|
|
5032
|
+
await sleep2(delay);
|
|
5033
|
+
}
|
|
5034
|
+
this.abortController = new AbortController();
|
|
5035
|
+
let timedOut = false;
|
|
5036
|
+
const timeoutId = this.timeoutMs == null ? null : setTimeout(() => {
|
|
5037
|
+
timedOut = true;
|
|
5038
|
+
this.abortController?.abort();
|
|
5039
|
+
}, this.timeoutMs);
|
|
5040
|
+
const combinedSignal = signal ? anySignal([signal, this.abortController.signal]) : this.abortController.signal;
|
|
5041
|
+
const attemptStart = nowMs();
|
|
5042
|
+
try {
|
|
5043
|
+
const response = await undiciFetch(url, {
|
|
5044
|
+
method,
|
|
5045
|
+
headers,
|
|
5046
|
+
body,
|
|
5047
|
+
signal: combinedSignal
|
|
5048
|
+
});
|
|
5049
|
+
if (timeoutId != null) clearTimeout(timeoutId);
|
|
5050
|
+
logSdkTiming("http.request", "headers", attemptStart, {
|
|
5051
|
+
method,
|
|
5052
|
+
path: timingPath,
|
|
5053
|
+
status: response.status,
|
|
5054
|
+
attempt,
|
|
5055
|
+
server_trace_id: traceId
|
|
5056
|
+
});
|
|
5057
|
+
if (response.ok) {
|
|
5058
|
+
logSdkTiming("http.request", "complete", operationStart, {
|
|
5059
|
+
method,
|
|
5060
|
+
path: timingPath,
|
|
5061
|
+
status: response.status,
|
|
5062
|
+
attempts: attempt + 1,
|
|
5063
|
+
server_trace_id: traceId
|
|
5064
|
+
});
|
|
5065
|
+
return { response, traceId };
|
|
5066
|
+
}
|
|
5067
|
+
if (retryableStatusCodes.has(response.status) && attempt < this.maxRetries) {
|
|
5068
|
+
lastError = new RemoteAPIError(
|
|
5069
|
+
response.status,
|
|
5070
|
+
await response.text().catch(() => "")
|
|
5071
|
+
);
|
|
5072
|
+
logSdkTimingEvent("http.request", "retryable_status", {
|
|
5073
|
+
method,
|
|
5074
|
+
path: timingPath,
|
|
5075
|
+
status: response.status,
|
|
5076
|
+
attempt,
|
|
5077
|
+
server_trace_id: traceId
|
|
5078
|
+
});
|
|
5079
|
+
continue;
|
|
5080
|
+
}
|
|
5081
|
+
if (allowHttpErrors || allowedErrorStatusCodes.has(response.status)) {
|
|
5082
|
+
logSdkTiming("http.request", "complete", operationStart, {
|
|
5083
|
+
method,
|
|
5084
|
+
path: timingPath,
|
|
5085
|
+
status: response.status,
|
|
5086
|
+
attempts: attempt + 1,
|
|
5087
|
+
server_trace_id: traceId
|
|
5088
|
+
});
|
|
5089
|
+
return { response, traceId };
|
|
5090
|
+
}
|
|
5091
|
+
const errorBody = await response.text().catch(() => "");
|
|
5092
|
+
throwMappedError(response.status, errorBody, path2);
|
|
5093
|
+
} catch (err) {
|
|
5094
|
+
if (timeoutId != null) clearTimeout(timeoutId);
|
|
5095
|
+
if (err instanceof RemoteAPIError || err instanceof SandboxNotFoundError || err instanceof PoolNotFoundError || err instanceof PoolInUseError) {
|
|
5096
|
+
throw err;
|
|
5097
|
+
}
|
|
5098
|
+
if (signal?.aborted && !timedOut) {
|
|
5099
|
+
throw new SandboxConnectionError("request aborted by caller", {
|
|
5100
|
+
cause: err
|
|
5101
|
+
});
|
|
5102
|
+
}
|
|
5103
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
5104
|
+
logSdkTiming("http.request", "error", attemptStart, {
|
|
5105
|
+
method,
|
|
5106
|
+
path: timingPath,
|
|
5107
|
+
attempt,
|
|
5108
|
+
server_trace_id: traceId,
|
|
5109
|
+
error: timedOut ? `request timed out after ${this.timeoutMs}ms` : describeError(err)
|
|
5110
|
+
});
|
|
5111
|
+
if (attempt >= this.maxRetries) {
|
|
5112
|
+
const detail = timedOut ? `request timed out after ${this.timeoutMs}ms` : describeError(err);
|
|
5113
|
+
throw new SandboxConnectionError(detail, { cause: err });
|
|
5114
|
+
}
|
|
5115
|
+
}
|
|
5116
|
+
}
|
|
5117
|
+
throw new SandboxConnectionError(
|
|
5118
|
+
lastError ? describeError(lastError) : "request failed",
|
|
5119
|
+
{ cause: lastError }
|
|
5120
|
+
);
|
|
5121
|
+
}
|
|
5122
|
+
};
|
|
5123
|
+
function hasHeader(headers, name) {
|
|
5124
|
+
const lowered = name.toLowerCase();
|
|
5125
|
+
return Object.keys(headers).some((key) => key.toLowerCase() === lowered);
|
|
5126
|
+
}
|
|
5127
|
+
function normalizeRequestBody(body) {
|
|
5128
|
+
if (body == null) {
|
|
5129
|
+
return void 0;
|
|
5130
|
+
}
|
|
5131
|
+
if (body instanceof Uint8Array) {
|
|
5132
|
+
return Uint8Array.from(body).buffer;
|
|
5133
|
+
}
|
|
5134
|
+
return body;
|
|
5135
|
+
}
|
|
5136
|
+
function stripPathPayload(path2) {
|
|
5137
|
+
const queryStart = path2.search(/[?#]/);
|
|
5138
|
+
return queryStart === -1 ? path2 : path2.slice(0, queryStart);
|
|
5139
|
+
}
|
|
5140
|
+
function throwMappedError(status, body, path2) {
|
|
5141
|
+
let message = body;
|
|
5142
|
+
try {
|
|
5143
|
+
const parsed = JSON.parse(body);
|
|
5144
|
+
if (parsed.message) message = parsed.message;
|
|
5145
|
+
else if (parsed.error) message = parsed.error;
|
|
5146
|
+
} catch {
|
|
5147
|
+
}
|
|
5148
|
+
if (status === 404) {
|
|
5149
|
+
if (path2.includes("sandbox-pools") || path2.includes("pools")) {
|
|
5150
|
+
const match = path2.match(/sandbox-pools\/([^/]+)/);
|
|
5151
|
+
if (match) throw new PoolNotFoundError(match[1]);
|
|
5152
|
+
}
|
|
5153
|
+
if (path2.includes("sandboxes")) {
|
|
5154
|
+
const match = path2.match(/sandboxes\/([^/]+)/);
|
|
5155
|
+
if (match) throw new SandboxNotFoundError(match[1]);
|
|
5156
|
+
}
|
|
5157
|
+
throw new RemoteAPIError(404, message);
|
|
5158
|
+
}
|
|
5159
|
+
if (status === 409) {
|
|
5160
|
+
if (path2.includes("sandbox-pools") || path2.includes("pools")) {
|
|
5161
|
+
const match = path2.match(/sandbox-pools\/([^/]+)/);
|
|
5162
|
+
if (match) throw new PoolInUseError(match[1], message);
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
throw new RemoteAPIError(status, message);
|
|
5166
|
+
}
|
|
5167
|
+
function anySignal(signals) {
|
|
5168
|
+
const controller = new AbortController();
|
|
5169
|
+
for (const signal of signals) {
|
|
5170
|
+
if (signal.aborted) {
|
|
5171
|
+
controller.abort(signal.reason);
|
|
5172
|
+
return controller.signal;
|
|
5173
|
+
}
|
|
5174
|
+
signal.addEventListener("abort", () => controller.abort(signal.reason), {
|
|
5175
|
+
once: true
|
|
5176
|
+
});
|
|
5177
|
+
}
|
|
5178
|
+
return controller.signal;
|
|
5179
|
+
}
|
|
5180
|
+
function sleep2(ms) {
|
|
5181
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5182
|
+
}
|
|
5183
|
+
|
|
5184
|
+
// src/cloud-client.ts
|
|
5230
5185
|
init_models();
|
|
5231
|
-
|
|
5186
|
+
|
|
5187
|
+
// src/sse.ts
|
|
5188
|
+
async function* parseSSEMessages(stream, signal) {
|
|
5189
|
+
const reader = stream.getReader();
|
|
5190
|
+
const decoder = new TextDecoder();
|
|
5191
|
+
let buffer = "";
|
|
5192
|
+
try {
|
|
5193
|
+
while (true) {
|
|
5194
|
+
if (signal?.aborted) break;
|
|
5195
|
+
const { done, value } = await reader.read();
|
|
5196
|
+
if (done) break;
|
|
5197
|
+
buffer += decoder.decode(value, { stream: true });
|
|
5198
|
+
const parts = buffer.split(/\r?\n\r?\n/);
|
|
5199
|
+
buffer = parts.pop() ?? "";
|
|
5200
|
+
for (const part of parts) {
|
|
5201
|
+
const lines = part.split(/\r?\n/);
|
|
5202
|
+
const dataLines = [];
|
|
5203
|
+
let event;
|
|
5204
|
+
let id;
|
|
5205
|
+
for (const line of lines) {
|
|
5206
|
+
if (!line || line.startsWith(":")) continue;
|
|
5207
|
+
const separator = line.indexOf(":");
|
|
5208
|
+
const field = separator === -1 ? line : line.slice(0, separator);
|
|
5209
|
+
let value2 = separator === -1 ? "" : line.slice(separator + 1);
|
|
5210
|
+
if (value2.startsWith(" ")) {
|
|
5211
|
+
value2 = value2.slice(1);
|
|
5212
|
+
}
|
|
5213
|
+
if (field === "data") {
|
|
5214
|
+
dataLines.push(value2);
|
|
5215
|
+
} else if (field === "event") {
|
|
5216
|
+
event = value2;
|
|
5217
|
+
} else if (field === "id") {
|
|
5218
|
+
id = value2;
|
|
5219
|
+
}
|
|
5220
|
+
}
|
|
5221
|
+
if (dataLines.length > 0 || event || id) {
|
|
5222
|
+
yield { data: dataLines.join("\n"), event, id };
|
|
5223
|
+
}
|
|
5224
|
+
}
|
|
5225
|
+
}
|
|
5226
|
+
} finally {
|
|
5227
|
+
reader.releaseLock();
|
|
5228
|
+
}
|
|
5229
|
+
}
|
|
5230
|
+
async function* parseSSEStream(stream, signal) {
|
|
5231
|
+
for await (const message of parseSSEMessages(stream, signal)) {
|
|
5232
|
+
if (!message.data) continue;
|
|
5233
|
+
try {
|
|
5234
|
+
yield JSON.parse(message.data);
|
|
5235
|
+
} catch {
|
|
5236
|
+
}
|
|
5237
|
+
}
|
|
5238
|
+
}
|
|
5239
|
+
|
|
5240
|
+
// src/cloud-client.ts
|
|
5232
5241
|
var CloudClient = class _CloudClient {
|
|
5233
5242
|
http;
|
|
5234
5243
|
organizationId;
|