From a8d8614c65ee7edc338edfd2f0ddfdd630766064 Mon Sep 17 00:00:00 2001 From: masonxhuang Date: Sat, 2 May 2026 20:08:12 +0800 Subject: [PATCH] fix(update): block unknown gateway runtime status --- src/cli/update-cli.test.ts | 77 ++++++++++++++++------------ src/cli/update-cli/update-command.ts | 3 +- 2 files changed, 47 insertions(+), 33 deletions(-) diff --git a/src/cli/update-cli.test.ts b/src/cli/update-cli.test.ts index b6b2209c5c3..2933751dc6c 100644 --- a/src/cli/update-cli.test.ts +++ b/src/cli/update-cli.test.ts @@ -1344,40 +1344,53 @@ describe("update-cli", () => { ); }); - it("refuses package updates from inherited gateway service env when runtime inspection is inconclusive", async () => { - mockPackageInstallStatus(createCaseDir("openclaw-update")); - serviceReadCommand.mockResolvedValue({ - programArguments: ["openclaw", "gateway", "run"], - environment: { - OPENCLAW_SERVICE_MARKER: "openclaw", - OPENCLAW_SERVICE_KIND: "gateway", - }, - }); - serviceReadRuntime.mockRejectedValueOnce(new Error("runtime probe failed")); + it.each([ + { + name: "runtime probe fails", + setupRuntime: () => + serviceReadRuntime.mockRejectedValueOnce(new Error("runtime probe failed")), + }, + { + name: "runtime status is unknown", + setupRuntime: () => serviceReadRuntime.mockResolvedValueOnce({ status: "unknown" }), + }, + ])( + "refuses package updates from inherited gateway service env when $name", + async ({ setupRuntime }) => { + mockPackageInstallStatus(createCaseDir("openclaw-update")); + serviceReadCommand.mockResolvedValue({ + programArguments: ["openclaw", "gateway", "run"], + environment: { + OPENCLAW_SERVICE_MARKER: "openclaw", + OPENCLAW_SERVICE_KIND: "gateway", + }, + }); + setupRuntime(); - await withEnvAsync( - { - OPENCLAW_SERVICE_MARKER: "openclaw", - OPENCLAW_SERVICE_KIND: "gateway", - }, - async () => { - await updateCommand({ yes: true }); - }, - ); + await withEnvAsync( + { + OPENCLAW_SERVICE_MARKER: "openclaw", + OPENCLAW_SERVICE_KIND: "gateway", + }, + async () => { + await updateCommand({ yes: true }); + }, + ); - expect(defaultRuntime.error).toHaveBeenCalledWith( - expect.stringContaining( - "Package updates cannot run from inside the gateway service process.", - ), - ); - expect(defaultRuntime.exit).toHaveBeenCalledWith(1); - expect(serviceStop).not.toHaveBeenCalled(); - expect(runGatewayUpdate).not.toHaveBeenCalled(); - expect(runCommandWithTimeout).not.toHaveBeenCalledWith( - ["npm", "i", "-g", "openclaw@latest", "--no-fund", "--no-audit", "--loglevel=error"], - expect.any(Object), - ); - }); + expect(defaultRuntime.error).toHaveBeenCalledWith( + expect.stringContaining( + "Package updates cannot run from inside the gateway service process.", + ), + ); + expect(defaultRuntime.exit).toHaveBeenCalledWith(1); + expect(serviceStop).not.toHaveBeenCalled(); + expect(runGatewayUpdate).not.toHaveBeenCalled(); + expect(runCommandWithTimeout).not.toHaveBeenCalledWith( + ["npm", "i", "-g", "openclaw@latest", "--no-fund", "--no-audit", "--loglevel=error"], + expect.any(Object), + ); + }, + ); it("refuses package updates from inherited gateway service env when the service definition is missing but runtime is live", async () => { mockPackageInstallStatus(createCaseDir("openclaw-update")); diff --git a/src/cli/update-cli/update-command.ts b/src/cli/update-cli/update-command.ts index bab81761fc9..d4730da3ac4 100644 --- a/src/cli/update-cli/update-command.ts +++ b/src/cli/update-cli/update-command.ts @@ -181,7 +181,8 @@ async function maybeStopManagedServiceBeforePackageUpdate(params: { return { stopped: false, inspected: false, runtimeInspected: false, running: false }; } - const runtimeInspected = Boolean(serviceState.runtime); + const runtimeStatus = serviceState.runtime?.status; + const runtimeInspected = runtimeStatus === "running" || runtimeStatus === "stopped"; if (!serviceState.installed) { return { stopped: false,