

























@@ -1,21 +1,108 @@
1+import fs from "node:fs";
12import path from "node:path";
23import { formatCliCommand } from "../cli/command-format.js";
34import type { OpenClawConfig } from "../config/types.openclaw.js";
45import { resolveOpenClawPackageRootSync } from "../infra/openclaw-root.js";
6+import { resolveBundledPluginsDir } from "../plugins/bundled-dir.js";
57import {
68createBundledRuntimeDepsWritableInstallSpecs,
79repairBundledRuntimeDepsInstallRootAsync,
810resolveBundledRuntimeDependencyPackageInstallRootPlan,
911scanBundledPluginRuntimeDeps,
1012type BundledRuntimeDepsInstallParams,
1113} from "../plugins/bundled-runtime-deps.js";
14+import { normalizePluginsConfig } from "../plugins/config-state.js";
1215import { resolveEffectivePluginIds } from "../plugins/effective-plugin-ids.js";
16+import { passesManifestOwnerBasePolicy } from "../plugins/manifest-owner-policy.js";
1317import type { RuntimeEnv } from "../runtime.js";
1418import { note } from "../terminal/note.js";
1519import type { DoctorPrompter } from "./doctor-prompter.js";
16201721const RUNTIME_DEPS_INSTALL_HEARTBEAT_MS = 15_000;
182223+function filterPluginIdsPresentInBundledTree(
24+bundledPluginsDir: string,
25+pluginIds: readonly string[],
26+): string[] | undefined {
27+const present = pluginIds.filter((pluginId) => {
28+if (path.basename(pluginId) !== pluginId) {
29+return false;
30+}
31+return fs.existsSync(path.join(bundledPluginsDir, pluginId));
32+});
33+return present.length > 0 ? present : undefined;
34+}
35+36+function collectPackagedRuntimeDepsRepairPluginIds(params: {
37+bundledPluginsDir: string;
38+config: OpenClawConfig;
39+includeConfiguredChannels?: boolean;
40+}): string[] {
41+if (!fs.existsSync(params.bundledPluginsDir)) {
42+return [];
43+}
44+const plugins = normalizePluginsConfig(params.config.plugins);
45+const ids = new Set<string>();
46+for (const entry of fs.readdirSync(params.bundledPluginsDir, { withFileTypes: true })) {
47+if (!entry.isDirectory()) {
48+continue;
49+}
50+const pluginDir = path.join(params.bundledPluginsDir, entry.name);
51+let manifest: Record<string, unknown>;
52+try {
53+manifest = JSON.parse(
54+fs.readFileSync(path.join(pluginDir, "openclaw.plugin.json"), "utf-8"),
55+) as Record<string, unknown>;
56+} catch {
57+continue;
58+}
59+const pluginId = typeof manifest.id === "string" && manifest.id ? manifest.id : entry.name;
60+if (
61+!passesManifestOwnerBasePolicy({
62+plugin: { id: pluginId },
63+normalizedConfig: plugins,
64+allowRestrictiveAllowlistBypass: true,
65+})
66+) {
67+continue;
68+}
69+if (plugins.allow.includes(pluginId) || plugins.entries[pluginId]?.enabled === true) {
70+ids.add(pluginId);
71+continue;
72+}
73+const channels = Array.isArray(manifest.channels)
74+ ? manifest.channels.filter((channel): channel is string => typeof channel === "string")
75+ : [];
76+if (
77+channels.some((channelId) => {
78+const channelConfig = (params.config.channels as Record<string, unknown> | undefined)?.[
79+channelId
80+];
81+if (!channelConfig || typeof channelConfig !== "object" || Array.isArray(channelConfig)) {
82+return false;
83+}
84+if ((channelConfig as { enabled?: unknown }).enabled === false) {
85+return false;
86+}
87+return (
88+(channelConfig as { enabled?: unknown }).enabled === true ||
89+params.includeConfiguredChannels === true
90+);
91+})
92+) {
93+ids.add(pluginId);
94+continue;
95+}
96+const providers = Array.isArray(manifest.providers)
97+ ? manifest.providers.filter((provider): provider is string => typeof provider === "string")
98+ : [];
99+if (manifest.enabledByDefault === true && providers.length === 0 && channels.length === 0) {
100+ids.add(pluginId);
101+}
102+}
103+return [...ids].toSorted((left, right) => left.localeCompare(right));
104+}
105+19106function formatElapsedMs(elapsedMs: number): string {
20107if (elapsedMs < 1000) {
21108return `${elapsedMs}ms`;
@@ -56,13 +143,23 @@ export async function maybeRepairBundledPluginRuntimeDeps(params: {
56143const env = params.env ?? process.env;
57144const bundledPluginsDir = path.join(packageRoot, "dist", "extensions");
58145const effectivePluginIds = params.config
59- ? resolveEffectivePluginIds({
60-config: params.config,
61-env: {
62- ...env,
63-OPENCLAW_BUNDLED_PLUGINS_DIR: bundledPluginsDir,
64-},
65-})
146+ ? resolveBundledPluginsDir({ ...env, OPENCLAW_BUNDLED_PLUGINS_DIR: bundledPluginsDir }) ===
147+bundledPluginsDir
148+ ? filterPluginIdsPresentInBundledTree(
149+bundledPluginsDir,
150+resolveEffectivePluginIds({
151+config: params.config,
152+env: {
153+ ...env,
154+OPENCLAW_BUNDLED_PLUGINS_DIR: bundledPluginsDir,
155+},
156+}),
157+)
158+ : collectPackagedRuntimeDepsRepairPluginIds({
159+ bundledPluginsDir,
160+config: params.config,
161+includeConfiguredChannels: params.includeConfiguredChannels,
162+})
66163 : undefined;
67164const { deps, missing, conflicts } = scanBundledPluginRuntimeDeps({
68165 packageRoot,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。