refactor: unify plugin metadata snapshot callers

This commit is contained in:
Peter Steinberger
2026-05-02 07:51:12 +01:00
parent e9ba9ffad0
commit ebb45a8a28
14 changed files with 285 additions and 100 deletions

View File

@@ -0,0 +1,68 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
const loadPluginMetadataSnapshot = vi.hoisted(() => vi.fn());
vi.mock("./plugin-metadata-snapshot.js", () => ({
loadPluginMetadataSnapshot,
}));
import { resolveManifestContractRuntimePluginResolution } from "./manifest-contract-runtime.js";
describe("resolveManifestContractRuntimePluginResolution", () => {
beforeEach(() => {
loadPluginMetadataSnapshot.mockReset();
loadPluginMetadataSnapshot.mockReturnValue({
index: { plugins: [] },
plugins: [],
});
});
it("resolves contract plugins from the shared metadata snapshot", () => {
loadPluginMetadataSnapshot.mockReturnValue({
index: {
plugins: [
{
pluginId: "bundled-search",
origin: "bundled",
enabled: true,
enabledByDefault: true,
},
{
pluginId: "external-search",
origin: "global",
enabled: true,
enabledByDefault: true,
},
],
},
plugins: [
{
id: "bundled-search",
origin: "bundled",
contracts: { webSearchProviders: ["search"] },
},
{
id: "external-search",
origin: "global",
contracts: { webSearchProviders: ["search"] },
},
],
});
expect(
resolveManifestContractRuntimePluginResolution({
cfg: {},
contract: "webSearchProviders",
value: "search",
}),
).toEqual({
pluginIds: ["bundled-search", "external-search"],
bundledCompatPluginIds: ["bundled-search"],
});
expect(loadPluginMetadataSnapshot).toHaveBeenCalledWith({
config: {},
env: process.env,
preferPersisted: false,
});
});
});

View File

