DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Qq Bot — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
Q

dsh-qq-bot

Qq Bot

把 QQ 接到 DSH Agent 的插件:通过 NapCat 等 OneBot 11 实现收发消息,每个 QQ 会话对应一个 DSH Agent。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:arrow1031/dsh-qq-bot#a70539df6dd359e00118400288f5eba19cb28e74
README兼容性版本

兼容性与来源证明

Qq Bot 以 dsh-qq-bot 发布,当前版本为 0.6.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/9/21

版本

0.6.0stable
2026/9/21

相关插件

正在加载相关插件…

最新版
0.6.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/9/20
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

README

dsh-qq-bot —— 把 QQ 接到 DSH Agent

⚠️ 维护状态:已封版,不承诺更新

功能完整、123 项测试全绿,但作者不做持续性维护——不修 bug、不适配上游变更、不接受功能请求。 遇到问题请 fork 下来自己改(MIT 许可,随便改随便发)。 为方便动手,仓库里有 ARCHITECTURE.md:文件职责、数据流、设计决策原因、分步改动指南。

作者 @arrow1031 · 许可 MIT · AI 生成声明与已知限制 AI-DISCLOSURE.md · 更新日志 CHANGELOG.md

个人网站 sputnikzaychik.icu · QQ 群 1091766276

一个可以装进 DSH profile 的插件:QQ 消息进来 → 交给 DSH Agent → 回复发回 QQ。

复用现成生态,不自造协议:

QQ 客户端
   │ QQ NT 协议
   ▼
NapCat(或 OneDisc / Lagrange 等 OneBot 11 实现)   ← 登录、收发、风控
   │ OneBot 11:正向 WS / 反向 WS + HTTP API
   ▼
lib/onebot-adapter.mjs(本插件自带,零 npm 依赖)    ← 协议翻译层
   │ stdin/stdout 换行分隔 JSON
   ▼
lib/index.js(Cordis 插件)                          ← 每个 QQ 会话一个 DSH Agent
   ▲ 设置面板读写(lib/client.js)                        ← 「设置 → QQ 机器人」选项卡
   │ sessionController → agent.followup → whenIdle
   ▼
DSH Agent(默认模型 + preset 人格 + 工具)

一、已验证的东西(不是设计稿)

全部在本机实跑:

测试命令结果
正向 WS 适配器回归node test/test-adapter.mjs19/19
反向 WS 适配器回归node test/test-adapter-reverse.mjs12/12
打包后插件端到端(Host)node test/test-plugin.mjs25/25
客户端设置页(含真实渲染冒烟)node test/test-client.mjs31/31
权限·群管理·访客工具闸node test/test-admin.mjs36/36
组合能否加载dsh --profile web --dump-config --patch <补丁>通过(行被正确插入)
装到 profile 后的模块解析解包到 profile 后 import('@deepseek-ai/dsh-tools')通过

test-plugin.mjs 用假 ctx(自己实现了 subprocess / sessionController / timer / effect / tools)加载真正的 lib/index.js,配一个真的假 OneBot 服务,跑完整条链路:假 QQ 消息 → 假 OneBot → 真适配器 → 真插件 → 假 Agent → 回复 → 真适配器 → 假 OneBot。所以不需要 DSH、不需要 QQ 号、不需要网络就能回归。

另外,本插件的前身(examples/dynamic-package-host.js)曾用动态 Cordis Package 的形式在真 DSH 里跑过完整链路:真模型回复、会话落盘、qqbot 工具注册都验过。所以 Cordis 服务接线本身也是验证过的。

二、快速开始

1. 起一个 OneBot 11 实现(推荐 NapCat)

NapCat 官方 Docker 镜像(支持 amd64 与 arm64):

