mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-09 03:22:40 +00:00
fix(gateway): handle missing launchd gui domains
This commit is contained in:
@@ -59,14 +59,14 @@ type LaunchAgentBootstrapDoctorOutcome =
|
||||
function noteGatewayRuntime(
|
||||
serviceRuntime: GatewayServiceRuntime | undefined,
|
||||
env: Record<string, string | undefined>,
|
||||
): boolean {
|
||||
): void {
|
||||
const summary = formatGatewayRuntimeSummary(serviceRuntime);
|
||||
const hints = buildGatewayRuntimeHints(serviceRuntime, {
|
||||
platform: process.platform,
|
||||
env,
|
||||
});
|
||||
if (!summary && hints.length === 0) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
const lines: string[] = [];
|
||||
@@ -75,7 +75,6 @@ function noteGatewayRuntime(
|
||||
}
|
||||
lines.push(...hints);
|
||||
note(lines.join("\n"), "Gateway");
|
||||
return true;
|
||||
}
|
||||
|
||||
async function maybeRepairLaunchAgentBootstrap(params: {
|
||||
@@ -208,6 +207,8 @@ export async function maybeRepairGatewayDaemon(params: {
|
||||
const serviceRepairPolicy = resolveServiceRepairPolicy();
|
||||
const serviceRepairExternal = isServiceRepairExternallyManaged(serviceRepairPolicy);
|
||||
const service = resolveGatewayService();
|
||||
const isLocalDarwinGateway =
|
||||
process.platform === "darwin" && params.cfg.gateway?.mode !== "remote";
|
||||
// systemd can throw in containers/WSL; treat as "not loaded" and fall back to hints.
|
||||
let loaded;
|
||||
try {
|
||||
@@ -225,8 +226,7 @@ export async function maybeRepairGatewayDaemon(params: {
|
||||
...command.environment,
|
||||
} satisfies NodeJS.ProcessEnv)
|
||||
: process.env;
|
||||
const shouldReadRuntime =
|
||||
loaded || (process.platform === "darwin" && params.cfg.gateway?.mode !== "remote");
|
||||
const shouldReadRuntime = loaded || isLocalDarwinGateway;
|
||||
if (shouldReadRuntime) {
|
||||
serviceRuntime = await service.readRuntime(serviceEnv).catch(() => undefined);
|
||||
}
|
||||
@@ -237,7 +237,7 @@ export async function maybeRepairGatewayDaemon(params: {
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === "darwin" && params.cfg.gateway?.mode !== "remote") {
|
||||
if (isLocalDarwinGateway) {
|
||||
const gatewayRepair = serviceRuntime?.missingGuiSession
|
||||
? ({ status: "gui-session-unavailable", detail: serviceRuntime.detail ?? "" } as const)
|
||||
: await maybeRepairLaunchAgentBootstrap({
|
||||
@@ -296,7 +296,7 @@ export async function maybeRepairGatewayDaemon(params: {
|
||||
|
||||
if (!loaded) {
|
||||
if (
|
||||
process.platform === "darwin" &&
|
||||
isLocalDarwinGateway &&
|
||||
(serviceRuntime?.missingGuiSession ||
|
||||
serviceRuntime?.missingSupervision ||
|
||||
serviceRuntime?.cachedLabel)
|
||||
|
||||
@@ -428,10 +428,13 @@ describe("launchd runtime state", () => {
|
||||
expect(runtime.detail).toBe("Could not find service");
|
||||
});
|
||||
|
||||
it("marks installed LaunchAgents unavailable when the GUI domain is missing", async () => {
|
||||
it.each([
|
||||
"Bootstrap failed: 125: Domain does not support specified action",
|
||||
"Could not find domain for user gui: 999999",
|
||||
])("marks installed LaunchAgents unavailable when launchd reports %s", async (detail) => {
|
||||
const env = createDefaultLaunchdEnv();
|
||||
state.files.set(resolveLaunchAgentPlistPath(env), "<plist/>");
|
||||
state.printError = "Bootstrap failed: 125: Domain does not support specified action";
|
||||
state.printError = detail;
|
||||
state.printFailuresRemaining = 1;
|
||||
|
||||
const runtime = await readLaunchAgentRuntime(env);
|
||||
@@ -439,7 +442,7 @@ describe("launchd runtime state", () => {
|
||||
expect(runtime.status).toBe("unknown");
|
||||
expect(runtime.missingSupervision).toBe(true);
|
||||
expect(runtime.missingGuiSession).toBe(true);
|
||||
expect(runtime.detail).toContain("Domain does not support specified action");
|
||||
expect(runtime.detail).toBe(detail);
|
||||
});
|
||||
|
||||
it("marks a missing unit when launchd has no job and no plist exists", async () => {
|
||||
@@ -793,8 +796,11 @@ describe("launchd bootstrap repair", () => {
|
||||
expect(launchctlCommandNames()).not.toContain("kickstart");
|
||||
});
|
||||
|
||||
it("classifies headless GUI bootstrap failures separately from generic not-loaded repair", async () => {
|
||||
state.bootstrapError = "Bootstrap failed: 125: Domain does not support specified action";
|
||||
it.each([
|
||||
"Bootstrap failed: 125: Domain does not support specified action",
|
||||
"Could not find domain for user gui: 999999",
|
||||
])("classifies %s separately from generic not-loaded repair", async (detail) => {
|
||||
state.bootstrapError = detail;
|
||||
const env = createDefaultLaunchdEnv();
|
||||
|
||||
const repair = await repairLaunchAgentBootstrap({ env });
|
||||
@@ -802,7 +808,7 @@ describe("launchd bootstrap repair", () => {
|
||||
expect(repair).toEqual({
|
||||
ok: false,
|
||||
status: "gui-session-unavailable",
|
||||
detail: "Bootstrap failed: 125: Domain does not support specified action",
|
||||
detail,
|
||||
domain: typeof process.getuid === "function" ? `gui/${process.getuid()}` : "gui/501",
|
||||
});
|
||||
expect(launchctlCommandNames()).not.toContain("kickstart");
|
||||
|
||||
@@ -707,6 +707,7 @@ function isUnsupportedGuiDomain(detail: string): boolean {
|
||||
const normalized = normalizeLowercaseStringOrEmpty(detail);
|
||||
return (
|
||||
normalized.includes("domain does not support specified action") ||
|
||||
normalized.includes("could not find domain for user gui") ||
|
||||
normalized.includes("bootstrap failed: 125")
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user