dsh-discord-bot:把 dsh 投影到 Discord,不开放任何入站端口

前言

dsh 跑在家里或公司的一台机器上,人在外面,想看某个会话走到哪一步,或者临时给某个工作区派个活。常规做法是 SSH 端口转发,或者把 web UI 暴露到公网——前者要求你能路由到那台机器,后者要求公网 IP 或反向代理,再不然就是挂一个隧道服务,而隧道服务手里握着你机器的钥匙。

dsh-discord-bot 走的是另一条路:把 Discord 当作控制面。机器只向 Discord gateway 发起一条出站 WebSocket 并保持住,命令从这条既有连接进来。不监听端口、不转发端口、不引入新的入站攻击面。下面介绍这个插件能做什么、怎么装、开哪些开关之前要想清楚什么。

这是什么

dsh-discord-bot 是 DeepSeek Harness(下称 dsh)的社区插件,由 Oliver0804 维护,MIT 许可证,当前版本 0.3.4,依赖 discord.js ^14.27.0,要求 Node >= 20。它把一个 dsh profile 投影到一个 Discord guild 上:一个分类(category)下,每个工作区对应一个文本频道,再配一套 /dsh … 命令,可以在频道里读会话轨迹、看实时子代理、下发工作。dsh 的理念是「一切皆插件」,远程访问这件事在这里也被做成了插件。

核心功能

只出站的连接

插件向 Discord gateway 打开一条 WebSocket 并保持。每条命令都经由这条既有连接送达,所以 harness 可以在 NAT、CGNAT、酒店网络或公司防火墙后面应答——无需端口转发、动态 DNS、反向代理,也不需要持有你机器密钥的隧道服务。只要这台机器访问得了 discord.com,你就能用手机查它。没有任何东西在监听,也就没有新增的入站攻击面。

读取与监视

读取侧覆盖:sessions、trajectories、raw event timelines、subagents、lineage。在工作区频道内用 /dsh … 命令可以读会话轨迹(session trajectories)与实时子代理(live subagents)。

监视侧是 mirror: true:harness 的每个回合——不管由 web UI、tui 还是 cron 发起——都会实时出现在对应工作区频道里。默认关闭,因为它意味着持续把会话内容导出到聊天平台。

写入与运行

写入包括注册工作区和切换 profile 配置:

  • /dsh workspace:注册工作区
  • /dsh model:切换默认模型
  • /dsh preset:切换 agent preset
  • /dsh permission:切换权限 preset

运行侧只有一条 /dsh run <prompt>:向工作区 agent 交付提示词,并流式回传该回合。默认关闭(allowRun: false)——其余命令都是在描述工作,只有这一条会让工作真的发生在你的机器上。

频道默认私有

分类与频道拒绝 @everyone,只授予 bot 和 allowedUserIds 里的用户;名单为空时只剩 guild owner。同一条规则也决定谁能运行命令,所以一个人能读什么和他能问什么不会分叉。

隐私设置在每次同步时重新应用,而不是只在创建时:已经存在的分类、或在此功能开启之前建好的分类,同样会被锁上。如果 bot 缺少 Manage Channels,它做不了任何限制,会在日志和 /dsh sync 里明确说明,而不是让你误以为已经锁好。

另外,bot 以点击者所用的语言回答。

安装与启用

先在 Discord 侧准备 bot,再装进 dsh profile。

