feat(doctor): repair launch agent bootstrap

Co-authored-by: Dr Alexander Mikhalev <alex@metacortex.engineer>
This commit is contained in:
Peter Steinberger
2026-01-18 16:09:55 +00:00
parent d024dceef7
commit 1db0384090
4 changed files with 270 additions and 31 deletions

View File

@@ -170,9 +170,22 @@ export async function isLaunchAgentLoaded(args: {
return res.code === 0;
}
async function hasLaunchAgentPlist(env: Record<string, string | undefined>): Promise<boolean> {
const plistPath = resolveLaunchAgentPlistPath(env);
export async function isLaunchAgentListed(args: {
env?: Record<string, string | undefined>;
}): Promise<boolean> {
const label = resolveLaunchAgentLabel({ env: args.env });
const res = await execLaunchctl(["list"]);
if (res.code !== 0) return false;
return res.stdout
.split(/\r?\n/)
.some((line) => line.trim().split(/\s+/).at(-1) === label);
}
export async function launchAgentPlistExists(
env: Record<string, string | undefined>,
): Promise<boolean> {
try {
const plistPath = resolveLaunchAgentPlistPath(env);
await fs.access(plistPath);
return true;
} catch {
@@ -194,7 +207,7 @@ export async function readLaunchAgentRuntime(
};
}
const parsed = parseLaunchctlPrint(res.stdout || res.stderr || "");
const plistExists = await hasLaunchAgentPlist(env);
const plistExists = await launchAgentPlistExists(env);
const state = parsed.state?.toLowerCase();
const status = state === "running" || parsed.pid ? "running" : state ? "stopped" : "unknown";
return {
@@ -207,6 +220,24 @@ export async function readLaunchAgentRuntime(
};
}
export async function repairLaunchAgentBootstrap(args: {
env?: Record<string, string | undefined>;
}): Promise<{ ok: boolean; detail?: string }> {
const env = args.env ?? (process.env as Record<string, string | undefined>);
const domain = resolveGuiDomain();
const label = resolveLaunchAgentLabel({ env });
const plistPath = resolveLaunchAgentPlistPath(env);
const boot = await execLaunchctl(["bootstrap", domain, plistPath]);
if (boot.code !== 0) {
return { ok: false, detail: (boot.stderr || boot.stdout).trim() || undefined };
}
const kick = await execLaunchctl(["kickstart", "-k", `${domain}/${label}`]);
if (kick.code !== 0) {
return { ok: false, detail: (kick.stderr || kick.stdout).trim() || undefined };
}
return { ok: true };
}
export type LegacyLaunchAgent = {
label: string;
plistPath: string;