import { resolveChannelDefaultAccountId } from "../channels/plugins/helpers.js"; import { getChannelPlugin, normalizeChannelId } from "../channels/plugins/index.js"; import { loadConfig, type OpenClawConfig } from "../config/config.js"; import { setVerbose } from "../globals.js"; import { resolveMessageChannelSelection } from "../infra/outbound/channel-selection.js"; import { defaultRuntime, type RuntimeEnv } from "../runtime.js"; type ChannelAuthOptions = { channel?: string; account?: string; verbose?: boolean; }; type ChannelPlugin = NonNullable>; type ChannelAuthMode = "login" | "logout"; async function resolveChannelPluginForMode( opts: ChannelAuthOptions, mode: ChannelAuthMode, cfg: OpenClawConfig, ): Promise<{ channelInput: string; channelId: string; plugin: ChannelPlugin }> { const explicitChannel = opts.channel?.trim(); const channelInput = explicitChannel ? explicitChannel : (await resolveMessageChannelSelection({ cfg })).channel; const channelId = normalizeChannelId(channelInput); if (!channelId) { throw new Error(`Unsupported channel: ${channelInput}`); } const plugin = getChannelPlugin(channelId); const supportsMode = mode === "login" ? Boolean(plugin?.auth?.login) : Boolean(plugin?.gateway?.logoutAccount); if (!supportsMode) { throw new Error(`Channel ${channelId} does not support ${mode}`); } return { channelInput, channelId, plugin: plugin as ChannelPlugin }; } function resolveAccountContext( plugin: ChannelPlugin, opts: ChannelAuthOptions, cfg: OpenClawConfig, ) { const accountId = opts.account?.trim() || resolveChannelDefaultAccountId({ plugin, cfg }); return { accountId }; } export async function runChannelLogin( opts: ChannelAuthOptions, runtime: RuntimeEnv = defaultRuntime, ) { const cfg = loadConfig(); const { channelInput, plugin } = await resolveChannelPluginForMode(opts, "login", cfg); const login = plugin.auth?.login; if (!login) { throw new Error(`Channel ${channelInput} does not support login`); } // Auth-only flow: do not mutate channel config here. setVerbose(Boolean(opts.verbose)); const { accountId } = resolveAccountContext(plugin, opts, cfg); await login({ cfg, accountId, runtime, verbose: Boolean(opts.verbose), channelInput, }); } export async function runChannelLogout( opts: ChannelAuthOptions, runtime: RuntimeEnv = defaultRuntime, ) { const cfg = loadConfig(); const { channelInput, plugin } = await resolveChannelPluginForMode(opts, "logout", cfg); const logoutAccount = plugin.gateway?.logoutAccount; if (!logoutAccount) { throw new Error(`Channel ${channelInput} does not support logout`); } // Auth-only flow: resolve account + clear session state only. const { accountId } = resolveAccountContext(plugin, opts, cfg); const account = plugin.config.resolveAccount(cfg, accountId); await logoutAccount({ cfg, accountId, account, runtime, }); }