Metadata-Version: 2.4
Name: teamcc-client
Version: 0.2.2
Summary: TeamCC client CLI and shared contracts
Author: TeamCC
License: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.27
Requires-Dist: watchdog>=4.0
Requires-Dist: keyring>=24.0
Requires-Dist: cryptography>=42.0
Requires-Dist: pydantic>=2.6

# Team-CC — Claude Code 多设备管理系统

用于管理多设备接入 Claude Code 的本地客户端 + 服务端系统。聚焦解决**多设备统一接入**、**身份鉴权**、**按设备消耗统计**三大问题。客户端内置本地 Proxy Agent（简易版 ClashX 内核），服务端作为代理池调度中心与 Token 托管中心。

**身份模型**：Claude Auth Token 由**管理员在后台统一录入并分配**给设备（服务端加密托管）。设备登录 TeamCC 后客户端自动拉取被分配的 Token 并配置 Claude Code，**免授权自动登录**——设备使用者不参与任何 Anthropic 授权流程，客户端也不唤起浏览器。管理员可**一键撤销**（解绑/替换 Token 并使会话失效）。

> 本仓库是**第一期（iteration 1）**实现：服务端功能完整可用；客户端核心逻辑（鉴权、settings.json 生命周期、本地代理、消耗统计）完整，CLI 驱动。出站层采用**接口抽象 + HTTP/SOCKS 透传**，Xray-core 与图形界面/托盘留作后续迭代（见下方「范围与桩」）。

## 目录结构

```
teamcc_shared/      # 服务端 / 客户端共享的 API 契约（Pydantic 模型 + 计价表）
teamcc_server/      # FastAPI 服务端（MySQL + SQLModel；可经 TEAMCC_DATABASE_URL 切其它）
  static/index.html #   管理后台单页前端（FastAPI 挂载于 /admin/）
  routers/          #   auth / config / usage / admin / pricing 路由
  proxy_pool.py     #   代理池分发
  claude_tokens.py  #   Claude Auth Token 池：录入/分配/解析（管理员托管）
  billing.py        #   计费：模型价格表(默认+覆盖) + 汇率，USD/CNY 折算
  security.py       #   bcrypt 密码、Fernet Token 加密、user_id 哈希
teamcc_client/      # 客户端
  auth_manager.py   #   会话：登录 / 静默续期 / 401 重试 / 注销
  settings_manager.py#  settings.json 备份-注入-恢复（原子写 + 脏数据恢复）
  proxy/            #   本地代理 agent、出站抽象（direct/http/socks5/xray桩）、连通性探测
  usage_reporter.py #   watchdog 监听 JSONL 日志，增量解析 token 上报
  lifecycle.py      #   启动注入 / 退出清理 编排
  cli.py            #   命令行入口
scripts/smoke_test.sh
```

## 普通用户如何使用（优先阅读）

如果你是日常使用 TeamCC 的设备用户，通常不需要关心服务端部署和数据库。

### 设备用户（推荐 npm CLI）

1. 安装 npm CLI 包（由管理员提供制品库地址）

```bash
npm i -g @teamcc/teamcc-cli
```

如需卸载并重新安装（例如升级/修复安装异常）：

```bash
# 卸载旧版本
npm uninstall -g @teamcc/teamcc-cli

# 重新安装最新版本
npm i -g @teamcc/teamcc-cli
```

2. 设置服务端地址（一次配置，后续复用）

```bash
teamcc config set server http://127.0.0.1:8000
```

3. 初始化托管 Python runtime（首次安装必需）

```bash
teamcc setup
```

4. 登录并开始使用

```bash
teamcc login --username demo --password demo123
teamcc start
teamcc usage --range today
```

说明：

