feat(doctor): repair launch agent bootstrap
Co-authored-by: Dr Alexander Mikhalev <alex@metacortex.engineer>
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { resolveGatewayPort } from "../config/config.js";
|
||||
import { resolveGatewayLaunchAgentLabel } from "../daemon/constants.js";
|
||||
import { resolveGatewayLaunchAgentLabel, resolveNodeLaunchAgentLabel } from "../daemon/constants.js";
|
||||
import { readLastGatewayErrorLine } from "../daemon/diagnostics.js";
|
||||
import {
|
||||
isLaunchAgentListed,
|
||||
isLaunchAgentLoaded,
|
||||
launchAgentPlistExists,
|
||||
repairLaunchAgentBootstrap,
|
||||
} from "../daemon/launchd.js";
|
||||
import { resolveGatewayService } from "../daemon/service.js";
|
||||
import { isSystemdUserServiceAvailable } from "../daemon/systemd.js";
|
||||
import { renderSystemdUnavailableHints } from "../daemon/systemd-hints.js";
|
||||
@@ -21,6 +27,53 @@ import type { DoctorOptions, DoctorPrompter } from "./doctor-prompter.js";
|
||||
import { healthCommand } from "./health.js";
|
||||
import { formatHealthCheckFailure } from "./health-format.js";
|
||||
|
||||
async function maybeRepairLaunchAgentBootstrap(params: {
|
||||
env: Record<string, string | undefined>;
|
||||
title: string;
|
||||
runtime: RuntimeEnv;
|
||||
prompter: DoctorPrompter;
|
||||
}): Promise<boolean> {
|
||||
if (process.platform !== "darwin") return false;
|
||||
|
||||
const listed = await isLaunchAgentListed({ env: params.env });
|
||||
if (!listed) return false;
|
||||
|
||||
const loaded = await isLaunchAgentLoaded({ env: params.env });
|
||||
if (loaded) return false;
|
||||
|
||||
const plistExists = await launchAgentPlistExists(params.env);
|
||||
if (!plistExists) return false;
|
||||
|
||||
note(
|
||||
"LaunchAgent is listed but not loaded in launchd.",
|
||||
`${params.title} LaunchAgent`,
|
||||
);
|
||||
|
||||
const shouldFix = await params.prompter.confirmSkipInNonInteractive({
|
||||
message: `Repair ${params.title} LaunchAgent bootstrap now?`,
|
||||
initialValue: true,
|
||||
});
|
||||
if (!shouldFix) return false;
|
||||
|
||||
params.runtime.log(`Bootstrapping ${params.title} LaunchAgent...`);
|
||||
const repair = await repairLaunchAgentBootstrap({ env: params.env });
|
||||
if (!repair.ok) {
|
||||
params.runtime.error(
|
||||
`${params.title} LaunchAgent bootstrap failed: ${repair.detail ?? "unknown error"}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
const verified = await isLaunchAgentLoaded({ env: params.env });
|
||||
if (!verified) {
|
||||
params.runtime.error(`${params.title} LaunchAgent still not loaded after repair.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
note(`${params.title} LaunchAgent repaired.`, `${params.title} LaunchAgent`);
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function maybeRepairGatewayDaemon(params: {
|
||||
cfg: ClawdbotConfig;
|
||||
runtime: RuntimeEnv;
|
||||
@@ -32,12 +85,33 @@ export async function maybeRepairGatewayDaemon(params: {
|
||||
if (params.healthOk) return;
|
||||
|
||||
const service = resolveGatewayService();
|
||||
const loaded = await service.isLoaded({ env: process.env });
|
||||
let loaded = await service.isLoaded({ env: process.env });
|
||||
let serviceRuntime: Awaited<ReturnType<typeof service.readRuntime>> | undefined;
|
||||
if (loaded) {
|
||||
serviceRuntime = await service.readRuntime(process.env).catch(() => undefined);
|
||||
}
|
||||
|
||||
if (process.platform === "darwin" && params.cfg.gateway?.mode !== "remote") {
|
||||
const gatewayRepaired = await maybeRepairLaunchAgentBootstrap({
|
||||
env: process.env,
|
||||
title: "Gateway",
|
||||
runtime: params.runtime,
|
||||
prompter: params.prompter,
|
||||
});
|
||||
await maybeRepairLaunchAgentBootstrap({
|
||||
env: { ...process.env, CLAWDBOT_LAUNCHD_LABEL: resolveNodeLaunchAgentLabel() },
|
||||
title: "Node",
|
||||
runtime: params.runtime,
|
||||
prompter: params.prompter,
|
||||
});
|
||||
if (gatewayRepaired) {
|
||||
loaded = await service.isLoaded({ env: process.env });
|
||||
if (loaded) {
|
||||
serviceRuntime = await service.readRuntime(process.env).catch(() => undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (params.cfg.gateway?.mode !== "remote") {
|
||||
const port = resolveGatewayPort(params.cfg, process.env);
|
||||
const diagnostics = await inspectPortUsage(port);
|
||||
|
||||
Reference in New Issue
Block a user