dsh-session-superagent 0.8.16 → 0.8.27

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 CHANGED
@@ -2,6 +2,203 @@
2
2
 
3
3
  格式参照 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [0.8.27] - 2026-11-02 —— 文案核对 + 把 0.8.21–0.8.27 一次发到 npm
6
+
7
+ ### 文案
8
+ - 「已添加,30 秒内开始轮询」不准确:`startWatcher()` 会**立刻**跑一次 tick(不等 30 秒的下限),
9
+ 改成「已添加,首轮立即开始轮询」。
10
+
11
+ ### 发布
12
+ - 一次把 0.8.21–0.8.27 发出去(npm 上原来停在 0.8.20)。这一批里有三个真机事故修复:
13
+ `inactive context`(唤醒会失败)、未预期异常不退避、热重载后重复路由导致面板全灭。
14
+
15
+ ### 测试
16
+ - 合计 **89 例**。
17
+
18
+ ## [0.8.26] - 2026-11-02 —— 卸载时真的把路由摘掉(重复路由的根因)
19
+
20
+ 0.8.25 让「一条重复不再全灭」,这一版处理根因:**卸载时要把旧路由摘掉**,否则宿主重载后
21
+ 永远是新旧两套并存,面板读到的是旧 handler(改了密码却还是老数据那种感觉)。
22
+
23
+ ### 修复
24
+ - 把清理逻辑抽成 `teardown()`,同时挂在 **`ctx.effect(() => teardown)`** 与 `ctx.on('dispose', …)` 两条路上:
25
+ effect 作用域销毁时一定调用,事件广播方式也能覆盖。`teardown()` 本身可重复调用(摘完就清空列表)。
26
+
27
+ ### 测试
28
+ - 新增用例:卸载时六条路由都被 unregister;重复卸载不会重复 unregister。合计 **89 例**。
29
+
30
+ ## [0.8.25] - 2026-11-02 —— 热重载时一条重复路由会让**全部**接口消失
31
+
32
+ ### 真机现场
33
+ ```
34
+ server = {at: 19:38:26Z, error: 'webserver: duplicate exact route "/session-superagent/state"'}
35
+ ```
36
+ 宿主热重载时,旧实例的路由还没被 dispose,新实例注册第一条就抛 —— 而注册是**一整块 try**,
37
+ 于是六条路由全军覆没,面板一片空白(数据接口全 404),只有重启才能恢复。
38
+
39
+ ### 修复
40
+ 每条路由改成单独容错(`addRoute()`):重复的那条把原因记进 `state.server.failed`,
41
+ 其余照常注册。单条重复不再等于全站不可用。
42
+
43
+ ### 守门
44
+ 新增 `test/route-resilience.test.mjs`:让宿主替身在注册 `client-error` 时抛重复路由,
45
+ 断言其余 5 条仍然注册成功、`/state` 仍在、失败原因被记录下来。
46
+
47
+ ### 测试
48
+ - 合计 **88 例**。
49
+
50
+ ## [0.8.24] - 2026-11-02 —— 安装说明是错的(`npm i` 不会让 DSH 加载插件)
51
+
52
+ ### 修复
53
+ README 原来写「方式一:npm,`npm i dsh-session-superagent`」,但 **DSH 插件必须装进 profile**
54
+ 才会被加载(包里的 `dsh.bundle.patch` 由 profile 的安装流程读取);`npm i` 只是下载到本地
55
+ `node_modules`,什么都不会发生。另一条「`dsh plugin add < GitHub URL>`」本机也没有这个子命令。
56
+
57
+ 对照同机的其它插件(`dsh-unlag` 用 `dsh plugin install dsh-unlag`、
58
+ `dsh-better-sidebar` 用 `dsh plugin --profile web add …@latest`)改成:
59
+
60
+ ```bash
61
+ dsh plugin install dsh-session-superagent
62
+ dsh plugin --profile desktop add dsh-session-superagent@latest
63
+ dsh plugin install /path/to/dsh-session-superagent # 本地开发
64
+ ```
65
+
66
+ 并写清「`npm i` 不会生效」与「装进 profile 后是硬链接、新增文件要 `sync:profile`」。
67
+
68
+ ### 测试
69
+ - 合计 **87 例**。
70
+
71
+ ## [0.8.23] - 2026-11-02 —— README 补「故障排查」,并把链接到的文档发进包
72
+
73
+ ### 文档
74
+ - README 新增**故障排查**表,全部来自真实事故:新增文件没进 profile 导致插件不挂载、
75
+ 硬链接断了导致「改了但没生效」、`inactive context`、幽灵会话不退避、
76
+ npm 刚发布时的 `ETARGET`、以及「有动静但没被叫醒」该看哪里(`watch_status` 的 `deferred=`、面板的「最近被过滤」)。
77
+ - README 底部链接的 `UPDATING.md` / `CHECKLIST.md` / `MAINTENANCE.md` 之前**不在 `files` 里**,
78
+ npm 页面上点了是 404。现在一起发出去(`npm pack` 10 → 13 个文件)。
79
+
80
+ ### 测试
81
+ - 合计 **87 例**。
82
+
83
+ ## [0.8.22] - 2026-11-02 —— 未预期异常也要退避(0.8.21 的同一类问题再补一刀)
84
+
85
+ 0.8.21 修的是「读 `ctx.agents` 会抛」。修完回头看 tick 的兜底:
86
+
87
+ ```js
88
+ } catch (error) {
89
+ entry.lastError = String(error.message) // 只记错误
90
+ }
91
+ ```
92
+
93
+ 也就是说 **任何**未预期异常都会让 watcher 按 `intervalSec` 无限重试 —— 正是 inactive-context
94
+ 那次的症状(`consecutiveFailures: 0`、`nextAt: None`)。现在 catch 里也 `bumpFailure(entry, every)`,
95
+ 坏掉的东西最多 30 分钟试一次,不会持续打 GitHub。
96
+
97
+ ### 说明
98
+ 这属于「同类问题系统性收口」,不是新功能:凡是走出 `pollOnce` 正常分支的失败,现在都会进退避窗口。
99
+
100
+ ### 测试
101
+ - 合计 **87 例**(该分支需要真实定时器上下文,靠重载后的真机复验确认)。
102
+
103
+ ## [0.8.21] - 2026-11-02 —— 真机复验抓到的**唤醒致命 bug**:inactive context
104
+
105
+ 宿主重载后(`routes: 6`,0.8.14–0.8.20 全部生效)做了真机复验,造了一条绑定「不存在会话」的 watcher:
106
+
107
+ ```
108
+ lastError: cannot get required service "agents" in inactive context
109
+ consecutiveFailures: 0 ← 没进退避
110
+ nextAt: None ← 每 intervalSec 都会再打一次 GitHub
111
+ ```
112
+
113
+ ### 根因
114
+ 定时器回调跑在**非活跃上下文**里,直接读 `ctx.agents` 会抛错。读 `goals` 一直用的是
115
+ `ctx.get('goals')`(不抛),唯独 `agents` 用的是 `ctx.agents` —— 这行代码在**每一次真唤醒**、
116
+ 每一次「会话还在不在」判断上都会执行。
117
+
118
+ ### 影响
119
+ - 幽灵会话判断(0.8.11)在本机其实从没跑到过:轮询在更早的一行就抛了。
120
+ - 更严重的是:**如果宿主对定时器上下文同样报错,真实唤醒也会失败**(看起来"在盯",其实一次都没成)。
121
+
122
+ ### 修复
123
+ 新增 `agentsService(ctx)`:优先 `ctx.get('agents')`,取不到再兜底直接读;`wake()`、
124
+ `pollOnce()` 与两个工具(`watch_start` / `watch_stop`)全部改走它。
125
+
126
+ ### 守门
127
+ 新增 `test/agents-context.test.mjs`:用一个**读 `ctx.agents` 就抛**的宿主替身,验证
128
+ `watch_start` / `watch_status` 仍然正常,并断言不会把宿主的上下文错误当成轮询错误。这条路以前完全没测过。
129
+
130
+ ### 测试
131
+ - 合计 **87 例**。
132
+
133
+ ## [0.8.20] - 2026-11-02 —— 冷启动演练:工具返回要能证明「选项生效了」
134
+
135
+ 照 HANDOFF + README 走一遍「加 watcher → 观察 → 确认」(不看代码,只看文档),暴露的摩擦点:
136
+
137
+ ### 修复
138
+ - **`watch_start` 的返回看不出生效项**:只说 `watching … every 3600s`,`observe` / 过滤 / 时段 / 限流
139
+ 到底有没有落上,只能再开面板或读状态文件。现在返回会带回显:
140
+ `[observe-only(只记录不唤醒) labels=ai-fix batch=5 perHour=6 goal="…"]`,更新已有 watcher 时标 `(updated existing watcher)`。
141
+ - **`watch_status` 同样不回显**:现在每行带 `observe / isolated / paused / hours= / labels= / onlyAssigned / deferred=`,
142
+ 一眼能看出「为什么没动静」。
143
+ - HANDOFF 补「在会话里自查」小表:首选工具(`watch_status` / `watch_doctor`),其次直接读状态文件,
144
+ HTTP 只在面板/调试时用 —— 并写清端口每次重启都会变、怎么找(会话上下文里的 GUI URL 或按 PID 查 netstat)。
145
+ 冷启动演练时就是卡在「端口找不到」这一步。
146
+
147
+ ### 发布
148
+ - 已发 `dsh-session-superagent@0.8.20`(一次性带上 0.8.17–0.8.20 的改动;`latest = 0.8.20`)。
149
+ - tarball 直装验证:`version 0.8.20`、**routes 6**、`client.js` 语法通过、
150
+ 包内只有 7 项(CHANGELOG/LICENSE/README/client.js/cordis.patch.yml/lib/package.json)。
151
+ - **CI 已接入 `preflight --fast`**:GitHub Actions 在 Node 22 / 24 两个矩阵上跑
152
+ 「单测 / 语法 / 版本一致 / 路由面 / 包内容」,本地无 profile 时该检查自动 SKIP。
153
+ 运行记录:`gh run list` → `test (22)` 与 `test (24)` 均 success。
154
+
155
+ ### 测试
156
+ - 合计 **85 例**。
157
+
158
+ ## [0.8.19] - 2026-11-02 —— `pnpm run preflight`:把「发版必跑」变成一条命令
159
+
160
+ ### 新增
161
+ - `scripts/preflight.mjs`:七项检查一次跑完并以退出码给结论 ——
162
+ 单测、语法、profile 一致性(`--check`)、版本五处一致、路由面(6 条)、`npm pack` 内容、冒烟(gh 未登录自动 SKIP)。
163
+ HANDOFF 的验证清单从散文改成这一条命令(散文会过期,脚本不会)。
164
+ - 自测时抓到并修掉一个真问题:preflight 自己的 `DSH_SUPERAGENT_NO_POLL=1` 会**泄漏给子进程**,
165
+ 导致冒烟「3 项 OK / 9 项 FAIL」。现在子进程环境会剔掉这三个开关。
166
+
167
+ ### 变更
168
+ - **包内容精简**:`files` 去掉 `scripts/`,只发运行时需要的 6 项(`lib`、`client.js`、
169
+ `cordis.patch.yml`、README、CHANGELOG、LICENSE)。`npm pack` 从 14 个文件降到 10 个。
170
+ 开发脚本仍在仓库里(`pnpm run preview` / `smoke:observe` / `preflight` 照旧可用)。
171
+
172
+ ### 测试
173
+ - 合计 **85 例**(本轮无新增用例,改动是工具与打包)。
174
+
175
+ ## [0.8.18] - 2026-11-02 —— watch_start 工具补齐参数(面板能配的,工具也要能配)
176
+
177
+ ### 修复
178
+ - **`watch_start` 之前只接受 `repo` / `intervalSec`**:面板上能设的过滤、仅观察、隔离、时段、
179
+ 限流、每 watcher 目标,用工具都设不了 —— 而 README 把它们都写成能力。现在工具参数补齐
180
+ (`labels` / `ignoreLabels` / `onlyAssigned` / `assignee` / `observe` / `isolated` / `workdir` /
181
+ `activeHours` / `batchLimit` / `maxWakesPerHour` / `goal`)。
182
+ - **`intervalSec` 没夹紧**:工具里只判断 `> 0`,能把 5 秒写进状态文件(调度时按 30 秒跑,
183
+ 状态与实际不一致,面板也会显示「每 5s」)。现在夹到 30–3600。
184
+ - `workdir` 走工具时也要求绝对路径且存在。
185
+
186
+ ### 测试
187
+ - 新增 2 例:工具参数落盘与夹紧(5→30、99→20、0→1)、相对 `workdir` 被拒。合计 **84 例**。
188
+
189
+ ## [0.8.17] - 2026-11-02 —— README 声明与面板能力对齐(限流可改)
190
+
191
+ 对着 README 逐条核代码时发现的落差:`batchLimit` / `maxWakesPerHour` 在服务端和工具里都支持,
192
+ **面板却没法设** —— 用户按 README 找这两个开关会找不到。
193
+
194
+ ### 修复
195
+ - 「触发」页新增两个字段:**单次最多唤醒条数**(默认 5,夹到 1-20)、**每小时最多唤醒次数**(默认 6,夹到 1-60)。
196
+ - 行内编辑器同样能改这两项,保存走同一个 `POST /watch`。
197
+ - README 的限流一行补上「面板可改」并标注默认值与范围。
198
+
199
+ ### 测试
200
+ - 新增用例:面板设置 `maxWakesPerHour: 99` 会被夹成 60 并回传。合计 **82 例**。
201
+
5
202
  ## [0.8.16] - 2026-11-02 —— 第一次在真浏览器里看面板,抓到一个对比度问题