- 设备用户不参与 Claude OAuth 授权。登录 TeamCC 后会自动拉取管理员分配的 Access Token。
- `teamcc setup` 只需首次执行一次；它会从官方 PyPI（`https://pypi.org/simple`）下载并安装 Python 客户端包。
- `teamcc start` 默认仅注入 `ANTHROPIC_AUTH_TOKEN`，不改 `HTTP_PROXY/HTTPS_PROXY`；如需启用 TeamCC 代理，请使用 `teamcc start --proxy`。
- 可选：如果你们公司要求走私有仓库，可设置 `TEAMCC_PIP_INDEX_URL`（主源）和 `TEAMCC_PIP_EXTRA_INDEX_URL`（补充源）覆盖默认配置。
- 如果提示未分配 Token 或额度耗尽，需要联系管理员处理。

### 管理员（Web 后台）

管理员使用浏览器访问后台：

- 地址：`http://127.0.0.1:8000/admin/`
- 默认账号：`admin / admin123`（仅开发演示）

管理员主要操作：

- 创建设备账号，分配/撤销 Access Token
- 在 Access Token 池中通过 OAuth Authorization Code 生成并入池
- 查看请求明细和用量统计，维护计费价格与汇率
- 设置用户额度，超额自动停用

## 本地开发与自建部署（给开发者）

```bash
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

# 0) 准备 MySQL（默认连接 root@127.0.0.1:3306/teamcc）
mysql -uroot -p -e "CREATE DATABASE IF NOT EXISTS teamcc CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
#   配置数据库密码（必须，否则会 1045 Access denied）。二选一：
cp .env.example .env && vi .env          # 推荐：写入 .env，服务端自动加载，之后无需每次设置
#   或当次设置环境变量：export TEAMCC_DB_PASSWORD=你的MySQL密码   # 本机示例：123
#   也可整条覆盖：export TEAMCC_DATABASE_URL="mysql+pymysql://user:pass@host:3306/teamcc?charset=utf8mb4"

# 1) 建表 + 种子数据（admin/admin123, demo/demo123, 一个 direct 节点）
python -m teamcc_server.seed

# 2) 启动服务端（127.0.0.1:8000；管理后台 /admin/，Swagger 文档 /docs）
python -m teamcc_server.main

# 3) 另开终端：客户端
source .venv/bin/activate
python -m teamcc_client.cli login -u demo -p demo123
python -m teamcc_client.cli status
python -m teamcc_client.cli start      # 探测 -> 注入 settings.json -> 起代理 -> 上报，Ctrl-C 退出并恢复
python -m teamcc_client.cli usage --range today   # 本人用量看板：按模型分列，USD+CNY（today/week/month/all）
```

## CLI（npm 形态，开发说明）

针对习惯 Claude Code 命令行体验的用户，仓库已提供 npm CLI 外壳：

- 包目录：`npm/teamcc-cli`
- 命令名：`teamcc`
- 工作方式：把 `teamcc ...` 转发到现有 Python CLI（`python -m teamcc_client.cli ...`）

本地试装：

```bash
cd npm/teamcc-cli
npm pack
npm i -g ./teamcc-teamcc-cli-0.1.0.tgz
```

安装后使用：

```bash
teamcc --help
teamcc login --username demo --password demo123
teamcc start
teamcc usage --range today
```

说明：

- 需 Python 3.10+。
- 建议在 TeamCC 仓库目录内运行；
  若不在仓库目录，设置 `TEAMCC_ROOT=/path/to/teamcc` 让 wrapper 定位 Python 模块。
- 服务端地址可通过 `TEAMCC_SERVER_URL` 覆盖（与 Python CLI 保持一致）。

## 种子账号（`python -m teamcc_server.seed` 创建，仅供开发/演示）

| 用户名 | 密码 | 角色 | 说明 |
|---|---|---|---|
| `admin` | `admin123` | 管理员 | 登录管理后台 `/admin/`，管理设备账号 / Token / 计费 / 额度 |
| `demo` | `demo123` | 设备账号 | 已预分配一个占位 Claude Auth Token，客户端可直接登录使用 |

