# CPA Companion (cpac)

CPAC 将 Codex 和 Claude Code 接入远端 CLIProxyAPI（CPA）：

- **Codex**：注入模型目录和 loopback 网关，保留内置 `openai` provider。无用户改动时逐字节恢复原配置；注入期间改过文件则只撤掉 CPAC 字段。
- **Claude Code**：通过临时环境变量启动，不修改 Claude Code 配置。

实现基于 TypeScript + Node.js，运行时零依赖。

## 要求

- [Node.js](https://nodejs.org/) 20 或更新版本（npm 随 Node 安装）
- 已安装需要使用的 agent：`codex`、`claude`、`pi`、`kimi`、`grok`、`zed` 或 `hermes`
- 有权访问的 CPA API key

## 安装

从 npm 安装后：

```bash
npm install -g @yhong91/cpac
cpac
```

## 快速上手

```bash
# 1. 安装
npm install -g @yhong91/cpac

# 2. 配置密钥：无参数运行进入交互引导，自动写入 shell 启动文件；
#    也可手动写入（zsh 示例）：
cpac
echo 'export CPA_API_KEY="<你的key>"' >> ~/.zshrc && source ~/.zshrc

# 3. 验证
cpac status

# 4a. 持久注入（长期生效，写入 agent 配置，可完整恢复）
cpac install --all                     # 注入所有检测到的目标（codex/pi/kimi/grok/zed/hermes）
cpac install codex                     # 只注入 Codex（默认开启 multi-agent v2）
cpac install codex --v2_models         # 先多选 spawn_agent 模型再注入
cpac install codex --max_context       # 注入时把模型上下文提升到实测上限（opt-in，仅限 codex）
cpac restore codex                     # 撤掉注入；注入期间的用户改动会留下

# 4b. 临时接管（退出无痕，不写任何配置文件）
cpac claude                            # Claude Code 走 CPA

# 5. 可选：Agent 配置向导与模型选择（只写配置，不启动）
cpac setup codex                       # Codex 配置向导 → 注入
cpac setup claude                      # Claude 模型向导
cpac claude setup                      # 配置完再启动（agent 下的 setup 才会 launch）

# 6. 升级（装上新版本后自动 sync 已接入的 agent）
cpac upgrade
```

各命令的完整参数与行为见下方对应章节。

## Agent 自动化安装指南

本节面向 AI agent：按顺序执行即可完成安装与配置，**全程无交互**。注意：`cpac codex --config` 和 `cpac claude --config` 的交互向导是 TTY-only，agent 必须走下方直接写配置 JSON 的路径。

1. **检查前置**：`node --version` ≥ 20；确认用户需要哪些 agent（`codex`/`claude`/`pi`/`kimi`/`grok`/`zed`/`hermes` 在 PATH 上）。向用户索取 CPA API key。

2. **安装与密钥**：

```bash
npm install -g @yhong91/cpac
echo 'export CPA_API_KEY="<key>"' >> ~/.zshrc   # bash 用 ~/.bash_profile 或 ~/.bashrc
source ~/.zshrc
cpac status   # 验证：CPA_API_KEY 应显示 configured
```

1. **写入模型配置**（创建或合并 `~/.config/cpac/config.json`，已有键保留）：

```json
{
  "spawn_models": ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", "gemini-3.7-flash-high", "grok-4.6"]
}
```

   Claude 槽位（opus/sonnet/haiku）通常不用配：cpac 启动时自动选择——同家族 claude 模型优先，否则 gemini → grok → luna → 目录第一个；opus 无匹配则不设。这个链条在常见目录上正好给出理想结果：sonnet 落在 claude-sonnet-*（它同时充当 Auto mode 分类器的替身，避免分类请求失败后回退到昂贵的会话主模型），haiku 落在快的 gemini flash。只有目录奇怪时才用 `cpac setup claude` 交互式配置。codex spawn subagent 取三个 GPT + gemini + grok 五席（顺序即优先级，Codex 只广告 catalog 前 5），可通过 `cpac setup codex` 交互式配置。候选替换：`mimo-v2.5`（haiku）、`grok-4.5`（haiku）。若上述 slug 不在目录中（`curl -s -H "Authorization: Bearer $CPA_API_KEY" "$CPA_BASE_URL/v1/models"` 可查），按家族就近替换。分类器机制细节见 [docs/claude-auto-mode-classifier.md](docs/claude-auto-mode-classifier.md)。

1. **持久注入**（只对检测到的目标；codex 会应用步骤 3 的 spawn_models）：

```bash
cpac install --all
cpac status   # 验证：各 target 显示 ready/injected
```

1. **统一启动与配置**：
   - 启动：`cpac <codex|claude|kimi|grok|pi|zed|hermes> [args...]`（所有原生参数如 `-c` 等原样透明传递）
     - `setup` / `--setup`：配置 Codex/Claude，然后启动
     - `clear`：还原原生配置
   - 只写配置、不启动：`cpac setup [claude|codex]`；查看当前：`cpac setup`

2. **已安装后升级**：npm 发布新版本后，等 publish 成功再执行 `cpac upgrade`。它会按需 `npm install -g`，再对已接入的 Codex / Pi / Kimi / Grok / Zed / Hermes 做 `sync`。不要拆成 `npm install -g @yhong91/cpac` 再手动 `cpac sync`。只刷新配置、不升 CLI 时才用 `cpac sync`。

失败处理：任何一步报错时先跑 `cpac status`；密钥未配置回到步骤 2；注入后异常用 `cpac <agent> clear` 或 `cpac restore <codex|pi|kimi|grok|zed|hermes>` 恢复原配置。

## 首次引导

无参数运行 `cpac` 会显示当前 CPA、密钥状态和可用命令：

```text
CPA Companion

CPA: http://124.223.178.52:8317
CPA_API_KEY: configured / not configured

Commands:
  cpac <agent> [args...] Launch an agent through CPA
  cpac setup            Show current Codex/Claude setup
  cpac status            Show agent support status
  cpac models            List remote CPA catalog models
  cpac restore <codex|pi|kimi|grok|zed|hermes> | --all  Detach injected agents
  cpac --help            Show command usage
```

默认 CPA 地址内置为 `http://124.223.178.52:8317`，不需要创建 `~/.config/cpac/`。

若 `CPA_API_KEY` 尚未设置，交互式引导会隐藏输入密钥，并将受 CPAC 标记管理的 export 区块写入当前 shell 的启动文件：

| Shell | 写入位置 |
| --- | --- |
| zsh | `~/.zshrc` |
| bash（macOS） | `~/.bash_profile` |
| bash（Linux 等） | `~/.bashrc` |
| sh / dash / ksh | `~/.profile` |

写入内容形如：

```bash
# >>> CPAC CPA_API_KEY >>>
export CPA_API_KEY='...'
# <<< CPAC CPA_API_KEY <<<
```

CPAC 会保留启动文件中的其他设置；再次运行引导配置密钥时，受管区块会原位更新，不会重复追加。写入后按引导提示打开新终端，或在当前终端执行：

```bash
source ~/.zshrc # 使用引导实际显示的文件
```

程序无法直接修改已经运行的父 shell，所以 `source` 或新终端仍然是必要的。

也可以跳过引导，自行设置：

```bash
export CPA_API_KEY='...'
```

## 命令

### 引导和帮助

```bash
cpac
cpac --help
```

### 统一管理（detect / install / sync / restore / uninstall / upgrade）

参考 vibetime 的目标式命令，对 `codex`、`pi`、`kimi`、`grok`、`zed`、`hermes` 六个持久注入目标统一管理（claude 是临时接管，用 `cpac claude` 启动，不属于 install 目标）：

```bash
cpac detect [--json]
cpac models [--json]
cpac install [codex|pi|kimi|grok|zed|hermes...] [--all] [--dry-run] [--force] [--v2_off] [--v2_models] [--max_context]
cpac sync [codex|pi|kimi|grok|zed|hermes...] [--all] [--dry-run] [--v2_off] [--v2_models] [--max_context]
cpac restore <codex|pi|kimi|grok|zed|hermes...> | --all [--dry-run]
cpac uninstall [--dry-run]
cpac upgrade [--check]
cpac -v | --version
```

- `models` 从远端 CPA 拉取 rich catalog，默认一行一个 slug；`--json` 输出 `cpa_url` 和模型字段（`slug` / `display_name` / `context_window`）。用来核对 catalog 里有没有某个模型（例如 `*-fast`），不写本地配置。
- `detect` 列出每个目标是否被检测到、是否已接入 CPAC；`--json` 输出机器可读结果。
- `install` 默认作用于检测到的目标；后面直接写 agent 名（`cpac install codex pi`），`--all` 全部。`codex` 写入 catalog 并拉起 loopback 代理；`pi`/`kimi`/`grok`/`zed`/`hermes` 写入各自配置。`--dry-run` 只打印将要做的事，`--force` 允许重装已安装目标。
- `sync` 按当前 `cpa_url` 和 CPA 目录强制刷新**已经接入**的目标（Codex 重新注入、Pi / Kimi / Grok / Zed / Hermes 配置重写），用来在 CPA 地址或模型目录变化后一次对齐本地配置。不带参数只动已安装目标；`--all` 会对检测到的目标做同样的强制刷新（尚未安装的会装上）。`--max_context` 仍只对 `cpac install codex` 有效。
- Codex 注入默认开启 multi-agent v2（写入 `[features.multi_agent_v2] enabled = true`，catalog 行打 `multi_agent_version: "v2"`）；`install codex --v2_off` 关掉 toml 并去掉 catalog 戳。v2 开着时会剥掉 `[agents] max_threads`（Codex 否则拒启动）。`cpac status` 对照 config 里的 `v2_off` / `max_context` 和 live toml / catalog。服务端还需在 CPA 配置中开启 `codex.optimize-multi-agent-v2`。
- `--v2_models` 进入 arrow-key 复选框选择最多 5 个 spawn_agent 模型（勾选顺序即推荐顺序），选择结果存入配置文件的 `spawn_models`，注入时把这些模型排到 `codex-models.json` 最前——Codex 客户端只把 catalog 前 5 个可见模型广告为 spawn 覆盖项。
- `restore` 必须带 agent 名或 `--all`，卸掉指定（或全部）已注入目标。`--all` 时未安装的目标静默跳过。
- `uninstall` 先用和 `cpac setup codex` 相同的方向键选择器确认（默认 Cancel），再 `restore --all`，最后 `npm uninstall -g @yhong91/cpac`。不要跟 agent 名；卸载的是 cpac 包本身。非终端取消。`--dry-run` 跳过确认。
- `upgrade` 是发布后更新本机的一次性入口：查 npm → 需要时 `npm install -g` → 对**已接入**的 Codex / Pi / Kimi / Grok / Zed / Hermes 执行 `sync`。已是最新也会 sync。不要先手动 `npm install -g` 再 `cpac sync`。`--check` 只报告版本，不安装也不 sync。没有已安装目标时跳过 sync，不报错。只刷新配置、不升 CLI 时用 `cpac sync`。

### Claude Code

通过 CPA 启动 Claude Code，后续所有原生参数原样传递（如 `-c` 继续会话、`--model` 等）：

```bash
cpac claude
cpac claude -c                         # 继续上一次对话（原生 -c 原样直通）
cpac claude --model claude-sonnet-4-5
cpac claude -- -p "检查当前项目"
```

#### 配置与清除

```bash
cpac setup claude                      # 交互式依次配置 Sonnet / Haiku / Opus（不启动）
cpac claude setup                      # 同上，完成后启动
cpac claude clear                      # 重置所有覆盖，恢复自动选择
```

未设置的槽位启动时自动选择：claude 池内同家族模型优先，否则按 gemini → grok → luna → 目录第一个回退；opus 无匹配则不设。

所有设定写入配置文件持久缓存，每次 `cpac claude` 启动自动生效。

`--opus` / `--sonnet` / `--haiku` 对应 `ANTHROPIC_DEFAULT_*_MODEL`（haiku 同时写入 `ANTHROPIC_SMALL_FAST_MODEL`）。旧版的 `--classifier` 已移除：`CLAUDE_CODE_AUTO_MODE_MODEL` 在 Claude Code 2.1.224 上不被读取，Auto mode 分类器由官方自选（默认 Sonnet 5）；现版本唯一能影响分类器替身的旋钮是 `--sonnet`（副作用：同时改 `sonnet` 别名）。完整选择顺序、回退规则与出处见 [docs/claude-auto-mode-classifier.md](docs/claude-auto-mode-classifier.md)。

你自己 export 同名环境变量时以你为准。

Claude Code 的发现协议不携带 context 信息，claude 前缀的未知模型默认按 200K 记账，且 `CLAUDE_CODE_MAX_CONTEXT_TOKENS` 对 claude 前缀的名字不生效。cpac 采用与 opencodex 相同的机制：目录里窗口足够大的别名模型会带 `[1m]` 标记出现在选择器中（Claude Code 按 1M 记账，发请求前自己剥掉标记），同时 `cpac claude` 注入 `CLAUDE_CODE_AUTO_COMPACT_WINDOW`（默认 350K，与 opencodex 一致，接受范围 100K–1M）作为压缩阈值。你自己 export 该变量时以你为准。claude 系模型如需 1M 窗口，在模型名后加 `[1m]`（如 `cpac claude --model claude-sonnet-4-6[1m]`）。

> CPA 服务端必须支持 Claude Code 使用的 Anthropic Messages API（`/v1/messages`）。

### Codex

启动 Codex，后续所有原生参数原样传递（如 `-c` 动态覆盖配置、`exec` 等）：

```bash
cpac codex                             # 自动确保注入和代理后拉起 Codex
cpac codex -c model="gpt-5"            # 动态覆盖 Codex 配置（原生 -c 原样直通）
cpac codex exec "帮我写一个测试"
```

#### 配置与清除

```bash
cpac setup codex                       # 交互式配置后注入（不启动）
cpac codex setup                       # 同上，完成后启动 Codex
cpac codex clear                       # 还原 Codex 原生配置并关闭代理
```

#### 注入（`cpac install codex`）

```bash
cpac install codex [--v2_off] [--v2_models] [--max_context]
cpac restore codex
```

`install codex` 从远端 `/v1/models?client_version=1` 获取 Codex rich catalog，并启动只监听 `127.0.0.1` 的轻量转发代理。Codex 配置保留内置 `openai` provider，只写入根级 `model_catalog_json` 和：

```toml
openai_base_url = "http://127.0.0.1:10101/v1"
```

Codex App/CLI 发给本地代理的 ChatGPT bearer 不会转发到 CPA；代理会改用 `CPA_API_KEY`，并将请求和流式响应原样转发到远端 CPA。这保留了 Codex App 的原生 `openai` provider 身份、历史和账号相关界面。已有用户自定义根级 `openai_base_url` 或非 `openai` 的活动 `model_provider` 时，CPAC 会拒绝覆盖。

Codex App 的长驻 `app-server` 可能缓存旧目录。注入会删除 `models_cache.json`；如果选择器仍未更新，请重启 Codex App。

注入命令可选 `--max_context`（`cpac install codex --max_context`，默认不带）：把 catalog 中 `max_context_window` 高于 `context_window` 的模型提升到上限，并把 `auto_compact_token_limit` 设为上限的 90%。Codex 把 `context_window` 当输入预算而非展示标签，上游默认保留保守运营值（如 GPT-5.6 家族 272k），带上该参数后可用到实测上限（约 921k，opencodex 实测）；90% 压缩线确保在硬上限前触发 auto-compact。不带参数注入的仍是原本上下文的目录。该参数仅限 codex，与其他 target 组合使用会直接报错。

`config.toml` 不存在时拒绝注入。当前是 native 时把原文件备份到 `state_dir/codex/config.toml`（覆盖更旧的 native 快照）；已注入后再装不覆盖备份。kimi/grok/zed/hermes/pi 同样：没有旧配置不写空备份，native 覆盖旧快照，已接入则保留。所有 target 的 restore 都按 Codex 规则：注入后记下文件 hash；相对注入未改则逐字节还原备份（没有备份则删除我们创建的文件）；注入后改过则只剥 CPAC 字段，用户改动留下。没有其它需要代理的 agent 时才关闭代理。

loopback 代理是 detached 用户进程，不安装系统服务。机器重启或进程意外退出后，`cpac status` 会报告 `loopback proxy stopped`；重新执行 `cpac install codex` 或 `cpac pi` / `cpac kimi` / `cpac grok` / `cpac zed` / `cpac hermes` 即可恢复。

默认路径：

```text
Codex 配置：$CODEX_HOME/config.toml，未设置 CODEX_HOME 时为 ~/.codex/config.toml
CPAC state：~/.cpac/state.json（公用 proxy 字段 + 已接入 agent 记录）
各 agent 配置备份：~/.cpac/<agent>/<原文件名>
  Codex  ~/.cpac/codex/config.toml
  Kimi   ~/.cpac/kimi/config.toml
  Grok   ~/.cpac/grok/config.toml
  Zed    ~/.cpac/zed/settings.json
  Hermes ~/.cpac/hermes/config.yaml
  Pi     ~/.cpac/pi/models.json
restore / `<agent> clear` 会删掉该 agent 的备份目录和 state.json 里的对应记录
```

为防止误删或覆盖，`state_dir` 不能是文件系统根目录、用户 home、系统临时目录，也不能包含 Codex 配置文件。

### Pi

```bash
cpac install pi
cpac pi
```

它在 `~/.pi/agent/models.json` 的 `providers.cpac` 写入 loopback `baseUrl`、占位 `apiKey`、`api: openai-responses`，以及 CPA catalog 的模型列表。每个模型的 `cost` 是 USD / 百万 token（input / output / cacheRead / cacheWrite）。注入时若 `~/.cpac/model-prices.json` 超过 24 小时会从 [models.dev](https://models.dev) 刷新（官方价优先，否则 OpenRouter；失败则用打包快照）；未知模型为 0。不改当前默认模型；切换用 `/model`。密钥由 loopback 注入。旧的 `npm:@yhong91/cpac` package 和 `extensions/pi-cpac.ts` 会在 install / restore 时删掉。

卸载：`cpac restore pi` 或 `cpac pi clear`。尊重 `PI_CODING_AGENT_DIR`。

### Kimi Code

写入 CPA provider 配置到 Kimi Code：

```bash
cpac kimi install
```

它在 `~/.kimi-code/config.toml` 追加一个由 CPAC 管理的块，注册 `cpac` OpenAI 兼容 provider，并把 CPA 目录里的每个模型映射为一个 `cpac/<模型名>` 条目。带 `supported_reasoning_levels` 的模型会写入 `capabilities`、`support_efforts` 和 `default_effort`，否则 Kimi 只给 Claude 名套内置 thinking profile，其它模型会停在 thinking off 且无法改 effort。`base_url` 指向共享 loopback 代理，因此密钥不落盘。install 会写入 `state.json` 并在需要时启动代理，不必先单独注入 Codex。`restore` 未改过注入文件则还原备份，改过则只剥 CPAC 块。卸载与状态：

```bash
cpac restore kimi
```

Kimi Code 配置目录尊重 `KIMI_CODE_HOME` 环境变量。首次写入前若 `config.toml` 已存在，会在 `state_dir/kimi/config.toml` 保留一份原始快照（只存最早版本），万一文件损坏可手动复制回去。

### Zed

写入 CPA 的 OpenAI-compatible provider 到 Zed Agent：

```bash
cpac install zed
cpac zed
```

它在 `~/.config/zed/settings.json` 的 `language_models.openai_compatible.cpac` 写入 `api_url`（指向共享 loopback 代理）和 CPA 目录里的 `available_models`。模型走 Responses API（`chat_completions: false`），与 Pi / Grok 一致。密钥不写进 settings：`cpac zed` 会注入占位 `CPAC_API_KEY`；从 Dock 打开 Zed 时在 Agent Settings 里给 `cpac` provider 填任意非空值（loopback 会换成真正的 `CPA_API_KEY`）。这是 Zed 内置 Agent 的 OpenAI-compatible provider，不是 Codex ACP（`codex-acp`）。卸载：

```bash
cpac restore zed
```

配置目录尊重 `ZED_CONFIG_DIR`（否则 `$XDG_CONFIG_HOME/zed` 或 `~/.config/zed`）。首次写入前若 `settings.json` 已存在，会在 `state_dir/zed/settings.json` 保留原始快照。

### Hermes Agent

写入 CPA 的自定义 OpenAI-compatible provider 到 Hermes：

```bash
cpac install hermes
cpac hermes
```

它在 default `~/.hermes/config.yaml` 以及每个 `~/.hermes/profiles/<name>/config.yaml` 的 `providers.cpac` 写入 loopback `base_url`/`api`、占位 `api_key`、`api_mode: responses`，以及 CPA catalog 的 slug 列表（`discover_models: false`）。不改当前默认模型；切换用 `hermes model` 或 `/model`。密钥由 loopback 注入，不写进 `.env`。新建 profile 后再跑一次 `cpac install hermes`（或 `cpac hermes`）即可写入。卸载：

```bash
cpac restore hermes
```

配置目录尊重 `HERMES_HOME`（Windows 默认 `%LOCALAPPDATA%\hermes`）。首次写入前若 default `config.yaml` 已存在，会在 `state_dir/hermes/config.yaml` 保留原始快照。profile 文件不单独备份，restore 只剥 `providers.cpac`。

## 环境变量

| 变量 | 默认值 | 作用 |
| --- | --- | --- |
| `CPA_API_KEY` | 无 | CPA Bearer key；可由 `cpac` 首次引导写入 shell 启动文件 |
| `CPA_BASE_URL` | `http://124.223.178.52:8317` | 覆盖内置 CPA 地址 |
| `PI_CODING_AGENT_DIR` | `~/.pi/agent` | 覆盖 Pi agent 目录（影响 `models.json` 写入位置） |
| `KIMI_CODE_HOME` | `~/.kimi-code` | 覆盖 Kimi Code 目录（影响 `cpac install kimi` 写入位置） |
| `ZED_CONFIG_DIR` | `~/.config/zed` | 覆盖 Zed 配置目录（影响 `cpac install zed` 写入位置） |
| `HERMES_HOME` | `~/.hermes` | 覆盖 Hermes 目录（影响 `cpac install hermes` 写入位置） |
| `CPAC_CONFIG` | `~/.config/cpac/config.json` | 指定可选 JSON 配置路径 |
| `CODEX_HOME` | `~/.codex` | Codex home 目录 |

CPAC CLI 可临时覆盖 CPA 地址：

```bash
CPA_BASE_URL='https://cpa.example.com' cpac claude
```

Pi 扩展只从进程环境读取 `CPA_BASE_URL`（默认内置地址）和 `CPA_API_KEY`；不读取 `config.json` 的 `cpa_url`，也不读用户 home 中的私有文件。

## 可选 JSON 配置

默认场景不需要配置文件，文件不存在时 CPAC 直接使用内置默认值，也不会创建目录。仅在需要覆盖 Codex 路径、state 目录或密钥变量名时创建配置，例如：

```json
{
  "cpa_url": "https://cpa.example.com",
  "api_key_env": "CPA_API_KEY",
  "codex_config": "~/.codex/config.toml",
  "codex_proxy_port": 10101,
  "state_dir": "~/.cpac"
}
```

支持字段：

- `cpa_url`：可选，绝对 `http(s)` URL，可带或不带 `/v1`
- `api_key_env`：可选，密钥环境变量名，默认 `CPA_API_KEY`
- `codex_config`：可选，Codex 配置路径
- `codex_proxy_port`：可选，本地 Codex 转发端口，默认 `10101`；设为 `0` 时自动选择空闲端口
- `state_dir`：可选，CPAC state 路径

使用自定义配置：

```bash
cpac install codex --config ./cpac.json
cpac claude --config ./cpac.json -- --model claude-sonnet-4-5
```

也可以设置：

```bash
export CPAC_CONFIG=/path/to/cpac.json
```

## 安全行为

- API key 不写入 CPAC JSON、Codex 配置、备份、state、catalog 或日志。
- Codex loopback 代理仅监听 `127.0.0.1`，拒绝非本地浏览器 Origin，并在内存中用 CPA key 替换入站 Authorization。
- 引导输入不回显，写入 shell 时会正确引用特殊字符。
- shell 启动文件和 Codex 配置均通过同目录临时文件原子替换。
- CPA 请求失败、catalog 无效或 key 缺失时，首次注入不会修改 Codex 配置。
- `restore` 只删除 CPAC 自有的 state、backup 和 catalog，不删除 `state_dir` 中的其他文件。
