Files
cdxs/docs/account-management.md
T

11 KiB
Raw Blame History

账号管理

本文说明 cdxs 当前账号管理功能的实现方式、调用机制和运行过程。

功能范围

账号管理主要覆盖以下能力:

  • 从 Codex 原生 auth.json 导入账号。
  • 添加 API Key 账号。
  • 列出所有已保存账号。
  • 查看当前账号。
  • 查看指定账号详情。
  • 删除指定账号。
  • 将指定账号切换为当前账号并写入目标 auth.json

核心文件

  • src/main.rs:CLI 入口,负责把命令分发到具体模块。
  • src/cli.rs:定义账号相关命令和参数。
  • src/account.rs:账号管理主逻辑。
  • src/config_store.rscdxs.toml 的数据模型、加载、保存和查询。
  • src/auth_file.rsCodex 原生 auth.json 的读取和写入。
  • src/paths.rs:解析 CODEX_HOMEauth.jsoncdxs.toml 路径。
  • src/atomic.rs:写入配置或认证文件前备份,并使用原子写入。
  • src/jwt.rs:从 OAuth id_token 中解析邮箱、计划、组织等账号元数据。
  • src/token.rs:切换 OAuth 账号前按需刷新 token。

数据保存方式

cdxs 自己的账号状态保存在当前 CODEX_HOME 下的 cdxs.toml

<CODEX_HOME>\cdxs.toml

如果没有设置 CODEX_HOME,默认使用:

%USERPROFILE%\.codex

账号列表保存在 Store.accounts 中,当前账号 ID 保存在:

Store.meta.current_account_id

每个账号使用 Account 结构保存,关键字段包括:

  • id:稳定账号 ID。
  • email:显示用邮箱或 API Key 虚拟邮箱。
  • auth_modeoauthapi_key
  • tokensOAuth 账号的 token。
  • openai_api_keyAPI Key 账号的 key。
  • api_base_urlAPI Key 账号的可选 base URL。
  • plan_type:账号套餐类型。
  • account_idOAuth 账号 ID。
  • organization_idOAuth 组织 ID。
  • quota:最近一次配额查询结果。
  • requires_reauthOAuth refresh 失败后标记是否需要重新登录。

账号 ID 生成原理

账号 ID 由 account.rs 中的 stable_id 生成。

OAuth 账号使用以下信息生成稳定 ID:

  • 固定前缀:oauth
  • 邮箱
  • account_id
  • organization_id

API Key 账号使用以下信息生成稳定 ID:

  • 固定前缀:apikey
  • API Key
  • base_url

生成方式是对这些字段做 SHA-256,然后取前 16 位十六进制字符串:

oauth_xxxxxxxxxxxxxxxx
apikey_xxxxxxxxxxxxxxxx

这样重复导入同一个账号时,会更新原有记录,而不是创建重复账号。

命令调用机制

账号相关命令由 src/cli.rs 定义,再由 src/main.rs 分发到 src/account.rs

flowchart TD
    A[用户执行 cdxs 命令] --> B[Cli::parse]
    B --> C[src/main.rs match Commands]
    C --> D{账号相关命令}
    D -->|cdxs import auth| E[account::import_auth]
    D -->|cdxs account add-api-key| F[account::add_api_key]
    D -->|cdxs list / account list| G[account::list_accounts]
    D -->|cdxs account current| H[account::current_account]
    D -->|cdxs account show| I[account::show_account]
    D -->|cdxs account remove| J[account::remove_account]
    D -->|cdxs switch| K[account::switch_account]

导入 auth.json 运行过程

命令:

cdxs import auth

可选参数:

cdxs import auth --file <auth.json路径>
cdxs import auth --codex-home <路径>
cdxs import auth --switch

