























@@ -994,6 +994,87 @@ describe("updateNpmInstalledPlugins", () => {
994994}
995995});
996996997+it("continues repairing sibling openclaw peer links after one recorded npm install cannot be relinked", async () => {
998+const plugins = [
999+{ pluginId: "brave", packageName: "@openclaw/brave-plugin" },
1000+{ pluginId: "codex", packageName: "@openclaw/codex" },
1001+];
1002+const { installPaths, peerLinkPath, linkPeer } = createOpenClawPeerLinkFixtures(plugins);
1003+const brokenInstallPath = createInstalledPackageDir({
1004+name: "@openclaw/broken-plugin",
1005+version: "2026.5.4",
1006+peerDependencies: { openclaw: ">=2026.5.4" },
1007+});
1008+fs.writeFileSync(path.join(brokenInstallPath, "node_modules"), "not a directory");
1009+linkPeer("brave");
1010+mockNpmViewMetadata({
1011+name: "@openclaw/codex",
1012+version: "2026.5.4",
1013+integrity: "sha512-same",
1014+shasum: "same",
1015+});
1016+installPluginFromNpmSpecMock.mockImplementation(() => {
1017+for (const { pluginId } of plugins) {
1018+fs.rmSync(peerLinkPath(pluginId), { recursive: true, force: true });
1019+}
1020+linkPeer("codex");
1021+return Promise.resolve(
1022+createSuccessfulNpmUpdateResult({
1023+pluginId: "codex",
1024+targetDir: installPaths.codex,
1025+version: "2026.5.4",
1026+npmResolution: {
1027+name: "@openclaw/codex",
1028+version: "2026.5.4",
1029+resolvedSpec: "@openclaw/codex@2026.5.4",
1030+},
1031+}),
1032+);
1033+});
1034+const warnMessages: string[] = [];
1035+1036+await updateNpmInstalledPlugins({
1037+config: {
1038+plugins: {
1039+installs: {
1040+broken: {
1041+source: "npm",
1042+spec: "@openclaw/broken-plugin",
1043+installPath: brokenInstallPath,
1044+resolvedName: "@openclaw/broken-plugin",
1045+resolvedVersion: "2026.5.4",
1046+resolvedSpec: "@openclaw/broken-plugin@2026.5.4",
1047+},
1048+ ...Object.fromEntries(
1049+plugins.map(({ pluginId, packageName }) => [
1050+pluginId,
1051+{
1052+source: "npm",
1053+spec: packageName,
1054+installPath: installPaths[pluginId],
1055+resolvedName: packageName,
1056+resolvedVersion: "2026.5.4",
1057+resolvedSpec: `${packageName}@2026.5.4`,
1058+integrity: "sha512-same",
1059+shasum: "same",
1060+},
1061+]),
1062+),
1063+},
1064+},
1065+},
1066+pluginIds: ["codex"],
1067+logger: { warn: (message) => warnMessages.push(message) },
1068+});
1069+1070+expect(installPluginFromNpmSpecMock).toHaveBeenCalledTimes(1);
1071+expect(fs.existsSync(peerLinkPath("brave"))).toBe(true);
1072+expect(fs.existsSync(peerLinkPath("codex"))).toBe(true);
1073+expect(warnMessages).toContainEqual(
1074+expect.stringContaining('Could not repair openclaw peer link for "broken"'),
1075+);
1076+});
1077+9971078it("refreshes legacy npm install records before skipping unchanged artifacts", async () => {
9981079const installPath = createInstalledPackageDir({
9991080name: "@martian-engineering/lossless-claw",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。