docker run -d --name napcat --restart=always \
  -e NAPCAT_GID=$(id -g) -e NAPCAT_UID=$(id -u) \
  -p 3000:3000 -p 3001:3001 -p 6099:6099 \
  mlikiowa/napcat-docker:latest
  • WebUI:http://<主机IP>:6099/webui,用手机 QQ 扫码登录;登录 token 默认在 docker logs napcat 里。
  • 容器内配置目录 /app/napcat/config,QQ 数据目录 /app/.config/QQ(想固化登录就挂这两个卷)。
  • 在 NapCat 的「网络配置」里确认 OneBot 11:
    • HTTP 服务器 3000 —— 用来发消息
    • 正向 WebSocket 3001 —— 用来收事件
    • 若设了 access_token,记下来填进插件配置
  • 端口默认就是 3000/3001,和本插件默认值一致,不用改代码。

2. 安装插件

dsh plugin --profile web add /path/to/dsh-qq-bot-<版本>.tgz

dsh plugin 本质是 pnpm 的前置封装:装完包后,它会自动把声明了 dsh.bundle.patch 的依赖加入 profile 的层栈(本包的 package.json 已声明)。然后重启 dsh web 生效。

升级注意(重要):dsh plugin add 转发给 pnpm,而 pnpm 按 tarball 的完整性哈希缓存。 改了代码必须打成新的版本号文件名再装(0.3.0 → 0.3.1);沿用同一个文件名会被判成“lockfile 已最新”而跳过,装的还是旧内容。 npm pack 默认就带版本号,所以正常发版流程不会踩到。

客户端改动要多一步:lib/client.js 是浏览器侧代码,装好并重启 dsh web 之后, 还要刷新浏览器页面才会加载新的客户端 bundle。

不想打包也行,直接 dsh plugin --profile web add <你的 git 仓库或 tarball 地址>。

3. 在 QQ 里试

  • 群聊:默认要 @机器人 才有反应,@ 会被去掉再交给 Agent。
  • 私聊:默认直接回。
  • 长回复按换行切成 ≤1200 字多条发送。
  • 每个群/每个人是独立的 DSH 会话(id 形如 qqbot-group-33333),会落盘并出现在 Web UI 会话列表里,可以像普通会话一样打开继续聊。

三、两种连接模式,怎么选

场景用哪个配置
NapCat 和 DSH 在同一台机器/同一容器正向 WS(默认)mode: forward,我们连 ws://127.0.0.1:3001
NapCat 在别的容器/主机,或网络方向只允许 NapCat 连出来反向 WSmode: reverse,我们在 0.0.0.0:6199 监听,NapCat 里填 ws://<DSH主机IP>:6199/

反向 WS 的鉴权:请求头 Authorization: Bearer <token> 或 URL 上的 ?access_token=<token> 都接受(后者是因为浏览器/Node 的标准 WebSocket 客户端无法自定义请求头)。

四、配置

方式一:在 DSH 设置里改(推荐)

重启 dsh web 后,打开 设置 → QQ 机器人,所有配置都在这一个选项卡里: 连接方式、WS/HTTP 地址、access_token、群聊开关与 @ 要求、白名单、命令前缀、 回复分片长度、慢回复提示、来源前缀、每会话独立、preset、工作目录、逐帧日志。

  • 改完点「保存」;连接方式那几项(模式/地址/token)保存后会自动重启适配器,其它项下一条消息生效。
  • 类型映射:布尔是勾选框;白名单是逗号分隔的文本框;连接方式是下拉框。
  • 配置分两层:composition 行配置是 base 层,设置面板里改的是 用户层(存在 $DSH_HOME 的设置文档里)。
  • 连接状态不在设置页里显示,在对话里让 Agent 调用 qqbot 工具(action=status)看。
  • access_token 是只写字段:已设置时页面显示「已设置;留空则保持不变」,只有输入新值才会覆盖。 Host 侧把它声明成 schema 的 secret,值不会下发到浏览器,保存时也不会被空值清掉。

方式二:改 composition 行配置(当 base 层用)

不要改包内的 cordis.patch.yml(升级会覆盖)。写进你自己的 $DSH_HOME/profiles/web/cordis.patch.yml —— 它在所有 bundle 之后应用,按 id 定向覆盖:

