# 贡献指南 感谢你愿意帮 catbus(猫巴士)变得更好。本文说明怎么搭开发环境、怎么新增或修改平台命令、怎么写对拍测试,以及提交和 PR 的约定。 项目的规范本身在 [AGENTS.md](../AGENTS.md):命令语法(第 4 节)、登录态(第 5 节)、输出(第 6 节)、架构(第 7 节)、开发约定(第 8 节)。本文与它冲突时以 AGENTS.md 为准。**改规范时先改 AGENTS.md,再改代码。** 测试的细节见 [TESTING.md](TESTING.md),安全问题的报告方式见 [SECURITY.md](SECURITY.md)。 ## 1. 开发环境 | 工具 | 版本 | 用途 | |---|---|---| | Node | `^22.22.2 \|\| ^24.15.0 \|\| >=26.0.0` | 构建、测试、运行(见 AGENTS 7.1) | | npm | 随 Node 附带 | 依赖与 workspace | | git | 任意较新版本 | | | Python 3.13 + [uv](https://docs.astral.sh/uv/) | 3.13 | **只用于生成对拍数据**(见第 5 节)。只改文档、CLI、core 或者不涉及请求构造的代码时不需要 | 用户侧只需要 Node。不要引入需要 Python、C/C++ 编译器、node-gyp 或 install 脚本的依赖(AGENTS 7.3、8)。 ## 2. 拉代码与构建 ```bash git clone https://github.com/cv-cat/catbus.git cd catbus npm ci npm run assets:jd # 取回京东验证码模型(约 81 MB),放到 packages/assets-jd/models/ npm run assets:ocr # 取回 ddddocr 的 OCR 模型(约 34 MB),放到 packages/assets-ocr/models/ npm run build # tsc 编译到 dist/ npm link # 可选:把 catbus 命令链到全局,之后可以直接运行 catbus npm run typecheck # 类型检查(vitest 不做类型检查,要单独跑) npm test # 离线测试:单元测试 + 对拍测试 ``` - 模型文件不进 git。离线测试里的京东验证码求解和 B 站极验点选识别要用到它们,所以跑 `npm test` 之前先取回。`assets:jd` 在有 `references/JdApis` 时从本地复制,否则从 GitHub 下载;`assets:ocr` 从 PyPI 上固定版本的 ddddocr wheel 里取出。两者都校验 sha256。 - 不想 `npm link` 时,也可以直接运行 `node dist/cli/main.js <参数>`。 - 改了 `src/` 之后要重新 `npm run build`,`npm link` 出来的 `catbus` 才会生效。测试直接跑 TypeScript 源码,不需要先构建。 常用 npm scripts(见 [package.json](../package.json)): | 命令 | 作用 | |---|---| | `npm run build` | 清理 `dist/` 后编译 | | `npm run typecheck` | `tsc --noEmit`,包括 `tests/` | | `npm test` | 离线测试(vitest) | | `npm run test:e2e` | 在线测试,用本机 `~/.catbus` 的登录态,只跑只读命令(见 TESTING.md) | | `npm run gen:capabilities` | 由注册表重新生成 [docs/capabilities.md](../docs/capabilities.md) | | `npm run assets:jd` / `assets:ocr` | 取回模型文件 | ## 3. 项目结构 ``` catbus/ ├── AGENTS.md # 规范(CLAUDE.md 通过 @AGENTS.md 引用它) ├── docs/ │ ├── capabilities.md # 能力矩阵,由注册表生成,不手改 │ └── upstream-map.md # 上游移植对照 ├── src/ │ ├── cli/ # 入口 main.ts、argv 解析、帮助、信封输出、危险操作确认、全局命令 │ ├── core/ # 注册表、词表、auth store、config、errors、http、vm、rand、py、log…… │ └── platforms/ │ ├──

/index.ts # 平台声明与命令注册 │ ├──

/UPSTREAM # 移植所基于的上游仓库与 commit │ ├──

/web/ # web 端实现 │ └── _shared/ # 几个平台共用的实现(例如闲鱼、淘宝共用的钉钉 IMPaaS 私信长连) ├── static/

/ # 上游签名 JS、proto 等,按字节原样复制 ├── packages/assets-jd/ # @cv-cat/catbus-assets-jd(京东验证码模型) ├── packages/assets-ocr/ # @cv-cat/catbus-assets-ocr(ddddocr 模型) ├── scripts/ │ ├── golden/ # 调用上游 Python 生成对拍数据 │ ├── smoke.mjs # CI 冒烟测试 │ ├── release-notes.mjs # 从 CHANGELOG 生成 GitHub Release 的说明 │ └── fetch-*-models.mjs # 取回模型 ├── tests/ # vitest;golden/

