agent-ecosystem/src/renderer/components/team/dialogs/ProvisioningProviderRuntimeSettingsDialog.tsx
2026-05-20 00:03:28 +03:00

198 lines
6.7 KiB
TypeScript

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<ProviderTerminalState | null>(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<CliProviderId | null>(() => {
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 (
<>
<ProviderRuntimeSettingsDialog
open={Boolean(openProviderId)}
onOpenChange={(open) => {
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 && (
<TerminalModal
title={`${getRuntimeDisplayName(cliStatus, multimodelEnabled)} ${
providerTerminal.action === 'login' ? 'Login' : 'Logout'
}: ${getProvisioningProviderLabel(providerTerminal.providerId)}`}
command={cliStatus.binaryPath}
args={providerTerminalCommand?.args}
env={providerTerminalCommand?.env}
onClose={() => {
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'
}
/>
)}
</>
);
};