mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 04:20:43 +00:00
Plugins: extract provider auth application flow
This commit is contained in:
@@ -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<typeof runExtensionHostProviderAuthMethod>[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<ApplyAuthChoiceResult | null> {
|
||||
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<ApplyAuthChoiceResult | null> {
|
||||
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);
|
||||
}
|
||||
|
||||
235
src/extension-host/provider-auth-flow.ts
Normal file
235
src/extension-host/provider-auth-flow.ts
Normal file
@@ -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<ApplyAuthChoiceResult | null> {
|
||||
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<ApplyAuthChoiceResult | null> {
|
||||
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 };
|
||||
}
|
||||
Reference in New Issue
Block a user