mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-07 17:10:43 +00:00
refactor: reuse plugin metadata snapshots
This commit is contained in:
66
src/plugins/manifest-contract-eligibility.test.ts
Normal file
66
src/plugins/manifest-contract-eligibility.test.ts
Normal 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,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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([]);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 & {
|
||||
|
||||
Reference in New Issue
Block a user