Files
claude-code/docs/jira/MULTI-AUTH-DESIGN.md
unraid 8945f08708 feat: integrate fork work onto upstream main (squashed)
Squash-merge of feat/autofix-pr-test (69 commits) onto upstream/main
with -X ours strategy (upstream as authoritative for content conflicts).

Key features brought in from fork:
- LocalMemoryRecall + VaultHttpFetch tools (end-to-end wired)
- /local-memory, /local-vault, /memory-stores, /skill-store interactive panels
- /agents-platform, /schedule, /vault command scaffolding
- /login: switch / replace / remove of workspace API key
- statusline refactor (built-in status row, /statusline as info command)
- autofix-pr command + workflow

Conflict resolutions (upstream-wins):
- 10 .js command stubs kept from upstream (alongside fork's .ts implementations)
- src/components/BuiltinStatusLine.tsx accepted upstream's deletion
  (fork's wire-up references in StatusLine.tsx will be cleaned up next)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:58:26 +08:00

16 KiB
Raw Blame History

多 Auth 模式设计Workspace API key + 第三方 + 订阅 OAuth

日期2026-05-04 目标:让被隐藏的 /agents-platform /vault /memory-stores 命令在用户配置 workspace API key 后启用;同时让 fork 支持第三方 API provider(如 Cerebras / Groq / 阿里通义 / 自建 OpenAI 兼容 endpoint通过同一选择器接入。


1. Fork 现状盘点(不要从零起)

已有基础设施

模块 路径 功能
7 个 provider 流适配器 src/services/api/{claude,bedrockClient,gemini,grok,openai,...}.ts firstParty / bedrock / vertex / foundry / openai / gemini / grokCLAUDE.md 已记录)
Provider 选择器 src/utils/model/providers.ts 优先级modelType > 环境变量 > 默认 firstParty
API key auth 识别 src/cli/handlers/auth.ts:239 已读 ANTHROPIC_API_KEY env var + apiKeySource 字段
OAuth subscription auth src/utils/teleport/api.ts:181 prepareApiRequest() 拿 OAuth token + orgUUID已 work for /v1/code/triggers
Workspace API client 没实现4 个 P2 clientvault/agents/memory-stores/skill-store当前只走 OAuth
第三方 API key env vars CLAUDE.md 列了 OPENAI_API_KEY GEMINI_API_KEY GROK_API_KEY OPENAI_BASE_URL 用于聊天 endpoint 不是管理 endpoint
/login 命令 src/commands/login/* 已支持切 OAuth / API key 模式

不可逾越的约束

  1. 第三方 provider 永远没有 vault/agents/memory_stores 等价端点 — 这是 Anthropic 私有功能OpenAI/Gemini/Grok/Bedrock 没等价。所以"第三方支持"指的是聊天/推理 endpoint,不是管理 endpoint。
  2. workspace API key 只能调 Anthropic api.anthropic.com,与第三方 host 不通。
  3. 订阅 OAuth ≠ workspace API key,必须双轨并存(不强制用户选一个)。

2. 三层 auth plane 设计

                       ┌─────────────────────────────────────┐
   User CLI              用户输入 / 命令派发                   │
                       └────────┬────────────────────────────┘
                                │
                ┌───────────────┼─────────────────┐
                ▼               ▼                 ▼
      ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐
      │ 推理 endpoint│  │ 订阅 endpoint│  │ workspace endpoint│
      │ (聊天/补全)  │  │ /v1/code/*   │  │ /v1/agents       │
      │              │  │ /v1/sessions │  │ /v1/vaults       │
      │              │  │ ultrareview  │  │ /v1/memory_stores│
      │              │  │ /schedule    │  │ /v1/skills       │
      └──────┬───────┘  └──────┬───────┘  └────────┬─────────┘
             │                 │                   │
             ▼                 ▼                   ▼
   ┌─────────────────┐ ┌──────────────┐  ┌────────────────────┐
   │ Provider 选择器 │ │ Subscription │  │ Workspace API key  │
   │ ─────────────── │ │ OAuth bearer │  │ ────────────────── │
   │ firstParty (默)│ │ /login 拿到  │  │ ANTHROPIC_API_KEY  │
   │ bedrock        │ │ prepareApiReq│  │ (sk-ant-api03-*)   │
   │ vertex         │ │              │  │ console.anthropic  │
   │ foundry        │ │              │  │                    │
   │ openai (compat)│ │              │  │                    │
   │ gemini         │ │              │  │                    │
   │ grok           │ │              │  │                    │
   │ 第三方:        │ │              │  │ 第三方 workspace:  │
   │ - Cerebras     │ │              │  │ 不支持(这些 plane │
   │ - Groq         │ │              │  │ 是 Anthropic 私有)│
   │ - 通义/混元    │ │              │  │                    │
   │ - 自建 OpenAI  │ │              │  │                    │
   │   兼容 endpoint│ │              │  │                    │
   └────────────────┘ └──────────────┘  └────────────────────┘

3 个 auth plane 互不替换 — 用户可同时拥有

  • 推理 endpoint:每次 API call 都用,按 token 计费API key或包含在订阅
  • 订阅 endpoint:仅 /login 拿到 OAuth bearer 后能用,免费包含在订阅
  • workspace endpoint:管理 agent/vault/memory store 等"组织资源",只接受 workspace API keysk-ant-api03-*),独立计费

3. 实施方案(分 4 个 PR

PR-1Workspace API key 模式(让隐藏的 3 命令复活)

目标:用户设 ANTHROPIC_API_KEY=sk-ant-api03-* 后,/vault /agents-platform /memory-stores 启用。

改动文件

  • src/utils/teleport/api.tsprepareWorkspaceApiRequest(): { apiKey: string }

    export async function prepareWorkspaceApiRequest(): Promise<{ apiKey: string }> {
      const apiKey = process.env.ANTHROPIC_API_KEY?.trim()
      if (!apiKey) {
        throw new Error(
          'Workspace API key required. Set ANTHROPIC_API_KEY=sk-ant-api03-* (from https://console.anthropic.com/settings/keys). Subscription OAuth bearer cannot reach workspace endpoints.',
        )
      }
      if (!apiKey.startsWith('sk-ant-api03-')) {
        throw new Error('ANTHROPIC_API_KEY must start with sk-ant-api03- (workspace key, not subscription token).')
      }
      return { apiKey }
    }
    
  • 4 个 P2 client buildHeaders() 改:

    async function buildHeaders(): Promise<Record<string, string>> {
      const { apiKey } = await prepareWorkspaceApiRequest()
      return {
        'x-api-key': apiKey,
        'anthropic-version': '2023-06-01',
        'anthropic-beta': BETA_HEADER, // 各文件原值
        'content-type': 'application/json',
      }
    }
    
    • vault/vaultsApi.ts / memory-stores/memoryStoresApi.ts / agents-platform/agentsApi.ts / skill-store/skillsApi.ts
    • 注意:不再需要 x-organization-uuidAPI key 自带 org 路由)
  • 4 个 index.tsisHidden 为动态:

    isHidden: !process.env.ANTHROPIC_API_KEY, // 有 key 自动显示,无 key 隐藏
    
  • 4 个 __tests__/api.test.ts 改 mockmock prepareWorkspaceApiRequest 而非 prepareApiRequest断言 x-api-key header 而非 Authorization

测试:每个 client 加 1 测试确认 x-api-key header 被传 + 1 测试确认无 key 时抛清晰错。

估算500 行含测试1 个 PR。


PR-2第三方 API provider 注册框架

目标:让用户接 Cerebras / Groq / 通义 / 自建 OpenAI-compatible endpoint扩展现有 7-provider 列表为可注册。

关键观察fork 已有 CLAUDE_CODE_USE_OPENAI OPENAI_BASE_URL OPENAI_MODEL 模式(文档化),可直接接任何 OpenAI 兼容 endpoint含 Cerebras https://api.cerebras.ai/v1 和 Groq https://api.groq.com/openai/v1)。无需新代码 — 已 work。

真正缺的

  1. 配置文件 ~/.claude/providers.json 让用户存多个 provider 切换:
    {
      "providers": [
        { "id": "cerebras", "kind": "openai-compat", "baseUrl": "https://api.cerebras.ai/v1", "apiKeyEnv": "CEREBRAS_API_KEY", "defaultModel": "llama-3.3-70b" },
        { "id": "groq", "kind": "openai-compat", "baseUrl": "https://api.groq.com/openai/v1", "apiKeyEnv": "GROQ_API_KEY", "defaultModel": "llama-3.3-70b-versatile" },
        { "id": "qwen", "kind": "openai-compat", "baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1", "apiKeyEnv": "DASHSCOPE_API_KEY" },
        { "id": "deepseek", "kind": "openai-compat", "baseUrl": "https://api.deepseek.com/v1", "apiKeyEnv": "DEEPSEEK_API_KEY" }
      ],
      "default": "cerebras"
    }
    
  2. /provider 命令切换:/provider use cerebras → 设 CLAUDE_CODE_USE_OPENAI=1 OPENAI_BASE_URL=https://api.cerebras.ai/v1 然后重启。

改动文件

  • 新建 src/services/providerRegistry/loader.tsswitcher.ts__tests__/
  • 新建 src/commands/provider/index.ts + launchProvider.tsxInk picker 列 providerEnter 选)
  • 注册到主 COMMANDS

估算800 行1 个 PR。前提PR-1 先合(保持 commit 顺序)。


PR-3本地等价物无 workspace key 用户的兜底)

目标:没 workspace API key 的订阅用户也能用 vault/memory-stores 的核心功能(管 secret / 跨 session 持久化),通过 fork 本地实现。

  • /local-vaultaliases /lv /local-secret
    • 用 OS keychain@napi-rs/keyring)存 secretfallback ~/.claude/local-vault.enc.json AES-256-GCM
    • 子命令:list / set <key> <value> / get <key> / delete <key>
    • 命令名独立 — 与 /vaultworkspace不冲突
  • /local-memoryaliases /lm
    • 复用 fork 已有 src/services/SessionMemory/,扩展为多 store
    • 子命令:list / create <name> / store <name> <key> <value> / fetch <name> <key>

估算1000 行1 个 PR。P3 优先级(用户没明确要本地版,可跳过)。


PR-4/login UX 升级

目标:让 /login 让用户看清 3 个 auth plane 各自状态 + 一键配置。

UI 大约:

Anthropic auth status:
  ☑ Subscription (claude.ai)         pro plan
  ☐ Workspace API key                not set
       To enable /vault /agents-platform /memory-stores:
       1. Open https://console.anthropic.com/settings/keys
       2. Create a key (sk-ant-api03-*)
       3. Set ANTHROPIC_API_KEY=<paste>
       4. Restart Claude Code

Third-party providers:
  ✓ cerebras   (CEREBRAS_API_KEY set, 5 models)
  ☐ groq       (GROQ_API_KEY not set)
  ☐ qwen       (DASHSCOPE_API_KEY not set)

Press 1 to switch active provider, 2 to add a third-party, q to quit.

估算400 行1 个 PR。


4. 安全设计(每 PR 都要满足)

风险 缓解
API key 写到日志 sanitizeErrorMessage() 已实现mask sk-ant-* sk-* 等)— 4 个 P2 client 的 catch 块都已 reuse
API key 误传到第三方 endpoint switcher.ts 严格验证 apiKeyEnvbaseUrl 配对,配置文件加 schema 校验
OS keychain 不可用环境headless / CI local-vault 自动 fallback AES-256-GCM 加密文件,密码从 ~/.claude/local-vault.passphrasegitignore
用户误把订阅 OAuth 当 workspace key 配 prepareWorkspaceApiRequest() 检查 apiKey.startsWith('sk-ant-api03-'),不是的话明确报错

5. 实施顺序 + 测试

Step PR 工作量 测试 依赖
1 PR-1 workspace API key ~500 行 mock prepareWorkspaceApiRequest + 4 client 各 5 测试 + 1 集成
2 PR-2 provider registry ~800 行 loader.ts schema test + switcher.ts 4 测试 + provider 命令 8 测试 PR-1
3 PR-4 /login UI ~400 行 Ink render test 5 测试 PR-1 + PR-2
4 PR-3 local-vault / local-memory ~1000 行 keyring mock + crypto test 12 测试 无(独立可做)

:约 2700 行 + 60 测试4 个 PR。


6. 推荐先做哪个

最小 viable = PR-1 单做。

  • /vault /agents-platform /memory-stores 在用户配 workspace API key 后立即启用
  • 零破坏(无 key 时仍隐藏)
  • ~500 行可周末完成
  • 高优先级:直接解决用户当前痛点

P2 = PR-2(第三方 provider 切换)—— 第三方推理 endpoint 已 workCLAUDE.md缺的是注册管理 UI。

P3 = PR-4/login UI 升级)—— nice-to-have等前 2 个稳定后做。

P4 = PR-3(本地 vault/memory—— 用户没明确要,可跳。


7. 反向问题

  1. workspace API key 是否有 spending cap 用户配后会不会被恶意 prompt 大量调用? → fork 应在每次调用前 log 一次 estimated cost超阈值如 $1/call警告
  2. 订阅用户配 API key 后调聊天会优先用哪个? → 现有 prepareApiRequest() 优先 OAuthworkspace API key 仅用于 P2 管理 endpoint。需要在文档明确不混用
  3. Cerebras / Groq 等只能 OpenAI-compat 吗?还是 Anthropic-compat → 调研:截至 2026-05主要是 OpenAI Chat Completions 兼容Anthropic-compat 只有 Anthropic 自己 + Bedrock + Vertex
  4. 本地 vault 如何处理 git rotate → AES key 不进 git~/.claude/.local-vault-rotate-log 记录最近 rotation

报告作者Claude Opus 4.7 Codex 验证:完成 2026-05-04codex CLI v0.125.0


8. Codex 反馈合入

Q1 → CONFIRM

PR-1 header shape 正确。引用 https://platform.claude.com/docs/en/api/beta/agents/create + API Overview官方 /v1/agents 请求只需 Content-Type / anthropic-version / anthropic-beta: managed-agents-2026-04-01 / X-Api-Keyx-organization-uuidorg 由 server 在 response 里通过 anthropic-organization-id 返回)。采纳4 P2 client 删 x-organization-uuid 行

Q2 → EXPANDPR-2 兼容性风险)

PR-2 不只是 config UI。第三方"OpenAI 兼容"实际有差异,需要 per-provider 回归测试:

Provider 已知差异
DeepSeek reasoning_content 跨模式行为不一致thinking-only / thinking+tools / 普通fork 当前"always preserve reasoning_content"对 DeepSeek 需针对性测试
严格"兼容"endpoint 可能拒绝 stream_options: { include_usage: true } 和额外 thinking 字段 — 需要 graceful drop
Groq / Cerebras 主流 streaming + tool_calls 应该 OKfork 已支持),但要测试新模型名(如 Groq llama-3.3-70b-versatile

采纳PR-2 加一个 providerCompatMatrix.ts,每个 provider 配置允许传的 fieldswhitelist 模式而非 dump 全部)。

Q3 → EXPANDroute/header coupling 守卫)

主漏点不是 plane 共存,是 route/header 错配。Codex 验证:

  • ✓ 订阅 bearer 不会到 CerebrasgetOpenAIClient() 只读 OPENAI_* env
  • ⚠️ workspace key 可达 /v1/messages — 技术合法但 billing intent 惊喜用户以为只用订阅workspace key 也扣钱)

采纳:必加 3 个硬边界守卫

// src/services/auth/hostGuard.ts (新建)
export function assertWorkspaceHost(url: string): void {
  if (!url.startsWith('https://api.anthropic.com')) {
    throw new Error(`Workspace API key only callable to api.anthropic.com, got ${new URL(url).host}`)
  }
}
export function assertNoAnthropicEnvForOpenAI(): void {
  // OpenAI-compat client should never read ANTHROPIC_* — guard at construct time
  const leaked = Object.keys(process.env).filter(k => k.startsWith('ANTHROPIC_') && process.env[k])
  if (leaked.length > 0) {
    // not throw — just warn (user may still legit have workspace key)
    console.warn(`[OpenAI client] ANTHROPIC_* env vars present (${leaked.join(',')}) — these are NOT used by this provider; check intent`)
  }
}
export function assertSubscriptionBaseUrl(url: string): void {
  if (!url.startsWith('https://api.anthropic.com')) {
    throw new Error(`Subscription OAuth helpers must not use arbitrary base URL, got ${url}`)
  }
}

3 个 client 工厂调用入口处 invoke 这些 guard。

综合采纳总结

Codex 反馈 设计调整
header shape CONFIRM 直接采用,不改设计
PR-2 compat 新增 providerCompatMatrix.ts + per-provider 测试套
host guard 新增 src/services/auth/hostGuard.ts 三方法PR-1 立即用