运行过程:

  1. 解析主配置 home,也就是保存 cdxs.toml 的位置。
  2. 解析来源 home,用于定位要导入的 auth.json
  3. 调用 auth_file::read_auth_file 读取并解析 auth.json
  4. 调用 account_from_auth 判断是 OAuth 账号还是 API Key 账号。
  5. OAuth 账号会解析 token,并从 id_token 中读取邮箱、计划、账号 ID、组织 ID。
  6. API Key 账号会读取 OPENAI_API_KEY 和可选 base URL。
  7. 生成稳定账号 ID。
  8. 调用 Store::upsert_account 写入或更新账号。
  9. 如果带 --switch,同时写入目标 home 的 auth.json,并设置当前账号。
  10. 调用 Store::save 保存 cdxs.toml
sequenceDiagram
    participant U as 用户
    participant M as main.rs
    participant A as account.rs
    participant P as paths.rs
    participant AF as auth_file.rs
    participant S as config_store.rs
    participant J as jwt.rs

    U->>M: cdxs import auth
    M->>A: import_auth(file, codex_home, switch)
    A->>P: codex_home(None)
    P-->>A: 配置 home
    A->>P: codex_home(codex_home)
    P-->>A: 来源 home
    A->>AF: read_auth_file(auth_path)
    AF-->>A: CodexAuthFile
    A->>A: account_from_auth
    alt OAuth 账号
        A->>J: decode_payload(id_token)
        J-->>A: email / plan / account_id / organization_id
        A->>A: oauth_account
    else API Key 账号
        A->>AF: extract_api_key / api_base_url
        AF-->>A: key / base_url
        A->>A: api_key_account
    end
    A->>S: Store::load
    S-->>A: Store
    A->>S: upsert_account
    opt --switch
        A->>AF: write_account_to_auth
        A->>A: 设置 current_account_id
    end
    A->>S: save
    S-->>U: 导入完成

添加 API Key 账号运行过程

命令:

cdxs account add-api-key --key sk-...

可选参数:

cdxs account add-api-key --key sk-... --base-url https://example.com/v1
cdxs account add-api-key --key sk-... --switch

运行过程:

  1. 解析当前 CODEX_HOME
  2. 加载 cdxs.toml
  3. 校验 API Key 非空。
  4. 根据 API Key 和 base URL 生成稳定账号 ID。
  5. 生成显示用邮箱,格式类似 api-key-xxxxxxxx
  6. 调用 Store::upsert_account 保存账号。
  7. 如果带 --switch,写入当前 home 的 auth.json
  8. 保存 cdxs.toml
flowchart TD
    A[cdxs account add-api-key] --> B[paths::codex_home]
    B --> C[Store::load]
    C --> D[api_key_account]
    D --> E[stable_id]
    E --> F[Store::upsert_account]
    F --> G{是否 --switch}
    G -->|是| H[auth_file::write_account_to_auth]
    G -->|否| I[跳过 auth.json 写入]
    H --> J[Store::save]
    I --> J

列出账号运行过程

命令:

cdxs list
cdxs account list

JSON 输出:

cdxs list --json
cdxs account list --json

运行过程:

  1. 解析当前 CODEX_HOME
  2. 加载 cdxs.toml
  3. 如果 --json,直接输出 store.accounts 的 JSON。
  4. 如果不是 JSON,按表格输出账号 ID、邮箱、认证模式、套餐和配额。
  5. 当前账号会用 * 标记。

查看当前账号运行过程

命令:

cdxs account current

JSON 输出:

cdxs account current --json

运行过程:

  1. 加载 cdxs.toml
  2. 读取 Store.meta.current_account_id
  3. 如果没有当前账号,输出未设置账号。
  4. 如果当前账号 ID 不存在,返回错误。
  5. 找到账号后输出详情或 JSON。

查看指定账号运行过程

命令:

cdxs account show <账号ID或邮箱前缀>

查找账号使用 Store::find_account,支持三种匹配方式:

  • 完整账号 ID。
  • 完整邮箱。
  • 邮箱前缀。

