dsh-router-traework

dsh-router 的 TRAE SOLO 供应商插件

npm version MIT license 支持的 DSH 版本:0.1.5-rc.1+

快速安装 · 签到判定 · dsh-router 核心

为 [dsh-router](https://github.com/CARVIN94/dsh-router) 提供 `traework` 供应商(免费 SOLO 聊天通道)。 单独装它没用——它只是向核心注册一个供应商,面板、账号池、组合回退都在核心里。 本项目是 [traework2api](https://github.com/Sliverkiss/traework2api) 的 DSH 插件版, 上游协议与签到语义参考 [wild-work](https://github.com/rockswang/wild-work)。 ## 快速安装 需要 **DSH `0.1.5-rc.1` 及以上**。先装核心,再装本插件,然后**重启 `dsh web`**: ```bash dsh plugin --profile web add dsh-router-core dsh plugin --profile web add dsh-router-traework ``` `dsh plugin add` 会在 profile 里 `pnpm add`,并自动把声明了 `dsh.bundle.patch` 的包加入 `dsh.profile.bundles`(本插件即声明了,即 `cordis.patch.yml`)。 重启后本插件以 cordis service `router.suppliers` 向 dsh-router 注册 `traework` 供应商,面板「供应商」出现 traework 卡片,加账号即用。 > 本地开发版:不用 npm,直接 `dependencies` 加 > `"dsh-router-traework": "link:/path/to/dsh-router-traework"` 指向本地仓库。 ## 与核心的分工 本插件只管**对单个账号调通上游**:SOLO 协议、token 刷新、SSE 转换、签到、积分。 **策略全在核心**(`AccountPool`):选号、冷却、禁用、连续错误累计、遍历回退、 响应写入。所以: - `chatOnce(uid, req)` 一次只服务一个账号,**不遍历账号、不维护冷却表、不写响应** - 失败时返回语义状态(`rate_limit` / `quota` / `session_dead` / `unavailable` / `transport` / `unknown`),由核心决定冷却多久、是否禁用、要不要换号 - `status()` 只报「现在状态」(凭证 + 积分),冷却/禁用由核心叠加后给面板 - 积分只报**值**,不落盘(持久化归核心 `supplier-config.json`):启动时用核心 缓存预热,拿不到时报 `-1`(不是 0),核心保留上次持久化的值 **本插件不做自动调度**:不挂后台定时器。签到是核心在用户点「签到」时逐个 链接触发的(`POST /suppliers/:id/checkin` → `checkinNow(uid)`),token 刷新走 `chatOnce` 触发式路径。曾移植自 traework2api 的每日自动签到 `Scheduler` 已删除 (核心没有定时入口,放着是死代码)。 完整契约见 [dsh-router 的 `docs/suppliers.md`](https://github.com/CARVIN94/dsh-router/blob/main/docs/suppliers.md)。 ## 签到判定 上游 `checkin_credits/*` **一律返回 HTTP 200**,成败只藏在 body 的 `code` 里: | code | 含义 | 处理 | | ------ | -------------------- | ---------------------------------------------------------- | | `0` | 成功 | ok(**注意**:已签到后重复调用也返回 0,是幂等、不加积分) | | `9095` | 今日已签到 | already(幂等成功,不是失败) | | `9074` | 「当前参与用户太多」 | **账号级稳定拒绝**,不是抖动——只重试一次(等 1s)就判失败 | | `1001` | token/会话失效 | session_dead(与 chat 401 同义) | 据此定下的判定规则: - **不能只看 HTTP 状态**(一律 200,会误报成功) - **不能只看 `code 0` 就以为签上了** —— claim 后要**回查 `checkin_credits/status` 的 `checked_in`** 才算数 - **不能拿积分当签到凭据** —— `ide_user_ent_usage` 是所有包的聚合剩余额度, 签到前后可能是同一个数 - `checked_in` 是**当前登录态设备**的读数,换 deviceId 会短暂变 `false`, 那是缓存假象,不代表「这个账号今天还能再签一次」 - 判重维度是**账号**不是设备:换 deviceId 后 `checked_in` 依然是 `true` (所以存量凭证把 deviceId 一次性迁到真实形态是安全的,不会重复签到) ### 剩余积分的计算 `ide_user_ent_usage` 返回 `user_entitlement_pack_list`,剩余 = Σ(`credits_limit` - `credits_amount`)。实测要点: - **请求体必须是 `{"require_usage":true,"req_source":2}`**,发 `{}` 拿不到完整 usage - **过期包必须跳过**:签到积分是当日发放、**31 天后过期**的独立包 (`entitlement_id` 形如 `checkin_20260902_`,每个 200 额度)。不滤掉 就是把历史所有签到包累加进「剩余」,面板越签越多、永远用不完 - 请求头与 chat 链路**不是同一套身份**:签到/积分走 VSCode 插件进程 (UA `VSCode 1.107.1 (TRAE SOLO CN)`),chat 走 IDE 主进程 - `x-device-id` 必须是 **16 位纯数字**(真实客户端实测值如 `1711320556112436`), 发 hex32 / UUID 在风控眼里不是设备号 ### 9074 的真相(账号级稳定拒绝,不是抖动) 9074 **不是短时抖动,是账号级稳定拒绝**。诊断实验(直连上游): - 同一账号 40 余次请求(间隔 0~15s 递增)**全部 9074**,一次没过 - 刷新 token、换全新 deviceId、换 UA/region/请求体 **均无效** - 决定性对照:把失败账号的 deviceId 借给成功账号 → 成功账号照样 `code 0`; 把成功账号的 deviceId 借给失败账号 → 失败账号仍 9074 —— **失败跟着账号走,不跟着设备走** 所以保留的 1s 重试只是兜「上游万一恢复成真抖动」,落空即判失败、不空耗 8s。 从 `ide_user_ent_usage` 的「签到奖励」包看,被拒账号会整段缺签(可能连续多天没 签上)——这是上游对账号的发放策略,客户端绕不过,只能如实报失败,别谎报成功。 ## 架构 通过 cordis service `router.suppliers` 向 dsh-router 注册 `traework` 供应商工厂。 ``` src/ index.ts 插件入口(提供 router.suppliers service) contract.ts 供应商契约 types.ts 类型定义 api/ 供应商实现(上游客户端、账号凭证池、登录、签到能力等) ``` ## 开发 ```bash pnpm install pnpm build # lib/index.js pnpm typecheck pnpm test # node --test "src/**/*.test.ts" ``` ## 致谢 - [Sliverkiss/traework2api](https://github.com/Sliverkiss/traework2api) —— 本插件的 直接移植来源:上游客户端、登录流程与常量表都来自它(其内置的每日自动签到 `Scheduler` 因本插件不做自动调度已删除); - [rockswang/wild-work](https://github.com/rockswang/wild-work) —— 签到语义的参考: `checkin_credits` 的业务码含义、以及「成败只看 body code、积分不能当凭据」 这些判定规则的来源; - [star620/TRAE-Automatic-sign-in](https://github.com/star620/TRAE-Automatic-sign-in) —— TRAE 每日自动签到助手的参考。 ## 许可证 [MIT](LICENSE) ## 免责声明 本项目仅用于学习与技术研究,请勿用于商业用途。