dsh-crossterm 1.0.1 → 1.0.5

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/MAINTAINERS.md +152 -50
  3. package/README.md +43 -20
  4. package/components/device/manifest.json +1 -1
  5. package/components/restart/core.js +64 -33
  6. package/components/restart/manifest.json +1 -1
  7. package/components/restart/scripts/busy-check.mjs +7 -3
  8. package/components/restart/scripts/countdown.html +88 -50
  9. package/components/restart/scripts/dsh-restart-helper.mjs +49 -7
  10. package/components/restart/scripts/rearm-watch.mjs +9 -2
  11. package/components/restart/scripts/trigger-restart.mjs +75 -19
  12. package/components/session/manifest.json +1 -1
  13. package/components/terminal/manifest.json +1 -1
  14. package/components/terminal/selftest.mjs +341 -0
  15. package/{lib → components/ui/lib}/web.js +18 -10
  16. package/cordis.patch.yml +2 -2
  17. package/functions/cloud-ops/README.md +43 -0
  18. package/functions/cloud-ops/cloud/fanout.js +658 -0
  19. package/{lib → functions/cloud-ops}/cloud/home.js +28 -4
  20. package/{lib → functions/cloud-ops}/cloud/layout.js +6 -0
  21. package/{lib → functions/cloud-ops}/cloud/pan.js +110 -26
  22. package/functions/cloud-ops/cloud/relay.js +778 -0
  23. package/functions/cloud-ops/cloud/session-backup.js +479 -0
  24. package/{lib → functions/cloud-ops}/cloud/sync.js +8 -1
  25. package/lib/bus.js +63 -25
  26. package/lib/index.js +699 -106
  27. package/package.json +2 -1
  28. package/lib/cloud/relay.js +0 -361
  29. package/lib/cloud/session-backup.js +0 -261
  30. /package/{lib → functions/cloud-ops}/cloud/devices.js +0 -0
  31. /package/{lib → functions/cloud-ops}/cloud/match.js +0 -0
  32. /package/{lib → functions/cloud-ops}/cloud/presence.js +0 -0
  33. /package/{lib → functions/cloud-ops}/cloud/setup.js +0 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,107 @@
