






















@@ -25,6 +25,7 @@ const serviceLoaded = vi.fn();
2525const prepareRestartScript = vi.fn();
2626const runRestartScript = vi.fn();
2727const mockedRunDaemonInstall = vi.fn();
28+const serviceReadCommand = vi.fn();
2829const serviceReadRuntime = vi.fn();
2930const inspectPortUsage = vi.fn();
3031const classifyPortListener = vi.fn();
@@ -164,8 +165,27 @@ vi.mock("../plugins/installed-plugin-index-records.js", async (importOriginal) =
164165});
165166166167vi.mock("../daemon/service.js", () => ({
168+readGatewayServiceState: async () => {
169+const command = await serviceReadCommand();
170+const env = {
171+ ...process.env,
172+ ...(command && typeof command === "object" && "environment" in command
173+ ? (command.environment as NodeJS.ProcessEnv | undefined)
174+ : undefined),
175+};
176+const [loaded, runtime] = await Promise.all([serviceLoaded({ env }), serviceReadRuntime(env)]);
177+return {
178+installed: command !== null,
179+ loaded,
180+running: runtime?.status === "running",
181+ env,
182+ command,
183+ runtime,
184+};
185+},
167186resolveGatewayService: vi.fn(() => ({
168187isLoaded: (...args: unknown[]) => serviceLoaded(...args),
188+readCommand: (...args: unknown[]) => serviceReadCommand(...args),
169189readRuntime: (...args: unknown[]) => serviceReadRuntime(...args),
170190})),
171191}));
@@ -451,6 +471,9 @@ describe("update-cli", () => {
451471readPackageVersion.mockResolvedValue("1.0.0");
452472resolveGlobalManager.mockResolvedValue("npm");
453473serviceLoaded.mockResolvedValue(false);
474+serviceReadCommand.mockImplementation(async () =>
475+(await serviceLoaded()) ? { programArguments: ["openclaw", "gateway", "run"] } : null,
476+);
454477serviceReadRuntime.mockResolvedValue({
455478status: "running",
456479pid: 4242,
@@ -543,11 +566,12 @@ describe("update-cli", () => {
543566});
544567545568it("keeps downgrade post-update work in the current process", async () => {
569+const downgradedRoot = createCaseDir("openclaw-downgraded-root");
546570setupUpdatedRootRefresh({
547571gatewayUpdateImpl: async () =>
548572makeOkUpdateResult({
549573mode: "npm",
550-root: createCaseDir("openclaw-downgraded-root"),
574+root: downgradedRoot,
551575before: { version: "2026.4.14" },
552576after: { version: "2026.4.10" },
553577}),
@@ -574,13 +598,13 @@ describe("update-cli", () => {
574598url: "ws://127.0.0.1:18789",
575599});
576600577-await updateCommand({ yes: true, tag: "2026.4.10" });
601+await updateCommand({ yes: true, tag: "2026.4.10", restart: false });
578602579603expect(spawn).not.toHaveBeenCalled();
580604expect(syncPluginsForUpdateChannel).toHaveBeenCalled();
581605expect(updateNpmInstalledPlugins).toHaveBeenCalled();
582-expect(runDaemonInstall).toHaveBeenCalled();
583-expect(probeGateway).toHaveBeenCalled();
606+expect(runDaemonInstall).not.toHaveBeenCalled();
607+expect(probeGateway).not.toHaveBeenCalled();
584608expect(defaultRuntime.exit).not.toHaveBeenCalledWith(1);
585609});
586610@@ -1872,25 +1896,32 @@ describe("update-cli", () => {
1872189618731897await updateCommand({ yes: true });
187418981875-expect(runDaemonInstall).toHaveBeenCalledWith({
1876-force: true,
1877-json: undefined,
1878-});
1899+expect(runDaemonInstall).not.toHaveBeenCalled();
18791900expect(runRestartScript).not.toHaveBeenCalled();
18801901expect(defaultRuntime.exit).toHaveBeenCalledWith(1);
1902+expect(
1903+vi
1904+.mocked(defaultRuntime.log)
1905+.mock.calls.map((call) => String(call[0]))
1906+.join("\n"),
1907+).toContain("updated install entrypoint not found");
18811908});
1882190918831910it("fails a JSON package update when fallback restart leaves the old gateway running", async () => {
1911+const updatedRoot = createCaseDir("openclaw-updated-root");
1912+const updatedEntrypoint = path.join(updatedRoot, "dist", "entry.js");
18841913setupUpdatedRootRefresh({
1914+entrypoints: [updatedEntrypoint],
18851915gatewayUpdateImpl: async () =>
18861916makeOkUpdateResult({
18871917mode: "npm",
1888-root: createCaseDir("openclaw-updated-root"),
1918+root: updatedRoot,
18891919before: { version: "2026.4.23" },
18901920after: { version: "2026.4.24" },
18911921}),
18921922});
18931923prepareRestartScript.mockResolvedValue(null);
1924+serviceLoaded.mockResolvedValue(true);
18941925probeGateway.mockResolvedValue({
18951926ok: true,
18961927close: null,
@@ -1911,7 +1942,11 @@ describe("update-cli", () => {
19111942await updateCommand({ yes: true, json: true });
1912194319131944expect(runRestartScript).not.toHaveBeenCalled();
1914-expect(runDaemonRestart).toHaveBeenCalled();
1945+expect(runDaemonRestart).not.toHaveBeenCalled();
1946+expect(runCommandWithTimeout).toHaveBeenCalledWith(
1947+[expect.stringMatching(/node/), updatedEntrypoint, "gateway", "restart", "--json"],
1948+expect.objectContaining({ cwd: updatedRoot, timeoutMs: 60_000 }),
1949+);
19151950expect(probeGateway).toHaveBeenCalledWith(expect.objectContaining({ includeDetails: true }));
19161951expect(defaultRuntime.exit).toHaveBeenCalledWith(1);
19171952expect(defaultRuntime.writeJson).not.toHaveBeenCalled();
@@ -1927,11 +1962,14 @@ describe("update-cli", () => {
19271962});
1928196319291964it("fails a package update when the restarted gateway reports activated plugin load errors", async () => {
1965+const updatedRoot = createCaseDir("openclaw-updated-root");
1966+const updatedEntrypoint = path.join(updatedRoot, "dist", "entry.js");
19301967setupUpdatedRootRefresh({
1968+entrypoints: [updatedEntrypoint],
19311969gatewayUpdateImpl: async () =>
19321970makeOkUpdateResult({
19331971mode: "npm",
1934-root: createCaseDir("openclaw-updated-root"),
1972+root: updatedRoot,
19351973before: { version: "2026.4.23" },
19361974after: { version: "2026.4.24" },
19371975}),
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。