From b7340ec6a9ed9aecc51d1aa2f92aaf9349537804 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 25 Apr 2026 21:28:26 -0700 Subject: [PATCH] fix(plugins): scope metadata manifest reads --- src/plugins/setup-registry.test.ts | 20 +++++++++++++++++++ src/plugins/setup-registry.ts | 3 +++ ...web-provider-resolution-candidates.test.ts | 6 ++++++ src/plugins/web-provider-resolution-shared.ts | 8 +++++++- 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/plugins/setup-registry.test.ts b/src/plugins/setup-registry.test.ts index a332123705d..fe6a8e89ceb 100644 --- a/src/plugins/setup-registry.test.ts +++ b/src/plugins/setup-registry.test.ts @@ -207,6 +207,26 @@ describe("setup-registry getJiti", () => { ); }); + it("passes explicit plugin id scope into setup manifest reads", () => { + const pluginRoot = makeTempDir(); + fs.writeFileSync(path.join(pluginRoot, "setup-api.js"), "export default {};\n", "utf-8"); + mocks.loadPluginManifestRegistry.mockReturnValue({ + plugins: [{ id: "test-plugin", rootDir: pluginRoot }], + diagnostics: [], + }); + + resolvePluginSetupRegistry({ + pluginIds: ["test-plugin"], + env: {}, + }); + + expect(mocks.loadPluginManifestRegistry).toHaveBeenCalledWith( + expect.objectContaining({ + pluginIds: ["test-plugin"], + }), + ); + }); + it("skips setup-api loading when config has no relevant migration triggers", () => { const pluginRoot = makeTempDir(); fs.writeFileSync(path.join(pluginRoot, "setup-api.js"), "export default {};\n", "utf-8"); diff --git a/src/plugins/setup-registry.ts b/src/plugins/setup-registry.ts index 5158a4d19c3..d405e5621ae 100644 --- a/src/plugins/setup-registry.ts +++ b/src/plugins/setup-registry.ts @@ -380,12 +380,14 @@ function loadSetupManifestRegistry(params?: { config?: OpenClawConfig; workspaceDir?: string; env?: NodeJS.ProcessEnv; + pluginIds?: readonly string[]; }) { const env = params?.env ?? process.env; return loadPluginManifestRegistryForPluginRegistry({ config: params?.config, workspaceDir: params?.workspaceDir, env, + pluginIds: params?.pluginIds, includeDisabled: true, }); } @@ -532,6 +534,7 @@ export function resolvePluginSetupRegistry(params?: { const manifestRegistry = loadSetupManifestRegistry({ workspaceDir: params?.workspaceDir, env, + pluginIds: params?.pluginIds, }); for (const record of manifestRegistry.plugins) { diff --git a/src/plugins/web-provider-resolution-candidates.test.ts b/src/plugins/web-provider-resolution-candidates.test.ts index 334927b0c15..20609003ca8 100644 --- a/src/plugins/web-provider-resolution-candidates.test.ts +++ b/src/plugins/web-provider-resolution-candidates.test.ts @@ -62,6 +62,7 @@ describe("resolveManifestDeclaredWebProviderCandidatePluginIds", () => { onlyPluginIds: [], }), ).toEqual([]); + expect(mocks.loadPluginManifestRegistryForInstalledIndex).not.toHaveBeenCalled(); }); it("keeps runtime fallback for scoped plugins with no declared web candidates", () => { @@ -72,6 +73,11 @@ describe("resolveManifestDeclaredWebProviderCandidatePluginIds", () => { onlyPluginIds: ["missing-plugin"], }), ).toBeUndefined(); + expect(mocks.loadPluginManifestRegistryForInstalledIndex).toHaveBeenCalledWith( + expect.objectContaining({ + pluginIds: ["missing-plugin"], + }), + ); }); it("derives provider candidates from a single manifest-registry read", () => { diff --git a/src/plugins/web-provider-resolution-shared.ts b/src/plugins/web-provider-resolution-shared.ts index 93eee1232f9..aa2a60678a6 100644 --- a/src/plugins/web-provider-resolution-shared.ts +++ b/src/plugins/web-provider-resolution-shared.ts @@ -63,11 +63,13 @@ function loadInstalledWebProviderManifestRecords(params: { config?: PluginLoadOptions["config"]; workspaceDir?: string; env?: PluginLoadOptions["env"]; + pluginIds?: readonly string[]; }): readonly PluginManifestRecord[] { return loadPluginManifestRegistryForPluginRegistry({ config: params.config, workspaceDir: params.workspaceDir, env: params.env, + pluginIds: params.pluginIds, includeDisabled: true, }).plugins; } @@ -82,11 +84,15 @@ export function resolveManifestDeclaredWebProviderCandidatePluginIds(params: { origin?: PluginManifestRecord["origin"]; }): string[] | undefined { const scopedPluginIds = normalizePluginIdScope(params.onlyPluginIds); + if (scopedPluginIds?.length === 0) { + return []; + } const onlyPluginIdSet = createPluginIdScopeSet(scopedPluginIds); const ids = loadInstalledWebProviderManifestRecords({ config: params.config, workspaceDir: params.workspaceDir, env: params.env, + pluginIds: scopedPluginIds, }) .filter( (plugin) => @@ -99,7 +105,7 @@ export function resolveManifestDeclaredWebProviderCandidatePluginIds(params: { if (ids.length > 0) { return ids; } - return scopedPluginIds?.length === 0 ? [] : undefined; + return undefined; } function resolveBundledWebProviderCompatPluginIds(params: {