tensorlake 0.4.39

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 ADDED
@@ -0,0 +1,1462 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ APIClient: () => APIClient,
24
+ CloudClient: () => CloudClient,
25
+ ContainerState: () => ContainerState,
26
+ OutputMode: () => OutputMode,
27
+ PoolInUseError: () => PoolInUseError,
28
+ PoolNotFoundError: () => PoolNotFoundError,
29
+ ProcessStatus: () => ProcessStatus,
30
+ RemoteAPIError: () => RemoteAPIError,
31
+ RequestExecutionError: () => RequestExecutionError,
32
+ RequestFailedError: () => RequestFailedError,
33
+ RequestNotFinishedError: () => RequestNotFinishedError,
34
+ Sandbox: () => Sandbox,
35
+ SandboxClient: () => SandboxClient,
36
+ SandboxConnectionError: () => SandboxConnectionError,
37
+ SandboxError: () => SandboxError,
38
+ SandboxException: () => SandboxException,
39
+ SandboxNotFoundError: () => SandboxNotFoundError,
40
+ SandboxStatus: () => SandboxStatus,
41
+ SnapshotStatus: () => SnapshotStatus,
42
+ StdinMode: () => StdinMode
43
+ });
44
+ module.exports = __toCommonJS(index_exports);
45
+
46
+ // src/defaults.ts
47
+ var API_URL = process.env.TENSORLAKE_API_URL ?? "https://api.tensorlake.ai";
48
+ var API_KEY = process.env.TENSORLAKE_API_KEY ?? void 0;
49
+ var NAMESPACE = process.env.INDEXIFY_NAMESPACE ?? "default";
50
+ var SANDBOX_PROXY_URL = process.env.TENSORLAKE_SANDBOX_PROXY_URL ?? "https://sandbox.tensorlake.ai";
51
+ var DEFAULT_HTTP_TIMEOUT_MS = 3e4;
52
+ var MAX_RETRIES = 3;
53
+ var RETRY_BACKOFF_MS = 500;
54
+ var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
55
+
56
+ // src/errors.ts
57
+ var SandboxException = class extends Error {
58
+ constructor(message) {
59
+ super(message);
60
+ this.name = "SandboxException";
61
+ }
62
+ };
63
+ var SandboxError = class extends SandboxException {
64
+ constructor(message) {
65
+ super(message);
66
+ this.name = "SandboxError";
67
+ }
68
+ };
69
+ var SandboxConnectionError = class extends SandboxError {
70
+ constructor(message) {
71
+ super(`Connection error: ${message}`);
72
+ this.name = "SandboxConnectionError";
73
+ }
74
+ };
75
+ var SandboxNotFoundError = class extends SandboxError {
76
+ sandboxId;
77
+ constructor(sandboxId) {
78
+ super(`Sandbox not found: ${sandboxId}`);
79
+ this.name = "SandboxNotFoundError";
80
+ this.sandboxId = sandboxId;
81
+ }
82
+ };
83
+ var PoolNotFoundError = class extends SandboxError {
84
+ poolId;
85
+ constructor(poolId) {
86
+ super(`Sandbox pool not found: ${poolId}`);
87
+ this.name = "PoolNotFoundError";
88
+ this.poolId = poolId;
89
+ }
90
+ };
91
+ var PoolInUseError = class extends SandboxError {
92
+ poolId;
93
+ constructor(poolId, message) {
94
+ const base = `Cannot delete pool ${poolId}: pool is in use`;
95
+ super(message ? `${base} - ${message}` : base);
96
+ this.name = "PoolInUseError";
97
+ this.poolId = poolId;
98
+ }
99
+ };
100
+ var RemoteAPIError = class extends SandboxError {
101
+ statusCode;
102
+ responseMessage;
103
+ constructor(statusCode, message) {
104
+ super(`API error (status ${statusCode}): ${message}`);
105
+ this.name = "RemoteAPIError";
106
+ this.statusCode = statusCode;
107
+ this.responseMessage = message;
108
+ }
109
+ };
110
+ var RequestNotFinishedError = class extends Error {
111
+ constructor() {
112
+ super("Request has not finished yet");
113
+ this.name = "RequestNotFinishedError";
114
+ }
115
+ };
116
+ var RequestFailedError = class extends Error {
117
+ failure;
118
+ constructor(failure) {
119
+ super(`Request failed: ${failure}`);
120
+ this.name = "RequestFailedError";
121
+ this.failure = failure;
122
+ }
123
+ };
124
+ var RequestExecutionError = class extends Error {
125
+ functionName;
126
+ constructor(message, functionName) {
127
+ super(
128
+ functionName ? `Request error in ${functionName}: ${message}` : message
129
+ );
130
+ this.name = "RequestExecutionError";
131
+ this.functionName = functionName;
132
+ }
133
+ };
134
+
135
+ // src/http.ts
136
+ var HttpClient = class {
137
+ baseUrl;
138
+ headers;
139
+ maxRetries;
140
+ retryBackoffMs;
141
+ timeoutMs;
142
+ abortController = null;
143
+ constructor(options) {
144
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
145
+ this.maxRetries = options.maxRetries ?? MAX_RETRIES;
146
+ this.retryBackoffMs = options.retryBackoffMs ?? RETRY_BACKOFF_MS;
147
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;
148
+ this.headers = {};
149
+ if (options.apiKey) {
150
+ this.headers["Authorization"] = `Bearer ${options.apiKey}`;
151
+ }
152
+ if (options.organizationId) {
153
+ this.headers["X-Forwarded-Organization-Id"] = options.organizationId;
154
+ }
155
+ if (options.projectId) {
156
+ this.headers["X-Forwarded-Project-Id"] = options.projectId;
157
+ }
158
+ if (options.hostHeader) {
159
+ this.headers["Host"] = options.hostHeader;
160
+ }
161
+ }
162
+ close() {
163
+ this.abortController?.abort();
164
+ this.abortController = null;
165
+ }
166
+ /** Make a JSON request, returning the parsed response body. */
167
+ async requestJson(method, path, options) {
168
+ const response = await this.requestResponse(method, path, {
169
+ json: options?.body,
170
+ headers: options?.headers,
171
+ signal: options?.signal
172
+ });
173
+ const text = await response.text();
174
+ if (!text) return void 0;
175
+ return JSON.parse(text);
176
+ }
177
+ /** Make a request returning raw bytes. */
178
+ async requestBytes(method, path, options) {
179
+ const headers = { ...options?.headers ?? {} };
180
+ if (options?.contentType) {
181
+ headers["Content-Type"] = options.contentType;
182
+ }
183
+ const response = await this.requestResponse(
184
+ method,
185
+ path,
186
+ {
187
+ body: options?.body,
188
+ headers,
189
+ signal: options?.signal
190
+ }
191
+ );
192
+ const buffer = await response.arrayBuffer();
193
+ return new Uint8Array(buffer);
194
+ }
195
+ /** Make a request and return the raw Response (for SSE streaming). */
196
+ async requestStream(method, path, options) {
197
+ const response = await this.requestResponse(
198
+ method,
199
+ path,
200
+ {
201
+ headers: { Accept: "text/event-stream" },
202
+ signal: options?.signal
203
+ }
204
+ );
205
+ if (!response.body) {
206
+ throw new RemoteAPIError(response.status, "No response body for SSE stream");
207
+ }
208
+ return response.body;
209
+ }
210
+ /** Make a request and return the raw Response. */
211
+ async requestResponse(method, path, options) {
212
+ const headers = {
213
+ ...this.headers,
214
+ ...options?.headers ?? {}
215
+ };
216
+ const hasJsonBody = options?.json !== void 0;
217
+ if (hasJsonBody && !hasHeader(headers, "Content-Type")) {
218
+ headers["Content-Type"] = "application/json";
219
+ }
220
+ const body = hasJsonBody ? JSON.stringify(options?.json) : normalizeRequestBody(options?.body);
221
+ return this.doFetch(
222
+ method,
223
+ path,
224
+ body,
225
+ headers,
226
+ options?.signal,
227
+ options?.allowHttpErrors ?? false
228
+ );
229
+ }
230
+ async doFetch(method, path, body, headers, signal, allowHttpErrors = false) {
231
+ const url = `${this.baseUrl}${path}`;
232
+ let lastError;
233
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
234
+ if (attempt > 0) {
235
+ const delay = this.retryBackoffMs * Math.pow(2, attempt - 1);
236
+ await sleep(delay);
237
+ }
238
+ this.abortController = new AbortController();
239
+ const timeoutId = setTimeout(
240
+ () => this.abortController?.abort(),
241
+ this.timeoutMs
242
+ );
243
+ const combinedSignal = signal ? anySignal([signal, this.abortController.signal]) : this.abortController.signal;
244
+ try {
245
+ const response = await fetch(url, {
246
+ method,
247
+ headers,
248
+ body,
249
+ signal: combinedSignal
250
+ });
251
+ clearTimeout(timeoutId);
252
+ if (response.ok) return response;
253
+ if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < this.maxRetries) {
254
+ lastError = new RemoteAPIError(
255
+ response.status,
256
+ await response.text().catch(() => "")
257
+ );
258
+ continue;
259
+ }
260
+ if (allowHttpErrors) {
261
+ return response;
262
+ }
263
+ const errorBody = await response.text().catch(() => "");
264
+ throwMappedError(response.status, errorBody, path);
265
+ } catch (err) {
266
+ clearTimeout(timeoutId);
267
+ if (err instanceof RemoteAPIError || err instanceof SandboxNotFoundError || err instanceof PoolNotFoundError || err instanceof PoolInUseError) {
268
+ throw err;
269
+ }
270
+ if (signal?.aborted) {
271
+ throw new SandboxConnectionError("Request aborted");
272
+ }
273
+ lastError = err instanceof Error ? err : new Error(String(err));
274
+ if (attempt >= this.maxRetries) {
275
+ throw new SandboxConnectionError(lastError.message);
276
+ }
277
+ }
278
+ }
279
+ throw new SandboxConnectionError(lastError?.message ?? "Request failed");
280
+ }
281
+ };
282
+ function hasHeader(headers, name) {
283
+ const lowered = name.toLowerCase();
284
+ return Object.keys(headers).some((key) => key.toLowerCase() === lowered);
285
+ }
286
+ function normalizeRequestBody(body) {
287
+ if (body == null) {
288
+ return void 0;
289
+ }
290
+ if (body instanceof Uint8Array) {
291
+ return Uint8Array.from(body).buffer;
292
+ }
293
+ return body;
294
+ }
295
+ function throwMappedError(status, body, path) {
296
+ let message = body;
297
+ try {
298
+ const parsed = JSON.parse(body);
299
+ if (parsed.message) message = parsed.message;
300
+ else if (parsed.error) message = parsed.error;
301
+ } catch {
302
+ }
303
+ if (status === 404) {
304
+ if (path.includes("sandbox-pools") || path.includes("pools")) {
305
+ const match = path.match(/sandbox-pools\/([^/]+)/);
306
+ if (match) throw new PoolNotFoundError(match[1]);
307
+ }
308
+ if (path.includes("sandboxes")) {
309
+ const match = path.match(/sandboxes\/([^/]+)/);
310
+ if (match) throw new SandboxNotFoundError(match[1]);
311
+ }
312
+ throw new RemoteAPIError(404, message);
313
+ }
314
+ if (status === 409) {
315
+ if (path.includes("sandbox-pools") || path.includes("pools")) {
316
+ const match = path.match(/sandbox-pools\/([^/]+)/);
317
+ if (match) throw new PoolInUseError(match[1], message);
318
+ }
319
+ }
320
+ throw new RemoteAPIError(status, message);
321
+ }
322
+ function anySignal(signals) {
323
+ const controller = new AbortController();
324
+ for (const signal of signals) {
325
+ if (signal.aborted) {
326
+ controller.abort(signal.reason);
327
+ return controller.signal;
328
+ }
329
+ signal.addEventListener("abort", () => controller.abort(signal.reason), {
330
+ once: true
331
+ });
332
+ }
333
+ return controller.signal;
334
+ }
335
+ function sleep(ms) {
336
+ return new Promise((resolve) => setTimeout(resolve, ms));
337
+ }
338
+
339
+ // src/models.ts
340
+ var SandboxStatus = /* @__PURE__ */ ((SandboxStatus2) => {
341
+ SandboxStatus2["PENDING"] = "pending";
342
+ SandboxStatus2["RUNNING"] = "running";
343
+ SandboxStatus2["SNAPSHOTTING"] = "snapshotting";
344
+ SandboxStatus2["SUSPENDED"] = "suspended";
345
+ SandboxStatus2["TERMINATED"] = "terminated";
346
+ return SandboxStatus2;
347
+ })(SandboxStatus || {});
348
+ var SnapshotStatus = /* @__PURE__ */ ((SnapshotStatus2) => {
349
+ SnapshotStatus2["IN_PROGRESS"] = "in_progress";
350
+ SnapshotStatus2["COMPLETED"] = "completed";
351
+ SnapshotStatus2["FAILED"] = "failed";
352
+ return SnapshotStatus2;
353
+ })(SnapshotStatus || {});
354
+ var ProcessStatus = /* @__PURE__ */ ((ProcessStatus2) => {
355
+ ProcessStatus2["RUNNING"] = "running";
356
+ ProcessStatus2["EXITED"] = "exited";
357
+ ProcessStatus2["SIGNALED"] = "signaled";
358
+ return ProcessStatus2;
359
+ })(ProcessStatus || {});
360
+ var StdinMode = /* @__PURE__ */ ((StdinMode2) => {
361
+ StdinMode2["CLOSED"] = "closed";
362
+ StdinMode2["PIPE"] = "pipe";
363
+ return StdinMode2;
364
+ })(StdinMode || {});
365
+ var OutputMode = /* @__PURE__ */ ((OutputMode2) => {
366
+ OutputMode2["CAPTURE"] = "capture";
367
+ OutputMode2["DISCARD"] = "discard";
368
+ return OutputMode2;
369
+ })(OutputMode || {});
370
+ var ContainerState = /* @__PURE__ */ ((ContainerState2) => {
371
+ ContainerState2["IDLE"] = "Idle";
372
+ ContainerState2["RUNNING"] = "Running";
373
+ return ContainerState2;
374
+ })(ContainerState || {});
375
+ function snakeToCamel(str) {
376
+ return str.replace(/_([a-z])/g, (_, ch) => ch.toUpperCase());
377
+ }
378
+ function parseTimestamp(v) {
379
+ if (v == null) return void 0;
380
+ if (v instanceof Date) return v;
381
+ if (typeof v === "string") {
382
+ const parsed = Date.parse(v);
383
+ return Number.isNaN(parsed) ? void 0 : new Date(parsed);
384
+ }
385
+ const ts = Number(v);
386
+ if (isNaN(ts)) return void 0;
387
+ if (ts > 1e15) return new Date(ts / 1e3);
388
+ if (ts > 1e12) return new Date(ts);
389
+ return new Date(ts * 1e3);
390
+ }
391
+ function fromSnakeKeys(obj, idField) {
392
+ if (Array.isArray(obj)) return obj.map((item) => fromSnakeKeys(item, idField));
393
+ if (obj !== null && typeof obj === "object" && !(obj instanceof Date)) {
394
+ const result = {};
395
+ for (const [k, v] of Object.entries(obj)) {
396
+ let key;
397
+ if (k === "id" && idField) {
398
+ key = idField;
399
+ } else {
400
+ key = snakeToCamel(k);
401
+ }
402
+ if (key.endsWith("At") || key === "timestamp" || key === "startedAt" || key === "endedAt") {
403
+ result[key] = parseTimestamp(v);
404
+ } else if (typeof v === "object" && v !== null && !Array.isArray(v)) {
405
+ result[key] = fromSnakeKeys(v);
406
+ } else if (Array.isArray(v)) {
407
+ result[key] = v.map((item) => fromSnakeKeys(item));
408
+ } else {
409
+ result[key] = v === null ? void 0 : v;
410
+ }
411
+ }
412
+ return result;
413
+ }
414
+ return obj;
415
+ }
416
+
417
+ // src/sse.ts
418
+ async function* parseSSEMessages(stream, signal) {
419
+ const reader = stream.getReader();
420
+ const decoder = new TextDecoder();
421
+ let buffer = "";
422
+ try {
423
+ while (true) {
424
+ if (signal?.aborted) break;
425
+ const { done, value } = await reader.read();
426
+ if (done) break;
427
+ buffer += decoder.decode(value, { stream: true });
428
+ const parts = buffer.split(/\r?\n\r?\n/);
429
+ buffer = parts.pop() ?? "";
430
+ for (const part of parts) {
431
+ const lines = part.split(/\r?\n/);
432
+ const dataLines = [];
433
+ let event;
434
+ let id;
435
+ for (const line of lines) {
436
+ if (!line || line.startsWith(":")) continue;
437
+ const separator = line.indexOf(":");
438
+ const field = separator === -1 ? line : line.slice(0, separator);
439
+ let value2 = separator === -1 ? "" : line.slice(separator + 1);
440
+ if (value2.startsWith(" ")) {
441
+ value2 = value2.slice(1);
442
+ }
443
+ if (field === "data") {
444
+ dataLines.push(value2);
445
+ } else if (field === "event") {
446
+ event = value2;
447
+ } else if (field === "id") {
448
+ id = value2;
449
+ }
450
+ }
451
+ if (dataLines.length > 0 || event || id) {
452
+ yield { data: dataLines.join("\n"), event, id };
453
+ }
454
+ }
455
+ }
456
+ } finally {
457
+ reader.releaseLock();
458
+ }
459
+ }
460
+ async function* parseSSEStream(stream, signal) {
461
+ for await (const message of parseSSEMessages(stream, signal)) {
462
+ if (!message.data) continue;
463
+ try {
464
+ yield JSON.parse(message.data);
465
+ } catch {
466
+ }
467
+ }
468
+ }
469
+
470
+ // src/url.ts
471
+ function isLocalhost(apiUrl) {
472
+ try {
473
+ const parsed = new URL(apiUrl);
474
+ return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
475
+ } catch {
476
+ return false;
477
+ }
478
+ }
479
+ function resolveProxyUrl(apiUrl) {
480
+ const explicit = process.env.TENSORLAKE_SANDBOX_PROXY_URL;
481
+ if (explicit) return explicit;
482
+ if (isLocalhost(apiUrl)) return "http://localhost:9443";
483
+ try {
484
+ const parsed = new URL(apiUrl);
485
+ const host = parsed.hostname;
486
+ if (host.startsWith("api.")) {
487
+ const proxyHost = "sandbox." + host.slice(4);
488
+ return `${parsed.protocol}//${proxyHost}`;
489
+ }
490
+ } catch {
491
+ }
492
+ return SANDBOX_PROXY_URL;
493
+ }
494
+ function resolveProxyTarget(proxyUrl, sandboxId) {
495
+ try {
496
+ const parsed = new URL(proxyUrl);
497
+ const host = parsed.hostname;
498
+ if (host === "localhost" || host === "127.0.0.1") {
499
+ return {
500
+ baseUrl: proxyUrl.replace(/\/+$/, ""),
501
+ hostHeader: `${sandboxId}.local`
502
+ };
503
+ }
504
+ const port = parsed.port ? `:${parsed.port}` : "";
505
+ return {
506
+ baseUrl: `${parsed.protocol}//${sandboxId}.${host}${port}`,
507
+ hostHeader: void 0
508
+ };
509
+ } catch {
510
+ return {
511
+ baseUrl: `${proxyUrl.replace(/\/+$/, "")}/${sandboxId}`,
512
+ hostHeader: void 0
513
+ };
514
+ }
515
+ }
516
+ function lifecyclePath(path, isLocal, namespace) {
517
+ if (isLocal) {
518
+ return `/v1/namespaces/${namespace}/${path}`;
519
+ }
520
+ return `/${path}`;
521
+ }
522
+
523
+ // src/sandbox.ts
524
+ var Sandbox = class {
525
+ sandboxId;
526
+ http;
527
+ baseUrl;
528
+ ownsSandbox = false;
529
+ lifecycleClient = null;
530
+ constructor(options) {
531
+ this.sandboxId = options.sandboxId;
532
+ const proxyUrl = options.proxyUrl ?? SANDBOX_PROXY_URL;
533
+ const { baseUrl, hostHeader } = resolveProxyTarget(proxyUrl, options.sandboxId);
534
+ this.baseUrl = baseUrl;
535
+ this.http = new HttpClient({
536
+ baseUrl,
537
+ apiKey: options.apiKey,
538
+ organizationId: options.organizationId,
539
+ projectId: options.projectId,
540
+ hostHeader
541
+ });
542
+ }
543
+ /** @internal Used by SandboxClient.createAndConnect to set ownership. */
544
+ _setOwner(client) {
545
+ this.ownsSandbox = true;
546
+ this.lifecycleClient = client;
547
+ }
548
+ close() {
549
+ this.http.close();
550
+ }
551
+ async terminate() {
552
+ const client = this.lifecycleClient;
553
+ this.ownsSandbox = false;
554
+ this.lifecycleClient = null;
555
+ this.close();
556
+ if (client) {
557
+ await client.delete(this.sandboxId);
558
+ }
559
+ }
560
+ // --- High-level convenience ---
561
+ async run(command, options) {
562
+ const proc = await this.startProcess(command, {
563
+ args: options?.args,
564
+ env: options?.env,
565
+ workingDir: options?.workingDir
566
+ });
567
+ const deadline = options?.timeout ? Date.now() + options.timeout * 1e3 : null;
568
+ let info;
569
+ while (true) {
570
+ info = await this.getProcess(proc.pid);
571
+ if (info.status !== "running" /* RUNNING */) break;
572
+ if (deadline && Date.now() > deadline) {
573
+ await this.killProcess(proc.pid);
574
+ throw new SandboxError(`Command timed out after ${options.timeout}s`);
575
+ }
576
+ await sleep2(100);
577
+ }
578
+ const stdoutResp = await this.getStdout(proc.pid);
579
+ const stderrResp = await this.getStderr(proc.pid);
580
+ let exitCode;
581
+ if (info.exitCode != null) {
582
+ exitCode = info.exitCode;
583
+ } else if (info.signal != null) {
584
+ exitCode = -info.signal;
585
+ } else {
586
+ exitCode = -1;
587
+ }
588
+ return {
589
+ exitCode,
590
+ stdout: stdoutResp.lines.join("\n"),
591
+ stderr: stderrResp.lines.join("\n")
592
+ };
593
+ }
594
+ // --- Process management ---
595
+ async startProcess(command, options) {
596
+ const payload = { command };
597
+ if (options?.args != null) payload.args = options.args;
598
+ if (options?.env != null) payload.env = options.env;
599
+ if (options?.workingDir != null) payload.working_dir = options.workingDir;
600
+ if (options?.stdinMode != null && options.stdinMode !== "closed" /* CLOSED */) {
601
+ payload.stdin_mode = options.stdinMode;
602
+ }
603
+ if (options?.stdoutMode != null && options.stdoutMode !== "capture" /* CAPTURE */) {
604
+ payload.stdout_mode = options.stdoutMode;
605
+ }
606
+ if (options?.stderrMode != null && options.stderrMode !== "capture" /* CAPTURE */) {
607
+ payload.stderr_mode = options.stderrMode;
608
+ }
609
+ const raw = await this.http.requestJson(
610
+ "POST",
611
+ "/api/v1/processes",
612
+ { body: payload }
613
+ );
614
+ return fromSnakeKeys(raw);
615
+ }
616
+ async listProcesses() {
617
+ const raw = await this.http.requestJson(
618
+ "GET",
619
+ "/api/v1/processes"
620
+ );
621
+ return (raw.processes ?? []).map((p) => fromSnakeKeys(p));
622
+ }
623
+ async getProcess(pid) {
624
+ const raw = await this.http.requestJson(
625
+ "GET",
626
+ `/api/v1/processes/${pid}`
627
+ );
628
+ return fromSnakeKeys(raw);
629
+ }
630
+ async killProcess(pid) {
631
+ await this.http.requestJson("DELETE", `/api/v1/processes/${pid}`);
632
+ }
633
+ async sendSignal(pid, signal) {
634
+ const raw = await this.http.requestJson(
635
+ "POST",
636
+ `/api/v1/processes/${pid}/signal`,
637
+ { body: { signal } }
638
+ );
639
+ return fromSnakeKeys(raw);
640
+ }
641
+ // --- Process I/O ---
642
+ async writeStdin(pid, data) {
643
+ await this.http.requestBytes("POST", `/api/v1/processes/${pid}/stdin`, {
644
+ body: data,
645
+ contentType: "application/octet-stream"
646
+ });
647
+ }
648
+ async closeStdin(pid) {
649
+ await this.http.requestJson("POST", `/api/v1/processes/${pid}/stdin/close`);
650
+ }
651
+ async getStdout(pid) {
652
+ const raw = await this.http.requestJson(
653
+ "GET",
654
+ `/api/v1/processes/${pid}/stdout`
655
+ );
656
+ return fromSnakeKeys(raw);
657
+ }
658
+ async getStderr(pid) {
659
+ const raw = await this.http.requestJson(
660
+ "GET",
661
+ `/api/v1/processes/${pid}/stderr`
662
+ );
663
+ return fromSnakeKeys(raw);
664
+ }
665
+ async getOutput(pid) {
666
+ const raw = await this.http.requestJson(
667
+ "GET",
668
+ `/api/v1/processes/${pid}/output`
669
+ );
670
+ return fromSnakeKeys(raw);
671
+ }
672
+ // --- Streaming (SSE) ---
673
+ async *followStdout(pid, options) {
674
+ const stream = await this.http.requestStream(
675
+ "GET",
676
+ `/api/v1/processes/${pid}/stdout/follow`,
677
+ options
678
+ );
679
+ for await (const raw of parseSSEStream(
680
+ stream,
681
+ options?.signal
682
+ )) {
683
+ yield fromSnakeKeys(raw);
684
+ }
685
+ }
686
+ async *followStderr(pid, options) {
687
+ const stream = await this.http.requestStream(
688
+ "GET",
689
+ `/api/v1/processes/${pid}/stderr/follow`,
690
+ options
691
+ );
692
+ for await (const raw of parseSSEStream(
693
+ stream,
694
+ options?.signal
695
+ )) {
696
+ yield fromSnakeKeys(raw);
697
+ }
698
+ }
699
+ async *followOutput(pid, options) {
700
+ const stream = await this.http.requestStream(
701
+ "GET",
702
+ `/api/v1/processes/${pid}/output/follow`,
703
+ options
704
+ );
705
+ for await (const raw of parseSSEStream(
706
+ stream,
707
+ options?.signal
708
+ )) {
709
+ yield fromSnakeKeys(raw);
710
+ }
711
+ }
712
+ // --- File operations ---
713
+ async readFile(path) {
714
+ return this.http.requestBytes(
715
+ "GET",
716
+ `/api/v1/files?path=${encodeURIComponent(path)}`
717
+ );
718
+ }
719
+ async writeFile(path, content) {
720
+ await this.http.requestBytes(
721
+ "PUT",
722
+ `/api/v1/files?path=${encodeURIComponent(path)}`,
723
+ { body: content, contentType: "application/octet-stream" }
724
+ );
725
+ }
726
+ async deleteFile(path) {
727
+ await this.http.requestJson(
728
+ "DELETE",
729
+ `/api/v1/files?path=${encodeURIComponent(path)}`
730
+ );
731
+ }
732
+ async listDirectory(path) {
733
+ const raw = await this.http.requestJson(
734
+ "GET",
735
+ `/api/v1/files/list?path=${encodeURIComponent(path)}`
736
+ );
737
+ return fromSnakeKeys(raw);
738
+ }
739
+ // --- PTY ---
740
+ async createPtySession(options) {
741
+ const payload = {
742
+ command: options.command,
743
+ rows: options.rows ?? 24,
744
+ cols: options.cols ?? 80
745
+ };
746
+ if (options.args != null) payload.args = options.args;
747
+ if (options.env != null) payload.env = options.env;
748
+ if (options.workingDir != null) payload.working_dir = options.workingDir;
749
+ const raw = await this.http.requestJson(
750
+ "POST",
751
+ "/api/v1/pty",
752
+ { body: payload }
753
+ );
754
+ return fromSnakeKeys(raw);
755
+ }
756
+ ptyWsUrl(sessionId, token) {
757
+ let wsBase;
758
+ if (this.baseUrl.startsWith("https://")) {
759
+ wsBase = "wss://" + this.baseUrl.slice(8);
760
+ } else if (this.baseUrl.startsWith("http://")) {
761
+ wsBase = "ws://" + this.baseUrl.slice(7);
762
+ } else {
763
+ wsBase = this.baseUrl;
764
+ }
765
+ return `${wsBase}/api/v1/pty/${sessionId}/ws?token=${token}`;
766
+ }
767
+ // --- Health ---
768
+ async health() {
769
+ const raw = await this.http.requestJson(
770
+ "GET",
771
+ "/api/v1/health"
772
+ );
773
+ return fromSnakeKeys(raw);
774
+ }
775
+ async info() {
776
+ const raw = await this.http.requestJson(
777
+ "GET",
778
+ "/api/v1/info"
779
+ );
780
+ return fromSnakeKeys(raw);
781
+ }
782
+ };
783
+ function sleep2(ms) {
784
+ return new Promise((resolve) => setTimeout(resolve, ms));
785
+ }
786
+
787
+ // src/client.ts
788
+ var SandboxClient = class _SandboxClient {
789
+ http;
790
+ apiUrl;
791
+ apiKey;
792
+ organizationId;
793
+ projectId;
794
+ namespace;
795
+ local;
796
+ constructor(options) {
797
+ this.apiUrl = options?.apiUrl ?? API_URL;
798
+ this.apiKey = options?.apiKey ?? API_KEY;
799
+ this.organizationId = options?.organizationId;
800
+ this.projectId = options?.projectId;
801
+ this.namespace = options?.namespace ?? NAMESPACE;
802
+ this.local = isLocalhost(this.apiUrl);
803
+ this.http = new HttpClient({
804
+ baseUrl: this.apiUrl,
805
+ apiKey: this.apiKey,
806
+ organizationId: this.organizationId,
807
+ projectId: this.projectId,
808
+ maxRetries: options?.maxRetries ?? MAX_RETRIES,
809
+ retryBackoffMs: options?.retryBackoffMs ?? RETRY_BACKOFF_MS
810
+ });
811
+ }
812
+ /** Create a client for the TensorLake cloud platform. */
813
+ static forCloud(options) {
814
+ return new _SandboxClient({
815
+ apiUrl: options?.apiUrl ?? "https://api.tensorlake.ai",
816
+ apiKey: options?.apiKey,
817
+ organizationId: options?.organizationId,
818
+ projectId: options?.projectId
819
+ });
820
+ }
821
+ /** Create a client for a local Indexify server. */
822
+ static forLocalhost(options) {
823
+ return new _SandboxClient({
824
+ apiUrl: options?.apiUrl ?? "http://localhost:8900",
825
+ namespace: options?.namespace ?? "default"
826
+ });
827
+ }
828
+ close() {
829
+ this.http.close();
830
+ }
831
+ // --- Path helper ---
832
+ path(subpath) {
833
+ return lifecyclePath(subpath, this.local, this.namespace);
834
+ }
835
+ // --- Sandbox CRUD ---
836
+ async create(options) {
837
+ const body = {
838
+ resources: {
839
+ cpus: options?.cpus ?? 1,
840
+ memory_mb: options?.memoryMb ?? 1024,
841
+ ephemeral_disk_mb: options?.ephemeralDiskMb ?? 1024
842
+ }
843
+ };
844
+ if (options?.image != null) body.image = options.image;
845
+ if (options?.secretNames != null) body.secret_names = options.secretNames;
846
+ if (options?.timeoutSecs != null) body.timeout_secs = options.timeoutSecs;
847
+ if (options?.entrypoint != null) body.entrypoint = options.entrypoint;
848
+ if (options?.snapshotId != null) body.snapshot_id = options.snapshotId;
849
+ if (options?.name != null) body.name = options.name;
850
+ if (options?.allowInternetAccess === false || options?.allowOut != null || options?.denyOut != null) {
851
+ body.network = {
852
+ allow_internet_access: options?.allowInternetAccess ?? true,
853
+ allow_out: options?.allowOut ?? [],
854
+ deny_out: options?.denyOut ?? []
855
+ };
856
+ }
857
+ const raw = await this.http.requestJson(
858
+ "POST",
859
+ this.path("sandboxes"),
860
+ { body }
861
+ );
862
+ return fromSnakeKeys(raw, "sandboxId");
863
+ }
864
+ async get(sandboxId) {
865
+ const raw = await this.http.requestJson(
866
+ "GET",
867
+ this.path(`sandboxes/${sandboxId}`)
868
+ );
869
+ return fromSnakeKeys(raw, "sandboxId");
870
+ }
871
+ async list() {
872
+ const raw = await this.http.requestJson(
873
+ "GET",
874
+ this.path("sandboxes")
875
+ );
876
+ return (raw.sandboxes ?? []).map(
877
+ (s) => fromSnakeKeys(s, "sandboxId")
878
+ );
879
+ }
880
+ async update(sandboxId, options) {
881
+ const body = {};
882
+ if (options.name != null) body.name = options.name;
883
+ const raw = await this.http.requestJson(
884
+ "PATCH",
885
+ this.path(`sandboxes/${sandboxId}`),
886
+ { body }
887
+ );
888
+ return fromSnakeKeys(raw, "sandboxId");
889
+ }
890
+ async delete(sandboxId) {
891
+ await this.http.requestJson(
892
+ "DELETE",
893
+ this.path(`sandboxes/${sandboxId}`)
894
+ );
895
+ }
896
+ async claim(poolId) {
897
+ const raw = await this.http.requestJson(
898
+ "POST",
899
+ this.path(`sandbox-pools/${poolId}/sandboxes`)
900
+ );
901
+ return fromSnakeKeys(raw, "sandboxId");
902
+ }
903
+ // --- Snapshots ---
904
+ async snapshot(sandboxId) {
905
+ const raw = await this.http.requestJson(
906
+ "POST",
907
+ this.path(`sandboxes/${sandboxId}/snapshot`)
908
+ );
909
+ return fromSnakeKeys(raw, "snapshotId");
910
+ }
911
+ async getSnapshot(snapshotId) {
912
+ const raw = await this.http.requestJson(
913
+ "GET",
914
+ this.path(`snapshots/${snapshotId}`)
915
+ );
916
+ return fromSnakeKeys(raw, "snapshotId");
917
+ }
918
+ async listSnapshots() {
919
+ const raw = await this.http.requestJson(
920
+ "GET",
921
+ this.path("snapshots")
922
+ );
923
+ return (raw.snapshots ?? []).map(
924
+ (s) => fromSnakeKeys(s, "snapshotId")
925
+ );
926
+ }
927
+ async deleteSnapshot(snapshotId) {
928
+ await this.http.requestJson(
929
+ "DELETE",
930
+ this.path(`snapshots/${snapshotId}`)
931
+ );
932
+ }
933
+ async snapshotAndWait(sandboxId, options) {
934
+ const timeout = options?.timeout ?? 300;
935
+ const pollInterval = options?.pollInterval ?? 1;
936
+ const result = await this.snapshot(sandboxId);
937
+ const deadline = Date.now() + timeout * 1e3;
938
+ while (Date.now() < deadline) {
939
+ const info = await this.getSnapshot(result.snapshotId);
940
+ if (info.status === "completed" /* COMPLETED */) return info;
941
+ if (info.status === "failed" /* FAILED */) {
942
+ throw new SandboxError(
943
+ `Snapshot ${result.snapshotId} failed: ${info.error}`
944
+ );
945
+ }
946
+ await sleep3(pollInterval * 1e3);
947
+ }
948
+ throw new SandboxError(
949
+ `Snapshot ${result.snapshotId} did not complete within ${timeout}s`
950
+ );
951
+ }
952
+ // --- Pools ---
953
+ async createPool(options) {
954
+ const body = {
955
+ image: options.image,
956
+ resources: {
957
+ cpus: options.cpus ?? 1,
958
+ memory_mb: options.memoryMb ?? 1024,
959
+ ephemeral_disk_mb: options.ephemeralDiskMb ?? 1024
960
+ },
961
+ timeout_secs: options.timeoutSecs ?? 0
962
+ };
963
+ if (options.secretNames != null) body.secret_names = options.secretNames;
964
+ if (options.entrypoint != null) body.entrypoint = options.entrypoint;
965
+ if (options.maxContainers != null) body.max_containers = options.maxContainers;
966
+ if (options.warmContainers != null) body.warm_containers = options.warmContainers;
967
+ const raw = await this.http.requestJson(
968
+ "POST",
969
+ this.path("sandbox-pools"),
970
+ { body }
971
+ );
972
+ return fromSnakeKeys(raw, "poolId");
973
+ }
974
+ async getPool(poolId) {
975
+ const raw = await this.http.requestJson(
976
+ "GET",
977
+ this.path(`sandbox-pools/${poolId}`)
978
+ );
979
+ return fromSnakeKeys(raw, "poolId");
980
+ }
981
+ async listPools() {
982
+ const raw = await this.http.requestJson(
983
+ "GET",
984
+ this.path("sandbox-pools")
985
+ );
986
+ return (raw.pools ?? []).map(
987
+ (p) => fromSnakeKeys(p, "poolId")
988
+ );
989
+ }
990
+ async updatePool(poolId, options) {
991
+ const body = {
992
+ image: options.image,
993
+ resources: {
994
+ cpus: options.cpus ?? 1,
995
+ memory_mb: options.memoryMb ?? 1024,
996
+ ephemeral_disk_mb: options.ephemeralDiskMb ?? 1024
997
+ },
998
+ timeout_secs: options.timeoutSecs ?? 0
999
+ };
1000
+ if (options.secretNames != null) body.secret_names = options.secretNames;
1001
+ if (options.entrypoint != null) body.entrypoint = options.entrypoint;
1002
+ if (options.maxContainers != null) body.max_containers = options.maxContainers;
1003
+ if (options.warmContainers != null) body.warm_containers = options.warmContainers;
1004
+ const raw = await this.http.requestJson(
1005
+ "PUT",
1006
+ this.path(`sandbox-pools/${poolId}`),
1007
+ { body }
1008
+ );
1009
+ return fromSnakeKeys(raw, "poolId");
1010
+ }
1011
+ async deletePool(poolId) {
1012
+ await this.http.requestJson(
1013
+ "DELETE",
1014
+ this.path(`sandbox-pools/${poolId}`)
1015
+ );
1016
+ }
1017
+ // --- Connect ---
1018
+ connect(sandboxId, proxyUrl) {
1019
+ const resolvedProxy = proxyUrl ?? resolveProxyUrl(this.apiUrl);
1020
+ return new Sandbox({
1021
+ sandboxId,
1022
+ proxyUrl: resolvedProxy,
1023
+ apiKey: this.apiKey,
1024
+ organizationId: this.organizationId,
1025
+ projectId: this.projectId
1026
+ });
1027
+ }
1028
+ async createAndConnect(options) {
1029
+ const startupTimeout = options?.startupTimeout ?? 60;
1030
+ let result;
1031
+ if (options?.poolId != null) {
1032
+ result = await this.claim(options.poolId);
1033
+ } else {
1034
+ result = await this.create(options);
1035
+ }
1036
+ const deadline = Date.now() + startupTimeout * 1e3;
1037
+ while (Date.now() < deadline) {
1038
+ const info = await this.get(result.sandboxId);
1039
+ if (info.status === "running" /* RUNNING */) {
1040
+ const sandbox = this.connect(result.sandboxId, options?.proxyUrl);
1041
+ sandbox._setOwner(this);
1042
+ return sandbox;
1043
+ }
1044
+ if (info.status === "terminated" /* TERMINATED */) {
1045
+ throw new SandboxError(
1046
+ `Sandbox ${result.sandboxId} terminated during startup`
1047
+ );
1048
+ }
1049
+ await sleep3(500);
1050
+ }
1051
+ try {
1052
+ await this.delete(result.sandboxId);
1053
+ } catch {
1054
+ }
1055
+ throw new SandboxError(
1056
+ `Sandbox ${result.sandboxId} did not start within ${startupTimeout}s`
1057
+ );
1058
+ }
1059
+ };
1060
+ function sleep3(ms) {
1061
+ return new Promise((resolve) => setTimeout(resolve, ms));
1062
+ }
1063
+
1064
+ // src/cloud-client.ts
1065
+ var CloudClient = class _CloudClient {
1066
+ http;
1067
+ organizationId;
1068
+ projectId;
1069
+ namespace;
1070
+ constructor(options) {
1071
+ this.organizationId = options?.organizationId;
1072
+ this.projectId = options?.projectId;
1073
+ this.namespace = options?.namespace ?? NAMESPACE;
1074
+ this.http = new HttpClient({
1075
+ baseUrl: options?.apiUrl ?? API_URL,
1076
+ apiKey: options?.apiKey ?? API_KEY,
1077
+ organizationId: this.organizationId,
1078
+ projectId: this.projectId,
1079
+ maxRetries: options?.maxRetries ?? MAX_RETRIES,
1080
+ retryBackoffMs: options?.retryBackoffMs ?? RETRY_BACKOFF_MS
1081
+ });
1082
+ }
1083
+ static forCloud(options) {
1084
+ return new _CloudClient(options);
1085
+ }
1086
+ close() {
1087
+ this.http.close();
1088
+ }
1089
+ async upsertApplication(manifest, codeZip, upgradeRunningRequests = false) {
1090
+ const form = new FormData();
1091
+ form.append(
1092
+ "code",
1093
+ new Blob([toBlobPart(codeZip)], { type: "application/zip" }),
1094
+ "code.zip"
1095
+ );
1096
+ form.append("code_content_type", "application/zip");
1097
+ form.append("application", JSON.stringify(manifest));
1098
+ form.append(
1099
+ "upgrade_requests_to_latest_code",
1100
+ String(upgradeRunningRequests)
1101
+ );
1102
+ await this.http.requestResponse("POST", this.namespacePath("applications"), {
1103
+ body: form
1104
+ });
1105
+ }
1106
+ async deleteApplication(applicationName) {
1107
+ await this.http.requestResponse(
1108
+ "DELETE",
1109
+ this.namespacePath(`applications/${encodeURIComponent(applicationName)}`)
1110
+ );
1111
+ }
1112
+ async applications() {
1113
+ const raw = await this.http.requestJson(
1114
+ "GET",
1115
+ this.namespacePath("applications")
1116
+ );
1117
+ return (raw.applications ?? []).map(
1118
+ (application) => fromSnakeKeys(application)
1119
+ );
1120
+ }
1121
+ async applicationManifest(applicationName) {
1122
+ const raw = await this.http.requestJson(
1123
+ "GET",
1124
+ this.namespacePath(`applications/${encodeURIComponent(applicationName)}`)
1125
+ );
1126
+ return fromSnakeKeys(raw);
1127
+ }
1128
+ async runRequest(applicationName, inputs = []) {
1129
+ const path = this.namespacePath(
1130
+ `applications/${encodeURIComponent(applicationName)}`
1131
+ );
1132
+ const response = inputs.length === 0 ? await this.http.requestResponse("POST", path, {
1133
+ body: new Uint8Array(),
1134
+ headers: { Accept: "application/json" }
1135
+ }) : inputs.length === 1 && inputs[0].name === "0" ? await this.http.requestResponse("POST", path, {
1136
+ body: toRequestBody(inputs[0].data),
1137
+ headers: {
1138
+ Accept: "application/json",
1139
+ "Content-Type": inputs[0].contentType
1140
+ }
1141
+ }) : await this.runMultipartRequest(path, inputs);
1142
+ const body = await parseJsonResponse(response);
1143
+ const requestId = body?.request_id;
1144
+ if (!requestId) {
1145
+ throw new Error("missing request_id in run request response body");
1146
+ }
1147
+ return requestId;
1148
+ }
1149
+ async waitOnRequestCompletion(applicationName, requestId) {
1150
+ const stream = await this.http.requestStream(
1151
+ "GET",
1152
+ this.namespacePath(
1153
+ `applications/${encodeURIComponent(applicationName)}/requests/${encodeURIComponent(requestId)}/progress`
1154
+ )
1155
+ );
1156
+ for await (const event of parseSSEStream(stream)) {
1157
+ if (Object.prototype.hasOwnProperty.call(event, "RequestFinished")) {
1158
+ return;
1159
+ }
1160
+ }
1161
+ throw new Error("progress stream ended before request completion");
1162
+ }
1163
+ async requestMetadata(applicationName, requestId) {
1164
+ const raw = await this.http.requestJson(
1165
+ "GET",
1166
+ this.namespacePath(
1167
+ `applications/${encodeURIComponent(applicationName)}/requests/${encodeURIComponent(requestId)}`
1168
+ )
1169
+ );
1170
+ return fromSnakeKeys(raw);
1171
+ }
1172
+ async requestOutput(applicationName, requestId) {
1173
+ const response = await this.http.requestResponse(
1174
+ "GET",
1175
+ this.namespacePath(
1176
+ `applications/${encodeURIComponent(applicationName)}/requests/${encodeURIComponent(requestId)}/output`
1177
+ )
1178
+ );
1179
+ const serializedValue = new Uint8Array(await response.arrayBuffer());
1180
+ const contentType = response.headers.get("content-type") ?? "";
1181
+ return {
1182
+ serializedValue,
1183
+ contentType
1184
+ };
1185
+ }
1186
+ async introspectApiKey() {
1187
+ const raw = await this.http.requestJson(
1188
+ "POST",
1189
+ "/platform/v1/keys/introspect"
1190
+ );
1191
+ return fromSnakeKeys(raw);
1192
+ }
1193
+ async listSecrets(options) {
1194
+ const scope = this.resolveScope(options?.organizationId, options?.projectId);
1195
+ const raw = await this.http.requestJson(
1196
+ "GET",
1197
+ `/platform/v1/organizations/${encodeURIComponent(scope.organizationId)}/projects/${encodeURIComponent(scope.projectId)}/secrets?pageSize=${options?.pageSize ?? 100}`
1198
+ );
1199
+ return fromSnakeKeys(raw);
1200
+ }
1201
+ async getSecret(secretId, options) {
1202
+ const scope = this.resolveScope(options?.organizationId, options?.projectId);
1203
+ const raw = await this.http.requestJson(
1204
+ "GET",
1205
+ `/platform/v1/organizations/${encodeURIComponent(scope.organizationId)}/projects/${encodeURIComponent(scope.projectId)}/secrets/${encodeURIComponent(secretId)}`
1206
+ );
1207
+ return fromSnakeKeys(raw);
1208
+ }
1209
+ async upsertSecrets(secrets, options) {
1210
+ const scope = this.resolveScope(options?.organizationId, options?.projectId);
1211
+ const raw = await this.http.requestJson(
1212
+ "PUT",
1213
+ `/platform/v1/organizations/${encodeURIComponent(scope.organizationId)}/projects/${encodeURIComponent(scope.projectId)}/secrets`,
1214
+ { body: secrets }
1215
+ );
1216
+ if (Array.isArray(raw)) {
1217
+ return raw.map((secret) => fromSnakeKeys(secret));
1218
+ }
1219
+ return fromSnakeKeys(raw);
1220
+ }
1221
+ async deleteSecret(secretId, options) {
1222
+ const scope = this.resolveScope(options?.organizationId, options?.projectId);
1223
+ await this.http.requestResponse(
1224
+ "DELETE",
1225
+ `/platform/v1/organizations/${encodeURIComponent(scope.organizationId)}/projects/${encodeURIComponent(scope.projectId)}/secrets/${encodeURIComponent(secretId)}`
1226
+ );
1227
+ }
1228
+ async startImageBuild(buildServicePath, request) {
1229
+ const form = new FormData();
1230
+ form.append("graph_name", request.applicationName);
1231
+ form.append("graph_version", request.applicationVersion);
1232
+ form.append("graph_function_name", request.functionName);
1233
+ form.append("image_name", request.imageName);
1234
+ form.append("image_id", request.imageId);
1235
+ form.append(
1236
+ "context",
1237
+ new Blob([toBlobPart(request.buildContext)]),
1238
+ "context.tar.gz"
1239
+ );
1240
+ const response = await this.http.requestResponse(
1241
+ "PUT",
1242
+ `${trimTrailingSlashes(buildServicePath)}/builds`,
1243
+ { body: form }
1244
+ );
1245
+ const raw = await parseJsonResponse(response);
1246
+ return fromSnakeKeys(raw);
1247
+ }
1248
+ async createApplicationBuild(buildServicePath, request, imageContexts) {
1249
+ const form = createApplicationBuildForm(request, imageContexts);
1250
+ const response = await this.http.requestResponse(
1251
+ "POST",
1252
+ trimTrailingSlashes(buildServicePath),
1253
+ { body: form }
1254
+ );
1255
+ const raw = await parseJsonResponse(response);
1256
+ return fromSnakeKeys(raw);
1257
+ }
1258
+ async applicationBuildInfo(buildServicePath, applicationBuildId) {
1259
+ const raw = await this.http.requestJson(
1260
+ "GET",
1261
+ `${trimTrailingSlashes(buildServicePath)}/${encodeURIComponent(applicationBuildId)}`
1262
+ );
1263
+ return fromSnakeKeys(raw);
1264
+ }
1265
+ async cancelApplicationBuild(buildServicePath, applicationBuildId) {
1266
+ const raw = await this.http.requestJson(
1267
+ "POST",
1268
+ `${trimTrailingSlashes(buildServicePath)}/${encodeURIComponent(applicationBuildId)}/cancel`
1269
+ );
1270
+ return fromSnakeKeys(raw);
1271
+ }
1272
+ async buildInfo(buildServicePath, buildId) {
1273
+ const raw = await this.http.requestJson(
1274
+ "GET",
1275
+ `${trimTrailingSlashes(buildServicePath)}/builds/${encodeURIComponent(buildId)}`
1276
+ );
1277
+ return fromSnakeKeys(raw);
1278
+ }
1279
+ async cancelBuild(buildServicePath, buildId) {
1280
+ await this.http.requestResponse(
1281
+ "POST",
1282
+ `${trimTrailingSlashes(buildServicePath)}/builds/${encodeURIComponent(buildId)}/cancel`
1283
+ );
1284
+ }
1285
+ async *streamBuildLogs(buildServicePath, buildId, signal) {
1286
+ const stream = await this.http.requestStream(
1287
+ "GET",
1288
+ `${trimTrailingSlashes(buildServicePath)}/builds/${encodeURIComponent(buildId)}/logs`,
1289
+ { signal }
1290
+ );
1291
+ for await (const event of parseSSEStream(stream, signal)) {
1292
+ yield fromSnakeKeys(event);
1293
+ }
1294
+ }
1295
+ async runMultipartRequest(path, inputs) {
1296
+ const form = new FormData();
1297
+ for (const input of inputs) {
1298
+ form.append(
1299
+ input.name,
1300
+ new Blob([toBlobPart(input.data)], { type: input.contentType }),
1301
+ input.name
1302
+ );
1303
+ }
1304
+ return this.http.requestResponse("POST", path, {
1305
+ body: form,
1306
+ headers: { Accept: "application/json" }
1307
+ });
1308
+ }
1309
+ namespacePath(subpath) {
1310
+ return `/v1/namespaces/${encodeURIComponent(this.namespace)}/${subpath.replace(/^\/+/, "")}`;
1311
+ }
1312
+ resolveScope(organizationId, projectId) {
1313
+ const resolvedOrganizationId = organizationId ?? this.organizationId;
1314
+ const resolvedProjectId = projectId ?? this.projectId;
1315
+ if (!resolvedOrganizationId || !resolvedProjectId) {
1316
+ throw new Error(
1317
+ "organizationId and projectId are required for this operation"
1318
+ );
1319
+ }
1320
+ return {
1321
+ organizationId: resolvedOrganizationId,
1322
+ projectId: resolvedProjectId
1323
+ };
1324
+ }
1325
+ };
1326
+ function createApplicationBuildForm(request, imageContexts) {
1327
+ const contextsByPartName = /* @__PURE__ */ new Map();
1328
+ for (const context of imageContexts) {
1329
+ if (contextsByPartName.has(context.contextTarPartName)) {
1330
+ throw new Error(
1331
+ `duplicate image context part name '${context.contextTarPartName}'`
1332
+ );
1333
+ }
1334
+ contextsByPartName.set(context.contextTarPartName, context);
1335
+ }
1336
+ const form = new FormData();
1337
+ form.append(
1338
+ "app_version",
1339
+ new Blob([JSON.stringify(request)], { type: "application/json" }),
1340
+ "app_version"
1341
+ );
1342
+ for (const image of request.images) {
1343
+ const context = contextsByPartName.get(image.contextTarPartName);
1344
+ if (!context) {
1345
+ throw new Error(
1346
+ `missing image context for part '${image.contextTarPartName}'`
1347
+ );
1348
+ }
1349
+ form.append(
1350
+ image.contextTarPartName,
1351
+ new Blob([toBlobPart(context.contextTarGz)]),
1352
+ `${image.contextTarPartName}.tar.gz`
1353
+ );
1354
+ }
1355
+ for (const context of imageContexts) {
1356
+ if (!request.images.some((image) => image.contextTarPartName === context.contextTarPartName)) {
1357
+ throw new Error(
1358
+ `unexpected image context for part '${context.contextTarPartName}'`
1359
+ );
1360
+ }
1361
+ }
1362
+ return form;
1363
+ }
1364
+ function trimTrailingSlashes(value) {
1365
+ return value.replace(/\/+$/, "");
1366
+ }
1367
+ function toBlobPart(data) {
1368
+ if (typeof data === "string" || data instanceof Blob) {
1369
+ return data;
1370
+ }
1371
+ if (data instanceof Uint8Array) {
1372
+ return Uint8Array.from(data).buffer;
1373
+ }
1374
+ return data;
1375
+ }
1376
+ function toRequestBody(data) {
1377
+ return toBlobPart(data);
1378
+ }
1379
+ async function parseJsonResponse(response) {
1380
+ const text = await response.text();
1381
+ if (!text) {
1382
+ return void 0;
1383
+ }
1384
+ return JSON.parse(text);
1385
+ }
1386
+
1387
+ // src/api-client.ts
1388
+ var APIClient = class {
1389
+ cloudClient;
1390
+ constructor(options) {
1391
+ this.cloudClient = new CloudClient(options);
1392
+ }
1393
+ close() {
1394
+ this.cloudClient.close();
1395
+ }
1396
+ async upsertApplication(manifest, codeZip, upgradeRunningRequests = false) {
1397
+ await this.cloudClient.upsertApplication(
1398
+ manifest,
1399
+ codeZip,
1400
+ upgradeRunningRequests
1401
+ );
1402
+ }
1403
+ async deleteApplication(applicationName) {
1404
+ await this.cloudClient.deleteApplication(applicationName);
1405
+ }
1406
+ async applications() {
1407
+ return this.cloudClient.applications();
1408
+ }
1409
+ async application(applicationName) {
1410
+ return this.cloudClient.applicationManifest(applicationName);
1411
+ }
1412
+ async runRequest(applicationName, inputs) {
1413
+ return this.cloudClient.runRequest(applicationName, inputs);
1414
+ }
1415
+ async waitOnRequestCompletion(applicationName, requestId) {
1416
+ await this.cloudClient.waitOnRequestCompletion(applicationName, requestId);
1417
+ }
1418
+ async requestOutput(applicationName, requestId) {
1419
+ const metadata = await this.cloudClient.requestMetadata(
1420
+ applicationName,
1421
+ requestId
1422
+ );
1423
+ if (metadata.outcome == null) {
1424
+ throw new RequestNotFinishedError();
1425
+ }
1426
+ if (typeof metadata.outcome === "object") {
1427
+ if (metadata.requestError?.message) {
1428
+ throw new RequestExecutionError(
1429
+ metadata.requestError.message,
1430
+ metadata.requestError.functionName
1431
+ );
1432
+ }
1433
+ const failure = typeof metadata.outcome.failure === "string" ? metadata.outcome.failure : JSON.stringify(metadata.outcome);
1434
+ throw new RequestFailedError(failure);
1435
+ }
1436
+ return this.cloudClient.requestOutput(applicationName, requestId);
1437
+ }
1438
+ };
1439
+ // Annotate the CommonJS export names for ESM import in node:
1440
+ 0 && (module.exports = {
1441
+ APIClient,
1442
+ CloudClient,
1443
+ ContainerState,
1444
+ OutputMode,
1445
+ PoolInUseError,
1446
+ PoolNotFoundError,
1447
+ ProcessStatus,
1448
+ RemoteAPIError,
1449
+ RequestExecutionError,
1450
+ RequestFailedError,
1451
+ RequestNotFinishedError,
1452
+ Sandbox,
1453
+ SandboxClient,
1454
+ SandboxConnectionError,
1455
+ SandboxError,
1456
+ SandboxException,
1457
+ SandboxNotFoundError,
1458
+ SandboxStatus,
1459
+ SnapshotStatus,
1460
+ StdinMode
1461
+ });
1462
+ //# sourceMappingURL=index.cjs.map