- id: dsh-qq-bot
  config:
    mode: forward                 # forward | reverse
    workspace: '/home/dsh'        # 每个 QQ 会话 Agent 的工作目录
    onebot:
      wsUrl: 'ws://127.0.0.1:3001'      # mode=forward 用
      listen: '0.0.0.0:6199'            # mode=reverse 用
      httpUrl: 'http://127.0.0.1:3000'  # 留空则改用 WS action 发消息
      accessToken: ''
    group:
      enabled: true
      requireAt: true             # 群里是否必须 @
      allow: []                   # 群号白名单,空数组 = 所有群
    private:
      enabled: true
      allow: []                   # 用户号白名单,空数组 = 所有人
    commandPrefix: ''             # 例如 '/ai',空串 = 不限制
    reply:
      maxChars: 1200
      ackAfterMs: 8000            # 慢回合先回「正在处理」;0 关闭
      ackText: '正在处理,请稍候…'
      contextHeader: true         # 给消息加 "[QQ群 123 · 小明(22222)]" 前缀
      firstTurnHint: '(提示:这是一次 QQ 聊天。请用简体中文、简洁自然的口语作答;不要输出 Markdown 表格,代码块保持简短。)'
    auth:                         # 权限,见「五、权限与群管理」
      owners: []                  # 拥有者 QQ:全权限 + 永久授权 + 可走独立会话
      superAdmins: []             # 后台设定的高级管理员(不可永久授权)
      ownerPreset: ''             # 填了才把拥有者/非拥有者分成两条会话
      guestPreset: ''             # 非拥有者用的 preset(仅人格分离,非安全边界)
      guestDshTools: false        # 是否允许访客用 DSH 内部工具(默认否)
      guestToolAllow: ['qqbot', 'qqgroup']   # 访客仍可用的工具白名单
      grantTtlMinutes: 60         # 群管理员动态授权默认时长
      grantMaxTtlMinutes: 1440    # 群管理员单次可授最长
    admin:
      enabled: true
      manageGroups: []            # 可管理的群;空 = 回退用 group.allow
      requireGroupAdmin: true     # 是否承认「本群管理员」这一路权限
      prefix: '/'                 # 指令前缀(必须在消息首位)
      dryRun: false               # true = 只回复将执行什么
      allowConsole: false         # 允许 Web UI 会话直接调用管理工具
    perChatSession: true          # true=每个 QQ 会话一个 DSH 会话
    agentPreset: ''               # ''=部署默认 preset;可填自定义 preset id 换人格/工具
    adapterPath: ''               # ''=用包内自带适配器
    nodePath: ''                  # ''=自动解析 node
    logAdapterFrames: false       # 排查时开,会打印每一帧 JSON

agentPreset 是换人格的正规做法:给 QQ 机器人做一个专属 preset,把 id 填进来。

五、权限与群管理

权限是三路并行取并集(不是"先命中先返回"):

路径谁能做什么
静态名单 auth.owners拥有者全部权限 + 永久授权 + 可走独立的拥有者会话
静态名单 auth.superAdmins后台设定的高级管理员群管理权限(不能永久授权)
动态授权被临时授权的人仅被授予的那几项,仅限本群、带时限
本群身份该群的 admin / owner群管理权限(可用 admin.requireGroupAdmin 关掉这一路)

任一路给到某项权限即拥有它;来源会在 /whoami、/perms 里如实列出,便于排查"他为什么有权"。

权限项:query(查询)、mute(禁言)、kick(踢人)、recall(撤回)、settings(群名/名片/管理员/全员禁言)、grant(给本群成员授权),拥有者另有 permanent(永久授权)。

动态授权(群维度,不可跨群)

  • 拥有者或本群管理员可以给本群成员临时授权:/grant @某人 mute 30(30 分钟)
  • 只有拥有者能永久授权:/grant @某人 kick forever(非拥有者发这条会被拒)
  • 群管理员单次最长 auth.grantMaxTtlMinutes(默认 1440 分钟)
  • 授权键是「群号 + QQ 号」:在 A 群授的权,在 B 群一律无效
  • /revoke @某人 [权限] 撤销、/grants 列出本群全部授权

非拥有者不使用 DSH 内部功能:执行级工具闸

