/** * @license * Copyright 2025 Qwen * SPDX-License-Identifier: Apache-2.0 */ import React, { useState, useEffect } from 'react'; import { Box, Text, useInput } from 'ink'; import Spinner from 'ink-spinner'; import Link from 'ink-link'; import qrcode from 'qrcode-terminal'; import { Colors } from '../colors.js'; import { DeviceAuthorizationInfo } from '../hooks/useQwenAuth.js'; interface QwenOAuthProgressProps { onTimeout: () => void; onCancel: () => void; deviceAuth?: DeviceAuthorizationInfo; authStatus?: | 'idle' | 'polling' | 'success' | 'error' | 'timeout' | 'rate_limit'; authMessage?: string | null; } export function QwenOAuthProgress({ onTimeout, onCancel, deviceAuth, authStatus, authMessage, }: QwenOAuthProgressProps): React.JSX.Element { const defaultTimeout = deviceAuth?.expires_in || 300; // Default 5 minutes const [timeRemaining, setTimeRemaining] = useState(defaultTimeout); const [dots, setDots] = useState(''); const [qrCodeData, setQrCodeData] = useState(null); useInput((input, key) => { if (authStatus === 'timeout') { // Any key press in timeout state should trigger cancel to return to auth dialog onCancel(); } else if (key.escape) { onCancel(); } }); // Generate QR code when device auth is available useEffect(() => { if (!deviceAuth) { setQrCodeData(null); return; } // Generate QR code string const generateQR = () => { try { qrcode.generate( deviceAuth.verification_uri_complete, { small: true }, (qrcode: string) => { setQrCodeData(qrcode); }, ); } catch (error) { console.error('Failed to generate QR code:', error); setQrCodeData(null); } }; generateQR(); }, [deviceAuth]); // Countdown timer useEffect(() => { const timer = setInterval(() => { setTimeRemaining((prev) => { if (prev <= 1) { onTimeout(); return 0; } return prev - 1; }); }, 1000); return () => clearInterval(timer); }, [onTimeout]); // Animated dots useEffect(() => { const dotsTimer = setInterval(() => { setDots((prev) => { if (prev.length >= 3) return ''; return prev + '.'; }); }, 500); return () => clearInterval(dotsTimer); }, []); const formatTime = (seconds: number): string => { const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`; }; // Handle timeout state if (authStatus === 'timeout') { return ( Qwen OAuth Authentication Timeout {authMessage || `OAuth token expired (over ${defaultTimeout} seconds). Please select authentication method again.`} Press any key to return to authentication type selection. ); } if (!deviceAuth) { return ( Waiting for Qwen OAuth authentication... Time remaining: {formatTime(timeRemaining)} (Press ESC to cancel) ); } return ( Qwen OAuth Authentication Please visit this URL to authorize: {deviceAuth.verification_uri_complete} {qrCodeData && ( <> Or scan the QR code below: {qrCodeData} )} Waiting for authorization{dots} Time remaining: {formatTime(timeRemaining)} (Press ESC to cancel) ); }