> ⚠️ 这些是默认弱口令,**仅用于本地开发/演示**。生产环境请先在管理后台修改/删除,或改 `teamcc_server/seed.py` 后重新初始化。

> **数据库**：服务端用 **MySQL**（SQLModel/SQLAlchemy + PyMySQL 驱动）。连接参数走环境变量
> `TEAMCC_DB_HOST/PORT/USER/PASSWORD/NAME`（推荐写进 `.env`，自动加载），或用
> `TEAMCC_DATABASE_URL` 整条覆盖（也可填 `sqlite:///...` 退回 SQLite 跑测试）。字符串列已按
> MySQL 要求设定 VARCHAR 长度 / TEXT。
>
> **常见报错 `(1045, "Access denied for user 'root'@'localhost' (using password: NO)")`**：
> 说明没配数据库密码（默认空密码连接）。配置 `TEAMCC_DB_PASSWORD`（或 `.env`）即可。

## 管理后台（前端页面）

服务端内置一个**零构建的管理后台单页**（原生 HTML+JS，FastAPI 同源托管），浏览器打开
**`http://127.0.0.1:8000/admin/`**（根路径 `/` 会自动跳转）。用管理员账号（`admin/admin123`）登录后可：

- **用量看板**：按设备账号 / 模型 / 设备账号×模型（下钻）多维查询，时间范围筛选，USD + CNY 双币种；
  「按设备账号」视图可**展开每个设备账号的每日消耗**（最多 30 天），超出可一键跳到请求明细。
- **请求明细**：列出每一条请求的消耗，支持按**设备账号 / 日期 / 模型**筛选 + 分页。
- **计费价格**：查看默认价 + 覆盖、在线改单价、维护美元兑人民币汇率。
- **设备管理**：建设备账号、分配 / 撤销 Claude Token（一个 Token 绑定一个设备账号）、停用；
  **设置每个设备账号的 ¥ 消费额度**，并实时显示「已用 / 额度 / 正常·已停用」状态。
- **Token 池**：支持生成 Auth 登录链接（手动模式）+ 录入 Token（加密存储、列表脱敏）、删除。
- **代理池**：节点增删。
- **审计日志**：敏感操作记录。

## Claude OAuth（PKCE）流程说明

TeamCC 的 Token 入池支持管理员使用 OAuth Authorization Code + PKCE（S256）方式完成兑换。
当前实现采用“管理员发起、管理员完成、服务端入池”的模型：

1. 管理员在后台调用 `POST /api/admin/claude-auth/login-link` 生成登录链接。
2. 服务端生成一次性 `state` 与 PKCE 参数（`code_verifier` / `code_challenge`）。
3. 管理员在浏览器完成 Claude 授权，回调地址拿到 `code`（以及可选 `state`）。
4. 管理员把 `code`（和之前的 `code_verifier`）提交到 `POST /api/admin/claude-auth/exchange`。
5. 服务端使用 JSON 请求调用 Claude Token Endpoint 兑换 Access Token。
6. 兑换成功后，TeamCC 将 Access Token 加密保存到 Token 池（数据库脱敏展示），后续可分配给设备账号。

### TeamCC 当前 OAuth 关键配置

- Authorize URL：`https://claude.ai/oauth/authorize`
- Token URL：`https://api.anthropic.com/v1/oauth/token`
- Client ID：`9d1c250a-e61b-44d9-88ed-5944d1962f5e`
- Redirect URI（默认）：`https://platform.claude.com/oauth/code/callback`
- Token 交换请求格式：`application/json`

### `/api/admin/claude-auth/exchange` 请求体示例

```json
{
  "label": "team-subscription-auto",
  "code": "<authorization_code>",
  "code_verifier": "<pkce_code_verifier>",
  "state": "<optional_state>"
}
```

服务端会提交以下字段到 Token Endpoint：

