From 893f5800727ee8df73acb2aa5a48f253da64deaf Mon Sep 17 00:00:00 2001 From: "clawsweeper[bot]" <274271284+clawsweeper[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 16:48:08 +0000 Subject: [PATCH] fix(update): tailor gateway recovery hints by platform (#83191) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: - The PR updates the CLI post-update gateway recovery formatter and tests to show Linux, macOS, Windows, or generic service-manager guidance, plus a changelog entry. - Reproducibility: yes. Source inspection gives a high-confidence reproduction path: current main reaches a fo ... hAgent recovery text, while the platform contract says Linux uses systemd and Windows uses Scheduled Tasks. Automerge notes: - PR branch already contained follow-up commit before automerge: fix(update): tailor gateway recovery hints by platform Validation: - ClawSweeper review passed for head 0cf2a0c5a77cdb41f01943c04b84345fab09e42b. - Required merge gates passed before the squash merge. Prepared head SHA: 0cf2a0c5a77cdb41f01943c04b84345fab09e42b Review: https://github.com/openclaw/openclaw/pull/83191#issuecomment-4471471293 Co-authored-by: Rubén Cuevas Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> --- CHANGELOG.md | 1 + src/cli/update-cli/update-command.test.ts | 48 +++++++++++++++++++++++ src/cli/update-cli/update-command.ts | 31 +++++++++++++-- 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1f3b09419f..886a437d15b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- CLI/update: tailor post-update Gateway recovery hints by platform, showing systemd, LaunchAgent, Scheduled Task, or generic service-manager guidance instead of macOS-only recovery text. (#83096) Thanks @rubencu. - Plugins: apply a default 15-second timeout to legacy `before_agent_start` hooks so hung plugin handlers no longer block agent startup. Fixes #48534. (#83136) Thanks @therahul-yo. - Feishu: refresh inbound session delivery context for DM, group, and broadcast turns so later replies do not inherit stale WebChat routing. Fixes #78274. - QA-Lab/qa-channel: attach redacted agent tool-start traces to outbound `QaBusMessage` records so scenarios can assert actual tool use instead of relying only on reply text. Fixes #67637. Thanks @100yenadmin. diff --git a/src/cli/update-cli/update-command.test.ts b/src/cli/update-cli/update-command.test.ts index f575347277b..d2e39cef06b 100644 --- a/src/cli/update-cli/update-command.test.ts +++ b/src/cli/update-cli/update-command.test.ts @@ -6,9 +6,11 @@ import { buildGatewayInstallEntrypointCandidates as resolveGatewayInstallEntrypointCandidates, resolveGatewayInstallEntrypoint, } from "../../daemon/gateway-entrypoint.js"; +import type { UpdateRunResult } from "../../infra/update-runner.js"; import { buildInvalidConfigPostCoreUpdateResult, collectMissingPluginInstallPayloads, + formatPostUpdateGatewayRecoveryInstructions, recoverInstalledLaunchAgentAfterUpdate, recoverLaunchAgentAndRecheckGatewayHealth, resolvePostCoreUpdateChildStdio, @@ -348,6 +350,52 @@ describe("shouldUseLegacyProcessRestartAfterUpdate", () => { expect(shouldUseLegacyProcessRestartAfterUpdate({ updateMode: "unknown" })).toBe(true); }); }); + +describe("formatPostUpdateGatewayRecoveryInstructions", () => { + const result: UpdateRunResult = { + status: "error", + mode: "git", + steps: [], + durationMs: 0, + }; + + it("uses systemd wording on Linux instead of macOS LaunchAgent instructions", () => { + const [line] = formatPostUpdateGatewayRecoveryInstructions(result, "linux"); + + expect(line).toContain("the systemd user service"); + expect(line).toContain("openclaw gateway restart"); + expect(line).toContain("openclaw gateway install --force"); + expect(line).toContain("openclaw gateway status --deep"); + expect(line).not.toContain("Linux reports"); + expect(line).not.toContain("macOS"); + expect(line).not.toContain("LaunchAgent"); + }); + + it("keeps LaunchAgent recovery wording on macOS", () => { + const [line] = formatPostUpdateGatewayRecoveryInstructions(result, "darwin"); + + expect(line).toContain("the LaunchAgent is installed but not loaded"); + expect(line).toContain("logged-in macOS user session"); + }); + + it("uses Windows service-manager wording on Windows", () => { + const [line] = formatPostUpdateGatewayRecoveryInstructions(result, "win32"); + + expect(line).toContain("the gateway Scheduled Task or Windows login item"); + expect(line).not.toContain("LaunchAgent"); + expect(line).not.toContain("Startup-folder"); + }); + + it("uses generic service-manager wording for unsupported Node platforms", () => { + const [line] = formatPostUpdateGatewayRecoveryInstructions(result, "freebsd"); + + expect(line).toContain("local service manager"); + expect(line).not.toContain("systemd"); + expect(line).not.toContain("LaunchAgent"); + expect(line).not.toContain("Scheduled Task"); + }); +}); + describe("recoverInstalledLaunchAgentAfterUpdate", () => { it("re-bootstraps an installed-but-not-loaded macOS LaunchAgent after update", async () => { const service = {} as never; diff --git a/src/cli/update-cli/update-command.ts b/src/cli/update-cli/update-command.ts index f22084b22df..ec930f9c73b 100644 --- a/src/cli/update-cli/update-command.ts +++ b/src/cli/update-cli/update-command.ts @@ -703,10 +703,33 @@ export async function recoverLaunchAgentAndRecheckGatewayHealth(params: { return { health, launchAgentRecovery }; } -function formatPostUpdateGatewayRecoveryInstructions(result: UpdateRunResult): string[] { - const lines = [ - `Recovery: run \`${replaceCliName(formatCliCommand("openclaw gateway restart"), CLI_NAME)}\`; if macOS reports the LaunchAgent is installed but not loaded, run \`${replaceCliName(formatCliCommand("openclaw gateway install --force"), CLI_NAME)}\` from the logged-in user session, then rerun \`${replaceCliName(formatCliCommand("openclaw gateway status --deep"), CLI_NAME)}\`.`, - ]; +function formatPostUpdateGatewayRecoveryLine(platform: NodeJS.Platform): string { + const restartCommand = replaceCliName(formatCliCommand("openclaw gateway restart"), CLI_NAME); + const installCommand = replaceCliName( + formatCliCommand("openclaw gateway install --force"), + CLI_NAME, + ); + const statusCommand = replaceCliName( + formatCliCommand("openclaw gateway status --deep"), + CLI_NAME, + ); + if (platform === "darwin") { + return `Recovery: run \`${restartCommand}\`; if the LaunchAgent is installed but not loaded, run \`${installCommand}\` from the logged-in macOS user session, then rerun \`${statusCommand}\`.`; + } + if (platform === "linux") { + return `Recovery: run \`${restartCommand}\`; if the systemd user service is missing, stale, or not active, run \`${installCommand}\` from the same user account, then rerun \`${statusCommand}\`.`; + } + if (platform === "win32") { + return `Recovery: run \`${restartCommand}\`; if the gateway Scheduled Task or Windows login item is missing, stale, or not running, run \`${installCommand}\` from the same user account, then rerun \`${statusCommand}\`.`; + } + return `Recovery: run \`${restartCommand}\`; if the local service manager reports the gateway service is missing, stale, or not running, run \`${installCommand}\` from the same user account, then rerun \`${statusCommand}\`.`; +} + +export function formatPostUpdateGatewayRecoveryInstructions( + result: UpdateRunResult, + platform: NodeJS.Platform = process.platform, +): string[] { + const lines = [formatPostUpdateGatewayRecoveryLine(platform)]; const beforeVersion = normalizeOptionalString(result.before?.version); if (isPackageManagerUpdateMode(result.mode) && beforeVersion) { lines.push(