


















@@ -10,8 +10,20 @@ import {
1010type ResolvePluginControlPlaneContextParams,
1111} from "./plugin-control-plane-context.js";
1212import type { PluginMetadataSnapshot } from "./plugin-metadata-snapshot.types.js";
13+import { resolvePluginCacheInputs } from "./roots.js";
13141415type CurrentPluginMetadataSnapshotState = ReturnType<typeof getCurrentPluginMetadataSnapshotState>;
16+type CurrentPluginMetadataConfigCacheEntry = {
17+configFingerprint: string;
18+discoveryKey: string;
19+policyHash: string;
20+policyKey: string;
21+};
22+23+let currentPluginMetadataConfigIdentityCache = new WeakMap<
24+OpenClawConfig,
25+CurrentPluginMetadataConfigCacheEntry
26+>();
15271628export function resolvePluginMetadataControlPlaneFingerprint(
1729config?: OpenClawConfig,
@@ -29,6 +41,65 @@ export function isReusableCurrentPluginMetadataSnapshot(
2941return true;
3042}
314344+function resolveConfiguredPluginLoadPaths(
45+config: OpenClawConfig | undefined,
46+): readonly string[] | undefined {
47+const paths = config?.plugins?.load?.paths;
48+return Array.isArray(paths) ? paths : undefined;
49+}
50+51+function resolveCurrentPluginMetadataDiscoveryKey(params: {
52+config?: OpenClawConfig;
53+env: NodeJS.ProcessEnv;
54+workspaceDir?: string;
55+}): string {
56+return JSON.stringify(
57+resolvePluginCacheInputs({
58+env: params.env,
59+workspaceDir: params.workspaceDir,
60+loadPaths: [...(resolveConfiguredPluginLoadPaths(params.config) ?? [])],
61+}),
62+);
63+}
64+65+function resolveCurrentPluginMetadataPolicyKey(config: OpenClawConfig | undefined): string {
66+const plugins = config?.plugins;
67+const rawEntries = plugins?.entries;
68+const entries: Record<string, unknown> = {};
69+if (rawEntries && typeof rawEntries === "object" && !Array.isArray(rawEntries)) {
70+for (const [pluginId, entry] of Object.entries(rawEntries)) {
71+if (entry && typeof entry === "object" && !Array.isArray(entry)) {
72+const enabled = (entry as Record<string, unknown>).enabled;
73+if (enabled !== undefined) {
74+entries[pluginId] = enabled;
75+}
76+}
77+}
78+}
79+const channelPolicy: Record<string, boolean> = {};
80+const channels = config?.channels;
81+if (channels && typeof channels === "object" && !Array.isArray(channels)) {
82+for (const [channelId, value] of Object.entries(channels)) {
83+if (value && typeof value === "object" && !Array.isArray(value)) {
84+const enabled = (value as Record<string, unknown>).enabled;
85+if (typeof enabled === "boolean") {
86+channelPolicy[channelId] = enabled;
87+}
88+}
89+}
90+}
91+return JSON.stringify({
92+plugins: {
93+enabled: plugins?.enabled,
94+allow: plugins?.allow,
95+deny: plugins?.deny,
96+slots: plugins?.slots,
97+ entries,
98+},
99+channels: channelPolicy,
100+});
101+}
102+32103// Single-slot Gateway-owned handoff. Replace or clear it at lifecycle boundaries;
33104// never accumulate historical metadata snapshots here.
34105export function setCurrentPluginMetadataSnapshot(
@@ -40,6 +111,7 @@ export function setCurrentPluginMetadataSnapshot(
40111workspaceDir?: string;
41112} = {},
42113): void {
114+currentPluginMetadataConfigIdentityCache = new WeakMap();
43115const compatiblePolicyHashes = snapshot
44116 ? options.compatibleConfigs?.map((config) => resolveInstalledPluginIndexPolicyHash(config))
45117 : undefined;
@@ -66,9 +138,52 @@ export function setCurrentPluginMetadataSnapshot(
66138compatiblePolicyHashes,
67139compatibleConfigFingerprints,
68140);
141+if (!snapshot) {
142+return;
143+}
144+const env = options.env ?? process.env;
145+const workspaceDir = options.workspaceDir ?? snapshot.workspaceDir;
146+if (options.config) {
147+const policyHash = resolveInstalledPluginIndexPolicyHash(options.config);
148+const policyKey = resolveCurrentPluginMetadataPolicyKey(options.config);
149+const discoveryKey = resolveCurrentPluginMetadataDiscoveryKey({
150+config: options.config,
151+ env,
152+ workspaceDir,
153+});
154+currentPluginMetadataConfigIdentityCache.set(options.config, {
155+ policyHash,
156+ policyKey,
157+ discoveryKey,
158+configFingerprint: resolvePluginMetadataControlPlaneFingerprint(options.config, {
159+ env,
160+index: snapshot.index,
161+ policyHash,
162+ workspaceDir,
163+}),
164+});
165+}
166+for (const [index, config] of (options.compatibleConfigs ?? []).entries()) {
167+const policyHash = compatiblePolicyHashes?.[index];
168+const configFingerprint = compatibleConfigFingerprints?.[index];
169+if (!policyHash || !configFingerprint) {
170+continue;
171+}
172+currentPluginMetadataConfigIdentityCache.set(config, {
173+ policyHash,
174+policyKey: resolveCurrentPluginMetadataPolicyKey(config),
175+ configFingerprint,
176+discoveryKey: resolveCurrentPluginMetadataDiscoveryKey({
177+ config,
178+ env,
179+ workspaceDir,
180+}),
181+});
182+}
69183}
7018471185export function clearCurrentPluginMetadataSnapshot(): void {
186+currentPluginMetadataConfigIdentityCache = new WeakMap();
72187clearCurrentPluginMetadataSnapshotState();
73188}
74189@@ -79,6 +194,7 @@ export function captureCurrentPluginMetadataSnapshotState(): CurrentPluginMetada
79194export function restoreCurrentPluginMetadataSnapshotState(
80195state: CurrentPluginMetadataSnapshotState,
81196): void {
197+currentPluginMetadataConfigIdentityCache = new WeakMap();
82198setCurrentPluginMetadataSnapshotState(
83199state.snapshot,
84200state.configFingerprint,
@@ -106,25 +222,47 @@ export function getCurrentPluginMetadataSnapshot(
106222if (!snapshot) {
107223return undefined;
108224}
109-const requestedPolicyHash = params.config
110- ? resolveInstalledPluginIndexPolicyHash(params.config)
111- : undefined;
225+const env = params.env ?? process.env;
226+const requestedWorkspaceDir =
227+params.workspaceDir ??
228+(params.allowWorkspaceScopedSnapshot === true ? snapshot.workspaceDir : undefined);
229+const cachedConfig = params.config && currentPluginMetadataConfigIdentityCache.get(params.config);
230+const requestedPolicyKey =
231+params.config && cachedConfig
232+ ? resolveCurrentPluginMetadataPolicyKey(params.config)
233+ : undefined;
234+const requestedDiscoveryKey =
235+params.config && cachedConfig
236+ ? resolveCurrentPluginMetadataDiscoveryKey({
237+config: params.config,
238+ env,
239+workspaceDir: requestedWorkspaceDir,
240+})
241+ : undefined;
242+const canReuseCachedConfig =
243+cachedConfig !== undefined &&
244+cachedConfig.policyKey === requestedPolicyKey &&
245+cachedConfig.discoveryKey === requestedDiscoveryKey;
246+const requestedPolicyHash = canReuseCachedConfig
247+ ? cachedConfig.policyHash
248+ : params.config
249+ ? resolveInstalledPluginIndexPolicyHash(params.config)
250+ : undefined;
112251if (requestedPolicyHash && snapshot.policyHash !== requestedPolicyHash) {
113252const compatiblePolicies = new Set(compatiblePolicyHashes ?? []);
114253if (!compatiblePolicies.has(requestedPolicyHash)) {
115254return undefined;
116255}
117256}
118-const requestedWorkspaceDir =
119-params.workspaceDir ??
120-(params.allowWorkspaceScopedSnapshot === true ? snapshot.workspaceDir : undefined);
121257if (params.config) {
122-const requestedConfigFingerprint = resolvePluginMetadataControlPlaneFingerprint(params.config, {
123-env: params.env,
124-index: snapshot.index,
125-policyHash: requestedPolicyHash,
126-workspaceDir: requestedWorkspaceDir,
127-});
258+const requestedConfigFingerprint = canReuseCachedConfig
259+ ? cachedConfig.configFingerprint
260+ : resolvePluginMetadataControlPlaneFingerprint(params.config, {
261+ env,
262+index: snapshot.index,
263+policyHash: requestedPolicyHash,
264+workspaceDir: requestedWorkspaceDir,
265+});
128266const compatibleFingerprints = new Set(compatibleConfigFingerprints ?? []);
129267const fingerprintMatches =
130268configFingerprint === requestedConfigFingerprint ||
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。