import React, { useCallback, useMemo, useState } from 'react'; import { ProviderRuntimeSettingsDialog } from '@renderer/components/runtime/ProviderRuntimeSettingsDialog'; import { getProviderTerminalCommand, getProviderTerminalLogoutCommand, } from '@renderer/components/runtime/providerTerminalCommands'; import { TerminalModal } from '@renderer/components/terminal/TerminalModal'; import { useStore } from '@renderer/store'; import { refreshCliStatusForCurrentMode } from '@renderer/utils/refreshCliStatus'; import { getRuntimeDisplayName } from '@renderer/utils/runtimeDisplayName'; import { useShallow } from 'zustand/react/shallow'; import { getProvisioningProviderLabel } from './ProvisioningProviderStatusList'; import type { CliProviderId, CliProviderStatus } from '@shared/types'; interface ProvisioningProviderRuntimeSettingsDialogProps { readonly openProviderId: CliProviderId | null; readonly onOpenProviderIdChange: (providerId: CliProviderId | null) => void; readonly providers: CliProviderStatus[]; readonly projectPath?: string | null; readonly disabled?: boolean; readonly onProviderRuntimeChanged?: (providerId: CliProviderId) => void; } interface ProviderTerminalState { providerId: CliProviderId; action: 'login' | 'logout'; } export const ProvisioningProviderRuntimeSettingsDialog = ({ openProviderId, onOpenProviderIdChange, providers, projectPath = null, disabled = false, onProviderRuntimeChanged, }: ProvisioningProviderRuntimeSettingsDialogProps): React.JSX.Element | null => { const [providerTerminal, setProviderTerminal] = useState(null); const { appConfig, bootstrapCliStatus, cliProviderStatusLoading, cliStatus, cliStatusLoading, codexRuntimeStatus, codexRuntimeStatusLoading, fetchCliProviderStatus, fetchCliStatus, installCodexRuntime, invalidateCliStatus, multimodelEnabled, updateConfig, } = useStore( useShallow((s) => ({ appConfig: s.appConfig, bootstrapCliStatus: s.bootstrapCliStatus, cliProviderStatusLoading: s.cliProviderStatusLoading, cliStatus: s.cliStatus, cliStatusLoading: s.cliStatusLoading, codexRuntimeStatus: s.codexRuntimeStatus, codexRuntimeStatusLoading: s.codexRuntimeStatusLoading, fetchCliProviderStatus: s.fetchCliProviderStatus, fetchCliStatus: s.fetchCliStatus, installCodexRuntime: s.installCodexRuntime, invalidateCliStatus: s.invalidateCliStatus, multimodelEnabled: s.appConfig?.general?.multimodelEnabled ?? true, updateConfig: s.updateConfig, })) ); const selectedProviderId = useMemo(() => { if (!openProviderId || providers.length === 0) { return null; } return providers.some((provider) => provider.providerId === openProviderId) ? openProviderId : (providers[0]?.providerId ?? null); }, [openProviderId, providers]); const handleProviderBackendChange = useCallback( async (providerId: CliProviderId, backendId: string) => { if (providerId !== 'gemini' && providerId !== 'codex') { return; } const currentBackends = appConfig?.runtime?.providerBackends ?? { gemini: 'auto' as const, codex: 'codex-native' as const, }; await updateConfig('runtime', { providerBackends: { ...currentBackends, [providerId]: backendId, }, }); try { await fetchCliProviderStatus(providerId, { silent: false }); onProviderRuntimeChanged?.(providerId); } catch { throw new Error('Runtime updated, but failed to refresh provider status.'); } }, [ appConfig?.runtime?.providerBackends, fetchCliProviderStatus, onProviderRuntimeChanged, updateConfig, ] ); const handleProviderRefresh = useCallback( async (providerId: CliProviderId) => { await fetchCliProviderStatus(providerId, { silent: false }); onProviderRuntimeChanged?.(providerId); }, [fetchCliProviderStatus, onProviderRuntimeChanged] ); const refreshRuntimeAfterTerminal = useCallback(() => { void (async () => { await invalidateCliStatus(); await refreshCliStatusForCurrentMode({ multimodelEnabled, bootstrapCliStatus, fetchCliStatus, }); })(); }, [bootstrapCliStatus, fetchCliStatus, invalidateCliStatus, multimodelEnabled]); const activeTerminalProvider = providerTerminal ? (providers.find((provider) => provider.providerId === providerTerminal.providerId) ?? null) : null; const providerTerminalCommand = providerTerminal && activeTerminalProvider ? providerTerminal.action === 'login' ? getProviderTerminalCommand(activeTerminalProvider) : getProviderTerminalLogoutCommand(activeTerminalProvider) : null; if (!selectedProviderId) { return null; } return ( <> { if (!open) { onOpenProviderIdChange(null); } }} providers={providers} projectPath={projectPath} initialProviderId={selectedProviderId} providerStatusLoading={cliProviderStatusLoading} disabled={disabled || cliStatusLoading || !cliStatus?.binaryPath} codexRuntimeStatus={codexRuntimeStatus} codexRuntimeStatusLoading={codexRuntimeStatusLoading} onInstallCodexRuntime={() => installCodexRuntime()} onSelectBackend={handleProviderBackendChange} onRefreshProvider={handleProviderRefresh} onRequestLogin={(providerId) => setProviderTerminal({ providerId, action: 'login' })} /> {providerTerminal && cliStatus?.binaryPath && ( { setProviderTerminal(null); onProviderRuntimeChanged?.(providerTerminal.providerId); refreshRuntimeAfterTerminal(); }} onExit={() => { onProviderRuntimeChanged?.(providerTerminal.providerId); refreshRuntimeAfterTerminal(); }} autoCloseOnSuccessMs={3000} successMessage={ providerTerminal.action === 'login' ? 'Authentication updated' : 'Provider logged out' } failureMessage={ providerTerminal.action === 'login' ? 'Authentication failed' : 'Logout failed' } /> )} ); };