1
+ ## 1.0.5 — 2026-10-21
2
+ ### 修复:插件卡片「简介 / 更新日志 / 运行日志」消失
3
+
4
+ - 现象(机主 2026-10-21 报):插件面板卡片上的插件简介没了,「更新日志」「运行日志」两个按钮也没了。
5
+ - 根因:1.0.3 目录重组把 `lib/web.js` 搬到 `components/ui/lib/web.js`,但 `ROOT` 仍按「上一级」算 ⇒ 只到 `components/ui`;`aboutOf()` 于是读不到 `locale/zh.json` 与 `components/*/manifest.json`,接口 `/crossterm/api/about` 返回 `ok:true` 但 `description`/`changelog` 为空、组件版本全空。卡片渲染条件是 `about && about.changelog`,changelog 空 ⇒ 按钮整块不渲染。
6
+ - 修复:`ROOT` 改成往上三级(真正回到包根);`/crossterm/api/logs` 本身正常(实测 `ok:true`、120 行),不需要改。
7
+ - 附带修复:`components/terminal/manifest.json` 曾带 UTF-8 BOM(PowerShell 5.1 写回所致),`JSON.parse` 直接抛 ⇒ 组件版本读不到;已去 BOM,并在 `aboutOf()` 里增加 `readJsonFile()` 容忍 BOM。
8
+ - 生效条件:`components/**` 由宿主启动时加载,**必须重启 DSH 才生效**(机主定:随 1.0.5 一起重启)。
9
+ - 回归网:新增 `test/about.test.mjs`,直接问 `aboutOf()` 要「简介 / 更新日志 / 五组件版本」,ROOT 再算错或 manifest 再带 BOM 就会红(此前没有任何测试覆盖这条,1.0.3 才漏出去)。
10
+
11
+ ### 变更:磁盘邮件头按机主口径调整(跨工作区 0.2.4)
12
+
13
+ - 机主 2026-10-21 直接下令:去掉「发件终端」行;「发件会话」改名「发件人」;其下新增「收件人」行。
14
+ - 落点:`lib/bus.js` `writeMail()` 头部顺序改为「主题 → 特征码(仅链)→ 发件人 → 收件人 → 发件工作区 → 邮件 id → 发出时间 → 附件」;`lib/index.js` `send()→enqueueTo()` 的信封 `to{}` 新增 `sessionLabel`(优先「名字(短号)」,撞车用 idTag,解析不出退回 `labelOf`)。
15
+ - 「发件工作区」保留(机主只要求去掉终端)。
16
+ - 已知限制:接收侧若发件方是旧版本(信封无 `sessionLabel`),发件人一行退化为裸 `sessionId`;跨机老版本兼容不做额外处理。
17
+ - 越界备案:`lib/bus.js` 属跨会话/主负责人范围,本次为机主直接令就地改,已记 `ownership.log` 追认,不回滚。
18
+
19
+ ### 变更:M2 接线落地(mapFanoutResults 进仓库)
20
+
21
+ - `functions/cloud-ops/cloud/fanout.js` 现含 `mapFanoutResults`(M2 结果映射),落盘基线 31,692 B / `302e5c75`;相对云端候选另补两项:`recipients`(取 `plan.counts.input`)、合并组目标设备版本 <1.0.2 时 `warnings` 记 `v1-degrade`。
22
+ - 形状定稿 v3:`docs/sendmany-result-shape.md`(4,341 B)——行按目标数展开、`summary` 只数成行目标、`expectedReceipts` 单独记信封数、`fallback` 为 `{device,sessionId?,sessionLabel?,reason,dropped[]}`;`plan.failed/skipped` 不并入 `summary`,仍需消费方并列展示。
23
+ - 回归网:`test/map-fanout.test.mjs` 20 组(含 recipients / v1-degrade 新增用例)。
24
+ ### 变更:跨设备信封 silent 附加字段(跨设备 0.4.4 内)
25
+
26
+ - `functions/cloud-ops/cloud/relay.js` `sendEnvelope` 支持 `silent`:**仅 true 时**写 `envelope.silent = true`;未签名附加字段,v1/v2 canonical 串与验签结果逐字节不变;`parseEnvelope` 透传给 `deliver`,由接收端决定是否自动注入。
27
+ - 多目标 + `silent` **本轮不放开**:`sendManyCloud` fail-closed 拒绝(detail:`silent 只支持本机/单目标`),语义等 1.0.6 再议;`docs/sendmany-result-shape.md` 已记「本轮实现约束」。
28
+
29
+ ### 变更:M2 一发多投接线(`sendManyCloud` / `term_send`)
30
+
31
+ - `lib/index.js` `send()` 接受 `targets` / `toStrict`;解析出多于 1 个目标即走 `sendManyCloud()`:`planFanout`(本终端目标直接拒、带 version)→ 逐组 `groupSendArgs` + `home.send` → `mapFanoutResults` → `{...out, via:'cloud'}`。
32
+ - `term_send` 增加 `targets`(数组)与 `toStrict`,透传 `send()`;**`to` 与 `targets` 同给时拒绝**(`to 与 targets 只能给一个`)。
33
+ - 单目标老路径零改动(含 `to="self"` + `sessionId`)。
34
+
35
+ ### 修复:默认会话失效时不再乱投(跨会话 0.5.7)
36
+
37
+ - `pickDeliverSession` 第 2 级 `defaultSessionId` 指向**未打开**的会话时,明确返回失败原因(`默认会话『X』当前没打开…`)并按留件重试,不再静默落到第 3/4 级猜测。
38
+
39
+ ### 修复:倒计时窗口在检查点卡住、进度跳 0(自动重启 0.3.46)
40
+
41
+ - `components/restart/scripts/countdown.html` 的检查点改为异步探测 `busy-check`,删除 2 秒硬截断,零点等结论再关窗;新增 `test/countdown.test.mjs`(假时钟两幕)。
42
+
43
+ ## 1.0.3 — 2026-10-21
44
+ ### 重构:目录按组件/功能分文件夹(机主指令)
45
+
46
+ - 背景:机主 2026-10-20 指令「要求组件、功能,分文件夹存放,防止混淆、误改」。当时 `components/*/` 已是五组件各自目录,但 `lib/` 里混着三类东西:插件入口与工具注册(`lib/index.js`)、总线(`lib/bus.js`)、**云端运维的 10 个功能文件(`functions/cloud-ops/cloud/**`)**,以及属于入口组件的 `lib/web.js`。
47
+ - 搬迁:`lib/cloud/**` → `functions/cloud-ops/cloud/**`;`lib/web.js` → `components/ui/lib/web.js`。`lib/` 只留宿主入口与共享核心(`index.js`、`bus.js`、`logs.js`、`names.js`)。
48
+ - 同步:`lib/index.js` 的 5 处 import、`components/ui/lib/web.js` 的 3 处 import、`test/` 三个测试文件的引用、`package.json` 的 `files` 白名单(新增 `functions`)。
49
+ - 文档:`MAINTAINERS.md` 新增「目录归属」表与四条硬规矩(新功能一律 `functions/<名字>/`;越界改别人文件夹算误改;搬迁只由主负责人做且必须同步 files/import/test/docs;搬迁后需重启宿主);新增 `functions/cloud-ops/README.md`。
50
+ - 验证:全仓 37 个 js/mjs `node --check` 全过;10 个测试文件全绿;历史 CHANGELOG 条目里的旧路径保留(那是历史记录,不改写)。
51
+ - 生效范围:`lib/**` 与 `components/**` 是宿主启动时加载的,**这次搬迁要重启一次 DSH 才按新目录加载**;测试是直接跑文件,不受影响。
52
+ ### 清理:发版物去本机痕迹(验收建议)
53
+
54
+ - `countdown.html` 的 `resolveBase()` 兜底原写死 `F:\dsh\001X\dsh-restart\scripts`(维护者本机旧路径,跨设备必错)。改为**拿不到就返回空串**,初始化处立即 `dbg("resolveBase 失败…")` 后 `return`,不猜路径。
55
+ - `cordis.patch.yml` 安装注释的本机链接路径 → `<插件目录>` 占位。
56
+ - `core.js` 头注释里的 `F:\dsh\DeepSeek Harness.exe` → 改为「由 helper 的 `APP_EXE` 解析」。
57
+ - `README.md` 的示例邮件/日志里的真实 `nodeId` 与 `session-<短号>` → 占位符(`<nodeId>`、`session-<短号>…`);`MAINTAINERS.md` 保留真实 id(审计用,且 `lib/logs.js` 依赖它做归属)。
58
+ - 组件版本:自动重启 0.3.43 → **0.3.44**(只有 `countdown.html` 与注释/文档变动)。
59
+ ### 清理:`lib/index.js` 去掉未使用的 `readMaintainersDoc` 导入
60
+
61
+ - 该符号只在 `lib/logs.js` 内部与 `lib/web.js:587` 用到;`lib/index.js:44` 的导入自 0.9.x 起就没被调用过(`recordOwnership` 仍在用)。
62
+ - 纯清理,无行为变化;10 个测试文件与 `node --check` 全绿。
63
+
64
+ ### 加固:跨设备版本探测加 60 秒缓存
65
+
66
+ - `deviceVersion()` 加 60 秒缓存,缓存过期后重读 `_device.json`;同一设备的两次发信只读一次盘。
67
+ - 版本读失败一律按 v1 处理,且**只 warn 一次**(避免每封信刷一行)。
68
+ - 目的:一发多投时不再对每个目标重复读盘;同时保证缓存过期后能及时看到对端升级。
69
+
70
+ ### 修复:presence 回收阈值抬到 9 分钟(否则「离线」这一档永远显示不出来)
71
+
72
+ - 背景:清理由 `tick()` 每 60 秒跑一次 `cleanupStale()`,回收阈值与展示阈值同为 3 分钟 ⇒ 节点从「在线」直接跳到「从列表消失」,`term_list` 的「(离线)」与 `send` 的 `{offline:true, presenceAgeMs}` 在正常运行下不可达,只能靠假 stale 文件测出来(跨工作区 001 `2d8f3f19`)。
73
+ - 改法:`lib/bus.js` 新增 `PRESENCE_CLEANUP_MS = PRESENCE_TTL_MS * 3`(9 分钟),`cleanupStale()` 的 `limit` 改用它。窗口变成:0–3 分钟在线、3–9 分钟明确显示离线、≥9 分钟才回收;回收本来就与链成员资格解耦(见上一条),所以不影响关系。
74
+ - 测试:`test/bus.test.mjs` 新增 8b —— 5 分钟未心跳的 presence 必须保留(可显示离线),10 分钟的才被回收。
75
+
76
+ > 说明:1.0.2 从未发布,其内容(重启链路 B1/S1–S3、relay 信封 v2)全部并入本版 1.0.3 一起发。
77
+
78
+ ### 修复:链成员资格与存活解耦(合盖 / 关机不再被踢出链)
79
+
80
+ - 背景:`pruneChain()` 与 `dropChainIfOrphan()` 原先按 presence 心跳年龄(3 分钟)判成员死活,没心跳就从台账剔掉、全员离线就把整份台账删掉。链是**关系**、在线是**瞬时状态**,绑在一起的后果是:笔记本合盖一晚、远端设备关机,醒来发现自己已经从链上被摘掉;如果是别人用 `term_unlink all=true` 清理无主台账,整串码都可能消失。
81
+ - 改法:`pruneChain()` 不再按年龄剔成员(成员只由显式 `leaveChainNode` / `term_unlink` 移除,台账空了才收口);`dropChainIfOrphan()` 改为**只按台账自身年龄**回收 —— 30 天无更新(`ORPHAN_CHAIN_TTL_MS`,`lib/bus.js` 导出)才当垃圾删,成员是否在线不再参与删除决策。presence 读不到仍 fail-closed(拒绝删除)。
82
+ - `term_unlink all=true` 命中「无主但台账新鲜」时,明确返回 `ok:false` + 原因「成员离线不等于解散…」,并给出成员数与台账年龄;不再伪装成「本终端没接在这串码上」。
83
+ - 测试:`test/chain.test.mjs` 用例 2 改写(新鲜无主台账必须保留),新增 2b(台账过期 30 天可回收)、2c(`pruneChain` 不得剔离线成员)。
84
+
85
+ ### 加固:离线阈值单点化(一处定义,禁止再写第二份)
86
+
87
+ - `lib/index.js` 删除自带的 `3 * 60 * 1000` 字面量,改为 `const PRESENCE_OFFLINE_MS = PRESENCE_TTL_MS`(从 `lib/bus.js` 导入)。此前同一个值在 `lib/bus.js:49` 与 `lib/index.js:74` 各写一份,改一处漏一处;`docs/presence-online-semantics.md` 的变更纪律同步为「只在 `lib/bus.js` 定义」。
88
+ ### 修复:重启链路在「端口由宿主分配」的机器上会二次杀 + 假失败(第二设备阻断 B1 / 建议 S1–S3)
89
+
90
+ - 背景:1.0.1 在第二设备(YOCE_C)复验时,真实重启成功但报 exit 1:helper 由 `mshta`/WMI 拉起,**不继承宿主环境**,`DSH_WEB_URL` 拿不到 → `PORT` 回落 19387,而该机端口是动态的(53168 / 重启后 63963)→ `waitUp()` 永远 closed → 12 万毫秒后 `kill#2` 把刚拉起的健康实例再杀一次 → `launch#2` → exit 1。
91
+ - **B1 修法(端口一路透传)**:`trigger-restart.mjs` 在自己进程里解析端口(它跑在宿主内,只有它看得见 `DSH_WEB_URL`)并写进 `request.json` 的 `webPort`;helper 用 `applyWebPort(req)` 让**请求优先、环境兜底**;`countdown.html` 的两个检查点与 `rearm-watch.mjs` 都带 `--port`;`busy-check.mjs` CLI 新增 `--port`。helper 日志改为 `port=<N> (<request|env>)`,事后能直接看出端口来源。
92
+ - **S1(同因)**:倒计时窗口内的 `busy-check` 也走 `--port`,不再只靠「心跳恰好新鲜」通过。
93
+ - **S2**:`taskkill` 的中文输出按控制台代码页(cp936/GBK)解码后再写日志,不再出现 `[kill#1] taskkill ok: 乱码`。
94
+ - **S3(文档)**:README 注明脚本只从 `~/.dsh/crossterm/restart-module/` 的部署副本调用;直接跑 `node_modules` 副本时该目录没有 `logs/busy-agents.json`,检查点会判 unknown 取消,属预期而非缺陷。
95
+ - 测试:`test/restart.test.mjs` 新增 3 例(9 `applyWebPort` 六种输入;10 trigger/countdown/rearm 三处透传契约;11 GBK 解码「成功」),全套 **11/11** 通过。
96
+ - 组件版本:自动重启 `0.3.42` → **0.3.43**(本版最终 **0.3.44**,见下节「清理」);其余组件不动。
97
+ ### 特性:跨设备信封签名 v2 / 一发多投元数据(relay v2 重放包)
98
+
99
+ - 背景:跨设备 P0 分两段落地 —— 云端运维负责的 TTL / 终态回执 / 去重 LRU(0.4.1)先合;签名 v2 与 `targets` 元数据由跨设备负责人做成「基于 26,338 B 基线(`C6D7B363…`)的重放包」交出,本版按该包整文件合并(备份 `archive/relay-before-v2-20261020-233452.js`,26,338 B)。合并之后又做了两笔收尾:① 23:43 给 `expiredDir`/`deadDir` 加「有键用键、否则 deviceRoot 派生」的 fallback;② 23:48 把 `layout.paths.receiptsDir` 纠正为 `receiptsDirName`(只给目录名,回执落点仍是 `shared/<发件方>/receipts`)。最终 `functions/cloud-ops/cloud/relay.js` 34,630 B `3B4B82C1…`,并由云端运维独立 rebase 复现同一 sha256。
100
+ - 新增:`canonicalEnvelopeV2`(v1 原序 + `expiresAt` + `targets` + 字面量 2)、`signEnvelopeV1/V2` 按 `e.v` 分派、收信侧 `v>=2` 只按 v2 规范验签(禁止降级,否则改 `expiresAt`/`targets` 可绕过签名)、`compareVersion` + `SUPPORTS_SIG_V2_MIN='1.0.2'` 门控(读目标 `shared/<dev>/_device.json#version`,读不到或太旧一律发 v1)、`sendEnvelope({targets:[{nodeId,sessionId?,sessionName?}]})`、新多目标路径默认 7 天 TTL(`DEFAULT_TTL_MS`,老单目标路径不传不写)。
101
+ - 边界:`layout.paths` 未新增路径键 —— 只补了 `receiptsDirName`(**目录名**,不是路径)与 `expiredDir`/`deadDir` 三个设备级键,且全部由 `deviceRoot` 派生;回执路径仍按发件方设备计算。回执格式、5 句中文失败文案(与 `cloudFailureStage()` 正则耦合)、退避与 8 次进 `dead/` 全部不变。
102
+ - 门控现实:presence 的 `version` 由运行中插件常量写(现网仍是 0.9.13),所以本版部署后**实际只会发 v1**,v2 要等 1.0.3 真发版 + 重启后才启用 —— 方向是安全的(旧端能收)。
103
+ - 测试:仓库新增 `test/relay-v2.test.mjs` **9/9**(v1 门控、v2 受签名保护、篡改 `expiresAt` 拒收、v1/v2 混投、缺 `_device.json` 退 v1、targets 默认 7 天、`compareVersion`、**版本缓存过期后重读 + 读失败只 warn 一次 + 同设备只读一次 `_device.json` + v2→v1 收端退避留件与降级签名拒收**);`test/relay.test.mjs` 7/7;10 个测试文件全 exit 0。
104
+ - 组件版本:跨设备 `0.4.0` → **0.4.2**(0.4.1 是云端 TTL/回执那一笔,本版是 v2 重放那一笔)。
1
105
  ## 1.0.1 — 2026-10-20
