fix(opencode): pass project path to provider management

This commit is contained in:
777genius 2026-04-25 18:27:08 +03:00
parent 351ae4f4ed
commit 2eba377be2
8 changed files with 166 additions and 64 deletions

View file

@ -150,22 +150,26 @@ export interface RuntimeProviderManagementModelTestResponse {
export interface RuntimeProviderManagementLoadViewInput { export interface RuntimeProviderManagementLoadViewInput {
runtimeId: RuntimeProviderManagementRuntimeId; runtimeId: RuntimeProviderManagementRuntimeId;
projectPath?: string | null;
} }
export interface RuntimeProviderManagementConnectApiKeyInput { export interface RuntimeProviderManagementConnectApiKeyInput {
runtimeId: RuntimeProviderManagementRuntimeId; runtimeId: RuntimeProviderManagementRuntimeId;
providerId: string; providerId: string;
apiKey: string; apiKey: string;
projectPath?: string | null;
} }
export interface RuntimeProviderManagementForgetInput { export interface RuntimeProviderManagementForgetInput {
runtimeId: RuntimeProviderManagementRuntimeId; runtimeId: RuntimeProviderManagementRuntimeId;
providerId: string; providerId: string;
projectPath?: string | null;
} }
export interface RuntimeProviderManagementLoadModelsInput { export interface RuntimeProviderManagementLoadModelsInput {
runtimeId: RuntimeProviderManagementRuntimeId; runtimeId: RuntimeProviderManagementRuntimeId;
providerId: string; providerId: string;
projectPath?: string | null;
query?: string | null; query?: string | null;
limit?: number | null; limit?: number | null;
} }
@ -174,6 +178,7 @@ export interface RuntimeProviderManagementTestModelInput {
runtimeId: RuntimeProviderManagementRuntimeId; runtimeId: RuntimeProviderManagementRuntimeId;
providerId: string; providerId: string;
modelId: string; modelId: string;
projectPath?: string | null;
} }
export interface RuntimeProviderManagementSetDefaultModelInput { export interface RuntimeProviderManagementSetDefaultModelInput {
@ -181,4 +186,5 @@ export interface RuntimeProviderManagementSetDefaultModelInput {
providerId: string; providerId: string;
modelId: string; modelId: string;
probe?: boolean; probe?: boolean;
projectPath?: string | null;
} }

View file

@ -107,6 +107,22 @@ function normalizeCommandFailure(error: unknown): string {
return 'Runtime provider management command failed'; return 'Runtime provider management command failed';
} }
function normalizeProjectPath(projectPath: string | null | undefined): string | null {
const normalized = projectPath?.trim();
return normalized ? normalized : null;
}
function appendProjectPathArgs(args: string[], projectPath: string | null): string[] {
return projectPath ? [...args, '--project-path', projectPath] : args;
}
function runtimeProviderCommandOptions<T extends { env: NodeJS.ProcessEnv }>(
options: T,
projectPath: string | null
): T & { cwd?: string } {
return projectPath ? { ...options, cwd: projectPath } : options;
}
async function resolveCliEnv(): Promise<{ async function resolveCliEnv(): Promise<{
binaryPath: string | null; binaryPath: string | null;
env: NodeJS.ProcessEnv; env: NodeJS.ProcessEnv;
@ -194,11 +210,15 @@ export class AgentTeamsRuntimeProviderManagementCliClient implements RuntimeProv
); );
} }
const projectPath = normalizeProjectPath(input.projectPath);
try { try {
const { stdout } = await execCli( const { stdout } = await execCli(
binaryPath, binaryPath,
['runtime', 'providers', 'view', '--runtime', input.runtimeId, '--json', '--compact'], appendProjectPathArgs(
{ env, timeout: COMMAND_TIMEOUT_MS } ['runtime', 'providers', 'view', '--runtime', input.runtimeId, '--json', '--compact'],
projectPath
),
runtimeProviderCommandOptions({ env, timeout: COMMAND_TIMEOUT_MS }, projectPath)
); );
return extractJsonObject<RuntimeProviderManagementViewResponse>(stdout); return extractJsonObject<RuntimeProviderManagementViewResponse>(stdout);
} catch (error) { } catch (error) {
@ -225,24 +245,31 @@ export class AgentTeamsRuntimeProviderManagementCliClient implements RuntimeProv
); );
} }
const projectPath = normalizeProjectPath(input.projectPath);
try { try {
const child = spawnCli( const child = spawnCli(
binaryPath, binaryPath,
[ appendProjectPathArgs(
'runtime', [
'providers', 'runtime',
'connect-api-key', 'providers',
'--runtime', 'connect-api-key',
input.runtimeId, '--runtime',
'--provider', input.runtimeId,
input.providerId, '--provider',
'--stdin-key', input.providerId,
'--json', '--stdin-key',
], '--json',
{ ],
env, projectPath
stdio: 'pipe', ),
} runtimeProviderCommandOptions(
{
env,
stdio: 'pipe' as const,
},
projectPath
)
) as ChildProcessWithoutNullStreams; ) as ChildProcessWithoutNullStreams;
const result = await collectSpawnOutput(child, input.apiKey); const result = await collectSpawnOutput(child, input.apiKey);
if (result.code === 0) { if (result.code === 0) {
@ -281,20 +308,24 @@ export class AgentTeamsRuntimeProviderManagementCliClient implements RuntimeProv
); );
} }
const projectPath = normalizeProjectPath(input.projectPath);
try { try {
const { stdout } = await execCli( const { stdout } = await execCli(
binaryPath, binaryPath,
[ appendProjectPathArgs(
'runtime', [
'providers', 'runtime',
'forget', 'providers',
'--runtime', 'forget',
input.runtimeId, '--runtime',
'--provider', input.runtimeId,
input.providerId, '--provider',
'--json', input.providerId,
], '--json',
{ env, timeout: COMMAND_TIMEOUT_MS } ],
projectPath
),
runtimeProviderCommandOptions({ env, timeout: COMMAND_TIMEOUT_MS }, projectPath)
); );
return extractJsonObject<RuntimeProviderManagementProviderResponse>(stdout); return extractJsonObject<RuntimeProviderManagementProviderResponse>(stdout);
} catch (error) { } catch (error) {
@ -317,7 +348,8 @@ export class AgentTeamsRuntimeProviderManagementCliClient implements RuntimeProv
); );
} }
const args = [ const projectPath = normalizeProjectPath(input.projectPath);
let args = [
'runtime', 'runtime',
'providers', 'providers',
'models', 'models',
@ -333,10 +365,11 @@ export class AgentTeamsRuntimeProviderManagementCliClient implements RuntimeProv
if (typeof input.limit === 'number' && Number.isFinite(input.limit) && input.limit > 0) { if (typeof input.limit === 'number' && Number.isFinite(input.limit) && input.limit > 0) {
args.push('--limit', String(Math.floor(input.limit))); args.push('--limit', String(Math.floor(input.limit)));
} }
args = appendProjectPathArgs(args, projectPath);
try { try {
const { stdout } = await execCli(binaryPath, args, { const { stdout } = await execCli(binaryPath, args, {
env, ...runtimeProviderCommandOptions({ env }, projectPath),
timeout: COMMAND_TIMEOUT_MS, timeout: COMMAND_TIMEOUT_MS,
}); });
return extractJsonObject<RuntimeProviderManagementModelsResponse>(stdout); return extractJsonObject<RuntimeProviderManagementModelsResponse>(stdout);
@ -364,22 +397,26 @@ export class AgentTeamsRuntimeProviderManagementCliClient implements RuntimeProv
); );
} }
const projectPath = normalizeProjectPath(input.projectPath);
try { try {
const { stdout } = await execCli( const { stdout } = await execCli(
binaryPath, binaryPath,
[ appendProjectPathArgs(
'runtime', [
'providers', 'runtime',
'test-model', 'providers',
'--runtime', 'test-model',
input.runtimeId, '--runtime',
'--provider', input.runtimeId,
input.providerId, '--provider',
'--model', input.providerId,
input.modelId, '--model',
'--json', input.modelId,
], '--json',
{ env, timeout: PROBE_COMMAND_TIMEOUT_MS } ],
projectPath
),
runtimeProviderCommandOptions({ env, timeout: PROBE_COMMAND_TIMEOUT_MS }, projectPath)
); );
return extractJsonObject<RuntimeProviderManagementModelTestResponse>(stdout); return extractJsonObject<RuntimeProviderManagementModelTestResponse>(stdout);
} catch (error) { } catch (error) {
@ -408,24 +445,28 @@ export class AgentTeamsRuntimeProviderManagementCliClient implements RuntimeProv
); );
} }
const projectPath = normalizeProjectPath(input.projectPath);
try { try {
const { stdout } = await execCli( const { stdout } = await execCli(
binaryPath, binaryPath,
[ appendProjectPathArgs(
'runtime', [
'providers', 'runtime',
'set-default', 'providers',
'--runtime', 'set-default',
input.runtimeId, '--runtime',
'--provider', input.runtimeId,
input.providerId, '--provider',
'--model', input.providerId,
input.modelId, '--model',
'--probe', input.modelId,
'--compact', '--probe',
'--json', '--compact',
], '--json',
{ env, timeout: PROBE_COMMAND_TIMEOUT_MS } ],
projectPath
),
runtimeProviderCommandOptions({ env, timeout: PROBE_COMMAND_TIMEOUT_MS }, projectPath)
); );
return extractJsonObject<RuntimeProviderManagementViewResponse>(stdout); return extractJsonObject<RuntimeProviderManagementViewResponse>(stdout);
} catch (error) { } catch (error) {

View file

@ -7,6 +7,7 @@ import type { JSX } from 'react';
interface RuntimeProviderManagementPanelProps { interface RuntimeProviderManagementPanelProps {
readonly runtimeId: RuntimeProviderManagementRuntimeId; readonly runtimeId: RuntimeProviderManagementRuntimeId;
readonly open: boolean; readonly open: boolean;
readonly projectPath?: string | null;
readonly disabled?: boolean; readonly disabled?: boolean;
readonly onProviderChanged?: () => Promise<void> | void; readonly onProviderChanged?: () => Promise<void> | void;
} }
@ -14,14 +15,23 @@ interface RuntimeProviderManagementPanelProps {
export function RuntimeProviderManagementPanel({ export function RuntimeProviderManagementPanel({
runtimeId, runtimeId,
open, open,
projectPath = null,
disabled = false, disabled = false,
onProviderChanged, onProviderChanged,
}: RuntimeProviderManagementPanelProps): JSX.Element { }: RuntimeProviderManagementPanelProps): JSX.Element {
const [state, actions] = useRuntimeProviderManagement({ const [state, actions] = useRuntimeProviderManagement({
runtimeId, runtimeId,
enabled: open, enabled: open,
projectPath,
onProviderChanged, onProviderChanged,
}); });
return <RuntimeProviderManagementPanelView state={state} actions={actions} disabled={disabled} />; return (
<RuntimeProviderManagementPanelView
state={state}
actions={actions}
disabled={disabled}
projectPath={projectPath}
/>
);
} }

View file

@ -19,6 +19,7 @@ import type {
interface UseRuntimeProviderManagementOptions { interface UseRuntimeProviderManagementOptions {
runtimeId: RuntimeProviderManagementRuntimeId; runtimeId: RuntimeProviderManagementRuntimeId;
enabled: boolean; enabled: boolean;
projectPath?: string | null;
onProviderChanged?: () => Promise<void> | void; onProviderChanged?: () => Promise<void> | void;
} }
@ -175,6 +176,7 @@ export function useRuntimeProviderManagement(
try { try {
const response = await api.runtimeProviderManagement.loadView({ const response = await api.runtimeProviderManagement.loadView({
runtimeId: options.runtimeId, runtimeId: options.runtimeId,
projectPath: options.projectPath ?? null,
}); });
if (response.error) { if (response.error) {
setView(null); setView(null);
@ -195,7 +197,7 @@ export function useRuntimeProviderManagement(
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [options.enabled, options.runtimeId]); }, [options.enabled, options.projectPath, options.runtimeId]);
useEffect(() => { useEffect(() => {
if (!options.enabled) { if (!options.enabled) {
@ -226,6 +228,7 @@ export function useRuntimeProviderManagement(
api.runtimeProviderManagement.loadModels({ api.runtimeProviderManagement.loadModels({
runtimeId: options.runtimeId, runtimeId: options.runtimeId,
providerId: modelPickerProviderId, providerId: modelPickerProviderId,
projectPath: options.projectPath ?? null,
query: modelQuery.trim() || null, query: modelQuery.trim() || null,
limit: 250, limit: 250,
}), }),
@ -268,7 +271,7 @@ export function useRuntimeProviderManagement(
return () => { return () => {
cancelled = true; cancelled = true;
}; };
}, [modelPickerProviderId, modelQuery, options.enabled, options.runtimeId]); }, [modelPickerProviderId, modelQuery, options.enabled, options.projectPath, options.runtimeId]);
useEffect(() => { useEffect(() => {
if (!options.enabled || activeFormProviderId) { if (!options.enabled || activeFormProviderId) {
@ -338,6 +341,7 @@ export function useRuntimeProviderManagement(
runtimeId: options.runtimeId, runtimeId: options.runtimeId,
providerId, providerId,
apiKey, apiKey,
projectPath: options.projectPath ?? null,
}), }),
'Provider connect timed out' 'Provider connect timed out'
); );
@ -381,6 +385,7 @@ export function useRuntimeProviderManagement(
api.runtimeProviderManagement.forgetCredential({ api.runtimeProviderManagement.forgetCredential({
runtimeId: options.runtimeId, runtimeId: options.runtimeId,
providerId, providerId,
projectPath: options.projectPath ?? null,
}), }),
'Provider forget timed out' 'Provider forget timed out'
); );
@ -456,6 +461,7 @@ export function useRuntimeProviderManagement(
runtimeId: options.runtimeId, runtimeId: options.runtimeId,
providerId, providerId,
modelId, modelId,
projectPath: options.projectPath ?? null,
}), }),
'Model test timed out', 'Model test timed out',
100_000 100_000
@ -486,7 +492,7 @@ export function useRuntimeProviderManagement(
setTestingModelId(null); setTestingModelId(null);
} }
}, },
[options.runtimeId] [options.projectPath, options.runtimeId]
); );
const setDefaultModel = useCallback( const setDefaultModel = useCallback(
@ -501,6 +507,7 @@ export function useRuntimeProviderManagement(
providerId, providerId,
modelId, modelId,
probe: true, probe: true,
projectPath: options.projectPath ?? null,
}), }),
'Set default model timed out', 'Set default model timed out',
100_000 100_000

View file

@ -45,6 +45,7 @@ interface RuntimeProviderManagementPanelViewProps {
readonly state: RuntimeProviderManagementState; readonly state: RuntimeProviderManagementState;
readonly actions: RuntimeProviderManagementActions; readonly actions: RuntimeProviderManagementActions;
readonly disabled: boolean; readonly disabled: boolean;
readonly projectPath?: string | null;
} }
interface ProviderActionsProps { interface ProviderActionsProps {
@ -97,7 +98,8 @@ function RuntimeSummary({
state, state,
onRefresh, onRefresh,
disabled, disabled,
}: Pick<RuntimeProviderManagementPanelViewProps, 'state' | 'disabled'> & { projectPath,
}: Pick<RuntimeProviderManagementPanelViewProps, 'state' | 'disabled' | 'projectPath'> & {
onRefresh: () => void; onRefresh: () => void;
}): JSX.Element { }): JSX.Element {
const runtime = state.view?.runtime; const runtime = state.view?.runtime;
@ -136,6 +138,15 @@ function RuntimeSummary({
</span> </span>
) : null} ) : null}
</div> </div>
<div
className="mt-1 truncate text-[11px]"
style={{ color: 'var(--color-text-muted)' }}
title={projectPath ?? undefined}
>
{projectPath
? `Managing selected project profile: ${projectPath}`
: 'Managing fallback OpenCode profile. Select a project to manage launch credentials for that project.'}
</div>
{state.loading ? ( {state.loading ? (
<div <div
className="mt-2 flex items-center gap-2 text-xs" className="mt-2 flex items-center gap-2 text-xs"
@ -784,6 +795,7 @@ export function RuntimeProviderManagementPanelView({
state, state,
actions, actions,
disabled, disabled,
projectPath = null,
}: RuntimeProviderManagementPanelViewProps): JSX.Element { }: RuntimeProviderManagementPanelViewProps): JSX.Element {
const providerQuery = state.providerQuery.trim().toLowerCase(); const providerQuery = state.providerQuery.trim().toLowerCase();
const filteredProviders = providerQuery const filteredProviders = providerQuery
@ -809,7 +821,12 @@ export function RuntimeProviderManagementPanelView({
return ( return (
<div className="space-y-3"> <div className="space-y-3">
<RuntimeSummary state={state} disabled={disabled} onRefresh={() => void actions.refresh()} /> <RuntimeSummary
state={state}
disabled={disabled}
projectPath={projectPath}
onRefresh={() => void actions.refresh()}
/>
{state.error ? ( {state.error ? (
<div <div

View file

@ -44,6 +44,7 @@ import { createLoadingMultimodelCliStatus } from '@renderer/store/slices/cliInst
import { formatBytes } from '@renderer/utils/formatters'; import { formatBytes } from '@renderer/utils/formatters';
import { filterMainScreenCliProviders } from '@renderer/utils/geminiUiFreeze'; import { filterMainScreenCliProviders } from '@renderer/utils/geminiUiFreeze';
import { isMultimodelRuntimeStatus } from '@renderer/utils/multimodelProviderVisibility'; import { isMultimodelRuntimeStatus } from '@renderer/utils/multimodelProviderVisibility';
import { resolveProjectPathById } from '@renderer/utils/projectLookup';
import { refreshCliStatusForCurrentMode } from '@renderer/utils/refreshCliStatus'; import { refreshCliStatusForCurrentMode } from '@renderer/utils/refreshCliStatus';
import { getRuntimeDisplayName as getHumanRuntimeDisplayName } from '@renderer/utils/runtimeDisplayName'; import { getRuntimeDisplayName as getHumanRuntimeDisplayName } from '@renderer/utils/runtimeDisplayName';
import { import {
@ -1013,6 +1014,9 @@ const InstalledBanner = ({
export const CliStatusBanner = (): React.JSX.Element | null => { export const CliStatusBanner = (): React.JSX.Element | null => {
const isElectron = useMemo(() => isElectronMode(), []); const isElectron = useMemo(() => isElectronMode(), []);
const appConfig = useStore((s) => s.appConfig); const appConfig = useStore((s) => s.appConfig);
const selectedProjectId = useStore((s) => s.selectedProjectId);
const projects = useStore((s) => s.projects);
const repositoryGroups = useStore((s) => s.repositoryGroups);
const updateConfig = useStore((s) => s.updateConfig); const updateConfig = useStore((s) => s.updateConfig);
const { const {
cliStatus, cliStatus,
@ -1048,6 +1052,10 @@ export const CliStatusBanner = (): React.JSX.Element | null => {
loadDashboardCliStatusBannerCollapsed() loadDashboardCliStatusBannerCollapsed()
); );
const multimodelEnabled = appConfig?.general?.multimodelEnabled ?? true; const multimodelEnabled = appConfig?.general?.multimodelEnabled ?? true;
const selectedProjectPath = useMemo(
() => resolveProjectPathById(selectedProjectId, projects, repositoryGroups)?.path ?? null,
[projects, repositoryGroups, selectedProjectId]
);
const loadingCliStatus = useMemo( const loadingCliStatus = useMemo(
() => () =>
!cliStatus && cliStatusLoading && multimodelEnabled !cliStatus && cliStatusLoading && multimodelEnabled
@ -1289,6 +1297,7 @@ export const CliStatusBanner = (): React.JSX.Element | null => {
open={manageDialogOpen} open={manageDialogOpen}
onOpenChange={setManageDialogOpen} onOpenChange={setManageDialogOpen}
providers={visibleCliProviders} providers={visibleCliProviders}
projectPath={selectedProjectPath}
initialProviderId={ initialProviderId={
visibleCliProviders.some((provider) => provider.providerId === manageProviderId) visibleCliProviders.some((provider) => provider.providerId === manageProviderId)
? manageProviderId ? manageProviderId

View file

@ -72,6 +72,7 @@ interface Props {
readonly onOpenChange: (open: boolean) => void; readonly onOpenChange: (open: boolean) => void;
readonly providers: CliProviderStatus[]; readonly providers: CliProviderStatus[];
readonly initialProviderId: CliProviderId; readonly initialProviderId: CliProviderId;
readonly projectPath?: string | null;
readonly providerStatusLoading?: Partial<Record<CliProviderId, boolean>>; readonly providerStatusLoading?: Partial<Record<CliProviderId, boolean>>;
readonly disabled?: boolean; readonly disabled?: boolean;
readonly onSelectBackend: (providerId: CliProviderId, backendId: string) => Promise<void> | void; readonly onSelectBackend: (providerId: CliProviderId, backendId: string) => Promise<void> | void;
@ -543,6 +544,7 @@ export const ProviderRuntimeSettingsDialog = ({
onOpenChange, onOpenChange,
providers, providers,
initialProviderId, initialProviderId,
projectPath = null,
providerStatusLoading = {}, providerStatusLoading = {},
disabled = false, disabled = false,
onSelectBackend, onSelectBackend,
@ -1168,6 +1170,7 @@ export const ProviderRuntimeSettingsDialog = ({
<RuntimeProviderManagementPanel <RuntimeProviderManagementPanel
runtimeId="opencode" runtimeId="opencode"
open={open} open={open}
projectPath={projectPath}
disabled={disabled || selectedProviderLoading} disabled={disabled || selectedProviderLoading}
onProviderChanged={() => onRefreshProvider?.('opencode')} onProviderChanged={() => onRefreshProvider?.('opencode')}
/> />

View file

@ -32,6 +32,7 @@ import { useCliInstaller } from '@renderer/hooks/useCliInstaller';
import { useStore } from '@renderer/store'; import { useStore } from '@renderer/store';
import { createLoadingMultimodelCliStatus } from '@renderer/store/slices/cliInstallerSlice'; import { createLoadingMultimodelCliStatus } from '@renderer/store/slices/cliInstallerSlice';
import { formatBytes } from '@renderer/utils/formatters'; import { formatBytes } from '@renderer/utils/formatters';
import { resolveProjectPathById } from '@renderer/utils/projectLookup';
import { refreshCliStatusForCurrentMode } from '@renderer/utils/refreshCliStatus'; import { refreshCliStatusForCurrentMode } from '@renderer/utils/refreshCliStatus';
import { getRuntimeDisplayName } from '@renderer/utils/runtimeDisplayName'; import { getRuntimeDisplayName } from '@renderer/utils/runtimeDisplayName';
import { import {
@ -185,6 +186,9 @@ function getProviderTerminalLogoutCommand(provider: CliProviderStatus): {
export const CliStatusSection = (): React.JSX.Element | null => { export const CliStatusSection = (): React.JSX.Element | null => {
const isElectron = useMemo(() => isElectronMode(), []); const isElectron = useMemo(() => isElectronMode(), []);
const appConfig = useStore((s) => s.appConfig); const appConfig = useStore((s) => s.appConfig);
const selectedProjectId = useStore((s) => s.selectedProjectId);
const projects = useStore((s) => s.projects);
const repositoryGroups = useStore((s) => s.repositoryGroups);
const openExtensionsTab = useStore((s) => s.openExtensionsTab); const openExtensionsTab = useStore((s) => s.openExtensionsTab);
const updateConfig = useStore((s) => s.updateConfig); const updateConfig = useStore((s) => s.updateConfig);
const { const {
@ -211,6 +215,10 @@ export const CliStatusSection = (): React.JSX.Element | null => {
const [manageProviderId, setManageProviderId] = useState<CliProviderId>('gemini'); const [manageProviderId, setManageProviderId] = useState<CliProviderId>('gemini');
const [manageDialogOpen, setManageDialogOpen] = useState(false); const [manageDialogOpen, setManageDialogOpen] = useState(false);
const multimodelEnabled = appConfig?.general?.multimodelEnabled ?? true; const multimodelEnabled = appConfig?.general?.multimodelEnabled ?? true;
const selectedProjectPath = useMemo(
() => resolveProjectPathById(selectedProjectId, projects, repositoryGroups)?.path ?? null,
[projects, repositoryGroups, selectedProjectId]
);
const loadingCliStatus = const loadingCliStatus =
!cliStatus && cliStatusLoading && multimodelEnabled !cliStatus && cliStatusLoading && multimodelEnabled
? createLoadingMultimodelCliStatus() ? createLoadingMultimodelCliStatus()
@ -645,6 +653,7 @@ export const CliStatusSection = (): React.JSX.Element | null => {
open={manageDialogOpen} open={manageDialogOpen}
onOpenChange={setManageDialogOpen} onOpenChange={setManageDialogOpen}
providers={effectiveCliStatus.providers} providers={effectiveCliStatus.providers}
projectPath={selectedProjectPath}
initialProviderId={manageProviderId} initialProviderId={manageProviderId}
providerStatusLoading={cliProviderStatusLoading} providerStatusLoading={cliProviderStatusLoading}
disabled={!effectiveCliStatus.binaryPath || isBusy || cliStatusLoading} disabled={!effectiveCliStatus.binaryPath || isBusy || cliStatusLoading}