用一个 tools.guard 硬闸,只在建会话时注册一次,判据是闭包里的可变标志位:

  • 注册在 agent.ctx 上 → 只作用于该会话的 agent
  • 每次工具执行时才求值 → 拥有者/访客交替说话不需要增删限制
  • 用白名单(auth.guestToolAllow,默认 qqbot,qqgroup)→ run_code(PTC 保留传输)这类"不在全局层"的特殊工具也会被自动挡住
  • 被拦时返回拒绝原因,模型会把它转达给用户(最多多一次"被拒绝的调用"往返)

因此它同时满足:0 额外 token(不注入提示词、不改工具表、不记 request/header)、单会话单历史、不改 DSH 本身。

想放开就设 auth.guestDshTools: true(不建议)。

为什么不用"每条消息增删 tools.restrict":restrict 是静态过滤器,想按说话人变就得反复增删,工具面抖动会让 agent loop 多记 request/header,反而更费 token、代码也更多。guard 是每次执行才求值的函数,配一个可变标志位就够。

会话分流(可选,仅供人格分离)

auth.ownerPreset / auth.guestPreset 都留空时,拥有者与非拥有者共用一条会话(默认,也是推荐)。填了 preset id 才分成两条:

  • 拥有者走 qqbot-group-<群号>,挂 ownerPreset
  • 非拥有者走 ...-guest,挂 guestPreset

这只是换人格,不是安全边界——工具隔离由上面的 guard 负责。分流要付代价:同一个群会有两条历史,拥有者看不到访客说了什么。除非确实要给访客不同的说话风格,否则别开。

上下文头会带身份,例如 [QQ群 33333 · 小明(22222) · 身份:访客|本群管理员+动态授权[mute 至 ...]],让 Agent 自己决定怎么对待这个请求。

指令的严格性(防"聊天内容夹带指令样内容"被误执行)

  1. 前缀必须位于消息首位(前面只允许空白)——你好 /mute @x 不会被当成指令
  2. 动词必须完全匹配已知指令,未知动词照常交给模型
  3. token 数量必须落在该指令声明的 [min,max] 内,多余内容判为用法错误、不执行
  4. 权限不足直接拒绝,且不做任何动作

指令不需要 @ 机器人(刻意放在 requireAt 之前),但上述规则一条都不放宽。

指令表

指令权限作用
/help—列出全部指令
/whoami—看我在本群的身份与权限来源
/perms [@某人]query看某人在本群的权限来源(三路)
/grantsgrant列出本群的动态授权
/grant @某人 权限[,权限] [分钟|forever]grant临时授权(仅本群)
/revoke @某人 [权限]grant撤销授权
/membersquery成员列表(最多列 30 条)
/infoquery群信息
/mute @某人 [分钟=10]mute禁言(0 = 解禁)
/unmute @某人mute解除禁言
/kick @某人kick移出本群
/recall [消息id]recall撤回;回复某条消息再发 /recall 可省略 id
/banall on|offsettings全员禁言开关
/card @某人 新名片settings改群名片
/admin @某人 on|offsettings设/撤管理员
/rename 新群名settings改群名

Agent 工具(与指令共用同一套鉴权)

qqgroup 的 action:只读 group-list/group-info/member-list/member-info,写操作 mute/unmute/kick/whole-ban/set-card/set-admin/rename/recall/poke。

工具的"说话人"取自本会话最近一条消息,所以群里普通成员没法靠一句话驱动管理动作——这一点是提示注入的主要防线。group_id 留空时默认用当前会话所在的群。

群事件

适配器会把完整事件字段透传上来(operator_id 谁操作的、duration 禁言时长、target_id、card,以及被引用消息的 reply_to)。当前版本只做透传与计数(qqbot status 里能看到),自动治理尚未实现。

建议的上线顺序

  1. 在设置里填 auth.owners(至少填自己)
  2. 打开 admin.dryRun,在群里发几条指令确认解析与回执
  3. 关掉 dryRun,再按需加 auth.superAdmins
  4. 非拥有者用 DSH 功能由访客工具闸默认拦截,无需额外配置;确实想放开再改 auth.guestDshTools

六、Agent 能用的工具