6
203
 
7
204
  宿主的 web 界面有鉴权(`dsh web authentication required`),ego 浏览器过不去,所以搭了个
@@ -404,7 +601,7 @@ OK .dsh-zhuli 下正好一个副本
404
601
 
405
602
  ## [0.8.0] - 2026-11-01 —— 移植官方 superagent 的 GUI
406
603
 
407
- 把官方 `dsh-superagent`(D:\主工作区\projects\dsh-superagent-prototype)的**设计系统与信息架构**
604
+ 把官方 `dsh-superagent`(本地 checkout 的 dsh-superagent-prototype)的**设计系统与信息架构**
408
605
  搬进「主理鱼」标签页,数据接我们自己的 `/state`、`/watch`。
409
606
 
410
607
  ### 移植了什么
@@ -534,7 +731,7 @@ OK .dsh-zhuli 下正好一个副本
534
731
 
535
732
  ### 修复
536
733
  - **中文路径被写成乱码**:Windows 下 argv 里的中文常是 GBK 字节,按 UTF-8 解得到一串 U+FFFD
537
- (实测 `D:/主工作区/...` 存成 `D:/??????/...`,worktree 建不出来)。新增 `decodeBody()`:
734
+ (实测中文路径 `D:/某中文目录/...` 存成 `D:/??????/...`,worktree 建不出来)。新增 `decodeBody()`:
538
735
  UTF-8 解码出现替换字符就用 `TextDecoder('gbk')` 重解。
