mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-20 13:53:32 +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({
|
export async function getAnthropicClient({
|
||||||
apiKey,
|
apiKey,
|
||||||
maxRetries,
|
maxRetries,
|
||||||
@ -319,7 +337,7 @@ export async function getAnthropicClient({
|
|||||||
? null
|
? null
|
||||||
: usingOpenAICodex
|
: usingOpenAICodex
|
||||||
? OPENAI_OAUTH_DUMMY_KEY
|
? OPENAI_OAUTH_DUMMY_KEY
|
||||||
: apiKey || getAnthropicApiKey(),
|
: resolveAnthropicClientApiKey({ explicitApiKey: apiKey }),
|
||||||
authToken: isClaudeAISubscriber()
|
authToken: isClaudeAISubscriber()
|
||||||
? getClaudeAIOAuthTokens()?.accessToken
|
? getClaudeAIOAuthTokens()?.accessToken
|
||||||
: undefined,
|
: undefined,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user