插件注册了一个动态工具 qqbot:

  • {action:'status'} —— 连接状态、模式、适配器 pid/端口、各 QQ 会话的回合数、统计计数
  • {action:'send', message_type:'group'|'private', target_id:'123', text:'...'} —— 主动发一条 QQ 消息
  • {action:'interrupt', conversation:'group-123'|'all'} —— 中断正在跑的回合

qqgroup(群管理)

见「五、群管理」一节。工具路径的授权依据是「当前会话最近一条消息是否来自管理员白名单」。

七、排错

现象检查
日志一直「OneBot 未连接」NapCat 的「正向 WebSocket」是否开在 onebot.wsUrl;reverse 模式下 NapCat 是否指向了 ws://<DSH>:6199/;防火墙;token 是否一致
能连上但收不到消息OneBot 版本要选 v11;不能只开 HTTP,必须开正向或反向 WS
群里 @ 了也不回group.enabled;group.allow 白名单;self_id 与消息里的 self_id 是否一致(logAdapterFrames: true 能看到原始帧)
回「(本轮没有产生文字回复:...)」括号里是从会话日志读出的 turn/end 原因,据此定位(模型路由、额度、工具报错等)
发送失败NapCat 的 HTTP 服务器(默认 3000)是否开着;accessToken;看日志里的「发送失败」
日志在哪插件前缀 [dsh-qq-bot],适配器前缀 [dsh-qq-bot]/adapter,都打到 DSH Host 的 stdout

八、无 QQ 号自测

裸克隆即可运行:需要 DSH 运行时才能成立的少数断言(动态工具注册)会自动跳过,而不是判失败。 整套 123 项:

npm test

或分开跑:

# 适配器回归(正向 19 项 + 反向 12 项)
node test/test-adapter.mjs
node test/test-adapter-reverse.mjs

# 打包后插件的端到端(假 ctx + 真适配器 + 假 OneBot)
node test/test-plugin.mjs    # Host 半边(25 项)
node test/test-client.mjs     # 客户端设置页(31 项)
node test/test-admin.mjs      # 权限·群管理·访客工具闸(36 项)

也可以手动拿假 OneBot 试(端口正好是 NapCat 默认值):

node test/mock-onebot.mjs --http-port 3000 --ws-port 3001
# 另开一个终端,伪造一条 @机器人的群消息
node -e "fetch('http://127.0.0.1:3000/__inject',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({message_type:'group',group_id:33333,user_id:22222,nickname:'小明',message:[{type:'at',data:{qq:'10000'}},{type:'text',data:{text:'你好'}}]})}).then(r=>r.json()).then(console.log)"
# 看机器人回了什么
node -e "fetch('http://127.0.0.1:3000/__sent').then(r=>r.json()).then(j=>console.log(JSON.stringify(j.messages,null,2)))"

九、关于 AstrBot(容易搞混,单独说)

AstrBot 不是 OneBot 的提供方,它是消费方。 它的 aiocqhttp 适配器是 OneBot v11 的反向 WS 服务端(默认 6199 端口、/ws 路径),由 NapCat 连进去推事件——也就是说 AstrBot 本身就是「大脑」,和本插件的角色重叠。

所以:

  • 想用 DSH 当大脑 → 只需要 NapCat 这类 OneBot 实现,对端不要再接 AstrBot,否则两边都会抢着回复同一条消息。
  • 想用 AstrBot 的插件生态当大脑 → 那就不需要本插件了。
  • 两者可以共存于同一个 DSH 里(NapCat 同时开正向 WS 给本插件、反向 WS 给 AstrBot),但同一条消息会被两个大脑各回一次,一般不是你要的。

对端是「OneBot 11 实现」就行,NapCat / OneDisc / Lagrange / LLOneBot 都可以,只要开正向或反向 WS + HTTP。

十、在手机容器里跑 NapCat 的注意事项

  • NapCat 官方镜像有 arm64 版本,所以理论上能在 arm64 容器里跑;但容器里再跑 Docker 通常需要 privileged 权限,不一定允许。
  • 备选:不用 Docker,直接在容器里跑 NapCat.Shell(需要对应的 Linux QQ + NapCat 版本)。
  • 同容器时用 forward 模式连 127.0.0.1:3001 最省事;如果 NapCat 在别的容器,用 reverse 模式,让 NapCat 连到 ws://<DSH主机IP>:6199/。
  • 端口 6099 是 NapCat 的 WebUI(扫码登录用),不要暴露到公网。

