@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.js CHANGED
@@ -46,7 +46,7 @@ var init_defaults = __esm({
46
46
  "src/defaults.ts"() {
47
47
  "use strict";
48
48
  init_esm_shims();
49
- SDK_VERSION = "0.5.151";
49
+ SDK_VERSION = "0.5.153";
50
50
  API_URL = process.env.TENSORLAKE_API_URL ?? "https://api.tensorlake.ai";
51
51
  API_KEY = process.env.TENSORLAKE_API_KEY ?? void 0;
52
52
  NAMESPACE = process.env.INDEXIFY_NAMESPACE ?? "default";
@@ -313,6 +313,11 @@ function createWorkerBinding(runtime) {
313
313
  return runtime.handle("NativeRepositoryClient", args);
314
314
  }
315
315
  },
316
+ NativeTcpTunnel: class {
317
+ constructor() {
318
+ return runtime.handle("NativeTcpTunnel", []);
319
+ }
320
+ },
316
321
  validateManagedName: (name) => runtime.call(void 0, "validateManagedName", [name])
317
322
  };
318
323
  }
@@ -490,7 +495,7 @@ var init_native_worker_client = __esm({
490
495
  }
491
496
  };
492
497
  runtimeKey = /* @__PURE__ */ Symbol.for(
493
- `tensorlake.native.worker.${false ? "source" : "0.5.151"}.v1`
498
+ `tensorlake.native.worker.${false ? "source" : "0.5.153"}.v1`
494
499
  );
495
500
  runtimes = globalThis;
496
501
  }
@@ -590,6 +595,8 @@ var init_models = __esm({
590
595
  SnapshotStatus2["LOCAL_READY"] = "local_ready";
591
596
  SnapshotStatus2["COMPLETED"] = "completed";
592
597
  SnapshotStatus2["FAILED"] = "failed";
598
+ SnapshotStatus2["DELETING"] = "deleting";
599
+ SnapshotStatus2["REPLICATING"] = "replicating";
593
600
  return SnapshotStatus2;
594
601
  })(SnapshotStatus || {});
