






















@@ -837,7 +837,7 @@ describe("update-cli", () => {
837837);
838838});
839839840-it("skips package-manager updates when the installed version already matches the target", async () => {
840+it("refreshes package-manager updates when the installed version already matches the target", async () => {
841841const tempDir = createCaseDir("openclaw-update");
842842mockPackageInstallStatus(tempDir);
843843readPackageVersion.mockResolvedValue("2026.4.22");
@@ -853,15 +853,11 @@ describe("update-cli", () => {
853853.mock.calls.filter(
854854([argv]) => Array.isArray(argv) && argv[0] === "npm" && argv[1] === "i" && argv[2] === "-g",
855855);
856-expect(installCalls).toEqual([]);
857-expect(syncPluginsForUpdateChannel).not.toHaveBeenCalled();
858-expect(updateNpmInstalledPlugins).not.toHaveBeenCalled();
856+expect(installCalls).toHaveLength(1);
857+expect(updateNpmInstalledPlugins).toHaveBeenCalled();
859858expect(replaceConfigFile).not.toHaveBeenCalled();
860-expect(runRestartScript).not.toHaveBeenCalled();
861-expect(runDaemonRestart).not.toHaveBeenCalled();
862-expect(defaultRuntime.exit).toHaveBeenCalledWith(0);
863859const logs = vi.mocked(defaultRuntime.log).mock.calls.map((call) => String(call[0]));
864-expect(logs.join("\n")).toContain("already-current");
860+expect(logs.join("\n")).not.toContain("already-current");
865861});
866862867863it("blocks package updates when the target requires a newer Node runtime", async () => {
@@ -1042,6 +1038,78 @@ describe("update-cli", () => {
10421038);
10431039});
104410401041+it("refreshes package installs even when the current version already matches the target", async () => {
1042+const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-update-current-"));
1043+const nodeModules = path.join(tempDir, "node_modules");
1044+const pkgRoot = path.join(nodeModules, "openclaw");
1045+const entryPath = path.join(pkgRoot, "dist", "index.js");
1046+mockPackageInstallStatus(pkgRoot);
1047+readPackageVersion.mockResolvedValue("2026.4.23");
1048+vi.mocked(resolveNpmChannelTag).mockResolvedValue({
1049+tag: "latest",
1050+version: "2026.4.23",
1051+});
1052+await fs.mkdir(path.dirname(entryPath), { recursive: true });
1053+await fs.writeFile(
1054+path.join(pkgRoot, "package.json"),
1055+JSON.stringify({ name: "openclaw", version: "2026.4.23" }),
1056+"utf-8",
1057+);
1058+await fs.writeFile(entryPath, "export {};\n", "utf-8");
1059+for (const relativePath of TEST_BUNDLED_RUNTIME_SIDECAR_PATHS) {
1060+const absolutePath = path.join(pkgRoot, relativePath);
1061+await fs.mkdir(path.dirname(absolutePath), { recursive: true });
1062+await fs.writeFile(absolutePath, "export {};\n", "utf-8");
1063+}
1064+await writePackageDistInventory(pkgRoot);
1065+pathExists.mockImplementation(async (candidate: string) => {
1066+try {
1067+await fs.access(candidate);
1068+return true;
1069+} catch {
1070+return false;
1071+}
1072+});
1073+vi.mocked(runCommandWithTimeout).mockImplementation(async (argv) => {
1074+if (Array.isArray(argv) && argv[0] === "npm" && argv[1] === "root" && argv[2] === "-g") {
1075+return {
1076+stdout: `${nodeModules}\n`,
1077+stderr: "",
1078+code: 0,
1079+signal: null,
1080+killed: false,
1081+termination: "exit",
1082+};
1083+}
1084+return {
1085+stdout: "",
1086+stderr: "",
1087+code: 0,
1088+signal: null,
1089+killed: false,
1090+termination: "exit",
1091+};
1092+});
1093+1094+await updateCommand({ yes: true, restart: false });
1095+1096+expect(runCommandWithTimeout).toHaveBeenCalledWith(
1097+["npm", "i", "-g", "openclaw@latest", "--no-fund", "--no-audit", "--loglevel=error"],
1098+expect.any(Object),
1099+);
1100+expect(runCommandWithTimeout).toHaveBeenCalledWith(
1101+[expect.stringMatching(/node/), entryPath, "doctor", "--non-interactive"],
1102+expect.any(Object),
1103+);
1104+expect(updateNpmInstalledPlugins).toHaveBeenCalled();
1105+expect(
1106+vi
1107+.mocked(defaultRuntime.log)
1108+.mock.calls.map((call) => String(call[0]))
1109+.join("\n"),
1110+).not.toContain("already-current");
1111+});
1112+10451113it("uses the owning npm binary for package updates when PATH npm points elsewhere", async () => {
10461114const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("darwin");
10471115const brewPrefix = createCaseDir("brew-prefix");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。