overlord-cli 0.2608231148.0 → 0.2608240740.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
@@ -20809,6 +20809,9 @@ var init_pre_launch = __esm({
20809
20809
  });
20810
20810
 
20811
20811
  // src/terminal-launcher.ts
20812
+ function terminalOpenSpawnTimeoutMs(execution) {
20813
+ return execution.terminal ? TERMINAL_OPEN_SPAWN_TIMEOUT_MS : void 0;
20814
+ }
20812
20815
  function shellQuote2(value) {
20813
20816
  return `'${value.replace(/'/g, `'\\''`)}'`;
20814
20817
  }
@@ -21126,11 +21129,14 @@ function resolveLaunchExecution({
21126
21129
  });
21127
21130
  return { command: full, args: [], useShell: true, terminal: launcher, display: full };
21128
21131
  }
21132
+ var TERMINAL_OPEN_SPAWN_TIMEOUT_MS, TERMINAL_OPEN_SPAWN_KILL_SIGNAL;
21129
21133
  var init_terminal_launcher = __esm({
21130
21134
  "src/terminal-launcher.ts"() {
21131
21135
  "use strict";
21132
21136
  init_project_tmp();
21133
21137
  init_terminal_launch_chord();
21138
+ TERMINAL_OPEN_SPAWN_TIMEOUT_MS = 45e3;
21139
+ TERMINAL_OPEN_SPAWN_KILL_SIGNAL = "SIGKILL";
21134
21140
  }
21135
21141
  });
21136
21142
 
@@ -21142,6 +21148,40 @@ function interactiveLoginShellInvocation(command, configuredShell = process.env.
21142
21148
  const shell = configuredShell?.trim() || "/bin/bash";
21143
21149
  return { command: shell, args: ["-ilc", command] };
21144
21150
  }
21151
+ function spawnSyncOptionsForLaunch({
21152
+ cwd,
21153
+ env,
21154
+ timeoutMs
21155
+ }) {
21156
+ return {
21157
+ cwd,
21158
+ env,
21159
+ stdio: "inherit",
21160
+ ...timeoutMs === void 0 ? {} : { timeout: timeoutMs, killSignal: TERMINAL_OPEN_SPAWN_KILL_SIGNAL }
21161
+ };
21162
+ }
21163
+ function isSpawnSyncTimedOut(result) {
21164
+ return result.error?.code === "ETIMEDOUT";
21165
+ }
21166
+ function terminalOpenTimeoutMessage({
21167
+ terminal,
21168
+ timeoutMs
21169
+ }) {
21170
+ const seconds = Math.round(timeoutMs / 1e3);
21171
+ return `osascript/Apple Event timed out after ${seconds}s while opening ${terminal}. The execution request failed so the runner can keep claiming. ${terminal} may still open after this timeout (orphan window).`;
21172
+ }
21173
+ function throwIfTerminalOpenTimedOut({
21174
+ result,
21175
+ execution
21176
+ }) {
21177
+ if (!execution.terminal || !isSpawnSyncTimedOut(result)) return;
21178
+ throw new Error(
21179
+ terminalOpenTimeoutMessage({
21180
+ terminal: execution.terminal,
21181
+ timeoutMs: TERMINAL_OPEN_SPAWN_TIMEOUT_MS
21182
+ })
21183
+ );
21184
+ }
21145
21185
  function overlordLaunchEnv({
21146
21186
  backendUrl,
21147
21187
  missionId,
@@ -21573,16 +21613,15 @@ async function launchAgent({
21573
21613
  };
21574
21614
  }
21575
21615
  const { execution } = plan;
21576
- const shellInvocation = execution.useShell ? interactiveLoginShellInvocation(execution.command) : null;
21577
- const result = shellInvocation ? spawnSync3(shellInvocation.command, shellInvocation.args, {
21578
- cwd: options.workingDirectory,
21579
- env,
21580
- stdio: "inherit"
21581
- }) : spawnSync3(execution.command, execution.args, {
21616
+ const timeoutMs = terminalOpenSpawnTimeoutMs(execution);
21617
+ const spawnOptions = spawnSyncOptionsForLaunch({
21582
21618
  cwd: options.workingDirectory,
21583
21619
  env,
21584
- stdio: "inherit"
21620
+ timeoutMs
21585
21621
  });
21622
+ const shellInvocation = execution.useShell ? interactiveLoginShellInvocation(execution.command) : null;
21623
+ const result = shellInvocation ? spawnSync3(shellInvocation.command, shellInvocation.args, spawnOptions) : spawnSync3(execution.command, execution.args, spawnOptions);
21624
+ throwIfTerminalOpenTimedOut({ result, execution });
21586
21625
  return {
21587
21626
  plan,
21588
21627
  status: result.status,
@@ -22795,13 +22834,66 @@ function resolveOvldInvocation(argv = process.argv, execPath = process.execPath)
22795
22834
  }
22796
22835
  return { program: execPath, args: ["runner", "supervise"] };
22797
22836
  }
22837
+ function composeRunnerServicePath({
22838
+ homeDir = os5.homedir()
22839
+ } = {}) {
22840
+ const localBin = path33.join(homeDir, ".local", "bin");
22841
+ const dirs = [...RUNNER_SERVICE_PATH_PREFIX_DIRS, localBin, ...RUNNER_SERVICE_SYSTEM_PATH_DIRS];
22842
+ return [...new Set(dirs)].join(":");
22843
+ }
22844
+ function parseLaunchdProcessType(plist) {
22845
+ const match = /<key>ProcessType<\/key>\s*<string>([^<]*)<\/string>/.exec(plist);
22846
+ return match?.[1] ?? null;
22847
+ }
22848
+ function parseLaunchdEnvPath(plist) {
22849
+ const match = /<key>PATH<\/key>\s*<string>([^<]*)<\/string>/.exec(plist);
22850
+ return match?.[1] ?? null;
22851
+ }
22852
+ function inspectInstalledLaunchdPlist(plistPath) {
22853
+ if (!existsSync28(plistPath)) return { processType: null, path: null };
22854
+ try {
22855
+ const plist = readFileSync19(plistPath, "utf8");
22856
+ return {
22857
+ processType: parseLaunchdProcessType(plist),
22858
+ path: parseLaunchdEnvPath(plist)
22859
+ };
22860
+ } catch {
22861
+ return { processType: null, path: null };
22862
+ }
22863
+ }
22864
+ function runnerServiceReinstallHint({
22865
+ installed,
22866
+ publisher,
22867
+ processType,
22868
+ path: path36
22869
+ }) {
22870
+ if (!installed) return null;
22871
+ const hints = [];
22872
+ if (publisher === "node") {
22873
+ hints.push(
22874
+ '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".'
22875
+ );
22876
+ }
22877
+ if (processType && processType !== LAUNCHD_PROCESS_TYPE) {
22878
+ hints.push(
22879
+ `This LaunchAgent is ProcessType=${processType}. App auto-update respawns the supervisor but does not rewrite ProcessType, so Apple Events to Terminal/iTerm stay App-Napped. Reinstall with \`ovld runner service install\` (or Desktop \u2192 Reinstall service) to switch to ${LAUNCHD_PROCESS_TYPE}.`
22880
+ );
22881
+ }
22882
+ if (path36 !== void 0 && path36 !== composeRunnerServicePath()) {
22883
+ hints.push(
22884
+ "This LaunchAgent PATH is not the deterministic prefix (`/opt/homebrew/bin:/usr/local/bin:$HOME/.local/bin` plus system dirs). App auto-update respawns the supervisor but does not rewrite PATH, so Latch/`open`/agent binaries spawned by the service may be missing. Reinstall with `ovld runner service install` (or Desktop \u2192 Reinstall service) to apply it."
22885
+ );
22886
+ }
22887
+ return hints.length > 0 ? hints.join(" ") : null;
22888
+ }
22798
22889
  function buildRunnerServiceEnv({
22799
22890
  backendUrl,
22800
22891
  overlordHome,
22801
- runAsElectronNode
22892
+ runAsElectronNode,
22893
+ homeDir
22802
22894
  }) {
22803
22895
  const env = {
22804
- PATH: process.env.PATH?.trim() || "/usr/local/bin:/usr/bin:/bin"
22896
+ PATH: composeRunnerServicePath({ homeDir })
22805
22897
  };
22806
22898
  if (!isLoopbackBackendUrl(backendUrl)) env.OVERLORD_BACKEND_URL = backendUrl;
22807
22899
  const home = overlordHome?.trim() || process.env.OVLD_HOME?.trim();
@@ -22848,7 +22940,7 @@ ${envEntries}
22848
22940
  <key>StandardErrorPath</key>
22849
22941
  <string>${xmlEscape(path33.join(logDir, "runner-service.err.log"))}</string>
22850
22942
  <key>ProcessType</key>
22851
- <string>Background</string>
22943
+ <string>${LAUNCHD_PROCESS_TYPE}</string>
22852
22944
  </dict>
22853
22945
  </plist>
22854
22946
  `;
@@ -22913,7 +23005,7 @@ function resolveServiceManager(platform2 = process.platform) {
22913
23005
  if (platform2 === "linux") return new SystemdUserManager();
22914
23006
  return null;
22915
23007
  }
22916
- var execFileAsync, RUNNER_SERVICE_STATE_FILENAME, LAUNCHD_LABEL, SYSTEMD_UNIT_NAME, FALLBACK_POLL_INTERVAL_MS, LaunchdManager, SystemdUserManager;
23008
+ var execFileAsync, RUNNER_SERVICE_STATE_FILENAME, LAUNCHD_LABEL, SYSTEMD_UNIT_NAME, FALLBACK_POLL_INTERVAL_MS, LAUNCHD_PROCESS_TYPE, RUNNER_SERVICE_SYSTEM_PATH_DIRS, RUNNER_SERVICE_PATH_PREFIX_DIRS, LaunchdManager, SystemdUserManager;
22917
23009
  var init_runner_service = __esm({
22918
23010
  "src/runner-service.ts"() {
22919
23011
  "use strict";
@@ -22923,6 +23015,9 @@ var init_runner_service = __esm({
22923
23015
  LAUNCHD_LABEL = "io.overlord.runner";
22924
23016
  SYSTEMD_UNIT_NAME = "overlord-runner";
22925
23017
  FALLBACK_POLL_INTERVAL_MS = 5e3;
23018
+ LAUNCHD_PROCESS_TYPE = "Interactive";
23019
+ RUNNER_SERVICE_SYSTEM_PATH_DIRS = ["/usr/bin", "/bin", "/usr/sbin", "/sbin"];
23020
+ RUNNER_SERVICE_PATH_PREFIX_DIRS = ["/opt/homebrew/bin", "/usr/local/bin"];
22926
23021
  LaunchdManager = class {
22927
23022
  kind = "launchd";
22928
23023
  identifier = LAUNCHD_LABEL;
@@ -24821,7 +24916,14 @@ async function runRunnerServiceCommand({
24821
24916
  const runState = await manager.status();
24822
24917
  const state = readRunnerServiceState();
24823
24918
  const publisher = process.platform !== "darwin" || !state.execProgram ? "unknown" : state.execProgram.includes(".app/Contents/MacOS/") ? "overlord" : "node";
24824
- 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;
24919
+ const inspected = manager.kind === "launchd" && runState.installed ? inspectInstalledLaunchdPlist(manager.unitPath()) : null;
24920
+ const processType = inspected?.processType ?? null;
24921
+ const reinstallHint = runnerServiceReinstallHint({
24922
+ installed: runState.installed,
24923
+ publisher,
24924
+ processType,
24925
+ ...inspected ? { path: inspected.path } : {}
24926
+ });
24825
24927
  const payload = {
24826
24928
  supported: true,
24827
24929
  kind: manager.kind,
@@ -24837,6 +24939,8 @@ async function runRunnerServiceCommand({
24837
24939
  lastError: state.lastError,
24838
24940
  currentPollIntervalMs: state.currentPollIntervalMs,
24839
24941
  publisher,
24942
+ processType,
24943
+ path: inspected?.path ?? null,
24840
24944
  reinstallHint
24841
24945
  };
24842
24946
  if (json) printJson(payload);
@@ -24846,6 +24950,7 @@ async function runRunnerServiceCommand({
24846
24950
  Installed: runState.installed ? "yes" : "no",
24847
24951
  Running: runState.running,
24848
24952
  Publisher: publisher === "unknown" ? "(unknown)" : publisher,
24953
+ ...processType ? { "Process type": processType } : {},
24849
24954
  Backend: state.backendUrl ?? "(unknown)",
24850
24955
  "Last heartbeat": state.lastHeartbeatAt ?? "(never)",
24851
24956
  "Last launched": state.lastLaunchedAt ?? "(never)",
@@ -24881,7 +24986,7 @@ Note: ${reinstallHint}`);
24881
24986
  if (json) printJson({ started: autoStart, ...runState });
24882
24987
  else
24883
24988
  console.log(
24884
- `Persistent runner installed (${manager.kind}) and ${autoStart ? "started" : "not started"}.`
24989
+ `Persistent runner installed (${manager.kind}) and ${autoStart ? "started" : "not started"}.` + (manager.kind === "launchd" ? " Re-running install rewrites the LaunchAgent (ProcessType Interactive and PATH prefix); app auto-update respawns the process but does not rewrite the plist." : "")
24885
24990
  );
24886
24991
  return;
24887
24992
  }