mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:20:43 +00:00
refactor: share plugin activation decisions
This commit is contained in:
@@ -12,6 +12,298 @@ type EnableStateParamsLike = {
|
||||
|
||||
type PluginKindLike = string | readonly string[] | undefined;
|
||||
|
||||
export type PluginActivationSource = "disabled" | "explicit" | "auto" | "default";
|
||||
|
||||
export type PluginExplicitSelectionCause =
|
||||
| "enabled-in-config"
|
||||
| "bundled-channel-enabled-in-config"
|
||||
| "selected-memory-slot"
|
||||
| "selected-context-engine-slot"
|
||||
| "selected-in-allowlist";
|
||||
|
||||
export type PluginActivationCause =
|
||||
| PluginExplicitSelectionCause
|
||||
| "plugins-disabled"
|
||||
| "blocked-by-denylist"
|
||||
| "disabled-in-config"
|
||||
| "workspace-disabled-by-default"
|
||||
| "not-in-allowlist"
|
||||
| "enabled-by-effective-config"
|
||||
| "bundled-channel-configured"
|
||||
| "bundled-default-enablement"
|
||||
| "bundled-disabled-by-default";
|
||||
|
||||
export type PluginActivationStateLike = {
|
||||
enabled: boolean;
|
||||
activated: boolean;
|
||||
explicitlyEnabled: boolean;
|
||||
source: PluginActivationSource;
|
||||
reason?: string;
|
||||
};
|
||||
|
||||
export type PluginActivationDecision = PluginActivationStateLike & {
|
||||
cause?: PluginActivationCause;
|
||||
};
|
||||
|
||||
type PluginActivationConfigLike = {
|
||||
enabled: boolean;
|
||||
allow: readonly string[];
|
||||
deny: readonly string[];
|
||||
slots: {
|
||||
memory?: string | null;
|
||||
contextEngine?: string | null;
|
||||
};
|
||||
entries: Record<string, { enabled?: boolean } | undefined>;
|
||||
};
|
||||
|
||||
export type PluginActivationConfigSourceLike<TRootConfig> = {
|
||||
plugins: PluginActivationConfigLike;
|
||||
rootConfig?: TRootConfig;
|
||||
};
|
||||
|
||||
export const PLUGIN_ACTIVATION_REASON_BY_CAUSE: Record<PluginActivationCause, string> = {
|
||||
"enabled-in-config": "enabled in config",
|
||||
"bundled-channel-enabled-in-config": "channel enabled in config",
|
||||
"selected-memory-slot": "selected memory slot",
|
||||
"selected-context-engine-slot": "selected context engine slot",
|
||||
"selected-in-allowlist": "selected in allowlist",
|
||||
"plugins-disabled": "plugins disabled",
|
||||
"blocked-by-denylist": "blocked by denylist",
|
||||
"disabled-in-config": "disabled in config",
|
||||
"workspace-disabled-by-default": "workspace plugin (disabled by default)",
|
||||
"not-in-allowlist": "not in allowlist",
|
||||
"enabled-by-effective-config": "enabled by effective config",
|
||||
"bundled-channel-configured": "channel configured",
|
||||
"bundled-default-enablement": "bundled default enablement",
|
||||
"bundled-disabled-by-default": "bundled (disabled by default)",
|
||||
};
|
||||
|
||||
export function resolvePluginActivationReason(
|
||||
cause?: PluginActivationCause,
|
||||
reason?: string,
|
||||
): string | undefined {
|
||||
if (reason) {
|
||||
return reason;
|
||||
}
|
||||
return cause ? PLUGIN_ACTIVATION_REASON_BY_CAUSE[cause] : undefined;
|
||||
}
|
||||
|
||||
export function toPluginActivationState(
|
||||
decision: PluginActivationDecision,
|
||||
): PluginActivationStateLike {
|
||||
return {
|
||||
enabled: decision.enabled,
|
||||
activated: decision.activated,
|
||||
explicitlyEnabled: decision.explicitlyEnabled,
|
||||
source: decision.source,
|
||||
reason: resolvePluginActivationReason(decision.cause, decision.reason),
|
||||
};
|
||||
}
|
||||
|
||||
function resolveExplicitPluginSelectionShared<TRootConfig>(params: {
|
||||
id: string;
|
||||
origin: string;
|
||||
config: PluginActivationConfigLike;
|
||||
rootConfig?: TRootConfig;
|
||||
isBundledChannelEnabledByChannelConfig: (
|
||||
rootConfig: TRootConfig | undefined,
|
||||
pluginId: string,
|
||||
) => boolean;
|
||||
}): { explicitlyEnabled: boolean; cause?: PluginExplicitSelectionCause } {
|
||||
if (params.config.entries[params.id]?.enabled === true) {
|
||||
return { explicitlyEnabled: true, cause: "enabled-in-config" };
|
||||
}
|
||||
if (
|
||||
params.origin === "bundled" &&
|
||||
params.isBundledChannelEnabledByChannelConfig(params.rootConfig, params.id)
|
||||
) {
|
||||
return { explicitlyEnabled: true, cause: "bundled-channel-enabled-in-config" };
|
||||
}
|
||||
if (params.config.slots.memory === params.id) {
|
||||
return { explicitlyEnabled: true, cause: "selected-memory-slot" };
|
||||
}
|
||||
if (params.config.slots.contextEngine === params.id) {
|
||||
return { explicitlyEnabled: true, cause: "selected-context-engine-slot" };
|
||||
}
|
||||
if (params.origin !== "bundled" && params.config.allow.includes(params.id)) {
|
||||
return { explicitlyEnabled: true, cause: "selected-in-allowlist" };
|
||||
}
|
||||
return { explicitlyEnabled: false };
|
||||
}
|
||||
|
||||
export function resolvePluginActivationDecisionShared<TRootConfig>(params: {
|
||||
id: string;
|
||||
origin: string;
|
||||
config: PluginActivationConfigLike;
|
||||
rootConfig?: TRootConfig;
|
||||
enabledByDefault?: boolean;
|
||||
activationSource?: PluginActivationConfigSourceLike<TRootConfig>;
|
||||
autoEnabledReason?: string;
|
||||
allowBundledChannelExplicitBypassesAllowlist?: boolean;
|
||||
isBundledChannelEnabledByChannelConfig: (
|
||||
rootConfig: TRootConfig | undefined,
|
||||
pluginId: string,
|
||||
) => boolean;
|
||||
}): PluginActivationDecision {
|
||||
const activationSource = params.activationSource ?? {
|
||||
plugins: params.config,
|
||||
rootConfig: params.rootConfig,
|
||||
};
|
||||
const explicitSelection = resolveExplicitPluginSelectionShared({
|
||||
id: params.id,
|
||||
origin: params.origin,
|
||||
config: activationSource.plugins,
|
||||
rootConfig: activationSource.rootConfig,
|
||||
isBundledChannelEnabledByChannelConfig: params.isBundledChannelEnabledByChannelConfig,
|
||||
});
|
||||
|
||||
if (!params.config.enabled) {
|
||||
return {
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "disabled",
|
||||
cause: "plugins-disabled",
|
||||
};
|
||||
}
|
||||
if (params.config.deny.includes(params.id)) {
|
||||
return {
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "disabled",
|
||||
cause: "blocked-by-denylist",
|
||||
};
|
||||
}
|
||||
const entry = params.config.entries[params.id];
|
||||
if (entry?.enabled === false) {
|
||||
return {
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "disabled",
|
||||
cause: "disabled-in-config",
|
||||
};
|
||||
}
|
||||
const explicitlyAllowed = params.config.allow.includes(params.id);
|
||||
if (
|
||||
params.origin === "workspace" &&
|
||||
!explicitlyAllowed &&
|
||||
entry?.enabled !== true &&
|
||||
explicitSelection.cause !== "selected-context-engine-slot"
|
||||
) {
|
||||
return {
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "disabled",
|
||||
cause: "workspace-disabled-by-default",
|
||||
};
|
||||
}
|
||||
if (params.config.slots.memory === params.id) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: true,
|
||||
source: "explicit",
|
||||
cause: "selected-memory-slot",
|
||||
};
|
||||
}
|
||||
if (params.config.slots.contextEngine === params.id) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: true,
|
||||
source: "explicit",
|
||||
cause: "selected-context-engine-slot",
|
||||
};
|
||||
}
|
||||
if (
|
||||
params.allowBundledChannelExplicitBypassesAllowlist === true &&
|
||||
explicitSelection.cause === "bundled-channel-enabled-in-config"
|
||||
) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: true,
|
||||
source: "explicit",
|
||||
cause: explicitSelection.cause,
|
||||
};
|
||||
}
|
||||
if (params.config.allow.length > 0 && !explicitlyAllowed) {
|
||||
return {
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "disabled",
|
||||
cause: "not-in-allowlist",
|
||||
};
|
||||
}
|
||||
if (explicitSelection.explicitlyEnabled) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: true,
|
||||
source: "explicit",
|
||||
cause: explicitSelection.cause,
|
||||
};
|
||||
}
|
||||
if (params.autoEnabledReason) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: false,
|
||||
source: "auto",
|
||||
reason: params.autoEnabledReason,
|
||||
};
|
||||
}
|
||||
if (entry?.enabled === true) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: false,
|
||||
source: "auto",
|
||||
cause: "enabled-by-effective-config",
|
||||
};
|
||||
}
|
||||
if (
|
||||
params.origin === "bundled" &&
|
||||
params.isBundledChannelEnabledByChannelConfig(params.rootConfig, params.id)
|
||||
) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: false,
|
||||
source: "auto",
|
||||
cause: "bundled-channel-configured",
|
||||
};
|
||||
}
|
||||
if (params.origin === "bundled" && params.enabledByDefault === true) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: false,
|
||||
source: "default",
|
||||
cause: "bundled-default-enablement",
|
||||
};
|
||||
}
|
||||
if (params.origin === "bundled") {
|
||||
return {
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: false,
|
||||
source: "disabled",
|
||||
cause: "bundled-disabled-by-default",
|
||||
};
|
||||
}
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "default",
|
||||
};
|
||||
}
|
||||
|
||||
export function toEnableStateResult(state: EnableStateLike): { enabled: boolean; reason?: string } {
|
||||
return state.enabled ? { enabled: true } : { enabled: false, reason: state.reason };
|
||||
}
|
||||
@@ -30,6 +322,29 @@ export function resolveEnableStateShared<TParams extends EnableStateParamsLike>(
|
||||
return resolveEnableStateResult(params, resolveState);
|
||||
}
|
||||
|
||||
export function createPluginEnableStateResolver<TConfig, TOrigin extends string>(
|
||||
resolveState: (params: {
|
||||
id: string;
|
||||
origin: TOrigin;
|
||||
config: TConfig;
|
||||
enabledByDefault?: boolean;
|
||||
}) => EnableStateLike,
|
||||
): (
|
||||
id: string,
|
||||
origin: TOrigin,
|
||||
config: TConfig,
|
||||
enabledByDefault?: boolean,
|
||||
) => { enabled: boolean; reason?: string } {
|
||||
return (id, origin, config, enabledByDefault) =>
|
||||
resolveEnableStateResult({ id, origin, config, enabledByDefault }, resolveState);
|
||||
}
|
||||
|
||||
export function createEffectiveEnableStateResolver<TParams>(
|
||||
resolveState: (params: TParams) => EnableStateLike,
|
||||
): (params: TParams) => { enabled: boolean; reason?: string } {
|
||||
return (params) => resolveEnableStateResult(params, resolveState);
|
||||
}
|
||||
|
||||
function hasKind(kind: PluginKindLike, target: string): boolean {
|
||||
if (!kind) {
|
||||
return false;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import {
|
||||
createEffectiveEnableStateResolver,
|
||||
createPluginEnableStateResolver,
|
||||
resolveMemorySlotDecisionShared,
|
||||
resolveEnableStateShared,
|
||||
resolveEnableStateResult,
|
||||
resolvePluginActivationDecisionShared,
|
||||
toPluginActivationState,
|
||||
type PluginActivationSource,
|
||||
type PluginActivationStateLike,
|
||||
} from "./config-activation-shared.js";
|
||||
import {
|
||||
hasExplicitPluginConfig as hasExplicitPluginConfigShared,
|
||||
@@ -15,15 +19,8 @@ import {
|
||||
import type { PluginKind } from "./plugin-kind.types.js";
|
||||
import type { PluginOrigin } from "./plugin-origin.types.js";
|
||||
|
||||
export type PluginActivationSource = "disabled" | "explicit" | "auto" | "default";
|
||||
|
||||
export type PluginActivationState = {
|
||||
enabled: boolean;
|
||||
activated: boolean;
|
||||
explicitlyEnabled: boolean;
|
||||
source: PluginActivationSource;
|
||||
reason?: string;
|
||||
};
|
||||
export type { PluginActivationSource };
|
||||
export type PluginActivationState = PluginActivationStateLike;
|
||||
|
||||
export type NormalizedPluginsConfig = SharedNormalizedPluginsConfig;
|
||||
|
||||
@@ -34,33 +31,6 @@ export function normalizePluginsConfigWithResolver(
|
||||
return normalizePluginsConfigWithResolverShared(config, normalizePluginId);
|
||||
}
|
||||
|
||||
function resolveExplicitPluginSelection(params: {
|
||||
id: string;
|
||||
origin: PluginOrigin;
|
||||
config: NormalizedPluginsConfig;
|
||||
rootConfig?: OpenClawConfig;
|
||||
}): { explicitlyEnabled: boolean; reason?: string } {
|
||||
if (params.config.entries[params.id]?.enabled === true) {
|
||||
return { explicitlyEnabled: true, reason: "enabled in config" };
|
||||
}
|
||||
if (
|
||||
params.origin === "bundled" &&
|
||||
isBundledChannelEnabledByChannelConfig(params.rootConfig, params.id)
|
||||
) {
|
||||
return { explicitlyEnabled: true, reason: "channel enabled in config" };
|
||||
}
|
||||
if (params.config.slots.memory === params.id) {
|
||||
return { explicitlyEnabled: true, reason: "selected memory slot" };
|
||||
}
|
||||
if (params.config.slots.contextEngine === params.id) {
|
||||
return { explicitlyEnabled: true, reason: "selected context engine slot" };
|
||||
}
|
||||
if (params.origin !== "bundled" && params.config.allow.includes(params.id)) {
|
||||
return { explicitlyEnabled: true, reason: "selected in allowlist" };
|
||||
}
|
||||
return { explicitlyEnabled: false };
|
||||
}
|
||||
|
||||
export function resolvePluginActivationState(params: {
|
||||
id: string;
|
||||
origin: PluginOrigin;
|
||||
@@ -71,170 +41,27 @@ export function resolvePluginActivationState(params: {
|
||||
sourceRootConfig?: OpenClawConfig;
|
||||
autoEnabledReason?: string;
|
||||
}): PluginActivationState {
|
||||
const explicitSelection = resolveExplicitPluginSelection({
|
||||
id: params.id,
|
||||
origin: params.origin,
|
||||
config: params.sourceConfig ?? params.config,
|
||||
rootConfig: params.sourceRootConfig ?? params.rootConfig,
|
||||
});
|
||||
|
||||
if (!params.config.enabled) {
|
||||
return {
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "disabled",
|
||||
reason: "plugins disabled",
|
||||
};
|
||||
}
|
||||
if (params.config.deny.includes(params.id)) {
|
||||
return {
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "disabled",
|
||||
reason: "blocked by denylist",
|
||||
};
|
||||
}
|
||||
const entry = params.config.entries[params.id];
|
||||
if (entry?.enabled === false) {
|
||||
return {
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "disabled",
|
||||
reason: "disabled in config",
|
||||
};
|
||||
}
|
||||
const explicitlyAllowed = params.config.allow.includes(params.id);
|
||||
if (
|
||||
params.origin === "workspace" &&
|
||||
!explicitlyAllowed &&
|
||||
entry?.enabled !== true &&
|
||||
explicitSelection.reason !== "selected context engine slot"
|
||||
) {
|
||||
return {
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "disabled",
|
||||
reason: "workspace plugin (disabled by default)",
|
||||
};
|
||||
}
|
||||
if (params.config.slots.memory === params.id) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: true,
|
||||
source: "explicit",
|
||||
reason: "selected memory slot",
|
||||
};
|
||||
}
|
||||
if (params.config.slots.contextEngine === params.id) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: true,
|
||||
source: "explicit",
|
||||
reason: "selected context engine slot",
|
||||
};
|
||||
}
|
||||
if (params.config.allow.length > 0 && !explicitlyAllowed) {
|
||||
return {
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "disabled",
|
||||
reason: "not in allowlist",
|
||||
};
|
||||
}
|
||||
if (explicitSelection.explicitlyEnabled) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: true,
|
||||
source: "explicit",
|
||||
reason: explicitSelection.reason,
|
||||
};
|
||||
}
|
||||
if (params.autoEnabledReason) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: false,
|
||||
source: "auto",
|
||||
reason: params.autoEnabledReason,
|
||||
};
|
||||
}
|
||||
if (entry?.enabled === true) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: false,
|
||||
source: "auto",
|
||||
reason: "enabled by effective config",
|
||||
};
|
||||
}
|
||||
if (
|
||||
params.origin === "bundled" &&
|
||||
isBundledChannelEnabledByChannelConfig(params.rootConfig, params.id)
|
||||
) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: false,
|
||||
source: "auto",
|
||||
reason: "channel configured",
|
||||
};
|
||||
}
|
||||
if (params.origin === "bundled" && params.enabledByDefault === true) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: false,
|
||||
source: "default",
|
||||
reason: "bundled default enablement",
|
||||
};
|
||||
}
|
||||
if (params.origin === "bundled") {
|
||||
return {
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: false,
|
||||
source: "disabled",
|
||||
reason: "bundled (disabled by default)",
|
||||
};
|
||||
}
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "default",
|
||||
};
|
||||
}
|
||||
export function hasExplicitPluginConfig(plugins?: OpenClawConfig["plugins"]): boolean {
|
||||
return hasExplicitPluginConfigShared(plugins);
|
||||
}
|
||||
export function resolveEnableState(
|
||||
id: string,
|
||||
origin: PluginOrigin,
|
||||
config: NormalizedPluginsConfig,
|
||||
enabledByDefault?: boolean,
|
||||
): { enabled: boolean; reason?: string } {
|
||||
return resolveEnableStateShared(
|
||||
{ id, origin, config, enabledByDefault },
|
||||
resolvePluginActivationState,
|
||||
return toPluginActivationState(
|
||||
resolvePluginActivationDecisionShared({
|
||||
...params,
|
||||
activationSource: {
|
||||
plugins: params.sourceConfig ?? params.config,
|
||||
rootConfig: params.sourceRootConfig ?? params.rootConfig,
|
||||
},
|
||||
isBundledChannelEnabledByChannelConfig,
|
||||
}),
|
||||
);
|
||||
}
|
||||
export const hasExplicitPluginConfig = hasExplicitPluginConfigShared;
|
||||
|
||||
export function isBundledChannelEnabledByChannelConfig(
|
||||
cfg: OpenClawConfig | undefined,
|
||||
pluginId: string,
|
||||
): boolean {
|
||||
return isBundledChannelEnabledByChannelConfigShared(cfg, pluginId);
|
||||
}
|
||||
export const resolveEnableState = createPluginEnableStateResolver<
|
||||
NormalizedPluginsConfig,
|
||||
PluginOrigin
|
||||
>(resolvePluginActivationState);
|
||||
|
||||
export function resolveEffectiveEnableState(params: {
|
||||
export const isBundledChannelEnabledByChannelConfig = isBundledChannelEnabledByChannelConfigShared;
|
||||
|
||||
type PolicyEffectiveActivationParams = {
|
||||
id: string;
|
||||
origin: PluginOrigin;
|
||||
config: NormalizedPluginsConfig;
|
||||
@@ -243,20 +70,16 @@ export function resolveEffectiveEnableState(params: {
|
||||
sourceConfig?: NormalizedPluginsConfig;
|
||||
sourceRootConfig?: OpenClawConfig;
|
||||
autoEnabledReason?: string;
|
||||
}): { enabled: boolean; reason?: string } {
|
||||
return resolveEnableStateResult(params, resolveEffectivePluginActivationState);
|
||||
}
|
||||
};
|
||||
|
||||
export function resolveEffectivePluginActivationState(params: {
|
||||
id: string;
|
||||
origin: PluginOrigin;
|
||||
config: NormalizedPluginsConfig;
|
||||
rootConfig?: OpenClawConfig;
|
||||
enabledByDefault?: boolean;
|
||||
sourceConfig?: NormalizedPluginsConfig;
|
||||
sourceRootConfig?: OpenClawConfig;
|
||||
autoEnabledReason?: string;
|
||||
}): PluginActivationState {
|
||||
export const resolveEffectiveEnableState =
|
||||
createEffectiveEnableStateResolver<PolicyEffectiveActivationParams>(
|
||||
resolveEffectivePluginActivationState,
|
||||
);
|
||||
|
||||
export function resolveEffectivePluginActivationState(
|
||||
params: PolicyEffectiveActivationParams,
|
||||
): PluginActivationState {
|
||||
return resolvePluginActivationState(params);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,14 @@ import {
|
||||
normalizeOptionalString,
|
||||
} from "../shared/string-coerce.js";
|
||||
import {
|
||||
createEffectiveEnableStateResolver,
|
||||
createPluginEnableStateResolver,
|
||||
resolveMemorySlotDecisionShared,
|
||||
resolveEnableStateShared,
|
||||
resolveEnableStateResult,
|
||||
resolvePluginActivationDecisionShared,
|
||||
toPluginActivationState,
|
||||
type PluginActivationConfigSourceLike,
|
||||
type PluginActivationSource,
|
||||
type PluginActivationStateLike,
|
||||
} from "./config-activation-shared.js";
|
||||
import {
|
||||
hasExplicitPluginConfig as hasExplicitPluginConfigShared,
|
||||
@@ -17,48 +22,13 @@ import {
|
||||
import { loadPluginManifestRegistry } from "./manifest-registry.js";
|
||||
import type { PluginOrigin } from "./plugin-origin.types.js";
|
||||
|
||||
export type PluginActivationSource = "disabled" | "explicit" | "auto" | "default";
|
||||
|
||||
export type PluginExplicitSelectionCause =
|
||||
| "enabled-in-config"
|
||||
| "bundled-channel-enabled-in-config"
|
||||
| "selected-memory-slot"
|
||||
| "selected-context-engine-slot"
|
||||
| "selected-in-allowlist";
|
||||
|
||||
export type PluginActivationCause =
|
||||
| PluginExplicitSelectionCause
|
||||
| "plugins-disabled"
|
||||
| "blocked-by-denylist"
|
||||
| "disabled-in-config"
|
||||
| "workspace-disabled-by-default"
|
||||
| "not-in-allowlist"
|
||||
| "enabled-by-effective-config"
|
||||
| "bundled-channel-configured"
|
||||
| "bundled-default-enablement"
|
||||
| "bundled-disabled-by-default";
|
||||
|
||||
export type PluginActivationState = {
|
||||
enabled: boolean;
|
||||
activated: boolean;
|
||||
explicitlyEnabled: boolean;
|
||||
source: PluginActivationSource;
|
||||
reason?: string;
|
||||
};
|
||||
|
||||
type PluginActivationDecision = {
|
||||
enabled: boolean;
|
||||
activated: boolean;
|
||||
explicitlyEnabled: boolean;
|
||||
source: PluginActivationSource;
|
||||
cause?: PluginActivationCause;
|
||||
reason?: string;
|
||||
};
|
||||
export type { PluginActivationSource };
|
||||
export type PluginActivationState = PluginActivationStateLike;
|
||||
|
||||
export type PluginActivationConfigSource = {
|
||||
plugins: NormalizedPluginsConfig;
|
||||
rootConfig?: OpenClawConfig;
|
||||
};
|
||||
} & PluginActivationConfigSourceLike<OpenClawConfig>;
|
||||
|
||||
export type NormalizedPluginsConfig = SharedNormalizedPluginsConfig;
|
||||
|
||||
@@ -101,43 +71,6 @@ export function normalizePluginId(id: string): string {
|
||||
return getBundledPluginAliasLookup().get(normalized) ?? trimmed;
|
||||
}
|
||||
|
||||
const PLUGIN_ACTIVATION_REASON_BY_CAUSE: Record<PluginActivationCause, string> = {
|
||||
"enabled-in-config": "enabled in config",
|
||||
"bundled-channel-enabled-in-config": "channel enabled in config",
|
||||
"selected-memory-slot": "selected memory slot",
|
||||
"selected-context-engine-slot": "selected context engine slot",
|
||||
"selected-in-allowlist": "selected in allowlist",
|
||||
"plugins-disabled": "plugins disabled",
|
||||
"blocked-by-denylist": "blocked by denylist",
|
||||
"disabled-in-config": "disabled in config",
|
||||
"workspace-disabled-by-default": "workspace plugin (disabled by default)",
|
||||
"not-in-allowlist": "not in allowlist",
|
||||
"enabled-by-effective-config": "enabled by effective config",
|
||||
"bundled-channel-configured": "channel configured",
|
||||
"bundled-default-enablement": "bundled default enablement",
|
||||
"bundled-disabled-by-default": "bundled (disabled by default)",
|
||||
};
|
||||
|
||||
function resolvePluginActivationReason(
|
||||
cause?: PluginActivationCause,
|
||||
reason?: string,
|
||||
): string | undefined {
|
||||
if (reason) {
|
||||
return reason;
|
||||
}
|
||||
return cause ? PLUGIN_ACTIVATION_REASON_BY_CAUSE[cause] : undefined;
|
||||
}
|
||||
|
||||
function toPluginActivationState(decision: PluginActivationDecision): PluginActivationState {
|
||||
return {
|
||||
enabled: decision.enabled,
|
||||
activated: decision.activated,
|
||||
explicitlyEnabled: decision.explicitlyEnabled,
|
||||
source: decision.source,
|
||||
reason: resolvePluginActivationReason(decision.cause, decision.reason),
|
||||
};
|
||||
}
|
||||
|
||||
export const normalizePluginsConfig = (
|
||||
config?: OpenClawConfig["plugins"],
|
||||
): NormalizedPluginsConfig => {
|
||||
@@ -215,33 +148,6 @@ export function isTestDefaultMemorySlotDisabled(
|
||||
return true;
|
||||
}
|
||||
|
||||
function resolveExplicitPluginSelection(params: {
|
||||
id: string;
|
||||
origin: PluginOrigin;
|
||||
config: NormalizedPluginsConfig;
|
||||
rootConfig?: OpenClawConfig;
|
||||
}): { explicitlyEnabled: boolean; cause?: PluginExplicitSelectionCause } {
|
||||
if (params.config.entries[params.id]?.enabled === true) {
|
||||
return { explicitlyEnabled: true, cause: "enabled-in-config" };
|
||||
}
|
||||
if (
|
||||
params.origin === "bundled" &&
|
||||
isBundledChannelEnabledByChannelConfig(params.rootConfig, params.id)
|
||||
) {
|
||||
return { explicitlyEnabled: true, cause: "bundled-channel-enabled-in-config" };
|
||||
}
|
||||
if (params.config.slots.memory === params.id) {
|
||||
return { explicitlyEnabled: true, cause: "selected-memory-slot" };
|
||||
}
|
||||
if (params.config.slots.contextEngine === params.id) {
|
||||
return { explicitlyEnabled: true, cause: "selected-context-engine-slot" };
|
||||
}
|
||||
if (params.origin !== "bundled" && params.config.allow.includes(params.id)) {
|
||||
return { explicitlyEnabled: true, cause: "selected-in-allowlist" };
|
||||
}
|
||||
return { explicitlyEnabled: false };
|
||||
}
|
||||
|
||||
export function resolvePluginActivationState(params: {
|
||||
id: string;
|
||||
origin: PluginOrigin;
|
||||
@@ -251,200 +157,49 @@ export function resolvePluginActivationState(params: {
|
||||
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: activationSource.plugins,
|
||||
rootConfig: activationSource.rootConfig,
|
||||
});
|
||||
|
||||
if (!params.config.enabled) {
|
||||
return toPluginActivationState({
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "disabled",
|
||||
cause: "plugins-disabled",
|
||||
});
|
||||
}
|
||||
if (params.config.deny.includes(params.id)) {
|
||||
return toPluginActivationState({
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "disabled",
|
||||
cause: "blocked-by-denylist",
|
||||
});
|
||||
}
|
||||
const entry = params.config.entries[params.id];
|
||||
if (entry?.enabled === false) {
|
||||
return toPluginActivationState({
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "disabled",
|
||||
cause: "disabled-in-config",
|
||||
});
|
||||
}
|
||||
const explicitlyAllowed = params.config.allow.includes(params.id);
|
||||
if (
|
||||
params.origin === "workspace" &&
|
||||
!explicitlyAllowed &&
|
||||
entry?.enabled !== true &&
|
||||
explicitSelection.cause !== "selected-context-engine-slot"
|
||||
) {
|
||||
return toPluginActivationState({
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "disabled",
|
||||
cause: "workspace-disabled-by-default",
|
||||
});
|
||||
}
|
||||
if (params.config.slots.memory === params.id) {
|
||||
return toPluginActivationState({
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: true,
|
||||
source: "explicit",
|
||||
cause: "selected-memory-slot",
|
||||
});
|
||||
}
|
||||
if (params.config.slots.contextEngine === params.id) {
|
||||
return toPluginActivationState({
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: true,
|
||||
source: "explicit",
|
||||
cause: "selected-context-engine-slot",
|
||||
});
|
||||
}
|
||||
if (explicitSelection.cause === "bundled-channel-enabled-in-config") {
|
||||
return toPluginActivationState({
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: true,
|
||||
source: "explicit",
|
||||
cause: explicitSelection.cause,
|
||||
});
|
||||
}
|
||||
if (params.config.allow.length > 0 && !explicitlyAllowed) {
|
||||
return toPluginActivationState({
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "disabled",
|
||||
cause: "not-in-allowlist",
|
||||
});
|
||||
}
|
||||
if (explicitSelection.explicitlyEnabled) {
|
||||
return toPluginActivationState({
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: true,
|
||||
source: "explicit",
|
||||
cause: explicitSelection.cause,
|
||||
});
|
||||
}
|
||||
if (params.autoEnabledReason) {
|
||||
return toPluginActivationState({
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: false,
|
||||
source: "auto",
|
||||
reason: params.autoEnabledReason,
|
||||
});
|
||||
}
|
||||
if (entry?.enabled === true) {
|
||||
return toPluginActivationState({
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: false,
|
||||
source: "auto",
|
||||
cause: "enabled-by-effective-config",
|
||||
});
|
||||
}
|
||||
if (
|
||||
params.origin === "bundled" &&
|
||||
isBundledChannelEnabledByChannelConfig(params.rootConfig, params.id)
|
||||
) {
|
||||
return toPluginActivationState({
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: false,
|
||||
source: "auto",
|
||||
cause: "bundled-channel-configured",
|
||||
});
|
||||
}
|
||||
if (params.origin === "bundled" && params.enabledByDefault === true) {
|
||||
return toPluginActivationState({
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: false,
|
||||
source: "default",
|
||||
cause: "bundled-default-enablement",
|
||||
});
|
||||
}
|
||||
if (params.origin === "bundled") {
|
||||
return toPluginActivationState({
|
||||
enabled: false,
|
||||
activated: false,
|
||||
explicitlyEnabled: false,
|
||||
source: "disabled",
|
||||
cause: "bundled-disabled-by-default",
|
||||
});
|
||||
}
|
||||
return toPluginActivationState({
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: explicitSelection.explicitlyEnabled,
|
||||
source: "default",
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveEnableState(
|
||||
id: string,
|
||||
origin: PluginOrigin,
|
||||
config: NormalizedPluginsConfig,
|
||||
enabledByDefault?: boolean,
|
||||
): { enabled: boolean; reason?: string } {
|
||||
return resolveEnableStateShared(
|
||||
{ id, origin, config, enabledByDefault },
|
||||
resolvePluginActivationState,
|
||||
return toPluginActivationState(
|
||||
resolvePluginActivationDecisionShared({
|
||||
...params,
|
||||
activationSource:
|
||||
params.activationSource ??
|
||||
createPluginActivationSource({
|
||||
config: params.rootConfig,
|
||||
plugins: params.config,
|
||||
}),
|
||||
allowBundledChannelExplicitBypassesAllowlist: true,
|
||||
isBundledChannelEnabledByChannelConfig,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function isBundledChannelEnabledByChannelConfig(
|
||||
cfg: OpenClawConfig | undefined,
|
||||
pluginId: string,
|
||||
): boolean {
|
||||
return isBundledChannelEnabledByChannelConfigShared(cfg, pluginId);
|
||||
}
|
||||
export const resolveEnableState = createPluginEnableStateResolver<
|
||||
NormalizedPluginsConfig,
|
||||
PluginOrigin
|
||||
>(resolvePluginActivationState);
|
||||
|
||||
export function resolveEffectiveEnableState(params: {
|
||||
export const isBundledChannelEnabledByChannelConfig = isBundledChannelEnabledByChannelConfigShared;
|
||||
|
||||
type EffectiveActivationParams = {
|
||||
id: string;
|
||||
origin: PluginOrigin;
|
||||
config: NormalizedPluginsConfig;
|
||||
rootConfig?: OpenClawConfig;
|
||||
enabledByDefault?: boolean;
|
||||
activationSource?: PluginActivationConfigSource;
|
||||
}): { enabled: boolean; reason?: string } {
|
||||
return resolveEnableStateResult(params, resolveEffectivePluginActivationState);
|
||||
}
|
||||
};
|
||||
|
||||
export const resolveEffectiveEnableState =
|
||||
createEffectiveEnableStateResolver<EffectiveActivationParams>(
|
||||
resolveEffectivePluginActivationState,
|
||||
);
|
||||
|
||||
export function resolveEffectivePluginActivationState(params: {
|
||||
id: string;
|
||||
origin: PluginOrigin;
|
||||
config: NormalizedPluginsConfig;
|
||||
rootConfig?: OpenClawConfig;
|
||||
enabledByDefault?: boolean;
|
||||
activationSource?: PluginActivationConfigSource;
|
||||
id: EffectiveActivationParams["id"];
|
||||
origin: EffectiveActivationParams["origin"];
|
||||
config: EffectiveActivationParams["config"];
|
||||
rootConfig?: EffectiveActivationParams["rootConfig"];
|
||||
enabledByDefault?: EffectiveActivationParams["enabledByDefault"];
|
||||
activationSource?: EffectiveActivationParams["activationSource"];
|
||||
autoEnabledReason?: string;
|
||||
}): PluginActivationState {
|
||||
return resolvePluginActivationState(params);
|
||||
|
||||
Reference in New Issue
Block a user