595
602
  ProcessStatus = /* @__PURE__ */ ((ProcessStatus2) => {
@@ -862,7 +869,6 @@ var init_pending_sandbox = __esm({
862
869
  });
863
870
 
864
871
  // src/tunnel.ts
865
- import * as net from "net";
866
872
  import WebSocket from "ws";
867
873
  async function openTunnelWebSocket(options) {
868
874
  const wsUrl = buildTunnelWsUrl(options.baseUrl, options.remotePort);
@@ -940,116 +946,6 @@ function normalizeWebSocketData(message) {
940
946
  }
941
947
  return Buffer.from(message);
942
948
  }
943
- async function relaySocket(localSocket, websocket) {
944
- return new Promise((resolve2) => {
945
- let settled = false;
946
- const keepAliveInterval = setInterval(() => {
947
- if (websocket.readyState === WebSocket.OPEN) {
948
- websocket.ping();
949
- }
950
- }, WEBSOCKET_KEEPALIVE_INTERVAL_MS);
951
- keepAliveInterval.unref?.();
952
- const finish = () => {
953
- if (settled) return;
954
- settled = true;
955
- clearInterval(keepAliveInterval);
956
- cleanup();
957
- resolve2();
958
- };
959
- const cleanup = () => {
960
- localSocket.removeListener("data", onLocalData);
961
- localSocket.removeListener("end", onLocalEnd);
962
- localSocket.removeListener("close", onLocalClose);
963
- localSocket.removeListener("error", onLocalError);
964
- websocket.removeListener("message", onWsMessage);
965
- websocket.removeListener("close", onWsClose);
966
- websocket.removeListener("error", onWsError);
967
- websocket.removeListener("ping", onWsPing);
968
- };
969
- const onLocalData = (chunk) => {
970
- if (websocket.readyState !== WebSocket.OPEN) {
971
- localSocket.destroy();
972
- return;
973
- }
974
- websocket.send(chunk, { binary: true }, (error) => {
975
- if (error) {
976
- localSocket.destroy(error);
977
- }
978
- });
979
- };
980
- const onLocalEnd = () => {
981
- if (websocket.readyState === WebSocket.OPEN) {
982
- websocket.close();
983
- }
984
- };
985
- const onLocalClose = () => {
986
- if (websocket.readyState === WebSocket.OPEN || websocket.readyState === WebSocket.CONNECTING) {
987
- websocket.close();
988
- }
989
- finish();
990
- };
991
- const onLocalError = () => {
992
- websocket.close();
993
- finish();
994
- };
995
- const onWsMessage = (message, isBinary) => {
996
- if (!isBinary) {
997
- localSocket.destroy(
998
- new SandboxError("received unexpected text frame from tunnel")
999
- );
1000
- websocket.close();
1001
- return;
1002
- }
1003
- const payload = normalizeWebSocketData(message);
1004
- if (!localSocket.destroyed) {
1005
- localSocket.write(payload);
1006
- }
1007
- };
1008
- const onWsClose = () => {
1009
- if (!localSocket.destroyed) {
1010
- localSocket.end();
1011
- }
1012
- finish();
1013
- };
1014
- const onWsError = (error) => {
1015
- localSocket.destroy(error);
1016
- finish();
1017
- };
1018
- const onWsPing = (data) => {
1019
- if (websocket.readyState === WebSocket.OPEN) {
1020
- websocket.pong(data, false, () => {
1021
- });
1022
- }
1023
- };
1024
- localSocket.on("data", onLocalData);
1025
- localSocket.on("end", onLocalEnd);
1026
- localSocket.on("close", onLocalClose);
1027
- localSocket.on("error", onLocalError);
1028
- websocket.on("message", onWsMessage);
1029
- websocket.on("close", onWsClose);
1030
- websocket.on("error", onWsError);
1031
- websocket.on("ping", onWsPing);
1032
- });
1033
- }
1034
- async function listenServer(server, localPort, localHost) {
1035
- await new Promise((resolve2, reject) => {
1036
- const onError = (error) => {
1037
- server.removeListener("listening", onListening);
1038
- reject(
1039
- new SandboxError(
1040
- `failed to bind local tunnel listener on ${localHost}:${localPort}: ${error.message}`
1041
- )
1042
- );
1043
- };
1044
- const onListening = () => {
1045
- server.removeListener("error", onError);
1046
- resolve2();
1047
- };
1048
- server.once("error", onError);
1049
- server.once("listening", onListening);
1050
- server.listen(localPort, localHost);
1051
- });
1052
- }
1053
949
  function validatePort(port, label, allowZero = false) {
1054
950
  if (!Number.isInteger(port)) {
1055
951
  throw new SandboxError(`${label} must be an integer, got ${port}`);
@@ -1088,6 +984,8 @@ var init_tunnel = __esm({
1088
984
  "use strict";
1089
985
  init_esm_shims();
1090
986
  init_errors();
987
+ init_native_sandbox();
988
+ init_native_worker_client();
1091
989
  DEFAULT_TUNNEL_CONNECT_TIMEOUT_MS = 1e4;
1092
990
  WEBSOCKET_KEEPALIVE_INTERVAL_MS = 15e3;
1093
991
  TunnelByteStream = class _TunnelByteStream {
@@ -1217,24 +1115,17 @@ var init_tunnel = __esm({
1217
1115
  }
1218
1116
  };
1219
1117
  TcpTunnel = class _TcpTunnel {
1220
- remotePort;
1221
- localHost;
1222
- localPort;
1223
- baseUrl;
1224
- wsHeaders;
1225
- server;
1226
- connectTimeoutMs;
1227
- activeRelays = /* @__PURE__ */ new Set();
1228
- closePromise = null;
1229
- constructor(options) {
1230
- this.baseUrl = options.baseUrl;
1231
- this.wsHeaders = options.wsHeaders;
1232
- this.remotePort = options.remotePort;
1233
- this.localHost = options.localHost;
1234
- this.localPort = options.localPort;
1235
- this.server = options.server;
1236
- this.connectTimeoutMs = options.connectTimeoutMs;
1118
+ constructor(native, remotePort, localHost, localPort) {
1119
+ this.native = native;
1120
+ this.remotePort = remotePort;
1121
+ this.localHost = localHost;
1122
+ this.localPort = localPort;
1123
+ this.lifetime = native.waitClosed();
1124
+ void this.lifetime.catch(() => {
1125
+ });
1237
1126
  }
1127
+ closePromise = null;
1128
+ lifetime;
1238
1129
  static async listen(options) {
1239
1130
  const remotePort = validatePort(options.remotePort, "remote port");
1240
1131
  const localHost = options.localHost ?? "127.0.0.1";
@@ -1243,69 +1134,37 @@ var init_tunnel = __esm({
1243
1134
  "local port",
1244
1135
  true
1245
1136
  );
1246
- const connectTimeoutMs = secondsToMillis(options.connectTimeout ?? 10);
1247
- const server = net.createServer();
1248
- await listenServer(server, localPort, localHost);
1249
- const address = server.address();
1250
- if (!address || typeof address === "string") {
1251
- server.close();
1252
- throw new SandboxError("failed to determine bound tunnel address");
1253
- }
1254
- const tunnel = new _TcpTunnel({
1255
- baseUrl: options.baseUrl,
1256
- wsHeaders: options.wsHeaders,
1257
- remotePort,
1258
- localHost,
1259
- localPort: address.port,
1260
- server,
1261
- connectTimeoutMs
1262
- });
1263
- server.on("connection", (localSocket) => {
1264
- void tunnel.handleConnection(localSocket);
1265
- });
1266
- return tunnel;
1137
+ const timeout = secondsToMillis(options.connectTimeout ?? 10) / 1e3;
1138
+ const native = new (loadNativeSandboxBinding()).NativeTcpTunnel();
1139
+ try {
1140
+ const port = await callNative(
1141
+ () => native.listen(
1142
+ options.baseUrl,
1143
+ options.wsHeaders,
1144
+ remotePort,
1145
+ localHost,
1146
+ localPort,
1147
+ timeout
1148
+ )
1149
+ );
1150
+ return new _TcpTunnel(native, remotePort, localHost, port);
1151
+ } catch (error) {
1152
+ releaseNativeHandle(native);
1153
+ throw error;
1154
+ }
1267
1155
  }
1268
1156
  address() {
1269
1157
  return { host: this.localHost, port: this.localPort };
1270
1158
  }
1271
1159
  async close() {
1272
- if (this.closePromise) {
1273
- return this.closePromise;
1274
- }
1275
- for (const relay of this.activeRelays) {
1276
- relay.localSocket.destroy();
1277
- relay.websocket?.close();
1278
- }
1279
- this.closePromise = new Promise((resolve2, reject) => {
1280
- this.server.close((error) => {
1281
- if (error) {
1282
- reject(new SandboxError(`failed to close tunnel listener: ${error.message}`));
1283
- return;
1284
- }
1285
- resolve2();
1286
- });
1287
- });
1288
- return this.closePromise;
1289
- }
1290
- async handleConnection(localSocket) {
1291
- localSocket.setNoDelay(true);
1292
- const relay = { localSocket, websocket: null };
1293
- this.activeRelays.add(relay);
1294
- try {
1295
- relay.websocket = await openTunnelWebSocket({
1296
- baseUrl: this.baseUrl,
1297
- wsHeaders: this.wsHeaders,
1298
- remotePort: this.remotePort,
1299
- connectTimeoutMs: this.connectTimeoutMs
1300
- });
1301
- await relaySocket(localSocket, relay.websocket);
1302
- } catch (error) {
1303
- localSocket.destroy(
1304
- error instanceof Error ? error : new Error(String(error))
1305
- );
1306
- } finally {
1307
- this.activeRelays.delete(relay);
1308
- }
1160
+ return this.closePromise ??= (async () => {
1161
+ try {
1162
+ await callNative(() => this.native.close());
1163
+ await this.lifetime;
1164
+ } finally {
1165
+ releaseNativeHandle(this.native);
1166
+ }
1167
+ })();
1309
1168
  }
1310
1169
  };
1311
1170
  }
@@ -4026,9 +3885,8 @@ var init_sandbox = __esm({
4026
3885
  *
4027
3886
  * Resource targets use create's cpus, memoryMb, and diskMb names. Resize
4028
3887
  * waits by default; wait=false returns admission. Timeout does not cancel it.
4029
- * CPU-only CAS sandboxes support CPU, memory, and root-disk resize; GPU CAS sandboxes
4030
- * support only root-disk growth and reject CPU and memory changes.
4031
- * Non-CAS sandboxes do not support live resize.
3888
+ * Which runtimes and dimensions support a live resize is decided by the
3889
+ * server, whose policy error is thrown unchanged.
4032
3890
  * Wait, timeout, and pollInterval are ignored on non-resource updates.
4033
3891
  * A no-op returns current resources with absent or earlier resize metadata.
4034
3892
  *
@@ -4446,29 +4304,29 @@ var init_sandbox = __esm({
4446
4304
  }
4447
4305
  // --- File operations ---
4448
4306
  /** Read a file from the sandbox and return its raw bytes. */
4449
- async readFile(path4) {
4307
+ async readFile(path5) {
4450
4308
  const proxy = await this.proxy.client();
4451
- const { traceId, data } = await callNative(() => proxy.readFile(path4), {
4309
+ const { traceId, data } = await callNative(() => proxy.readFile(path5), {
4452
4310
  sandboxId: this.sandboxId
4453
4311
  });
4454
4312
  return Object.assign(Uint8Array.from(data), { traceId });
4455
4313
  }
4456
4314
  /** Write raw bytes to a file in the sandbox, creating it if it does not exist. */
4457
- async writeFile(path4, content) {
4315
+ async writeFile(path5, content) {
4458
4316
  const proxy = await this.proxy.client();
4459
- await callNative(() => proxy.writeFile(path4, Buffer.from(content)), {
4317
+ await callNative(() => proxy.writeFile(path5, Buffer.from(content)), {
4460
4318
  sandboxId: this.sandboxId
4461
4319
  });
4462
4320
  }
4463
4321
  /** Delete a file from the sandbox. */
4464
- async deleteFile(path4) {
4322
+ async deleteFile(path5) {
4465
4323
  const proxy = await this.proxy.client();
4466
- await callNative(() => proxy.deleteFile(path4), { sandboxId: this.sandboxId });
4324
+ await callNative(() => proxy.deleteFile(path5), { sandboxId: this.sandboxId });
4467
4325
  }
4468
4326
  /** List the contents of a directory in the sandbox. */
4469
- async listDirectory(path4) {
4327
+ async listDirectory(path5) {
4470
4328
  const proxy = await this.proxy.client();
4471
- const { traceId, json } = await callNative(() => proxy.listDirectory(path4), {
4329
+ const { traceId, json } = await callNative(() => proxy.listDirectory(path5), {
4472
4330
  sandboxId: this.sandboxId
4473
4331
  });
4474
4332
  return Object.assign(fromSnakeKeys(JSON.parse(json)), {
@@ -5134,9 +4992,9 @@ var init_client = __esm({
5134
4992
  });
5135
4993
  }
5136
4994
  /**
5137
- * Update properties or resize a running sandbox. CPU-only CAS sandboxes support
5138
- * CPU, memory, and root-disk resize; GPU CAS sandboxes support only root-disk
5139
- * growth. Non-CAS sandboxes do not support live resize.
4995
+ * Update properties or resize a running sandbox. Which runtimes and
4996
+ * dimensions support a live resize is decided by the server, whose policy
4997
+ * error is thrown unchanged.
5140
4998
  * Resource names match create. Resize waits by default; wait=false returns
5141
4999
  * admission. Timeout leaves the resize running and reports its generation.
5142
5000
  * Wait, timeout, and pollInterval are ignored on non-resource updates.
@@ -5455,6 +5313,11 @@ var init_client = __esm({
5455
5313
  `Snapshot ${result.snapshotId} failed: ${info.error}`
5456
5314
  );
5457
5315
  }
5316
+ if (info.status === "deleting" /* DELETING */) {
5317
+ throw new SandboxError(
5318
+ `Snapshot ${result.snapshotId} is being deleted`
5319
+ );
5320
+ }
5458
5321
  await sleep2(pollInterval * 1e3);
5459
5322
  }
5460
5323
  throw new SandboxError(
@@ -5712,705 +5575,126 @@ var init_client = __esm({
5712
5575
  }
5713
5576
  });
5714
5577
 
5715
- // src/native-cloud.ts
5716
- async function serializeRequest(method, path4, options) {
5717
- const headers = { ...options?.headers };
5718
- let body = options?.body;
5719
- if (options?.json !== void 0) {
5720
- body = JSON.stringify(options.json);
5721
- if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) {
5722
- headers["Content-Type"] = "application/json";
5723
- }
5724
- }
5725
- const request = {
5726
- method,
5727
- path: path4,
5728
- headersJson: JSON.stringify(headers),
5729
- statusOnlyCodes: [...options?.statusOnlyCodes ?? []]
5578
+ // src/image.ts
5579
+ import { randomUUID } from "crypto";
5580
+ function cloneOperation(op) {
5581
+ return {
5582
+ type: op.type,
5583
+ args: [...op.args],
5584
+ options: { ...op.options }
5730
5585
  };
5731
- if (body instanceof FormData) {
5732
- request.parts = [];
5733
- for (const [name, value] of body.entries()) {
5734
- 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" });
5735
- }
5736
- } else if (body !== void 0) {
5737
- if (body instanceof Blob) request.body = Buffer.from(await body.arrayBuffer());
5738
- else if (typeof body === "string") request.body = Buffer.from(body);
5739
- else if (body instanceof ArrayBuffer) request.body = Buffer.from(new Uint8Array(body));
5740
- else request.body = Buffer.from(body);
5586
+ }
5587
+ function renderOptions(options) {
5588
+ const entries = Object.entries(options);
5589
+ if (entries.length === 0) {
5590
+ return "";
5741
5591
  }
5742
- return request;
5592
+ return ` ${entries.map(([key, value]) => `--${key}=${value}`).join(" ")}`;
5593
+ }
5594
+ function renderBuildOp(op) {
5595
+ const options = renderOptions(op.options);
5596
+ if (op.type === ImageBuildOperationType.ENV) {
5597
+ return `ENV${options} ${op.args[0]}=${JSON.stringify(op.args[1])}`;
5598
+ }
5599
+ return `${op.type}${options} ${op.args.join(" ")}`;
5600
+ }
5601
+ function dockerfileContent(image) {
5602
+ const lines = image.baseImage == null ? [] : [`FROM ${image.baseImage}`];
5603
+ lines.push(...image.buildOperations.map((op) => renderBuildOp(op)));
5604
+ return lines.join("\n");
5743
5605
  }
5744
- var NativeCloudTransport;
5745
- var init_native_cloud = __esm({
5746
- "src/native-cloud.ts"() {
5606
+ var DEFAULT_IMAGE_NAME, ImageBuildOperationType, Image;
5607
+ var init_image = __esm({
5608
+ "src/image.ts"() {
5747
5609
  "use strict";
5748
5610
  init_esm_shims();
5749
- init_native_sandbox();
5750
- init_native_worker_client();
5751
- NativeCloudTransport = class {
5752
- constructor(options) {
5753
- this.options = options;
5754
- if (!Number.isSafeInteger(options.maxRetries) || options.maxRetries < 0 || options.maxRetries > 4294967295) {
5755
- throw new RangeError("maxRetries must be a non-negative 32-bit integer");
5756
- }
5757
- if (!Number.isFinite(options.retryBackoffMs) || options.retryBackoffMs < 0) {
5758
- throw new RangeError("retryBackoffMs must be a non-negative finite number");
5611
+ DEFAULT_IMAGE_NAME = "default";
5612
+ ImageBuildOperationType = {
5613
+ ADD: "ADD",
5614
+ COPY: "COPY",
5615
+ ENV: "ENV",
5616
+ RUN: "RUN",
5617
+ WORKDIR: "WORKDIR"
5618
+ };
5619
+ Image = class {
5620
+ _id;
5621
+ _name;
5622
+ _tag;
5623
+ _baseImage;
5624
+ _buildOperations;
5625
+ constructor(nameOrOptions = {}, tag = "latest", baseImage = null) {
5626
+ this._id = randomUUID();
5627
+ this._buildOperations = [];
5628
+ if (typeof nameOrOptions === "string") {
5629
+ this._name = nameOrOptions;
5630
+ this._tag = tag;
5631
+ this._baseImage = baseImage;
5632
+ return;
5759
5633
  }
5634
+ this._name = nameOrOptions.name ?? DEFAULT_IMAGE_NAME;
5635
+ this._tag = nameOrOptions.tag ?? "latest";
5636
+ this._baseImage = nameOrOptions.baseImage ?? null;
5760
5637
  }
5761
- native;
5762
- closed = false;
5763
- client() {
5764
- if (this.closed) throw new Error("Tensorlake native client is closed");
5765
- if (this.native) return this.native;
5766
- const ctor = loadNativeSandboxBinding().NativeCloudClient;
5767
- if (!ctor) throw new Error("Native binding does not export NativeCloudClient; rebuild with 'npm run build:native'");
5768
- return this.native = new ctor(JSON.stringify(this.options));
5638
+ get name() {
5639
+ return this._name;
5769
5640
  }
5770
- close() {
5771
- this.closed = true;
5772
- releaseNativeHandle(this.native ?? null);
5773
- this.native = void 0;
5774
- }
5775
- async requestResponse(method, path4, options) {
5776
- const request = await serializeRequest(method, path4, options);
5777
- const raw = await callNative(() => this.client().request(request));
5778
- const response = new Response(
5779
- [204, 205, 304].includes(raw.status) ? null : Uint8Array.from(raw.data).buffer,
5780
- { status: raw.status, headers: JSON.parse(raw.headersJson) }
5781
- );
5782
- return Object.assign(response, { traceId: raw.traceId });
5641
+ get tag() {
5642
+ return this._tag;
5643
+ }
5644
+ get baseImage() {
5645
+ return this._baseImage;
5646
+ }
5647
+ get buildOperations() {
5648
+ return this._buildOperations.map(cloneOperation);
5649
+ }
5650
+ add(src, dest, options = void 0) {
5651
+ return this._addOperation({
5652
+ type: ImageBuildOperationType.ADD,
5653
+ args: [src, dest],
5654
+ options: options == null ? {} : { ...options }
5655
+ });
5656
+ }
5657
+ copy(src, dest, options = void 0) {
5658
+ return this._addOperation({
5659
+ type: ImageBuildOperationType.COPY,
5660
+ args: [src, dest],
5661
+ options: options == null ? {} : { ...options }
5662
+ });
5663
+ }
5664
+ env(key, value) {
5665
+ return this._addOperation({
5666
+ type: ImageBuildOperationType.ENV,
5667
+ args: [key, value],
5668
+ options: {}
5669
+ });
5670
+ }
5671
+ run(commands, options = void 0) {
5672
+ return this._addOperation({
5673
+ type: ImageBuildOperationType.RUN,
5674
+ args: Array.isArray(commands) ? [...commands] : [commands],
5675
+ options: options == null ? {} : { ...options }
5676
+ });
5677
+ }
5678
+ workdir(directory) {
5679
+ return this._addOperation({
5680
+ type: ImageBuildOperationType.WORKDIR,
5681
+ args: [directory],
5682
+ options: {}
5683
+ });
5783
5684
  }
5784
- async requestJson(method, path4) {
5785
- const response = await this.requestResponse(method, path4);
5786
- const text = await response.text();
5787
- const value = text ? JSON.parse(text) : null;
5788
- return Object.assign(value ?? {}, { traceId: response.traceId });
5685
+ /**
5686
+ * Build this image as a sandbox template and register it.
5687
+ *
5688
+ * Materializes the image in a build sandbox, snapshots the filesystem,
5689
+ * and registers the snapshot as a named sandbox template.
5690
+ */
5691
+ async build(options = {}) {
5692
+ const { createSandboxImage: createSandboxImage2 } = await Promise.resolve().then(() => (init_sandbox_image(), sandbox_image_exports));
5693
+ return createSandboxImage2(this, options);
5789
5694
  }
5790
- async *stream(method, path4, signal) {
5791
- if (signal?.aborted) return;
5792
- const request = await serializeRequest(method, path4, { headers: { Accept: "text/event-stream" } });
5793
- yield* nativeEventStream((emit) => this.client().stream(request, emit), void 0, signal);
5794
- }
5795
- };
5796
- }
5797
- });
5798
-
5799
- // src/cloud-client.ts
5800
- import { nanoid } from "nanoid";
5801
- function secretFromMetadata(metadata) {
5802
- return {
5803
- id: metadata.id,
5804
- name: metadata.name,
5805
- createdAt: new Date(metadata.created_at_ms)
5806
- };
5807
- }
5808
- function applicationAllowsUnauthenticatedRequests(manifest) {
5809
- return Array.isArray(manifest.allow) && manifest.allow.includes(UNAUTHENTICATED_REQUESTS);
5810
- }
5811
- function publicEndpointIdFromManifest(manifest) {
5812
- for (const key of ["public_endpoint_id", "publicEndpointId"]) {
5813
- const value = manifest[key];
5814
- if (typeof value === "string" && value.length > 0) {
5815
- return value;
5816
- }
5817
- }
5818
- return void 0;
5819
- }
5820
- function generatePublicEndpointId() {
5821
- return `endpoint_${nanoid()}`;
5822
- }
5823
- function createApplicationBuildForm(request, imageContexts) {
5824
- const contextsByPartName = /* @__PURE__ */ new Map();
5825
- for (const context of imageContexts) {
5826
- if (contextsByPartName.has(context.contextTarPartName)) {
5827
- throw new Error(
5828
- `duplicate image context part name '${context.contextTarPartName}'`
5829
- );
5830
- }
5831
- contextsByPartName.set(context.contextTarPartName, context);
5832
- }
5833
- const form = new FormData();
5834
- form.append(
5835
- "app_version",
5836
- new Blob([JSON.stringify(request)], { type: "application/json" }),
5837
- "app_version"
5838
- );
5839
- for (const image of request.images) {
5840
- const context = contextsByPartName.get(image.contextTarPartName);
5841
- if (!context) {
5842
- throw new Error(
5843
- `missing image context for part '${image.contextTarPartName}'`
5844
- );
5845
- }
5846
- form.append(
5847
- image.contextTarPartName,
5848
- new Blob([toBlobPart(context.contextTarGz)]),
5849
- `${image.contextTarPartName}.tar.gz`
5850
- );
5851
- }
5852
- for (const context of imageContexts) {
5853
- if (!request.images.some((image) => image.contextTarPartName === context.contextTarPartName)) {
5854
- throw new Error(
5855
- `unexpected image context for part '${context.contextTarPartName}'`
5856
- );
5857
- }
5858
- }
5859
- return form;
5860
- }
5861
- function trimTrailingSlashes2(value) {
5862
- return value.endsWith("/") ? value.slice(0, -1) : value;
5863
- }
5864
- function nextRequestPath(next) {
5865
- const schemeIndex = next.indexOf("://");
5866
- if (schemeIndex !== -1) {
5867
- const afterScheme = next.slice(schemeIndex + 3);
5868
- const slashIndex = afterScheme.indexOf("/");
5869
- return slashIndex === -1 ? "/" : afterScheme.slice(slashIndex);
5870
- }
5871
- return next.startsWith("/") ? next : `/${next}`;
5872
- }
5873
- function toBlobPart(data) {
5874
- if (typeof data === "string" || data instanceof Blob) {
5875
- return data;
5876
- }
5877
- if (data instanceof Uint8Array) {
5878
- return Uint8Array.from(data).buffer;
5879
- }
5880
- return data;
5881
- }
5882
- function toRequestBody(data) {
5883
- return toBlobPart(data);
5884
- }
5885
- async function parseJsonResponse(response) {
5886
- const text = await response.text();
5887
- if (!text) {
5888
- return void 0;
5889
- }
5890
- return JSON.parse(text);
5891
- }
5892
- var CloudClient, UNAUTHENTICATED_REQUESTS;
5893
- var init_cloud_client = __esm({
5894
- "src/cloud-client.ts"() {
5895
- "use strict";
5896
- init_esm_shims();
5897
- init_defaults();
5898
- init_native_cloud();
5899
- init_models();
5900
- CloudClient = class _CloudClient {
5901
- transport;
5902
- organizationId;
5903
- projectId;
5904
- namespace;
5905
- constructor(options) {
5906
- this.organizationId = options?.organizationId;
5907
- this.projectId = options?.projectId;
5908
- this.namespace = options?.namespace ?? NAMESPACE;
5909
- this.transport = new NativeCloudTransport({
5910
- baseUrl: options?.apiUrl ?? API_URL,
5911
- apiKey: options?.apiKey ?? API_KEY,
5912
- organizationId: this.organizationId,
5913
- projectId: this.projectId,
5914
- maxRetries: options?.maxRetries ?? MAX_RETRIES,
5915
- retryBackoffMs: options?.retryBackoffMs ?? RETRY_BACKOFF_MS,
5916
- timeoutMs: DEFAULT_HTTP_TIMEOUT_MS,
5917
- userAgent: `tensorlake-typescript-sdk/${SDK_VERSION}`
5918
- });
5919
- }
5920
- static forCloud(options) {
5921
- return new _CloudClient(options);
5922
- }
5923
- close() {
5924
- this.transport.close();
5925
- }
5926
- async upsertApplication(manifest, codeZip, upgradeRunningRequests = false) {
5927
- const applicationManifest = { ...manifest };
5928
- let publicEndpointId = publicEndpointIdFromManifest(applicationManifest);
5929
- if (applicationAllowsUnauthenticatedRequests(applicationManifest) && publicEndpointId === void 0) {
5930
- const applicationName = applicationManifest.name;
5931
- if (typeof applicationName === "string" && applicationName.length > 0) {
5932
- publicEndpointId = await this.existingApplicationPublicEndpointId(applicationName);
5933
- }
5934
- publicEndpointId ??= generatePublicEndpointId();
5935
- }
5936
- if (publicEndpointId !== void 0) {
5937
- applicationManifest.public_endpoint_id = publicEndpointId;
5938
- delete applicationManifest.publicEndpointId;
5939
- }
5940
- const form = new FormData();
5941
- form.append(
5942
- "code",
5943
- new Blob([toBlobPart(codeZip)], { type: "application/zip" }),
5944
- "code.zip"
5945
- );
5946
- form.append("code_content_type", "application/zip");
5947
- form.append("application", JSON.stringify(applicationManifest));
5948
- form.append(
5949
- "upgrade_requests_to_latest_code",
5950
- String(upgradeRunningRequests)
5951
- );
5952
- await this.transport.requestResponse("POST", this.namespacePath("applications"), {
5953
- body: form
5954
- });
5955
- }
5956
- async deleteApplication(applicationName) {
5957
- await this.transport.requestResponse(
5958
- "DELETE",
5959
- this.namespacePath(`applications/${encodeURIComponent(applicationName)}`)
5960
- );
5961
- }
5962
- async deleteSandboxImage(imageName) {
5963
- await this.transport.requestResponse(
5964
- "DELETE",
5965
- this.namespacePath(`sandbox-images/${encodeURIComponent(imageName)}`)
5966
- );
5967
- }
5968
- /**
5969
- * Look up a registered sandbox image (template) by its registered name.
5970
- *
5971
- * Returns the template, or `null` when no image with that name exists.
5972
- * API-key clients may omit organization/project IDs and use the
5973
- * token-scoped Platform API route directly. Explicit scope remains
5974
- * available for PAT callers.
5975
- */
5976
- async findSandboxImageByName(imageName, options) {
5977
- const base = this.platformProjectPath(
5978
- "sandbox-templates",
5979
- options?.organizationId,
5980
- options?.projectId
5981
- );
5982
- const response = await this.transport.requestResponse(
5983
- "GET",
5984
- `${base}/by-name/${encodeURIComponent(imageName)}`,
5985
- { statusOnlyCodes: /* @__PURE__ */ new Set([404]) }
5986
- );
5987
- if (response.status === 404) {
5988
- return null;
5989
- }
5990
- const raw = await parseJsonResponse(response);
5991
- return fromSnakeKeys(raw);
5992
- }
5993
- /**
5994
- * List all registered sandbox images (templates), following pagination to
5995
- * the end. API-key clients may omit organization/project IDs and use the
5996
- * token-scoped Platform API route directly.
5997
- */
5998
- async listSandboxImages(options) {
5999
- const base = this.platformProjectPath(
6000
- "sandbox-templates",
6001
- options?.organizationId,
6002
- options?.projectId
6003
- );
6004
- let path4 = `${base}?pageSize=100`;
6005
- const templates = [];
6006
- while (path4 !== null) {
6007
- const page = await this.transport.requestJson(
6008
- "GET",
6009
- path4
6010
- );
6011
- for (const item of page.items ?? []) {
6012
- templates.push(fromSnakeKeys(item));
6013
- }
6014
- const next = page.pagination?.next;
6015
- path4 = next ? nextRequestPath(next) : null;
6016
- }
6017
- return templates;
6018
- }
6019
- async applications() {
6020
- const raw = await this.transport.requestJson(
6021
- "GET",
6022
- this.namespacePath("applications")
6023
- );
6024
- return (raw.applications ?? []).map(
6025
- (application) => fromSnakeKeys(application)
6026
- );
6027
- }
6028
- async applicationManifest(applicationName) {
6029
- const raw = await this.transport.requestJson(
6030
- "GET",
6031
- this.namespacePath(`applications/${encodeURIComponent(applicationName)}`)
6032
- );
6033
- return fromSnakeKeys(raw);
6034
- }
6035
- async runRequest(applicationName, inputs = []) {
6036
- const path4 = this.namespacePath(
6037
- `applications/${encodeURIComponent(applicationName)}`
6038
- );
6039
- const response = inputs.length === 0 ? await this.transport.requestResponse("POST", path4, {
6040
- body: new Uint8Array(),
6041
- headers: { Accept: "application/json" }
6042
- }) : inputs.length === 1 && inputs[0].name === "0" ? await this.transport.requestResponse("POST", path4, {
6043
- body: toRequestBody(inputs[0].data),
6044
- headers: {
6045
- Accept: "application/json",
6046
- "Content-Type": inputs[0].contentType
6047
- }
6048
- }) : await this.runMultipartRequest(path4, inputs);
6049
- const body = await parseJsonResponse(response);
6050
- const requestId = body?.request_id;
6051
- if (!requestId) {
6052
- throw new Error("missing request_id in run request response body");
6053
- }
6054
- return requestId;
6055
- }
6056
- async waitOnRequestCompletion(applicationName, requestId) {
6057
- const stream = this.transport.stream(
6058
- "GET",
6059
- this.namespacePath(
6060
- `applications/${encodeURIComponent(applicationName)}/requests/${encodeURIComponent(requestId)}/progress`
6061
- )
6062
- );
6063
- for await (const event of stream) {
6064
- if (Object.prototype.hasOwnProperty.call(event, "RequestFinished")) {
6065
- return;
6066
- }
6067
- }
6068
- throw new Error("progress stream ended before request completion");
6069
- }
6070
- async requestMetadata(applicationName, requestId) {
6071
- const raw = await this.transport.requestJson(
6072
- "GET",
6073
- this.namespacePath(
6074
- `applications/${encodeURIComponent(applicationName)}/requests/${encodeURIComponent(requestId)}`
6075
- )
6076
- );
6077
- return fromSnakeKeys(raw);
6078
- }
6079
- async requestOutput(applicationName, requestId) {
6080
- const response = await this.transport.requestResponse(
6081
- "GET",
6082
- this.namespacePath(
6083
- `applications/${encodeURIComponent(applicationName)}/requests/${encodeURIComponent(requestId)}/output`
6084
- )
6085
- );
6086
- const serializedValue = new Uint8Array(await response.arrayBuffer());
6087
- const contentType = response.headers.get("content-type") ?? "";
6088
- return {
6089
- serializedValue,
6090
- contentType
6091
- };
6092
- }
6093
- async listSecrets(options) {
6094
- const base = this.secretCollectionPath(
6095
- options?.organizationId,
6096
- options?.projectId
6097
- );
6098
- const raw = await this.transport.requestJson("GET", base);
6099
- const pageSize = Math.max(0, options?.pageSize ?? 100);
6100
- return Object.assign(
6101
- {
6102
- items: raw.slice(0, pageSize).map(secretFromMetadata),
6103
- pagination: { total: raw.length }
6104
- },
6105
- { traceId: raw.traceId }
6106
- );
6107
- }
6108
- async getSecret(secretId, options) {
6109
- const base = this.secretCollectionPath(
6110
- options?.organizationId,
6111
- options?.projectId
6112
- );
6113
- const raw = await this.transport.requestJson(
6114
- "GET",
6115
- `${base}/${encodeURIComponent(secretId)}`
6116
- );
6117
- return Object.assign(secretFromMetadata(raw), { traceId: raw.traceId });
6118
- }
6119
- async upsertSecrets(secrets, options) {
6120
- const base = this.secretCollectionPath(
6121
- options?.organizationId,
6122
- options?.projectId
6123
- );
6124
- if (Array.isArray(secrets)) {
6125
- const results = [];
6126
- for (const secret of secrets) {
6127
- results.push(await this.upsertSecret(base, secret));
6128
- }
6129
- return results;
6130
- }
6131
- return this.upsertSecret(base, secrets);
6132
- }
6133
- async deleteSecret(secretId, options) {
6134
- const base = this.secretCollectionPath(
6135
- options?.organizationId,
6136
- options?.projectId
6137
- );
6138
- await this.transport.requestResponse(
6139
- "DELETE",
6140
- `${base}/${encodeURIComponent(secretId)}`
6141
- );
6142
- }
6143
- async upsertSecret(base, secret) {
6144
- let response = await this.transport.requestResponse("POST", base, {
6145
- json: secret,
6146
- headers: { "Idempotency-Key": nanoid() },
6147
- statusOnlyCodes: /* @__PURE__ */ new Set([409])
6148
- });
6149
- if (response.status === 409) {
6150
- const prefix = base.slice(0, -"/secrets".length);
6151
- const existing = await this.transport.requestJson(
6152
- "GET",
6153
- `${prefix}/secret-names/${encodeURIComponent(secret.name)}`
6154
- );
6155
- response = await this.transport.requestResponse(
6156
- "POST",
6157
- `${base}/${encodeURIComponent(existing.id)}/versions`,
6158
- {
6159
- json: { value: secret.value },
6160
- headers: { "Idempotency-Key": nanoid() }
6161
- }
6162
- );
6163
- }
6164
- const metadata = await parseJsonResponse(response);
6165
- return Object.assign(secretFromMetadata(metadata), {
6166
- traceId: response.traceId
6167
- });
6168
- }
6169
- async startImageBuild(buildServicePath, request) {
6170
- const form = new FormData();
6171
- form.append("graph_name", request.applicationName);
6172
- form.append("graph_version", request.applicationVersion);
6173
- form.append("graph_function_name", request.functionName);
6174
- form.append("image_name", request.imageName);
6175
- form.append("image_id", request.imageId);
6176
- form.append(
6177
- "context",
6178
- new Blob([toBlobPart(request.buildContext)]),
6179
- "context.tar.gz"
6180
- );
6181
- const response = await this.transport.requestResponse(
6182
- "PUT",
6183
- `${trimTrailingSlashes2(buildServicePath)}/builds`,
6184
- { body: form }
6185
- );
6186
- const raw = await parseJsonResponse(response);
6187
- return fromSnakeKeys(raw);
6188
- }
6189
- async createApplicationBuild(buildServicePath, request, imageContexts) {
6190
- const form = createApplicationBuildForm(request, imageContexts);
6191
- const response = await this.transport.requestResponse(
6192
- "POST",
6193
- trimTrailingSlashes2(buildServicePath),
6194
- { body: form }
6195
- );
6196
- const raw = await parseJsonResponse(response);
6197
- return fromSnakeKeys(raw);
6198
- }
6199
- async applicationBuildInfo(buildServicePath, applicationBuildId) {
6200
- const raw = await this.transport.requestJson(
6201
- "GET",
6202
- `${trimTrailingSlashes2(buildServicePath)}/${encodeURIComponent(applicationBuildId)}`
6203
- );
6204
- return fromSnakeKeys(raw);
6205
- }
6206
- async cancelApplicationBuild(buildServicePath, applicationBuildId) {
6207
- const raw = await this.transport.requestJson(
6208
- "POST",
6209
- `${trimTrailingSlashes2(buildServicePath)}/${encodeURIComponent(applicationBuildId)}/cancel`
6210
- );
6211
- return fromSnakeKeys(raw);
6212
- }
6213
- async buildInfo(buildServicePath, buildId) {
6214
- const raw = await this.transport.requestJson(
6215
- "GET",
6216
- `${trimTrailingSlashes2(buildServicePath)}/builds/${encodeURIComponent(buildId)}`
6217
- );
6218
- return fromSnakeKeys(raw);
6219
- }
6220
- async cancelBuild(buildServicePath, buildId) {
6221
- await this.transport.requestResponse(
6222
- "POST",
6223
- `${trimTrailingSlashes2(buildServicePath)}/builds/${encodeURIComponent(buildId)}/cancel`
6224
- );
6225
- }
6226
- async *streamBuildLogs(buildServicePath, buildId, signal) {
6227
- const stream = this.transport.stream(
6228
- "GET",
6229
- `${trimTrailingSlashes2(buildServicePath)}/builds/${encodeURIComponent(buildId)}/logs`,
6230
- signal
6231
- );
6232
- for await (const event of stream) {
6233
- yield fromSnakeKeys(event);
6234
- }
6235
- }
6236
- async runMultipartRequest(path4, inputs) {
6237
- const form = new FormData();
6238
- for (const input of inputs) {
6239
- form.append(
6240
- input.name,
6241
- new Blob([toBlobPart(input.data)], { type: input.contentType }),
6242
- input.name
6243
- );
6244
- }
6245
- return this.transport.requestResponse("POST", path4, {
6246
- body: form,
6247
- headers: { Accept: "application/json" }
6248
- });
6249
- }
6250
- namespacePath(subpath) {
6251
- return `/v1/namespaces/${encodeURIComponent(this.namespace)}/${subpath.replace(/^\/+/, "")}`;
6252
- }
6253
- async existingApplicationPublicEndpointId(applicationName) {
6254
- const response = await this.transport.requestResponse(
6255
- "GET",
6256
- this.namespacePath(`applications/${encodeURIComponent(applicationName)}`),
6257
- { statusOnlyCodes: /* @__PURE__ */ new Set([404]) }
6258
- );
6259
- if (response.status === 404) {
6260
- return void 0;
6261
- }
6262
- const application = await parseJsonResponse(response);
6263
- return publicEndpointIdFromManifest(application);
6264
- }
6265
- platformProjectPath(resource, organizationId, projectId) {
6266
- const resolvedOrganizationId = organizationId ?? this.organizationId;
6267
- const resolvedProjectId = projectId ?? this.projectId;
6268
- if (!resolvedOrganizationId && !resolvedProjectId) {
6269
- return `/platform/v1/${resource}`;
6270
- }
6271
- if (!resolvedOrganizationId || !resolvedProjectId) {
6272
- throw new Error(
6273
- "organizationId and projectId must be provided together"
6274
- );
6275
- }
6276
- return `/platform/v1/organizations/${encodeURIComponent(resolvedOrganizationId)}/projects/${encodeURIComponent(resolvedProjectId)}/${resource}`;
6277
- }
6278
- secretCollectionPath(organizationId, projectId) {
6279
- const resolvedOrganizationId = organizationId ?? this.organizationId;
6280
- const resolvedProjectId = projectId ?? this.projectId;
6281
- if (!resolvedOrganizationId && !resolvedProjectId) {
6282
- return this.namespacePath("secrets");
6283
- }
6284
- if (!resolvedOrganizationId || !resolvedProjectId) {
6285
- throw new Error("organizationId and projectId must be provided together");
6286
- }
6287
- return this.namespacePath("secrets");
6288
- }
6289
- };
6290
- UNAUTHENTICATED_REQUESTS = "unauthenticated_requests";
6291
- }
6292
- });
6293
-
6294
- // src/image.ts
6295
- import { randomUUID } from "crypto";
6296
- function cloneOperation(op) {
6297
- return {
6298
- type: op.type,
6299
- args: [...op.args],
6300
- options: { ...op.options }
6301
- };
6302
- }
6303
- function renderOptions(options) {
6304
- const entries = Object.entries(options);
6305
- if (entries.length === 0) {
6306
- return "";
6307
- }
6308
- return ` ${entries.map(([key, value]) => `--${key}=${value}`).join(" ")}`;
6309
- }
6310
- function renderBuildOp(op) {
6311
- const options = renderOptions(op.options);
6312
- if (op.type === ImageBuildOperationType.ENV) {
6313
- return `ENV${options} ${op.args[0]}=${JSON.stringify(op.args[1])}`;
6314
- }
6315
- return `${op.type}${options} ${op.args.join(" ")}`;
6316
- }
6317
- function dockerfileContent(image) {
6318
- const lines = image.baseImage == null ? [] : [`FROM ${image.baseImage}`];
6319
- lines.push(...image.buildOperations.map((op) => renderBuildOp(op)));
6320
- return lines.join("\n");
6321
- }
6322
- var DEFAULT_IMAGE_NAME, ImageBuildOperationType, Image;
6323
- var init_image = __esm({
6324
- "src/image.ts"() {
6325
- "use strict";
6326
- init_esm_shims();
6327
- DEFAULT_IMAGE_NAME = "default";
6328
- ImageBuildOperationType = {
6329
- ADD: "ADD",
6330
- COPY: "COPY",
6331
- ENV: "ENV",
6332
- RUN: "RUN",
6333
- WORKDIR: "WORKDIR"
6334
- };
6335
- Image = class {
6336
- _id;
6337
- _name;
6338
- _tag;
6339
- _baseImage;
6340
- _buildOperations;
6341
- constructor(nameOrOptions = {}, tag = "latest", baseImage = null) {
6342
- this._id = randomUUID();
6343
- this._buildOperations = [];
6344
- if (typeof nameOrOptions === "string") {
6345
- this._name = nameOrOptions;
6346
- this._tag = tag;
6347
- this._baseImage = baseImage;
6348
- return;
6349
- }
6350
- this._name = nameOrOptions.name ?? DEFAULT_IMAGE_NAME;
6351
- this._tag = nameOrOptions.tag ?? "latest";
6352
- this._baseImage = nameOrOptions.baseImage ?? null;
6353
- }
6354
- get name() {
6355
- return this._name;
6356
- }
6357
- get tag() {
6358
- return this._tag;
6359
- }
6360
- get baseImage() {
6361
- return this._baseImage;
6362
- }
6363
- get buildOperations() {
6364
- return this._buildOperations.map(cloneOperation);
6365
- }
6366
- add(src, dest, options = void 0) {
6367
- return this._addOperation({
6368
- type: ImageBuildOperationType.ADD,
6369
- args: [src, dest],
6370
- options: options == null ? {} : { ...options }
6371
- });
6372
- }
6373
- copy(src, dest, options = void 0) {
6374
- return this._addOperation({
6375
- type: ImageBuildOperationType.COPY,
6376
- args: [src, dest],
6377
- options: options == null ? {} : { ...options }
6378
- });
6379
- }
6380
- env(key, value) {
6381
- return this._addOperation({
6382
- type: ImageBuildOperationType.ENV,
6383
- args: [key, value],
6384
- options: {}
6385
- });
6386
- }
6387
- run(commands, options = void 0) {
6388
- return this._addOperation({
6389
- type: ImageBuildOperationType.RUN,
6390
- args: Array.isArray(commands) ? [...commands] : [commands],
6391
- options: options == null ? {} : { ...options }
6392
- });
6393
- }
6394
- workdir(directory) {
6395
- return this._addOperation({
6396
- type: ImageBuildOperationType.WORKDIR,
6397
- args: [directory],
6398
- options: {}
6399
- });
6400
- }
6401
- /**
6402
- * Build this image as a sandbox template and register it.
6403
- *
6404
- * Materializes the image in a build sandbox, snapshots the filesystem,
6405
- * and registers the snapshot as a named sandbox template.
6406
- */
6407
- async build(options = {}) {
6408
- const { createSandboxImage: createSandboxImage2 } = await Promise.resolve().then(() => (init_sandbox_image(), sandbox_image_exports));
6409
- return createSandboxImage2(this, options);
6410
- }
6411
- _addOperation(op) {
6412
- this._buildOperations.push(op);
6413
- return this;
5695
+ _addOperation(op) {
5696
+ this._buildOperations.push(op);
5697
+ return this;
6414
5698
  }
6415
5699
  };
6416
5700
  }
@@ -6562,22 +5846,477 @@ var init_native_binding = __esm({
6562
5846
  }
6563
5847
  });
6564
5848
 
5849
+ // src/image-builds.ts
5850
+ import path3 from "path";
5851
+ import os from "os";
5852
+ import { existsSync as existsSync2, mkdtempSync, rmSync } from "fs";
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 = path3.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 = mkdtempSync(path3.join(os.tmpdir(), "tl-build-context-"));
6017
+ contextDir = tempContextDir;
6018
+ }
6019
+ dockerfilePath = path3.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 = path3.resolve(source);
6026
+ if (!existsSync2(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) 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 ImageBuildError, ImageBuildNotFoundError, ImageBuildInProgressError, ImageBuildFailedError, ImageBuildCancelledError, ImageBuildTimeoutError, ImageBuildInterruptedError, ImageBuildHandle;
6162
+ var init_image_builds = __esm({
6163
+ "src/image-builds.ts"() {
6164
+ "use strict";
6165
+ init_esm_shims();
6166
+ init_image();
6167
+ init_sandbox_image();
6168
+ ImageBuildError = class extends Error {
6169
+ code;
6170
+ buildId;
6171
+ clientRequestId;
6172
+ details;
6173
+ constructor(details) {
6174
+ super(details.message);
6175
+ this.name = "ImageBuildError";
6176
+ this.code = details.code;
6177
+ this.details = details;
6178
+ if (details.buildId) this.buildId = details.buildId;
6179
+ if (details.clientRequestId) this.clientRequestId = details.clientRequestId;
6180
+ }
6181
+ };
6182
+ ImageBuildNotFoundError = class extends ImageBuildError {
6183
+ constructor(details) {
6184
+ super(details);
6185
+ this.name = "ImageBuildNotFoundError";
6186
+ }
6187
+ };
6188
+ ImageBuildInProgressError = class extends ImageBuildError {
6189
+ imageName;
6190
+ activeBuildId;
6191
+ activeKind;
6192
+ activeStatus;
6193
+ constructor(details) {
6194
+ super(details);
6195
+ this.name = "ImageBuildInProgressError";
6196
+ if (details.imageName) this.imageName = details.imageName;
6197
+ if (details.activeBuildId) this.activeBuildId = details.activeBuildId;
6198
+ if (details.activeKind) this.activeKind = details.activeKind;
6199
+ if (details.activeStatus) this.activeStatus = details.activeStatus;
6200
+ }
6201
+ };
6202
+ ImageBuildFailedError = class extends ImageBuildError {
6203
+ error;
6204
+ info;
6205
+ constructor(details) {
6206
+ super(details);
6207
+ this.name = "ImageBuildFailedError";
6208
+ this.error = details.error ?? "no error recorded";
6209
+ if (details.info) this.info = parseBuildInfo(details.info);
6210
+ }
6211
+ };
6212
+ ImageBuildCancelledError = class extends ImageBuildError {
6213
+ info;
6214
+ constructor(details) {
6215
+ super(details);
6216
+ this.name = "ImageBuildCancelledError";
6217
+ if (details.info) this.info = parseBuildInfo(details.info);
6218
+ }
6219
+ };
6220
+ ImageBuildTimeoutError = class extends ImageBuildError {
6221
+ constructor(details) {
6222
+ super(details);
6223
+ this.name = "ImageBuildTimeoutError";
6224
+ }
6225
+ };
6226
+ ImageBuildInterruptedError = class extends ImageBuildError {
6227
+ outcome;
6228
+ confirmed;
6229
+ result;
6230
+ constructor(details) {
6231
+ super(details);
6232
+ this.name = "ImageBuildInterruptedError";
6233
+ this.outcome = details.outcome ?? "unconfirmed";
6234
+ this.confirmed = details.confirmed === true;
6235
+ if (details.result) this.result = details.result;
6236
+ }
6237
+ };
6238
+ ImageBuildHandle = class {
6239
+ buildId;
6240
+ /**
6241
+ * The admission's request id, when this handle started the build. Never
6242
+ * needed to cancel; a recovery aid for a lost admission answer.
6243
+ */
6244
+ clientRequestId;
6245
+ #info;
6246
+ #credentials;
6247
+ /** @internal */
6248
+ constructor(info, credentials, clientRequestId) {
6249
+ this.buildId = info.buildId;
6250
+ this.clientRequestId = clientRequestId ?? info.clientRequestId ?? null;
6251
+ this.#info = info;
6252
+ this.#credentials = credentials;
6253
+ }
6254
+ /** The last observed state. */
6255
+ get info() {
6256
+ return this.#info;
6257
+ }
6258
+ /** Re-read the current state. */
6259
+ async refresh() {
6260
+ const binding = __imageBuildInternals.loadNativeBinding();
6261
+ let json;
6262
+ try {
6263
+ json = await binding.getImageBuild(clientOptions(this.#credentials), this.buildId);
6264
+ } catch (error) {
6265
+ throw toImageBuildError(error);
6266
+ }
6267
+ this.#info = parseBuildInfo(JSON.parse(json));
6268
+ return this.#info;
6269
+ }
6270
+ /**
6271
+ * Wait for publication and return this build's immutable result. See
6272
+ * {@link ImageBuildWaitOptions} for the timeout/abort distinction.
6273
+ */
6274
+ async wait(options = {}) {
6275
+ const binding = __imageBuildInternals.loadNativeBinding();
6276
+ const emitter = makeEmitter(options);
6277
+ const json = await __runWithInterrupt(
6278
+ options.signal,
6279
+ (interruptId) => binding.waitImageBuild(
6280
+ clientOptions(this.#credentials),
6281
+ this.buildId,
6282
+ { timeoutMs: options.timeoutMs, followLogs: options.followLogs ?? false },
6283
+ emitter.forward,
6284
+ interruptId
6285
+ )
6286
+ );
6287
+ emitter.rethrow();
6288
+ const result = parseResult(json);
6289
+ this.#info = { ...this.#info, status: "succeeded", result };
6290
+ return result;
6291
+ }
6292
+ /** Cancel this build by its id (the sufficient selector). */
6293
+ async cancel() {
6294
+ return cancelImageBuildWith(this.#credentials, { buildId: this.buildId });
6295
+ }
6296
+ };
6297
+ }
6298
+ });
6299
+
6565
6300
  // src/sandbox-image.ts
6566
6301
  var sandbox_image_exports = {};
6567
6302
  __export(sandbox_image_exports, {
6303
+ __imageBuildInternals: () => __imageBuildInternals,
6568
6304
  __setNativeBindingForTest: () => __setNativeBindingForTest,
6569
6305
  buildContextFromEnv: () => buildContextFromEnv,
6570
6306
  createSandboxImage: () => createSandboxImage,
6571
6307
  deleteSandboxImage: () => deleteSandboxImage,
6308
+ deleteSandboxImageWithCredentials: () => deleteSandboxImageWithCredentials,
6572
6309
  findSandboxImageByName: () => findSandboxImageByName,
6310
+ findSandboxImageByNameWithCredentials: () => findSandboxImageByNameWithCredentials,
6573
6311
  importSandboxImage: () => importSandboxImage,
6574
6312
  listSandboxImages: () => listSandboxImages,
6313
+ listSandboxImagesWithCredentials: () => listSandboxImagesWithCredentials,
6575
6314
  runCreateSandboxImageCli: () => runCreateSandboxImageCli,
6576
6315
  runImportSandboxImageCli: () => runImportSandboxImageCli
6577
6316
  });
6578
- import path3 from "path";
6579
- import os from "os";
6580
- import { existsSync as existsSync2, mkdtempSync, rmSync } from "fs";
6317
+ import path4 from "path";
6318
+ import os2 from "os";
6319
+ import { existsSync as existsSync3, mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "fs";
6581
6320
  import { parseArgs } from "util";
6582
6321
  function loadNativeBinding() {
6583
6322
  if (cachedNativeBinding) return cachedNativeBinding;
@@ -6611,9 +6350,9 @@ function buildContextFromEnv() {
6611
6350
  };
6612
6351
  }
6613
6352
  function defaultRegisteredName(dockerfilePath) {
6614
- const parsed = path3.parse(dockerfilePath);
6353
+ const parsed = path4.parse(dockerfilePath);
6615
6354
  if (parsed.name.toLowerCase() === "dockerfile") {
6616
- const parentName = path3.basename(path3.dirname(dockerfilePath)).trim();
6355
+ const parentName = path4.basename(path4.dirname(dockerfilePath)).trim();
6617
6356
  return parentName || "sandbox-image";
6618
6357
  }
6619
6358
  return parsed.name || "sandbox-image";
@@ -6649,6 +6388,15 @@ function eventToEmitDict(event) {
6649
6388
  message: event.message
6650
6389
  };
6651
6390
  if (event.stream != null) out.stream = event.stream;
6391
+ if (event.buildId != null) out.build_id = event.buildId;
6392
+ if (event.clientRequestId !== void 0 && event.eventType !== "status") {
6393
+ out.client_request_id = event.clientRequestId;
6394
+ }
6395
+ if (event.status != null) out.status = event.status;
6396
+ if (event.phase !== void 0 && event.eventType.startsWith("build_")) out.phase = event.phase;
6397
+ if (event.attempt != null) out.attempt = event.attempt;
6398
+ if (event.outcome != null) out.outcome = event.outcome;
6399
+ if (event.confirmed != null) out.confirmed = event.confirmed;
6652
6400
  return out;
6653
6401
  }
6654
6402
  function runOpMountsContext(op) {
@@ -6703,24 +6451,24 @@ async function createSandboxImage(source, options = {}, deps = {}) {
6703
6451
  }
6704
6452
  let resolvedContext;
6705
6453
  if (options.contextDir != null) {
6706
- resolvedContext = path3.resolve(options.contextDir);
6454
+ resolvedContext = path4.resolve(options.contextDir);
6707
6455
  } else if (imageRequiresContext(source)) {
6708
6456
  throw new Error(
6709
6457
  "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>`."
6710
6458
  );
6711
6459
  } else {
6712
- tempContextDir = mkdtempSync(path3.join(os.tmpdir(), "tl-build-context-"));
6460
+ tempContextDir = mkdtempSync2(path4.join(os2.tmpdir(), "tl-build-context-"));
6713
6461
  resolvedContext = tempContextDir;
6714
6462
  }
6715
- dockerfilePath = path3.join(resolvedContext, "Dockerfile");
6463
+ dockerfilePath = path4.join(resolvedContext, "Dockerfile");
6716
6464
  let text = dockerfileContent(source);
6717
6465
  if (!text.endsWith("\n")) text += "\n";
6718
6466
  dockerfileText = text;
6719
6467
  nativeContextDir = resolvedContext;
6720
6468
  effectiveName = options.registeredName ?? source.name;
6721
6469
  } else if (typeof source === "string") {
6722
- const resolvedPath = path3.resolve(source);
6723
- if (!existsSync2(resolvedPath)) {
6470
+ const resolvedPath = path4.resolve(source);
6471
+ if (!existsSync3(resolvedPath)) {
6724
6472
  throw new Error(`Dockerfile not found: ${source}`);
6725
6473
  }
6726
6474
  dockerfilePath = resolvedPath;
@@ -6750,38 +6498,47 @@ async function createSandboxImage(source, options = {}, deps = {}) {
6750
6498
  deps._allowPat ? "Missing TENSORLAKE_API_KEY or TENSORLAKE_PAT credentials." : "Missing TENSORLAKE_API_KEY credentials."
6751
6499
  );
6752
6500
  }
6501
+ if (options.signal !== void 0 && !options.cas) {
6502
+ throw new Error(
6503
+ "signal is only supported for CAS image builds (cas: true); a legacy build has no Image Service build operation to cancel"
6504
+ );
6505
+ }
6753
6506
  emit({ type: "status", message: `Building image '${effectiveName}'...` });
6754
6507
  const binding = loadNativeBinding();
6755
6508
  let emitError;
6756
- const resultJson = await binding.buildSandboxImage(
6757
- {
6758
- apiUrl: context.apiUrl,
6759
- bearerToken,
6760
- dockerfilePath,
6761
- registeredName: effectiveName,
6762
- diskMb: options.diskMb,
6763
- builderDiskMb: options.builderDiskMb,
6764
- cpus: options.cpus,
6765
- memoryMb: options.memoryMb,
6766
- isPublic: options.isPublic ?? false,
6767
- organizationId: usePat ? context.organizationId : void 0,
6768
- projectId: usePat ? context.projectId : void 0,
6769
- namespace: context.namespace,
6770
- useScopeHeaders: usePat,
6771
- userAgent: void 0,
6772
- dockerCompat: options.dockerCompat ?? false,
6773
- dockerConfigJson: options.dockerConfigJson,
6774
- dockerfileText,
6775
- contextDir: nativeContextDir
6776
- },
6777
- (event) => {
6778
- try {
6779
- emit(eventToEmitDict(event));
6780
- } catch (error) {
6781
- emitError ??= error;
6782
- }
6509
+ const nativeOptions = {
6510
+ apiUrl: context.apiUrl,
6511
+ bearerToken,
6512
+ dockerfilePath,
6513
+ registeredName: effectiveName,
6514
+ diskMb: options.diskMb,
6515
+ builderDiskMb: options.builderDiskMb,
6516
+ cpus: options.cpus,
6517
+ memoryMb: options.memoryMb,
6518
+ isPublic: options.isPublic ?? false,
6519
+ organizationId: usePat ? context.organizationId : void 0,
6520
+ projectId: usePat ? context.projectId : void 0,
6521
+ namespace: context.namespace,
6522
+ useScopeHeaders: usePat,
6523
+ userAgent: void 0,
6524
+ dockerCompat: options.dockerCompat ?? false,
6525
+ dockerConfigJson: options.dockerConfigJson,
6526
+ dockerfileText,
6527
+ contextDir: nativeContextDir,
6528
+ cas: options.cas ?? false,
6529
+ buildArgs: options.buildArgs
6530
+ };
6531
+ const forward = (event) => {
6532
+ try {
6533
+ emit(eventToEmitDict(event));
6534
+ } catch (error) {
6535
+ emitError ??= error;
6783
6536
  }
6784
- );
6537
+ };
6538
+ const resultJson = options.cas ? await __runWithInterrupt(
6539
+ options.signal,
6540
+ (interruptId) => binding.buildSandboxImage(nativeOptions, forward, interruptId)
6541
+ ) : await binding.buildSandboxImage(nativeOptions, forward);
6785
6542
  if (emitError != null) {
6786
6543
  throw emitError;
6787
6544
  }
@@ -6798,7 +6555,7 @@ async function createSandboxImage(source, options = {}, deps = {}) {
6798
6555
  return result;
6799
6556
  } finally {
6800
6557
  if (tempContextDir != null) {
6801
- rmSync(tempContextDir, { recursive: true, force: true });
6558
+ rmSync2(tempContextDir, { recursive: true, force: true });
6802
6559
  }
6803
6560
  }
6804
6561
  }
@@ -6827,39 +6584,47 @@ async function importSandboxImage(imageReference, options = {}, deps = {}) {
6827
6584
  deps._allowPat ? "Missing TENSORLAKE_API_KEY or TENSORLAKE_PAT credentials." : "Missing TENSORLAKE_API_KEY credentials."
6828
6585
  );
6829
6586
  }
6587
+ if (options.signal !== void 0 && !options.cas) {
6588
+ throw new Error(
6589
+ "signal is only supported for CAS image imports (cas: true); a legacy import has no Image Service build operation to cancel"
6590
+ );
6591
+ }
6830
6592
  emit({
6831
6593
  type: "status",
6832
6594
  message: `Importing image '${reference}' as '${effectiveName}'...`
6833
6595
  });
6834
6596
  const binding = loadNativeBinding();
6835
6597
  let emitError;
6836
- const resultJson = await binding.importSandboxImage(
6837
- {
6838
- apiUrl: context.apiUrl,
6839
- bearerToken,
6840
- imageReference: reference,
6841
- registeredName: effectiveName,
6842
- diskMb: options.diskMb,
6843
- builderDiskMb: options.builderDiskMb,
6844
- cpus: options.cpus,
6845
- memoryMb: options.memoryMb,
6846
- isPublic: options.isPublic ?? false,
6847
- organizationId: usePat ? context.organizationId : void 0,
6848
- projectId: usePat ? context.projectId : void 0,
6849
- namespace: context.namespace,
6850
- useScopeHeaders: usePat,
6851
- userAgent: void 0,
6852
- dockerCompat: options.dockerCompat ?? false,
6853
- dockerConfigJson: options.dockerConfigJson
6854
- },
6855
- (event) => {
6856
- try {
6857
- emit(eventToEmitDict(event));
6858
- } catch (error) {
6859
- emitError ??= error;
6860
- }
6598
+ const nativeOptions = {
6599
+ apiUrl: context.apiUrl,
6600
+ bearerToken,
6601
+ imageReference: reference,
6602
+ registeredName: effectiveName,
6603
+ diskMb: options.diskMb,
6604
+ builderDiskMb: options.builderDiskMb,
6605
+ cpus: options.cpus,
6606
+ memoryMb: options.memoryMb,
6607
+ isPublic: options.isPublic ?? false,
6608
+ organizationId: usePat ? context.organizationId : void 0,
6609
+ projectId: usePat ? context.projectId : void 0,
6610
+ namespace: context.namespace,
6611
+ useScopeHeaders: usePat,
6612
+ userAgent: void 0,
6613
+ dockerCompat: options.dockerCompat ?? false,
6614
+ dockerConfigJson: options.dockerConfigJson,
6615
+ cas: options.cas ?? false
6616
+ };
6617
+ const forward = (event) => {
6618
+ try {
6619
+ emit(eventToEmitDict(event));
6620
+ } catch (error) {
6621
+ emitError ??= error;
6861
6622
  }
6862
- );
6623
+ };
6624
+ const resultJson = options.cas ? await __runWithInterrupt(
6625
+ options.signal,
6626
+ (interruptId) => binding.importSandboxImage(nativeOptions, forward, interruptId)
6627
+ ) : await binding.importSandboxImage(nativeOptions, forward);
6863
6628
  if (emitError != null) {
6864
6629
  throw emitError;
6865
6630
  }
@@ -6875,10 +6640,7 @@ async function importSandboxImage(imageReference, options = {}, deps = {}) {
6875
6640
  emit({ type: "done" });
6876
6641
  return result;
6877
6642
  }
6878
- async function deleteSandboxImage(imageName) {
6879
- if (typeof imageName !== "string" || imageName.length === 0) {
6880
- throw new TypeError("imageName must be a non-empty string");
6881
- }
6643
+ function catalogOptionsFromEnv() {
6882
6644
  const context = buildContextFromEnv();
6883
6645
  const bearerToken = context.apiKey;
6884
6646
  if (!bearerToken) {
@@ -6889,63 +6651,73 @@ async function deleteSandboxImage(imageName) {
6889
6651
  }
6890
6652
  throw new Error("Missing TENSORLAKE_API_KEY credentials.");
6891
6653
  }
6892
- const client = new CloudClient({
6893
- apiUrl: context.apiUrl,
6894
- apiKey: bearerToken,
6895
- namespace: context.namespace
6896
- });
6897
- try {
6898
- await client.deleteSandboxImage(imageName);
6899
- } finally {
6900
- client.close();
6901
- }
6654
+ return { apiUrl: context.apiUrl, bearerToken, useScopeHeaders: false };
6902
6655
  }
6903
- async function findSandboxImageByName(imageName) {
6904
- if (typeof imageName !== "string" || imageName.length === 0) {
6905
- throw new TypeError("imageName must be a non-empty string");
6906
- }
6656
+ function catalogOptionsFromCredentials(credentials) {
6907
6657
  const context = buildContextFromEnv();
6908
- const bearerToken = context.apiKey;
6658
+ const bearerToken = credentials.apiKey ?? context.apiKey;
6909
6659
  if (!bearerToken) {
6910
- if (context.personalAccessToken) {
6911
- throw new Error(
6912
- "Sandbox image SDKs require TENSORLAKE_API_KEY. Personal access tokens are CLI-only."
6913
- );
6914
- }
6915
6660
  throw new Error("Missing TENSORLAKE_API_KEY credentials.");
6916
6661
  }
6917
- const client = new CloudClient({
6918
- apiUrl: context.apiUrl,
6919
- apiKey: bearerToken,
6920
- namespace: context.namespace
6921
- });
6922
- try {
6923
- return await client.findSandboxImageByName(imageName);
6924
- } finally {
6925
- client.close();
6662
+ const { organizationId, projectId } = credentials;
6663
+ if (organizationId === void 0 !== (projectId === void 0)) {
6664
+ throw new TypeError("organizationId and projectId must be given together");
6926
6665
  }
6666
+ return {
6667
+ apiUrl: credentials.apiUrl ?? context.apiUrl,
6668
+ bearerToken,
6669
+ organizationId,
6670
+ projectId,
6671
+ useScopeHeaders: organizationId !== void 0
6672
+ };
6927
6673
  }
6928
- async function listSandboxImages() {
6929
- const context = buildContextFromEnv();
6930
- const bearerToken = context.apiKey;
6931
- if (!bearerToken) {
6932
- if (context.personalAccessToken) {
6933
- throw new Error(
6934
- "Sandbox image SDKs require TENSORLAKE_API_KEY. Personal access tokens are CLI-only."
6935
- );
6936
- }
6937
- throw new Error("Missing TENSORLAKE_API_KEY credentials.");
6674
+ function parseTemplate(json) {
6675
+ return JSON.parse(json);
6676
+ }
6677
+ async function deleteSandboxImage(imageName) {
6678
+ if (typeof imageName !== "string" || imageName.length === 0) {
6679
+ throw new TypeError("imageName must be a non-empty string");
6938
6680
  }
6939
- const client = new CloudClient({
6940
- apiUrl: context.apiUrl,
6941
- apiKey: bearerToken,
6942
- namespace: context.namespace
6943
- });
6944
- try {
6945
- return await client.listSandboxImages();
6946
- } finally {
6947
- client.close();
6681
+ await loadNativeBinding().deleteSandboxImage(catalogOptionsFromEnv(), imageName);
6682
+ }
6683
+ async function deleteSandboxImageWithCredentials(credentials, imageName) {
6684
+ if (typeof imageName !== "string" || imageName.length === 0) {
6685
+ throw new TypeError("imageName must be a non-empty string");
6686
+ }
6687
+ await loadNativeBinding().deleteSandboxImage(
6688
+ catalogOptionsFromCredentials(credentials),
6689
+ imageName
6690
+ );
6691
+ }
6692
+ async function findSandboxImageByName(imageName) {
6693
+ if (typeof imageName !== "string" || imageName.length === 0) {
6694
+ throw new TypeError("imageName must be a non-empty string");
6695
+ }
6696
+ const json = await loadNativeBinding().findSandboxImage(
6697
+ catalogOptionsFromEnv(),
6698
+ imageName
6699
+ );
6700
+ return json == null ? null : parseTemplate(json);
6701
+ }
6702
+ async function findSandboxImageByNameWithCredentials(credentials, imageName) {
6703
+ if (typeof imageName !== "string" || imageName.length === 0) {
6704
+ throw new TypeError("imageName must be a non-empty string");
6948
6705
  }
6706
+ const json = await loadNativeBinding().findSandboxImage(
6707
+ catalogOptionsFromCredentials(credentials),
6708
+ imageName
6709
+ );
6710
+ return json == null ? null : parseTemplate(json);
6711
+ }
6712
+ async function listSandboxImages() {
6713
+ const json = await loadNativeBinding().listSandboxImages(catalogOptionsFromEnv());
6714
+ return JSON.parse(json);
6715
+ }
6716
+ async function listSandboxImagesWithCredentials(credentials) {
6717
+ const json = await loadNativeBinding().listSandboxImages(
6718
+ catalogOptionsFromCredentials(credentials)
6719
+ );
6720
+ return JSON.parse(json);
6949
6721
  }
6950
6722
  async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
6951
6723
  const parsed = parseArgs({
@@ -7055,15 +6827,25 @@ async function runImportSandboxImageCli(argv = process.argv.slice(2)) {
7055
6827
  { emit: ndjsonStdoutEmit, _allowPat: true }
7056
6828
  );
7057
6829
  }
7058
- var cachedNativeBinding, cachedNativeBindingError, DEFAULT_IMAGE_NAME2;
6830
+ var cachedNativeBinding, cachedNativeBindingError, DEFAULT_IMAGE_NAME2, __imageBuildInternals;
7059
6831
  var init_sandbox_image = __esm({
7060
6832
  "src/sandbox-image.ts"() {
7061
6833
  "use strict";
7062
6834
  init_esm_shims();
7063
- init_cloud_client();
7064
6835
  init_image();
7065
6836
  init_native_binding();
6837
+ init_image_builds();
7066
6838
  DEFAULT_IMAGE_NAME2 = "default";
6839
+ __imageBuildInternals = {
6840
+ loadNativeBinding: () => loadNativeBinding(),
6841
+ buildContextFromEnv: () => buildContextFromEnv(),
6842
+ stderrEmit: (event) => stderrEmit(event),
6843
+ noopEmit: (event) => noopEmit(event),
6844
+ eventToEmitDict: (event) => eventToEmitDict(event),
6845
+ imageRequiresContext: (image) => imageRequiresContext(image),
6846
+ defaultRegisteredName: (dockerfilePath) => defaultRegisteredName(dockerfilePath),
6847
+ defaultRegisteredNameFromImage: (reference) => defaultRegisteredNameFromImage(reference)
6848
+ };
7067
6849
  }
7068
6850
  });
7069
6851
 
@@ -7075,11 +6857,546 @@ init_pending_sandbox();
7075
6857
  init_sandbox();
7076
6858
  init_desktop();
7077
6859
  init_tunnel();
7078
- init_cloud_client();
6860
+
6861
+ // src/cloud-client.ts
6862
+ init_esm_shims();
6863
+ init_defaults();
6864
+ import { nanoid } from "nanoid";
6865
+
6866
+ // src/native-cloud.ts
6867
+ init_esm_shims();
6868
+ init_native_sandbox();
6869
+ init_native_worker_client();
6870
+ var NativeCloudTransport = class {
6871
+ constructor(options) {
6872
+ this.options = options;
6873
+ if (!Number.isSafeInteger(options.maxRetries) || options.maxRetries < 0 || options.maxRetries > 4294967295) {
6874
+ throw new RangeError("maxRetries must be a non-negative 32-bit integer");
6875
+ }
6876
+ if (!Number.isFinite(options.retryBackoffMs) || options.retryBackoffMs < 0) {
6877
+ throw new RangeError("retryBackoffMs must be a non-negative finite number");
6878
+ }
6879
+ }
6880
+ native;
6881
+ closed = false;
6882
+ client() {
6883
+ if (this.closed) throw new Error("Tensorlake native client is closed");
6884
+ if (this.native) return this.native;
6885
+ const ctor = loadNativeSandboxBinding().NativeCloudClient;
6886
+ if (!ctor) throw new Error("Native binding does not export NativeCloudClient; rebuild with 'npm run build:native'");
6887
+ return this.native = new ctor(JSON.stringify(this.options));
6888
+ }
6889
+ close() {
6890
+ this.closed = true;
6891
+ releaseNativeHandle(this.native ?? null);
6892
+ this.native = void 0;
6893
+ }
6894
+ async requestResponse(method, path5, options) {
6895
+ const request = await serializeRequest(method, path5, options);
6896
+ const raw = await callNative(() => this.client().request(request));
6897
+ const response = new Response(
6898
+ [204, 205, 304].includes(raw.status) ? null : Uint8Array.from(raw.data).buffer,
6899
+ { status: raw.status, headers: JSON.parse(raw.headersJson) }
6900
+ );
6901
+ return Object.assign(response, { traceId: raw.traceId });
6902
+ }
6903
+ async requestJson(method, path5) {
6904
+ const response = await this.requestResponse(method, path5);
6905
+ const text = await response.text();
6906
+ const value = text ? JSON.parse(text) : null;
6907
+ return Object.assign(value ?? {}, { traceId: response.traceId });
6908
+ }
6909
+ async *stream(method, path5, signal) {
6910
+ if (signal?.aborted) return;
6911
+ const request = await serializeRequest(method, path5, { headers: { Accept: "text/event-stream" } });
6912
+ yield* nativeEventStream((emit) => this.client().stream(request, emit), void 0, signal);
6913
+ }
6914
+ };
6915
+ async function serializeRequest(method, path5, options) {
6916
+ const headers = { ...options?.headers };
6917
+ let body = options?.body;
6918
+ if (options?.json !== void 0) {
6919
+ body = JSON.stringify(options.json);
6920
+ if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) {
6921
+ headers["Content-Type"] = "application/json";
6922
+ }
6923
+ }
6924
+ const request = {
6925
+ method,
6926
+ path: path5,
6927
+ headersJson: JSON.stringify(headers),
6928
+ statusOnlyCodes: [...options?.statusOnlyCodes ?? []]
6929
+ };
6930
+ if (body instanceof FormData) {
6931
+ request.parts = [];
6932
+ for (const [name, value] of body.entries()) {
6933
+ 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" });
6934
+ }
6935
+ } else if (body !== void 0) {
6936
+ if (body instanceof Blob) request.body = Buffer.from(await body.arrayBuffer());
6937
+ else if (typeof body === "string") request.body = Buffer.from(body);
6938
+ else if (body instanceof ArrayBuffer) request.body = Buffer.from(new Uint8Array(body));
6939
+ else request.body = Buffer.from(body);
6940
+ }
6941
+ return request;
6942
+ }
6943
+
6944
+ // src/cloud-client.ts
6945
+ init_models();
6946
+ init_sandbox_image();
6947
+ var CloudClient = class _CloudClient {
6948
+ transport;
6949
+ apiUrl;
6950
+ apiKey;
6951
+ organizationId;
6952
+ projectId;
6953
+ namespace;
6954
+ constructor(options) {
6955
+ this.apiUrl = options?.apiUrl ?? API_URL;
6956
+ this.apiKey = options?.apiKey ?? API_KEY;
6957
+ this.organizationId = options?.organizationId;
6958
+ this.projectId = options?.projectId;
6959
+ this.namespace = options?.namespace ?? NAMESPACE;
6960
+ this.transport = new NativeCloudTransport({
6961
+ baseUrl: options?.apiUrl ?? API_URL,
6962
+ apiKey: options?.apiKey ?? API_KEY,
6963
+ organizationId: this.organizationId,
6964
+ projectId: this.projectId,
6965
+ maxRetries: options?.maxRetries ?? MAX_RETRIES,
6966
+ retryBackoffMs: options?.retryBackoffMs ?? RETRY_BACKOFF_MS,
6967
+ timeoutMs: DEFAULT_HTTP_TIMEOUT_MS,
6968
+ userAgent: `tensorlake-typescript-sdk/${SDK_VERSION}`
6969
+ });
6970
+ }
6971
+ static forCloud(options) {
6972
+ return new _CloudClient(options);
6973
+ }
6974
+ close() {
6975
+ this.transport.close();
6976
+ }
6977
+ async upsertApplication(manifest, codeZip, upgradeRunningRequests = false) {
6978
+ const applicationManifest = { ...manifest };
6979
+ let publicEndpointId = publicEndpointIdFromManifest(applicationManifest);
6980
+ if (applicationAllowsUnauthenticatedRequests(applicationManifest) && publicEndpointId === void 0) {
6981
+ const applicationName = applicationManifest.name;
6982
+ if (typeof applicationName === "string" && applicationName.length > 0) {
6983
+ publicEndpointId = await this.existingApplicationPublicEndpointId(applicationName);
6984
+ }
6985
+ publicEndpointId ??= generatePublicEndpointId();
6986
+ }
6987
+ if (publicEndpointId !== void 0) {
6988
+ applicationManifest.public_endpoint_id = publicEndpointId;
6989
+ delete applicationManifest.publicEndpointId;
6990
+ }
6991
+ const form = new FormData();
6992
+ form.append(
6993
+ "code",
6994
+ new Blob([toBlobPart(codeZip)], { type: "application/zip" }),
6995
+ "code.zip"
6996
+ );
6997
+ form.append("code_content_type", "application/zip");
6998
+ form.append("application", JSON.stringify(applicationManifest));
6999
+ form.append(
7000
+ "upgrade_requests_to_latest_code",
7001
+ String(upgradeRunningRequests)
7002
+ );
7003
+ await this.transport.requestResponse("POST", this.namespacePath("applications"), {
7004
+ body: form
7005
+ });
7006
+ }
7007
+ async deleteApplication(applicationName) {
7008
+ await this.transport.requestResponse(
7009
+ "DELETE",
7010
+ this.namespacePath(`applications/${encodeURIComponent(applicationName)}`)
7011
+ );
7012
+ }
7013
+ /**
7014
+ * Delete a registered sandbox image by name or id through the merged
7015
+ * catalog, with this client's credentials: an `image_` id or a name the
7016
+ * Image Service knows deletes a CAS image, a `sandbox_template_` id or a
7017
+ * name registered only in the platform registry deletes a legacy image.
7018
+ * Rejects when neither catalog knows the reference. The same semantics
7019
+ * and accepted differences as the module-level `deleteSandboxImage`.
7020
+ */
7021
+ async deleteSandboxImage(imageName) {
7022
+ await deleteSandboxImageWithCredentials(this.catalogCredentials(), imageName);
7023
+ }
7024
+ /**
7025
+ * Look up a registered sandbox image by name or id through the merged
7026
+ * catalog, with this client's credentials. Returns the image in the
7027
+ * sandbox-template shape, or `null` when neither the Image Service nor the
7028
+ * platform registry knows the reference. An explicit organization/project
7029
+ * scope (the client's, or the call's override) addresses that project's
7030
+ * routes directly, as before; without one the key's own scope applies.
7031
+ */
7032
+ async findSandboxImageByName(imageName, options) {
7033
+ return findSandboxImageByNameWithCredentials(
7034
+ this.catalogCredentials(options),
7035
+ imageName
7036
+ );
7037
+ }
7038
+ /**
7039
+ * List every registered sandbox image of the project (CAS and legacy, in
7040
+ * the sandbox-template shape, newest first) through the merged catalog,
7041
+ * with this client's credentials. An explicit organization/project scope
7042
+ * (the client's, or the call's override) addresses that project's routes
7043
+ * directly, as before; without one the key's own scope applies.
7044
+ */
7045
+ async listSandboxImages(options) {
7046
+ return listSandboxImagesWithCredentials(this.catalogCredentials(options));
7047
+ }
7048
+ catalogCredentials(options) {
7049
+ return {
7050
+ apiUrl: this.apiUrl,
7051
+ apiKey: this.apiKey,
7052
+ organizationId: options?.organizationId ?? this.organizationId,
7053
+ projectId: options?.projectId ?? this.projectId
7054
+ };
7055
+ }
7056
+ async applications() {
7057
+ const raw = await this.transport.requestJson(
7058
+ "GET",
7059
+ this.namespacePath("applications")
7060
+ );
7061
+ return (raw.applications ?? []).map(
7062
+ (application) => fromSnakeKeys(application)
7063
+ );
7064
+ }
7065
+ async applicationManifest(applicationName) {
7066
+ const raw = await this.transport.requestJson(
7067
+ "GET",
7068
+ this.namespacePath(`applications/${encodeURIComponent(applicationName)}`)
7069
+ );
7070
+ return fromSnakeKeys(raw);
7071
+ }
7072
+ async runRequest(applicationName, inputs = []) {
7073
+ const path5 = this.namespacePath(
7074
+ `applications/${encodeURIComponent(applicationName)}`
7075
+ );
7076
+ const response = inputs.length === 0 ? await this.transport.requestResponse("POST", path5, {
7077
+ body: new Uint8Array(),
7078
+ headers: { Accept: "application/json" }
7079
+ }) : inputs.length === 1 && inputs[0].name === "0" ? await this.transport.requestResponse("POST", path5, {
7080
+ body: toRequestBody(inputs[0].data),
7081
+ headers: {
7082
+ Accept: "application/json",
7083
+ "Content-Type": inputs[0].contentType
7084
+ }
7085
+ }) : await this.runMultipartRequest(path5, inputs);
7086
+ const body = await parseJsonResponse(response);
7087
+ const requestId = body?.request_id;
7088
+ if (!requestId) {
7089
+ throw new Error("missing request_id in run request response body");
7090
+ }
7091
+ return requestId;
7092
+ }
7093
+ async waitOnRequestCompletion(applicationName, requestId) {
7094
+ const stream = this.transport.stream(
7095
+ "GET",
7096
+ this.namespacePath(
7097
+ `applications/${encodeURIComponent(applicationName)}/requests/${encodeURIComponent(requestId)}/progress`
7098
+ )
7099
+ );
7100
+ for await (const event of stream) {
7101
+ if (Object.prototype.hasOwnProperty.call(event, "RequestFinished")) {
7102
+ return;
7103
+ }
7104
+ }
7105
+ throw new Error("progress stream ended before request completion");
7106
+ }
7107
+ async requestMetadata(applicationName, requestId) {
7108
+ const raw = await this.transport.requestJson(
7109
+ "GET",
7110
+ this.namespacePath(
7111
+ `applications/${encodeURIComponent(applicationName)}/requests/${encodeURIComponent(requestId)}`
7112
+ )
7113
+ );
7114
+ return fromSnakeKeys(raw);
7115
+ }
7116
+ async requestOutput(applicationName, requestId) {
7117
+ const response = await this.transport.requestResponse(
7118
+ "GET",
7119
+ this.namespacePath(
7120
+ `applications/${encodeURIComponent(applicationName)}/requests/${encodeURIComponent(requestId)}/output`
7121
+ )
7122
+ );
7123
+ const serializedValue = new Uint8Array(await response.arrayBuffer());
7124
+ const contentType = response.headers.get("content-type") ?? "";
7125
+ return {
7126
+ serializedValue,
7127
+ contentType
7128
+ };
7129
+ }
7130
+ async listSecrets(options) {
7131
+ const base = this.secretCollectionPath(
7132
+ options?.organizationId,
7133
+ options?.projectId
7134
+ );
7135
+ const raw = await this.transport.requestJson("GET", base);
7136
+ const pageSize = Math.max(0, options?.pageSize ?? 100);
7137
+ return Object.assign(
7138
+ {
7139
+ items: raw.slice(0, pageSize).map(secretFromMetadata),
7140
+ pagination: { total: raw.length }
7141
+ },
7142
+ { traceId: raw.traceId }
7143
+ );
7144
+ }
7145
+ async getSecret(secretId, options) {
7146
+ const base = this.secretCollectionPath(
7147
+ options?.organizationId,
7148
+ options?.projectId
7149
+ );
7150
+ const raw = await this.transport.requestJson(
7151
+ "GET",
7152
+ `${base}/${encodeURIComponent(secretId)}`
7153
+ );
7154
+ return Object.assign(secretFromMetadata(raw), { traceId: raw.traceId });
7155
+ }
7156
+ async upsertSecrets(secrets, options) {
7157
+ const base = this.secretCollectionPath(
7158
+ options?.organizationId,
7159
+ options?.projectId
7160
+ );
7161
+ if (Array.isArray(secrets)) {
7162
+ const results = [];
7163
+ for (const secret of secrets) {
7164
+ results.push(await this.upsertSecret(base, secret));
7165
+ }
7166
+ return results;
7167
+ }
7168
+ return this.upsertSecret(base, secrets);
7169
+ }
7170
+ async deleteSecret(secretId, options) {
7171
+ const base = this.secretCollectionPath(
7172
+ options?.organizationId,
7173
+ options?.projectId
7174
+ );
7175
+ await this.transport.requestResponse(
7176
+ "DELETE",
7177
+ `${base}/${encodeURIComponent(secretId)}`
7178
+ );
7179
+ }
7180
+ async upsertSecret(base, secret) {
7181
+ let response = await this.transport.requestResponse("POST", base, {
7182
+ json: secret,
7183
+ headers: { "Idempotency-Key": nanoid() },
7184
+ statusOnlyCodes: /* @__PURE__ */ new Set([409])
7185
+ });
7186
+ if (response.status === 409) {
7187
+ const prefix = base.slice(0, -"/secrets".length);
7188
+ const existing = await this.transport.requestJson(
7189
+ "GET",
7190
+ `${prefix}/secret-names/${encodeURIComponent(secret.name)}`
7191
+ );
7192
+ response = await this.transport.requestResponse(
7193
+ "POST",
7194
+ `${base}/${encodeURIComponent(existing.id)}/versions`,
7195
+ {
7196
+ json: { value: secret.value },
7197
+ headers: { "Idempotency-Key": nanoid() }
7198
+ }
7199
+ );
7200
+ }
7201
+ const metadata = await parseJsonResponse(response);
7202
+ return Object.assign(secretFromMetadata(metadata), {
7203
+ traceId: response.traceId
7204
+ });
7205
+ }
7206
+ async startImageBuild(buildServicePath, request) {
7207
+ const form = new FormData();
7208
+ form.append("graph_name", request.applicationName);
7209
+ form.append("graph_version", request.applicationVersion);
7210
+ form.append("graph_function_name", request.functionName);
7211
+ form.append("image_name", request.imageName);
7212
+ form.append("image_id", request.imageId);
7213
+ form.append(
7214
+ "context",
7215
+ new Blob([toBlobPart(request.buildContext)]),
7216
+ "context.tar.gz"
7217
+ );
7218
+ const response = await this.transport.requestResponse(
7219
+ "PUT",
7220
+ `${trimTrailingSlashes2(buildServicePath)}/builds`,
7221
+ { body: form }
7222
+ );
7223
+ const raw = await parseJsonResponse(response);
7224
+ return fromSnakeKeys(raw);
7225
+ }
7226
+ async createApplicationBuild(buildServicePath, request, imageContexts) {
7227
+ const form = createApplicationBuildForm(request, imageContexts);
7228
+ const response = await this.transport.requestResponse(
7229
+ "POST",
7230
+ trimTrailingSlashes2(buildServicePath),
7231
+ { body: form }
7232
+ );
7233
+ const raw = await parseJsonResponse(response);
7234
+ return fromSnakeKeys(raw);
7235
+ }
7236
+ async applicationBuildInfo(buildServicePath, applicationBuildId) {
7237
+ const raw = await this.transport.requestJson(
7238
+ "GET",
7239
+ `${trimTrailingSlashes2(buildServicePath)}/${encodeURIComponent(applicationBuildId)}`
7240
+ );
7241
+ return fromSnakeKeys(raw);
7242
+ }
7243
+ async cancelApplicationBuild(buildServicePath, applicationBuildId) {
7244
+ const raw = await this.transport.requestJson(
7245
+ "POST",
7246
+ `${trimTrailingSlashes2(buildServicePath)}/${encodeURIComponent(applicationBuildId)}/cancel`
7247
+ );
7248
+ return fromSnakeKeys(raw);
7249
+ }
7250
+ async buildInfo(buildServicePath, buildId) {
7251
+ const raw = await this.transport.requestJson(
7252
+ "GET",
7253
+ `${trimTrailingSlashes2(buildServicePath)}/builds/${encodeURIComponent(buildId)}`
7254
+ );
7255
+ return fromSnakeKeys(raw);
7256
+ }
7257
+ async cancelBuild(buildServicePath, buildId) {
7258
+ await this.transport.requestResponse(
7259
+ "POST",
7260
+ `${trimTrailingSlashes2(buildServicePath)}/builds/${encodeURIComponent(buildId)}/cancel`
7261
+ );
7262
+ }
7263
+ async *streamBuildLogs(buildServicePath, buildId, signal) {
7264
+ const stream = this.transport.stream(
7265
+ "GET",
7266
+ `${trimTrailingSlashes2(buildServicePath)}/builds/${encodeURIComponent(buildId)}/logs`,
7267
+ signal
7268
+ );
7269
+ for await (const event of stream) {
7270
+ yield fromSnakeKeys(event);
7271
+ }
7272
+ }
7273
+ async runMultipartRequest(path5, inputs) {
7274
+ const form = new FormData();
7275
+ for (const input of inputs) {
7276
+ form.append(
7277
+ input.name,
7278
+ new Blob([toBlobPart(input.data)], { type: input.contentType }),
7279
+ input.name
7280
+ );
7281
+ }
7282
+ return this.transport.requestResponse("POST", path5, {
7283
+ body: form,
7284
+ headers: { Accept: "application/json" }
7285
+ });
7286
+ }
7287
+ namespacePath(subpath) {
7288
+ return `/v1/namespaces/${encodeURIComponent(this.namespace)}/${subpath.replace(/^\/+/, "")}`;
7289
+ }
7290
+ async existingApplicationPublicEndpointId(applicationName) {
7291
+ const response = await this.transport.requestResponse(
7292
+ "GET",
7293
+ this.namespacePath(`applications/${encodeURIComponent(applicationName)}`),
7294
+ { statusOnlyCodes: /* @__PURE__ */ new Set([404]) }
7295
+ );
7296
+ if (response.status === 404) {
7297
+ return void 0;
7298
+ }
7299
+ const application = await parseJsonResponse(response);
7300
+ return publicEndpointIdFromManifest(application);
7301
+ }
7302
+ secretCollectionPath(organizationId, projectId) {
7303
+ const resolvedOrganizationId = organizationId ?? this.organizationId;
7304
+ const resolvedProjectId = projectId ?? this.projectId;
7305
+ if (!resolvedOrganizationId && !resolvedProjectId) {
7306
+ return this.namespacePath("secrets");
7307
+ }
7308
+ if (!resolvedOrganizationId || !resolvedProjectId) {
7309
+ throw new Error("organizationId and projectId must be provided together");
7310
+ }
7311
+ return this.namespacePath("secrets");
7312
+ }
7313
+ };
7314
+ function secretFromMetadata(metadata) {
7315
+ return {
7316
+ id: metadata.id,
7317
+ name: metadata.name,
7318
+ createdAt: new Date(metadata.created_at_ms)
7319
+ };
7320
+ }
7321
+ var UNAUTHENTICATED_REQUESTS = "unauthenticated_requests";
7322
+ function applicationAllowsUnauthenticatedRequests(manifest) {
7323
+ return Array.isArray(manifest.allow) && manifest.allow.includes(UNAUTHENTICATED_REQUESTS);
7324
+ }
7325
+ function publicEndpointIdFromManifest(manifest) {
7326
+ for (const key of ["public_endpoint_id", "publicEndpointId"]) {
7327
+ const value = manifest[key];
7328
+ if (typeof value === "string" && value.length > 0) {
7329
+ return value;
7330
+ }
7331
+ }
7332
+ return void 0;
7333
+ }
7334
+ function generatePublicEndpointId() {
7335
+ return `endpoint_${nanoid()}`;
7336
+ }
7337
+ function createApplicationBuildForm(request, imageContexts) {
7338
+ const contextsByPartName = /* @__PURE__ */ new Map();
7339
+ for (const context of imageContexts) {
7340
+ if (contextsByPartName.has(context.contextTarPartName)) {
7341
+ throw new Error(
7342
+ `duplicate image context part name '${context.contextTarPartName}'`
7343
+ );
7344
+ }
7345
+ contextsByPartName.set(context.contextTarPartName, context);
7346
+ }
7347
+ const form = new FormData();
7348
+ form.append(
7349
+ "app_version",
7350
+ new Blob([JSON.stringify(request)], { type: "application/json" }),
7351
+ "app_version"
7352
+ );
7353
+ for (const image of request.images) {
7354
+ const context = contextsByPartName.get(image.contextTarPartName);
7355
+ if (!context) {
7356
+ throw new Error(
7357
+ `missing image context for part '${image.contextTarPartName}'`
7358
+ );
7359
+ }
7360
+ form.append(
7361
+ image.contextTarPartName,
7362
+ new Blob([toBlobPart(context.contextTarGz)]),
7363
+ `${image.contextTarPartName}.tar.gz`
7364
+ );
7365
+ }
7366
+ for (const context of imageContexts) {
7367
+ if (!request.images.some((image) => image.contextTarPartName === context.contextTarPartName)) {
7368
+ throw new Error(
7369
+ `unexpected image context for part '${context.contextTarPartName}'`
7370
+ );
7371
+ }
7372
+ }
7373
+ return form;
7374
+ }
7375
+ function trimTrailingSlashes2(value) {
7376
+ return value.endsWith("/") ? value.slice(0, -1) : value;
7377
+ }
7378
+ function toBlobPart(data) {
7379
+ if (typeof data === "string" || data instanceof Blob) {
7380
+ return data;
7381
+ }
7382
+ if (data instanceof Uint8Array) {
7383
+ return Uint8Array.from(data).buffer;
7384
+ }
7385
+ return data;
7386
+ }
7387
+ function toRequestBody(data) {
7388
+ return toBlobPart(data);
7389
+ }
7390
+ async function parseJsonResponse(response) {
7391
+ const text = await response.text();
7392
+ if (!text) {
7393
+ return void 0;
7394
+ }
7395
+ return JSON.parse(text);
7396
+ }
7079
7397
 
7080
7398
  // src/api-client.ts
7081
7399
  init_esm_shims();
7082
- init_cloud_client();
7083
7400
 
7084
7401
  // src/filesystem.ts
7085
7402
  init_esm_shims();
@@ -7097,12 +7414,12 @@ import { promisify } from "util";
7097
7414
  init_esm_shims();
7098
7415
  var GIT_MODE_DIR = 16384;
7099
7416
  var GIT_MODE_SYMLINK = 40960;
7100
- function trimSlashes(path4) {
7417
+ function trimSlashes(path5) {
7101
7418
  let start = 0;
7102
- let end = path4.length;
7103
- while (start < end && path4.charCodeAt(start) === 47) start += 1;
7104
- while (end > start && path4.charCodeAt(end - 1) === 47) end -= 1;
7105
- return path4.slice(start, end);
7419
+ let end = path5.length;
7420
+ while (start < end && path5.charCodeAt(start) === 47) start += 1;
7421
+ while (end > start && path5.charCodeAt(end - 1) === 47) end -= 1;
7422
+ return path5.slice(start, end);
7106
7423
  }
7107
7424
  function fileEntryFromWire(entry, dirPath) {
7108
7425
  const mode = entry.mode ?? 33188;
@@ -7120,9 +7437,9 @@ function fileEntryFromWire(entry, dirPath) {
7120
7437
  }
7121
7438
  function mountStatusFromRaw(raw, localPath) {
7122
7439
  const mounted = "mounted" in raw ? Boolean(raw.mounted) : "active" in raw ? Boolean(raw.active) : true;
7123
- const path4 = String(raw.path || raw.mount_path || localPath || "");
7440
+ const path5 = String(raw.path || raw.mount_path || localPath || "");
7124
7441
  const filesystem = raw.filesystem || raw.file_system || raw.repository || null;
7125
- return { path: path4, filesystem, mounted, raw };
7442
+ return { path: path5, filesystem, mounted, raw };
7126
7443
  }
7127
7444
  var FilesystemError = class extends Error {
7128
7445
  constructor(message) {
@@ -7141,11 +7458,11 @@ var FilesystemNotFoundError = class extends FilesystemError {
7141
7458
  var FileNotFoundInFilesystemError = class extends FilesystemError {
7142
7459
  filesystem;
7143
7460
  path;
7144
- constructor(filesystem, path4) {
7145
- super(`File not found in filesystem ${filesystem}: ${path4}`);
7461
+ constructor(filesystem, path5) {
7462
+ super(`File not found in filesystem ${filesystem}: ${path5}`);
7146
7463
  this.name = "FileNotFoundInFilesystemError";
7147
7464
  this.filesystem = filesystem;
7148
- this.path = path4;
7465
+ this.path = path5;
7149
7466
  }
7150
7467
  };
7151
7468
  var FilesystemAPIError = class extends FilesystemError {
@@ -7499,8 +7816,8 @@ var Filesystem = class {
7499
7816
  }
7500
7817
  // -- writes -------------------------------------------------------------------
7501
7818
  /** Write one file. Returns the snapshot (version) the write produced. */
7502
- async writeFile(path4, data, message) {
7503
- return await this.writeFiles(/* @__PURE__ */ new Map([[path4, data]]), message);
7819
+ async writeFile(path5, data, message) {
7820
+ return await this.writeFiles(/* @__PURE__ */ new Map([[path5, data]]), message);
7504
7821
  }
7505
7822
  /** Write several files (and/or delete paths) in one atomic snapshot. */
7506
7823
  async writeFiles(files, message, deletes = []) {
@@ -7508,8 +7825,8 @@ var Filesystem = class {
7508
7825
  if (entries.length === 0 && deletes.length === 0) {
7509
7826
  throw new FilesystemError("nothing to write: no files or deletions given");
7510
7827
  }
7511
- const writes = entries.map(([path4, data]) => ({
7512
- path: path4,
7828
+ const writes = entries.map(([path5, data]) => ({
7829
+ path: path5,
7513
7830
  content: toBuffer(data)
7514
7831
  }));
7515
7832
  const resolvedMessage = message || `write ${writes.length} file(s) via SDK`;
@@ -7525,9 +7842,9 @@ var Filesystem = class {
7525
7842
  * Stream one local file directly to object storage in bounded parts.
7526
7843
  * This is the memory-bounded API for multi-GiB files.
7527
7844
  */
7528
- async writeFileFromPath(path4, localPath, message) {
7845
+ async writeFileFromPath(path5, localPath, message) {
7529
7846
  return await this.writeFilesFromPaths(
7530
- /* @__PURE__ */ new Map([[path4, localPath]]),
7847
+ /* @__PURE__ */ new Map([[path5, localPath]]),
7531
7848
  message
7532
7849
  );
7533
7850
  }
@@ -7540,7 +7857,7 @@ var Filesystem = class {
7540
7857
  if (entries.length === 0) {
7541
7858
  throw new FilesystemError("nothing to write: no local files given");
7542
7859
  }
7543
- const writes = entries.map(([path4, localPath]) => ({ path: path4, localPath }));
7860
+ const writes = entries.map(([path5, localPath]) => ({ path: path5, localPath }));
7544
7861
  const resolvedMessage = message || `write ${writes.length} local file(s) via SDK`;
7545
7862
  const branch = await this.branch();
7546
7863
  const traced = await callNative2(
@@ -7617,11 +7934,11 @@ var Filesystem = class {
7617
7934
  };
7618
7935
  }
7619
7936
  /** Delete one file. Returns the durable version the deletion produced. */
7620
- async deleteFile(path4, message) {
7937
+ async deleteFile(path5, message) {
7621
7938
  return await this.writeFiles(
7622
7939
  /* @__PURE__ */ new Map(),
7623
- message || `delete ${path4} via SDK`,
7624
- [path4]
7940
+ message || `delete ${path5} via SDK`,
7941
+ [path5]
7625
7942
  );
7626
7943
  }
7627
7944
  /**
@@ -7672,15 +7989,15 @@ var Filesystem = class {
7672
7989
  * Read a file's bytes at `version` (the current `main` head or a retained
7673
7990
  * snapshot id; defaults to the filesystem's current head).
7674
7991
  */
7675
- async readFile(path4, version) {
7676
- return (await this.readFileWithMetadata(path4, { version })).data;
7992
+ async readFile(path5, version) {
7993
+ return (await this.readFileWithMetadata(path5, { version })).data;
7677
7994
  }
7678
7995
  /**
7679
7996
  * Read bytes and immutable metadata in one request. A range is executed by
7680
7997
  * Artifact Storage, so callers never download the unrequested remainder.
7681
7998
  */
7682
- async readFileWithMetadata(path4, options = {}) {
7683
- if (trimSlashes(path4) === "") {
7999
+ async readFileWithMetadata(path5, options = {}) {
8000
+ if (trimSlashes(path5) === "") {
7684
8001
  throw new FilesystemError("file path must not be empty");
7685
8002
  }
7686
8003
  const range = options.range;
@@ -7693,12 +8010,12 @@ var Filesystem = class {
7693
8010
  const traced = await callNative2(
7694
8011
  () => this.native.readFilesystemFile(
7695
8012
  this.name,
7696
- path4,
8013
+ path5,
7697
8014
  resolvedVersion,
7698
8015
  range?.offset,
7699
8016
  range?.length
7700
8017
  ),
7701
- new FileNotFoundInFilesystemError(this.name, path4)
8018
+ new FileNotFoundInFilesystemError(this.name, path5)
7702
8019
  );
7703
8020
  const fullSize = traced.fullSize;
7704
8021
  if (!traced.contentId || fullSize === void 0 || !Number.isSafeInteger(fullSize) || fullSize < 0 || traced.data.byteLength > fullSize) {
@@ -7713,9 +8030,9 @@ var Filesystem = class {
7713
8030
  };
7714
8031
  }
7715
8032
  /** Read a file as UTF-8 text at `version`. Throws on non-UTF-8 content. */
7716
- async readText(path4, version) {
8033
+ async readText(path5, version) {
7717
8034
  return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(
7718
- await this.readFile(path4, version)
8035
+ await this.readFile(path5, version)
7719
8036
  );
7720
8037
  }
7721
8038
  /** List one directory (non-recursive) at `version`. */
@@ -7778,11 +8095,11 @@ var FilesystemMount = class {
7778
8095
  path;
7779
8096
  readonly;
7780
8097
  /** @internal — obtain instances via `FilesystemClient.mount()`. */
7781
- constructor(client, cli, filesystem, path4, readonly) {
8098
+ constructor(client, cli, filesystem, path5, readonly) {
7782
8099
  this.client = client;
7783
8100
  this.cli = cli;
7784
8101
  this.filesystem = filesystem;
7785
- this.path = path4;
8102
+ this.path = path5;
7786
8103
  this.readonly = readonly;
7787
8104
  }
7788
8105
  /** Flush pending local changes into a durable snapshot. */
@@ -7800,6 +8117,7 @@ var FilesystemMount = class {
7800
8117
  };
7801
8118
 
7802
8119
  // src/api-client.ts
8120
+ init_sandbox_image();
7803
8121
  init_errors();
7804
8122
  var APIClient = class {
7805
8123
  cloudClient;
@@ -7827,7 +8145,7 @@ var APIClient = class {
7827
8145
  await this.cloudClient.deleteApplication(applicationName);
7828
8146
  }
7829
8147
  async deleteSandboxImage(imageName) {
7830
- await this.cloudClient.deleteSandboxImage(imageName);
8148
+ await deleteSandboxImageWithCredentials(this.filesystemOptions, imageName);
7831
8149
  }
7832
8150
  async createFileSystem(name, description, options) {
7833
8151
  const filesystem = await new FilesystemClient(this.filesystemOptions).create(
@@ -7914,14 +8232,14 @@ var GitCli = class extends MountCli {
7914
8232
  if (options.readOnly && (options.publish || options.workspace !== void 0) || options.publish && options.workspace !== void 0) {
7915
8233
  throw new TypeError("readOnly, publish, and workspace are mutually exclusive");
7916
8234
  }
7917
- const path4 = resolve(localPath);
8235
+ const path5 = resolve(localPath);
7918
8236
  const args = ["mount"];
7919
8237
  if (options.readOnly) args.push("--ro");
7920
8238
  if (options.publish) args.push("--publish");
7921
8239
  if (options.workspace !== void 0) args.push(`--workspace=${options.workspace}`);
7922
- args.push("--", target, path4);
8240
+ args.push("--", target, path5);
7923
8241
  await this.run(args);
7924
- return new RepositoryMount(this, target, path4, options.readOnly ?? false);
8242
+ return new RepositoryMount(this, target, path5, options.readOnly ?? false);
7925
8243
  }
7926
8244
  async unmount(localPath, options = {}) {
7927
8245
  requireNonEmpty(localPath, "localPath");
@@ -7959,44 +8277,44 @@ var GitCli = class extends MountCli {
7959
8277
  raw
7960
8278
  };
7961
8279
  }
7962
- async snapshot(path4, message) {
8280
+ async snapshot(path5, message) {
7963
8281
  const args = ["snapshot"];
7964
8282
  if (message !== void 0) args.push(`--message=${message}`);
7965
- args.push("--", path4);
8283
+ args.push("--", path5);
7966
8284
  await this.run(args);
7967
8285
  }
7968
- async sync(path4, target) {
7969
- const args = ["sync", "--", path4];
8286
+ async sync(path5, target) {
8287
+ const args = ["sync", "--", path5];
7970
8288
  if (target !== void 0) {
7971
8289
  requireNonEmpty(target, "target");
7972
8290
  args.push(target);
7973
8291
  }
7974
8292
  await this.run(args);
7975
8293
  }
7976
- async rebase(path4, target, failOnConflict) {
8294
+ async rebase(path5, target, failOnConflict) {
7977
8295
  requireNonEmpty(target, "target");
7978
8296
  const args = ["rebase"];
7979
8297
  if (failOnConflict) args.push("--fail-on-conflict");
7980
- args.push("--", path4, target);
8298
+ args.push("--", path5, target);
7981
8299
  await this.run(args);
7982
8300
  }
7983
- async promote(path4, branch, merge) {
8301
+ async promote(path5, branch, merge) {
7984
8302
  requireNonEmpty(branch, "branch");
7985
8303
  const args = ["promote"];
7986
8304
  if (merge) args.push("--merge");
7987
- args.push("--", path4, branch);
8305
+ args.push("--", path5, branch);
7988
8306
  await this.run(args);
7989
8307
  }
7990
- async prefetch(path4) {
7991
- await this.run(["prefetch", "--", path4]);
8308
+ async prefetch(path5) {
8309
+ await this.run(["prefetch", "--", path5]);
7992
8310
  }
7993
8311
  };
7994
8312
  var RepositoryMount = class {
7995
8313
  /** @internal Obtain instances via RepositoryClient.mount(). */
7996
- constructor(cli, target, path4, readOnly) {
8314
+ constructor(cli, target, path5, readOnly) {
7997
8315
  this.cli = cli;
7998
8316
  this.target = target;
7999
- this.path = path4;
8317
+ this.path = path5;
8000
8318
  this.readOnly = readOnly;
8001
8319
  }
8002
8320
  /** Inspect workspace state, pending changes, and daemon diagnostics. */
@@ -8231,6 +8549,7 @@ function validateTimeoutMs2(timeoutMs) {
8231
8549
 
8232
8550
  // src/index.ts
8233
8551
  init_sandbox_image();
8552
+ init_image_builds();
8234
8553
 
8235
8554
  // src/file-system.ts
8236
8555
  init_esm_shims();
@@ -9614,38 +9933,38 @@ init_esm_shims();
9614
9933
  import { createHash } from "crypto";
9615
9934
  var textEncoder = new TextEncoder();
9616
9935
  var textDecoder = new TextDecoder("utf-8", { fatal: true });
9617
- function assertJSONValue(value, path4, seen) {
9936
+ function assertJSONValue(value, path5, seen) {
9618
9937
  if (value === null || typeof value === "string" || typeof value === "boolean") return;
9619
9938
  if (typeof value === "number") {
9620
- if (!Number.isFinite(value)) throw new SerializationError(`${path4} contains a non-finite number`);
9939
+ if (!Number.isFinite(value)) throw new SerializationError(`${path5} contains a non-finite number`);
9621
9940
  return;
9622
9941
  }
9623
9942
  if (typeof value !== "object") {
9624
- throw new SerializationError(`${path4} contains unsupported ${typeof value}`);
9943
+ throw new SerializationError(`${path5} contains unsupported ${typeof value}`);
9625
9944
  }
9626
9945
  if (isFile(value)) {
9627
- throw new SerializationError(`${path4} contains a nested File; File is only supported as a direct value`);
9946
+ throw new SerializationError(`${path5} contains a nested File; File is only supported as a direct value`);
9628
9947
  }
9629
9948
  if (isHttpBody(value)) {
9630
9949
  throw new SerializationError(
9631
- `${path4} contains a nested HttpBody; HttpBody is only supported as a direct value`
9950
+ `${path5} contains a nested HttpBody; HttpBody is only supported as a direct value`
9632
9951
  );
9633
9952
  }
9634
- if (seen.has(value)) throw new SerializationError(`${path4} contains a cycle`);
9953
+ if (seen.has(value)) throw new SerializationError(`${path5} contains a cycle`);
9635
9954
  if (Object.getPrototypeOf(value) !== Object.prototype && !Array.isArray(value)) {
9636
- throw new SerializationError(`${path4} contains unsupported ${value.constructor?.name ?? "object"}`);
9955
+ throw new SerializationError(`${path5} contains unsupported ${value.constructor?.name ?? "object"}`);
9637
9956
  }
9638
9957
  seen.add(value);
9639
9958
  if (Array.isArray(value)) {
9640
9959
  for (let index = 0; index < value.length; index += 1) {
9641
9960
  if (!Object.prototype.hasOwnProperty.call(value, index)) {
9642
- throw new SerializationError(`${path4} contains a sparse array entry at index ${index}`);
9961
+ throw new SerializationError(`${path5} contains a sparse array entry at index ${index}`);
9643
9962
  }
9644
- assertJSONValue(value[index], `${path4}[${index}]`, seen);
9963
+ assertJSONValue(value[index], `${path5}[${index}]`, seen);
9645
9964
  }
9646
9965
  } else {
9647
9966
  for (const [key, item] of Object.entries(value)) {
9648
- assertJSONValue(item, `${path4}.${key}`, seen);
9967
+ assertJSONValue(item, `${path5}.${key}`, seen);
9649
9968
  }
9650
9969
  }
9651
9970
  seen.delete(value);
@@ -10094,7 +10413,15 @@ export {
10094
10413
  Headers,
10095
10414
  HttpBody,
10096
10415
  Image,
10416
+ ImageBuildCancelledError,
10417
+ ImageBuildError,
10418
+ ImageBuildFailedError,
10419
+ ImageBuildHandle,
10420
+ ImageBuildInProgressError,
10421
+ ImageBuildInterruptedError,
10422
+ ImageBuildNotFoundError,
10097
10423
  ImageBuildOperationType,
10424
+ ImageBuildTimeoutError,
10098
10425
  MountError,
10099
10426
  OutputMode,
10100
10427
  PendingSandbox,
@@ -10127,12 +10454,14 @@ export {
10127
10454
  TERMINATION_REASON_CANCELLED,
10128
10455
  TERMINATION_REASON_NO_CAPACITY,
10129
10456
  TcpTunnel,
10457
+ cancelImageBuild,
10130
10458
  createFileSystem,
10131
10459
  createSandboxImage,
10132
10460
  deleteFileSystem,
10133
10461
  deleteSandboxImage,
10134
10462
  dockerfileContent,
10135
10463
  findSandboxImageByName,
10464
+ getImageBuild,
10136
10465
  importSandboxImage,
10137
10466
  isSandboxPending,
10138
10467
  listFileSystems,
@@ -10144,6 +10473,9 @@ export {
10144
10473
  runLocal,
10145
10474
  runRemote,
10146
10475
  sandboxUrlFromIngressEndpoint,
10147
- schema
10476
+ schema,
10477
+ startImageBuild,
10478
+ startImageImport,
10479
+ waitImageBuild
10148
10480
  };
10149
10481
  //# sourceMappingURL=index.js.map