File size: 2,340 Bytes
f778c12 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | import { assertNoSystemLaunchDaemonOwnership } from "../../daemon/launchd-system.js";
// macOS LaunchAgent recovery helper for daemon lifecycle commands.
import {
formatLaunchAgentGuiSessionError,
launchAgentPlistExists,
repairLaunchAgentBootstrap,
resolveLaunchAgentLabel,
} from "../../daemon/launchd.js";
const LAUNCH_AGENT_RECOVERY_MESSAGE =
"Gateway LaunchAgent was installed but not loaded; re-bootstrapped launchd service.";
type LaunchAgentRecoveryAction = "started" | "restarted";
type LaunchAgentRecoveryResult<TResult extends LaunchAgentRecoveryAction> = {
result: TResult;
loaded: true;
message: string;
};
/** Re-bootstrap an installed but unloaded LaunchAgent after a daemon start/restart command. */
export async function recoverInstalledLaunchAgent<
TResult extends LaunchAgentRecoveryAction,
>(params: {
result: TResult;
env?: Record<string, string | undefined>;
}): Promise<LaunchAgentRecoveryResult<TResult> | null> {
if (process.platform !== "darwin") {
return null;
}
const env = params.env ?? (process.env as Record<string, string | undefined>);
// Check host-wide ownership even when no user plist exists. Otherwise start
// and restart would report "not installed" for a gateway already supervised
// by a same-label system LaunchDaemon.
await assertNoSystemLaunchDaemonOwnership(resolveLaunchAgentLabel(env));
const plistExists = await launchAgentPlistExists(env).catch(() => false);
if (!plistExists) {
return null;
}
const repaired = await repairLaunchAgentBootstrap({ env }).catch(() => ({
ok: false as const,
status: "bootstrap-failed" as const,
}));
if (!repaired.ok) {
if (
repaired.status === "system-launchdaemon-conflict" ||
repaired.status === "system-launchdaemon-unverifiable"
) {
throw new Error(repaired.detail);
}
if (repaired.status === "gui-session-unavailable") {
const actionHint =
params.result === "started" ? "openclaw gateway start" : "openclaw gateway restart";
throw new Error(
formatLaunchAgentGuiSessionError({
detail: repaired.detail,
domain: repaired.domain,
actionHint,
}),
);
}
return null;
}
return {
result: params.result,
loaded: true,
message: LAUNCH_AGENT_RECOVERY_MESSAGE,
};
}
|