mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
Merge provider auth token header fix into main
Bring the bearer-token provider fix from the isolated worktree into the local main checkout without touching the existing unrelated desktop/session edits already present in main. Constraint: Local main had unrelated dirty files outside src/services/api Rejected: Rebase or reset main | would risk disturbing existing local changes Confidence: high Scope-risk: narrow Directive: Keep custom provider auth fixes in the CLI SDK path, not only the Provider Test fetch path Tested: Merge completed without conflicts Not-tested: Full main checkout verification after merge because unrelated dirty files remain
This commit is contained in:
commit
7b2b19369d
87
src/services/api/client.test.ts
Normal file
87
src/services/api/client.test.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { describe, expect, mock, test } from 'bun:test'
|
||||
|
||||
mock.module('src/utils/http.js', () => ({
|
||||
getAuthHeaders: mock(() => ({})),
|
||||
getMCPUserAgent: mock(() => 'client-test-agent'),
|
||||
getUserAgent: mock(() => 'client-test-agent'),
|
||||
getWebFetchUserAgent: mock(() => 'client-test-agent'),
|
||||
withOAuth401Retry: mock(async <T>(fn: () => Promise<T>) => fn()),
|
||||
}))
|
||||
|
||||
describe('resolveAnthropicClientApiKey', () => {
|
||||
test('does not inherit a local api key when a provider auth token is explicit', async () => {
|
||||
const { resolveAnthropicClientApiKey } = await import('./client.js')
|
||||
const getFallbackApiKey = mock(() => 'sk-keychain-fallback')
|
||||
|
||||
const apiKey = resolveAnthropicClientApiKey({
|
||||
envAuthToken: 'provider-bearer-token',
|
||||
envApiKey: undefined,
|
||||
getFallbackApiKey,
|
||||
})
|
||||
|
||||
expect(apiKey).toBeNull()
|
||||
expect(getFallbackApiKey).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('preserves an explicit api key when the caller opts into dual auth', async () => {
|
||||
const { resolveAnthropicClientApiKey } = await import('./client.js')
|
||||
const getFallbackApiKey = mock(() => 'sk-keychain-fallback')
|
||||
|
||||
const apiKey = resolveAnthropicClientApiKey({
|
||||
explicitApiKey: 'sk-explicit-api-key',
|
||||
envAuthToken: 'provider-bearer-token',
|
||||
getFallbackApiKey,
|
||||
})
|
||||
|
||||
expect(apiKey).toBe('sk-explicit-api-key')
|
||||
expect(getFallbackApiKey).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('falls back to the local api key when no provider auth token is present', async () => {
|
||||
const { resolveAnthropicClientApiKey } = await import('./client.js')
|
||||
const getFallbackApiKey = mock(() => 'sk-keychain-fallback')
|
||||
|
||||
const apiKey = resolveAnthropicClientApiKey({
|
||||
envAuthToken: undefined,
|
||||
envApiKey: undefined,
|
||||
getFallbackApiKey,
|
||||
})
|
||||
|
||||
expect(apiKey).toBe('sk-keychain-fallback')
|
||||
expect(getFallbackApiKey).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('getAnthropicClient', () => {
|
||||
test('passes bearer-token provider auth without an SDK api key', async () => {
|
||||
const { getAnthropicClient } = await import('./client.js')
|
||||
const originalAuthToken = process.env.ANTHROPIC_AUTH_TOKEN
|
||||
const originalApiKey = process.env.ANTHROPIC_API_KEY
|
||||
const originalSimple = process.env.CLAUDE_CODE_SIMPLE
|
||||
|
||||
process.env.ANTHROPIC_AUTH_TOKEN = 'provider-bearer-token'
|
||||
process.env.CLAUDE_CODE_SIMPLE = '1'
|
||||
delete process.env.ANTHROPIC_API_KEY
|
||||
|
||||
try {
|
||||
const client = await getAnthropicClient({
|
||||
maxRetries: 0,
|
||||
model: 'claude-sonnet-4-6',
|
||||
})
|
||||
|
||||
expect(client.apiKey).toBeNull()
|
||||
expect(client._options.defaultHeaders).toMatchObject({
|
||||
Authorization: 'Bearer provider-bearer-token',
|
||||
})
|
||||
} finally {
|
||||
if (originalAuthToken === undefined) delete process.env.ANTHROPIC_AUTH_TOKEN
|
||||
else process.env.ANTHROPIC_AUTH_TOKEN = originalAuthToken
|
||||
|
||||
if (originalApiKey === undefined) delete process.env.ANTHROPIC_API_KEY
|
||||
else process.env.ANTHROPIC_API_KEY = originalApiKey
|
||||
|
||||
if (originalSimple === undefined) delete process.env.CLAUDE_CODE_SIMPLE
|
||||
else process.env.CLAUDE_CODE_SIMPLE = originalSimple
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -91,6 +91,24 @@ function createStderrLogger(): ClientOptions['logger'] {
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveAnthropicClientApiKey({
|
||||
explicitApiKey,
|
||||
envAuthToken = process.env.ANTHROPIC_AUTH_TOKEN,
|
||||
envApiKey = process.env.ANTHROPIC_API_KEY,
|
||||
getFallbackApiKey = getAnthropicApiKey,
|
||||
}: {
|
||||
explicitApiKey?: string
|
||||
envAuthToken?: string
|
||||
envApiKey?: string
|
||||
getFallbackApiKey?: () => string | null
|
||||
}): string | null {
|
||||
if (envAuthToken && !explicitApiKey && !envApiKey) {
|
||||
return null
|
||||
}
|
||||
|
||||
return explicitApiKey || getFallbackApiKey()
|
||||
}
|
||||
|
||||
export async function getAnthropicClient({
|
||||
apiKey,
|
||||
maxRetries,
|
||||
@ -319,7 +337,7 @@ export async function getAnthropicClient({
|
||||
? null
|
||||
: usingOpenAICodex
|
||||
? OPENAI_OAUTH_DUMMY_KEY
|
||||
: apiKey || getAnthropicApiKey(),
|
||||
: resolveAnthropicClientApiKey({ explicitApiKey: apiKey }),
|
||||
authToken: isClaudeAISubscriber()
|
||||
? getClaudeAIOAuthTokens()?.accessToken
|
||||
: undefined,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user