feat(qoder): 弄清国际版每日 100 Credits 的「按设备下发」规则 + 一键设备授权登录

排查结论(本机对真实接口做了 30+ 组对照实验)
- GET /sash/api/v1/me/campaigns 只返回一条常驻 VIEW_DETAILS 广告
  (act-20260901-493,BOGO 订阅推广,claimStatus=CLAIMED 与签到无关)
- 无论 Cosy-MachineToken/Code/Type 用 env 0/3/4/prod、裸调用/带 stdin、
  是否附 Cosy-MachineId/OS/Hostname/Origin/Referer/X-*、是否带 clientType,
  都拿不到 CLAIM_BEFIT 轮次;账号本人 balances/achievements 正常
- 三个社区实现(qoder2api-hub、dsh-connect-qoder、CreditDaddy)一致:
  每日轮次按「登录过官方客户端的那台设备」下发,NAS 侧另造的设备看不到它

修复
- _benefits() 改为只认 actionType=CLAIM_BEFIT(不再把 VIEW_DETAILS 当签到),
  窗口过期也排除;claimStatus 未知按可领处理(领取接口幂等)
- 两段式取活动:先裸取拿 uid → 按 uid 生成风控身份 → 再带设备身份重取
- 状态文案改为「未下发每日签到活动」而不是误导性的「今日已领取」

新功能
- Qoder 一键登录(OAuth 设备授权 / PKCE S256):应用内点按钮 → 浏览器确认一次,
  Token 直接绑定本机设备;同 uid 已存在则更新凭证而不是新增重复账号
- 手工导入桌面端设备身份接口 /api/qoder/import_device
- 诊断接口:/api/qoder/{probe,experiment,growth,matrix,desktop}

测试:39 后端 + 25 E2E + 9 前端全绿
This commit is contained in:
AgentCheckin
2026-10-19 12:49:06 +08:00
parent c263edc3e7
commit 556e45d642
11 changed files with 943 additions and 70 deletions
+14 -6
View File
@@ -78,7 +78,7 @@ Token 约 60 天有效,过期后在面板重新粘贴即可。
| 方式 | 操作 | 适合 |
|---|---|---|
| **A · 设备授权登录(推荐)** | 下载社区工具 `qoder_login.py`([L0NE-6/Qoder-Daily](https://github.com/L0NE-6/Qoder-Daily)),运行 `python qoder_login.py`,浏览器打开授权页确认一次,工具输出 `access_token\|refresh_token` | 所有人,无需抓包 |
| **A · 应用内一键登录(推荐)** | 添加账号 → Qoder → 点「一键登录(设备授权)」,浏览器里确认一次即可(PKCE S256 设备流)。**国际版必须走这条**:每日 100 Credits 只下发给登录过的那台设备 | 所有人,无需抓包 |
| **B · 浏览器抓包(最快)** | 浏览器登录 Qoder 网页版 → F12 开发者工具 → 网络 → 任选一个请求,看请求头 `authorization: Bearer dt-...`,`dt-` 后那串即 Access Token;若还能看到 `drt-...` 刷新串,一并填入可自动续期 | 会看开发者工具 |
| **C · 本机客户端提取** | 在装了并登录 Qoder 客户端的 Windows 上运行 `01_extract.py`([wallechfox/qoder-checkin](https://github.com/wallechfox/qoder-checkin)),自动 DPAPI+AES-GCM 解密 `%APPDATA%\com.qodercn.app.*\auth.v1.dat` 取出 Token 和设备标识 | 需要设备标识时 |
@@ -166,11 +166,19 @@ fnos-agent-checkin/
> 2. **站点隔离**:国内版(qoder.cn 客户端)的 Token 只能用于 `openapi.qoder.com.cn`,
> 国际版(qoder.com 客户端)的 Token 只能用于 `openapi.qoder.sh`,混用直接 401。
> 面板的「修复站点」按钮会自动探测 Token 所属站点并切换。
> 3. **国际版需要设备风控身份**:国际版服务端**只对携带 `Cosy-MachineToken/Code/Type` 的请求下发
> 「每天领 100 Credits」活动**,否则活动列表返回空。本应用会自动从官方 npm 包
> `@qoder-ai/qodercli` 提取 UMID 组件(runtime-info)生成身份,无需手动配置。
> 4. **国际版活动 schema 与国内版不同**:国际版 `actionType=VIEW_DETAILS`(国内版 `CLAIM_BENEFIT`),
> 但 `claimStatus`/`startAt`/`endAt` 一致;每日 10:00 (UTC+8) 刷新,领取后 30 天有效。
> 3. **只有 `actionType=CLAIM_BENEFIT` 才是签到轮次**:同一个账号还挂着一条
> `actionType=VIEW_DETAILS` 的订阅推广(无 benefit、`claimStatus` 却是 `CLAIMED`),
> 只看 `claimStatus` 会把它误当成「今日已领取」。响应顶层的 `claimable` 也不可信——
> 「今天已领」和「当前没有活动」它都返回 `false`。
> 4. **国际版的每日 100 Credits 按「设备」下发**(实测 + 社区一致结论):服务端只把
> `CLAIM_BEFIT` 轮次下发给**登录过官方客户端的那台设备**;从 NAS 上另造一套
> `Cosy-MachineToken/Code/Type` 只能拿到常驻的 `VIEW_DETAILS` 广告,活动列表里
> 根本没有每日轮次(这就是「活动列表为空 / 看不到 100 Credits」的根因)。
> 本应用因此提供**「一键登录(设备授权,PKCE S256)」**:把登录绑定到本机,
> 之后才能看到并领取每日轮次;也支持把桌面客户端那台机器的
> `machineToken/Code/Type` 导入进来复用它的设备身份。
> 每日 10:00 (UTC+8) 刷新,错过不补,领取后 30 天有效;同一台设备每天只有一个
> 国际版账号能领到。
**TRAE(api.trae.cn)**