/ 放对拍数据,e2e/ 放在线测试 └── references/ # 上游仓库的只读副本,不进版本控制 ``` ### references/:上游仓库,只读 `references/` 里是作者 [cv-cat](https://github.com/cv-cat) 的 10 个上游仓库(平台 id 与仓库名的对应见 AGENTS 第 3 节)。它不进本仓库的版本控制,需要时自己克隆,并切到 `src/platforms/

/UPSTREAM` 记录的 commit(记有 `branch` 的,就在那个分支上): ```bash git clone https://github.com/cv-cat/BilibiliApis references/BilibiliApis git -C references/BilibiliApis checkout 0234d8188b40314d011358d60424bb88fd291a1f # 取自 src/platforms/bilibili/UPSTREAM ``` 规则(AGENTS 3、8): - 可以读,也可以运行(用来生成对拍数据); - 可以在上游 gitignore 的位置装依赖,例如 `node_modules/`; - **不能修改其中任何受版本控制的文件**。发现上游 bug 时,修到对应的上游仓库,再按新 commit 重新同步(第 6 节)。 只跑 `npm test` 不需要 `references/`:对拍数据已经生成好、放在 `tests/golden/` 里。 ## 4. 新增或修改平台命令 ### 4.1 三步走(AGENTS 8「新增命令」) 1. **先查词表。** 看 AGENTS 第 4 节:通用词表(4.5)、原生叫法对照(4.6)、参数(4.8)、标准选项(4.9)。含义相同的能力在所有平台上用同一个命令、同一组参数、同一种输出结构(「同义同名」)。 - 词表不够用时,**先改 AGENTS.md**:平台特有的命令或私有选项登记到 4.7「平台扩展」;新的筛选取值加到 4.9;新的输出类型或字段加到 6.2。 - 私有选项不能与标准选项重名。平台原生叫法(`note`、`video`……)不做别名。 2. **在注册表里声明。** 编辑 `src/platforms/

/index.ts`。词表里的命令只写与默认不同的部分,扩展命令把字段写全: ```ts 'item related': impl('full', 'itemRelated'), 'danmaku list': { upstream: 'full', summary: '视频弹幕', args: [item], auth: 'required', output: 'Danmaku[]', handler: h('danmakuList') }, ``` - `upstream` 取 `full` / `partial` / `none`,即矩阵里的 ✓ / ◐ / ○;上游只部分支持时在 `note` 里写清限制。 - handler 名必须是 ``(例如 `draft delete` → `draftDelete`),测试会检查。 - 改完运行 `npm run gen:capabilities`,把重新生成的 `docs/capabilities.md` 一起提交。它不手改,测试会检查它是否最新。 3. **再实现。** 见下一节。 ### 4.2 平台实现的约定(AGENTS 7.5) 参考实现是 [src/platforms/bilibili/web/](../src/platforms/bilibili/web/),新平台或新命令照它的结构写: | 文件 | 内容 | |---|---| | `client.ts` | 会话:设备初始化、签名密钥、带重试的请求、业务码 → 错误 | | `api.ts` | 一个函数对应一个上游方法,字段与顺序照抄上游 | | `commands.ts` | 命令 handler,导出名与注册表里的 handler 名一致 | | `normalize.ts` | 原始对象 → AGENTS 6.2 的归一化类型,挂上原始对象供 `--raw` | | `resolve.ts` | 参数归一化:ID、URL、分享短链、自然标识、`me` | 写代码时: - **行为以上游为准**:同样的输入,产出同样的请求(URL、query、header 顺序、cookie、body、签名)。 - 随机数和当前时间只经 `core/rand.ts`,等待用 `rand.sleep`。否则对拍时随机数序列会错位。 - 与 Python 对应的编码(`urlencode`、`quote`、`json.dumps`)用 `core/py.ts`;需要精确键序的 JSON 用 `Map`。 - 解析平台响应用 `core/py.ts` 的 `jsonLoads`,不直接用 `JSON.parse`(它和 Python 的 `json.loads` 一样保留 64 位整数 ID)。 - HTTP 用 `toolkit.httpClient(ctx)` 拿到的 `HttpClient`,请求头按上游顺序显式给出。cookie 在凭证的 cookie 罐里,Set-Cookie 自动写回,命令结束时由 core 落盘。平台代码不直接读写 `~/.catbus/`。 - 上游的签名 JS 原样复制到 `static/

