diff --git a/src/plugin-sdk/provider-enable-config.test.ts b/src/plugin-sdk/provider-enable-config.test.ts new file mode 100644 index 00000000000..a3a8c63fcef --- /dev/null +++ b/src/plugin-sdk/provider-enable-config.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, it } from "vitest"; +import { enablePluginInConfig as enableFetchPluginInConfig } from "./provider-web-fetch-contract.js"; +import { enablePluginInConfig as enableSearchPluginInConfig } from "./provider-web-search-contract.js"; + +describe("provider contract enablePluginInConfig", () => { + it("enables and allowlists provider plugins without touching channels", () => { + const config = { + plugins: { + allow: ["openai"], + }, + channels: { + brave: { enabled: false }, + }, + }; + + const result = enableSearchPluginInConfig(config, "brave"); + + expect(result).toEqual({ + enabled: true, + config: { + plugins: { + allow: ["openai", "brave"], + entries: { + brave: { enabled: true }, + }, + }, + channels: { + brave: { enabled: false }, + }, + }, + }); + }); + + it("shares denylist behavior across provider contract subpaths", () => { + const config = { + plugins: { + deny: ["firecrawl"], + }, + }; + + expect(enableFetchPluginInConfig(config, "firecrawl")).toEqual({ + config, + enabled: false, + reason: "blocked by denylist", + }); + }); +}); diff --git a/src/plugin-sdk/provider-enable-config.ts b/src/plugin-sdk/provider-enable-config.ts new file mode 100644 index 00000000000..5d830ecacca --- /dev/null +++ b/src/plugin-sdk/provider-enable-config.ts @@ -0,0 +1,37 @@ +import type { OpenClawConfig } from "../config/config.js"; +import { ensurePluginAllowlisted } from "../config/plugins-allowlist.js"; + +export type PluginEnableResult = { + config: OpenClawConfig; + enabled: boolean; + reason?: string; +}; + +/** + * Provider contract surfaces only ever enable provider plugins, so they do not + * need the built-in channel normalization path from plugins/enable.ts. + */ +export function enablePluginInConfig(cfg: OpenClawConfig, pluginId: string): PluginEnableResult { + if (cfg.plugins?.enabled === false) { + return { config: cfg, enabled: false, reason: "plugins disabled" }; + } + if (cfg.plugins?.deny?.includes(pluginId)) { + return { config: cfg, enabled: false, reason: "blocked by denylist" }; + } + + let next: OpenClawConfig = { + ...cfg, + plugins: { + ...cfg.plugins, + entries: { + ...cfg.plugins?.entries, + [pluginId]: { + ...(cfg.plugins?.entries?.[pluginId] as object | undefined), + enabled: true, + }, + }, + }, + }; + next = ensurePluginAllowlisted(next, pluginId); + return { config: next, enabled: true }; +} diff --git a/src/plugin-sdk/provider-web-fetch-contract.ts b/src/plugin-sdk/provider-web-fetch-contract.ts index 102aa5ee05b..b339f32fe59 100644 --- a/src/plugin-sdk/provider-web-fetch-contract.ts +++ b/src/plugin-sdk/provider-web-fetch-contract.ts @@ -2,5 +2,5 @@ import type { WebFetchProviderPlugin } from "../plugins/types.js"; -export { enablePluginInConfig } from "../plugins/enable.js"; +export { enablePluginInConfig } from "./provider-enable-config.js"; export type { WebFetchProviderPlugin }; diff --git a/src/plugin-sdk/provider-web-search-contract.ts b/src/plugin-sdk/provider-web-search-contract.ts index 17c09ea8778..c06311c2c90 100644 --- a/src/plugin-sdk/provider-web-search-contract.ts +++ b/src/plugin-sdk/provider-web-search-contract.ts @@ -15,7 +15,7 @@ export { setProviderWebSearchPluginConfigValue, setTopLevelCredentialValue, } from "../agents/tools/web-search-provider-config.js"; -export { enablePluginInConfig } from "../plugins/enable.js"; +export { enablePluginInConfig } from "./provider-enable-config.js"; export type { WebSearchCredentialResolutionSource, WebSearchProviderSetupContext,