对话笔记直发: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/,零重登。
执行步骤(按顺序)
- 先备份:
/root/backups/20260829-cpa-migration/cpa-auths.tar.gz(75 个文件,13 个插件账号)config.yaml、docker-compose.yml、两个.socpa-manager-data.tar.gz(46M 卷)、keys.txt、models-before.json
- 部署三个服务到
/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)
- 坑:容器内是 uid 10001,主机目录必须
- 拷贝凭证,先验证账号能正常出流
- 停用 CPA 插件(
plugins.enabled: false,并把.so挪到plugins.disabled.bak-20260829/)- ⚠️ 顺序很重要:先停插件再起新服务,否则两边共用同一 refreshToken,轮换会互相踢下线
- 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"
- 升级:CPA
v7.2.68→v7.2.145(99 个模型正常);CPA-Manager-Plusv1.12.1→v1.12.6(/management.html200、/healthOK、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=cliplatform必须是 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 后自己弹登录框,服务端不做重定向。
当前状态
| 服务 | 账号 | 情况 |
|---|---|---|
| WorkBuddy | 7 | 5 个正常(积分 3600~8700,今日已签);2 个凭证被回收 |
| QoderWork | 6 | 全部正常(积分 100~2000,今日已签) |
| TRAE SOLO | 0 | 尚未登录 |
三个服务内置每天 09:00 / 21:00 自动签到,面板签到只是手动补充,共用同一份 auth 文件。
待办
- TRAE 还没账号:面板 → 添加账号 → TRAE SOLO → 生成链接 → 浏览器登录 → 复制地址栏整条
127.0.0.1回调链接粘回面板 → 提交 → 重启服务 - 2 个 WorkBuddy 账号要重登:
司华灵(10942eaa…)、13135413915(958bf0df…),上游 401 + refreshinvalid_grant,只能重新登录覆盖同名文件 - TRAE 登录完成后,把
traework2api也挂回 CPA8317作为上游 - 改看板口令:编辑
/opt/cpa2api-board/.env的BOARD_PASSWORD,然后docker compose up -d