/`,不手改(`.gitattributes` 让它按字节保存),用 `core/vm.ts` 的 `loadScript` / `callScript` 执行,需要适配的地方在 TS 一侧包装。 - 业务错误映射到 AGENTS 6.4 的错误码:登录墙用 `toolkit.authError`,验证码或风控报 `RISK_CONTROL`(`detail.kind` 为 `captcha` / `rate_limit` / `blocked`),其余报 `UPSTREAM`,原始错误码放进 `detail`。 - stdout 只输出信封,调试信息走 `ctx.log`(stderr,已打码)。面向用户的文字(`message`、`hint`、提示)用中文。 - 上游没有对应方法、catbus 自己补的请求,也放进 `api.ts` 并在注释里注明「非上游」。这类请求没法对拍,要用命令测试覆盖(见 TESTING.md)。 - 上游方法与 catbus 命令、选项的对应关系变了,同步更新 [docs/upstream-map.md](../docs/upstream-map.md)。 ## 5. 对拍测试 请求构造和签名**必须**有对拍测试(AGENTS 7.5):先用上游 Python 代码在固定的随机数与时钟下生成请求序列,再让 TS 实现在同样的条件下运行,逐字节比较。 ### 5.1 准备对拍环境 每个平台一个不进 git 的虚拟环境 `.golden/

/`: ```bash uv venv .golden/

--python 3.13 VIRTUAL_ENV=.golden/

uv pip install -r references//requirements.txt ``` 各平台还要装哪些额外依赖,写在 `scripts/golden/

/gen.py` 开头的说明里。例如 douyin 的 blackboxprotobuf 要单独 `--no-deps` 安装;xhs、jd 还要在 `references//` 下 `npm install`(装在上游 gitignore 的 `node_modules/` 里)。 ### 5.2 生成对拍数据 在仓库根目录运行(macOS / Linux 是 `bin/python`,Windows 是 `Scripts\python`): ```bash .golden/bilibili/bin/python scripts/golden/bilibili/gen.py ``` 框架 `scripts/golden/catbus_golden.py` 会: - 把 Python 的 `random` / `secrets` / `uuid` / `os.urandom` / `time` 换成与 `src/core/rand.ts` 一一对应的确定性序列,时区固定为北京时间; - 截获 curl_cffi 与 requests 的请求,不联网,按用例给的响应回复; - 上游起的 node 子进程通过 `NODE_OPTIONS` 预加载 `node_determinism.cjs`,同样固定 `Math.random` 与 `Date`; - 每个用例写成 `tests/golden/

/.json`,内容包括 `input`、`seed`、`now`、`requests`、`responses`、`result`、`error`。 在 `gen.py` 里加一个用例,例如: ```python case('user_info', logged(lambda a: BiliApi.get_user_info(a, '2')), mid='2') ``` `respond(req)` 按请求的 URL 返回假响应,不写时默认回 `{"code": 0, "data": {}}`。 **只能用假凭证。** cookie、token、私钥、账号 ID 一律写成 `fake-sessdata`、`10001` 这样的假值,不要从浏览器复制真实值,哪怕「只是临时试一下」。对拍数据会进版本控制。 ### 5.3 在 TS 侧对拍 在 `tests/

