Download src/hooks/useApiKeyVerification.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 9.32 kB
-
https://huggingface.co/chenbhao/codev/resolve/main/src/hooks/useApiKeyVerification.ts
- Command line
-
hf download hf://chenbhao/codev/src/hooks/useApiKeyVerification.ts
-
curl -L -o useApiKeyVerification.ts https://huggingface.co/chenbhao/codev/resolve/main/src/hooks/useApiKeyVerification.ts
9.32 kB
| import { useCallback, useEffect, useState } from 'react' | |
| import { getIsNonInteractiveSession } from '../bootstrap/state.js' | |
| import { verifyApiKey } from '../services/api/claude.js' | |
| import { getGlobalConfig } from '../utils/config.js' | |
| import { | |
| getAnthropicApiKeyWithSource, | |
| getApiKeyFromApiKeyHelper, | |
| isAnthropicAuthEnabled, | |
| isClaudeAISubscriber, | |
| getConfiguredAuthProvider, | |
| getConfiguredAuthProviderFromFile, | |
| getOpenAIAuthTokens, | |
| getLocalBaseUrl, | |
| getLocalModelName, | |
| } from '../utils/auth.js' | |
| import { useAppState } from '../state/AppState.js' | |
| export type VerificationStatus = | |
| | 'loading' | |
| | 'valid' | |
| | 'invalid' | |
| | 'missing' | |
| | 'error' | |
| export type ApiKeyVerificationResult = { | |
| status: VerificationStatus | |
| reverify: () => Promise<void> | |
| error: Error | null | |
| } | |
| export function useApiKeyVerification(): ApiKeyVerificationResult { | |
| const [status, setStatus] = useState<VerificationStatus>(() => { | |
| const authProvider = getConfiguredAuthProviderFromFile() | |
| // Check Local authentication | |
| if (authProvider === 'local') { | |
| const baseUrl = getLocalBaseUrl() | |
| const modelName = getLocalModelName() | |
| if (baseUrl && modelName) { | |
| return 'valid' | |
| } | |
| return 'missing' | |
| } | |
| // Check OpenAI authentication | |
| if (authProvider === 'openai') { | |
| const config = getGlobalConfig() | |
| const hasApiKey = !!config.openAiApiKey | |
| const hasAccessToken = !!config.openAiAccessToken | |
| if (hasApiKey || hasAccessToken) { | |
| return 'valid' | |
| } | |
| return 'missing' | |
| } | |
| // Check OpenRouter authentication | |
| if (authProvider === 'openrouter') { | |
| const config = getGlobalConfig() | |
| const hasApiKey = !!config.openRouterApiKey | |
| if (hasApiKey) { | |
| return 'valid' | |
| } | |
| return 'missing' | |
| } | |
| // Check OpenCode Zen authentication | |
| // OpenCode Zen uses free models by default, no API key required | |
| if (authProvider === 'opencode') { | |
| return 'valid' | |
| } | |
| // Check NVIDIA authentication | |
| if (authProvider === 'nvidia') { | |
| const config = getGlobalConfig() | |
| const hasApiKey = !!config.nvidiaApiKey | |
| if (hasApiKey) { | |
| return 'valid' | |
| } | |
| return 'missing' | |
| } | |
| // Anthropic authentication | |
| 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<Error | null>(null) | |
| // Watch authVersion changes to re-verify API key | |
| const authVersion = useAppState(s => s.authVersion) | |
| useEffect(() => { | |
| // When authVersion changes, re-verify the API key | |
| const reverify = async (): Promise<void> => { | |
| const authProvider = getConfiguredAuthProviderFromFile() | |
| console.log('[useApiKeyVerification] authVersion changed, authProvider:', authProvider) | |
| // Check Local authentication | |
| if (authProvider === 'local') { | |
| // Force re-read config by reading directly from file | |
| const baseUrl = getLocalBaseUrl() | |
| const modelName = getLocalModelName() | |
| console.log('[useApiKeyVerification] Local - baseUrl:', baseUrl, 'modelName:', modelName) | |
| if (baseUrl && modelName) { | |
| setStatus('valid') | |
| } else { | |
| setStatus('missing') | |
| } | |
| return | |
| } | |
| // Check OpenAI authentication | |
| if (authProvider === 'openai') { | |
| const { getGlobalConfig: getConfig } = await import('../utils/config.js') | |
| const freshConfig = getConfig() | |
| const hasApiKey = !!freshConfig.openAiApiKey | |
| const hasAccessToken = !!freshConfig.openAiAccessToken | |
| if (hasApiKey || hasAccessToken) { | |
| setStatus('valid') | |
| } else { | |
| setStatus('missing') | |
| } | |
| return | |
| } | |
| // Check OpenRouter authentication | |
| if (authProvider === 'openrouter') { | |
| const { getGlobalConfig: getConfig } = await import('../utils/config.js') | |
| const freshConfig = getConfig() | |
| const hasApiKey = !!freshConfig.openRouterApiKey | |
| if (hasApiKey) { | |
| setStatus('valid') | |
| } else { | |
| setStatus('missing') | |
| } | |
| return | |
| } | |
| // Check OpenCode Zen authentication | |
| if (authProvider === 'opencode') { | |
| setStatus('valid') | |
| return | |
| } | |
| // Check NVIDIA authentication | |
| if (authProvider === 'nvidia') { | |
| const { getGlobalConfig: getConfig } = await import('../utils/config.js') | |
| const freshConfig = getConfig() | |
| const hasApiKey = !!freshConfig.nvidiaApiKey | |
| if (hasApiKey) { | |
| setStatus('valid') | |
| } else { | |
| setStatus('missing') | |
| } | |
| return | |
| } | |
| // Anthropic authentication | |
| 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 | |
| } | |
| } | |
| void reverify() | |
| }, [authVersion]) | |
| const verify = useCallback(async (): Promise<void> => { | |
| const authProvider = getConfiguredAuthProviderFromFile() | |
| // Check Local authentication | |
| if (authProvider === 'local') { | |
| const baseUrl = getLocalBaseUrl() | |
| const modelName = getLocalModelName() | |
| if (baseUrl && modelName) { | |
| setStatus('valid') | |
| } else { | |
| setStatus('missing') | |
| } | |
| return | |
| } | |
| // Check OpenAI authentication | |
| if (authProvider === 'openai') { | |
| const config = getGlobalConfig() | |
| const hasApiKey = !!config.openAiApiKey | |
| const hasAccessToken = !!config.openAiAccessToken | |
| if (hasApiKey || hasAccessToken) { | |
| setStatus('valid') | |
| } else { | |
| setStatus('missing') | |
| } | |
| return | |
| } | |
| // Check OpenRouter authentication | |
| if (authProvider === 'openrouter') { | |
| const config = getGlobalConfig() | |
| const hasApiKey = !!config.openRouterApiKey | |
| if (hasApiKey) { | |
| setStatus('valid') | |
| } else { | |
| setStatus('missing') | |
| } | |
| return | |
| } | |
| // Check OpenCode Zen authentication | |
| if (authProvider === 'opencode') { | |
| setStatus('valid') | |
| return | |
| } | |
| // Check NVIDIA authentication | |
| if (authProvider === 'nvidia') { | |
| const config = getGlobalConfig() | |
| const hasApiKey = !!config.nvidiaApiKey | |
| if (hasApiKey) { | |
| setStatus('valid') | |
| } else { | |
| setStatus('missing') | |
| } | |
| return | |
| } | |
| // Anthropic authentication | |
| 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, | |
| } | |
| } | |