agent-relay-orchestrator 0.10.27 → 0.11.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-orchestrator",
3
- "version": "0.10.27",
3
+ "version": "0.11.1",
4
4
  "description": "Agent Relay orchestrator — manages agent lifecycle across hosts",
5
5
  "type": "module",
6
6
  "bin": {
package/src/relay.ts CHANGED
@@ -25,7 +25,7 @@ export interface ManagedAgentReport {
25
25
  workspaceMode?: WorkspaceMode;
26
26
  workspace?: WorkspaceMetadata;
27
27
  sessionName?: string;
28
- supervisor?: "process" | "systemd" | "unknown";
28
+ supervisor?: "process" | "systemd" | "launchd" | "unknown";
29
29
  systemdUnit?: string;
30
30
  terminalSession?: string;
31
31
  terminalAvailable?: boolean;
@@ -52,7 +52,7 @@ export interface ManagedSessionExitDiagnostics {
52
52
  policyName?: string;
53
53
  spawnRequestId?: string;
54
54
  automationRunId?: string;
55
- supervisor: "process" | "systemd" | "unknown";
55
+ supervisor: "process" | "systemd" | "launchd" | "unknown";
56
56
  systemdUnit?: string;
57
57
  terminalSession?: string;
58
58
  terminalAvailable?: boolean;
@@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
2
2
  import { fileURLToPath } from "node:url";
3
3
 
4
4
  export interface SelfSupervision {
5
- supervisor: "process" | "systemd" | "unknown";
5
+ supervisor: "process" | "systemd" | "launchd" | "unknown";
6
6
  selfUnit?: string;
7
7
  runtimePrefix?: string;
8
8
  }
@@ -11,7 +11,7 @@ let cached: SelfSupervision | undefined;
11
11
 
12
12
  /**
13
13
  * Detect how this orchestrator process is supervised so the relay can target a
14
- * remote self-upgrade at the correct systemd unit and install prefix. Result is
14
+ * remote self-upgrade at the correct unit/label and install prefix. Result is
15
15
  * stable for the process lifetime, so it is computed once and cached.
16
16
  */
17
17
  export function detectSelfSupervision(moduleUrl: string = import.meta.url): SelfSupervision {
@@ -21,6 +21,12 @@ export function detectSelfSupervision(moduleUrl: string = import.meta.url): Self
21
21
  if (unit) {
22
22
  cached.supervisor = "systemd";
23
23
  cached.selfUnit = unit;
24
+ } else if (process.platform === "darwin") {
25
+ const label = detectLaunchdLabel();
26
+ if (label) {
27
+ cached.supervisor = "launchd";
28
+ cached.selfUnit = label;
29
+ }
24
30
  }
25
31
  return cached;
26
32
  }
@@ -64,6 +70,29 @@ function detectSystemdUnit(): string | undefined {
64
70
  }
65
71
  }
66
72
 
73
+ /**
74
+ * Parse `launchctl list` output to find the label for a given PID. Output format
75
+ * is tab-separated: PID\tStatus\tLabel (PID is "-" when not running).
76
+ */
77
+ export function parseLaunchdLabelForPid(output: string, pid: number): string | undefined {
78
+ const target = String(pid);
79
+ for (const line of output.split("\n")) {
80
+ const parts = line.split("\t");
81
+ if (parts[0] === target && parts[2]) return parts[2];
82
+ }
83
+ return undefined;
84
+ }
85
+
86
+ function detectLaunchdLabel(): string | undefined {
87
+ try {
88
+ const result = Bun.spawnSync({ cmd: ["launchctl", "list"], stdout: "pipe", stderr: "ignore" });
89
+ if (result.exitCode !== 0) return undefined;
90
+ return parseLaunchdLabelForPid(new TextDecoder().decode(result.stdout), process.pid);
91
+ } catch {
92
+ return undefined;
93
+ }
94
+ }
95
+
67
96
  /**
68
97
  * The install prefix is the directory above `node_modules` when the orchestrator
69
98
  * runs from an installed package (e.g. ~/.agent-relay/runtime). Undefined when
@@ -59,8 +59,8 @@ export function planSelfUpgrade(
59
59
  }
60
60
  const providers = normalizeProviders(params.providers);
61
61
 
62
- if (supervision.supervisor !== "systemd" || !supervision.selfUnit) {
63
- throw new Error("orchestrator is not under systemd --user; remote self-upgrade requires a systemd unit (P1)");
62
+ if ((supervision.supervisor !== "systemd" && supervision.supervisor !== "launchd") || !supervision.selfUnit) {
63
+ throw new Error("orchestrator is not under systemd or launchd; remote self-upgrade requires a managed service");
64
64
  }
65
65
  const unit = supervision.selfUnit;
66
66
  const binary = resolveBinary(supervision.runtimePrefix);
@@ -74,13 +74,24 @@ export function planSelfUpgrade(
74
74
  ];
75
75
  if (supervision.runtimePrefix) installCmd.push("--runtime-prefix", supervision.runtimePrefix);
76
76
 
77
- // Decouple the restart from this orchestrator's own cgroup: restarting our unit
78
- // SIGTERMs us, and a child in our cgroup would be killed mid-restart. systemd-run
79
- // schedules it as an independent transient unit that survives our teardown.
80
- const restartDetached = runner.commandExists("systemd-run");
81
- const restartCmd = restartDetached
82
- ? ["systemd-run", "--user", "--collect", "--description", "agent-relay orchestrator self-upgrade restart", "systemctl", "--user", "restart", unit]
83
- : ["setsid", "systemctl", "--user", "restart", unit];
77
+ let restartDetached: boolean;
78
+ let restartCmd: string[];
79
+
80
+ if (supervision.supervisor === "launchd") {
81
+ const uid = process.getuid?.() ?? 501;
82
+ // Shell-background the kickstart so it survives our process teardown when
83
+ // launchd kills us. The shell exits immediately; launchd handles the restart.
84
+ restartCmd = ["/bin/sh", "-c", `launchctl kickstart -kp gui/${uid}/${unit} &`];
85
+ restartDetached = true;
86
+ } else {
87
+ // Decouple the restart from this orchestrator's own cgroup: restarting our unit
88
+ // SIGTERMs us, and a child in our cgroup would be killed mid-restart. systemd-run
89
+ // schedules it as an independent transient unit that survives our teardown.
90
+ restartDetached = runner.commandExists("systemd-run");
91
+ restartCmd = restartDetached
92
+ ? ["systemd-run", "--user", "--collect", "--description", "agent-relay orchestrator self-upgrade restart", "systemctl", "--user", "restart", unit]
93
+ : ["setsid", "systemctl", "--user", "restart", unit];
94
+ }
84
95
 
85
96
  return { targetVersion, providers, unit, runtimePrefix: supervision.runtimePrefix, binary, installCmd, restartCmd, restartDetached };
86
97
  }