dsh-crossterm 0.6.0 → 0.6.1

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
@@ -1,5 +1,22 @@
1
1
  # dsh-crossterm 变更记录
2
2
 
3
+ ## 0.6.1 — 2026-10-19
4
+
5
+ ### 修:旧布局缓存挡住了新增的云端文档
6
+ - 症状:0.5.0 / 0.6.0 升级后,云盘里看不到 `_规则-账号.md`、`_规则-设备.md`、`inbox/_说明.md`,
7
+ `status.home.layout.rulesVersion` 也还是老值 —— 文档写着「命中缓存会自动补写」,实际没补。
8
+ - 根因:`ensureLayout()` 命中缓存时直接沿用 `cached.paths`,而路径是**算出来的**(`buildPaths()`)。
9
+ 旧版缓存里没有 `accountRulesFile` / `deviceRulesFile` / `inboxReadmeFile` 三个字段,
10
+ `ensureRules()` 拿到 `undefined` 就跳过,随后仍把 `rulesVersion` 标成最新 —— 等于自己认定「已写」。
11
+ - 修法(`lib/cloud/layout.js`):
12
+ - 命中缓存时用 `buildPaths()` 按 `home / account / deviceId` **重新算路径**,缓存只负责存 `account`、`role`、`ensuredAt` 这类状态;
13
+ - `ensureRules()` 返回 `{ written, failed }`,路径缺失或上传失败都计入 `failed`;
14
+ - 只有 `failed === 0` 才把 `rulesVersion` 抬到 `RULES_VERSION` 并写回缓存,否则保持 0,下次 `ensure` 继续重试;
15
+ - 缓存新增 `home` 字段,非默认 `cloudHome` 也能重建路径。
16
+ - 探针 `F:\dsh\001X\crossterm-work\probe-layout-stale-cache.mjs`:**12 项全 PASS**
17
+ (旧缓存→重算路径→补写三份文档→写回新缓存;第二次不重复上传;上传失败时 `rulesVersion` 不冒充最新)。
18
+ 回归:`probe-layout-rules.mjs` 26 PASS、`probe-cloud-home.mjs` 42 PASS、`probe-inbox-readme.mjs` 17 PASS。
19
+
3
20
  ## 0.6.0 — 2026-10-19
4
21
 
5
22
  ### 共享收件箱里也放一份说明
package/README.md CHANGED
@@ -23,15 +23,18 @@ A 终端看不到 B 终端里有哪个会话、在干什么,也没法把一条
23
23
  | 跨设备 | 经云端总线,在多台机器的 DSH 之间互发邮件与文件 | `term_cloud` |
24
24
  | 跨账号 | 预留位:后续用于跨账号互通,当前不挂工具 | — |
25
25
 
26
- **本版更新(0.6.0)**
27
-
28
- - **共享收件箱里也带一份说明**:`/dsh/<账号>/shared/<设备>/inbox/_说明.md` 由插件自动生成,
29
- 打开云盘就知道这个收件箱是干什么的 —— 谁写谁取、信封 JSON 格式、`_` 开头的系统文件会被跳过、
30
- 别手工删改信封、收件人找不到就留件不消费、附件怎么存。
31
- - `lib/cloud/relay.js` 新增 `isEnvelopeFile()`:只把 `.json` 且不以 `_` 开头、非 `.tmp`/隐藏文件当信封,
32
- 所以说明文件不会被反复解析失败、也不影响正常投递与 `done/` 归档。
33
- - `RULES_VERSION` 升到 2,升级后命中旧缓存会自动补写这份新说明。
34
- - 探针:`probe-layout-rules.mjs` 26 项、`probe-inbox-readme.mjs` 17 项、`probe-cloud-home.mjs` 42 项全 PASS。
26
+ **本版更新(0.6.1)**
27
+
28
+ - **修:旧布局缓存把新增的云端文档挡住了**。0.5.0 / 0.6.0 说「命中旧缓存会自动补写」,实际没补 ——
29
+ 云盘里根本没有 `_规则-账号.md`、`_规则-设备.md`、`inbox/_说明.md`。
30
+ - 根因:`ensureLayout()` 命中缓存时直接沿用 `cached.paths`,而路径是 `buildPaths()` **算出来的**;
31
+ 旧缓存没有 `accountRulesFile` / `deviceRulesFile` / `inboxReadmeFile` 三个字段,`ensureRules()` 拿到 `undefined`
32
+ 就跳过,还照样把 `rulesVersion` 标成最新。
33
+ - 修法:命中缓存时按 `home / account / deviceId` 重新算路径;`ensureRules()` 返回 `{ written, failed }`,
34
+ **只有 `failed === 0` 才把 `rulesVersion` 抬上去**,否则保持 0 下次继续重试。
35
+ - 探针 `probe-layout-stale-cache.mjs` 12 项全 PASS(旧缓存→重算→补写三份文档→写回新缓存;
36
+ 第二次不重复上传;上传失败时不冒充最新),回归 26 + 42 + 17 项全 PASS。
37
+ - 上一版(0.6.0)的内容仍在:共享收件箱里的 `_说明.md`、中转站 `isEnvelopeFile()` 只认 `.json` 信封。
35
38
 
