dsh-session-guardian 0.0.0-stage → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/LICENSE +21 -0
- package/README.en.md +60 -0
- package/README.md +244 -2
- package/cordis.patch.yml +44 -0
- package/docs/RELEASE_NOTES_v1.5.0.md +92 -0
- package/icon.svg +13 -0
- package/lib/client.js +937 -0
- package/lib/index.js +312 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +94 -4
- package/smoke/smoke-test.mjs +645 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# 更新日志
|
|
2
|
+
|
|
3
|
+
本文件记录 dsh-session-guardian(会话监督联动提醒 · 聚合包)的版本更迭。
|
|
4
|
+
全部条目用中文书写。
|
|
5
|
+
|
|
6
|
+
## v1.5.0(2026-11-11)
|
|
7
|
+
|
|
8
|
+
首个正式发版版本。前身是本机迭代的 `dsh-wechat-reminder`,本次改名为
|
|
9
|
+
`dsh-session-guardian`(显示名「会话监督联动提醒」),并建立 GitHub / Gitee / npm
|
|
10
|
+
三端全新仓库与自动发布流水线。
|
|
11
|
+
|
|
12
|
+
### 功能总览
|
|
13
|
+
|
|
14
|
+
- **会话监督联动提醒**:订阅 `uiSession.sessionStatus`,对全部会话做三类边沿检测——
|
|
15
|
+
需要确认权限、需要手动选择、任务跑完(模型这一轮回复结束)。
|
|
16
|
+
- **桌面弹窗 + 提示音**:三类事件都弹一张带具体内容的系统通知(标题「会话名 · 事件标签」,
|
|
17
|
+
正文从 `pendingInteraction` 抽出问题文本 / 工具名 + 原因),并播放合成提示音;
|
|
18
|
+
点任意一张通知都切到对应会话界面。
|
|
19
|
+
- **微信兜底**:桌面提醒发出后 30 秒内既没切过去看、也没把待办处理掉,就把仍未响应的
|
|
20
|
+
条目汇总成一条微信,经 dsh-im 的微信机器人(普通微信)发到你指定的那一个微信对话。
|
|
21
|
+
- **聚合包**:装这一个包就同时带上「会话监督联动提醒」与 `@xmanrui/dsh-im`(微信机器人),
|
|
22
|
+
一个 `insert` 挂两条 loader 行;dsh-im 保留可升级范围、走 npm 官方源。
|
|
23
|
+
- **会话级规则**:窗口前台时只提醒「没在看」的那些会话,正盯着的那条保持安静;
|
|
24
|
+
窗口整个切到后台时所有会话都提醒;「有响应」= 切到那条会话去看 / 把待办处理掉。
|
|
25
|
+
|
|
26
|
+
### 迭代历史(本机阶段,累进到 v1.5.0)
|
|
27
|
+
|
|
28
|
+
- v1.0.0:三类触发 + 桌面通知 / 提示音 + 30 秒 Server 酱超时补发微信。
|
|
29
|
+
- v1.1.0:微信通道去掉 Server 酱,改走 dsh-im 主动投递;设置页改为 Bot ID + 目标 ID。
|
|
30
|
+
- v1.1.1:修复「测试微信推送」报 `Invalid target id`——整段「复制调用参数」JSON 自动拆成
|
|
31
|
+
两个纯 ID、旧配置自动清洗、英文报错中文化。
|
|
32
|
+
- v1.2.0:升级为聚合包,装一个包带上两个插件;dsh-im 保留顶层 `^5.10.0` 依赖与防重复挂载守卫。
|
|
33
|
+
- v1.3.0:修复「3 条会话并行、界面停在一条时其他会话不通知也不发微信」——会话级静音与
|
|
34
|
+
会话级「已响应」判据。
|
|
35
|
+
- v1.4.0:去掉重复的空白「DSH 需要你操作」通知;任务跑完通知带会话名;所有通知点击跳会话。
|
|
36
|
+
- v1.5.0:三类事件都弹与 dsh-pet 一致、带具体内容的系统通知;点任意通知跳到对应会话。
|
|
37
|
+
|
|
38
|
+
### 改名迁移
|
|
39
|
+
|
|
40
|
+
从旧名 `dsh-wechat-reminder` 改名重装后,浏览器半边会优先读新 localStorage key
|
|
41
|
+
`dshSessionGuardian.config`;若为空则回退读旧 key `dshWechatReminder.config`,
|
|
42
|
+
用户已填的 Bot ID / 目标 ID / 超时秒数不会丢失(旧 key 保留不清)。
|
|
43
|
+
|
|
44
|
+
### 发布相关
|
|
45
|
+
|
|
46
|
+
- `.github/workflows/test.yml`:push / PR 时跑离线冒烟测试(Node 20 / 22)。
|
|
47
|
+
- `.github/workflows/publish.yml`:push main / 打 `v*` tag / 发布 Release / 手动触发时,
|
|
48
|
+
先跑自检,再用幂等守卫判断该版本是否已发布;未发布且配了仓库 Secret `NPM_TOKEN`
|
|
49
|
+
才 `npm publish --access public`。没改版本号的 push 自动跳过,不会因 npm 版本不可变报错。
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 sweet560
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.en.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# dsh-session-guardian (Session Guardian · Umbrella Bundle)
|
|
2
|
+
|
|
3
|
+
A reminder plugin for the **DeepSeek Harness desktop app**. It watches all your running
|
|
4
|
+
sessions and links desktop alerts with a WeChat fallback.
|
|
5
|
+
|
|
6
|
+
> When a session **needs your manual choice**, **needs permission approval**, or **finishes its
|
|
7
|
+
> turn**, it raises a **desktop notification (system toast + alert sound)** with the real content,
|
|
8
|
+
> and you can **click the notification to jump to that session**. If you neither switch to that
|
|
9
|
+
> session nor clear its pending item within **30 seconds**, it sends a summarized reminder to one
|
|
10
|
+
> fixed WeChat chat through the **dsh-im WeChat bot (regular WeChat)**.
|
|
11
|
+
|
|
12
|
+
Installing this one package brings both the Session Guardian plugin and `@xmanrui/dsh-im`
|
|
13
|
+
(the WeChat bot). See the Chinese [README.md](./README.md) for full documentation.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- Three triggers: permission approval / manual question / task finished (end of a model turn).
|
|
18
|
+
- Content-rich toasts: title `session name · event label`, body extracted from `pendingInteraction`
|
|
19
|
+
(question text / tool name + reason), capped at 160 chars.
|
|
20
|
+
- Click any notification to jump to the corresponding session (`uiWorkspace.openSession(sid)`).
|
|
21
|
+
- Session-level supervision: when the window is in the foreground, only sessions you are NOT
|
|
22
|
+
looking at raise alerts (the one on screen stays quiet); when the window is in the background,
|
|
23
|
+
all sessions raise alerts.
|
|
24
|
+
- "Responded" = switching to that session or clearing its pending item — cancels WeChat fallback.
|
|
25
|
+
- WeChat fallback via dsh-im proactive delivery; no external webhook / Server 酱 / SendKey needed.
|
|
26
|
+
- Umbrella bundle: one `insert` mounts two loader rows; dsh-im keeps an upgradeable `^5.10.0`
|
|
27
|
+
range and stays on the npm official source.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
dsh plugin --profile desktop add dsh-session-guardian
|
|
33
|
+
# or from local source:
|
|
34
|
+
dsh plugin --profile desktop add "file:F:/.../dsh-session-guardian"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Restart the DSH desktop app afterwards, then open **Settings → 会话监督联动提醒**, fill in the
|
|
38
|
+
Bot ID + Target ID (copy Bot ID from dsh-im IM bot settings, and the targetId from "copy call
|
|
39
|
+
params"; pasting the whole JSON into either field splits automatically), and use the two test
|
|
40
|
+
buttons to verify.
|
|
41
|
+
|
|
42
|
+
## Depends on two plugins
|
|
43
|
+
|
|
44
|
+
| Part | Source | Role |
|
|
45
|
+
| --- | --- | --- |
|
|
46
|
+
| Session Guardian (this package) | `lib/index.js` + `lib/client.js` | watches all sessions, desktop toasts + sound, 30s batch, settings page, click-to-jump |
|
|
47
|
+
| `@xmanrui/dsh-im` | npm official source, `^5.10.0` | regular WeChat bot that delivers the timeout summary |
|
|
48
|
+
|
|
49
|
+
## Development
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm test # offline smoke test (pure Node)
|
|
53
|
+
npm run pack # npm pack --dry-run
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
CI: `.github/workflows/test.yml` runs the smoke test on push/PR (Node 20/22);
|
|
57
|
+
`.github/workflows/publish.yml` publishes to npm when `package.json` version becomes a value not
|
|
58
|
+
yet on the registry, guarded by an idempotency check and the repository secret `NPM_TOKEN`.
|
|
59
|
+
|
|
60
|
+
Current version: **1.5.0**.
|
package/README.md
CHANGED
|
@@ -1,3 +1,245 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-session-guardian(会话监督联动提醒 · 聚合包)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
给 **DeepSeek Harness 桌面端**用的提醒插件。核心功能:
|
|
4
|
+
|
|
5
|
+
> 会话里出现「需要你手动选择」「需要你确认权限」「模型这一轮回复结束」时,做**桌面提醒(提示音 / 系统通知栏)**;如果超过 **30 秒**还没切过去看 / 没把待办处理掉,就通过 **dsh-im 的微信机器人(普通微信)**把汇总提醒发到你指定的那一个微信对话。
|
|
6
|
+
|
|
7
|
+
> **多条会话并行也管用**:界面停在 A 会话时,B / C 会话需要确认、需要选择或跑完,都会照常提醒;只有你正盯着的那条保持安静。窗口整个切到后台时,所有会话都提醒。
|
|
8
|
+
|
|
9
|
+
**v1.5.0 三类都弹「带具体内容」的通知**:以前本插件弹的是空洞的「DSH 需要你操作」。现在**需要确认权限 / 需要手动选择 / 任务跑完**三类事件都会弹**一张**系统通知,格式对齐 dsh-pet 的卡片——标题是「会话名 · 事件标签」(正在申请权限 / 模型在等你回答 / 对话完成),正文是 `pendingInteraction` 里抽出来的**具体问题文本 / 工具名+原因**;抽不到具体内容时回退成一句中文说明,不再是空洞卡片。**点任意一张通知都能切到对应会话界面**(`uiWorkspace.openSession(sid)` + 窗口提前台,照抄 dsh-pet)。提示音和 30 秒微信兜底**全部保留**。按你的决策,本插件与 dsh-pet 各自弹一张,屏幕上会出现两张相近卡片。详见第 1、4 节。
|
|
10
|
+
|
|
11
|
+
**v1.4.0 起「点击通知跳到对应会话」已实现**:所有系统通知的 `onclick` 都走 `jumpToSession(sid)`,v1.5.0 继续沿用。
|
|
12
|
+
|
|
13
|
+
**v1.3.0 修复「3 条会话并行、界面停在一条时其他 2 条不通知也不发微信」的 bug**:旧版把「仅后台提醒」错误地做成了「整个窗口在前台就全部静音」,导致其他会话的事件被吞掉。现在提醒判据与「已响应」判据都改为**会话级**(参考 dsh-whale-widget / dsh-notify-me 的会话级提示)。详见第 1、4 节。
|
|
14
|
+
|
|
15
|
+
**v1.2.0 起本包升级为「聚合包」**:装这一个包,就同时带上「会话监督联动提醒」和 `@xmanrui/dsh-im`(微信机器人),不用再分两次装。两个插件的代码仍各自独立,dsh-im 继续走 npm 官方源、可单独升级。详见第 8 节。
|
|
16
|
+
|
|
17
|
+
**v1.1.0 起微信通道已改为 dsh-im 主动投递,不再使用 Server 酱 / SendKey。** 微信推送走的是 DSH 里已经接入的微信机器人,不需要任何外部 Webhook 服务。
|
|
18
|
+
|
|
19
|
+
**v1.1.1 修复「测试微信推送」报 `Invalid target id` 的问题**:dsh-im 的「复制调用参数」是一整段 JSON(`{"botId":"...","targetId":"..."}`),以前必须手动拆成两个纯 ID 分别填;现在**把整段直接粘到任意一个输入框里,会自动拆成两个纯 ID**,已存的旧配置也会自动清洗。报错也翻成了中文原因。
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 1. 它会在什么时候提醒
|
|
24
|
+
|
|
25
|
+
| 触发事件 | 判定来源 |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| 需要确认权限 | `uiSession.sessionStatus` 里 `pendingInteraction` 新出现(`approval` / `plan-review`) |
|
|
28
|
+
| 需要手动选择 | `uiSession.sessionStatus` 里 `pendingInteraction` 新出现(`question`) |
|
|
29
|
+
| 任务跑完 | `running` 由 true 变 false 且无待处理交互,或 `completionUnread` 出现上升沿(模型这一轮回复结束) |
|
|
30
|
+
|
|
31
|
+
数据源 `uiSession.sessionStatus` 是 `Map<sessionId, {...}>`,**天然覆盖全部会话**,不是只有当前打开的那条。当前正看着哪条,由 `sessions.list` 快照推导(老宿主读 `current`,0.1.6+ / 0.2.x 读 `retainedBy.mainView > 0` 的行)。
|
|
32
|
+
|
|
33
|
+
已确认的规则(按你的选择):
|
|
34
|
+
|
|
35
|
+
- **会话级「仅后台 / 离开时提醒」**(v1.3.0 修正):
|
|
36
|
+
- 窗口整个切到后台 / 人离开桌面:所有会话都算「没在看」,三类事件一律提醒。
|
|
37
|
+
- 窗口在前台(含 3 条会话并行、界面停在 A):**只有「不是当前正看着的那条」才提醒**;A 自己保持安静——不弹通知、不响提示音、不启动 30 秒计时、也不补发微信。
|
|
38
|
+
- 这就是旧规则「正看着窗口时一律不提醒」的正确解读:它指的是「正看着的那条会话」,不是「整个窗口」。
|
|
39
|
+
- **v1.5.0 三类事件都弹一张「带具体内容」的系统通知**(格式与 dsh-pet 一致):
|
|
40
|
+
- 标题统一为「会话名 · 事件标签」:需要确认权限 =「正在申请权限」、需要手动选择 =「模型在等你回答」、任务跑完 =「对话完成」。
|
|
41
|
+
- 正文显示从 `pendingInteraction` 抽出来的具体内容(问题文本 / 工具名 + 原因,最长 160 字);确实抽不到时回退成一句中文说明,不再弹空洞的「DSH 需要你操作」。
|
|
42
|
+
- **任意一张通知点一下都切到对应会话界面**(`uiWorkspace.openSession(sid)` + 窗口提前台,照抄 dsh-pet)。提示音照响、30 秒微信兜底照常。
|
|
43
|
+
- 按你的决策,本插件与 dsh-pet 各自弹一张,屏幕上会出现两张相近卡片;本插件不动 dsh-pet。
|
|
44
|
+
- **「有响应」= 切到那条会话去看 / 把待办处理掉**(v1.3.0 修正,替代旧的「回到窗口 / 页面变可见」):桌面提醒发出后,只要**切到对应会话**或**把那条 `pendingInteraction` 处理掉**,就视为已响应,取消该条的微信补发。人本来就在窗口前时,「回到窗口」永远不会触发,所以旧判据会失效——这正是之前「也没有微信通知」的原因。
|
|
45
|
+
- **三类都算 30 秒兜底**:桌面提醒发出后开一个批次,后续同类提醒并入同一批;超时把这一批里**仍未响应**的条目汇总成**一条**微信。前台也照发(只要没切过去看 / 没处理)。
|
|
46
|
+
- **发到你指定的那一个微信目标**:永远发到设置页里填的 `Bot ID + 目标 ID` 对应的那一个微信对话,不按会话来源推断、也不群发。
|
|
47
|
+
|
|
48
|
+
会话名会带进三类系统通知的标题和微信汇总里(取 `sessions.list` 行的 `displayTitle` / `title`,取不到再退回会话 id),这样能看出「哪条会话需要看 / 哪条跑完」。
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 2. 准备工作:拿到 dsh-im 的「Bot ID + 目标 ID」
|
|
53
|
+
|
|
54
|
+
微信推送依赖 `@xmanrui/dsh-im`(5.10.0,已启用,v1.2.0 起随本聚合包一起装上),用它已经接入的**普通微信机器人**做主动投递。
|
|
55
|
+
|
|
56
|
+
1. 在 DSH 里打开 **设置 → IM机器人**,确认你的微信机器人已接入并在线。
|
|
57
|
+
2. 点微信机器人卡片上的**齿轮**,复制 **Bot ID**(一串不透明字符串,形如 `bot_9577c8572d454122a4ef7fb4d8420a91`)。
|
|
58
|
+
3. 给这个机器人**新建目标**(从已聊过的会话里选,或手动填微信用户 ID `toUserId`),保存并「测试」能发通。
|
|
59
|
+
4. 点 **「复制调用参数」**,得到类似:
|
|
60
|
+
```json
|
|
61
|
+
{ "botId": "bot_9577c8572d454122a4ef7fb4d8420a91", "targetId": "tgt_a1b2c3d4e5f60718" }
|
|
62
|
+
```
|
|
63
|
+
5. 把这两个值填到本插件的设置页(第 3 节)。
|
|
64
|
+
|
|
65
|
+
> **最省事的填法**:直接把第 4 步复制到的**整段 JSON** 粘到设置页的「Bot ID」或「微信目标 ID」任意一栏,插件会自动拆成两个纯 ID 存好。不必手动裁切、也不必分别复制。以前若已把整段粘进去过,重开后会自动清洗成纯 ID。
|
|
66
|
+
|
|
67
|
+
> 微信渠道的主动投递复用「该机器人最近收到的对应用户 `context_token`」。升级 / 重启后若一直发不通(`ret=-2 prepare failed`),让目标用户给机器人发一条消息重建缓存即可,不需要反复重试心跳。
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 3. 安装与配置
|
|
72
|
+
|
|
73
|
+
### 安装(已装过可跳过)
|
|
74
|
+
|
|
75
|
+
命令行(桌面版 profile 一般是 `desktop`):
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
dsh plugin --profile desktop add "file:F:/DeepSeek Harness workspace/default-workspace/dsh-session-guardian"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
或用桌面应用内的「插件管理」填本地目录路径。**装完必须重开 DSH / 刷新页面**——浏览器半边是在页面加载时注入的。
|
|
82
|
+
|
|
83
|
+
> v1.2.0 起这个包是聚合包,装它会自动带上 dsh-im,不必再单独 `add @xmanrui/dsh-im`。本机已通过 `plugin_manager` 装好并写入 `profiles/desktop/package.json`。切换后的 profile 结构见第 8 节。
|
|
84
|
+
|
|
85
|
+
### 配置
|
|
86
|
+
|
|
87
|
+
重开 DSH 后,打开 **设置 → 会话监督联动提醒**:
|
|
88
|
+
|
|
89
|
+
| 字段 | 说明 |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| 启用本插件 | 总开关。关掉后不弹通知、不响提示音、不发微信 |
|
|
92
|
+
| 微信机器人 Bot ID | 第 2 步复制的 Bot ID(纯 ID,如 `wx_xxx`);也可把整段「复制调用参数」粘到这里 |
|
|
93
|
+
| 微信目标 ID | 第 2 步复制的 targetId(纯 ID,如 `tgt_xxx`);整段 JSON 粘进来会自动拆成两个 ID |
|
|
94
|
+
| 超时秒数 | 默认 30,可填 5–3600 |
|
|
95
|
+
| 提示音 / 音量 | 三类事件都播放一段合成提示音,并各自弹一张带具体内容的系统通知 |
|
|
96
|
+
| 超时发微信 | 关掉后只做桌面提醒,不补发微信 |
|
|
97
|
+
|
|
98
|
+
两个 ID 都留空时,只弹桌面通知,不发微信;也可以把它们写进 `cordis.patch.yml` 的 `config.botId` / `config.targetId` 作为服务端兜底(注意该文件随 profile 落盘、对本机用户可读)。
|
|
99
|
+
|
|
100
|
+
设置页有两个按钮:**测试桌面通知**(弹一张「会话名 · 对话完成」的卡,点它可跳到对应会话)、**测试微信推送**,用来确认权限与投递链路。
|
|
101
|
+
|
|
102
|
+
浏览器控制台里还有调试 API:
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
window.__dshSessionGuardian.config // 当前配置
|
|
106
|
+
window.__dshSessionGuardian.testDesktop() // 手动弹一次「会话名 · 对话完成」桌面提醒(可点它跳会话)
|
|
107
|
+
window.__dshSessionGuardian.testWechat() // 手动发一条测试微信
|
|
108
|
+
window.__dshSessionGuardian.debug() // { bound, source, away, currentSid, sessionsBound, batch }
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`debug().currentSid` 是插件推导出的「当前正看着哪条会话」,`sessionsBound` 表示 `sessions.list` 是否已订阅——排查多会话提醒时用得上。
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 4. 实现结构
|
|
116
|
+
|
|
117
|
+
| 文件 | 职责 |
|
|
118
|
+
| --- | --- |
|
|
119
|
+
| `lib/index.js`(Host half) | 注册 `/__dsh-session-guardian/wechat`(POST)与 `/health`(GET)两条同源回环路由;校验准入后调用 `ctx.dshIm.send(botId, targetId, text, { signal })` |
|
|
120
|
+
| `lib/client.js`(Client half) | 订阅 `uiSession.sessionStatus` 做**全部会话**的三类边沿检测;订阅 `sessions.list` 推当前会话;会话级静音 + WebAudio 提示音 + **三类都弹带具体内容、点击跳会话的系统通知**(标题「会话名 · 事件标签」);单批 30 秒会话级计时;设置页;超时把汇总 POST 给 Host 路由 |
|
|
121
|
+
| `cordis.patch.yml` | **一次 insert 挂两条 loader 行**(本插件 + `@xmanrui/dsh-im`),后者带防重守卫;另含服务端兜底 `config.botId` / `config.targetId` |
|
|
122
|
+
| `package.json` | `dependencies` 声明可升级的 `@xmanrui/dsh-im`(装伞包自动带上);`dsh.bundle.patch` / `dsh.client.inject` / `dsh.manifestVersion: 1` |
|
|
123
|
+
| `locale/en.json`、`locale/zh.json` | 展示元数据(en 为发现基线) |
|
|
124
|
+
| `icon.svg` | 36×36 双色渐变图标 |
|
|
125
|
+
| `smoke/smoke-test.mjs` | 纯 Node 冒烟测试(含多会话回归) |
|
|
126
|
+
|
|
127
|
+
### 会话级逻辑(v1.3.0,修复多会话静音)
|
|
128
|
+
|
|
129
|
+
- `shouldAlert(sid)`:`isAway()` 为真(窗口整个在后台)时永远提醒;否则 `sid !== currentSessionId()` 才提醒。旧版这里是 `if (!isAway()) return;` 的**窗口级一刀切**,正是 bug 根因。
|
|
130
|
+
- `currentSessionId()` / `sessionLabel(sid)`:照抄 dsh-notify-me `lib/client.js:2693-2705` 的推导(`snapshot.current` 或 `retainedBy.mainView > 0`),并从 `sessions.list` 行取显示名。
|
|
131
|
+
- `toast(title, body, isDone, sid)`:通知 `tag` 带会话 id,不同会话互不顶替;`onclick` 调 `jumpToSession(sid)` 切过去看。
|
|
132
|
+
- 批次每项记 `{kind,label,sid,dedupe,at,curAt}`,`curAt` 是「提醒发出时正看着哪条」,用来区分「后来切过去看」与「本来就是当前会话」。
|
|
133
|
+
- `itemResponded(it,cur,snap)`:切到该会话(`it.sid===cur && cur!==it.curAt`)或「非 done 且 `pendingInteraction` 消失 / 会话从快照消失」。`done` 类只认切过去看,避免把 `running→false` 的自然变化误判成已查看。
|
|
134
|
+
- `reapBatch(fromReturn)`:会话切换 / 状态变化时回收已响应条目;`fromReturn=true`(窗口从后台回前台)时,把「本来就是给当前会话弹的」提醒算已看到。
|
|
135
|
+
- `fireBatchTimeout()`:不再有 `isAway()` 闸门,超时点按最新 `currentSessionId()` + `uiSession` 快照再筛一次,只发仍未响应的条目(前台也照发)。
|
|
136
|
+
|
|
137
|
+
### 桌面通知 / 点击跳转(v1.5.0,三类都弹带具体内容)
|
|
138
|
+
|
|
139
|
+
- `textFromPayload(payload)`:照抄 `dsh-notify-me` `lib/client.js:2847-2885`,从 `pendingInteraction`(扁平字段 / `PendingApproval(toolName,reason)` / `PendingQuestion(questions[])`)里抽 `text/question/prompt/title/summary/description/message/operation/toolName/reason/name/tool` 与 `questions[0].question`,最长 160 字;`toolName` 带 `reason` 时拼成「工具名 · 原因」。
|
|
140
|
+
- `noteAlert(kind, label, sid, key, payload)`:**三类都调 `toast(...)`**,不再分叉成「只响提示音」:
|
|
141
|
+
- 标题 = `sessionLabel(sid) + ' · ' + 事件标签`,事件标签取 `正在申请权限` / `模型在等你回答` / `对话完成`(与 dsh-pet 一致)。
|
|
142
|
+
- 正文 = `textFromPayload(payload)`;抽不到具体内容时回退成「事件标签:点这条通知切到该会话查看具体内容;{n} 秒内没看会发微信。」
|
|
143
|
+
- 第 4 参 `sid` 既作通知 `tag`(不同会话互不顶替)又作点击跳转目标;`playCue(isDone)` 由 `toast()` 内部统一播放。
|
|
144
|
+
- `jumpToSession(sid)` / `toast()` 的 `onclick`:照抄 `dsh-pet` `lib/client.js:3431-3448` 的 `wireNotificationClick` / `jumpToSession`——`lookupService('uiWorkspace').openSession(sid)` 切会话(旧版没该服务就跳过)、`window.focus()`、Electron 下用 `dsh://open` iframe 把窗口提到前台。
|
|
145
|
+
- `testDesktop()` 与 `window.__dshSessionGuardian.testDesktop()` 弹「会话名 · 对话完成」这张卡,方便验证通知与点击跳转。
|
|
146
|
+
|
|
147
|
+
### 为什么微信要经 Host 中转
|
|
148
|
+
|
|
149
|
+
浏览器半边只负责「什么时候该提醒」和「桌面通知 / 提示音」;`dshIm` 主动投递服务只存在于 **Host** 侧(`ctx.dshIm`)。所以超时后浏览器把 `{title, desp, botId, targetId}` POST 给本插件自己的同源路由,由 Host 半边调用 `ctx.dshIm.send(...)` 发出去。这样也自动复用了 DSH 的 HTTP 准入裁决(`ctx.connection.requestRejection(req) === undefined` 才放行)。
|
|
150
|
+
|
|
151
|
+
`dshIm` 是**软依赖**:用 `ctx.inject(['dshIm'], ...)` 按需绑定,即使 dsh-im 暂时未就绪,本插件仍能挂载(此时发微信返回明确的 `502 dshim-unavailable`,桌面提醒不受影响)。
|
|
152
|
+
|
|
153
|
+
### 关键宿主契约(照抄已验证写法)
|
|
154
|
+
|
|
155
|
+
- 路由一律 `ctx.effect(() => ctx.webServer.register({ kind: 'exact', path, handler }))`,返回值作为清理函数。
|
|
156
|
+
- 每条 HTTP 路由先过 `ctx.connection.requestRejection(req)`,仅 `undefined` 放行。
|
|
157
|
+
- 客户端经 `window.__ModuleLoader__.load({ id, factory })` 注入;`uiSession` 用 `ctx.get('uiSession')` 懒查(直接读 `ctx.uiSession` 会抛 `cannot get property "uiSession" without inject`)。
|
|
158
|
+
- 设置页走 `ctx.slots.inject('settings.section', ...)`,配置存 `localStorage`。
|
|
159
|
+
- 不声明 `engines.dsh`;不含 `cordis` 依赖;不含 `preinstall/install/postinstall/prepare` 脚本。
|
|
160
|
+
|
|
161
|
+
### dsh-im 主动投递契约(来自 `PROACTIVE_DELIVERY.md`)
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
ctx.dshIm.send(botId, targetId, text, { signal, format? }) -> { sent: true }
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
- `result.sent !== true` 一律视为失败。
|
|
168
|
+
- 只发非空文字;不保存投递历史、不自动重试、不生成幂等键。`{sent:true}` 只代表平台接口已接受,不承诺送达或已读。
|
|
169
|
+
- 微信长文本按 1800 字分段;本插件把汇总文本截到 1800 字以内,避免刷屏。
|
|
170
|
+
- 常见错误:`unknown-bot` / `unknown-target` / `bot-not-connected`(机器人离线)/ `delivery-failed` / `target-rejected` / `invalid-target`(`Invalid target id`,即把整段 JSON 当成纯目标 ID 传了,v1.1.1 起本插件会自动拆分)。
|
|
171
|
+
- v1.1.1 起,这些英文错误在「测试微信推送」里会显示为中文原因,不再是英文原文或一坨 JSON。
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 5. 取舍与已知边界
|
|
176
|
+
|
|
177
|
+
- **只提醒「插件加载后新发生」的事件**:插件加载那一刻已经挂着的待办 / 已完成状态属于陈旧状态,只建立基线、不补发提醒(seed 抑制)。
|
|
178
|
+
- **单批 30 秒,会话级回收**:第一条提醒起算,后续同类提醒并入同一批(最多 20 条);切到对应会话 / 把待办点掉即从批次里移除,全响应则整批取消;超时只对仍未响应的条目发一条汇总微信,不会刷屏。
|
|
179
|
+
- **桌面通知需要系统权限**:首次用户手势会申请 `Notification.permission`;没授权时只有提示音,不会有通知栏。Electron 44 的 renderer 通知不放操作按钮,所以本插件的桌面通知只做提醒,不在通知上做「同意 / 拒绝」。点任意一张通知都会切到对应会话;旧版宿主没有 `uiWorkspace.openSession` 时点击仍能 `window.focus()` 把窗口提前台,只是不会切换会话。
|
|
180
|
+
- **微信机器人必须在线且目标有效**:机器人离线或目标 ID 不对时会返回错误,页面控制台会记一条 warn;桌面提醒仍然照常。
|
|
181
|
+
- **「当前会话」依赖 `sessions.list` 的 retention 信息**:`inject` 已声明 `sessions`;若某个宿主版本完全没有该服务或快照结构不同,`currentSessionId()` 会退回 `null`,此时前台下所有会话都会提醒(不会漏提醒,只是可能多提醒当前那条)。后台逻辑不受影响。
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 6. 改完代码如何让已安装的插件生效
|
|
186
|
+
|
|
187
|
+
`profiles/desktop/node_modules/dsh-session-guardian` 是安装时**复制**进去的目录(不是软链),所以直接改工作区源码不会自动同步。让改动生效有两种方式:
|
|
188
|
+
|
|
189
|
+
1. **重启 DSH**:Host half 由 profile 的 bundle 加载,重启会重新读盘。
|
|
190
|
+
2. **重新安装一次**(推荐,确保 profile 副本与源码一致):
|
|
191
|
+
```bash
|
|
192
|
+
dsh plugin --profile desktop remove dsh-session-guardian
|
|
193
|
+
dsh plugin --profile desktop add "file:F:/DeepSeek Harness workspace/default-workspace/dsh-session-guardian"
|
|
194
|
+
```
|
|
195
|
+
然后重开 DSH 刷新页面,client half 才会重新注入。
|
|
196
|
+
|
|
197
|
+
验证:`npm test` 跑冒烟测试;`GET /__dsh-session-guardian/health` 返回 `200` 且 `dshImReady: true`(从 DSH 页面里同源访问;命令行裸请求会被准入裁决挡成 403,属预期)。
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## 7. 开发与自检
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
npm test # 冒烟测试(纯 Node,无需 DSH 宿主)
|
|
205
|
+
npm run pack # npm pack --dry-run,确认打包文件清单
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## 8. 聚合包:一次装两个 + 怎么各自更新(v1.2.0)
|
|
211
|
+
|
|
212
|
+
这是按你「把 2 个插件打包成 1 个插件,同时两个插件都能随时拿到各自最新版」的要求做的形态。需要诚实说明里面的约束,别把它理解成「揉成一份代码」或「后台自动升级」。
|
|
213
|
+
|
|
214
|
+
### 8.1 「装 1 个包带上两个」是怎么做到的
|
|
215
|
+
|
|
216
|
+
- `package.json` 里声明 `dependencies: { "@xmanrui/dsh-im": "^5.10.0" }`(**可升级范围,不锁死精确版本**)。装本包时 pnpm 会把 dsh-im 一起装进 profile 的 `node_modules`。
|
|
217
|
+
- `cordis.patch.yml` 里**一个 `insert` 挂两条 loader 行**:
|
|
218
|
+
1. `id: dsh-session-guardian / name: 'dsh-session-guardian'` —— 本插件自己;
|
|
219
|
+
2. `id: session-guardian-dsh-im / name: '@xmanrui/dsh-im'` —— 微信机器人,带一条 `disabled: !!js` **防重复挂载守卫**(含 `e.options.id !== 'session-guardian-dsh-im'` 自排除,照抄 dsh-better-sidebar 已验证范式)。
|
|
220
|
+
- 宿主 client 半边是按每条 loader 行的 `options.name` 逐条扫描的:两条行会各自加载自己包里的 `./client`。所以「会话监督联动提醒」设置页和 dsh-im 的「IM机器人」设置页**都在**,不会丢。
|
|
221
|
+
- 两个插件的代码仍然是各自独立的两份(本包 + dsh-im 的 npm 包),没有揉成一份,也没有把 dsh-im 那 29 MiB、带原生模块的源码物理复制进来——那样会变成固定快照,反而违背「随时最新」。
|
|
222
|
+
|
|
223
|
+
### 8.2 为什么 dsh-im 还得保留 profile 顶层直接依赖
|
|
224
|
+
|
|
225
|
+
DSH 的「检查更新」和 dsh-im 自带更新器**只认 profile 顶层 `dependencies` 里的直接 registry 依赖**;如果一个包只是伞包的传递依赖,就不会被当成独立可更新项。因此切换时:
|
|
226
|
+
|
|
227
|
+
- **保留** `profiles/desktop/package.json` 顶层的 `"@xmanrui/dsh-im": "^5.10.0"`(这是它能拿官方新版的前提);
|
|
228
|
+
- **从 `dsh.profile.bundles` 里移除**原来那条独立的 `"@xmanrui/dsh-im"` 挂载行——否则它自带的 patch 会再挂一条同 `name` 的 entry,与聚合包这条重复,启动会报 `duplicate prefix route` / 同名包多来源。挂载权统一收归聚合包这一条。
|
|
229
|
+
- 第二条 entry 上的 `disabled: !!js` 守卫是安全网:万一有人又把独立 bundle 加了回来且排在前面,本行会自动退避,不会双挂。
|
|
230
|
+
|
|
231
|
+
> 聚合包这条 dsh-im entry 用独立 id `session-guardian-dsh-im`(不用 dsh-im 自带的 `xmanrui-dsh-im`),是为了让守卫里的「自排除」能区分自己和另一条同名挂载:若 id 相同,自排除会把需要退避的独立挂载也排掉,守卫就失效了。已核实 dsh-im 不按 loader entry id 索引机器人配置(走 settings / credential-store),换 id 不影响你已配好的微信机器人。
|
|
232
|
+
|
|
233
|
+
### 8.3 「我说一声就更新」——怎么做、边界在哪
|
|
234
|
+
|
|
235
|
+
按你选的「我说一声就更新」,**不做后台定时 / 启动自动检查**(DSH 本身也没有这种自动更新服务)。你要更新时告诉我一声,我显式跑一条依赖更新把两个插件都拉到各自最新,然后你重开 DSH 生效:
|
|
236
|
+
|
|
237
|
+
- **dsh-im**:作为顶层直接依赖,走 `pnpm update @xmanrui/dsh-im`(或经插件管理器的「检查更新」)拉 npm 官方最新版 —— 这是真正能拿到「官方新版」的那一个。
|
|
238
|
+
- **dsh-session-guardian**:它是 `file:` 本地源码、不是发布到 npm 的包,没有「官方发布的最新版本」可拉;它的「最新」就等于本工作区这份源码。我改完源码、同步进 profile 副本即可。
|
|
239
|
+
- 更新完**必须重开 DSH 桌面端**(清 ESM 缓存、让两个 client 半边重新注入)。
|
|
240
|
+
|
|
241
|
+
诚实边界:在「一个全新 profile、纯靠伞包安装、顶层没有 dsh-im 直接依赖」的场景里,dsh-im 会退化成传递依赖,失去市场/自带更新器的独立识别——这时只能整包重发 + 手动重解析依赖。本机通过保留顶层直接依赖规避了这一点。
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
当前版本:**1.5.0**(三类事件都弹一张带具体内容、对齐 dsh-pet 格式的系统通知,点任意通知切到对应会话;提示音与 30 秒微信兜底全部保留;会话级提醒 / 会话级「已响应」;装 1 个包带上微信提醒 + dsh-im 微信机器人,dsh-im 走 npm 官方源可升级;微信通道 dsh-im 主动投递;v1.1.1 的整段 JSON 自动拆分与中文报错仍在)。
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# dsh-session-guardian 聚合包的 profile 挂载层(v1.2.0)。
|
|
2
|
+
#
|
|
3
|
+
# 一个包挂两条 cordis loader 行,实现「装这一个包就带上两个插件」:
|
|
4
|
+
# ① 会话监督联动提醒自己
|
|
5
|
+
# ② @xmanrui/dsh-im(微信机器人,普通微信主动投递)
|
|
6
|
+
# 两个插件的代码仍各自独立;dsh-im 继续走 npm 官方源、可单独升级。
|
|
7
|
+
#
|
|
8
|
+
# 宿主 client 半边是按每条 loader 行的 options.name 逐条扫描的(client-modules
|
|
9
|
+
# 已证实):两条行会各自加载自己包里的 ./client,因此「会话监督联动提醒」设置页
|
|
10
|
+
# 与 dsh-im 的「IM机器人」设置页都会保留。
|
|
11
|
+
#
|
|
12
|
+
# 【关键约束】@xmanrui/dsh-im 必须保留在 profile 顶层 dependencies(^5.10.0,
|
|
13
|
+
# 可升级范围)——这是它能被市场「检查更新」和它自带更新器识别的前提;
|
|
14
|
+
# 但「不要」同时把它作为独立条目留在 dsh.profile.bundles 里,否则那一行的
|
|
15
|
+
# patch 会再挂一条同 name 的 entry,与聚合包这条重复挂载 → 启动报
|
|
16
|
+
# "duplicate prefix route" / 同名包多来源。切换时请把独立行从 bundles 移除。
|
|
17
|
+
#
|
|
18
|
+
# 第二条带 name 级防重守卫:若在本行之前已有别的 enabled 行挂了
|
|
19
|
+
# @xmanrui/dsh-im(例如某次操作把独立 bundle 又加了回来且排在前面),
|
|
20
|
+
# 本行就退避,让先挂载的那条拥有它,避免双挂。loader 按 entry-list 顺序
|
|
21
|
+
# 求值、只看本行之前的行(与 dsh-better-sidebar 的守卫同一范式)。
|
|
22
|
+
#
|
|
23
|
+
# 覆盖方式:profile 里同名 id 的行会「整份替换」下面的 config(不做深合并),
|
|
24
|
+
# 所以 lib/index.js 的 normalizeConfig 会对每个缺省 key 用同样的默认值兜底。
|
|
25
|
+
# 若要把微信目标放在服务端而不是设置页,可在这里填 config.botId / config.targetId;
|
|
26
|
+
# 但注意本文件随 profile 落盘、对本机用户可读,更推荐在插件设置页里填写。
|
|
27
|
+
- insert:
|
|
28
|
+
- id: dsh-session-guardian
|
|
29
|
+
name: 'dsh-session-guardian'
|
|
30
|
+
config:
|
|
31
|
+
# 服务端兜底:dsh-im 微信机器人的 Bot ID(留空则用浏览器设置页里保存的值)
|
|
32
|
+
botId: ''
|
|
33
|
+
# 服务端兜底:该机器人下指定的那一个微信目标 ID(稳定别名)
|
|
34
|
+
targetId: ''
|
|
35
|
+
# 这条用一个独立 id(与 dsh-im 自带 patch 的 id 不同),是为了让下面守卫里的
|
|
36
|
+
# 「自排除」能区分「自己」和「另一条同名 entry」:若沿用相同 id,自排除会把需要
|
|
37
|
+
# 退避的那条独立挂载也一起排除掉,守卫就失效了。照抄 dsh-better-sidebar 的
|
|
38
|
+
# 已验证范式(disabled 里带 `&& e.options.id !== '<自己 id>'`),两种 loader
|
|
39
|
+
# 实现(求值时 entries() 是否含当前行)下都正确。
|
|
40
|
+
# 已核实 dsh-im 的微信机器人配置不按 loader entry id 索引(走 settings /
|
|
41
|
+
# credential-store),换 id 不影响你已配好的微信机器人。
|
|
42
|
+
- id: session-guardian-dsh-im
|
|
43
|
+
name: '@xmanrui/dsh-im'
|
|
44
|
+
disabled: !!js "[...ctx.loader.entries()].some((e) => e.options.name === '@xmanrui/dsh-im' && e.options.id !== 'session-guardian-dsh-im' && !e.disabled)"
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# v1.5.0 发布说明(会话监督联动提醒 · 首个正式版本)
|
|
2
|
+
|
|
3
|
+
发版日期:2026-11-11
|
|
4
|
+
npm 包:`dsh-session-guardian@1.5.0`
|
|
5
|
+
仓库:GitHub `sweet560/dsh-session-guardian` / Gitee `wtt520wyl/dsh-session-guardian`
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 这个插件是做什么的
|
|
10
|
+
|
|
11
|
+
给 **DeepSeek Harness 桌面端**用的「会话监督 + 联动提醒」聚合包。它会盯着你所有正在跑的会话:
|
|
12
|
+
|
|
13
|
+
- 哪条会话**需要你确认权限**、**需要你手动选择**、或者**任务跑完了**,都弹一张**带具体内容**的
|
|
14
|
+
系统通知并响提示音;
|
|
15
|
+
- **点那张通知就能直接切到对应会话界面**,不用自己在会话列表里找;
|
|
16
|
+
- 如果桌面提醒发出后 **30 秒**还没切过去看 / 没把待办处理掉,就通过 **dsh-im 的微信机器人
|
|
17
|
+
(普通微信)**把汇总提醒发到你指定的那一个微信对话。
|
|
18
|
+
|
|
19
|
+
装这一个包,就同时带上了「会话监督联动提醒」和 `@xmanrui/dsh-im`(微信机器人)两个插件。
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 包含的两个插件(没漏)
|
|
24
|
+
|
|
25
|
+
| 组成部分 | 来源 | 作用 |
|
|
26
|
+
| --- | --- | --- |
|
|
27
|
+
| 会话监督联动提醒(本包) | 本仓库 `lib/index.js` + `lib/client.js` | 监督全部会话的三类事件、桌面弹窗 + 提示音、30 秒超时汇总、设置页、点击跳会话 |
|
|
28
|
+
| `@xmanrui/dsh-im` | npm 官方源,声明为 `^5.10.0` 可升级依赖 | 普通微信机器人,负责把超时汇总真正发到微信 |
|
|
29
|
+
|
|
30
|
+
`cordis.patch.yml` 一个 `insert` 挂两条 loader 行(本插件 + dsh-im),两者代码各自独立,
|
|
31
|
+
没有把 dsh-im 那 29 MiB、带原生模块的源码物理复制进来。dsh-im 继续走 npm 官方源、可单独升级。
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 主要特性
|
|
36
|
+
|
|
37
|
+
- **三类触发全覆盖**:需要确认权限 / 需要手动选择 / 任务跑完(模型这一轮回复结束)。
|
|
38
|
+
- **带具体内容的通知**:标题「会话名 · 事件标签」(正在申请权限 / 模型在等你回答 / 对话完成),
|
|
39
|
+
正文是从 `pendingInteraction` 抽出的问题文本 / 工具名 + 原因,最长 160 字;抽不到时回退中文说明。
|
|
40
|
+
- **点击跳会话**:所有系统通知的 `onclick` 走 `uiWorkspace.openSession(sid)` + `window.focus()`
|
|
41
|
+
+ Electron `dsh://open` 唤起窗口(照抄 dsh-pet 已验证写法)。
|
|
42
|
+
- **会话级监督**:窗口前台时只提醒「没在看」的那些会话,正盯着的那条保持安静;窗口整个切到
|
|
43
|
+
后台时所有会话都提醒。修复了「3 条会话并行、界面停在一条时其他 2 条不通知也不发微信」的 bug。
|
|
44
|
+
- **会话级「已响应」**:切到对应会话去看、或把那条待办处理掉,就取消微信补发;前台 30 秒没理
|
|
45
|
+
照样发微信。
|
|
46
|
+
- **微信兜底可配置**:Bot ID + 目标 ID、超时秒数、提示音 / 音量、总开关都在设置页;
|
|
47
|
+
「复制调用参数」整段 JSON 可直接粘进任意一栏自动拆分;英文报错已中文化。
|
|
48
|
+
- **不依赖外部 Webhook**:微信推送走 DSH 里已接入的微信机器人主动投递,不需要 Server 酱 / SendKey。
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 从旧版 dsh-wechat-reminder 升级
|
|
53
|
+
|
|
54
|
+
本机旧包名是 `dsh-wechat-reminder`。改名重装后,浏览器半边会优先读新 localStorage key
|
|
55
|
+
`dshSessionGuardian.config`,为空时回退读旧 key `dshWechatReminder.config`:
|
|
56
|
+
**已填的 Bot ID / 目标 ID / 超时秒数不会丢**,无需重新填。旧 key 保留不清,避免误删。
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 安装
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
dsh plugin --profile desktop add dsh-session-guardian
|
|
64
|
+
# 或本地源码安装:
|
|
65
|
+
dsh plugin --profile desktop add "file:F:/.../dsh-session-guardian"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
装完**重开 DSH 桌面端**,再到 设置 → 会话监督联动提醒 填写 Bot ID / 目标 ID,
|
|
69
|
+
用「测试桌面通知」「测试微信推送」验证链路。
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 验证
|
|
74
|
+
|
|
75
|
+
- `npm test`:离线冒烟测试(本版本共 40 项断言,含改名迁移回归、三类通知、会话级规则、
|
|
76
|
+
整段 JSON 自动拆分、错误中文化、点击跳会话等)。
|
|
77
|
+
- `npm pack --dry-run`:确认发布文件清单。
|
|
78
|
+
- `.github/workflows/publish.yml`:推 main / 打 `v*` tag / 发 Release 时,自检通过后用幂等守卫
|
|
79
|
+
判断该版本是否已发布,未发布且配了 `NPM_TOKEN` 才自动 `npm publish`。
|
|
80
|
+
没改版本号的 push 自动跳过,不会因 npm 版本不可变报错。
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 自动发布的一次性配置
|
|
85
|
+
|
|
86
|
+
在 GitHub 仓库 **Settings → Secrets and variables → Actions → New repository secret** 添加:
|
|
87
|
+
|
|
88
|
+
- `NPM_TOKEN`:npmjs.com 上属于包 owner、对本包有 publish 权限的 **Automation token**。
|
|
89
|
+
不要设置 Allowed IP ranges 白名单(GitHub-hosted runner 出口 IP 不固定)。
|
|
90
|
+
|
|
91
|
+
配好后,重大更新只要**把 `package.json` 的 `version` 升成 registry 上还不存在的新值**并推到
|
|
92
|
+
main / 打 `v*` tag,CI 就会自动把它发布到 npm。
|
package/icon.svg
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
|
4
|
+
<stop offset="0" stop-color="#3B82F6"/>
|
|
5
|
+
<stop offset="1" stop-color="#8B5CF6"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect x="0" y="0" width="36" height="36" rx="9" fill="url(#g)"/>
|
|
9
|
+
<path fill="#FFFFFF" d="M18 8.2c-5 0-9 3.4-9 7.6 0 2.4 1.3 4.5 3.4 5.9l-.8 3.3c-.1.4.3.7.7.5l3.9-2.1c.6.1 1.2.1 1.8.1 5 0 9-3.4 9-7.6s-4-7.7-9-7.7z"/>
|
|
10
|
+
<circle cx="13.6" cy="15.6" r="1.25" fill="#3B82F6"/>
|
|
11
|
+
<circle cx="22.4" cy="15.6" r="1.25" fill="#3B82F6"/>
|
|
12
|
+
<path fill="#FFFFFF" d="M25 21.6a6 6 0 0 0-8.4 5.5H26a1 1 0 0 0 1-1v-3.2a6 6 0 0 0-2-1.3z"/>
|
|
13
|
+
</svg>
|