fix(plugins): warn when npm install is shadowed

This commit is contained in:
Yuyummy
2026-05-03 18:20:05 +08:00
committed by Peter Steinberger
parent 0459bff556
commit f73a614d66
4 changed files with 177 additions and 1 deletions

View File

@@ -5,6 +5,7 @@ import { tracePluginLifecyclePhaseAsync } from "../plugins/plugin-lifecycle-trac
import { defaultRuntime } from "../runtime.js";
import { formatDocsLink } from "../terminal/links.js";
import { theme } from "../terminal/theme.js";
import { shortenHomeInString } from "../utils.js";
import type { PluginInspectOptions } from "./plugins-inspect-command.js";
import type { PluginsListOptions } from "./plugins-list-command.js";
import { applyParentDefaultHelpAction } from "./program/parent-default-help.js";
@@ -62,6 +63,14 @@ function matchesPluginId(plugin: { id: string }, id: string) {
return plugin.id === id;
}
function isConfigSelectedShadowDiagnostic(entry: { level?: string; message?: string }): boolean {
return (
entry.level === "warn" &&
typeof entry.message === "string" &&
entry.message.includes("duplicate plugin id resolved by explicit config-selected plugin")
);
}
export function registerPluginsCli(program: Command) {
const plugins = program
.command("plugins")
@@ -336,9 +345,15 @@ export function registerPluginsCli(program: Command) {
const report = buildPluginDiagnosticsReport({ effectiveOnly: true });
const errors = report.plugins.filter((p) => p.status === "error");
const diags = report.diagnostics.filter((d) => d.level === "error");
const shadowed = report.diagnostics.filter(isConfigSelectedShadowDiagnostic);
const compatibility = buildPluginCompatibilityNotices({ report });
if (errors.length === 0 && diags.length === 0 && compatibility.length === 0) {
if (
errors.length === 0 &&
diags.length === 0 &&
shadowed.length === 0 &&
compatibility.length === 0
) {
defaultRuntime.log("No plugin issues detected.");
return;
}
@@ -361,6 +376,31 @@ export function registerPluginsCli(program: Command) {
lines.push(`- ${target}${diag.message}`);
}
}
if (shadowed.length > 0) {
if (lines.length > 0) {
lines.push("");
}
lines.push(theme.warn("Plugin source shadowing:"));
for (const diag of shadowed) {
const active = report.plugins.find((plugin) => plugin.id === diag.pluginId);
const target = diag.pluginId ? `${diag.pluginId}: ` : "";
lines.push(`- ${target}${diag.message}`);
if (active) {
lines.push(` active: ${shortenHomeInString(active.source)} (${active.origin})`);
if (active.status === "error") {
lines.push(` active status: error${active.error ? `: ${active.error}` : ""}`);
}
}
if (diag.source) {
lines.push(` shadowed: ${shortenHomeInString(diag.source)}`);
}
lines.push(" repair:");
lines.push(" openclaw plugins inspect " + (diag.pluginId ?? "<plugin-id>"));
lines.push(" edit or remove the config-selected plugin source");
lines.push(" openclaw plugins registry --refresh");
lines.push(" openclaw gateway restart --force");
}
}
if (compatibility.length > 0) {
if (lines.length > 0) {
lines.push("");