36
39
  ## 一、安装与卸载
37
40
 
package/lib/bus.js CHANGED
@@ -22,7 +22,7 @@ import os from 'node:os'
22
22
  import path from 'node:path'
23
23
  import crypto from 'node:crypto'
24
24
 
25
- export const VERSION = '0.6.0'
25
+ export const VERSION = '0.6.1'
26
26
 
27
27
  /** DSH 的家目录:与宿主其它状态(sessions / profiles)同一个根,多终端天然共享 */
28
28
  export function dshHome() {
@@ -242,17 +242,22 @@ export async function ensureRules(pan, paths, info = {}) {
242
242
  ]
243
243
  if (paths.inboxReadmeFile) docs.push([paths.inboxReadmeFile, inboxReadmeMarkdown({ account, device, version })])
244
244
  const written = []
245
+ let failed = 0
245
246
  for (const [remote, text] of docs) {
246
247
  try {
248
+ if (!remote) { failed += 1; continue }
247
249
  if (await pan.exists(remote)) continue
248
250
  await pan.putBuffer(Buffer.from(text, 'utf8'), remote)
249
251
  written.push(remote)
250
252
  log(`云端规则已写入:${remote}`)
251
253
  } catch (e) {
254
+ failed += 1
252
255
  log(`写云端规则失败 ${remote}:${(e && e.message) || String(e)}`)
253
256
  }
254
257
  }
255
- return { written }
258
+ // 收件箱说明算一份必须存在的文档:缺少它的旧路径缓存不能冒充「规则已是最新」
259
+ if (!paths.inboxReadmeFile) failed += 1
260
+ return { written, failed }
256
261
  }
257
262
 
258
263
  /**
@@ -263,18 +268,29 @@ export async function ensureLayout({ pan, cfg = {}, device = {}, cacheFile, forc
263
268
  const nodeId = sanitize(device.nodeId || 'device')
264
269
  const cached = readCache(cacheFile)
265
270
  if (!force && cached && cached.deviceId === nodeId && Date.now() - Number(cached.at || 0) < CACHE_MS && cached.paths) {
271
+ // 路径是「算出来的」而不是状态:升级后新增的路径(规则文档、收件箱说明)必须重新算,
272
+ // 直接沿用旧缓存的 paths 会让 ensureRules 拿到 undefined、规则永远写不出去。
273
+ const paths = buildPaths({
274
+ home: cached.home || cfg.cloudHome || DEFAULT_HOME,
275
+ account: cached.account || cached.paths.account || 'default',
276
+ deviceId: nodeId,
277
+ })
278
+ const state = { ...cached, home: cached.home || cfg.cloudHome || DEFAULT_HOME, paths }
266
279
  if (Number(cached.rulesVersion || 0) < RULES_VERSION) {
267
- await ensureRules(pan, cached.paths, {
280
+ const r = await ensureRules(pan, paths, {
268
281
  log,
269
- account: cached.account,
282
+ account: state.account,
270
283
  device: nodeId,
271
- role: cached.role,
284
+ role: state.role,
272
285
  version: device.version || '',
273
286
  })
274
- cached.rulesVersion = RULES_VERSION
275
- writeCache(cacheFile, cached)
287
+ if (!r.failed) {
288
+ state.rulesVersion = RULES_VERSION
289
+ state.rulesWritten = r.written
290
+ writeCache(cacheFile, state)
291
+ }
276
292
  }
277
- return { ...cached, cached: true, ensuredAt: cached.at }
293
+ return { ...state, cached: true, ensuredAt: cached.at }
278
294
  }
279
295
 
280
296
  const who = await resolveAccount(pan, cfg, log)
@@ -358,9 +374,12 @@ export async function ensureLayout({ pan, cfg = {}, device = {}, cacheFile, forc
358
374
  deviceId: nodeId,
359
375
  account: who.account,
360
376
  accountSource: who.source,
377
+ home: cfg.cloudHome || DEFAULT_HOME,
361
378
  role,
362
- rulesVersion: RULES_VERSION,
379
+ // 有文档没写成功就不要把版本号标上去,下次 ensure 还会重试
380
+ rulesVersion: rules.failed ? 0 : RULES_VERSION,
363
381
  rulesWritten: rules.written,
382
+ rulesFailed: rules.failed,
364
383
  paths,
365
384
  created,
366
385
  accountInfo,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-crossterm",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "DSH 跨终端互联:同一账号下的多个 DSH 终端/进程互发邮件与文件(主题内联、正文落盘,收件方自己读)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",