fix(config): resolve plugin contracts cold

This commit is contained in:
Vincent Koc
2026-04-25 19:33:52 -07:00
parent a0ca546997
commit 10763781fd
5 changed files with 46 additions and 15 deletions

View File

@@ -3,15 +3,20 @@ import type { PluginManifestRegistry } from "./manifest-registry.js";
const mocks = vi.hoisted(() => ({
findBundledPluginMetadataById: vi.fn(),
loadPluginManifestRegistry: vi.fn(),
loadPluginManifestRegistryForInstalledIndex: vi.fn(),
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
}));
vi.mock("./bundled-plugin-metadata.js", () => ({
findBundledPluginMetadataById: mocks.findBundledPluginMetadataById,
}));
vi.mock("./manifest-registry.js", () => ({
loadPluginManifestRegistry: mocks.loadPluginManifestRegistry,
vi.mock("./manifest-registry-installed.js", () => ({
loadPluginManifestRegistryForInstalledIndex: mocks.loadPluginManifestRegistryForInstalledIndex,
}));
vi.mock("./plugin-registry.js", () => ({
loadPluginRegistrySnapshot: mocks.loadPluginRegistrySnapshot,
}));
import { resolvePluginConfigContractsById } from "./config-contracts.js";
@@ -26,12 +31,14 @@ function createRegistry(plugins: PluginManifestRegistry["plugins"]): PluginManif
describe("resolvePluginConfigContractsById", () => {
beforeEach(() => {
mocks.findBundledPluginMetadataById.mockReset();
mocks.loadPluginManifestRegistry.mockReset();
mocks.loadPluginManifestRegistry.mockReturnValue(createRegistry([]));
mocks.loadPluginManifestRegistryForInstalledIndex.mockReset();
mocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue(createRegistry([]));
mocks.loadPluginRegistrySnapshot.mockReset();
mocks.loadPluginRegistrySnapshot.mockReturnValue({ plugins: [] });
});
it("does not fall back to bundled metadata when registry already resolved a plugin without config contracts", () => {
mocks.loadPluginManifestRegistry.mockReturnValue(
mocks.loadPluginManifestRegistryForInstalledIndex.mockReturnValue(
createRegistry([
{
id: "brave",

View File

@@ -1,9 +1,10 @@
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { isRecord } from "../utils.js";
import { findBundledPluginMetadataById } from "./bundled-plugin-metadata.js";
import { loadPluginManifestRegistry } from "./manifest-registry.js";
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
import type { PluginManifestConfigContracts } from "./manifest.js";
import type { PluginOrigin } from "./plugin-origin.types.js";
import { loadPluginRegistrySnapshot } from "./plugin-registry.js";
export type PluginConfigContractMatch = {
path: string;
@@ -114,12 +115,19 @@ export function resolvePluginConfigContractsById(params: {
}
const resolvedPluginIds = new Set<string>();
const registry = loadPluginManifestRegistry({
const index = loadPluginRegistrySnapshot({
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
cache: params.cache,
});
const registry = loadPluginManifestRegistryForInstalledIndex({
index,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
includeDisabled: true,
});
for (const plugin of registry.plugins) {
if (!pluginIds.includes(plugin.id)) {
continue;

View File

@@ -1 +1,2 @@
export { loadPluginManifestRegistry } from "../plugins/manifest-registry.js";
export { loadPluginManifestRegistryForInstalledIndex } from "../plugins/manifest-registry-installed.js";
export { loadPluginRegistrySnapshot } from "../plugins/plugin-registry.js";

View File

@@ -1,17 +1,23 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { asConfig, setupSecretsRuntimeSnapshotTestHooks } from "./runtime.test-support.ts";
const loadPluginManifestRegistry = vi.hoisted(() => vi.fn());
const manifestMocks = vi.hoisted(() => ({
loadPluginManifestRegistryForInstalledIndex: vi.fn(),
loadPluginRegistrySnapshot: vi.fn(() => ({ plugins: [] })),
}));
vi.mock("./runtime-manifest.runtime.js", () => ({
loadPluginManifestRegistry,
loadPluginManifestRegistryForInstalledIndex:
manifestMocks.loadPluginManifestRegistryForInstalledIndex,
loadPluginRegistrySnapshot: manifestMocks.loadPluginRegistrySnapshot,
}));
const { prepareSecretsRuntimeSnapshot } = setupSecretsRuntimeSnapshotTestHooks();
describe("prepareSecretsRuntimeSnapshot loadable plugin origins", () => {
afterEach(() => {
loadPluginManifestRegistry.mockReset();
manifestMocks.loadPluginManifestRegistryForInstalledIndex.mockReset();
manifestMocks.loadPluginRegistrySnapshot.mockReset();
});
it("skips manifest registry loading when plugin entries are absent", async () => {
@@ -30,6 +36,7 @@ describe("prepareSecretsRuntimeSnapshot loadable plugin origins", () => {
includeAuthStoreRefs: false,
});
expect(loadPluginManifestRegistry).not.toHaveBeenCalled();
expect(manifestMocks.loadPluginManifestRegistryForInstalledIndex).not.toHaveBeenCalled();
expect(manifestMocks.loadPluginRegistrySnapshot).not.toHaveBeenCalled();
});
});

View File

@@ -140,13 +140,21 @@ async function resolveLoadablePluginOrigins(params: {
params.config,
resolveDefaultAgentId(params.config),
);
const { loadPluginManifestRegistry } = await loadRuntimeManifestHelpers();
const manifestRegistry = loadPluginManifestRegistry({
const { loadPluginManifestRegistryForInstalledIndex, loadPluginRegistrySnapshot } =
await loadRuntimeManifestHelpers();
const index = loadPluginRegistrySnapshot({
config: params.config,
workspaceDir,
cache: true,
env: params.env,
});
const manifestRegistry = loadPluginManifestRegistryForInstalledIndex({
index,
config: params.config,
workspaceDir,
env: params.env,
includeDisabled: true,
});
return new Map(manifestRegistry.plugins.map((record) => [record.id, record.origin]));
}