fix(provider): add recovery guidance for missing OpenAI API key (#616)
This commit is contained in:
committed by
GitHub
parent
41a86d05fa
commit
9419e8a4a2
@@ -3,6 +3,9 @@ import { afterEach, expect, test } from 'bun:test'
|
|||||||
import { getProviderValidationError } from './providerValidation.ts'
|
import { getProviderValidationError } from './providerValidation.ts'
|
||||||
|
|
||||||
const originalEnv = {
|
const originalEnv = {
|
||||||
|
CLAUDE_CODE_USE_OPENAI: process.env.CLAUDE_CODE_USE_OPENAI,
|
||||||
|
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
|
||||||
|
OPENAI_BASE_URL: process.env.OPENAI_BASE_URL,
|
||||||
CLAUDE_CODE_USE_GEMINI: process.env.CLAUDE_CODE_USE_GEMINI,
|
CLAUDE_CODE_USE_GEMINI: process.env.CLAUDE_CODE_USE_GEMINI,
|
||||||
GEMINI_API_KEY: process.env.GEMINI_API_KEY,
|
GEMINI_API_KEY: process.env.GEMINI_API_KEY,
|
||||||
GOOGLE_API_KEY: process.env.GOOGLE_API_KEY,
|
GOOGLE_API_KEY: process.env.GOOGLE_API_KEY,
|
||||||
@@ -20,6 +23,9 @@ function restoreEnv(key: string, value: string | undefined): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
restoreEnv('CLAUDE_CODE_USE_OPENAI', originalEnv.CLAUDE_CODE_USE_OPENAI)
|
||||||
|
restoreEnv('OPENAI_API_KEY', originalEnv.OPENAI_API_KEY)
|
||||||
|
restoreEnv('OPENAI_BASE_URL', originalEnv.OPENAI_BASE_URL)
|
||||||
restoreEnv('CLAUDE_CODE_USE_GEMINI', originalEnv.CLAUDE_CODE_USE_GEMINI)
|
restoreEnv('CLAUDE_CODE_USE_GEMINI', originalEnv.CLAUDE_CODE_USE_GEMINI)
|
||||||
restoreEnv('GEMINI_API_KEY', originalEnv.GEMINI_API_KEY)
|
restoreEnv('GEMINI_API_KEY', originalEnv.GEMINI_API_KEY)
|
||||||
restoreEnv('GOOGLE_API_KEY', originalEnv.GOOGLE_API_KEY)
|
restoreEnv('GOOGLE_API_KEY', originalEnv.GOOGLE_API_KEY)
|
||||||
@@ -71,3 +77,19 @@ test('still errors when no Gemini credential source is available', async () => {
|
|||||||
'GEMINI_API_KEY, GOOGLE_API_KEY, GEMINI_ACCESS_TOKEN, or Google ADC credentials are required when CLAUDE_CODE_USE_GEMINI=1.',
|
'GEMINI_API_KEY, GOOGLE_API_KEY, GEMINI_ACCESS_TOKEN, or Google ADC credentials are required when CLAUDE_CODE_USE_GEMINI=1.',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('openai missing key error includes recovery guidance and config locations', async () => {
|
||||||
|
process.env.CLAUDE_CODE_USE_OPENAI = '1'
|
||||||
|
process.env.OPENAI_BASE_URL = 'https://api.openai.com/v1'
|
||||||
|
delete process.env.OPENAI_API_KEY
|
||||||
|
|
||||||
|
const message = await getProviderValidationError(process.env)
|
||||||
|
expect(message).toContain(
|
||||||
|
'OPENAI_API_KEY is required when CLAUDE_CODE_USE_OPENAI=1 and OPENAI_BASE_URL is not local.',
|
||||||
|
)
|
||||||
|
expect(message).toContain(
|
||||||
|
'set CLAUDE_CODE_USE_OPENAI=0 in your shell environment',
|
||||||
|
)
|
||||||
|
expect(message).toContain('Saved startup settings can come from')
|
||||||
|
expect(message).toContain('.openclaude-profile.json')
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
|
import { resolve } from 'node:path'
|
||||||
import {
|
import {
|
||||||
getGithubEndpointType,
|
getGithubEndpointType,
|
||||||
isLocalProviderUrl,
|
isLocalProviderUrl,
|
||||||
resolveCodexApiCredentials,
|
resolveCodexApiCredentials,
|
||||||
resolveProviderRequest,
|
resolveProviderRequest,
|
||||||
} from '../services/api/providerConfig.js'
|
} from '../services/api/providerConfig.js'
|
||||||
|
import { getGlobalClaudeFile } from './env.js'
|
||||||
import {
|
import {
|
||||||
type GeminiResolvedCredential,
|
type GeminiResolvedCredential,
|
||||||
resolveGeminiCredential,
|
resolveGeminiCredential,
|
||||||
} from './geminiAuth.js'
|
} from './geminiAuth.js'
|
||||||
import { redactSecretValueForDisplay } from './providerProfile.js'
|
import { PROFILE_FILE_NAME, redactSecretValueForDisplay } from './providerProfile.js'
|
||||||
|
|
||||||
function isEnvTruthy(value: string | undefined): boolean {
|
function isEnvTruthy(value: string | undefined): boolean {
|
||||||
if (!value) return false
|
if (!value) return false
|
||||||
@@ -61,6 +63,17 @@ function checkGithubTokenStatus(
|
|||||||
return 'valid'
|
return 'valid'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getOpenAIMissingKeyMessage(): string {
|
||||||
|
const globalConfigPath = getGlobalClaudeFile()
|
||||||
|
const profilePath = resolve(process.cwd(), PROFILE_FILE_NAME)
|
||||||
|
|
||||||
|
return [
|
||||||
|
'OPENAI_API_KEY is required when CLAUDE_CODE_USE_OPENAI=1 and OPENAI_BASE_URL is not local.',
|
||||||
|
`To recover, run /provider and switch provider, or set CLAUDE_CODE_USE_OPENAI=0 in your shell environment.`,
|
||||||
|
`Saved startup settings can come from ${globalConfigPath} or ${profilePath}.`,
|
||||||
|
].join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
export async function getProviderValidationError(
|
export async function getProviderValidationError(
|
||||||
env: NodeJS.ProcessEnv = process.env,
|
env: NodeJS.ProcessEnv = process.env,
|
||||||
options?: {
|
options?: {
|
||||||
@@ -137,7 +150,7 @@ export async function getProviderValidationError(
|
|||||||
if (useGithub && hasGithubToken) {
|
if (useGithub && hasGithubToken) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return 'OPENAI_API_KEY is required when CLAUDE_CODE_USE_OPENAI=1 and OPENAI_BASE_URL is not local.'
|
return getOpenAIMissingKeyMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
|||||||
Reference in New Issue
Block a user