mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-01 02:30:23 +00:00
refactor: extract daemon launchd recovery helper
This commit is contained in:
@@ -247,7 +247,7 @@ describe("launchd bootstrap repair", () => {
|
||||
OPENCLAW_PROFILE: "default",
|
||||
};
|
||||
const repair = await repairLaunchAgentBootstrap({ env });
|
||||
expect(repair.ok).toBe(true);
|
||||
expect(repair).toEqual({ ok: true, status: "repaired" });
|
||||
|
||||
const { serviceId, bootstrapIndex } = expectLaunchctlEnableBootstrapOrder(env);
|
||||
const kickstartIndex = state.launchctlCalls.findIndex(
|
||||
@@ -268,7 +268,7 @@ describe("launchd bootstrap repair", () => {
|
||||
|
||||
const repair = await repairLaunchAgentBootstrap({ env });
|
||||
|
||||
expect(repair.ok).toBe(true);
|
||||
expect(repair).toEqual({ ok: true, status: "already-loaded" });
|
||||
expect(state.launchctlCalls.filter((call) => call[0] === "kickstart")).toHaveLength(1);
|
||||
});
|
||||
|
||||
@@ -282,7 +282,7 @@ describe("launchd bootstrap repair", () => {
|
||||
|
||||
const repair = await repairLaunchAgentBootstrap({ env });
|
||||
|
||||
expect(repair.ok).toBe(true);
|
||||
expect(repair).toEqual({ ok: true, status: "already-loaded" });
|
||||
expect(state.launchctlCalls.filter((call) => call[0] === "kickstart")).toHaveLength(1);
|
||||
});
|
||||
|
||||
@@ -295,10 +295,30 @@ describe("launchd bootstrap repair", () => {
|
||||
|
||||
const repair = await repairLaunchAgentBootstrap({ env });
|
||||
|
||||
expect(repair.ok).toBe(false);
|
||||
expect(repair.detail).toContain("Could not find specified service");
|
||||
expect(repair).toMatchObject({
|
||||
ok: false,
|
||||
status: "bootstrap-failed",
|
||||
detail: expect.stringContaining("Could not find specified service"),
|
||||
});
|
||||
expect(state.launchctlCalls.some((call) => call[0] === "kickstart")).toBe(false);
|
||||
});
|
||||
|
||||
it("returns a typed kickstart failure", async () => {
|
||||
state.kickstartError = "launchctl kickstart failed: permission denied";
|
||||
state.kickstartFailuresRemaining = 1;
|
||||
const env: Record<string, string | undefined> = {
|
||||
HOME: "/Users/test",
|
||||
OPENCLAW_PROFILE: "default",
|
||||
};
|
||||
|
||||
const repair = await repairLaunchAgentBootstrap({ env });
|
||||
|
||||
expect(repair).toEqual({
|
||||
ok: false,
|
||||
status: "kickstart-failed",
|
||||
detail: "launchctl kickstart failed: permission denied",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("launchd install", () => {
|
||||
|
||||
@@ -313,9 +313,13 @@ export async function readLaunchAgentRuntime(
|
||||
};
|
||||
}
|
||||
|
||||
export type LaunchAgentBootstrapRepairResult =
|
||||
| { ok: true; status: "repaired" | "already-loaded" }
|
||||
| { ok: false; status: "bootstrap-failed" | "kickstart-failed"; detail?: string };
|
||||
|
||||
export async function repairLaunchAgentBootstrap(args: {
|
||||
env?: Record<string, string | undefined>;
|
||||
}): Promise<{ ok: boolean; detail?: string }> {
|
||||
}): Promise<LaunchAgentBootstrapRepairResult> {
|
||||
const env = args.env ?? (process.env as Record<string, string | undefined>);
|
||||
const domain = resolveGuiDomain();
|
||||
const label = resolveLaunchAgentLabel({ env });
|
||||
@@ -324,19 +328,25 @@ export async function repairLaunchAgentBootstrap(args: {
|
||||
// (matches the same guard in installLaunchAgent and restartLaunchAgent).
|
||||
await execLaunchctl(["enable", `${domain}/${label}`]);
|
||||
const boot = await execLaunchctl(["bootstrap", domain, plistPath]);
|
||||
let repairStatus: LaunchAgentBootstrapRepairResult["status"] = "repaired";
|
||||
if (boot.code !== 0) {
|
||||
const detail = (boot.stderr || boot.stdout).trim();
|
||||
const normalized = detail.toLowerCase();
|
||||
const alreadyLoaded = boot.code === 130 || normalized.includes("already exists in domain");
|
||||
if (!alreadyLoaded) {
|
||||
return { ok: false, detail: detail || undefined };
|
||||
return { ok: false, status: "bootstrap-failed", detail: detail || undefined };
|
||||
}
|
||||
repairStatus = "already-loaded";
|
||||
}
|
||||
const kick = await execLaunchctl(["kickstart", "-k", `${domain}/${label}`]);
|
||||
if (kick.code !== 0) {
|
||||
return { ok: false, detail: (kick.stderr || kick.stdout).trim() || undefined };
|
||||
return {
|
||||
ok: false,
|
||||
status: "kickstart-failed",
|
||||
detail: (kick.stderr || kick.stdout).trim() || undefined,
|
||||
};
|
||||
}
|
||||
return { ok: true };
|
||||
return { ok: true, status: repairStatus };
|
||||
}
|
||||
|
||||
export type LegacyLaunchAgent = {
|
||||
|
||||
Reference in New Issue
Block a user