From 05cbc0119e20ccf5fe25be1040c2bb0a4a0dc927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Thu, 16 Apr 2026 19:44:54 +0800 Subject: [PATCH] =?UTF-8?q?fix(desktop):=20=E6=BF=80=E6=B4=BB=E5=AE=98?= =?UTF-8?q?=E6=96=B9=E6=97=B6=E6=8A=8A=20CLI=20=E6=A0=87=E8=AE=B0=E4=B8=BA?= =?UTF-8?q?=20managed-OAuth=20=E9=81=BF=E5=85=8D=20403?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 桌面端激活"官方"后请求被服务端返回 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) --- src/server/services/conversationService.ts | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/server/services/conversationService.ts b/src/server/services/conversationService.ts index a30935f9..8d431077 100644 --- a/src/server/services/conversationService.ts +++ b/src/server/services/conversationService.ts @@ -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 } + 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) 选模式。当前进程要么