Skills Plugins MCP Prompt Model 导航 博客 资讯 我的中心

dsh-ntfy-remote

把每个 DSH 会话桥接到一个 ntfy 话题:任务完成、待审批、待提问都推到手机,可在手机上点按钮或回话把答案送回运行中的会话

tongwoojun @tongwoojun ⬇ 1 ★ 3 main

安装

dsh plugin --profile web add github:tongwoojun/dsh-ntfy-remote
下载安装清单

需要可复现安装时,可在仓库后追加 #commit 固定提交。

把每个 DSH 会话桥接到一个 ntfy 话题:任务完成、待审批、待提问都推到手机,可在手机上点按钮或回话把答案送回运行中的会话

该插件未提供要点说明,请参考仓库 README。

  1. 安装并启动 DeepSeek Harness:npx @deepseek-ai/dsh web
  2. 在终端执行上面的安装命令(CLI 会解析插件并核验来源)
  3. 用 dsh plugins list 确认已安装,必要时重启 Harness 生效

插件以当前 dsh 进程的权限运行,安装时可能执行代码。请先通读仓库源码与许可证,确认无破坏性命令与越权访问;本站只做索引,不对第三方插件安全性作担保。

代码仓库github.com/tongwoojun/dsh-ntfy-remote
许可证MIT
主要语言main
下载量1
GitHub 星标3
最近推送2026-09-16
收录日期2026-09-19
分类通知与集成

事实信息来自公开插件目录快照(2026-10-01),介绍文案由本站再加工。

以下为插件仓库 README 全文(原始内容,由公开目录抓取整理)。

# Ntfy Remote

