





















@@ -923,13 +923,13 @@ describe("installPluginFromNpmSpec", () => {
923923expect(managedManifest.dependencies?.["@openclaw/codex"]).toBeUndefined();
924924});
925925926-it("rejects npm plugins whose package compatibility requires a newer host", async () => {
926+it("rejects exact npm plugins whose package compatibility requires a newer host", async () => {
927927const stateDir = suiteTempRootTracker.makeTempDir();
928928const npmRoot = path.join(stateDir, "npm");
929929vi.stubEnv("OPENCLAW_COMPATIBILITY_HOST_VERSION", "2026.5.10-beta.1");
930930931931mockNpmViewAndInstall({
932-spec: "@openclaw/whatsapp",
932+spec: "@openclaw/whatsapp@2026.5.27",
933933packageName: "@openclaw/whatsapp",
934934version: "2026.5.27",
935935pluginId: "whatsapp",
@@ -943,7 +943,7 @@ describe("installPluginFromNpmSpec", () => {
943943});
944944945945const result = await installPluginFromNpmSpec({
946-spec: "@openclaw/whatsapp",
946+spec: "@openclaw/whatsapp@2026.5.27",
947947npmDir: npmRoot,
948948logger: { info: () => {}, warn: () => {} },
949949});
@@ -963,12 +963,75 @@ describe("installPluginFromNpmSpec", () => {
963963).toBe(false);
964964});
965965966-it("preserves an existing npm plugin when update metadata requires a newer host", async () => {
966+it("installs the newest compatible npm version for unpinned plugins", async () => {
967+const stateDir = suiteTempRootTracker.makeTempDir();
968+const npmRoot = path.join(stateDir, "npm");
969+const warnings: string[] = [];
970+vi.stubEnv("OPENCLAW_COMPATIBILITY_HOST_VERSION", "2026.5.10-beta.1");
971+972+mockNpmViewAndInstallMany([
973+{
974+spec: "@openclaw/whatsapp",
975+packageName: "@openclaw/whatsapp",
976+version: "2026.5.27",
977+pluginId: "whatsapp",
978+ npmRoot,
979+versions: ["2026.5.26", "2026.5.27"],
980+openclaw: {
981+extensions: ["./dist/index.js"],
982+install: { minHostVersion: ">=2026.4.25" },
983+compat: { pluginApi: ">=2026.5.27" },
984+},
985+},
986+{
987+spec: "@openclaw/whatsapp@2026.5.26",
988+packageName: "@openclaw/whatsapp",
989+version: "2026.5.26",
990+pluginId: "whatsapp",
991+ npmRoot,
992+expectedDependencySpec: "2026.5.26",
993+openclaw: {
994+extensions: ["./dist/index.js"],
995+install: { minHostVersion: ">=2026.4.25" },
996+compat: { pluginApi: ">=2026.5.10-beta.1" },
997+},
998+},
999+]);
1000+1001+const result = await installPluginFromNpmSpec({
1002+spec: "@openclaw/whatsapp",
1003+npmDir: npmRoot,
1004+logger: { info: () => {}, warn: (message) => warnings.push(message) },
1005+});
1006+1007+expect(result.ok).toBe(true);
1008+if (!result.ok) {
1009+return;
1010+}
1011+expect(result.npmResolution?.resolvedSpec).toBe("@openclaw/whatsapp@2026.5.26");
1012+expect(result.npmResolution?.version).toBe("2026.5.26");
1013+expect(warnings.join("\n")).toContain("using newest compatible @openclaw/whatsapp@2026.5.26");
1014+expect(
1015+JSON.parse(
1016+fs.readFileSync(
1017+path.join(resolveTestPluginPackageDir(npmRoot, "@openclaw/whatsapp"), "package.json"),
1018+"utf8",
1019+),
1020+).version,
1021+).toBe("2026.5.26");
1022+});
1023+1024+it("preserves an existing npm plugin by resolving update metadata to a compatible version", async () => {
9671025const stateDir = suiteTempRootTracker.makeTempDir();
9681026const npmRoot = path.join(stateDir, "npm");
969-fs.mkdirSync(npmRoot, { recursive: true });
1027+const npmProjectRoot = resolvePluginNpmProjectDir({
1028+npmDir: npmRoot,
1029+packageName: "@openclaw/whatsapp",
1030+});
1031+const warnings: string[] = [];
1032+fs.mkdirSync(npmProjectRoot, { recursive: true });
9701033fs.writeFileSync(
971-path.join(npmRoot, "package.json"),
1034+path.join(npmProjectRoot, "package.json"),
9721035JSON.stringify({
9731036private: true,
9741037dependencies: {
@@ -981,54 +1044,71 @@ describe("installPluginFromNpmSpec", () => {
9811044packageName: "@openclaw/whatsapp",
9821045version: "2026.5.26",
9831046pluginId: "whatsapp",
984- npmRoot,
1047+npmRoot: npmProjectRoot,
9851048openclaw: {
9861049extensions: ["./dist/index.js"],
9871050install: { minHostVersion: ">=2026.4.25" },
988-compat: { pluginApi: ">=2026.5.26" },
1051+compat: { pluginApi: ">=2026.5.10-beta.1" },
9891052},
9901053});
9911054vi.stubEnv("OPENCLAW_COMPATIBILITY_HOST_VERSION", "2026.5.10-beta.1");
992-mockNpmViewAndInstall({
993-spec: "@openclaw/whatsapp",
994-packageName: "@openclaw/whatsapp",
995-version: "2026.5.27",
996-pluginId: "whatsapp",
997- npmRoot,
998-openclaw: {
999-extensions: ["./dist/index.js"],
1000-install: { minHostVersion: ">=2026.4.25" },
1001-compat: { pluginApi: ">=2026.5.27" },
1055+mockNpmViewAndInstallMany([
1056+{
1057+spec: "@openclaw/whatsapp",
1058+packageName: "@openclaw/whatsapp",
1059+version: "2026.5.27",
1060+pluginId: "whatsapp",
1061+ npmRoot,
1062+versions: ["2026.5.26", "2026.5.27"],
1063+openclaw: {
1064+extensions: ["./dist/index.js"],
1065+install: { minHostVersion: ">=2026.4.25" },
1066+compat: { pluginApi: ">=2026.5.27" },
1067+},
10021068},
1003-});
1069+{
1070+spec: "@openclaw/whatsapp@2026.5.26",
1071+packageName: "@openclaw/whatsapp",
1072+version: "2026.5.26",
1073+pluginId: "whatsapp",
1074+ npmRoot,
1075+expectedDependencySpec: "2026.5.26",
1076+openclaw: {
1077+extensions: ["./dist/index.js"],
1078+install: { minHostVersion: ">=2026.4.25" },
1079+compat: { pluginApi: ">=2026.5.10-beta.1" },
1080+},
1081+},
1082+]);
1004108310051084const result = await installPluginFromNpmSpec({
10061085spec: "@openclaw/whatsapp",
10071086npmDir: npmRoot,
10081087mode: "update",
1009-logger: { info: () => {}, warn: () => {} },
1088+logger: { info: () => {}, warn: (message) => warnings.push(message) },
10101089});
101110901012-expect(result.ok).toBe(false);
1013-if (result.ok) {
1091+expect(result.ok).toBe(true);
1092+if (!result.ok) {
10141093return;
10151094}
1016-expect(result.code).toBe(PLUGIN_INSTALL_ERROR_CODE.INCOMPATIBLE_PLUGIN_API);
1095+expect(result.npmResolution?.resolvedSpec).toBe("@openclaw/whatsapp@2026.5.26");
1096+expect(warnings.join("\n")).toContain("using newest compatible @openclaw/whatsapp@2026.5.26");
10171097expect(
10181098JSON.parse(
10191099fs.readFileSync(
1020-path.join(npmRoot, "node_modules", "@openclaw", "whatsapp", "package.json"),
1100+path.join(resolveTestPluginPackageDir(npmRoot, "@openclaw/whatsapp"), "package.json"),
10211101"utf8",
10221102),
10231103).version,
10241104).toBe("2026.5.26");
10251105const managedManifest = JSON.parse(
1026-fs.readFileSync(path.join(npmRoot, "package.json"), "utf8"),
1106+fs.readFileSync(path.join(npmProjectRoot, "package.json"), "utf8"),
10271107) as { dependencies?: Record<string, string> };
10281108expect(managedManifest.dependencies?.["@openclaw/whatsapp"]).toBe("2026.5.26");
10291109expect(
10301110runCommandWithTimeoutMock.mock.calls.some(([argv]) => isManagedNpmInstallCommand(argv)),
1031-).toBe(false);
1111+).toBe(true);
10321112});
1033111310341114it.runIf(process.platform !== "win32")(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。