十一、文件清单

文件作用
lib/index.jsCordis 插件本体(Host 侧)。导出 name / inject / apply,并注册 qq-bot 设置命名空间
lib/client.js客户端半边(手写 bundle):在「设置」里加「QQ 机器人」选项卡
lib/onebot-adapter.mjsOneBot 11 传输适配器。零依赖(Node 22 内置 WebSocket/fetch),正向/反向 WS + HTTP,自动重连、发送限速
cordis.patch.ymlbundle 补丁:把插件插入 profile 组合树
test/mock-onebot.mjs零依赖 OneBot 11 模拟器(手写 RFC 6455 帧编解码)
test/test-adapter.mjs正向 WS 适配器回归(19 项)
test/test-adapter-reverse.mjs反向 WS 适配器回归(12 项)
test/test-plugin.mjsHost 插件端到端(25 项,假 ctx + 真适配器 + schema 契约)
test/test-client.mjs客户端设置页(31 项,模拟模块加载器 + 最小 React 渲染 + 只写密钥护栏)
test/test-admin.mjs权限·群管理·访客工具闸专项(36 项)
examples/dynamic-package-host.js同一个桥接的「动态 Cordis Package」版本:不想装包、只想在当前 DSH 进程里临时跑时用
ARCHITECTURE.md改动指南(给要 fork 的人):文件职责、数据流、设计决策原因、分步改动指南、调试手册、术语表
AI-DISCLOSURE.mdAI 生成内容声明、验证情况、尚未验证清单、第三方归属、安全免责
CHANGELOG.md版本演进(0.1.0 → 0.6.0)
LICENSEMIT

十二、设计说明(踩过的坑)

为什么中间要一个子进程? DSH 动态插件的沙箱里没有 fetch / WebSocket / require(都被拦成报错)。所以插件用 ctx.subprocess 拉起一个真 Node 进程,两者用换行分隔 JSON 通信。好处是协议层可以独立测试(test-adapter.mjs),换 QQ 框架不影响插件。

为什么用 sessionController 建会话? 直接 agentLoop.create() 建出来的 Agent 没有安装模型路由(agentOptions 为空、没挂 preset),第一次模型请求必然失败。sessionController.create() + resolveAgent() 才是官方组装路径:它从 agentDefaultModel 装上 provider/model,并挂载 Agent preset(人格 + 工具)。这个坑在代码注释里标注了。

为什么用 whenIdle() + deriveMessages() 取回复? 每个会话的回合是串行排队的,回合结束后会话日志里最新一条有文本的 assistant 消息就是这一轮的回复。比订阅作用域流式事件更简单也更稳。

访客工具闸为什么是 guard 而不是拆会话 / restrict? 拆会话会把一个群变成两条历史,破坏对话一致性;restrict 是静态过滤器,按说话人变就要反复增删,工具面抖动会多记 request/header、更费 token。tools.guard 是每次执行才求值的函数:只在建 agent 时注册一次,判据是可变标志位,于是同时做到 0 额外 token、单会话单历史、不用改 DSH。另外标志位必须在串行回合内部设置——放在接收入口会被排队的两条消息互相覆盖,导致用错权限。