```json
{
  "grant_type": "authorization_code",
  "client_id": "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
  "code": "...",
  "code_verifier": "...",
  "redirect_uri": "http://localhost:54545/callback",
  "state": "..."
}
```

### 参考实现来源

本项目在排查 Claude OAuth 交换参数时，参考了以下开源实现（仅用于协议行为对齐与互操作验证）：

- 仓库：https://github.com/router-for-me/CLIProxyAPI
- 关键点：
  - PKCE S256（`code_verifier` -> SHA256 -> `code_challenge`）
  - Token Endpoint 使用 `https://api.anthropic.com/v1/oauth/token`
  - 交换时使用 JSON 请求体并携带 `code_verifier`

注意：OAuth 相关参数与上游策略可能变化，生产部署请以 Anthropic 最新官方文档为准。

## 计费价格维度（与官方对齐）

管理后台「计费价格」已按 Claude 官方 API 定价维度展示与维护（单位：USD / MTok）：

- Base Input Tokens
- Cache Write（1h）
- Cache Hits & Refreshes（Cache Read）
- Output Tokens

当前 TeamCC 的 usage 上报结构中，缓存写入仅有 `cache_creation_input_tokens` 单一口径，
未区分 5m/1h 写入来源；因此服务端在自动计费时按 **1h Cache Write（高价）** 价格计算该字段。
如需精确按 5m/1h 分开核算，需要客户端上报额外维度后再做拆分计费。

客户端单元测试（pytest，hermetic：临时目录 + 文件态密钥库,不碰真实 keychain/网络）：

```bash
pip install -r requirements-dev.txt
pytest tests/ -v        # 覆盖 settings 注入恢复 / 凭证加密 / 鉴权续期 / 用量解析 / 代理转发 / lifecycle 编排
```

一键端到端自检：

```bash
source .venv/bin/activate
bash scripts/smoke_test.sh        # 服务端 API + 客户端会话/计费 全链路（curl + CLI）
```

管理后台 UI 冒烟/截图（Playwright + 系统 Chrome，无需下载浏览器内核）：

```bash
# 先起好服务端，再用装有 playwright 的解释器运行：
python3 scripts/ui_smoke.py --seed-data            # 登录→逐个 Tab→断言渲染→每页截图
python3 scripts/ui_smoke.py --headed --out /tmp/shots   # 可视化跑 / 自定义截图目录
```

`ui_smoke.py` 会：等待 `/healthz` 就绪 → （可选）经 REST 灌入示例设备账号/用量/节点 → 逐个 Tab
断言关键文案出现、捕获未捕获 JS 错误（favicon 等资源 404 仅告警不失败）→ 每页落 PNG；
任一视图渲染失败或有 JS 错误则**退出码非 0**，可用于 CI 回归门禁。

## 部署（Docker / Kubernetes）

服务端可容器化部署，镜像只装服务端依赖（`requirements-server.txt`，不含客户端的 watchdog/keyring）。

```bash
# 构建
docker build -t teamcc-server:latest .

# 运行（连本机 MySQL；生产请用 Secret 注入 JWT/Token 密钥）
docker run --rm -p 8000:8000 \
  -e TEAMCC_DB_HOST=host.docker.internal -e TEAMCC_DB_PASSWORD=123 \
  -e TEAMCC_JWT_SECRET=$(openssl rand -hex 48) \
  -e TEAMCC_TOKEN_KEY=$(python -c "from cryptography.fernet import Fernet;print(Fernet.generate_key().decode())") \
  teamcc-server:latest
# 浏览 http://127.0.0.1:8000/admin/
```

镜像要点：Python 3.12-slim、非 root（uid 10001）、`uvicorn --host 0.0.0.0`、内置 `/healthz` HEALTHCHECK；
密钥材料默认写在 `TEAMCC_DATA_DIR`（默认 `/data`），生产用 env/Secret 注入则不落盘。

