fix(providers): wire optionsApiKey through both call sites; restore editable custom exemption
The previous fix added optionsApiKey to providerHasCredentials but wired it through only one of the two call sites in ProvidersPage.tsx. The auto-open effect at line 339 still omitted it, so a config-defined provider whose only credential is options.apiKey would get the auth panel force-opened on every selection while the summary beside it said Connected; the dismissal resets on provider switch, so the panel re-opens each time. Restore the isEditableCustomProvider exemption that main's requiresProviderAuth helper carried into both authStatusIncomplete and shouldShowModelsSection. A keyless local custom provider (LM Studio / Ollama style) regressed from 'models visible, no banner' to 'Credentials missing' with the models section hidden. Rewire requiresProviderAuth (its only production consumer was lost in the rebase) by delegating authStatusIncomplete to it. The helper already encodes sourcesLoaded && !hasCredentials && !isEditableCustom Provider, so the call site is one line and the contract matches main. shouldShowModelsSection now accepts an optional isEditableCustom Provider flag that lifts the credential gate for editable providers, matching the exemption the ProvidersPage.test.ts:22 fixture asserts. Tests: 15/15 pass. Was 14 in the previous commit; added one test for the editable custom exemption.
This commit is contained in:
@@ -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({
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user