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.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.126";
49
+ SDK_VERSION = "0.5.128";
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";
@@ -81,6 +81,27 @@ function describeError(err) {
81
81
  if (parts.length > 0) return parts.join(": ");
82
82
  return err instanceof Error ? err.message : String(err);
83
83
  }
84
+ function formatErrorDetails(errorDetails) {
85
+ if (errorDetails == null) return void 0;
86
+ if (typeof errorDetails === "string") {
87
+ const detail = errorDetails.trim();
88
+ return detail || void 0;
89
+ }
90
+ if (Array.isArray(errorDetails)) {
91
+ const parts = errorDetails.map((item) => formatErrorDetails(item)).filter((item) => Boolean(item));
92
+ return parts.length > 0 ? parts.join("; ") : JSON.stringify(errorDetails);
93
+ }
94
+ if (typeof errorDetails === "object") {
95
+ for (const key of ["message", "detail", "error", "reason"]) {
96
+ const value = errorDetails[key];
97
+ if (typeof value === "string" && value.trim()) {
98
+ return value.trim();
99
+ }
100
+ }
101
+ return JSON.stringify(errorDetails);
102
+ }
103
+ return String(errorDetails);
104
+ }
84
105
  var SandboxException, SandboxError, SandboxConnectionError, SandboxNotFoundError, PoolNotFoundError, PoolInUseError, RemoteAPIError, RequestNotFinishedError, RequestFailedError, RequestExecutionError;
