fix(desktop): 激活官方时把 CLI 标记为 managed-OAuth 避免 403

桌面端激活"官方"后请求被服务端返回 403 "Request not allowed":
CLI 继承到的 env 里只要残留 ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN /
apiKeyHelper, isAnthropicAuthEnabled() 就会静默禁用 OAuth, 请求缺
oauth-2025 beta header。

conversationService.buildChildEnv 新增 shouldMarkManagedOAuth(): 只有
cc-haha/settings.json 没写 provider env (即"官方"模式) 时,才给子进程设
CLAUDE_CODE_ENTRYPOINT=claude-desktop, 让 CLI 忽略外部凭据强制走 OAuth。
自定义 provider 场景下不设, 避免 CLI 把 provider 的 AUTH_TOKEN 当作需忽略
的残留、错走 OAuth 打到第三方 endpoint。

详见 src/utils/auth.ts isManagedOAuthContext()。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
程序员阿江(Relakkes) 2026-04-16 19:44:54 +08:00
parent 4c4945c9a8
commit 05cbc0119e

View File

@ -505,6 +505,14 @@ export class ConversationService {
// should come from Desktop-managed config or inherited launch env, not
// be reintroduced from the repo's .env file.
CC_HAHA_SKIP_DOTENV: '1',
// "官方" 模式 (cc-haha/settings.json 没 provider env) 下,把 CLI 标记为
// managed-OAuth,让它忽略外部 ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN
// 残留、只走用户 /login 的 OAuth token。自定义 provider 模式绝不能设,
// 否则 CLI 会忽略 provider 的 AUTH_TOKEN、错误地走 OAuth 打到第三方
// endpoint。详见 src/utils/auth.ts isManagedOAuthContext()。
...(this.shouldMarkManagedOAuth()
? { CLAUDE_CODE_ENTRYPOINT: 'claude-desktop' }
: {}),
}
}
@ -537,6 +545,37 @@ export class ConversationService {
}
}
/**
* "官方"( provider), CLI
* managed-OAuth provider settings.json ANTHROPIC_AUTH_TOKEN;
* CLI token endpoint, managed
* OAuth
*
* ( settings.json) "官方" 使 cc-haha
* provider , OAuth
*/
private shouldMarkManagedOAuth(): boolean {
const configDir =
process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude')
const settingsPath = path.join(configDir, 'cc-haha', 'settings.json')
try {
const raw = fs.readFileSync(settingsPath, 'utf-8')
const parsed = JSON.parse(raw) as { env?: Record<string, string> }
const env = parsed.env ?? {}
const hasProviderEnv = [
'ANTHROPIC_API_KEY',
'ANTHROPIC_AUTH_TOKEN',
'ANTHROPIC_BASE_URL',
].some(
(key) =>
typeof env[key] === 'string' && env[key]!.trim().length > 0,
)
return !hasProviderEnv
} catch {
return true
}
}
private resolveBundledCliPath(): string | null {
// 桌面端 P0+P2 之后只有一个合并的 sidecar 二进制 —— `claude-sidecar`
// 它通过第一个 positional 参数 (server / cli) 选模式。当前进程要么