diff --git a/src/cli/update-cli.test.ts b/src/cli/update-cli.test.ts index 7e0fb3f9c07..b6b2209c5c3 100644 --- a/src/cli/update-cli.test.ts +++ b/src/cli/update-cli.test.ts @@ -1286,6 +1286,10 @@ describe("update-cli", () => { it("allows package updates from inherited gateway service env when the managed gateway is not running", async () => { mockPackageInstallStatus(createCaseDir("openclaw-update")); + serviceReadRuntime.mockResolvedValueOnce({ + status: "stopped", + state: "stopped", + }); await withEnvAsync( { @@ -1375,6 +1379,39 @@ describe("update-cli", () => { ); }); + it("refuses package updates from inherited gateway service env when the service definition is missing but runtime is live", async () => { + mockPackageInstallStatus(createCaseDir("openclaw-update")); + serviceReadCommand.mockResolvedValue(null); + serviceReadRuntime.mockResolvedValueOnce({ + status: "running", + pid: 4242, + state: "running", + }); + + 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), + ); + }); + it("blocks package updates when the target requires a newer Node runtime", async () => { mockPackageInstallStatus(createCaseDir("openclaw-update")); vi.mocked(fetchNpmPackageTargetStatus).mockResolvedValue({ diff --git a/src/cli/update-cli/update-command.ts b/src/cli/update-cli/update-command.ts index deef66841f9..bab81761fc9 100644 --- a/src/cli/update-cli/update-command.ts +++ b/src/cli/update-cli/update-command.ts @@ -181,11 +181,17 @@ async function maybeStopManagedServiceBeforePackageUpdate(params: { return { stopped: false, inspected: false, runtimeInspected: false, running: false }; } + const runtimeInspected = Boolean(serviceState.runtime); if (!serviceState.installed) { - return { stopped: false, inspected: true, runtimeInspected: true, running: false }; + return { + stopped: false, + inspected: true, + runtimeInspected, + running: serviceState.running, + serviceEnv: serviceState.env, + }; } - const runtimeInspected = Boolean(serviceState.runtime); if (!params.shouldRestart) { if (!params.jsonMode && serviceState.running) { defaultRuntime.log(