overlord-cli 0.2608132027.0 → 0.2608141841.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
@@ -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, {
@@ -16556,12 +16567,88 @@ var init_latch_environment = __esm({
16556
16567
  }
16557
16568
  });
16558
16569
 
16559
- // ../packages/core/dist/service/latch-launch.js
16570
+ // ../packages/core/dist/service/terminal-profile-types.js
16571
+ function parseViewerOpenAs(value) {
16572
+ return typeof value === "string" && value.trim().toLowerCase() === "tab" ? "tab" : DEFAULT_VIEWER_OPEN_AS;
16573
+ }
16560
16574
  function trimmed(value) {
16561
16575
  return typeof value === "string" && value.trim() ? value.trim() : null;
16562
16576
  }
16577
+ function viewerKindForLauncher(launcher) {
16578
+ const value = trimmed(launcher);
16579
+ if (!value)
16580
+ return "inline";
16581
+ const lowered = value.toLowerCase();
16582
+ if (lowered === "iterm2" || lowered === "iterm")
16583
+ return "iterm";
16584
+ if (lowered === "terminal")
16585
+ return "terminal";
16586
+ return "custom";
16587
+ }
16588
+ function normalizeExecutionProvider(value) {
16589
+ if (!value || typeof value !== "object" || Array.isArray(value))
16590
+ return null;
16591
+ const cast = value;
16592
+ const kind = trimmed(cast.kind)?.toLowerCase() === "latch" ? "latch" : "direct";
16593
+ return {
16594
+ kind,
16595
+ executable: trimmed(cast.executable) ?? DEFAULT_LATCH_EXECUTABLE
16596
+ };
16597
+ }
16598
+ function launchSessionSnapshotFromMetadata(metadata) {
16599
+ const raw = metadata?.[LAUNCH_SESSION_METADATA_KEY];
16600
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
16601
+ return null;
16602
+ const cast = raw;
16603
+ if (cast.version !== TERMINAL_PROFILE_VERSION)
16604
+ return null;
16605
+ const viewer = cast.viewer && typeof cast.viewer === "object" && !Array.isArray(cast.viewer) ? cast.viewer : null;
16606
+ const launcher = trimmed(viewer?.launcher);
16607
+ const source = (value) => trimmed(value) === "target" ? "target" : "user_default";
16608
+ return {
16609
+ version: TERMINAL_PROFILE_VERSION,
16610
+ executionProvider: normalizeExecutionProvider(cast.executionProvider) ?? {
16611
+ ...DEFAULT_EXECUTION_PROVIDER
16612
+ },
16613
+ viewer: {
16614
+ kind: trimmed(viewer?.kind) ? trimmed(viewer?.kind) : viewerKindForLauncher(launcher),
16615
+ launcher,
16616
+ openOnLaunch: viewer?.openOnLaunch !== false,
16617
+ // A snapshot frozen before `openAs` existed carries none; `window` is what
16618
+ // those runs actually did, so the absent case must not become `tab`.
16619
+ openAs: parseViewerOpenAs(viewer?.openAs)
16620
+ },
16621
+ executionProviderSource: source(cast.executionProviderSource),
16622
+ viewerOpenSource: source(cast.viewerOpenSource),
16623
+ resolvedAt: trimmed(cast.resolvedAt) ?? ""
16624
+ };
16625
+ }
16626
+ var DEFAULT_VIEWER_OPEN_AS, TERMINAL_PROFILE_VERSION, DEFAULT_LATCH_EXECUTABLE, DEFAULT_EXECUTION_PROVIDER, DEFAULT_OPEN_VIEWER_ON_LAUNCH, DEFAULT_LAUNCH_SESSION_DEFAULTS, LAUNCH_SESSION_METADATA_KEY;
16627
+ var init_terminal_profile_types2 = __esm({
16628
+ "../packages/core/dist/service/terminal-profile-types.js"() {
16629
+ "use strict";
16630
+ DEFAULT_VIEWER_OPEN_AS = "window";
16631
+ TERMINAL_PROFILE_VERSION = 1;
16632
+ DEFAULT_LATCH_EXECUTABLE = "latch";
16633
+ DEFAULT_EXECUTION_PROVIDER = {
16634
+ kind: "direct",
16635
+ executable: DEFAULT_LATCH_EXECUTABLE
16636
+ };
16637
+ DEFAULT_OPEN_VIEWER_ON_LAUNCH = true;
16638
+ DEFAULT_LAUNCH_SESSION_DEFAULTS = {
16639
+ executionProvider: { ...DEFAULT_EXECUTION_PROVIDER },
16640
+ openViewerOnLaunch: DEFAULT_OPEN_VIEWER_ON_LAUNCH
16641
+ };
16642
+ LAUNCH_SESSION_METADATA_KEY = "launchSession";
16643
+ }
16644
+ });
16645
+
16646
+ // ../packages/core/dist/service/latch-launch.js
16647
+ function trimmed2(value) {
16648
+ return typeof value === "string" && value.trim() ? value.trim() : null;
16649
+ }
16563
16650
  function buildLatchCreateManifest({ commandString, shell = process.env.SHELL?.trim() || "/bin/bash", cwd, env, size = DEFAULT_LATCH_TERMINAL_SIZE, title, name, commandLabel, externalRunId }) {
16564
- const shellPath = trimmed(shell) ?? "/bin/bash";
16651
+ const shellPath = trimmed2(shell) ?? "/bin/bash";
16565
16652
  const cleanedEnv = {};
16566
16653
  for (const [key, value] of Object.entries(env)) {
16567
16654
  if (key.trim() && typeof value === "string")
@@ -16570,15 +16657,15 @@ function buildLatchCreateManifest({ commandString, shell = process.env.SHELL?.tr
16570
16657
  const display = {
16571
16658
  source: {
16572
16659
  kind: "overlord",
16573
- ...trimmed(externalRunId) ? { external_run_id: trimmed(externalRunId) } : {}
16660
+ ...trimmed2(externalRunId) ? { external_run_id: trimmed2(externalRunId) } : {}
16574
16661
  }
16575
16662
  };
16576
- if (trimmed(title))
16577
- display.title = trimmed(title);
16578
- if (trimmed(name))
16579
- display.name = trimmed(name);
16580
- if (trimmed(commandLabel))
16581
- display.command_label = trimmed(commandLabel);
16663
+ if (trimmed2(title))
16664
+ display.title = trimmed2(title);
16665
+ if (trimmed2(name))
16666
+ display.name = trimmed2(name);
16667
+ if (trimmed2(commandLabel))
16668
+ display.command_label = trimmed2(commandLabel);
16582
16669
  return {
16583
16670
  format_version: LATCH_MANIFEST_FORMAT_VERSION,
16584
16671
  launch: {
@@ -16597,7 +16684,7 @@ function buildLatchCreateManifest({ commandString, shell = process.env.SHELL?.tr
16597
16684
  function parseLatchCreateReport(raw) {
16598
16685
  try {
16599
16686
  const parsed = JSON.parse(raw);
16600
- const id = trimmed(parsed.session?.id);
16687
+ const id = trimmed2(parsed.session?.id);
16601
16688
  if (!id)
16602
16689
  return null;
16603
16690
  const protocolVersion = typeof parsed.protocolVersion === "number" && Number.isFinite(parsed.protocolVersion) ? parsed.protocolVersion : LATCH_MANIFEST_FORMAT_VERSION;
@@ -16605,9 +16692,9 @@ function parseLatchCreateReport(raw) {
16605
16692
  protocolVersion,
16606
16693
  session: {
16607
16694
  id,
16608
- name: trimmed(parsed.session?.name) ?? id,
16609
- state: trimmed(parsed.session?.state) ?? "running",
16610
- createdAt: trimmed(parsed.session?.createdAt) ?? (/* @__PURE__ */ new Date()).toISOString()
16695
+ name: trimmed2(parsed.session?.name) ?? id,
16696
+ state: trimmed2(parsed.session?.state) ?? "running",
16697
+ createdAt: trimmed2(parsed.session?.createdAt) ?? (/* @__PURE__ */ new Date()).toISOString()
16611
16698
  }
16612
16699
  };
16613
16700
  } catch {
@@ -16619,28 +16706,28 @@ function toExecutionProviderSession({ createReport, executionTargetId, createdAt
16619
16706
  provider: "latch",
16620
16707
  providerSessionId: createReport.session.id,
16621
16708
  sessionName: createReport.session.name,
16622
- executionTargetId: trimmed(executionTargetId),
16709
+ executionTargetId: trimmed2(executionTargetId),
16623
16710
  agentSessionId: null,
16624
16711
  createdAt: createdAt ?? createReport.session.createdAt,
16625
16712
  lastObservedState: createReport.session.state
16626
16713
  };
16627
16714
  }
16628
16715
  function existingLatchProviderSession({ latchSessionId, executionTargetId, createdAt = (/* @__PURE__ */ new Date()).toISOString() }) {
16629
- const providerSessionId = trimmed(latchSessionId);
16716
+ const providerSessionId = trimmed2(latchSessionId);
16630
16717
  if (!providerSessionId)
16631
16718
  return null;
16632
16719
  return {
16633
16720
  provider: "latch",
16634
16721
  providerSessionId,
16635
16722
  sessionName: null,
16636
- executionTargetId: trimmed(executionTargetId),
16723
+ executionTargetId: trimmed2(executionTargetId),
16637
16724
  agentSessionId: null,
16638
16725
  createdAt,
16639
16726
  lastObservedState: "running"
16640
16727
  };
16641
16728
  }
16642
16729
  function latchViewerFlagForKind(kind) {
16643
- switch (trimmed(kind)?.toLowerCase()) {
16730
+ switch (trimmed2(kind)?.toLowerCase()) {
16644
16731
  case "iterm":
16645
16732
  case "iterm2":
16646
16733
  return "iterm";
@@ -16648,28 +16735,59 @@ function latchViewerFlagForKind(kind) {
16648
16735
  return null;
16649
16736
  }
16650
16737
  }
16738
+ function compareLatchProductVersions(a, b) {
16739
+ const parts = (value) => value.trim().split(".").map((segment) => {
16740
+ const parsed = Number.parseInt(segment, 10);
16741
+ return Number.isFinite(parsed) ? parsed : 0;
16742
+ });
16743
+ const left = parts(a);
16744
+ const right = parts(b);
16745
+ const length = Math.max(left.length, right.length);
16746
+ for (let index = 0; index < length; index += 1) {
16747
+ const diff = (left[index] ?? 0) - (right[index] ?? 0);
16748
+ if (diff !== 0)
16749
+ return diff;
16750
+ }
16751
+ return 0;
16752
+ }
16753
+ function latchSupportsOpenAs(productVersion) {
16754
+ const version = trimmed2(productVersion);
16755
+ if (!version)
16756
+ return false;
16757
+ return compareLatchProductVersions(version, LATCH_OPEN_AS_MIN_PRODUCT_VERSION) >= 0;
16758
+ }
16759
+ function buildLatchOpenArgs({ providerSessionId, viewer, openAs, productVersion }) {
16760
+ const args = ["open", providerSessionId, "--with", viewer];
16761
+ if (openAs && latchSupportsOpenAs(productVersion)) {
16762
+ args.push("--as", parseViewerOpenAs(openAs));
16763
+ }
16764
+ args.push("--json");
16765
+ return args;
16766
+ }
16651
16767
  function latchAttachCommand({ executable = "latch", providerSessionId }) {
16652
- return `${trimmed(executable) ?? "latch"} attach ${providerSessionId}`;
16768
+ return `${trimmed2(executable) ?? "latch"} attach ${providerSessionId}`;
16653
16769
  }
16654
16770
  function shouldUseLatchProvider({ providerKind, latchSelectable }) {
16655
- return trimmed(providerKind)?.toLowerCase() === "latch" && latchSelectable === true;
16771
+ return trimmed2(providerKind)?.toLowerCase() === "latch" && latchSelectable === true;
16656
16772
  }
16657
- var LATCH_MANIFEST_FORMAT_VERSION, DEFAULT_LATCH_TERMINAL_SIZE;
16773
+ var LATCH_MANIFEST_FORMAT_VERSION, DEFAULT_LATCH_TERMINAL_SIZE, LATCH_OPEN_AS_MIN_PRODUCT_VERSION;
16658
16774
  var init_latch_launch = __esm({
16659
16775
  "../packages/core/dist/service/latch-launch.js"() {
16660
16776
  "use strict";
16777
+ init_terminal_profile_types2();
16661
16778
  LATCH_MANIFEST_FORMAT_VERSION = 1;
16662
16779
  DEFAULT_LATCH_TERMINAL_SIZE = { cols: 120, rows: 36 };
16780
+ LATCH_OPEN_AS_MIN_PRODUCT_VERSION = "0.2608140931.0";
16663
16781
  }
16664
16782
  });
16665
16783
 
16666
16784
  // ../packages/core/dist/service/latch-session.js
16667
16785
  import { spawnSync as spawnSync2 } from "node:child_process";
16668
- function trimmed2(value) {
16786
+ function trimmed3(value) {
16669
16787
  return typeof value === "string" && value.trim() ? value.trim() : null;
16670
16788
  }
16671
16789
  function parseState(value) {
16672
- switch (trimmed2(value)) {
16790
+ switch (trimmed3(value)) {
16673
16791
  case "running":
16674
16792
  return "running";
16675
16793
  case "exited":
@@ -16708,11 +16826,11 @@ function runLatchJson({ executable, args }) {
16708
16826
  }
16709
16827
  }
16710
16828
  function inspectLatchSession({ executable = "latch", providerSessionId }) {
16711
- const sessionId = trimmed2(providerSessionId);
16829
+ const sessionId = trimmed3(providerSessionId);
16712
16830
  if (!sessionId)
16713
16831
  throw new LatchSessionCommandError("A Latch session id is required.");
16714
16832
  const report = runLatchJson({
16715
- executable: trimmed2(executable) ?? "latch",
16833
+ executable: trimmed3(executable) ?? "latch",
16716
16834
  args: ["inspect", sessionId, "--json"]
16717
16835
  });
16718
16836
  const state = parseState(report.state);
@@ -16720,47 +16838,61 @@ function inspectLatchSession({ executable = "latch", providerSessionId }) {
16720
16838
  throw new LatchSessionCommandError("Latch returned an unsupported session state.");
16721
16839
  const exit = report.exit && typeof report.exit === "object" && !Array.isArray(report.exit) ? report.exit : null;
16722
16840
  return {
16723
- providerSessionId: trimmed2(report.id) ?? sessionId,
16724
- name: trimmed2(report.name) ?? sessionId,
16841
+ providerSessionId: trimmed3(report.id) ?? sessionId,
16842
+ name: trimmed3(report.name) ?? sessionId,
16725
16843
  state,
16726
16844
  exitCode: typeof exit?.code === "number" && Number.isFinite(exit.code) ? exit.code : null,
16727
16845
  inspectedAt: (/* @__PURE__ */ new Date()).toISOString()
16728
16846
  };
16729
16847
  }
16730
- function openLatchSession({ executable = "latch", providerSessionId, viewerKind }) {
16731
- const sessionId = trimmed2(providerSessionId);
16848
+ function readLatchProductVersion(executable) {
16849
+ try {
16850
+ return trimmed3(runLatchJson({ executable, args: ["capabilities", "--json"] }).productVersion);
16851
+ } catch {
16852
+ return null;
16853
+ }
16854
+ }
16855
+ function openLatchSession({ executable = "latch", providerSessionId, viewerKind, openAs }) {
16856
+ const sessionId = trimmed3(providerSessionId);
16732
16857
  if (!sessionId)
16733
16858
  throw new LatchSessionCommandError("A Latch session id is required.");
16734
16859
  const viewer = latchViewerFlagForKind(viewerKind);
16735
16860
  if (!viewer) {
16736
16861
  throw new LatchSessionCommandError(`Latch cannot open the configured viewer "${viewerKind}".`);
16737
16862
  }
16863
+ const exe = trimmed3(executable) ?? "latch";
16738
16864
  const report = runLatchJson({
16739
- executable: trimmed2(executable) ?? "latch",
16740
- args: ["open", sessionId, "--with", viewer, "--json"]
16865
+ executable: exe,
16866
+ args: buildLatchOpenArgs({
16867
+ providerSessionId: sessionId,
16868
+ viewer,
16869
+ openAs,
16870
+ productVersion: openAs ? readLatchProductVersion(exe) : null
16871
+ })
16741
16872
  });
16742
16873
  if (report.opened !== true) {
16743
16874
  throw new LatchSessionCommandError(`Latch did not open the ${viewer} viewer.`);
16744
16875
  }
16745
16876
  return {
16746
- providerSessionId: trimmed2(report.id) ?? sessionId,
16747
- viewer: trimmed2(report.viewer) ?? viewer,
16748
- opened: true
16877
+ providerSessionId: trimmed3(report.id) ?? sessionId,
16878
+ viewer: trimmed3(report.viewer) ?? viewer,
16879
+ opened: true,
16880
+ behavior: trimmed3(report.behavior) ? parseViewerOpenAs(report.behavior) : null
16749
16881
  };
16750
16882
  }
16751
16883
  function stopLatchSession({ executable = "latch", providerSessionId }) {
16752
- const sessionId = trimmed2(providerSessionId);
16884
+ const sessionId = trimmed3(providerSessionId);
16753
16885
  if (!sessionId)
16754
16886
  throw new LatchSessionCommandError("A Latch session id is required.");
16755
16887
  const report = runLatchJson({
16756
- executable: trimmed2(executable) ?? "latch",
16888
+ executable: trimmed3(executable) ?? "latch",
16757
16889
  args: ["stop", sessionId, "--json"]
16758
16890
  });
16759
16891
  const state = parseState(report.state);
16760
16892
  if (!state)
16761
16893
  throw new LatchSessionCommandError("Latch returned an unsupported session state.");
16762
16894
  return {
16763
- providerSessionId: trimmed2(report.id) ?? sessionId,
16895
+ providerSessionId: trimmed3(report.id) ?? sessionId,
16764
16896
  state
16765
16897
  };
16766
16898
  }
@@ -16770,6 +16902,7 @@ var init_latch_session = __esm({
16770
16902
  "use strict";
16771
16903
  init_latch_environment();
16772
16904
  init_latch_launch();
16905
+ init_terminal_profile_types2();
16773
16906
  LatchSessionCommandError = class extends Error {
16774
16907
  constructor(message) {
16775
16908
  super(message);
@@ -16781,17 +16914,17 @@ var init_latch_session = __esm({
16781
16914
 
16782
16915
  // ../packages/core/dist/service/latch-events.js
16783
16916
  import { spawn as spawn3 } from "node:child_process";
16784
- function trimmed3(value) {
16917
+ function trimmed4(value) {
16785
16918
  return typeof value === "string" && value.trim() ? value.trim() : null;
16786
16919
  }
16787
16920
  function parseHarnessEvent(value) {
16788
16921
  if (!value || typeof value !== "object" || Array.isArray(value))
16789
16922
  return null;
16790
16923
  const row = value;
16791
- const type = trimmed3(row.type);
16792
- const sessionId = trimmed3(row.sessionId);
16793
- const at = trimmed3(row.at);
16794
- const harnessVersion = trimmed3(row.harnessVersion);
16924
+ const type = trimmed4(row.type);
16925
+ const sessionId = trimmed4(row.sessionId);
16926
+ const at = trimmed4(row.at);
16927
+ const harnessVersion = trimmed4(row.harnessVersion);
16795
16928
  const connectorEpoch = typeof row.connectorEpoch === "number" && Number.isFinite(row.connectorEpoch) ? row.connectorEpoch : null;
16796
16929
  if (!type || !HARNESS_EVENT_TYPES.has(type) || !sessionId || !at || !harnessVersion)
16797
16930
  return null;
@@ -16802,26 +16935,26 @@ function parseHarnessEvent(value) {
16802
16935
  case "user_message":
16803
16936
  case "assistant_delta":
16804
16937
  case "assistant_message": {
16805
- const text = trimmed3(row.text);
16938
+ const text = trimmed4(row.text);
16806
16939
  if (!text)
16807
16940
  return null;
16808
16941
  return { ...base, type, text };
16809
16942
  }
16810
16943
  case "tool_started": {
16811
- const tool = trimmed3(row.tool);
16944
+ const tool = trimmed4(row.tool);
16812
16945
  if (!tool)
16813
16946
  return null;
16814
16947
  return { ...base, type, tool, input: row.input };
16815
16948
  }
16816
16949
  case "tool_finished": {
16817
- const tool = trimmed3(row.tool);
16950
+ const tool = trimmed4(row.tool);
16818
16951
  if (!tool)
16819
16952
  return null;
16820
16953
  return { ...base, type, tool, output: row.output };
16821
16954
  }
16822
16955
  case "awaiting_input": {
16823
- const requestId = trimmed3(row.requestId);
16824
- const kind = trimmed3(row.kind);
16956
+ const requestId = trimmed4(row.requestId);
16957
+ const kind = trimmed4(row.kind);
16825
16958
  const prompt = typeof row.prompt === "string" ? row.prompt : null;
16826
16959
  if (!requestId || kind !== "permission" && kind !== "question" || prompt === null) {
16827
16960
  return null;
@@ -16837,7 +16970,7 @@ function parseHarnessEvent(value) {
16837
16970
  };
16838
16971
  }
16839
16972
  case "status": {
16840
- const status = trimmed3(row.status);
16973
+ const status = trimmed4(row.status);
16841
16974
  if (!status)
16842
16975
  return null;
16843
16976
  return { ...base, type, status };
@@ -16862,11 +16995,11 @@ function parseHarnessEventNdjson(raw) {
16862
16995
  return events;
16863
16996
  }
16864
16997
  async function collectLatchEvents({ executable = "latch", providerSessionId, from = 0, idleMs = 250, timeoutMs = 8e3 }) {
16865
- const sessionId = trimmed3(providerSessionId);
16998
+ const sessionId = trimmed4(providerSessionId);
16866
16999
  if (!sessionId)
16867
17000
  throw new LatchSessionCommandError("A Latch session id is required.");
16868
17001
  const fromCursor = Number.isFinite(from) && from > 0 ? Math.floor(from) : 0;
16869
- const bin = trimmed3(executable) ?? "latch";
17002
+ const bin = trimmed4(executable) ?? "latch";
16870
17003
  return new Promise((resolve, reject) => {
16871
17004
  const child = spawn3(bin, ["events", sessionId, "--json", "--from", String(fromCursor)], {
16872
17005
  stdio: ["ignore", "pipe", "pipe"],
@@ -17281,75 +17414,6 @@ var init_git_tree = __esm({
17281
17414
  }
17282
17415
  });
17283
17416
 
17284
- // ../packages/core/dist/service/terminal-profile-types.js
17285
- function trimmed4(value) {
17286
- return typeof value === "string" && value.trim() ? value.trim() : null;
17287
- }
17288
- function viewerKindForLauncher(launcher) {
17289
- const value = trimmed4(launcher);
17290
- if (!value)
17291
- return "inline";
17292
- const lowered = value.toLowerCase();
17293
- if (lowered === "iterm2" || lowered === "iterm")
17294
- return "iterm";
17295
- if (lowered === "terminal")
17296
- return "terminal";
17297
- return "custom";
17298
- }
17299
- function normalizeExecutionProvider(value) {
17300
- if (!value || typeof value !== "object" || Array.isArray(value))
17301
- return null;
17302
- const cast = value;
17303
- const kind = trimmed4(cast.kind)?.toLowerCase() === "latch" ? "latch" : "direct";
17304
- return {
17305
- kind,
17306
- executable: trimmed4(cast.executable) ?? DEFAULT_LATCH_EXECUTABLE
17307
- };
17308
- }
17309
- function launchSessionSnapshotFromMetadata(metadata) {
17310
- const raw = metadata?.[LAUNCH_SESSION_METADATA_KEY];
17311
- if (!raw || typeof raw !== "object" || Array.isArray(raw))
17312
- return null;
17313
- const cast = raw;
17314
- if (cast.version !== TERMINAL_PROFILE_VERSION)
17315
- return null;
17316
- const viewer = cast.viewer && typeof cast.viewer === "object" && !Array.isArray(cast.viewer) ? cast.viewer : null;
17317
- const launcher = trimmed4(viewer?.launcher);
17318
- const source = (value) => trimmed4(value) === "target" ? "target" : "user_default";
17319
- return {
17320
- version: TERMINAL_PROFILE_VERSION,
17321
- executionProvider: normalizeExecutionProvider(cast.executionProvider) ?? {
17322
- ...DEFAULT_EXECUTION_PROVIDER
17323
- },
17324
- viewer: {
17325
- kind: trimmed4(viewer?.kind) ? trimmed4(viewer?.kind) : viewerKindForLauncher(launcher),
17326
- launcher,
17327
- openOnLaunch: viewer?.openOnLaunch !== false
17328
- },
17329
- executionProviderSource: source(cast.executionProviderSource),
17330
- viewerOpenSource: source(cast.viewerOpenSource),
17331
- resolvedAt: trimmed4(cast.resolvedAt) ?? ""
17332
- };
17333
- }
17334
- var TERMINAL_PROFILE_VERSION, DEFAULT_LATCH_EXECUTABLE, DEFAULT_EXECUTION_PROVIDER, DEFAULT_OPEN_VIEWER_ON_LAUNCH, DEFAULT_LAUNCH_SESSION_DEFAULTS, LAUNCH_SESSION_METADATA_KEY;
17335
- var init_terminal_profile_types2 = __esm({
17336
- "../packages/core/dist/service/terminal-profile-types.js"() {
17337
- "use strict";
17338
- TERMINAL_PROFILE_VERSION = 1;
17339
- DEFAULT_LATCH_EXECUTABLE = "latch";
17340
- DEFAULT_EXECUTION_PROVIDER = {
17341
- kind: "direct",
17342
- executable: DEFAULT_LATCH_EXECUTABLE
17343
- };
17344
- DEFAULT_OPEN_VIEWER_ON_LAUNCH = true;
17345
- DEFAULT_LAUNCH_SESSION_DEFAULTS = {
17346
- executionProvider: { ...DEFAULT_EXECUTION_PROVIDER },
17347
- openViewerOnLaunch: DEFAULT_OPEN_VIEWER_ON_LAUNCH
17348
- };
17349
- LAUNCH_SESSION_METADATA_KEY = "launchSession";
17350
- }
17351
- });
17352
-
17353
17417
  // ../packages/core/dist/service/latch-discovery.js
17354
17418
  import { execFileSync as execFileSync3 } from "node:child_process";
17355
17419
  import { existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync15, statSync as statSync4, writeFileSync as writeFileSync8 } from "node:fs";
@@ -19186,7 +19250,9 @@ function createLatchSession({
19186
19250
  function openLatchViewer({
19187
19251
  executable = DEFAULT_LATCH_EXECUTABLE,
19188
19252
  providerSessionId,
19189
- viewerKind
19253
+ viewerKind,
19254
+ openAs,
19255
+ productVersion
19190
19256
  }) {
19191
19257
  const viewer = latchViewerFlagForKind(viewerKind);
19192
19258
  const attachCommand = latchAttachCommand({ executable, providerSessionId });
@@ -19197,9 +19263,10 @@ function openLatchViewer({
19197
19263
  attachCommand
19198
19264
  };
19199
19265
  }
19266
+ const sendsOpenAs = Boolean(openAs) && latchSupportsOpenAs(productVersion);
19200
19267
  const result = spawnLatchJson({
19201
19268
  executable,
19202
- args: ["open", providerSessionId, "--with", viewer, "--json"]
19269
+ args: buildLatchOpenArgs({ providerSessionId, viewer, openAs, productVersion })
19203
19270
  });
19204
19271
  if (result.status !== 0) {
19205
19272
  const detail = (result.stderr || result.stdout || `exit ${result.status ?? "null"}`).trim();
@@ -19209,7 +19276,7 @@ function openLatchViewer({
19209
19276
  attachCommand
19210
19277
  };
19211
19278
  }
19212
- return { ok: true, viewer };
19279
+ return { ok: true, viewer, openAs: sendsOpenAs ? parseViewerOpenAs(openAs) : null };
19213
19280
  }
19214
19281
  var init_latch_launch2 = __esm({
19215
19282
  "src/latch-launch.ts"() {
@@ -19917,6 +19984,7 @@ async function launchAgent({
19917
19984
  let providerFallbackWarning = null;
19918
19985
  let useLatch = false;
19919
19986
  let resolvedLatchExecutable = executable;
19987
+ let latchProductVersion = null;
19920
19988
  const existingProviderSession = providerKind === "latch" ? existingLatchProviderSession({
19921
19989
  latchSessionId: process.env.LATCH_SESSION_ID,
19922
19990
  executionTargetId: options.executionTargetId
@@ -19960,6 +20028,7 @@ async function launchAgent({
19960
20028
  });
19961
20029
  if (useLatch && discovery.state === "found") {
19962
20030
  resolvedLatchExecutable = discovery.resolvedPath;
20031
+ latchProductVersion = discovery.productVersion;
19963
20032
  }
19964
20033
  if (!useLatch) {
19965
20034
  providerFallbackWarning = discovery.state === "not_installed" ? `Latch is not installed on this execution target (${discovery.installCommand}); falling back to direct launch.` : discovery.state === "incompatible" ? `Latch is incompatible (${discovery.missingCapability}: ${discovery.detail}); falling back to direct launch.` : "Latch is unavailable; falling back to direct launch.";
@@ -19984,7 +20053,9 @@ async function launchAgent({
19984
20053
  viewerOpen = openLatchViewer({
19985
20054
  executable: resolvedLatchExecutable,
19986
20055
  providerSessionId: created.providerSession.providerSessionId,
19987
- viewerKind: snapshot?.viewer.kind ?? "iterm"
20056
+ viewerKind: snapshot?.viewer.kind ?? "iterm",
20057
+ openAs: snapshot?.viewer.openAs ?? null,
20058
+ productVersion: latchProductVersion
19988
20059
  });
19989
20060
  }
19990
20061
  return {
@@ -21744,7 +21815,15 @@ var init_runner_identity = __esm({
21744
21815
 
21745
21816
  // src/runner-service.ts
21746
21817
  import { execFile } from "node:child_process";
21747
- import { chmodSync as chmodSync5, existsSync as existsSync26, mkdirSync as mkdirSync14, readFileSync as readFileSync22, rmSync as rmSync6, writeFileSync as writeFileSync13 } from "node:fs";
21818
+ import {
21819
+ chmodSync as chmodSync5,
21820
+ existsSync as existsSync26,
21821
+ mkdirSync as mkdirSync14,
21822
+ readFileSync as readFileSync22,
21823
+ rmSync as rmSync6,
21824
+ statSync as statSync7,
21825
+ writeFileSync as writeFileSync13
21826
+ } from "node:fs";
21748
21827
  import os5 from "node:os";
21749
21828
  import path31 from "node:path";
21750
21829
  import { promisify } from "node:util";
@@ -21752,6 +21831,38 @@ function writeServiceFile(filePath, contents) {
21752
21831
  writeFileSync13(filePath, contents, { mode: 384 });
21753
21832
  chmodSync5(filePath, 384);
21754
21833
  }
21834
+ function resolveRunnerEntryScript(argv = process.argv) {
21835
+ return argv[1] ? path31.resolve(argv[1]) : null;
21836
+ }
21837
+ function readRunnerExecutableFileIdentity(filePath, stat = statSync7) {
21838
+ try {
21839
+ const metadata = stat(filePath);
21840
+ return { dev: metadata.dev, ino: metadata.ino, mtimeMs: metadata.mtimeMs };
21841
+ } catch (error) {
21842
+ return error.code === "ENOENT" ? null : void 0;
21843
+ }
21844
+ }
21845
+ function captureRunnerSupervisorIdentity({
21846
+ execPath = process.execPath,
21847
+ entryScript = resolveRunnerEntryScript(),
21848
+ stat = statSync7
21849
+ } = {}) {
21850
+ return {
21851
+ program: readRunnerExecutableFileIdentity(execPath, stat),
21852
+ entryScript: entryScript ? readRunnerExecutableFileIdentity(entryScript, stat) : void 0
21853
+ };
21854
+ }
21855
+ function executableIdentityChanged(initial, current) {
21856
+ if (current === null) return true;
21857
+ if (!initial || !current) return false;
21858
+ return initial.dev !== current.dev || initial.ino !== current.ino || initial.mtimeMs !== current.mtimeMs;
21859
+ }
21860
+ function shouldRestartRunnerSupervisor({
21861
+ initial,
21862
+ current
21863
+ }) {
21864
+ return executableIdentityChanged(initial.program, current.program) || executableIdentityChanged(initial.entryScript, current.entryScript);
21865
+ }
21755
21866
  function emptyRunnerServiceState() {
21756
21867
  return {
21757
21868
  serviceKind: null,
@@ -21790,41 +21901,10 @@ function patchRunnerServiceState(patch, dataDir = resolveGlobalDataDir()) {
21790
21901
  writeRunnerServiceState(next, dataDir);
21791
21902
  return next;
21792
21903
  }
21793
- function runnerFocusStatePath(dataDir = resolveGlobalDataDir()) {
21794
- return path31.join(dataDir, RUNNER_FOCUS_STATE_FILENAME);
21795
- }
21796
- function readDesktopFocusState(dataDir = resolveGlobalDataDir()) {
21797
- const filePath = runnerFocusStatePath(dataDir);
21798
- if (!existsSync26(filePath)) return { lastFocusedAt: null };
21799
- try {
21800
- const parsed = JSON.parse(readFileSync22(filePath, "utf8"));
21801
- return { lastFocusedAt: parsed.lastFocusedAt ?? null };
21802
- } catch {
21803
- return { lastFocusedAt: null };
21804
- }
21805
- }
21806
- function withinWindow(timestamp, now, windowMs) {
21807
- if (!timestamp) return false;
21808
- const ms = Date.parse(timestamp);
21809
- if (Number.isNaN(ms)) return false;
21810
- return now - ms <= windowMs;
21811
- }
21812
- function selectBasePollIntervalMs({
21813
- lastLaunchedAt,
21814
- lastDesktopFocusAt = null,
21815
- now
21816
- }) {
21817
- const jobActive = withinWindow(lastLaunchedAt, now, IDLE_BACKOFF_MS);
21818
- const focusActive = withinWindow(lastDesktopFocusAt, now, DESKTOP_FOCUS_WINDOW_MS);
21819
- return jobActive || focusActive ? FAST_POLL_INTERVAL_MS : SLOW_POLL_INTERVAL_MS;
21820
- }
21821
21904
  function applyPollJitter(intervalMs, random = Math.random) {
21822
21905
  const spread = intervalMs * 0.1;
21823
21906
  return Math.round(intervalMs + (random() * 2 - 1) * spread);
21824
21907
  }
21825
- function nextRunnerLastError(pollError) {
21826
- return pollError;
21827
- }
21828
21908
  function resolveOverlordAppInvocation({
21829
21909
  platform: platform2 = process.platform,
21830
21910
  homedir: homedir2 = os5.homedir(),
@@ -21860,27 +21940,15 @@ function resolveOvldInvocation(argv = process.argv, execPath = process.execPath)
21860
21940
  }
21861
21941
  return { program: execPath, args: ["runner", "supervise"] };
21862
21942
  }
21863
- function describeServicePublisher({
21864
- execProgram,
21865
- platform: platform2 = process.platform
21866
- }) {
21867
- if (platform2 !== "darwin" || !execProgram) {
21868
- return { publisher: "unknown", needsReinstallForOverlord: false };
21869
- }
21870
- if (execProgram.includes(".app/Contents/MacOS/")) {
21871
- return { publisher: "overlord", needsReinstallForOverlord: false };
21872
- }
21873
- return { publisher: "node", needsReinstallForOverlord: true };
21874
- }
21875
21943
  function buildRunnerServiceEnv({
21876
21944
  backendUrl,
21877
21945
  overlordHome,
21878
21946
  runAsElectronNode
21879
21947
  }) {
21880
21948
  const env = {
21881
- OVERLORD_BACKEND_URL: backendUrl,
21882
21949
  PATH: process.env.PATH?.trim() || "/usr/local/bin:/usr/bin:/bin"
21883
21950
  };
21951
+ if (!isLoopbackBackendUrl(backendUrl)) env.OVERLORD_BACKEND_URL = backendUrl;
21884
21952
  const home = overlordHome?.trim() || process.env.OVLD_HOME?.trim();
21885
21953
  if (home) env.OVLD_HOME = path31.resolve(home);
21886
21954
  const token = process.env.OVERLORD_USER_TOKEN?.trim() || process.env.OVLD_USER_TOKEN?.trim();
@@ -21965,31 +22033,72 @@ async function runCommand(program, args) {
21965
22033
  const { stdout } = await execFileAsync(program, args);
21966
22034
  return { stdout: stdout.toString() };
21967
22035
  }
22036
+ function launchdDomainTarget(uid) {
22037
+ return `gui/${uid}`;
22038
+ }
22039
+ function launchdServiceTarget(uid) {
22040
+ return `${launchdDomainTarget(uid)}/${LAUNCHD_LABEL}`;
22041
+ }
22042
+ function planLaunchdUnloadCommands(opts) {
22043
+ return [{ args: ["bootout", launchdServiceTarget(opts.uid)], tolerateFailure: true }];
22044
+ }
22045
+ function planLaunchdLoadCommands(opts) {
22046
+ const service = launchdServiceTarget(opts.uid);
22047
+ return [
22048
+ { args: ["enable", service], tolerateFailure: true },
22049
+ {
22050
+ args: ["bootstrap", launchdDomainTarget(opts.uid), opts.plistPath],
22051
+ tolerateFailure: true
22052
+ },
22053
+ { args: ["kickstart", service], tolerateFailure: false }
22054
+ ];
22055
+ }
21968
22056
  function resolveServiceManager(platform2 = process.platform) {
21969
22057
  if (platform2 === "darwin") return new LaunchdManager();
21970
22058
  if (platform2 === "linux") return new SystemdUserManager();
21971
22059
  return null;
21972
22060
  }
21973
- 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;
22061
+ var execFileAsync, RUNNER_SERVICE_STATE_FILENAME, LAUNCHD_LABEL, SYSTEMD_UNIT_NAME, FALLBACK_POLL_INTERVAL_MS, LaunchdManager, SystemdUserManager;
21974
22062
  var init_runner_service = __esm({
21975
22063
  "src/runner-service.ts"() {
21976
22064
  "use strict";
21977
22065
  init_config();
21978
22066
  execFileAsync = promisify(execFile);
21979
22067
  RUNNER_SERVICE_STATE_FILENAME = "runner-service.json";
21980
- RUNNER_FOCUS_STATE_FILENAME = "runner-focus.json";
21981
22068
  LAUNCHD_LABEL = "io.overlord.runner";
21982
22069
  SYSTEMD_UNIT_NAME = "overlord-runner";
21983
- FAST_POLL_INTERVAL_MS = 3e3;
21984
- SLOW_POLL_INTERVAL_MS = 5e3;
21985
- IDLE_BACKOFF_MS = 2 * 60 * 60 * 1e3;
21986
- DESKTOP_FOCUS_WINDOW_MS = 30 * 60 * 1e3;
22070
+ FALLBACK_POLL_INTERVAL_MS = 5e3;
21987
22071
  LaunchdManager = class {
21988
22072
  kind = "launchd";
21989
22073
  identifier = LAUNCHD_LABEL;
22074
+ exec;
22075
+ constructor(exec) {
22076
+ this.exec = exec ?? ((args) => runCommand("launchctl", args));
22077
+ }
21990
22078
  unitPath() {
21991
22079
  return path31.join(launchAgentsDir(), `${LAUNCHD_LABEL}.plist`);
21992
22080
  }
22081
+ uid() {
22082
+ return process.getuid?.() ?? "";
22083
+ }
22084
+ async runSteps(steps) {
22085
+ for (const step of steps) {
22086
+ try {
22087
+ await this.exec(step.args);
22088
+ } catch (error) {
22089
+ if (!step.tolerateFailure) throw error;
22090
+ }
22091
+ }
22092
+ }
22093
+ /** True when launchd still has the label registered, whatever its run state. */
22094
+ async isLoaded() {
22095
+ try {
22096
+ await this.exec(["list", LAUNCHD_LABEL]);
22097
+ return true;
22098
+ } catch {
22099
+ return false;
22100
+ }
22101
+ }
21993
22102
  render(opts) {
21994
22103
  return renderLaunchdPlist({
21995
22104
  label: LAUNCHD_LABEL,
@@ -22001,41 +22110,33 @@ var init_runner_service = __esm({
22001
22110
  async install(opts) {
22002
22111
  mkdirSync14(launchAgentsDir(), { recursive: true });
22003
22112
  mkdirSync14(path31.join(resolveGlobalDataDir(), "logs"), { recursive: true });
22113
+ await this.runSteps(planLaunchdUnloadCommands({ uid: this.uid() }));
22004
22114
  writeServiceFile(this.unitPath(), this.render(opts));
22005
22115
  if (opts.autoStart) await this.start();
22006
22116
  }
22007
22117
  async start() {
22008
- try {
22009
- await runCommand("launchctl", ["load", "-w", this.unitPath()]);
22010
- } catch {
22011
- await runCommand("launchctl", [
22012
- "kickstart",
22013
- "-k",
22014
- `gui/${process.getuid?.() ?? ""}/${LAUNCHD_LABEL}`
22015
- ]);
22016
- }
22118
+ await this.runSteps(planLaunchdLoadCommands({ uid: this.uid(), plistPath: this.unitPath() }));
22017
22119
  }
22018
22120
  async stop() {
22019
- await runCommand("launchctl", ["unload", "-w", this.unitPath()]);
22121
+ await this.runSteps(planLaunchdUnloadCommands({ uid: this.uid() }));
22020
22122
  }
22021
22123
  async restart() {
22022
- try {
22023
- await this.stop();
22024
- } catch {
22025
- }
22124
+ await this.stop();
22026
22125
  await this.start();
22027
22126
  }
22028
22127
  async uninstall() {
22029
- try {
22030
- await this.stop();
22031
- } catch {
22128
+ await this.runSteps(planLaunchdUnloadCommands({ uid: this.uid() }));
22129
+ if (await this.isLoaded()) {
22130
+ throw new Error(
22131
+ `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\`.`
22132
+ );
22032
22133
  }
22033
22134
  if (existsSync26(this.unitPath())) rmSync6(this.unitPath());
22034
22135
  }
22035
22136
  async status() {
22036
22137
  const installed = existsSync26(this.unitPath());
22037
22138
  try {
22038
- const { stdout } = await runCommand("launchctl", ["list"]);
22139
+ const { stdout } = await this.exec(["list"]);
22039
22140
  const line = stdout.split("\n").find((row) => row.includes(LAUNCHD_LABEL));
22040
22141
  if (!line) return { installed, running: installed ? "stopped" : "unknown" };
22041
22142
  const pid = line.trim().split(/\s+/)[0];
@@ -22212,7 +22313,8 @@ var commands_exports = {};
22212
22313
  __export(commands_exports, {
22213
22314
  resolveProtocolFileInputs: () => resolveProtocolFileInputs,
22214
22315
  runManagementCommand: () => runManagementCommand,
22215
- runProtocolCommand: () => runProtocolCommand
22316
+ runProtocolCommand: () => runProtocolCommand,
22317
+ runRunnerOnce: () => runRunnerOnce
22216
22318
  });
22217
22319
  import { existsSync as existsSync28, readFileSync as readFileSync24 } from "node:fs";
22218
22320
  import os6 from "node:os";
@@ -23375,6 +23477,165 @@ function isNoExecutionTargetRegisteredError(error) {
23375
23477
  const message = error instanceof Error ? error.message : String(error);
23376
23478
  return message.includes("(no_execution_target_registered)");
23377
23479
  }
23480
+ async function runRunnerOnce({
23481
+ runtime,
23482
+ parsed,
23483
+ json
23484
+ }) {
23485
+ let claim;
23486
+ try {
23487
+ claim = await runtime.backend.post({
23488
+ path: "/api/runner/claim",
23489
+ body: {
23490
+ projectId: flagValue(parsed.flags, "--project-id"),
23491
+ ...clientDeviceIdentity(),
23492
+ // Contract v40: the claim poll doubles as this runner instance's
23493
+ // heartbeat, so local target liveness reflects a runner that can
23494
+ // actually take work rather than any CLI traffic from the machine.
23495
+ ...runnerRegistrationPayload()
23496
+ }
23497
+ });
23498
+ } catch (error) {
23499
+ if (isNoExecutionTargetRegisteredError(error)) {
23500
+ throw new CliError({
23501
+ message: `${error instanceof Error ? error.message : String(error)}
23502
+ The runner will not register a target for you \u2014 declare this machine first, then start it again.`
23503
+ });
23504
+ }
23505
+ throw error;
23506
+ }
23507
+ const request = asRecord5(claim).request;
23508
+ const longPoll = asRecord5(claim).longPoll === true;
23509
+ if (!request) return { launched: false, longPoll };
23510
+ const requestRecord = asRecord5(request);
23511
+ const requestId = String(requestRecord.id);
23512
+ const projectId = String(requestRecord.projectId ?? "");
23513
+ const executionTargetId = typeof requestRecord.executionTargetId === "string" ? requestRecord.executionTargetId.trim() : "";
23514
+ if (projectId && executionTargetId) {
23515
+ try {
23516
+ await reportRunnerResourceObservations({
23517
+ backend: runtime.backend,
23518
+ projectId,
23519
+ executionTargetId
23520
+ });
23521
+ } catch {
23522
+ }
23523
+ }
23524
+ try {
23525
+ await runtime.backend.post({ path: `/api/runner/requests/${requestId}/launching` });
23526
+ const mutation = parseMutationFromMetadata(requestRecord.metadata);
23527
+ if (mutation) {
23528
+ const mutationResult = await executeLocalTargetMutation({ mutation });
23529
+ if (!mutationResult.ok) {
23530
+ await runtime.backend.post({
23531
+ path: `/api/runner/requests/${requestId}/failed`,
23532
+ body: { error: mutationResult.message }
23533
+ });
23534
+ throw new CliError({ message: mutationResult.message });
23535
+ }
23536
+ await runtime.backend.post({
23537
+ path: `/api/runner/requests/${requestId}/completed`,
23538
+ body: { mutationResult }
23539
+ });
23540
+ if (json) printJson({ request, mutationResult });
23541
+ else console.log(`Completed ${mutation.kind} for ${requestRecord.missionId}`);
23542
+ return { launched: true, longPoll };
23543
+ }
23544
+ const requestedAgent = typeof requestRecord.requestedAgent === "string" ? requestRecord.requestedAgent.trim() : "";
23545
+ if (!requestedAgent) {
23546
+ throw new CliError({
23547
+ message: `Execution request ${requestId} has no agent; the objective must specify one before it can be launched.`
23548
+ });
23549
+ }
23550
+ const launchConfig = asRecord5(requestRecord.launchConfig);
23551
+ const terminal = await resolveTerminalLaunchSettings({ runtime, flags: parsed.flags });
23552
+ const missionId = String(requestRecord.missionId);
23553
+ const dryRun = flagBoolean(parsed.flags, "--dry-run");
23554
+ const prepared = await prepareMissionBranch({
23555
+ runtime,
23556
+ options: {
23557
+ missionId,
23558
+ workingDirectory: String(requestRecord.workingDirectory ?? process.cwd()),
23559
+ objectiveId: String(requestRecord.objectiveId ?? ""),
23560
+ workspaceAutomationEnabled: await readWorktreeBranchAutomationEnabled(runtime),
23561
+ dryRun,
23562
+ overrideBranch: flagValue(parsed.flags, "--branch"),
23563
+ noWorktree: flagBoolean(parsed.flags, "--no-worktree")
23564
+ }
23565
+ });
23566
+ await recordBranchPrepared({
23567
+ runtime,
23568
+ missionId,
23569
+ requestId,
23570
+ branchAutomation: prepared.branchAutomation
23571
+ });
23572
+ const launchSession = launchSessionSnapshotFromMetadata(asRecord5(requestRecord.metadata));
23573
+ const result = await launchAgent({
23574
+ runtime,
23575
+ options: {
23576
+ agent: requestedAgent,
23577
+ missionId,
23578
+ workingDirectory: prepared.workingDirectory,
23579
+ model: typeof requestRecord.requestedModel === "string" ? requestRecord.requestedModel : void 0,
23580
+ thinking: typeof requestRecord.requestedReasoningEffort === "string" ? requestRecord.requestedReasoningEffort : void 0,
23581
+ flags: normalizeAgentLaunchFlags(launchConfig.flags),
23582
+ preCommand: typeof launchConfig.preCommand === "string" ? launchConfig.preCommand : void 0,
23583
+ preLaunchCommands: Array.isArray(requestRecord.preLaunchCommands) ? requestRecord.preLaunchCommands.filter(
23584
+ (value) => typeof value === "string"
23585
+ ) : void 0,
23586
+ launchEnvVars: parseLaunchEnvVarsValue(requestRecord.launchEnvVars),
23587
+ executionRequestId: requestId,
23588
+ executionTargetId: executionTargetId || void 0,
23589
+ launchSession,
23590
+ ...terminal,
23591
+ dryRun
23592
+ }
23593
+ });
23594
+ if (result.providerFallbackWarning) {
23595
+ console.error(`[overlord] ${result.providerFallbackWarning}`);
23596
+ }
23597
+ if (result.viewerOpen && result.viewerOpen.ok === false) {
23598
+ console.error(`[overlord] ${result.viewerOpen.warning}`);
23599
+ }
23600
+ if (result.status && result.status !== 0) {
23601
+ throw new CliError({ message: `Launch command exited with status ${result.status}` });
23602
+ }
23603
+ await runtime.backend.post({
23604
+ path: `/api/runner/requests/${requestId}/launched`,
23605
+ body: result.providerSession ? { providerSession: result.providerSession } : {}
23606
+ });
23607
+ if (result.providerSession && !dryRun) {
23608
+ void postLatchHarnessEventsAfterLaunch({
23609
+ backend: runtime.backend,
23610
+ requestId,
23611
+ providerSessionId: result.providerSession.providerSessionId,
23612
+ executable: launchSession?.executionProvider.kind === "latch" ? launchSession.executionProvider.executable : void 0
23613
+ });
23614
+ }
23615
+ if (json || dryRun) {
23616
+ printJson({
23617
+ request,
23618
+ plan: result.plan,
23619
+ status: result.status,
23620
+ providerSession: result.providerSession ?? null,
23621
+ viewerOpen: result.viewerOpen ?? null,
23622
+ providerFallbackWarning: result.providerFallbackWarning ?? null
23623
+ });
23624
+ } else {
23625
+ console.log(`Launched ${requestedAgent} for ${requestRecord.missionId}`);
23626
+ }
23627
+ return { launched: true, longPoll };
23628
+ } catch (error) {
23629
+ try {
23630
+ await runtime.backend.post({
23631
+ path: `/api/runner/requests/${requestId}/failed`,
23632
+ body: { error: error instanceof Error ? error.message : String(error) }
23633
+ });
23634
+ } catch {
23635
+ }
23636
+ throw error;
23637
+ }
23638
+ }
23378
23639
  async function runRunnerCommand({
23379
23640
  runtime,
23380
23641
  parsed,
@@ -23408,161 +23669,7 @@ async function runRunnerCommand({
23408
23669
  message: "Usage: ovld runner once|start|supervise|status|clear <objective_id>|clear-all|service <install|start|stop|restart|status|uninstall>"
23409
23670
  });
23410
23671
  }
23411
- const runOnce = async () => {
23412
- let claim;
23413
- try {
23414
- claim = await runtime.backend.post({
23415
- path: "/api/runner/claim",
23416
- body: {
23417
- projectId: flagValue(parsed.flags, "--project-id"),
23418
- ...clientDeviceIdentity(),
23419
- // Contract v40: the claim poll doubles as this runner instance's
23420
- // heartbeat, so local target liveness reflects a runner that can
23421
- // actually take work rather than any CLI traffic from the machine.
23422
- ...runnerRegistrationPayload()
23423
- }
23424
- });
23425
- } catch (error) {
23426
- if (isNoExecutionTargetRegisteredError(error)) {
23427
- throw new CliError({
23428
- message: `${error instanceof Error ? error.message : String(error)}
23429
- The runner will not register a target for you \u2014 declare this machine first, then start it again.`
23430
- });
23431
- }
23432
- throw error;
23433
- }
23434
- const request = asRecord5(claim).request;
23435
- const longPoll = asRecord5(claim).longPoll === true;
23436
- if (!request) return { launched: false, longPoll };
23437
- const requestRecord = asRecord5(request);
23438
- const requestId = String(requestRecord.id);
23439
- const projectId = String(requestRecord.projectId ?? "");
23440
- const executionTargetId = typeof requestRecord.executionTargetId === "string" ? requestRecord.executionTargetId.trim() : "";
23441
- if (projectId && executionTargetId) {
23442
- try {
23443
- await reportRunnerResourceObservations({
23444
- backend: runtime.backend,
23445
- projectId,
23446
- executionTargetId
23447
- });
23448
- } catch {
23449
- }
23450
- }
23451
- try {
23452
- await runtime.backend.post({ path: `/api/runner/requests/${requestId}/launching` });
23453
- const mutation = parseMutationFromMetadata(requestRecord.metadata);
23454
- if (mutation) {
23455
- const mutationResult = await executeLocalTargetMutation({ mutation });
23456
- if (!mutationResult.ok) {
23457
- await runtime.backend.post({
23458
- path: `/api/runner/requests/${requestId}/failed`,
23459
- body: { error: mutationResult.message }
23460
- });
23461
- throw new CliError({ message: mutationResult.message });
23462
- }
23463
- await runtime.backend.post({
23464
- path: `/api/runner/requests/${requestId}/completed`,
23465
- body: { mutationResult }
23466
- });
23467
- if (json) printJson({ request, mutationResult });
23468
- else console.log(`Completed ${mutation.kind} for ${requestRecord.missionId}`);
23469
- return { launched: true, longPoll };
23470
- }
23471
- const requestedAgent = typeof requestRecord.requestedAgent === "string" ? requestRecord.requestedAgent.trim() : "";
23472
- if (!requestedAgent) {
23473
- throw new CliError({
23474
- message: `Execution request ${requestId} has no agent; the objective must specify one before it can be launched.`
23475
- });
23476
- }
23477
- const launchConfig = asRecord5(requestRecord.launchConfig);
23478
- const terminal = await resolveTerminalLaunchSettings({ runtime, flags: parsed.flags });
23479
- const missionId = String(requestRecord.missionId);
23480
- const dryRun = flagBoolean(parsed.flags, "--dry-run");
23481
- const prepared = await prepareMissionBranch({
23482
- runtime,
23483
- options: {
23484
- missionId,
23485
- workingDirectory: String(requestRecord.workingDirectory ?? process.cwd()),
23486
- objectiveId: String(requestRecord.objectiveId ?? ""),
23487
- workspaceAutomationEnabled: await readWorktreeBranchAutomationEnabled(runtime),
23488
- dryRun,
23489
- overrideBranch: flagValue(parsed.flags, "--branch"),
23490
- noWorktree: flagBoolean(parsed.flags, "--no-worktree")
23491
- }
23492
- });
23493
- await recordBranchPrepared({
23494
- runtime,
23495
- missionId,
23496
- requestId,
23497
- branchAutomation: prepared.branchAutomation
23498
- });
23499
- const launchSession = launchSessionSnapshotFromMetadata(asRecord5(requestRecord.metadata));
23500
- const result = await launchAgent({
23501
- runtime,
23502
- options: {
23503
- agent: requestedAgent,
23504
- missionId,
23505
- workingDirectory: prepared.workingDirectory,
23506
- model: typeof requestRecord.requestedModel === "string" ? requestRecord.requestedModel : void 0,
23507
- thinking: typeof requestRecord.requestedReasoningEffort === "string" ? requestRecord.requestedReasoningEffort : void 0,
23508
- flags: normalizeAgentLaunchFlags(launchConfig.flags),
23509
- preCommand: typeof launchConfig.preCommand === "string" ? launchConfig.preCommand : void 0,
23510
- preLaunchCommands: Array.isArray(requestRecord.preLaunchCommands) ? requestRecord.preLaunchCommands.filter(
23511
- (value) => typeof value === "string"
23512
- ) : void 0,
23513
- launchEnvVars: parseLaunchEnvVarsValue(requestRecord.launchEnvVars),
23514
- executionRequestId: requestId,
23515
- executionTargetId: executionTargetId || void 0,
23516
- launchSession,
23517
- ...terminal,
23518
- dryRun
23519
- }
23520
- });
23521
- if (result.providerFallbackWarning) {
23522
- console.error(`[overlord] ${result.providerFallbackWarning}`);
23523
- }
23524
- if (result.viewerOpen && result.viewerOpen.ok === false) {
23525
- console.error(`[overlord] ${result.viewerOpen.warning}`);
23526
- }
23527
- if (result.status && result.status !== 0) {
23528
- throw new CliError({ message: `Launch command exited with status ${result.status}` });
23529
- }
23530
- await runtime.backend.post({
23531
- path: `/api/runner/requests/${requestId}/launched`,
23532
- body: result.providerSession ? { providerSession: result.providerSession } : {}
23533
- });
23534
- if (result.providerSession && !dryRun) {
23535
- void postLatchHarnessEventsAfterLaunch({
23536
- backend: runtime.backend,
23537
- requestId,
23538
- providerSessionId: result.providerSession.providerSessionId,
23539
- executable: launchSession?.executionProvider.kind === "latch" ? launchSession.executionProvider.executable : void 0
23540
- });
23541
- }
23542
- if (json || dryRun) {
23543
- printJson({
23544
- request,
23545
- plan: result.plan,
23546
- status: result.status,
23547
- providerSession: result.providerSession ?? null,
23548
- viewerOpen: result.viewerOpen ?? null,
23549
- providerFallbackWarning: result.providerFallbackWarning ?? null
23550
- });
23551
- } else {
23552
- console.log(`Launched ${requestedAgent} for ${requestRecord.missionId}`);
23553
- }
23554
- return { launched: true, longPoll };
23555
- } catch (error) {
23556
- try {
23557
- await runtime.backend.post({
23558
- path: `/api/runner/requests/${requestId}/failed`,
23559
- body: { error: error instanceof Error ? error.message : String(error) }
23560
- });
23561
- } catch {
23562
- }
23563
- throw error;
23564
- }
23565
- };
23672
+ const runOnce = () => runRunnerOnce({ runtime, parsed, json });
23566
23673
  if (sub === "once") {
23567
23674
  const result = await runOnce();
23568
23675
  if (!result.launched) {
@@ -23586,8 +23693,18 @@ async function runRunnerSupervisor({
23586
23693
  runOnce,
23587
23694
  json
23588
23695
  }) {
23589
- if (!json) console.log("Runner supervisor started. Adaptive polling for execution requests.");
23696
+ if (!json) console.log("Runner supervisor started. Polling for execution requests.");
23697
+ const initialExecutableIdentity = captureRunnerSupervisorIdentity();
23590
23698
  for (; ; ) {
23699
+ if (shouldRestartRunnerSupervisor({
23700
+ initial: initialExecutableIdentity,
23701
+ current: captureRunnerSupervisorIdentity()
23702
+ })) {
23703
+ console.error(
23704
+ "Runner supervisor executable was replaced or removed; exiting cleanly so the service manager restarts the installed build."
23705
+ );
23706
+ process.exit(0);
23707
+ }
23591
23708
  const now = /* @__PURE__ */ new Date();
23592
23709
  let result = { launched: false, longPoll: false };
23593
23710
  let lastError = null;
@@ -23596,25 +23713,22 @@ async function runRunnerSupervisor({
23596
23713
  } catch (error) {
23597
23714
  if (isNoExecutionTargetRegisteredError(error)) throw error;
23598
23715
  lastError = error instanceof Error ? error.message : String(error);
23716
+ console.error(`[overlord] Runner poll failed: ${lastError}`);
23599
23717
  }
23600
23718
  const state = readRunnerServiceState();
23601
- const focus = readDesktopFocusState();
23602
- const base = selectBasePollIntervalMs({
23603
- lastLaunchedAt: result.launched ? now.toISOString() : state.lastLaunchedAt,
23604
- lastDesktopFocusAt: focus.lastFocusedAt,
23605
- now: now.getTime()
23606
- });
23607
23719
  patchRunnerServiceState({
23608
23720
  lastHeartbeatAt: now.toISOString(),
23609
23721
  lastClaimedAt: result.launched ? now.toISOString() : state.lastClaimedAt,
23610
23722
  lastLaunchedAt: result.launched ? now.toISOString() : state.lastLaunchedAt,
23611
- // Reflect the latest poll's outcome so a resolved failure (e.g. an auth
23612
- // error from before the user logged in) clears instead of sticking.
23613
- lastError: nextRunnerLastError(lastError),
23614
- currentPollIntervalMs: base
23723
+ // Reflect the latest poll's outcome rather than falling back to the stored
23724
+ // value. A successful poll must clear a resolved failure (for example, an
23725
+ // auth error from before login) instead of leaving the desktop status box
23726
+ // stuck on "Runner error"; persistent failures recur on every poll.
23727
+ lastError,
23728
+ currentPollIntervalMs: FALLBACK_POLL_INTERVAL_MS
23615
23729
  });
23616
23730
  if (!result.longPoll) {
23617
- await new Promise((resolve) => setTimeout(resolve, applyPollJitter(base)));
23731
+ await new Promise((resolve) => setTimeout(resolve, applyPollJitter(FALLBACK_POLL_INTERVAL_MS)));
23618
23732
  }
23619
23733
  }
23620
23734
  }
@@ -23632,10 +23746,8 @@ async function runRunnerServiceCommand({
23632
23746
  if (action === "status") {
23633
23747
  const runState = await manager.status();
23634
23748
  const state = readRunnerServiceState();
23635
- const { publisher, needsReinstallForOverlord } = describeServicePublisher({
23636
- execProgram: state.execProgram
23637
- });
23638
- 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;
23749
+ const publisher = process.platform !== "darwin" || !state.execProgram ? "unknown" : state.execProgram.includes(".app/Contents/MacOS/") ? "overlord" : "node";
23750
+ 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;
23639
23751
  const payload = {
23640
23752
  supported: true,
23641
23753
  kind: manager.kind,