UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

1,317 lines (1,313 loc) • 46.5 kB
/** * Provider Configuration Utility * Consolidated configuration helpers for all AI providers * Eliminates duplicate error messages and configuration logic * Enhanced with format validation and advanced error classification * Extended with Claude subscription OAuth support */ import { logger } from "./logger.js"; // Re-export subscription types for convenience /** * API key format validation patterns (extracted from advanced validation system) * Exported for use across the codebase to replace scattered regex patterns */ export const API_KEY_FORMATS = { openai: /^sk-[A-Za-z0-9]{48,}$/, anthropic: /^sk-ant-[A-Za-z0-9\-_]{95,}$/, "google-ai": /^AIza[A-Za-z0-9\-_]{35}$/, huggingface: /^hf_[A-Za-z0-9]{37}$/, mistral: /^[A-Za-z0-9]{32}$/, azure: /^[A-Za-z0-9]{32}$/, aws: /^[A-Z0-9]{20}$/, // Access Key ID format bedrock: /^[A-Z0-9]{20}$/, // AWS access key ID: 20 uppercase alphanumerics }; /** * OAuth token format validation patterns * These patterns are more flexible as OAuth tokens can vary by provider */ export const OAUTH_TOKEN_FORMATS = { // OAuth access tokens are typically JWT or opaque tokens // Claude OAuth access tokens: Bearer tokens with typical JWT structure or opaque format "anthropic-access": /^[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+$|^[A-Za-z0-9\-_]{32,}$/, // Refresh tokens are typically longer opaque strings "anthropic-refresh": /^[A-Za-z0-9\-_]{32,}$/, }; /** * API key length constants to replace scattered magic numbers */ export const API_KEY_LENGTHS = { OPENAI_MIN: 48, // OpenAI API keys minimum length ANTHROPIC_MIN: 95, // Anthropic API keys minimum length HUGGINGFACE_EXACT: 37, // HuggingFace tokens exact length AZURE_MIN: 32, // Azure OpenAI API keys minimum length MISTRAL_EXACT: 32, // Mistral API keys exact length AWS_ACCESS_KEY: 20, // AWS access key ID exact length GOOGLE_AI_EXACT: 39, // Google AI Studio keys exact length (with AIza prefix) }; /** * Project ID format validation (for Google Cloud) */ export const PROJECT_ID_FORMAT = { MIN_LENGTH: 6, // Minimum project ID length MAX_LENGTH: 30, // Maximum project ID length PATTERN: /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/, // Google Cloud project ID format }; /** * Validates API key format for a specific provider * @param providerKey Provider identifier (e.g., 'openai', 'anthropic') * @param apiKey The API key to validate * @returns True if format is valid */ export function validateApiKeyFormat(providerKey, apiKey) { const format = API_KEY_FORMATS[providerKey.toLowerCase()]; if (!format) { // No format validation available, assume valid if not empty return apiKey.length > 0; } return format.test(apiKey); } /** * Enhanced validation with format checking * @param config Provider configuration options * @param enableFormatValidation Whether to validate API key format * @returns Validation result with detailed information */ export function validateApiKeyEnhanced(config, enableFormatValidation = false) { // Check primary environment variable let apiKey = process.env[config.envVarName]; // Check fallback environment variables if provided if (!apiKey && config.fallbackEnvVars) { for (const fallbackVar of config.fallbackEnvVars) { apiKey = process.env[fallbackVar]; if (apiKey) { break; } } } if (!apiKey) { if (config.optional) { // Local providers — base URL defaulted; treat as valid with empty value. return { isValid: true, apiKey: "" }; } return { isValid: false, apiKey: "", errorType: "missing", error: createConfigErrorMessage(config), }; } // Optional format validation if (enableFormatValidation) { const providerKey = config.providerName.toLowerCase().replace(/\s/g, "-"); const formatValid = validateApiKeyFormat(providerKey, apiKey); if (!formatValid) { return { isValid: false, apiKey, formatValid: false, errorType: "format", error: `Invalid ${config.providerName} API key format. Please check your API key.`, }; } } return { isValid: true, apiKey, formatValid: enableFormatValidation ? true : undefined, }; } /** * Validates an API key for a provider and returns it (BACKWARD COMPATIBLE) * Throws detailed error message if validation fails * @param config Provider configuration options * @returns The validated API key */ export function validateApiKey(config) { // Check primary environment variable let apiKey = process.env[config.envVarName]; // Check fallback environment variables if provided if (!apiKey && config.fallbackEnvVars) { for (const fallbackVar of config.fallbackEnvVars) { apiKey = process.env[fallbackVar]; if (apiKey) { break; } } } if (!apiKey) { // Local providers (LM Studio, llama.cpp) treat envVarName as a base-URL // override, not a credential. Returning "" lets callers fall back to the // documented default URL without raising a configuration error. if (config.optional) { return ""; } throw new Error(createConfigErrorMessage(config)); } return apiKey; } /** * Creates a standardized configuration error message * @param config Provider configuration options * @returns Formatted error message with setup instructions */ function createConfigErrorMessage(config) { const envVarsList = config.fallbackEnvVars ? [config.envVarName, ...config.fallbackEnvVars].join(" or ") : config.envVarName; return `āŒ ${config.providerName} Provider Configuration Error Missing required environment variable: ${envVarsList} šŸ”§ Step 1: Get ${config.description} ${config.instructions.join("\n")} šŸ”§ Step 2: Set Environment Variable Add to your .env file: ${config.envVarName}=your_key_here šŸ”§ Step 3: Restart Application Restart your application to load the new environment variables.`; } /** * Gets a provider model with fallback to default * @param envVar Environment variable name for the model * @param defaultModel Default model to use if env var not set * @returns The model name to use */ export function getProviderModel(envVar, defaultModel) { return process.env[envVar] || defaultModel; } /** * Checks if provider credentials are available * @param envVars Array of environment variable names to check * @returns True if one of the credentials is available */ export function hasProviderCredentials(envVars) { return envVars.some((envVar) => !!process.env[envVar]); } // ============================================================================= // PROVIDER-SPECIFIC CONFIGURATION CREATORS // ============================================================================= /** * Creates Anthropic provider configuration * Supports both API key and OAuth authentication methods */ export function createAnthropicConfig() { const authMethod = getAnthropicAuthMethod(); const tier = getAnthropicSubscriptionTier(); // Base instructions for API key authentication const apiKeyInstructions = [ "šŸ”‘ Option 1: API Key Authentication (Recommended for developers)", "1. Visit: https://console.anthropic.com/", "2. Sign in or create an account", "3. Go to API Keys section", "4. Create a new API key", "5. Set ANTHROPIC_API_KEY in your .env file", ]; // OAuth instructions for Claude subscription users const oauthInstructions = [ "", "šŸ” Option 2: OAuth Authentication (For Claude Pro/Max subscribers)", "1. Set ANTHROPIC_AUTH_METHOD=oauth in your .env file", "2. Set ANTHROPIC_SUBSCRIPTION_TIER to your tier (free, pro, max)", "3. Run the OAuth flow to obtain tokens, or set pre-configured tokens:", " - ANTHROPIC_OAUTH_TOKEN=your_access_token", " - ANTHROPIC_OAUTH_REFRESH_TOKEN=your_refresh_token (optional)", "", "šŸ“‹ Available Subscription Tiers:", " - free: Free tier with limited usage", " - pro: Claude Pro ($20/month) - Extended usage limits", " - max: Claude Max - Highest usage limits", " - api: API-based access (pay-per-use)", ]; // Choose instructions based on current auth method const instructions = authMethod === "oauth" ? [...oauthInstructions.slice(1), "", ...apiKeyInstructions] : [...apiKeyInstructions, ...oauthInstructions]; return { providerName: "Anthropic", envVarName: authMethod === "oauth" ? "ANTHROPIC_OAUTH_TOKEN" : "ANTHROPIC_API_KEY", setupUrl: authMethod === "oauth" ? "https://claude.ai/settings" : "https://console.anthropic.com/", description: authMethod === "oauth" ? `Anthropic OAuth Token (${tier} tier)` : "Anthropic API Key", instructions, fallbackEnvVars: authMethod === "oauth" ? ["ANTHROPIC_API_KEY"] // Fall back to API key if OAuth token not present : [ "ANTHROPIC_OAUTH_TOKEN", "CLAUDE_OAUTH_TOKEN", "ANTHROPIC_OAUTH_ACCESS_TOKEN", ], // Fall back to OAuth if API key not present }; } /** * Creates OpenAI provider configuration */ export function createOpenAIConfig() { return { providerName: "OPENAI", envVarName: "OPENAI_API_KEY", setupUrl: "https://platform.openai.com/api-keys", description: "Credentials", instructions: [ "1. Visit: https://platform.openai.com/api-keys", "2. Create new API key", "3. Copy the key", ], }; } /** * Creates HuggingFace provider configuration */ export function createHuggingFaceConfig() { return { providerName: "HuggingFace", envVarName: "HUGGINGFACE_API_KEY", setupUrl: "https://huggingface.co/settings/tokens", description: "Credentials", instructions: [ "1. Visit: https://huggingface.co/settings/tokens", "2. Create new API token", "3. Copy the token", ], fallbackEnvVars: ["HF_TOKEN"], }; } /** * Creates Mistral provider configuration */ export function createMistralConfig() { return { providerName: "Mistral", envVarName: "MISTRAL_API_KEY", setupUrl: "https://console.mistral.ai/", description: "API key", instructions: [ "1. Visit: https://console.mistral.ai/", "2. Create or sign in to your account", "3. Generate a new API key", ], }; } /** * Creates AWS Access Key configuration for Bedrock */ export function createAWSAccessKeyConfig() { return { providerName: "AWS Bedrock", envVarName: "AWS_ACCESS_KEY_ID", setupUrl: "https://console.aws.amazon.com/iam/", description: "AWS Credentials", instructions: [ "1. Visit: https://console.aws.amazon.com/iam/", "2. Create IAM user with Bedrock permissions", "3. Generate access key", ], }; } /** * Creates AWS Secret Key configuration for Bedrock */ export function createAWSSecretConfig() { return { providerName: "AWS Bedrock", envVarName: "AWS_SECRET_ACCESS_KEY", setupUrl: "https://console.aws.amazon.com/iam/", description: "AWS Credentials", instructions: [ "1. Visit: https://console.aws.amazon.com/iam/", "2. Create IAM user with Bedrock permissions", "3. Generate access key", ], }; } /** * Creates Azure OpenAI API Key configuration */ export function createAzureAPIKeyConfig() { return { providerName: "Azure OpenAI", envVarName: "AZURE_OPENAI_API_KEY", setupUrl: "https://portal.azure.com/", description: "Azure OpenAI API Key", instructions: [ "1. Visit: https://portal.azure.com/", "2. Create or access Azure OpenAI resource", "3. Get API key from Keys and Endpoint section", ], }; } /** * Creates Azure OpenAI Endpoint configuration */ export function createAzureEndpointConfig() { return { providerName: "Azure OpenAI", envVarName: "AZURE_OPENAI_ENDPOINT", setupUrl: "https://portal.azure.com/", description: "Azure OpenAI Endpoint", instructions: [ "1. Visit: https://portal.azure.com/", "2. Access your Azure OpenAI resource", "3. Copy endpoint URL from Keys and Endpoint section", ], }; } /** * Creates OpenAI Compatible provider configuration */ export function createOpenAICompatibleConfig() { return { providerName: "OpenAI Compatible", envVarName: "OPENAI_COMPATIBLE_API_KEY", setupUrl: "https://openrouter.ai/", description: "OpenAI-compatible API credentials", instructions: [ "1. Set OPENAI_COMPATIBLE_BASE_URL to your endpoint (e.g., https://api.openrouter.ai/api/v1)", "2. Get API key from your OpenAI-compatible service:", " • OpenRouter: https://openrouter.ai/keys", " • vLLM: Use a random value for local deployments", " • LiteLLM: Check your LiteLLM server configuration", "3. Set OPENAI_COMPATIBLE_API_KEY to your API key", "4. Optionally set OPENAI_COMPATIBLE_MODEL (will auto-discover if not set)", ], }; } /** * Creates DeepSeek provider configuration */ export function createDeepSeekConfig() { return { providerName: "DeepSeek", envVarName: "DEEPSEEK_API_KEY", setupUrl: "https://platform.deepseek.com/api_keys", description: "API key", instructions: [ "1. Visit: https://platform.deepseek.com/api_keys", "2. Create or sign in to your DeepSeek account", "3. Generate a new API key", "4. Set DEEPSEEK_API_KEY in your .env file", ], }; } /** * Creates NVIDIA NIM provider configuration */ export function createNvidiaNimConfig() { return { providerName: "NVIDIA NIM", envVarName: "NVIDIA_NIM_API_KEY", setupUrl: "https://build.nvidia.com/settings/api-keys", description: "API key", instructions: [ "1. Visit: https://build.nvidia.com/", "2. Sign in with your NVIDIA developer account", "3. Open Settings → API Keys", "4. Generate a new API key (Bearer token)", "5. Set NVIDIA_NIM_API_KEY in your .env file", ], }; } /** * Creates LM Studio provider configuration (local server) */ export function createLmStudioConfig() { return { providerName: "LM Studio", envVarName: "LM_STUDIO_BASE_URL", setupUrl: "https://lmstudio.ai/", description: "LM Studio server URL", instructions: [ "1. Install LM Studio: https://lmstudio.ai/", "2. Open LM Studio and download a model (e.g. Llama 3.2 3B Instruct)", '3. Click "Local Server" → Start Server', "4. Default URL is http://localhost:1234/v1 (override via LM_STUDIO_BASE_URL)", ], // Base URL is optional — defaults to http://localhost:1234/v1 if unset. optional: true, }; } /** * Creates llama.cpp provider configuration (local server) */ export function createLlamaCppConfig() { return { providerName: "llama.cpp", envVarName: "LLAMACPP_BASE_URL", setupUrl: "https://github.com/ggerganov/llama.cpp", description: "llama.cpp server URL", instructions: [ "1. Build llama.cpp: https://github.com/ggerganov/llama.cpp#build", "2. Run: ./llama-server -m model.gguf --port 8080", "3. Default URL is http://localhost:8080/v1 (override via LLAMACPP_BASE_URL)", ], // Base URL is optional — defaults to http://localhost:8080/v1 if unset. optional: true, }; } /** * Creates xAI Grok provider configuration. */ export function createXaiConfig() { return { providerName: "xAI", envVarName: "XAI_API_KEY", setupUrl: "https://console.x.ai/", description: "API key", instructions: [ "1. Visit: https://console.x.ai/", "2. Sign in with your xAI account", "3. Create an API key", "4. Set XAI_API_KEY in your .env file", ], }; } /** * Creates Groq provider configuration. */ export function createGroqConfig() { return { providerName: "Groq", envVarName: "GROQ_API_KEY", setupUrl: "https://console.groq.com/keys", description: "API key", instructions: [ "1. Visit: https://console.groq.com/keys", "2. Sign in to your Groq account", "3. Create a new API key", "4. Set GROQ_API_KEY in your .env file", ], }; } /** * Creates Cohere provider configuration. */ export function createCohereConfig() { return { providerName: "Cohere", envVarName: "COHERE_API_KEY", setupUrl: "https://dashboard.cohere.com/api-keys", description: "API key", instructions: [ "1. Visit: https://dashboard.cohere.com/api-keys", "2. Sign in to your Cohere account", "3. Create a new API key", "4. Set COHERE_API_KEY in your .env file", ], }; } /** * Creates Replicate provider configuration. */ export function createReplicateConfig() { return { providerName: "Replicate", envVarName: "REPLICATE_API_TOKEN", setupUrl: "https://replicate.com/account/api-tokens", description: "API token", instructions: [ "1. Visit: https://replicate.com/account/api-tokens", "2. Sign in to your Replicate account", "3. Create a new API token", "4. Set REPLICATE_API_TOKEN in your .env file", ], }; } /** * Creates Together AI provider configuration. */ export function createTogetherAIConfig() { return { providerName: "Together AI", envVarName: "TOGETHER_API_KEY", setupUrl: "https://api.together.xyz/settings/api-keys", description: "API key", instructions: [ "1. Visit: https://api.together.xyz/settings/api-keys", "2. Sign in to your Together AI account", "3. Create a new API key", "4. Set TOGETHER_API_KEY in your .env file", ], }; } /** * Creates Fireworks AI provider configuration. */ export function createFireworksConfig() { return { providerName: "Fireworks AI", envVarName: "FIREWORKS_API_KEY", setupUrl: "https://fireworks.ai/account/api-keys", description: "API key", instructions: [ "1. Visit: https://fireworks.ai/account/api-keys", "2. Sign in to your Fireworks AI account", "3. Create a new API key", "4. Set FIREWORKS_API_KEY in your .env file", ], }; } /** * Creates Perplexity provider configuration. */ export function createPerplexityConfig() { return { providerName: "Perplexity", envVarName: "PERPLEXITY_API_KEY", setupUrl: "https://www.perplexity.ai/settings/api", description: "API key", instructions: [ "1. Visit: https://www.perplexity.ai/settings/api", "2. Sign in to your Perplexity account", "3. Create a new API key (Sonar tier)", "4. Set PERPLEXITY_API_KEY in your .env file", ], }; } /** * Creates Voyage AI provider configuration (embedding-only). */ export function createVoyageConfig() { return { providerName: "Voyage AI", envVarName: "VOYAGE_API_KEY", setupUrl: "https://dash.voyageai.com/api-keys", description: "API key", instructions: [ "1. Visit: https://dash.voyageai.com/api-keys", "2. Sign in to your Voyage AI account", "3. Create a new API key", "4. Set VOYAGE_API_KEY in your .env file", ], }; } /** * Creates Jina AI provider configuration (embeddings + reranking). */ export function createJinaConfig() { return { providerName: "Jina AI", envVarName: "JINA_API_KEY", setupUrl: "https://jina.ai/?sui=apikey", description: "API key", instructions: [ "1. Visit: https://jina.ai/?sui=apikey", "2. Sign in / create account", "3. Copy your API key", "4. Set JINA_API_KEY in your .env file", ], }; } /** * Creates Stability AI provider configuration (image generation). */ export function createStabilityConfig() { return { providerName: "Stability AI", envVarName: "STABILITY_API_KEY", setupUrl: "https://platform.stability.ai/account/keys", description: "API key", instructions: [ "1. Visit: https://platform.stability.ai/account/keys", "2. Sign in to your Stability AI account", "3. Create a new API key", "4. Set STABILITY_API_KEY in your .env file", ], }; } /** * Creates Ideogram provider configuration (image generation). */ export function createIdeogramConfig() { return { providerName: "Ideogram", envVarName: "IDEOGRAM_API_KEY", setupUrl: "https://developer.ideogram.ai/", description: "API key", instructions: [ "1. Visit: https://developer.ideogram.ai/", "2. Sign in / create account", "3. Generate a new API key", "4. Set IDEOGRAM_API_KEY in your .env file", ], }; } /** * Creates Recraft provider configuration (image generation, vector focus). */ export function createRecraftConfig() { return { providerName: "Recraft", envVarName: "RECRAFT_API_KEY", setupUrl: "https://www.recraft.ai/api", description: "API token", instructions: [ "1. Visit: https://www.recraft.ai/api", "2. Sign in / create account", "3. Create an API token", "4. Set RECRAFT_API_KEY in your .env file", ], }; } /** * Creates Cloudflare Workers AI provider configuration. * * Cloudflare requires both `CLOUDFLARE_API_KEY` (workers-AI-scoped token) * AND `CLOUDFLARE_ACCOUNT_ID` since the endpoint is per-account. */ export function createCloudflareConfig() { return { providerName: "Cloudflare Workers AI", envVarName: "CLOUDFLARE_API_KEY", setupUrl: "https://dash.cloudflare.com/profile/api-tokens", description: "API token (Workers AI Read+Write scope)", instructions: [ "1. Visit: https://dash.cloudflare.com/profile/api-tokens", "2. Create a token with 'Workers AI: Read + Write' scope", "3. Set CLOUDFLARE_API_KEY in your .env file", "4. Also set CLOUDFLARE_ACCOUNT_ID (find it in the dashboard URL or under 'Account ID')", ], }; } /** * Creates Google Vertex Project ID configuration */ export function createVertexProjectConfig() { return { providerName: "Google Vertex AI", envVarName: "GOOGLE_CLOUD_PROJECT_ID", setupUrl: "https://console.cloud.google.com/", description: "Google Cloud Credentials", instructions: [ "1. Visit: https://console.cloud.google.com/", "2. Create or select a project", "3. Enable Vertex AI API", "4. Set up authentication", ], fallbackEnvVars: [ "VERTEX_PROJECT_ID", "GOOGLE_VERTEX_PROJECT", "GOOGLE_CLOUD_PROJECT", ], }; } /** * Creates Google Cloud Authentication configuration */ export function createGoogleAuthConfig() { return { providerName: "Google Vertex AI", envVarName: "GOOGLE_APPLICATION_CREDENTIALS", setupUrl: "https://console.cloud.google.com/", description: "Google Cloud authentication", instructions: [ "šŸ”§ Option 1: Service Account Key File", "GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json", "", "šŸ”§ Option 2: Service Account Key (Base64)", "GOOGLE_SERVICE_ACCOUNT_KEY=base64_encoded_key", "", "šŸ”§ Option 3: Individual Credentials", "GOOGLE_AUTH_CLIENT_EMAIL=your-service-account@project.iam.gserviceaccount.com", "GOOGLE_AUTH_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----...", ], fallbackEnvVars: ["GOOGLE_SERVICE_ACCOUNT_KEY"], }; } /** * Creates Anthropic Base Provider configuration */ export function createAnthropicBaseConfig() { return { providerName: "ANTHROPIC", envVarName: "ANTHROPIC_API_KEY", setupUrl: "https://console.anthropic.com/", description: "Credentials", instructions: [ "Get your API key from https://console.anthropic.com/", "", "šŸ’” Step 2: Add to your .env file (or export in CLI):", ], }; } // ============================================================================= // HELPER FUNCTIONS FOR SPECIFIC PROVIDER NEEDS // ============================================================================= /** * Gets AWS Region with default fallback * Supports both AWS_REGION and AWS_DEFAULT_REGION for broader compatibility */ export function getAWSRegion() { return (process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || "us-east-1"); } /** * Gets AWS Session Token if available */ export function getAWSSessionToken() { return process.env.AWS_SESSION_TOKEN; } /** * Checks if HuggingFace credentials are available */ export function hasHuggingFaceCredentials() { return hasProviderCredentials(["HUGGINGFACE_API_KEY", "HF_TOKEN"]); } // ============================================================================= // ANTHROPIC/CLAUDE SUBSCRIPTION AUTH HELPERS // ============================================================================= /** * Gets the configured Anthropic authentication method * Defaults to "api_key" for backward compatibility * @returns The configured authentication method */ export function getAnthropicAuthMethod() { const method = process.env.ANTHROPIC_AUTH_METHOD?.toLowerCase(); if (method === "oauth") { return "oauth"; } return "api_key"; } /** * Gets the configured Claude subscription tier * Defaults to "api" for backward compatibility (API key users) * @returns The configured subscription tier */ export function getAnthropicSubscriptionTier() { const tier = process.env.ANTHROPIC_SUBSCRIPTION_TIER?.toLowerCase(); switch (tier) { case "free": return "free"; case "pro": return "pro"; case "max": return "max"; case "max_5": return "max_5"; case "max_20": return "max_20"; case "api": default: return "api"; } } /** * Validates OAuth access token format * @param token The token to validate * @returns True if the token format is valid */ export function validateOAuthAccessToken(token) { if (!token || token.length < 32) { return false; } const format = OAUTH_TOKEN_FORMATS["anthropic-access"]; return format.test(token); } /** * Validates OAuth refresh token format * @param token The token to validate * @returns True if the token format is valid */ export function validateOAuthRefreshToken(token) { if (!token || token.length < 32) { return false; } const format = OAUTH_TOKEN_FORMATS["anthropic-refresh"]; return format.test(token); } /** * Detects the best available authentication method for Anthropic * Checks environment variables and returns the most appropriate auth configuration * @returns Complete authentication configuration */ export function detectAnthropicAuth() { const explicitMethod = process.env.ANTHROPIC_AUTH_METHOD?.toLowerCase(); const tier = getAnthropicSubscriptionTier(); // Check for OAuth tokens — canonical + fallbacks for backward compatibility const accessToken = process.env.ANTHROPIC_OAUTH_TOKEN ?? process.env.CLAUDE_OAUTH_TOKEN ?? process.env.ANTHROPIC_OAUTH_ACCESS_TOKEN; const refreshToken = process.env.ANTHROPIC_OAUTH_REFRESH_TOKEN; // Check for API key const apiKey = process.env.ANTHROPIC_API_KEY; // If explicit method is set, use it if (explicitMethod === "oauth") { if (accessToken) { const isValidAccessToken = validateOAuthAccessToken(accessToken); const isValidRefreshToken = refreshToken ? validateOAuthRefreshToken(refreshToken) : true; if (!isValidAccessToken) { return { method: "oauth", tier, accessToken, refreshToken, isConfigured: false, error: "Invalid OAuth access token format. Token should be a valid JWT or opaque token.", }; } if (refreshToken && !isValidRefreshToken) { return { method: "oauth", tier, accessToken, refreshToken, isConfigured: false, error: "Invalid OAuth refresh token format. Token should be at least 32 characters.", }; } return { method: "oauth", tier, accessToken, refreshToken, isConfigured: true, }; } // OAuth method specified but no token return { method: "oauth", tier, isConfigured: false, error: "OAuth authentication method specified but ANTHROPIC_OAUTH_TOKEN not set.", }; } // If explicit method is api_key or not set if (apiKey) { const isValidApiKey = validateApiKeyFormat("anthropic", apiKey); if (!isValidApiKey) { // Still return as configured but note the format issue return { method: "api_key", tier: "api", // API key users are always on "api" tier apiKey, isConfigured: true, error: "API key format may be invalid. Expected format: sk-ant-...", }; } return { method: "api_key", tier: "api", apiKey, isConfigured: true, }; } // Check if OAuth tokens are available without explicit method set if (accessToken) { const isValidAccessToken = validateOAuthAccessToken(accessToken); return { method: "oauth", tier, accessToken, refreshToken, isConfigured: isValidAccessToken, error: isValidAccessToken ? undefined : "Invalid OAuth access token format.", }; } // No authentication configured return { method: "api_key", tier: "api", isConfigured: false, error: "No Anthropic authentication configured. Set ANTHROPIC_API_KEY or configure OAuth.", }; } /** * Checks if Anthropic credentials are available (either API key or OAuth) * @returns True if any valid authentication is configured */ export function hasAnthropicCredentials() { const auth = detectAnthropicAuth(); return auth.isConfigured; } /** * Gets the authentication token or key for Anthropic API calls * Returns the appropriate credential based on configured auth method * @returns The API key or OAuth access token, or undefined if not configured */ export function getAnthropicCredential() { const auth = detectAnthropicAuth(); if (!auth.isConfigured) { return undefined; } return auth.method === "oauth" ? auth.accessToken : auth.apiKey; } /** * Checks if OAuth refresh is needed based on token state * This is a placeholder for actual token expiration checking * @returns True if refresh is needed */ export function needsOAuthRefresh() { const auth = detectAnthropicAuth(); if (auth.method !== "oauth" || !auth.isConfigured) { return false; } // In a real implementation, you would check token expiration // For now, we just check if a refresh token is available // The actual refresh logic would be in the OAuth client return !!auth.refreshToken; } /** * Gets subscription tier limits for informational purposes * These are approximate limits and may change * @param tier The subscription tier * @returns Object with tier limit information */ export function getSubscriptionTierLimits(tier) { switch (tier) { case "free": return { messagesPerDay: 10, contextWindow: 100000, priorityAccess: false, description: "Free tier with limited daily messages", }; case "pro": return { messagesPerDay: 100, contextWindow: 200000, priorityAccess: true, description: "Claude Pro subscription with extended limits", }; case "max": return { messagesPerDay: "unlimited", contextWindow: 200000, priorityAccess: true, description: "Claude Max subscription with highest limits", }; case "max_5": return { messagesPerDay: "unlimited", contextWindow: 200000, priorityAccess: true, description: "Claude Max 5x usage tier with priority processing", }; case "max_20": return { messagesPerDay: "unlimited", contextWindow: 200000, priorityAccess: true, description: "Claude Max 20x usage tier with maximum capacity", }; case "api": default: return { messagesPerDay: "unlimited", contextWindow: 200000, priorityAccess: true, description: "API access with pay-per-use billing", }; } } // ============================================================================= // ENVIRONMENT VARIABLE CONSTANTS // ============================================================================= /** * Environment variables for Anthropic/Claude subscription configuration * These control authentication method, subscription tier, and feature flags */ export const ANTHROPIC_ENV_VARS = { /** Authentication method: "api_key" or "oauth" */ AUTH_METHOD: "ANTHROPIC_AUTH_METHOD", /** Subscription tier: "free", "pro", "max", "max_5", "max_20", or "api" */ SUBSCRIPTION_TIER: "ANTHROPIC_SUBSCRIPTION_TIER", /** Enable beta features: "true" or "false" */ ENABLE_BETA_FEATURES: "ANTHROPIC_ENABLE_BETA_FEATURES", /** API key for api_key authentication */ API_KEY: "ANTHROPIC_API_KEY", /** OAuth access token for oauth authentication (canonical, with BC fallbacks) */ OAUTH_ACCESS_TOKEN: "ANTHROPIC_OAUTH_TOKEN", /** OAuth refresh token for oauth authentication */ OAUTH_REFRESH_TOKEN: "ANTHROPIC_OAUTH_REFRESH_TOKEN", /** OAuth token expiry timestamp (Unix epoch in seconds) */ OAUTH_TOKEN_EXPIRY: "ANTHROPIC_OAUTH_TOKEN_EXPIRY", }; /** * Valid subscription tier values for validation */ export const VALID_SUBSCRIPTION_TIERS = [ "free", "pro", "max", "max_5", "max_20", "api", ]; /** * Valid authentication method values for validation */ export const VALID_AUTH_METHODS = [ "api_key", "oauth", ]; // ============================================================================= // SUBSCRIPTION TIER VALIDATION // ============================================================================= /** * Validates a subscription tier value * @param tier The tier value to validate * @returns True if the tier is valid, false otherwise */ export function isValidSubscriptionTier(tier) { if (!tier) { return false; } return VALID_SUBSCRIPTION_TIERS.includes(tier); } /** * Validates an authentication method value * @param method The method value to validate * @returns True if the method is valid, false otherwise */ export function isValidAuthMethod(method) { if (!method) { return false; } return VALID_AUTH_METHODS.includes(method); } /** * Validates subscription tier and returns a detailed result * @param tier The tier value to validate * @returns Validation result with error details if invalid */ export function validateSubscriptionTier(tier) { if (!tier) { return { isValid: false, error: "Subscription tier is required but not provided.", }; } const normalizedTier = tier.toLowerCase().trim(); if (isValidSubscriptionTier(normalizedTier)) { return { isValid: true, tier: normalizedTier, }; } return { isValid: false, error: `Invalid subscription tier "${tier}". Valid values are: ${VALID_SUBSCRIPTION_TIERS.join(", ")}`, }; } // ============================================================================= // ANTHROPIC AUTH CONFIG FUNCTIONS // ============================================================================= /** * Gets the complete Anthropic authentication configuration * Detects auth method from environment or config and loads appropriate credentials * * @returns Complete AnthropicAuthConfig with method, credentials, and tier */ export function getAnthropicAuthConfig() { const method = getAnthropicAuthMethod(); const tier = detectSubscriptionTier(); if (method === "oauth") { const accessToken = process.env[ANTHROPIC_ENV_VARS.OAUTH_ACCESS_TOKEN]; const refreshToken = process.env[ANTHROPIC_ENV_VARS.OAUTH_REFRESH_TOKEN]; const tokenExpiryStr = process.env[ANTHROPIC_ENV_VARS.OAUTH_TOKEN_EXPIRY]; // Parse token expiry if provided let expiresAt; if (tokenExpiryStr) { const parsed = parseInt(tokenExpiryStr, 10); if (!isNaN(parsed)) { expiresAt = parsed; } } // Build OAuth token object const oauthToken = accessToken ? { accessToken, refreshToken, expiresAt, tokenType: "Bearer", } : undefined; return { method: "oauth", oauthToken, accessToken, // Legacy field for backward compatibility refreshToken, // Legacy field for backward compatibility tokenExpiry: expiresAt ? expiresAt * 1000 : undefined, // Convert to milliseconds subscriptionTier: tier, autoRefresh: !!refreshToken, }; } // API key authentication const apiKey = process.env[ANTHROPIC_ENV_VARS.API_KEY]; return { method: "api_key", apiKey, subscriptionTier: tier, autoRefresh: false, }; } /** * Detects the subscription tier from environment variables or config * Checks environment variable first, then config, defaults to "api" if using API key * * @returns The detected subscription tier */ export function detectSubscriptionTier() { // 1. Check environment variable first (highest priority) const envTier = process.env[ANTHROPIC_ENV_VARS.SUBSCRIPTION_TIER]; if (envTier) { const validation = validateSubscriptionTier(envTier); if (validation.isValid && validation.tier) { return validation.tier; } logger.warn("Invalid ANTHROPIC_SUBSCRIPTION_TIER value", { value: envTier, validValues: VALID_SUBSCRIPTION_TIERS, fallback: "Defaulting based on auth method", }); } // 2. Check config file (could be extended to read from config file) // For now, we check if there's a tier set via other means // This is a placeholder for config file integration // const configTier = loadConfigTier(); // Future: implement config file loading // 3. Default based on authentication method const authMethod = getAnthropicAuthMethod(); if (authMethod === "oauth") { // OAuth users are typically subscription users, default to "pro" // unless they've explicitly configured otherwise logger.debug("[detectSubscriptionTier] OAuth auth detected, defaulting to pro tier"); return "pro"; } // 4. API key users default to "api" tier logger.debug("[detectSubscriptionTier] API key auth, defaulting to api tier"); return "api"; } /** * Determines whether beta features should be enabled * Checks environment/config and defaults based on authentication method * * @returns True if beta features should be enabled */ export function shouldEnableBetaFeatures() { // 1. Check explicit environment variable first (highest priority) const envValue = process.env[ANTHROPIC_ENV_VARS.ENABLE_BETA_FEATURES]; if (envValue !== undefined) { const normalized = envValue.toLowerCase().trim(); if (normalized === "true" || normalized === "1" || normalized === "yes") { return true; } if (normalized === "false" || normalized === "0" || normalized === "no") { return false; } logger.warn("Invalid ANTHROPIC_ENABLE_BETA_FEATURES value", { value: envValue, expected: ["true", "false"], fallback: "Defaulting based on auth method", }); } // 2. Check config file (placeholder for future config integration) // const configValue = loadConfigBetaFeatures(); // 3. Default based on authentication method // OAuth users get beta features enabled by default // API key users get beta features disabled by default for stability const authMethod = getAnthropicAuthMethod(); return authMethod === "oauth"; } /** * Gets complete subscription configuration including auth, tier, and features * Combines all configuration sources into a unified config object * * @returns Complete subscription configuration */ export function getAnthropicSubscriptionConfig() { const auth = getAnthropicAuthConfig(); const tier = detectSubscriptionTier(); const betaFeaturesEnabled = shouldEnableBetaFeatures(); const limits = getSubscriptionTierLimits(tier); // Determine if properly configured let isConfigured = false; let error; if (auth.method === "oauth") { if (auth.oauthToken?.accessToken || auth.accessToken) { isConfigured = true; } else { error = "OAuth authentication method specified but no access token configured. " + `Set ${ANTHROPIC_ENV_VARS.OAUTH_ACCESS_TOKEN} environment variable.`; } } else { if (auth.apiKey) { isConfigured = true; } else { error = "API key authentication method specified but no API key configured. " + `Set ${ANTHROPIC_ENV_VARS.API_KEY} environment variable.`; } } return { auth, tier, betaFeaturesEnabled, limits, isConfigured, error, }; } /** * Checks if the current subscription tier has access to a specific feature * @param feature The feature to check * @param currentTier The current subscription tier (optional, auto-detects if not provided) * @returns True if the tier has access to the feature */ export function hasSubscriptionFeature(feature, currentTier) { const tier = currentTier ?? detectSubscriptionTier(); const limits = getSubscriptionTierLimits(tier); // Map features to tier capabilities switch (feature) { case "extended_thinking": // Extended thinking requires pro or higher, or API tier return tier !== "free"; case "priority_access": return limits.priorityAccess; case "vision": case "file_analysis": // Available on all tiers return true; case "mcp_tools": // MCP tools require pro or higher return tier !== "free"; case "computer_use": // Computer use requires max tier or API return (tier === "max" || tier === "max_5" || tier === "max_20" || tier === "api"); case "web_search": // Web search available on pro or higher return tier !== "free"; default: return false; } } /** * Gets a human-readable description of the current authentication configuration * Useful for debugging and user feedback * * @returns Human-readable configuration description */ export function describeAnthropicConfig() { const config = getAnthropicSubscriptionConfig(); const lines = []; lines.push(`Authentication Method: ${config.auth.method}`); lines.push(`Subscription Tier: ${config.tier}`); lines.push(`Beta Features: ${config.betaFeaturesEnabled ? "Enabled" : "Disabled"}`); lines.push(`Configured: ${config.isConfigured ? "Yes" : "No"}`); if (config.error) { lines.push(`Error: ${config.error}`); } lines.push(`Daily Messages: ${config.limits.messagesPerDay}`); lines.push(`Context Window: ${config.limits.contextWindow.toLocaleString()} tokens`); lines.push(`Priority Access: ${config.limits.priorityAccess ? "Yes" : "No"}`); return lines.join("\n"); } //# sourceMappingURL=providerConfig.js.map