refactor: reuse plugin metadata snapshots

This commit is contained in:
Peter Steinberger
2026-05-02 07:13:11 +01:00
parent f9cdf2f552
commit 71da5af164
13 changed files with 312 additions and 118 deletions

View File

@@ -0,0 +1,66 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
getCurrentPluginMetadataSnapshot: vi.fn(),
loadPluginMetadataSnapshot: vi.fn(),
}));
vi.mock("./current-plugin-metadata-snapshot.js", () => ({
getCurrentPluginMetadataSnapshot: mocks.getCurrentPluginMetadataSnapshot,
}));
vi.mock("./plugin-metadata-snapshot.js", () => ({
loadPluginMetadataSnapshot: mocks.loadPluginMetadataSnapshot,
}));
import { loadManifestContractSnapshot } from "./manifest-contract-eligibility.js";
describe("loadManifestContractSnapshot", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.getCurrentPluginMetadataSnapshot.mockReturnValue(undefined);
mocks.loadPluginMetadataSnapshot.mockReturnValue({
index: { plugins: [] },
plugins: [],
});
});
it("checks the current metadata snapshot with env and workspace scope", () => {
const env = { HOME: "/home/snapshot" } as NodeJS.ProcessEnv;
const current = {
index: { plugins: [] },
plugins: [],
};
mocks.getCurrentPluginMetadataSnapshot.mockReturnValue(current);
expect(loadManifestContractSnapshot({ config: {}, workspaceDir: "/workspace", env })).toBe(
current,
);
expect(mocks.getCurrentPluginMetadataSnapshot).toHaveBeenCalledWith({
config: {},
env,
workspaceDir: "/workspace",
});
expect(mocks.loadPluginMetadataSnapshot).not.toHaveBeenCalled();
});
it("falls back to the shared metadata snapshot loader", () => {
const env = { HOME: "/home/fallback" } as NodeJS.ProcessEnv;
const snapshot = {
index: { plugins: [{ pluginId: "demo" }] },
plugins: [{ id: "demo" }],
};
mocks.loadPluginMetadataSnapshot.mockReturnValue(snapshot);
expect(loadManifestContractSnapshot({ config: {}, env })).toEqual({
index: snapshot.index,
plugins: snapshot.plugins,
});
expect(mocks.loadPluginMetadataSnapshot).toHaveBeenCalledWith({
config: {},
env,
});
});
});

View File

@@ -1,10 +1,12 @@
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { getCurrentPluginMetadataSnapshot } from "./current-plugin-metadata-snapshot.js";
import { isInstalledPluginEnabled } from "./installed-plugin-index.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestContractListKey, PluginManifestRecord } from "./manifest-registry.js";
import type { PluginMetadataSnapshot } from "./plugin-metadata-snapshot.types.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadPluginMetadataSnapshot } from "./plugin-metadata-snapshot.js";
import type {
PluginMetadataManifestView,
PluginMetadataSnapshot,
} from "./plugin-metadata-snapshot.types.js";
export function isManifestPluginAvailableForControlPlane(params: {
snapshot: Pick<PluginMetadataSnapshot, "index">;
@@ -65,28 +67,23 @@ export function loadManifestContractSnapshot(params: {
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
}): Pick<PluginMetadataSnapshot, "index" | "plugins"> {
}): PluginMetadataManifestView {
const env = params.env ?? process.env;
const current = getCurrentPluginMetadataSnapshot({
config: params.config,
env,
...(params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}),
});
if (current) {
return current;
}
const env = params.env ?? process.env;
const index = loadPluginRegistrySnapshot({
config: params.config,
const snapshot = loadPluginMetadataSnapshot({
config: params.config ?? {},
env,
...(params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}),
});
return {
index,
plugins: loadPluginManifestRegistryForInstalledIndex({
index,
config: params.config,
env,
includeDisabled: true,
...(params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}),
}).plugins,
index: snapshot.index,
plugins: snapshot.plugins,
};
}

View File