[![npm version](https://img.shields.io/npm/v/dsh-ntfy-remote?label=npm)](https://www.npmjs.com/package/dsh-ntfy-remote)
[![license](https://img.shields.io/npm/l/dsh-ntfy-remote)](./LICENSE)
[![node](https://img.shields.io/node/v/dsh-ntfy-remote)](package.json)

**让 DSH 干完活叫你一声,也能在手机上直接把活答完。**

DSH 跑长任务时你得守着屏幕才知道结果;模型一旦要审批、要你选方案,任务就卡在那儿等你。
Ntfy Remote 把**每个会话桥接到一个 [ntfy](https://ntfy.sh) 话题**:任务完成、待审批、
待提问都推到手机,你在手机上点一下按钮或打一句话,答案就直接回到正在运行的会话里。

不需要公网 IP、不需要端口映射、不需要自己写机器人 —— 插件主动向 ntfy 服务器建立长连接,
手机端就是一个普通的 ntfy App。

## 为什么用它

| 痛点 | 它怎么解决 |
|---|---|
| 长任务跑完才知道结果 | 回合结束时把最终回复整段推到手机 |
| 模型要审批 / 提问,你不在电脑前,任务干等 | 待决请求推到手机,点按钮或回一句话就作答 |
| 自己接推送要公网入口、端口映射、HTTPS、机器人后端 | 手机端只是 ntfy App,插件主动出站长连接 |
| 不想为通知维护一套服务 | 复用 ntfy 的发布 / 订阅,零后端代码 |
| 一个 Agent 只能绑一个微信 / 飞书,多会话消息全挤在一起 | 每个会话一个话题,哪个 Agent 在找你一眼分清 |
| 通知渠道被单一 IM 绑定,换项目 / 换环境要重新配 | 支持配置多个 ntfy 服务器,按项目、按环境分开推 |

## 和微信 / 飞书通知的区别

微信 / 飞书通知的本质是:**把你的人绑进一个 IM 账号**,再让 Agent 往这个账号里发消息。
这带来两个问题:

- **一个 Agent 只能绑一个账号 / 机器人。** 多开几个会话,消息全进同一个对话框,你分不清是谁在找你。
- **渠道是写死的。** 想按项目分开推、想换一个推送服务,都得重新折腾一遍接入。

Ntfy Remote 换了个思路:**不绑人,绑会话**。

- **每个会话 = 一个独立话题(topic)。** 会话 A 的审批不会混进会话 B 的结果里,手机上按话题就能分辨来源。
- **可以配多个 ntfy 服务器。** 生产环境推一个、实验环境推另一个,或者一台手机收全部、按需订阅。
- **渠道和 Agent 解耦。** 你用的是 ntfy 这个通用发布 / 订阅通道,不是某个 IM 的机器人接口。今天用 ntfy,
  明天想换,Agent 侧不用动。

> 一句话:微信 / 飞书是「一个 Agent 找一个账号」,它是「每个会话有自己的频道」。

## 功能

### 推送到手机

- **回合结束**:把这一轮的最终回复原文推给你(超长会拆成多条继续发,不截断)
- **待审批 / 待提问**:高优先级推送,审批直接带 `Approve` / `Deny` 按钮
- **错误与中断**:模型报错、达到输出上限、被策略拦截时,推一条精简原因
- 你自己在桌面点「停止」、以及子 agent 的回合**不会**打扰你
- 通知就落在会话话题里:手机上点开通知即可直接回复,不依赖 `ntfy://` 深链接,**iOS 也能用**

### 回合心跳:知道 DSH 是在思考、卡住、还是断了

从手机发出一条消息后,最难熬的是**等的那段时间**——不知道它收到了没、在不在干活、还是已经死了。

回合进行中,插件会在会话话题里维护**一条**状态通知,它原地更新,不刷屏(ntfy 的
`sequence_id` 机制:手机上始终只有一条,标题里的秒数自己在走):

| 你看到的 | 含义 |
|---|---|
| `🟢 DSH 思考中 · 1 分 20 秒` | 正常,正在干活;正文里还有「最近一次输出:12 秒前」 |
| `🟠 DSH 疑似卡住 · 3 分 10 秒` | **进程活着但 9×心跳间隔没任何输出**,这条会**响铃** |
| `✅ DSH 已完成 · 3 分 20 秒` | 本轮正常跑完,通知原地变成结果 |
| `⏹ DSH 已中止 · 1 分 35 秒` | 你点了「停止」(或在话题里发了 `/stop`)。这条同时承担了「已收到中止请求」的回执——**不会再单独多推一条** |
| `🔴 DSH 心跳已停止` | **连进程都没了**——这条由 **ntfy 服务器**在超时后自动发出 |

最后一条是整个机制的要点:插件在回合开始时往服务器挂一条 `3×心跳间隔` 的**定时告警**,
每一拍把它往后推。**进程一旦死掉就没人再推,服务器到点自己把告警发出来**——这是任何
"客户端自己发心跳"的方案都做不到的(客户端死了就发不出任何东西了)。

所以判断方法很简单:

- **秒数在走** = 活着
- **秒数冻住** = 出事了(进程卡死 / 事件循环阻塞 / ntfy 断线)
- **收到 🔴 告警** = 已经彻底联系不上,去看机器

状态通知上还挂了一个「**停止**」按钮(点击发回的是 `/stop`,和手打完全等价),
看到疑似卡住可以直接点,不用回话题打字。点完之后话题里只会多出**你那条 `/stop`**——
状态通知会自己变成 `⏹ 已中止`,不会再另外推一条「已请求中止当前回合。」。

状态通知带一个 `dsh状态通知` 标签,方便和普通推送区分。(它同时还带一个内部的
`dsh-ntfy-remote` 标记——那是回声过滤的主防线,不能去掉:插件订阅了自己发布的话题,
服务器会把消息原样推回来,靠这个标记才能认出「是我自己发的」而不当成用户输入。)

进程**崩溃**与**优雅退出**是被区分开的:正常停止(含开发期热重载)会当场撤销那条定时告警,
不会留下假警报;只有真的崩了才会收到 🔴。

只有**一个**时间配置项(设置页里的「心跳间隔」,默认 20 秒):死信超时 = 3×,卡住阈值 = 9×,
由它推导——避免手填出"心跳 60 秒、死信 30 秒"这种健康时就误报的组合。

**每回合发一条新的状态通知**,所以空闲时你从手机发一条消息,那条 🟢 会**立刻出现在通知栏最上面**。
做法是:开场先把**上一轮**那条删掉,再用新的 `sequence_id` 发本轮的 —— 同一个 `sequence_id` 在
客户端只会**原地替换**(内容变、位置不动),新 id 才会被当成新通知。通知栏里因此**始终只有一条**。

> 实现上**不需要额外进程**:定时器就跑在 DSH 进程里。而"定时器停了"恰恰是最准的故障信号。

### 在手机上作答

- **审批**:点 `Approve` / `Deny`
- **提问**:选项 ≤ 3 时是按钮;超过 3 个改成编号列表,回编号即可
- **多选提问**:**可以直接点按钮**——点一下选中、再点一下取消,最后点「提交」;
  也可以回 `1,3` 一次选中多个(`,`、`、`、空格都算分隔符)
- **自由文本**:在话题里直接打字,内容会作为你的消息注入正在运行的会话
- **撤回**:点 **`⏳ 已排队` 通知**上的「**删除**」按钮——还排着队的那条会被直接抽掉、压根不跑(正在跑的那一轮用状态通知上的「停止」)
- **指令**:`/stop` 中止当前回合、`/status` 看状态、`/key` 看话题、`/help`

### 撤回:发错了就把它删掉

在手机上发出去才发现说错话,不用干等它跑完。**点通知上的「删除」按钮**,插件就把这条当成不算数:

| 那条消息此刻 | 「删除」按钮在哪 | 插件做什么 |
| --- | --- | --- |
| 还**排着队**(同一会话前一轮还没跑完) | 插件补发的 `⏳ 已排队` 通知上 | 从队列里**按条抽掉**,压根不会跑 |
| **正在跑** | 没有「删除」按钮了 —— 用状态通知上的「**停止**」(两者对正在跑的那轮效果一样,见下) | —— |
| **早就跑完了** | 没有按钮(没什么可撤的) | —— |

两种情况都会**顺手做三件清理**:把你发的那条原消息从服务器删掉(**手机 App 里它也跟着消失**)、
把插件自己那条通知删掉、以及**把按钮自己留下的那条载荷消息也删掉**(见下面的表格)。

> **为什么不靠「在 App 里长按删除」?** 实测(ntfy 安卓端 + 自建服务器,三轮):长按删除
> **只删本机、不向服务器发 DELETE**,服务器因此不广播 `message_delete`,插件收不到任何信号——
> 同时挂着的独立订阅者全程在线,一条删除事件都没看到。而按钮走的是「点击 = 往话题发一条消息」
> 这条通道(`/stop` 用的就是它),实测点击一秒内生效。
>
> 另外,桥接的订阅会**周期性断线**(实测一天十余次)。普通消息能在重连后用 `since` 补回来,
> 但 `message_delete` 是**瞬时事件、补不回来**——这也是不该把撤回挂在删除事件上的第二个理由。

**「停止」和「删除」是两个按钮,别混**:

| 按钮 | 挂在哪 | 做什么 | 你发的那条消息 |
| --- | --- | --- | --- |
| **停止** | 状态通知(🟢 / ✅ / ⏹) | 只中止当前回合 | **留在话题里**(方便你改一版再发) |
| **删除** | `⏳ 已排队` 通知 | 从队列里按条抽掉,**压根不跑** | **一起删掉**(App 里也跟着消失) |

> **为什么状态通知上不再挂「删除」?** 对**正在跑**的那一轮,它和「停止」效果重合(都只是中止),
> 而且它**删不掉 DSH 会话里的记录**(会话日志只追加,插件不该也无法回删一条已经记录的用户消息),
> 用户觉得"删了个寂寞"。而 `⏳ 已排队` 上那个是**不可替代**的:那是取消一条还没轮到的消息的
> **唯一**入口 —— 那种情况下「停止」是无用的(那会儿没有回合在跑)。

**两个按钮自己留下的那条"点击载荷"都会被插件删掉** —— 否则话题里会多出一行
`{"stop":true}` 或 `{"undo":"…"}` 的操作痕迹(实测报过)。而你**手打**的 `/stop` 不会被删:
按钮发的是 JSON、手打的是纯文本,插件分得清。

不是手机发起的那一轮(网页 / 桌面)没有可删的原消息,所以状态通知上只挂「停止」。

插件自己推出去的通知(状态通知、已撤销的心跳告警)你要是在 App 里删掉了,插件只记一行日志,
不会误判。

### 扫码添加到手机话题

会话右上角 `● ntfy` 弹窗里有一张**二维码**:内容就是该会话的 `ntfy://服务器/话题` 深链接,
用手机 ntfy App 扫一下即可订阅这个话题,不必手抄那串 `dsh_<会话 id>`。换会话就换一张码,
扫错不会串到别的话题。

二维码**在宿主侧现算**(`GET /dsh-ntfy-remote/session/qr?sessionId=…` 返回一张自包含的 SVG),
不是把话题名发给第三方在线生成服务——话题名不离开本机,断网也能出图。

### 通知偏好

五项,**全局默认 + 每个会话单独覆盖**(改过的项会高亮,可一键「跟随默认」):

| 设置 | 作用 | 默认 |
|---|---|---|
| 回合结束推送 | 正常结束时推送最终回复 | 开 |
| 审批 / 提问推送 | 待决时推高优先级通知并接管作答 | 开 |
| 错误 / 中断推送 | 异常结束时推送精简原因 | 开 |
| 手机优先接管作答 | 由手机而不是网页来答审批 / 提问 | 开 |
| 从网页发起的回合也通知手机 | 关掉后**只有手机发起**的回合才会通知手机:回复、状态心跳、**以及提问/审批的中转**都不发给手机(提问会留在网页上由网页作答) | 开 |
| 作答超时 | 等手机作答的秒数;超时回落网页端,网页弹窗继续等,请求不会丢 | 180 秒 |

长文本**不需要配置**:ntfy 对单条消息的硬上限是 4095 **字节**(1 个汉字约 3 字节),
超了会被服务端整条拒收,所以插件会自动按字节**拆成多条继续发**,内容不会丢——只有
第一条响铃,后续几条静音并在标题上标 `(2/3)` 这样的序号。推送正文按 markdown 渲染
(标题、列表、代码块、链接都会正常显示)。

> 「网页发起的回合也推送」是给「人就在电脑前,别再来打扰我」的场景准备的。关掉之后,
> 你在网页/桌面上顺手问的那些不会再推到手机;代价是「在电脑上发起长任务、走开后手机
> 收结果」这个场景也会一起没有,所以它是个开关而不是默认行为。**逐会话可设**——你可以
> 让随手问的会话安静,让跑长任务的会话照常通知。

会话里的「本会话通知偏好」把上面这张表直接写到了界面上:**每一项控件下面都有一行灰字说明
它做什么**(同时也是悬停提示),不用去翻文档。

### 多服务器

可以配置多个带名称的 ntfy 服务器(官方 ntfy.sh 或自建,支持 access token)。会话
**首次开启**时选定服务器,之后不可变更 —— 避免运行中改地址造成话题漂移。服务器失联或
地址填错时留了**解绑**退路:关闭该会话的桥接后即可解绑并重选。还有会话绑着的服务器
不允许删除,避免那些会话永久失去推送。

### 三处入口

1. **会话右上角 `● ntfy`**:就地开关、查看话题与链接、**扫码把话题加到手机**、调整本会话通知偏好;
   话题名旁边是两个同样式的小按钮——**网页打开**(新标签打开该话题页)和**复制**(复制话题名到剪贴板,
   成功/失败就地回显)
2. **设置 → Ntfy Remote**:服务器增删改、全局默认偏好、所有会话总览
3. **状态页** `http://127.0.0.1:<端口>/dsh-ntfy-remote`:内容与设置页一致,不需要任何前端构建

### 会话归档或删除后自动收起

你在 DSH 里**归档**(= 从侧边栏收起)或**删除**某个会话时,插件会自动断开它的桥接,并把这行
从「设置 → Ntfy Remote」的列表里移除 —— 不会留下一行"运行中"的幽灵记录,也不会继续订阅那个话题。

列表里只列出**开过桥接的会话**(有绑定 / 开关 / 偏好),每行都有一个 **删除** 按钮:只删 Ntfy
Remote 里的绑定 / 开关 / 偏好,**不会删除 DSH 会话**(会话文件和聊天记录都在,想再用就在那个会话里
重新 `/ntfy on`)。与「解绑」不同,它不需要先关闭桥接,随时可用。活着但从没开过桥接的会话不会
列在这里 —— 它们在自己的 `● ntfy` 弹窗里开启。

## 安装

前提:`pnpm` 在 PATH 上(`dsh plugin` 是它的转发器)。

```sh
dsh plugin --profile web add dsh-ntfy-remote
```

升级 / 卸载:

```sh
dsh plugin --profile web update dsh-ntfy-remote
dsh plugin --profile web remove dsh-ntfy-remote
```

## 快速开始

1. 手机装 **ntfy** App(iOS / Android / F-Droid 都有)
2. 在 DSH 输入框里执行 `/ntfy on`
3. 在会话右上角 `● ntfy` 弹窗里**扫描二维码**订阅本会话话题;或者执行 `/ntfy test` 发一条
   测试通知,在手机上点开它,同样落在本会话的话题里
4. 之后这个会话的完成、审批、提问都会推到这台手机 —— 直接在话题里回复即可作答

或者在dsh web 中进入会话,在右上角找到ntfy进行点击操作

## 命令

**DSH 输入框**

| 命令 | 作用 |
|---|---|
| `/ntfy on [服务器名]` | 用默认(或指定)服务器开启本会话桥接,指定仅首次有效 |
| `/ntfy off` | 关闭桥接(保留服务器绑定与偏好) |
| `/ntfy key` | 只看话题 |
| `/ntfy status` | 绑定服务器、话题、偏好及其来源 |
| `/ntfy servers` | 列出服务器(★ 标默认) |
| `/ntfy test` | 发一条测试通知 |

**手机上(在话题里发文本)**

| 指令 | 作用 |
|---|---|
| `/stop` | 中止当前回合 |
| `/status` / `/key` | 桥接开关、会话是否在运行、服务器、话题与订阅地址 |
| `/help` | 帮助 |
| 其它任何文本 | 作为你的消息注入会话;审批 / 提问待决时优先结算该请求 |

## ⚠️ 安全:请务必读这一段

话题名是 `dsh_<完整会话 id>`,**可以从会话 id 直接推导出来**(44 位会话 id 再加一个密钥
就会超过 ntfy 的 64 字话题名上限,所以话题名里没有密钥)。而会话 id 不是机密 ——
它会出现在界面、日志、状态页和截图里。

后果是:**知道会话 id 的人就能往该话题发消息,而插件会把非自己发布的消息当作你的指令
注入会话。** 插件只能过滤掉自己发的消息,**无法分辨其余消息是不是你本人**。

所以:

- **用公共 ntfy.sh 时**:注册账号、启用 access token(坑,付费用户),并使用保留话题前缀,让别人无法发布。建议自建ntfy。
- **或者自建 ntfy**:给话题配置 ACL,只允许你的账号读写
- 不要把会话 id 随意外传(截图、日志、分享状态页都要留意)

注入的消息仍然走 DSH 的沙箱与权限策略,不会绕过审批;但「手机指令 = 用户指令」本身
就是权限入口。

## 已知限制

- 同一个 `DSH_HOME` 下不要同时跑多个 `dsh` 实例:它们会订阅同一话题,同一条回复可能被处理两次
- 会话不在内存中(冷会话)时无法注入,插件会提示你先在 DSH 里打开它
- 长文本自动按字节拆成多条发送(无需配置),暂不支持发送附件
- 子 agent(subagent)的回合不推送,避免刷屏

## 常见问题

**手机的ntfy App哪里下?**
iOS:[App Store](https://apps.apple.com/cn/app/ntfy/id1625396347)
Android:[Github](https://github.com/binwiederhier/ntfy-android/releases/download/v1.25.2/ntfy-1.25.2-play-release.apk)

**手机收不到推送?**
按顺序检查:`/ntfy status` 是否显示「桥接:已开启」→ ntfy App 里是否订阅了该话题 →
App 的通知权限 → 自建服务器是否可达。
注意坑:公共 ntfy.sh 的免费额度会限流(HTTP 429)。 如果有自己的服务可以自己搭建ntfy 参考: [Github](https://github.com/binwiederhier/ntfy)
插件日志在 `$DSH_HOME/dsh-ntfy-remote/plugin.log`。

**通知里多出来的 `dsh-ntfy-remote` 小字是什么?**
插件给自己消息打的标记,用来过滤回声 —— 单话题下没有它就会「自己回自己」形成死循环。

**我在桌面点「停止」也会推给我吗?**
不会,用户主动取消不推送。

**手机上答过了,网页端还会再弹一次吗?**
不会。手机优先接管期间网页端不显示该提问;只有等不到手机回执、超时之后才回落到网页端,
而本地弹窗一直在等,请求不会丢。

## 版本更新

**每次发版都在这里补一行**(详细变更见 [`CHANGELOG.md`](./CHANGELOG.md))。

| 版本 | 增加了什么 / 修了什么 |
|---|---|
| **v1.1.1** | **撤回改走通知上的「删除」按钮**(不再依赖「在 App 里长按删除」——实测那个只删本机、不发 DELETE):排队中的补一条「⏳ 已排队」通知并挂按钮;点下去 = 撤回那一轮 + **把你发的原消息从服务器删掉**(App 里随之消失)+ 删掉按钮自己那条载荷。**「停止」也改成发 JSON 载荷、点完不留痕迹**(手打的 `/stop` 不受影响)。**状态通知改成每回合一条**,空闲时一发消息就置顶。另修**并发发布偶发 403 导致通知静默丢失**(改为自动重试) |
| **v1.1.0** | **回合心跳**:手机上看得到「🟢 思考中 / 🟠 疑似卡住 / ✅ 已完成 / ⏹ 已中止 / 🔴 心跳已停止」,秒数原地更新不刷屏;状态通知上带「**停止**」按钮;**撤回**(在手机上删掉自己发的消息,即中止那一轮且不回推);**多选提问可以点按钮**(点选 / 取消 + 提交);新增逐会话开关「**网页发起的回合也推送**」(关掉后手机端静默);修掉「空回复哨兵 `triggered` 被当成用户输入」与「过期按钮回执被当成用户输入」 |
| **v1.0.6** | 修**分片推到手机后顺序错乱**(倒序发送 + 相邻 1.1 秒);修**分片还没发完就点按钮丢回执**的竞态;移除「单条上限」配置项(字节预算改固定值) |
| **v1.0.5** | 会话弹窗新增**二维码扫码订阅**;长中文通知改为**按字节分片**续发(原先超限会被 ntfy 静默拒收、整条丢失);提问补齐选项描述 / 短标题 / 计划正文;推送正文按 **markdown** 渲染;修自建服务器按钮 **HTTP 403** |
| **v1.0.4** | 新增二维码编码器 `qr.js`(弹窗接入在 v1.0.5) |
| **v1.0.3** | README 补充卖点,新增「和微信 / 飞书通知的区别」(纯文档) |
| **v1.0.2** | 弹窗底部新增 ntfy App 下载入口(iOS / Android) |
| **v1.0.1** | 修多选提问只回一个选项;`/stop` 不再多推一条 `unknown` 中断;偏好取值校验;会话归档自动断开桥接;会话列表新增「删除」 |
| **v1.0.0** | 首个发布:会话 ↔ ntfy 话题桥接、回合推送、手机作答 |

## 开发者

实现细节、架构取舍、开发 / 测试 / 发布流程、逐项验证记录见
[`README_FOR_ME.md`](./README_FOR_ME.md);版本变化见 [`CHANGELOG.md`](./CHANGELOG.md)。

## License

MIT

## web 操作参考图片

dsh web中的展示

**设置->ntfy remote 配置**

![截图](./images/2.png)

**会话中的配置1**

![截图](./images/1.png)

**会话中的配置2**
![截图](./images/3.png)

**手机订阅**
打开 ntfy app 添加话题,话就是会话中的配置2中的:dsh_session-823eacc5-00c7-4495-898c-ebb283e43ed0
[图片: 截图]

然后点击订阅。

如果需要实时通知需要在该话题下点击右上角 "..." 弹出如图
[图片: 截图]

然后在点击订阅设置中的即时交付
[图片: 截图]

**微信群**

[图片: 截图]

数据来源:公开的 DeepSeek Harness 插件目录与各插件 GitHub 仓库。本站为独立第三方目录,与 DeepSeek、幻方(High-Flyer)及插件作者均无隶属或背书关系。

每日精选 Skill 推荐,免费送到你邮箱

输入邮箱,每天接收一个精选 AI Agent 技能推荐。完全免费,持续更新。

提交后我们会发送一封确认邮件,点击邮件里的链接才会开始收信。

完全免费,取消任意时间。我们不会发送垃圾邮件。