dsh-session-superagent 0.8.27 → 0.8.29
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 +41 -1
- package/README.md +3 -1
- package/client.js +1 -1
- package/lib/index.js +31 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,45 @@
|
|
|
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.29] - 2026-11-02 —— `watch_doctor` 现在会告诉你「面板接口死没死」
|
|
6
|
+
|
|
7
|
+
这一轮的真机事故(重复路由 → 六条接口全灭)有个共同特征:**工具还是好的,只有面板挂了**。
|
|
8
|
+
`watch_status` 看不出异常,用户只会觉得"面板空白"。而 `state.server` 里其实一直记着挂载结果。
|
|
9
|
+
|
|
10
|
+
### 新增
|
|
11
|
+
`watch_doctor` 第一行现在是挂载状态:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
OK 面板接口挂载:6 条,无失败
|
|
15
|
+
FAIL 面板接口挂载:webserver: duplicate exact route "/session-superagent/state"(工具仍可用,但面板会空白;重载一次即可)
|
|
16
|
+
WARN 面板接口部分失败(5/6 成功):…
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 测试
|
|
20
|
+
- 新增用例:doctor 输出里必须有挂载状态行。合计 **91 例**。
|
|
21
|
+
(写这个用例时踩到一个小坑:`cleanup` 用例会删掉临时状态目录,doctor 用例必须排在它前面 —— 否则看到的是"没有挂载记录"。)
|
|
22
|
+
|
|
23
|
+
## [0.8.28] - 2026-11-02 —— 旧实例还在吃 HTTP 请求(面板建的 watcher 一开就废)
|
|
24
|
+
|
|
25
|
+
### 真机复验发现
|
|
26
|
+
宿主在 19:49 挂载成功(`routes: 6, failed: []`),但**通过 HTTP(面板)建的 watcher 仍然报**:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
lastError: cannot get required service "agents" in inactive context
|
|
30
|
+
consecutiveFailures: 0 / nextAt: None
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
对照实验:用**工具**(`watch_start`)建的 watcher 同一时刻轮询正常、无错误。
|
|
34
|
+
差别在于请求被谁处理 —— 路由最初由 19:32 那个实例注册,重载后若没走 dispose,
|
|
35
|
+
它继续吃 HTTP 请求,而它的 `ctx` 早已 dispose;于是那些 watcher 的定时器跑在死上下文里。
|
|
36
|
+
|
|
37
|
+
### 修复
|
|
38
|
+
模块级 `activeRoutes`:每次 `apply()` 注册路由前,**先摘掉上一次注册的**(接管而不是并存)。
|
|
39
|
+
配合 0.8.25 的单条容错与 0.8.26 的 teardown,重载后的三条路径都覆盖了。
|
|
40
|
+
|
|
41
|
+
### 测试
|
|
42
|
+
- 新增用例:同一模块 `apply` 两次时,第二次会先 unregister 第一批(6 条)再注册新的 6 条。合计 **90 例**。
|
|
43
|
+
|
|
5
44
|
## [0.8.27] - 2026-11-02 —— 文案核对 + 把 0.8.21–0.8.27 一次发到 npm
|
|
6
45
|
|
|
7
46
|
### 文案
|
|
@@ -9,7 +48,8 @@
|
|
|
9
48
|
改成「已添加,首轮立即开始轮询」。
|
|
10
49
|
|
|
11
50
|
### 发布
|
|
12
|
-
- 一次把 0.8.21–0.8.27 发出去(npm 上原来停在 0.8.20
|
|
51
|
+
- 一次把 0.8.21–0.8.27 发出去(npm 上原来停在 0.8.20)。已确认 `latest = 0.8.27`,
|
|
52
|
+
tarball 直装验证:`version 0.8.27`、`routes 6`、`client.js` 语法通过、新文案在位。这一批里有三个真机事故修复:
|
|
13
53
|
`inactive context`(唤醒会失败)、未预期异常不退避、热重载后重复路由导致面板全灭。
|
|
14
54
|
|
|
15
55
|
### 测试
|
package/README.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# dsh-session-superagent
|
|
2
2
|
|
|
3
|
+
<img src="assets/icon-180.png" width="88" height="88" alt="">
|
|
4
|
+
|
|
3
5
|
[](LICENSE)
|
|
4
|
-
[](https://github.com/MYCF711/dsh-session-superagent/releases)
|
|
5
7
|
[](https://www.npmjs.com/package/dsh-session-superagent)
|
|
6
8
|
[](https://github.com/topics/dsh-plugin)
|
|
7
9
|
|
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.
|
|
17
|
+
const CLIENT_VERSION = '0.8.29'
|
|
18
18
|
const BASE = '/session-superagent'
|
|
19
19
|
const ENDPOINT = BASE + '/state'
|
|
20
20
|
const POLL_MS = 5000
|
package/lib/index.js
CHANGED
|
@@ -41,6 +41,15 @@ const TRIGGER_FILE = join(STATE_DIR, 'trigger.json')
|
|
|
41
41
|
const CLIENT_NOTE_KEYS = new Set(['client-version', 'apply', 'desk-registered', 'desk-fail', 'orbit-registered', 'orbit-fail', 'orbit-mount', 'badge-registered', 'badge-mount', 'badge-state', 'error'])
|
|
42
42
|
const DEFAULT_COOLDOWN_MS = 180_000
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* 当前挂在这个进程上的路由 disposer。
|
|
46
|
+
* 宿主重载时若没走 dispose,旧实例会继续吃 HTTP 请求,而它的 ctx 可能已经 dispose ——
|
|
47
|
+
* 真机表现:面板建的 watcher 第一轮就报
|
|
48
|
+
* `cannot get required service "agents" in inactive context`(2026-11-02 复验)。
|
|
49
|
+
* 所以每次 apply 注册路由前,先把上一次的摘掉。
|
|
50
|
+
*/
|
|
51
|
+
const activeRoutes = []
|
|
52
|
+
|
|
44
53
|
function readState() {
|
|
45
54
|
try { return JSON.parse(readFileSync(STATE_FILE, 'utf8')) } catch { return { watchers: {} } }
|
|
46
55
|
}
|
|
@@ -589,6 +598,8 @@ export function apply(ctx) {
|
|
|
589
598
|
// does not wait for it.
|
|
590
599
|
const registerRoutes = (httpServer) => {
|
|
591
600
|
if (!httpServer || typeof httpServer.register !== 'function') return
|
|
601
|
+
for (const dispose of activeRoutes) { try { dispose() } catch {} }
|
|
602
|
+
activeRoutes.length = 0
|
|
592
603
|
const routes = []
|
|
593
604
|
// 宿主热重载时旧实例的路由可能还没 dispose;一条重复会让整个 try 崩掉,
|
|
594
605
|
// 面板和所有接口一起 404(2026-11-02 真机踩到 webserver: duplicate exact route)。
|
|
@@ -596,7 +607,9 @@ export function apply(ctx) {
|
|
|
596
607
|
const failedRoutes = []
|
|
597
608
|
const addRoute = (options) => {
|
|
598
609
|
try {
|
|
599
|
-
|
|
610
|
+
const dispose = httpServer['register'](options)
|
|
611
|
+
routes.push(dispose)
|
|
612
|
+
activeRoutes.push(dispose)
|
|
600
613
|
} catch (error) {
|
|
601
614
|
failedRoutes.push(String((error && error.message) || error).slice(0, 120))
|
|
602
615
|
}
|
|
@@ -857,6 +870,7 @@ export function apply(ctx) {
|
|
|
857
870
|
const teardown = () => {
|
|
858
871
|
for (const dispose of routes) { try { dispose() } catch {} }
|
|
859
872
|
routes.length = 0
|
|
873
|
+
activeRoutes.length = 0
|
|
860
874
|
// 插件卸载/热重载后旧定时器不能继续跑:它们还持有已 dispose 的 ctx
|
|
861
875
|
for (const handle of timers.values()) { try { clearInterval(handle) } catch {} }
|
|
862
876
|
timers.clear()
|
|
@@ -972,6 +986,22 @@ export function apply(ctx) {
|
|
|
972
986
|
const all = Object.values(state.watchers || {})
|
|
973
987
|
const wanted = args.repo ? all.filter((w) => w && w.repo === String(args.repo)) : all
|
|
974
988
|
|
|
989
|
+
// 0. 挂载状态:面板/HTTP 靠路由,路由注册失败时工具仍然可用 —— 这种「半死」状态
|
|
990
|
+
// 光看工具是发现不了的(2026-11-02 真机踩过:duplicate exact route 让六条接口全灭)
|
|
991
|
+
if (state.server) {
|
|
992
|
+
const mounted = Number(state.server.routes) || 0
|
|
993
|
+
const failed = Array.isArray(state.server.failed) ? state.server.failed : []
|
|
994
|
+
if (state.server.error) {
|
|
995
|
+
lines.push(`FAIL 面板接口挂载:${state.server.error}(工具仍可用,但面板会空白;重载一次即可)`)
|
|
996
|
+
} else if (failed.length > 0) {
|
|
997
|
+
lines.push(`WARN 面板接口部分失败(${mounted}/6 成功):${failed.join(';')}`)
|
|
998
|
+
} else {
|
|
999
|
+
lines.push(`OK 面板接口挂载:${mounted} 条,无失败`)
|
|
1000
|
+
}
|
|
1001
|
+
} else {
|
|
1002
|
+
lines.push('WARN 还没有挂载记录(刚装上或从未加载过路由)')
|
|
1003
|
+
}
|
|
1004
|
+
|
|
975
1005
|
// 1. gh 是否登录
|
|
976
1006
|
const auth = await run('gh', ['auth', 'status'], { env: ghEnv() })
|
|
977
1007
|
const authed = auth.code === 0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-session-superagent",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.29",
|
|
4
4
|
"description": "Session-level superagent for DeepSeek Harness: watch a GitHub repository and wake a session when something needs doing. Credentials never reach the model.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|