**Kubernetes**：清单在 `deploy/k8s/`（Namespace / ConfigMap / Secret 模板 / Deployment / Service / Ingress + kustomization），
含就绪/存活探针、滚动更新、只读根文件系统、资源限额。一键部署见 [`deploy/k8s/README.md`](deploy/k8s/README.md)：

```bash
kubectl apply -f deploy/k8s/secret.yaml     # 由 secret.example.yaml 填好真实值（gitignore）
kubectl apply -k deploy/k8s/
```

> 多副本必须共享**稳定**的 `TEAMCC_JWT_SECRET`（变了所有会话失效）与 `TEAMCC_TOKEN_KEY`（变了已托管的
> Claude Token 无法解密）——务必放进 Secret，切勿让其每次随机生成。MySQL 由外部提供（清单不部署数据库）。
> 建表用启动时幂等 `create_all`（`IF NOT EXISTS`），多副本同时启动安全。

## 桌面客户端（Windows / macOS）

客户端除 CLI 外，提供 **PySide6 系统托盘桌面应用**（复用同一套 Lifecycle 核心：登录 / 启动探测注入 / 停止恢复 / 额度感知 / 退出清理）。

```bash
# 本地运行（开发）
pip install -r requirements-desktop.txt
python -m teamcc_client.gui
```

界面：登录窗（服务器地址 + 账号密码，回车登录）→ 主窗（Token 状态、¥额度 已用/剩余、代理状态、启动/停止/注销）→ 关闭窗口最小化到系统托盘后台静默；托盘菜单可显示/启动/停止/退出，退出自动恢复 settings.json。额度用尽或管理员撤销时自动停用并弹出托盘通知。

### 打包成可分发应用

用 PyInstaller 打包（**不能交叉编译**：Windows 包在 Windows 上打、macOS 包在 macOS 上打）：

```bash
pip install -r requirements-desktop.txt pyinstaller
pyinstaller packaging/teamcc-desktop.spec
# 产物：macOS -> dist/TeamCC.app ；Windows -> dist/TeamCC/TeamCC.exe
```

CI 一键出双平台产物：`.github/workflows/desktop.yml`（`workflow_dispatch` 或打 `v*` tag 触发，在 macos-latest / windows-latest 各自构建、自检并上传 artifact）。自检命令（无显示环境）：

```bash
QT_QPA_PLATFORM=offscreen python -m teamcc_client.gui --selftest   # 打印 SELFTEST_OK
```

> macOS 包设了 `LSUIElement`（菜单栏/托盘常驻、不占 Dock）。首次打开未签名应用需在「系统设置 → 隐私与安全性」放行；正式分发建议做代码签名/公证。

## 服务端 API