2
106
 
3
107
  ### 修复:换台机器就跑不了的三处写死(第二设备验收 F1/F2/F3)
package/MAINTAINERS.md CHANGED
@@ -10,60 +10,92 @@
10
10
  "session": "session-3195b037-9ebd-4e9a-83dd-fb6acfc8a77c",
11
11
  "short": "3195b037",
12
12
  "name": "插件—互通插件负责人001",
13
- "role": "负责人",
13
+ "role": "主负责人(整合优化、版本发布、任务下发)",
14
14
  "terminal": "TS-01",
15
15
  "nodeId": "ts-01-a52535",
16
16
  "workspace": "H:\\dshGZQ\\dsh-crossterm",
17
17
  "since": "2026-10-20T19:55:00+08:00",
18
- "basis": "机主指令:「负责人交接,权限交接:插件—互通插件负责人001」"
18
+ "basis": "机主指令(2026-10-20 22:5x):「任命你(插件—互通插件负责人001)为互通插件主负责人,主要负责整合下级各组件、功能的优化升级;负责新版本发布;负责任务下发」"
19
19
  },
20
- "assistant": {
21
- "session": "session-ccf34a5a-ad6d-4423-9fa8-fd6ffc44052b",
22
- "short": "ccf34a5a",
23
- "name": "同端多会话2",
24
- "role": "助手",
20
+ "deputy": {
21
+ "session": "",
22
+ "name": "插件—互通插件负责人002",
23
+ "role": "次负责人(新版本检测、验收、反馈)",
24
+ "terminal": "YOCE_C",
25
+ "nodeId": "yoce-c-4fb38e",
26
+ "machine": "第二设备(D:\\dsh)",
27
+ "since": "2026-10-20",
28
+ "basis": "机主指令:「任命第二设备:插件—互通插件负责人002为互通插件次负责人,主要负责插件新版本检测验收反馈,上级(插件—互通插件负责人001)」",
29
+ "rule": "每版发布后 24h 内出验收结论(阻断项 / 建议项分开);阻断项在主负责人修完前不催发下一版;不得自行发版"
30
+ },
31
+ "fallback": {
32
+ "session": "session-71734ef7-e18b-496e-a621-054b79b3afa1",
33
+ "short": "71734ef7",
34
+ "name": "兜底会话",
35
+ "role": "互通插件组兜底(主负责人不可用时接管值守与复核)",
25
36
  "terminal": "TS-01",
26
37
  "nodeId": "ts-01-a52535",
27
- "since": "2026-10-18T00:00:00+08:00",
28
- "scope": "按负责人派活改 lib/*.js;不得改版本号、总线状态、重启、C 盘写入、AGENTS.md",
29
- "status": "2026-10-20 19:55 随负责人交接暂停派活;是否续用由新负责人 3195b037 决定"
38
+ "workspace": "H:\\dshGZQ\\dsh-crossterm",
39
+ "since": "2026-10-20",
40
+ "basis": "机主指令(2026-10-20):「任命兜底会话为互通插件组的兜底工作」",
41
+ "positioning": "尽量不依赖插件:宿主原生工具(pwsh / read / write / glob / grep)能做的就不走插件;插件整体失效时仍要能值守总线、仓库与日志",
42
+ "scope": "主负责人不在线/卡住时的接管:只读复核、跑测试、看总线与日志、记录 STATE 结论、代主负责人对外通报事实;接管期间对仓库与总线有值守权",
43
+ "rule": "不得自行发版(bump 版本号 / publish / 改 CHANGELOG 定稿);不得改 package.json、lib/bus.js 的 VERSION、MAINTAINERS.md(除更正自己的记录);真重启需机主同意;主负责人回来即交回"
44
+ },
45
+ "functions": {
46
+ "cloud-ops": {
47
+ "session": "session-eb661573-241b-458b-ae60-24df0449a0c9",
48
+ "short": "eb661573",
49
+ "name": "功能—云端运维负责人001",
50
+ "role": "云端运维(云端功能维护、优化与更新)",
51
+ "terminal": "TS-01",
52
+ "nodeId": "ts-01-a52535",
53
+ "since": "2026-10-20",
54
+ "scope": "functions/cloud-ops/**(中转站、备份、layout、账号与设备目录、同步与恢复)、云盘目录规则文档、term_cloud 的云端行为",
55
+ "rule": "不改插件总版本号;组件版本随 functions/cloud-ops/** 改动升;不自行发版;结论与风险写进 STATE 或邮件给主负责人"
56
+ }
30
57
  },
31
58
  "modules": {
32
59
  "ui": {
33
60
  "session": "session-3195b037-9ebd-4e9a-83dd-fb6acfc8a77c",
34
61
  "short": "3195b037",
35
- "role": "入口/自更新组件负责人(= 插件负责人)",
62
+ "name": "插件—互通插件负责人001",
63
+ "role": "入口/自更新组件负责人(兼主负责人)",
36
64
  "since": "2026-10-20",
37
- "scope": "components/ui/**、client/**、lib/web.js 的卡片与自动更新、locale/**"
65
+ "scope": "components/ui/**(含 `components/ui/lib/web.js`:卡片与自动更新)、client/**、locale/**"
38
66
  },
39
67
  "session": {
40
- "session": "session-3195b037-9ebd-4e9a-83dd-fb6acfc8a77c",
41
- "short": "3195b037",
42
- "role": "跨会话组件负责人(= 插件负责人)",
68
+ "session": "session-d4984f54-5acf-40d8-987b-528e470051f1",
69
+ "short": "d4984f54",
70
+ "name": "组件—跨会话负责人001",
71
+ "role": "跨会话组件负责人",
43
72
  "since": "2026-10-20",
44
- "scope": "components/session/**、lib/bus.js 的链/邮件/收件箱"
73
+ "scope": "components/session/**、lib/bus.js 的链/邮件/收件箱、lib/index.js 中链与邮件相关工具(term_link / term_unlink / term_broadcast / term_send / term_inbox)"
45
74
  },
46
75
  "terminal": {
47
- "session": "session-3195b037-9ebd-4e9a-83dd-fb6acfc8a77c",
48
- "short": "3195b037",
49
- "role": "跨工作区组件负责人(= 插件负责人)",
76
+ "session": "session-b71bbc49-d131-4780-b247-a682384905ac",
77
+ "short": "b71bbc49",
78
+ "name": "组件—跨工作区负责人001",
79
+ "role": "跨工作区组件负责人",
50
80
  "since": "2026-10-20",
51
- "scope": "components/terminal/**、本机 presence / term_send / term_send_files"
81
+ "scope": "components/terminal/**、本机 presence / term_list / 同终端跨会话投递"
52
82
  },
53
83
  "device": {
54
- "session": "session-3195b037-9ebd-4e9a-83dd-fb6acfc8a77c",
55
- "short": "3195b037",
56
- "role": "跨设备组件负责人(= 插件负责人)",
84
+ "session": "session-61b1ec6c-2d7a-40ed-bd1a-faa3da9179a9",
85
+ "short": "61b1ec6c",
86
+ "name": "组件—跨设备负责人001",
87
+ "role": "跨设备组件负责人",
57
88
  "since": "2026-10-20",
58
- "scope": "components/device/**、lib/cloud/**(中转站、备份、layout)"
89
+ "scope": "components/device/**、functions/cloud-ops/cloud/relay.js 与 presence.js 的跨设备投递协议、term_send 的云端信封分支、term_cloud(`functions/cloud-ops/**` 的落盘权仍归 eb661573,他只交 diff)"
59
90
  },
60
91
  "restart": {
61
- "session": "session-3195b037-9ebd-4e9a-83dd-fb6acfc8a77c",
62
- "short": "3195b037",
63
- "role": "自动重启组件负责人(暂由插件负责人直管,等后续交接时任命新的)",
92
+ "session": "session-357e0561-4280-4288-9093-d3105d83f700",
93
+ "short": "357e0561",
94
+ "name": "组件—自动重启负责人001",
95
+ "role": "自动重启组件负责人",
64
96
  "since": "2026-10-20",
65
- "scope": "components/restart/ 下的 core.js、runtime.js、scripts/**",
66
- "rule": "原负责人 a3e9f80e 已于 2026-10-20 19:33 交回维护权(机主指示:转给插件负责人,等负责人任命新的);暂由 3195b037 直管,只改 components/restart/** 时把组件版本号(现 0.3.29)加一,仍由插件负责人统一发版"
97
+ "scope": "components/restart/**(core.js / runtime.js / scripts/**)",
98
+ "rule": "只改本组件时把 components/restart/manifest.json 的 version 加一(现 0.3.41);不改插件总版本号;不自行发版;不得自行触发真重启(真重启需机主同意);改完把 helper/busy-check/trigger 三份同步到总线 restart-module"
67
99
  }
68
100
  },
69
101
  "previous": [
@@ -77,17 +109,6 @@
77
109
  "from": "2026-10-18T00:59:27+08:00",
78
110
  "to": "2026-10-20T19:55:00+08:00",
79
111
  "reason": "机主指令交接给「插件—互通插件负责人001」"
80
- },
81
- {
82
- "session": "session-6ef6da30-a7f1-4a4e-...",
83
- "short": "6ef6da30",
84
- "name": "互通插件主维护(初版)",
85
- "role": "负责人",
86
- "terminal": "TS-01",
87
- "nodeId": "ts-01-a52535",
88
- "from": "2026-10-17T20:30:15+08:00",
89
- "to": "2026-10-18T01:09:28+08:00",
90
- "reason": "会话故障(投递不消费,转录停在 5.2 MB),用户改派"
91
112
  }
92
113
  ]
