@tensorlakeai/tensorlake 0.5.151 → 0.5.153

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.
Files changed (42) hide show
  1. package/README.md +8 -0
  2. package/dist/applications/index.cjs +3510 -3384
  3. package/dist/applications/index.cjs.map +1 -1
  4. package/dist/applications/index.d.cts +3 -3
  5. package/dist/applications/index.d.ts +3 -3
  6. package/dist/applications/index.js +3286 -3160
  7. package/dist/applications/index.js.map +1 -1
  8. package/dist/function-agent/main.cjs +1 -1
  9. package/dist/function-agent/main.cjs.map +1 -1
  10. package/dist/function-agent/main.js +1 -1
  11. package/dist/function-agent/main.js.map +1 -1
  12. package/dist/function-executor/main.cjs +2 -2
  13. package/dist/function-executor/main.cjs.map +1 -1
  14. package/dist/function-executor/main.js +2 -2
  15. package/dist/function-executor/main.js.map +1 -1
  16. package/dist/index.cjs +1424 -1079
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +313 -27
  19. package/dist/index.d.ts +313 -27
  20. package/dist/index.js +1401 -1069
  21. package/dist/index.js.map +1 -1
  22. package/dist/{remote-CC48cvYP.d.cts → remote-B3vn4tt1.d.cts} +1 -1
  23. package/dist/{remote-BbSrvgUn.d.ts → remote-DqBUuciu.d.ts} +1 -1
  24. package/dist/{sandbox-image-CAK_K7ND.d.cts → sandbox-image-BMiFlzAs.d.cts} +168 -13
  25. package/dist/{sandbox-image-CAK_K7ND.d.ts → sandbox-image-BMiFlzAs.d.ts} +168 -13
  26. package/dist/sandbox-image.cjs +339 -1295
  27. package/dist/sandbox-image.cjs.map +1 -1
  28. package/dist/sandbox-image.d.cts +1 -1
  29. package/dist/sandbox-image.d.ts +1 -1
  30. package/dist/sandbox-image.js +338 -1294
  31. package/dist/sandbox-image.js.map +1 -1
  32. package/package.json +8 -7
  33. package/runtime/function-executor/manifest.json +7 -7
  34. package/runtime/function-executor/package/dist/function-executor/main.js +2 -2
  35. package/runtime/function-executor/package/dist/function-executor/main.js.map +1 -1
  36. package/runtime/function-executor/package/npm-shrinkwrap.json +2 -2
  37. package/runtime/function-executor/package/package.json +1 -1
  38. package/runtime/typescript-function-runner/manifest.json +7 -7
  39. package/runtime/typescript-function-runner/package/dist/function-agent/main.js +1 -1
  40. package/runtime/typescript-function-runner/package/dist/function-agent/main.js.map +1 -1
  41. package/runtime/typescript-function-runner/package/npm-shrinkwrap.json +26 -26
  42. package/runtime/typescript-function-runner/package/package.json +7 -7
package/dist/index.cjs CHANGED
@@ -49,7 +49,7 @@ var init_defaults = __esm({
49
49
  "src/defaults.ts"() {
50
50
  "use strict";
51
51
  init_cjs_shims();
52
- SDK_VERSION = "0.5.151";
52
+ SDK_VERSION = "0.5.153";
53
53
  API_URL = process.env.TENSORLAKE_API_URL ?? "https://api.tensorlake.ai";
54
54
  API_KEY = process.env.TENSORLAKE_API_KEY ?? void 0;
55
55
  NAMESPACE = process.env.INDEXIFY_NAMESPACE ?? "default";
@@ -314,6 +314,11 @@ function createWorkerBinding(runtime) {
314
314
  return runtime.handle("NativeRepositoryClient", args);
315
315
  }
316
316
  },
317
+ NativeTcpTunnel: class {
318
+ constructor() {
319
+ return runtime.handle("NativeTcpTunnel", []);
320
+ }
321
+ },
317
322
  validateManagedName: (name) => runtime.call(void 0, "validateManagedName", [name])
318
323
  };
319
324
  }
@@ -493,7 +498,7 @@ var init_native_worker_client = __esm({
493
498
  }
494
499
  };
495
500
  runtimeKey = /* @__PURE__ */ Symbol.for(
496
- `tensorlake.native.worker.${false ? "source" : "0.5.151"}.v1`
501
+ `tensorlake.native.worker.${false ? "source" : "0.5.153"}.v1`
497
502
  );
498
503
  runtimes = globalThis;
499
504
  }
@@ -593,6 +598,8 @@ var init_models = __esm({
593
598
  SnapshotStatus2["LOCAL_READY"] = "local_ready";
594
599
  SnapshotStatus2["COMPLETED"] = "completed";
595
600
  SnapshotStatus2["FAILED"] = "failed";
601
+ SnapshotStatus2["DELETING"] = "deleting";
602
+ SnapshotStatus2["REPLICATING"] = "replicating";
596
603
  return SnapshotStatus2;
597
604
  })(SnapshotStatus || {});
598
605
  ProcessStatus = /* @__PURE__ */ ((ProcessStatus2) => {
@@ -941,116 +948,6 @@ function normalizeWebSocketData(message) {
941
948
  }
942
949
  return Buffer.from(message);
943
950
  }
944
- async function relaySocket(localSocket, websocket) {
945
- return new Promise((resolve2) => {
946
- let settled = false;
947
- const keepAliveInterval = setInterval(() => {
948
- if (websocket.readyState === import_ws.default.OPEN) {
949
- websocket.ping();
950
- }
951
- }, WEBSOCKET_KEEPALIVE_INTERVAL_MS);
952
- keepAliveInterval.unref?.();
953
- const finish = () => {
954
- if (settled) return;
955
- settled = true;
956
- clearInterval(keepAliveInterval);
957
- cleanup();
958
- resolve2();
959
- };
960
- const cleanup = () => {
961
- localSocket.removeListener("data", onLocalData);
962
- localSocket.removeListener("end", onLocalEnd);
963
- localSocket.removeListener("close", onLocalClose);
964
- localSocket.removeListener("error", onLocalError);
965
- websocket.removeListener("message", onWsMessage);
966
- websocket.removeListener("close", onWsClose);
967
- websocket.removeListener("error", onWsError);
968
- websocket.removeListener("ping", onWsPing);
969
- };
970
- const onLocalData = (chunk) => {
971
- if (websocket.readyState !== import_ws.default.OPEN) {
972
- localSocket.destroy();
973
- return;
974
- }
975
- websocket.send(chunk, { binary: true }, (error) => {
976
- if (error) {
977
- localSocket.destroy(error);
978
- }
979
- });
980
- };
981
- const onLocalEnd = () => {
982
- if (websocket.readyState === import_ws.default.OPEN) {
983
- websocket.close();
984
- }
985
- };
986
- const onLocalClose = () => {
987
- if (websocket.readyState === import_ws.default.OPEN || websocket.readyState === import_ws.default.CONNECTING) {
988
- websocket.close();
989
- }
990
- finish();
991
- };
992
- const onLocalError = () => {
993
- websocket.close();
994
- finish();
995
- };
996
- const onWsMessage = (message, isBinary) => {
997
- if (!isBinary) {
998
- localSocket.destroy(
999
- new SandboxError("received unexpected text frame from tunnel")
1000
- );
1001
- websocket.close();
1002
- return;
1003
- }
1004
- const payload = normalizeWebSocketData(message);
1005
- if (!localSocket.destroyed) {
1006
- localSocket.write(payload);
1007
- }
1008
- };
1009
- const onWsClose = () => {
1010
- if (!localSocket.destroyed) {
1011
- localSocket.end();
1012
- }
1013
- finish();
1014
- };
1015
- const onWsError = (error) => {
1016
- localSocket.destroy(error);
1017
- finish();
1018
- };
1019
- const onWsPing = (data) => {
1020
- if (websocket.readyState === import_ws.default.OPEN) {
1021
- websocket.pong(data, false, () => {
1022
- });
1023
- }
1024
- };
1025
- localSocket.on("data", onLocalData);
1026
- localSocket.on("end", onLocalEnd);
1027
- localSocket.on("close", onLocalClose);
1028
- localSocket.on("error", onLocalError);
1029
- websocket.on("message", onWsMessage);
1030
- websocket.on("close", onWsClose);
1031
- websocket.on("error", onWsError);
1032
- websocket.on("ping", onWsPing);
1033
- });
1034
- }
1035
- async function listenServer(server, localPort, localHost) {
1036
- await new Promise((resolve2, reject) => {
1037
- const onError = (error) => {
1038
- server.removeListener("listening", onListening);
1039
- reject(
1040
- new SandboxError(
1041
- `failed to bind local tunnel listener on ${localHost}:${localPort}: ${error.message}`
1042
- )
1043
- );
1044
- };
1045
- const onListening = () => {
1046
- server.removeListener("error", onError);
1047
- resolve2();
1048
- };
1049
- server.once("error", onError);
1050
- server.once("listening", onListening);
1051
- server.listen(localPort, localHost);
1052
- });
1053
- }
1054
951
  function validatePort(port, label, allowZero = false) {
1055
952
  if (!Number.isInteger(port)) {
1056
953
  throw new SandboxError(`${label} must be an integer, got ${port}`);
@@ -1083,14 +980,15 @@ async function withTimeout(timeoutMs, operation, timeoutMessage) {
1083
980
  });
1084
981
  });
1085
982
  }