| 方法 & 路径 | 说明 | 鉴权 |
|---|---|---|
| `POST /api/auth/login` | 账号密码登录，签发 access/refresh | — |
| `POST /api/auth/refresh` | refresh 换取新 access | — |
| `POST /api/auth/logout` | 使 refresh token 失效 | — |
| `GET /api/me` | 当前设备账号身份 + `user_id_hash` + 是否已分配 Token | access |
| `GET /api/config` | 拉取分配节点 + 解密后的 Claude Auth Token | access |
| `POST /api/proxy/assign` | 故障切换时重新分配节点（不含 Token） | access |
| `POST /api/usage` | 批量上报消耗（含**模型名**维度，按 `request_id` 幂等去重） | access |
| `GET /api/usage/me` | **当前设备用量看板**：按模型分列（in/out tokens + 请求数 + USD/CNY），支持时间范围 | access |
| `GET /api/usage/summary` | **多设备用量看板**：`group_by=user\|model\|user_model` 多维聚合 + 下钻，USD/CNY 双币种 | admin |
| `GET /api/usage/daily` | 某设备账号**按天聚合**（≤30天，时区感知，超出 `truncated` 标记可下钻） | admin |
| `GET /api/usage/records` | **逐条请求明细**，按设备账号/模型/日期筛选 + 分页，USD/CNY | admin |
| `GET /api/admin/pricing` | 计费价格表（默认价 + 覆盖）+ 汇率 | admin |
| `PUT /api/admin/pricing/models` | 覆盖某模型单价（input/output 分别计价） | admin |
| `DELETE /api/admin/pricing/models/{key}` | 撤销覆盖，回落默认价 | admin |
| `PUT /api/admin/pricing/exchange-rate` | 维护美元兑人民币汇率 | admin |
| `POST /api/admin/claude-auth/login-link` | 生成 Claude Auth 登录链接（手动模式，含 state） | admin |
| `GET/POST/DELETE /api/admin/claude-tokens` | **录入/列出/删除** Claude Auth Token（列表中 Token 脱敏） | admin |
| `POST /api/admin/users` | 建设备账号（可同时分配 `assigned_token_id`） | admin |
| `POST /api/admin/users/{id}/assign-token` | 给设备账号**分配** Token（**一个 Token 只能绑定一个设备账号**，重复分配返回 409） | admin |
| `POST /api/admin/users/{id}/quota` | 设置设备账号 **¥ 额度**（`quota_cny`，null=不限）；超额自动停用 | admin |
| `POST /api/admin/users/{id}/revoke-token` | **一键撤销**：解绑 Token + 失效会话 | admin |
| `POST /api/admin/users/{id}/disable` | 停用设备账号 | admin |
| `GET/POST/DELETE /api/admin/proxies` `…/bulk` | 代理池 CRUD / 批量导入 | admin |
| `GET /api/admin/audit` | 审计日志 | admin |

## 安全设计落点

- **settings.json 不可破坏**：注入前**备份原始字节**，退出按原始字节**逐字节恢复**（保留设备本地原有缩进/键序/换行）；原文件不存在时恢复=删除我方创建的文件；原子写（临时文件 + `os.replace`）防止崩溃损坏；启动时脏数据检测优先恢复残留注入；注入后任一步失败自动完整回滚。
- **仅作用于 Claude Code**：代理地址只写入 settings.json 的 `env.HTTP_PROXY`，不改系统全局代理。
- **零信任**：代理只透传不解密，CONNECT 隧道内只计字节数，绝不读 Prompt / 回复；Token 统计来自 Claude Code 自己写的 JSONL 元数据。
- **凭证**：服务端 Fernet 加密托管 Claude Auth Token（管理员录入的池）；客户端会话 Token 加密落盘（密钥优先存 OS keyring，回落 0600 密钥文件），Claude Auth Token **只在内存**、退出即清；上报只带 `user_id_hash`，绝不带真实 Token。
- **消费额度（¥）与停用**：管理员可给每个设备账号设 `quota_cny` 人民币额度。累计消费（= 该设备账号
  `cost_cny` 之和）达到额度即**停用**：`GET /api/config` 返回 **402** 拒绝下发 Token；运行中的客户端
  在下一次用量上报的 `UsageAck` 中收到 `quota_exhausted=True`，**自动停止代理并恢复 settings.json**
  （停止由前台主循环执行，不在上报线程内调用，避免自锁）。管理员把额度调高于已用即解除停用；
  设为 null 即不限。客户端 `status` / `GET /api/me` 可见「已用 / 额度 / 剩余 / 是否停用」。
- **计费口径统一**：金额折算**只在服务端做**——按上报记录的模型 × token 数 × 单价得 USD，再 × 汇率得 CNY，并在**入库时按当时价格/汇率快照** `cost_usd`/`cost_cny`，因此管理员调价/改汇率**只影响其后**的记录、历史金额留存不变（符合 §4.3）。客户端与后台都展示服务端结果，口径一致。
- **请求幂等防重**：服务端按 `(user_id, request_id)` 执行幂等去重，避免同一请求被重复入库和重复计费；启动时会清理历史重复数据并建立唯一索引，防止并发/重复上报导致账单放大。
- **管理员兜底**：一键撤销（解绑设备账号 Token + 失效全部会话），所有敏感操作写审计日志。注：access token 是无状态 JWT，撤销后最长在其 TTL（默认 1 小时）内仍可通过校验；但 `GET /api/config` 会因 Token 已解绑而返回 409，客户端随即无法取得凭证并自清理。如需「立即」级撤销，可缩短 `TEAMCC_ACCESS_TTL_SECONDS` 或为 access token 增加吊销名单。

