From ca4f964547470f4a23abb32676e11dd2bf231dad Mon Sep 17 00:00:00 2001 From: Shakker Date: Mon, 27 Apr 2026 16:13:19 +0100 Subject: [PATCH] refactor: let config validation use plugin metadata snapshot --- .../validation.channel-metadata.test.ts | 27 +++++++++++++++++++ src/config/validation.ts | 19 ++++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/config/validation.channel-metadata.test.ts b/src/config/validation.channel-metadata.test.ts index 01f0f099760..0e007049a3c 100644 --- a/src/config/validation.channel-metadata.test.ts +++ b/src/config/validation.channel-metadata.test.ts @@ -234,4 +234,31 @@ describe("validateConfigObjectWithPlugins bundled allowlist compatibility", () = expect(result.ok).toBe(true); expect(mockLoadPluginManifestRegistry).toHaveBeenCalledOnce(); }); + + it("uses a provided plugin metadata snapshot during plugin validation", () => { + const result = validateConfigObjectWithPlugins( + { + plugins: { + entries: { + opik: { + enabled: true, + }, + }, + }, + }, + { + pluginMetadataSnapshot: { + manifestRegistry: createPluginConfigSchemaRegistry(), + }, + }, + ); + + expect(result.ok).toBe(true); + expect(mockLoadPluginManifestRegistry).not.toHaveBeenCalled(); + if (result.ok) { + expect(result.config.plugins?.entries?.opik?.config).toEqual({ + workspace: "default-workspace", + }); + } + }); }); diff --git a/src/config/validation.ts b/src/config/validation.ts index b2c336f6fd1..7176b22ca44 100644 --- a/src/config/validation.ts +++ b/src/config/validation.ts @@ -16,6 +16,7 @@ import { import { loadInstalledPluginIndexInstallRecordsSync } from "../plugins/installed-plugin-index-record-reader.js"; import { resolveManifestCommandAliasOwnerInRegistry } from "../plugins/manifest-command-aliases.js"; import type { PluginManifestRegistry } from "../plugins/manifest-registry.js"; +import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js"; import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js"; import { validateJsonSchemaValue } from "../plugins/schema-validator.js"; import { hasKind } from "../plugins/slots.js"; @@ -706,31 +707,39 @@ type ValidateConfigWithPluginsResult = warnings: ConfigValidationIssue[]; }; +type ValidateConfigWithPluginsParams = { + env?: NodeJS.ProcessEnv; + pluginValidation?: "full" | "skip"; + pluginMetadataSnapshot?: Pick; +}; + export function validateConfigObjectWithPlugins( raw: unknown, - params?: { env?: NodeJS.ProcessEnv; pluginValidation?: "full" | "skip" }, + params?: ValidateConfigWithPluginsParams, ): ValidateConfigWithPluginsResult { return validateConfigObjectWithPluginsBase(raw, { applyDefaults: true, env: params?.env, pluginValidation: params?.pluginValidation ?? "full", + pluginMetadataSnapshot: params?.pluginMetadataSnapshot, }); } export function validateConfigObjectRawWithPlugins( raw: unknown, - params?: { env?: NodeJS.ProcessEnv; pluginValidation?: "full" | "skip" }, + params?: ValidateConfigWithPluginsParams, ): ValidateConfigWithPluginsResult { return validateConfigObjectWithPluginsBase(raw, { applyDefaults: false, env: params?.env, pluginValidation: params?.pluginValidation ?? "full", + pluginMetadataSnapshot: params?.pluginMetadataSnapshot, }); } function validateConfigObjectWithPluginsBase( raw: unknown, - opts: { applyDefaults: boolean; env?: NodeJS.ProcessEnv; pluginValidation?: "full" | "skip" }, + opts: ValidateConfigWithPluginsParams & { applyDefaults: boolean }, ): ValidateConfigWithPluginsResult { const base = opts.applyDefaults ? validateConfigObject(raw) : validateConfigObjectRaw(raw); if (!base.ok) { @@ -772,7 +781,9 @@ function validateConfigObjectWithPluginsBase( >; }; - let registryInfo: RegistryInfo | null = null; + let registryInfo: RegistryInfo | null = opts.pluginMetadataSnapshot + ? { registry: opts.pluginMetadataSnapshot.manifestRegistry } + : null; let compatConfig: OpenClawConfig | null | undefined; let compatPluginIds: ReadonlySet | null = null; let compatPluginIdsResolved = false;