diff --git a/src/plugins/provider-auth-choice.runtime.ts b/src/plugins/provider-auth-choice.runtime.ts index 3660ac189ae..1faf872efbe 100644 --- a/src/plugins/provider-auth-choice.runtime.ts +++ b/src/plugins/provider-auth-choice.runtime.ts @@ -3,12 +3,14 @@ import { runProviderModelSelectedHook as runProviderModelSelectedHookImpl, } from "./provider-wizard.js"; import { resolvePluginProviders as resolvePluginProvidersImpl } from "./providers.runtime.js"; +import { resolvePluginSetupProvider as resolvePluginSetupProviderImpl } from "./setup-registry.js"; type ResolveProviderPluginChoice = typeof import("./provider-wizard.js").resolveProviderPluginChoice; type RunProviderModelSelectedHook = typeof import("./provider-wizard.js").runProviderModelSelectedHook; type ResolvePluginProviders = typeof import("./providers.runtime.js").resolvePluginProviders; +type ResolvePluginSetupProvider = typeof import("./setup-registry.js").resolvePluginSetupProvider; export function resolveProviderPluginChoice( ...args: Parameters @@ -27,3 +29,9 @@ export function resolvePluginProviders( ): ReturnType { return resolvePluginProvidersImpl(...args); } + +export function resolvePluginSetupProvider( + ...args: Parameters +): ReturnType { + return resolvePluginSetupProviderImpl(...args); +} diff --git a/src/plugins/provider-auth-choice.ts b/src/plugins/provider-auth-choice.ts index 75dcde3b831..30d3e7cbb8e 100644 --- a/src/plugins/provider-auth-choice.ts +++ b/src/plugins/provider-auth-choice.ts @@ -25,7 +25,7 @@ import { applyAuthProfileConfig } from "./provider-auth-helpers.js"; import { resolveProviderInstallCatalogEntry } from "./provider-install-catalog.js"; import { createVpsAwareOAuthHandlers } from "./provider-oauth-flow.js"; import { isRemoteEnvironment, openUrl } from "./setup-browser.js"; -import type { ProviderAuthMethod, ProviderAuthOptionBag } from "./types.js"; +import type { ProviderAuthMethod, ProviderAuthOptionBag, ProviderPlugin } from "./types.js"; export type ApplyProviderAuthChoiceParams = { authChoice: string; @@ -172,6 +172,10 @@ function resolveManifestAuthChoiceScope(params: { }); } +function withProviderPluginId(provider: ProviderPlugin, pluginId: string): ProviderPlugin { + return provider.pluginId === pluginId ? provider : { ...provider, pluginId }; +} + export const __testing = { resetDepsForTest(): void { providerAuthChoiceDeps = defaultProviderAuthChoiceDeps; @@ -274,8 +278,12 @@ export async function applyAuthChoiceLoadedPluginProvider( resolveAgentWorkspaceDir(params.config, agentId) ?? resolveDefaultAgentWorkspaceDir(); let nextConfig = params.config; let enabledConfig = params.config; - const { resolvePluginProviders, resolveProviderPluginChoice, runProviderModelSelectedHook } = - await loadPluginProviderRuntime(); + const { + resolvePluginProviders, + resolvePluginSetupProvider, + resolveProviderPluginChoice, + runProviderModelSelectedHook, + } = await loadPluginProviderRuntime(); const manifestAuthChoice = resolveManifestAuthChoiceScope({ authChoice: params.authChoice, config: nextConfig, @@ -301,22 +309,43 @@ export async function applyAuthChoiceLoadedPluginProvider( enabledConfig = enableResult.config; } - let providers = resolvePluginProviders({ - config: enabledConfig, - workspaceDir, - env: params.env, - mode: "setup", - ...(manifestAuthChoice - ? { - onlyPluginIds: [manifestAuthChoice.pluginId], - providerRefs: [manifestAuthChoice.providerId], - } - : {}), - }); + const resolveScopedRuntimeProviders = (config: OpenClawConfig): ProviderPlugin[] => + resolvePluginProviders({ + config, + workspaceDir, + env: params.env, + mode: "setup", + ...(manifestAuthChoice + ? { + onlyPluginIds: [manifestAuthChoice.pluginId], + providerRefs: [manifestAuthChoice.providerId], + } + : {}), + }); + + const setupProvider = manifestAuthChoice + ? resolvePluginSetupProvider({ + provider: manifestAuthChoice.providerId, + config: enabledConfig, + workspaceDir, + env: params.env, + pluginIds: [manifestAuthChoice.pluginId], + }) + : undefined; + let providers = setupProvider + ? [withProviderPluginId(setupProvider, manifestAuthChoice!.pluginId)] + : resolveScopedRuntimeProviders(enabledConfig); let resolved = resolveProviderPluginChoice({ providers, choice: params.authChoice, }); + if (!resolved && setupProvider) { + providers = resolveScopedRuntimeProviders(enabledConfig); + resolved = resolveProviderPluginChoice({ + providers, + choice: params.authChoice, + }); + } if (!resolved && installCatalogEntry) { const [{ ensureOnboardingPluginInstalled }, { clearPluginDiscoveryCache }] = await Promise.all([ import("../commands/onboarding-plugin-install.js"), @@ -338,18 +367,7 @@ export async function applyAuthChoiceLoadedPluginProvider( } nextConfig = installResult.cfg; clearPluginDiscoveryCache(); - providers = resolvePluginProviders({ - config: nextConfig, - workspaceDir, - env: params.env, - mode: "setup", - ...(manifestAuthChoice - ? { - onlyPluginIds: [manifestAuthChoice.pluginId], - providerRefs: [manifestAuthChoice.providerId], - } - : {}), - }); + providers = resolveScopedRuntimeProviders(nextConfig); resolved = resolveProviderPluginChoice({ providers, choice: params.authChoice, diff --git a/src/plugins/provider-wizard.ts b/src/plugins/provider-wizard.ts index 49fd178014a..76ac80a5826 100644 --- a/src/plugins/provider-wizard.ts +++ b/src/plugins/provider-wizard.ts @@ -7,6 +7,7 @@ import { } from "../shared/string-coerce.js"; import type { WizardPrompter } from "../wizard/prompts.js"; import { resolvePluginProviders } from "./providers.runtime.js"; +import { resolvePluginSetupProvider } from "./setup-registry.js"; import type { ProviderAuthMethod, ProviderPlugin, @@ -293,12 +294,19 @@ export async function runProviderModelSelectedHook(params: { return; } - const providers = resolveProviderWizardProviders({ + const setupProvider = resolvePluginSetupProvider({ + provider: selectedProviderId, config: params.config, workspaceDir: params.workspaceDir, env: params.env, }); - const provider = providers.find((entry) => normalizeProviderId(entry.id) === selectedProviderId); + const provider = + setupProvider ?? + resolveProviderWizardProviders({ + config: params.config, + workspaceDir: params.workspaceDir, + env: params.env, + }).find((entry) => normalizeProviderId(entry.id) === selectedProviderId); if (!provider?.onModelSelected) { return; } diff --git a/src/plugins/setup-registry.ts b/src/plugins/setup-registry.ts index d405e5621ae..ac9ed08ff32 100644 --- a/src/plugins/setup-registry.ts +++ b/src/plugins/setup-registry.ts @@ -153,6 +153,7 @@ function setCachedSetupValue(cache: Map, key: string, value: T): v } function buildSetupRegistryCacheKey(params: { + config?: OpenClawConfig; workspaceDir?: string; env?: NodeJS.ProcessEnv; pluginIds?: readonly string[]; @@ -160,18 +161,22 @@ function buildSetupRegistryCacheKey(params: { const { roots, loadPaths } = resolvePluginCacheInputs({ workspaceDir: params.workspaceDir, env: params.env, + loadPaths: params.config?.plugins?.load?.paths, }); return JSON.stringify({ roots, loadPaths, + hasConfig: Boolean(params.config), pluginIds: params.pluginIds ? [...new Set(params.pluginIds)].toSorted() : null, }); } function buildSetupProviderCacheKey(params: { provider: string; + config?: OpenClawConfig; workspaceDir?: string; env?: NodeJS.ProcessEnv; + pluginIds?: readonly string[]; }): string { return JSON.stringify({ provider: normalizeProviderId(params.provider), @@ -181,6 +186,7 @@ function buildSetupProviderCacheKey(params: { function buildSetupCliBackendCacheKey(params: { backend: string; + config?: OpenClawConfig; workspaceDir?: string; env?: NodeJS.ProcessEnv; }): string { @@ -493,12 +499,14 @@ function pushSetupDescriptorDriftDiagnostics(params: { } export function resolvePluginSetupRegistry(params?: { + config?: OpenClawConfig; workspaceDir?: string; env?: NodeJS.ProcessEnv; pluginIds?: readonly string[]; }): PluginSetupRegistry { const env = params?.env ?? process.env; const cacheKey = buildSetupRegistryCacheKey({ + config: params?.config, workspaceDir: params?.workspaceDir, env, pluginIds: params?.pluginIds, @@ -532,6 +540,7 @@ export function resolvePluginSetupRegistry(params?: { const cliBackendKeys = new Set(); const manifestRegistry = loadSetupManifestRegistry({ + config: params?.config, workspaceDir: params?.workspaceDir, env, pluginIds: params?.pluginIds, @@ -628,8 +637,10 @@ export function resolvePluginSetupRegistry(params?: { export function resolvePluginSetupProvider(params: { provider: string; + config?: OpenClawConfig; workspaceDir?: string; env?: NodeJS.ProcessEnv; + pluginIds?: readonly string[]; }): ProviderPlugin | undefined { const cacheKey = buildSetupProviderCacheKey(params); const cached = getCachedSetupValue(setupProviderCache, cacheKey); @@ -640,8 +651,10 @@ export function resolvePluginSetupProvider(params: { const env = params.env ?? process.env; const normalizedProvider = normalizeProviderId(params.provider); const manifestRegistry = loadSetupManifestRegistry({ + config: params.config, workspaceDir: params.workspaceDir, env, + pluginIds: params.pluginIds, }); const record = findUniqueSetupManifestOwner({ registry: manifestRegistry, @@ -697,6 +710,7 @@ export function resolvePluginSetupProvider(params: { export function resolvePluginSetupCliBackend(params: { backend: string; + config?: OpenClawConfig; workspaceDir?: string; env?: NodeJS.ProcessEnv; }): SetupCliBackendEntry | undefined { @@ -713,6 +727,7 @@ export function resolvePluginSetupCliBackend(params: { // plugin setup module. This avoids booting every setup-api just to find one // backend owner. const manifestRegistry = loadSetupManifestRegistry({ + config: params.config, workspaceDir: params.workspaceDir, env, }); @@ -786,6 +801,7 @@ export function runPluginSetupConfigMigrations(params: { } for (const entry of resolvePluginSetupRegistry({ + config: params.config, workspaceDir: params.workspaceDir, env: params.env, pluginIds, @@ -812,6 +828,7 @@ export function resolvePluginSetupAutoEnableReasons(params: { const seen = new Set(); for (const entry of resolvePluginSetupRegistry({ + config: params.config, workspaceDir: params.workspaceDir, env, pluginIds: params.pluginIds,