fix(daemon): enable LaunchAgent before bootstrap on restart
restartLaunchAgent was missing the launchctl enable call that installLaunchAgent already performs. launchd can persist a "disabled" state after bootout, causing bootstrap to silently fail and leaving the gateway unloaded until a manual reinstall. Fixes #39211 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
41eef15cdc
commit
30990d73b2
@ -241,7 +241,7 @@ describe("launchd install", () => {
|
|||||||
expect(plist).toContain(`<integer>${LAUNCH_AGENT_THROTTLE_INTERVAL_SECONDS}</integer>`);
|
expect(plist).toContain(`<integer>${LAUNCH_AGENT_THROTTLE_INTERVAL_SECONDS}</integer>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("restarts LaunchAgent with bootout-bootstrap-kickstart order", async () => {
|
it("restarts LaunchAgent with bootout-enable-bootstrap-kickstart order", async () => {
|
||||||
const env = createDefaultLaunchdEnv();
|
const env = createDefaultLaunchdEnv();
|
||||||
await restartLaunchAgent({
|
await restartLaunchAgent({
|
||||||
env,
|
env,
|
||||||
@ -251,20 +251,26 @@ describe("launchd install", () => {
|
|||||||
const domain = typeof process.getuid === "function" ? `gui/${process.getuid()}` : "gui/501";
|
const domain = typeof process.getuid === "function" ? `gui/${process.getuid()}` : "gui/501";
|
||||||
const label = "ai.openclaw.gateway";
|
const label = "ai.openclaw.gateway";
|
||||||
const plistPath = resolveLaunchAgentPlistPath(env);
|
const plistPath = resolveLaunchAgentPlistPath(env);
|
||||||
|
const serviceId = `${domain}/${label}`;
|
||||||
const bootoutIndex = state.launchctlCalls.findIndex(
|
const bootoutIndex = state.launchctlCalls.findIndex(
|
||||||
(c) => c[0] === "bootout" && c[1] === `${domain}/${label}`,
|
(c) => c[0] === "bootout" && c[1] === serviceId,
|
||||||
|
);
|
||||||
|
const enableIndex = state.launchctlCalls.findIndex(
|
||||||
|
(c) => c[0] === "enable" && c[1] === serviceId,
|
||||||
);
|
);
|
||||||
const bootstrapIndex = state.launchctlCalls.findIndex(
|
const bootstrapIndex = state.launchctlCalls.findIndex(
|
||||||
(c) => c[0] === "bootstrap" && c[1] === domain && c[2] === plistPath,
|
(c) => c[0] === "bootstrap" && c[1] === domain && c[2] === plistPath,
|
||||||
);
|
);
|
||||||
const kickstartIndex = state.launchctlCalls.findIndex(
|
const kickstartIndex = state.launchctlCalls.findIndex(
|
||||||
(c) => c[0] === "kickstart" && c[1] === "-k" && c[2] === `${domain}/${label}`,
|
(c) => c[0] === "kickstart" && c[1] === "-k" && c[2] === serviceId,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(bootoutIndex).toBeGreaterThanOrEqual(0);
|
expect(bootoutIndex).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(enableIndex).toBeGreaterThanOrEqual(0);
|
||||||
expect(bootstrapIndex).toBeGreaterThanOrEqual(0);
|
expect(bootstrapIndex).toBeGreaterThanOrEqual(0);
|
||||||
expect(kickstartIndex).toBeGreaterThanOrEqual(0);
|
expect(kickstartIndex).toBeGreaterThanOrEqual(0);
|
||||||
expect(bootoutIndex).toBeLessThan(bootstrapIndex);
|
expect(bootoutIndex).toBeLessThan(enableIndex);
|
||||||
|
expect(enableIndex).toBeLessThan(bootstrapIndex);
|
||||||
expect(bootstrapIndex).toBeLessThan(kickstartIndex);
|
expect(bootstrapIndex).toBeLessThan(kickstartIndex);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -466,6 +466,9 @@ export async function restartLaunchAgent({
|
|||||||
await waitForPidExit(previousPid);
|
await waitForPidExit(previousPid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// launchd can persist "disabled" state after bootout; clear it before bootstrap
|
||||||
|
// (matches the same guard in installLaunchAgent).
|
||||||
|
await execLaunchctl(["enable", `${domain}/${label}`]);
|
||||||
const boot = await execLaunchctl(["bootstrap", domain, plistPath]);
|
const boot = await execLaunchctl(["bootstrap", domain, plistPath]);
|
||||||
if (boot.code !== 0) {
|
if (boot.code !== 0) {
|
||||||
const detail = (boot.stderr || boot.stdout).trim();
|
const detail = (boot.stderr || boot.stdout).trim();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user