dsh-crossterm 0.8.13 → 0.8.14

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,19 @@
1
1
  # dsh-crossterm 变更记录
2
2
 
3
+ ## 0.8.14 — 2026-10-20
4
+
5
+ ### 修「更新按钮看起来没用」(YOCE_C 实测反馈)
6
+
7
+ 上一版起更新链路已经能装成功,但宿主进程里还是旧版本,卡片继续显示「有更新」,
8
+ 再点一次却回「已经是最新」—— 主观上就是点了没用。
9
+
10
+ - `/crossterm/api/status` 新增 `installed`(磁盘上 node_modules 里的真实版本)与
11
+ `needRestart`(磁盘版本 > 进程版本)。
12
+ - `/crossterm/api/check`:磁盘版本已达最新但进程未重启时返回
13
+ `updateAvailable=false, needRestart=true`,不再报「有更新」。
14
+ - `/crossterm/api/update`:已装未重启时提示「已装 X,重启 DSH 后生效(当前进程还在跑 Y)」。
15
+ - 卡片:多一行「待重启」,更新按钮变成不可点的「已装待重启」,检查更新也提示重启后生效。
16
+
3
17
  ## 0.8.13 — 2026-10-20
4
18
 
5
19
  ### 优化:已经是最新就不再重装;没写收件人的信优先投给维护者
package/README.md CHANGED
@@ -28,7 +28,14 @@ A 终端看不到 B 终端里有哪个会话、在干什么,也没法把一条
28
28
  > `locatePkgJson()` 对子路径行返回 undefined),而插件页顶部标题与这一行共用同一份 `locale/zh.json` 的 meta,
29
29
  > 所以它单独做入口(配置卡片 + 自动更新开关),标题带「(自更新)」;卡片头用 `shortTitle` 显示短名「跨端互通」。
30
30
 
31
- **本版更新(0.8.13)**
31
+ **本版更新(0.8.14)**
32
+
33
+ - **「更新」按钮不再看起来没用**:装完新版但还没重启时,卡片多一行「待重启」、
34
+ 更新按钮变成不可点的「已装待重启」,接口 `/check` 改回 `needRestart=true` 而不是继续报「有更新」。
35
+ - 根因是状态表达:宿主进程里还是旧版本号,而磁盘上已经装了新版 —— 现在这两个版本分开显示。
36
+ - 本机是 `link:` 源码版(磁盘版本永远等于源码版本),所以这行只会在 npm 安装的设备上出现。
37
+
38
+ **上一版(0.8.13)**
32
39
 
33
40
  - **已经是最新就不装**:点「更新」时若 npm 最新版 ≤ 当前版本,直接回「已经是最新(0.8.12),不用更新」,
34
41
  不再跑一次 15 秒的安装、也不再重写 `node_modules`。
