diff --git a/src/agent/oauth/providers.ts b/src/agent/oauth/providers.ts index 737c5439..53363cb7 100644 --- a/src/agent/oauth/providers.ts +++ b/src/agent/oauth/providers.ts @@ -269,3 +269,24 @@ export function getLoginInstructions(providerId: string): string { return "No login instructions available."; } + +/** + * Check if a provider uses OAuth authentication + */ +export function isOAuthProvider(providerId: string): boolean { + const info = PROVIDER_INFO[providerId]; + return info?.authMethod === "oauth"; +} + +/** + * Check if provider is available (has valid credentials) + */ +export function isProviderAvailable(providerId: string): boolean { + const info = PROVIDER_INFO[providerId]; + if (!info) return false; + + if (info.authMethod === "oauth") { + return isOAuthAvailable(providerId); + } + return isApiKeyConfigured(providerId); +} diff --git a/src/agent/runner.ts b/src/agent/runner.ts index 0fcc7543..e6478849 100644 --- a/src/agent/runner.ts +++ b/src/agent/runner.ts @@ -7,7 +7,7 @@ import { SessionManager } from "./session/session-manager.js"; import { ProfileManager } from "./profile/index.js"; import { SkillManager } from "./skills/index.js"; import { credentialManager, getCredentialsPath } from "./credentials.js"; -import { resolveProviderConfig } from "./oauth/providers.js"; +import { resolveProviderConfig, isOAuthProvider, getLoginInstructions } from "./oauth/providers.js"; import { checkContextWindow, DEFAULT_CONTEXT_TOKENS, @@ -79,10 +79,37 @@ export class Agent { const resolvedModel = resolveModelId(resolvedProvider, options.model); const apiKey = resolveApiKey(resolvedProvider, options.apiKey); + // Validate credentials before proceeding + if (!apiKey) { + if (isOAuthProvider(resolvedProvider)) { + // OAuth provider without valid credentials - show login instructions + const instructions = getLoginInstructions(resolvedProvider); + throw new Error( + `Provider "${resolvedProvider}" requires authentication.\n\n` + + `${instructions}\n\n` + + `After logging in, run: multica --provider ${resolvedProvider}`, + ); + } + // API Key provider without key - show configuration instructions + throw new Error( + `Provider "${resolvedProvider}" requires an API key.\n\n` + + `Add your API key to: ${getCredentialsPath()}\n\n` + + `Example:\n` + + `{\n` + + ` "llm": {\n` + + ` "provider": "${resolvedProvider}",\n` + + ` "providers": {\n` + + ` "${resolvedProvider}": {\n` + + ` "apiKey": "your-api-key-here"\n` + + ` }\n` + + ` }\n` + + ` }\n` + + `}`, + ); + } + this.agent = new PiAgentCore( - apiKey - ? { getApiKey: (_provider: string) => apiKey } - : {}, + { getApiKey: (_provider: string) => apiKey }, ); // Load Agent Profile (if profileId is specified)