Skip to content

Commit 8c2bc95

Browse files
authored
fix(plugins): hydrate bundled channel config metadata
Hydrate bundled channel schema metadata through opt-in registry schema paths while keeping ordinary manifest registry loads lightweight.
1 parent c45a7d7 commit 8c2bc95

7 files changed

Lines changed: 142 additions & 2 deletions

src/config/doc-baseline.runtime.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { collectBundledChannelConfigs as collectBundledChannelConfigsImpl } from "../plugins/bundled-channel-config-metadata.js";
12
import { loadPluginManifestRegistry as loadPluginManifestRegistryImpl } from "../plugins/manifest-registry.js";
23
import {
34
collectChannelSchemaMetadata as collectChannelSchemaMetadataImpl,
@@ -6,6 +7,7 @@ import {
67
import { buildConfigSchema as buildConfigSchemaImpl } from "./schema.js";
78

89
export const loadPluginManifestRegistry = loadPluginManifestRegistryImpl;
10+
export const collectBundledChannelConfigs = collectBundledChannelConfigsImpl;
911
export const collectChannelSchemaMetadata = collectChannelSchemaMetadataImpl;
1012
export const collectPluginSchemaMetadata = collectPluginSchemaMetadataImpl;
1113
export const buildConfigSchema = buildConfigSchemaImpl;

src/config/doc-baseline.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ async function loadBundledConfigSchemaResponse(): Promise<ConfigSchemaResponse>
368368
cache: false,
369369
env,
370370
config: {},
371+
bundledChannelConfigCollector: runtime.collectBundledChannelConfigs,
371372
});
372373
logConfigDocBaselineDebug(`loaded ${manifestRegistry.plugins.length} bundled plugin manifests`);
373374
const bundledRegistry = {

src/config/runtime-schema.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
2+
import { collectBundledChannelConfigs } from "../plugins/bundled-channel-config-metadata.js";
23
import { loadPluginManifestRegistryForPluginRegistry } from "../plugins/plugin-registry.js";
34
import {
45
collectChannelSchemaMetadata,
@@ -16,6 +17,7 @@ function loadManifestRegistry(config: OpenClawConfig, env?: NodeJS.ProcessEnv) {
1617
env,
1718
workspaceDir,
1819
includeDisabled: true,
20+
bundledChannelConfigCollector: collectBundledChannelConfigs,
1921
});
2022
}
2123

