





















@@ -10,6 +10,10 @@ import {
1010type InstalledPluginIndexRecordStoreOptions,
1111} from "../plugins/installed-plugin-index-records.js";
1212import { loadInstalledPluginIndex } from "../plugins/installed-plugin-index.js";
13+import {
14+auditOpenClawPeerDependenciesInManagedNpmRoot,
15+relinkOpenClawPeerDependenciesInManagedNpmRoot,
16+} from "../plugins/plugin-peer-link.js";
1317import { refreshPluginRegistry } from "../plugins/plugin-registry.js";
1418import { note } from "../terminal/note.js";
1519import { shortenHomePath } from "../utils.js";
@@ -35,6 +39,11 @@ type StaleManagedNpmBundledPlugin = {
3539version?: string;
3640};
374142+type PluginRegistryDoctorNoteLogger = {
43+info: (message: string) => void;
44+warn: (message: string) => void;
45+};
46+3847function isRecord(value: unknown): value is Record<string, unknown> {
3948return typeof value === "object" && value !== null && !Array.isArray(value);
4049}
@@ -57,6 +66,12 @@ function readStringMap(value: unknown): Record<string, string> {
5766return result;
5867}
596869+function resolveManagedPluginNpmRoot(params: PluginRegistryDoctorRepairParams): string {
70+return params.stateDir
71+ ? path.join(params.stateDir, "npm")
72+ : resolveDefaultPluginNpmDir(params.env);
73+}
74+6075function deleteObjectKey(record: Record<string, unknown>, key: string): boolean {
6176if (!Object.prototype.hasOwnProperty.call(record, key)) {
6277return false;
@@ -87,9 +102,7 @@ function listStaleManagedNpmBundledPlugins(
87102const bundledByPackage = new Map(
88103currentBundled.map((plugin) => [plugin.packageName, plugin] as const),
89104);
90-const npmRoot = params.stateDir
91- ? path.join(params.stateDir, "npm")
92- : resolveDefaultPluginNpmDir(params.env);
105+const npmRoot = resolveManagedPluginNpmRoot(params);
93106const npmPackageJsonPath = path.join(npmRoot, "package.json");
94107const dependencies = readStringMap(readJsonObject(npmPackageJsonPath)?.dependencies);
95108const stale: StaleManagedNpmBundledPlugin[] = [];
@@ -228,6 +241,54 @@ export function maybeRepairStaleManagedNpmBundledPlugins(
228241return true;
229242}
230243244+export async function maybeRepairManagedNpmOpenClawPeerLinks(
245+params: PluginRegistryDoctorRepairParams,
246+): Promise<boolean> {
247+const npmRoot = resolveManagedPluginNpmRoot(params);
248+if (!params.prompter.shouldRepair) {
249+const audit = await auditOpenClawPeerDependenciesInManagedNpmRoot({ npmRoot });
250+if (audit.broken > 0) {
251+note(
252+[
253+"Managed npm OpenClaw host peer links need repair:",
254+ ...audit.issues.map((issue) => `- ${issue.packageName}: ${issue.reason}`),
255+`Repair with ${formatCliCommand("openclaw doctor --fix")} to relink managed npm plugin packages.`,
256+].join("\n"),
257+"Plugin registry",
258+);
259+}
260+return false;
261+}
262+263+const messages: { level: "info" | "warn"; message: string }[] = [];
264+const logger: PluginRegistryDoctorNoteLogger = {
265+info: (message) => messages.push({ level: "info", message }),
266+warn: (message) => messages.push({ level: "warn", message }),
267+};
268+const result = await relinkOpenClawPeerDependenciesInManagedNpmRoot({
269+ npmRoot,
270+ logger,
271+});
272+273+if (result.repaired > 0) {
274+note(
275+`Repaired OpenClaw host peer link(s) for ${result.repaired} managed npm plugin package(s).`,
276+"Plugin registry",
277+);
278+}
279+const warnings = messages
280+.filter((message) => message.level === "warn")
281+.map((message) => `- ${message.message}`);
282+if (warnings.length > 0) {
283+note(
284+["Could not repair all managed npm OpenClaw host peer links:", ...warnings].join("\n"),
285+"Plugin registry",
286+);
287+}
288+289+return result.repaired > 0;
290+}
291+231292async function loadInstallRecordsWithoutPluginIds(
232293params: PluginRegistryDoctorRepairParams,
233294pluginIds: readonly string[],
@@ -262,6 +323,7 @@ export async function maybeRepairPluginRegistryState(
262323(plugin) => plugin.pluginId,
263324);
264325const removedStaleManagedNpmBundledPlugins = maybeRepairStaleManagedNpmBundledPlugins(params);
326+const repairedManagedNpmOpenClawPeerLinks = await maybeRepairManagedNpmOpenClawPeerLinks(params);
265327if (!params.prompter.shouldRepair) {
266328if (preflight.action === "migrate") {
267329note(
@@ -288,7 +350,11 @@ export async function maybeRepairPluginRegistryState(
288350return params.config;
289351}
290352291-if (preflight.action === "skip-existing" || removedStaleManagedNpmBundledPlugins) {
353+if (
354+preflight.action === "skip-existing" ||
355+removedStaleManagedNpmBundledPlugins ||
356+repairedManagedNpmOpenClawPeerLinks
357+) {
292358const index = await refreshPluginRegistry({
293359 ...migrationParams,
294360reason: "migration",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。