@@ -3,9 +3,8 @@ import {
hasManifestContractValue,
listAvailableManifestContractPlugins,
} from "./manifest-contract-eligibility.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestContractListKey } from "./manifest-registry.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadPluginMetadataSnapshot } from "./plugin-metadata-snapshot.js";
export type ManifestContractRuntimePluginResolution = {
pluginIds: string[];
@@ -21,17 +20,12 @@ export function resolveManifestContractRuntimePluginResolution(params: {
contract: PluginManifestContractListKey;
value?: string;
}): ManifestContractRuntimePluginResolution {
const index = loadPluginRegistrySnapshot({
config: params.cfg,
const snapshot = loadPluginMetadataSnapshot({
config: params.cfg ?? {},
env: process.env,
...DEMAND_ONLY_CONTRACT_LOOKUP_OPTIONS,
});
const allContractPlugins = loadPluginManifestRegistryForInstalledIndex({
index,
config: params.cfg,
env: process.env,
includeDisabled: true,
}).plugins.filter((plugin) =>
const allContractPlugins = snapshot.plugins.filter((plugin) =>
hasManifestContractValue({
plugin,
contract: params.contract,
@@ -42,7 +36,7 @@ export function resolveManifestContractRuntimePluginResolution(params: {
.filter((plugin) => plugin.origin === "bundled")
.map((plugin) => plugin.id);
const pluginIds = listAvailableManifestContractPlugins({
snapshot: { index, plugins: allContractPlugins },
snapshot: { index: snapshot.index, plugins: allContractPlugins },
contract: params.contract,
value: params.value,
config: params.cfg,

View File

@@ -180,7 +180,9 @@ function loadPluginMetadataSnapshotImpl(
const registryResult = loadPluginRegistrySnapshotWithMetadata({
config: params.config,
workspaceDir: params.workspaceDir,
...(params.stateDir ? { stateDir: params.stateDir } : {}),
env: params.env,
...(params.preferPersisted !== undefined ? { preferPersisted: params.preferPersisted } : {}),
...(params.index ? { index: params.index } : {}),
});
const registrySnapshotMs = performance.now() - registryStartedAt;

View File

@@ -55,6 +55,8 @@ export type PluginMetadataManifestView = Pick<PluginMetadataSnapshot, "index" |
export type LoadPluginMetadataSnapshotParams = {
config: OpenClawConfig;
workspaceDir?: string;
stateDir?: string;
env: NodeJS.ProcessEnv;
index?: InstalledPluginIndex;
preferPersisted?: boolean;
};

View File

@@ -2,6 +2,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
const loadPluginRegistrySnapshotMock = vi.hoisted(() => vi.fn());
const loadPluginManifestRegistryForInstalledIndexMock = vi.hoisted(() => vi.fn());
const loadPluginMetadataSnapshotMock = vi.hoisted(() => vi.fn());
vi.mock("./plugin-registry.js", async (importOriginal) => ({
...(await importOriginal<typeof import("./plugin-registry.js")>()),
@@ -10,36 +11,39 @@ vi.mock("./plugin-registry.js", async (importOriginal) => ({
vi.mock("./manifest-registry-installed.js", () => ({
loadPluginManifestRegistryForInstalledIndex: loadPluginManifestRegistryForInstalledIndexMock,
}));
vi.mock("./plugin-metadata-snapshot.js", () => ({
loadPluginMetadataSnapshot: loadPluginMetadataSnapshotMock,
}));
afterEach(() => {
loadPluginRegistrySnapshotMock.mockReset();
loadPluginManifestRegistryForInstalledIndexMock.mockReset();
loadPluginMetadataSnapshotMock.mockReset();
});
describe("setup-registry runtime fallback", () => {
it("uses bundled registry cliBackends when the setup-registry runtime is unavailable", async () => {
loadPluginRegistrySnapshotMock.mockReturnValue({
diagnostics: [],
plugins: [
{
pluginId: "openai",
origin: "bundled",
enabled: true,
},
{
pluginId: "disabled",
origin: "bundled",
enabled: false,
},
{
pluginId: "local",
origin: "workspace",
enabled: true,
},
],
});
loadPluginManifestRegistryForInstalledIndexMock.mockReturnValue({
diagnostics: [],
loadPluginMetadataSnapshotMock.mockReturnValue({
index: {
diagnostics: [],
plugins: [
{
pluginId: "openai",
origin: "bundled",
enabled: true,
},
{
pluginId: "disabled",
origin: "bundled",
enabled: false,
},
{
pluginId: "local",
origin: "workspace",
enabled: true,
},
],
},
plugins: [
{
id: "openai",
@@ -60,25 +64,26 @@ describe("setup-registry runtime fallback", () => {
});
expect(resolvePluginSetupCliBackendRuntime({ backend: "local-cli" })).toBeUndefined();
expect(resolvePluginSetupCliBackendRuntime({ backend: "disabled-cli" })).toBeUndefined();
expect(loadPluginRegistrySnapshotMock).toHaveBeenCalledTimes(3);
expect(loadPluginRegistrySnapshotMock).toHaveBeenCalledWith({});
expect(loadPluginManifestRegistryForInstalledIndexMock).toHaveBeenCalledWith({
index: expect.objectContaining({
plugins: expect.arrayContaining([expect.objectContaining({ pluginId: "openai" })]),
}),
expect(loadPluginMetadataSnapshotMock).toHaveBeenCalledTimes(3);
expect(loadPluginMetadataSnapshotMock).toHaveBeenCalledWith({
config: {},
env: process.env,
});
});
it("preserves fail-closed setup lookup when the runtime module explicitly declines to resolve", async () => {
loadPluginRegistrySnapshotMock.mockReturnValue({
diagnostics: [],
plugins: [
{
pluginId: "openai",
origin: "bundled",
enabled: true,
},
],
loadPluginMetadataSnapshotMock.mockReturnValue({
index: {
diagnostics: [],
plugins: [
{
pluginId: "openai",
origin: "bundled",
enabled: true,
},
],
},
plugins: [],
});
const { __testing, resolvePluginSetupCliBackendRuntime } =
@@ -89,6 +94,6 @@ describe("setup-registry runtime fallback", () => {
});
expect(resolvePluginSetupCliBackendRuntime({ backend: "codex-cli" })).toBeUndefined();
expect(loadPluginRegistrySnapshotMock).not.toHaveBeenCalled();
expect(loadPluginMetadataSnapshotMock).not.toHaveBeenCalled();
});
});

View File

@@ -1,7 +1,7 @@
import { createRequire } from "node:module";
import { normalizeProviderId } from "../agents/provider-id.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { isInstalledPluginEnabled } from "./installed-plugin-index.js";
import { loadPluginMetadataSnapshot } from "./plugin-metadata-snapshot.js";
type SetupRegistryRuntimeModule = Pick<
typeof import("./setup-registry.js"),
@@ -30,11 +30,9 @@ export const __testing = {
};
function resolveBundledSetupCliBackends(): SetupCliBackendRuntimeEntry[] {
const index = loadPluginRegistrySnapshot({});
return loadPluginManifestRegistryForInstalledIndex({
index,
}).plugins.flatMap((plugin) => {
if (plugin.origin !== "bundled") {
const snapshot = loadPluginMetadataSnapshot({ config: {}, env: process.env });
return snapshot.plugins.flatMap((plugin) => {
if (plugin.origin !== "bundled" || !isInstalledPluginEnabled(snapshot.index, plugin.id)) {
return [];
}
return [...plugin.cliBackends, ...(plugin.setup?.cliBackends ?? [])].map(

View File

@@ -15,6 +15,21 @@ const loadPluginMetadataRegistrySnapshotMock = vi.fn();
const loadPluginManifestRegistryForPluginRegistryMock = vi.fn();
const loadPluginRegistrySnapshotWithMetadataMock = vi.fn();
const loadPluginManifestRegistryForInstalledIndexMock = vi.fn();
const loadPluginMetadataSnapshotMock = vi.fn((rawParams: unknown = {}) => {
const params = rawParams as { index?: unknown };
const manifestRegistry = loadPluginManifestRegistryForInstalledIndexMock(params) ?? {
plugins: [],
diagnostics: [],
};
return {
index: params.index ?? createInstalledPluginIndexSnapshot([]),
manifestRegistry,
plugins: manifestRegistry.plugins,
byPluginId: new Map(
manifestRegistry.plugins.map((plugin: { id: string }) => [plugin.id, plugin]),
),
};
});
const applyPluginAutoEnableMock = vi.fn();
const resolveBundledProviderCompatPluginIdsMock = vi.fn();
const withBundledPluginAllowlistCompatMock = vi.fn();
@@ -61,6 +76,10 @@ vi.mock("./manifest-registry-installed.js", () => ({
loadPluginManifestRegistryForInstalledIndexMock(...args),
}));
vi.mock("./plugin-metadata-snapshot.js", () => ({
loadPluginMetadataSnapshot: (...args: unknown[]) => loadPluginMetadataSnapshotMock(...args),
}));
vi.mock("./providers.js", () => ({
resolveBundledProviderCompatPluginIds: (...args: unknown[]) =>
resolveBundledProviderCompatPluginIdsMock(...args),
@@ -370,6 +389,7 @@ describe("plugin status reports", () => {
loadPluginManifestRegistryForPluginRegistryMock.mockReset();
loadPluginRegistrySnapshotWithMetadataMock.mockReset();
loadPluginManifestRegistryForInstalledIndexMock.mockReset();
loadPluginMetadataSnapshotMock.mockClear();
applyPluginAutoEnableMock.mockReset();
resolveBundledProviderCompatPluginIdsMock.mockReset();
withBundledPluginAllowlistCompatMock.mockReset();

View File

@@ -20,12 +20,11 @@ import {
type PluginInspectShape,
} from "./inspect-shape.js";
import { loadOpenClawPlugins } from "./loader.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestRecord } from "./manifest-registry.js";
import type { PluginDiagnostic } from "./manifest-types.js";
import { tracePluginLifecyclePhase } from "./plugin-lifecycle-trace.js";
import { loadPluginMetadataSnapshot } from "./plugin-metadata-snapshot.js";
import {
loadPluginManifestRegistryForPluginRegistry,
loadPluginRegistrySnapshotWithMetadata,
type PluginRegistrySnapshotDiagnostic,
type PluginRegistrySnapshotSource,
@@ -230,14 +229,13 @@ export function buildPluginRegistrySnapshotReport(
}),
{ surface: "status" },
);
const manifestRegistry = loadPluginManifestRegistryForInstalledIndex({
const metadataSnapshot = loadPluginMetadataSnapshot({
index: result.snapshot,
config,
env: params?.env,
env: params?.env ?? process.env,
workspaceDir: params?.workspaceDir,
includeDisabled: true,
});
const manifestByPluginId = new Map(manifestRegistry.plugins.map((plugin) => [plugin.id, plugin]));
const manifestByPluginId = metadataSnapshot.byPluginId;
return {
workspaceDir: params?.workspaceDir,
...createEmptyPluginRegistry(),
@@ -258,12 +256,11 @@ function buildPluginReport(
const initialWorkspaceDir =
params?.workspaceDir ??
resolveAgentWorkspaceDir(rawConfig, resolveDefaultAgentId(rawConfig), params?.env);
const manifestRegistry = !loadModules
? loadPluginManifestRegistryForPluginRegistry({
const metadataSnapshot = !loadModules
? loadPluginMetadataSnapshot({
config: rawConfig,
env: params?.env,
env: params?.env ?? process.env,
workspaceDir: initialWorkspaceDir,
includeDisabled: true,
})
: undefined;
const baseContext = resolvePluginRuntimeLoadContext({
@@ -271,7 +268,7 @@ function buildPluginReport(
env: params?.env,
logger: params?.logger,
workspaceDir: initialWorkspaceDir,
manifestRegistry,
manifestRegistry: metadataSnapshot?.manifestRegistry,
});
const workspaceDir =
baseContext.workspaceDir ?? initialWorkspaceDir ?? resolveDefaultAgentWorkspaceDir();
@@ -294,7 +291,7 @@ function buildPluginReport(
config,
workspaceDir,
env: params?.env,
manifestRegistry,
manifestRegistry: metadataSnapshot?.manifestRegistry,
});
const effectiveConfig = withBundledPluginAllowlistCompat({
config,
@@ -344,7 +341,7 @@ function buildPluginReport(
logger: params?.logger,
loadModules: false,
onlyPluginIds,
manifestRegistry,
manifestRegistry: metadataSnapshot?.manifestRegistry,
runtimeContext: context,
}),
{ surface: "status", onlyPluginCount: onlyPluginIds?.length },