overlord-cli 0.2608141252.0 → 0.2608170844.0

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
@@ -278,16 +278,16 @@ function resolveCommandArgs({
278
278
  return parsed;
279
279
  }
280
280
  function normalizeVersion(raw) {
281
- const trimmed7 = raw.trim();
282
- if (!trimmed7) {
281
+ const trimmed8 = raw.trim();
282
+ if (!trimmed8) {
283
283
  throw new CliError({ message: "Received an empty version from npm." });
284
284
  }
285
285
  try {
286
- const parsed = JSON.parse(trimmed7);
286
+ const parsed = JSON.parse(trimmed8);
287
287
  if (typeof parsed === "string" && parsed.trim()) return parsed.trim();
288
288
  } catch {
289
289
  }
290
- return trimmed7.replace(/^"+|"+$/g, "");
290
+ return trimmed8.replace(/^"+|"+$/g, "");
291
291
  }
292
292
  function compareVersions(left, right) {
293
293
  const leftParts = left.split(".").map((part) => Number.parseInt(part, 10));
@@ -1346,11 +1346,11 @@ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, wr
1346
1346
  import os from "node:os";
1347
1347
  import path3 from "node:path";
1348
1348
  function normalizeBackendUrl(value) {
1349
- const trimmed7 = value.trim().replace(/\/+$/, "");
1350
- if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed7)) {
1351
- return trimmed7;
1349
+ const trimmed8 = value.trim().replace(/\/+$/, "");
1350
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed8)) {
1351
+ return trimmed8;
1352
1352
  }
1353
- return `http://${trimmed7}`;
1353
+ return `http://${trimmed8}`;
1354
1354
  }
