mirror of https://github.com/openclaw/openclaw.git
refactor: unify plugin activation source plumbing
This commit is contained in:
parent
975d2ddce2
commit
cd38eba316
|
|
@ -8,6 +8,7 @@ import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
|||
import { resolveBundledPluginsDir } from "../plugins/bundled-dir.js";
|
||||
import { resolveBundledPluginPublicSurfacePath } from "../plugins/bundled-plugin-metadata.js";
|
||||
import {
|
||||
createPluginActivationSource,
|
||||
normalizePluginsConfig,
|
||||
resolveEffectivePluginActivationState,
|
||||
} from "../plugins/config-state.js";
|
||||
|
|
@ -44,7 +45,7 @@ let cachedBoundaryResolvedConfig:
|
|||
rawConfig: OpenClawConfig;
|
||||
config: OpenClawConfig;
|
||||
normalizedPluginsConfig: ReturnType<typeof normalizePluginsConfig>;
|
||||
sourceNormalizedPluginsConfig: ReturnType<typeof normalizePluginsConfig>;
|
||||
activationSource: ReturnType<typeof createPluginActivationSource>;
|
||||
autoEnabledReasons: Record<string, string[]>;
|
||||
}
|
||||
| undefined;
|
||||
|
|
@ -157,7 +158,7 @@ function getFacadeBoundaryResolvedConfig() {
|
|||
rawConfig,
|
||||
config,
|
||||
normalizedPluginsConfig: normalizePluginsConfig(config?.plugins),
|
||||
sourceNormalizedPluginsConfig: normalizePluginsConfig(rawConfig?.plugins),
|
||||
activationSource: createPluginActivationSource({ config: rawConfig }),
|
||||
autoEnabledReasons: autoEnabled.autoEnabledReasons,
|
||||
};
|
||||
cachedBoundaryRawConfig = rawConfig;
|
||||
|
|
@ -202,21 +203,15 @@ function resolveBundledPluginPublicSurfaceAccess(params: {
|
|||
reason: `no bundled plugin manifest found for ${params.dirName}`,
|
||||
};
|
||||
}
|
||||
const {
|
||||
rawConfig,
|
||||
config,
|
||||
normalizedPluginsConfig,
|
||||
sourceNormalizedPluginsConfig,
|
||||
autoEnabledReasons,
|
||||
} = getFacadeBoundaryResolvedConfig();
|
||||
const { config, normalizedPluginsConfig, activationSource, autoEnabledReasons } =
|
||||
getFacadeBoundaryResolvedConfig();
|
||||
const activationState = resolveEffectivePluginActivationState({
|
||||
id: manifestRecord.id,
|
||||
origin: manifestRecord.origin,
|
||||
config: normalizedPluginsConfig,
|
||||
rootConfig: config,
|
||||
enabledByDefault: manifestRecord.enabledByDefault,
|
||||
sourceConfig: sourceNormalizedPluginsConfig,
|
||||
sourceRootConfig: rawConfig,
|
||||
activationSource,
|
||||
autoEnabledReason: autoEnabledReasons[manifestRecord.id]?.[0],
|
||||
});
|
||||
if (activationState.enabled) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { listPotentialConfiguredChannelIds } from "../channels/config-presence.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { normalizePluginsConfig, resolveEffectivePluginActivationState } from "./config-state.js";
|
||||
import {
|
||||
createPluginActivationSource,
|
||||
normalizePluginsConfig,
|
||||
resolveEffectivePluginActivationState,
|
||||
} from "./config-state.js";
|
||||
import { loadPluginManifestRegistry, type PluginManifestRecord } from "./manifest-registry.js";
|
||||
import { hasKind } from "./slots.js";
|
||||
|
||||
|
|
@ -83,9 +87,12 @@ export function resolveGatewayStartupPluginIds(params: {
|
|||
listPotentialConfiguredChannelIds(params.config, params.env).map((id) => id.trim()),
|
||||
);
|
||||
const pluginsConfig = normalizePluginsConfig(params.config.plugins);
|
||||
const sourcePluginsConfig = normalizePluginsConfig(
|
||||
(params.activationSourceConfig ?? params.config).plugins,
|
||||
);
|
||||
// Startup must classify allowlist exceptions against the raw config snapshot,
|
||||
// not the auto-enabled effective snapshot, or configured-only channels can be
|
||||
// misclassified as explicit enablement.
|
||||
const activationSource = createPluginActivationSource({
|
||||
config: params.activationSourceConfig ?? params.config,
|
||||
});
|
||||
return loadPluginManifestRegistry({
|
||||
config: params.config,
|
||||
workspaceDir: params.workspaceDir,
|
||||
|
|
@ -104,8 +111,7 @@ export function resolveGatewayStartupPluginIds(params: {
|
|||
config: pluginsConfig,
|
||||
rootConfig: params.config,
|
||||
enabledByDefault: plugin.enabledByDefault,
|
||||
sourceConfig: sourcePluginsConfig,
|
||||
sourceRootConfig: params.activationSourceConfig ?? params.config,
|
||||
activationSource,
|
||||
});
|
||||
if (!activationState.enabled) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
createPluginActivationSource,
|
||||
normalizePluginsConfig,
|
||||
resolveEffectiveEnableState,
|
||||
resolveEnableState,
|
||||
|
|
@ -206,7 +207,7 @@ describe("resolveEffectiveEnableState", () => {
|
|||
describe("resolveEffectivePluginActivationState", () => {
|
||||
it("distinguishes explicit enablement from auto activation", () => {
|
||||
const rawConfig: NonNullable<
|
||||
Parameters<typeof resolveEffectivePluginActivationState>[0]["sourceRootConfig"]
|
||||
Parameters<typeof resolveEffectivePluginActivationState>[0]["rootConfig"]
|
||||
> = {
|
||||
channels: {
|
||||
telegram: {
|
||||
|
|
@ -231,8 +232,7 @@ describe("resolveEffectivePluginActivationState", () => {
|
|||
origin: "bundled",
|
||||
config: normalizePluginsConfig(effectiveConfig.plugins),
|
||||
rootConfig: effectiveConfig,
|
||||
sourceConfig: normalizePluginsConfig(rawConfig.plugins),
|
||||
sourceRootConfig: rawConfig,
|
||||
activationSource: createPluginActivationSource({ config: rawConfig }),
|
||||
autoEnabledReason: "telegram configured",
|
||||
}),
|
||||
).toEqual({
|
||||
|
|
@ -262,8 +262,7 @@ describe("resolveEffectivePluginActivationState", () => {
|
|||
origin: "bundled",
|
||||
config: normalizePluginsConfig(rawConfig.plugins),
|
||||
rootConfig: rawConfig,
|
||||
sourceConfig: normalizePluginsConfig(rawConfig.plugins),
|
||||
sourceRootConfig: rawConfig,
|
||||
activationSource: createPluginActivationSource({ config: rawConfig }),
|
||||
}),
|
||||
).toEqual({
|
||||
enabled: false,
|
||||
|
|
@ -309,8 +308,7 @@ describe("resolveEffectivePluginActivationState", () => {
|
|||
origin: "bundled",
|
||||
config: normalizePluginsConfig(rawConfig.plugins),
|
||||
rootConfig: rawConfig,
|
||||
sourceConfig: normalizePluginsConfig(rawConfig.plugins),
|
||||
sourceRootConfig: rawConfig,
|
||||
activationSource: createPluginActivationSource({ config: rawConfig }),
|
||||
}),
|
||||
).toEqual({
|
||||
enabled: false,
|
||||
|
|
@ -339,8 +337,7 @@ describe("resolveEffectivePluginActivationState", () => {
|
|||
origin: "bundled",
|
||||
config: normalizePluginsConfig(rawConfig.plugins),
|
||||
rootConfig: rawConfig,
|
||||
sourceConfig: normalizePluginsConfig(rawConfig.plugins),
|
||||
sourceRootConfig: rawConfig,
|
||||
activationSource: createPluginActivationSource({ config: rawConfig }),
|
||||
}),
|
||||
).toEqual({
|
||||
enabled: true,
|
||||
|
|
@ -369,8 +366,7 @@ describe("resolveEffectivePluginActivationState", () => {
|
|||
origin: "bundled",
|
||||
config: normalizePluginsConfig(rawConfig.plugins),
|
||||
rootConfig: rawConfig,
|
||||
sourceConfig: normalizePluginsConfig(rawConfig.plugins),
|
||||
sourceRootConfig: rawConfig,
|
||||
activationSource: createPluginActivationSource({ config: rawConfig }),
|
||||
}),
|
||||
).toEqual({
|
||||
enabled: false,
|
||||
|
|
@ -394,8 +390,7 @@ describe("resolveEffectivePluginActivationState", () => {
|
|||
origin: "bundled",
|
||||
config: normalizePluginsConfig(rawConfig.plugins),
|
||||
rootConfig: rawConfig,
|
||||
sourceConfig: normalizePluginsConfig(rawConfig.plugins),
|
||||
sourceRootConfig: rawConfig,
|
||||
activationSource: createPluginActivationSource({ config: rawConfig }),
|
||||
autoEnabledReason: "telegram configured",
|
||||
}),
|
||||
).toEqual({
|
||||
|
|
@ -427,8 +422,7 @@ describe("resolveEffectivePluginActivationState", () => {
|
|||
origin: "bundled",
|
||||
config: normalizePluginsConfig(effectiveConfig.plugins),
|
||||
rootConfig: effectiveConfig,
|
||||
sourceConfig: normalizePluginsConfig(sourceConfig.plugins),
|
||||
sourceRootConfig: sourceConfig,
|
||||
activationSource: createPluginActivationSource({ config: sourceConfig }),
|
||||
}),
|
||||
).toEqual({
|
||||
enabled: true,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@ export type PluginActivationState = {
|
|||
reason?: string;
|
||||
};
|
||||
|
||||
export type PluginActivationConfigSource = {
|
||||
plugins: NormalizedPluginsConfig;
|
||||
rootConfig?: OpenClawConfig;
|
||||
};
|
||||
|
||||
export type NormalizedPluginsConfig = {
|
||||
enabled: boolean;
|
||||
allow: string[];
|
||||
|
|
@ -162,6 +167,16 @@ export const normalizePluginsConfig = (
|
|||
};
|
||||
};
|
||||
|
||||
export function createPluginActivationSource(params: {
|
||||
config?: OpenClawConfig;
|
||||
plugins?: NormalizedPluginsConfig;
|
||||
}): PluginActivationConfigSource {
|
||||
return {
|
||||
plugins: params.plugins ?? normalizePluginsConfig(params.config?.plugins),
|
||||
rootConfig: params.config,
|
||||
};
|
||||
}
|
||||
|
||||
const hasExplicitMemorySlot = (plugins?: OpenClawConfig["plugins"]) =>
|
||||
Boolean(plugins?.slots && Object.prototype.hasOwnProperty.call(plugins.slots, "memory"));
|
||||
|
||||
|
|
@ -275,15 +290,20 @@ export function resolvePluginActivationState(params: {
|
|||
config: NormalizedPluginsConfig;
|
||||
rootConfig?: OpenClawConfig;
|
||||
enabledByDefault?: boolean;
|
||||
sourceConfig?: NormalizedPluginsConfig;
|
||||
sourceRootConfig?: OpenClawConfig;
|
||||
activationSource?: PluginActivationConfigSource;
|
||||
autoEnabledReason?: string;
|
||||
}): PluginActivationState {
|
||||
const activationSource =
|
||||
params.activationSource ??
|
||||
createPluginActivationSource({
|
||||
config: params.rootConfig,
|
||||
plugins: params.config,
|
||||
});
|
||||
const explicitSelection = resolveExplicitPluginSelection({
|
||||
id: params.id,
|
||||
origin: params.origin,
|
||||
config: params.sourceConfig ?? params.config,
|
||||
rootConfig: params.sourceRootConfig ?? params.rootConfig,
|
||||
config: activationSource.plugins,
|
||||
rootConfig: activationSource.rootConfig,
|
||||
});
|
||||
const explicitlyConfiguredBundledChannel =
|
||||
params.origin === "bundled" &&
|
||||
|
|
@ -451,8 +471,7 @@ export function resolveEffectiveEnableState(params: {
|
|||
config: NormalizedPluginsConfig;
|
||||
rootConfig?: OpenClawConfig;
|
||||
enabledByDefault?: boolean;
|
||||
sourceConfig?: NormalizedPluginsConfig;
|
||||
sourceRootConfig?: OpenClawConfig;
|
||||
activationSource?: PluginActivationConfigSource;
|
||||
}): { enabled: boolean; reason?: string } {
|
||||
const state = resolveEffectivePluginActivationState(params);
|
||||
return state.enabled ? { enabled: true } : { enabled: false, reason: state.reason };
|
||||
|
|
@ -464,8 +483,7 @@ export function resolveEffectivePluginActivationState(params: {
|
|||
config: NormalizedPluginsConfig;
|
||||
rootConfig?: OpenClawConfig;
|
||||
enabledByDefault?: boolean;
|
||||
sourceConfig?: NormalizedPluginsConfig;
|
||||
sourceRootConfig?: OpenClawConfig;
|
||||
activationSource?: PluginActivationConfigSource;
|
||||
autoEnabledReason?: string;
|
||||
}): PluginActivationState {
|
||||
return resolvePluginActivationState(params);
|
||||
|
|
|
|||
|
|
@ -15,10 +15,12 @@ import { inspectBundleMcpRuntimeSupport } from "./bundle-mcp.js";
|
|||
import { clearPluginCommands } from "./command-registry-state.js";
|
||||
import {
|
||||
applyTestPluginDefaults,
|
||||
createPluginActivationSource,
|
||||
normalizePluginsConfig,
|
||||
resolveEffectiveEnableState,
|
||||
resolveEffectivePluginActivationState,
|
||||
resolveMemorySlotDecision,
|
||||
type PluginActivationConfigSource,
|
||||
type NormalizedPluginsConfig,
|
||||
type PluginActivationState,
|
||||
} from "./config-state.js";
|
||||
|
|
@ -315,12 +317,11 @@ function resolveRuntimeSubagentMode(
|
|||
}
|
||||
|
||||
function buildActivationMetadataHash(params: {
|
||||
sourcePlugins: NormalizedPluginsConfig;
|
||||
sourceConfig?: OpenClawConfig;
|
||||
activationSource: PluginActivationConfigSource;
|
||||
autoEnabledReasons: Readonly<Record<string, string[]>>;
|
||||
}): string {
|
||||
const enabledSourceChannels = Object.entries(
|
||||
(params.sourceConfig?.channels as Record<string, unknown>) ?? {},
|
||||
(params.activationSource.rootConfig?.channels as Record<string, unknown>) ?? {},
|
||||
)
|
||||
.filter(([, value]) => {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
|
|
@ -330,7 +331,7 @@ function buildActivationMetadataHash(params: {
|
|||
})
|
||||
.map(([channelId]) => channelId)
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
const pluginEntryStates = Object.entries(params.sourcePlugins.entries)
|
||||
const pluginEntryStates = Object.entries(params.activationSource.plugins.entries)
|
||||
.map(([pluginId, entry]) => [pluginId, entry?.enabled ?? null] as const)
|
||||
.toSorted(([left], [right]) => left.localeCompare(right));
|
||||
const autoEnableReasonEntries = Object.entries(params.autoEnabledReasons)
|
||||
|
|
@ -340,10 +341,10 @@ function buildActivationMetadataHash(params: {
|
|||
return createHash("sha256")
|
||||
.update(
|
||||
JSON.stringify({
|
||||
enabled: params.sourcePlugins.enabled,
|
||||
allow: params.sourcePlugins.allow,
|
||||
deny: params.sourcePlugins.deny,
|
||||
memorySlot: params.sourcePlugins.slots.memory,
|
||||
enabled: params.activationSource.plugins.enabled,
|
||||
allow: params.activationSource.plugins.allow,
|
||||
deny: params.activationSource.plugins.deny,
|
||||
memorySlot: params.activationSource.plugins.slots.memory,
|
||||
entries: pluginEntryStates,
|
||||
enabledChannels: enabledSourceChannels,
|
||||
autoEnabledReasons: autoEnableReasonEntries,
|
||||
|
|
@ -374,7 +375,9 @@ function resolvePluginLoadCacheContext(options: PluginLoadOptions = {}) {
|
|||
const cfg = applyTestPluginDefaults(options.config ?? {}, env);
|
||||
const activationSourceConfig = options.activationSourceConfig ?? options.config ?? {};
|
||||
const normalized = normalizePluginsConfig(cfg.plugins);
|
||||
const activationSourceNormalized = normalizePluginsConfig(activationSourceConfig.plugins);
|
||||
const activationSource = createPluginActivationSource({
|
||||
config: activationSourceConfig,
|
||||
});
|
||||
const onlyPluginIds = normalizeScopedPluginIds(options.onlyPluginIds);
|
||||
const includeSetupOnlyChannelPlugins = options.includeSetupOnlyChannelPlugins === true;
|
||||
const preferSetupRuntimeForChannelPlugins = options.preferSetupRuntimeForChannelPlugins === true;
|
||||
|
|
@ -383,8 +386,7 @@ function resolvePluginLoadCacheContext(options: PluginLoadOptions = {}) {
|
|||
workspaceDir: options.workspaceDir,
|
||||
plugins: normalized,
|
||||
activationMetadataKey: buildActivationMetadataHash({
|
||||
sourcePlugins: activationSourceNormalized,
|
||||
sourceConfig: activationSourceConfig,
|
||||
activationSource,
|
||||
autoEnabledReasons: options.autoEnabledReasons ?? {},
|
||||
}),
|
||||
installs: cfg.plugins?.installs,
|
||||
|
|
@ -402,7 +404,7 @@ function resolvePluginLoadCacheContext(options: PluginLoadOptions = {}) {
|
|||
cfg,
|
||||
normalized,
|
||||
activationSourceConfig,
|
||||
activationSourceNormalized,
|
||||
activationSource,
|
||||
autoEnabledReasons: options.autoEnabledReasons ?? {},
|
||||
onlyPluginIds,
|
||||
includeSetupOnlyChannelPlugins,
|
||||
|
|
@ -950,8 +952,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
|||
env,
|
||||
cfg,
|
||||
normalized,
|
||||
activationSourceConfig,
|
||||
activationSourceNormalized,
|
||||
activationSource,
|
||||
autoEnabledReasons,
|
||||
onlyPluginIds,
|
||||
includeSetupOnlyChannelPlugins,
|
||||
|
|
@ -1148,8 +1149,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
|||
config: normalized,
|
||||
rootConfig: cfg,
|
||||
enabledByDefault: manifestRecord.enabledByDefault,
|
||||
sourceConfig: activationSourceNormalized,
|
||||
sourceRootConfig: activationSourceConfig,
|
||||
activationSource,
|
||||
autoEnabledReason: formatAutoEnabledActivationReason(autoEnabledReasons[pluginId]),
|
||||
});
|
||||
const existingOrigin = seenIds.get(pluginId);
|
||||
|
|
@ -1183,8 +1183,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
|||
config: normalized,
|
||||
rootConfig: cfg,
|
||||
enabledByDefault: manifestRecord.enabledByDefault,
|
||||
sourceConfig: activationSourceNormalized,
|
||||
sourceRootConfig: activationSourceConfig,
|
||||
activationSource,
|
||||
});
|
||||
const entry = normalized.entries[pluginId];
|
||||
const record = createPluginRecord({
|
||||
|
|
@ -1622,20 +1621,12 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
|||
export async function loadOpenClawPluginCliRegistry(
|
||||
options: PluginLoadOptions = {},
|
||||
): Promise<PluginRegistry> {
|
||||
const {
|
||||
env,
|
||||
cfg,
|
||||
normalized,
|
||||
activationSourceConfig,
|
||||
activationSourceNormalized,
|
||||
autoEnabledReasons,
|
||||
onlyPluginIds,
|
||||
cacheKey,
|
||||
} = resolvePluginLoadCacheContext({
|
||||
...options,
|
||||
activate: false,
|
||||
cache: false,
|
||||
});
|
||||
const { env, cfg, normalized, activationSource, autoEnabledReasons, onlyPluginIds, cacheKey } =
|
||||
resolvePluginLoadCacheContext({
|
||||
...options,
|
||||
activate: false,
|
||||
cache: false,
|
||||
});
|
||||
const logger = options.logger ?? defaultLogger();
|
||||
const onlyPluginIdSet = onlyPluginIds ? new Set(onlyPluginIds) : null;
|
||||
const getJiti = createPluginJitiLoader(options);
|
||||
|
|
@ -1716,8 +1707,7 @@ export async function loadOpenClawPluginCliRegistry(
|
|||
config: normalized,
|
||||
rootConfig: cfg,
|
||||
enabledByDefault: manifestRecord.enabledByDefault,
|
||||
sourceConfig: activationSourceNormalized,
|
||||
sourceRootConfig: activationSourceConfig,
|
||||
activationSource,
|
||||
autoEnabledReason: formatAutoEnabledActivationReason(autoEnabledReasons[pluginId]),
|
||||
});
|
||||
const existingOrigin = seenIds.get(pluginId);
|
||||
|
|
@ -1751,8 +1741,7 @@ export async function loadOpenClawPluginCliRegistry(
|
|||
config: normalized,
|
||||
rootConfig: cfg,
|
||||
enabledByDefault: manifestRecord.enabledByDefault,
|
||||
sourceConfig: activationSourceNormalized,
|
||||
sourceRootConfig: activationSourceConfig,
|
||||
activationSource,
|
||||
});
|
||||
const entry = normalized.entries[pluginId];
|
||||
const record = createPluginRecord({
|
||||
|
|
|
|||
Loading…
Reference in New Issue