为什么用动态 import('@deepseek-ai/dsh-tools')? @deepseek-ai/* 是 peerDependency,由 DSH 运行时提供(安装后在 $DSH_HOME/profiles/node_modules 里能解析到)。用动态 import + try/catch,即使这个包不在,桥接本身照样工作,只是少一个 qqbot 工具。

十三、不想重启 DSH 也能先跑起来(动态 Cordis Package)

装包需要重启 dsh web。如果你想在当前进程里立刻试,用动态 Package:

动态插件的沙箱里没有 import/require,但可以用一个小加载器把磁盘上的同一个源文件当函数体求值:

const SOURCE_FILE = '/home/dsh/qq-bot/examples/dynamic-package-host.js';

return {
  name: 'qq-bot-bridge (dynamic source loader)',
  inject: ['fs', 'subprocess', 'sessionController', 'timer'],
  async apply(ctx) {
    const target = await ctx.fs.resolve(SOURCE_FILE);
    const source = await ctx.fs.readText(target);
    const factory = new Function('harness', 'console', 'TextDecoder', 'TextEncoder', 'btoa', 'atob', source);
    return factory(harness, console, TextDecoder, TextEncoder, btoa, atob).apply(ctx);
  },
};

这样动态路径和常驻插件共用同一份磁盘源码,不用把逻辑复制两遍。把这段交给 cordis_define(code.host)再 cordis_run 即可。

  • 优点:立刻生效,不用重启;inject 和沙箱 ctx 的语法和常驻插件一致。
  • 限制:只活在当前 DSH 进程里,DSH 一重启就没了,要长期在线还是得装包。
  • 动态沙箱里没有 require/fetch/setTimeout,所以传输层必须靠 ctx.subprocess 拉子进程(这也是 lib/onebot-adapter.mjs 存在的原因)。

十四、许可、成本与维护状态

许可与作者

MIT,见 LICENSE。作者 @arrow1031。

项目成本

由一次连续会话完成(2026-09-20),全部代码 + 测试 + 文档:

时间Token费用
约 5 小时 10 分约 110M约 ¥7.2

成本大概是一杯咖啡,以及一个无聊的下午。

时间由文件系统时间戳推算,含人工真机确认与等待;会话中途切换过多个模型。

维护状态:不承诺更新(请自行 fork)

本项目到此封版。 作者不做持续性维护:不承诺修 bug、不适配上游破坏性变更、不接受功能请求。

  • 遇到问题:fork 下来自己改。MIT 许可,随便改、随便发,不用打招呼。
  • 上游变了(DSH 升级 / NapCat 改字段 / OneBot 实现换代):自己跟。
  • 想加功能(自动治理、更多指令、别的 IM):自己加。

为了让你改得快,这些文档是专门为你写的:

文档内容
ARCHITECTURE.md改动指南:文件职责地图、一条消息的完整旅程、关键数据结构、每个设计决策的原因、加一条指令 / 加一个工具 / 加一个设置项的分步写法、调试手册、术语表、8 条"不要做的事"
AI-DISCLOSURE.mdAI 生成方式、人类决策点、验证到什么程度、尚未验证的清单、第三方归属、安全免责
CHANGELOG.md0.1.0 → 0.6.0 的完整演进,含每个修复的原因

致谢

  • NapCat —— 提供灵感、OneBot v11 接口与文档。 没有它把 NTQQ 的能力按 OneBot 规范暴露出来,这个项目根本无从起步。
  • 梁圣 —— 忠!橙!

联系

  • 个人网站:https://sputnikzaychik.icu
  • QQ 群:1091766276(点这里加群)
  • GitHub:@arrow1031

AI 生成内容声明见 AI-DISCLOSURE.md,使用前请务必读一遍(尤其"已知限制"一节)。

运行时依赖:零 npm 依赖。适配器只用 Node 22 内置的 WebSocket 与 fetch;唯一可选的 @deepseek-ai/dsh-tools 仅用于注册一个动态工具,缺失时自动降级。

相关插件

继续浏览 integrations-communication 分类下经过校验的插件。

Acp App@deepseek-ai/dsh-acp-appdsh ACP 配置文件包:基于 dsh-base 的仅限自动化的 JSON-RPC stdio 和进程生命周期管理Remote Web Ui@linxin666/dsh-remote-web-ui通过扫码配对访问 dsh Web GUI,共享一个官方界面:设置按钮旁的二维码可将手机和 PC 配对到同一个 Web GUI(手机采用竖屏触控适配层,PC 使用完整桌面界面),通过一次性令牌和 rPocketdsh-pocket把 DeepSeek Harness 装进你的口袋:一个包、一个设置页,手机扫码即同步访问电脑上的 DSH(局域网 + 公网,实时同屏)。DSCODE@toddzheng024/dscode-bundle完整的 DeepSeek 编码代理,支持持久化 shell、Ultra 协作和自动权限审查。