fix(web): resolve provider candidates from plugin registry

This commit is contained in:
Vincent Koc
2026-04-25 19:13:04 -07:00
parent 9b4f0779ce
commit 690046637f
5 changed files with 83 additions and 28 deletions

View File

@@ -5,8 +5,9 @@ import {
normalizePluginsConfig,
resolveEffectivePluginActivationState,
} from "./config-state.js";
import { loadPluginManifestRegistry } from "./manifest-registry.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestRecord } from "./manifest-registry.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadBundledWebContentExtractorEntriesFromDir } from "./web-content-extractor-public-artifacts.js";
import type { PluginWebContentExtractorEntry } from "./web-content-extractor-types.js";
@@ -30,10 +31,17 @@ function resolveBundledWebContentExtractorCompatPluginIds(params: {
}): string[] {
const onlyPluginIdSet =
params.onlyPluginIds && params.onlyPluginIds.length > 0 ? new Set(params.onlyPluginIds) : null;
return loadPluginManifestRegistry({
const index = loadPluginRegistrySnapshot({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
return loadPluginManifestRegistryForInstalledIndex({
index,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
includeDisabled: true,
})
.plugins.filter(
(plugin) =>
@@ -74,10 +82,17 @@ function resolveEnabledBundledExtractorPlugins(params: {
});
const onlyPluginIdSet =
params.onlyPluginIds && params.onlyPluginIds.length > 0 ? new Set(params.onlyPluginIds) : null;
return loadPluginManifestRegistry({
const index = loadPluginRegistrySnapshot({
config: activation.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
return loadPluginManifestRegistryForInstalledIndex({
index,
config: activation.config,
workspaceDir: params.workspaceDir,
env: params.env,
includeDisabled: true,
}).plugins.filter((plugin) => {
if (
plugin.origin !== "bundled" ||

View File

@@ -1,6 +1,7 @@
import path from "node:path";
import type { PluginLoadOptions } from "./loader.js";
import { loadPluginManifestRegistry } from "./manifest-registry.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import type { PluginWebFetchProviderEntry, PluginWebSearchProviderEntry } from "./types.js";
import { resolveBundledWebFetchResolutionConfig } from "./web-fetch-providers.shared.js";
import {
@@ -56,11 +57,18 @@ function resolveBundledManifestRecordsByPluginId(params: {
onlyPluginIds: readonly string[];
}) {
const allowedPluginIds = new Set(params.onlyPluginIds);
const index = loadPluginRegistrySnapshot({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
return new Map(
loadPluginManifestRegistry({
loadPluginManifestRegistryForInstalledIndex({
index,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
includeDisabled: true,
})
.plugins.filter((record) => record.origin === "bundled" && allowedPluginIds.has(record.id))
.map((record) => [record.id, record] as const),

View File

@@ -1,12 +1,17 @@
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
loadPluginManifestRegistry: vi.fn(),
loadPluginRegistrySnapshot: vi.fn(),
loadPluginManifestRegistryForInstalledIndex: vi.fn(),
}));
vi.mock("./manifest-registry.js", () => ({
loadPluginManifestRegistry: (...args: unknown[]) => mocks.loadPluginManifestRegistry(...args),
resolveManifestContractPluginIds: vi.fn(),
vi.mock("./plugin-registry.js", () => ({
loadPluginRegistrySnapshot: (...args: unknown[]) => mocks.loadPluginRegistrySnapshot(...args),
}));
vi.mock("./manifest-registry-installed.js", () => ({
loadPluginManifestRegistryForInstalledIndex: (...args: unknown[]) =>
mocks.loadPluginManifestRegistryForInstalledIndex(...args),
}));
let resolveManifestDeclaredWebProviderCandidatePluginIds: typeof import("./web-provider-resolution-shared.js").resolveManifestDeclaredWebProviderCandidatePluginIds;
@@ -18,8 +23,10 @@ describe("resolveManifestDeclaredWebProviderCandidatePluginIds", () => {
});
beforeEach(() => {
mocks.loadPluginManifestRegistry.mockReset();
mocks.loadPluginManifestRegistry.mockReturnValue({
mocks.loadPluginRegistrySnapshot.mockReset();
mocks.loadPluginRegistrySnapshot.mockReturnValue({ plugins: [] });
mocks.loadPluginManifestRegistryForInstalledIndex.mockReset();
mocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({
plugins: [
{
id: "alpha",
@@ -69,6 +76,7 @@ describe("resolveManifestDeclaredWebProviderCandidatePluginIds", () => {
configKey: "webSearch",
}),
).toEqual(["alpha", "beta"]);
expect(mocks.loadPluginManifestRegistry).toHaveBeenCalledTimes(1);
expect(mocks.loadPluginRegistrySnapshot).toHaveBeenCalledTimes(1);
expect(mocks.loadPluginManifestRegistryForInstalledIndex).toHaveBeenCalledTimes(1);
});
});

View File

@@ -1,10 +1,8 @@
import { resolveBundledPluginCompatibleLoadValues } from "./activation-context.js";
import type { PluginLoadOptions } from "./loader.js";
import {
loadPluginManifestRegistry,
resolveManifestContractPluginIds,
type PluginManifestRecord,
} from "./manifest-registry.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestRecord } from "./manifest-registry.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import {
createPluginIdScopeSet,
normalizePluginIdScope,
@@ -62,6 +60,25 @@ function pluginManifestDeclaresProviderConfig(
return typeof properties === "object" && properties !== null && configKey in properties;
}
function loadInstalledWebProviderManifestRecords(params: {
config?: PluginLoadOptions["config"];
workspaceDir?: string;
env?: PluginLoadOptions["env"];
}): readonly PluginManifestRecord[] {
const index = loadPluginRegistrySnapshot({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
return loadPluginManifestRegistryForInstalledIndex({
index,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
includeDisabled: true,
}).plugins;
}
export function resolveManifestDeclaredWebProviderCandidatePluginIds(params: {
contract: WebProviderContract;
configKey: WebProviderConfigKey;
@@ -73,12 +90,12 @@ export function resolveManifestDeclaredWebProviderCandidatePluginIds(params: {
}): string[] | undefined {
const scopedPluginIds = normalizePluginIdScope(params.onlyPluginIds);
const onlyPluginIdSet = createPluginIdScopeSet(scopedPluginIds);
const ids = loadPluginManifestRegistry({
const ids = loadInstalledWebProviderManifestRecords({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
})
.plugins.filter(
.filter(
(plugin) =>
(!params.origin || plugin.origin === params.origin) &&
(!onlyPluginIdSet || onlyPluginIdSet.has(plugin.id)) &&
@@ -98,13 +115,13 @@ function resolveBundledWebProviderCompatPluginIds(params: {
workspaceDir?: string;
env?: PluginLoadOptions["env"];
}): string[] {
return resolveManifestContractPluginIds({
contract: params.contract,
origin: "bundled",
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
});
return loadInstalledWebProviderManifestRecords(params)
.filter(
(plugin) =>
plugin.origin === "bundled" && (plugin.contracts?.[params.contract]?.length ?? 0) > 0,
)
.map((plugin) => plugin.id)
.toSorted((left, right) => left.localeCompare(right));
}
export function resolveBundledWebProviderResolutionConfig(params: {

View File

@@ -1,6 +1,7 @@
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { loadPluginManifestRegistry } from "./manifest-registry.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestRecord } from "./manifest-registry.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { resolvePluginWebSearchProviders } from "./web-search-providers.runtime.js";
function hasConfiguredCredentialValue(value: unknown): boolean {
@@ -43,9 +44,15 @@ function hasManifestWebSearchEnvCredentialCandidate(params: {
if (!env) {
return false;
}
return loadPluginManifestRegistry({
const index = loadPluginRegistrySnapshot({
config: params.config,
env,
});
return loadPluginManifestRegistryForInstalledIndex({
index,
config: params.config,
env,
includeDisabled: true,
}).plugins.some((plugin) => {
if (params.origin && plugin.origin !== params.origin) {
return false;