fix: quiet installed plugin override warnings

This commit is contained in:
Peter Steinberger
2026-04-27 14:53:31 +01:00
parent 2e99c1d227
commit 713cc74bff
3 changed files with 77 additions and 6 deletions

View File

@@ -30,6 +30,7 @@ Docs: https://docs.openclaw.ai
- Plugins/discovery: follow symlinked plugin directories in global and workspace plugin roots while keeping broken links ignored and existing package safety checks in place. Fixes #36754; carries forward #72695 and #63206. Thanks @Quackstro, @ming1523, and @xsfX20.
- Plugins/install: allow exact package-manager peer links back to the trusted OpenClaw host package during install security scans while continuing to block spoofed or nested escaping `node_modules` symlinks. Carries forward #70819. Thanks @fgabelmannjr.
- Plugins/install: resolve plugin install destinations from the active profile state dir across CLI, ClawHub, marketplace, local path, and channel setup installs, so `openclaw --profile <name> plugins install ...` no longer writes into the default profile. Fixes #69960; carries forward #69971. Thanks @FrancisLyman and @Sanjays2402.
- Plugins/registry: suppress duplicate-plugin startup warnings when a tracked npm-installed plugin intentionally overrides the bundled plugin with the same id. Carries forward #48673. Thanks @abdushsk.
- Plugins/startup: reuse canonical realpath lookups throughout each plugin discovery pass, including package and manifest boundary checks, so Windows npm-global startups no longer repeat expensive path resolution for the same plugin roots. Fixes #65733. Thanks @welfo-beo.
- Reply/link understanding: keep media and link preprocessing on stable runtime entrypoints and continue with raw message content if optional enrichment fails, so URL-bearing messages are no longer dropped after stale runtime chunk upgrades. Fixes #68466. Thanks @songshikang0111.
- Discord: persist routed model-picker overrides when the hidden `/model` dispatch succeeds but the bound thread session store is still stale, including LM Studio suffixed model ids. Carries forward #61473. Thanks @Nanako0129.

View File

@@ -370,7 +370,7 @@ describe("loadPluginManifestRegistry", () => {
expect(warning?.message).toContain(path.join(configDir, "index.ts"));
});
it("reports explicit installed globals as the effective duplicate winner", () => {
it("suppresses duplicate warnings for explicit installed globals overriding bundled plugins", () => {
const bundledDir = makeTempDir();
const globalDir = makeTempDir();
const manifest = { id: "zalouser", configSchema: { type: "object" } };
@@ -399,11 +399,41 @@ describe("loadPluginManifestRegistry", () => {
],
});
expect(
registry.diagnostics.some((diag) =>
diag.message.includes("bundled plugin will be overridden by global plugin"),
),
).toBe(true);
expect(countDuplicateWarnings(registry)).toBe(0);
expect(registry.plugins).toHaveLength(1);
expect(registry.plugins[0]?.origin).toBe("global");
});
it("suppresses duplicate warnings when the installed global is discovered before bundled", () => {
const bundledDir = makeTempDir();
const globalDir = makeTempDir();
const manifest = { id: "zalouser", configSchema: { type: "object" } };
writeManifest(bundledDir, manifest);
writeManifest(globalDir, manifest);
const registry = loadPluginManifestRegistry({
cache: false,
installRecords: {
zalouser: {
source: "npm",
installPath: globalDir,
},
},
candidates: [
createPluginCandidate({
idHint: "zalouser",
rootDir: globalDir,
origin: "global",
}),
createPluginCandidate({
idHint: "zalouser",
rootDir: bundledDir,
origin: "bundled",
}),
],
});
expect(countDuplicateWarnings(registry)).toBe(0);
expect(registry.plugins).toHaveLength(1);
expect(registry.plugins[0]?.origin).toBe("global");
});

View File

@@ -558,6 +558,34 @@ function resolveDuplicatePrecedenceRank(params: {
return 4;
}
function isIntentionalInstalledBundledDuplicate(params: {
pluginId: string;
left: PluginCandidate;
right: PluginCandidate;
config?: OpenClawConfig;
env: NodeJS.ProcessEnv;
installRecords: Record<string, PluginInstallRecord>;
}): boolean {
const leftIsInstalled = matchesInstalledPluginRecord({
pluginId: params.pluginId,
candidate: params.left,
config: params.config,
env: params.env,
installRecords: params.installRecords,
});
const rightIsInstalled = matchesInstalledPluginRecord({
pluginId: params.pluginId,
candidate: params.right,
config: params.config,
env: params.env,
installRecords: params.installRecords,
});
return (
(leftIsInstalled && params.right.origin === "bundled") ||
(rightIsInstalled && params.left.origin === "bundled")
);
}
export function loadPluginManifestRegistry(
params: {
config?: OpenClawConfig;
@@ -740,6 +768,18 @@ export function loadPluginManifestRegistry(
seenIds.set(manifest.id, { candidate, recordIndex: existing.recordIndex });
pushManifestCompatibilityDiagnostics({ record, diagnostics });
}
if (
isIntentionalInstalledBundledDuplicate({
pluginId: manifest.id,
left: candidate,
right: existing.candidate,
config,
env,
installRecords: getInstallRecords(),
})
) {
continue;
}
diagnostics.push({
level: "warn",
pluginId: manifest.id,