/** * @license * Copyright 2025 Vybestack LLC * SPDX-License-Identifier: Apache-2.0 */ import React, { useState, useEffect, useCallback } from 'react'; import { Box, Text } from 'ink'; import Spinner from 'ink-spinner'; import { Colors } from '../../colors.js'; import { useKeypress } from '../../hooks/useKeypress.js'; import { getSpinnerType } from '../../contexts/UnicodeRenderingContext.js'; interface AuthenticationStepProps { provider: string; method: 'oauth' | 'api_key'; onComplete: () => void | Promise; onError: (error: string) => void; onBack: () => void; triggerAuth: ( provider: string, method: 'oauth' | 'api_key', apiKey?: string, ) => Promise; isFocused?: boolean; } const OAuthFlowContent: React.FC<{ providerDisplay: string }> = ({ providerDisplay, }) => ( Step 4 of 5: Authenticating with {providerDisplay} {' '} Opening browser for OAuth authentication... Please complete the authentication in your browser. This window will update when done. Press Esc to cancel and go back ); const ApiKeyInputContent: React.FC<{ providerDisplay: string; apiKey: string; isAuthenticating: boolean; }> = ({ providerDisplay, apiKey, isAuthenticating }) => { const maskedValue = '•'.repeat(apiKey.length); return ( Step 4 of 5: Enter Your API Key {isAuthenticating ? ( {' '} Validating API key... ) : ( <> Enter your {providerDisplay} API key: API Key: {maskedValue} )} Press Enter when done, Esc to go back ); }; export const AuthenticationStep: React.FC = ({ provider, method, onComplete, onError, onBack, triggerAuth, isFocused = true, }) => { const [apiKey, setApiKey] = useState(''); const [isAuthenticating, setIsAuthenticating] = useState(false); const providerDisplay = provider.charAt(0).toUpperCase() + provider.slice(1); const handleApiKeySubmit = useCallback(async () => { if (!apiKey.trim()) return; setIsAuthenticating(true); try { await triggerAuth(provider, 'api_key', apiKey.trim()); await onComplete(); } catch (error: unknown) { setIsAuthenticating(false); onError(error instanceof Error ? error.message : String(error)); } }, [apiKey, provider, triggerAuth, onComplete, onError]); useKeypress( (key) => { if (key.name === 'escape' && !isAuthenticating) { onBack(); return; } if (method !== 'api_key' || isAuthenticating) return; if (key.name === 'return') { void handleApiKeySubmit(); return; } if (key.name === 'backspace' || key.name === 'delete') { setApiKey((prev) => prev.slice(0, -1)); return; } const char = key.sequence; if (char && !key.ctrl && !key.meta) { const printable = char.replace(/[^\x20-\x7E]/g, ''); if (printable) { setApiKey((prev) => prev + printable); } } }, { isActive: isFocused }, ); const authStartedRef = React.useRef(false); useEffect(() => { if (method === 'oauth' && !authStartedRef.current) { authStartedRef.current = true; setIsAuthenticating(true); triggerAuth(provider, 'oauth') .then(async () => { await onComplete(); }) .catch((error: unknown) => { setIsAuthenticating(false); onError(error instanceof Error ? error.message : String(error)); }); } }, [method, provider, triggerAuth, onComplete, onError]); if (method === 'oauth') { return ; } return ( ); };