.test.ts` 里给用例加上 TS 侧的等价调用,由 `tests/golden.ts` 的 `replay()` 在同样的随机数与时钟下运行,`expectRequests()` 比较请求序列: ```ts const CASES: Record Promise> = { user_info: () => logged((b) => api.userInfo(b, '2')), // ... } describe('bilibili 对拍:请求构造与签名', () => { for (const [name, run] of Object.entries(CASES)) { it(name, async () => { const c = loadCase('bilibili', name) const { requests, error } = await replay(c, run) if (error) throw error expectRequests(requests, c.requests) }) } }) ``` 对拍数据由 `gen.py` 生成,**不要手改 `tests/golden/` 里的 JSON**。重新生成后用 `git diff tests/golden/

` 检查:只应该有你打算改的用例变化。大量无关用例一起变了,通常是随机数的消耗顺序变了,或者 `references/` 不在 UPSTREAM 记录的 commit 上。 对拍失败时怎么排查,见 [TESTING.md](TESTING.md) 第 2.5 节。 ## 6. 上游更新时的同步流程 上游仓库更新后,按 AGENTS 7.5 的四步同步: 1. **查看变更**: ```bash git -C references/ pull git -C references/ log .. git -C references/ diff .. -- <关心的目录> ``` 2. **同步移植**:改 `api.ts` / `client.ts` 等;签名 JS 变了就把新文件原样复制到 `static/

/`。 3. **重新生成对拍数据**:运行 `scripts/golden/

/gen.py`,检查 `git diff tests/golden/

`,再跑 `npm test`。 4. **更新 `src/platforms/

/UPSTREAM`**:改成新的 commit(跟的是分支的,同时更新 `branch`)。 提交信息写成 `

:同步上游 ,<改了什么>`,例如 `x:同步上游 3fe6ea7——正文超过 280 权重自动发长推……`。 ## 7. 提交规范 看 `git log` 就能看出现有风格,请保持一致: - **用中文写**,标题格式为 `<范围>:<改了什么>`,冒号用全角「:」。不用 `feat:` / `fix:` 之类的前缀。 - **范围**: - 平台 id:`xhs:`、`douyin:`;多个平台用顿号:`xianyu、taobao:`; - 模块或文件:`core:`、`core/http:`、`core/vm:`、`docs/platforms/xhs.md:`; - 规范小节:`AGENTS 7.5:`; - 其他:`在线测试:`、`对拍框架:`、`release:`。 - **把原因写进去**:修了什么现象、为什么这样改,引用上游时注明方法名或 commit,例如 `(上游 get_following)`。 - 标题里有几件事时用「;」分开。改动大时,正文用 `- ` 列要点。 真实的例子: ``` core/http:响应头声明了非 UTF-8 的 charset(如京东的 gbk)时按它解码,与上游 resp.text 一致;修京东登录后用户名乱码 xhs:user following(上游 XHSLiveAPI.get_following,私信页的关注列表),只支持 me,查别人报 UNSUPPORTED AGENTS 7.5:解析平台响应用 jsonLoads 在线测试:平台风控(RISK_CONTROL)记为跳过 ``` 提交信息、PR 描述里不要出现 cookie、token 或真实账号数据。 ## 8. PR 检查清单 提交 PR 前逐项确认: - [ ] `npm run typecheck` 和 `npm test` 都通过(本地已取回模型)。 - [ ] 改了规范(命令、选项、输出字段、错误码)时,已经先改了 AGENTS.md。 - [ ] 新命令已在注册表里声明,`npm run gen:capabilities` 已重新生成 `docs/capabilities.md`。 - [ ] 请求构造和签名有对拍测试;对拍数据由 `gen.py` 生成,没有手改。非上游的请求有命令测试。 - [ ] 代码、测试 fixture、对拍数据、日志、提交信息里**只有假凭证**。 - [ ] 没有修改 `references/` 里受版本控制的文件;`static/` 里的上游 JS 是原样复制的。 - [ ] stdout 只输出信封;面向用户的文字是中文。 - [ ] 同步上游时,已更新 `src/platforms/

/UPSTREAM`;映射有变化时,已更新 `docs/upstream-map.md`。 - [ ] 新增依赖在全部目标系统(AGENTS 7.2)上都有预编译包,没有 install 脚本,也不需要 node-gyp。 - [ ] 做过真机验证的,写明验证了哪些命令、结果如何;新发现的限制记进 `docs/platforms/

.md` 的「已知限制」。 ## 9. 凭证安全红线 以下几条没有例外(AGENTS 8): - **严禁**把真实 cookie、token、私钥、证书写进代码、测试 fixture、对拍数据、日志、issue、PR 或提交记录。 - 凭证只存在本机的 `~/.catbus/`,只由 core 读写。平台实现从 core 拿凭证,更新后交回 core 落盘。 - 日志一律走 logger,它会给 cookie、token、Authorization、密码等字段打码。不要用 `console.log` 或 `process.stdout.write` 输出调试信息。 - 在线测试的报告 `.e2e/

.json` 里有真实账号返回的数据,不进版本控制,也不要贴到 issue 里。 - 贴 `-v` 的调试日志之前,自己再检查一遍有没有漏掉的敏感信息。 发现安全问题请按 [SECURITY.md](SECURITY.md) 私下报告,不要公开发 issue。 ## 10. 行为准则 参与本项目即表示你同意遵守 [行为准则](CODE_OF_CONDUCT.md)。