对话笔记直发:8.29 CPA插件迁移到独立2api服务并搭建统一看板。分类:AI工程。


date: 2026-08-29 tags:

  • jp-nas
  • docker
  • go
  • CLIProxyAPI
  • 2api
  • 面板开发
  • 运维

CPA 老插件迁移到三个独立 2api 服务 + 自研统一看板

背景

jp(局域网 NAS,IP 192.168.*.*,主机名 jp-tvbox-3566)上跑着 CLIProxyAPI(CPA,8317),里面装了两个老 Go 插件 qoderwork / workbuddy(来自 Sliverkiss/cpa-plugin)。插件是 .so,与 CPA 版本强绑定,一升级 CPA 就废。

于是提前把登录态迁到作者的两个独立轻量项目,另外再装一个新的:

  • workbuddy2api(7863)
  • qoderwork2api(7864)
  • traework2api(7865)

CPA 自带面板本来就关着(disable-control-panel: true),只保留后台服务 + 独立管理面板 CPA-Manager-Plus。

结论:迁移是「复制文件」,不用重新登录

关键发现:CPA 插件和三个独立项目的账号凭证是同构 JSON,都长这样:

{
  "auth":    {"accessToken":"…","refreshToken":"…","expiresAt":<unix秒>,"domain":"…"},
  "account": {"uid":"…","nickname":"…","enterpriseId":""}
}

发现方式也是按文件名前缀 glob:workbuddy*.json / qoderwork*.json / trae-*.json。 所以迁移 = 把 CPA auths/ 下对应文件拷到 /opt/<项目>/auths/,零重登。

执行步骤(按顺序)

  1. 先备份:/root/backups/20260829-cpa-migration/
    • cpa-auths.tar.gz(75 个文件,13 个插件账号)
    • config.yaml、docker-compose.yml、两个 .so
    • cpa-manager-data.tar.gz(46M 卷)、keys.txt、models-before.json
  2. 部署三个服务到 /opt/{workbuddy2api,qoderwork2api,traework2api}
    • 坑:容器内是 uid 10001,主机目录必须 chown -R 10001:10001 auths data,否则读写失败
    • 坑:qoderwork2api 的 compose 引用了不存在的外部网络 cpa-manager-plus_default,删掉
    • 坑:traework2api 默认也用 7864,改成 7865:7864
    • 每个项目生成 config.json(注入 api_key)
  3. 拷贝凭证,先验证账号能正常出流
  4. 停用 CPA 插件(plugins.enabled: false,并把 .so 挪到 plugins.disabled.bak-20260829/)
    • ⚠️ 顺序很重要:先停插件再起新服务,否则两边共用同一 refreshToken,轮换会互相踢下线
  5. CPA 挂回三个新上游:在 config.yaml 加 openai-compatibility 段
    • workbuddy2api(前缀 wb,http://192.168.*.*:7863/v1)
    • workbuddy2api-legacy(前缀 wbl,把老模型名 dmodel/dfmodel/gm51model/kmodel/mmodel 别名到新池)
    • qoderwork2api(前缀 qw,http://192.168.*.*:7864/v1)
    • 都设 proxy-url: "direct"
  6. 升级:CPA v7.2.68 → v7.2.145(99 个模型正常);CPA-Manager-Plus v1.12.1 → v1.12.6(/management.html 200、/health OK、managementKeySet=true)
    • 旧配置直接可用,新版本只新增了 claude-code、xai 两个段

自研统一看板 cpa2api-board

代码 /opt/cpa2api-board,容器同名,端口 7866,开机自启。 单二进制 Go(只用标准库)+ 内嵌原生 HTML/JS,多阶段构建(golang:1.26-alpine → alpine)。

挂载:三个 auths 目录(rw)+ docker.sock(用 Unix socket 直接调 Docker API 重启容器,不用在镜像里塞 docker CLI)。

功能:

  • 总览:账号数、容器状态、健康检查、下次自动签到时间
  • 账号表:昵称/UID、token 剩余天数、积分、今日签到;支持单账号或按服务批量「状态 / 签到」
  • 添加账号(半自动):WB / QW 生成授权链接 → 浏览器登录 → 点确认轮询;TRAE 生成链接 → 粘贴回调链接
  • 重启服务(加完账号点一下就加载)

接口:/api/overview、/api/accounts、POST /api/status、POST /api/signin、POST /api/restart/{provider}、POST /api/login/{provider}/start|poll。 鉴权:口令 + 12 小时 Cookie(HttpOnly/Lax),同一 IP 连错 5 次锁 60 秒。

实测踩到的坑(重要)

Qoder 签到状态接口不可信

GET /sash/api/v1/me/daily-check-in/status 对所有账号恒返回 status=DISABLED。 正确做法是直接 POST .../claim:

  • 200 = 本次签到成功
  • 409 AlreadyExists = 今日已签
  • 其它 = 失败

nextClaimAt 是数字不是字符串

结构体里写成 string 会让整个 json.Unmarshal 失败。表现很隐蔽:积分正常显示,但 checkedIn 永远是 null。改成 any 兜底。

workbuddy2api 仓库里根本没有 cmd/login

login.sh 会编译失败。实测出来的端点:

  • 生成链接:POST https://www.codebuddy.cn/v2/plugin/auth/state?platform=cli
    • platform 必须是 query 参数,放 body 里无效(会报 platform is empty)
    • 返回 {state, authUrl}
  • 轮询:GET /v2/plugin/auth/token?state=xxx
    • 未登录返回 {"code":11217,"msg":"11217:login ing..."}

WorkBuddy 401 = 凭证被回收

刷新会报 invalid_grant: Session doesn't have required client,没法救,只能重新登录。

重定向死循环(本次修的)

未登录访问 / 时 302 到 /?login=1,而 /?login=1 依然没登录 → 再 302,浏览器直接 ERR_TOO_MANY_REDIRECTS。 一开始误判成网络问题,其实 Windows 到 jp 是通的(局域网 IP 和 Tailscale IP 都能连)。 修法:页面本身直接返回 200,由前端 JS 探测 /api/me 拿到 401 后自己弹登录框,服务端不做重定向。

当前状态

服务账号情况
WorkBuddy75 个正常(积分 3600~8700,今日已签);2 个凭证被回收
QoderWork6全部正常(积分 100~2000,今日已签)
TRAE SOLO0尚未登录

三个服务内置每天 09:00 / 21:00 自动签到,面板签到只是手动补充,共用同一份 auth 文件。

待办

  • TRAE 还没账号:面板 → 添加账号 → TRAE SOLO → 生成链接 → 浏览器登录 → 复制地址栏整条 127.0.0.1 回调链接粘回面板 → 提交 → 重启服务
  • 2 个 WorkBuddy 账号要重登:司华灵(10942eaa…)、13135413915(958bf0df…),上游 401 + refresh invalid_grant,只能重新登录覆盖同名文件
  • TRAE 登录完成后,把 traework2api 也挂回 CPA 8317 作为上游
  • 改看板口令:编辑 /opt/cpa2api-board/.env 的 BOARD_PASSWORD,然后 docker compose up -d


相关: 00-索引-AI工程索引 8.2 jp CPA workbuddy qoderwork 插件故障排查