539
736
  - **同一个 watcher 并跑两轮**:首轮 tick 与手动「立刻」撞车会开两个隔离区。`entry.polling` 做并发闸,重复请求 409。
540
737
  - `/state` 补 `isolated` / `workdir` / `lastWorktree`。
package/CHECKLIST.md ADDED
@@ -0,0 +1,26 @@
1
+ # CHECKLIST —— 两张表,两个交付门
2
+
3
+ ## A 表 · 代码交付前
4
+
5
+ - [ ] `node --test` 全绿,且新增行为有测试
6
+ - [ ] 所有返回字符串都过 `redact()`(新增输出路径重点查)
7
+ - [ ] 写操作仍要求 `confirm: true`
8
+ - [ ] 唤醒仍受 `agent.status === 'idle'` 约束
9
+ - [ ] 唤醒失败时不推进水位(`entry.seen` 不动)
10
+ - [ ] `package.json` 的 `files` 覆盖所有要发布的文件(`lib` + 文档 + patch)
11
+ - [ ] 没有把本地路径、个人 token、机器名写进仓库
12
+
13
+ ## B 表 · 发版前
14
+
15
+ - [ ] 从最新 `main` 起功能分支
16
+ - [ ] `CHANGELOG.md` 增加条目,`package.json` bump 版本
17
+ - [ ] 本地 `node --test` 通过
18
+ - [ ] PR 合并到 `main`
19
+ - [ ] 打 annotated tag `vX.Y.Z` 并推送
20
+ - [ ] 创建 GitHub Release,说明取自 CHANGELOG
21
+ - [ ] 安装验证:`dsh plugin --profile <p> add github:MYCF711/dsh-session-superagent`
22
+
23
+ ## 发完之后
24
+
25
+ - [ ] 在测试仓库 `watch_start` 跑一轮,确认唤醒可达
26
+ - [ ] 记录一次真实运行的观察结果到本仓库 issue
package/MAINTENANCE.md ADDED
@@ -0,0 +1,35 @@
1
+ # MAINTENANCE —— 维护台账(给接手的 agent 看)
2
+
3
+ ## 一、身份
4
+
5
+ - 包名:`dsh-session-superagent`
6
+ - 形态:DSH 插件(tools-only + 自带 `cordis.patch.yml`)
7
+ - 目标宿主:DeepSeek Harness ≥ 0.2.1-rc.5
8
+ - 依赖:宿主已安装并登录 `gh` CLI;Node ≥ 22.19
9
+
10
+ ## 二、当前状态(2026-11-01)
11
+
12
+ - [x] 七个工具可用(github / repo_here / watch_start / watch_status / watch_stop / watch_ack / watch_doctor),`node --test` 88/88 通过
13
+ - [x] 自回声抑制(逐条水位)
14
+ - [x] 仅空闲注入(避免草稿回放)
15
+ - [x] 唤醒失败不推进水位
16
+ - [ ] webhook 代替轮询(未做)
17
+ - [ ] 多仓库单会话(未做,当前每会话一仓库)
18
+
19
+ ## 三、下一步
20
+
21
+ 1. 真实仓库连跑 24h,统计 `lastWake` 与误唤醒次数;
22
+ 2. 把 `trigger.json` 从正式包移出(或加开关),它只是测试通道;
23
+ 3. 评估 `steer()` 与 `followup()` 在不同场景下的取舍。
24
+
25
+ ## 四、硬约束
26
+
27
+ 1. **不注入正在运行的会话**——会污染客户端输入框,可能导致重复发送。
28
+ 2. **凭证永不出插件**:所有返回必须过 `redact()`;新增输出路径要补测试。
29
+ 3. **不改 profile 的 `bundles`**:那是操作者动作,包只带 patch。
30
+ 4. **不删除用户文件**;状态文件写失败必须静默降级。
31
+ 5. **没跑过的写成“未验证”**,不写“通过”。
32
+
33
+ ## 五、最短接手路径
34
+
35
+ 读 `README.md`(做什么)→ 本文件(现状与约束)→ `CHECKLIST.md`(交付门)→ `UPDATING.md`(怎么发版)→ `lib/watch.js`(核心逻辑,纯函数,测试齐全)。
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # dsh-session-superagent
2
2
 
