fix(opencode): pass project path to provider management
This commit is contained in:
parent
351ae4f4ed
commit
2eba377be2
8 changed files with 166 additions and 64 deletions
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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')}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue