import { useState, useCallback } from 'react';
import { Button } from '@/components/ui/button';
import { SkeletonPulse } from '@/components/ui/skeleton';
import { Spinner } from '@/components/ui/spinner';
import { CheckCircle2, AlertCircle, RefreshCw, XCircle } from 'lucide-react';
import { cn } from '@/lib/utils';
import type { CliStatus } from '../shared/types';
import type { CodexAuthStatus } from '@/store/setup-store';
import { OpenAIIcon } from '@/components/ui/provider-icon';
import { getElectronAPI } from '@/lib/electron';
import { toast } from 'sonner';
interface CliStatusProps {
status: CliStatus | null;
authStatus?: CodexAuthStatus | null;
isChecking: boolean;
onRefresh: () => void;
}
function getAuthMethodLabel(method: string): string {
switch (method) {
case 'api_key':
return 'API Key';
case 'api_key_env':
return 'API Key (Environment)';
case 'cli_authenticated':
case 'oauth':
return 'CLI Authentication';
default:
return method || 'Unknown';
}
}
function CodexCliStatusSkeleton() {
return (
{/* Installation status skeleton */}
{/* Auth status skeleton */}
);
}
export function CodexCliStatus({ status, authStatus, isChecking, onRefresh }: CliStatusProps) {
const [isAuthenticating, setIsAuthenticating] = useState(false);
const [isDeauthenticating, setIsDeauthenticating] = useState(false);
const handleSignIn = useCallback(async () => {
setIsAuthenticating(true);
try {
const api = getElectronAPI();
// Check if authCodex method exists on the API
const authCodex = (api.setup as Record | undefined)?.authCodex as
| (() => Promise<{ success: boolean; error?: string }>)
| undefined;
if (!authCodex) {
toast.error('Authentication Failed', {
description: 'Codex authentication is not available',
});
return;
}
const result = await authCodex();
if (result.success) {
toast.success('Signed In', {
description: 'Successfully authenticated Codex CLI',
});
onRefresh();
} else if (result.error) {
toast.error('Authentication Failed', {
description: result.error,
});
}
} catch (error) {
toast.error('Authentication Failed', {
description: error instanceof Error ? error.message : 'Unknown error',
});
} finally {
setIsAuthenticating(false);
}
}, [onRefresh]);
const handleSignOut = useCallback(async () => {
setIsDeauthenticating(true);
try {
const api = getElectronAPI();
// Check if deauthCodex method exists on the API
const deauthCodex = (api.setup as Record | undefined)?.deauthCodex as
| (() => Promise<{ success: boolean; error?: string }>)
| undefined;
if (!deauthCodex) {
toast.error('Sign Out Failed', {
description: 'Codex sign out is not available',
});
return;
}
const result = await deauthCodex();
if (result.success) {
toast.success('Signed Out', {
description: 'Successfully signed out from Codex CLI',
});
// Refresh status after successful logout
onRefresh();
} else if (result.error) {
toast.error('Sign Out Failed', {
description: result.error,
});
}
} catch (error) {
toast.error('Sign Out Failed', {
description: error instanceof Error ? error.message : 'Unknown error',
});
} finally {
setIsDeauthenticating(false);
}
}, [onRefresh]);
if (!status) return ;
return (
Codex CLI powers OpenAI models for coding and automation workflows.
{status.success && status.status === 'installed' ? (
Codex CLI Installed
{status.method && (
Method: {status.method}
)}
{status.version && (
Version: {status.version}
)}
{status.path && (
Path: {status.path}
)}
{/* Authentication Status */}
{authStatus?.authenticated ? (
Authenticated
Method:{' '}
{getAuthMethodLabel(authStatus.method)}
) : (
Not Authenticated
Click Sign In below to get authentication instructions.
)}
{status.recommendation && (
{status.recommendation}
)}
) : (
Codex CLI Not Detected
{status.recommendation ||
'Install Codex CLI to unlock OpenAI models with tool support.'}
{status.installCommands && (
Installation Commands:
{status.installCommands.npm && (
npm
{status.installCommands.npm}
)}
{status.installCommands.macos && (
macOS/Linux
{status.installCommands.macos}
)}
{status.installCommands.windows && (
Windows (PowerShell)
{status.installCommands.windows}
)}
)}
)}
);
}