/** * Auth — Configuration resolution * * Resolves ClawLink account configuration from the OpenClaw config object. * Supports two configuration layouts: * 1. Nested: cfg.channels.clawlink.accounts.{accountId}.* * 2. Fallback (F1 compat): cfg.channels.clawlink.agentId / .apiKey (top-level shorthand) * * See §0.1 of refactoring-plan.md. */ const DEFAULT_API_BASE = 'https://api.clawlink.club'; export interface ClawlinkAccountConfig { /** Resolved account ID */ accountId: string; /** Display name */ name: string; /** Whether the channel is enabled */ enabled: boolean; /** Whether required credentials (agentId + apiKey) are present */ configured: boolean; /** Agent identity on ClawLink network */ agentId: string; /** API key for auth verification */ apiKey: string; /** API base URL */ apiBase: string; /** Raw merged config object */ config: Record; } /** * Extract the ClawLink-specific config block from the full OpenClaw config. * Handles multiple config key locations that have drifted across versions. */ export function getClawlinkConfig(cfg: Record): Record { if (!cfg) return {}; const channels = cfg['channels'] as Record | undefined; const plugins = cfg['plugins'] as Record | undefined; const entries = plugins?.['entries'] as Record | undefined; return (channels?.['clawlink'] as Record) || (entries?.['openclaw-clawlink'] as Record)?.['config'] as Record || (entries?.['openclaw-plugin-clawlink'] as Record)?.['config'] as Record || {}; } /** * Resolve a specific account's configuration from OpenClaw config. * * @param cfg - Full OpenClaw configuration object * @param accountId - Target account ID, or 'default' to pick the first available */ export function resolveClawlinkAccount( cfg: Record, accountId?: string, ): ClawlinkAccountConfig { const clCfg = getClawlinkConfig(cfg); const accounts = (clCfg['accounts'] || {}) as Record>; // Resolve actual account ID let actualAccountId = accountId || 'default'; const keys = Object.keys(accounts); if (actualAccountId === 'default') { actualAccountId = keys.length > 0 ? keys[0]! : 'default'; } const targetAcc = accounts[actualAccountId] || {}; // F1 fallback: support top-level agentId/apiKey in the clawlink config block const agentId = (targetAcc['agentId'] || targetAcc['agent_id'] || clCfg['agentId'] || (actualAccountId !== 'default' ? actualAccountId : '')) as string; const apiKey = (targetAcc['api_key'] || targetAcc['apiKey'] || clCfg['apiKey'] || '') as string; const apiBase = (targetAcc['api_base'] || targetAcc['apiBase'] || clCfg['apiBase'] || DEFAULT_API_BASE) as string; return { accountId: actualAccountId, name: (clCfg['name'] || targetAcc['name'] || 'ClawLink') as string, enabled: clCfg['enabled'] !== false && targetAcc['enabled'] !== false, configured: Boolean(agentId && apiKey), agentId, apiKey, apiBase, config: { ...clCfg, ...targetAcc }, }; } /** * List available account IDs from the config. */ export function listAccountIds(cfg: Record): string[] { const clCfg = getClawlinkConfig(cfg); const accounts = clCfg['accounts'] as Record | undefined; return accounts ? Object.keys(accounts) : []; }