@@ -17,6 +17,8 @@ import { createPluginRegistryIdNormalizer } from "./plugin-registry-id-normalize
import { loadPluginRegistrySnapshotWithMetadata } from "./plugin-registry-snapshot.js";
export type {
LoadPluginMetadataSnapshotParams,
PluginMetadataManifestView,
PluginMetadataRegistryView,
PluginMetadataSnapshot,
PluginMetadataSnapshotMetrics,
PluginMetadataSnapshotOwnerMaps,
@@ -146,6 +148,12 @@ function buildPluginMetadataOwnerMaps(
};
}
export function listPluginOriginsFromMetadataSnapshot(
snapshot: Pick<PluginMetadataSnapshot, "plugins">,
): ReadonlyMap<string, PluginManifestRecord["origin"]> {
return new Map(snapshot.plugins.map((record) => [record.id, record.origin]));
}
export function loadPluginMetadataSnapshot(
params: LoadPluginMetadataSnapshotParams,
): PluginMetadataSnapshot {

View File

@@ -48,6 +48,10 @@ export type PluginMetadataSnapshot = {
metrics: PluginMetadataSnapshotMetrics;
};
export type PluginMetadataRegistryView = Pick<PluginMetadataSnapshot, "index" | "manifestRegistry">;
export type PluginMetadataManifestView = Pick<PluginMetadataSnapshot, "index" | "plugins">;
export type LoadPluginMetadataSnapshotParams = {
config: OpenClawConfig;
workspaceDir?: string;

View File

@@ -3,20 +3,19 @@ import type { PluginManifestRecord } from "./manifest-registry.js";
import type { ProviderPlugin } from "./types.js";
const mocks = vi.hoisted(() => ({
loadPluginRegistrySnapshot: vi.fn(),
loadPluginManifestRegistryForInstalledIndex: vi.fn(),
loadPluginMetadataSnapshot: vi.fn(),
resolveDiscoveredProviderPluginIds: vi.fn(),
resolvePluginProviders: vi.fn(),
loadSource: vi.fn(),
}));
vi.mock("./plugin-registry.js", () => ({
loadPluginRegistrySnapshot: mocks.loadPluginRegistrySnapshot,
}));
vi.mock("./manifest-registry-installed.js", () => ({
loadPluginManifestRegistryForInstalledIndex: mocks.loadPluginManifestRegistryForInstalledIndex,
}));
vi.mock("./plugin-metadata-snapshot.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./plugin-metadata-snapshot.js")>();
return {
...actual,
loadPluginMetadataSnapshot: mocks.loadPluginMetadataSnapshot,
};
});
vi.mock("./providers.js", () => ({
resolveDiscoveredProviderPluginIds: mocks.resolveDiscoveredProviderPluginIds,
@@ -82,11 +81,13 @@ function createProvider(params: { id: string; mode: "static" | "catalog" }): Pro
describe("resolvePluginDiscoveryProvidersRuntime", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.loadPluginRegistrySnapshot.mockReturnValue({ plugins: [] });
mocks.resolveDiscoveredProviderPluginIds.mockReturnValue(["deepseek"]);
mocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({
plugins: [createManifestPlugin("deepseek")],
diagnostics: [],
mocks.loadPluginMetadataSnapshot.mockReturnValue({
index: { plugins: [] },
manifestRegistry: {
plugins: [createManifestPlugin("deepseek")],
diagnostics: [],
},
});
});
@@ -116,20 +117,23 @@ describe("resolvePluginDiscoveryProvidersRuntime", () => {
"kilocode",
"unused",
]);
mocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({
plugins: [
createManifestPlugin("codex"),
createManifestPlugin("deepseek"),
createManifestPluginWithoutDiscovery({
id: "kilocode",
providerAuthEnvVars: { kilocode: ["KILOCODE_API_KEY"] },
}),
createManifestPluginWithoutDiscovery({
id: "unused",
providerAuthEnvVars: { unused: ["UNUSED_API_KEY"] },
}),
],
diagnostics: [],
mocks.loadPluginMetadataSnapshot.mockReturnValue({
index: { plugins: [] },
manifestRegistry: {
plugins: [
createManifestPlugin("codex"),
createManifestPlugin("deepseek"),
createManifestPluginWithoutDiscovery({
id: "kilocode",
providerAuthEnvVars: { kilocode: ["KILOCODE_API_KEY"] },
}),
createManifestPluginWithoutDiscovery({
id: "unused",
providerAuthEnvVars: { unused: ["UNUSED_API_KEY"] },
}),
],
diagnostics: [],
},
});
mocks.loadSource.mockImplementation((modulePath: string) =>
modulePath.includes("/codex/")
@@ -150,27 +154,25 @@ describe("resolvePluginDiscoveryProvidersRuntime", () => {
);
});
it("shares one registry snapshot and manifest registry between provider id discovery and entry loading", () => {
it("shares one metadata snapshot between provider id discovery and entry loading", () => {
const registry = { plugins: [] };
const manifestRegistry = {
plugins: [createManifestPlugin("deepseek")],
diagnostics: [],
};
mocks.loadPluginRegistrySnapshot.mockReturnValue(registry);
mocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue(manifestRegistry);
mocks.loadPluginMetadataSnapshot.mockReturnValue({
index: registry,
manifestRegistry,
});
mocks.loadSource.mockReturnValue(createProvider({ id: "deepseek", mode: "catalog" }));
resolvePluginDiscoveryProvidersRuntime({ config: {}, env: {} as NodeJS.ProcessEnv });
expect(mocks.loadPluginRegistrySnapshot).toHaveBeenCalledOnce();
expect(mocks.loadPluginManifestRegistryForInstalledIndex).toHaveBeenCalledWith({
index: registry,
expect(mocks.loadPluginMetadataSnapshot).toHaveBeenCalledWith({
config: {},
workspaceDir: undefined,
env: {},
includeDisabled: true,
});
expect(mocks.loadPluginManifestRegistryForInstalledIndex).toHaveBeenCalledOnce();
expect(mocks.loadPluginMetadataSnapshot).toHaveBeenCalledOnce();
expect(mocks.resolveDiscoveredProviderPluginIds).toHaveBeenCalledWith(
expect.objectContaining({
registry,
@@ -203,8 +205,7 @@ describe("resolvePluginDiscoveryProvidersRuntime", () => {
}),
]);
expect(mocks.loadPluginRegistrySnapshot).not.toHaveBeenCalled();
expect(mocks.loadPluginManifestRegistryForInstalledIndex).not.toHaveBeenCalled();
expect(mocks.loadPluginMetadataSnapshot).not.toHaveBeenCalled();
expect(mocks.resolveDiscoveredProviderPluginIds).toHaveBeenCalledWith(
expect.objectContaining({
registry,
@@ -228,9 +229,12 @@ describe("resolvePluginDiscoveryProvidersRuntime", () => {
});
it("does not fall back to full plugin loading when discovery entries are requested only", () => {
mocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue({
plugins: [createManifestPluginWithoutDiscovery({ id: "deepseek" })],
diagnostics: [],
mocks.loadPluginMetadataSnapshot.mockReturnValue({
index: { plugins: [] },
manifestRegistry: {
plugins: [createManifestPluginWithoutDiscovery({ id: "deepseek" })],
diagnostics: [],
},
});
expect(resolvePluginDiscoveryProvidersRuntime({ discoveryEntriesOnly: true })).toEqual([]);

View File

@@ -1,8 +1,7 @@
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestRecord } from "./manifest-registry.js";
import type { PluginMetadataSnapshot } from "./plugin-metadata-snapshot.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
import { loadPluginMetadataSnapshot } from "./plugin-metadata-snapshot.js";
import type { PluginMetadataRegistryView } from "./plugin-metadata-snapshot.types.js";
import { resolveDiscoveredProviderPluginIds } from "./providers.js";
import { resolvePluginProviders } from "./providers.runtime.js";
import { createPluginSourceLoader } from "./source-loader.js";
@@ -77,18 +76,17 @@ function resolveProviderDiscoveryEntryPlugins(params: {
includeUntrustedWorkspacePlugins?: boolean;
requireCompleteDiscoveryEntryCoverage?: boolean;
discoveryEntriesOnly?: boolean;
pluginMetadataSnapshot?: Pick<PluginMetadataSnapshot, "index" | "manifestRegistry">;
pluginMetadataSnapshot?: PluginMetadataRegistryView;
}): ProviderDiscoveryEntryResult {
const registry = params.pluginMetadataSnapshot?.index ?? loadPluginRegistrySnapshot(params);
const manifestRegistry =
params.pluginMetadataSnapshot?.manifestRegistry ??
loadPluginManifestRegistryForInstalledIndex({
index: registry,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
includeDisabled: true,
const metadataSnapshot =
params.pluginMetadataSnapshot ??
loadPluginMetadataSnapshot({
config: params.config ?? {},
env: params.env ?? process.env,
...(params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}),
});
const registry = metadataSnapshot.index;
const manifestRegistry = metadataSnapshot.manifestRegistry;
const pluginIds = resolveDiscoveredProviderPluginIds({
...params,
registry,
@@ -148,10 +146,10 @@ export function resolvePluginDiscoveryProvidersRuntime(params: {
includeUntrustedWorkspacePlugins?: boolean;
requireCompleteDiscoveryEntryCoverage?: boolean;
discoveryEntriesOnly?: boolean;
pluginMetadataSnapshot?: Pick<PluginMetadataSnapshot, "index" | "manifestRegistry">;
pluginMetadataSnapshot?: PluginMetadataRegistryView;
}): ProviderPlugin[] {
const env = params.env ?? process.env;
const entryResult = resolveProviderDiscoveryEntryPlugins(params);
const entryResult = resolveProviderDiscoveryEntryPlugins({ ...params, env });
if (params.discoveryEntriesOnly === true) {
return entryResult.providers;
}

View File

@@ -1,7 +1,7 @@
import { normalizeProviderId } from "../agents/model-selection.js";
import type { ModelProviderConfig } from "../config/types.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import type { PluginMetadataSnapshot } from "./plugin-metadata-snapshot.js";
import type { PluginMetadataRegistryView } from "./plugin-metadata-snapshot.types.js";
import {
listPluginContributionIds,
loadPluginRegistrySnapshot,
@@ -43,7 +43,7 @@ export type ResolveRuntimePluginDiscoveryProvidersParams = {
includeUntrustedWorkspacePlugins?: boolean;
requireCompleteDiscoveryEntryCoverage?: boolean;
discoveryEntriesOnly?: boolean;
pluginMetadataSnapshot?: Pick<PluginMetadataSnapshot, "index" | "manifestRegistry">;
pluginMetadataSnapshot?: PluginMetadataRegistryView;
};
export type ResolveInstalledPluginProviderContributionIdsParams = LoadPluginRegistryParams & {