3
3
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
4
- [![Version](https://img.shields.io/badge/version-0.8.16-green.svg)](https://github.com/MYCF711/dsh-session-superagent/releases)
4
+ [![Version](https://img.shields.io/badge/version-0.8.27-green.svg)](https://github.com/MYCF711/dsh-session-superagent/releases)
5
5
  [![npm](https://img.shields.io/npm/v/dsh-session-superagent.svg)](https://www.npmjs.com/package/dsh-session-superagent)
6
6
  [![dsh](https://img.shields.io/badge/DeepSeek%20Harness-plugin-orange.svg)](https://github.com/topics/dsh-plugin)
7
7
 
@@ -21,7 +21,7 @@
21
21
  | 盯仓库 | `watch_start` / `watch_status` / `watch_stop` | 30–3600 秒轮询;有变化时用 `Agent.followup()` 唤醒**本会话** |
22
22
  | 传递规则 | 仓库里的 `AGENTS.md` | 唤醒消息只说“有新动态”,怎么处理由项目规则决定 |
23
23
  | 收窄范围 | `filters` | 只处理带指定标签、或已指派给人的条目;默认跳过机器人、草稿 PR、已关闭条目 |
24
- | 限流 | `batchLimit` / `maxWakesPerHour` | 单次最多铺开 N 条,每小时最多唤醒 N 次,超了攒着 |
24
+ | 限流 | `batchLimit` / `maxWakesPerHour` | 单次最多铺开 N 条(默认 5,1-20),每小时最多唤醒 N 次(默认 6,1-60),超了攒着;**面板「触发」页可改** |
25
25
  | 幂等 | `watch_ack` 工具 | 处理完给条目打「主理鱼:已处理」标签(标签不存在会自动建),轮询直接跳过(换机器也不重报);支持 `numbers: "12,13"` 批量 |
26
26
  | 时段 | `activeHours` | 只在 `09:00-23:00` 这类时段唤醒,夜里攒着;支持跨夜 `22:00-06:00` |
27
27
  | 隔离 | `isolated` | 每次唤醒前开一次性 `git worktree`,改副本、推 `zhuli/*` 分支,不动主工作区;超过 3 天的旧副本自动回收 |
@@ -51,15 +51,24 @@ GitHub 有新动态 → watcher 轮询 → Agent.followup(消息) → 会话跑
51
51
 
52
52
  ## 安装
53
53
 
54
+ **装进 profile**(DSH 插件不是 `npm i` 就生效,要装到 profile 里):
55
+
54
56
  ```bash
55
- # 方式一:npm(已发布)
56
- npm i dsh-session-superagent
57
+ # 默认 profile
58
+ dsh plugin install dsh-session-superagent
59
+
60
+ # 指定 profile / 锁定版本
61
+ dsh plugin --profile desktop add dsh-session-superagent@latest
57
62
 
58
- # 方式二:从 GitHub 源码装(会跟踪 main)
59
- dsh plugin add https://github.com/MYCF711/dsh-session-superagent
63
+ # 本地开发:直接装这个目录(会以 file: 硬链接方式挂进 profile)
64
+ dsh plugin install /path/to/dsh-session-superagent
60
65
  ```
61
66
 
62
- 装进 profile 时是 `file:` 硬链接,改代码即改线上;**但新增文件不会被自动带过去**,
67
+ > `npm i dsh-session-superagent` 只会把它下载到本地 node_modules,**不会**让 DSH 加载它 ——
68
+ > 包里的 `dsh.bundle.patch` 要由 profile 的安装流程来读。
69
+ > 也可以在应用内的插件市场里搜索安装。
70
+
71
+ 装进 profile 后是 `file:` 硬链接:改源码即改线上;**但新增文件不会被自动带过去**,
63
72
  加完文件要跑 `pnpm run sync:profile`(否则下次重启插件会整体不挂载,这坑踩过一次)。
64
73
 
65
74
  ## 一次真实运行长什么样
@@ -88,11 +97,24 @@ dsh plugin add https://github.com/MYCF711/dsh-session-superagent
88
97
  ## 开发
89
98
 
90
99
  ```sh
91
- node --test # 77 项单测
100
+ node --test # 88 项单测
92
101
  ```
93
102
 
94
103
  版本与发布流程见 [UPDATING.md](UPDATING.md),交付门见 [CHECKLIST.md](CHECKLIST.md),接手台账见 [MAINTENANCE.md](MAINTENANCE.md)。
95
104
 
105
+ ## 故障排查(都是真实踩过的)
106
+
107
+ | 症状 | 原因 | 处理 |
108
+ |---|---|---|
109
+ | 面板空白、`watch_*` 工具消失、`/session-superagent/*` 全 404 | 新增的 `lib/*.js` 没同步进 profile(`file:` 安装只对**已有**文件建硬链接),宿主 import 失败 → 整个插件不挂载 | `pnpm run sync:profile`,然后重载插件 |
110
+ | 改了代码但线上行为没变 | 硬链接断了 —— **原子写、`git checkout/restore`、某些编辑器的保存**都会换 inode,profile 里还是旧文件 | `pnpm run sync:profile:check` 看漂移,`sync:profile:repair` 修 |
111
+ | 轮询报 `cannot get required service "agents" in inactive context` | 0.8.21 之前的 bug:定时器里直接读 `ctx.agents` | 升级到 ≥0.8.21 |
112
+ | 绑定的会话已删除,watcher 还在打 GitHub | 0.8.14 之前的 bug:该类失败没进退避 | 升级到 ≥0.8.14(0.8.22 起未预期异常也会退避) |
113
+ | `npm i dsh-session-superagent@<新版本>` 报 `ETARGET` | 刚发布,npm 客户端元数据缓存还没刷新 | 等 1-2 分钟,或 `npm i <tarball URL>` / `--prefer-online` |
114
+ | 有动静但会话没被叫醒 | 会话忙 / 时段外 / 每小时上限 / 被子过滤器刷掉 | `watch_status` 会显示 `deferred=` / `paused`;面板「总览」的「最近被过滤」给原因 |
115
+
116
+ `pnpm run preflight` 一条命令可自查:单测、语法、profile 一致性、版本一致、路由面、包内容、冒烟。
117
+
96
118
  ## 许可
97
119
 
98
120
  MIT,见 [LICENSE](LICENSE)。
package/UPDATING.md ADDED
@@ -0,0 +1,43 @@
1
+ # 更新与发布
2
+
3
+ ## 铁律
4
+
5
+ 默认分支只接受通过门禁的 PR;`main` 永远可安装。
6
+
7
+ ## 发一个版本
8
+
9
+ ```bash
10
+ git checkout main && git pull
11
+ git checkout -b feat/<主题>
12
+
13
+ # 1) 改代码 + 补测试
14
+ node --test
15
+
16
+ # 2) bump 版本 + 写 CHANGELOG
17
+ # 语义化:修 bug → patch;加能力 → minor;改默认行为/改 patch 行 id → major
18
+
19
+ # 3) 提交并推分支
20
+ git add -A && git commit -m "feat: ..."
21
+ git push -u origin feat/<主题>
22
+
23
+ # 4) PR 全绿后合并
24
+ gh pr create --fill && gh pr merge --squash --delete-branch
25
+
26
+ # 5) 回到 main 打 tag 并发 Release
27
+ git checkout main && git pull
28
+ git tag -a v0.1.1 -m "v0.1.1"
29
+ git push origin v0.1.1
30
+ gh release create v0.1.1 --title "v0.1.1" --notes-from-tag
31
+ ```
32
+
33
+ ## 用户怎么升级
34
+
35
+ ```bash
36
+ dsh plugin --profile <p> update dsh-session-superagent
37
+ ```
38
+
39
+ pnpm 依据 `github:` 规格重新解析;或重新 `add` 一次覆盖。
40
+
41
+ ## 兼容性
42
+
43
+ 插件依赖宿主内部 API(`ctx.tools`、`ctx.agents`)。升级到新的 DSH 内部 API 视为 **major**,因为旧宿主可能不再提供同样的服务。
package/client.js CHANGED
@@ -14,7 +14,7 @@ window.__ModuleLoader__.load({
14
14
  const { useCallback, useEffect, useRef, useState } = React
15
15
 
16
16
  // 客户端版本探针:每次模块加载写一次,用来确认浏览器里跑的是哪一版
17
- const CLIENT_VERSION = '0.8.16'
17
+ const CLIENT_VERSION = '0.8.27'
18
18
  const BASE = '/session-superagent'
19
19
  const ENDPOINT = BASE + '/state'
20
20
  const POLL_MS = 5000
@@ -699,6 +699,8 @@ window.__ModuleLoader__.load({
699
699
  isolated: !!draft.isolated,
700
700
  workdir: draft.workdir || '',
701
701
  observe: !!draft.observe,
702
+ batchLimit: Number(draft.batchLimit) || undefined,
703
+ maxWakesPerHour: Number(draft.maxWakesPerHour) || undefined,
702
704
  }),
703
705
  })
704
706
  setEditing(null)
@@ -732,10 +734,12 @@ window.__ModuleLoader__.load({
732
734
  isolated: !!form.isolated,
733
735
  workdir: form.workdir || '',
734
736
  observe: !!form.observe,
737
+ batchLimit: Number(form.batchLimit) || undefined,
738
+ maxWakesPerHour: Number(form.maxWakesPerHour) || undefined,
735
739
  }),
736
740
  })
737
741
  setForm({ repo: '', intervalSec: form.intervalSec, goal: '', labels: '', onlyAssigned: false, activeHours: form.activeHours, isolated: form.isolated, workdir: form.workdir, observe: form.observe })
738
- }, '已添加,30 秒内开始轮询')
742
+ }, '已添加,首轮立即开始轮询')
739
743
 
740
744
  const saveCredentials = () => run(async () => {
741
745
  const payload = {}
@@ -798,6 +802,8 @@ window.__ModuleLoader__.load({
798
802
  isolated: !!watcher.isolated,
799
803
  workdir: watcher.workdir || '',
800
804
  observe: !!watcher.observe,
805
+ batchLimit: String(watcher.batchLimit ?? 5),
806
+ maxWakesPerHour: String(watcher.maxWakesPerHour ?? 6),
801
807
  })
802
808
  },
803
809
  }, editing === key ? '收起' : '编辑'),
@@ -818,6 +824,8 @@ window.__ModuleLoader__.load({
818
824
  field('活跃时段', h(Input, { value: draft.activeHours, placeholder: '09:00-23:00,留空 = 全天', onChange: (e) => setDraft({ ...draft, activeHours: e.target.value }) })),
819
825
  field('目标/规则', h(Input, { value: draft.goal || '', placeholder: '可选,会附在唤醒消息里', onChange: (e) => setDraft({ ...draft, goal: e.target.value }) })),
820
826
  h('label', { className: 'dt-switch-row' }, h(Toggle, { checked: !!draft.onlyAssigned, onChange: (v) => setDraft({ ...draft, onlyAssigned: v }), label: '只看已指派' }), '只处理已指派给人的条目'),
827
+ field('单次最多唤醒条数', h(Input, { value: draft.batchLimit || '', placeholder: '5(1-20)', onChange: (e) => setDraft({ ...draft, batchLimit: e.target.value }) })),
828
+ field('每小时最多唤醒次数', h(Input, { value: draft.maxWakesPerHour || '', placeholder: '6(1-60)', onChange: (e) => setDraft({ ...draft, maxWakesPerHour: e.target.value }) })),
821
829
  h('label', { className: 'dt-switch-row' }, h(Toggle, { checked: !!draft.observe, onChange: (v) => setDraft({ ...draft, observe: v }), label: '仅观察' }), '仅观察(只记录不唤醒)'),
822
830
  h('label', { className: 'dt-switch-row' }, h(Toggle, { checked: !!draft.isolated, onChange: (v) => setDraft({ ...draft, isolated: v }), label: '隔离执行' }), '隔离执行(每次唤醒开一次性 worktree)'),
823
831
  draft.isolated ? field('隔离工作目录', h(Input, { value: draft.workdir || '', placeholder: '绝对路径,留空 = 当前工作目录', onChange: (e) => setDraft({ ...draft, workdir: e.target.value }) })) : null,
@@ -888,6 +896,8 @@ window.__ModuleLoader__.load({
888
896
  h('div', { className: 'dt-form' },
889
897
  field('仓库', h(Input, { value: form.repo, placeholder: 'owner/name', onChange: (e) => setForm({ ...form, repo: e.target.value }) })),
890
898
  field('间隔(秒)', h(Input, { value: form.intervalSec, onChange: (e) => setForm({ ...form, intervalSec: e.target.value }) })),
899
+ field('单次最多唤醒条数', h(Input, { value: form.batchLimit || '', placeholder: '5(1-20)', onChange: (e) => setForm({ ...form, batchLimit: e.target.value }) })),
900
+ field('每小时最多唤醒次数', h(Input, { value: form.maxWakesPerHour || '', placeholder: '6(1-60)', onChange: (e) => setForm({ ...form, maxWakesPerHour: e.target.value }) })),
891
901
  field('只处理这些标签', h(Input, { value: form.labels, placeholder: '逗号分隔,留空 = 全部', onChange: (e) => setForm({ ...form, labels: e.target.value }) })),
892
902
  field('活跃时段', h(Input, { value: form.activeHours, placeholder: '09:00-23:00,留空 = 全天', onChange: (e) => setForm({ ...form, activeHours: e.target.value }) })),
893
903
  field('目标/规则', h(Input, { value: form.goal || '', placeholder: '可选,会附在唤醒消息里', onChange: (e) => setForm({ ...form, goal: e.target.value }) })),