fix: prefer bundled channel plugins over npm duplicates (#40094)

* fix: prefer bundled channel plugins over npm duplicates

* fix: tighten bundled plugin review follow-ups

* fix: address check gate follow-ups

* docs: add changelog for bundled plugin install fix

* fix: align lifecycle test formatting with CI oxfmt
This commit is contained in:
Tak Hoffman
2026-03-08 13:00:24 -05:00
committed by GitHub
parent 6c9b49a10b
commit 74624e619d
9 changed files with 343 additions and 50 deletions

View File

@@ -1,6 +1,7 @@
import { describe, expect, it, vi } from "vitest";
import { PLUGIN_INSTALL_ERROR_CODE } from "../plugins/install.js";
import {
resolveBundledInstallPlanForCatalogEntry,
resolveBundledInstallPlanBeforeNpm,
resolveBundledInstallPlanForNpmFailure,
} from "./plugin-install-plan.js";
@@ -34,6 +35,53 @@ describe("plugin install plan helpers", () => {
expect(result).toBeNull();
});
it("prefers bundled catalog plugin by id before npm spec", () => {
const findBundledSource = vi
.fn()
.mockImplementation(({ kind, value }: { kind: "pluginId" | "npmSpec"; value: string }) => {
if (kind === "pluginId" && value === "voice-call") {
return {
pluginId: "voice-call",
localPath: "/tmp/extensions/voice-call",
npmSpec: "@openclaw/voice-call",
};
}
return undefined;
});
const result = resolveBundledInstallPlanForCatalogEntry({
pluginId: "voice-call",
npmSpec: "@openclaw/voice-call",
findBundledSource,
});
expect(findBundledSource).toHaveBeenCalledWith({ kind: "pluginId", value: "voice-call" });
expect(result?.bundledSource.localPath).toBe("/tmp/extensions/voice-call");
});
it("rejects npm-spec matches that resolve to a different plugin id", () => {
const findBundledSource = vi
.fn()
.mockImplementation(({ kind }: { kind: "pluginId" | "npmSpec"; value: string }) => {
if (kind === "npmSpec") {
return {
pluginId: "not-voice-call",
localPath: "/tmp/extensions/not-voice-call",
npmSpec: "@openclaw/voice-call",
};
}
return undefined;
});
const result = resolveBundledInstallPlanForCatalogEntry({
pluginId: "voice-call",
npmSpec: "@openclaw/voice-call",
findBundledSource,
});
expect(result).toBeNull();
});
it("uses npm-spec bundled fallback only for package-not-found", () => {
const findBundledSource = vi.fn().mockReturnValue({
pluginId: "voice-call",