93
114
  }
@@ -99,11 +120,13 @@
99
120
 
100
121
  | 组件(插件页顺序) | 当前版本 | 负责人 | 范围 | 规矩 |
101
122
  |---|---|---|---|---|
102
- | ① 跨端互通(自更新) | v0.9.3 | `3195b037` | `components/ui/**`、`client/**`、`locale/**`、`lib/web.js` | 组件版本号在 `components/ui/manifest.json` |
103
- | ② 跨会话 | v0.5.3 | `3195b037` | `components/session/**`、`lib/bus.js` 的链/邮件 | 同上 |
104
- | ③ 跨工作区 | v0.1.0 | `3195b037` | `components/terminal/**`、presence / `term_send` | 同上 |
105
- | ④ 跨设备 | v0.4.0 | `3195b037` | `components/device/**`、`lib/cloud/**` | 同上 |
106
- | ⑤ 自动重启 | v0.3.29 | `3195b037`(暂管;原 `a3e9f80e` 已于 2026-10-20 19:33 交回) | `components/restart/**`(`core.js` / `runtime.js` / `scripts/**`) | 不单独发版;新负责人等后续交接时任命;只动它就把它的版本号加一 |
123
+ | ① 跨端互通(自更新) | v0.9.3 | `3195b037`(主负责人兼) | `components/ui/**`(含 `components/ui/lib/web.js`)、`client/**`、`locale/**` | 组件版本在 `components/ui/manifest.json` |
124
+ | ② 跨会话 | v0.5.4 | `d4984f54`(组件—跨会话负责人001) | `components/session/**`、`lib/bus.js` 的链/邮件、`lib/index.js` 的链与邮件工具 | 同上 |
125
+ | ③ 跨工作区 | v0.2.2 | `b71bbc49`(组件—跨工作区负责人001) | `components/terminal/**`、presence / `term_list` / 同终端投递 | 同上 |
126
+ | ④ 跨设备 | v0.4.2 | `61b1ec6c`(组件—跨设备负责人001) | `components/device/**`、`functions/cloud-ops/cloud/relay.js`、`presence.js`(协议归他,落盘归云端运维)、`term_send` 云端分支 | 同上 |
127
+ | ⑤ 自动重启 | v0.3.44 | `357e0561`(组件—自动重启负责人001) | `components/restart/**` | 只动它就把 `components/restart/manifest.json` 版本加一;不改插件总版本;不自行发版;真重启需机主同意 |
128
+ | 云端运维(功能,不占插件页) | — | `eb661573`(功能—云端运维负责人001) | `functions/cloud-ops/**`、云盘目录规则、`term_cloud` 云端行为 | 组件版本随 `functions/cloud-ops/**` 改动升;不自行发版 |
129
+ | 兜底(不占插件页) | — | `71734ef7`(兜底会话) | 主负责人不在线时的值守:只读复核、测试、总线与日志、STATE 结论、对外通报 | 不发版、不 bump、不改 VERSION/CHANGELOG 定稿;真重启需机主同意;主负责人回来即交回 |
107
130
 
108
131
  **组件版本号怎么升**:只在该组件自己的文件改动时升它(在 `components/<part>/manifest.json` 的 `version`),
109
132
  跟着插件一起发版;插件总版本仍按发版技能的三分位规则。插件页与 `GET /crossterm/api/about` 的 `components[].version` 读的就是这里。
@@ -116,13 +139,65 @@
116
139
 
117
140
  现场验证(10 s 检查点取消 → 转后台 → 空闲后重排)仍挂起,等机主安排;不得自行触发真重启。
118
141
 
