import { useCallback, useState } from 'react' import { getIsNonInteractiveSession } from '../bootstrap/state.js' import { verifyApiKey } from '../services/api/claude.js' import { getAnthropicApiKeyWithSource, getApiKeyFromApiKeyHelper, isAnthropicAuthEnabled, isClaudeAISubscriber, } from '../utils/auth.js' export type VerificationStatus = | 'loading' | 'valid' | 'invalid' | 'missing' | 'error' export type ApiKeyVerificationResult = { status: VerificationStatus reverify: () => Promise error: Error | null } export function useApiKeyVerification(): ApiKeyVerificationResult { const [status, setStatus] = useState(() => { if (!isAnthropicAuthEnabled() || isClaudeAISubscriber()) { return 'valid' } // Use skipRetrievingKeyFromApiKeyHelper to avoid executing apiKeyHelper // before trust dialog is shown (security: prevents RCE via settings.json) const { key, source } = getAnthropicApiKeyWithSource({ skipRetrievingKeyFromApiKeyHelper: true, }) // If apiKeyHelper is configured, we have a key source even though we // haven't executed it yet - return 'loading' to indicate we'll verify later if (key || source === 'apiKeyHelper') { return 'loading' } return 'missing' }) const [error, setError] = useState(null) const verify = useCallback(async (): Promise => { if (!isAnthropicAuthEnabled() || isClaudeAISubscriber()) { setStatus('valid') return } // Warm the apiKeyHelper cache (no-op if not configured), then read from // all sources. getAnthropicApiKeyWithSource() reads the now-warm cache. await getApiKeyFromApiKeyHelper(getIsNonInteractiveSession()) const { key: apiKey, source } = getAnthropicApiKeyWithSource() if (!apiKey) { if (source === 'apiKeyHelper') { setStatus('error') setError(new Error('API key helper did not return a valid key')) return } const newStatus = 'missing' setStatus(newStatus) return } try { const isValid = await verifyApiKey(apiKey, false) const newStatus = isValid ? 'valid' : 'invalid' setStatus(newStatus) return } catch (error) { // This happens when there an error response from the API but it's not an invalid API key error // In this case, we still mark the API key as invalid - but we also log the error so we can // display it to the user to be more helpful setError(error as Error) const newStatus = 'error' setStatus(newStatus) return } }, []) return { status, reverify: verify, error, } }