1、创建 bot。在 Discord Developer Portal(https://discord.com/developers/applications)依次 New Application → Bot → Reset Token,复制 token。不需要任何特权 intents,Message Content 保持关闭。

2、邀请 bot 进服务器,scope 用 botapplications.commands

https://discord.com/oauth2/authorize?client_id=<APP_ID>&permissions=268487696&scope=bot+applications.commands

<APP_ID> 换成你的应用 ID。permissions=268487696 对应 View Channels、Send Messages、Embed Links、Attach Files、Manage Channels、Manage Roles。最后一个是容易漏的:写频道权限覆盖——也就是让频道变私有的那件事——需要 Manage Roles,而 Discord 在缺少它时只报一句 Missing Access。缺了它 bot 仍能工作,但频道保持全网可读,并且每次同步都会提示。

3、安装进 dsh profile:

npx dsh-discord-bot-setup --profile web

setup 脚本会安装包、向 profile 注册 bundle、把 token 写入 $DSH_HOME/discord-bot.token(mode 600),并在 profile 的 patch 层追加一条配置覆盖。没传的参数会交互式询问;profile 里已存在对应配置行时,脚本拒绝执行且不改动 profile。

非交互式写法:

npx dsh-discord-bot-setup --profile web --guild 123456789012345678 --token "$TOKEN" --yes

--print 只预览将写入的行,不做任何更改。

4、重启 dsh 并验证:

dsh --profile web

打开新出现的 dsh 分类,运行 /dsh status

手动安装与配置

不想用 setup 脚本的话:

dsh plugin --profile web add dsh-discord-bot

这一步安装包并把插件挂进 profile 的 bundle 层,插件自带的 cordis.patch.yml 由此成为组合配置树的一层。此时插件会离线启动并在日志里说明——因为还没有人告诉它绑定哪个 guild。在 profile 自己的 cordis.patch.yml 里补一条 id 定向 override:

- id: discord-bot
  config:
    guildId: '123456789012345678'
    tokenFile: '/Users/you/.dsh/discord-bot.token'
    categoryName: 'dsh'

注意这是 override,不是第二个 insert。bundle 层已经挂载了这一行,两层带同一 id 不会合并,而是组合出两个实例——同一个 guild 上两个 bot,每条命令回答两次。用下面的命令检查组合后的配置树,确认 id: discord-bot 只出现一次:

dsh --profile web --dump-config

顺带一提,这个插件属于 host plane,不要放进 agent preset:它服务所有工作区和所有会话,按会话复制一份在第二个会话就会冲突。

从 0.3.1 或更早版本升级

0.3.1 及更早版本没有 bundle 层,setup 脚本当时写进 profile 的是完整的 insert: 行;现在包自己挂载,那行会组合出第二个 bot。把旧行替换成上面的 id 覆盖行,保留你自己的值:

# 之前                             # 之后
- insert:                         - id: discord-bot
    - id: discord-bot               config:
      name: 'dsh-discord-bot'         guildId: '…'
      config:                         tokenFile: '…'
        guildId: '…'

暂时不改也不会坏:bundle 的行没有配置,只会记一条缺少 guildId 的日志并保持离线,不会让组合失败。改完再用 --dump-config 确认只有一个 id: discord-bot

典型用法

经过上面的步骤,guild 里会出现一个 dsh 分类,结构大致是:

dsh                    ← 分类
├── dsh                ← 工作区 /Users/you/Documents/dsh
├── sweepbot-home      ← 工作区 /Users/you/code/game/godot/sweepbot_home
└── my-api             ← 工作区 /Users/you/work/my-api

在对应频道里发 /dsh run <prompt>,频道里会出现一张运行卡片,标明当前在执行哪个工具,附 Trace、Timeline、Subagents、Todos、Export、Steer、Stop 等按钮;Trace 里是每一次工具调用与结果,底部附带耗时、token 与缓存命中率。会话进行到哪一步、每一步做了什么,都能在手机上直接看到。

安全边界与两个默认关闭的开关

mirrorallowRun 默认都是关的,原因相同:内容出口。

  • mirror: true 会持续把会话内容导出到 Discord。
  • allowRun: true 之后,allowedUserIds 内的任何人都能让 agent 在 harness 机器上编辑文件、运行命令——从手机上。此时名单不再只是隐私边界,而成了 shell 访问名单。README 的建议是:名单只留自己;开启时权限 preset 优先选 ask,而不是 danger-full-access

边界方面,bot 不会削弱 dsh 自身的沙箱与审批策略:本地会被 harness 拒绝的操作,在这里同样被拒绝,它也无法代你批准。它也跨不过工作区边界——访问不了其他工作区频道里的会话。

适用场景与注意

适合:

  • dsh 跑在 NAT、CGNAT 或公司防火墙后面、没有公网入口的机器上;
  • 需要在手机或任何装了 Discord 的设备上查看和驱动会话;
  • 小范围使用:把允许的人加进 allowedUserIds,频道可读范围和命令权限保持一致。

不适合:

  • 不接受会话内容经过 Discord 的情况——命令应答与 mirror 都经由 Discord 这条通道;
  • 需要把访问权开放给名单之外的人的场合,这套权限模型不为此设计。

两点提醒:插件以当前 dsh 进程的权限运行,它能触达的范围就是 dsh 进程能触达的范围;安装任何第三方插件前,建议先读一遍源码、确认许可证(本项目为 MIT)。

结尾

一句话回顾:一条出站 WebSocket,把工作区、会话轨迹和 harness 自己的命令带到 Discord,全程不开放入站端口。项目地址与社区目录页:

  • GitHub:https://github.com/Oliver0804/dsh-discord-bot
  • 社区目录页:https://www.skillhub.cn/plugins/Oliver0804/dsh-discord-bot

社区目录为独立站点,与 DeepSeek 或幻方无官方从属关系。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