diff --git a/packages/ui/src/components/sections/providers/ProvidersPage.test.ts b/packages/ui/src/components/sections/providers/ProvidersPage.test.ts index 5bb461f3..1861568c 100644 --- a/packages/ui/src/components/sections/providers/ProvidersPage.test.ts +++ b/packages/ui/src/components/sections/providers/ProvidersPage.test.ts @@ -133,6 +133,31 @@ describe('provider credential state helpers', () => { })).toBe(true); }); + test('editable custom provider keeps models visible even with no credentials signal', () => { + // Config-defined custom providers (e.g. local LM Studio/Ollama style) + // are user-editable in place; a stale 'Credentials missing' must not + // hide their models section. Without the exemption, a keyless local + // custom provider regresses to 'Credentials missing' with models hidden. + const hasCredentials = providerHasCredentials({ + key: undefined, + authSourceExists: false, + optionsApiKey: null, + }); + expect(hasCredentials).toBe(false); + expect(shouldShowModelsSection({ + modelCount: 1, + sourcesLoaded: true, + hasCredentials: false, + isEditableCustomProvider: true, + })).toBe(true); + expect(shouldShowModelsSection({ + modelCount: 1, + sourcesLoaded: true, + hasCredentials: false, + isEditableCustomProvider: false, + })).toBe(false); + }); + test('auth save followed by providers refresh recognizes credentials without stale missing state', () => { // Pre-save: sources say no auth, provider has no key yet. const before = providerHasCredentials({ diff --git a/packages/ui/src/components/sections/providers/ProvidersPage.tsx b/packages/ui/src/components/sections/providers/ProvidersPage.tsx index 18269918..d50b4875 100644 --- a/packages/ui/src/components/sections/providers/ProvidersPage.tsx +++ b/packages/ui/src/components/sections/providers/ProvidersPage.tsx @@ -27,7 +27,7 @@ import type { ModelMetadata } from '@/types'; import { getCurrentIntlLocale, useI18n } from '@/lib/i18n'; import { runtimeFetch } from '@/lib/runtime-fetch'; import { opencodeClient } from '@/lib/opencode/client'; -import { shouldLoadAvailableProviders } from './providerAvailability'; +import { requiresProviderAuth, shouldLoadAvailableProviders } from './providerAvailability'; import { getOAuthAuthMethods, parseAuthPayload, @@ -339,6 +339,7 @@ export const ProvidersPage: React.FC = () => { const hasCreds = providerHasCredentials({ key: provider?.key, authSourceExists: sources.auth.exists, + optionsApiKey: (provider as { options?: { apiKey?: string | null } } | undefined)?.options?.apiKey ?? null, }); const isEditableCustomProvider = Boolean( provider && isConfigDefinedCustomProvider(provider, sources) @@ -862,11 +863,12 @@ export const ProvidersPage: React.FC = () => { authSourceExists: selectedSources?.auth.exists, optionsApiKey: (selectedProvider as { options?: { apiKey?: string | null } }).options?.apiKey ?? null, }); - const authStatusIncomplete = sourcesLoaded && !hasCredentials; + const authStatusIncomplete = requiresProviderAuth(sourcesLoaded, hasCredentials, isEditableCustomProvider); const showModelsSection = shouldShowModelsSection({ modelCount: providerModels.length, sourcesLoaded, hasCredentials, + isEditableCustomProvider, }); const incompleteAuthHint = !showApiKeyAuth && oauthAuthMethods.length > 0 ? t('settings.providers.page.auth.useReconnectHint') diff --git a/packages/ui/src/components/sections/providers/providerAuth.ts b/packages/ui/src/components/sections/providers/providerAuth.ts index 7cc7b5ac..cff0ca5b 100644 --- a/packages/ui/src/components/sections/providers/providerAuth.ts +++ b/packages/ui/src/components/sections/providers/providerAuth.ts @@ -94,7 +94,17 @@ export const shouldShowModelsSection = (input: { modelCount: number; sourcesLoaded: boolean; hasCredentials: boolean; -}): boolean => input.modelCount > 0 && (!input.sourcesLoaded || input.hasCredentials); + /** + * Config-defined custom providers (providerSources.custom present and parsed + * via `isConfigDefinedCustomProvider`) are user-editable in place, so a + * stale `Credentials missing` signal must not hide their models section. + * Optional for back-compat; defaults to `false`, restoring the pre-rewrite + * exemption that `requiresProviderAuth` carried via `providerAvailability.ts`. + */ + isEditableCustomProvider?: boolean; +}): boolean => + input.modelCount > 0 && + (!input.sourcesLoaded || input.hasCredentials || Boolean(input.isEditableCustomProvider)); export const shouldAutoOpenAuthPanel = (input: { sourcesLoaded: boolean;