fix(providers): keep declared env vars as a credential signal
Providers that read credentials from several environment variables (Bedrock, Azure, Vertex) never get a single resolved Provider.key from OpenCode, so dropping the env signal made them show "Credentials missing", auto-open the auth panel, and hide their models even when fully configured. providerHasCredentials takes an envDeclared input again, and both call sites in ProvidersPage pass whether the provider declares any non-empty env var name.
This commit is contained in:
@@ -77,8 +77,8 @@ describe('provider auth method helpers', () => {
|
||||
});
|
||||
|
||||
describe('provider credential state helpers', () => {
|
||||
test('providerHasCredentials ignores declared env names and requires key or auth source', () => {
|
||||
// Built-in catalog entry with env var names but no actual credential.
|
||||
test('providerHasCredentials requires key, options.apiKey, declared env, or auth source', () => {
|
||||
// Built-in catalog entry with no credential signal at all.
|
||||
expect(providerHasCredentials({ key: undefined, authSourceExists: false })).toBe(false);
|
||||
expect(providerHasCredentials({ key: '', authSourceExists: false })).toBe(false);
|
||||
expect(providerHasCredentials({ key: ' ', authSourceExists: false })).toBe(false);
|
||||
@@ -89,6 +89,14 @@ describe('provider credential state helpers', () => {
|
||||
expect(providerHasCredentials({ key: undefined, authSourceExists: true })).toBe(true);
|
||||
});
|
||||
|
||||
test('providerHasCredentials counts declared env vars for multi-variable providers', () => {
|
||||
// Bedrock/Azure/Vertex resolve credentials from several env vars, so
|
||||
// OpenCode never sets Provider.key for them; the declared env list is the
|
||||
// only signal that the provider is configured.
|
||||
expect(providerHasCredentials({ key: undefined, authSourceExists: false, envDeclared: true })).toBe(true);
|
||||
expect(providerHasCredentials({ key: undefined, authSourceExists: false, envDeclared: false })).toBe(false);
|
||||
});
|
||||
|
||||
test('providerHasCredentials treats options.apiKey as a usable credential', () => {
|
||||
// Config-defined providers ship provider.options to the client but never
|
||||
// reach Provider.key, so the only authoritative signal is options.apiKey.
|
||||
|
||||
@@ -53,6 +53,14 @@ import {
|
||||
type ProviderConfigScope,
|
||||
} from './custom-provider-form';
|
||||
|
||||
/**
|
||||
* Providers whose credentials come from several env vars (Bedrock, Azure,
|
||||
* Vertex) never get a single resolved `Provider.key` from OpenCode, so the
|
||||
* declared env list is the only signal that they are configured at all.
|
||||
*/
|
||||
const providerDeclaresEnv = (provider: { env?: string[] } | undefined): boolean =>
|
||||
Array.isArray(provider?.env) && provider.env.some((name) => name.trim().length > 0);
|
||||
|
||||
const formatCompactNumber = (value: number) => new Intl.NumberFormat(getCurrentIntlLocale(), {
|
||||
notation: 'compact',
|
||||
compactDisplay: 'short',
|
||||
@@ -340,6 +348,7 @@ export const ProvidersPage: React.FC = () => {
|
||||
key: provider?.key,
|
||||
authSourceExists: sources.auth.exists,
|
||||
optionsApiKey: (provider as { options?: { apiKey?: string | null } } | undefined)?.options?.apiKey ?? null,
|
||||
envDeclared: providerDeclaresEnv(provider),
|
||||
});
|
||||
const isEditableCustomProvider = Boolean(
|
||||
provider && isConfigDefinedCustomProvider(provider, sources)
|
||||
@@ -860,6 +869,7 @@ export const ProvidersPage: React.FC = () => {
|
||||
key: selectedProvider.key,
|
||||
authSourceExists: selectedSources?.auth.exists,
|
||||
optionsApiKey: (selectedProvider as { options?: { apiKey?: string | null } }).options?.apiKey ?? null,
|
||||
envDeclared: providerDeclaresEnv(selectedProvider),
|
||||
});
|
||||
const authStatusIncomplete = requiresProviderAuth(sourcesLoaded, hasCredentials, isEditableCustomProvider);
|
||||
const showModelsSection = shouldShowModelsSection({
|
||||
|
||||
@@ -74,11 +74,19 @@ export interface ProviderCredentialInput {
|
||||
* `packages/web/server/lib/walkthrough/DOCUMENTATION.md:134`.
|
||||
*/
|
||||
optionsApiKey?: string | null;
|
||||
/**
|
||||
* The provider declares environment variables it reads credentials from.
|
||||
* Multi-variable providers (Bedrock, Azure, Vertex) never resolve a single
|
||||
* `Provider.key` upstream, so without this signal they read as
|
||||
* "Credentials missing" even when fully configured.
|
||||
*/
|
||||
envDeclared?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer authoritative credential signals. Do not treat Provider.env length as
|
||||
* proof of credentials — that array is declared env var *names*, not values.
|
||||
* Prefer authoritative credential signals. Declared env vars are the weakest of
|
||||
* them — the array holds variable *names*, not values — but for providers whose
|
||||
* credentials span several env vars it is the only signal OpenCode exposes.
|
||||
*/
|
||||
export const providerHasCredentials = (input: ProviderCredentialInput): boolean => {
|
||||
if (typeof input.key === 'string' && input.key.trim().length > 0) {
|
||||
@@ -87,6 +95,9 @@ export const providerHasCredentials = (input: ProviderCredentialInput): boolean
|
||||
if (typeof input.optionsApiKey === 'string' && input.optionsApiKey.trim().length > 0) {
|
||||
return true;
|
||||
}
|
||||
if (input.envDeclared === true) {
|
||||
return true;
|
||||
}
|
||||
return input.authSourceExists === true;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user