diff --git a/src/plugins/document-extractors.runtime.test.ts b/src/plugins/document-extractors.runtime.test.ts index aef9aeee1f2..402efaf75d5 100644 --- a/src/plugins/document-extractors.runtime.test.ts +++ b/src/plugins/document-extractors.runtime.test.ts @@ -1,6 +1,51 @@ -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { resolvePluginDocumentExtractors } from "./document-extractors.runtime.js"; +vi.mock("./document-extractor-public-artifacts.js", () => ({ + loadBundledDocumentExtractorEntriesFromDir: vi.fn( + ({ dirName }: { dirName: string; pluginId: string }) => + dirName === "document-extract" + ? [ + { + id: "pdf", + label: "PDF", + mimeTypes: ["application/pdf"], + pluginId: "document-extract", + extract: vi.fn(), + }, + ] + : null, + ), +})); + +vi.mock("./manifest-registry.js", () => ({ + loadPluginManifestRegistry: vi.fn(() => ({ + plugins: [ + { + id: "document-extract", + origin: "bundled", + enabledByDefault: true, + channels: [], + cliBackends: [], + providers: [], + legacyPluginIds: [], + contracts: { documentExtractors: ["pdf"] }, + }, + { + id: "openai", + origin: "bundled", + enabledByDefault: true, + channels: [], + cliBackends: [], + providers: ["openai", "openai-codex"], + legacyPluginIds: [], + contracts: {}, + }, + ], + })), + resolveManifestContractOwnerPluginId: vi.fn(() => undefined), +})); + describe("resolvePluginDocumentExtractors", () => { it("respects global plugin disablement", () => { expect( diff --git a/src/plugins/document-extractors.runtime.ts b/src/plugins/document-extractors.runtime.ts index d54e6eebd85..2b29d964e67 100644 --- a/src/plugins/document-extractors.runtime.ts +++ b/src/plugins/document-extractors.runtime.ts @@ -97,6 +97,30 @@ function resolveEnabledBundledDocumentExtractorPlugins(params: { }); } +function resolveExplicitAllowedDocumentExtractorPluginIds(params: { + config?: OpenClawConfig; + onlyPluginIds?: readonly string[]; +}): string[] | null { + const allow = params.config?.plugins?.allow; + if (!Array.isArray(allow) || allow.length === 0) { + return null; + } + const onlyPluginIdSet = + params.onlyPluginIds && params.onlyPluginIds.length > 0 ? new Set(params.onlyPluginIds) : null; + const deniedPluginIds = new Set(params.config?.plugins?.deny ?? []); + const entries = params.config?.plugins?.entries ?? {}; + return [ + ...new Set( + allow + .map((pluginId) => pluginId.trim()) + .filter(Boolean) + .filter((pluginId) => !onlyPluginIdSet || onlyPluginIdSet.has(pluginId)) + .filter((pluginId) => !deniedPluginIds.has(pluginId)) + .filter((pluginId) => entries[pluginId]?.enabled !== false), + ), + ].toSorted((left, right) => left.localeCompare(right)); +} + export function resolvePluginDocumentExtractors(params?: { config?: OpenClawConfig; workspaceDir?: string; @@ -105,17 +129,24 @@ export function resolvePluginDocumentExtractors(params?: { }): PluginDocumentExtractorEntry[] { const extractors: PluginDocumentExtractorEntry[] = []; const loadErrors: unknown[] = []; - for (const plugin of resolveEnabledBundledDocumentExtractorPlugins({ + const explicitAllowedPluginIds = resolveExplicitAllowedDocumentExtractorPluginIds({ config: params?.config, - workspaceDir: params?.workspaceDir, - env: params?.env, onlyPluginIds: params?.onlyPluginIds, - })) { + }); + const pluginIds = + explicitAllowedPluginIds ?? + resolveEnabledBundledDocumentExtractorPlugins({ + config: params?.config, + workspaceDir: params?.workspaceDir, + env: params?.env, + onlyPluginIds: params?.onlyPluginIds, + }).map((plugin) => plugin.id); + for (const pluginId of pluginIds) { let loaded: PluginDocumentExtractorEntry[] | null; try { loaded = loadBundledDocumentExtractorEntriesFromDir({ - dirName: plugin.id, - pluginId: plugin.id, + dirName: pluginId, + pluginId, }); } catch (error) { loadErrors.push(error);