src/plugins/manifest-registry-installed.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { PluginCandidate } from "./discovery.js";
55
import type { InstalledPluginIndex, InstalledPluginIndexRecord } from "./installed-plugin-index.js";
66
import { extractPluginInstallRecordsFromInstalledPluginIndex } from "./installed-plugin-index.js";
77
import { loadPluginManifestRegistry, type PluginManifestRegistry } from "./manifest-registry.js";
8+
import type { BundledChannelConfigCollector } from "./manifest-registry.js";
89
import {
910
DEFAULT_PLUGIN_ENTRY_CANDIDATES,
1011
getPackageManifestMetadata,
@@ -88,6 +89,7 @@ export function loadPluginManifestRegistryForInstalledIndex(params: {
8889
env?: NodeJS.ProcessEnv;
8990
pluginIds?: readonly string[];
9091
includeDisabled?: boolean;
92+
bundledChannelConfigCollector?: BundledChannelConfigCollector;
9193
}): PluginManifestRegistry {
9294
if (params.pluginIds && params.pluginIds.length === 0) {
9395
return { plugins: [], diagnostics: [] };
@@ -111,5 +113,8 @@ export function loadPluginManifestRegistryForInstalledIndex(params: {
111113
candidates,
112114
diagnostics: [...diagnostics],
113115
installRecords: extractPluginInstallRecordsFromInstalledPluginIndex(params.index),
116+
...(params.bundledChannelConfigCollector
117+
? { bundledChannelConfigCollector: params.bundledChannelConfigCollector }
118+
: {}),
114119
});
115120
}

src/plugins/manifest-registry.test.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import fs from "node:fs";
22
import path from "node:path";
33
import { afterEach, describe, expect, it, vi } from "vitest";
4+
import { collectChannelSchemaMetadata } from "../config/channel-config-metadata.js";
5+
import { collectBundledChannelConfigs } from "./bundled-channel-config-metadata.js";
46
import type { PluginCandidate } from "./discovery.js";
57
import {
68
clearPluginManifestRegistryCache,
@@ -630,6 +632,107 @@ describe("loadPluginManifestRegistry", () => {
630632
});
631633
});
632634

635+
it("hydrates bundled channel config metadata from plugin-local config surfaces", () => {
636+
const dir = makeTempDir();
637+
writeManifest(dir, {
638+
id: "alpha",
639+
channels: ["alpha"],
640+
configSchema: { type: "object" },
641+
channelConfigs: {
642+
alpha: {
643+
schema: {
644+
type: "object",
645+
properties: {
646+
manifestOnly: { type: "boolean" },
647+
},
648+
},
649+
uiHints: {
650+
manifestOnly: { help: "manifest hint" },
651+
},
652+
},
653+
},
654+
});
655+
writeTextFile(dir, "index.ts", "export {};\n");
656+
writeTextFile(
657+
dir,
658+
"src/config-schema.js",
659+
[
660+
"export const AlphaChannelConfigSchema = {",
661+
" schema: {",
662+
" type: 'object',",
663+
" properties: {",
664+
" generatedOnly: { type: 'string' },",
665+
" },",
666+
" additionalProperties: false,",
667+
" },",
668+
" uiHints: {",
669+
" generatedOnly: { label: 'Generated only' },",
670+
" },",
671+
"};",
672+
].join("\n"),
673+
);
674+
675+
const candidate = createPluginCandidate({
676+
idHint: "alpha",
677+
rootDir: dir,
678+
origin: "bundled",
679+
packageDir: dir,
680+
packageManifest: {
681+
channel: {
682+
id: "alpha",
683+
label: "Alpha",
684+
blurb: "Alpha channel",
685+
},
686+
},
687+
});
688+
expect(loadRegistry([candidate]).plugins[0]?.channelConfigs?.alpha?.schema).toEqual({
689+
type: "object",
690+
properties: {
691+
manifestOnly: { type: "boolean" },
692+
},
693+
});
694+
695+
const registry = loadPluginManifestRegistry({
696+
cache: false,
697+
bundledChannelConfigCollector: collectBundledChannelConfigs,
698+
candidates: [candidate],
699+
});
700+
701+
expect(registry.plugins[0]?.channelConfigs?.alpha).toEqual({
702+
schema: {
703+
type: "object",
704+
properties: {
705+
generatedOnly: { type: "string" },
706+
},
707+
additionalProperties: false,
708+
},
709+
label: "Alpha",
710+
description: "Alpha channel",
711+
uiHints: {
712+
generatedOnly: { label: "Generated only" },
713+
manifestOnly: { help: "manifest hint" },
714+
},
715+
});
716+
expect(collectChannelSchemaMetadata(registry)).toEqual([
717+
{
718+
id: "alpha",
719+
label: "Alpha",
720+
description: "Alpha channel",
721+
configSchema: {
722+
type: "object",
723+
properties: {
724+
generatedOnly: { type: "string" },
725+
},
726+
additionalProperties: false,
727+
},
728+
configUiHints: {
729+
generatedOnly: { label: "Generated only" },
730+
manifestOnly: { help: "manifest hint" },
731+
},
732+
},
733+
]);
734+
});
735+
633736
it("reports non-bundled providerAuthEnvVars as deprecated compat metadata", () => {
634737
const dir = makeTempDir();
635738
writeManifest(dir, {

src/plugins/manifest-registry.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ export type PluginManifestRegistry = {
158158
diagnostics: PluginDiagnostic[];
159159
};
160160

161+
export type BundledChannelConfigCollector = (params: {
162+
pluginDir: string;
163+
manifest: PluginManifest;
164+
packageManifest?: OpenClawPackageManifest;
165+
}) => Record<string, PluginManifestChannelConfig> | undefined;
166+
161167
const registryCache = pluginManifestRegistryCache as Map<
162168
string,
163169
{ expiresAt: number; registry: PluginManifestRegistry }
@@ -293,9 +299,18 @@ function buildRecord(params: {
293299
manifestPath: string;
294300
schemaCacheKey?: string;
295301
configSchema?: Record<string, unknown>;
302+
bundledChannelConfigCollector?: BundledChannelConfigCollector;
296303
}): PluginManifestRecord {
304+
const manifestChannelConfigs =
305+
params.candidate.origin === "bundled" && params.bundledChannelConfigCollector
306+
? params.bundledChannelConfigCollector({
307+
pluginDir: params.candidate.packageDir ?? params.candidate.rootDir,
308+
manifest: params.manifest,
309+
packageManifest: params.candidate.packageManifest,
310+
})
311+
: params.manifest.channelConfigs;
297312
const channelConfigs = mergePackageChannelMetaIntoChannelConfigs({
298-
channelConfigs: params.manifest.channelConfigs,
313+
channelConfigs: manifestChannelConfigs,
299314
packageChannel: params.candidate.packageManifest?.channel,
300315
});
301316
const packageChannelCommands = normalizePackageChannelCommands(
@@ -542,14 +557,18 @@ export function loadPluginManifestRegistry(
542557
candidates?: PluginCandidate[];
543558
diagnostics?: PluginDiagnostic[];
544559
installRecords?: Record<string, PluginInstallRecord>;
560+
bundledChannelConfigCollector?: BundledChannelConfigCollector;
545561
} = {},
546562
): PluginManifestRegistry {
547563
const config = params.config ?? {};
548564
const normalized = normalizePluginsConfigWithResolver(config.plugins);
549565
const env = params.env ?? process.env;
550566
const cacheKey = buildCacheKey({ workspaceDir: params.workspaceDir, plugins: normalized, env });
551567
const cacheEnabled =
552-
params.cache !== false && !params.installRecords && shouldUseManifestCache(env);
568+
params.cache !== false &&
569+
!params.installRecords &&
570+
!params.bundledChannelConfigCollector &&
571+
shouldUseManifestCache(env);
553572
if (cacheEnabled) {
554573
const cached = registryCache.get(cacheKey);
555574
if (cached && cached.expiresAt > Date.now()) {
@@ -659,6 +678,9 @@ export function loadPluginManifestRegistry(
659678
manifestPath: manifestRes.manifestPath,
660679
schemaCacheKey,
661680
configSchema,
681+
...(params.bundledChannelConfigCollector
682+
? { bundledChannelConfigCollector: params.bundledChannelConfigCollector }
683+
: {}),
662684
});
663685

664686
const existing = seenIds.get(manifest.id);

src/plugins/plugin-registry-contributions.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
import { isInstalledPluginEnabled } from "./installed-plugin-index.js";
88
import { loadPluginManifestRegistryForInstalledIndex } from "./manifest-registry-installed.js";
99
import type {
10+
BundledChannelConfigCollector,
1011
PluginManifestContractListKey,
1112
PluginManifestRecord,
1213
PluginManifestRegistry,
@@ -25,6 +26,7 @@ export type PluginRegistryContributionOptions = LoadPluginRegistryParams & {
2526
export type LoadPluginRegistryManifestParams = LoadPluginRegistryParams & {
2627
includeDisabled?: boolean;
2728
pluginIds?: readonly string[];
29+
bundledChannelConfigCollector?: BundledChannelConfigCollector;
2830
};
2931

3032
export type PluginRegistryContributionKey =
@@ -201,6 +203,9 @@ export function loadPluginManifestRegistryForPluginRegistry(
201203
env: params.env,
202204
pluginIds: params.pluginIds,
203205
includeDisabled: params.includeDisabled,
206+
...(params.bundledChannelConfigCollector
207+
? { bundledChannelConfigCollector: params.bundledChannelConfigCollector }
208+
: {}),
204209
});
205210
}
206211

0 commit comments

Comments
 (0)