dsh-wechat-status
为 dsh-wechat 补上缺失的掉线提醒:微信桥接
会话失效、需要重新扫码时,在 DSH Web 界面弹出醒目横幅——而不是让你自己翻设置页,
或者发现消息石沉大海才知道。
以静态 Cordis 插件交付,Host + Client 双半,零 @deepseek-ai/* 运行时依赖。
⚠️ 重要声明
请在使用前完整阅读本节。
全程由 AI 编写
本项目的全部代码、文档与测试均由 AI 生成,未经专业软件工程师人工审查。 AI 生成内容可能包含逻辑缺陷、安全漏洞、平台兼容性问题或与事实不符的描述, 即使作者已尽力验证,仍可能存在未被发现的错误。
按「现状」提供,不承担任何责任
本项目按 "AS IS" 提供,不作任何明示或暗示的担保,包括但不限于对 适销性、特定用途适用性、无侵权的担保。
在适用法律允许的最大范围内,作者及任何贡献者不对因使用或无法使用本项目 而产生的任何直接、间接、偶然、特殊、惩罚性或后果性损害承担责任,包括但不限于:
- 数据丢失、账号异常、消息发送失败或丢失
- 服务中断、设备损坏、业务损失
- 因使用本项目而违反任何第三方(含腾讯)服务条款所导致的后果
使用风险由你自行承担。
仅供学习与研究
本项目仅供个人学习、技术研究与交流,不得用于:
- 任何商业用途
- 群发消息、营销推广、骚扰他人
- 任何违反《微信个人账号使用规范》《微信 ClawBot 功能使用条款》或 相关法律法规的用途
底层依赖腾讯官方 iLink(微信 ClawBot)协议,接口可能随时变更或终止。 请遵守腾讯的相关服务条款,因违规使用导致的一切后果由使用者自负。
与上游项目无关联
本项目为非官方第三方插件,与 DeepSeek Harness、dsh-wechat、腾讯及微信
均无任何隶属或背书关系。
为什么需要它
dsh-wechat 的会话有效期是 24 小时(腾讯 iLink 服务端限制)。正常情况下它会自动调
notifyStart 重建服务端会话,你无需干预。但当自动恢复连续失败后,它只做三件事:
markTokenGiveUp() {
this.tokenGiveUp = true;
this.log("-14 recovery failed; re-scan required — discarding parked outbound");
this.discardQueuedOutbound("session-timeout-give-up");
}
置标志、写一行日志、把队列里待发的消息全部丢弃。 没有任何推送。你只能靠主动打开 DSH 设置 → WeChat 看状态,或者发现发出去的消息没有回应。
这个插件补上的就是这个缺口。
功能
- 状态轮询 — Host 侧每 15 秒读取
dsh-wechat的状态接口,归约为level / text / hint / qrUrl四个标量 - 掉线横幅 — Client 侧注册在
shell.overlay,状态异常时在界面底部居中弹出 - 零干扰 — 一切正常时完全不渲染(返回
null),不占任何视觉空间 - 一键扫码 — 横幅上的「打开二维码」直接打开扫码页,不用再翻设置
- 降级容错 —
dsh-wechat未加载(404)、上游报错、响应非法 JSON、连接超时, 全部退化为可见状态而非抛异常
行为表
| 桥接状态 | 界面表现 |
|---|---|
logged-in 微信在线 |
不显示(正常态零干扰) |
waiting-qr 等待扫码 |
🟡 琥珀色横幅「等待扫码登录」+「打开二维码」 |
failed 掉线需重扫 |
🔴 红色横幅「微信已掉线,需要重新扫码」+「打开二维码」 |
| 插件未加载(HTTP 404) | 🔴 红色横幅「微信插件未加载」 |
| 状态不可读 | 不显示(避免误报) |
安装
部署到 DSH profile:
# 从 GitHub 安装
npx @deepseek-ai/dsh plugin --profile <profile> add github:alk233/dsh-wechat-status
# 或本地开发(link 方式)
npx @deepseek-ai/dsh plugin --profile <profile> add "file:/path/to/dsh-wechat-status"
# 验证组合配置(应看到 "- id: dsh-wechat-status" 行)
npx @deepseek-ai/dsh --profile <profile> --dump-config
重启 DSH 后生效。
⚠️ 别用
npx dsh plugin——npm 上dsh这个名字早在 2016 年就被一个不相关的 JS shell 包占用(dsh@1.0.1,作者infusion),它没暴露 CLI bin,会报could not determine executable to run。DSH 的 CLI 在 scoped 包@deepseek-ai/dsh下,必须用完整名。
前置依赖
需要先安装并登录 dsh-wechat。本插件只读取它的
状态,不修改它的任何行为:
npx @deepseek-ai/dsh plugin --profile <profile> add dsh-wechat
# 重启后打开 http://127.0.0.1:3080/wechat/qr 扫码登录
架构
dsh-wechat dsh-wechat-status (本插件)
│ │
│ GET /wechat/api/status │ Host 半部:每 15s 轮询
│ ◄─────────────────────────────────────┤ 注册 exact 路由
│ │ /dsh-wechat-status/api/status
│ ▼
│ 归约为 {level,text,hint,qrUrl}
│ │
│ │ Client 半部:同源 fetch 轮询
│ ▼ 注册 shell.overlay → 渲染横幅
│ DSH Web 界面(底部居中)
Host 半部(lib/index.js)
inject = ['timer', 'webServer']——两个都以 context 属性访问,因此都必须声明- 在
webServer上注册exact路由/dsh-wechat-status/api/status,返回200 / application/json; charset=utf-8 / cache-control: no-store - 上游端口取
ctx.webServer.port(不硬编码 3080,因此--port 0随机端口部署同样正确) webServer缺失时 inert 返回、不抛异常——保证任何情况下都不会拖垮 plugin tree- 路由 handler 全程 catch;
refresh()经inFlight去重且永不 reject - route 与 poll 的 disposer 由同一个
return ctx.effect(...)统一持有
Client 半部(lib/client.js)
- 手写惰性 CJS bundle(
window.__ModuleLoader__.load({id, factory})),仅require('react')(属 shell 冻结的基线模块表,故dsh.client.external为空) inject = ['slots'],注册shell.overlay(list类型,id必填)- 同源
fetch拉取状态,window.setInterval每 15 秒一次 - 样式在组件
React.useEffect内创建<style>插入document.head,cleanup 时移除 - 组件不引用外层
ctx/host,无任何动态插件专属 Builtin
设计说明
这里用的全部是持久化包可用的机制。不依赖动态 Cordis 插件求值作用域里的任何东西
(没有 harness、没有 host.call、没有 styles、没有隐式 Builtin)——因为从 profile
加载的包由 profile 自己的 Cordis 树组装,这些一个都不存在。
为什么走 HTTP 而不是直接调用:dsh-wechat 没有为它的 bridge 暴露 Host 服务,其状态
只能通过它在 web carrier 上注册的路由取得。直接读磁盘上的 state.json 回答的是另一个
问题(「曾经登录过吗」),而不是我们关心的那个(「此刻活着吗」)。
开发约定(重要)
pnpm 的 file: 安装是硬链接。用编辑器原地修改源码会断开链接,导致
profiles/<profile>/node_modules/ 里的副本停在旧修订——改了代码却测试旧版本。
改完源码必须三步走:
# 1. 重装
npx @deepseek-ai/dsh plugin --profile <profile> add "file:/path/to/dsh-wechat-status"
# 2. 核对哈希(两者必须一致)
# 源码 lib/*.js vs profiles/<profile>/node_modules/@dsh-external/dsh-wechat-status/lib/*.js
# 3. 真实启动验证(出现 token URL 行即成功)
npx @deepseek-ai/dsh --profile <profile> --port 0 --no-open
# 期望输出:dsh web: http://127.0.0.1:<port>/?token=...
注意:部分 profile 的 cordis.patch.yml 会被第三方插件(如
dsh-remote-web-ui)托管并钉死 webserver 的 port,此时 --port 0 会被覆盖。若遇到
EADDRINUSE,改用隔离 profile 验证,或临时 --patch 覆盖该行。不要为了腾端口去停掉
正在使用的实例。
已验证
| 项目 | 结果 |
|---|---|
| 插件树加载 | ✅ 无 harness is not defined / plugin tree failed to load / cannot get property |
| 状态路由 | ✅ HTTP 200 + 合法 JSON |
| 客户端物化 | ✅ __DSH_BOOT__ 含本包条目,<style> 注入生效,横幅真实渲染 |
| 热重载 | ✅ 禁用 → 路由 404(disposer 生效);启用 → 200 且无重复路由;进程存活 |
| 降级路径 | ✅ 上游 404 / 非 JSON / 超时 全部退化为可见状态 |
| 硬编码检查 | ✅ 源码无 127.0.0.1:3080 字面量 |
No comments yet. Be the first to write one.