142
+ ## 任命与发布流程(2026-10-20 机主指令)
143
+
144
+ 1. **主负责人**(`3195b037`):整合各组件、定优先级、下发任务、审改动、统一发版(npm + zip + 云盘),并对外代表插件。
145
+ 2. **次负责人**(`002` / YOCE_C):每版发布后的检测与验收,阻断项/建议项分开出结论;不自行发版。
146
+ 3. **组件/功能负责人**:只改自己范围;只升自己组件的 `components/<part>/manifest.json` 版本;**不改插件总版本号、不自行发版**;改完把改动清单(文件 + 行号 + 自测)发主负责人。
147
+ 4. 发版顺序固定:组件负责人改 → 主负责人预检(`node --check` + 三套测试 + `pnpm pack` 检查残留)→ 主负责人发布 → 次负责人验收 → 结论回写 `STATE`。
148
+ 5. 只有主负责人能改 `package.json` / `lib/bus.js` 的 `VERSION` / `CHANGELOG.md` / `README.md` 的版本段 / `MAINTAINERS.md`。
149
+
150
+ ## 任务下发(2026-10-21 机主指令:「任务不一定要全部自己完成,可以按匹配度下发」)
151
+
152
+ 主负责人是**派活的人**,不是唯一的干活的人。凡是能落到某个组件/功能负责人职责范围内的事,一律下发,不自己代做。
153
+
154
+ | 任务类型 | 匹配给 | 说明 |
155
+ |---|---|---|
156
+ | `components/restart/**` 的改动与自测 | `357e0561` | 含 helper / busy-check / countdown / rearm / trigger / core |
157
+ | `components/device/**`、信封协议、版本门控 | `61b1ec6c` | 协议代码在 `functions/cloud-ops/cloud/{relay,presence}.js`,他提 diff |
158
+ | `components/session/**`、投递路由与收件箱语义 | `d4984f54` | `lib/index.js` 的投递调用点也归他(定点改、落地即报) |
159
+ | `components/terminal/**`、presence 与 `term_list` 展示 | `b71bbc49` | 显示层与解析层都在他范围 |
160
+ | `functions/cloud-ops/**`、网盘与备份链路 | `eb661573` | 该目录唯一落盘人 |
161
+ | 发版后的第二设备验收 | 副手 `yoce-c-4fb38e` | 阻断/建议两栏,不自行发版 |
162
+ | 本机装包后的实装自检、跨机对比 | 兜底 `71734ef7` | 只读为主,不参与写代码与发版 |
163
+ | 打包、版本号、CHANGELOG 定稿、`MAINTAINERS.md`、跨组件取舍、对外代表 | **主负责人保留** | 这些不下发 |
164
+
165
+ **下发格式**(每封必含五段,缺一段就会被退回来问):
166
+
167
+ 1. **目标**:要解决什么现象 / 达成什么。
168
+ 2. **范围**:允许改哪些文件(含「不许碰」清单)。
169
+ 3. **验收**:跑哪几个测试、看哪个字段、期望值是什么。
170
+ 4. **约束**:版本号归属、冻结窗口、锚点写法(函数名 + 唯一片段,不用行号)、落地即报。
171
+ 5. **回报**:交什么(diff / 自测输出 / 新 sha256 + mtime / 待我拍的点),以及**不需要我回信时直接做**。
172
+
173
+ **并行纪律**:同一文件同一时间只有一个写者;`lib/index.js` 按「跨工作区 → 跨设备 → 跨会话」排队;跨组件依赖先报主负责人裁决,不许自行扩大范围。
174
+
175
+ **留痕**:每封任务下发与完成各记一行进 `STATE-0.9.13.md`(下发人、负责人、目标、验收、状态)。
176
+
177
+ ## 兜底会话(2026-10-20 机主指令:「任命兜底会话为互通插件组的兜底工作」)
178
+
179
+ `兜底会话` = `session-71734ef7-e18b-496e-a621-054b79b3afa1`(短号 `71734ef7`,TS-01)。它的定位是**尽量不依赖插件**:宿主原生的 `pwsh` / `read` / `write` / `glob` / `grep` 能做的就不走插件,插件整体失效时仍要能值守。
180
+
181
+ - **什么时候接管**:主负责人不在线、卡住、或本会话不可用时,由它接管「互通插件组」的值守 —— 看总线与日志、跑 `test/` 下 10 个 `*.test.mjs`(`--import …/test/register.mjs`,不加会 `ERR_MODULE_NOT_FOUND @deepseek-ai/dsh-tools`)、只读复核别人交上来的改动、把结论写进 `STATE`、代主负责人对外通报事实。
182
+ - **能覆盖什么**:**插件失效但宿主还活着**(进程在、19387 在、会话还能跑);**不覆盖宿主进程崩溃** —— 那由机主手动处理,不做任何自动重启。
183
+ - **只读脚本的硬要求**:复核输入全是插件产出的 JSON,形状一晚改过好几轮(`inTurn` → `producer`/`eventsSeen`/`src`/`states`),所以任何只读脚本必须带 **schema 校验 + `at` 新鲜度判断**;读不懂一律报 `unknown`,**绝不能把「缺字段」当 `false`**(这就是 0.3.35 之前 fail-open 自锁的同一个坑)。
184
+ - **单点(结构性,不作承诺)**:兜底会话与主负责人同终端 TS-01、同 bus `C:\Users\Administrator\.dsh\crossterm`;`links\ts-01-a52535.json` 当前 `links: []`(原特征码 NA7SE5 已废弃)。本终端进程崩、或会话卡在长 turn 时**没有第二条在线兜底路径**,只能靠落盘断点(`F:\dsh\001X\logs\fallback-session-protocol.md`)与机主手动接入。
185
+ - **不能做什么**:不发版(不 bump `package.json` / `lib/bus.js VERSION`、不改 `CHANGELOG.md` 定稿、不 publish、不打 zip、不推云盘)、不改 `MAINTAINERS.md`(除更正自己这一行)、真重启要机主同意。
186
+ - **交回**:主负责人重新可用即交回;接管期间做的判断与数据要留痕(`STATE` 或邮件),便于主负责人复核。
187
+
119
188
  ## 任期表
120
189
 
121
190
  | 起(本地时间) | 止(本地时间) | 会话 | 终端 / nodeId | 角色 | 交接依据 |
122
191
  | --- | --- | --- | --- | --- | --- |
123
192
  | 2026-10-17 20:30 | 2026-10-18 01:09 | `session-6ef6da30…`(初版负责人) | TS-01 / `ts-01-a52535` | 负责人 | 会话故障,转录停止增长、投递不消费;用户改派 |
124
193
  | 2026-10-18 00:59 | 2026-10-20 19:55 | `session-b6aa099b…`(互通插件主维护) | TS-01 / `ts-01-a52535` | 负责人 | 用户首条指令:「你现在是互通插件的新负责人」;交卸依据:机主「负责人交接,权限交接」 |
125
- | 2026-10-20 19:55 | 至今 | `session-3195b037…`(插件—互通插件负责人001) | TS-01 / `ts-01-a52535`(工作区 `H:\dshGZQ\dsh-crossterm`) | 负责人 | 机主指令:「负责人交接,权限交接:插件—互通插件负责人001」 |
194
+ | 2026-10-20 19:55 | 至今 | `session-3195b037…`(插件—互通插件负责人001) | TS-01 / `ts-01-a52535`(工作区 `H:\dshGZQ\dsh-crossterm`) | **主负责人** | 机主指令:交接给「插件—互通插件负责人001」;2026-10-20 22:5x 改任「互通插件主负责人」 |
195
+ | 2026-10-20 22:5x | 至今 | YOCE_C(第二设备,无本机 session) | YOCE_C / `yoce-c-4fb38e` | **次负责人** | 机主指令:「任命第二设备:插件—互通插件负责人002为互通插件次负责人」 |
196
+ | 2026-10-20 22:5x | 至今 | `session-eb661573…`(功能—云端运维负责人001) | TS-01 / `ts-01-a52535` | 云端运维 | 机主任命 |
197
+ | 2026-10-20 22:5x | 至今 | `session-d4984f54…`(组件—跨会话负责人001) | TS-01 / `ts-01-a52535` | 跨会话组件负责人 | 机主任命 |
198
+ | 2026-10-20 22:5x | 至今 | `session-b71bbc49…`(组件—跨工作区负责人001) | TS-01 / `ts-01-a52535` | 跨工作区组件负责人 | 机主任命 |
199
+ | 2026-10-20 22:5x | 至今 | `session-61b1ec6c…`(组件—跨设备负责人001) | TS-01 / `ts-01-a52535` | 跨设备组件负责人 | 机主任命 |
200
+ | 2026-10-20 22:5x | 至今 | `session-357e0561…`(组件—自动重启负责人001) | TS-01 / `ts-01-a52535` | 自动重启组件负责人 | 机主任命 |
126
201
  | 2026-10-20 19:33 | 2026-10-20 19:33 | `session-a3e9f80e…`(自动重启组件) | TS-01 / `ts-01-a52535` | 组件负责人 | 交回:机主指示「将组件权限转给插件负责人,等负责人任命新的」 |