## 第一期范围与桩（替换点）

| 模块 | 现状 | 替换为 |
|---|---|---|
| 出站协议 | `direct` / `http` / `socks5` 真实可用；`vmess/vless/...` 走 `XrayOutbound` 桩，识别为「不支持」自动回落并告警 | 在 `proxy/outbound.py` 实现 `XrayOutbound`：拉起 Xray-core 进程 + 本地 socks 入站，再复用 `Socks5Outbound` |
| 连通性探测 | TCP/隧道层可达性（连上 `api.anthropic.com:443` 即通过） | 可加 TLS 握手 + 轻量 HTTP 探测 |
| 客户端形态 | CLI 驱动 | PySide6 托盘 GUI（登录窗、最小化托盘、退出清理） |
| 消耗统计主路径 | JSONL 监听（TLS 下无法解析隧道内 token，故以此为准） | 明文场景下的代理内存拦截 |

种子数据中管理员预录入了一个占位 Claude Auth Token 并分配给 `demo` 设备账号、代理池含一个 `direct` 透传节点，因此**无需真实 VPS、无需任何 Anthropic 授权即可端到端跑通**整套流程。

管理员录入并分配 Token 的示例：

```bash
# 录入一个 Claude Auth Token 到池
curl -X POST $URL/api/admin/claude-tokens -H "Authorization: Bearer $ADMIN" \
  -H 'content-type: application/json' -d '{"label":"team-key","token":"sk-ant-..."}'
# 分配给设备账号（token_id 来自上一步返回）
curl -X POST $URL/api/admin/users/$UID/assign-token -H "Authorization: Bearer $ADMIN" \
  -H 'content-type: application/json' -d '{"token_id":1}'
```

手动模式下先生成 Auth 登录链接（仅生成链接，不自动换取 token）：

```bash
curl -X POST $URL/api/admin/claude-auth/login-link -H "Authorization: Bearer $ADMIN"
```

可选环境变量（服务端）：

- `TEAMCC_CLAUDE_AUTH_LOGIN_URL_TEMPLATE`：登录链接模板（默认 `https://claude.ai/login?state={state}`）；若不含 `{state}`，服务端会自动追加 `state` 查询参数。
- 模板可用占位符：`{state}`、`{client_id}`、`{redirect_uri}`、`{response_type}`、`{scope}`、`{code_challenge}`、`{code_challenge_method}`、`{audience}`。
- `TEAMCC_CLAUDE_AUTH_CLIENT_ID`：可选，自动补齐 `client_id`。
- `TEAMCC_CLAUDE_AUTH_REDIRECT_URI`：可选，自动补齐 `redirect_uri`。
- `TEAMCC_CLAUDE_AUTH_RESPONSE_TYPE`：可选，默认 `code`。
- `TEAMCC_CLAUDE_AUTH_SCOPE`：可选，自动补齐 `scope`。
- `TEAMCC_CLAUDE_AUTH_CODE_CHALLENGE`：可选，自动补齐 `code_challenge`。
- `TEAMCC_CLAUDE_AUTH_CODE_CHALLENGE_METHOD`：可选，默认 `S256`。
- `TEAMCC_CLAUDE_AUTH_AUDIENCE`：可选，自动补齐 `audience`。
- `TEAMCC_CLAUDE_AUTH_STATE_TTL_SECONDS`：state 展示有效期（默认 `600` 秒，仅用于后台提示和审计）。
