dsh-session-superagent 0.8.29 → 0.8.30
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 +29 -0
- package/README.md +1 -1
- package/client.js +1 -1
- package/lib/index.js +19 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
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.30] - 2026-11-02 —— 0.8.28 没修好:宿主重载是「重新 import 一份模块」
|
|
6
|
+
|
|
7
|
+
### 真机判定实验(20:00 重载后,routes 6 / failed [])
|
|
8
|
+
面板(HTTP)建的 watcher **仍然报**:
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
lastError: cannot get required service "agents" in inactive context
|
|
12
|
+
consecutiveFailures: 0 / nextAt: None
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
说明 0.8.28 的模块级 `activeRoutes` 没有生效 —— 因为**宿主重载是重新 import 一份模块**,
|
|
16
|
+
模块级变量全新(数组是空的),新实例根本不知道旧实例注册过什么。
|
|
17
|
+
|
|
18
|
+
### 修复
|
|
19
|
+
把三个必须跨实例共享的结构挂到 `globalThis` 上(用 `Symbol.for` 做键):
|
|
20
|
+
|
|
21
|
+
| 结构 | 为什么必须共享 |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `activeRoutes`(路由 disposer) | 新实例要能摘掉旧实例的路由,否则旧 ctx 继续吃 HTTP |
|
|
24
|
+
| `timers`(定时器句柄) | 旧实例的 tick 还持有已 dispose 的 ctx,必须清掉 |
|
|
25
|
+
| `inFlight`(并发闸) | 旧实例没跑完的那轮,新实例不能同时再跑一遍 |
|
|
26
|
+
|
|
27
|
+
### 守门
|
|
28
|
+
新增 `test/cross-instance.test.mjs`:用 `import('../lib/index.js?instance=1')` 与 `?instance=2`
|
|
29
|
+
模拟两次 import,断言第二个实例会摘掉第一个实例注册的 6 条路由。
|
|
30
|
+
|
|
31
|
+
### 测试
|
|
32
|
+
- 合计 **92 例**。
|
|
33
|
+
|
|
5
34
|
## [0.8.29] - 2026-11-02 —— `watch_doctor` 现在会告诉你「面板接口死没死」
|
|
6
35
|
|
|
7
36
|
这一轮的真机事故(重复路由 → 六条接口全灭)有个共同特征:**工具还是好的,只有面板挂了**。
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<img src="assets/icon-180.png" width="88" height="88" alt="">
|
|
4
4
|
|
|
5
5
|
[](LICENSE)
|
|
6
|
-
[](https://github.com/MYCF711/dsh-session-superagent/releases)
|
|
7
7
|
[](https://www.npmjs.com/package/dsh-session-superagent)
|
|
8
8
|
[](https://github.com/topics/dsh-plugin)
|
|
9
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.30'
|
|
18
18
|
const BASE = '/session-superagent'
|
|
19
19
|
const ENDPOINT = BASE + '/state'
|
|
20
20
|
const POLL_MS = 5000
|
package/lib/index.js
CHANGED
|
@@ -43,12 +43,24 @@ const DEFAULT_COOLDOWN_MS = 180_000
|
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* 当前挂在这个进程上的路由 disposer。
|
|
46
|
+
*
|
|
46
47
|
* 宿主重载时若没走 dispose,旧实例会继续吃 HTTP 请求,而它的 ctx 可能已经 dispose ——
|
|
47
48
|
* 真机表现:面板建的 watcher 第一轮就报
|
|
48
49
|
* `cannot get required service "agents" in inactive context`(2026-11-02 复验)。
|
|
49
|
-
*
|
|
50
|
+
*
|
|
51
|
+
* 关键:宿主重载常常是**重新 import 一份模块**(模块级变量全新),所以光用模块级数组
|
|
52
|
+
* 接不过去 —— 必须挂在 `globalThis` 上(用 Symbol.for 做键,多个模块实例共享同一个)。
|
|
50
53
|
*/
|
|
51
|
-
|
|
54
|
+
// 这三个结构必须跨模块实例共享:宿主重载常常是重新 import 一份模块,
|
|
55
|
+
// 模块级变量全新,但进程只有一个。新实例要能清掉旧实例的路由与定时器,
|
|
56
|
+
// 也要能看见旧实例「正在跑的那轮」(否则并发闸形同虚设)。
|
|
57
|
+
const TIMERS_KEY = Symbol.for('dsh-session-superagent.timers')
|
|
58
|
+
const INFLIGHT_KEY = Symbol.for('dsh-session-superagent.inFlight')
|
|
59
|
+
const sharedTimers = (globalThis[TIMERS_KEY] ||= new Map())
|
|
60
|
+
const sharedInFlight = (globalThis[INFLIGHT_KEY] ||= new Set())
|
|
61
|
+
|
|
62
|
+
const ROUTES_KEY = Symbol.for('dsh-session-superagent.activeRoutes')
|
|
63
|
+
const activeRoutes = (globalThis[ROUTES_KEY] ||= [])
|
|
52
64
|
|
|
53
65
|
function readState() {
|
|
54
66
|
try { return JSON.parse(readFileSync(STATE_FILE, 'utf8')) } catch { return { watchers: {} } }
|
|
@@ -546,10 +558,11 @@ async function pollOnce(ctx, entry, baseMs) {
|
|
|
546
558
|
}
|
|
547
559
|
|
|
548
560
|
export function apply(ctx) {
|
|
549
|
-
const timers =
|
|
561
|
+
const timers = sharedTimers
|
|
550
562
|
// 并发闸的唯一事实源:tick 和 HTTP「立刻」拿到的是两个不同的 entry 对象,
|
|
551
563
|
// 靠 entry.polling 标记永远对不上(审核专员 probe-c 实证:同一条被唤醒 3 次)。
|
|
552
|
-
|
|
564
|
+
// 跨模块实例共享:旧实例还没跑完的那轮,新实例不能同时再跑一遍。
|
|
565
|
+
const inFlight = sharedInFlight
|
|
553
566
|
|
|
554
567
|
const startWatcher = (entry) => {
|
|
555
568
|
if (timers.has(entry.id)) return
|
|
@@ -600,6 +613,8 @@ export function apply(ctx) {
|
|
|
600
613
|
if (!httpServer || typeof httpServer.register !== 'function') return
|
|
601
614
|
for (const dispose of activeRoutes) { try { dispose() } catch {} }
|
|
602
615
|
activeRoutes.length = 0
|
|
616
|
+
for (const handle of timers.values()) { try { clearInterval(handle) } catch {} }
|
|
617
|
+
timers.clear()
|
|
603
618
|
const routes = []
|
|
604
619
|
// 宿主热重载时旧实例的路由可能还没 dispose;一条重复会让整个 try 崩掉,
|
|
605
620
|
// 面板和所有接口一起 404(2026-11-02 真机踩到 webserver: duplicate exact route)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-session-superagent",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.30",
|
|
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",
|