127
202
  | 2026-10-17 21:44 | 2026-10-20 19:55(随交接暂停派活,去留由新负责人定) | `session-ccf34a5a…`(同端多会话2) | TS-01 / `ts-01-a52535` | 助手 | 用户:「指定同端多会话2为你的助手,部分修改可让他进行」 |
128
203
 
@@ -136,15 +211,42 @@
136
211
  4. **CHANGELOG 提一句**:当版写「负责人变更:… → …(时间,依据)」。
137
212
  5. **通知相关会话**:在链上用 `term_send` / `term_broadcast` 说明移交,并提醒对方不要再按旧负责人办事。
138
213
 
139
- `logs/ownership.log` 每行一条,格式:
214
+ `logs/ownership.log` 每行一条,格式(**NDJSON:逐行 parse,别把整块当 JSON**):
140
215
 
141
- ```json
216
+ ```text
142
217
  {"at":"2026-10-18T00:59:27+08:00","from":"6ef6da30","to":"b6aa099b","role":"负责人","basis":"用户交接:原负责人会话故障"}
143
218
  {"at":"2026-10-20T19:55:00+08:00","from":"b6aa099b","to":"3195b037","role":"负责人","basis":"机主指令:负责人交接,权限交接:插件—互通插件负责人001"}
144
219
  ```
145
220
 
221
+ ## 目录归属(2026-10-20 机主指令:「要求组件、功能,分文件夹存放,防止混淆、误改」)
222
+
223
+ 每个组件、每个功能各占一个自己的文件夹;**谁的文件夹谁改**,越界即视为误改。
224
+
225
+ | 目录 | 归谁 | 里面是什么 | 边界 |
226
+ |---|---|---|---|
227
+ | `components/ui/` | `3195b037` | 插件页入口卡片、自动更新;`components/ui/lib/web.js` | 含 `manifest.json`(版本 0.9.3) |
228
+ | `components/session/` | `d4984f54` | 跨会话组件;链/邮件/收件箱的语义 | 共享核心 `lib/bus.js`、`lib/index.js` 的改动要报主负责人 |
229
+ | `components/terminal/` | `b71bbc49` | 跨工作区;presence / `term_list` / 同终端投递 | 同上 |
230
+ | `components/device/` | `61b1ec6c` | 跨设备;`manifest.json` 只有他能升 | 协议代码在 `functions/cloud-ops/cloud/{relay,presence}.js`,落盘权归云端运维 |
231
+ | `components/restart/` | `357e0561` | 自动重启组件;`core.js` + `scripts/**` | 真重启需机主同意 |
232
+ | `functions/cloud-ops/` | `eb661573` | 云端运维功能:`cloud/{relay,home,layout,pan,sync,presence,devices,match,setup,session-backup}.js` | `cloud/relay.js`、`cloud/presence.js` 的**协议**由 `61b1ec6c` 提 diff,落盘只由云端运维做 |
233
+ | `lib/` | 主负责人(共享核心) | `index.js`(插件入口/工具注册)、`bus.js`(总线)、`logs.js`、`names.js` | **不放业务组件代码**;多个负责人要改同一处时按「函数名 + 唯一片段」定点替换、落地即报 |
234
+ | `client/`、`locale/` | `3195b037` | 前端卡片资源、语言包 | — |
235
+ | `test/`、`docs/` | 主负责人 + 提测试的人 | 10 个 `*.test.mjs`、决策文档 | 测试跟着被测代码走,不按人分 |
236
+ | 写信与回信模板 `F:\dsh\001X\crossterm-data\outbox\templates\` | **各组件/功能负责人各管自己那份** | `cloud-ops.md`/`device.md`/`workspace.md`/`session.md`/`restart.md`/`deputy-002.md`;`shared-facts.md` 归主负责人 | 谁的范围谁写信;主负责人只做整合、发信、记录 |
237
+ | `archive/` | 只读 | 历史版本与备份 | 不进 tarball |
238
+
239
+ **硬规矩**:
240
+
241
+ 1. 新功能必须建 `functions/<名字>/`,**不许塞进 `lib/`**;新组件必须建 `components/<名字>/` 并带 `manifest.json`。
242
+ 2. 改别人的文件夹之前先发信说明,或在 `STATE` 记一条「谁批准」;没批准就动 = 误改,一律回滚并记 `ownership.log`。
243
+ 3. 目录搬迁(如本次 `lib/cloud/` → `functions/cloud-ops/cloud/`、`lib/web.js` → `components/ui/lib/web.js`)只由主负责人做,且必须同步:`package.json` 的 `files` 白名单、所有 `import` 路径、`test/**` 的引用、`docs/` 里的路径描述。
244
+ 4. 运行中的宿主仍加载旧路径,**目录搬迁后要重启一次**才按新结构加载(`test/**` 是直接跑文件,不受影响)。
245
+ 5. **信件模板由对应负责人管理**(机主 2026-10-21 指令):每个组件/功能负责人的回信正文,写在自己那份 `outbox\templates\<名字>.md` 里,自己改;技术事实(版本、指纹、测试结果)只引用 `shared-facts.md`,不各自量。主负责人不代写别人的技术结论,只在发信窗口整合发送。
246
+
146
247
  ## 规则(与 AGENTS.md §7 一致)
147
248
 
148
- - 只有当前负责人能改 `F:\dsh\001X\dsh-crossterm\`;其他会话可以测、可以报 bug、不能改。
149
- - 助手只能按负责人的派活改,且不得动版本号、总线状态文件、重启、C 盘写入、`AGENTS.md`。
249
+ - **主负责人**能改 `F:\dsh\001X\dsh-crossterm\` 的任何文件;**被任命的组件/功能负责人**只能改自己范围(见上表与 JSON 的 `modules` / `functions`),改完必须报主负责人复核。
250
+ - 版本号归主负责人:组件负责人只升自己 `components/<part>/manifest.json` 的 version;`package.json`、`lib/bus.js` 的 `VERSION`、`CHANGELOG.md`、发布动作只有主负责人能做。
251
+ - 真重启(非 dry-run)必须机主同意;组件负责人跑 dry-run 不受限。
150
252
  - 换人不是「口头说说」:没更新本文件、没写 `logs/ownership.log`,就还是旧负责人在负责。
package/README.md CHANGED
@@ -19,18 +19,41 @@ A 终端看不到 B 终端里有哪个会话、在干什么,也没法把一条
19
19
  | 顺序 | 组件名称 | 版本 | 作用 | 工具 |
20
20
  |---|---|---|---|---|
21
21
  | ① | 跨端互通(入口/自更新) | v0.9.3 | 自更新开关:打开后定期检查 npm 新版本并自动安装(默认关;装完要重启 DSH 生效)。关掉它只失去卡片与自动更新,下面四个组件照常工作。裸包名行,承载插件标题/简介与配置卡片,不挂工具 | — |
22
- | ② | 跨会话 | v0.5.3 | 同一终端里的多个会话之间互通:特征码链、内联邮件、收件箱 | `term_link` `term_unlink` `term_broadcast` `term_inbox` `term_config` |
23
- | ③ | 跨工作区 | v0.1.0 | 在本机不同工作区、不同会话之间互发邮件与文件(跨终端本轮不做,暂无条件) | `term_whoami` `term_list` `term_send` `term_send_files` |
24
- | ④ | 跨设备 | v0.4.0 | 经云端中转站,在多台机器的 DSH 之间互发邮件与文件 | `term_cloud` |
25
- | ⑤ | 自动重启 | v0.3.41 | 改完插件/配置后让 DSH 自己重启一次,并自动拉起离线会话(原 `dsh-restart` 插件,0.9.0 起并入) | `dsh_restart` |
22
+ | ② | 跨会话 | v0.5.4 | 同一终端里的多个会话之间互通:特征码链、内联邮件、收件箱 | `term_link` `term_unlink` `term_broadcast` `term_inbox` `term_config` |
23
+ | ③ | 跨工作区 | v0.2.2 | 在本机不同工作区、不同会话之间互发邮件与文件(跨终端本轮不做,暂无条件) | `term_whoami` `term_list` `term_send` `term_send_files` |
24
+ | ④ | 跨设备 | v0.4.2 | 经云端中转站,在多台机器的 DSH 之间互发邮件与文件 | `term_cloud` |
25
+ | ⑤ | 自动重启 | v0.3.44 | 改完插件/配置后让 DSH 自己重启一次,并自动拉起离线会话(原 `dsh-restart` 插件,0.9.0 起并入) | `dsh_restart` |
26
26
 
27
27
  > 入口行为什么必须用裸包名:浏览器 client 半端只对裸包名行生效(`dsh-client-modules` 的
28
28
  > `locatePkgJson()` 对子路径行返回 undefined),而插件页顶部标题与这一行共用同一份 `locale/zh.json` 的 meta,
29
29
  > 所以它单独做入口(配置卡片 + 自动更新开关),标题带「(自更新)」;卡片头用 `shortTitle` 显示短名「跨端互通」。
30
30
  >
31
31
  > 组件版本号怎么升:**只在该组件自己的文件改动时升它**,跟着插件一起发版(例:只改自动重启 → `components/restart` 的 `0.3.28` → `0.3.29`,其余四个不动)。插件总版本仍按发版技能的三分位规则。
32
+ >
33
+ > 重启脚本的调用位置:只从部署副本 `~/.dsh/crossterm/restart-module/` 调(`trigger-restart.mjs` / `busy-check.mjs` / `rearm-watch.mjs` / `dsh-restart-helper.mjs`)。直接跑 `node_modules` 里的副本时,那个目录下没有 `logs/busy-agents.json`,倒计时检查点会判 `unknown` 并取消本次重启 —— 这是预期的 fail-closed,不是缺陷。
34
+
35
+ **本版更新(1.0.5)**
36
+
37
+ - **修复:插件卡片「简介 / 更新日志 / 运行日志」消失** —— 1.0.3 目录重组把 `lib/web.js` 搬到 `components/ui/lib/web.js` 后,`ROOT` 只算到 `components/ui`,`aboutOf()` 读不到 `locale/zh.json` 与组件 manifest,接口 `ok:true` 但简介与更新日志为空,卡片整块不渲染。`ROOT` 已回到包根;另加 `readJsonFile()` 容忍 BOM(`components/terminal/manifest.json` 曾带 BOM)。新增 `test/about.test.mjs` 覆盖。
38
+ - **变更:磁盘邮件头按机主口径** —— 去掉「发件终端」,`发件会话` 改「发件人」,其下新增「收件人」行,「发件工作区」保留。
39
+ - **跨设备:信封 `silent` 附加字段** —— `sendEnvelope` 仅在 `true` 时写 `envelope.silent`,不参与签名;收端透传给 `deliver`。多目标 + `silent` 本轮仍 fail-closed 拒绝。
40
+ - **M2 一发多投接线** —— `send()` 接受 `targets` / `toStrict`,多于 1 个目标走 `sendManyCloud()`(`planFanout` → 逐组发送 → `mapFanoutResults`);`term_send` 增 `targets` / `toStrict`;形状按 `docs/sendmany-result-shape.md` v3(`recipients` 数目标、`expectedReceipts` 数信封)。
41
+ - **跨会话 0.5.7:默认会话失效 fail-closed** —— `pickDeliverSession` 第 2 级遇到 `defaultSessionId` 指向未打开的会话时明确报错并留件重试,不猜会话。
42
+ - **自动重启 0.3.46:倒计时不再「卡住跳 0」** —— 检查点异步探测 `busy-check`、删 2 秒硬截断、零点等结论;新增 `test/countdown.test.mjs`。
43
+ - 组件版本:跨会话 **0.5.7**、跨工作区 **0.2.4**、跨设备 **0.4.4**、自动重启 **0.3.46**、入口 **0.9.3**;`test/` 下 **15 个** `*.test.mjs`,本版 **15/15 全绿**。
44
+
45
+ **上一版(1.0.3)**
46
+
47
+ - **修复:换台机器就重启不了(第二设备验收 B1)** —— 桌面端的 web 端口是动态的,helper / 倒计时窗 / `rearm-watch` 由 mshta 或 WMI 拉起时拿不到 `DSH_WEB_URL`,端口退回 19387 → `waitUp` 永远判失败 → 把刚拉起来的健康实例杀掉再拉一次,一次重启变两次还报错。现在 `trigger` 把 `resolveWebPort()` 写进 `request.webPort`,helper 用 `applyWebPort(req)` 优先取请求里的端口(日志写 `port=19387 (request)`),`busy-check` / `rearm-watch` 认 `--port` 一路透传。顺带修 taskkill 输出在中文系统上的 GBK 乱码。
48
+ - **重构:目录按组件/功能分文件夹(机主指令)** —— `lib/cloud/**` → `functions/cloud-ops/cloud/**`,`lib/web.js` → `components/ui/lib/web.js`;`lib/` 只剩宿主入口 `index.js` 与共享核心 `bus.js` / `logs.js` / `names.js`。`MAINTAINERS.md` 新增「目录归属」表与硬规矩(新功能一律 `functions/<名字>/`,越界即误改)。
49
+ - **跨设备:信封签名 v2 + 一发多投元数据** —— `canonicalEnvelopeV2`(`expiresAt` + `targets` + 版本字面量)、`SUPPORTS_SIG_V2_MIN='1.0.3'` 门控(按目标设备 `_device.json` 的版本决定发 v1 还是 v2)、收端 `v>=2` 严格验签、`DEFAULT_TTL_MS` 7 天(只对新多目标路径)、终态回执 `receipts/<id>.<deviceId>.json`、`seenIds` 去重 2000 条、过期归档 `expired/`;版本探测加 60 秒缓存。旧单目标路径行为不变。
50
+ - **链成员资格与存活解耦** —— `pruneChain` 不再按 presence 年龄剔成员,只删空台账;`dropChainIfOrphan` 改为台账自身 30 天无更新才回收(`ORPHAN_CHAIN_TTL_MS`);`term_unlink all=true` 对「新鲜但无人在线」的台账回 `ok:false` 并说明「成员离线不等于解散」。
51
+ - **presence 三段窗口** —— 0–3 分钟在线;3–9 分钟显示「(离线)」且 `send` 照常入队(回 `{ok:true, offline:true, presenceAgeMs}`);≥9 分钟才回收 presence 文件(`PRESENCE_CLEANUP_MS = PRESENCE_TTL_MS * 3`)。此前清理阈值 60 秒 < 展示阈值 180 秒,「离线」这一档实际不可达。
52
+ - **投递路由 fail-closed + 默认会话** —— 五级解析:显式 `sessionId` → `term_config` 的 `defaultSessionId` → 单会话终端 → 多会话里唯一 `active` 的人工会话 → 其余不挑(留件 + `lastError`,回 `{ok:false, retry:true}`);automation / `im:` 会话永远不参与第 3/4 级兜底(它们进 `sessions()` 可以,但静默吞信比留件更糟)。`term_list` 显示短号与「会话 N 个」,`term_send` / `term_unlink` 接受短号但多命中会报歧义并列候选。
53
+ - **清理:发版物去本机痕迹** —— 倒计时窗兜底路径不再写死维护者本机旧路径(拿不到就空串并报错、不猜路径);`cordis.patch.yml` 安装注释、`core.js` 头注释里的本机路径改占位符;README 示例里的真实 nodeId / session 短号替换为 `<nodeId>` / `session-<短号>…`(`MAINTAINERS.md` 保留真实 id,它是归属注册表且 `lib/logs.js` 依赖它)。
54
+ - 组件版本:跨会话 `0.5.3` → **0.5.4**、跨工作区 `0.1.0` → **0.2.2**、自动重启 `0.3.41` → **0.3.44**;跨设备 `0.4.2`、入口 `0.9.3` 不变。测试从「八套」更正为 **`test/` 下 10 个 `*.test.mjs`**,本版 10/10 全绿。
32
55
 
33
- **本版更新(1.0.1)**
56
+ **上一版(1.0.1)**
34
57
 
35
58
  - 可移植性修复(第二设备验收报的三条阻断,全部在本机复现后修掉):**端口不再写死 19387** —— `DSH_RESTART_PORT` → `DSH_WEB_URL` → `19387` 三级回退,`dsh web` 或换端口的机器不再恒判 unknown;**桌面 App 路径不再写死 `F:\dsh`** —— 依次试 `DSH_RESTART_APP_EXE` → 正在跑的宿主进程 ImagePath → 各盘 `\dsh\DeepSeek Harness.exe` → `%LOCALAPPDATA%\Programs`,一个都找不到就**拒绝执行 kill**(「杀而不起」比不重启更糟);**守卫区分 unknown 与 busy** —— 读不到权威信号时退出码 4 并明说「市场接口与心跳都没读到」,不再误报「其他会话正在跑任务:[]」。
36
59
  - 组件版本:自动重启 `0.3.40` → `0.3.41`;其余组件不动。新增 `test-portability.mjs` 覆盖端口回退与 exe 解析。
@@ -155,13 +178,13 @@ dsh plugin --profile desktop remove dsh-crossterm
155
178
  ```text
156
179
  [跨会话邮件] 收到 1 封
157
180
 
158
- 终端:TS-01 (ts-01-a52535)
181
+ 终端:TS-01 (<nodeId>)
159
182
  工作区:F:\dsh\001X
160
183
  发件人:会话群聊插件
161
184
  主题:转:YOCE_C 已升 0.7.4,cloudRoot 保持空,旧云端目录可整删
162
185
  正文:F:\dsh\001X\crossterm-data\mail\af9c3bc7-….md(用 read 工具打开,1.1 KB)
163
186
 
164
- (回信:调用 term_send,参数 to="ts-01-a52535";消息会投进对方终端当时打开的会话。)
187
+ (回信:调用 term_send,参数 to="<nodeId>";消息会投进对方终端当时打开的会话。)
165
188
  ```
166
189
 
167
190
  跨工作区(同一终端、另一个工作目录里的会话):
@@ -169,7 +192,7 @@ dsh plugin --profile desktop remove dsh-crossterm
169
192
  ```text
170
193
  [跨工作区邮件] 收到 1 封
171
194
 
172
- 终端:TS-01 (ts-01-a52535)
195
+ 终端:TS-01 (<nodeId>)
173
196
  工作区:F:\dsh\001X\other-project
174
197
  发件人:会话群聊插件
175
198
  主题:那批图的尺寸表在附件里
@@ -179,7 +202,7 @@ dsh plugin --profile desktop remove dsh-crossterm
179
202
  附件已保存到本机:
180
203
  - F:\dsh\001X\other-project\crossterm-inbox\20261019-101500\sizes.csv(1.2 KB)
181
204
 
182
- (回信:调用 term_send,参数 to="ts-01-a52535";…)
205
+ (回信:调用 term_send,参数 to="<nodeId>";…)
183
206
  ```
184
207
 
185
208
  跨终端(同一台机器、另一个 DSH 进程):
@@ -201,14 +224,14 @@ dsh plugin --profile desktop remove dsh-crossterm
201
224
  ```text
202
225
  [跨设备邮件] 收到 1 封
203
226
 
204
- 终端:YOCE_C (yoce-c-4fb38e)
227
+ 终端:YOCE_C (<nodeId>)
205
228
  工作区:F:\dsh\project
206
229
  发件人:验证跨终端插件
207
230
  经由:云端中转站
208
231
  主题:旧云端目录已整删(/dsh云盘/crossterm/bus)
209
232
  正文:…\mail\….md(用 read 工具打开,1.1 KB)
210
233
 
211
- (回信:调用 term_send,参数 to="yoce-c-4fb38e";…)
234
+ (回信:调用 term_send,参数 to="<nodeId>";…)
212
235
  ```
213
236
 
214
237
  ### 3.4 四条硬规则
@@ -367,7 +390,7 @@ dsh plugin --profile desktop remove dsh-crossterm
367
390
  | `dsh headless` 等没有 web-api 层的宿主 | **拉取**:消息留在收件箱,对方的 `term_inbox`(或它自己 `drain` 失败后重试)来取,不消耗重试次数、不会被判失败 |
368
391
 
369
392
  **生命周期**:链关系落盘在 `<总线>/chains/<码>.json` 与 `<总线>/links/<nodeId>.json`。
370
- `nodeId` 是**跨重启稳定**的:`slug(显示名) + '-' + sha1(主机名|显示名)` 前 6 位(本机为 `ts-01-a52535`)。
393
+ `nodeId` 是**跨重启稳定**的:`slug(显示名) + '-' + sha1(主机名|显示名)` 前 6 位(本机为 `<终端slug>-<6位哈希>`)。
371
394
  稳定带来两个好处:① 进程重启后仍在同一条链上,不用重新 `term_link`;② 重启前投进本终端信箱的消息不会变成孤儿。
372
395
  代价是**同一台机器开多个终端必须区分显示名**(`DSH_CROSSTERM_NAME` 或 `term_config` 的 `name`),否则它们共用一个信箱。
373
396
 
@@ -516,7 +539,7 @@ dsh/
516
539
  共享盘方式下,谁能写这个目录,谁就能往你的会话里塞话。
517
540
  - 文件落地目录可被 agent 读写,`inboxDir` 建议指向专门目录而非系统目录。
518
541
  - **谁能改这个插件**:看仓库根目录的 [`MAINTAINERS.md`](./MAINTAINERS.md)。当前负责人是
519
- `session-3195b037…`(「插件—互通插件负责人001」,TS-01 / `ts-01-a52535`,自 2026-10-20 19:55),助手是 `session-ccf34a5a…`
542
+ `session-<短号>…`(「插件—互通插件负责人001」,TS-01 / `<nodeId>`,自 2026-10-20 19:55),助手是 `session-<短号>…`
520
543
  (同端多会话2,只能按负责人派活改 `lib/*.js`)。其他会话(QQ 会话、别的终端/窗口)默认只读,
521
544
  可协助测试与报 bug;**要改代码必须经用户明确点名允许**(见工作区 `AGENTS.md` 第 7 条)。
522
545
 
@@ -573,8 +596,8 @@ $env:DSH_CROSSTERM_NAME = 'WEB-2'; dsh --profile crossterm-web --port 19999
573
596
  投递与文件互传在真宿主里实测通过(`F:\dsh\001X\crossterm\e2e-send.mjs`、`e2e-files.mjs` 是对端模拟脚本):
574
597
 
575
598
  ```
576
- delivered e2e-mvceynw5 -> session-6ef6da30-… # 文本
577
- delivered e2e-files-mvceyze6 -> session-6ef6da30-… (+1 files) # 文件
599
+ delivered e2e-mvceynw5 -> session-<短号>-… # 文本
600
+ delivered e2e-files-mvceyze6 -> session-<短号>-… (+1 files) # 文件
578
601
  落地:F:\dsh\001X\crossterm-inbox\20261017-211003\互传样例.csv (41B)
579
602
  ```
580
603
 
@@ -591,12 +614,12 @@ timeout 20s → taskkill DSH → 停机窗口里 offline-send.mjs 投 2 条 →
591
614
  结果:
592
615
 
593
616
  ```
594
- offline-send.log target nodeId = ts-01-a52535
617
+ offline-send.log target nodeId = <nodeId>
595
618
  enqueued 2 messages (1 条文本 + 1 个文件 离线留存样例-222811.txt)
596
619
 
597
- 重启后 ts-01-a52535.jsonl 22:28:16 plugin loaded v0.2.0
598
- 22:28:16 started node=ts-01-a52535 name=TS-01
599
- 22:28:18 delivered 2 msg(s) -> session-ccf34a5a-… (+1 files)
620
+ 重启后 <nodeId>.jsonl 22:28:16 plugin loaded v0.2.0
621
+ 22:28:16 started node=<nodeId> name=TS-01
622
+ 22:28:18 delivered 2 msg(s) -> session-<短号>-… (+1 files)
600
623
  ```
601
624
 
602
625
  - **两条都在**,而且是在启动后第 2 秒投出去的;
@@ -619,7 +642,7 @@ offline-send.log target nodeId = ts-01-a52535
619
642
  结果:
620
643
 
621
644
  ```
622
- 日志 delivered 1 msg(s) -> session-6ef6da30-… (+1 files) (16:32:12)
645
+ 日志 delivered 1 msg(s) -> session-<短号>-… (+1 files) (16:32:12)
623
646
  落地 F:\dsh\001X\crossterm-inbox\20261018-003212\跨机器样例-20261017163211.bin
624
647
  校验 112 B,MD5 3D3782C59C94306CF174313FFF21483C(与发送侧一致)
625
648
  清理 bus\files\xhost-1792254731534\ 投递后自动清空
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "跨设备",
3
3
  "description": "经云端总线,在多台机器的 DSH 之间互发邮件与文件",
4
- "version": "0.4.0"
4
+ "version": "0.4.4"
5
5
  }