运行过程:

  1. 加载 cdxs.toml
  2. 调用 find_account 查找账号。
  3. 找不到则返回错误。
  4. 找到后输出详情或 JSON。

删除账号运行过程

命令:

cdxs account remove <账号ID或邮箱前缀>

运行过程:

  1. 加载 cdxs.toml
  2. 用账号 ID、邮箱或邮箱前缀查找账号。
  3. Store.accounts 中删除该账号。
  4. 如果该账号是当前账号,清空 current_account_id
  5. 如果有 home 绑定该账号,清空对应 bound_account_id
  6. 保存 cdxs.toml
flowchart TD
    A[cdxs account remove] --> B[Store::load]
    B --> C[Store::find_account]
    C --> D{是否存在}
    D -->|否| E[返回账号不存在错误]
    D -->|是| F[accounts.retain 删除账号]
    F --> G{是否当前账号}
    G -->|是| H[清空 current_account_id]
    G -->|否| I[保持 current_account_id]
    H --> J[清空 homes 中相关 bound_account_id]
    I --> J
    J --> K[Store::save]

切换账号运行过程

命令:

cdxs switch <账号ID或邮箱前缀>

可选指定目标 home

cdxs switch <账号> --codex-home <路径>

运行过程:

  1. 加载主配置 home 的 cdxs.toml
  2. 解析目标 home,用于确定要写入哪个 auth.json
  3. 用账号 ID、邮箱或邮箱前缀查找账号。
  4. 如果是 OAuth 账号,调用 token::refresh_account_if_needed 检查 access token 是否即将过期。
  5. 如果 token 需要刷新,则先刷新并更新 cdxs.toml 中的账号 token。
  6. 调用 auth_file::write_account_to_auth 把账号写入目标 home 的 auth.json
  7. 更新该账号的 last_used_at
  8. 设置 Store.meta.current_account_id
  9. 保存 cdxs.toml
sequenceDiagram
    participant U as 用户
    participant M as main.rs
    participant A as account.rs
    participant S as config_store.rs
    participant T as token.rs
    participant AF as auth_file.rs

    U->>M: cdxs switch <account>
    M->>A: switch_account
    A->>S: Store::load
    S-->>A: Store
    A->>S: find_account
    S-->>A: Account
    A->>T: refresh_account_if_needed
    alt OAuth token 即将过期
        T->>T: refresh_account
        T-->>A: 已更新 tokens
    else 不需要刷新或 API Key
        T-->>A: 无需刷新
    end
    A->>AF: write_account_to_auth
    AF-->>A: 写入 auth.json
    A->>A: 更新 last_used_at/current_account_id
    A->>S: save
    S-->>U: 切换完成

auth.json 写入规则

账号切换或带 --switch 保存账号时,会调用 auth_file::write_account_to_auth

OAuth 账号写入格式:

{
  "OPENAI_API_KEY": null,
  "tokens": {
    "id_token": "...",
    "access_token": "...",
    "refresh_token": "...",
    "account_id": "..."
  },
  "last_refresh": "..."
}

API Key 账号写入格式:

{
  "auth_mode": "apikey",
  "OPENAI_API_KEY": "sk-..."
}

写入安全机制

cdxs.tomlauth.json 写入时都使用同一套安全机制:

  1. 如果目标文件已存在,先备份到:
<CODEX_HOME>\cdxs-backups\
  1. 写入时先写到同目录临时文件:
.<原文件名>.tmp
  1. 再用 rename 替换目标文件。

这样可以降低写入中断导致配置文件损坏的风险。

当前实现边界

  • switch --apply-fingerprint 当前只输出提示,实际不会应用设备指纹。
  • API Key 账号没有 OAuth token,也不会参与 token refresh。
  • 账号查找支持邮箱前缀,但如果多个邮箱前缀相同,当前实现会返回第一个匹配项。
  • remove_account 只删除 cdxs.toml 中的账号记录,不会主动清理已经写入某个 home 的 auth.json