From 354ac4185e1b90d98ff2370e5713a5881a469cb2 Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Sun, 15 Mar 2026 17:50:08 +0000 Subject: [PATCH] Plugins: extract provider auth application flow --- .../auth-choice.apply.plugin-provider.ts | 217 +--------------- src/extension-host/provider-auth-flow.ts | 235 ++++++++++++++++++ 2 files changed, 245 insertions(+), 207 deletions(-) create mode 100644 src/extension-host/provider-auth-flow.ts diff --git a/src/commands/auth-choice.apply.plugin-provider.ts b/src/commands/auth-choice.apply.plugin-provider.ts index bd97928db91..e4b9b5b3dec 100644 --- a/src/commands/auth-choice.apply.plugin-provider.ts +++ b/src/commands/auth-choice.apply.plugin-provider.ts @@ -1,232 +1,35 @@ -import { resolveOpenClawAgentDir } from "../agents/agent-paths.js"; import { - resolveDefaultAgentId, - resolveAgentDir, - resolveAgentWorkspaceDir, -} from "../agents/agent-scope.js"; -import { upsertAuthProfile } from "../agents/auth-profiles.js"; -import { resolveDefaultAgentWorkspaceDir } from "../agents/workspace.js"; -import { enablePluginInConfig } from "../plugins/enable.js"; -import { - resolveProviderPluginChoice, - runProviderModelSelectedHook, -} from "../plugins/provider-wizard.js"; -import { resolvePluginProviders } from "../plugins/providers.js"; -import type { ProviderAuthMethod } from "../plugins/types.js"; + applyExtensionHostLoadedPluginProvider, + applyExtensionHostPluginProvider, + runExtensionHostProviderAuthMethod, + type ExtensionHostPluginProviderAuthChoiceOptions, +} from "../extension-host/provider-auth-flow.js"; import type { ApplyAuthChoiceParams, ApplyAuthChoiceResult } from "./auth-choice.apply.js"; -import { isRemoteEnvironment } from "./oauth-env.js"; -import { createVpsAwareOAuthHandlers } from "./oauth-flow.js"; -import { applyAuthProfileConfig } from "./onboard-auth.js"; -import { openUrl } from "./onboard-helpers.js"; -import { - applyDefaultModel, - mergeConfigPatch, - pickAuthMethod, - resolveProviderMatch, -} from "./provider-auth-helpers.js"; -export type PluginProviderAuthChoiceOptions = { - authChoice: string; - pluginId: string; - providerId: string; - methodId?: string; - label: string; -}; +export type PluginProviderAuthChoiceOptions = ExtensionHostPluginProviderAuthChoiceOptions; export async function runProviderPluginAuthMethod(params: { config: ApplyAuthChoiceParams["config"]; runtime: ApplyAuthChoiceParams["runtime"]; prompter: ApplyAuthChoiceParams["prompter"]; - method: ProviderAuthMethod; + method: Parameters[0]["method"]; agentDir?: string; agentId?: string; workspaceDir?: string; emitNotes?: boolean; }): Promise<{ config: ApplyAuthChoiceParams["config"]; defaultModel?: string }> { - const agentId = params.agentId ?? resolveDefaultAgentId(params.config); - const defaultAgentId = resolveDefaultAgentId(params.config); - const agentDir = - params.agentDir ?? - (agentId === defaultAgentId - ? resolveOpenClawAgentDir() - : resolveAgentDir(params.config, agentId)); - const workspaceDir = - params.workspaceDir ?? - resolveAgentWorkspaceDir(params.config, agentId) ?? - resolveDefaultAgentWorkspaceDir(); - - const isRemote = isRemoteEnvironment(); - const result = await params.method.run({ - config: params.config, - agentDir, - workspaceDir, - prompter: params.prompter, - runtime: params.runtime, - isRemote, - openUrl: async (url) => { - await openUrl(url); - }, - oauth: { - createVpsAwareHandlers: (opts) => createVpsAwareOAuthHandlers(opts), - }, - }); - - let nextConfig = params.config; - if (result.configPatch) { - nextConfig = mergeConfigPatch(nextConfig, result.configPatch); - } - - for (const profile of result.profiles) { - upsertAuthProfile({ - profileId: profile.profileId, - credential: profile.credential, - agentDir, - }); - - nextConfig = applyAuthProfileConfig(nextConfig, { - profileId: profile.profileId, - provider: profile.credential.provider, - mode: profile.credential.type === "token" ? "token" : profile.credential.type, - ...("email" in profile.credential && profile.credential.email - ? { email: profile.credential.email } - : {}), - }); - } - - if (params.emitNotes !== false && result.notes && result.notes.length > 0) { - await params.prompter.note(result.notes.join("\n"), "Provider notes"); - } - - return { - config: nextConfig, - defaultModel: result.defaultModel, - }; + return runExtensionHostProviderAuthMethod(params); } export async function applyAuthChoiceLoadedPluginProvider( params: ApplyAuthChoiceParams, ): Promise { - const agentId = params.agentId ?? resolveDefaultAgentId(params.config); - const workspaceDir = - resolveAgentWorkspaceDir(params.config, agentId) ?? resolveDefaultAgentWorkspaceDir(); - const providers = resolvePluginProviders({ config: params.config, workspaceDir }); - const resolved = resolveProviderPluginChoice({ - providers, - choice: params.authChoice, - }); - if (!resolved) { - return null; - } - - const applied = await runProviderPluginAuthMethod({ - config: params.config, - runtime: params.runtime, - prompter: params.prompter, - method: resolved.method, - agentDir: params.agentDir, - agentId: params.agentId, - workspaceDir, - }); - - let agentModelOverride: string | undefined; - if (applied.defaultModel) { - if (params.setDefaultModel) { - const nextConfig = applyDefaultModel(applied.config, applied.defaultModel); - await runProviderModelSelectedHook({ - config: nextConfig, - model: applied.defaultModel, - prompter: params.prompter, - agentDir: params.agentDir, - workspaceDir, - }); - await params.prompter.note( - `Default model set to ${applied.defaultModel}`, - "Model configured", - ); - return { config: nextConfig }; - } - agentModelOverride = applied.defaultModel; - } - - return { config: applied.config, agentModelOverride }; + return applyExtensionHostLoadedPluginProvider(params); } export async function applyAuthChoicePluginProvider( params: ApplyAuthChoiceParams, options: PluginProviderAuthChoiceOptions, ): Promise { - if (params.authChoice !== options.authChoice) { - return null; - } - - const enableResult = enablePluginInConfig(params.config, options.pluginId); - let nextConfig = enableResult.config; - if (!enableResult.enabled) { - await params.prompter.note( - `${options.label} plugin is disabled (${enableResult.reason ?? "blocked"}).`, - options.label, - ); - return { config: nextConfig }; - } - - const agentId = params.agentId ?? resolveDefaultAgentId(nextConfig); - const defaultAgentId = resolveDefaultAgentId(nextConfig); - const agentDir = - params.agentDir ?? - (agentId === defaultAgentId ? resolveOpenClawAgentDir() : resolveAgentDir(nextConfig, agentId)); - const workspaceDir = - resolveAgentWorkspaceDir(nextConfig, agentId) ?? resolveDefaultAgentWorkspaceDir(); - - const providers = resolvePluginProviders({ config: nextConfig, workspaceDir }); - const provider = resolveProviderMatch(providers, options.providerId); - if (!provider) { - await params.prompter.note( - `${options.label} auth plugin is not available. Enable it and re-run the wizard.`, - options.label, - ); - return { config: nextConfig }; - } - - const method = pickAuthMethod(provider, options.methodId) ?? provider.auth[0]; - if (!method) { - await params.prompter.note(`${options.label} auth method missing.`, options.label); - return { config: nextConfig }; - } - - const applied = await runProviderPluginAuthMethod({ - config: nextConfig, - runtime: params.runtime, - prompter: params.prompter, - method, - agentDir, - agentId, - workspaceDir, - }); - nextConfig = applied.config; - - let agentModelOverride: string | undefined; - if (applied.defaultModel) { - if (params.setDefaultModel) { - nextConfig = applyDefaultModel(nextConfig, applied.defaultModel); - await runProviderModelSelectedHook({ - config: nextConfig, - model: applied.defaultModel, - prompter: params.prompter, - agentDir, - workspaceDir, - }); - await params.prompter.note( - `Default model set to ${applied.defaultModel}`, - "Model configured", - ); - } else if (params.agentId) { - agentModelOverride = applied.defaultModel; - await params.prompter.note( - `Default model set to ${applied.defaultModel} for agent "${params.agentId}".`, - "Model configured", - ); - } - } - - return { config: nextConfig, agentModelOverride }; + return applyExtensionHostPluginProvider(params, options); } diff --git a/src/extension-host/provider-auth-flow.ts b/src/extension-host/provider-auth-flow.ts new file mode 100644 index 00000000000..606be794cbd --- /dev/null +++ b/src/extension-host/provider-auth-flow.ts @@ -0,0 +1,235 @@ +import { resolveOpenClawAgentDir } from "../agents/agent-paths.js"; +import { + resolveDefaultAgentId, + resolveAgentDir, + resolveAgentWorkspaceDir, +} from "../agents/agent-scope.js"; +import { upsertAuthProfile } from "../agents/auth-profiles.js"; +import { resolveDefaultAgentWorkspaceDir } from "../agents/workspace.js"; +import type { + ApplyAuthChoiceParams, + ApplyAuthChoiceResult, +} from "../commands/auth-choice.apply.js"; +import { isRemoteEnvironment } from "../commands/oauth-env.js"; +import { createVpsAwareOAuthHandlers } from "../commands/oauth-flow.js"; +import { applyAuthProfileConfig } from "../commands/onboard-auth.js"; +import { openUrl } from "../commands/onboard-helpers.js"; +import { enablePluginInConfig } from "../plugins/enable.js"; +import { + resolveProviderPluginChoice, + runProviderModelSelectedHook, +} from "../plugins/provider-wizard.js"; +import { resolvePluginProviders } from "../plugins/providers.js"; +import type { ProviderAuthMethod } from "../plugins/types.js"; +import { + applyExtensionHostDefaultModel, + mergeExtensionHostConfigPatch, + pickExtensionHostAuthMethod, + resolveExtensionHostProviderMatch, +} from "./provider-auth.js"; + +export type ExtensionHostPluginProviderAuthChoiceOptions = { + authChoice: string; + pluginId: string; + providerId: string; + methodId?: string; + label: string; +}; + +export async function runExtensionHostProviderAuthMethod(params: { + config: ApplyAuthChoiceParams["config"]; + runtime: ApplyAuthChoiceParams["runtime"]; + prompter: ApplyAuthChoiceParams["prompter"]; + method: ProviderAuthMethod; + agentDir?: string; + agentId?: string; + workspaceDir?: string; + emitNotes?: boolean; +}): Promise<{ config: ApplyAuthChoiceParams["config"]; defaultModel?: string }> { + const agentId = params.agentId ?? resolveDefaultAgentId(params.config); + const defaultAgentId = resolveDefaultAgentId(params.config); + const agentDir = + params.agentDir ?? + (agentId === defaultAgentId + ? resolveOpenClawAgentDir() + : resolveAgentDir(params.config, agentId)); + const workspaceDir = + params.workspaceDir ?? + resolveAgentWorkspaceDir(params.config, agentId) ?? + resolveDefaultAgentWorkspaceDir(); + + const isRemote = isRemoteEnvironment(); + const result = await params.method.run({ + config: params.config, + agentDir, + workspaceDir, + prompter: params.prompter, + runtime: params.runtime, + isRemote, + openUrl: async (url) => { + await openUrl(url); + }, + oauth: { + createVpsAwareHandlers: (opts) => createVpsAwareOAuthHandlers(opts), + }, + }); + + let nextConfig = params.config; + if (result.configPatch) { + nextConfig = mergeExtensionHostConfigPatch(nextConfig, result.configPatch); + } + + for (const profile of result.profiles) { + upsertAuthProfile({ + profileId: profile.profileId, + credential: profile.credential, + agentDir, + }); + + nextConfig = applyAuthProfileConfig(nextConfig, { + profileId: profile.profileId, + provider: profile.credential.provider, + mode: profile.credential.type === "token" ? "token" : profile.credential.type, + ...("email" in profile.credential && profile.credential.email + ? { email: profile.credential.email } + : {}), + }); + } + + if (params.emitNotes !== false && result.notes && result.notes.length > 0) { + await params.prompter.note(result.notes.join("\n"), "Provider notes"); + } + + return { + config: nextConfig, + defaultModel: result.defaultModel, + }; +} + +export async function applyExtensionHostLoadedPluginProvider( + params: ApplyAuthChoiceParams, +): Promise { + const agentId = params.agentId ?? resolveDefaultAgentId(params.config); + const workspaceDir = + resolveAgentWorkspaceDir(params.config, agentId) ?? resolveDefaultAgentWorkspaceDir(); + const providers = resolvePluginProviders({ config: params.config, workspaceDir }); + const resolved = resolveProviderPluginChoice({ + providers, + choice: params.authChoice, + }); + if (!resolved) { + return null; + } + + const applied = await runExtensionHostProviderAuthMethod({ + config: params.config, + runtime: params.runtime, + prompter: params.prompter, + method: resolved.method, + agentDir: params.agentDir, + agentId: params.agentId, + workspaceDir, + }); + + let agentModelOverride: string | undefined; + if (applied.defaultModel) { + if (params.setDefaultModel) { + const nextConfig = applyExtensionHostDefaultModel(applied.config, applied.defaultModel); + await runProviderModelSelectedHook({ + config: nextConfig, + model: applied.defaultModel, + prompter: params.prompter, + agentDir: params.agentDir, + workspaceDir, + }); + await params.prompter.note( + `Default model set to ${applied.defaultModel}`, + "Model configured", + ); + return { config: nextConfig }; + } + agentModelOverride = applied.defaultModel; + } + + return { config: applied.config, agentModelOverride }; +} + +export async function applyExtensionHostPluginProvider( + params: ApplyAuthChoiceParams, + options: ExtensionHostPluginProviderAuthChoiceOptions, +): Promise { + if (params.authChoice !== options.authChoice) { + return null; + } + + const enableResult = enablePluginInConfig(params.config, options.pluginId); + let nextConfig = enableResult.config; + if (!enableResult.enabled) { + await params.prompter.note( + `${options.label} plugin is disabled (${enableResult.reason ?? "blocked"}).`, + options.label, + ); + return { config: nextConfig }; + } + + const agentId = params.agentId ?? resolveDefaultAgentId(nextConfig); + const defaultAgentId = resolveDefaultAgentId(nextConfig); + const agentDir = + params.agentDir ?? + (agentId === defaultAgentId ? resolveOpenClawAgentDir() : resolveAgentDir(nextConfig, agentId)); + const workspaceDir = + resolveAgentWorkspaceDir(nextConfig, agentId) ?? resolveDefaultAgentWorkspaceDir(); + + const providers = resolvePluginProviders({ config: nextConfig, workspaceDir }); + const provider = resolveExtensionHostProviderMatch(providers, options.providerId); + if (!provider) { + await params.prompter.note( + `${options.label} auth plugin is not available. Enable it and re-run the wizard.`, + options.label, + ); + return { config: nextConfig }; + } + + const method = pickExtensionHostAuthMethod(provider, options.methodId) ?? provider.auth[0]; + if (!method) { + await params.prompter.note(`${options.label} auth method missing.`, options.label); + return { config: nextConfig }; + } + + const applied = await runExtensionHostProviderAuthMethod({ + config: nextConfig, + runtime: params.runtime, + prompter: params.prompter, + method, + agentDir, + agentId, + workspaceDir, + }); + nextConfig = applied.config; + + let agentModelOverride: string | undefined; + if (applied.defaultModel) { + if (params.setDefaultModel) { + nextConfig = applyExtensionHostDefaultModel(nextConfig, applied.defaultModel); + await runProviderModelSelectedHook({ + config: nextConfig, + model: applied.defaultModel, + prompter: params.prompter, + agentDir, + workspaceDir, + }); + await params.prompter.note( + `Default model set to ${applied.defaultModel}`, + "Model configured", + ); + } else if (params.agentId) { + agentModelOverride = applied.defaultModel; + await params.prompter.note( + `Default model set to ${applied.defaultModel} for agent "${params.agentId}".`, + "Model configured", + ); + } + } + + return { config: nextConfig, agentModelOverride }; +}