tensorlake 0.5.126 → 0.5.128

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -49,7 +49,7 @@ var init_defaults = __esm({
49
49
  "src/defaults.ts"() {
50
50
  "use strict";
51
51
  init_cjs_shims();
52
- SDK_VERSION = "0.5.126";
52
+ SDK_VERSION = "0.5.128";
53
53
  API_URL = process.env.TENSORLAKE_API_URL ?? "https://api.tensorlake.ai";
54
54
  API_KEY = process.env.TENSORLAKE_API_KEY ?? void 0;
55
55
  NAMESPACE = process.env.INDEXIFY_NAMESPACE ?? "default";
@@ -84,6 +84,27 @@ function describeError(err) {
84
84
  if (parts.length > 0) return parts.join(": ");
85
85
  return err instanceof Error ? err.message : String(err);
86
86
  }
87
+ function formatErrorDetails(errorDetails) {
88
+ if (errorDetails == null) return void 0;
89
+ if (typeof errorDetails === "string") {
90
+ const detail = errorDetails.trim();
91
+ return detail || void 0;
92
+ }
93
+ if (Array.isArray(errorDetails)) {
94
+ const parts = errorDetails.map((item) => formatErrorDetails(item)).filter((item) => Boolean(item));
95
+ return parts.length > 0 ? parts.join("; ") : JSON.stringify(errorDetails);
96
+ }
97
+ if (typeof errorDetails === "object") {
98
+ for (const key of ["message", "detail", "error", "reason"]) {
99
+ const value = errorDetails[key];
100
+ if (typeof value === "string" && value.trim()) {
101
+ return value.trim();
102
+ }
103
+ }
104
+ return JSON.stringify(errorDetails);
105
+ }
106
+ return String(errorDetails);
107
+ }
87
108
  var SandboxException, SandboxError, SandboxConnectionError, SandboxNotFoundError, PoolNotFoundError, PoolInUseError, RemoteAPIError, RequestNotFinishedError, RequestFailedError, RequestExecutionError;
88
109
  var init_errors = __esm({
89
110
  "src/errors.ts"() {
@@ -135,9 +156,39 @@ var init_errors = __esm({
135
156
  };
136
157
  RemoteAPIError = class extends SandboxError {
137
158
  statusCode;
159
+ /** Original response body, retained for compatibility and diagnostics. */
138
160
  responseMessage;
161
+ sandboxId;
162
+ /** Server reason, including unknown future reasons. */
163
+ reason;
164
+ errorDetails;
139
165
  constructor(statusCode, message) {
140
- super(`API error (status ${statusCode}): ${message}`);
166
+ let failure;
167
+ try {
168
+ const payload = JSON.parse(message);
169
+ if (payload && typeof payload === "object" && !Array.isArray(payload)) {
170
+ const record = payload;
171
+ if (typeof record.sandbox_id === "string" && record.sandbox_id.trim() && (record.status === "failed" || record.status === "terminated"))
172
+ failure = record;
173
+ }
174
+ } catch {
175
+ }
176
+ const sandboxId = failure?.sandbox_id;
177
+ const rawReason = failure?.reason ?? failure?.termination_reason;
178
+ const reason = typeof rawReason === "string" ? rawReason : void 0;
179
+ const errorDetails = failure?.error_details;
180
+ let displayMessage = message;
181
+ if (failure) {
182
+ displayMessage = `Sandbox ${sandboxId} ${failure.status}`;
183
+ if (reason) displayMessage += ` (${reason})`;
184
+ const detail = formatErrorDetails(errorDetails);
185
+ if (detail) displayMessage += `: ${detail}`;
186
+ if (!reason && !detail) displayMessage = message;
187
+ }
188
+ super(`API error (status ${statusCode}): ${displayMessage}`);
189
+ this.sandboxId = sandboxId;
190
+ this.reason = reason;
191
+ this.errorDetails = errorDetails;
141
192
  this.name = "RemoteAPIError";
142
193
  this.statusCode = statusCode;
143
194
  this.responseMessage = message;
@@ -318,10 +369,10 @@ var init_native_worker_client = __esm({
318
369
  call(handle, method, args, emit) {
319
370
  const id = this.nextId++;
320
371
  let worker;
321
- const promise = new Promise((resolve, reject) => {
372
+ const promise = new Promise((resolve2, reject) => {
322
373
  try {
323
374
  worker = this.getWorker();
324
- this.pending.set(id, { resolve, reject, emit, handle });
375
+ this.pending.set(id, { resolve: resolve2, reject, emit, handle });
325
376
  worker.ref();
326
377
  if (handle) this.register(handle, worker);
327
378
  this.send(worker, {
@@ -424,7 +475,7 @@ var init_native_worker_client = __esm({
424
475
  }
425
476
  };
426
477
  runtimeKey = /* @__PURE__ */ Symbol.for(
427
- `tensorlake.native.worker.${false ? "source" : "0.5.126"}.v1`
478
+ `tensorlake.native.worker.${false ? "source" : "0.5.128"}.v1`
428
479
  );
429
480
  runtimes = globalThis;
430
481
  }
@@ -492,9 +543,9 @@ async function* nativeEventStream(start, context, signal) {
492
543
  let stopped = false;
493
544
  let failure;
494
545
  const notify = () => {
495
- const resolve = wake;
546
+ const resolve2 = wake;
496
547
  wake = void 0;
497
- resolve?.();
548
+ resolve2?.();
498
549
  };
499
550
  const emit = (value) => {
500
551
  if (stopped) return Promise.resolve();
@@ -529,8 +580,8 @@ async function* nativeEventStream(start, context, signal) {
529
580
  } else if (finished) {
530
581
  break;
531
582
  } else {
532
- await new Promise((resolve) => {
533
- wake = resolve;
583
+ await new Promise((resolve2) => {
584
+ wake = resolve2;
534
585
  });
535
586
  }
536
587
  }
@@ -645,6 +696,7 @@ var init_models = __esm({
645
696
  SandboxStatus2["SUSPENDED"] = "suspended";
646
697
  SandboxStatus2["TERMINATED"] = "terminated";
647
698
  SandboxStatus2["TIMEOUT"] = "timeout";
699
+ SandboxStatus2["FAILED"] = "failed";
648
700
  return SandboxStatus2;
649
701
  })(SandboxStatus || {});
650
702
  SnapshotStatus = /* @__PURE__ */ ((SnapshotStatus2) => {
@@ -684,7 +736,7 @@ var init_models = __esm({
684
736
  async function openTunnelWebSocket(options) {
685
737
  const wsUrl = buildTunnelWsUrl(options.baseUrl, options.remotePort);
686
738
  const timeoutMs = options.connectTimeoutMs ?? DEFAULT_TUNNEL_CONNECT_TIMEOUT_MS;
687
- return new Promise((resolve, reject) => {
739
+ return new Promise((resolve2, reject) => {
688
740
  const socket = new import_ws.default(wsUrl, {
689
741
  headers: options.wsHeaders
690
742
  });
@@ -715,7 +767,7 @@ async function openTunnelWebSocket(options) {
715
767
  });
716
768
  };
717
769
  const onOpen = () => {
718
- settle(() => resolve(socket));
770
+ settle(() => resolve2(socket));
719
771
  };
720
772
  const onError = (error) => {
721
773
  fail(new SandboxConnectionError(error.message));
@@ -758,7 +810,7 @@ function normalizeWebSocketData(message) {
758
810
  return Buffer.from(message);
759
811
  }
760
812
  async function relaySocket(localSocket, websocket) {
761
- return new Promise((resolve) => {
813
+ return new Promise((resolve2) => {
762
814
  let settled = false;
763
815
  const keepAliveInterval = setInterval(() => {
764
816
  if (websocket.readyState === import_ws.default.OPEN) {
@@ -771,7 +823,7 @@ async function relaySocket(localSocket, websocket) {
771
823
  settled = true;
772
824
  clearInterval(keepAliveInterval);
773
825
  cleanup();
774
- resolve();
826
+ resolve2();
775
827
  };
776
828
  const cleanup = () => {
777
829
  localSocket.removeListener("data", onLocalData);
@@ -849,7 +901,7 @@ async function relaySocket(localSocket, websocket) {
849
901
  });
850
902
  }
851
903
  async function listenServer(server, localPort, localHost) {
852
- await new Promise((resolve, reject) => {
904
+ await new Promise((resolve2, reject) => {
853
905
  const onError = (error) => {
854
906
  server.removeListener("listening", onListening);
855
907
  reject(
@@ -860,7 +912,7 @@ async function listenServer(server, localPort, localHost) {
860
912
  };
861
913
  const onListening = () => {
862
914
  server.removeListener("error", onError);
863
- resolve();
915
+ resolve2();
864
916
  };
865
917
  server.once("error", onError);
866
918
  server.once("listening", onListening);
@@ -886,13 +938,13 @@ function secondsToMillis(seconds) {
886
938
  return Math.round(seconds * 1e3);
887
939
  }
888
940
  async function withTimeout(timeoutMs, operation, timeoutMessage) {
889
- return new Promise((resolve, reject) => {
941
+ return new Promise((resolve2, reject) => {
890
942
  const timer = setTimeout(() => {
891
943
  reject(new SandboxError(timeoutMessage));
892
944
  }, timeoutMs);
893
945
  void operation().then((value) => {
894
946
  clearTimeout(timer);
895
- resolve(value);
947
+ resolve2(value);
896
948
  }).catch((error) => {
897
949
  clearTimeout(timer);
898
950
  reject(error);
@@ -968,8 +1020,8 @@ var init_tunnel = __esm({
968
1020
  if (this.closeError) {
969
1021
  throw this.closeError;
970
1022
  }
971
- return new Promise((resolve, reject) => {
972
- this.pendingReads.push({ length, resolve, reject });
1023
+ return new Promise((resolve2, reject) => {
1024
+ this.pendingReads.push({ length, resolve: resolve2, reject });
973
1025
  });
974
1026
  }
975
1027
  async writeAll(data) {
@@ -980,13 +1032,13 @@ var init_tunnel = __esm({
980
1032
  throw new SandboxError("desktop tunnel is not connected");
981
1033
  }
982
1034
  const payload = Buffer.from(data);
983
- await new Promise((resolve, reject) => {
1035
+ await new Promise((resolve2, reject) => {
984
1036
  this.socket.send(payload, { binary: true }, (error) => {
985
1037
  if (error) {
986
1038
  reject(new SandboxConnectionError(error.message));
987
1039
  return;
988
1040
  }
989
- resolve();
1041
+ resolve2();
990
1042
  });
991
1043
  });
992
1044
  }
@@ -998,10 +1050,10 @@ var init_tunnel = __esm({
998
1050
  this.closePromise = Promise.resolve();
999
1051
  return this.closePromise;
1000
1052
  }
1001
- this.closePromise = new Promise((resolve) => {
1053
+ this.closePromise = new Promise((resolve2) => {
1002
1054
  const onClose = () => {
1003
1055
  this.socket.removeListener("close", onClose);
1004
- resolve();
1056
+ resolve2();
1005
1057
  };
1006
1058
  this.socket.on("close", onClose);
1007
1059
  this.socket.close();
@@ -1095,13 +1147,13 @@ var init_tunnel = __esm({
1095
1147
  relay.localSocket.destroy();
1096
1148
  relay.websocket?.close();
1097
1149
  }
1098
- this.closePromise = new Promise((resolve, reject) => {
1150
+ this.closePromise = new Promise((resolve2, reject) => {
1099
1151
  this.server.close((error) => {
1100
1152
  if (error) {
1101
1153
  reject(new SandboxError(`failed to close tunnel listener: ${error.message}`));
1102
1154
  return;
1103
1155
  }
1104
- resolve();
1156
+ resolve2();
1105
1157
  });
1106
1158
  });
1107
1159
  return this.closePromise;
@@ -1329,14 +1381,14 @@ function isReconnectableDesktopScreenshotError(error) {
1329
1381
  }
1330
1382
  function createDeferredSignal() {
1331
1383
  let resolveCurrent;
1332
- let promise = new Promise((resolve) => {
1333
- resolveCurrent = resolve;
1384
+ let promise = new Promise((resolve2) => {
1385
+ resolveCurrent = resolve2;
1334
1386
  });
1335
1387
  return {
1336
1388
  resolve() {
1337
1389
  resolveCurrent();
1338
- promise = new Promise((resolve) => {
1339
- resolveCurrent = resolve;
1390
+ promise = new Promise((resolve2) => {
1391
+ resolveCurrent = resolve2;
1340
1392
  });
1341
1393
  },
1342
1394
  wait() {
@@ -1681,7 +1733,7 @@ var init_desktop = __esm({
1681
1733
  validateNonNegativeInteger(delayMs, "double click delay");
1682
1734
  await this.click({ button, x: options?.x, y: options?.y });
1683
1735
  if (delayMs > 0) {
1684
- await new Promise((resolve) => setTimeout(resolve, delayMs));
1736
+ await new Promise((resolve2) => setTimeout(resolve2, delayMs));
1685
1737
  }
1686
1738
  await this.click({ button });
1687
1739
  }
@@ -3146,7 +3198,7 @@ function processUserPayload(user) {
3146
3198
  return user;
3147
3199
  }
3148
3200
  function sleep(ms) {
3149
- return new Promise((resolve) => setTimeout(resolve, ms));
3201
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
3150
3202
  }
3151
3203
  function normalizePtyMessage(message) {
3152
3204
  if (Buffer.isBuffer(message)) return message;
@@ -3167,8 +3219,8 @@ function encodePtyResize(cols, rows) {
3167
3219
  return frame;
3168
3220
  }
3169
3221
  function sendPtyFrame(socket, frame) {
3170
- return new Promise((resolve, reject) => {
3171
- socket.send(frame, (error) => error ? reject(error) : resolve());
3222
+ return new Promise((resolve2, reject) => {
3223
+ socket.send(frame, (error) => error ? reject(error) : resolve2());
3172
3224
  });
3173
3225
  }
3174
3226
  function markBindOutcome(sandbox, outcome) {
@@ -3410,8 +3462,8 @@ var init_sandbox = __esm({
3410
3462
  this.wsUrl = options.wsUrl;
3411
3463
  this.wsHeaders = options.wsHeaders;
3412
3464
  this.killSession = options.killSession;
3413
- this.waitPromise = new Promise((resolve, reject) => {
3414
- this.resolveWait = resolve;
3465
+ this.waitPromise = new Promise((resolve2, reject) => {
3466
+ this.resolveWait = resolve2;
3415
3467
  this.rejectWait = reject;
3416
3468
  });
3417
3469
  }
@@ -3434,7 +3486,7 @@ var init_sandbox = __esm({
3434
3486
  return this.connectPromise;
3435
3487
  }
3436
3488
  this.intentionalDisconnect = false;
3437
- this.connectPromise = new Promise((resolve, reject) => {
3489
+ this.connectPromise = new Promise((resolve2, reject) => {
3438
3490
  let opened = false;
3439
3491
  const socket = new import_ws2.default(this.wsUrl, {
3440
3492
  headers: this.wsHeaders
@@ -3444,7 +3496,7 @@ var init_sandbox = __esm({
3444
3496
  try {
3445
3497
  await sendPtyFrame(socket, Buffer.from([PTY_OP_READY]));
3446
3498
  opened = true;
3447
- resolve(this);
3499
+ resolve2(this);
3448
3500
  } catch (error) {
3449
3501
  reject(error);
3450
3502
  }
@@ -4375,7 +4427,7 @@ var init_sandbox = __esm({
4375
4427
  const remainingMs = deadlineMs - Date.now();
4376
4428
  if (remainingMs <= 0) break;
4377
4429
  await new Promise(
4378
- (resolve) => setTimeout(resolve, Math.min(probeIntervalMs, remainingMs))
4430
+ (resolve2) => setTimeout(resolve2, Math.min(probeIntervalMs, remainingMs))
4379
4431
  );
4380
4432
  }
4381
4433
  const detail = lastError instanceof Error ? `: ${lastError.message}` : "";
@@ -4482,7 +4534,7 @@ function validateTimeoutMs(timeoutMs) {
4482
4534
  }
4483
4535
  }
4484
4536
  function sleep2(ms) {
4485
- return new Promise((resolve) => setTimeout(resolve, ms));
4537
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
4486
4538
  }
4487
4539
  function snapshotStatusSatisfiesWaitCondition(status, waitUntil) {
4488
4540
  if (waitUntil === "local_ready") {
@@ -4491,37 +4543,16 @@ function snapshotStatusSatisfiesWaitCondition(status, waitUntil) {
4491
4543
  return status === "completed" /* COMPLETED */;
4492
4544
  }
4493
4545
  function formatStartupFailureMessage(sandboxId, status, options) {
4494
- const prefix = status === "terminated" /* TERMINATED */ ? `Sandbox ${sandboxId} terminated during startup` : `Sandbox ${sandboxId} became ${status} during startup`;
4546
+ let prefix = status === "terminated" /* TERMINATED */ ? `Sandbox ${sandboxId} terminated during startup` : `Sandbox ${sandboxId} became ${status} during startup`;
4547
+ if (options.terminationReason) {
4548
+ prefix += ` (${options.terminationReason})`;
4549
+ }
4495
4550
  const detail = formatErrorDetails(options.errorDetails);
4496
4551
  if (detail) {
4497
4552
  return `${prefix}: ${detail}`;
4498
4553
  }
4499
- if (options.terminationReason) {
4500
- return `${prefix}: termination reason: ${options.terminationReason}`;
4501
- }
4502
4554
  return prefix;
4503
4555
  }
4504
- function formatErrorDetails(errorDetails) {
4505
- if (errorDetails == null) return void 0;
4506
- if (typeof errorDetails === "string") {
4507
- const detail = errorDetails.trim();
4508
- return detail || void 0;
4509
- }
4510
- if (Array.isArray(errorDetails)) {
4511
- const parts = errorDetails.map((item) => formatErrorDetails(item)).filter((item) => Boolean(item));
4512
- return parts.length > 0 ? parts.join("; ") : JSON.stringify(errorDetails);
4513
- }
4514
- if (typeof errorDetails === "object") {
4515
- for (const key of ["message", "detail", "error", "reason"]) {
4516
- const value = errorDetails[key];
4517
- if (typeof value === "string" && value.trim()) {
4518
- return value.trim();
4519
- }
4520
- }
4521
- return JSON.stringify(errorDetails);
4522
- }
4523
- return String(errorDetails);
4524
- }
4525
4556
  function normalizeUserPorts(ports) {
4526
4557
  return dedupeAndSortPorts(ports.map(validateUserPort));
4527
4558
  }
@@ -5203,11 +5234,11 @@ var init_client = __esm({
5203
5234
  result.sandboxUrl
5204
5235
  );
5205
5236
  }
5206
- if (result.status === "suspended" /* SUSPENDED */ || result.status === "terminated" /* TERMINATED */) {
5237
+ if (result.status === "suspended" /* SUSPENDED */ || result.status === "terminated" /* TERMINATED */ || result.status === "failed" /* FAILED */) {
5207
5238
  throw new SandboxError(
5208
5239
  formatStartupFailureMessage(result.sandboxId, result.status, {
5209
5240
  errorDetails: result.errorDetails,
5210
- terminationReason: result.terminationReason
5241
+ terminationReason: result.terminationReason ?? result.reason
5211
5242
  })
5212
5243
  );
5213
5244
  }
@@ -5326,7 +5357,7 @@ function anySignal(signals) {
5326
5357
  return controller.signal;
5327
5358
  }
5328
5359
  function sleep3(ms) {
5329
- return new Promise((resolve) => setTimeout(resolve, ms));
5360
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
5330
5361
  }
5331
5362
  var import_undici, REQUEST_TIMEOUT_HEADER, HttpClient;
5332
5363
  var init_http = __esm({
@@ -6314,9 +6345,9 @@ var require_libc = __commonJS({
6314
6345
  // src/native-binding.ts
6315
6346
  function packageRoot() {
6316
6347
  const parent = (0, import_node_url.fileURLToPath)(new URL("../", importMetaUrl));
6317
- if (fs.existsSync(import_node_path.default.join(parent, "package.json"))) return parent;
6348
+ if (fs.existsSync(import_node_path2.default.join(parent, "package.json"))) return parent;
6318
6349
  const grandparent = (0, import_node_url.fileURLToPath)(new URL("../../", importMetaUrl));
6319
- if (fs.existsSync(import_node_path.default.join(grandparent, "package.json"))) return grandparent;
6350
+ if (fs.existsSync(import_node_path2.default.join(grandparent, "package.json"))) return grandparent;
6320
6351
  return parent;
6321
6352
  }
6322
6353
  function linuxLibcFamily(options) {
@@ -6339,7 +6370,7 @@ function nativeTargetId(platform = process.platform, arch = process.arch, option
6339
6370
  function nativeBindingPath(options = {}) {
6340
6371
  const platform = options.platform ?? process.platform;
6341
6372
  const arch = options.arch ?? process.arch;
6342
- return import_node_path.default.join(
6373
+ return import_node_path2.default.join(
6343
6374
  options.packageRoot ?? packageRoot(),
6344
6375
  "dist",
6345
6376
  "native",
@@ -6372,7 +6403,7 @@ function loadNative() {
6372
6403
  }
6373
6404
  return require2(packageEntry);
6374
6405
  }
6375
- var import_libc, fs, import_node_module2, import_node_path, import_node_url, NATIVE_PACKAGES, cachedLinuxLibc;
6406
+ var import_libc, fs, import_node_module2, import_node_path2, import_node_url, NATIVE_PACKAGES, cachedLinuxLibc;
6376
6407
  var init_native_binding = __esm({
6377
6408
  "src/native-binding.ts"() {
6378
6409
  "use strict";
@@ -6380,7 +6411,7 @@ var init_native_binding = __esm({
6380
6411
  import_libc = __toESM(require_libc(), 1);
6381
6412
  fs = __toESM(require("fs"), 1);
6382
6413
  import_node_module2 = require("module");
6383
- import_node_path = __toESM(require("path"), 1);
6414
+ import_node_path2 = __toESM(require("path"), 1);
6384
6415
  import_node_url = require("url");
6385
6416
  NATIVE_PACKAGES = {
6386
6417
  "darwin-arm64": "tensorlake-native-darwin-arm64",
@@ -6438,9 +6469,9 @@ function buildContextFromEnv() {
6438
6469
  };
6439
6470
  }
6440
6471
  function defaultRegisteredName(dockerfilePath) {
6441
- const parsed = import_node_path2.default.parse(dockerfilePath);
6472
+ const parsed = import_node_path3.default.parse(dockerfilePath);
6442
6473
  if (parsed.name.toLowerCase() === "dockerfile") {
6443
- const parentName = import_node_path2.default.basename(import_node_path2.default.dirname(dockerfilePath)).trim();
6474
+ const parentName = import_node_path3.default.basename(import_node_path3.default.dirname(dockerfilePath)).trim();
6444
6475
  return parentName || "sandbox-image";
6445
6476
  }
6446
6477
  return parsed.name || "sandbox-image";
@@ -6530,24 +6561,24 @@ async function createSandboxImage(source, options = {}, deps = {}) {
6530
6561
  }
6531
6562
  let resolvedContext;
6532
6563
  if (options.contextDir != null) {
6533
- resolvedContext = import_node_path2.default.resolve(options.contextDir);
6564
+ resolvedContext = import_node_path3.default.resolve(options.contextDir);
6534
6565
  } else if (imageRequiresContext(source)) {
6535
6566
  throw new Error(
6536
6567
  "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>`."
6537
6568
  );
6538
6569
  } else {
6539
- tempContextDir = (0, import_node_fs.mkdtempSync)(import_node_path2.default.join(import_node_os.default.tmpdir(), "tl-build-context-"));
6570
+ tempContextDir = (0, import_node_fs2.mkdtempSync)(import_node_path3.default.join(import_node_os2.default.tmpdir(), "tl-build-context-"));
6540
6571
  resolvedContext = tempContextDir;
6541
6572
  }
6542
- dockerfilePath = import_node_path2.default.join(resolvedContext, "Dockerfile");
6573
+ dockerfilePath = import_node_path3.default.join(resolvedContext, "Dockerfile");
6543
6574
  let text = dockerfileContent(source);
6544
6575
  if (!text.endsWith("\n")) text += "\n";
6545
6576
  dockerfileText = text;
6546
6577
  nativeContextDir = resolvedContext;
6547
6578
  effectiveName = options.registeredName ?? source.name;
6548
6579
  } else if (typeof source === "string") {
6549
- const resolvedPath = import_node_path2.default.resolve(source);
6550
- if (!(0, import_node_fs.existsSync)(resolvedPath)) {
6580
+ const resolvedPath = import_node_path3.default.resolve(source);
6581
+ if (!(0, import_node_fs2.existsSync)(resolvedPath)) {
6551
6582
  throw new Error(`Dockerfile not found: ${source}`);
6552
6583
  }
6553
6584
  dockerfilePath = resolvedPath;
@@ -6624,7 +6655,7 @@ async function createSandboxImage(source, options = {}, deps = {}) {
6624
6655
  return result;
6625
6656
  } finally {
6626
6657
  if (tempContextDir != null) {
6627
- (0, import_node_fs.rmSync)(tempContextDir, { recursive: true, force: true });
6658
+ (0, import_node_fs2.rmSync)(tempContextDir, { recursive: true, force: true });
6628
6659
  }
6629
6660
  }
6630
6661
  }
@@ -6773,7 +6804,7 @@ async function listSandboxImages() {
6773
6804
  }
6774
6805
  }
6775
6806
  async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
6776
- const parsed = (0, import_node_util.parseArgs)({
6807
+ const parsed = (0, import_node_util2.parseArgs)({
6777
6808
  args: argv,
6778
6809
  allowPositionals: true,
6779
6810
  options: {
@@ -6825,7 +6856,7 @@ async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
6825
6856
  );
6826
6857
  }
6827
6858
  async function runImportSandboxImageCli(argv = process.argv.slice(2)) {
6828
- const parsed = (0, import_node_util.parseArgs)({
6859
+ const parsed = (0, import_node_util2.parseArgs)({
6829
6860
  args: argv,
6830
6861
  allowPositionals: true,
6831
6862
  options: {
@@ -6876,15 +6907,15 @@ async function runImportSandboxImageCli(argv = process.argv.slice(2)) {
6876
6907
  { emit: ndjsonStdoutEmit, _allowPat: true }
6877
6908
  );
6878
6909
  }
6879
- var import_node_path2, import_node_os, import_node_fs, import_node_util, cachedNativeBinding, cachedNativeBindingError, DEFAULT_IMAGE_NAME2;
6910
+ var import_node_path3, import_node_os2, import_node_fs2, import_node_util2, cachedNativeBinding, cachedNativeBindingError, DEFAULT_IMAGE_NAME2;
6880
6911
  var init_sandbox_image = __esm({
6881
6912
  "src/sandbox-image.ts"() {
6882
6913
  "use strict";
6883
6914
  init_cjs_shims();
6884
- import_node_path2 = __toESM(require("path"), 1);
6885
- import_node_os = __toESM(require("os"), 1);
6886
- import_node_fs = require("fs");
6887
- import_node_util = require("util");
6915
+ import_node_path3 = __toESM(require("path"), 1);
6916
+ import_node_os2 = __toESM(require("os"), 1);
6917
+ import_node_fs2 = require("fs");
6918
+ import_node_util2 = require("util");
6888
6919
  init_cloud_client();
6889
6920
  init_image();
6890
6921
  init_native_binding();
@@ -6922,6 +6953,7 @@ __export(index_exports, {
6922
6953
  ReadOnlyMountNotSupportedError: () => ReadOnlyMountNotSupportedError,
6923
6954
  RemoteAPIError: () => RemoteAPIError,
6924
6955
  RepositoryClient: () => RepositoryClient,
6956
+ RepositoryMount: () => RepositoryMount,
6925
6957
  RequestContext: () => RequestContext,
6926
6958
  RequestExecutionError: () => RequestExecutionError,
6927
6959
  RequestFailedError: () => RequestFailedError,
@@ -6969,13 +7001,15 @@ init_cloud_client();
6969
7001
 
6970
7002
  // src/filesystem.ts
6971
7003
  init_cjs_shims();
6972
- var import_node_child_process = require("child_process");
6973
7004
  var import_node_crypto2 = require("crypto");
6974
- var import_node_fs2 = require("fs");
6975
- var import_node_os2 = require("os");
6976
- var import_node_path3 = require("path");
6977
- var import_node_util2 = require("util");
6978
- init_defaults();
7005
+
7006
+ // src/mount-cli.ts
7007
+ init_cjs_shims();
7008
+ var import_node_child_process = require("child_process");
7009
+ var import_node_fs = require("fs");
7010
+ var import_node_os = require("os");
7011
+ var import_node_path = require("path");
7012
+ var import_node_util = require("util");
6979
7013
 
6980
7014
  // src/filesystem-models.ts
6981
7015
  init_cjs_shims();
@@ -7055,18 +7089,72 @@ var ReadOnlyMountNotSupportedError = class extends MountError {
7055
7089
  }
7056
7090
  };
7057
7091
  var CliNotFoundError = class extends MountError {
7058
- constructor(detail) {
7092
+ constructor(detail, command = "fs") {
7059
7093
  super(
7060
- `Mount operations require the Tensorlake CLI (\`tl\`) with \`tl fs\` support: ${detail}. Install or upgrade it with: curl -fsSL https://tensorlake.ai/install.sh | sh`
7094
+ `Mount operations require the Tensorlake CLI (\`tl\`) with \`tl ${command}\` support: ${detail}. Install or upgrade it with: curl -fsSL https://tensorlake.ai/install.sh | sh`
7061
7095
  );
7062
7096
  this.name = "CliNotFoundError";
7063
7097
  }
7064
7098
  };
7065
7099
 
7100
+ // src/mount-cli.ts
7101
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
7102
+ var MountCli = class {
7103
+ constructor(command, envOverrides) {
7104
+ this.command = command;
7105
+ this.envOverrides = envOverrides;
7106
+ }
7107
+ binary = null;
7108
+ async findCli() {
7109
+ const candidates = [];
7110
+ if (process.env.TENSORLAKE_CLI) candidates.push(process.env.TENSORLAKE_CLI);
7111
+ candidates.push("tl");
7112
+ const installed = (0, import_node_path.join)((0, import_node_os.homedir)(), ".tensorlake", "bin", "tl");
7113
+ try {
7114
+ (0, import_node_fs.accessSync)(installed, import_node_fs.constants.F_OK);
7115
+ candidates.push(installed);
7116
+ } catch {
7117
+ }
7118
+ const support = this.command === "git" ? "git mount" : "fs";
7119
+ const probe = this.command === "git" ? ["git", "mount", "--help"] : ["fs", "--help"];
7120
+ let unsupported = null;
7121
+ for (const candidate of candidates) {
7122
+ try {
7123
+ await execFileAsync(candidate, probe, { timeout: 15e3 });
7124
+ return candidate;
7125
+ } catch (error) {
7126
+ if (error.code !== "ENOENT") unsupported = candidate;
7127
+ }
7128
+ }
7129
+ throw new CliNotFoundError(
7130
+ unsupported ? `\`tl\` at ${unsupported} does not support \`tl ${support}\` (upgrade required)` : "`tl` was not found on PATH",
7131
+ support
7132
+ );
7133
+ }
7134
+ async run(args, timeoutMs = 3e5) {
7135
+ if (this.binary === null) this.binary = await this.findCli();
7136
+ try {
7137
+ const { stdout } = await execFileAsync(this.binary, [this.command, ...args], {
7138
+ timeout: timeoutMs,
7139
+ env: { ...process.env, ...this.envOverrides },
7140
+ maxBuffer: 16 * 1024 * 1024
7141
+ });
7142
+ return stdout;
7143
+ } catch (error) {
7144
+ const failure = error;
7145
+ if (failure.killed) {
7146
+ throw new MountError(`\`tl ${this.command} ${args[0]}\` timed out after ${timeoutMs}ms`);
7147
+ }
7148
+ const detail = (failure.stderr || failure.stdout || String(error)).trim();
7149
+ throw new MountError(`\`tl ${this.command} ${args.join(" ")}\` failed: ${detail}`);
7150
+ }
7151
+ }
7152
+ };
7153
+
7066
7154
  // src/filesystem.ts
7155
+ init_defaults();
7067
7156
  init_native_sandbox();
7068
7157
  init_sandbox_image();
7069
- var execFileAsync = (0, import_node_util2.promisify)(import_node_child_process.execFile);
7070
7158
  var FILESYSTEM_REPO_KIND = "filesystem";
7071
7159
  function translateNativeError2(error, notFound) {
7072
7160
  const message = error instanceof Error ? error.message : String(error);
@@ -7098,58 +7186,9 @@ async function callNative2(fn, notFound) {
7098
7186
  throw translateNativeError2(error, notFound);
7099
7187
  }
7100
7188
  }
7101
- var FsCli = class {
7102
- binary = null;
7103
- envOverrides;
7189
+ var FsCli = class extends MountCli {
7104
7190
  constructor(envOverrides) {
7105
- this.envOverrides = envOverrides;
7106
- }
7107
- async findCli() {
7108
- const candidates = [];
7109
- if (process.env.TENSORLAKE_CLI) {
7110
- candidates.push(process.env.TENSORLAKE_CLI);
7111
- }
7112
- candidates.push("tl");
7113
- const installed = (0, import_node_path3.join)((0, import_node_os2.homedir)(), ".tensorlake", "bin", "tl");
7114
- try {
7115
- (0, import_node_fs2.accessSync)(installed, import_node_fs2.constants.F_OK);
7116
- candidates.push(installed);
7117
- } catch {
7118
- }
7119
- let unsupported = null;
7120
- for (const candidate of candidates) {
7121
- try {
7122
- await execFileAsync(candidate, ["fs", "--help"], { timeout: 15e3 });
7123
- return candidate;
7124
- } catch (error) {
7125
- if (error.code !== "ENOENT") {
7126
- unsupported = candidate;
7127
- }
7128
- }
7129
- }
7130
- throw new CliNotFoundError(
7131
- unsupported ? `\`tl\` at ${unsupported} does not support \`tl fs\` (upgrade required)` : "`tl` was not found on PATH"
7132
- );
7133
- }
7134
- async run(args, timeoutMs = 3e5) {
7135
- if (this.binary === null) {
7136
- this.binary = await this.findCli();
7137
- }
7138
- try {
7139
- const { stdout } = await execFileAsync(this.binary, ["fs", ...args], {
7140
- timeout: timeoutMs,
7141
- env: { ...process.env, ...this.envOverrides },
7142
- maxBuffer: 16 * 1024 * 1024
7143
- });
7144
- return stdout;
7145
- } catch (error) {
7146
- const failure = error;
7147
- if (failure.killed) {
7148
- throw new MountError(`\`tl fs ${args[0]}\` timed out after ${timeoutMs}ms`);
7149
- }
7150
- const detail = (failure.stderr || failure.stdout || String(error)).trim();
7151
- throw new MountError(`\`tl fs ${args.join(" ")}\` failed: ${detail}`);
7152
- }
7191
+ super("fs", envOverrides);
7153
7192
  }
7154
7193
  // Flags always precede a `--` end-of-options separator so caller-supplied
7155
7194
  // names/paths can never be parsed as CLI flags (e.g. a path literally
@@ -7764,9 +7803,146 @@ init_errors();
7764
7803
  init_native_sandbox();
7765
7804
  init_models();
7766
7805
  init_sandbox_image();
7806
+
7807
+ // src/repository-mount.ts
7808
+ init_cjs_shims();
7809
+ var import_node_path4 = require("path");
7810
+ function requireNonEmpty(value, name) {
7811
+ if (typeof value !== "string" || value.length === 0) {
7812
+ throw new TypeError(`${name} must be a non-empty string`);
7813
+ }
7814
+ }
7815
+ var GitCli = class extends MountCli {
7816
+ constructor(envOverrides) {
7817
+ super("git", envOverrides);
7818
+ }
7819
+ async mount(target, localPath, options = {}) {
7820
+ requireNonEmpty(target, "target");
7821
+ requireNonEmpty(localPath, "localPath");
7822
+ if (options.workspace !== void 0) requireNonEmpty(options.workspace, "workspace");
7823
+ if (options.readOnly && (options.publish || options.workspace !== void 0) || options.publish && options.workspace !== void 0) {
7824
+ throw new TypeError("readOnly, publish, and workspace are mutually exclusive");
7825
+ }
7826
+ const path3 = (0, import_node_path4.resolve)(localPath);
7827
+ const args = ["mount"];
7828
+ if (options.readOnly) args.push("--ro");
7829
+ if (options.publish) args.push("--publish");
7830
+ if (options.workspace !== void 0) args.push(`--workspace=${options.workspace}`);
7831
+ args.push("--", target, path3);
7832
+ await this.run(args);
7833
+ return new RepositoryMount(this, target, path3, options.readOnly ?? false);
7834
+ }
7835
+ async unmount(localPath, options = {}) {
7836
+ requireNonEmpty(localPath, "localPath");
7837
+ const args = ["unmount"];
7838
+ if (options.deleteWorkspace) args.push("--delete");
7839
+ if (options.discard) args.push("--discard");
7840
+ args.push("--", (0, import_node_path4.resolve)(localPath));
7841
+ await this.run(args);
7842
+ }
7843
+ async status(localPath) {
7844
+ const args = ["status", "--json"];
7845
+ if (localPath !== void 0) {
7846
+ requireNonEmpty(localPath, "localPath");
7847
+ args.push("--", (0, import_node_path4.resolve)(localPath));
7848
+ }
7849
+ const output = await this.run(args, 6e4);
7850
+ let raw;
7851
+ try {
7852
+ const payload = JSON.parse(output);
7853
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
7854
+ throw new Error("expected a JSON object");
7855
+ }
7856
+ raw = payload;
7857
+ } catch {
7858
+ throw new MountError(`\`tl git status --json\` returned invalid JSON: ${output.slice(0, 200)}`);
7859
+ }
7860
+ const stringField = (key) => typeof raw[key] === "string" ? raw[key] : null;
7861
+ return {
7862
+ path: stringField("mountpoint") ?? (localPath === void 0 ? "" : (0, import_node_path4.resolve)(localPath)),
7863
+ repository: stringField("repository"),
7864
+ reference: stringField("reference"),
7865
+ workspaceId: stringField("workspace"),
7866
+ state: stringField("state"),
7867
+ localChanges: typeof raw.local_changes === "boolean" ? raw.local_changes : null,
7868
+ raw
7869
+ };
7870
+ }
7871
+ async snapshot(path3, message) {
7872
+ const args = ["snapshot"];
7873
+ if (message !== void 0) args.push(`--message=${message}`);
7874
+ args.push("--", path3);
7875
+ await this.run(args);
7876
+ }
7877
+ async sync(path3, target) {
7878
+ const args = ["sync", "--", path3];
7879
+ if (target !== void 0) {
7880
+ requireNonEmpty(target, "target");
7881
+ args.push(target);
7882
+ }
7883
+ await this.run(args);
7884
+ }
7885
+ async rebase(path3, target, failOnConflict) {
7886
+ requireNonEmpty(target, "target");
7887
+ const args = ["rebase"];
7888
+ if (failOnConflict) args.push("--fail-on-conflict");
7889
+ args.push("--", path3, target);
7890
+ await this.run(args);
7891
+ }
7892
+ async promote(path3, branch, merge) {
7893
+ requireNonEmpty(branch, "branch");
7894
+ const args = ["promote"];
7895
+ if (merge) args.push("--merge");
7896
+ args.push("--", path3, branch);
7897
+ await this.run(args);
7898
+ }
7899
+ async prefetch(path3) {
7900
+ await this.run(["prefetch", "--", path3]);
7901
+ }
7902
+ };
7903
+ var RepositoryMount = class {
7904
+ /** @internal Obtain instances via RepositoryClient.mount(). */
7905
+ constructor(cli, target, path3, readOnly) {
7906
+ this.cli = cli;
7907
+ this.target = target;
7908
+ this.path = path3;
7909
+ this.readOnly = readOnly;
7910
+ }
7911
+ /** Inspect workspace state, pending changes, and daemon diagnostics. */
7912
+ async status() {
7913
+ return this.cli.status(this.path);
7914
+ }
7915
+ /** Materialize autosaved edits as a commit; publish mounts also update their branch. */
7916
+ async snapshot(message) {
7917
+ await this.cli.snapshot(this.path, message);
7918
+ }
7919
+ /** Refresh the current source, or switch an unsnapshotted workspace to a new base. */
7920
+ async sync(target) {
7921
+ await this.cli.sync(this.path, target);
7922
+ }
7923
+ /** Replay snapshots and pending edits onto another base. Conflicts become diff3 markers by default. */
7924
+ async rebase(target, options) {
7925
+ await this.cli.rebase(this.path, target, options?.failOnConflict ?? false);
7926
+ }
7927
+ /** Publish the workspace to a branch; merge allows landing when the branch moved. */
7928
+ async promote(branch, options) {
7929
+ await this.cli.promote(this.path, branch, options?.merge ?? false);
7930
+ }
7931
+ /** Download and verify the mounted working set for network-free reads. */
7932
+ async prefetch() {
7933
+ await this.cli.prefetch(this.path);
7934
+ }
7935
+ /** Detach, retaining the private workspace and unpublished state unless explicitly requested. */
7936
+ async unmount(options) {
7937
+ await this.cli.unmount(this.path, options);
7938
+ }
7939
+ };
7940
+
7941
+ // src/repositories.ts
7767
7942
  var RepositoryClient = class _RepositoryClient {
7768
7943
  native;
7769
7944
  requestTimeoutMs;
7945
+ cli;
7770
7946
  constructor(options) {
7771
7947
  this.requestTimeoutMs = resolveRequestTimeoutMs2(options);
7772
7948
  const apiKey = options?.apiKey ?? API_KEY;
@@ -7787,9 +7963,20 @@ var RepositoryClient = class _RepositoryClient {
7787
7963
  null,
7788
7964
  this.requestTimeoutMs / 1e3
7789
7965
  );
7966
+ const context = buildContextFromEnv();
7967
+ const envOverrides = {
7968
+ TENSORLAKE_API_KEY: apiKey,
7969
+ TENSORLAKE_API_URL: options?.apiUrl ?? API_URL
7970
+ };
7971
+ const organizationId = options?.organizationId ?? context.organizationId;
7972
+ const projectId = options?.projectId ?? context.projectId;
7973
+ if (organizationId) envOverrides.TENSORLAKE_ORGANIZATION_ID = organizationId;
7974
+ if (projectId) envOverrides.TENSORLAKE_PROJECT_ID = projectId;
7975
+ this.cli = new GitCli(envOverrides);
7790
7976
  }
7791
7977
  static forCloud(options) {
7792
7978
  return new _RepositoryClient({
7979
+ ...options,
7793
7980
  apiUrl: options?.apiUrl ?? "https://api.tensorlake.ai",
7794
7981
  apiKey: options?.apiKey,
7795
7982
  requestTimeout: options?.requestTimeout,
@@ -7808,12 +7995,30 @@ var RepositoryClient = class _RepositoryClient {
7808
7995
  }
7809
7996
  return new _RepositoryClient({
7810
7997
  apiUrl: context.apiUrl,
7811
- apiKey: context.apiKey
7998
+ apiKey: context.apiKey,
7999
+ organizationId: context.organizationId,
8000
+ projectId: context.projectId
7812
8001
  });
7813
8002
  }
7814
8003
  close() {
7815
8004
  releaseNativeHandle(this.native);
7816
8005
  }
8006
+ /**
8007
+ * Mount `repo[:branch|tag|full-commit][//subtree]` to a local directory.
8008
+ * Requires the separately installed `tl` CLI with mount support. Mounts
8009
+ * live on the machine running Node.js and must be explicitly unmounted.
8010
+ */
8011
+ async mount(target, localPath, options) {
8012
+ return this.cli.mount(target, localPath, options);
8013
+ }
8014
+ /** Detach a local repository mount, optionally deleting its private workspace. */
8015
+ async unmount(localPath, options) {
8016
+ await this.cli.unmount(localPath, options);
8017
+ }
8018
+ /** Inspect a local mount; defaults to the mount containing the current directory. */
8019
+ async mountStatus(localPath) {
8020
+ return this.cli.status(localPath);
8021
+ }
7817
8022
  async url(repo) {
7818
8023
  return callNative(() => this.native.gitRepoUrl(repo));
7819
8024
  }
@@ -8530,16 +8735,16 @@ var FunctionFuture = class _FunctionFuture {
8530
8735
  ))
8531
8736
  );
8532
8737
  } else {
8533
- wait = new Promise((resolve) => {
8738
+ wait = new Promise((resolve2) => {
8534
8739
  let remaining = pending.length;
8535
8740
  for (const future of pending) {
8536
8741
  void internalDurableThen(
8537
8742
  future.result(),
8538
8743
  () => {
8539
8744
  remaining -= 1;
8540
- if (remaining === 0) resolve({ kind: "all_completed" });
8745
+ if (remaining === 0) resolve2({ kind: "all_completed" });
8541
8746
  },
8542
- () => resolve({ kind: "cutoff", future })
8747
+ () => resolve2({ kind: "cutoff", future })
8543
8748
  );
8544
8749
  }
8545
8750
  });
@@ -8575,8 +8780,8 @@ async function waitUntilOrTimeout(promise, timeoutSeconds) {
8575
8780
  try {
8576
8781
  return await internalPromiseRace([
8577
8782
  promise,
8578
- new Promise((resolve) => {
8579
- timer = setTimeout(() => resolve({ kind: "timeout" }), timeoutSeconds * 1e3);
8783
+ new Promise((resolve2) => {
8784
+ timer = setTimeout(() => resolve2({ kind: "timeout" }), timeoutSeconds * 1e3);
8580
8785
  })
8581
8786
  ]);
8582
8787
  } finally {
@@ -9576,8 +9781,8 @@ var LocalRuntime = class {
9576
9781
  let timer;
9577
9782
  try {
9578
9783
  await waitWithAbortSignal(
9579
- new Promise((resolve) => {
9580
- timer = setTimeout(resolve, future.delaySeconds * 1e3);
9784
+ new Promise((resolve2) => {
9785
+ timer = setTimeout(resolve2, future.delaySeconds * 1e3);
9581
9786
  }),
9582
9787
  this.requestContext.signal
9583
9788
  );
@@ -9808,6 +10013,7 @@ init_models();
9808
10013
  ReadOnlyMountNotSupportedError,
9809
10014
  RemoteAPIError,
9810
10015
  RepositoryClient,
10016
+ RepositoryMount,
9811
10017
  RequestContext,
9812
10018
  RequestExecutionError,
9813
10019
  RequestFailedError,