前言¶
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 用 bot 和 applications.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 与缓存命中率。会话进行到哪一步、每一步做了什么,都能在手机上直接看到。
安全边界与两个默认关闭的开关¶
mirror 和 allowRun 默认都是关的,原因相同:内容出口。
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 或幻方无官方从属关系。