85
106
  var init_errors = __esm({
86
107
  "src/errors.ts"() {
@@ -132,9 +153,39 @@ var init_errors = __esm({
132
153
  };
133
154
  RemoteAPIError = class extends SandboxError {
134
155
  statusCode;
156
+ /** Original response body, retained for compatibility and diagnostics. */
135
157
  responseMessage;
158
+ sandboxId;
159
+ /** Server reason, including unknown future reasons. */
160
+ reason;
161
+ errorDetails;
136
162
  constructor(statusCode, message) {
137
- super(`API error (status ${statusCode}): ${message}`);
163
+ let failure;
164
+ try {
165
+ const payload = JSON.parse(message);
166
+ if (payload && typeof payload === "object" && !Array.isArray(payload)) {
167
+ const record = payload;
168
+ if (typeof record.sandbox_id === "string" && record.sandbox_id.trim() && (record.status === "failed" || record.status === "terminated"))
169
+ failure = record;
170
+ }
171
+ } catch {
172
+ }
173
+ const sandboxId = failure?.sandbox_id;
174
+ const rawReason = failure?.reason ?? failure?.termination_reason;
175
+ const reason = typeof rawReason === "string" ? rawReason : void 0;
176
+ const errorDetails = failure?.error_details;
177
+ let displayMessage = message;
178
+ if (failure) {
179
+ displayMessage = `Sandbox ${sandboxId} ${failure.status}`;
180
+ if (reason) displayMessage += ` (${reason})`;
181
+ const detail = formatErrorDetails(errorDetails);
182
+ if (detail) displayMessage += `: ${detail}`;
183
+ if (!reason && !detail) displayMessage = message;
184
+ }
185
+ super(`API error (status ${statusCode}): ${displayMessage}`);
186
+ this.sandboxId = sandboxId;
187
+ this.reason = reason;
188
+ this.errorDetails = errorDetails;
138
189
  this.name = "RemoteAPIError";
139
190
  this.statusCode = statusCode;
140
191
  this.responseMessage = message;
@@ -315,10 +366,10 @@ var init_native_worker_client = __esm({
315
366
  call(handle, method, args, emit) {
316
367
  const id = this.nextId++;
317
368
  let worker;
318
- const promise = new Promise((resolve, reject) => {
369
+ const promise = new Promise((resolve2, reject) => {
319
370
  try {
320
371
  worker = this.getWorker();
321
- this.pending.set(id, { resolve, reject, emit, handle });
372
+ this.pending.set(id, { resolve: resolve2, reject, emit, handle });
322
373
  worker.ref();
323
374
  if (handle) this.register(handle, worker);
324
375
  this.send(worker, {
@@ -421,7 +472,7 @@ var init_native_worker_client = __esm({
421
472
  }
422
473
  };
423
474
  runtimeKey = /* @__PURE__ */ Symbol.for(
424
- `tensorlake.native.worker.${false ? "source" : "0.5.126"}.v1`
475
+ `tensorlake.native.worker.${false ? "source" : "0.5.128"}.v1`
425
476
  );
426
477
  runtimes = globalThis;
427
478
  }
@@ -489,9 +540,9 @@ async function* nativeEventStream(start, context, signal) {
489
540
  let stopped = false;
490
541
  let failure;
491
542
  const notify = () => {
492
- const resolve = wake;
543
+ const resolve2 = wake;
493
544
  wake = void 0;
494
- resolve?.();
545
+ resolve2?.();
495
546
  };
496
547
  const emit = (value) => {
497
548
  if (stopped) return Promise.resolve();
@@ -526,8 +577,8 @@ async function* nativeEventStream(start, context, signal) {
526
577
  } else if (finished) {
527
578
  break;
528
579
  } else {
529
- await new Promise((resolve) => {
530
- wake = resolve;
580
+ await new Promise((resolve2) => {
581
+ wake = resolve2;
531
582
  });
532
583
  }
533
584
  }
@@ -642,6 +693,7 @@ var init_models = __esm({
642
693
  SandboxStatus2["SUSPENDED"] = "suspended";
643
694
  SandboxStatus2["TERMINATED"] = "terminated";
644
695
  SandboxStatus2["TIMEOUT"] = "timeout";
696
+ SandboxStatus2["FAILED"] = "failed";
645
697
  return SandboxStatus2;
646
698
  })(SandboxStatus || {});
647
699
  SnapshotStatus = /* @__PURE__ */ ((SnapshotStatus2) => {
@@ -683,7 +735,7 @@ import WebSocket from "ws";
683
735
  async function openTunnelWebSocket(options) {
684
736
  const wsUrl = buildTunnelWsUrl(options.baseUrl, options.remotePort);
685
737
  const timeoutMs = options.connectTimeoutMs ?? DEFAULT_TUNNEL_CONNECT_TIMEOUT_MS;
686
- return new Promise((resolve, reject) => {
738
+ return new Promise((resolve2, reject) => {
687
739
  const socket = new WebSocket(wsUrl, {
688
740
  headers: options.wsHeaders
689
741
  });
@@ -714,7 +766,7 @@ async function openTunnelWebSocket(options) {
714
766
  });
715
767
  };
716
768
  const onOpen = () => {
717
- settle(() => resolve(socket));
769
+ settle(() => resolve2(socket));
718
770
  };
719
771
  const onError = (error) => {
720
772
  fail(new SandboxConnectionError(error.message));
@@ -757,7 +809,7 @@ function normalizeWebSocketData(message) {
757
809
  return Buffer.from(message);
758
810
  }
759
811
  async function relaySocket(localSocket, websocket) {
760
- return new Promise((resolve) => {
812
+ return new Promise((resolve2) => {
761
813
  let settled = false;
762
814
  const keepAliveInterval = setInterval(() => {
763
815
  if (websocket.readyState === WebSocket.OPEN) {
@@ -770,7 +822,7 @@ async function relaySocket(localSocket, websocket) {
770
822
  settled = true;
771
823
  clearInterval(keepAliveInterval);
772
824
  cleanup();
773
- resolve();
825
+ resolve2();
774
826
  };
775
827
  const cleanup = () => {
776
828
  localSocket.removeListener("data", onLocalData);
@@ -848,7 +900,7 @@ async function relaySocket(localSocket, websocket) {
848
900
  });
849
901
  }
850
902
  async function listenServer(server, localPort, localHost) {
851
- await new Promise((resolve, reject) => {
903
+ await new Promise((resolve2, reject) => {
852
904
  const onError = (error) => {
853
905
  server.removeListener("listening", onListening);
854
906
  reject(
@@ -859,7 +911,7 @@ async function listenServer(server, localPort, localHost) {
859
911
  };
860
912
  const onListening = () => {
861
913
  server.removeListener("error", onError);
862
- resolve();
914
+ resolve2();
863
915
  };
864
916
  server.once("error", onError);
865
917
  server.once("listening", onListening);
@@ -885,13 +937,13 @@ function secondsToMillis(seconds) {
885
937
  return Math.round(seconds * 1e3);
886
938
  }
887
939
  async function withTimeout(timeoutMs, operation, timeoutMessage) {
888
- return new Promise((resolve, reject) => {
940
+ return new Promise((resolve2, reject) => {
889
941
  const timer = setTimeout(() => {
890
942
  reject(new SandboxError(timeoutMessage));
891
943
  }, timeoutMs);
892
944
  void operation().then((value) => {
893
945
  clearTimeout(timer);
894
- resolve(value);
946
+ resolve2(value);
895
947
  }).catch((error) => {
896
948
  clearTimeout(timer);
897
949
  reject(error);
@@ -965,8 +1017,8 @@ var init_tunnel = __esm({
965
1017
  if (this.closeError) {
966
1018
  throw this.closeError;
967
1019
  }
968
- return new Promise((resolve, reject) => {
969
- this.pendingReads.push({ length, resolve, reject });
1020
+ return new Promise((resolve2, reject) => {
1021
+ this.pendingReads.push({ length, resolve: resolve2, reject });
970
1022
  });
971
1023
  }
972
1024
  async writeAll(data) {
@@ -977,13 +1029,13 @@ var init_tunnel = __esm({
977
1029
  throw new SandboxError("desktop tunnel is not connected");
978
1030
  }
979
1031
  const payload = Buffer.from(data);
980
- await new Promise((resolve, reject) => {
1032
+ await new Promise((resolve2, reject) => {
981
1033
  this.socket.send(payload, { binary: true }, (error) => {
982
1034
  if (error) {
983
1035
  reject(new SandboxConnectionError(error.message));
984
1036
  return;
985
1037
  }
986
- resolve();
1038
+ resolve2();
987
1039
  });
988
1040
  });
989
1041
  }
@@ -995,10 +1047,10 @@ var init_tunnel = __esm({
995
1047
  this.closePromise = Promise.resolve();
996
1048
  return this.closePromise;
997
1049
  }
998
- this.closePromise = new Promise((resolve) => {
1050
+ this.closePromise = new Promise((resolve2) => {
999
1051
  const onClose = () => {
1000
1052
  this.socket.removeListener("close", onClose);
1001
- resolve();
1053
+ resolve2();
1002
1054
  };
1003
1055
  this.socket.on("close", onClose);
1004
1056
  this.socket.close();
@@ -1092,13 +1144,13 @@ var init_tunnel = __esm({
1092
1144
  relay.localSocket.destroy();
1093
1145
  relay.websocket?.close();
1094
1146
  }
1095
- this.closePromise = new Promise((resolve, reject) => {
1147
+ this.closePromise = new Promise((resolve2, reject) => {
1096
1148
  this.server.close((error) => {
1097
1149
  if (error) {
1098
1150
  reject(new SandboxError(`failed to close tunnel listener: ${error.message}`));
1099
1151
  return;
1100
1152
  }
1101
- resolve();
1153
+ resolve2();
1102
1154
  });
1103
1155
  });
1104
1156
  return this.closePromise;
@@ -1327,14 +1379,14 @@ function isReconnectableDesktopScreenshotError(error) {
1327
1379
  }
1328
1380
  function createDeferredSignal() {
1329
1381
  let resolveCurrent;
1330
- let promise = new Promise((resolve) => {
1331
- resolveCurrent = resolve;
1382
+ let promise = new Promise((resolve2) => {
1383
+ resolveCurrent = resolve2;
1332
1384
  });
1333
1385
  return {
1334
1386
  resolve() {
1335
1387
  resolveCurrent();
1336
- promise = new Promise((resolve) => {
1337
- resolveCurrent = resolve;
1388
+ promise = new Promise((resolve2) => {
1389
+ resolveCurrent = resolve2;
1338
1390
  });
1339
1391
  },
1340
1392
  wait() {
@@ -1678,7 +1730,7 @@ var init_desktop = __esm({
1678
1730
  validateNonNegativeInteger(delayMs, "double click delay");
1679
1731
  await this.click({ button, x: options?.x, y: options?.y });
1680
1732
  if (delayMs > 0) {
1681
- await new Promise((resolve) => setTimeout(resolve, delayMs));
1733
+ await new Promise((resolve2) => setTimeout(resolve2, delayMs));
1682
1734
  }
1683
1735
  await this.click({ button });
1684
1736
  }
@@ -3144,7 +3196,7 @@ function processUserPayload(user) {
3144
3196
  return user;
3145
3197
  }
3146
3198
  function sleep(ms) {
3147
- return new Promise((resolve) => setTimeout(resolve, ms));
3199
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
3148
3200
  }
3149
3201
  function normalizePtyMessage(message) {
3150
3202
  if (Buffer.isBuffer(message)) return message;
@@ -3165,8 +3217,8 @@ function encodePtyResize(cols, rows) {
3165
3217
  return frame;
3166
3218
  }
3167
3219
  function sendPtyFrame(socket, frame) {
3168
- return new Promise((resolve, reject) => {
3169
- socket.send(frame, (error) => error ? reject(error) : resolve());
3220
+ return new Promise((resolve2, reject) => {
3221
+ socket.send(frame, (error) => error ? reject(error) : resolve2());
3170
3222
  });
3171
3223
  }
3172
3224
  function markBindOutcome(sandbox, outcome) {
@@ -3407,8 +3459,8 @@ var init_sandbox = __esm({
3407
3459
  this.wsUrl = options.wsUrl;
3408
3460
  this.wsHeaders = options.wsHeaders;
3409
3461
  this.killSession = options.killSession;
3410
- this.waitPromise = new Promise((resolve, reject) => {
3411
- this.resolveWait = resolve;
3462
+ this.waitPromise = new Promise((resolve2, reject) => {
3463
+ this.resolveWait = resolve2;
3412
3464
  this.rejectWait = reject;
3413
3465
  });
3414
3466
  }
@@ -3431,7 +3483,7 @@ var init_sandbox = __esm({
3431
3483
  return this.connectPromise;
3432
3484
  }
3433
3485
  this.intentionalDisconnect = false;
3434
- this.connectPromise = new Promise((resolve, reject) => {
3486
+ this.connectPromise = new Promise((resolve2, reject) => {
3435
3487
  let opened = false;
3436
3488
  const socket = new WebSocket2(this.wsUrl, {
3437
3489
  headers: this.wsHeaders
@@ -3441,7 +3493,7 @@ var init_sandbox = __esm({
3441
3493
  try {
3442
3494
  await sendPtyFrame(socket, Buffer.from([PTY_OP_READY]));
3443
3495
  opened = true;
3444
- resolve(this);
3496
+ resolve2(this);
3445
3497
  } catch (error) {
3446
3498
  reject(error);
3447
3499
  }
@@ -4372,7 +4424,7 @@ var init_sandbox = __esm({
4372
4424
  const remainingMs = deadlineMs - Date.now();
4373
4425
  if (remainingMs <= 0) break;
4374
4426
  await new Promise(
4375
- (resolve) => setTimeout(resolve, Math.min(probeIntervalMs, remainingMs))
4427
+ (resolve2) => setTimeout(resolve2, Math.min(probeIntervalMs, remainingMs))
4376
4428
  );
4377
4429
  }
4378
4430
  const detail = lastError instanceof Error ? `: ${lastError.message}` : "";
@@ -4479,7 +4531,7 @@ function validateTimeoutMs(timeoutMs) {
4479
4531
  }
4480
4532
  }
4481
4533
  function sleep2(ms) {
4482
- return new Promise((resolve) => setTimeout(resolve, ms));
4534
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
4483
4535
  }
4484
4536
  function snapshotStatusSatisfiesWaitCondition(status, waitUntil) {
4485
4537
  if (waitUntil === "local_ready") {
@@ -4488,37 +4540,16 @@ function snapshotStatusSatisfiesWaitCondition(status, waitUntil) {
4488
4540
  return status === "completed" /* COMPLETED */;
4489
4541
  }
4490
4542
  function formatStartupFailureMessage(sandboxId, status, options) {
4491
- const prefix = status === "terminated" /* TERMINATED */ ? `Sandbox ${sandboxId} terminated during startup` : `Sandbox ${sandboxId} became ${status} during startup`;
4543
+ let prefix = status === "terminated" /* TERMINATED */ ? `Sandbox ${sandboxId} terminated during startup` : `Sandbox ${sandboxId} became ${status} during startup`;
4544
+ if (options.terminationReason) {
4545
+ prefix += ` (${options.terminationReason})`;
4546
+ }
4492
4547
  const detail = formatErrorDetails(options.errorDetails);
4493
4548
  if (detail) {
4494
4549
  return `${prefix}: ${detail}`;
4495
4550
  }
4496
- if (options.terminationReason) {
4497
- return `${prefix}: termination reason: ${options.terminationReason}`;
4498
- }
4499
4551
  return prefix;
4500
4552
  }
4501
- function formatErrorDetails(errorDetails) {
4502
- if (errorDetails == null) return void 0;
4503
- if (typeof errorDetails === "string") {
4504
- const detail = errorDetails.trim();
4505
- return detail || void 0;
4506
- }
4507
- if (Array.isArray(errorDetails)) {
4508
- const parts = errorDetails.map((item) => formatErrorDetails(item)).filter((item) => Boolean(item));
4509
- return parts.length > 0 ? parts.join("; ") : JSON.stringify(errorDetails);
4510
- }
4511
- if (typeof errorDetails === "object") {
4512
- for (const key of ["message", "detail", "error", "reason"]) {
4513
- const value = errorDetails[key];
4514
- if (typeof value === "string" && value.trim()) {
4515
- return value.trim();
4516
- }
4517
- }
4518
- return JSON.stringify(errorDetails);
4519
- }
4520
- return String(errorDetails);
4521
- }
4522
4553
  function normalizeUserPorts(ports) {
4523
4554
  return dedupeAndSortPorts(ports.map(validateUserPort));
4524
4555
  }
@@ -5200,11 +5231,11 @@ var init_client = __esm({
5200
5231
  result.sandboxUrl
5201
5232
  );
5202
5233
  }
5203
- if (result.status === "suspended" /* SUSPENDED */ || result.status === "terminated" /* TERMINATED */) {
5234
+ if (result.status === "suspended" /* SUSPENDED */ || result.status === "terminated" /* TERMINATED */ || result.status === "failed" /* FAILED */) {
5204
5235
  throw new SandboxError(
5205
5236
  formatStartupFailureMessage(result.sandboxId, result.status, {
5206
5237
  errorDetails: result.errorDetails,
5207
- terminationReason: result.terminationReason
5238
+ terminationReason: result.terminationReason ?? result.reason
5208
5239
  })
5209
5240
  );
5210
5241
  }
@@ -5328,7 +5359,7 @@ function anySignal(signals) {
5328
5359
  return controller.signal;
5329
5360
  }
5330
5361
  function sleep3(ms) {
5331
- return new Promise((resolve) => setTimeout(resolve, ms));
5362
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
5332
5363
  }
5333
5364
  var REQUEST_TIMEOUT_HEADER, HttpClient;
5334
5365
  var init_http = __esm({
@@ -6908,9 +6939,11 @@ init_cloud_client();
6908
6939
 
6909
6940
  // src/filesystem.ts
6910
6941
  init_esm_shims();
6911
- init_defaults();
6912
- import { execFile } from "child_process";
6913
6942
  import { randomBytes } from "crypto";
6943
+
6944
+ // src/mount-cli.ts
6945
+ init_esm_shims();
6946
+ import { execFile } from "child_process";
6914
6947
  import { accessSync, constants } from "fs";
6915
6948
  import { homedir } from "os";
6916
6949
  import { join } from "path";
@@ -6994,60 +7027,25 @@ var ReadOnlyMountNotSupportedError = class extends MountError {
6994
7027
  }
6995
7028
  };
6996
7029
  var CliNotFoundError = class extends MountError {
6997
- constructor(detail) {
7030
+ constructor(detail, command = "fs") {
6998
7031
  super(
6999
- `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`
7032
+ `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`
7000
7033
  );
7001
7034
  this.name = "CliNotFoundError";
7002
7035
  }
7003
7036
  };
7004
7037
 
7005
- // src/filesystem.ts
7006
- init_native_sandbox();
7007
- init_sandbox_image();
7038
+ // src/mount-cli.ts
7008
7039
  var execFileAsync = promisify(execFile);
7009
- var FILESYSTEM_REPO_KIND = "filesystem";
7010
- function translateNativeError2(error, notFound) {
7011
- const message = error instanceof Error ? error.message : String(error);
7012
- let payload;
7013
- try {
7014
- payload = JSON.parse(message);
7015
- } catch {
7016
- return error instanceof Error ? error : new FilesystemError(message);
7017
- }
7018
- if (!payload || typeof payload.category !== "string") {
7019
- return error instanceof Error ? error : new FilesystemError(message);
7020
- }
7021
- const detail = typeof payload.message === "string" ? payload.message : message;
7022
- if (payload.category === "connection") {
7023
- return new FilesystemError(detail);
7024
- }
7025
- if (payload.status === 404 && notFound !== void 0) {
7026
- return notFound;
7027
- }
7028
- if (typeof payload.status === "number") {
7029
- return new FilesystemAPIError(payload.status, detail);
7030
- }
7031
- return new FilesystemError(detail);
7032
- }
7033
- async function callNative2(fn, notFound) {
7034
- try {
7035
- return await fn();
7036
- } catch (error) {
7037
- throw translateNativeError2(error, notFound);
7038
- }
7039
- }
7040
- var FsCli = class {
7041
- binary = null;
7042
- envOverrides;
7043
- constructor(envOverrides) {
7040
+ var MountCli = class {
7041
+ constructor(command, envOverrides) {
7042
+ this.command = command;
7044
7043
  this.envOverrides = envOverrides;
7045
7044
  }
7045
+ binary = null;
7046
7046
  async findCli() {
7047
7047
  const candidates = [];
7048
- if (process.env.TENSORLAKE_CLI) {
7049
- candidates.push(process.env.TENSORLAKE_CLI);
7050
- }
7048
+ if (process.env.TENSORLAKE_CLI) candidates.push(process.env.TENSORLAKE_CLI);
7051
7049
  candidates.push("tl");
7052
7050
  const installed = join(homedir(), ".tensorlake", "bin", "tl");
7053
7051
  try {
@@ -7055,27 +7053,26 @@ var FsCli = class {
7055
7053
  candidates.push(installed);
7056
7054
  } catch {
7057
7055
  }
7056
+ const support = this.command === "git" ? "git mount" : "fs";
7057
+ const probe = this.command === "git" ? ["git", "mount", "--help"] : ["fs", "--help"];
7058
7058
  let unsupported = null;
7059
7059
  for (const candidate of candidates) {
7060
7060
  try {
7061
- await execFileAsync(candidate, ["fs", "--help"], { timeout: 15e3 });
7061
+ await execFileAsync(candidate, probe, { timeout: 15e3 });
7062
7062
  return candidate;
7063
7063
  } catch (error) {
7064
- if (error.code !== "ENOENT") {
7065
- unsupported = candidate;
7066
- }
7064
+ if (error.code !== "ENOENT") unsupported = candidate;
7067
7065
  }
7068
7066
  }
7069
7067
  throw new CliNotFoundError(
7070
- unsupported ? `\`tl\` at ${unsupported} does not support \`tl fs\` (upgrade required)` : "`tl` was not found on PATH"
7068
+ unsupported ? `\`tl\` at ${unsupported} does not support \`tl ${support}\` (upgrade required)` : "`tl` was not found on PATH",
7069
+ support
7071
7070
  );
7072
7071
  }
7073
7072
  async run(args, timeoutMs = 3e5) {
7074
- if (this.binary === null) {
7075
- this.binary = await this.findCli();
7076
- }
7073
+ if (this.binary === null) this.binary = await this.findCli();
7077
7074
  try {
7078
- const { stdout } = await execFileAsync(this.binary, ["fs", ...args], {
7075
+ const { stdout } = await execFileAsync(this.binary, [this.command, ...args], {
7079
7076
  timeout: timeoutMs,
7080
7077
  env: { ...process.env, ...this.envOverrides },
7081
7078
  maxBuffer: 16 * 1024 * 1024
@@ -7084,12 +7081,53 @@ var FsCli = class {
7084
7081
  } catch (error) {
7085
7082
  const failure = error;
7086
7083
  if (failure.killed) {
7087
- throw new MountError(`\`tl fs ${args[0]}\` timed out after ${timeoutMs}ms`);
7084
+ throw new MountError(`\`tl ${this.command} ${args[0]}\` timed out after ${timeoutMs}ms`);
7088
7085
  }
7089
7086
  const detail = (failure.stderr || failure.stdout || String(error)).trim();
7090
- throw new MountError(`\`tl fs ${args.join(" ")}\` failed: ${detail}`);
7087
+ throw new MountError(`\`tl ${this.command} ${args.join(" ")}\` failed: ${detail}`);
7091
7088
  }
7092
7089
  }
7090
+ };
7091
+
7092
+ // src/filesystem.ts
7093
+ init_defaults();
7094
+ init_native_sandbox();
7095
+ init_sandbox_image();
7096
+ var FILESYSTEM_REPO_KIND = "filesystem";
7097
+ function translateNativeError2(error, notFound) {
7098
+ const message = error instanceof Error ? error.message : String(error);
7099
+ let payload;
7100
+ try {
7101
+ payload = JSON.parse(message);
7102
+ } catch {
7103
+ return error instanceof Error ? error : new FilesystemError(message);
7104
+ }
7105
+ if (!payload || typeof payload.category !== "string") {
7106
+ return error instanceof Error ? error : new FilesystemError(message);
7107
+ }
7108
+ const detail = typeof payload.message === "string" ? payload.message : message;
7109
+ if (payload.category === "connection") {
7110
+ return new FilesystemError(detail);
7111
+ }
7112
+ if (payload.status === 404 && notFound !== void 0) {
7113
+ return notFound;
7114
+ }
7115
+ if (typeof payload.status === "number") {
7116
+ return new FilesystemAPIError(payload.status, detail);
7117
+ }
7118
+ return new FilesystemError(detail);
7119
+ }
7120
+ async function callNative2(fn, notFound) {
7121
+ try {
7122
+ return await fn();
7123
+ } catch (error) {
7124
+ throw translateNativeError2(error, notFound);
7125
+ }
7126
+ }
7127
+ var FsCli = class extends MountCli {
7128
+ constructor(envOverrides) {
7129
+ super("fs", envOverrides);
7130
+ }
7093
7131
  // Flags always precede a `--` end-of-options separator so caller-supplied
7094
7132
  // names/paths can never be parsed as CLI flags (e.g. a path literally
7095
7133
  // named "--discard" must stay a path, not become the destructive flag).
@@ -7703,9 +7741,146 @@ init_errors();
7703
7741
  init_native_sandbox();
7704
7742
  init_models();
7705
7743
  init_sandbox_image();
7744
+
7745
+ // src/repository-mount.ts
7746
+ init_esm_shims();
7747
+ import { resolve } from "path";
7748
+ function requireNonEmpty(value, name) {
7749
+ if (typeof value !== "string" || value.length === 0) {
7750
+ throw new TypeError(`${name} must be a non-empty string`);
7751
+ }
7752
+ }
7753
+ var GitCli = class extends MountCli {
7754
+ constructor(envOverrides) {
7755
+ super("git", envOverrides);
7756
+ }
7757
+ async mount(target, localPath, options = {}) {
7758
+ requireNonEmpty(target, "target");
7759
+ requireNonEmpty(localPath, "localPath");
7760
+ if (options.workspace !== void 0) requireNonEmpty(options.workspace, "workspace");
7761
+ if (options.readOnly && (options.publish || options.workspace !== void 0) || options.publish && options.workspace !== void 0) {
7762
+ throw new TypeError("readOnly, publish, and workspace are mutually exclusive");
7763
+ }
7764
+ const path4 = resolve(localPath);
7765
+ const args = ["mount"];
7766
+ if (options.readOnly) args.push("--ro");
7767
+ if (options.publish) args.push("--publish");
7768
+ if (options.workspace !== void 0) args.push(`--workspace=${options.workspace}`);
7769
+ args.push("--", target, path4);
7770
+ await this.run(args);
7771
+ return new RepositoryMount(this, target, path4, options.readOnly ?? false);
7772
+ }
7773
+ async unmount(localPath, options = {}) {
7774
+ requireNonEmpty(localPath, "localPath");
7775
+ const args = ["unmount"];
7776
+ if (options.deleteWorkspace) args.push("--delete");
7777
+ if (options.discard) args.push("--discard");
7778
+ args.push("--", resolve(localPath));
7779
+ await this.run(args);
7780
+ }
7781
+ async status(localPath) {
7782
+ const args = ["status", "--json"];
7783
+ if (localPath !== void 0) {
7784
+ requireNonEmpty(localPath, "localPath");
7785
+ args.push("--", resolve(localPath));
7786
+ }
7787
+ const output = await this.run(args, 6e4);
7788
+ let raw;
7789
+ try {
7790
+ const payload = JSON.parse(output);
7791
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
7792
+ throw new Error("expected a JSON object");
7793
+ }
7794
+ raw = payload;
7795
+ } catch {
7796
+ throw new MountError(`\`tl git status --json\` returned invalid JSON: ${output.slice(0, 200)}`);
7797
+ }
7798
+ const stringField = (key) => typeof raw[key] === "string" ? raw[key] : null;
7799
+ return {
7800
+ path: stringField("mountpoint") ?? (localPath === void 0 ? "" : resolve(localPath)),
7801
+ repository: stringField("repository"),
7802
+ reference: stringField("reference"),
7803
+ workspaceId: stringField("workspace"),
7804
+ state: stringField("state"),
7805
+ localChanges: typeof raw.local_changes === "boolean" ? raw.local_changes : null,
7806
+ raw
7807
+ };
7808
+ }
7809
+ async snapshot(path4, message) {
7810
+ const args = ["snapshot"];
7811
+ if (message !== void 0) args.push(`--message=${message}`);
7812
+ args.push("--", path4);
7813
+ await this.run(args);
7814
+ }
7815
+ async sync(path4, target) {
7816
+ const args = ["sync", "--", path4];
7817
+ if (target !== void 0) {
7818
+ requireNonEmpty(target, "target");
7819
+ args.push(target);
7820
+ }
7821
+ await this.run(args);
7822
+ }
7823
+ async rebase(path4, target, failOnConflict) {
7824
+ requireNonEmpty(target, "target");
7825
+ const args = ["rebase"];
7826
+ if (failOnConflict) args.push("--fail-on-conflict");
7827
+ args.push("--", path4, target);
7828
+ await this.run(args);
7829
+ }
7830
+ async promote(path4, branch, merge) {
7831
+ requireNonEmpty(branch, "branch");
7832
+ const args = ["promote"];
7833
+ if (merge) args.push("--merge");
7834
+ args.push("--", path4, branch);
7835
+ await this.run(args);
7836
+ }
7837
+ async prefetch(path4) {
7838
+ await this.run(["prefetch", "--", path4]);
7839
+ }
7840
+ };
7841
+ var RepositoryMount = class {
7842
+ /** @internal Obtain instances via RepositoryClient.mount(). */
7843
+ constructor(cli, target, path4, readOnly) {
7844
+ this.cli = cli;
7845
+ this.target = target;
7846
+ this.path = path4;
7847
+ this.readOnly = readOnly;
7848
+ }
7849
+ /** Inspect workspace state, pending changes, and daemon diagnostics. */
7850
+ async status() {
7851
+ return this.cli.status(this.path);
7852
+ }
7853
+ /** Materialize autosaved edits as a commit; publish mounts also update their branch. */
7854
+ async snapshot(message) {
7855
+ await this.cli.snapshot(this.path, message);
7856
+ }
7857
+ /** Refresh the current source, or switch an unsnapshotted workspace to a new base. */
7858
+ async sync(target) {
7859
+ await this.cli.sync(this.path, target);
7860
+ }
7861
+ /** Replay snapshots and pending edits onto another base. Conflicts become diff3 markers by default. */
7862
+ async rebase(target, options) {
7863
+ await this.cli.rebase(this.path, target, options?.failOnConflict ?? false);
7864
+ }
7865
+ /** Publish the workspace to a branch; merge allows landing when the branch moved. */
7866
+ async promote(branch, options) {
7867
+ await this.cli.promote(this.path, branch, options?.merge ?? false);
7868
+ }
7869
+ /** Download and verify the mounted working set for network-free reads. */
7870
+ async prefetch() {
7871
+ await this.cli.prefetch(this.path);
7872
+ }
7873
+ /** Detach, retaining the private workspace and unpublished state unless explicitly requested. */
7874
+ async unmount(options) {
7875
+ await this.cli.unmount(this.path, options);
7876
+ }
7877
+ };
7878
+
7879
+ // src/repositories.ts
7706
7880
  var RepositoryClient = class _RepositoryClient {
7707
7881
  native;
7708
7882
  requestTimeoutMs;
7883
+ cli;
7709
7884
  constructor(options) {
7710
7885
  this.requestTimeoutMs = resolveRequestTimeoutMs2(options);
7711
7886
  const apiKey = options?.apiKey ?? API_KEY;
@@ -7726,9 +7901,20 @@ var RepositoryClient = class _RepositoryClient {
7726
7901
  null,
7727
7902
  this.requestTimeoutMs / 1e3
7728
7903
  );
7904
+ const context = buildContextFromEnv();
7905
+ const envOverrides = {
7906
+ TENSORLAKE_API_KEY: apiKey,
7907
+ TENSORLAKE_API_URL: options?.apiUrl ?? API_URL
7908
+ };
7909
+ const organizationId = options?.organizationId ?? context.organizationId;
7910
+ const projectId = options?.projectId ?? context.projectId;
7911
+ if (organizationId) envOverrides.TENSORLAKE_ORGANIZATION_ID = organizationId;
7912
+ if (projectId) envOverrides.TENSORLAKE_PROJECT_ID = projectId;
7913
+ this.cli = new GitCli(envOverrides);
7729
7914
  }
7730
7915
  static forCloud(options) {
7731
7916
  return new _RepositoryClient({
7917
+ ...options,
7732
7918
  apiUrl: options?.apiUrl ?? "https://api.tensorlake.ai",
7733
7919
  apiKey: options?.apiKey,
7734
7920
  requestTimeout: options?.requestTimeout,
@@ -7747,12 +7933,30 @@ var RepositoryClient = class _RepositoryClient {
7747
7933
  }
7748
7934
  return new _RepositoryClient({
7749
7935
  apiUrl: context.apiUrl,
7750
- apiKey: context.apiKey
7936
+ apiKey: context.apiKey,
7937
+ organizationId: context.organizationId,
7938
+ projectId: context.projectId
7751
7939
  });
7752
7940
  }
7753
7941
  close() {
7754
7942
  releaseNativeHandle(this.native);
7755
7943
  }
7944
+ /**
7945
+ * Mount `repo[:branch|tag|full-commit][//subtree]` to a local directory.
7946
+ * Requires the separately installed `tl` CLI with mount support. Mounts
7947
+ * live on the machine running Node.js and must be explicitly unmounted.
7948
+ */
7949
+ async mount(target, localPath, options) {
7950
+ return this.cli.mount(target, localPath, options);
7951
+ }
7952
+ /** Detach a local repository mount, optionally deleting its private workspace. */
7953
+ async unmount(localPath, options) {
7954
+ await this.cli.unmount(localPath, options);
7955
+ }
7956
+ /** Inspect a local mount; defaults to the mount containing the current directory. */
7957
+ async mountStatus(localPath) {
7958
+ return this.cli.status(localPath);
7959
+ }
7756
7960
  async url(repo) {
7757
7961
  return callNative(() => this.native.gitRepoUrl(repo));
7758
7962
  }
@@ -8469,16 +8673,16 @@ var FunctionFuture = class _FunctionFuture {
8469
8673
  ))
8470
8674
  );
8471
8675
  } else {
8472
- wait = new Promise((resolve) => {
8676
+ wait = new Promise((resolve2) => {
8473
8677
  let remaining = pending.length;
8474
8678
  for (const future of pending) {
8475
8679
  void internalDurableThen(
8476
8680
  future.result(),
8477
8681
  () => {
8478
8682
  remaining -= 1;
8479
- if (remaining === 0) resolve({ kind: "all_completed" });
8683
+ if (remaining === 0) resolve2({ kind: "all_completed" });
8480
8684
  },
8481
- () => resolve({ kind: "cutoff", future })
8685
+ () => resolve2({ kind: "cutoff", future })
8482
8686
  );
8483
8687
  }
8484
8688
  });
@@ -8514,8 +8718,8 @@ async function waitUntilOrTimeout(promise, timeoutSeconds) {
8514
8718
  try {
8515
8719
  return await internalPromiseRace([
8516
8720
  promise,
8517
- new Promise((resolve) => {
8518
- timer = setTimeout(() => resolve({ kind: "timeout" }), timeoutSeconds * 1e3);
8721
+ new Promise((resolve2) => {
8722
+ timer = setTimeout(() => resolve2({ kind: "timeout" }), timeoutSeconds * 1e3);
8519
8723
  })
8520
8724
  ]);
8521
8725
  } finally {
@@ -9515,8 +9719,8 @@ var LocalRuntime = class {
9515
9719
  let timer;
9516
9720
  try {
9517
9721
  await waitWithAbortSignal(
9518
- new Promise((resolve) => {
9519
- timer = setTimeout(resolve, future.delaySeconds * 1e3);
9722
+ new Promise((resolve2) => {
9723
+ timer = setTimeout(resolve2, future.delaySeconds * 1e3);
9520
9724
  }),
9521
9725
  this.requestContext.signal
9522
9726
  );
@@ -9746,6 +9950,7 @@ export {
9746
9950
  ReadOnlyMountNotSupportedError,
9747
9951
  RemoteAPIError,
9748
9952
  RepositoryClient,
9953
+ RepositoryMount,
9749
9954
  RequestContext,
9750
9955
  RequestExecutionError,
9751
9956
  RequestFailedError,