dsh-qq-plugin
v1.0.5
Published
QQ channel for DeepSeek Harness: drive the agent on your computer from your phone in QQ. Runs on Tencent's official QQ bot platform, so no public IP and no reverse proxy are needed.
Maintainers
Readme
dsh-qq-plugin
在 QQ 里用手机指挥这台电脑上的 DeepSeek Harness agent。
走腾讯官方 QQ 机器人通道(api.bot.qq.com,扫码绑定),不需要公网 IP、不需要反向代理、
中间也没有第三方协议客户端。绑定一次,之后一直有效。
能在当前的 DSH 桌面端上跑 —— 针对 DSH 0.2.0-rc.2 开发和实测。
这是 dsh-wechat-plugin 的 QQ 版本,功能与它对齐; 有几处有意不同,因为 QQ 协议本来就不需要微信那套机器(见 与微信版的差异)。
English | 中文
一句话要一个文件,它就出现在手机上:在 QQ 里说「把我电脑上桌面那张 DSH 自画像发过来」, agent 在电脑上找到那个文件、发回来,并在下面回一句「已发送 ✅ 桌面上的「DSH 自画像.png」(29 KB,PNG)」。 输入框右边那个
+是附件入口——图片、视频、语音、文件都从那里发进去(见媒体双向)。
功能
- 文字双向:QQ 消息变成 DSH 提示词,回复发回 QQ。
- 图片、文件、语音进来,图片、视频、文件出去:收到的附件落盘到会话工作区并交给 agent;
agent 用
send_to_qq把本机文件发回来。超长回复自动转成.md文件发回手机。 - 一个 QQ 对话,多个 DSH 会话:
/new、/list、/switch、/current、/cancel, 或者直接说「换个对话」。列表分会话夹与会话两层。 - 模型与推理等级也能在手机上换:
/model列出模型和各自的推理等级,/model 2、/model 等级 high直接切换,对下一步生效。 - 在手机上回答权限与提问:权限审批回「允许」/「拒绝」(或数字选项), agent 的二选一/多选提问带编号发到 QQ,回个数字就行——离开电脑时回合不会再卡住。
- 主动推送:
notify_qq让这台机器上的任意会话都能给你发消息, 这正是「干完了告诉我」的实现。实测可用:连发十条消息全部送达,其中后六条走的就是主动推送。 (腾讯对主动推送的规则会变,插件不假设额度;任何原因没送达都会进队列,等你下次说话时先补发。) - 定时任务:用 DSH 宿主自带的
schedule_create/schedule_list/schedule_delete/schedule_update,说「23:12 叫我」就行。插件不重复实现 DSH 已有的能力,只做通道。 - 设置页:设置 → QQ,九个设置项、带原生文件夹选择器、带诊断区。
安装
给 DSH 桌面端用的,装在一台一直开着的机器上。它需要 DSH 的工作区、工具与 UI 服务—— 也就是标准的桌面端组合。
| | |
|---|---|
| DSH 桌面端 | @deepseek-ai/dsh-desktop 0.2.0-rc.2 或更高 |
| 手机 | 能装手机 QQ,用来扫一次二维码 |
| QQ 机器人 | 不需要事先注册。机器人应用是那次扫码当场创建的,不必提前准备 AppID/AppSecret |
| 腾讯的规则 | 未认证的机器人仅管理员可用——也就是只有你自己能用,这正是「私人遥控器」要的 |
打开侧边栏的 plugin 入口,选 添加插件。输入框接受包名、Git 地址、tarball 或本地路径。
要装的这个名字是
dsh-qq-plugin仓库与包同名,没有需要对上的东西——但
-plugin这个后缀很容易漏掉。 把dsh-qq-plugin复制过去,别手打。 安装命令必须与包名逐字一致。
在终端里就是一行(你用的 profile 是 web 就把 --profile desktop 换成 --profile web):
dsh plugin --profile desktop add dsh-qq-plugindsh-qq-plugin需要指定版本就写 [email protected]。
C:\path\to\dsh-qq指到放着 package.json 的那个目录:文件夹叫 dsh-qq,它声明的包名是 dsh-qq-plugin。
npm pack
# → dsh-qq-plugin-1.0.5.tgz把那个 .tgz 的绝对路径粘进 添加插件。
界面底下驱动的就是 pnpm,所以同样的事也能在终端里做:
dsh plugin --profile desktop add dsh-qq-plugin
dsh plugin --profile desktop add C:\path\to\dsh-qq然后得自己启用这个 bundle——也就是 立即启用 替你做的事——改
~/.dsh/profiles/desktop/package.json:
{
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-qq-plugin"
]
}
}
}等安装结束,点 立即启用,重启 DSH。
开始使用
重启 DSH 后打开 设置 → QQ,点 开始扫码。用手机 QQ 扫二维码并在手机上确认。
扫码这一步不用先去腾讯开放平台注册。 机器人应用是那次扫码当场创建的, 不需要事先准备 AppID/AppSecret,也不需要公网 IP 或反向代理。
凭据直接写进 DSH 的凭据存储——插件自己的状态文件里只有 AppID,没有 AppSecret。 绑定后通道会自动连接:在 QQ 里给机器人发消息即可开始对话。
上面这张是装上之后真正在用的样子:agent 在电脑上整理好一份 13 首的《秋日诗歌选》,
把 docx 和 pdf 两个文件直接发到手机上;同一条对话里问一句「告诉我,我现在电脑时间」,
它答 2026-10-13 22:43:28 星期二,还顺手问要不要设个提醒。
这一刻电脑是锁屏的——手机上的时间是 22:43,锁屏界面上的时间也是 22:43:
锁屏不会让 agent 停下来,所以你走开之后它还在干活,而手机上的对话照常推进。 反过来也一样成立:锁屏也正是你离开时该做的事(见安全)。
使用
直接发消息就会到达 agent:
你好下面按能力分块,每块都配一张真机截图说明它长什么样、在证明什么。
会话管理(手机上)
一个 QQ 对话,背后是任意多个 DSH 会话。/list 打开本工作区的会话,每屏 10 条:
这张图一次证明四件事:一屏 10 条、每行末尾一个 6 位短 id(7cee80、4b55e1…)、
当前会话标着「← 当前」、以及底部那句提示。列表还会告诉你其它会话夹里还有 89 个对话。
/list all 是上面那一层——会话夹(跨项目/工作区),每个夹子带会话数;
回编号或名字就进入,进去之后看到的还是会话列表(图里回的是 1,于是进了「QQ 会话」):
进会话夹只改浏览位置,不改绑定——真要切过去,还要在里面再选一个会话。
裸数字就是 /switch <数字>:列表就在眼前时不用打 /switch,直接回 2 就行,
而且它会告诉你切到了哪个(连短 id 一起回,你能对上是不是那一行):
★ 引用冻结编号:长按引用那条列表消息再回数字,就按你引用的那一屏解析。 列表在这期间变了也不会切错会话——编号冻结在那条消息里。列表底部自己就这么提示: 「引用这条消息再回编号,可以避免编号过期。」
大白话也行。 下面这张是发「查看所有会话」(不是指令):
agent 判的是意图而不是匹配固定规则:它认出了这是「列会话」,于是列出了会话夹, 还告诉你下一步可以回编号、也可以回「查看其余」。切换成功或失败它也都会说。
用这些指令控制你在和哪个 DSH 会话说话:
| 指令 | 作用 |
|---|---|
| /new [标题] | 新建对话。后面跟的文字就是标题:/new 修复登录 会开一个叫「修复登录」的对话 |
| /list | 直接进 QQ 工作区那一层(每屏最多 10 条) |
| /list all | 列出本机的会话夹(工作区),每个一行、带会话数;回复编号或名字进入 |
| /switch 3 | 切到那一屏的第 3 个 |
| /switch e716f0 | 按行尾那个短 id 切——跟编号无关,永远不会过期 |
| 回复「查看其余」 | 往下翻十条(两层都能用,一直翻到底)。编号跨屏连续(1–10,然后 11–20) |
| 直接回 3、名字或行尾 id | 列表就在眼前时不用打 /switch |
| 引用列表消息 → /switch 3 | 按那一屏的编号切,编号冻结在那条消息里 |
| /model | 看有哪些模型,以及每个模型的推理等级,标出当前用的那个 |
| /model 2 | 切到列表里的第 2 个,也可以直接发模型名字 |
| /model 等级 high | 换当前模型的推理等级(等级名字就是列表里印的那些) |
| /model 2 等级 high | 换模型的同时指定等级 |
| /current | 显示当前在哪个对话 |
| /cancel | 中断正在跑的任务 |
| /help | 显示这份列表 |
空格是可选的:/switch3、/switch 3、/switch:3 意思相同——这些指令是在手机键盘上敲的,
掉空格是常态。只是以指令名开头的词不算指令,所以 /news 不是 /new。
编号会过期:会话在你读列表时还在跑,等你回「3」时第 3 行可能已经换人了。所以每行末尾都带一个
最多 6 个字符的短 id,直接回它或 /switch 都收;或者引用那一屏的列表消息再回编号,
编号就按那条消息印的算。
列表分两层,每屏 10 条:/list 直接进本工作区的会话;/list all 列出会话夹(工作区),
回编号或名字进入。两层都用「查看其余」往下翻十条,编号跨屏连续(1–10,然后 11–20),
翻到底会明说而不是继续邀请你翻。进会话夹只改浏览位置,不改绑定——真要切过去,
还要在里面再选一个会话。
模型与推理等级
/model 列出有哪些模型(带「(当前)」「(默认)」标记)以及每个模型的推理等级。
/model <名字|编号> 切模型;/model 等级 high 只改推理等级;/model 2 等级 high 一起改。
切换对下一步生效。
推理等级的取值来自宿主适配器,本机自带的 DeepSeek 适配器发布的是
off/low/high/max(清单里印成Off/Low/High/Max)。/model 等级后面写的就是这些名字,大小写不敏感; 中文的「高」「低」不在取值里——等级名字就是列表里印的那些。
也可以直接用大白话说,例如「换个对话」「我有哪些对话」「停下」。agent 判的是意图而不是 匹配固定规则,而且切换会告诉你成功还是失败。
媒体双向:图片、视频、语音、文件
收:手机发图片 / 视频 / 语音 / 文件给 agent。语音用服务端转写,你无感。 Word、Excel 这类文档附件也能读。
下面是发一张狗的照片过去、只问一句「这是什么」:
图里值得注意的不是它认出了狗,而是那两处只有真看过图才知道的细节:黄身橙嘴的鸭子玩具、 画面左边人的腿,以及它主动指出照片被横过来了(拍照时手机拿反了)。
发一份 PDF 过去、同样只问一句「这是什么」:
文档类附件也能读:它说出了这是一份 5 页的招新任务文档、由 MarkText 导出, 并按「第一部分 环境配置 / 第二部分 用 AI+工程规范 / 第三部分 传统视觉 OpenCV」把内容概括了一遍。
发:agent 用 send_to_qq 把电脑上的文件发到手机——图片显示成图、视频显示成可播放的片段、
其它变文件附件:
这就是本页顶部那张封面图在讲的事,也是一次完整的取件:说清楚要哪个文件, 它在电脑上找到、发回来,并且回一句带文件名与大小的确认,你一眼能看出发的是不是你要的那个。
★ 超长回复会自动转成 .md 文件发回手机(默认上限 4000 字,可调;0 = 不限制):
手机上一屏读不完的东西,转成文件比刷长消息好用得多——不截断,也不拆成十几条刷屏。 阈值就是设置页里的「单条回复字数上限」。
语音:手机按住说话,agent 收到的是文字(服务端转写,不需要你多做什么):
图里那条 3 秒的语音气泡下面,直接就是一份完整的天气回答——中间那一步(把语音变成文字) 你在手机上完全看不到,也不需要看到。
交互
这三件事没有配图——它们是同一条对话里的一问一答,截出来的图跟上面那些几乎一样, 所以用文字说清楚更好。它们在手机上收到的是这样:
- 权限审批:agent 要动你的文件时,QQ 里会出现一条待批准的请求,带上它打算做什么。 你回 「允许」、「拒绝」,或者回数字选项都算回答;答完任务接着跑。 (想让这种提示真的出现,权限预设得是能弹提示的那两个——见设置。)
- 二选一/多选提问:agent 的提问会带编号选项发过来,回个数字就行。
- 引用当上下文:长按引用它的某条回答再说话,例如「这条里的第二点展开说」, 它知道「这条」是哪一条,也知道「第二点」指哪个。
主动性:干完了告诉你
★★ notify_qq:这台机器上任意会话都能主动推消息给你——「干完了告诉我」就是靠它。
下面左边是桌面端在干这个活儿的会话,右边是手机 QQ上收到的结果:
注意两边不是同一个会话:干活的是桌面端那个会话,收消息的是你绑定的 QQ 对话。
这正是 allowCrossSessionNotify 默认开着的原因——不放开这一条,干活的会话就报不了自己的结果。
实测可用:实测连发十条消息全部送达,其中后六条走的正是主动推送。 (腾讯对主动推送的规则会变,插件不假设额度;任何原因没送达都会进队列, 等你下次说话时先补发。)
定时任务走的是 DSH 宿主自带的 schedule_create / schedule_list / schedule_delete /
schedule_update——插件不重复实现 DSH 已有的能力,只做通道。说人话就行:
到点它真的会推一条过来(这是主动推送,不是你在问它):
「每天早上 9 点提醒我今天要做什么」「每小时给我发一次当前时间」「看看我设了哪些定时任务」
都归这条。看有哪些定时任务就是 schedule_list,删和改也一样。
可靠性
- 归档或消失的会话会自动换绑,并给你一句提示(不是把消息吞掉): 原对话被归档了 → 新建一个接上,你收到通知。
- 状态写入在 Windows 上有重试与退避(应对「文件被占用」那类 EPERM)。
- 空内容/未知帧类型的入站消息留日志(不再零痕迹)。
agent 能用的三个工具
| 工具 | 作用 |
|---|---|
| send_to_qq | 把本机文件发到提问的那个对话。图片变照片、视频变可播放片段、其它变文件附件 |
| notify_qq | 从任意会话推送到你当前所在的 QQ 对话。这是「干完了告诉我」的实现,实测可用(连发十条全部送达,其中后六条是主动推送) |
| qq_session_control | 查看/切换/新建/中断对话,以及查看或切换模型与推理等级 |
名字里的
qq_/_qq是有意的:工具名在宿主作用域下全局唯一, 而微信版已经占了session_control。两个插件抢同一个名字,后注册的那一方会静默失败。
这三个是插件给的。定时任务不在其中,因为它不需要插件实现:它走 DSH 宿主自带的
schedule_create / schedule_list / schedule_delete / schedule_update,
插件只负责把结果发到 QQ 这条通道上。
设置
设置 → QQ,按页面上的顺序一共六组:
- 帮助 —— 页面上那个「打开使用手册」按钮,打开的是用户手册(
docs/HANDBOOK-USER.md,指令与设置项都以它为准) - 连接 —— 通道开关、扫码绑定、已绑定账号、解绑
- 权限 —— QQ 会话的权限预设(只有
auto和「跟随 DSH 设置:和桌面端一样」还可能弹审批提示) - 行为设置 —— 见下表
- 指令 —— 页面上印的斜杠指令速查(
/help、/new、/list、/switch、/current、/cancel、/model) - 诊断 —— 日志尾、工作区路径、会话数、待处理交互
这是设置页的全貌(下面几张是把它分段放大):
这是页面顶部两张卡片,也是你第一次装完会看到的东西——「帮助」与「连接」:
扫码绑定就在这张图的下半部分:点「重新扫码」,用手机 QQ 扫一扫并在手机上确认。 你不需要事先去腾讯开放平台注册,也不需要准备 AppID/AppSecret——机器人应用是那次扫码当场创建的。 「已绑定账号」那一行是绑定之后的样子(AppID ← 一个不可逆的指纹 · 绑定时间), 旁边「已连接」是通道此刻的状态。
中间两组是权限与指令速查:
权限预设是三个中文选项,图里选中的是默认的那个:
| 选项 | 含义 |
|---|---|
| 完全访问:不询问,直接执行 | 默认。agent 不再问你——问了的话消息发到 QQ,而任务会一直等你回答 |
| 自动审核:电脑上判断 | 让电脑上那套策略来判,可能弹提示 |
| 跟随 DSH 设置:和桌面端一样 | 和桌面端完全一致,可能弹提示 |
只有后两个还可能把权限提示发到 QQ。想验证「权限审批能不能回流到手机」这件事, 就把这里切成能弹提示的那个预设——那时候你手机上会收到一条待批准的请求, 回「允许」/「拒绝」,或者回数字选项都算回答。
这一组就是 /help 在设置页里的样子,指令与设置项都以用户手册为准。
下面那句「说大白话也可以」也是真的:DSH 会先说明它要做什么,你同意后再执行——
想跳过这一步,把上面的「会话操作先确认」关掉。
页面下半部分的行为设置与会话文件夹路径:
会话文件夹这一项带原生文件夹选择器(就是那个「选择…」按钮)。 输入框里是你填的原文,右边「当前生效」是实际在用的路径——两者分开是有意的, 填错时你能看到自己填了什么去改,同时知道现在实际在用哪个。
最后是诊断区,出问题时的第一条线索:
诊断区给的是日志路径 + 末尾日志,所以大多数问题你自己就能看出是发失败了、
限流了,还是附件没落盘。图里最后一行正好是一次成功的 send_to_qq。
把这些卡片加起来,一共九个设置项:
| 设置 | 默认 | 说明 |
|---|---|---|
| permissionPreset | danger-full-access | QQ 会话的权限预设。权限提示发到手机上会让任务一直等你回答,所以默认不设闸——见安全 |
| requireConfirmation | 开 | agent 执行会话操作前是否先问你一次 |
| allowCrossSessionNotify | 开 | 关掉后 notify_qq 只能推给调用者自己的会话(更严的姿态) |
| mergeWindowMs | 10000 | 纯附件消息先停这么久,等你补一句指令;0 = 不等待 |
| autoReplyAttachments | 开 | 窗口内没等到指令时,是否把附件交给 agent。关掉不是忽略附件,而是等用户说话 |
| maxReplyChars | 4000 | 超过就作为 .md 文件发送;0 = 不限制 |
| settleMs | 1200 | 连续流片段之间静默多久就发出去 |
| presenceNote | 一段中文说明 | 随每条消息交给 agent:你在手机上、看不到这台电脑的屏幕,要看东西就直接发 |
| workspacePath | 空 = $DSH_HOME\dsh_qq | 会话文件夹。必须是绝对路径;填错只会「没生效」,绝不会让插件起不来 |
设置会持久化,重启后仍是你选的那份。老状态文件(没有 settings 块)照常可读。
每一项设置只定义在一处——lib/settings.js 的 SETTINGS_SCHEMA,每项一个校验器加一个
default。加载归一、落盘、设置页快照与往返测试全部由它派生,所以加一项设置就是在那张表里
加一行,其余各处不可能各走各的。纯 JS 没有编译器能拦住"加了设置却忘了加校验器",
所以 scripts/verify.mjs 里有一条断言:schema 的键集、"运行时此刻实际持有"的键集、
以及真正写到盘上的键集必须是同一个集合——那条断言就是我们没有的那个编译器。
(微信插件踩过这条故障:三份手抄名单各改各的,加设置时漏掉一份就会静默丢字段,
而且每次写盘都重新加载并重新规范化,于是下一次写盘把丢失持久化了。)
workspacePath 旁边显示的是实际生效的路径,而输入框里是你填的原文。两者分开是有意的:
填错时你能看到自己填了什么去改,同时知道现在实际在用哪个。
⚠️ 改路径不搬动已有的对话。 它们留在原来的文件夹,只有之后新建的用新路径。 这是唯一安全的做法——宿主在采用已存在会话时比对
cwd,不一致会抛ApiSessionCwdConflict,那个会话就永远接不回来了。
文件夹布局
| 用途 | 路径 |
|---|---|
| 会话工作区(桌面端显示为「QQ 会话」) | $DSH_HOME\dsh_qq(可用 workspacePath 改) |
| 入站媒体 + 长回复 .md | <会话工作区>\媒体 |
| 插件状态与日志 | $DSH_HOME\qq\state.json、$DSH_HOME\qq\boot.log |
出问题了看哪里
| 现象 | 先看哪里 |
|---|---|
| 设置页看不到 QQ 区块 | 客户端插件没加载。看 %APPDATA%\@deepseek-ai\dsh-desktop\logs\crash-*-web-boot.log 的 renderer console 段 |
| 整个应用起不来 | 同上。那条致命错误本身信息量很少,真正的原因在 renderer console 里 |
| 机器人不回消息 | boot.log 里有没有 入站;GET /.dsh-qq-plugin/status 的 errors 数组 |
| 图片/语音没反应 | boot.log 里找 附件下载失败 或 附件已停靠 |
| 「发不出去」 | 看 boot.log 里有没有 待发已入队——主动推送的规则可能变,插件不假设额度;没送达的消息会在你下次说话时补发 |
调试用的 HTTP 端点都在 /.dsh-qq-plugin/ 下:status、log、settings、
permission-presets、accounts、login、start、stop 等。
与微信版的差异
功能上对齐,但有几处是有意不同,因为 QQ 协议给了微信那边没有的东西:
| | |
|---|---|
| 引用消息 | QQ 的 msg_elements 直接给被引正文,所以微信那套 quoteHistory 缓存不需要 |
| 语音 | QQ 服务端直接给 voice_wav_url + asr_refer_text,不需要 SILK 解码,也没有随之而来的那句免责 |
| 待发队列 | 微信因为 iLink 不允许主动发而必须排队;QQ 主动推送可用,队列的用途是把没送达的消息补发(网络、平台拒绝、规则变化都算),不是某一种失败的兜底 |
| 工具名 | 见上文(宿主作用域全局唯一) |
| 安装 | 没有构建步骤,也没有 allowBuilds 询问:本插件就是普通 JavaScript,Git 安装直接打包就能跑 |
安全
只有你能碰到它。 通道只跟腾讯官方的 QQ 机器人服务说话,没有别的东西—— 没有第三方服务,中间也没有自建中转。机器人是一个独立的应用,管理员是你的 QQ 账号, 而且是你自己扫码创建的;未认证的机器人仅管理员可用。它不是一个能发出去的 QQ 名片: 没有地方能搜到它,也没有人能加它。不存在「别人也能给它发消息」这个面, 所以这里没有白名单——平台自己的规则就是白名单。
AppSecret 是长期密钥,就按长期密钥对待。 它以 grant 记录写进 DSH 的凭据存储,
而不是写进插件的明文状态文件(那里只有 AppID)。要轮换就回设置页重新绑定一次。
还有三件事是真的,值得知道:
QQ 来的对话默认全权限运行——agent 动手前不问。这是有意的:权限提示会发到 QQ,
然后任务就在那里等你,于是在手机上每条命令都会卡在你早已走开的那个对话里。
想要和坐在电脑前一样的护栏,就把设置页里的「QQ 会话的权限预设」改成 auto
(或改成跟随 profile 自己的策略)。
agent 以你的身份运行。 它能执行命令、读写文件,用的是你这个用户的权限—— 它本来就是干这个的,也就是说这台机器上你能做的事它都能做。所以:走开之前扫一眼你让它干的 是什么,离开时锁屏。锁屏不会让 agent 停下来,但能挡住路过的人。
默认任意会话都能推送到你的 QQ。 这是「干完了告诉我」能成立的原因:干活的那个会话通常不是 你 QQ 对话绑定的那个,不放开这一条它就报不了自己的结果。关掉 allowCrossSessionNotify 就回到更严的规则:只有绑定的那个会话能发。
文档
| | | |---|---| | docs/HANDBOOK-USER.md | 用户手册——设置页那个帮助按钮打开的就是它,指令与设置项都以它为准(中文) | | docs/POSTMORTEM.md | 每一个真实踩过的坑——每一条都付了代价,也每一条都是一个检查器存在的理由(中文) | | docs/SETTINGS-CONTRACT.md | 设置页背后的宿主/客户端路由契约,逐条读自真实调用点(中文) | | docs/RELEASING.md | 一次版本怎么发:tag、OIDC,以及一次性的 Trusted Publisher 配置(中文) | | docs/STEP3-能力调研与实现规格.md | QQ 交互消息与流式的能力调研。状态:暂缓,未实现——当前代码里没有任何按钮、也没有流式;调研结论是真的,留着给将来重启这项工作用(中文) |
开发
普通 JavaScript,没有构建步骤,没有转译器。CI 跑 Node 24;manifest 里不写 engines 范围——
本插件只需要跑在已经在场的那个 DSH 宿主里。
npm ci
node scripts/verify.mjs # 宿主侧
node scripts/verify-client.mjs # 客户端注册形状(不是视觉验证)
node scripts/verify-send-path.mjs # 发送路径,跑在假 SDK 上822 项宿主侧检查、108 项客户端检查、60 项发送路径检查。
这三个数字是从上面那几条命令的实际输出里读出来的,不是手打的:
tools/check-doc-counts.mjs 拿它们和脚本真正打印的数比,对不上时 CI 会失败并点名是哪个文件。
改了测试就改这里的数字,否则这道闸会拦住构建。
常设检查器
tools/ 里有四个检查器,每一个都对应一次真实事故,改动前请先跑:
node tools/check-unbound.mjs . # 用了但没导入
node tools/check-unused-imports.mjs . # 导入了但没用
node tools/check-doc-counts.mjs --run # 文档里的测试数量,以及 publish.yml 里的仓库名
node tools/check-cross-plugin.mjs <profileDir> # 跨插件共享资源check-doc-counts.mjs 从三个测试套件的实际输出里读出那三个数,再和本文件、README.md、
docs/RELEASING.md 里写的比,对不上就点名是哪个文件、写的多少、实际多少。CI 里它读的是三个
测试步骤已经落盘的输出,所以只多花一秒,而不是再跑一遍。--run 是本机用法:它自己把套件跑一次。
它还顺带核对第二样东西,理由相同:publish.yml 文件头那四个 Trusted Publisher 字段也是手抄的,
抄错的表现只是发布时 403,所以它拿那一块和 package.json(属主与仓库名)、工作流自己的
文件名与 name:、以及两版 README 的 CI 徽章逐个比。这里曾经写着 dsh-qq,而其它每一处都是
dsh-qq-plugin——这道闸就是为那次而加的。
check-cross-plugin.mjs 是本地检查,不进 CI:它要读一个真实的 DSH profile——
dsh.profile.bundles 那份列表,以及该 profile node_modules 下已装好的插件——
CI 的 runner 上两样都没有。它查的是所有插件共用的两样东西:客户端插件的顶层作用域
(shell 会把每个已启用 bundle 的 client.js 拼进同一个脚本,一份文件顶层的 const
就会和别人的撞车),以及宿主作用域的工具名(全局唯一)。
每个检查器都带反向对照:它必须能抓住它为之而写的那次故障,抓不住就判定为不可信并以 非零码退出。一个永远报「通过」的检查器,比没有检查器更糟——它让人以为已经验过了。
smoke/ 里两个探针(step3-probe.mjs、step4-probe.mjs)连的是腾讯的线上服务,需要真实凭据与
真实机器人;另外两个(step4-surface.mjs、check-sdk-surface.mjs)是离线表面核对,凭据和网络都不
需要。四个都不进 CI——两个探针是因为凭据不该出现在公开仓库的 CI 里,两个表面核对是因为它们是
一次性的调研脚本,只打印观察结果、不做通过/失败判定。
许可
MIT —— 见 LICENSE。