package/client/client.js CHANGED
@@ -310,6 +310,7 @@ window.__ModuleLoader__.load({
310
310
  try {
311
311
  const data = await call("/crossterm/api/check", "POST");
312
312
  if (!data || !data.ok) setMsg("检查失败:" + ((data && data.message) || "未知错误"));
313
+ else if (data.needRestart) setMsg(`已装 ${data.installed},重启 DSH 后生效(当前进程还在跑 ${data.current})`);
313
314
  else if (data.updateAvailable) setMsg(`发现新版本 ${data.latest}(当前 ${data.current})`);
314
315
  else if (data.ahead) setMsg(`本地 ${data.current} 领先已发布版 ${data.latest}`);
315
316
  else setMsg(`已是最新版本 ${data.current}`);
@@ -446,7 +447,9 @@ window.__ModuleLoader__.load({
446
447
 
447
448
  const version = status ? "v" + status.version : "";
448
449
  const mode = status && status.install ? status.install.mode : "";
449
- const cloud = (status && status.cloud) || {};
450
+ // 0.8.14:磁盘版本 > 进程版本 = 已装好但还没重启,卡片要明说,别再提示「有更新」。
451
+ const installedVer = (status && status.installed) || "";
452
+ const needRestart = Boolean(status && status.needRestart); const cloud = (status && status.cloud) || {};
450
453
  const autoUpdate = (status && status.autoUpdate) || {};
451
454
  const home = (status && status.home) || null;
452
455
  const subtitle = status
@@ -498,6 +501,12 @@ window.__ModuleLoader__.load({
498
501
  )
499
502
  : null,
500
503
  h(FriendlyRow, { label: "当前版本", value: status ? `v${status.version}${mode === "link" ? "(源码 link:)" : ""}` : "" }),
504
+ needRestart
505
+ ? h(FriendlyRow, {
506
+ label: "待重启",
507
+ value: `已装 v${installedVer},重启 DSH 后生效(当前进程还在跑 v${status && status.version})`,
508
+ })
509
+ : null,
501
510
  h(FriendlyRow, {
502
511
  label: "当前终端",
503
512
  value: status ? `${status.node}(${status.nodeId})· ${status.hostname}` : "",
@@ -613,7 +622,16 @@ window.__ModuleLoader__.load({
613
622
  "div",
614
623
  { style: S.actions },
615
624
  h("button", { style: S.btn, onClick: onCheck, disabled: busy !== "" }, busy === "check" ? "检查中…" : "检查更新"),
616
- h("button", { style: S.btnPrimary, onClick: onUpdate, disabled: busy !== "" }, busy === "update" ? "更新中…" : "更新"),
625
+ h(
626
+ "button",
627
+ {
628
+ style: needRestart ? S.btn : S.btnPrimary,
629
+ onClick: needRestart ? undefined : onUpdate,
630
+ disabled: busy !== "" || needRestart,
631
+ title: needRestart ? `已装 v${installedVer},重启 DSH 后生效` : "检查并安装最新版本",
632
+ },
633
+ busy === "update" ? "更新中…" : needRestart ? "已装待重启" : "更新",
634
+ ),
617
635
  h("button", { style: S.btn, onClick: () => void loadDevices(), disabled: Boolean(devices && devices.loading) }, devices && devices.loading ? "读取中…" : "刷新设备"),
618
636
  h("button", { style: S.btn, onClick: onRelayCloud, disabled: busy !== "" }, busy === "relay" ? "收信中…" : "收信"),
619
637
  h("button", { style: S.btn, onClick: onBackupNow, disabled: busy !== "" }, busy === "backup" ? "备份中…" : "备份会话"),
package/lib/bus.js CHANGED
@@ -23,7 +23,7 @@ import path from 'node:path'
23
23
  import crypto from 'node:crypto'
24
24
  import { rotateIfNeeded } from './logs.js'
25
25
 
26
- export const VERSION = '0.8.13'
26
+ export const VERSION = '0.8.14'
27
27
 
28
28
  /** DSH 的家目录:与宿主其它状态(sessions / profiles)同一个根,多终端天然共享 */
29
29
  export function dshHome() {
package/lib/web.js CHANGED
@@ -502,6 +502,10 @@ function statusOf(term) {
502
502
  const cfg = term.cfg || {}
503
503
  const info = profileInfo(cfg.busDir)
504
504
  const mode = installMode(info && info.spec)
505
+ // 0.8.14:磁盘上装着的版本可能比进程内存里的 VERSION 新(更新完还没重启),
506
+ // 这个状态必须能表达出来,否则卡片一直显示「有更新」、再点又说「已是最新」。
507
+ const onDisk = installedVersion(info) || ''
508
+ const pendingRestart = Boolean(onDisk && compareVersion(onDisk, VERSION) > 0)
505
509
  const chains = []
506
510
  try {
507
511
  for (const link of term.links.values()) if (!chains.includes(link.code)) chains.push(link.code)
@@ -516,6 +520,8 @@ function statusOf(term) {
516
520
  return {
517
521
  ok: true,
518
522
  version: VERSION,
523
+ installed: onDisk,
524
+ needRestart: pendingRestart,
519
525
  node: cfg.name || '',
520
526
  nodeId: term.nodeId || '',
521
527
  hostname: os.hostname(),
@@ -847,9 +853,15 @@ export function registerWeb(ctx, term, log) {
847
853
  const latest = await latestVersion()
848
854
  // 只有 npm 上的版本「更新」才算有更新:本地源码版常领先于已发布版,
849
855
  // 用 !== 会把 0.3.32 说成「发现新版本 0.3.31」。
850
- const updateAvailable = compareVersion(latest, VERSION) > 0
851
- const ahead = compareVersion(VERSION, latest) > 0
852
- json(res, 200, { ok: true, current: VERSION, latest, updateAvailable, ahead })
856
+ const info = profileInfo(term.cfg && term.cfg.busDir)
857
+ const installed = installedVersion(info) || ''
858
+ // 磁盘上已经装上新版、只是还没重启时:不再报「有更新」,改报「待重启」,
859
+ // 否则用户会以为「点了更新没用」。
860
+ const needRestart = Boolean(installed && compareVersion(installed, latest) >= 0 && compareVersion(installed, VERSION) > 0)
861
+ const base = installed && compareVersion(installed, VERSION) > 0 ? installed : VERSION
862
+ const updateAvailable = !needRestart && compareVersion(latest, base) > 0
863
+ const ahead = !needRestart && compareVersion(VERSION, latest) > 0
864
+ json(res, 200, { ok: true, current: VERSION, installed, latest, updateAvailable, ahead, needRestart })
853
865
  } catch (error) {
854
866
  json(res, 200, { ok: false, message: String((error && error.message) || error) })
855
867
  }
@@ -999,7 +1011,14 @@ export function registerWeb(ctx, term, log) {
999
1011
  const steps = (result.steps || []).map((s) => ({ label: s.label, code: s.code, ms: s.ms, out: s.out }))
1000
1012
  let message
1001
1013
  if (result.ok && result.updated) message = `已更新到 ${installed},重启 DSH 后生效`
1002
- else if (result.ok) message = `已经是最新(${installed || latest}),不用更新`
1014
+ else if (result.ok) {
1015
+ // 已经装上了新版、只是进程还是旧的:这时候要说清「重启后生效」,
1016
+ // 不能只说「已经是最新」,否则用户会觉得点了没反应。
1017
+ const pending = Boolean(installed && compareVersion(installed, VERSION) > 0)
1018
+ message = pending
1019
+ ? `已装 ${installed},重启 DSH 后生效(当前进程还在跑 ${VERSION})`
1020
+ : `已经是最新(${installed || latest}),不用更新`
1021
+ }
1003
1022
  else if (result.downgraded) {
1004
1023
  message = result.rollback && result.rollback.ok
1005
1024
  ? `更新把版本装低了(原 ${result.before},装成更旧版本),已恢复到 ${installed};请重试`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-crossterm",
3
- "version": "0.8.13",
3
+ "version": "0.8.14",
4
4
  "description": "跨端互通:同一 DSH 账号下的会话、终端、设备之间互通 —— 特征码链、内联邮件、云端中转站与自动备份",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",