1355
1355
  function isLoopbackBackendUrl(backendUrl) {
1356
1356
  try {
@@ -2412,12 +2412,12 @@ function parseConnectorManifestYaml(text) {
2412
2412
  };
2413
2413
  for (const rawLine of text.split("\n")) {
2414
2414
  const line = rawLine.replace(/\r$/, "");
2415
- const trimmed7 = line.trim();
2416
- if (trimmed7 === "" || trimmed7.startsWith("#")) continue;
2415
+ const trimmed8 = line.trim();
2416
+ if (trimmed8 === "" || trimmed8.startsWith("#")) continue;
2417
2417
  const indent = line.length - line.trimStart().length;
2418
2418
  if (pending) {
2419
2419
  if (indent > pending.indent) {
2420
- const container = trimmed7.startsWith("- ") ? [] : {};
2420
+ const container = trimmed8.startsWith("- ") ? [] : {};
2421
2421
  pending.parent[pending.key] = container;
2422
2422
  stack.push({ indent, container });
2423
2423
  pending = null;
@@ -2430,16 +2430,16 @@ function parseConnectorManifestYaml(text) {
2430
2430
  stack.pop();
2431
2431
  }
2432
2432
  const frame = stack[stack.length - 1];
2433
- if (trimmed7.startsWith("- ")) {
2433
+ if (trimmed8.startsWith("- ")) {
2434
2434
  if (Array.isArray(frame.container)) {
2435
- frame.container.push(parseScalar(trimmed7.slice(2)));
2435
+ frame.container.push(parseScalar(trimmed8.slice(2)));
2436
2436
  }
2437
2437
  continue;
2438
2438
  }
2439
- const colon = trimmed7.indexOf(":");
2439
+ const colon = trimmed8.indexOf(":");
2440
2440
  if (colon < 0 || Array.isArray(frame.container)) continue;
2441
- const key = trimmed7.slice(0, colon).trim();
2442
- const rest = trimmed7.slice(colon + 1).trim();
2441
+ const key = trimmed8.slice(0, colon).trim();
2442
+ const rest = trimmed8.slice(colon + 1).trim();
2443
2443
  if (rest === "") {
2444
2444
  pending = { parent: frame.container, key, indent };
2445
2445
  } else {
@@ -2633,10 +2633,10 @@ ${managedBlock}
2633
2633
 
2634
2634
  ${after}`;
2635
2635
  }
2636
- const trimmed7 = existingContent.trimEnd();
2637
- if (!trimmed7) return `${managedBlock}
2636
+ const trimmed8 = existingContent.trimEnd();
2637
+ if (!trimmed8) return `${managedBlock}
2638
2638
  `;
2639
- return `${trimmed7}
2639
+ return `${trimmed8}
2640
2640
 
2641
2641
  ${managedBlock}
2642
2642
  `;
@@ -3073,16 +3073,21 @@ function normalizeBaseUrl4(value) {
3073
3073
  function resolveAuthHeaders({ baseUrl }) {
3074
3074
  const fromEnv = process.env.OVERLORD_USER_TOKEN?.trim() || process.env.OVLD_USER_TOKEN?.trim() || process.env.USER_TOKEN?.trim();
3075
3075
  if (fromEnv) {
3076
- return { headers: { Authorization: `Bearer ${fromEnv}` }, fromStored: false };
3076
+ return {
3077
+ headers: { Authorization: `Bearer ${fromEnv}` },
3078
+ fromStored: false,
3079
+ storedCredentialBackendUrl: null
3080
+ };
3077
3081
  }
3078
3082
  const stored = readStoredAuthCredentials();
3079
- if (!stored) return { headers: {}, fromStored: false };
3083
+ if (!stored) return { headers: {}, fromStored: false, storedCredentialBackendUrl: null };
3080
3084
  if (normalizeBaseUrl4(stored.backendUrl) !== normalizeBaseUrl4(baseUrl)) {
3081
- return { headers: {}, fromStored: false };
3085
+ return { headers: {}, fromStored: false, storedCredentialBackendUrl: stored.backendUrl };
3082
3086
  }
3083
3087
  return {
3084
3088
  headers: { Authorization: `Bearer ${stored.token}` },
3085
- fromStored: true
3089
+ fromStored: true,
3090
+ storedCredentialBackendUrl: null
3086
3091
  };
3087
3092
  }
3088
3093
  async function readResponseJson2(response) {
@@ -3146,6 +3151,12 @@ function createBackendClient() {
3146
3151
  }) {
3147
3152
  const url = `${baseUrl}${path34.startsWith("/") ? path34 : `/${path34}`}`;
3148
3153
  const auth = resolveAuthHeaders({ baseUrl });
3154
+ if (auth.storedCredentialBackendUrl) {
3155
+ throw new CliError({
3156
+ message: `Saved credentials are for ${normalizeBaseUrl4(auth.storedCredentialBackendUrl)}, but this CLI is configured for ${baseUrl}.
3157
+ The backend URL may have changed. Run \`ovld auth login\` to sign in to the current backend.`
3158
+ });
3159
+ }
3149
3160
  let response;
3150
3161
  try {
3151
3162
  response = await fetch(url, {
@@ -5555,8 +5566,8 @@ function resolveNativeSessionId({
5555
5566
  env = process.env
5556
5567
  }) {
5557
5568
  if (explicit !== void 0) {
5558
- const trimmed7 = explicit.trim();
5559
- return trimmed7 === "null" ? null : trimmed7 || void 0;
5569
+ const trimmed8 = explicit.trim();
5570
+ return trimmed8 === "null" ? null : trimmed8 || void 0;
5560
5571
  }
5561
5572
  const normalizedAgent = agent.toLowerCase();
5562
5573
  if (normalizedAgent === "codex") {
@@ -16324,22 +16335,22 @@ var init_contract = __esm({
16324
16335
 
16325
16336
  // ../packages/contract/dist/agent-launch-flags.js
16326
16337
  function parseAgentLaunchFlagText(text) {
16327
- const trimmed7 = text.trim();
16328
- if (!trimmed7)
16338
+ const trimmed8 = text.trim();
16339
+ if (!trimmed8)
16329
16340
  return null;
16330
- const eqIndex = trimmed7.indexOf("=");
16331
- if (eqIndex > 0 && trimmed7.startsWith("--")) {
16332
- const name = trimmed7.slice(0, eqIndex).trim();
16333
- const value = trimmed7.slice(eqIndex + 1).trim();
16341
+ const eqIndex = trimmed8.indexOf("=");
16342
+ if (eqIndex > 0 && trimmed8.startsWith("--")) {
16343
+ const name = trimmed8.slice(0, eqIndex).trim();
16344
+ const value = trimmed8.slice(eqIndex + 1).trim();
16334
16345
  return name ? { name, value: value.length > 0 ? value : null } : null;
16335
16346
  }
16336
- const spaceIndex = trimmed7.indexOf(" ");
16337
- if (spaceIndex > 0 && trimmed7.startsWith("--")) {
16338
- const name = trimmed7.slice(0, spaceIndex).trim();
16339
- const value = trimmed7.slice(spaceIndex + 1).trim();
16347
+ const spaceIndex = trimmed8.indexOf(" ");
16348
+ if (spaceIndex > 0 && trimmed8.startsWith("--")) {
16349
+ const name = trimmed8.slice(0, spaceIndex).trim();
16350
+ const value = trimmed8.slice(spaceIndex + 1).trim();
16340
16351
  return name ? { name, value: value.length > 0 ? value : null } : null;
16341
16352
  }
16342
- return { name: trimmed7 };
16353
+ return { name: trimmed8 };
16343
16354
  }
16344
16355
  function normalizeAgentLaunchFlags(input) {
16345
16356
  if (!Array.isArray(input))
@@ -16490,11 +16501,11 @@ function accessModeToResourcePathPermission({ accessMode, isPrimary = false }) {
16490
16501
  return accessMode === "read" ? "ro" : "rw";
16491
16502
  }
16492
16503
  function formatResourcePathWithPermission({ path: path34, accessMode, isPrimary = false }) {
16493
- const trimmed7 = path34.trim();
16494
- if (!trimmed7)
16504
+ const trimmed8 = path34.trim();
16505
+ if (!trimmed8)
16495
16506
  return "";
16496
16507
  const permission = accessModeToResourcePathPermission({ accessMode, isPrimary });
16497
- return `${trimmed7}:${permission}`;
16508
+ return `${trimmed8}:${permission}`;
16498
16509
  }
16499
16510
  function formatProjectResourcePathsFromManifest(projectResources) {
16500
16511
  if (!Array.isArray(projectResources))
@@ -16632,12 +16643,304 @@ var init_terminal_profile_types2 = __esm({
16632
16643
  }
16633
16644
  });
16634
16645
 
16635
- // ../packages/core/dist/service/latch-launch.js
16646
+ // ../packages/core/dist/service/latch-discovery.js
16647
+ import { execFileSync } from "node:child_process";
16648
+ import { existsSync as existsSync15, mkdirSync as mkdirSync8, readFileSync as readFileSync14, statSync as statSync4, writeFileSync as writeFileSync7 } from "node:fs";
16649
+ import os3 from "node:os";
16650
+ import path15 from "node:path";
16651
+ function defaultLatchDiscoveryCachePath() {
16652
+ const home = process.env.OVLD_HOME?.trim() || path15.join(os3.homedir(), ".ovld");
16653
+ return path15.join(home, LATCH_DISCOVERY_CACHE_FILENAME);
16654
+ }
16636
16655
  function trimmed2(value) {
16637
16656
  return typeof value === "string" && value.trim() ? value.trim() : null;
16638
16657
  }
16658
+ function asBoolean(value) {
16659
+ return value === true;
16660
+ }
16661
+ function asStringArray(value) {
16662
+ if (!Array.isArray(value))
16663
+ return [];
16664
+ return value.filter((entry) => typeof entry === "string");
16665
+ }
16666
+ function parseLatchCapabilitiesReport(raw) {
16667
+ const text = raw.trim();
16668
+ if (!text)
16669
+ return null;
16670
+ let parsed;
16671
+ try {
16672
+ parsed = JSON.parse(text);
16673
+ } catch {
16674
+ return null;
16675
+ }
16676
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
16677
+ return null;
16678
+ const cast = parsed;
16679
+ const protocolVersion = typeof cast.protocolVersion === "number" && Number.isFinite(cast.protocolVersion) ? Math.trunc(cast.protocolVersion) : null;
16680
+ const productVersion = trimmed2(cast.productVersion);
16681
+ if (protocolVersion === null || !productVersion)
16682
+ return null;
16683
+ if (!cast.capabilities || typeof cast.capabilities !== "object" || Array.isArray(cast.capabilities)) {
16684
+ return null;
16685
+ }
16686
+ const caps = cast.capabilities;
16687
+ return {
16688
+ protocolVersion,
16689
+ productVersion,
16690
+ capabilities: {
16691
+ create: asBoolean(caps.create),
16692
+ openViewer: asBoolean(caps.openViewer),
16693
+ localAttach: asBoolean(caps.localAttach),
16694
+ cloudAttach: asBoolean(caps.cloudAttach),
16695
+ selfUpdate: asBoolean(caps.selfUpdate),
16696
+ extensions: asStringArray(caps.extensions)
16697
+ }
16698
+ };
16699
+ }
16700
+ function evaluateLatchCapabilities({ report, executable, resolvedPath, checkedAt = (/* @__PURE__ */ new Date()).toISOString() }) {
16701
+ if (report.protocolVersion !== SUPPORTED_LATCH_PROTOCOL_VERSION) {
16702
+ return {
16703
+ state: "incompatible",
16704
+ executable,
16705
+ resolvedPath,
16706
+ protocolVersion: report.protocolVersion,
16707
+ productVersion: report.productVersion,
16708
+ missingCapability: "protocolVersion",
16709
+ detail: `Latch protocolVersion ${report.protocolVersion} is not supported (need ${SUPPORTED_LATCH_PROTOCOL_VERSION}).`,
16710
+ checkedAt,
16711
+ latchSelectable: false,
16712
+ directSelectable: true
16713
+ };
16714
+ }
16715
+ for (const required of REQUIRED_LATCH_CAPABILITIES) {
16716
+ if (!report.capabilities[required]) {
16717
+ return {
16718
+ state: "incompatible",
16719
+ executable,
16720
+ resolvedPath,
16721
+ protocolVersion: report.protocolVersion,
16722
+ productVersion: report.productVersion,
16723
+ missingCapability: required,
16724
+ detail: `Latch is missing required capability \`${required}\`.`,
16725
+ checkedAt,
16726
+ latchSelectable: false,
16727
+ directSelectable: true
16728
+ };
16729
+ }
16730
+ }
16731
+ return {
16732
+ state: "found",
16733
+ executable,
16734
+ resolvedPath,
16735
+ protocolVersion: report.protocolVersion,
16736
+ productVersion: report.productVersion,
16737
+ capabilities: report.capabilities,
16738
+ checkedAt,
16739
+ latchSelectable: true,
16740
+ directSelectable: true
16741
+ };
16742
+ }
16743
+ function resolveLatchExecutableOnPath(executable) {
16744
+ try {
16745
+ const found = execFileSync("which", [executable], {
16746
+ encoding: "utf8",
16747
+ stdio: ["ignore", "pipe", "ignore"]
16748
+ }).trim();
16749
+ return found || null;
16750
+ } catch {
16751
+ return null;
16752
+ }
16753
+ }
16754
+ function resolveLatchExecutablePathFromEnvironment({ executable, homeDirectory = os3.homedir(), platform: platform2 = process.platform, resolveOnPath = resolveLatchExecutableOnPath, fileExists = existsSync15 }) {
16755
+ const name = trimmed2(executable) ?? DEFAULT_LATCH_EXECUTABLE;
16756
+ if (path15.isAbsolute(name) || name.includes("/") || name.includes("\\")) {
16757
+ return fileExists(name) ? path15.resolve(name) : null;
16758
+ }
16759
+ const resolvedOnPath = resolveOnPath(name);
16760
+ if (resolvedOnPath)
16761
+ return resolvedOnPath;
16762
+ const fallbacks = [
16763
+ path15.join(homeDirectory, ".local", "bin", name),
16764
+ ...platform2 === "darwin" ? [`/opt/homebrew/bin/${name}`, `/usr/local/bin/${name}`] : []
16765
+ ];
16766
+ const fallback = fallbacks.find(fileExists);
16767
+ return fallback ? path15.resolve(fallback) : null;
16768
+ }
16769
+ function resolveLatchExecutablePath(executable) {
16770
+ return resolveLatchExecutablePathFromEnvironment({ executable });
16771
+ }
16772
+ function notInstalledResult({ executable, checkedAt }) {
16773
+ return {
16774
+ state: "not_installed",
16775
+ executable,
16776
+ installCommand: LATCH_STANDALONE_INSTALL_COMMAND,
16777
+ checkedAt,
16778
+ latchSelectable: false,
16779
+ directSelectable: true
16780
+ };
16781
+ }
16782
+ function probeLatchCapabilities({ executable = DEFAULT_LATCH_EXECUTABLE, resolvePath = resolveLatchExecutablePath, runCommand: runCommand2 = defaultCommandRunner, now = () => (/* @__PURE__ */ new Date()).toISOString() } = {}) {
16783
+ const exe = trimmed2(executable) ?? DEFAULT_LATCH_EXECUTABLE;
16784
+ const checkedAt = now();
16785
+ const resolvedPath = resolvePath(exe);
16786
+ if (!resolvedPath) {
16787
+ return notInstalledResult({ executable: exe, checkedAt });
16788
+ }
16789
+ const ran = runCommand2({ executable: resolvedPath, argv: ["capabilities", "--json"] });
16790
+ if (ran.status === null && ran.stderr === "ENOENT") {
16791
+ return notInstalledResult({ executable: exe, checkedAt });
16792
+ }
16793
+ const report = parseLatchCapabilitiesReport(ran.stdout);
16794
+ if (!report) {
16795
+ return {
16796
+ state: "incompatible",
16797
+ executable: exe,
16798
+ resolvedPath,
16799
+ protocolVersion: null,
16800
+ productVersion: null,
16801
+ missingCapability: "capabilities",
16802
+ detail: ran.status === 0 ? "Latch capabilities output was not valid JSON in the expected shape." : `Latch capabilities failed (exit ${ran.status ?? "null"}).`,
16803
+ checkedAt,
16804
+ latchSelectable: false,
16805
+ directSelectable: true
16806
+ };
16807
+ }
16808
+ return evaluateLatchCapabilities({ report, executable: exe, resolvedPath, checkedAt });
16809
+ }
16810
+ function latchDiscoveryCacheKey({ executionTargetId, executable }) {
16811
+ const exe = trimmed2(executable) ?? DEFAULT_LATCH_EXECUTABLE;
16812
+ return `${executionTargetId}::${exe}`;
16813
+ }
16814
+ function readBinaryMtimeMs(resolvedPath) {
16815
+ if (!resolvedPath)
16816
+ return null;
16817
+ try {
16818
+ return statSync4(resolvedPath).mtimeMs;
16819
+ } catch {
16820
+ return null;
16821
+ }
16822
+ }
16823
+ function isLatchDiscoveryCacheFresh({ entry, resolvePath = resolveLatchExecutablePath }) {
16824
+ const { result } = entry;
16825
+ if (result.state === "not_installed") {
16826
+ return resolvePath(result.executable) === null;
16827
+ }
16828
+ const pathStill = result.resolvedPath;
16829
+ if (!pathStill || !existsSync15(pathStill))
16830
+ return false;
16831
+ const mtime = readBinaryMtimeMs(pathStill);
16832
+ if (mtime === null || entry.binaryMtimeMs === null)
16833
+ return false;
16834
+ return mtime === entry.binaryMtimeMs;
16835
+ }
16836
+ function readLatchDiscoveryCacheFile(filePath) {
16837
+ if (!existsSync15(filePath))
16838
+ return { version: 1, entries: {} };
16839
+ try {
16840
+ const parsed = JSON.parse(readFileSync14(filePath, "utf8"));
16841
+ if (parsed.version !== 1 || !parsed.entries || typeof parsed.entries !== "object") {
16842
+ return { version: 1, entries: {} };
16843
+ }
16844
+ return { version: 1, entries: parsed.entries };
16845
+ } catch {
16846
+ return { version: 1, entries: {} };
16847
+ }
16848
+ }
16849
+ function writeLatchDiscoveryCacheFile({ filePath, cache: cache2 }) {
16850
+ mkdirSync8(path15.dirname(filePath), { recursive: true });
16851
+ writeFileSync7(filePath, `${JSON.stringify(cache2, null, 2)}
16852
+ `, { mode: 384 });
16853
+ }
16854
+ function discoverLatchForExecutionTarget({ executionTargetId, executable = DEFAULT_LATCH_EXECUTABLE, cacheFilePath, force = false, resolvePath = resolveLatchExecutablePath, runCommand: runCommand2 = defaultCommandRunner, now = () => (/* @__PURE__ */ new Date()).toISOString() }) {
16855
+ const exe = trimmed2(executable) ?? DEFAULT_LATCH_EXECUTABLE;
16856
+ const key = latchDiscoveryCacheKey({ executionTargetId, executable: exe });
16857
+ const cache2 = readLatchDiscoveryCacheFile(cacheFilePath);
16858
+ const existing = cache2.entries[key];
16859
+ if (!force && existing && isLatchDiscoveryCacheFresh({ entry: existing, resolvePath })) {
16860
+ return { ...existing.result, checkedAt: existing.result.checkedAt, directSelectable: true };
16861
+ }
16862
+ const result = probeLatchCapabilities({ executable: exe, resolvePath, runCommand: runCommand2, now });
16863
+ const binaryMtimeMs = result.state === "not_installed" ? null : readBinaryMtimeMs(result.resolvedPath);
16864
+ cache2.entries[key] = {
16865
+ result,
16866
+ binaryMtimeMs,
16867
+ cachedAt: result.checkedAt
16868
+ };
16869
+ writeLatchDiscoveryCacheFile({ filePath: cacheFilePath, cache: cache2 });
16870
+ return result;
16871
+ }
16872
+ var SUPPORTED_LATCH_PROTOCOL_VERSION, REQUIRED_LATCH_CAPABILITIES, LATCH_STANDALONE_INSTALL_COMMAND, LATCH_DISCOVERY_CACHE_FILENAME, defaultCommandRunner;
16873
+ var init_latch_discovery = __esm({
16874
+ "../packages/core/dist/service/latch-discovery.js"() {
16875
+ "use strict";
16876
+ init_latch_environment();
16877
+ init_terminal_profile_types2();
16878
+ SUPPORTED_LATCH_PROTOCOL_VERSION = 1;
16879
+ REQUIRED_LATCH_CAPABILITIES = ["create"];
16880
+ LATCH_STANDALONE_INSTALL_COMMAND = "curl -fsSL https://raw.githubusercontent.com/jchaselubitz/Latch/main/scripts/install-cli.sh | bash";
16881
+ LATCH_DISCOVERY_CACHE_FILENAME = "latch-discovery.json";
16882
+ defaultCommandRunner = ({ executable, argv }) => {
16883
+ try {
16884
+ const stdout = execFileSync(executable, argv, {
16885
+ encoding: "utf8",
16886
+ stdio: ["ignore", "pipe", "pipe"],
16887
+ timeout: 5e3,
16888
+ env: latchChildEnvironment()
16889
+ });
16890
+ return { status: 0, stdout: String(stdout), stderr: "" };
16891
+ } catch (error) {
16892
+ const err = error;
16893
+ if (err.code === "ENOENT") {
16894
+ return { status: null, stdout: "", stderr: "ENOENT" };
16895
+ }
16896
+ return {
16897
+ status: typeof err.status === "number" ? err.status : 1,
16898
+ stdout: err.stdout ? String(err.stdout) : "",
16899
+ stderr: err.stderr ? String(err.stderr) : String(error)
16900
+ };
16901
+ }
16902
+ };
16903
+ }
16904
+ });
16905
+
16906
+ // ../packages/core/dist/service/latch-binary.js
16907
+ import { existsSync as existsSync16 } from "node:fs";
16908
+ function trimmed3(value) {
16909
+ return typeof value === "string" && value.trim() ? value.trim() : null;
16910
+ }
16911
+ function resolveLatchBinaryPath(executable, resolve = resolveLatchExecutablePath) {
16912
+ const name = trimmed3(executable) ?? DEFAULT_LATCH_EXECUTABLE;
16913
+ const cached = cache.get(name);
16914
+ if (cached) {
16915
+ if (existsSync16(cached))
16916
+ return cached;
16917
+ cache.delete(name);
16918
+ }
16919
+ const resolved = resolve(name);
16920
+ if (resolved)
16921
+ cache.set(name, resolved);
16922
+ return resolved;
16923
+ }
16924
+ function latchBinaryMissingMessage(executable) {
16925
+ const name = trimmed3(executable) ?? DEFAULT_LATCH_EXECUTABLE;
16926
+ return `Latch executable "${name}" was not found on this device (checked PATH, ~/.local/bin, and the Homebrew prefixes).`;
16927
+ }
16928
+ var cache;
16929
+ var init_latch_binary = __esm({
16930
+ "../packages/core/dist/service/latch-binary.js"() {
16931
+ "use strict";
16932
+ init_latch_discovery();
16933
+ init_terminal_profile_types2();
16934
+ cache = /* @__PURE__ */ new Map();
16935
+ }
16936
+ });
16937
+
16938
+ // ../packages/core/dist/service/latch-launch.js
16939
+ function trimmed4(value) {
16940
+ return typeof value === "string" && value.trim() ? value.trim() : null;
16941
+ }
16639
16942
  function buildLatchCreateManifest({ commandString, shell = process.env.SHELL?.trim() || "/bin/bash", cwd, env, size = DEFAULT_LATCH_TERMINAL_SIZE, title, name, commandLabel, externalRunId }) {
16640
- const shellPath = trimmed2(shell) ?? "/bin/bash";
16943
+ const shellPath = trimmed4(shell) ?? "/bin/bash";
16641
16944
  const cleanedEnv = {};
16642
16945
  for (const [key, value] of Object.entries(env)) {
16643
16946
  if (key.trim() && typeof value === "string")
@@ -16646,15 +16949,15 @@ function buildLatchCreateManifest({ commandString, shell = process.env.SHELL?.tr
16646
16949
  const display = {
16647
16950
  source: {
16648
16951
  kind: "overlord",
16649
- ...trimmed2(externalRunId) ? { external_run_id: trimmed2(externalRunId) } : {}
16952
+ ...trimmed4(externalRunId) ? { external_run_id: trimmed4(externalRunId) } : {}
16650
16953
  }
16651
16954
  };
16652
- if (trimmed2(title))
16653
- display.title = trimmed2(title);
16654
- if (trimmed2(name))
16655
- display.name = trimmed2(name);
16656
- if (trimmed2(commandLabel))
16657
- display.command_label = trimmed2(commandLabel);
16955
+ if (trimmed4(title))
16956
+ display.title = trimmed4(title);
16957
+ if (trimmed4(name))
16958
+ display.name = trimmed4(name);
16959
+ if (trimmed4(commandLabel))
16960
+ display.command_label = trimmed4(commandLabel);
16658
16961
  return {
16659
16962
  format_version: LATCH_MANIFEST_FORMAT_VERSION,
16660
16963
  launch: {
@@ -16673,7 +16976,7 @@ function buildLatchCreateManifest({ commandString, shell = process.env.SHELL?.tr
16673
16976
  function parseLatchCreateReport(raw) {
16674
16977
  try {
16675
16978
  const parsed = JSON.parse(raw);
16676
- const id = trimmed2(parsed.session?.id);
16979
+ const id = trimmed4(parsed.session?.id);
16677
16980
  if (!id)
16678
16981
  return null;
16679
16982
  const protocolVersion = typeof parsed.protocolVersion === "number" && Number.isFinite(parsed.protocolVersion) ? parsed.protocolVersion : LATCH_MANIFEST_FORMAT_VERSION;
@@ -16681,9 +16984,9 @@ function parseLatchCreateReport(raw) {
16681
16984
  protocolVersion,
16682
16985
  session: {
16683
16986
  id,
16684
- name: trimmed2(parsed.session?.name) ?? id,
16685
- state: trimmed2(parsed.session?.state) ?? "running",
16686
- createdAt: trimmed2(parsed.session?.createdAt) ?? (/* @__PURE__ */ new Date()).toISOString()
16987
+ name: trimmed4(parsed.session?.name) ?? id,
16988
+ state: trimmed4(parsed.session?.state) ?? "running",
16989
+ createdAt: trimmed4(parsed.session?.createdAt) ?? (/* @__PURE__ */ new Date()).toISOString()
16687
16990
  }
16688
16991
  };
16689
16992
  } catch {
@@ -16695,28 +16998,28 @@ function toExecutionProviderSession({ createReport, executionTargetId, createdAt
16695
16998
  provider: "latch",
16696
16999
  providerSessionId: createReport.session.id,
16697
17000
  sessionName: createReport.session.name,
16698
- executionTargetId: trimmed2(executionTargetId),
17001
+ executionTargetId: trimmed4(executionTargetId),
16699
17002
  agentSessionId: null,
16700
17003
  createdAt: createdAt ?? createReport.session.createdAt,
16701
17004
  lastObservedState: createReport.session.state
16702
17005
  };
16703
17006
  }
16704
17007
  function existingLatchProviderSession({ latchSessionId, executionTargetId, createdAt = (/* @__PURE__ */ new Date()).toISOString() }) {
16705
- const providerSessionId = trimmed2(latchSessionId);
17008
+ const providerSessionId = trimmed4(latchSessionId);
16706
17009
  if (!providerSessionId)
16707
17010
  return null;
16708
17011
  return {
16709
17012
  provider: "latch",
16710
17013
  providerSessionId,
16711
17014
  sessionName: null,
16712
- executionTargetId: trimmed2(executionTargetId),
17015
+ executionTargetId: trimmed4(executionTargetId),
16713
17016
  agentSessionId: null,
16714
17017
  createdAt,
16715
17018
  lastObservedState: "running"
16716
17019
  };
16717
17020
  }
16718
17021
  function latchViewerFlagForKind(kind) {
16719
- switch (trimmed2(kind)?.toLowerCase()) {
17022
+ switch (trimmed4(kind)?.toLowerCase()) {
16720
17023
  case "iterm":
16721
17024
  case "iterm2":
16722
17025
  return "iterm";
@@ -16740,7 +17043,7 @@ function compareLatchProductVersions(a, b) {
16740
17043
  return 0;
16741
17044
  }
16742
17045
  function latchSupportsOpenAs(productVersion) {
16743
- const version = trimmed2(productVersion);
17046
+ const version = trimmed4(productVersion);
16744
17047
  if (!version)
16745
17048
  return false;
16746
17049
  return compareLatchProductVersions(version, LATCH_OPEN_AS_MIN_PRODUCT_VERSION) >= 0;
@@ -16754,10 +17057,10 @@ function buildLatchOpenArgs({ providerSessionId, viewer, openAs, productVersion
16754
17057
  return args;
16755
17058
  }
16756
17059
  function latchAttachCommand({ executable = "latch", providerSessionId }) {
16757
- return `${trimmed2(executable) ?? "latch"} attach ${providerSessionId}`;
17060
+ return `${trimmed4(executable) ?? "latch"} attach ${providerSessionId}`;
16758
17061
  }
16759
17062
  function shouldUseLatchProvider({ providerKind, latchSelectable }) {
16760
- return trimmed2(providerKind)?.toLowerCase() === "latch" && latchSelectable === true;
17063
+ return trimmed4(providerKind)?.toLowerCase() === "latch" && latchSelectable === true;
16761
17064
  }
16762
17065
  var LATCH_MANIFEST_FORMAT_VERSION, DEFAULT_LATCH_TERMINAL_SIZE, LATCH_OPEN_AS_MIN_PRODUCT_VERSION;
16763
17066
  var init_latch_launch = __esm({
@@ -16772,11 +17075,11 @@ var init_latch_launch = __esm({
16772
17075
 
16773
17076
  // ../packages/core/dist/service/latch-session.js
16774
17077
  import { spawnSync as spawnSync2 } from "node:child_process";
16775
- function trimmed3(value) {
17078
+ function trimmed5(value) {
16776
17079
  return typeof value === "string" && value.trim() ? value.trim() : null;
16777
17080
  }
16778
17081
  function parseState(value) {
16779
- switch (trimmed3(value)) {
17082
+ switch (trimmed5(value)) {
16780
17083
  case "running":
16781
17084
  return "running";
16782
17085
  case "exited":
@@ -16790,7 +17093,10 @@ function parseState(value) {
16790
17093
  }
16791
17094
  }
16792
17095
  function runLatchJson({ executable, args }) {
16793
- const result = spawnSync2(executable, args, {
17096
+ const binary = resolveLatchBinaryPath(executable);
17097
+ if (!binary)
17098
+ throw new LatchSessionCommandError(latchBinaryMissingMessage(executable));
17099
+ const result = spawnSync2(binary, args, {
16794
17100
  encoding: "utf8",
16795
17101
  shell: false,
16796
17102
  timeout: 1e4,
@@ -16815,11 +17121,11 @@ function runLatchJson({ executable, args }) {
16815
17121
  }
16816
17122
  }
16817
17123
  function inspectLatchSession({ executable = "latch", providerSessionId }) {
16818
- const sessionId = trimmed3(providerSessionId);
17124
+ const sessionId = trimmed5(providerSessionId);
16819
17125
  if (!sessionId)
16820
17126
  throw new LatchSessionCommandError("A Latch session id is required.");
16821
17127
  const report = runLatchJson({
16822
- executable: trimmed3(executable) ?? "latch",
17128
+ executable: trimmed5(executable) ?? "latch",
16823
17129
  args: ["inspect", sessionId, "--json"]
16824
17130
  });
16825
17131
  const state = parseState(report.state);
@@ -16827,8 +17133,8 @@ function inspectLatchSession({ executable = "latch", providerSessionId }) {
16827
17133
  throw new LatchSessionCommandError("Latch returned an unsupported session state.");
16828
17134
  const exit = report.exit && typeof report.exit === "object" && !Array.isArray(report.exit) ? report.exit : null;
16829
17135
  return {
16830
- providerSessionId: trimmed3(report.id) ?? sessionId,
16831
- name: trimmed3(report.name) ?? sessionId,
17136
+ providerSessionId: trimmed5(report.id) ?? sessionId,
17137
+ name: trimmed5(report.name) ?? sessionId,
16832
17138
  state,
16833
17139
  exitCode: typeof exit?.code === "number" && Number.isFinite(exit.code) ? exit.code : null,
16834
17140
  inspectedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -16836,20 +17142,20 @@ function inspectLatchSession({ executable = "latch", providerSessionId }) {
16836
17142
  }
16837
17143
  function readLatchProductVersion(executable) {
16838
17144
  try {
16839
- return trimmed3(runLatchJson({ executable, args: ["capabilities", "--json"] }).productVersion);
17145
+ return trimmed5(runLatchJson({ executable, args: ["capabilities", "--json"] }).productVersion);
16840
17146
  } catch {
16841
17147
  return null;
16842
17148
  }
16843
17149
  }
16844
17150
  function openLatchSession({ executable = "latch", providerSessionId, viewerKind, openAs }) {
16845
- const sessionId = trimmed3(providerSessionId);
17151
+ const sessionId = trimmed5(providerSessionId);
16846
17152
  if (!sessionId)
16847
17153
  throw new LatchSessionCommandError("A Latch session id is required.");
16848
17154
  const viewer = latchViewerFlagForKind(viewerKind);
16849
17155
  if (!viewer) {
16850
17156
  throw new LatchSessionCommandError(`Latch cannot open the configured viewer "${viewerKind}".`);
16851
17157
  }
16852
- const exe = trimmed3(executable) ?? "latch";
17158
+ const exe = trimmed5(executable) ?? "latch";
16853
17159
  const report = runLatchJson({
16854
17160
  executable: exe,
16855
17161
  args: buildLatchOpenArgs({
@@ -16863,25 +17169,25 @@ function openLatchSession({ executable = "latch", providerSessionId, viewerKind,
16863
17169
  throw new LatchSessionCommandError(`Latch did not open the ${viewer} viewer.`);
16864
17170
  }
16865
17171
  return {
16866
- providerSessionId: trimmed3(report.id) ?? sessionId,
16867
- viewer: trimmed3(report.viewer) ?? viewer,
17172
+ providerSessionId: trimmed5(report.id) ?? sessionId,
17173
+ viewer: trimmed5(report.viewer) ?? viewer,
16868
17174
  opened: true,
16869
- behavior: trimmed3(report.behavior) ? parseViewerOpenAs(report.behavior) : null
17175
+ behavior: trimmed5(report.behavior) ? parseViewerOpenAs(report.behavior) : null
16870
17176
  };
16871
17177
  }
16872
17178
  function stopLatchSession({ executable = "latch", providerSessionId }) {
16873
- const sessionId = trimmed3(providerSessionId);
17179
+ const sessionId = trimmed5(providerSessionId);
16874
17180
  if (!sessionId)
16875
17181
  throw new LatchSessionCommandError("A Latch session id is required.");
16876
17182
  const report = runLatchJson({
16877
- executable: trimmed3(executable) ?? "latch",
17183
+ executable: trimmed5(executable) ?? "latch",
16878
17184
  args: ["stop", sessionId, "--json"]
16879
17185
  });
16880
17186
  const state = parseState(report.state);
16881
17187
  if (!state)
16882
17188
  throw new LatchSessionCommandError("Latch returned an unsupported session state.");
16883
17189
  return {
16884
- providerSessionId: trimmed3(report.id) ?? sessionId,
17190
+ providerSessionId: trimmed5(report.id) ?? sessionId,
16885
17191
  state
16886
17192
  };
16887
17193
  }
@@ -16889,6 +17195,7 @@ var LatchSessionCommandError;
16889
17195
  var init_latch_session = __esm({
16890
17196
  "../packages/core/dist/service/latch-session.js"() {
16891
17197
  "use strict";
17198
+ init_latch_binary();
16892
17199
  init_latch_environment();
16893
17200
  init_latch_launch();
16894
17201
  init_terminal_profile_types2();
@@ -16903,17 +17210,17 @@ var init_latch_session = __esm({
16903
17210
 
16904
17211
  // ../packages/core/dist/service/latch-events.js
16905
17212
  import { spawn as spawn3 } from "node:child_process";
16906
- function trimmed4(value) {
17213
+ function trimmed6(value) {
16907
17214
  return typeof value === "string" && value.trim() ? value.trim() : null;
16908
17215
  }
16909
17216
  function parseHarnessEvent(value) {
16910
17217
  if (!value || typeof value !== "object" || Array.isArray(value))
16911
17218
  return null;
16912
17219
  const row = value;
16913
- const type = trimmed4(row.type);
16914
- const sessionId = trimmed4(row.sessionId);
16915
- const at = trimmed4(row.at);
16916
- const harnessVersion = trimmed4(row.harnessVersion);
17220
+ const type = trimmed6(row.type);
17221
+ const sessionId = trimmed6(row.sessionId);
17222
+ const at = trimmed6(row.at);
17223
+ const harnessVersion = trimmed6(row.harnessVersion);
16917
17224
  const connectorEpoch = typeof row.connectorEpoch === "number" && Number.isFinite(row.connectorEpoch) ? row.connectorEpoch : null;
16918
17225
  if (!type || !HARNESS_EVENT_TYPES.has(type) || !sessionId || !at || !harnessVersion)
16919
17226
  return null;
@@ -16924,26 +17231,26 @@ function parseHarnessEvent(value) {
16924
17231
  case "user_message":
16925
17232
  case "assistant_delta":
16926
17233
  case "assistant_message": {
16927
- const text = trimmed4(row.text);
17234
+ const text = trimmed6(row.text);
16928
17235
  if (!text)
16929
17236
  return null;
16930
17237
  return { ...base, type, text };
16931
17238
  }
16932
17239
  case "tool_started": {
16933
- const tool = trimmed4(row.tool);
17240
+ const tool = trimmed6(row.tool);
16934
17241
  if (!tool)
16935
17242
  return null;
16936
17243
  return { ...base, type, tool, input: row.input };
16937
17244
  }
16938
17245
  case "tool_finished": {
16939
- const tool = trimmed4(row.tool);
17246
+ const tool = trimmed6(row.tool);
16940
17247
  if (!tool)
16941
17248
  return null;
16942
17249
  return { ...base, type, tool, output: row.output };
16943
17250
  }
16944
17251
  case "awaiting_input": {
16945
- const requestId = trimmed4(row.requestId);
16946
- const kind = trimmed4(row.kind);
17252
+ const requestId = trimmed6(row.requestId);
17253
+ const kind = trimmed6(row.kind);
16947
17254
  const prompt = typeof row.prompt === "string" ? row.prompt : null;
16948
17255
  if (!requestId || kind !== "permission" && kind !== "question" || prompt === null) {
16949
17256
  return null;
@@ -16959,7 +17266,7 @@ function parseHarnessEvent(value) {
16959
17266
  };
16960
17267
  }
16961
17268
  case "status": {
16962
- const status = trimmed4(row.status);
17269
+ const status = trimmed6(row.status);
16963
17270
  if (!status)
16964
17271
  return null;
16965
17272
  return { ...base, type, status };
@@ -16984,11 +17291,13 @@ function parseHarnessEventNdjson(raw) {
16984
17291
  return events;
16985
17292
  }
16986
17293
  async function collectLatchEvents({ executable = "latch", providerSessionId, from = 0, idleMs = 250, timeoutMs = 8e3 }) {
16987
- const sessionId = trimmed4(providerSessionId);
17294
+ const sessionId = trimmed6(providerSessionId);
16988
17295
  if (!sessionId)
16989
17296
  throw new LatchSessionCommandError("A Latch session id is required.");
16990
17297
  const fromCursor = Number.isFinite(from) && from > 0 ? Math.floor(from) : 0;
16991
- const bin = trimmed4(executable) ?? "latch";
17298
+ const bin = resolveLatchBinaryPath(executable);
17299
+ if (!bin)
17300
+ throw new LatchSessionCommandError(latchBinaryMissingMessage(executable));
16992
17301
  return new Promise((resolve, reject) => {
16993
17302
  const child = spawn3(bin, ["events", sessionId, "--json", "--from", String(fromCursor)], {
16994
17303
  stdio: ["ignore", "pipe", "pipe"],
@@ -17056,6 +17365,7 @@ var HARNESS_EVENT_TYPES;
17056
17365
  var init_latch_events = __esm({
17057
17366
  "../packages/core/dist/service/latch-events.js"() {
17058
17367
  "use strict";
17368
+ init_latch_binary();
17059
17369
  init_latch_environment();
17060
17370
  init_latch_session();
17061
17371
  HARNESS_EVENT_TYPES = /* @__PURE__ */ new Set([
@@ -17085,12 +17395,12 @@ var init_util2 = __esm({
17085
17395
  });
17086
17396
 
17087
17397
  // ../packages/core/dist/service/local-target/project-metadata.js
17088
- import { execFileSync } from "node:child_process";
17089
- import { existsSync as existsSync15, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "node:fs";
17090
- import path15 from "node:path";
17398
+ import { execFileSync as execFileSync2 } from "node:child_process";
17399
+ import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "node:fs";
17400
+ import path16 from "node:path";
17091
17401
  function writeIfChanged(filePath, content) {
17092
- if (!existsSync15(filePath) || readFileSync14(filePath, "utf8") !== content) {
17093
- writeFileSync7(filePath, content);
17402
+ if (!existsSync17(filePath) || readFileSync15(filePath, "utf8") !== content) {
17403
+ writeFileSync8(filePath, content);
17094
17404
  }
17095
17405
  }
17096
17406
  function replaceManagedBlock(content, block) {
@@ -17101,25 +17411,25 @@ function replaceManagedBlock(content, block) {
17101
17411
  }
17102
17412
  function ensureAgentInstructions(checkoutPath) {
17103
17413
  for (const filename of ["AGENTS.md", "CLAUDE.md"]) {
17104
- const instructionPath = path15.join(checkoutPath, filename);
17105
- const existing = existsSync15(instructionPath) ? readFileSync14(instructionPath, "utf8") : "";
17414
+ const instructionPath = path16.join(checkoutPath, filename);
17415
+ const existing = existsSync17(instructionPath) ? readFileSync15(instructionPath, "utf8") : "";
17106
17416
  writeIfChanged(instructionPath, replaceManagedBlock(existing, PROJECT_METADATA_INSTRUCTION));
17107
17417
  }
17108
17418
  }
17109
17419
  function ensureGitignore(checkoutPath) {
17110
- const gitignorePath = path15.join(checkoutPath, ".gitignore");
17111
- const existing = existsSync15(gitignorePath) ? readFileSync14(gitignorePath, "utf8") : "";
17420
+ const gitignorePath = path16.join(checkoutPath, ".gitignore");
17421
+ const existing = existsSync17(gitignorePath) ? readFileSync15(gitignorePath, "utf8") : "";
17112
17422
  const existingPatterns = new Set(existing.split(/\r?\n/).map((line) => line.trim().replace(/^\//, "")).filter(Boolean));
17113
17423
  const additions = [".overlord/tmp/", ".overlord/logs/"].filter((pattern) => !existingPatterns.has(pattern));
17114
17424
  if (additions.length === 0)
17115
17425
  return;
17116
17426
  const separator = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
17117
- writeFileSync7(gitignorePath, `${existing}${separator}${additions.join("\n")}
17427
+ writeFileSync8(gitignorePath, `${existing}${separator}${additions.join("\n")}
17118
17428
  `);
17119
17429
  }
17120
17430
  function resolveGitRoot(directoryPath) {
17121
17431
  try {
17122
- return execFileSync("git", ["-C", directoryPath, "rev-parse", "--show-toplevel"], {
17432
+ return execFileSync2("git", ["-C", directoryPath, "rev-parse", "--show-toplevel"], {
17123
17433
  encoding: "utf8",
17124
17434
  stdio: ["ignore", "pipe", "ignore"]
17125
17435
  }).trim();
@@ -17176,9 +17486,9 @@ function selectPrimaryProjectEntry(projects) {
17176
17486
  return latestLinkedAt(projects);
17177
17487
  }
17178
17488
  function parseProjectJson(projectJsonPath) {
17179
- if (!existsSync15(projectJsonPath))
17489
+ if (!existsSync17(projectJsonPath))
17180
17490
  return null;
17181
- const parsed = JSON.parse(readFileSync14(projectJsonPath, "utf8"));
17491
+ const parsed = JSON.parse(readFileSync15(projectJsonPath, "utf8"));
17182
17492
  const fromArray = Array.isArray(parsed.projects) ? parsed.projects.map((entry) => parseProjectEntry(entry)).filter((entry) => entry !== null) : [];
17183
17493
  const fromTopLevel = parseProjectEntry(parsed);
17184
17494
  const projects = fromArray.length > 0 ? fromArray : fromTopLevel ? [fromTopLevel] : [];
@@ -17245,12 +17555,12 @@ function serializeProjectEntry(entry) {
17245
17555
  };
17246
17556
  }
17247
17557
  function writeProjectJson({ directoryPath, projectId, projectName, resourceId, resourceKey, executionTargetId, isPrimary }) {
17248
- const overlordDir = path15.join(directoryPath, ".overlord");
17249
- mkdirSync8(overlordDir, { recursive: true });
17250
- mkdirSync8(path15.join(overlordDir, "tmp"), { recursive: true });
17251
- mkdirSync8(path15.join(overlordDir, "logs"), { recursive: true });
17558
+ const overlordDir = path16.join(directoryPath, ".overlord");
17559
+ mkdirSync9(overlordDir, { recursive: true });
17560
+ mkdirSync9(path16.join(overlordDir, "tmp"), { recursive: true });
17561
+ mkdirSync9(path16.join(overlordDir, "logs"), { recursive: true });
17252
17562
  protectProjectMetadata(directoryPath);
17253
- const projectJsonPath = path15.join(overlordDir, "project.json");
17563
+ const projectJsonPath = path16.join(overlordDir, "project.json");
17254
17564
  const existing = parseProjectJson(projectJsonPath);
17255
17565
  const existingProjects = existing?.projects ?? [];
17256
17566
  const existingEntry = existingProjects.find((entry) => entry.projectId === projectId);
@@ -17277,7 +17587,7 @@ function writeProjectJson({ directoryPath, projectId, projectName, resourceId, r
17277
17587
  ];
17278
17588
  const primary = selectPrimaryProjectEntry(projects) ?? nextEntry;
17279
17589
  const primaryResourceIds = primary.resourceIdsByExecutionTarget ?? {};
17280
- writeFileSync7(projectJsonPath, `${JSON.stringify({
17590
+ writeFileSync8(projectJsonPath, `${JSON.stringify({
17281
17591
  _warning: PROJECT_JSON_WARNING,
17282
17592
  version: PROJECT_JSON_VERSION,
17283
17593
  projectId: primary.projectId,
@@ -17312,11 +17622,11 @@ ${INSTRUCTION_BLOCK_END}`;
17312
17622
  });
17313
17623
 
17314
17624
  // ../packages/core/dist/repository/git-tree.js
17315
- import { execFileSync as execFileSync2 } from "node:child_process";
17316
- import path16 from "node:path";
17625
+ import { execFileSync as execFileSync3 } from "node:child_process";
17626
+ import path17 from "node:path";
17317
17627
  function runGit(cwd, args) {
17318
17628
  try {
17319
- return execFileSync2("git", args, {
17629
+ return execFileSync3("git", args, {
17320
17630
  cwd,
17321
17631
  encoding: "utf8",
17322
17632
  maxBuffer: 10 * 1024 * 1024,
@@ -17336,17 +17646,17 @@ function gitPath(cwd, args) {
17336
17646
  }
17337
17647
  }
17338
17648
  function addDirectoryAncestors(paths, filePath) {
17339
- let parent = path16.posix.dirname(filePath);
17649
+ let parent = path17.posix.dirname(filePath);
17340
17650
  while (parent !== "." && parent !== "/") {
17341
17651
  paths.add(parent);
17342
- parent = path16.posix.dirname(parent);
17652
+ parent = path17.posix.dirname(parent);
17343
17653
  }
17344
17654
  }
17345
17655
  function toEntry(entryPath, type) {
17346
- const parentPath = path16.posix.dirname(entryPath);
17656
+ const parentPath = path17.posix.dirname(entryPath);
17347
17657
  return {
17348
17658
  path: entryPath,
17349
- name: path16.posix.basename(entryPath),
17659
+ name: path17.posix.basename(entryPath),
17350
17660
  type,
17351
17661
  parentPath: parentPath === "." ? null : parentPath,
17352
17662
  depth: entryPath.split("/").length - 1
@@ -17358,7 +17668,7 @@ function compareEntries(a, b) {
17358
17668
  return a.path < b.path ? -1 : 1;
17359
17669
  }
17360
17670
  function readRepositoryTree(rootPath, options = {}) {
17361
- const resolvedRoot = path16.resolve(rootPath);
17671
+ const resolvedRoot = path17.resolve(rootPath);
17362
17672
  const gitRoot = gitPath(resolvedRoot, ["rev-parse", "--show-toplevel"]);
17363
17673
  if (!gitRoot) {
17364
17674
  throw new RepositoryReadError("not_git_repository", `${resolvedRoot} is not inside a git repository.`);
@@ -17381,7 +17691,7 @@ function readRepositoryTree(rootPath, options = {}) {
17381
17691
  const truncated = entries.length > maxEntries;
17382
17692
  return {
17383
17693
  rootPath: resolvedRoot,
17384
- gitRoot: path16.resolve(gitRoot),
17694
+ gitRoot: path17.resolve(gitRoot),
17385
17695
  branch: gitPath(gitRoot, ["branch", "--show-current"]),
17386
17696
  commit: gitPath(gitRoot, ["rev-parse", "--short", "HEAD"]),
17387
17697
  entries: truncated ? entries.slice(0, maxEntries) : entries,
@@ -17403,274 +17713,14 @@ var init_git_tree = __esm({
17403
17713
  }
17404
17714
  });
17405
17715
 
17406
- // ../packages/core/dist/service/latch-discovery.js
17407
- import { execFileSync as execFileSync3 } from "node:child_process";
17408
- import { existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync15, statSync as statSync4, writeFileSync as writeFileSync8 } from "node:fs";
17409
- import os3 from "node:os";
17410
- import path17 from "node:path";
17411
- function defaultLatchDiscoveryCachePath() {
17412
- const home = process.env.OVLD_HOME?.trim() || path17.join(os3.homedir(), ".ovld");
17413
- return path17.join(home, LATCH_DISCOVERY_CACHE_FILENAME);
17414
- }
17415
- function trimmed5(value) {
17416
- return typeof value === "string" && value.trim() ? value.trim() : null;
17417
- }
17418
- function asBoolean(value) {
17419
- return value === true;
17420
- }
17421
- function asStringArray(value) {
17422
- if (!Array.isArray(value))
17423
- return [];
17424
- return value.filter((entry) => typeof entry === "string");
17425
- }
17426
- function parseLatchCapabilitiesReport(raw) {
17427
- const text = raw.trim();
17428
- if (!text)
17429
- return null;
17430
- let parsed;
17431
- try {
17432
- parsed = JSON.parse(text);
17433
- } catch {
17434
- return null;
17435
- }
17436
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
17437
- return null;
17438
- const cast = parsed;
17439
- const protocolVersion = typeof cast.protocolVersion === "number" && Number.isFinite(cast.protocolVersion) ? Math.trunc(cast.protocolVersion) : null;
17440
- const productVersion = trimmed5(cast.productVersion);
17441
- if (protocolVersion === null || !productVersion)
17442
- return null;
17443
- if (!cast.capabilities || typeof cast.capabilities !== "object" || Array.isArray(cast.capabilities)) {
17444
- return null;
17445
- }
17446
- const caps = cast.capabilities;
17447
- return {
17448
- protocolVersion,
17449
- productVersion,
17450
- capabilities: {
17451
- create: asBoolean(caps.create),
17452
- openViewer: asBoolean(caps.openViewer),
17453
- localAttach: asBoolean(caps.localAttach),
17454
- cloudAttach: asBoolean(caps.cloudAttach),
17455
- selfUpdate: asBoolean(caps.selfUpdate),
17456
- extensions: asStringArray(caps.extensions)
17457
- }
17458
- };
17459
- }
17460
- function evaluateLatchCapabilities({ report, executable, resolvedPath, checkedAt = (/* @__PURE__ */ new Date()).toISOString() }) {
17461
- if (report.protocolVersion !== SUPPORTED_LATCH_PROTOCOL_VERSION) {
17462
- return {
17463
- state: "incompatible",
17464
- executable,
17465
- resolvedPath,
17466
- protocolVersion: report.protocolVersion,
17467
- productVersion: report.productVersion,
17468
- missingCapability: "protocolVersion",
17469
- detail: `Latch protocolVersion ${report.protocolVersion} is not supported (need ${SUPPORTED_LATCH_PROTOCOL_VERSION}).`,
17470
- checkedAt,
17471
- latchSelectable: false,
17472
- directSelectable: true
17473
- };
17474
- }
17475
- for (const required of REQUIRED_LATCH_CAPABILITIES) {
17476
- if (!report.capabilities[required]) {
17477
- return {
17478
- state: "incompatible",
17479
- executable,
17480
- resolvedPath,
17481
- protocolVersion: report.protocolVersion,
17482
- productVersion: report.productVersion,
17483
- missingCapability: required,
17484
- detail: `Latch is missing required capability \`${required}\`.`,
17485
- checkedAt,
17486
- latchSelectable: false,
17487
- directSelectable: true
17488
- };
17489
- }
17490
- }
17491
- return {
17492
- state: "found",
17493
- executable,
17494
- resolvedPath,
17495
- protocolVersion: report.protocolVersion,
17496
- productVersion: report.productVersion,
17497
- capabilities: report.capabilities,
17498
- checkedAt,
17499
- latchSelectable: true,
17500
- directSelectable: true
17501
- };
17502
- }
17503
- function resolveLatchExecutableOnPath(executable) {
17504
- try {
17505
- const found = execFileSync3("which", [executable], {
17506
- encoding: "utf8",
17507
- stdio: ["ignore", "pipe", "ignore"]
17508
- }).trim();
17509
- return found || null;
17510
- } catch {
17511
- return null;
17512
- }
17513
- }
17514
- function resolveLatchExecutablePathFromEnvironment({ executable, homeDirectory = os3.homedir(), platform: platform2 = process.platform, resolveOnPath = resolveLatchExecutableOnPath, fileExists = existsSync16 }) {
17515
- const name = trimmed5(executable) ?? DEFAULT_LATCH_EXECUTABLE;
17516
- if (path17.isAbsolute(name) || name.includes("/") || name.includes("\\")) {
17517
- return fileExists(name) ? path17.resolve(name) : null;
17518
- }
17519
- const resolvedOnPath = resolveOnPath(name);
17520
- if (resolvedOnPath)
17521
- return resolvedOnPath;
17522
- const fallbacks = [
17523
- path17.join(homeDirectory, ".local", "bin", name),
17524
- ...platform2 === "darwin" ? [`/opt/homebrew/bin/${name}`, `/usr/local/bin/${name}`] : []
17525
- ];
17526
- const fallback = fallbacks.find(fileExists);
17527
- return fallback ? path17.resolve(fallback) : null;
17528
- }
17529
- function resolveLatchExecutablePath(executable) {
17530
- return resolveLatchExecutablePathFromEnvironment({ executable });
17531
- }
17532
- function notInstalledResult({ executable, checkedAt }) {
17533
- return {
17534
- state: "not_installed",
17535
- executable,
17536
- installCommand: LATCH_STANDALONE_INSTALL_COMMAND,
17537
- checkedAt,
17538
- latchSelectable: false,
17539
- directSelectable: true
17540
- };
17541
- }
17542
- function probeLatchCapabilities({ executable = DEFAULT_LATCH_EXECUTABLE, resolvePath = resolveLatchExecutablePath, runCommand: runCommand2 = defaultCommandRunner, now = () => (/* @__PURE__ */ new Date()).toISOString() } = {}) {
17543
- const exe = trimmed5(executable) ?? DEFAULT_LATCH_EXECUTABLE;
17544
- const checkedAt = now();
17545
- const resolvedPath = resolvePath(exe);
17546
- if (!resolvedPath) {
17547
- return notInstalledResult({ executable: exe, checkedAt });
17548
- }
17549
- const ran = runCommand2({ executable: resolvedPath, argv: ["capabilities", "--json"] });
17550
- if (ran.status === null && ran.stderr === "ENOENT") {
17551
- return notInstalledResult({ executable: exe, checkedAt });
17552
- }
17553
- const report = parseLatchCapabilitiesReport(ran.stdout);
17554
- if (!report) {
17555
- return {
17556
- state: "incompatible",
17557
- executable: exe,
17558
- resolvedPath,
17559
- protocolVersion: null,
17560
- productVersion: null,
17561
- missingCapability: "capabilities",
17562
- detail: ran.status === 0 ? "Latch capabilities output was not valid JSON in the expected shape." : `Latch capabilities failed (exit ${ran.status ?? "null"}).`,
17563
- checkedAt,
17564
- latchSelectable: false,
17565
- directSelectable: true
17566
- };
17567
- }
17568
- return evaluateLatchCapabilities({ report, executable: exe, resolvedPath, checkedAt });
17569
- }
17570
- function latchDiscoveryCacheKey({ executionTargetId, executable }) {
17571
- const exe = trimmed5(executable) ?? DEFAULT_LATCH_EXECUTABLE;
17572
- return `${executionTargetId}::${exe}`;
17573
- }
17574
- function readBinaryMtimeMs(resolvedPath) {
17575
- if (!resolvedPath)
17576
- return null;
17577
- try {
17578
- return statSync4(resolvedPath).mtimeMs;
17579
- } catch {
17580
- return null;
17581
- }
17582
- }
17583
- function isLatchDiscoveryCacheFresh({ entry, resolvePath = resolveLatchExecutablePath }) {
17584
- const { result } = entry;
17585
- if (result.state === "not_installed") {
17586
- return resolvePath(result.executable) === null;
17587
- }
17588
- const pathStill = result.resolvedPath;
17589
- if (!pathStill || !existsSync16(pathStill))
17590
- return false;
17591
- const mtime = readBinaryMtimeMs(pathStill);
17592
- if (mtime === null || entry.binaryMtimeMs === null)
17593
- return false;
17594
- return mtime === entry.binaryMtimeMs;
17595
- }
17596
- function readLatchDiscoveryCacheFile(filePath) {
17597
- if (!existsSync16(filePath))
17598
- return { version: 1, entries: {} };
17599
- try {
17600
- const parsed = JSON.parse(readFileSync15(filePath, "utf8"));
17601
- if (parsed.version !== 1 || !parsed.entries || typeof parsed.entries !== "object") {
17602
- return { version: 1, entries: {} };
17603
- }
17604
- return { version: 1, entries: parsed.entries };
17605
- } catch {
17606
- return { version: 1, entries: {} };
17607
- }
17608
- }
17609
- function writeLatchDiscoveryCacheFile({ filePath, cache }) {
17610
- mkdirSync9(path17.dirname(filePath), { recursive: true });
17611
- writeFileSync8(filePath, `${JSON.stringify(cache, null, 2)}
17612
- `, { mode: 384 });
17613
- }
17614
- function discoverLatchForExecutionTarget({ executionTargetId, executable = DEFAULT_LATCH_EXECUTABLE, cacheFilePath, force = false, resolvePath = resolveLatchExecutablePath, runCommand: runCommand2 = defaultCommandRunner, now = () => (/* @__PURE__ */ new Date()).toISOString() }) {
17615
- const exe = trimmed5(executable) ?? DEFAULT_LATCH_EXECUTABLE;
17616
- const key = latchDiscoveryCacheKey({ executionTargetId, executable: exe });
17617
- const cache = readLatchDiscoveryCacheFile(cacheFilePath);
17618
- const existing = cache.entries[key];
17619
- if (!force && existing && isLatchDiscoveryCacheFresh({ entry: existing, resolvePath })) {
17620
- return { ...existing.result, checkedAt: existing.result.checkedAt, directSelectable: true };
17621
- }
17622
- const result = probeLatchCapabilities({ executable: exe, resolvePath, runCommand: runCommand2, now });
17623
- const binaryMtimeMs = result.state === "not_installed" ? null : readBinaryMtimeMs(result.resolvedPath);
17624
- cache.entries[key] = {
17625
- result,
17626
- binaryMtimeMs,
17627
- cachedAt: result.checkedAt
17628
- };
17629
- writeLatchDiscoveryCacheFile({ filePath: cacheFilePath, cache });
17630
- return result;
17631
- }
17632
- var SUPPORTED_LATCH_PROTOCOL_VERSION, REQUIRED_LATCH_CAPABILITIES, LATCH_STANDALONE_INSTALL_COMMAND, LATCH_DISCOVERY_CACHE_FILENAME, defaultCommandRunner;
17633
- var init_latch_discovery = __esm({
17634
- "../packages/core/dist/service/latch-discovery.js"() {
17635
- "use strict";
17636
- init_latch_environment();
17637
- init_terminal_profile_types2();
17638
- SUPPORTED_LATCH_PROTOCOL_VERSION = 1;
17639
- REQUIRED_LATCH_CAPABILITIES = ["create"];
17640
- LATCH_STANDALONE_INSTALL_COMMAND = "curl -fsSL https://raw.githubusercontent.com/jchaselubitz/Latch/main/scripts/install-cli.sh | bash";
17641
- LATCH_DISCOVERY_CACHE_FILENAME = "latch-discovery.json";
17642
- defaultCommandRunner = ({ executable, argv }) => {
17643
- try {
17644
- const stdout = execFileSync3(executable, argv, {
17645
- encoding: "utf8",
17646
- stdio: ["ignore", "pipe", "pipe"],
17647
- timeout: 5e3,
17648
- env: latchChildEnvironment()
17649
- });
17650
- return { status: 0, stdout: String(stdout), stderr: "" };
17651
- } catch (error) {
17652
- const err = error;
17653
- if (err.code === "ENOENT") {
17654
- return { status: null, stdout: "", stderr: "ENOENT" };
17655
- }
17656
- return {
17657
- status: typeof err.status === "number" ? err.status : 1,
17658
- stdout: err.stdout ? String(err.stdout) : "",
17659
- stderr: err.stderr ? String(err.stderr) : String(error)
17660
- };
17661
- }
17662
- };
17663
- }
17664
- });
17665
-
17666
17716
  // ../packages/core/dist/service/latch-send.js
17667
17717
  import { spawnSync as spawnSync3 } from "node:child_process";
17668
- function trimmed6(value) {
17718
+ function trimmed7(value) {
17669
17719
  return typeof value === "string" && value.trim() ? value.trim() : null;
17670
17720
  }
17671
17721
  function probeLatchInteractionCapabilities({ executable = "latch", providerSessionId }) {
17672
- const sessionId = trimmed6(providerSessionId);
17673
- const bin = trimmed6(executable) ?? "latch";
17722
+ const sessionId = trimmed7(providerSessionId);
17723
+ const bin = resolveLatchBinaryPath(executable);
17674
17724
  if (!sessionId) {
17675
17725
  return {
17676
17726
  sendMessage: false,
@@ -17679,6 +17729,14 @@ function probeLatchInteractionCapabilities({ executable = "latch", providerSessi
17679
17729
  canSend: { ok: false, reason: "A Latch session id is required." }
17680
17730
  };
17681
17731
  }
17732
+ if (!bin) {
17733
+ return {
17734
+ sendMessage: false,
17735
+ sendKeys: false,
17736
+ resolve: false,
17737
+ canSend: { ok: false, reason: latchBinaryMissingMessage(executable) }
17738
+ };
17739
+ }
17682
17740
  const help = spawnSync3(bin, ["send", "--help"], {
17683
17741
  encoding: "utf8",
17684
17742
  shell: false,
@@ -17705,9 +17763,9 @@ ${help.stderr}`),
17705
17763
  };
17706
17764
  }
17707
17765
  function resolveLatchInput({ executable = "latch", providerSessionId, requestId, choice }) {
17708
- const sessionId = trimmed6(providerSessionId);
17709
- const id = trimmed6(requestId);
17710
- const selected = trimmed6(choice);
17766
+ const sessionId = trimmed7(providerSessionId);
17767
+ const id = trimmed7(requestId);
17768
+ const selected = trimmed7(choice);
17711
17769
  if (!sessionId)
17712
17770
  throw new LatchSessionCommandError("A Latch session id is required.");
17713
17771
  if (!id)
@@ -17721,7 +17779,9 @@ function resolveLatchInput({ executable = "latch", providerSessionId, requestId,
17721
17779
  if (!capabilities.resolve || !capabilities.canSend.ok) {
17722
17780
  throw new LatchSessionCommandError(capabilities.canSend.reason || "This Latch session cannot resolve a pending prompt.");
17723
17781
  }
17724
- const bin = trimmed6(executable) ?? "latch";
17782
+ const bin = resolveLatchBinaryPath(executable);
17783
+ if (!bin)
17784
+ throw new LatchSessionCommandError(latchBinaryMissingMessage(executable));
17725
17785
  const result = spawnSync3(bin, ["send", sessionId, "--resolve", `${id}=${selected}`, "--json"], {
17726
17786
  encoding: "utf8",
17727
17787
  shell: false,
@@ -17745,6 +17805,7 @@ function resolveLatchInput({ executable = "latch", providerSessionId, requestId,
17745
17805
  var init_latch_send = __esm({
17746
17806
  "../packages/core/dist/service/latch-send.js"() {
17747
17807
  "use strict";
17808
+ init_latch_binary();
17748
17809
  init_latch_environment();
17749
17810
  init_latch_session();
17750
17811
  }
@@ -17789,7 +17850,7 @@ var init_git_run = __esm({
17789
17850
  });
17790
17851
 
17791
17852
  // ../packages/core/dist/service/local-target/worktree-git.js
17792
- import { existsSync as existsSync17, realpathSync as realpathSync2 } from "node:fs";
17853
+ import { existsSync as existsSync18, realpathSync as realpathSync2 } from "node:fs";
17793
17854
  import path18 from "node:path";
17794
17855
  function resolveRealPath(targetPath) {
17795
17856
  try {
@@ -17825,7 +17886,7 @@ function removeGitWorktree({ primaryRepoPath, worktreePath, force }) {
17825
17886
  args.push(worktreePath);
17826
17887
  runGitResult(primaryRepoPath, args);
17827
17888
  runGitResult(primaryRepoPath, ["worktree", "prune"]);
17828
- return !existsSync17(worktreePath);
17889
+ return !existsSync18(worktreePath);
17829
17890
  }
17830
17891
  function collectManagedWorktrees({ worktreeRoot, projects }) {
17831
17892
  const resolvedRoot = resolveRealPath(worktreeRoot);
@@ -17833,7 +17894,7 @@ function collectManagedWorktrees({ worktreeRoot, projects }) {
17833
17894
  const seen = /* @__PURE__ */ new Set();
17834
17895
  for (const project of projects) {
17835
17896
  const repoPath = project.primaryRepoPath;
17836
- if (!repoPath || !existsSync17(repoPath))
17897
+ if (!repoPath || !existsSync18(repoPath))
17837
17898
  continue;
17838
17899
  const out = runGitResult(repoPath, ["worktree", "list", "--porcelain"]);
17839
17900
  if (!out.ok)
@@ -17911,10 +17972,10 @@ var init_worktree_git = __esm({
17911
17972
  });
17912
17973
 
17913
17974
  // ../packages/core/dist/service/local-target/branch-actions-git.js
17914
- import { existsSync as existsSync18 } from "node:fs";
17975
+ import { existsSync as existsSync19 } from "node:fs";
17915
17976
  function integrateBranch(input) {
17916
17977
  const { branchName, baseBranch, worktreePath, primaryRepoPath } = input;
17917
- if (!existsSync18(worktreePath)) {
17978
+ if (!existsSync19(worktreePath)) {
17918
17979
  return {
17919
17980
  ok: false,
17920
17981
  code: "BRANCH_NO_WORKTREE",
@@ -17988,15 +18049,15 @@ function integrateBranch(input) {
17988
18049
  }
17989
18050
  function commitBranch(input) {
17990
18051
  const { branchName, worktreePath } = input;
17991
- const trimmed7 = (input.message ?? "").trim();
17992
- if (!trimmed7) {
18052
+ const trimmed8 = (input.message ?? "").trim();
18053
+ if (!trimmed8) {
17993
18054
  return {
17994
18055
  ok: false,
17995
18056
  code: "BRANCH_COMMIT_MESSAGE_REQUIRED",
17996
18057
  message: "A commit message is required."
17997
18058
  };
17998
18059
  }
17999
- if (!existsSync18(worktreePath)) {
18060
+ if (!existsSync19(worktreePath)) {
18000
18061
  return {
18001
18062
  ok: false,
18002
18063
  code: "BRANCH_NO_WORKTREE",
@@ -18028,7 +18089,7 @@ function commitBranch(input) {
18028
18089
  detail: staged.stderr || staged.stdout
18029
18090
  };
18030
18091
  }
18031
- const commit = runGitResult(worktreePath, ["commit", "-m", trimmed7]);
18092
+ const commit = runGitResult(worktreePath, ["commit", "-m", trimmed8]);
18032
18093
  if (!commit.ok) {
18033
18094
  return {
18034
18095
  ok: false,
@@ -18052,7 +18113,7 @@ function pushParent(input) {
18052
18113
  };
18053
18114
  }
18054
18115
  let summary = `Pushed ${baseBranch} to origin.`;
18055
- if (existsSync18(worktreePath) && !worktreeIsDirty(worktreePath)) {
18116
+ if (existsSync19(worktreePath) && !worktreeIsDirty(worktreePath)) {
18056
18117
  if (removeGitWorktree({ primaryRepoPath, worktreePath, force: false })) {
18057
18118
  summary += ` Removed the merged worktree for ${branchName}.`;
18058
18119
  }
@@ -18061,7 +18122,7 @@ function pushParent(input) {
18061
18122
  }
18062
18123
  function publishBranch(input) {
18063
18124
  const { branchName, worktreePath, primaryRepoPath } = input;
18064
- const repo = existsSync18(worktreePath) ? worktreePath : primaryRepoPath;
18125
+ const repo = existsSync19(worktreePath) ? worktreePath : primaryRepoPath;
18065
18126
  const push = runGitResult(repo, ["push", "-u", "origin", branchName]);
18066
18127
  if (!push.ok) {
18067
18128
  return {
@@ -18111,7 +18172,7 @@ var init_branch_status_git = __esm({
18111
18172
  });
18112
18173
 
18113
18174
  // ../packages/core/dist/service/local-target/commit-message-diff-git.js
18114
- import { existsSync as existsSync19 } from "node:fs";
18175
+ import { existsSync as existsSync20 } from "node:fs";
18115
18176
  function collectWorktreeChanges(worktreePath) {
18116
18177
  const status = runGitResult(worktreePath, ["status", "--porcelain"]);
18117
18178
  const diff = runGitResult(worktreePath, ["diff", "HEAD"]);
@@ -18127,7 +18188,7 @@ ${diff.stdout}`);
18127
18188
  return sections.join("\n\n");
18128
18189
  }
18129
18190
  function gatherCommitMessageDiff(worktreePath) {
18130
- if (!existsSync19(worktreePath)) {
18191
+ if (!existsSync20(worktreePath)) {
18131
18192
  return {
18132
18193
  ok: false,
18133
18194
  code: "BRANCH_NO_WORKTREE",
@@ -18188,7 +18249,7 @@ var init_result = __esm({
18188
18249
  });
18189
18250
 
18190
18251
  // ../packages/core/dist/service/local-target/in-process-provider.js
18191
- import { existsSync as existsSync20 } from "node:fs";
18252
+ import { existsSync as existsSync21 } from "node:fs";
18192
18253
  var InProcessProvider;
18193
18254
  var init_in_process_provider = __esm({
18194
18255
  "../packages/core/dist/service/local-target/in-process-provider.js"() {
@@ -18221,7 +18282,7 @@ var init_in_process_provider = __esm({
18221
18282
  */
18222
18283
  async observeResource(input) {
18223
18284
  const observedAt = (/* @__PURE__ */ new Date()).toISOString();
18224
- const state = existsSync20(input.path) ? "available" : "missing";
18285
+ const state = existsSync21(input.path) ? "available" : "missing";
18225
18286
  return ok(this.target, { state, observedAt });
18226
18287
  }
18227
18288
  /**
@@ -18668,7 +18729,7 @@ var init_branch_planning = __esm({
18668
18729
 
18669
18730
  // src/branch-preparation.ts
18670
18731
  import { execFileSync as execFileSync6 } from "node:child_process";
18671
- import { existsSync as existsSync21, mkdirSync as mkdirSync10, statSync as statSync5 } from "node:fs";
18732
+ import { existsSync as existsSync22, mkdirSync as mkdirSync10, statSync as statSync5 } from "node:fs";
18672
18733
  import os4 from "node:os";
18673
18734
  import path21 from "node:path";
18674
18735
  function runGit3(cwd, args, options = {}) {
@@ -18840,7 +18901,7 @@ function ensureBranchRef(gitRoot, decision) {
18840
18901
  function ensureWorktree(gitRoot, decision) {
18841
18902
  ensureBranchRef(gitRoot, decision);
18842
18903
  mkdirSync10(path21.dirname(decision.worktreePath), { recursive: true });
18843
- if (existsSync21(decision.worktreePath)) {
18904
+ if (existsSync22(decision.worktreePath)) {
18844
18905
  if (!statSync5(decision.worktreePath).isDirectory()) {
18845
18906
  throw new Error(`Worktree path exists and is not a directory: ${decision.worktreePath}`);
18846
18907
  }
@@ -19015,16 +19076,16 @@ async function resolveProjectByIdOrName({
19015
19076
  backend,
19016
19077
  projectRef
19017
19078
  }) {
19018
- const trimmed7 = projectRef.trim();
19079
+ const trimmed8 = projectRef.trim();
19019
19080
  try {
19020
- return await backend.get(`/api/projects/${encodeURIComponent(trimmed7)}`);
19081
+ return await backend.get(`/api/projects/${encodeURIComponent(trimmed8)}`);
19021
19082
  } catch (error) {
19022
19083
  if (!(error instanceof CliError) || !/project not found/i.test(error.message)) throw error;
19023
19084
  }
19024
19085
  const projects = await listAccessibleProjects({ backend });
19025
- const match = projects.find((project) => project.name === trimmed7) ?? projects.find((project) => project.slug === trimmed7);
19086
+ const match = projects.find((project) => project.name === trimmed8) ?? projects.find((project) => project.slug === trimmed8);
19026
19087
  if (!match) {
19027
- throw new CliError({ message: `Project not found: ${trimmed7}` });
19088
+ throw new CliError({ message: `Project not found: ${trimmed8}` });
19028
19089
  }
19029
19090
  return match;
19030
19091
  }
@@ -20108,7 +20169,7 @@ var init_launch = __esm({
20108
20169
  });
20109
20170
 
20110
20171
  // src/launch-bootstrap.ts
20111
- import { existsSync as existsSync22, readdirSync as readdirSync4, readFileSync as readFileSync16, statSync as statSync6 } from "node:fs";
20172
+ import { existsSync as existsSync23, readdirSync as readdirSync4, readFileSync as readFileSync16, statSync as statSync6 } from "node:fs";
20112
20173
  import path25 from "node:path";
20113
20174
  function firstCapture(match) {
20114
20175
  if (!match) return null;
@@ -20130,7 +20191,7 @@ function recoverLaunchBootstrapFromProjectTmp({
20130
20191
  const trimmedMission = missionId.trim();
20131
20192
  if (!trimmedMission) return empty;
20132
20193
  const tmpDir = projectTmpDir(workingDirectory);
20133
- if (!existsSync22(tmpDir)) return empty;
20194
+ if (!existsSync23(tmpDir)) return empty;
20134
20195
  const prefix = `launch-${safeMissionPart(trimmedMission)}-`;
20135
20196
  let newest = null;
20136
20197
  try {
@@ -20188,8 +20249,8 @@ function promptText({
20188
20249
  return new Promise((resolve) => {
20189
20250
  rl.question(`${question}${suffix}: `, (answer) => {
20190
20251
  rl.close();
20191
- const trimmed7 = answer.trim();
20192
- resolve(trimmed7 || defaultValue || "");
20252
+ const trimmed8 = answer.trim();
20253
+ resolve(trimmed8 || defaultValue || "");
20193
20254
  });
20194
20255
  });
20195
20256
  }
@@ -20778,7 +20839,7 @@ var init_protocol_help = __esm({
20778
20839
 
20779
20840
  // src/vcs-sessions.ts
20780
20841
  import { createHash as createHash8 } from "node:crypto";
20781
- import { existsSync as existsSync23, mkdirSync as mkdirSync11, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "node:fs";
20842
+ import { existsSync as existsSync24, mkdirSync as mkdirSync11, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "node:fs";
20782
20843
  import path27 from "node:path";
20783
20844
  function manifestPath2(workingDirectory) {
20784
20845
  const key = createHash8("sha256").update(path27.resolve(workingDirectory)).digest("hex");
@@ -20787,7 +20848,7 @@ function manifestPath2(workingDirectory) {
20787
20848
  function readEntries(workingDirectory) {
20788
20849
  try {
20789
20850
  const target = manifestPath2(workingDirectory);
20790
- if (!existsSync23(target)) return [];
20851
+ if (!existsSync24(target)) return [];
20791
20852
  const raw = JSON.parse(readFileSync18(target, "utf8"));
20792
20853
  if (!Array.isArray(raw.entries)) return [];
20793
20854
  const now = Date.now();
@@ -20861,7 +20922,7 @@ var init_vcs_sessions = __esm({
20861
20922
  import { execFileSync as execFileSync7 } from "node:child_process";
20862
20923
  import { createHash as createHash9 } from "node:crypto";
20863
20924
  import {
20864
- existsSync as existsSync24,
20925
+ existsSync as existsSync25,
20865
20926
  mkdirSync as mkdirSync12,
20866
20927
  readdirSync as readdirSync5,
20867
20928
  readFileSync as readFileSync19,
@@ -20973,7 +21034,7 @@ function readBashSnapshot({
20973
21034
  }) {
20974
21035
  try {
20975
21036
  const target = bashSnapshotPath({ workingDirectory, missionId });
20976
- if (!existsSync24(target)) return /* @__PURE__ */ new Map();
21037
+ if (!existsSync25(target)) return /* @__PURE__ */ new Map();
20977
21038
  const raw = JSON.parse(readFileSync19(target, "utf8"));
20978
21039
  const byPath = /* @__PURE__ */ new Map();
20979
21040
  if (Array.isArray(raw.files)) {
@@ -21065,7 +21126,7 @@ function resetTouchedFiles({
21065
21126
  touchedFilesPath({ workingDirectory, missionId }),
21066
21127
  bashSnapshotPath({ workingDirectory, missionId })
21067
21128
  ]) {
21068
- if (existsSync24(target)) rmSync5(target);
21129
+ if (existsSync25(target)) rmSync5(target);
21069
21130
  }
21070
21131
  } catch {
21071
21132
  }
@@ -21076,14 +21137,14 @@ function resetRationaleNotes({
21076
21137
  }) {
21077
21138
  try {
21078
21139
  const target = rationaleNotesPath({ workingDirectory, missionId });
21079
- if (existsSync24(target)) rmSync5(target);
21140
+ if (existsSync25(target)) rmSync5(target);
21080
21141
  } catch {
21081
21142
  }
21082
21143
  }
21083
21144
  function truncateNoteText(value, max = 500) {
21084
- const trimmed7 = (value ?? "").replace(/\s+/g, " ").trim();
21085
- if (!trimmed7) return null;
21086
- return trimmed7.length > max ? `${trimmed7.slice(0, max - 1)}...` : trimmed7;
21145
+ const trimmed8 = (value ?? "").replace(/\s+/g, " ").trim();
21146
+ if (!trimmed8) return null;
21147
+ return trimmed8.length > max ? `${trimmed8.slice(0, max - 1)}...` : trimmed8;
21087
21148
  }
21088
21149
  function readRationaleNotes({
21089
21150
  workingDirectory,
@@ -21091,7 +21152,7 @@ function readRationaleNotes({
21091
21152
  }) {
21092
21153
  try {
21093
21154
  const target = rationaleNotesPath({ workingDirectory, missionId });
21094
- if (!existsSync24(target)) return [];
21155
+ if (!existsSync25(target)) return [];
21095
21156
  const raw = JSON.parse(readFileSync19(target, "utf8"));
21096
21157
  if (!Array.isArray(raw.notes)) return [];
21097
21158
  return raw.notes.flatMap((note) => {
@@ -21179,7 +21240,7 @@ function hasTouchedFilesLog({
21179
21240
  workingDirectory,
21180
21241
  missionId
21181
21242
  }) {
21182
- return existsSync24(touchedFilesPath({ workingDirectory, missionId }));
21243
+ return existsSync25(touchedFilesPath({ workingDirectory, missionId }));
21183
21244
  }
21184
21245
  function readTouchedLog({
21185
21246
  workingDirectory,
@@ -21187,7 +21248,7 @@ function readTouchedLog({
21187
21248
  }) {
21188
21249
  try {
21189
21250
  const target = touchedFilesPath({ workingDirectory, missionId });
21190
- if (!existsSync24(target)) return null;
21251
+ if (!existsSync25(target)) return null;
21191
21252
  const raw = JSON.parse(readFileSync19(target, "utf8"));
21192
21253
  const result = /* @__PURE__ */ new Set();
21193
21254
  if (Array.isArray(raw.paths)) {
@@ -21241,7 +21302,7 @@ function readPeerTouchedClaims({
21241
21302
  );
21242
21303
  if (activeMissionIds.size === 0) return [];
21243
21304
  const directory = path28.join(resolveGlobalDataDir(), "vcs-touched");
21244
- if (!existsSync24(directory)) return [];
21305
+ if (!existsSync25(directory)) return [];
21245
21306
  const claims = [];
21246
21307
  for (const name of readdirSync5(directory)) {
21247
21308
  if (!name.endsWith(".json") || name.endsWith(".bash-snapshot.json")) continue;
@@ -21271,7 +21332,7 @@ function readBaselineSnapshot({
21271
21332
  }) {
21272
21333
  try {
21273
21334
  const target = baselineFilePath({ workingDirectory, missionId });
21274
- if (!existsSync24(target)) return /* @__PURE__ */ new Map();
21335
+ if (!existsSync25(target)) return /* @__PURE__ */ new Map();
21275
21336
  const raw = JSON.parse(readFileSync19(target, "utf8"));
21276
21337
  if (Array.isArray(raw.files)) {
21277
21338
  const byPath = /* @__PURE__ */ new Map();
@@ -21367,7 +21428,7 @@ function compileIgnorePattern(rawPattern) {
21367
21428
  function loadIgnoreRules(repoRoot) {
21368
21429
  try {
21369
21430
  const target = path28.join(repoRoot, OVERLORD_IGNORE_FILENAME);
21370
- if (!existsSync24(target)) return [];
21431
+ if (!existsSync25(target)) return [];
21371
21432
  const rules = [];
21372
21433
  for (const rawLine of readFileSync19(target, "utf8").split("\n")) {
21373
21434
  const line = rawLine.replace(/\r$/, "").trim();
@@ -21501,18 +21562,18 @@ import { readFileSync as readFileSync20 } from "node:fs";
21501
21562
  import path29 from "node:path";
21502
21563
  function firstNonEmptyString(value) {
21503
21564
  if (typeof value !== "string") return null;
21504
- const trimmed7 = value.trim();
21505
- return trimmed7 ? trimmed7 : null;
21565
+ const trimmed8 = value.trim();
21566
+ return trimmed8 ? trimmed8 : null;
21506
21567
  }
21507
21568
  function collectCandidates(toolInput) {
21508
21569
  const counts = /* @__PURE__ */ new Map();
21509
21570
  const order = [];
21510
21571
  const bump = (raw, amount) => {
21511
21572
  if (typeof raw !== "string") return;
21512
- const trimmed7 = raw.trim();
21513
- if (!trimmed7) return;
21514
- if (!counts.has(trimmed7)) order.push(trimmed7);
21515
- counts.set(trimmed7, (counts.get(trimmed7) ?? 0) + amount);
21573
+ const trimmed8 = raw.trim();
21574
+ if (!trimmed8) return;
21575
+ if (!counts.has(trimmed8)) order.push(trimmed8);
21576
+ counts.set(trimmed8, (counts.get(trimmed8) ?? 0) + amount);
21516
21577
  };
21517
21578
  bump(toolInput.file_path, 1);
21518
21579
  bump(toolInput.filePath, 1);
@@ -21736,7 +21797,7 @@ var init_resource_observations = __esm({
21736
21797
 
21737
21798
  // src/runner-identity.ts
21738
21799
  import { randomUUID } from "node:crypto";
21739
- import { existsSync as existsSync25, mkdirSync as mkdirSync13, readFileSync as readFileSync21, writeFileSync as writeFileSync12 } from "node:fs";
21800
+ import { existsSync as existsSync26, mkdirSync as mkdirSync13, readFileSync as readFileSync21, writeFileSync as writeFileSync12 } from "node:fs";
21740
21801
  import { hostname as hostname4 } from "node:os";
21741
21802
  import path30 from "node:path";
21742
21803
  function envValue(name) {
@@ -21750,7 +21811,7 @@ function resolveRunnerInstanceId(dataDir = resolveGlobalDataDir()) {
21750
21811
  const pinned = envValue("OVERLORD_RUNNER_INSTANCE_ID");
21751
21812
  if (pinned) return pinned;
21752
21813
  const statePath = runnerInstanceStatePath(dataDir);
21753
- if (existsSync25(statePath)) {
21814
+ if (existsSync26(statePath)) {
21754
21815
  try {
21755
21816
  const parsed = JSON.parse(readFileSync21(statePath, "utf8"));
21756
21817
  if (typeof parsed.runnerInstanceId === "string" && parsed.runnerInstanceId.trim()) {
@@ -21804,7 +21865,15 @@ var init_runner_identity = __esm({
21804
21865
 
21805
21866
  // src/runner-service.ts
21806
21867
  import { execFile } from "node:child_process";
21807
- import { chmodSync as chmodSync5, existsSync as existsSync26, mkdirSync as mkdirSync14, readFileSync as readFileSync22, rmSync as rmSync6, writeFileSync as writeFileSync13 } from "node:fs";
21868
+ import {
21869
+ chmodSync as chmodSync5,
21870
+ existsSync as existsSync27,
21871
+ mkdirSync as mkdirSync14,
21872
+ readFileSync as readFileSync22,
21873
+ rmSync as rmSync6,
21874
+ statSync as statSync7,
21875
+ writeFileSync as writeFileSync13
21876
+ } from "node:fs";
21808
21877
  import os5 from "node:os";
21809
21878
  import path31 from "node:path";
21810
21879
  import { promisify } from "node:util";
@@ -21812,6 +21881,38 @@ function writeServiceFile(filePath, contents) {
21812
21881
  writeFileSync13(filePath, contents, { mode: 384 });
21813
21882
  chmodSync5(filePath, 384);
21814
21883
  }
21884
+ function resolveRunnerEntryScript(argv = process.argv) {
21885
+ return argv[1] ? path31.resolve(argv[1]) : null;
21886
+ }
21887
+ function readRunnerExecutableFileIdentity(filePath, stat = statSync7) {
21888
+ try {
21889
+ const metadata = stat(filePath);
21890
+ return { dev: metadata.dev, ino: metadata.ino, mtimeMs: metadata.mtimeMs };
21891
+ } catch (error) {
21892
+ return error.code === "ENOENT" ? null : void 0;
21893
+ }
21894
+ }
21895
+ function captureRunnerSupervisorIdentity({
21896
+ execPath = process.execPath,
21897
+ entryScript = resolveRunnerEntryScript(),
21898
+ stat = statSync7
21899
+ } = {}) {
21900
+ return {
21901
+ program: readRunnerExecutableFileIdentity(execPath, stat),
21902
+ entryScript: entryScript ? readRunnerExecutableFileIdentity(entryScript, stat) : void 0
21903
+ };
21904
+ }
21905
+ function executableIdentityChanged(initial, current) {
21906
+ if (current === null) return true;
21907
+ if (!initial || !current) return false;
21908
+ return initial.dev !== current.dev || initial.ino !== current.ino || initial.mtimeMs !== current.mtimeMs;
21909
+ }
21910
+ function shouldRestartRunnerSupervisor({
21911
+ initial,
21912
+ current
21913
+ }) {
21914
+ return executableIdentityChanged(initial.program, current.program) || executableIdentityChanged(initial.entryScript, current.entryScript);
21915
+ }
21815
21916
  function emptyRunnerServiceState() {
21816
21917
  return {
21817
21918
  serviceKind: null,
@@ -21832,7 +21933,7 @@ function runnerServiceStatePath(dataDir = resolveGlobalDataDir()) {
21832
21933
  }
21833
21934
  function readRunnerServiceState(dataDir = resolveGlobalDataDir()) {
21834
21935
  const filePath = runnerServiceStatePath(dataDir);
21835
- if (!existsSync26(filePath)) return emptyRunnerServiceState();
21936
+ if (!existsSync27(filePath)) return emptyRunnerServiceState();
21836
21937
  try {
21837
21938
  const parsed = JSON.parse(readFileSync22(filePath, "utf8"));
21838
21939
  return { ...emptyRunnerServiceState(), ...parsed };
@@ -21850,45 +21951,14 @@ function patchRunnerServiceState(patch, dataDir = resolveGlobalDataDir()) {
21850
21951
  writeRunnerServiceState(next, dataDir);
21851
21952
  return next;
21852
21953
  }
21853
- function runnerFocusStatePath(dataDir = resolveGlobalDataDir()) {
21854
- return path31.join(dataDir, RUNNER_FOCUS_STATE_FILENAME);
21855
- }
21856
- function readDesktopFocusState(dataDir = resolveGlobalDataDir()) {
21857
- const filePath = runnerFocusStatePath(dataDir);
21858
- if (!existsSync26(filePath)) return { lastFocusedAt: null };
21859
- try {
21860
- const parsed = JSON.parse(readFileSync22(filePath, "utf8"));
21861
- return { lastFocusedAt: parsed.lastFocusedAt ?? null };
21862
- } catch {
21863
- return { lastFocusedAt: null };
21864
- }
21865
- }
21866
- function withinWindow(timestamp, now, windowMs) {
21867
- if (!timestamp) return false;
21868
- const ms = Date.parse(timestamp);
21869
- if (Number.isNaN(ms)) return false;
21870
- return now - ms <= windowMs;
21871
- }
21872
- function selectBasePollIntervalMs({
21873
- lastLaunchedAt,
21874
- lastDesktopFocusAt = null,
21875
- now
21876
- }) {
21877
- const jobActive = withinWindow(lastLaunchedAt, now, IDLE_BACKOFF_MS);
21878
- const focusActive = withinWindow(lastDesktopFocusAt, now, DESKTOP_FOCUS_WINDOW_MS);
21879
- return jobActive || focusActive ? FAST_POLL_INTERVAL_MS : SLOW_POLL_INTERVAL_MS;
21880
- }
21881
21954
  function applyPollJitter(intervalMs, random = Math.random) {
21882
21955
  const spread = intervalMs * 0.1;
21883
21956
  return Math.round(intervalMs + (random() * 2 - 1) * spread);
21884
21957
  }
21885
- function nextRunnerLastError(pollError) {
21886
- return pollError;
21887
- }
21888
21958
  function resolveOverlordAppInvocation({
21889
21959
  platform: platform2 = process.platform,
21890
21960
  homedir: homedir2 = os5.homedir(),
21891
- exists = existsSync26
21961
+ exists = existsSync27
21892
21962
  } = {}) {
21893
21963
  if (platform2 !== "darwin") return null;
21894
21964
  const appDirs = [
@@ -21920,27 +21990,15 @@ function resolveOvldInvocation(argv = process.argv, execPath = process.execPath)
21920
21990
  }
21921
21991
  return { program: execPath, args: ["runner", "supervise"] };
21922
21992
  }
21923
- function describeServicePublisher({
21924
- execProgram,
21925
- platform: platform2 = process.platform
21926
- }) {
21927
- if (platform2 !== "darwin" || !execProgram) {
21928
- return { publisher: "unknown", needsReinstallForOverlord: false };
21929
- }
21930
- if (execProgram.includes(".app/Contents/MacOS/")) {
21931
- return { publisher: "overlord", needsReinstallForOverlord: false };
21932
- }
21933
- return { publisher: "node", needsReinstallForOverlord: true };
21934
- }
21935
21993
  function buildRunnerServiceEnv({
21936
21994
  backendUrl,
21937
21995
  overlordHome,
21938
21996
  runAsElectronNode
21939
21997
  }) {
21940
21998
  const env = {
21941
- OVERLORD_BACKEND_URL: backendUrl,
21942
21999
  PATH: process.env.PATH?.trim() || "/usr/local/bin:/usr/bin:/bin"
21943
22000
  };
22001
+ if (!isLoopbackBackendUrl(backendUrl)) env.OVERLORD_BACKEND_URL = backendUrl;
21944
22002
  const home = overlordHome?.trim() || process.env.OVLD_HOME?.trim();
21945
22003
  if (home) env.OVLD_HOME = path31.resolve(home);
21946
22004
  const token = process.env.OVERLORD_USER_TOKEN?.trim() || process.env.OVLD_USER_TOKEN?.trim();
@@ -22025,31 +22083,72 @@ async function runCommand(program, args) {
22025
22083
  const { stdout } = await execFileAsync(program, args);
22026
22084
  return { stdout: stdout.toString() };
22027
22085
  }
22086
+ function launchdDomainTarget(uid) {
22087
+ return `gui/${uid}`;
22088
+ }
22089
+ function launchdServiceTarget(uid) {
22090
+ return `${launchdDomainTarget(uid)}/${LAUNCHD_LABEL}`;
22091
+ }
22092
+ function planLaunchdUnloadCommands(opts) {
22093
+ return [{ args: ["bootout", launchdServiceTarget(opts.uid)], tolerateFailure: true }];
22094
+ }
22095
+ function planLaunchdLoadCommands(opts) {
22096
+ const service = launchdServiceTarget(opts.uid);
22097
+ return [
22098
+ { args: ["enable", service], tolerateFailure: true },
22099
+ {
22100
+ args: ["bootstrap", launchdDomainTarget(opts.uid), opts.plistPath],
22101
+ tolerateFailure: true
22102
+ },
22103
+ { args: ["kickstart", service], tolerateFailure: false }
22104
+ ];
22105
+ }
22028
22106
  function resolveServiceManager(platform2 = process.platform) {
22029
22107
  if (platform2 === "darwin") return new LaunchdManager();
22030
22108
  if (platform2 === "linux") return new SystemdUserManager();
22031
22109
  return null;
22032
22110
  }
22033
- var execFileAsync, RUNNER_SERVICE_STATE_FILENAME, RUNNER_FOCUS_STATE_FILENAME, LAUNCHD_LABEL, SYSTEMD_UNIT_NAME, FAST_POLL_INTERVAL_MS, SLOW_POLL_INTERVAL_MS, IDLE_BACKOFF_MS, DESKTOP_FOCUS_WINDOW_MS, LaunchdManager, SystemdUserManager;
22111
+ var execFileAsync, RUNNER_SERVICE_STATE_FILENAME, LAUNCHD_LABEL, SYSTEMD_UNIT_NAME, FALLBACK_POLL_INTERVAL_MS, LaunchdManager, SystemdUserManager;
22034
22112
  var init_runner_service = __esm({
22035
22113
  "src/runner-service.ts"() {
22036
22114
  "use strict";
22037
22115
  init_config();
22038
22116
  execFileAsync = promisify(execFile);
22039
22117
  RUNNER_SERVICE_STATE_FILENAME = "runner-service.json";
22040
- RUNNER_FOCUS_STATE_FILENAME = "runner-focus.json";
22041
22118
  LAUNCHD_LABEL = "io.overlord.runner";
22042
22119
  SYSTEMD_UNIT_NAME = "overlord-runner";
22043
- FAST_POLL_INTERVAL_MS = 3e3;
22044
- SLOW_POLL_INTERVAL_MS = 5e3;
22045
- IDLE_BACKOFF_MS = 2 * 60 * 60 * 1e3;
22046
- DESKTOP_FOCUS_WINDOW_MS = 30 * 60 * 1e3;
22120
+ FALLBACK_POLL_INTERVAL_MS = 5e3;
22047
22121
  LaunchdManager = class {
22048
22122
  kind = "launchd";
22049
22123
  identifier = LAUNCHD_LABEL;
22124
+ exec;
22125
+ constructor(exec) {
22126
+ this.exec = exec ?? ((args) => runCommand("launchctl", args));
22127
+ }
22050
22128
  unitPath() {
22051
22129
  return path31.join(launchAgentsDir(), `${LAUNCHD_LABEL}.plist`);
22052
22130
  }
22131
+ uid() {
22132
+ return process.getuid?.() ?? "";
22133
+ }
22134
+ async runSteps(steps) {
22135
+ for (const step of steps) {
22136
+ try {
22137
+ await this.exec(step.args);
22138
+ } catch (error) {
22139
+ if (!step.tolerateFailure) throw error;
22140
+ }
22141
+ }
22142
+ }
22143
+ /** True when launchd still has the label registered, whatever its run state. */
22144
+ async isLoaded() {
22145
+ try {
22146
+ await this.exec(["list", LAUNCHD_LABEL]);
22147
+ return true;
22148
+ } catch {
22149
+ return false;
22150
+ }
22151
+ }
22053
22152
  render(opts) {
22054
22153
  return renderLaunchdPlist({
22055
22154
  label: LAUNCHD_LABEL,
@@ -22061,41 +22160,33 @@ var init_runner_service = __esm({
22061
22160
  async install(opts) {
22062
22161
  mkdirSync14(launchAgentsDir(), { recursive: true });
22063
22162
  mkdirSync14(path31.join(resolveGlobalDataDir(), "logs"), { recursive: true });
22163
+ await this.runSteps(planLaunchdUnloadCommands({ uid: this.uid() }));
22064
22164
  writeServiceFile(this.unitPath(), this.render(opts));
22065
22165
  if (opts.autoStart) await this.start();
22066
22166
  }
22067
22167
  async start() {
22068
- try {
22069
- await runCommand("launchctl", ["load", "-w", this.unitPath()]);
22070
- } catch {
22071
- await runCommand("launchctl", [
22072
- "kickstart",
22073
- "-k",
22074
- `gui/${process.getuid?.() ?? ""}/${LAUNCHD_LABEL}`
22075
- ]);
22076
- }
22168
+ await this.runSteps(planLaunchdLoadCommands({ uid: this.uid(), plistPath: this.unitPath() }));
22077
22169
  }
22078
22170
  async stop() {
22079
- await runCommand("launchctl", ["unload", "-w", this.unitPath()]);
22171
+ await this.runSteps(planLaunchdUnloadCommands({ uid: this.uid() }));
22080
22172
  }
22081
22173
  async restart() {
22082
- try {
22083
- await this.stop();
22084
- } catch {
22085
- }
22174
+ await this.stop();
22086
22175
  await this.start();
22087
22176
  }
22088
22177
  async uninstall() {
22089
- try {
22090
- await this.stop();
22091
- } catch {
22178
+ await this.runSteps(planLaunchdUnloadCommands({ uid: this.uid() }));
22179
+ if (await this.isLoaded()) {
22180
+ throw new Error(
22181
+ `launchctl still reports ${LAUNCHD_LABEL} as loaded after bootout; left ${this.unitPath()} in place so the service can be repaired with \`ovld runner service restart\`.`
22182
+ );
22092
22183
  }
22093
- if (existsSync26(this.unitPath())) rmSync6(this.unitPath());
22184
+ if (existsSync27(this.unitPath())) rmSync6(this.unitPath());
22094
22185
  }
22095
22186
  async status() {
22096
- const installed = existsSync26(this.unitPath());
22187
+ const installed = existsSync27(this.unitPath());
22097
22188
  try {
22098
- const { stdout } = await runCommand("launchctl", ["list"]);
22189
+ const { stdout } = await this.exec(["list"]);
22099
22190
  const line = stdout.split("\n").find((row) => row.includes(LAUNCHD_LABEL));
22100
22191
  if (!line) return { installed, running: installed ? "stopped" : "unknown" };
22101
22192
  const pid = line.trim().split(/\s+/)[0];
@@ -22136,14 +22227,14 @@ var init_runner_service = __esm({
22136
22227
  await runCommand("systemctl", ["--user", "disable", this.identifier]);
22137
22228
  } catch {
22138
22229
  }
22139
- if (existsSync26(this.unitPath())) rmSync6(this.unitPath());
22230
+ if (existsSync27(this.unitPath())) rmSync6(this.unitPath());
22140
22231
  try {
22141
22232
  await runCommand("systemctl", ["--user", "daemon-reload"]);
22142
22233
  } catch {
22143
22234
  }
22144
22235
  }
22145
22236
  async status() {
22146
- const installed = existsSync26(this.unitPath());
22237
+ const installed = existsSync27(this.unitPath());
22147
22238
  try {
22148
22239
  const { stdout } = await runCommand("systemctl", ["--user", "is-active", this.identifier]);
22149
22240
  return { installed, running: stdout.trim() === "active" ? "running" : "stopped" };
@@ -22158,9 +22249,9 @@ var init_runner_service = __esm({
22158
22249
  // src/select-prompt.ts
22159
22250
  import { createInterface as createInterface4 } from "node:readline";
22160
22251
  function parseNumberedSelection(answer, count) {
22161
- const trimmed7 = answer.trim();
22162
- if (!/^\d+$/.test(trimmed7)) return null;
22163
- const value = Number.parseInt(trimmed7, 10);
22252
+ const trimmed8 = answer.trim();
22253
+ if (!/^\d+$/.test(trimmed8)) return null;
22254
+ const value = Number.parseInt(trimmed8, 10);
22164
22255
  if (!Number.isInteger(value) || value < 1 || value > count) return null;
22165
22256
  return value - 1;
22166
22257
  }
@@ -22210,7 +22301,7 @@ var init_select_prompt = __esm({
22210
22301
 
22211
22302
  // src/session-key.ts
22212
22303
  import { createHash as createHash10 } from "node:crypto";
22213
- import { existsSync as existsSync27, mkdirSync as mkdirSync15, readFileSync as readFileSync23, rmSync as rmSync7, writeFileSync as writeFileSync14 } from "node:fs";
22304
+ import { existsSync as existsSync28, mkdirSync as mkdirSync15, readFileSync as readFileSync23, rmSync as rmSync7, writeFileSync as writeFileSync14 } from "node:fs";
22214
22305
  import path32 from "node:path";
22215
22306
  function sessionKeyCachePath({
22216
22307
  missionId,
@@ -22225,7 +22316,7 @@ function readCachedSessionKey({
22225
22316
  }) {
22226
22317
  try {
22227
22318
  const filePath = sessionKeyCachePath({ missionId, workingDirectory });
22228
- if (!existsSync27(filePath)) return void 0;
22319
+ if (!existsSync28(filePath)) return void 0;
22229
22320
  const raw = JSON.parse(readFileSync23(filePath, "utf8"));
22230
22321
  return typeof raw.sessionKey === "string" && raw.sessionKey.trim() ? raw.sessionKey.trim() : void 0;
22231
22322
  } catch {
@@ -22238,13 +22329,13 @@ function writeCachedSessionKey({
22238
22329
  sessionKey
22239
22330
  }) {
22240
22331
  try {
22241
- const trimmed7 = sessionKey.trim();
22242
- if (!trimmed7) return;
22332
+ const trimmed8 = sessionKey.trim();
22333
+ if (!trimmed8) return;
22243
22334
  const filePath = sessionKeyCachePath({ missionId, workingDirectory });
22244
22335
  mkdirSync15(path32.dirname(filePath), { recursive: true });
22245
22336
  writeFileSync14(
22246
22337
  filePath,
22247
- JSON.stringify({ sessionKey: trimmed7, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }),
22338
+ JSON.stringify({ sessionKey: trimmed8, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }),
22248
22339
  { mode: 384 }
22249
22340
  );
22250
22341
  } catch {
@@ -22256,7 +22347,7 @@ function clearCachedSessionKey({
22256
22347
  }) {
22257
22348
  try {
22258
22349
  const filePath = sessionKeyCachePath({ missionId, workingDirectory });
22259
- if (existsSync27(filePath)) rmSync7(filePath);
22350
+ if (existsSync28(filePath)) rmSync7(filePath);
22260
22351
  } catch {
22261
22352
  }
22262
22353
  }
@@ -22272,9 +22363,10 @@ var commands_exports = {};
22272
22363
  __export(commands_exports, {
22273
22364
  resolveProtocolFileInputs: () => resolveProtocolFileInputs,
22274
22365
  runManagementCommand: () => runManagementCommand,
22275
- runProtocolCommand: () => runProtocolCommand
22366
+ runProtocolCommand: () => runProtocolCommand,
22367
+ runRunnerOnce: () => runRunnerOnce
22276
22368
  });
22277
- import { existsSync as existsSync28, readFileSync as readFileSync24 } from "node:fs";
22369
+ import { existsSync as existsSync29, readFileSync as readFileSync24 } from "node:fs";
22278
22370
  import os6 from "node:os";
22279
22371
  import path33 from "node:path";
22280
22372
  function resourceAccessModeBody(value) {
@@ -22904,7 +22996,7 @@ async function runProtocolCommand({
22904
22996
  clearCachedSessionKey({ missionId, workingDirectory });
22905
22997
  removeActiveSession({ workingDirectory, missionId });
22906
22998
  clearActiveMissionPointer(workingDirectory);
22907
- const connectorInstallsEditHook = existsSync28(path33.join(os6.homedir(), ".claude")) || existsSync28(
22999
+ const connectorInstallsEditHook = existsSync29(path33.join(os6.homedir(), ".claude")) || existsSync29(
22908
23000
  path33.join(
22909
23001
  os6.homedir(),
22910
23002
  ".cursor",
@@ -23435,6 +23527,165 @@ function isNoExecutionTargetRegisteredError(error) {
23435
23527
  const message = error instanceof Error ? error.message : String(error);
23436
23528
  return message.includes("(no_execution_target_registered)");
23437
23529
  }
23530
+ async function runRunnerOnce({
23531
+ runtime,
23532
+ parsed,
23533
+ json
23534
+ }) {
23535
+ let claim;
23536
+ try {
23537
+ claim = await runtime.backend.post({
23538
+ path: "/api/runner/claim",
23539
+ body: {
23540
+ projectId: flagValue(parsed.flags, "--project-id"),
23541
+ ...clientDeviceIdentity(),
23542
+ // Contract v40: the claim poll doubles as this runner instance's
23543
+ // heartbeat, so local target liveness reflects a runner that can
23544
+ // actually take work rather than any CLI traffic from the machine.
23545
+ ...runnerRegistrationPayload()
23546
+ }
23547
+ });
23548
+ } catch (error) {
23549
+ if (isNoExecutionTargetRegisteredError(error)) {
23550
+ throw new CliError({
23551
+ message: `${error instanceof Error ? error.message : String(error)}
23552
+ The runner will not register a target for you \u2014 declare this machine first, then start it again.`
23553
+ });
23554
+ }
23555
+ throw error;
23556
+ }
23557
+ const request = asRecord5(claim).request;
23558
+ const longPoll = asRecord5(claim).longPoll === true;
23559
+ if (!request) return { launched: false, longPoll };
23560
+ const requestRecord = asRecord5(request);
23561
+ const requestId = String(requestRecord.id);
23562
+ const projectId = String(requestRecord.projectId ?? "");
23563
+ const executionTargetId = typeof requestRecord.executionTargetId === "string" ? requestRecord.executionTargetId.trim() : "";
23564
+ if (projectId && executionTargetId) {
23565
+ try {
23566
+ await reportRunnerResourceObservations({
23567
+ backend: runtime.backend,
23568
+ projectId,
23569
+ executionTargetId
23570
+ });
23571
+ } catch {
23572
+ }
23573
+ }
23574
+ try {
23575
+ await runtime.backend.post({ path: `/api/runner/requests/${requestId}/launching` });
23576
+ const mutation = parseMutationFromMetadata(requestRecord.metadata);
23577
+ if (mutation) {
23578
+ const mutationResult = await executeLocalTargetMutation({ mutation });
23579
+ if (!mutationResult.ok) {
23580
+ await runtime.backend.post({
23581
+ path: `/api/runner/requests/${requestId}/failed`,
23582
+ body: { error: mutationResult.message }
23583
+ });
23584
+ throw new CliError({ message: mutationResult.message });
23585
+ }
23586
+ await runtime.backend.post({
23587
+ path: `/api/runner/requests/${requestId}/completed`,
23588
+ body: { mutationResult }
23589
+ });
23590
+ if (json) printJson({ request, mutationResult });
23591
+ else console.log(`Completed ${mutation.kind} for ${requestRecord.missionId}`);
23592
+ return { launched: true, longPoll };
23593
+ }
23594
+ const requestedAgent = typeof requestRecord.requestedAgent === "string" ? requestRecord.requestedAgent.trim() : "";
23595
+ if (!requestedAgent) {
23596
+ throw new CliError({
23597
+ message: `Execution request ${requestId} has no agent; the objective must specify one before it can be launched.`
23598
+ });
23599
+ }
23600
+ const launchConfig = asRecord5(requestRecord.launchConfig);
23601
+ const terminal = await resolveTerminalLaunchSettings({ runtime, flags: parsed.flags });
23602
+ const missionId = String(requestRecord.missionId);
23603
+ const dryRun = flagBoolean(parsed.flags, "--dry-run");
23604
+ const prepared = await prepareMissionBranch({
23605
+ runtime,
23606
+ options: {
23607
+ missionId,
23608
+ workingDirectory: String(requestRecord.workingDirectory ?? process.cwd()),
23609
+ objectiveId: String(requestRecord.objectiveId ?? ""),
23610
+ workspaceAutomationEnabled: await readWorktreeBranchAutomationEnabled(runtime),
23611
+ dryRun,
23612
+ overrideBranch: flagValue(parsed.flags, "--branch"),
23613
+ noWorktree: flagBoolean(parsed.flags, "--no-worktree")
23614
+ }
23615
+ });
23616
+ await recordBranchPrepared({
23617
+ runtime,
23618
+ missionId,
23619
+ requestId,
23620
+ branchAutomation: prepared.branchAutomation
23621
+ });
23622
+ const launchSession = launchSessionSnapshotFromMetadata(asRecord5(requestRecord.metadata));
23623
+ const result = await launchAgent({
23624
+ runtime,
23625
+ options: {
23626
+ agent: requestedAgent,
23627
+ missionId,
23628
+ workingDirectory: prepared.workingDirectory,
23629
+ model: typeof requestRecord.requestedModel === "string" ? requestRecord.requestedModel : void 0,
23630
+ thinking: typeof requestRecord.requestedReasoningEffort === "string" ? requestRecord.requestedReasoningEffort : void 0,
23631
+ flags: normalizeAgentLaunchFlags(launchConfig.flags),
23632
+ preCommand: typeof launchConfig.preCommand === "string" ? launchConfig.preCommand : void 0,
23633
+ preLaunchCommands: Array.isArray(requestRecord.preLaunchCommands) ? requestRecord.preLaunchCommands.filter(
23634
+ (value) => typeof value === "string"
23635
+ ) : void 0,
23636
+ launchEnvVars: parseLaunchEnvVarsValue(requestRecord.launchEnvVars),
23637
+ executionRequestId: requestId,
23638
+ executionTargetId: executionTargetId || void 0,
23639
+ launchSession,
23640
+ ...terminal,
23641
+ dryRun
23642
+ }
23643
+ });
23644
+ if (result.providerFallbackWarning) {
23645
+ console.error(`[overlord] ${result.providerFallbackWarning}`);
23646
+ }
23647
+ if (result.viewerOpen && result.viewerOpen.ok === false) {
23648
+ console.error(`[overlord] ${result.viewerOpen.warning}`);
23649
+ }
23650
+ if (result.status && result.status !== 0) {
23651
+ throw new CliError({ message: `Launch command exited with status ${result.status}` });
23652
+ }
23653
+ await runtime.backend.post({
23654
+ path: `/api/runner/requests/${requestId}/launched`,
23655
+ body: result.providerSession ? { providerSession: result.providerSession } : {}
23656
+ });
23657
+ if (result.providerSession && !dryRun) {
23658
+ void postLatchHarnessEventsAfterLaunch({
23659
+ backend: runtime.backend,
23660
+ requestId,
23661
+ providerSessionId: result.providerSession.providerSessionId,
23662
+ executable: launchSession?.executionProvider.kind === "latch" ? launchSession.executionProvider.executable : void 0
23663
+ });
23664
+ }
23665
+ if (json || dryRun) {
23666
+ printJson({
23667
+ request,
23668
+ plan: result.plan,
23669
+ status: result.status,
23670
+ providerSession: result.providerSession ?? null,
23671
+ viewerOpen: result.viewerOpen ?? null,
23672
+ providerFallbackWarning: result.providerFallbackWarning ?? null
23673
+ });
23674
+ } else {
23675
+ console.log(`Launched ${requestedAgent} for ${requestRecord.missionId}`);
23676
+ }
23677
+ return { launched: true, longPoll };
23678
+ } catch (error) {
23679
+ try {
23680
+ await runtime.backend.post({
23681
+ path: `/api/runner/requests/${requestId}/failed`,
23682
+ body: { error: error instanceof Error ? error.message : String(error) }
23683
+ });
23684
+ } catch {
23685
+ }
23686
+ throw error;
23687
+ }
23688
+ }
23438
23689
  async function runRunnerCommand({
23439
23690
  runtime,
23440
23691
  parsed,
@@ -23468,161 +23719,7 @@ async function runRunnerCommand({
23468
23719
  message: "Usage: ovld runner once|start|supervise|status|clear <objective_id>|clear-all|service <install|start|stop|restart|status|uninstall>"
23469
23720
  });
23470
23721
  }
23471
- const runOnce = async () => {
23472
- let claim;
23473
- try {
23474
- claim = await runtime.backend.post({
23475
- path: "/api/runner/claim",
23476
- body: {
23477
- projectId: flagValue(parsed.flags, "--project-id"),
23478
- ...clientDeviceIdentity(),
23479
- // Contract v40: the claim poll doubles as this runner instance's
23480
- // heartbeat, so local target liveness reflects a runner that can
23481
- // actually take work rather than any CLI traffic from the machine.
23482
- ...runnerRegistrationPayload()
23483
- }
23484
- });
23485
- } catch (error) {
23486
- if (isNoExecutionTargetRegisteredError(error)) {
23487
- throw new CliError({
23488
- message: `${error instanceof Error ? error.message : String(error)}
23489
- The runner will not register a target for you \u2014 declare this machine first, then start it again.`
23490
- });
23491
- }
23492
- throw error;
23493
- }
23494
- const request = asRecord5(claim).request;
23495
- const longPoll = asRecord5(claim).longPoll === true;
23496
- if (!request) return { launched: false, longPoll };
23497
- const requestRecord = asRecord5(request);
23498
- const requestId = String(requestRecord.id);
23499
- const projectId = String(requestRecord.projectId ?? "");
23500
- const executionTargetId = typeof requestRecord.executionTargetId === "string" ? requestRecord.executionTargetId.trim() : "";
23501
- if (projectId && executionTargetId) {
23502
- try {
23503
- await reportRunnerResourceObservations({
23504
- backend: runtime.backend,
23505
- projectId,
23506
- executionTargetId
23507
- });
23508
- } catch {
23509
- }
23510
- }
23511
- try {
23512
- await runtime.backend.post({ path: `/api/runner/requests/${requestId}/launching` });
23513
- const mutation = parseMutationFromMetadata(requestRecord.metadata);
23514
- if (mutation) {
23515
- const mutationResult = await executeLocalTargetMutation({ mutation });
23516
- if (!mutationResult.ok) {
23517
- await runtime.backend.post({
23518
- path: `/api/runner/requests/${requestId}/failed`,
23519
- body: { error: mutationResult.message }
23520
- });
23521
- throw new CliError({ message: mutationResult.message });
23522
- }
23523
- await runtime.backend.post({
23524
- path: `/api/runner/requests/${requestId}/completed`,
23525
- body: { mutationResult }
23526
- });
23527
- if (json) printJson({ request, mutationResult });
23528
- else console.log(`Completed ${mutation.kind} for ${requestRecord.missionId}`);
23529
- return { launched: true, longPoll };
23530
- }
23531
- const requestedAgent = typeof requestRecord.requestedAgent === "string" ? requestRecord.requestedAgent.trim() : "";
23532
- if (!requestedAgent) {
23533
- throw new CliError({
23534
- message: `Execution request ${requestId} has no agent; the objective must specify one before it can be launched.`
23535
- });
23536
- }
23537
- const launchConfig = asRecord5(requestRecord.launchConfig);
23538
- const terminal = await resolveTerminalLaunchSettings({ runtime, flags: parsed.flags });
23539
- const missionId = String(requestRecord.missionId);
23540
- const dryRun = flagBoolean(parsed.flags, "--dry-run");
23541
- const prepared = await prepareMissionBranch({
23542
- runtime,
23543
- options: {
23544
- missionId,
23545
- workingDirectory: String(requestRecord.workingDirectory ?? process.cwd()),
23546
- objectiveId: String(requestRecord.objectiveId ?? ""),
23547
- workspaceAutomationEnabled: await readWorktreeBranchAutomationEnabled(runtime),
23548
- dryRun,
23549
- overrideBranch: flagValue(parsed.flags, "--branch"),
23550
- noWorktree: flagBoolean(parsed.flags, "--no-worktree")
23551
- }
23552
- });
23553
- await recordBranchPrepared({
23554
- runtime,
23555
- missionId,
23556
- requestId,
23557
- branchAutomation: prepared.branchAutomation
23558
- });
23559
- const launchSession = launchSessionSnapshotFromMetadata(asRecord5(requestRecord.metadata));
23560
- const result = await launchAgent({
23561
- runtime,
23562
- options: {
23563
- agent: requestedAgent,
23564
- missionId,
23565
- workingDirectory: prepared.workingDirectory,
23566
- model: typeof requestRecord.requestedModel === "string" ? requestRecord.requestedModel : void 0,
23567
- thinking: typeof requestRecord.requestedReasoningEffort === "string" ? requestRecord.requestedReasoningEffort : void 0,
23568
- flags: normalizeAgentLaunchFlags(launchConfig.flags),
23569
- preCommand: typeof launchConfig.preCommand === "string" ? launchConfig.preCommand : void 0,
23570
- preLaunchCommands: Array.isArray(requestRecord.preLaunchCommands) ? requestRecord.preLaunchCommands.filter(
23571
- (value) => typeof value === "string"
23572
- ) : void 0,
23573
- launchEnvVars: parseLaunchEnvVarsValue(requestRecord.launchEnvVars),
23574
- executionRequestId: requestId,
23575
- executionTargetId: executionTargetId || void 0,
23576
- launchSession,
23577
- ...terminal,
23578
- dryRun
23579
- }
23580
- });
23581
- if (result.providerFallbackWarning) {
23582
- console.error(`[overlord] ${result.providerFallbackWarning}`);
23583
- }
23584
- if (result.viewerOpen && result.viewerOpen.ok === false) {
23585
- console.error(`[overlord] ${result.viewerOpen.warning}`);
23586
- }
23587
- if (result.status && result.status !== 0) {
23588
- throw new CliError({ message: `Launch command exited with status ${result.status}` });
23589
- }
23590
- await runtime.backend.post({
23591
- path: `/api/runner/requests/${requestId}/launched`,
23592
- body: result.providerSession ? { providerSession: result.providerSession } : {}
23593
- });
23594
- if (result.providerSession && !dryRun) {
23595
- void postLatchHarnessEventsAfterLaunch({
23596
- backend: runtime.backend,
23597
- requestId,
23598
- providerSessionId: result.providerSession.providerSessionId,
23599
- executable: launchSession?.executionProvider.kind === "latch" ? launchSession.executionProvider.executable : void 0
23600
- });
23601
- }
23602
- if (json || dryRun) {
23603
- printJson({
23604
- request,
23605
- plan: result.plan,
23606
- status: result.status,
23607
- providerSession: result.providerSession ?? null,
23608
- viewerOpen: result.viewerOpen ?? null,
23609
- providerFallbackWarning: result.providerFallbackWarning ?? null
23610
- });
23611
- } else {
23612
- console.log(`Launched ${requestedAgent} for ${requestRecord.missionId}`);
23613
- }
23614
- return { launched: true, longPoll };
23615
- } catch (error) {
23616
- try {
23617
- await runtime.backend.post({
23618
- path: `/api/runner/requests/${requestId}/failed`,
23619
- body: { error: error instanceof Error ? error.message : String(error) }
23620
- });
23621
- } catch {
23622
- }
23623
- throw error;
23624
- }
23625
- };
23722
+ const runOnce = () => runRunnerOnce({ runtime, parsed, json });
23626
23723
  if (sub === "once") {
23627
23724
  const result = await runOnce();
23628
23725
  if (!result.launched) {
@@ -23646,8 +23743,18 @@ async function runRunnerSupervisor({
23646
23743
  runOnce,
23647
23744
  json
23648
23745
  }) {
23649
- if (!json) console.log("Runner supervisor started. Adaptive polling for execution requests.");
23746
+ if (!json) console.log("Runner supervisor started. Polling for execution requests.");
23747
+ const initialExecutableIdentity = captureRunnerSupervisorIdentity();
23650
23748
  for (; ; ) {
23749
+ if (shouldRestartRunnerSupervisor({
23750
+ initial: initialExecutableIdentity,
23751
+ current: captureRunnerSupervisorIdentity()
23752
+ })) {
23753
+ console.error(
23754
+ "Runner supervisor executable was replaced or removed; exiting cleanly so the service manager restarts the installed build."
23755
+ );
23756
+ process.exit(0);
23757
+ }
23651
23758
  const now = /* @__PURE__ */ new Date();
23652
23759
  let result = { launched: false, longPoll: false };
23653
23760
  let lastError = null;
@@ -23656,25 +23763,22 @@ async function runRunnerSupervisor({
23656
23763
  } catch (error) {
23657
23764
  if (isNoExecutionTargetRegisteredError(error)) throw error;
23658
23765
  lastError = error instanceof Error ? error.message : String(error);
23766
+ console.error(`[overlord] Runner poll failed: ${lastError}`);
23659
23767
  }
23660
23768
  const state = readRunnerServiceState();
23661
- const focus = readDesktopFocusState();
23662
- const base = selectBasePollIntervalMs({
23663
- lastLaunchedAt: result.launched ? now.toISOString() : state.lastLaunchedAt,
23664
- lastDesktopFocusAt: focus.lastFocusedAt,
23665
- now: now.getTime()
23666
- });
23667
23769
  patchRunnerServiceState({
23668
23770
  lastHeartbeatAt: now.toISOString(),
23669
23771
  lastClaimedAt: result.launched ? now.toISOString() : state.lastClaimedAt,
23670
23772
  lastLaunchedAt: result.launched ? now.toISOString() : state.lastLaunchedAt,
23671
- // Reflect the latest poll's outcome so a resolved failure (e.g. an auth
23672
- // error from before the user logged in) clears instead of sticking.
23673
- lastError: nextRunnerLastError(lastError),
23674
- currentPollIntervalMs: base
23773
+ // Reflect the latest poll's outcome rather than falling back to the stored
23774
+ // value. A successful poll must clear a resolved failure (for example, an
23775
+ // auth error from before login) instead of leaving the desktop status box
23776
+ // stuck on "Runner error"; persistent failures recur on every poll.
23777
+ lastError,
23778
+ currentPollIntervalMs: FALLBACK_POLL_INTERVAL_MS
23675
23779
  });
23676
23780
  if (!result.longPoll) {
23677
- await new Promise((resolve) => setTimeout(resolve, applyPollJitter(base)));
23781
+ await new Promise((resolve) => setTimeout(resolve, applyPollJitter(FALLBACK_POLL_INTERVAL_MS)));
23678
23782
  }
23679
23783
  }
23680
23784
  }
@@ -23692,10 +23796,8 @@ async function runRunnerServiceCommand({
23692
23796
  if (action === "status") {
23693
23797
  const runState = await manager.status();
23694
23798
  const state = readRunnerServiceState();
23695
- const { publisher, needsReinstallForOverlord } = describeServicePublisher({
23696
- execProgram: state.execProgram
23697
- });
23698
- const reinstallHint = runState.installed && needsReinstallForOverlord ? 'This service is registered under "Node.js Foundation" because it runs the plain node binary. Reinstall it from the Overlord desktop app (or with the desktop app present) to re-register it under "Overlord".' : null;
23799
+ const publisher = process.platform !== "darwin" || !state.execProgram ? "unknown" : state.execProgram.includes(".app/Contents/MacOS/") ? "overlord" : "node";
23800
+ const reinstallHint = runState.installed && publisher === "node" ? 'This service is registered under "Node.js Foundation" because it runs the plain node binary. Reinstall it from the Overlord desktop app (or with the desktop app present) to re-register it under "Overlord".' : null;
23699
23801
  const payload = {
23700
23802
  supported: true,
23701
23803
  kind: manager.kind,