mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 18:39:20 -05:00
Gateway: clarify launchctl domain bootstrap error (#13795)
This commit is contained in:
@@ -171,6 +171,67 @@ describe("launchd install", () => {
|
||||
expect(plist).toContain("<key>TMPDIR</key>");
|
||||
expect(plist).toContain(`<string>${tmpDir}</string>`);
|
||||
});
|
||||
|
||||
it("shows actionable guidance when launchctl gui domain does not support bootstrap", async () => {
|
||||
const originalPath = process.env.PATH;
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-launchctl-test-"));
|
||||
try {
|
||||
const binDir = path.join(tmpDir, "bin");
|
||||
const homeDir = path.join(tmpDir, "home");
|
||||
await fs.mkdir(binDir, { recursive: true });
|
||||
await fs.mkdir(homeDir, { recursive: true });
|
||||
|
||||
const stubJsPath = path.join(binDir, "launchctl.js");
|
||||
await fs.writeFile(
|
||||
stubJsPath,
|
||||
[
|
||||
"const args = process.argv.slice(2);",
|
||||
'if (args[0] === "bootstrap") {',
|
||||
' process.stderr.write("Bootstrap failed: 125: Domain does not support specified action\\n");',
|
||||
" process.exit(1);",
|
||||
"}",
|
||||
"process.exit(0);",
|
||||
"",
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
if (process.platform === "win32") {
|
||||
await fs.writeFile(
|
||||
path.join(binDir, "launchctl.cmd"),
|
||||
`@echo off\r\nnode "%~dp0\\launchctl.js" %*\r\n`,
|
||||
"utf8",
|
||||
);
|
||||
} else {
|
||||
const shPath = path.join(binDir, "launchctl");
|
||||
await fs.writeFile(shPath, `#!/bin/sh\nnode "$(dirname "$0")/launchctl.js" "$@"\n`, "utf8");
|
||||
await fs.chmod(shPath, 0o755);
|
||||
}
|
||||
|
||||
process.env.PATH = `${binDir}${path.delimiter}${originalPath ?? ""}`;
|
||||
|
||||
const env: Record<string, string | undefined> = {
|
||||
HOME: homeDir,
|
||||
OPENCLAW_PROFILE: "default",
|
||||
};
|
||||
let message = "";
|
||||
try {
|
||||
await installLaunchAgent({
|
||||
env,
|
||||
stdout: new PassThrough(),
|
||||
programArguments: ["node", "-e", "process.exit(0)"],
|
||||
});
|
||||
} catch (error) {
|
||||
message = String(error);
|
||||
}
|
||||
expect(message).toContain("logged-in macOS GUI session");
|
||||
expect(message).toContain("wrong user (including sudo)");
|
||||
expect(message).toContain("https://docs.openclaw.ai/gateway");
|
||||
} finally {
|
||||
process.env.PATH = originalPath;
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveLaunchAgentPlistPath", () => {
|
||||
|
||||
@@ -334,6 +334,14 @@ function isLaunchctlNotLoaded(res: { stdout: string; stderr: string; code: numbe
|
||||
);
|
||||
}
|
||||
|
||||
function isUnsupportedGuiDomain(detail: string): boolean {
|
||||
const normalized = detail.toLowerCase();
|
||||
return (
|
||||
normalized.includes("domain does not support specified action") ||
|
||||
normalized.includes("bootstrap failed: 125")
|
||||
);
|
||||
}
|
||||
|
||||
export async function stopLaunchAgent({
|
||||
stdout,
|
||||
env,
|
||||
@@ -402,7 +410,19 @@ export async function installLaunchAgent({
|
||||
await execLaunchctl(["enable", `${domain}/${label}`]);
|
||||
const boot = await execLaunchctl(["bootstrap", domain, plistPath]);
|
||||
if (boot.code !== 0) {
|
||||
throw new Error(`launchctl bootstrap failed: ${boot.stderr || boot.stdout}`.trim());
|
||||
const detail = (boot.stderr || boot.stdout).trim();
|
||||
if (isUnsupportedGuiDomain(detail)) {
|
||||
throw new Error(
|
||||
[
|
||||
`launchctl bootstrap failed: ${detail}`,
|
||||
`LaunchAgent install requires a logged-in macOS GUI session for this user (${domain}).`,
|
||||
"This usually means you are running from SSH/headless context or as the wrong user (including sudo).",
|
||||
"Fix: sign in to the macOS desktop as the target user and rerun `openclaw gateway install --force`.",
|
||||
"Headless deployments should use a dedicated logged-in user session or a custom LaunchDaemon (not shipped): https://docs.openclaw.ai/gateway",
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
throw new Error(`launchctl bootstrap failed: ${detail}`);
|
||||
}
|
||||
await execLaunchctl(["kickstart", "-k", `${domain}/${label}`]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user