1086
- var net, import_ws, DEFAULT_TUNNEL_CONNECT_TIMEOUT_MS, WEBSOCKET_KEEPALIVE_INTERVAL_MS, TunnelByteStream, TcpTunnel;
983
+ var import_ws, DEFAULT_TUNNEL_CONNECT_TIMEOUT_MS, WEBSOCKET_KEEPALIVE_INTERVAL_MS, TunnelByteStream, TcpTunnel;
1087
984
  var init_tunnel = __esm({
1088
985
  "src/tunnel.ts"() {
1089
986
  "use strict";
1090
987
  init_cjs_shims();
1091
- net = __toESM(require("net"), 1);
1092
988
  init_errors();
1093
989
  import_ws = __toESM(require("ws"), 1);
990
+ init_native_sandbox();
991
+ init_native_worker_client();
1094
992
  DEFAULT_TUNNEL_CONNECT_TIMEOUT_MS = 1e4;
1095
993
  WEBSOCKET_KEEPALIVE_INTERVAL_MS = 15e3;
1096
994
  TunnelByteStream = class _TunnelByteStream {
@@ -1220,24 +1118,17 @@ var init_tunnel = __esm({
1220
1118
  }
1221
1119
  };
1222
1120
  TcpTunnel = class _TcpTunnel {
1223
- remotePort;
1224
- localHost;
1225
- localPort;
1226
- baseUrl;
1227
- wsHeaders;
1228
- server;
1229
- connectTimeoutMs;
1230
- activeRelays = /* @__PURE__ */ new Set();
1231
- closePromise = null;
1232
- constructor(options) {
1233
- this.baseUrl = options.baseUrl;
1234
- this.wsHeaders = options.wsHeaders;
1235
- this.remotePort = options.remotePort;
1236
- this.localHost = options.localHost;
1237
- this.localPort = options.localPort;
1238
- this.server = options.server;
1239
- this.connectTimeoutMs = options.connectTimeoutMs;
1121
+ constructor(native, remotePort, localHost, localPort) {
1122
+ this.native = native;
1123
+ this.remotePort = remotePort;
1124
+ this.localHost = localHost;
1125
+ this.localPort = localPort;
1126
+ this.lifetime = native.waitClosed();
1127
+ void this.lifetime.catch(() => {
1128
+ });
1240
1129
  }
1130
+ closePromise = null;
1131
+ lifetime;
1241
1132
  static async listen(options) {
1242
1133
  const remotePort = validatePort(options.remotePort, "remote port");
1243
1134
  const localHost = options.localHost ?? "127.0.0.1";
@@ -1246,69 +1137,37 @@ var init_tunnel = __esm({
1246
1137
  "local port",
1247
1138
  true
1248
1139
  );
1249
- const connectTimeoutMs = secondsToMillis(options.connectTimeout ?? 10);
1250
- const server = net.createServer();
1251
- await listenServer(server, localPort, localHost);
1252
- const address = server.address();
1253
- if (!address || typeof address === "string") {
1254
- server.close();
1255
- throw new SandboxError("failed to determine bound tunnel address");
1256
- }
1257
- const tunnel = new _TcpTunnel({
1258
- baseUrl: options.baseUrl,
1259
- wsHeaders: options.wsHeaders,
1260
- remotePort,
1261
- localHost,
1262
- localPort: address.port,
1263
- server,
1264
- connectTimeoutMs
1265
- });
1266
- server.on("connection", (localSocket) => {
1267
- void tunnel.handleConnection(localSocket);
1268
- });
1269
- return tunnel;
1140
+ const timeout = secondsToMillis(options.connectTimeout ?? 10) / 1e3;
1141
+ const native = new (loadNativeSandboxBinding()).NativeTcpTunnel();
1142
+ try {
1143
+ const port = await callNative(
1144
+ () => native.listen(
1145
+ options.baseUrl,
1146
+ options.wsHeaders,
1147
+ remotePort,
1148
+ localHost,
1149
+ localPort,
1150
+ timeout
1151
+ )
1152
+ );
1153
+ return new _TcpTunnel(native, remotePort, localHost, port);
1154
+ } catch (error) {
1155
+ releaseNativeHandle(native);
1156
+ throw error;
1157
+ }
1270
1158
  }
1271
1159
  address() {
1272
1160
  return { host: this.localHost, port: this.localPort };
1273
1161
  }
1274
1162
  async close() {
1275
- if (this.closePromise) {
1276
- return this.closePromise;
1277
- }
1278
- for (const relay of this.activeRelays) {
1279
- relay.localSocket.destroy();
1280
- relay.websocket?.close();
1281
- }
1282
- this.closePromise = new Promise((resolve2, reject) => {
1283
- this.server.close((error) => {
1284
- if (error) {
1285
- reject(new SandboxError(`failed to close tunnel listener: ${error.message}`));
1286
- return;
1287
- }
1288
- resolve2();
1289
- });
1290
- });
1291
- return this.closePromise;
1292
- }
1293
- async handleConnection(localSocket) {
1294
- localSocket.setNoDelay(true);
1295
- const relay = { localSocket, websocket: null };
1296
- this.activeRelays.add(relay);
1297
- try {
1298
- relay.websocket = await openTunnelWebSocket({
1299
- baseUrl: this.baseUrl,
1300
- wsHeaders: this.wsHeaders,
1301
- remotePort: this.remotePort,
1302
- connectTimeoutMs: this.connectTimeoutMs
1303
- });
1304
- await relaySocket(localSocket, relay.websocket);
1305
- } catch (error) {
1306
- localSocket.destroy(
1307
- error instanceof Error ? error : new Error(String(error))
1308
- );
1309
- } finally {
1310
- this.activeRelays.delete(relay);
1311
- }
1163
+ return this.closePromise ??= (async () => {
1164
+ try {
1165
+ await callNative(() => this.native.close());
1166
+ await this.lifetime;
1167
+ } finally {
1168
+ releaseNativeHandle(this.native);
1169
+ }
1170
+ })();
1312
1171
  }
1313
1172
  };
1314
1173
  }
@@ -4029,9 +3888,8 @@ var init_sandbox = __esm({
4029
3888
  *
4030
3889
  * Resource targets use create's cpus, memoryMb, and diskMb names. Resize
4031
3890
  * waits by default; wait=false returns admission. Timeout does not cancel it.
4032
- * CPU-only CAS sandboxes support CPU, memory, and root-disk resize; GPU CAS sandboxes
4033
- * support only root-disk growth and reject CPU and memory changes.
4034
- * Non-CAS sandboxes do not support live resize.
3891
+ * Which runtimes and dimensions support a live resize is decided by the
3892
+ * server, whose policy error is thrown unchanged.
4035
3893
  * Wait, timeout, and pollInterval are ignored on non-resource updates.
4036
3894
  * A no-op returns current resources with absent or earlier resize metadata.
4037
3895
  *
@@ -4449,29 +4307,29 @@ var init_sandbox = __esm({
4449
4307
  }
4450
4308
  // --- File operations ---
4451
4309
  /** Read a file from the sandbox and return its raw bytes. */
4452
- async readFile(path3) {
4310
+ async readFile(path4) {
4453
4311
  const proxy = await this.proxy.client();
4454
- const { traceId, data } = await callNative(() => proxy.readFile(path3), {
4312
+ const { traceId, data } = await callNative(() => proxy.readFile(path4), {
4455
4313
  sandboxId: this.sandboxId
4456
4314
  });
4457
4315
  return Object.assign(Uint8Array.from(data), { traceId });
4458
4316
  }
4459
4317
  /** Write raw bytes to a file in the sandbox, creating it if it does not exist. */
4460
- async writeFile(path3, content) {
4318
+ async writeFile(path4, content) {
4461
4319
  const proxy = await this.proxy.client();
4462
- await callNative(() => proxy.writeFile(path3, Buffer.from(content)), {
4320
+ await callNative(() => proxy.writeFile(path4, Buffer.from(content)), {
4463
4321
  sandboxId: this.sandboxId
4464
4322
  });
4465
4323
  }
4466
4324
  /** Delete a file from the sandbox. */
4467
- async deleteFile(path3) {
4325
+ async deleteFile(path4) {
4468
4326
  const proxy = await this.proxy.client();
4469
- await callNative(() => proxy.deleteFile(path3), { sandboxId: this.sandboxId });
4327
+ await callNative(() => proxy.deleteFile(path4), { sandboxId: this.sandboxId });
4470
4328
  }
4471
4329
  /** List the contents of a directory in the sandbox. */
4472
- async listDirectory(path3) {
4330
+ async listDirectory(path4) {
4473
4331
  const proxy = await this.proxy.client();
4474
- const { traceId, json } = await callNative(() => proxy.listDirectory(path3), {
4332
+ const { traceId, json } = await callNative(() => proxy.listDirectory(path4), {
4475
4333
  sandboxId: this.sandboxId
4476
4334
  });
4477
4335
  return Object.assign(fromSnakeKeys(JSON.parse(json)), {
@@ -5137,9 +4995,9 @@ var init_client = __esm({
5137
4995
  });
5138
4996
  }
5139
4997
  /**
5140
- * Update properties or resize a running sandbox. CPU-only CAS sandboxes support
5141
- * CPU, memory, and root-disk resize; GPU CAS sandboxes support only root-disk
5142
- * growth. Non-CAS sandboxes do not support live resize.
4998
+ * Update properties or resize a running sandbox. Which runtimes and
4999
+ * dimensions support a live resize is decided by the server, whose policy
5000
+ * error is thrown unchanged.
5143
5001
  * Resource names match create. Resize waits by default; wait=false returns
5144
5002
  * admission. Timeout leaves the resize running and reports its generation.
5145
5003
  * Wait, timeout, and pollInterval are ignored on non-resource updates.
@@ -5458,6 +5316,11 @@ var init_client = __esm({
5458
5316
  `Snapshot ${result.snapshotId} failed: ${info.error}`
5459
5317
  );
5460
5318
  }
5319
+ if (info.status === "deleting" /* DELETING */) {
5320
+ throw new SandboxError(
5321
+ `Snapshot ${result.snapshotId} is being deleted`
5322
+ );
5323
+ }
5461
5324
  await sleep2(pollInterval * 1e3);
5462
5325
  }
5463
5326
  throw new SandboxError(
@@ -5715,705 +5578,126 @@ var init_client = __esm({
5715
5578
  }
5716
5579
  });
5717
5580
 
5718
- // src/native-cloud.ts
5719
- async function serializeRequest(method, path3, options) {
5720
- const headers = { ...options?.headers };
5721
- let body = options?.body;
5722
- if (options?.json !== void 0) {
5723
- body = JSON.stringify(options.json);
5724
- if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) {
5725
- headers["Content-Type"] = "application/json";
5726
- }
5727
- }
5728
- const request = {
5729
- method,
5730
- path: path3,
5731
- headersJson: JSON.stringify(headers),
5732
- statusOnlyCodes: [...options?.statusOnlyCodes ?? []]
5581
+ // src/image.ts
5582
+ function cloneOperation(op) {
5583
+ return {
5584
+ type: op.type,
5585
+ args: [...op.args],
5586
+ options: { ...op.options }
5733
5587
  };
5734
- if (body instanceof FormData) {
5735
- request.parts = [];
5736
- for (const [name, value] of body.entries()) {
5737
- request.parts.push(typeof value === "string" ? { name, data: Buffer.from(value) } : { name, data: Buffer.from(await value.arrayBuffer()), filename: value.name, contentType: value.type || "application/octet-stream" });
5738
- }
5739
- } else if (body !== void 0) {
5740
- if (body instanceof Blob) request.body = Buffer.from(await body.arrayBuffer());
5741
- else if (typeof body === "string") request.body = Buffer.from(body);
5742
- else if (body instanceof ArrayBuffer) request.body = Buffer.from(new Uint8Array(body));
5743
- else request.body = Buffer.from(body);
5588
+ }
5589
+ function renderOptions(options) {
5590
+ const entries = Object.entries(options);
5591
+ if (entries.length === 0) {
5592
+ return "";
5744
5593
  }
5745
- return request;
5594
+ return ` ${entries.map(([key, value]) => `--${key}=${value}`).join(" ")}`;
5595
+ }
5596
+ function renderBuildOp(op) {
5597
+ const options = renderOptions(op.options);
5598
+ if (op.type === ImageBuildOperationType.ENV) {
5599
+ return `ENV${options} ${op.args[0]}=${JSON.stringify(op.args[1])}`;
5600
+ }
5601
+ return `${op.type}${options} ${op.args.join(" ")}`;
5602
+ }
5603
+ function dockerfileContent(image) {
5604
+ const lines = image.baseImage == null ? [] : [`FROM ${image.baseImage}`];
5605
+ lines.push(...image.buildOperations.map((op) => renderBuildOp(op)));
5606
+ return lines.join("\n");
5746
5607
  }
5747
- var NativeCloudTransport;
5748
- var init_native_cloud = __esm({
5749
- "src/native-cloud.ts"() {
5608
+ var import_node_crypto, DEFAULT_IMAGE_NAME, ImageBuildOperationType, Image;
5609
+ var init_image = __esm({
5610
+ "src/image.ts"() {
5750
5611
  "use strict";
5751
5612
  init_cjs_shims();
5752
- init_native_sandbox();
5753
- init_native_worker_client();
5754
- NativeCloudTransport = class {
5755
- constructor(options) {
5756
- this.options = options;
5757
- if (!Number.isSafeInteger(options.maxRetries) || options.maxRetries < 0 || options.maxRetries > 4294967295) {
5758
- throw new RangeError("maxRetries must be a non-negative 32-bit integer");
5759
- }
5760
- if (!Number.isFinite(options.retryBackoffMs) || options.retryBackoffMs < 0) {
5761
- throw new RangeError("retryBackoffMs must be a non-negative finite number");
5613
+ import_node_crypto = require("crypto");
5614
+ DEFAULT_IMAGE_NAME = "default";
5615
+ ImageBuildOperationType = {
5616
+ ADD: "ADD",
5617
+ COPY: "COPY",
5618
+ ENV: "ENV",
5619
+ RUN: "RUN",
5620
+ WORKDIR: "WORKDIR"
5621
+ };
5622
+ Image = class {
5623
+ _id;
5624
+ _name;
5625
+ _tag;
5626
+ _baseImage;
5627
+ _buildOperations;
5628
+ constructor(nameOrOptions = {}, tag = "latest", baseImage = null) {
5629
+ this._id = (0, import_node_crypto.randomUUID)();
5630
+ this._buildOperations = [];
5631
+ if (typeof nameOrOptions === "string") {
5632
+ this._name = nameOrOptions;
5633
+ this._tag = tag;
5634
+ this._baseImage = baseImage;
5635
+ return;
5762
5636
  }
5637
+ this._name = nameOrOptions.name ?? DEFAULT_IMAGE_NAME;
5638
+ this._tag = nameOrOptions.tag ?? "latest";
5639
+ this._baseImage = nameOrOptions.baseImage ?? null;
5763
5640
  }
5764
- native;
5765
- closed = false;
5766
- client() {
5767
- if (this.closed) throw new Error("Tensorlake native client is closed");
5768
- if (this.native) return this.native;
5769
- const ctor = loadNativeSandboxBinding().NativeCloudClient;
5770
- if (!ctor) throw new Error("Native binding does not export NativeCloudClient; rebuild with 'npm run build:native'");
5771
- return this.native = new ctor(JSON.stringify(this.options));
5641
+ get name() {
5642
+ return this._name;
5772
5643
  }
5773
- close() {
5774
- this.closed = true;
5775
- releaseNativeHandle(this.native ?? null);
5776
- this.native = void 0;
5777
- }
5778
- async requestResponse(method, path3, options) {
5779
- const request = await serializeRequest(method, path3, options);
5780
- const raw = await callNative(() => this.client().request(request));
5781
- const response = new Response(
5782
- [204, 205, 304].includes(raw.status) ? null : Uint8Array.from(raw.data).buffer,
5783
- { status: raw.status, headers: JSON.parse(raw.headersJson) }
5784
- );
5785
- return Object.assign(response, { traceId: raw.traceId });
5644
+ get tag() {
5645
+ return this._tag;
5646
+ }
5647
+ get baseImage() {
5648
+ return this._baseImage;
5649
+ }
5650
+ get buildOperations() {
5651
+ return this._buildOperations.map(cloneOperation);
5652
+ }
5653
+ add(src, dest, options = void 0) {
5654
+ return this._addOperation({
5655
+ type: ImageBuildOperationType.ADD,
5656
+ args: [src, dest],
5657
+ options: options == null ? {} : { ...options }
5658
+ });
5659
+ }
5660
+ copy(src, dest, options = void 0) {
5661
+ return this._addOperation({
5662
+ type: ImageBuildOperationType.COPY,
5663
+ args: [src, dest],
5664
+ options: options == null ? {} : { ...options }
5665
+ });
5666
+ }
5667
+ env(key, value) {
5668
+ return this._addOperation({
5669
+ type: ImageBuildOperationType.ENV,
5670
+ args: [key, value],
5671
+ options: {}
5672
+ });
5673
+ }
5674
+ run(commands, options = void 0) {
5675
+ return this._addOperation({
5676
+ type: ImageBuildOperationType.RUN,
5677
+ args: Array.isArray(commands) ? [...commands] : [commands],
5678
+ options: options == null ? {} : { ...options }
5679
+ });
5680
+ }
5681
+ workdir(directory) {
5682
+ return this._addOperation({
5683
+ type: ImageBuildOperationType.WORKDIR,
5684
+ args: [directory],
5685
+ options: {}
5686
+ });
5786
5687
  }
5787
- async requestJson(method, path3) {
5788
- const response = await this.requestResponse(method, path3);
5789
- const text = await response.text();
5790
- const value = text ? JSON.parse(text) : null;
5791
- return Object.assign(value ?? {}, { traceId: response.traceId });
5688
+ /**
5689
+ * Build this image as a sandbox template and register it.
5690
+ *
5691
+ * Materializes the image in a build sandbox, snapshots the filesystem,
5692
+ * and registers the snapshot as a named sandbox template.
5693
+ */
5694
+ async build(options = {}) {
5695
+ const { createSandboxImage: createSandboxImage2 } = await Promise.resolve().then(() => (init_sandbox_image(), sandbox_image_exports));
5696
+ return createSandboxImage2(this, options);
5792
5697
  }
5793
- async *stream(method, path3, signal) {
5794
- if (signal?.aborted) return;
5795
- const request = await serializeRequest(method, path3, { headers: { Accept: "text/event-stream" } });
5796
- yield* nativeEventStream((emit) => this.client().stream(request, emit), void 0, signal);
5797
- }
5798
- };
5799
- }
5800
- });
5801
-
5802
- // src/cloud-client.ts
5803
- function secretFromMetadata(metadata) {
5804
- return {
5805
- id: metadata.id,
5806
- name: metadata.name,
5807
- createdAt: new Date(metadata.created_at_ms)
5808
- };
5809
- }
5810
- function applicationAllowsUnauthenticatedRequests(manifest) {
5811
- return Array.isArray(manifest.allow) && manifest.allow.includes(UNAUTHENTICATED_REQUESTS);
5812
- }
5813
- function publicEndpointIdFromManifest(manifest) {
5814
- for (const key of ["public_endpoint_id", "publicEndpointId"]) {
5815
- const value = manifest[key];
5816
- if (typeof value === "string" && value.length > 0) {
5817
- return value;
5818
- }
5819
- }
5820
- return void 0;
5821
- }
5822
- function generatePublicEndpointId() {
5823
- return `endpoint_${(0, import_nanoid.nanoid)()}`;
5824
- }
5825
- function createApplicationBuildForm(request, imageContexts) {
5826
- const contextsByPartName = /* @__PURE__ */ new Map();
5827
- for (const context of imageContexts) {
5828
- if (contextsByPartName.has(context.contextTarPartName)) {
5829
- throw new Error(
5830
- `duplicate image context part name '${context.contextTarPartName}'`
5831
- );
5832
- }
5833
- contextsByPartName.set(context.contextTarPartName, context);
5834
- }
5835
- const form = new FormData();
5836
- form.append(
5837
- "app_version",
5838
- new Blob([JSON.stringify(request)], { type: "application/json" }),
5839
- "app_version"
5840
- );
5841
- for (const image of request.images) {
5842
- const context = contextsByPartName.get(image.contextTarPartName);
5843
- if (!context) {
5844
- throw new Error(
5845
- `missing image context for part '${image.contextTarPartName}'`
5846
- );
5847
- }
5848
- form.append(
5849
- image.contextTarPartName,
5850
- new Blob([toBlobPart(context.contextTarGz)]),
5851
- `${image.contextTarPartName}.tar.gz`
5852
- );
5853
- }
5854
- for (const context of imageContexts) {
5855
- if (!request.images.some((image) => image.contextTarPartName === context.contextTarPartName)) {
5856
- throw new Error(
5857
- `unexpected image context for part '${context.contextTarPartName}'`
5858
- );
5859
- }
5860
- }
5861
- return form;
5862
- }
5863
- function trimTrailingSlashes2(value) {
5864
- return value.endsWith("/") ? value.slice(0, -1) : value;
5865
- }
5866
- function nextRequestPath(next) {
5867
- const schemeIndex = next.indexOf("://");
5868
- if (schemeIndex !== -1) {
5869
- const afterScheme = next.slice(schemeIndex + 3);
5870
- const slashIndex = afterScheme.indexOf("/");
5871
- return slashIndex === -1 ? "/" : afterScheme.slice(slashIndex);
5872
- }
5873
- return next.startsWith("/") ? next : `/${next}`;
5874
- }
5875
- function toBlobPart(data) {
5876
- if (typeof data === "string" || data instanceof Blob) {
5877
- return data;
5878
- }
5879
- if (data instanceof Uint8Array) {
5880
- return Uint8Array.from(data).buffer;
5881
- }
5882
- return data;
5883
- }
5884
- function toRequestBody(data) {
5885
- return toBlobPart(data);
5886
- }
5887
- async function parseJsonResponse(response) {
5888
- const text = await response.text();
5889
- if (!text) {
5890
- return void 0;
5891
- }
5892
- return JSON.parse(text);
5893
- }
5894
- var import_nanoid, CloudClient, UNAUTHENTICATED_REQUESTS;
5895
- var init_cloud_client = __esm({
5896
- "src/cloud-client.ts"() {
5897
- "use strict";
5898
- init_cjs_shims();
5899
- import_nanoid = require("nanoid");
5900
- init_defaults();
5901
- init_native_cloud();
5902
- init_models();
5903
- CloudClient = class _CloudClient {
5904
- transport;
5905
- organizationId;
5906
- projectId;
5907
- namespace;
5908
- constructor(options) {
5909
- this.organizationId = options?.organizationId;
5910
- this.projectId = options?.projectId;
5911
- this.namespace = options?.namespace ?? NAMESPACE;
5912
- this.transport = new NativeCloudTransport({
5913
- baseUrl: options?.apiUrl ?? API_URL,
5914
- apiKey: options?.apiKey ?? API_KEY,
5915
- organizationId: this.organizationId,
5916
- projectId: this.projectId,
5917
- maxRetries: options?.maxRetries ?? MAX_RETRIES,
5918
- retryBackoffMs: options?.retryBackoffMs ?? RETRY_BACKOFF_MS,
5919
- timeoutMs: DEFAULT_HTTP_TIMEOUT_MS,
5920
- userAgent: `tensorlake-typescript-sdk/${SDK_VERSION}`
5921
- });
5922
- }
5923
- static forCloud(options) {
5924
- return new _CloudClient(options);
5925
- }
5926
- close() {
5927
- this.transport.close();
5928
- }
5929
- async upsertApplication(manifest, codeZip, upgradeRunningRequests = false) {
5930
- const applicationManifest = { ...manifest };
5931
- let publicEndpointId = publicEndpointIdFromManifest(applicationManifest);
5932
- if (applicationAllowsUnauthenticatedRequests(applicationManifest) && publicEndpointId === void 0) {
5933
- const applicationName = applicationManifest.name;
5934
- if (typeof applicationName === "string" && applicationName.length > 0) {
5935
- publicEndpointId = await this.existingApplicationPublicEndpointId(applicationName);
5936
- }
5937
- publicEndpointId ??= generatePublicEndpointId();
5938
- }
5939
- if (publicEndpointId !== void 0) {
5940
- applicationManifest.public_endpoint_id = publicEndpointId;
5941
- delete applicationManifest.publicEndpointId;
5942
- }
5943
- const form = new FormData();
5944
- form.append(
5945
- "code",
5946
- new Blob([toBlobPart(codeZip)], { type: "application/zip" }),
5947
- "code.zip"
5948
- );
5949
- form.append("code_content_type", "application/zip");
5950
- form.append("application", JSON.stringify(applicationManifest));
5951
- form.append(
5952
- "upgrade_requests_to_latest_code",
5953
- String(upgradeRunningRequests)
5954
- );
5955
- await this.transport.requestResponse("POST", this.namespacePath("applications"), {
5956
- body: form
5957
- });
5958
- }
5959
- async deleteApplication(applicationName) {
5960
- await this.transport.requestResponse(
5961
- "DELETE",
5962
- this.namespacePath(`applications/${encodeURIComponent(applicationName)}`)
5963
- );
5964
- }
5965
- async deleteSandboxImage(imageName) {
5966
- await this.transport.requestResponse(
5967
- "DELETE",
5968
- this.namespacePath(`sandbox-images/${encodeURIComponent(imageName)}`)
5969
- );
5970
- }
5971
- /**
5972
- * Look up a registered sandbox image (template) by its registered name.
5973
- *
5974
- * Returns the template, or `null` when no image with that name exists.
5975
- * API-key clients may omit organization/project IDs and use the
5976
- * token-scoped Platform API route directly. Explicit scope remains
5977
- * available for PAT callers.
5978
- */
5979
- async findSandboxImageByName(imageName, options) {
5980
- const base = this.platformProjectPath(
5981
- "sandbox-templates",
5982
- options?.organizationId,
5983
- options?.projectId
5984
- );
5985
- const response = await this.transport.requestResponse(
5986
- "GET",
5987
- `${base}/by-name/${encodeURIComponent(imageName)}`,
5988
- { statusOnlyCodes: /* @__PURE__ */ new Set([404]) }
5989
- );
5990
- if (response.status === 404) {
5991
- return null;
5992
- }
5993
- const raw = await parseJsonResponse(response);
5994
- return fromSnakeKeys(raw);
5995
- }
5996
- /**
5997
- * List all registered sandbox images (templates), following pagination to
5998
- * the end. API-key clients may omit organization/project IDs and use the
5999
- * token-scoped Platform API route directly.
6000
- */
6001
- async listSandboxImages(options) {
6002
- const base = this.platformProjectPath(
6003
- "sandbox-templates",
6004
- options?.organizationId,
6005
- options?.projectId
6006
- );
6007
- let path3 = `${base}?pageSize=100`;
6008
- const templates = [];
6009
- while (path3 !== null) {
6010
- const page = await this.transport.requestJson(
6011
- "GET",
6012
- path3
6013
- );
6014
- for (const item of page.items ?? []) {
6015
- templates.push(fromSnakeKeys(item));
6016
- }
6017
- const next = page.pagination?.next;
6018
- path3 = next ? nextRequestPath(next) : null;
6019
- }
6020
- return templates;
6021
- }
6022
- async applications() {
6023
- const raw = await this.transport.requestJson(
6024
- "GET",
6025
- this.namespacePath("applications")
6026
- );
6027
- return (raw.applications ?? []).map(
6028
- (application) => fromSnakeKeys(application)
6029
- );
6030
- }
6031
- async applicationManifest(applicationName) {
6032
- const raw = await this.transport.requestJson(
6033
- "GET",
6034
- this.namespacePath(`applications/${encodeURIComponent(applicationName)}`)
6035
- );
6036
- return fromSnakeKeys(raw);
6037
- }
6038
- async runRequest(applicationName, inputs = []) {
6039
- const path3 = this.namespacePath(
6040
- `applications/${encodeURIComponent(applicationName)}`
6041
- );
6042
- const response = inputs.length === 0 ? await this.transport.requestResponse("POST", path3, {
6043
- body: new Uint8Array(),
6044
- headers: { Accept: "application/json" }
6045
- }) : inputs.length === 1 && inputs[0].name === "0" ? await this.transport.requestResponse("POST", path3, {
6046
- body: toRequestBody(inputs[0].data),
6047
- headers: {
6048
- Accept: "application/json",
6049
- "Content-Type": inputs[0].contentType
6050
- }
6051
- }) : await this.runMultipartRequest(path3, inputs);
6052
- const body = await parseJsonResponse(response);
6053
- const requestId = body?.request_id;
6054
- if (!requestId) {
6055
- throw new Error("missing request_id in run request response body");
6056
- }
6057
- return requestId;
6058
- }
6059
- async waitOnRequestCompletion(applicationName, requestId) {
6060
- const stream = this.transport.stream(
6061
- "GET",
6062
- this.namespacePath(
6063
- `applications/${encodeURIComponent(applicationName)}/requests/${encodeURIComponent(requestId)}/progress`
6064
- )
6065
- );
6066
- for await (const event of stream) {
6067
- if (Object.prototype.hasOwnProperty.call(event, "RequestFinished")) {
6068
- return;
6069
- }
6070
- }
6071
- throw new Error("progress stream ended before request completion");
6072
- }
6073
- async requestMetadata(applicationName, requestId) {
6074
- const raw = await this.transport.requestJson(
6075
- "GET",
6076
- this.namespacePath(
6077
- `applications/${encodeURIComponent(applicationName)}/requests/${encodeURIComponent(requestId)}`
6078
- )
6079
- );
6080
- return fromSnakeKeys(raw);
6081
- }
6082
- async requestOutput(applicationName, requestId) {
6083
- const response = await this.transport.requestResponse(
6084
- "GET",
6085
- this.namespacePath(
6086
- `applications/${encodeURIComponent(applicationName)}/requests/${encodeURIComponent(requestId)}/output`
6087
- )
6088
- );
6089
- const serializedValue = new Uint8Array(await response.arrayBuffer());
6090
- const contentType = response.headers.get("content-type") ?? "";
6091
- return {
6092
- serializedValue,
6093
- contentType
6094
- };
6095
- }
6096
- async listSecrets(options) {
6097
- const base = this.secretCollectionPath(
6098
- options?.organizationId,
6099
- options?.projectId
6100
- );
6101
- const raw = await this.transport.requestJson("GET", base);
6102
- const pageSize = Math.max(0, options?.pageSize ?? 100);
6103
- return Object.assign(
6104
- {
6105
- items: raw.slice(0, pageSize).map(secretFromMetadata),
6106
- pagination: { total: raw.length }
6107
- },
6108
- { traceId: raw.traceId }
6109
- );
6110
- }
6111
- async getSecret(secretId, options) {
6112
- const base = this.secretCollectionPath(
6113
- options?.organizationId,
6114
- options?.projectId
6115
- );
6116
- const raw = await this.transport.requestJson(
6117
- "GET",
6118
- `${base}/${encodeURIComponent(secretId)}`
6119
- );
6120
- return Object.assign(secretFromMetadata(raw), { traceId: raw.traceId });
6121
- }
6122
- async upsertSecrets(secrets, options) {
6123
- const base = this.secretCollectionPath(
6124
- options?.organizationId,
6125
- options?.projectId
6126
- );
6127
- if (Array.isArray(secrets)) {
6128
- const results = [];
6129
- for (const secret of secrets) {
6130
- results.push(await this.upsertSecret(base, secret));
6131
- }
6132
- return results;
6133
- }
6134
- return this.upsertSecret(base, secrets);
6135
- }
6136
- async deleteSecret(secretId, options) {
6137
- const base = this.secretCollectionPath(
6138
- options?.organizationId,
6139
- options?.projectId
6140
- );
6141
- await this.transport.requestResponse(
6142
- "DELETE",
6143
- `${base}/${encodeURIComponent(secretId)}`
6144
- );
6145
- }
6146
- async upsertSecret(base, secret) {
6147
- let response = await this.transport.requestResponse("POST", base, {
6148
- json: secret,
6149
- headers: { "Idempotency-Key": (0, import_nanoid.nanoid)() },
6150
- statusOnlyCodes: /* @__PURE__ */ new Set([409])
6151
- });
6152
- if (response.status === 409) {
6153
- const prefix = base.slice(0, -"/secrets".length);
6154
- const existing = await this.transport.requestJson(
6155
- "GET",
6156
- `${prefix}/secret-names/${encodeURIComponent(secret.name)}`
6157
- );
6158
- response = await this.transport.requestResponse(
6159
- "POST",
6160
- `${base}/${encodeURIComponent(existing.id)}/versions`,
6161
- {
6162
- json: { value: secret.value },
6163
- headers: { "Idempotency-Key": (0, import_nanoid.nanoid)() }
6164
- }
6165
- );
6166
- }
6167
- const metadata = await parseJsonResponse(response);
6168
- return Object.assign(secretFromMetadata(metadata), {
6169
- traceId: response.traceId
6170
- });
6171
- }
6172
- async startImageBuild(buildServicePath, request) {
6173
- const form = new FormData();
6174
- form.append("graph_name", request.applicationName);
6175
- form.append("graph_version", request.applicationVersion);
6176
- form.append("graph_function_name", request.functionName);
6177
- form.append("image_name", request.imageName);
6178
- form.append("image_id", request.imageId);
6179
- form.append(
6180
- "context",
6181
- new Blob([toBlobPart(request.buildContext)]),
6182
- "context.tar.gz"
6183
- );
6184
- const response = await this.transport.requestResponse(
6185
- "PUT",
6186
- `${trimTrailingSlashes2(buildServicePath)}/builds`,
6187
- { body: form }
6188
- );
6189
- const raw = await parseJsonResponse(response);
6190
- return fromSnakeKeys(raw);
6191
- }
6192
- async createApplicationBuild(buildServicePath, request, imageContexts) {
6193
- const form = createApplicationBuildForm(request, imageContexts);
6194
- const response = await this.transport.requestResponse(
6195
- "POST",
6196
- trimTrailingSlashes2(buildServicePath),
6197
- { body: form }
6198
- );
6199
- const raw = await parseJsonResponse(response);
6200
- return fromSnakeKeys(raw);
6201
- }
6202
- async applicationBuildInfo(buildServicePath, applicationBuildId) {
6203
- const raw = await this.transport.requestJson(
6204
- "GET",
6205
- `${trimTrailingSlashes2(buildServicePath)}/${encodeURIComponent(applicationBuildId)}`
6206
- );
6207
- return fromSnakeKeys(raw);
6208
- }
6209
- async cancelApplicationBuild(buildServicePath, applicationBuildId) {
6210
- const raw = await this.transport.requestJson(
6211
- "POST",
6212
- `${trimTrailingSlashes2(buildServicePath)}/${encodeURIComponent(applicationBuildId)}/cancel`
6213
- );
6214
- return fromSnakeKeys(raw);
6215
- }
6216
- async buildInfo(buildServicePath, buildId) {
6217
- const raw = await this.transport.requestJson(
6218
- "GET",
6219
- `${trimTrailingSlashes2(buildServicePath)}/builds/${encodeURIComponent(buildId)}`
6220
- );
6221
- return fromSnakeKeys(raw);
6222
- }
6223
- async cancelBuild(buildServicePath, buildId) {
6224
- await this.transport.requestResponse(
6225
- "POST",
6226
- `${trimTrailingSlashes2(buildServicePath)}/builds/${encodeURIComponent(buildId)}/cancel`
6227
- );
6228
- }
6229
- async *streamBuildLogs(buildServicePath, buildId, signal) {
6230
- const stream = this.transport.stream(
6231
- "GET",
6232
- `${trimTrailingSlashes2(buildServicePath)}/builds/${encodeURIComponent(buildId)}/logs`,
6233
- signal
6234
- );
6235
- for await (const event of stream) {
6236
- yield fromSnakeKeys(event);
6237
- }
6238
- }
6239
- async runMultipartRequest(path3, inputs) {
6240
- const form = new FormData();
6241
- for (const input of inputs) {
6242
- form.append(
6243
- input.name,
6244
- new Blob([toBlobPart(input.data)], { type: input.contentType }),
6245
- input.name
6246
- );
6247
- }
6248
- return this.transport.requestResponse("POST", path3, {
6249
- body: form,
6250
- headers: { Accept: "application/json" }
6251
- });
6252
- }
6253
- namespacePath(subpath) {
6254
- return `/v1/namespaces/${encodeURIComponent(this.namespace)}/${subpath.replace(/^\/+/, "")}`;
6255
- }
6256
- async existingApplicationPublicEndpointId(applicationName) {
6257
- const response = await this.transport.requestResponse(
6258
- "GET",
6259
- this.namespacePath(`applications/${encodeURIComponent(applicationName)}`),
6260
- { statusOnlyCodes: /* @__PURE__ */ new Set([404]) }
6261
- );
6262
- if (response.status === 404) {
6263
- return void 0;
6264
- }
6265
- const application = await parseJsonResponse(response);
6266
- return publicEndpointIdFromManifest(application);
6267
- }
6268
- platformProjectPath(resource, organizationId, projectId) {
6269
- const resolvedOrganizationId = organizationId ?? this.organizationId;
6270
- const resolvedProjectId = projectId ?? this.projectId;
6271
- if (!resolvedOrganizationId && !resolvedProjectId) {
6272
- return `/platform/v1/${resource}`;
6273
- }
6274
- if (!resolvedOrganizationId || !resolvedProjectId) {
6275
- throw new Error(
6276
- "organizationId and projectId must be provided together"
6277
- );
6278
- }
6279
- return `/platform/v1/organizations/${encodeURIComponent(resolvedOrganizationId)}/projects/${encodeURIComponent(resolvedProjectId)}/${resource}`;
6280
- }
6281
- secretCollectionPath(organizationId, projectId) {
6282
- const resolvedOrganizationId = organizationId ?? this.organizationId;
6283
- const resolvedProjectId = projectId ?? this.projectId;
6284
- if (!resolvedOrganizationId && !resolvedProjectId) {
6285
- return this.namespacePath("secrets");
6286
- }
6287
- if (!resolvedOrganizationId || !resolvedProjectId) {
6288
- throw new Error("organizationId and projectId must be provided together");
6289
- }
6290
- return this.namespacePath("secrets");
6291
- }
6292
- };
6293
- UNAUTHENTICATED_REQUESTS = "unauthenticated_requests";
6294
- }
6295
- });
6296
-
6297
- // src/image.ts
6298
- function cloneOperation(op) {
6299
- return {
6300
- type: op.type,
6301
- args: [...op.args],
6302
- options: { ...op.options }
6303
- };
6304
- }
6305
- function renderOptions(options) {
6306
- const entries = Object.entries(options);
6307
- if (entries.length === 0) {
6308
- return "";
6309
- }
6310
- return ` ${entries.map(([key, value]) => `--${key}=${value}`).join(" ")}`;
6311
- }
6312
- function renderBuildOp(op) {
6313
- const options = renderOptions(op.options);
6314
- if (op.type === ImageBuildOperationType.ENV) {
6315
- return `ENV${options} ${op.args[0]}=${JSON.stringify(op.args[1])}`;
6316
- }
6317
- return `${op.type}${options} ${op.args.join(" ")}`;
6318
- }
6319
- function dockerfileContent(image) {
6320
- const lines = image.baseImage == null ? [] : [`FROM ${image.baseImage}`];
6321
- lines.push(...image.buildOperations.map((op) => renderBuildOp(op)));
6322
- return lines.join("\n");
6323
- }
6324
- var import_node_crypto, DEFAULT_IMAGE_NAME, ImageBuildOperationType, Image;
6325
- var init_image = __esm({
6326
- "src/image.ts"() {
6327
- "use strict";
6328
- init_cjs_shims();
6329
- import_node_crypto = require("crypto");
6330
- DEFAULT_IMAGE_NAME = "default";
6331
- ImageBuildOperationType = {
6332
- ADD: "ADD",
6333
- COPY: "COPY",
6334
- ENV: "ENV",
6335
- RUN: "RUN",
6336
- WORKDIR: "WORKDIR"
6337
- };
6338
- Image = class {
6339
- _id;
6340
- _name;
6341
- _tag;
6342
- _baseImage;
6343
- _buildOperations;
6344
- constructor(nameOrOptions = {}, tag = "latest", baseImage = null) {
6345
- this._id = (0, import_node_crypto.randomUUID)();
6346
- this._buildOperations = [];
6347
- if (typeof nameOrOptions === "string") {
6348
- this._name = nameOrOptions;
6349
- this._tag = tag;
6350
- this._baseImage = baseImage;
6351
- return;
6352
- }
6353
- this._name = nameOrOptions.name ?? DEFAULT_IMAGE_NAME;
6354
- this._tag = nameOrOptions.tag ?? "latest";
6355
- this._baseImage = nameOrOptions.baseImage ?? null;
6356
- }
6357
- get name() {
6358
- return this._name;
6359
- }
6360
- get tag() {
6361
- return this._tag;
6362
- }
6363
- get baseImage() {
6364
- return this._baseImage;
6365
- }
6366
- get buildOperations() {
6367
- return this._buildOperations.map(cloneOperation);
6368
- }
6369
- add(src, dest, options = void 0) {
6370
- return this._addOperation({
6371
- type: ImageBuildOperationType.ADD,
6372
- args: [src, dest],
6373
- options: options == null ? {} : { ...options }
6374
- });
6375
- }
6376
- copy(src, dest, options = void 0) {
6377
- return this._addOperation({
6378
- type: ImageBuildOperationType.COPY,
6379
- args: [src, dest],
6380
- options: options == null ? {} : { ...options }
6381
- });
6382
- }
6383
- env(key, value) {
6384
- return this._addOperation({
6385
- type: ImageBuildOperationType.ENV,
6386
- args: [key, value],
6387
- options: {}
6388
- });
6389
- }
6390
- run(commands, options = void 0) {
6391
- return this._addOperation({
6392
- type: ImageBuildOperationType.RUN,
6393
- args: Array.isArray(commands) ? [...commands] : [commands],
6394
- options: options == null ? {} : { ...options }
6395
- });
6396
- }
6397
- workdir(directory) {
6398
- return this._addOperation({
6399
- type: ImageBuildOperationType.WORKDIR,
6400
- args: [directory],
6401
- options: {}
6402
- });
6403
- }
6404
- /**
6405
- * Build this image as a sandbox template and register it.
6406
- *
6407
- * Materializes the image in a build sandbox, snapshots the filesystem,
6408
- * and registers the snapshot as a named sandbox template.
6409
- */
6410
- async build(options = {}) {
6411
- const { createSandboxImage: createSandboxImage2 } = await Promise.resolve().then(() => (init_sandbox_image(), sandbox_image_exports));
6412
- return createSandboxImage2(this, options);
6413
- }
6414
- _addOperation(op) {
6415
- this._buildOperations.push(op);
6416
- return this;
5698
+ _addOperation(op) {
5699
+ this._buildOperations.push(op);
5700
+ return this;
6417
5701
  }
6418
5702
  };
6419
5703
  }
@@ -6486,9 +5770,9 @@ var require_libc = __commonJS({
6486
5770
  // src/native-binding.ts
6487
5771
  function packageRoot() {
6488
5772
  const parent = (0, import_node_url.fileURLToPath)(new URL("../", importMetaUrl));
6489
- if (fs.existsSync(import_node_path2.default.join(parent, "package.json"))) return parent;
5773
+ if (fs.existsSync(import_node_path.default.join(parent, "package.json"))) return parent;
6490
5774
  const grandparent = (0, import_node_url.fileURLToPath)(new URL("../../", importMetaUrl));
6491
- if (fs.existsSync(import_node_path2.default.join(grandparent, "package.json"))) return grandparent;
5775
+ if (fs.existsSync(import_node_path.default.join(grandparent, "package.json"))) return grandparent;
6492
5776
  return parent;
6493
5777
  }
6494
5778
  function linuxLibcFamily(options) {
@@ -6511,7 +5795,7 @@ function nativeTargetId(platform = process.platform, arch = process.arch, option
6511
5795
  function nativeBindingPath(options = {}) {
6512
5796
  const platform = options.platform ?? process.platform;
6513
5797
  const arch = options.arch ?? process.arch;
6514
- return import_node_path2.default.join(
5798
+ return import_node_path.default.join(
6515
5799
  options.packageRoot ?? packageRoot(),
6516
5800
  "dist",
6517
5801
  "native",
@@ -6544,7 +5828,7 @@ function loadNative() {
6544
5828
  }
6545
5829
  return require2(packageEntry);
6546
5830
  }
6547
- var import_libc, fs, import_node_module2, import_node_path2, import_node_url, NATIVE_PACKAGES, cachedLinuxLibc;
5831
+ var import_libc, fs, import_node_module2, import_node_path, import_node_url, NATIVE_PACKAGES, cachedLinuxLibc;
6548
5832
  var init_native_binding = __esm({
6549
5833
  "src/native-binding.ts"() {
6550
5834
  "use strict";
@@ -6552,7 +5836,7 @@ var init_native_binding = __esm({
6552
5836
  import_libc = __toESM(require_libc(), 1);
6553
5837
  fs = __toESM(require("fs"), 1);
6554
5838
  import_node_module2 = require("module");
6555
- import_node_path2 = __toESM(require("path"), 1);
5839
+ import_node_path = __toESM(require("path"), 1);
6556
5840
  import_node_url = require("url");
6557
5841
  NATIVE_PACKAGES = {
6558
5842
  "darwin-arm64": "@tensorlakeai/native-darwin-arm64",
@@ -6565,16 +5849,471 @@ var init_native_binding = __esm({
6565
5849
  }
6566
5850
  });
6567
5851
 
5852
+ // src/image-builds.ts
5853
+ function camelize(key) {
5854
+ return key.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
5855
+ }
5856
+ function toImageBuildError(error) {
5857
+ if (!(error instanceof Error)) return error;
5858
+ let payload;
5859
+ try {
5860
+ payload = JSON.parse(error.message);
5861
+ } catch {
5862
+ return error;
5863
+ }
5864
+ if (!payload || payload.category !== "image_build") return error;
5865
+ const details = {};
5866
+ for (const [key, value] of Object.entries(payload)) {
5867
+ if (key === "category") continue;
5868
+ details[camelize(key)] = value;
5869
+ }
5870
+ const typed = details;
5871
+ switch (typed.code) {
5872
+ case "not_found":
5873
+ return new ImageBuildNotFoundError(typed);
5874
+ case "image_build_in_progress":
5875
+ return new ImageBuildInProgressError(typed);
5876
+ case "failed":
5877
+ return new ImageBuildFailedError(typed);
5878
+ case "cancelled":
5879
+ return new ImageBuildCancelledError(typed);
5880
+ case "timeout":
5881
+ return new ImageBuildTimeoutError(typed);
5882
+ case "interrupted":
5883
+ return new ImageBuildInterruptedError(typed);
5884
+ default:
5885
+ return new ImageBuildError(typed);
5886
+ }
5887
+ }
5888
+ function camelizeRecord(value) {
5889
+ const out = {};
5890
+ for (const [key, inner] of Object.entries(value)) out[camelize(key)] = inner;
5891
+ return out;
5892
+ }
5893
+ function parseBuildInfo(raw) {
5894
+ const camel = camelizeRecord(raw);
5895
+ const result = raw.result && typeof raw.result === "object" ? camelizeRecord(raw.result) : null;
5896
+ return {
5897
+ buildId: String(raw.build_id ?? ""),
5898
+ clientRequestId: camel.clientRequestId ?? null,
5899
+ kind: String(raw.kind ?? ""),
5900
+ name: raw.name ?? null,
5901
+ status: String(raw.status ?? ""),
5902
+ phase: raw.phase ?? null,
5903
+ attemptNo: Number(raw.attempt_no ?? 0),
5904
+ builderSandboxId: camel.builderSandboxId ?? null,
5905
+ error: raw.error ?? null,
5906
+ result,
5907
+ operationDeadlineMs: camel.operationDeadlineMs ?? null,
5908
+ createdAtMs: camel.createdAtMs ?? null,
5909
+ updatedAtMs: camel.updatedAtMs ?? null,
5910
+ raw
5911
+ };
5912
+ }
5913
+ function parseResult(json) {
5914
+ return camelizeRecord(JSON.parse(json));
5915
+ }
5916
+ function credentialsFromEnv() {
5917
+ const context = __imageBuildInternals.buildContextFromEnv();
5918
+ if (!context.apiKey) {
5919
+ if (context.personalAccessToken) {
5920
+ throw new ImageBuildError({
5921
+ code: "auth",
5922
+ message: "Sandbox image SDKs require TENSORLAKE_API_KEY. Personal access tokens are CLI-only."
5923
+ });
5924
+ }
5925
+ throw new ImageBuildError({ code: "auth", message: "Missing TENSORLAKE_API_KEY credentials." });
5926
+ }
5927
+ return { apiUrl: context.apiUrl, bearerToken: context.apiKey, namespace: context.namespace };
5928
+ }
5929
+ function clientOptions(credentials) {
5930
+ return {
5931
+ apiUrl: credentials.apiUrl,
5932
+ bearerToken: credentials.bearerToken,
5933
+ namespace: credentials.namespace,
5934
+ useScopeHeaders: false
5935
+ };
5936
+ }
5937
+ function rejectLegacyReference(buildId) {
5938
+ if (typeof buildId !== "string" || buildId.trim().length === 0) {
5939
+ throw new ImageBuildError({ code: "usage", message: "buildId must be a non-empty string" });
5940
+ }
5941
+ if (buildId.startsWith("sandbox_template_")) {
5942
+ throw new ImageBuildError({
5943
+ code: "usage",
5944
+ message: `'${buildId}' is a legacy sandbox template id; legacy images have no build operation to get, wait for or cancel`,
5945
+ buildId
5946
+ });
5947
+ }
5948
+ if (buildId.startsWith("image_")) {
5949
+ throw new ImageBuildError({
5950
+ code: "usage",
5951
+ message: `'${buildId}' is an image id, not a build id; look the image up with findSandboxImageByName or use the build id admission returned`,
5952
+ buildId
5953
+ });
5954
+ }
5955
+ }
5956
+ async function __runWithInterrupt(signal, run) {
5957
+ const binding = __imageBuildInternals.loadNativeBinding();
5958
+ if (signal === void 0) {
5959
+ try {
5960
+ return await run(void 0);
5961
+ } catch (error) {
5962
+ throw toImageBuildError(error);
5963
+ }
5964
+ }
5965
+ const interruptId = binding.createImageBuildInterrupt();
5966
+ const onAbort = () => binding.triggerImageBuildInterrupt(interruptId);
5967
+ if (signal.aborted) onAbort();
5968
+ else signal.addEventListener("abort", onAbort, { once: true });
5969
+ try {
5970
+ return await run(interruptId);
5971
+ } catch (error) {
5972
+ throw toImageBuildError(error);
5973
+ } finally {
5974
+ signal.removeEventListener("abort", onAbort);
5975
+ binding.releaseImageBuildInterrupt(interruptId);
5976
+ }
5977
+ }
5978
+ function makeEmitter(options) {
5979
+ const emit = options.emit ?? (options.verbose ? __imageBuildInternals.stderrEmit : __imageBuildInternals.noopEmit);
5980
+ let emitError;
5981
+ const forward = (event) => {
5982
+ try {
5983
+ emit(__imageBuildInternals.eventToEmitDict(event));
5984
+ } catch (error) {
5985
+ emitError ??= error;
5986
+ }
5987
+ };
5988
+ return {
5989
+ forward,
5990
+ emit,
5991
+ rethrow() {
5992
+ if (emitError != null) throw emitError;
5993
+ }
5994
+ };
5995
+ }
5996
+ function decodeStarted(json, credentials) {
5997
+ const payload = JSON.parse(json);
5998
+ return new ImageBuildHandle(parseBuildInfo(payload.build ?? {}), credentials, payload.client_request_id);
5999
+ }
6000
+ async function startImageBuild(source, options = {}, deps = {}) {
6001
+ const emitter = makeEmitter({ emit: deps.emit ?? options.emit, verbose: options.verbose });
6002
+ let dockerfilePath;
6003
+ let dockerfileText;
6004
+ let contextDir;
6005
+ let name;
6006
+ let tempContextDir;
6007
+ if (source instanceof Image) {
6008
+ if (!source.baseImage) throw new Error("Image must have a baseImage to build");
6009
+ if (options.contextDir != null) {
6010
+ contextDir = import_node_path2.default.resolve(options.contextDir);
6011
+ } else if (__imageBuildInternals.imageRequiresContext(source)) {
6012
+ throw new Error(
6013
+ "This image reads files from the host (via COPY/ADD or a RUN bind mount), so it needs a build context. Pass `contextDir` pointing at the directory that contains those sources; they resolve relative to it, like `docker build <contextDir>`."
6014
+ );
6015
+ } else {
6016
+ tempContextDir = (0, import_node_fs.mkdtempSync)(import_node_path2.default.join(import_node_os.default.tmpdir(), "tl-build-context-"));
6017
+ contextDir = tempContextDir;
6018
+ }
6019
+ dockerfilePath = import_node_path2.default.join(contextDir, "Dockerfile");
6020
+ let text = dockerfileContent(source);
6021
+ if (!text.endsWith("\n")) text += "\n";
6022
+ dockerfileText = text;
6023
+ name = options.registeredName ?? source.name;
6024
+ } else if (typeof source === "string") {
6025
+ const resolved = import_node_path2.default.resolve(source);
6026
+ if (!(0, import_node_fs.existsSync)(resolved)) throw new Error(`Dockerfile not found: ${source}`);
6027
+ dockerfilePath = resolved;
6028
+ name = options.registeredName ?? __imageBuildInternals.defaultRegisteredName(resolved);
6029
+ } else {
6030
+ throw new TypeError(`source must be an Image or a Dockerfile path, got ${typeof source}`);
6031
+ }
6032
+ try {
6033
+ const credentials = credentialsFromEnv();
6034
+ const binding = __imageBuildInternals.loadNativeBinding();
6035
+ emitter.emit({ type: "status", message: `Building image '${name}'...` });
6036
+ const json = await __runWithInterrupt(
6037
+ options.signal,
6038
+ (interruptId) => binding.startImageBuild(
6039
+ {
6040
+ apiUrl: credentials.apiUrl,
6041
+ bearerToken: credentials.bearerToken,
6042
+ dockerfilePath,
6043
+ registeredName: name,
6044
+ diskMb: options.diskMb,
6045
+ cpus: options.cpus,
6046
+ memoryMb: options.memoryMb,
6047
+ isPublic: options.isPublic ?? false,
6048
+ namespace: credentials.namespace,
6049
+ useScopeHeaders: false,
6050
+ dockerConfigJson: options.dockerConfigJson,
6051
+ dockerfileText,
6052
+ contextDir,
6053
+ cas: true,
6054
+ buildArgs: options.buildArgs
6055
+ },
6056
+ emitter.forward,
6057
+ interruptId
6058
+ )
6059
+ );
6060
+ emitter.rethrow();
6061
+ return decodeStarted(json, credentials);
6062
+ } finally {
6063
+ if (tempContextDir != null) (0, import_node_fs.rmSync)(tempContextDir, { recursive: true, force: true });
6064
+ }
6065
+ }
6066
+ async function startImageImport(imageReference, options = {}, deps = {}) {
6067
+ if (typeof imageReference !== "string" || imageReference.trim().length === 0) {
6068
+ throw new Error("image reference to import must not be empty");
6069
+ }
6070
+ const reference = imageReference.trim();
6071
+ const name = options.registeredName ?? __imageBuildInternals.defaultRegisteredNameFromImage(reference);
6072
+ const emitter = makeEmitter({ emit: deps.emit ?? options.emit, verbose: options.verbose });
6073
+ const credentials = credentialsFromEnv();
6074
+ const binding = __imageBuildInternals.loadNativeBinding();
6075
+ emitter.emit({ type: "status", message: `Importing image '${reference}' as '${name}'...` });
6076
+ const json = await __runWithInterrupt(
6077
+ options.signal,
6078
+ (interruptId) => binding.startImageImport(
6079
+ {
6080
+ apiUrl: credentials.apiUrl,
6081
+ bearerToken: credentials.bearerToken,
6082
+ imageReference: reference,
6083
+ registeredName: name,
6084
+ diskMb: options.diskMb,
6085
+ cpus: options.cpus,
6086
+ memoryMb: options.memoryMb,
6087
+ isPublic: options.isPublic ?? false,
6088
+ namespace: credentials.namespace,
6089
+ useScopeHeaders: false,
6090
+ dockerConfigJson: options.dockerConfigJson,
6091
+ cas: true
6092
+ },
6093
+ emitter.forward,
6094
+ interruptId
6095
+ )
6096
+ );
6097
+ emitter.rethrow();
6098
+ return decodeStarted(json, credentials);
6099
+ }
6100
+ async function getImageBuild(buildId) {
6101
+ rejectLegacyReference(buildId);
6102
+ const credentials = credentialsFromEnv();
6103
+ const binding = __imageBuildInternals.loadNativeBinding();
6104
+ let json;
6105
+ try {
6106
+ json = await binding.getImageBuild(clientOptions(credentials), buildId);
6107
+ } catch (error) {
6108
+ throw toImageBuildError(error);
6109
+ }
6110
+ return new ImageBuildHandle(parseBuildInfo(JSON.parse(json)), credentials);
6111
+ }
6112
+ async function waitImageBuild(buildId, options = {}) {
6113
+ rejectLegacyReference(buildId);
6114
+ const credentials = credentialsFromEnv();
6115
+ const binding = __imageBuildInternals.loadNativeBinding();
6116
+ const emitter = makeEmitter(options);
6117
+ const json = await __runWithInterrupt(
6118
+ options.signal,
6119
+ (interruptId) => binding.waitImageBuild(
6120
+ clientOptions(credentials),
6121
+ buildId,
6122
+ { timeoutMs: options.timeoutMs, followLogs: options.followLogs ?? false },
6123
+ emitter.forward,
6124
+ interruptId
6125
+ )
6126
+ );
6127
+ emitter.rethrow();
6128
+ return parseResult(json);
6129
+ }
6130
+ async function cancelImageBuildWith(credentials, selector) {
6131
+ const binding = __imageBuildInternals.loadNativeBinding();
6132
+ let json;
6133
+ try {
6134
+ json = await binding.cancelImageBuild(clientOptions(credentials), {
6135
+ buildId: selector.buildId,
6136
+ clientRequestId: selector.clientRequestId
6137
+ });
6138
+ } catch (error) {
6139
+ throw toImageBuildError(error);
6140
+ }
6141
+ const payload = JSON.parse(json);
6142
+ const outcome = String(payload.outcome ?? "");
6143
+ return {
6144
+ outcome,
6145
+ build: payload.build ? parseBuildInfo(payload.build) : null,
6146
+ confirmed: ["cancelled", "already_cancelled", "already_failed"].includes(outcome)
6147
+ };
6148
+ }
6149
+ async function cancelImageBuild(selector) {
6150
+ const hasBuild = selector.buildId != null;
6151
+ const hasRequest = selector.clientRequestId != null;
6152
+ if (hasBuild === hasRequest) {
6153
+ throw new ImageBuildError({
6154
+ code: "usage",
6155
+ message: "pass exactly one of buildId or clientRequestId"
6156
+ });
6157
+ }
6158
+ if (selector.buildId != null) rejectLegacyReference(selector.buildId);
6159
+ return cancelImageBuildWith(credentialsFromEnv(), selector);
6160
+ }
6161
+ var import_node_path2, import_node_os, import_node_fs, ImageBuildError, ImageBuildNotFoundError, ImageBuildInProgressError, ImageBuildFailedError, ImageBuildCancelledError, ImageBuildTimeoutError, ImageBuildInterruptedError, ImageBuildHandle;
6162
+ var init_image_builds = __esm({
6163
+ "src/image-builds.ts"() {
6164
+ "use strict";
6165
+ init_cjs_shims();
6166
+ import_node_path2 = __toESM(require("path"), 1);
6167
+ import_node_os = __toESM(require("os"), 1);
6168
+ import_node_fs = require("fs");
6169
+ init_image();
6170
+ init_sandbox_image();
6171
+ ImageBuildError = class extends Error {
6172
+ code;
6173
+ buildId;
6174
+ clientRequestId;
6175
+ details;
6176
+ constructor(details) {
6177
+ super(details.message);
6178
+ this.name = "ImageBuildError";
6179
+ this.code = details.code;
6180
+ this.details = details;
6181
+ if (details.buildId) this.buildId = details.buildId;
6182
+ if (details.clientRequestId) this.clientRequestId = details.clientRequestId;
6183
+ }
6184
+ };
6185
+ ImageBuildNotFoundError = class extends ImageBuildError {
6186
+ constructor(details) {
6187
+ super(details);
6188
+ this.name = "ImageBuildNotFoundError";
6189
+ }
6190
+ };
6191
+ ImageBuildInProgressError = class extends ImageBuildError {
6192
+ imageName;
6193
+ activeBuildId;
6194
+ activeKind;
6195
+ activeStatus;
6196
+ constructor(details) {
6197
+ super(details);
6198
+ this.name = "ImageBuildInProgressError";
6199
+ if (details.imageName) this.imageName = details.imageName;
6200
+ if (details.activeBuildId) this.activeBuildId = details.activeBuildId;
6201
+ if (details.activeKind) this.activeKind = details.activeKind;
6202
+ if (details.activeStatus) this.activeStatus = details.activeStatus;
6203
+ }
6204
+ };
6205
+ ImageBuildFailedError = class extends ImageBuildError {
6206
+ error;
6207
+ info;
6208
+ constructor(details) {
6209
+ super(details);
6210
+ this.name = "ImageBuildFailedError";
6211
+ this.error = details.error ?? "no error recorded";
6212
+ if (details.info) this.info = parseBuildInfo(details.info);
6213
+ }
6214
+ };
6215
+ ImageBuildCancelledError = class extends ImageBuildError {
6216
+ info;
6217
+ constructor(details) {
6218
+ super(details);
6219
+ this.name = "ImageBuildCancelledError";
6220
+ if (details.info) this.info = parseBuildInfo(details.info);
6221
+ }
6222
+ };
6223
+ ImageBuildTimeoutError = class extends ImageBuildError {
6224
+ constructor(details) {
6225
+ super(details);
6226
+ this.name = "ImageBuildTimeoutError";
6227
+ }
6228
+ };
6229
+ ImageBuildInterruptedError = class extends ImageBuildError {
6230
+ outcome;
6231
+ confirmed;
6232
+ result;
6233
+ constructor(details) {
6234
+ super(details);
6235
+ this.name = "ImageBuildInterruptedError";
6236
+ this.outcome = details.outcome ?? "unconfirmed";
6237
+ this.confirmed = details.confirmed === true;
6238
+ if (details.result) this.result = details.result;
6239
+ }
6240
+ };
6241
+ ImageBuildHandle = class {
6242
+ buildId;
6243
+ /**
6244
+ * The admission's request id, when this handle started the build. Never
6245
+ * needed to cancel; a recovery aid for a lost admission answer.
6246
+ */
6247
+ clientRequestId;
6248
+ #info;
6249
+ #credentials;
6250
+ /** @internal */
6251
+ constructor(info, credentials, clientRequestId) {
6252
+ this.buildId = info.buildId;
6253
+ this.clientRequestId = clientRequestId ?? info.clientRequestId ?? null;
6254
+ this.#info = info;
6255
+ this.#credentials = credentials;
6256
+ }
6257
+ /** The last observed state. */
6258
+ get info() {
6259
+ return this.#info;
6260
+ }
6261
+ /** Re-read the current state. */
6262
+ async refresh() {
6263
+ const binding = __imageBuildInternals.loadNativeBinding();
6264
+ let json;
6265
+ try {
6266
+ json = await binding.getImageBuild(clientOptions(this.#credentials), this.buildId);
6267
+ } catch (error) {
6268
+ throw toImageBuildError(error);
6269
+ }
6270
+ this.#info = parseBuildInfo(JSON.parse(json));
6271
+ return this.#info;
6272
+ }
6273
+ /**
6274
+ * Wait for publication and return this build's immutable result. See
6275
+ * {@link ImageBuildWaitOptions} for the timeout/abort distinction.
6276
+ */
6277
+ async wait(options = {}) {
6278
+ const binding = __imageBuildInternals.loadNativeBinding();
6279
+ const emitter = makeEmitter(options);
6280
+ const json = await __runWithInterrupt(
6281
+ options.signal,
6282
+ (interruptId) => binding.waitImageBuild(
6283
+ clientOptions(this.#credentials),
6284
+ this.buildId,
6285
+ { timeoutMs: options.timeoutMs, followLogs: options.followLogs ?? false },
6286
+ emitter.forward,
6287
+ interruptId
6288
+ )
6289
+ );
6290
+ emitter.rethrow();
6291
+ const result = parseResult(json);
6292
+ this.#info = { ...this.#info, status: "succeeded", result };
6293
+ return result;
6294
+ }
6295
+ /** Cancel this build by its id (the sufficient selector). */
6296
+ async cancel() {
6297
+ return cancelImageBuildWith(this.#credentials, { buildId: this.buildId });
6298
+ }
6299
+ };
6300
+ }
6301
+ });
6302
+
6568
6303
  // src/sandbox-image.ts
6569
6304
  var sandbox_image_exports = {};
6570
6305
  __export(sandbox_image_exports, {
6306
+ __imageBuildInternals: () => __imageBuildInternals,
6571
6307
  __setNativeBindingForTest: () => __setNativeBindingForTest,
6572
6308
  buildContextFromEnv: () => buildContextFromEnv,
6573
6309
  createSandboxImage: () => createSandboxImage,
6574
6310
  deleteSandboxImage: () => deleteSandboxImage,
6311
+ deleteSandboxImageWithCredentials: () => deleteSandboxImageWithCredentials,
6575
6312
  findSandboxImageByName: () => findSandboxImageByName,
6313
+ findSandboxImageByNameWithCredentials: () => findSandboxImageByNameWithCredentials,
6576
6314
  importSandboxImage: () => importSandboxImage,
6577
6315
  listSandboxImages: () => listSandboxImages,
6316
+ listSandboxImagesWithCredentials: () => listSandboxImagesWithCredentials,
6578
6317
  runCreateSandboxImageCli: () => runCreateSandboxImageCli,
6579
6318
  runImportSandboxImageCli: () => runImportSandboxImageCli
6580
6319
  });
@@ -6648,6 +6387,15 @@ function eventToEmitDict(event) {
6648
6387
  message: event.message
6649
6388
  };
6650
6389
  if (event.stream != null) out.stream = event.stream;
6390
+ if (event.buildId != null) out.build_id = event.buildId;
6391
+ if (event.clientRequestId !== void 0 && event.eventType !== "status") {
6392
+ out.client_request_id = event.clientRequestId;
6393
+ }
6394
+ if (event.status != null) out.status = event.status;
6395
+ if (event.phase !== void 0 && event.eventType.startsWith("build_")) out.phase = event.phase;
6396
+ if (event.attempt != null) out.attempt = event.attempt;
6397
+ if (event.outcome != null) out.outcome = event.outcome;
6398
+ if (event.confirmed != null) out.confirmed = event.confirmed;
6651
6399
  return out;
6652
6400
  }
6653
6401
  function runOpMountsContext(op) {
@@ -6749,38 +6497,47 @@ async function createSandboxImage(source, options = {}, deps = {}) {
6749
6497
  deps._allowPat ? "Missing TENSORLAKE_API_KEY or TENSORLAKE_PAT credentials." : "Missing TENSORLAKE_API_KEY credentials."
6750
6498
  );
6751
6499
  }
6500
+ if (options.signal !== void 0 && !options.cas) {
6501
+ throw new Error(
6502
+ "signal is only supported for CAS image builds (cas: true); a legacy build has no Image Service build operation to cancel"
6503
+ );
6504
+ }
6752
6505
  emit({ type: "status", message: `Building image '${effectiveName}'...` });
6753
6506
  const binding = loadNativeBinding();
6754
6507
  let emitError;
6755
- const resultJson = await binding.buildSandboxImage(
6756
- {
6757
- apiUrl: context.apiUrl,
6758
- bearerToken,
6759
- dockerfilePath,
6760
- registeredName: effectiveName,
6761
- diskMb: options.diskMb,
6762
- builderDiskMb: options.builderDiskMb,
6763
- cpus: options.cpus,
6764
- memoryMb: options.memoryMb,
6765
- isPublic: options.isPublic ?? false,
6766
- organizationId: usePat ? context.organizationId : void 0,
6767
- projectId: usePat ? context.projectId : void 0,
6768
- namespace: context.namespace,
6769
- useScopeHeaders: usePat,
6770
- userAgent: void 0,
6771
- dockerCompat: options.dockerCompat ?? false,
6772
- dockerConfigJson: options.dockerConfigJson,
6773
- dockerfileText,
6774
- contextDir: nativeContextDir
6775
- },
6776
- (event) => {
6777
- try {
6778
- emit(eventToEmitDict(event));
6779
- } catch (error) {
6780
- emitError ??= error;
6781
- }
6508
+ const nativeOptions = {
6509
+ apiUrl: context.apiUrl,
6510
+ bearerToken,
6511
+ dockerfilePath,
6512
+ registeredName: effectiveName,
6513
+ diskMb: options.diskMb,
6514
+ builderDiskMb: options.builderDiskMb,
6515
+ cpus: options.cpus,
6516
+ memoryMb: options.memoryMb,
6517
+ isPublic: options.isPublic ?? false,
6518
+ organizationId: usePat ? context.organizationId : void 0,
6519
+ projectId: usePat ? context.projectId : void 0,
6520
+ namespace: context.namespace,
6521
+ useScopeHeaders: usePat,
6522
+ userAgent: void 0,
6523
+ dockerCompat: options.dockerCompat ?? false,
6524
+ dockerConfigJson: options.dockerConfigJson,
6525
+ dockerfileText,
6526
+ contextDir: nativeContextDir,
6527
+ cas: options.cas ?? false,
6528
+ buildArgs: options.buildArgs
6529
+ };
6530
+ const forward = (event) => {
6531
+ try {
6532
+ emit(eventToEmitDict(event));
6533
+ } catch (error) {
6534
+ emitError ??= error;
6782
6535
  }
6783
- );
6536
+ };
6537
+ const resultJson = options.cas ? await __runWithInterrupt(
6538
+ options.signal,
6539
+ (interruptId) => binding.buildSandboxImage(nativeOptions, forward, interruptId)
6540
+ ) : await binding.buildSandboxImage(nativeOptions, forward);
6784
6541
  if (emitError != null) {
6785
6542
  throw emitError;
6786
6543
  }
@@ -6826,39 +6583,47 @@ async function importSandboxImage(imageReference, options = {}, deps = {}) {
6826
6583
  deps._allowPat ? "Missing TENSORLAKE_API_KEY or TENSORLAKE_PAT credentials." : "Missing TENSORLAKE_API_KEY credentials."
6827
6584
  );
6828
6585
  }
6586
+ if (options.signal !== void 0 && !options.cas) {
6587
+ throw new Error(
6588
+ "signal is only supported for CAS image imports (cas: true); a legacy import has no Image Service build operation to cancel"
6589
+ );
6590
+ }
6829
6591
  emit({
6830
6592
  type: "status",
6831
6593
  message: `Importing image '${reference}' as '${effectiveName}'...`
6832
6594
  });
6833
6595
  const binding = loadNativeBinding();
6834
6596
  let emitError;
6835
- const resultJson = await binding.importSandboxImage(
6836
- {
6837
- apiUrl: context.apiUrl,
6838
- bearerToken,
6839
- imageReference: reference,
6840
- registeredName: effectiveName,
6841
- diskMb: options.diskMb,
6842
- builderDiskMb: options.builderDiskMb,
6843
- cpus: options.cpus,
6844
- memoryMb: options.memoryMb,
6845
- isPublic: options.isPublic ?? false,
6846
- organizationId: usePat ? context.organizationId : void 0,
6847
- projectId: usePat ? context.projectId : void 0,
6848
- namespace: context.namespace,
6849
- useScopeHeaders: usePat,
6850
- userAgent: void 0,
6851
- dockerCompat: options.dockerCompat ?? false,
6852
- dockerConfigJson: options.dockerConfigJson
6853
- },
6854
- (event) => {
6855
- try {
6856
- emit(eventToEmitDict(event));
6857
- } catch (error) {
6858
- emitError ??= error;
6859
- }
6597
+ const nativeOptions = {
6598
+ apiUrl: context.apiUrl,
6599
+ bearerToken,
6600
+ imageReference: reference,
6601
+ registeredName: effectiveName,
6602
+ diskMb: options.diskMb,
6603
+ builderDiskMb: options.builderDiskMb,
6604
+ cpus: options.cpus,
6605
+ memoryMb: options.memoryMb,
6606
+ isPublic: options.isPublic ?? false,
6607
+ organizationId: usePat ? context.organizationId : void 0,
6608
+ projectId: usePat ? context.projectId : void 0,
6609
+ namespace: context.namespace,
6610
+ useScopeHeaders: usePat,
6611
+ userAgent: void 0,
6612
+ dockerCompat: options.dockerCompat ?? false,
6613
+ dockerConfigJson: options.dockerConfigJson,
6614
+ cas: options.cas ?? false
6615
+ };
6616
+ const forward = (event) => {
6617
+ try {
6618
+ emit(eventToEmitDict(event));
6619
+ } catch (error) {
6620
+ emitError ??= error;
6860
6621
  }
6861
- );
6622
+ };
6623
+ const resultJson = options.cas ? await __runWithInterrupt(
6624
+ options.signal,
6625
+ (interruptId) => binding.importSandboxImage(nativeOptions, forward, interruptId)
6626
+ ) : await binding.importSandboxImage(nativeOptions, forward);
6862
6627
  if (emitError != null) {
6863
6628
  throw emitError;
6864
6629
  }
@@ -6874,10 +6639,7 @@ async function importSandboxImage(imageReference, options = {}, deps = {}) {
6874
6639
  emit({ type: "done" });
6875
6640
  return result;
6876
6641
  }
6877
- async function deleteSandboxImage(imageName) {
6878
- if (typeof imageName !== "string" || imageName.length === 0) {
6879
- throw new TypeError("imageName must be a non-empty string");
6880
- }
6642
+ function catalogOptionsFromEnv() {
6881
6643
  const context = buildContextFromEnv();
6882
6644
  const bearerToken = context.apiKey;
6883
6645
  if (!bearerToken) {
@@ -6888,66 +6650,76 @@ async function deleteSandboxImage(imageName) {
6888
6650
  }
6889
6651
  throw new Error("Missing TENSORLAKE_API_KEY credentials.");
6890
6652
  }
6891
- const client = new CloudClient({
6892
- apiUrl: context.apiUrl,
6893
- apiKey: bearerToken,
6894
- namespace: context.namespace
6895
- });
6896
- try {
6897
- await client.deleteSandboxImage(imageName);
6898
- } finally {
6899
- client.close();
6900
- }
6653
+ return { apiUrl: context.apiUrl, bearerToken, useScopeHeaders: false };
6901
6654
  }
6902
- async function findSandboxImageByName(imageName) {
6903
- if (typeof imageName !== "string" || imageName.length === 0) {
6904
- throw new TypeError("imageName must be a non-empty string");
6905
- }
6655
+ function catalogOptionsFromCredentials(credentials) {
6906
6656
  const context = buildContextFromEnv();
6907
- const bearerToken = context.apiKey;
6657
+ const bearerToken = credentials.apiKey ?? context.apiKey;
6908
6658
  if (!bearerToken) {
6909
- if (context.personalAccessToken) {
6910
- throw new Error(
6911
- "Sandbox image SDKs require TENSORLAKE_API_KEY. Personal access tokens are CLI-only."
6912
- );
6913
- }
6914
6659
  throw new Error("Missing TENSORLAKE_API_KEY credentials.");
6915
6660
  }
6916
- const client = new CloudClient({
6917
- apiUrl: context.apiUrl,
6918
- apiKey: bearerToken,
6919
- namespace: context.namespace
6920
- });
6921
- try {
6922
- return await client.findSandboxImageByName(imageName);
6923
- } finally {
6924
- client.close();
6661
+ const { organizationId, projectId } = credentials;
6662
+ if (organizationId === void 0 !== (projectId === void 0)) {
6663
+ throw new TypeError("organizationId and projectId must be given together");
6925
6664
  }
6665
+ return {
6666
+ apiUrl: credentials.apiUrl ?? context.apiUrl,
6667
+ bearerToken,
6668
+ organizationId,
6669
+ projectId,
6670
+ useScopeHeaders: organizationId !== void 0
6671
+ };
6926
6672
  }
6927
- async function listSandboxImages() {
6928
- const context = buildContextFromEnv();
6929
- const bearerToken = context.apiKey;
6930
- if (!bearerToken) {
6931
- if (context.personalAccessToken) {
6932
- throw new Error(
6933
- "Sandbox image SDKs require TENSORLAKE_API_KEY. Personal access tokens are CLI-only."
6934
- );
6935
- }
6936
- throw new Error("Missing TENSORLAKE_API_KEY credentials.");
6673
+ function parseTemplate(json) {
6674
+ return JSON.parse(json);
6675
+ }
6676
+ async function deleteSandboxImage(imageName) {
6677
+ if (typeof imageName !== "string" || imageName.length === 0) {
6678
+ throw new TypeError("imageName must be a non-empty string");
6937
6679
  }
6938
- const client = new CloudClient({
6939
- apiUrl: context.apiUrl,
6940
- apiKey: bearerToken,
6941
- namespace: context.namespace
6942
- });
6943
- try {
6944
- return await client.listSandboxImages();
6945
- } finally {
6946
- client.close();
6680
+ await loadNativeBinding().deleteSandboxImage(catalogOptionsFromEnv(), imageName);
6681
+ }
6682
+ async function deleteSandboxImageWithCredentials(credentials, imageName) {
6683
+ if (typeof imageName !== "string" || imageName.length === 0) {
6684
+ throw new TypeError("imageName must be a non-empty string");
6685
+ }
6686
+ await loadNativeBinding().deleteSandboxImage(
6687
+ catalogOptionsFromCredentials(credentials),
6688
+ imageName
6689
+ );
6690
+ }
6691
+ async function findSandboxImageByName(imageName) {
6692
+ if (typeof imageName !== "string" || imageName.length === 0) {
6693
+ throw new TypeError("imageName must be a non-empty string");
6694
+ }
6695
+ const json = await loadNativeBinding().findSandboxImage(
6696
+ catalogOptionsFromEnv(),
6697
+ imageName
6698
+ );
6699
+ return json == null ? null : parseTemplate(json);
6700
+ }
6701
+ async function findSandboxImageByNameWithCredentials(credentials, imageName) {
6702
+ if (typeof imageName !== "string" || imageName.length === 0) {
6703
+ throw new TypeError("imageName must be a non-empty string");
6947
6704
  }
6705
+ const json = await loadNativeBinding().findSandboxImage(
6706
+ catalogOptionsFromCredentials(credentials),
6707
+ imageName
6708
+ );
6709
+ return json == null ? null : parseTemplate(json);
6710
+ }
6711
+ async function listSandboxImages() {
6712
+ const json = await loadNativeBinding().listSandboxImages(catalogOptionsFromEnv());
6713
+ return JSON.parse(json);
6714
+ }
6715
+ async function listSandboxImagesWithCredentials(credentials) {
6716
+ const json = await loadNativeBinding().listSandboxImages(
6717
+ catalogOptionsFromCredentials(credentials)
6718
+ );
6719
+ return JSON.parse(json);
6948
6720
  }
6949
6721
  async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
6950
- const parsed = (0, import_node_util2.parseArgs)({
6722
+ const parsed = (0, import_node_util.parseArgs)({
6951
6723
  args: argv,
6952
6724
  allowPositionals: true,
6953
6725
  options: {
@@ -7001,7 +6773,7 @@ async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
7001
6773
  );
7002
6774
  }
7003
6775
  async function runImportSandboxImageCli(argv = process.argv.slice(2)) {
7004
- const parsed = (0, import_node_util2.parseArgs)({
6776
+ const parsed = (0, import_node_util.parseArgs)({
7005
6777
  args: argv,
7006
6778
  allowPositionals: true,
7007
6779
  options: {
@@ -7054,7 +6826,7 @@ async function runImportSandboxImageCli(argv = process.argv.slice(2)) {
7054
6826
  { emit: ndjsonStdoutEmit, _allowPat: true }
7055
6827
  );
7056
6828
  }
7057
- var import_node_path3, import_node_os2, import_node_fs2, import_node_util2, cachedNativeBinding, cachedNativeBindingError, DEFAULT_IMAGE_NAME2;
6829
+ var import_node_path3, import_node_os2, import_node_fs2, import_node_util, cachedNativeBinding, cachedNativeBindingError, DEFAULT_IMAGE_NAME2, __imageBuildInternals;
7058
6830
  var init_sandbox_image = __esm({
7059
6831
  "src/sandbox-image.ts"() {
7060
6832
  "use strict";
@@ -7062,11 +6834,21 @@ var init_sandbox_image = __esm({
7062
6834
  import_node_path3 = __toESM(require("path"), 1);
7063
6835
  import_node_os2 = __toESM(require("os"), 1);
7064
6836
  import_node_fs2 = require("fs");
7065
- import_node_util2 = require("util");
7066
- init_cloud_client();
6837
+ import_node_util = require("util");
7067
6838
  init_image();
7068
6839
  init_native_binding();
6840
+ init_image_builds();
7069
6841
  DEFAULT_IMAGE_NAME2 = "default";
6842
+ __imageBuildInternals = {
6843
+ loadNativeBinding: () => loadNativeBinding(),
6844
+ buildContextFromEnv: () => buildContextFromEnv(),
6845
+ stderrEmit: (event) => stderrEmit(event),
6846
+ noopEmit: (event) => noopEmit(event),
6847
+ eventToEmitDict: (event) => eventToEmitDict(event),
6848
+ imageRequiresContext: (image) => imageRequiresContext(image),
6849
+ defaultRegisteredName: (dockerfilePath) => defaultRegisteredName(dockerfilePath),
6850
+ defaultRegisteredNameFromImage: (reference) => defaultRegisteredNameFromImage(reference)
6851
+ };
7070
6852
  }
7071
6853
  });
7072
6854
 
@@ -7091,7 +6873,15 @@ __export(index_exports, {
7091
6873
  Headers: () => Headers,
7092
6874
  HttpBody: () => HttpBody,
7093
6875
  Image: () => Image,
6876
+ ImageBuildCancelledError: () => ImageBuildCancelledError,
6877
+ ImageBuildError: () => ImageBuildError,
6878
+ ImageBuildFailedError: () => ImageBuildFailedError,
6879
+ ImageBuildHandle: () => ImageBuildHandle,
6880
+ ImageBuildInProgressError: () => ImageBuildInProgressError,
6881
+ ImageBuildInterruptedError: () => ImageBuildInterruptedError,
6882
+ ImageBuildNotFoundError: () => ImageBuildNotFoundError,
7094
6883
  ImageBuildOperationType: () => ImageBuildOperationType,
6884
+ ImageBuildTimeoutError: () => ImageBuildTimeoutError,
7095
6885
  MountError: () => MountError,
7096
6886
  OutputMode: () => OutputMode,
7097
6887
  PendingSandbox: () => PendingSandbox,
@@ -7124,12 +6914,14 @@ __export(index_exports, {
7124
6914
  TERMINATION_REASON_CANCELLED: () => TERMINATION_REASON_CANCELLED,
7125
6915
  TERMINATION_REASON_NO_CAPACITY: () => TERMINATION_REASON_NO_CAPACITY,
7126
6916
  TcpTunnel: () => TcpTunnel,
6917
+ cancelImageBuild: () => cancelImageBuild,
7127
6918
  createFileSystem: () => createFileSystem,
7128
6919
  createSandboxImage: () => createSandboxImage,
7129
6920
  deleteFileSystem: () => deleteFileSystem,
7130
6921
  deleteSandboxImage: () => deleteSandboxImage,
7131
6922
  dockerfileContent: () => dockerfileContent,
7132
6923
  findSandboxImageByName: () => findSandboxImageByName,
6924
+ getImageBuild: () => getImageBuild,
7133
6925
  importSandboxImage: () => importSandboxImage,
7134
6926
  isSandboxPending: () => isSandboxPending,
7135
6927
  listFileSystems: () => listFileSystems,
@@ -7141,7 +6933,10 @@ __export(index_exports, {
7141
6933
  runLocal: () => runLocal,
7142
6934
  runRemote: () => runRemote,
7143
6935
  sandboxUrlFromIngressEndpoint: () => sandboxUrlFromIngressEndpoint,
7144
- schema: () => schema
6936
+ schema: () => schema,
6937
+ startImageBuild: () => startImageBuild,
6938
+ startImageImport: () => startImageImport,
6939
+ waitImageBuild: () => waitImageBuild
7145
6940
  });
7146
6941
  module.exports = __toCommonJS(index_exports);
7147
6942
  init_cjs_shims();
@@ -7151,11 +6946,546 @@ init_pending_sandbox();
7151
6946
  init_sandbox();
7152
6947
  init_desktop();
7153
6948
  init_tunnel();
7154
- init_cloud_client();
6949
+
6950
+ // src/cloud-client.ts
6951
+ init_cjs_shims();
6952
+ var import_nanoid = require("nanoid");
6953
+ init_defaults();
6954
+
6955
+ // src/native-cloud.ts
6956
+ init_cjs_shims();
6957
+ init_native_sandbox();
6958
+ init_native_worker_client();
6959
+ var NativeCloudTransport = class {
6960
+ constructor(options) {
6961
+ this.options = options;
6962
+ if (!Number.isSafeInteger(options.maxRetries) || options.maxRetries < 0 || options.maxRetries > 4294967295) {
6963
+ throw new RangeError("maxRetries must be a non-negative 32-bit integer");
6964
+ }
6965
+ if (!Number.isFinite(options.retryBackoffMs) || options.retryBackoffMs < 0) {
6966
+ throw new RangeError("retryBackoffMs must be a non-negative finite number");
6967
+ }
6968
+ }
6969
+ native;
6970
+ closed = false;
6971
+ client() {
6972
+ if (this.closed) throw new Error("Tensorlake native client is closed");
6973
+ if (this.native) return this.native;
6974
+ const ctor = loadNativeSandboxBinding().NativeCloudClient;
6975
+ if (!ctor) throw new Error("Native binding does not export NativeCloudClient; rebuild with 'npm run build:native'");
6976
+ return this.native = new ctor(JSON.stringify(this.options));
6977
+ }
6978
+ close() {
6979
+ this.closed = true;
6980
+ releaseNativeHandle(this.native ?? null);
6981
+ this.native = void 0;
6982
+ }
6983
+ async requestResponse(method, path4, options) {
6984
+ const request = await serializeRequest(method, path4, options);
6985
+ const raw = await callNative(() => this.client().request(request));
6986
+ const response = new Response(
6987
+ [204, 205, 304].includes(raw.status) ? null : Uint8Array.from(raw.data).buffer,
6988
+ { status: raw.status, headers: JSON.parse(raw.headersJson) }
6989
+ );
6990
+ return Object.assign(response, { traceId: raw.traceId });
6991
+ }
6992
+ async requestJson(method, path4) {
6993
+ const response = await this.requestResponse(method, path4);
6994
+ const text = await response.text();
6995
+ const value = text ? JSON.parse(text) : null;
6996
+ return Object.assign(value ?? {}, { traceId: response.traceId });
6997
+ }
6998
+ async *stream(method, path4, signal) {
6999
+ if (signal?.aborted) return;
7000
+ const request = await serializeRequest(method, path4, { headers: { Accept: "text/event-stream" } });
7001
+ yield* nativeEventStream((emit) => this.client().stream(request, emit), void 0, signal);
7002
+ }
7003
+ };
7004
+ async function serializeRequest(method, path4, options) {
7005
+ const headers = { ...options?.headers };
7006
+ let body = options?.body;
7007
+ if (options?.json !== void 0) {
7008
+ body = JSON.stringify(options.json);
7009
+ if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) {
7010
+ headers["Content-Type"] = "application/json";
7011
+ }
7012
+ }
7013
+ const request = {
7014
+ method,
7015
+ path: path4,
7016
+ headersJson: JSON.stringify(headers),
7017
+ statusOnlyCodes: [...options?.statusOnlyCodes ?? []]
7018
+ };
7019
+ if (body instanceof FormData) {
7020
+ request.parts = [];
7021
+ for (const [name, value] of body.entries()) {
7022
+ request.parts.push(typeof value === "string" ? { name, data: Buffer.from(value) } : { name, data: Buffer.from(await value.arrayBuffer()), filename: value.name, contentType: value.type || "application/octet-stream" });
7023
+ }
7024
+ } else if (body !== void 0) {
7025
+ if (body instanceof Blob) request.body = Buffer.from(await body.arrayBuffer());
7026
+ else if (typeof body === "string") request.body = Buffer.from(body);
7027
+ else if (body instanceof ArrayBuffer) request.body = Buffer.from(new Uint8Array(body));
7028
+ else request.body = Buffer.from(body);
7029
+ }
7030
+ return request;
7031
+ }
7032
+
7033
+ // src/cloud-client.ts
7034
+ init_models();
7035
+ init_sandbox_image();
7036
+ var CloudClient = class _CloudClient {
7037
+ transport;
7038
+ apiUrl;
7039
+ apiKey;
7040
+ organizationId;
7041
+ projectId;
7042
+ namespace;
7043
+ constructor(options) {
7044
+ this.apiUrl = options?.apiUrl ?? API_URL;
7045
+ this.apiKey = options?.apiKey ?? API_KEY;
7046
+ this.organizationId = options?.organizationId;
7047
+ this.projectId = options?.projectId;
7048
+ this.namespace = options?.namespace ?? NAMESPACE;
7049
+ this.transport = new NativeCloudTransport({
7050
+ baseUrl: options?.apiUrl ?? API_URL,
7051
+ apiKey: options?.apiKey ?? API_KEY,
7052
+ organizationId: this.organizationId,
7053
+ projectId: this.projectId,
7054
+ maxRetries: options?.maxRetries ?? MAX_RETRIES,
7055
+ retryBackoffMs: options?.retryBackoffMs ?? RETRY_BACKOFF_MS,
7056
+ timeoutMs: DEFAULT_HTTP_TIMEOUT_MS,
7057
+ userAgent: `tensorlake-typescript-sdk/${SDK_VERSION}`
7058
+ });
7059
+ }
7060
+ static forCloud(options) {
7061
+ return new _CloudClient(options);
7062
+ }
7063
+ close() {
7064
+ this.transport.close();
7065
+ }
7066
+ async upsertApplication(manifest, codeZip, upgradeRunningRequests = false) {
7067
+ const applicationManifest = { ...manifest };
7068
+ let publicEndpointId = publicEndpointIdFromManifest(applicationManifest);
7069
+ if (applicationAllowsUnauthenticatedRequests(applicationManifest) && publicEndpointId === void 0) {
7070
+ const applicationName = applicationManifest.name;
7071
+ if (typeof applicationName === "string" && applicationName.length > 0) {
7072
+ publicEndpointId = await this.existingApplicationPublicEndpointId(applicationName);
7073
+ }
7074
+ publicEndpointId ??= generatePublicEndpointId();
7075
+ }
7076
+ if (publicEndpointId !== void 0) {
7077
+ applicationManifest.public_endpoint_id = publicEndpointId;
7078
+ delete applicationManifest.publicEndpointId;
7079
+ }
7080
+ const form = new FormData();
7081
+ form.append(
7082
+ "code",
7083
+ new Blob([toBlobPart(codeZip)], { type: "application/zip" }),
7084
+ "code.zip"
7085
+ );
7086
+ form.append("code_content_type", "application/zip");
7087
+ form.append("application", JSON.stringify(applicationManifest));
7088
+ form.append(
7089
+ "upgrade_requests_to_latest_code",
7090
+ String(upgradeRunningRequests)
7091
+ );
7092
+ await this.transport.requestResponse("POST", this.namespacePath("applications"), {
7093
+ body: form
7094
+ });
7095
+ }
7096
+ async deleteApplication(applicationName) {
7097
+ await this.transport.requestResponse(
7098
+ "DELETE",
7099
+ this.namespacePath(`applications/${encodeURIComponent(applicationName)}`)
7100
+ );
7101
+ }
7102
+ /**
7103
+ * Delete a registered sandbox image by name or id through the merged
7104
+ * catalog, with this client's credentials: an `image_` id or a name the
7105
+ * Image Service knows deletes a CAS image, a `sandbox_template_` id or a
7106
+ * name registered only in the platform registry deletes a legacy image.
7107
+ * Rejects when neither catalog knows the reference. The same semantics
7108
+ * and accepted differences as the module-level `deleteSandboxImage`.
7109
+ */
7110
+ async deleteSandboxImage(imageName) {
7111
+ await deleteSandboxImageWithCredentials(this.catalogCredentials(), imageName);
7112
+ }
7113
+ /**
7114
+ * Look up a registered sandbox image by name or id through the merged
7115
+ * catalog, with this client's credentials. Returns the image in the
7116
+ * sandbox-template shape, or `null` when neither the Image Service nor the
7117
+ * platform registry knows the reference. An explicit organization/project
7118
+ * scope (the client's, or the call's override) addresses that project's
7119
+ * routes directly, as before; without one the key's own scope applies.
7120
+ */
7121
+ async findSandboxImageByName(imageName, options) {
7122
+ return findSandboxImageByNameWithCredentials(
7123
+ this.catalogCredentials(options),
7124
+ imageName
7125
+ );
7126
+ }
7127
+ /**
7128
+ * List every registered sandbox image of the project (CAS and legacy, in
7129
+ * the sandbox-template shape, newest first) through the merged catalog,
7130
+ * with this client's credentials. An explicit organization/project scope
7131
+ * (the client's, or the call's override) addresses that project's routes
7132
+ * directly, as before; without one the key's own scope applies.
7133
+ */
7134
+ async listSandboxImages(options) {
7135
+ return listSandboxImagesWithCredentials(this.catalogCredentials(options));
7136
+ }
7137
+ catalogCredentials(options) {
7138
+ return {
7139
+ apiUrl: this.apiUrl,
7140
+ apiKey: this.apiKey,
7141
+ organizationId: options?.organizationId ?? this.organizationId,
7142
+ projectId: options?.projectId ?? this.projectId
7143
+ };
7144
+ }
7145
+ async applications() {
7146
+ const raw = await this.transport.requestJson(
7147
+ "GET",
7148
+ this.namespacePath("applications")
7149
+ );
7150
+ return (raw.applications ?? []).map(
7151
+ (application) => fromSnakeKeys(application)
7152
+ );
7153
+ }
7154
+ async applicationManifest(applicationName) {
7155
+ const raw = await this.transport.requestJson(
7156
+ "GET",
7157
+ this.namespacePath(`applications/${encodeURIComponent(applicationName)}`)
7158
+ );
7159
+ return fromSnakeKeys(raw);
7160
+ }
7161
+ async runRequest(applicationName, inputs = []) {
7162
+ const path4 = this.namespacePath(
7163
+ `applications/${encodeURIComponent(applicationName)}`
7164
+ );
7165
+ const response = inputs.length === 0 ? await this.transport.requestResponse("POST", path4, {
7166
+ body: new Uint8Array(),
7167
+ headers: { Accept: "application/json" }
7168
+ }) : inputs.length === 1 && inputs[0].name === "0" ? await this.transport.requestResponse("POST", path4, {
7169
+ body: toRequestBody(inputs[0].data),
7170
+ headers: {
7171
+ Accept: "application/json",
7172
+ "Content-Type": inputs[0].contentType
7173
+ }
7174
+ }) : await this.runMultipartRequest(path4, inputs);
7175
+ const body = await parseJsonResponse(response);
7176
+ const requestId = body?.request_id;
7177
+ if (!requestId) {
7178
+ throw new Error("missing request_id in run request response body");
7179
+ }
7180
+ return requestId;
7181
+ }
7182
+ async waitOnRequestCompletion(applicationName, requestId) {
7183
+ const stream = this.transport.stream(
7184
+ "GET",
7185
+ this.namespacePath(
7186
+ `applications/${encodeURIComponent(applicationName)}/requests/${encodeURIComponent(requestId)}/progress`
7187
+ )
7188
+ );
7189
+ for await (const event of stream) {
7190
+ if (Object.prototype.hasOwnProperty.call(event, "RequestFinished")) {
7191
+ return;
7192
+ }
7193
+ }
7194
+ throw new Error("progress stream ended before request completion");
7195
+ }
7196
+ async requestMetadata(applicationName, requestId) {
7197
+ const raw = await this.transport.requestJson(
7198
+ "GET",
7199
+ this.namespacePath(
7200
+ `applications/${encodeURIComponent(applicationName)}/requests/${encodeURIComponent(requestId)}`
7201
+ )
7202
+ );
7203
+ return fromSnakeKeys(raw);
7204
+ }
7205
+ async requestOutput(applicationName, requestId) {
7206
+ const response = await this.transport.requestResponse(
7207
+ "GET",
7208
+ this.namespacePath(
7209
+ `applications/${encodeURIComponent(applicationName)}/requests/${encodeURIComponent(requestId)}/output`
7210
+ )
7211
+ );
7212
+ const serializedValue = new Uint8Array(await response.arrayBuffer());
7213
+ const contentType = response.headers.get("content-type") ?? "";
7214
+ return {
7215
+ serializedValue,
7216
+ contentType
7217
+ };
7218
+ }
7219
+ async listSecrets(options) {
7220
+ const base = this.secretCollectionPath(
7221
+ options?.organizationId,
7222
+ options?.projectId
7223
+ );
7224
+ const raw = await this.transport.requestJson("GET", base);
7225
+ const pageSize = Math.max(0, options?.pageSize ?? 100);
7226
+ return Object.assign(
7227
+ {
7228
+ items: raw.slice(0, pageSize).map(secretFromMetadata),
7229
+ pagination: { total: raw.length }
7230
+ },
7231
+ { traceId: raw.traceId }
7232
+ );
7233
+ }
7234
+ async getSecret(secretId, options) {
7235
+ const base = this.secretCollectionPath(
7236
+ options?.organizationId,
7237
+ options?.projectId
7238
+ );
7239
+ const raw = await this.transport.requestJson(
7240
+ "GET",
7241
+ `${base}/${encodeURIComponent(secretId)}`
7242
+ );
7243
+ return Object.assign(secretFromMetadata(raw), { traceId: raw.traceId });
7244
+ }
7245
+ async upsertSecrets(secrets, options) {
7246
+ const base = this.secretCollectionPath(
7247
+ options?.organizationId,
7248
+ options?.projectId
7249
+ );
7250
+ if (Array.isArray(secrets)) {
7251
+ const results = [];
7252
+ for (const secret of secrets) {
7253
+ results.push(await this.upsertSecret(base, secret));
7254
+ }
7255
+ return results;
7256
+ }
7257
+ return this.upsertSecret(base, secrets);
7258
+ }
7259
+ async deleteSecret(secretId, options) {
7260
+ const base = this.secretCollectionPath(
7261
+ options?.organizationId,
7262
+ options?.projectId
7263
+ );
7264
+ await this.transport.requestResponse(
7265
+ "DELETE",
7266
+ `${base}/${encodeURIComponent(secretId)}`
7267
+ );
7268
+ }
7269
+ async upsertSecret(base, secret) {
7270
+ let response = await this.transport.requestResponse("POST", base, {
7271
+ json: secret,
7272
+ headers: { "Idempotency-Key": (0, import_nanoid.nanoid)() },
7273
+ statusOnlyCodes: /* @__PURE__ */ new Set([409])
7274
+ });
7275
+ if (response.status === 409) {
7276
+ const prefix = base.slice(0, -"/secrets".length);
7277
+ const existing = await this.transport.requestJson(
7278
+ "GET",
7279
+ `${prefix}/secret-names/${encodeURIComponent(secret.name)}`
7280
+ );
7281
+ response = await this.transport.requestResponse(
7282
+ "POST",
7283
+ `${base}/${encodeURIComponent(existing.id)}/versions`,
7284
+ {
7285
+ json: { value: secret.value },
7286
+ headers: { "Idempotency-Key": (0, import_nanoid.nanoid)() }
7287
+ }
7288
+ );
7289
+ }
7290
+ const metadata = await parseJsonResponse(response);
7291
+ return Object.assign(secretFromMetadata(metadata), {
7292
+ traceId: response.traceId
7293
+ });
7294
+ }
7295
+ async startImageBuild(buildServicePath, request) {
7296
+ const form = new FormData();
7297
+ form.append("graph_name", request.applicationName);
7298
+ form.append("graph_version", request.applicationVersion);
7299
+ form.append("graph_function_name", request.functionName);
7300
+ form.append("image_name", request.imageName);
7301
+ form.append("image_id", request.imageId);
7302
+ form.append(
7303
+ "context",
7304
+ new Blob([toBlobPart(request.buildContext)]),
7305
+ "context.tar.gz"
7306
+ );
7307
+ const response = await this.transport.requestResponse(
7308
+ "PUT",
7309
+ `${trimTrailingSlashes2(buildServicePath)}/builds`,
7310
+ { body: form }
7311
+ );
7312
+ const raw = await parseJsonResponse(response);
7313
+ return fromSnakeKeys(raw);
7314
+ }
7315
+ async createApplicationBuild(buildServicePath, request, imageContexts) {
7316
+ const form = createApplicationBuildForm(request, imageContexts);
7317
+ const response = await this.transport.requestResponse(
7318
+ "POST",
7319
+ trimTrailingSlashes2(buildServicePath),
7320
+ { body: form }
7321
+ );
7322
+ const raw = await parseJsonResponse(response);
7323
+ return fromSnakeKeys(raw);
7324
+ }
7325
+ async applicationBuildInfo(buildServicePath, applicationBuildId) {
7326
+ const raw = await this.transport.requestJson(
7327
+ "GET",
7328
+ `${trimTrailingSlashes2(buildServicePath)}/${encodeURIComponent(applicationBuildId)}`
7329
+ );
7330
+ return fromSnakeKeys(raw);
7331
+ }
7332
+ async cancelApplicationBuild(buildServicePath, applicationBuildId) {
7333
+ const raw = await this.transport.requestJson(
7334
+ "POST",
7335
+ `${trimTrailingSlashes2(buildServicePath)}/${encodeURIComponent(applicationBuildId)}/cancel`
7336
+ );
7337
+ return fromSnakeKeys(raw);
7338
+ }
7339
+ async buildInfo(buildServicePath, buildId) {
7340
+ const raw = await this.transport.requestJson(
7341
+ "GET",
7342
+ `${trimTrailingSlashes2(buildServicePath)}/builds/${encodeURIComponent(buildId)}`
7343
+ );
7344
+ return fromSnakeKeys(raw);
7345
+ }
7346
+ async cancelBuild(buildServicePath, buildId) {
7347
+ await this.transport.requestResponse(
7348
+ "POST",
7349
+ `${trimTrailingSlashes2(buildServicePath)}/builds/${encodeURIComponent(buildId)}/cancel`
7350
+ );
7351
+ }
7352
+ async *streamBuildLogs(buildServicePath, buildId, signal) {
7353
+ const stream = this.transport.stream(
7354
+ "GET",
7355
+ `${trimTrailingSlashes2(buildServicePath)}/builds/${encodeURIComponent(buildId)}/logs`,
7356
+ signal
7357
+ );
7358
+ for await (const event of stream) {
7359
+ yield fromSnakeKeys(event);
7360
+ }
7361
+ }
7362
+ async runMultipartRequest(path4, inputs) {
7363
+ const form = new FormData();
7364
+ for (const input of inputs) {
7365
+ form.append(
7366
+ input.name,
7367
+ new Blob([toBlobPart(input.data)], { type: input.contentType }),
7368
+ input.name
7369
+ );
7370
+ }
7371
+ return this.transport.requestResponse("POST", path4, {
7372
+ body: form,
7373
+ headers: { Accept: "application/json" }
7374
+ });
7375
+ }
7376
+ namespacePath(subpath) {
7377
+ return `/v1/namespaces/${encodeURIComponent(this.namespace)}/${subpath.replace(/^\/+/, "")}`;
7378
+ }
7379
+ async existingApplicationPublicEndpointId(applicationName) {
7380
+ const response = await this.transport.requestResponse(
7381
+ "GET",
7382
+ this.namespacePath(`applications/${encodeURIComponent(applicationName)}`),
7383
+ { statusOnlyCodes: /* @__PURE__ */ new Set([404]) }
7384
+ );
7385
+ if (response.status === 404) {
7386
+ return void 0;
7387
+ }
7388
+ const application = await parseJsonResponse(response);
7389
+ return publicEndpointIdFromManifest(application);
7390
+ }
7391
+ secretCollectionPath(organizationId, projectId) {
7392
+ const resolvedOrganizationId = organizationId ?? this.organizationId;
7393
+ const resolvedProjectId = projectId ?? this.projectId;
7394
+ if (!resolvedOrganizationId && !resolvedProjectId) {
7395
+ return this.namespacePath("secrets");
7396
+ }
7397
+ if (!resolvedOrganizationId || !resolvedProjectId) {
7398
+ throw new Error("organizationId and projectId must be provided together");
7399
+ }
7400
+ return this.namespacePath("secrets");
7401
+ }
7402
+ };
7403
+ function secretFromMetadata(metadata) {
7404
+ return {
7405
+ id: metadata.id,
7406
+ name: metadata.name,
7407
+ createdAt: new Date(metadata.created_at_ms)
7408
+ };
7409
+ }
7410
+ var UNAUTHENTICATED_REQUESTS = "unauthenticated_requests";
7411
+ function applicationAllowsUnauthenticatedRequests(manifest) {
7412
+ return Array.isArray(manifest.allow) && manifest.allow.includes(UNAUTHENTICATED_REQUESTS);
7413
+ }
7414
+ function publicEndpointIdFromManifest(manifest) {
7415
+ for (const key of ["public_endpoint_id", "publicEndpointId"]) {
7416
+ const value = manifest[key];
7417
+ if (typeof value === "string" && value.length > 0) {
7418
+ return value;
7419
+ }
7420
+ }
7421
+ return void 0;
7422
+ }
7423
+ function generatePublicEndpointId() {
7424
+ return `endpoint_${(0, import_nanoid.nanoid)()}`;
7425
+ }
7426
+ function createApplicationBuildForm(request, imageContexts) {
7427
+ const contextsByPartName = /* @__PURE__ */ new Map();
7428
+ for (const context of imageContexts) {
7429
+ if (contextsByPartName.has(context.contextTarPartName)) {
7430
+ throw new Error(
7431
+ `duplicate image context part name '${context.contextTarPartName}'`
7432
+ );
7433
+ }
7434
+ contextsByPartName.set(context.contextTarPartName, context);
7435
+ }
7436
+ const form = new FormData();
7437
+ form.append(
7438
+ "app_version",
7439
+ new Blob([JSON.stringify(request)], { type: "application/json" }),
7440
+ "app_version"
7441
+ );
7442
+ for (const image of request.images) {
7443
+ const context = contextsByPartName.get(image.contextTarPartName);
7444
+ if (!context) {
7445
+ throw new Error(
7446
+ `missing image context for part '${image.contextTarPartName}'`
7447
+ );
7448
+ }
7449
+ form.append(
7450
+ image.contextTarPartName,
7451
+ new Blob([toBlobPart(context.contextTarGz)]),
7452
+ `${image.contextTarPartName}.tar.gz`
7453
+ );
7454
+ }
7455
+ for (const context of imageContexts) {
7456
+ if (!request.images.some((image) => image.contextTarPartName === context.contextTarPartName)) {
7457
+ throw new Error(
7458
+ `unexpected image context for part '${context.contextTarPartName}'`
7459
+ );
7460
+ }
7461
+ }
7462
+ return form;
7463
+ }
7464
+ function trimTrailingSlashes2(value) {
7465
+ return value.endsWith("/") ? value.slice(0, -1) : value;
7466
+ }
7467
+ function toBlobPart(data) {
7468
+ if (typeof data === "string" || data instanceof Blob) {
7469
+ return data;
7470
+ }
7471
+ if (data instanceof Uint8Array) {
7472
+ return Uint8Array.from(data).buffer;
7473
+ }
7474
+ return data;
7475
+ }
7476
+ function toRequestBody(data) {
7477
+ return toBlobPart(data);
7478
+ }
7479
+ async function parseJsonResponse(response) {
7480
+ const text = await response.text();
7481
+ if (!text) {
7482
+ return void 0;
7483
+ }
7484
+ return JSON.parse(text);
7485
+ }
7155
7486
 
7156
7487
  // src/api-client.ts
7157
7488
  init_cjs_shims();
7158
- init_cloud_client();
7159
7489
 
7160
7490
  // src/filesystem.ts
7161
7491
  init_cjs_shims();
@@ -7164,21 +7494,21 @@ var import_node_crypto2 = require("crypto");
7164
7494
  // src/mount-cli.ts
7165
7495
  init_cjs_shims();
7166
7496
  var import_node_child_process = require("child_process");
7167
- var import_node_fs = require("fs");
7168
- var import_node_os = require("os");
7169
- var import_node_path = require("path");
7170
- var import_node_util = require("util");
7497
+ var import_node_fs3 = require("fs");
7498
+ var import_node_os3 = require("os");
7499
+ var import_node_path4 = require("path");
7500
+ var import_node_util2 = require("util");
7171
7501
 
7172
7502
  // src/filesystem-models.ts
7173
7503
  init_cjs_shims();
7174
7504
  var GIT_MODE_DIR = 16384;
7175
7505
  var GIT_MODE_SYMLINK = 40960;
7176
- function trimSlashes(path3) {
7506
+ function trimSlashes(path4) {
7177
7507
  let start = 0;
7178
- let end = path3.length;
7179
- while (start < end && path3.charCodeAt(start) === 47) start += 1;
7180
- while (end > start && path3.charCodeAt(end - 1) === 47) end -= 1;
7181
- return path3.slice(start, end);
7508
+ let end = path4.length;
7509
+ while (start < end && path4.charCodeAt(start) === 47) start += 1;
7510
+ while (end > start && path4.charCodeAt(end - 1) === 47) end -= 1;
7511
+ return path4.slice(start, end);
7182
7512
  }
7183
7513
  function fileEntryFromWire(entry, dirPath) {
7184
7514
  const mode = entry.mode ?? 33188;
@@ -7196,9 +7526,9 @@ function fileEntryFromWire(entry, dirPath) {
7196
7526
  }
7197
7527
  function mountStatusFromRaw(raw, localPath) {
7198
7528
  const mounted = "mounted" in raw ? Boolean(raw.mounted) : "active" in raw ? Boolean(raw.active) : true;
7199
- const path3 = String(raw.path || raw.mount_path || localPath || "");
7529
+ const path4 = String(raw.path || raw.mount_path || localPath || "");
7200
7530
  const filesystem = raw.filesystem || raw.file_system || raw.repository || null;
7201
- return { path: path3, filesystem, mounted, raw };
7531
+ return { path: path4, filesystem, mounted, raw };
7202
7532
  }
7203
7533
  var FilesystemError = class extends Error {
7204
7534
  constructor(message) {
@@ -7217,11 +7547,11 @@ var FilesystemNotFoundError = class extends FilesystemError {
7217
7547
  var FileNotFoundInFilesystemError = class extends FilesystemError {
7218
7548
  filesystem;
7219
7549
  path;
7220
- constructor(filesystem, path3) {
7221
- super(`File not found in filesystem ${filesystem}: ${path3}`);
7550
+ constructor(filesystem, path4) {
7551
+ super(`File not found in filesystem ${filesystem}: ${path4}`);
7222
7552
  this.name = "FileNotFoundInFilesystemError";
7223
7553
  this.filesystem = filesystem;
7224
- this.path = path3;
7554
+ this.path = path4;
7225
7555
  }
7226
7556
  };
7227
7557
  var FilesystemAPIError = class extends FilesystemError {
@@ -7256,7 +7586,7 @@ var CliNotFoundError = class extends MountError {
7256
7586
  };
7257
7587
 
7258
7588
  // src/mount-cli.ts
7259
- var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
7589
+ var execFileAsync = (0, import_node_util2.promisify)(import_node_child_process.execFile);
7260
7590
  var MountCli = class {
7261
7591
  constructor(command, envOverrides) {
7262
7592
  this.command = command;
@@ -7267,9 +7597,9 @@ var MountCli = class {
7267
7597
  const candidates = [];
7268
7598
  if (process.env.TENSORLAKE_CLI) candidates.push(process.env.TENSORLAKE_CLI);
7269
7599
  candidates.push("tl");
7270
- const installed = (0, import_node_path.join)((0, import_node_os.homedir)(), ".tensorlake", "bin", "tl");
7600
+ const installed = (0, import_node_path4.join)((0, import_node_os3.homedir)(), ".tensorlake", "bin", "tl");
7271
7601
  try {
7272
- (0, import_node_fs.accessSync)(installed, import_node_fs.constants.F_OK);
7602
+ (0, import_node_fs3.accessSync)(installed, import_node_fs3.constants.F_OK);
7273
7603
  candidates.push(installed);
7274
7604
  } catch {
7275
7605
  }
@@ -7575,8 +7905,8 @@ var Filesystem = class {
7575
7905
  }
7576
7906
  // -- writes -------------------------------------------------------------------
7577
7907
  /** Write one file. Returns the snapshot (version) the write produced. */
7578
- async writeFile(path3, data, message) {
7579
- return await this.writeFiles(/* @__PURE__ */ new Map([[path3, data]]), message);
7908
+ async writeFile(path4, data, message) {
7909
+ return await this.writeFiles(/* @__PURE__ */ new Map([[path4, data]]), message);
7580
7910
  }
7581
7911
  /** Write several files (and/or delete paths) in one atomic snapshot. */
7582
7912
  async writeFiles(files, message, deletes = []) {
@@ -7584,8 +7914,8 @@ var Filesystem = class {
7584
7914
  if (entries.length === 0 && deletes.length === 0) {
7585
7915
  throw new FilesystemError("nothing to write: no files or deletions given");
7586
7916
  }
7587
- const writes = entries.map(([path3, data]) => ({
7588
- path: path3,
7917
+ const writes = entries.map(([path4, data]) => ({
7918
+ path: path4,
7589
7919
  content: toBuffer(data)
7590
7920
  }));
7591
7921
  const resolvedMessage = message || `write ${writes.length} file(s) via SDK`;
@@ -7601,9 +7931,9 @@ var Filesystem = class {
7601
7931
  * Stream one local file directly to object storage in bounded parts.
7602
7932
  * This is the memory-bounded API for multi-GiB files.
7603
7933
  */
7604
- async writeFileFromPath(path3, localPath, message) {
7934
+ async writeFileFromPath(path4, localPath, message) {
7605
7935
  return await this.writeFilesFromPaths(
7606
- /* @__PURE__ */ new Map([[path3, localPath]]),
7936
+ /* @__PURE__ */ new Map([[path4, localPath]]),
7607
7937
  message
7608
7938
  );
7609
7939
  }
@@ -7616,7 +7946,7 @@ var Filesystem = class {
7616
7946
  if (entries.length === 0) {
7617
7947
  throw new FilesystemError("nothing to write: no local files given");
7618
7948
  }
7619
- const writes = entries.map(([path3, localPath]) => ({ path: path3, localPath }));
7949
+ const writes = entries.map(([path4, localPath]) => ({ path: path4, localPath }));
7620
7950
  const resolvedMessage = message || `write ${writes.length} local file(s) via SDK`;
7621
7951
  const branch = await this.branch();
7622
7952
  const traced = await callNative2(
@@ -7693,11 +8023,11 @@ var Filesystem = class {
7693
8023
  };
7694
8024
  }
7695
8025
  /** Delete one file. Returns the durable version the deletion produced. */
7696
- async deleteFile(path3, message) {
8026
+ async deleteFile(path4, message) {
7697
8027
  return await this.writeFiles(
7698
8028
  /* @__PURE__ */ new Map(),
7699
- message || `delete ${path3} via SDK`,
7700
- [path3]
8029
+ message || `delete ${path4} via SDK`,
8030
+ [path4]
7701
8031
  );
7702
8032
  }
7703
8033
  /**
@@ -7748,15 +8078,15 @@ var Filesystem = class {
7748
8078
  * Read a file's bytes at `version` (the current `main` head or a retained
7749
8079
  * snapshot id; defaults to the filesystem's current head).
7750
8080
  */
7751
- async readFile(path3, version) {
7752
- return (await this.readFileWithMetadata(path3, { version })).data;
8081
+ async readFile(path4, version) {
8082
+ return (await this.readFileWithMetadata(path4, { version })).data;
7753
8083
  }
7754
8084
  /**
7755
8085
  * Read bytes and immutable metadata in one request. A range is executed by
7756
8086
  * Artifact Storage, so callers never download the unrequested remainder.
7757
8087
  */
7758
- async readFileWithMetadata(path3, options = {}) {
7759
- if (trimSlashes(path3) === "") {
8088
+ async readFileWithMetadata(path4, options = {}) {
8089
+ if (trimSlashes(path4) === "") {
7760
8090
  throw new FilesystemError("file path must not be empty");
7761
8091
  }
7762
8092
  const range = options.range;
@@ -7769,12 +8099,12 @@ var Filesystem = class {
7769
8099
  const traced = await callNative2(
7770
8100
  () => this.native.readFilesystemFile(
7771
8101
  this.name,
7772
- path3,
8102
+ path4,
7773
8103
  resolvedVersion,
7774
8104
  range?.offset,
7775
8105
  range?.length
7776
8106
  ),
7777
- new FileNotFoundInFilesystemError(this.name, path3)
8107
+ new FileNotFoundInFilesystemError(this.name, path4)
7778
8108
  );
7779
8109
  const fullSize = traced.fullSize;
7780
8110
  if (!traced.contentId || fullSize === void 0 || !Number.isSafeInteger(fullSize) || fullSize < 0 || traced.data.byteLength > fullSize) {
@@ -7789,9 +8119,9 @@ var Filesystem = class {
7789
8119
  };
7790
8120
  }
7791
8121
  /** Read a file as UTF-8 text at `version`. Throws on non-UTF-8 content. */
7792
- async readText(path3, version) {
8122
+ async readText(path4, version) {
7793
8123
  return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(
7794
- await this.readFile(path3, version)
8124
+ await this.readFile(path4, version)
7795
8125
  );
7796
8126
  }
7797
8127
  /** List one directory (non-recursive) at `version`. */
@@ -7854,11 +8184,11 @@ var FilesystemMount = class {
7854
8184
  path;
7855
8185
  readonly;
7856
8186
  /** @internal — obtain instances via `FilesystemClient.mount()`. */
7857
- constructor(client, cli, filesystem, path3, readonly) {
8187
+ constructor(client, cli, filesystem, path4, readonly) {
7858
8188
  this.client = client;
7859
8189
  this.cli = cli;
7860
8190
  this.filesystem = filesystem;
7861
- this.path = path3;
8191
+ this.path = path4;
7862
8192
  this.readonly = readonly;
7863
8193
  }
7864
8194
  /** Flush pending local changes into a durable snapshot. */
@@ -7876,6 +8206,7 @@ var FilesystemMount = class {
7876
8206
  };
7877
8207
 
7878
8208
  // src/api-client.ts
8209
+ init_sandbox_image();
7879
8210
  init_errors();
7880
8211
  var APIClient = class {
7881
8212
  cloudClient;
@@ -7903,7 +8234,7 @@ var APIClient = class {
7903
8234
  await this.cloudClient.deleteApplication(applicationName);
7904
8235
  }
7905
8236
  async deleteSandboxImage(imageName) {
7906
- await this.cloudClient.deleteSandboxImage(imageName);
8237
+ await deleteSandboxImageWithCredentials(this.filesystemOptions, imageName);
7907
8238
  }
7908
8239
  async createFileSystem(name, description, options) {
7909
8240
  const filesystem = await new FilesystemClient(this.filesystemOptions).create(
@@ -7973,7 +8304,7 @@ init_sandbox_image();
7973
8304
 
7974
8305
  // src/repository-mount.ts
7975
8306
  init_cjs_shims();
7976
- var import_node_path4 = require("path");
8307
+ var import_node_path5 = require("path");
7977
8308
  function requireNonEmpty(value, name) {
7978
8309
  if (typeof value !== "string" || value.length === 0) {
7979
8310
  throw new TypeError(`${name} must be a non-empty string`);
@@ -7990,28 +8321,28 @@ var GitCli = class extends MountCli {
7990
8321
  if (options.readOnly && (options.publish || options.workspace !== void 0) || options.publish && options.workspace !== void 0) {
7991
8322
  throw new TypeError("readOnly, publish, and workspace are mutually exclusive");
7992
8323
  }
7993
- const path3 = (0, import_node_path4.resolve)(localPath);
8324
+ const path4 = (0, import_node_path5.resolve)(localPath);
7994
8325
  const args = ["mount"];
7995
8326
  if (options.readOnly) args.push("--ro");
7996
8327
  if (options.publish) args.push("--publish");
7997
8328
  if (options.workspace !== void 0) args.push(`--workspace=${options.workspace}`);
7998
- args.push("--", target, path3);
8329
+ args.push("--", target, path4);
7999
8330
  await this.run(args);
8000
- return new RepositoryMount(this, target, path3, options.readOnly ?? false);
8331
+ return new RepositoryMount(this, target, path4, options.readOnly ?? false);
8001
8332
  }
8002
8333
  async unmount(localPath, options = {}) {
8003
8334
  requireNonEmpty(localPath, "localPath");
8004
8335
  const args = ["unmount"];
8005
8336
  if (options.deleteWorkspace) args.push("--delete");
8006
8337
  if (options.discard) args.push("--discard");
8007
- args.push("--", (0, import_node_path4.resolve)(localPath));
8338
+ args.push("--", (0, import_node_path5.resolve)(localPath));
8008
8339
  await this.run(args);
8009
8340
  }
8010
8341
  async status(localPath) {
8011
8342
  const args = ["status", "--json"];
8012
8343
  if (localPath !== void 0) {
8013
8344
  requireNonEmpty(localPath, "localPath");
8014
- args.push("--", (0, import_node_path4.resolve)(localPath));
8345
+ args.push("--", (0, import_node_path5.resolve)(localPath));
8015
8346
  }
8016
8347
  const output = await this.run(args, 6e4);
8017
8348
  let raw;
@@ -8026,7 +8357,7 @@ var GitCli = class extends MountCli {
8026
8357
  }
8027
8358
  const stringField = (key) => typeof raw[key] === "string" ? raw[key] : null;
8028
8359
  return {
8029
- path: stringField("mountpoint") ?? (localPath === void 0 ? "" : (0, import_node_path4.resolve)(localPath)),
8360
+ path: stringField("mountpoint") ?? (localPath === void 0 ? "" : (0, import_node_path5.resolve)(localPath)),
8030
8361
  repository: stringField("repository"),
8031
8362
  reference: stringField("reference"),
8032
8363
  workspaceId: stringField("workspace"),
@@ -8035,44 +8366,44 @@ var GitCli = class extends MountCli {
8035
8366
  raw
8036
8367
  };
8037
8368
  }
8038
- async snapshot(path3, message) {
8369
+ async snapshot(path4, message) {
8039
8370
  const args = ["snapshot"];
8040
8371
  if (message !== void 0) args.push(`--message=${message}`);
8041
- args.push("--", path3);
8372
+ args.push("--", path4);
8042
8373
  await this.run(args);
8043
8374
  }
8044
- async sync(path3, target) {
8045
- const args = ["sync", "--", path3];
8375
+ async sync(path4, target) {
8376
+ const args = ["sync", "--", path4];
8046
8377
  if (target !== void 0) {
8047
8378
  requireNonEmpty(target, "target");
8048
8379
  args.push(target);
8049
8380
  }
8050
8381
  await this.run(args);
8051
8382
  }
8052
- async rebase(path3, target, failOnConflict) {
8383
+ async rebase(path4, target, failOnConflict) {
8053
8384
  requireNonEmpty(target, "target");
8054
8385
  const args = ["rebase"];
8055
8386
  if (failOnConflict) args.push("--fail-on-conflict");
8056
- args.push("--", path3, target);
8387
+ args.push("--", path4, target);
8057
8388
  await this.run(args);
8058
8389
  }
8059
- async promote(path3, branch, merge) {
8390
+ async promote(path4, branch, merge) {
8060
8391
  requireNonEmpty(branch, "branch");
8061
8392
  const args = ["promote"];
8062
8393
  if (merge) args.push("--merge");
8063
- args.push("--", path3, branch);
8394
+ args.push("--", path4, branch);
8064
8395
  await this.run(args);
8065
8396
  }
8066
- async prefetch(path3) {
8067
- await this.run(["prefetch", "--", path3]);
8397
+ async prefetch(path4) {
8398
+ await this.run(["prefetch", "--", path4]);
8068
8399
  }
8069
8400
  };
8070
8401
  var RepositoryMount = class {
8071
8402
  /** @internal Obtain instances via RepositoryClient.mount(). */
8072
- constructor(cli, target, path3, readOnly) {
8403
+ constructor(cli, target, path4, readOnly) {
8073
8404
  this.cli = cli;
8074
8405
  this.target = target;
8075
- this.path = path3;
8406
+ this.path = path4;
8076
8407
  this.readOnly = readOnly;
8077
8408
  }
8078
8409
  /** Inspect workspace state, pending changes, and daemon diagnostics. */
@@ -8307,6 +8638,7 @@ function validateTimeoutMs2(timeoutMs) {
8307
8638
 
8308
8639
  // src/index.ts
8309
8640
  init_sandbox_image();
8641
+ init_image_builds();
8310
8642
 
8311
8643
  // src/file-system.ts
8312
8644
  init_cjs_shims();
@@ -9690,38 +10022,38 @@ init_cjs_shims();
9690
10022
  var import_node_crypto4 = require("crypto");
9691
10023
  var textEncoder = new TextEncoder();
9692
10024
  var textDecoder = new TextDecoder("utf-8", { fatal: true });
9693
- function assertJSONValue(value, path3, seen) {
10025
+ function assertJSONValue(value, path4, seen) {
9694
10026
  if (value === null || typeof value === "string" || typeof value === "boolean") return;
9695
10027
  if (typeof value === "number") {
9696
- if (!Number.isFinite(value)) throw new SerializationError(`${path3} contains a non-finite number`);
10028
+ if (!Number.isFinite(value)) throw new SerializationError(`${path4} contains a non-finite number`);
9697
10029
  return;
9698
10030
  }
9699
10031
  if (typeof value !== "object") {
9700
- throw new SerializationError(`${path3} contains unsupported ${typeof value}`);
10032
+ throw new SerializationError(`${path4} contains unsupported ${typeof value}`);
9701
10033
  }
9702
10034
  if (isFile(value)) {
9703
- throw new SerializationError(`${path3} contains a nested File; File is only supported as a direct value`);
10035
+ throw new SerializationError(`${path4} contains a nested File; File is only supported as a direct value`);
9704
10036
  }
9705
10037
  if (isHttpBody(value)) {
9706
10038
  throw new SerializationError(
9707
- `${path3} contains a nested HttpBody; HttpBody is only supported as a direct value`
10039
+ `${path4} contains a nested HttpBody; HttpBody is only supported as a direct value`
9708
10040
  );
9709
10041
  }
9710
- if (seen.has(value)) throw new SerializationError(`${path3} contains a cycle`);
10042
+ if (seen.has(value)) throw new SerializationError(`${path4} contains a cycle`);
9711
10043
  if (Object.getPrototypeOf(value) !== Object.prototype && !Array.isArray(value)) {
9712
- throw new SerializationError(`${path3} contains unsupported ${value.constructor?.name ?? "object"}`);
10044
+ throw new SerializationError(`${path4} contains unsupported ${value.constructor?.name ?? "object"}`);
9713
10045
  }
9714
10046
  seen.add(value);
9715
10047
  if (Array.isArray(value)) {
9716
10048
  for (let index = 0; index < value.length; index += 1) {
9717
10049
  if (!Object.prototype.hasOwnProperty.call(value, index)) {
9718
- throw new SerializationError(`${path3} contains a sparse array entry at index ${index}`);
10050
+ throw new SerializationError(`${path4} contains a sparse array entry at index ${index}`);
9719
10051
  }
9720
- assertJSONValue(value[index], `${path3}[${index}]`, seen);
10052
+ assertJSONValue(value[index], `${path4}[${index}]`, seen);
9721
10053
  }
9722
10054
  } else {
9723
10055
  for (const [key, item] of Object.entries(value)) {
9724
- assertJSONValue(item, `${path3}.${key}`, seen);
10056
+ assertJSONValue(item, `${path4}.${key}`, seen);
9725
10057
  }
9726
10058
  }
9727
10059
  seen.delete(value);
@@ -10171,7 +10503,15 @@ init_models();
10171
10503
  Headers,
10172
10504
  HttpBody,
10173
10505
  Image,
10506
+ ImageBuildCancelledError,
10507
+ ImageBuildError,
10508
+ ImageBuildFailedError,
10509
+ ImageBuildHandle,
10510
+ ImageBuildInProgressError,
10511
+ ImageBuildInterruptedError,
10512
+ ImageBuildNotFoundError,
10174
10513
  ImageBuildOperationType,
10514
+ ImageBuildTimeoutError,
10175
10515
  MountError,
10176
10516
  OutputMode,
10177
10517
  PendingSandbox,
@@ -10204,12 +10544,14 @@ init_models();
10204
10544
  TERMINATION_REASON_CANCELLED,
10205
10545
  TERMINATION_REASON_NO_CAPACITY,
10206
10546
  TcpTunnel,
10547
+ cancelImageBuild,
10207
10548
  createFileSystem,
10208
10549
  createSandboxImage,
10209
10550
  deleteFileSystem,
10210
10551
  deleteSandboxImage,
10211
10552
  dockerfileContent,
10212
10553
  findSandboxImageByName,
10554
+ getImageBuild,
10213
10555
  importSandboxImage,
10214
10556
  isSandboxPending,
10215
10557
  listFileSystems,
@@ -10221,6 +10563,9 @@ init_models();
10221
10563
  runLocal,
10222
10564
  runRemote,
10223
10565
  sandboxUrlFromIngressEndpoint,
10224
- schema
10566
+ schema,
10567
+ startImageBuild,
10568
+ startImageImport,
10569
+ waitImageBuild
10225
10570
  });
10226
10571
  //# sourceMappingURL=index.cjs.map