16 KiB
贡献指南
感谢你愿意帮 catbus(猫巴士)变得更好。本文说明怎么搭开发环境、怎么新增或修改平台命令、怎么写对拍测试,以及提交和 PR 的约定。
项目的规范本身在 AGENTS.md:命令语法(第 4 节)、登录态(第 5 节)、输出(第 6 节)、架构(第 7 节)、开发约定(第 8 节)。本文与它冲突时以 AGENTS.md 为准。改规范时先改 AGENTS.md,再改代码。
测试的细节见 TESTING.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 | 3.13 | 只用于生成对拍数据(见第 5 节)。只改文档、CLI、core 或者不涉及请求构造的代码时不需要 |
用户侧只需要 Node。不要引入需要 Python、C/C++ 编译器、node-gyp 或 install 脚本的依赖(AGENTS 7.3、8)。
2. 拉代码与构建
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):
| 命令 | 作用 |
|---|---|
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 |
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/
│ ├── <p>/index.ts # 平台声明与命令注册
│ ├── <p>/UPSTREAM # 移植所基于的上游仓库与 commit
│ ├── <p>/web/ # web 端实现
│ └── _shared/ # 几个平台共用的实现(例如闲鱼、淘宝共用的钉钉 IMPaaS 私信长连)
├── static/<p>/ # 上游签名 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/<p>/ 放对拍数据,e2e/ 放在线测试
└── references/ # 上游仓库的只读副本,不进版本控制
references/:上游仓库,只读
references/ 里是作者 cv-cat 的 10 个上游仓库(平台 id 与仓库名的对应见 AGENTS 第 3 节)。它不进本仓库的版本控制,需要时自己克隆,并切到 src/platforms/<p>/UPSTREAM 记录的 commit(记有 branch 的,就在那个分支上):
git clone https://github.com/cv-cat/BilibiliApis references/BilibiliApis
git -C references/BilibiliApis checkout 7d80150893b429651deee76929af760ea642b844 # 取自 src/platforms/bilibili/UPSTREAM
规则(AGENTS 3、8):
- 可以读,也可以运行(用来生成对拍数据);
- 可以在上游 gitignore 的位置装依赖,例如
node_modules/; - 不能修改其中任何受版本控制的文件。发现上游 bug 时,修到对应的上游仓库,再按新 commit 重新同步(第 6 节)。
只跑 npm test 不需要 references/:对拍数据已经生成好、放在 tests/golden/ 里。
4. 新增或修改平台命令
4.1 三步走(AGENTS 8「新增命令」)
-
先查词表。 看 AGENTS 第 4 节:通用词表(4.5)、原生叫法对照(4.6)、参数(4.8)、标准选项(4.9)。含义相同的能力在所有平台上用同一个命令、同一组参数、同一种输出结构(「同义同名」)。
- 词表不够用时,先改 AGENTS.md:平台特有的命令或私有选项登记到 4.7「平台扩展」;新的筛选取值加到 4.9;新的输出类型或字段加到 6.2。
- 私有选项不能与标准选项重名。平台原生叫法(
note、video……)不做别名。
-
在注册表里声明。 编辑
src/platforms/<p>/index.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 名必须是
<resource><Action>(例如draft delete→draftDelete),测试会检查。 - 改完运行
npm run gen:capabilities,把重新生成的docs/capabilities.md一起提交。它不手改,测试会检查它是否最新。
-
再实现。 见下一节。
4.2 平台实现的约定(AGENTS 7.5)
参考实现是 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/<p>/,不手改(.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。
5. 对拍测试
请求构造和签名必须有对拍测试(AGENTS 7.5):先用上游 Python 代码在固定的随机数与时钟下生成请求序列,再让 TS 实现在同样的条件下运行,逐字节比较。
5.1 准备对拍环境
每个平台一个不进 git 的虚拟环境 .golden/<p>/:
uv venv .golden/<p> --python 3.13
VIRTUAL_ENV=.golden/<p> uv pip install -r references/<repo>/requirements.txt
各平台还要装哪些额外依赖,写在 scripts/golden/<p>/gen.py 开头的说明里。例如 douyin 的 blackboxprotobuf 要单独 --no-deps 安装;xhs、jd 还要在 references/<repo>/ 下 npm install(装在上游 gitignore 的 node_modules/ 里)。
5.2 生成对拍数据
在仓库根目录运行(macOS / Linux 是 bin/python,Windows 是 Scripts\python):
.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/<p>/<case>.json,内容包括input、seed、now、requests、responses、result、error。
在 gen.py 里加一个用例,例如:
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/<p>.test.ts 里给用例加上 TS 侧的等价调用,由 tests/golden.ts 的 replay() 在同样的随机数与时钟下运行,expectRequests() 比较请求序列:
const CASES: Record<string, () => Promise<unknown>> = {
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/<p> 检查:只应该有你打算改的用例变化。大量无关用例一起变了,通常是随机数的消耗顺序变了,或者 references/ 不在 UPSTREAM 记录的 commit 上。
对拍失败时怎么排查,见 TESTING.md 第 2.5 节。
6. 上游更新时的同步流程
上游仓库更新后,按 AGENTS 7.5 的四步同步:
-
查看变更:
git -C references/<repo> pull git -C references/<repo> log <UPSTREAM 里的 commit>.. git -C references/<repo> diff <UPSTREAM 里的 commit>.. -- <关心的目录> -
同步移植:改
api.ts/client.ts等;签名 JS 变了就把新文件原样复制到static/<p>/。 -
重新生成对拍数据:运行
scripts/golden/<p>/gen.py,检查git diff tests/golden/<p>,再跑npm test。 -
更新
src/platforms/<p>/UPSTREAM:改成新的 commit(跟的是分支的,同时更新branch)。
提交信息写成 <p>:同步上游 <commit 或分支>,<改了什么>,例如 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:。
- 平台 id:
- 把原因写进去:修了什么现象、为什么这样改,引用上游时注明方法名或 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/<p>/UPSTREAM;映射有变化时,已更新docs/upstream-map.md。 - 新增依赖在全部目标系统(AGENTS 7.2)上都有预编译包,没有 install 脚本,也不需要 node-gyp。
- 做过真机验证的,写明验证了哪些命令、结果如何;新发现的限制记进
docs/platforms/<p>.md的「已知限制」。
9. 凭证安全红线
以下几条没有例外(AGENTS 8):
- 严禁把真实 cookie、token、私钥、证书写进代码、测试 fixture、对拍数据、日志、issue、PR 或提交记录。
- 凭证只存在本机的
~/.catbus/,只由 core 读写。平台实现从 core 拿凭证,更新后交回 core 落盘。 - 日志一律走 logger,它会给 cookie、token、Authorization、密码等字段打码。不要用
console.log或process.stdout.write输出调试信息。 - 在线测试的报告
.e2e/<p>.json里有真实账号返回的数据,不进版本控制,也不要贴到 issue 里。 - 贴
-v的调试日志之前,自己再检查一遍有没有漏掉的敏感信息。
发现安全问题请按 SECURITY.md 私下报告,不要公开发 issue。
10. 行为准则
参与本项目即表示你同意遵守 行为准则。