dsh-ssh-tui 0.8.1 → 0.8.2

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 (93) hide show
  1. package/CHANGELOG.md +158 -0
  2. package/README.en.md +118 -15
  3. package/README.md +78 -13
  4. package/docs/release-notes-0.7.4.md +91 -0
  5. package/docs/release-notes-0.8.0.md +56 -0
  6. package/docs/release-notes-0.8.1.md +79 -0
  7. package/docs/release-notes-0.8.2-rc.1.md +159 -0
  8. package/docs/release-notes-0.8.2.md +62 -0
  9. package/docs/screenshots/reconnect.gif +0 -0
  10. package/docs/terminals.md +23 -0
  11. package/docs/windows.md +13 -0
  12. package/lib/approval-state.js +148 -0
  13. package/lib/approval-state.js.map +1 -0
  14. package/lib/auth-failure.js +38 -0
  15. package/lib/auth-failure.js.map +1 -1
  16. package/lib/color-depth.js +26 -0
  17. package/lib/color-depth.js.map +1 -1
  18. package/lib/copy-text.js +30 -8
  19. package/lib/copy-text.js.map +1 -1
  20. package/lib/dialogs.js +81 -0
  21. package/lib/dialogs.js.map +1 -1
  22. package/lib/display-sock.js +138 -23
  23. package/lib/display-sock.js.map +1 -1
  24. package/lib/footer-accents.js +28 -0
  25. package/lib/footer-accents.js.map +1 -0
  26. package/lib/footer-budget.js +328 -0
  27. package/lib/footer-budget.js.map +1 -0
  28. package/lib/footer-format.js +433 -0
  29. package/lib/footer-format.js.map +1 -0
  30. package/lib/footer-layout.js +407 -0
  31. package/lib/footer-layout.js.map +1 -0
  32. package/lib/footer-meters.js +185 -0
  33. package/lib/footer-meters.js.map +1 -0
  34. package/lib/footer.js +19 -558
  35. package/lib/footer.js.map +1 -1
  36. package/lib/i18n/en.js +65 -5
  37. package/lib/i18n/en.js.map +1 -1
  38. package/lib/i18n/zh.js +67 -5
  39. package/lib/i18n/zh.js.map +1 -1
  40. package/lib/paint.js +53 -8
  41. package/lib/paint.js.map +1 -1
  42. package/lib/plan-projection.js +291 -0
  43. package/lib/plan-projection.js.map +1 -0
  44. package/lib/plan.js +50 -0
  45. package/lib/plan.js.map +1 -1
  46. package/lib/question-state.js +83 -0
  47. package/lib/question-state.js.map +1 -0
  48. package/lib/representation.js +283 -0
  49. package/lib/representation.js.map +1 -0
  50. package/lib/screen.js +105 -0
  51. package/lib/screen.js.map +1 -0
  52. package/lib/selection.js +5 -3
  53. package/lib/selection.js.map +1 -1
  54. package/lib/startup.js +49 -0
  55. package/lib/startup.js.map +1 -1
  56. package/lib/stats.js +101 -3
  57. package/lib/stats.js.map +1 -1
  58. package/lib/term-text.js +154 -55
  59. package/lib/term-text.js.map +1 -1
  60. package/lib/theme.js +20 -3
  61. package/lib/theme.js.map +1 -1
  62. package/lib/throughput.js +305 -0
  63. package/lib/throughput.js.map +1 -0
  64. package/lib/tool-present.js +57 -7
  65. package/lib/tool-present.js.map +1 -1
  66. package/lib/tui.js +3776 -1105
  67. package/lib/tui.js.map +1 -1
  68. package/lib/types/approval-state.d.ts +75 -0
  69. package/lib/types/auth-failure.d.ts +25 -0
  70. package/lib/types/color-depth.d.ts +21 -0
  71. package/lib/types/copy-text.d.ts +18 -1
  72. package/lib/types/dialogs.d.ts +111 -1
  73. package/lib/types/display-sock.d.ts +15 -0
  74. package/lib/types/footer-accents.d.ts +22 -0
  75. package/lib/types/footer-budget.d.ts +204 -0
  76. package/lib/types/footer-format.d.ts +221 -0
  77. package/lib/types/footer-layout.d.ts +292 -0
  78. package/lib/types/footer-meters.d.ts +151 -0
  79. package/lib/types/footer.d.ts +19 -269
  80. package/lib/types/paint.d.ts +48 -0
  81. package/lib/types/plan-projection.d.ts +162 -0
  82. package/lib/types/plan.d.ts +13 -0
  83. package/lib/types/question-state.d.ts +96 -0
  84. package/lib/types/representation.d.ts +498 -0
  85. package/lib/types/screen.d.ts +126 -0
  86. package/lib/types/startup.d.ts +24 -0
  87. package/lib/types/stats.d.ts +64 -1
  88. package/lib/types/term-text.d.ts +45 -1
  89. package/lib/types/throughput.d.ts +163 -0
  90. package/lib/types/tool-present.d.ts +41 -0
  91. package/lib/types/transcript-types.d.ts +63 -2
  92. package/lib/types/tui.d.ts +862 -11
  93. package/package.json +16 -6
package/CHANGELOG.md ADDED
@@ -0,0 +1,158 @@
1
+ # CHANGELOG
2
+
3
+ 面向用户的变更记录。每个版本的**发版说明**(含头等事、兼容矩阵与已知问题)在
4
+ `docs/release-notes-<version>.md`;本文件只记一行一条的变更,按版本倒序。
5
+
6
+ 变更分类:**Added** / **Changed** / **Fixed** / **Removed** / **Docs** / **Internal**。
7
+
8
+ > 本文件随包发布(`package.json` → `files`),发版说明也在包里(`docs/release-notes-*.md`);
9
+ > 指向 `docs/decisions/`、`docs/plans/`、`docs/checkpoints.md` 的链接是**仓库内**文档,只有 GitHub 上可读。
10
+
11
+ ---
12
+
13
+ ## 0.8.2 — 2026-10-05(`latest` + `next`)
14
+
15
+ 发版说明:[`docs/release-notes-0.8.2.md`](release-notes-0.8.2.md);候选版说明保留在
16
+ [`docs/release-notes-0.8.2-rc.1.md`](release-notes-0.8.2-rc.1.md)。
17
+ 本版由 **0.8.2-rc.1**(2026-10-04,只上 `next`)沉淀一周后转正,**内容与候选版相同**,
18
+ 下列变更即候选版以来累积的全部变更。
19
+
20
+ > `dsh-ssh-tui` is now presented as DSH Relay. The npm package name and the install command
21
+ > have not changed. —— `dsh-ssh-tui` 现在以 DSH Relay 为产品名展示;包名与安装命令没有变化。
22
+
23
+ B2("what deserves to exist")的六个阶段 B2.1 – B2.6 已完成并通过最终审计,本节汇总用户能看到的
24
+ 部分。详细证据与冻结记录见 [`docs/checkpoints.md`](checkpoints.md) 与
25
+ [`docs/decisions/b2-architecture-decisions.md`](decisions/b2-architecture-decisions.md) §20。
26
+
27
+ ### Added
28
+
29
+ - **报告是 Screen**:`/status`、`/help`、`/doctor`、`/subagents`、`/diag` 等报告不再写进转写、
30
+ 不再改变历史窗口,翻页只重画正文区,Esc 回到原处。
31
+ - **计划 artifact 投影**:计划模式下的待办与评审从会话日志折叠出来;一份计划一行引用、一个 dock、
32
+ 一个评审 Surface,resume 后身份一致。
33
+ - **工具卡片的审批状态**:卡片自己拥有 `waiting / approved / rejected / unknown`(以及来源:
34
+ live / policy / durable / inferred);不再有独立的审批行。
35
+ - **首启向导是 Screen**:9 步向导不再是"借工作区 composer 的对话框",中途 detach/reattach 不丢步骤,
36
+ 也不再清空读者已经打了一半的草稿。
37
+ - **`npm run freeze`**:一条命令跑完 B2 的八条冻结不变量与七条退役路径扫描(CI 在 Linux 与
38
+ Windows 两条腿上各跑一次)。
39
+ - **`npm run bench`**:五个动作(流式 delta / 等待时钟 / 报告翻页 / 菜单移动 / 向导打字)的性能基线,
40
+ 并断言这五个动作都不整屏重画。
41
+
42
+ ### Changed
43
+
44
+ - **反馈分流**:控制面的确认去页脚(echo,最低优先级,直到下一次输入),警告/失败去身份行(notice),
45
+ 只有会改变历史的事实才进转写([`docs/checkpoints.md`](checkpoints.md) B2.3b)。
46
+ - **`/copy` 语法**:`/copy` 或 `/copy reply` 复制模型最后一条回复(默认);`/copy highlight` 复制焦点卡片;
47
+ `/copy error` 复制最新的错误/诊断行。复制键保留"取不到焦点就退回回复"的语义。
48
+ - **工作区在方向键后的停留**:resume 进来不再从窗口中间开始滚动,直接停在最新内容。
49
+
50
+ ### Fixed
51
+
52
+ - **工具卡内容有多行代码时会窜到输入区**。`bash` 的摘要就是命令行原文,所以 heredoc 会带着换行进入
53
+ 卡片标题行;而单行裁剪器(`truncateToWidth`)保留 LF/CR,于是一"行"里含多个换行 —— 终端会因此换行,
54
+ 而按绝对行号定位的帧随后把下面每一行都写错位,卡片内容落到输入框和页脚上,还会出现两段文字拼在同一行。
55
+ 实测:31 行的一帧里有 11 行含裸换行;修复后为 0。
56
+ - **Windows Terminal 下输入法的预输入字符不再在处理中卡片那一行的最右边闪烁**。终端把 IME 预输入画在
57
+ 光标处,而写一行会把光标留在该行文本末尾;跑一轮时实时区域每 tick 重画、且位于输入框上方,于是光标停在
58
+ 处理中卡片上、预输入就闪在那里。现在除光标自己所在行外,每写一行都把光标交还输入框。
59
+ - **命令提示不再一直占着位置**:确认("已复制 412 字")与警告(命令失败)显示 6 秒后自动让位、恢复原来的
60
+ 遥测行(提交仍会立即清掉);**"将在下个步骤生效"的排队提示例外** —— 它显示到那条消息真正被提交为止,
61
+ 因为它是"你有话在排队"的唯一标记。
62
+ - **全新安装不再跳过配置向导**。前端拉起后台 Host 时永远带 `--resume=<id>`(连它刚生成的新 id 也带),
63
+ 而首启判定把 `resume` 当成"这台机器配置过",于是**新机器永远不会自动出现向导**,也没有任何提示。
64
+ 判定现在只看凭据存不存在。
65
+ - **裸 400 现在会说明它是什么**:网关拒收的是请求本身(不是 key),并给出这一族的上游记录与绕法
66
+ (`/effort off` 或换 messages 风味路线);**不自动重试**(实测这类失败成串出现,重发由读者决定)。
67
+ - 流式输出不再每个 tick 整屏重画(B2.2:live 区是投影,不是转写行)。
68
+ - 高亮的工具卡片不再把内容画进相邻卡片;处理中卡片遇到长内容不再折断成一行。
69
+ - 计划卡不再吃掉输入法的预输入字符(dock 高度不得超过工作区一半,光标行永远在屏)。
70
+ - resume 的历史回放不再逐帧重画(20k 事件从 51 帧 / 1.2 s 降到一次重建)。
71
+ - 部分命令执行后没有任何提示(页脚放不下的确认现在退到身份行显示)。
72
+ - 待办未收尾的提醒不再伪装成一条"计划转写引用"(B2 最终审计:`plan-row` → `plan-notice`)。
73
+ - **resume 进入时不再有一段黑屏(1–3 秒)**:前端先在备用屏打出"正在载入历史会话…",relay 接入时
74
+ 宿主又写了一次 `?1049h` —— 终端已在备用屏时这个序列会**清屏**,而回放期间按 B2.4 不合成任何帧,
75
+ 于是读者面对的是一块空白,直到日志重建完(实测 20k 事件:9.1 s 清屏、12.6 s 首帧,中间 3.5 s 全黑)。
76
+ 这条"进屏"序列现在搭在第一帧上发出,与它要清出来的内容同一个 tick;没有东西要等的 reattach 行为不变。
77
+ (上一版试过相反的做法 —— 加载期间由宿主补画一行 —— 结果写进了 relay 的测速窗口,那版已回退。)
78
+ - **思考卡片的文字不再发白**:`reasoning` 角色是 `2;3`,只有属性 —— "暗"完全靠 faint,不实现 faint 的
79
+ 终端(报告的 256 色终端)就退回**默认前景色**,与旁边的回复一样亮。四个调色板现在各自给出 muted 颜色
80
+ (default `90`,catppuccin / gruvbox 用各自 `md-muted` 的色,mono 仍然只用属性),属性保留给支持的终端。
81
+ - **状态行不再把"绘制节奏"当成"链路延迟"**:终端不回 `CSI 6n` 时(现场 `/diag`:Linux + xterm-256color
82
+ + SSH,`探测 未知`),这一格显示的是**回退的绘制节奏** 160ms,读者无法分辨它和真实延迟。现在改为
83
+ `SSH ○○○○ 未测` / `n/a`;测到过的链路照旧显示测量值,绘制节奏仍留在 `/diag` 那一行(写明是"绘制")。
84
+ - **`q` / `Q` 真的能关掉报告**:`inspectClosesOn` 一直声明接受它们("全屏报告理应响应 q"),
85
+ 但可打印字符的提示分支在它之前返回,于是这两个键只会打印"这是报告视图,按 Esc 返回"。
86
+
87
+ ### Docs
88
+
89
+ - [`docs/release.md`](release.md) 新增"发布前人工门槛":Windows 实机清单、fresh-home 首启探针命令、
90
+ 以及 `npm run freeze` / `npm run bench` 两条发版自检。
91
+ - [`docs/decisions/b2-architecture-decisions.md`](decisions/b2-architecture-decisions.md) §20 记录 B2 冻结:
92
+ 八条不变量、七条退役路径、以及"表示的分类必须与它包裹的行一致"这条规则。
93
+
94
+ ### Internal
95
+
96
+ - **链路验收补齐"带历史的 resume"**:`scripts/tui-rtt-probe.mjs` 现在有两个阶段——新会话(先慢后快的
97
+ 链路)与**带历史的 resume**(探针自己写一份 20k 事件的持久化日志,回放期间不合成帧)。后者同时断言
98
+ 三件事:读者画面上出现过加载行之后**不得再变空**、转写落地、chip 落在真实测量值而不是 `○○○○ 160ms`
99
+ 占位。两条断言都做过变异校验(改回"接入即写"报 4 秒黑屏;关掉 RTT 应用报 chip 停在占位)。
100
+ - **`verify-batch.mjs` 不再把 SKIP 说成 PASS**:有步骤跳过时收尾为 `RESULT: INCOMPLETE`(exit 2),
101
+ 与 `FAIL`(exit 1)分开;`link` 已加入验收步骤表(`--only link` 可单跑)。
102
+ - 表示管线:每条转写行都带 durability / class / destination;审计对未分类行、未知 destination、
103
+ live 源行与重复 primary 表示各自计数,四个数字都必须为 0。
104
+ - 退役并在 CI 上禁止复活:`dedicated` Surface 角色、onboarding 的对话框渲染路径、
105
+ `upsertPlanRow`(计划行的第二个写入者)、`ask_user_question` 的通用工具卡。
106
+ - **Windows 实机轮(2026-10-03)修掉五处探针缺陷,产品代码未改**:`verify-batch` 在 Windows 上
107
+ 因 `spawn('npm')` 必然 `ENOENT`(改用 `$npm_execpath`);`tui-route-probe` 的 `/tmp` 字面量
108
+ (改 `os.tmpdir()`)与目录符号链接(Windows 改 junction);`footer-windows-probe` 补了**未测量
109
+ 链路**档案并逐格断言时长槽(`未测` 而非 `160ms`);`tui-mock-probe` 的鼠标坐标改由真实终端网格
110
+ 给出(此前从字节流解析 `ESC[<row>;1H`,ConPTY 下拼出伪行,拖选看起来偏了两格——**是探针缺陷,
111
+ 不是选择缺陷**)。
112
+ - **新增 `scripts/tui-unmeasured-probe.mjs`**(管道父进程,进 `verify-batch` 与 CI):ConPTY 自己回
113
+ `CSI 6n`,所以"终端不回 DSR"这一格在 Windows 本机摸不到,而这个形状正是读者报过两次的那个。两条腿
114
+ 分别断言 `SSH ○○○○ 未测`(绘制节奏只留在 `/diag`)与 `SSH ●●●● <n>ms`。
115
+ - **ConPTY 盲区改为显式 `SKIP:`**(不再是"读错就判红"):`tui-cut-probe` 的静默半场、
116
+ `tui-rtt-probe` 的慢链路半场、`tui-setup-probe`/`--line-mode` 的字节形状检查。SKIP 仍收尾
117
+ `INCOMPLETE`。真实 home 未配置时(会进配置向导)同样 `SKIP:` 而不是超时报红;
118
+ `verify-batch --home <dir>` 可把读 profile 的三步指向自建 home。
119
+
120
+ ### 已知问题
121
+
122
+ - **0.8.2-rc.1 → 0.8.2 无代码变更**:转正只改版本号与发版材料,包内 `lib/` 与候选版逐字节相同
123
+ (候选版沉淀期间未收到新的回归报告)。
124
+ - Windows / ConPTY **实机人工验收已完成**(2026-10-03,维护者逐条走过 12 项清单,结论见
125
+ [`docs/checkpoints.md`](checkpoints.md)「Windows 实机轮」):可自动化的部分由 `verify-batch --home
126
+ <dir>` 覆盖(12 PASS · 2 SKIP · 0 FAIL),**没有自动化、只能人眼的两目(输入法组合、多行中文粘贴)
127
+ 已确认通过**。两处 `SKIP` 是覆盖边界、不是通过:ConPTY 自己回 `CSI 6n`,"慢链路"与"静默但没断连"
128
+ 这两个形状它扮演不了(这两个形状在 Linux 腿上真跑)。
129
+ - fresh-home 首启有两条探针覆盖(`scripts/tui-setup-probe.mjs` 断言向导自动出现 + 第二次启动不再出现;
130
+ `scripts/probe-onboarding.mjs` 供工作区探针识别"这是首启"并 `SKIP:` 而不是误报),命令在
131
+ [`docs/release.md`](release.md)。只读 pnpm store 的沙箱建不出 home,那是环境限制,不是覆盖缺口。
132
+ - ~~`scripts/tui-drop-probe.mjs` 约 1/7 概率因自身的 `/status` 重试竞态误报~~ —— **本版已修**:
133
+ 改为一次请求在飞、按每次请求的输出判定、并按"当前是否真有 Screen"决定关闭,连跑 20 次无失败。
134
+ - 打包:`npm pack --dry-run` 实测 **243 个文件 / 1.4 MB(tarball)/ 解包 4.2 MB**,CHANGELOG 随包发布;
135
+ 无凭据、无 home、无 sqlite、无 scratch。
136
+
137
+ ---
138
+
139
+ ## 0.8.1 — 2026-09-30(`latest` + `next`)
140
+
141
+ 发版说明:[`docs/release-notes-0.8.1.md`](release-notes-0.8.1.md)。
142
+
143
+ ### Fixed
144
+
145
+ - 没有终端的进程(桌面版 Electron-as-Node、管道、cron)不再抛错退出,而是保持惰性:不取锁、
146
+ 不装定时器、不查更新,记一行说明,退出码 0。
147
+
148
+ ### Added
149
+
150
+ - `DSH_TUI_DISPLAY=stdio`(或 `--display stdio`):宿主自带终端控件时可以把 TUI 当子进程用,
151
+ 画面走 stdout、按键走 stdin、尺寸由 `CSI 8 ; rows ; cols t` 报告。
152
+
153
+ ---
154
+
155
+ ## 0.8.0 — 2026-09-29(`next`,随后提升为 `latest`)
156
+
157
+ 发版说明:[`docs/release-notes-0.8.0.md`](release-notes-0.8.0.md)。基线切到上游 `0.2.0-rc` 线
158
+ (桌面版 Harness 的基线),兼容窗口与 CI 腿随之上移。
package/README.en.md CHANGED
@@ -1,17 +1,52 @@
1
- # dsh-ssh-tui
1
+ # DSH Relay
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/dsh-ssh-tui?style=flat-square&color=4b6fff)](https://www.npmjs.com/package/dsh-ssh-tui)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/dsh-ssh-tui?style=flat-square)](https://www.npmjs.com/package/dsh-ssh-tui)
5
5
  [![CI](https://github.com/cyjyyd/dsh-ssh-tui/actions/workflows/ci.yml/badge.svg)](https://github.com/cyjyyd/dsh-ssh-tui/actions/workflows/ci.yml)
6
6
  [![dshfind](https://dshfind.com/api/badge/cyjyyd/dsh-ssh-tui)](https://dshfind.com/en/plugins/cyjyyd/dsh-ssh-tui?ref=badge)
7
7
 
8
- A DeepSeek Harness terminal for jump hosts, headless servers, and high-latency
9
- SSH. Plain ANSI and incremental redraws. No browser required. **When the SSH
10
- session drops mid-turn, the Host keeps the turn; reconnecting with the same
11
- command attaches back to it, so the session is not lost.**
8
+ **A resilient terminal workspace for DeepSeek Harness.**
9
+ remote-first · reconnectable · in your terminal
10
+
11
+ **SSH drops. The session survives.**
12
+ **Reconnect. Reattach. Continue.**
13
+
14
+ That is the whole idea behind the name: the *display* can disappear without the
15
+ *session* disappearing. The host process stays up, and the same command attaches
16
+ back to the same session — transcript, plan cards, approvals, queued messages, all
17
+ where you left them. It is a remote-first terminal workspace, not just an
18
+ SSH-flavoured screen.
19
+
20
+ > **What happens to the turn that was running? That depends on `/disconnect`:**
21
+ > the default `pause` stops the in-flight turn when the display goes away and keeps
22
+ > the session — you reconnect and carry on; `continue` lets that turn keep running
23
+ > in the background and finish without you. The host survives under both. The demo
24
+ > below runs `continue` (the caption says so); do not read it as the default.
25
+
26
+ > **The npm package name has not changed, and neither has the install command.**
27
+ > The published coordinate is still `dsh-ssh-tui`; `DSH Relay` is the product's
28
+ > display name. See [`docs/decisions/brand-dsh-relay.md`](docs/decisions/brand-dsh-relay.md).
29
+
30
+ ```bash
31
+ npm i -g @deepseek-ai/dsh
32
+ dsh plugin --profile tui add dsh-ssh-tui@latest
33
+ dsh --profile tui
34
+ ```
35
+
36
+ Reconnect with `dsh --profile tui --resume`.
37
+
38
+ ![Reconnect demo: a turn streams, the window is closed, the host keeps running, the same command attaches back, and the session is where it was; the demo runs the continue policy, so that turn also finished in the background](docs/screenshots/reconnect.gif)
39
+
40
+ *Recorded, not staged: a real PTY, a real host, a real SIGHUP, a real resume
41
+ (`npm run screenshots:reconnect` regenerates it). The demo sets `/disconnect
42
+ continue`, which is why the turn finishes on screen after the window is gone; under
43
+ the default `pause` the same run would stop mid-turn and the session would still be
44
+ there to attach back to.*
12
45
 
13
46
  中文部署指南:[README.md](README.md)
14
47
 
48
+ Plain ANSI and incremental redraws. No browser required.
49
+
15
50
  If you mostly work over SSH — a jump host, a test box, a keyboard-only
16
51
  session — start here. A local desktop terminal with themes and layout
17
52
  you already like can stay as it is.
@@ -47,7 +82,7 @@ dsh --profile tui
47
82
  Current `dsh` requires `--profile` (`dsh plugin add …` errors without it).
48
83
  Swap `tui` for another profile name.
49
84
 
50
- **On the 0.2.0-rc host line?** 0.8.0 is the release that supports it (older versions are refused by the launcher's version check), but it is published under `next` for now — `latest` is still 0.7.4 until 2026-10-01. Until then install with `dsh plugin --profile tui add dsh-ssh-tui@next`; on the 0.1.7-rc line `@latest` (0.7.4) is enough, and moving to 0.8.0 later costs nothing. **0.1.5 and older hosts are no longer supported as of 0.8.0.**
85
+ **Which host line?** `0.8.1` is `latest` and supports the `0.2.0-rc` line (the desktop harness's baseline) as well as `0.1.7-rc`; older releases are refused by the launcher's version check. **0.1.5 and older hosts are no longer supported as of 0.8.0.**
51
86
 
52
87
  **Updates must use `@latest`.** `dsh plugin` forwards the rest of the line to
53
88
  pnpm in the profile directory. A bare `add dsh-ssh-tui` keeps the version
@@ -55,6 +90,40 @@ already pinned in `pnpm-lock.yaml` (often 0.3.7). Do not put `--profile`
55
90
  after `add`: `dsh plugin add --profile tui add dsh-ssh-tui` is not valid.
56
91
  Remove with `dsh plugin --profile tui remove dsh-ssh-tui`.
57
92
 
93
+ ## Verified, not assumed
94
+
95
+ <!-- bench:begin -->
96
+ Measured on 100×20, median of 40 runs per action (`npm run bench`; this block is
97
+ generated by `npm run bench:report`, not typed by hand):
98
+
99
+ | Action | ms | bytes | rows addressed | full clears |
100
+ |---|---|---|---|---|
101
+ | one streamed delta while an answer is arriving | 1.64 | 501 | 3 | **0** |
102
+ | one repaint while the agent is busy (the wait card’s clock) | 1.28 | 465 | 3 | **0** |
103
+ | one page key inside a report Screen (PgDn, then PgUp) | 1.41 | 2457 | 17 | **0** |
104
+ | one cursor key inside an open control-plane menu (↓, then ↑) | 1.27 | 588 | 4 | **0** |
105
+ | one character into the wizard’s own field | 0.38 | 140 | 1 | **0** |
106
+
107
+ **0 full-screen clears** across all five actions. That is the performance promise for this
108
+ version: however slow the terminal and however bad the link, frames repaint by row — no tick
109
+ repaints the whole screen.
110
+ <!-- bench:end -->
111
+
112
+ Both commands above are the evidence: `npm run bench` (the numbers), `npm run freeze`
113
+ (that the architecture stayed what it claims). CI re-runs them on every leg — Linux and
114
+ Windows, four host versions. The link-side promises have their own real-PTY probes:
115
+ drop/reattach, a host that outlives the window mid-turn, a resume with history, and the
116
+ "terminal does not answer cursor probes" case; `npm run freeze` lists them.
117
+
118
+ **Tested DSH Relay on a terminal we have not covered yet?**
119
+ [Add a compatibility report](https://github.com/cyjyyd/dsh-ssh-tui/issues/new?template=terminal-compatibility.yml) — one
120
+ form, no code required; results land in the community section of
121
+ [`docs/terminals.md`](docs/terminals.md).
122
+
123
+ If DSH Relay makes remote Harness sessions more reliable for you,
124
+ [starring the repository](https://github.com/cyjyyd/dsh-ssh-tui) helps other terminal
125
+ users find it.
126
+
58
127
  ## Official headless vs this TUI
59
128
 
60
129
  There is no shipped TUI. The default terminal entry on a remote box is
@@ -92,8 +161,13 @@ OSC 52 into your local clipboard; a tool card still expands on click; an SSH ses
92
161
  into the local terminal and no longer warns that it cannot) · a reply is a selectable card too:
93
162
  one `↑` on an empty input lands on the newest reply-or-card (marked `▶`, in screen order;
94
163
  `Alt+4` selects the latest reply), `/copy` then takes that one as written rather than the
95
- wrapped screen text, and `Enter` opens it full-screen · the footer is one priority-ordered chip
96
- strip, and `⚠` opens `/doctor` · the quota bar is on screen from the first frame and names
164
+ wrapped screen text, and `Enter` opens it full-screen · the status row answers five questions
165
+ in one order — link health, what is running, tokens per second, quota left, context used — and
166
+ degrades by dropping cells as the terminal narrows (`⚠` still opens `/doctor`; the install
167
+ warning outranks everything), and the row under it is *ordered* by priority and cut from the
168
+ right — `dir:… · search n/m · queued n · sub:…`, closed by `OpenCode-GO` or
169
+ `CommandCode-GOAT` on those two subscriptions, so a narrow terminal loses the least
170
+ load-bearing part first · the quota meter is on screen from the first frame and names
97
171
  its window (`5Hr`/`1Wk`/`1Mo`, smallest window by default, `?%` with a 15-second retry until
98
172
  a reading arrives) · `/mode` groups presets and filters with `/` · the compact view names
99
173
  every changed file · tool diffs are line-level, emphasise only what changed, and go
@@ -117,6 +191,19 @@ suite and the real-PTY probes — not "no errors, so it probably works". Many te
117
191
  stranded on the 0.1.5 line; few terminal UIs install on the 0.2.0-rc line at all, which is why the window is
118
192
  maintained this tightly.
119
193
 
194
+ ## A warning before the Desktop section
195
+
196
+ > ⚠ **Do not add this plugin to a Desktop (or `web`) profile.** It is not "installed but
197
+ > idle" — it crashes the application. The desktop host's command line belongs to the web
198
+ > app (it passes `--no-open`), and dsh does not arbitrate: every app plugin parses the
199
+ > same argv with *its own* grammar, and an unknown flag exits the process. From 0.8.2 the
200
+ > plugin's startup row declines a command line that is not its own (the desktop launcher,
201
+ > or a web app that has already taken over); 0.8.1 and earlier exited 1 with
202
+ > `error: unknown option '--no-open'`, and the app reported
203
+ > `dsh desktop host exited with 1` and died. If it is already in there, remove the four
204
+ > `ssh-tui-*` rows from `profiles\desktop\cordis.patch.yml` (or restore that file from a
205
+ > backup) and the application works again.
206
+
120
207
  ## Desktop Harness users, read this first
121
208
 
122
209
  **The official desktop app (<https://www.deepseek.com/harness/>) does not need this plugin** — use its own UI
@@ -463,11 +550,14 @@ model, and sends unrecognized shapes (`npm publish`, interpreter `-c`/`-e`)
463
550
  to the subagent-model reviewer (user message + args/reason/sandbox; English
464
551
  UI uses the English reviewer; `authorization=yes` required). `/approval
465
552
  status` also reports how many AI reviews ran this session.
466
- The identity footer row shows a one-cell Braille ring after the
467
- remaining-quota bar for occupancy of the routed model's context window
468
- (DSH `contextPressure`, provider-agnostic); green / yellow / red map
469
- to ok / 80% / 95%.
470
- `/status` prints the same figures. Warnings fire near 80%/95%; idle
553
+ The status row shows context occupancy as a capacity meter over the routed
554
+ model's context window (DSH `contextPressure`, provider-agnostic); the bands are
555
+ the harness's own pressure levels, so 80% turns the meter yellow and 95% red —
556
+ exactly where compaction starts warning.
557
+ `/status` prints the same figures, plus everything the row gave up: turns,
558
+ steps, input/output/cached tokens, the session total (with the accounting it
559
+ came from), model time, tool time, cache hit and the exact last-step and
560
+ running `tokens/sec`. Warnings fire near 80%/95%; idle
471
561
  auto-`/compact` starts near 72% so recovery is not left to a mid-turn
472
562
  overflow. `/compact` shows a spinning compact card and footer until it
473
563
  finishes, then the tokens recovered. `Tab` completes, `Enter` runs.
@@ -720,8 +810,10 @@ at the slowest tier. `DSH_TUI_PAINT_MS` always wins (40–1000). The stats line
720
810
  starts with `SSH ●●●○ 90ms` (1 pip red, 2 yellow, 3+ green). The probe
721
811
  does not write into the transcript.
722
812
 
723
- Idle hangup exits the Host. A busy turn keeps it; `--resume` attaches to that
724
- process. Do not start a second Host.
813
+ Idle hangup exits the Host. A *busy* session keeps the Host alive so you can attach
814
+ back; whether the turn itself keeps running across the gap is the `/disconnect`
815
+ policy (`pause` stops it by default, `continue` finishes it in the background).
816
+ `--resume` attaches to that process. Do not start a second Host.
725
817
 
726
818
  ## Troubleshooting (Q&A)
727
819
 
@@ -891,8 +983,19 @@ src/i18n/ zh/en UI catalogs
891
983
  ```sh
892
984
  npm install
893
985
  npm run build
986
+ npm test
987
+ ```
988
+
989
+ Two commands that do not ask "is anything broken" but "is anything still held":
990
+
991
+ ```sh
992
+ npm run freeze # the B2 freeze: eight invariants + seven retired paths (unclassified / live source rows / …)
993
+ npm run bench # the perf baseline: stream / wait / report paging / menu move / wizard typing, all incremental
894
994
  ```
895
995
 
996
+ The release rules — including the two things CI cannot cover (a Windows machine, and a
997
+ first-run home) — are in [docs/release.md](docs/release.md) (Chinese).
998
+
896
999
  ## Privacy
897
1000
 
898
1001
  All sessions, credentials, and settings live under `$DSH_HOME` (default
package/README.md CHANGED
@@ -1,23 +1,26 @@
1
- # dsh-ssh-tui
1
+ # DSH Relay
2
+
3
+ **A resilient terminal workspace for DeepSeek Harness.**
4
+ 远端优先 · 可重连 · 就在终端里
2
5
 
3
6
  [![npm](https://img.shields.io/npm/v/dsh-ssh-tui?style=flat-square&color=4b6fff)](https://www.npmjs.com/package/dsh-ssh-tui)
4
7
  [![npm downloads](https://img.shields.io/npm/dm/dsh-ssh-tui?style=flat-square)](https://www.npmjs.com/package/dsh-ssh-tui)
5
8
  [![CI](https://github.com/cyjyyd/dsh-ssh-tui/actions/workflows/ci.yml/badge.svg)](https://github.com/cyjyyd/dsh-ssh-tui/actions/workflows/ci.yml)
6
9
  [![dshfind](https://dshfind.com/api/badge/cyjyyd/dsh-ssh-tui)](https://dshfind.com/zh/plugins/cyjyyd/dsh-ssh-tui?ref=badge)
7
10
 
8
- **A resilient terminal frontend for DeepSeek Harness.**
9
-
10
- 纯 ANSI · 增量重绘 · SSH 断线重接 · Windows / ConPTY · Host-aware activation · 无需浏览器
11
-
12
- **连接可以断,终端可以换,Harness 可以升级;正在工作的会话不应该因此变得脆弱。**
13
-
14
- English: [README.en.md](README.en.md)
11
+ **SSH 断了。会话还在。接回来继续。**
15
12
 
16
- ---
13
+ “Relay” 说的是这件事:**显示可以消失,会话不必跟着消失**。宿主进程活着,同一句命令接回来
14
+ 就回到原来的会话 —— 转写、计划卡片、审批、排队中的消息都在原处。
15
+ 这是远程优先的终端工作区,不只是“一个 SSH 风格的界面”。
17
16
 
18
- ## 30 秒开始
17
+ > **断线时正在跑的那一轮会怎样?取决于 `/disconnect`:**
18
+ > 默认 `pause` —— **当前这一轮在窗口离开时停下**,会话本身不丢,接回来接着用;
19
+ > 显式设成 `continue` —— 当前这一轮在显示端离开后**继续在后台跑完**。
20
+ > 两种策略下宿主都不退出。演示动图用的是 `continue`(字幕里标了),别把它当成默认行为。
19
21
 
20
- 需要 Node.js ≥ 22.19 和 DeepSeek Harness CLI。
22
+ > **npm 包名没有变,安装命令也没有变。** 发布坐标仍然是 `dsh-ssh-tui`;
23
+ > `DSH Relay` 是产品展示名。见 [`docs/decisions/brand-dsh-relay.md`](docs/decisions/brand-dsh-relay.md)。
21
24
 
22
25
  ```bash
23
26
  npm i -g @deepseek-ai/dsh
@@ -25,6 +28,51 @@ dsh plugin --profile tui add dsh-ssh-tui@latest
25
28
  dsh --profile tui
26
29
  ```
27
30
 
31
+ 需要 Node.js ≥ 22.19 和 DeepSeek Harness CLI。断开之后接回来:`dsh --profile tui --resume`。
32
+
33
+ ![重连演示:一轮正在流式输出时窗口关闭,宿主进程不退出,同一条命令接回来,这个会话还在原处;演示用 continue 策略,所以这一轮也在后台跑完了](docs/screenshots/reconnect.gif)
34
+
35
+ *实拍,不是摆拍:真 PTY、真宿主、真 SIGHUP、真 resume(`npm run screenshots:reconnect` 可重放)。
36
+ 演示显式设了 `/disconnect continue`,所以画面上这一轮在窗口消失后跑完了;换成默认的 `pause`,
37
+ 同一段会停在中断处,会话照样接得回来。*
38
+
39
+ English: [README.en.md](README.en.md)
40
+
41
+ ---
42
+
43
+ ## 已测,不是号称
44
+
45
+ <!-- bench:begin -->
46
+ 在 100×20 下实测,每个动作 40 次取中位(`npm run bench`;本节由 `npm run bench:report` 生成,
47
+ 不是手抄的):
48
+
49
+ | 动作 | 毫秒 | 字节 | 寻址行 | 整屏清屏 |
50
+ |---|---|---|---|---|
51
+ | 流式输出时的一次重绘 | 1.64 | 501 | 3 | **0** |
52
+ | 等待卡片时钟走一格 | 1.28 | 465 | 3 | **0** |
53
+ | 报告屏翻一页(PgDn / PgUp) | 1.41 | 2457 | 17 | **0** |
54
+ | 菜单里移动一次光标 | 1.27 | 588 | 4 | **0** |
55
+ | 向导字段里打一个字符 | 0.38 | 140 | 1 | **0** |
56
+
57
+ 五个动作合计 **0 次整屏清屏**。这条是这一版的性能承诺:不管终端多慢、链路多差,
58
+ 画面按行增量重绘,不会因为一次 tick 把整屏重画一遍。
59
+ <!-- bench:end -->
60
+
61
+ 跑法就是上面两条命令;这两条断言在 CI 的每一条腿上都会重跑(Linux 与 Windows、四个宿主版本)。
62
+ 链路上的承诺另有真 PTY 探针:断链重接、断线后宿主存活、带历史的 resume、以及"终端不回光标位置"
63
+ 时的链路显示,各自一个脚本,全部在 CI 里跑(`npm run freeze` 会把它们列出来)。
64
+
65
+ **你在我们没覆盖过的终端上试过 DSH Relay 吗?**
66
+ [提一份终端兼容报告](https://github.com/cyjyyd/dsh-ssh-tui/issues/new?template=terminal-compatibility.yml) —— 不用读代码,填一张表就行,
67
+ 结果会进 [`docs/terminals.md`](docs/terminals.md) 的「社区实测」区。
68
+
69
+ 如果 DSH Relay 让你的远程会话更可靠了,
70
+ [给仓库点个 star](https://github.com/cyjyyd/dsh-ssh-tui) 能帮别的终端用户找到它。
71
+
72
+ ## 30 秒开始
73
+
74
+ 上面三条命令装好就进工作区。之后常用的几条:
75
+
28
76
  恢复旧会话:
29
77
 
30
78
  ```bash
@@ -196,7 +244,7 @@ docs/screenshots/slow-link.json
196
244
  - reasoning 折叠与实时查看;
197
245
  - plan / approval / ask-user-question;
198
246
  - 子代理独立状态卡;
199
- - 会话 token、速度、额度与活动状态;
247
+ - 状态行按一眼扫读的顺序回答五个问题:链路健康、正在做什么、tok/s、额度余量、上下文占用(宽度不够时按优先级逐项收敛,窄屏更简洁而不是更拥挤);轮数 / 步数 / 模型时间 / 工具时间 / 缓存命中移出常驻行,仍可在 `/status` 与 `/diag` 查看;
200
248
  - `/model`、`/mode`、`/submodel` 等 Harness 能力直接进入终端。
201
249
 
202
250
  ### Session continuity
@@ -237,7 +285,8 @@ docs/screenshots/slow-link.json
237
285
  - 调工具;
238
286
  - 运行子代理;
239
287
 
240
- TUI 可以保留 Host,并允许重新接入。
288
+ TUI 可以保留 Host,并允许重新接入(保留的是**宿主**;这一轮本身是否在断线期间继续跑,看
289
+ [`/disconnect`](#ssh-断了之后) 的策略)。
241
290
 
242
291
  ```bash
243
292
  dsh --profile tui --resume
@@ -295,6 +344,14 @@ dsh --profile tui
295
344
 
296
345
  ## 官方 Harness Desktop
297
346
 
347
+ > ⚠ **不要把本插件加进 Desktop(或 `web`)profile。** 这不是"装了没用",是**会让应用崩**:
348
+ > Desktop 的宿主命令行走的是 web app 的语法(它传 `--no-open`),而命令行在 dsh 里没有仲裁——profile 里每个 app
349
+ > 都会用**自己的**语法解析同一份 argv,遇到不认识的旗标直接退出进程。0.8.2 起本插件的 startup 行会在识别到
350
+ > "这条命令行不属于我"(Desktop 启动器,或 web app 已接管)时**不认领**它;0.8.1 及更早则会以
351
+ > `error: unknown option '--no-open'` 让宿主退出 1,应用随即报 `dsh desktop host exited with 1` 并崩。
352
+ > 万一已经加进去了:从 `profiles\desktop\cordis.patch.yml` 里删掉 `ssh-tui-startup` / `ssh-tui` /
353
+ > `ssh-tui-routes` / `ssh-tui-subagent` 四行(或从备份恢复该文件),应用立刻恢复。
354
+
298
355
  官方 Desktop 是 GUI 应用,本身**不需要这个 TUI**。
299
356
 
300
357
  Desktop 内部的 Harness Host 和真正的 console runtime 也不是一回事。
@@ -498,6 +555,13 @@ npm run build
498
555
  npm test
499
556
  ```
500
557
 
558
+ 两条不跑"有没有坏"、而跑"有没有守住"的命令:
559
+
560
+ ```bash
561
+ npm run freeze # B2 冻结:八条不变量 + 七条退役路径(unclassified / live source rows / …)
562
+ npm run bench # 性能基线:流式 / 等待 / 报告翻页 / 菜单移动 / 向导打字,并断言都不整屏重画
563
+ ```
564
+
501
565
  真实终端、断线与平台行为由额外 probe 覆盖。
502
566
 
503
567
  项目的目标不是让模拟测试代替终端,而是让:
@@ -522,6 +586,7 @@ cross-platform CI
522
586
  | [remote-ops.md](docs/remote-ops.md) | SSH、断线、attach、Host 生命周期 |
523
587
  | [desktop.md](docs/desktop.md) | 官方 Harness Desktop / no-TTY Host |
524
588
  | [platform.md](docs/platform.md) | 平台生命周期设计与维护者约束 |
589
+ | [release.md](docs/release.md) | 发版流程(硬性规则)与发布前人工门槛 |
525
590
  | [terminals.md](docs/terminals.md) | Terminal capability 与兼容矩阵 |
526
591
  | [upstream-desktop-report.md](docs/upstream-desktop-report.md) | 可复现的上游 Desktop 问题记录 |
527
592
 
@@ -0,0 +1,91 @@
1
+ # 0.7.4 发版说明(草稿)
2
+
3
+ > **状态:未发版。** 版本号、tag、GitHub Release、npm 都还没动(`docs/release.md` 第 1 条)。
4
+ > 本文件是发版时要粘进 GitHub Release 的正文草稿 + 当次核对清单,写完就不必临场回想。
5
+ > 上一次发版:**0.7.3**(2026-09-24,commit `e505768` / tag `v0.7.3` / npm `latest`+`next`)。
6
+
7
+ ## 头等事
8
+
9
+ 这一版是「把已经做出来的东西做对」:0.7.3 之后落的六个功能各自被真机与对抗式审查拧了一遍,
10
+ 修掉了 8 个实际缺陷(含两个只有读代码才看得见的:现场思考卡被挤出焦点环、覆盖层里的复制
11
+ 拿到的是另一份正文)。行为上最明显的变化是**回复变成了可选中的卡片**。
12
+
13
+ ## 回复可以选中、可以复制(本版主线)
14
+
15
+ `/copy` 取的是"被选中的那一行",而可选列表里**从来没有回复**——所以只要按过一次 ↑/↓ 选中
16
+ 任何卡片,`/copy` 就再也拿不到回复,最新回复只是"没有选中时"的兜底。现在:
17
+
18
+ - 空输入 `↑`/`↓` 在**回复与卡片**之间按屏幕顺序走,选中的那一行带 `▶` 标记;`Alt+4` 直接选中最新回复。
19
+ - `/copy`(或 `Ctrl+Shift+C`)复制的是**选中的那一条的原文**——不是折行后的屏幕文本;
20
+ 复制后保留选中,连按两次拿到的是同一条。
21
+ - 在选中的回复上按 `Enter`:打开「回复全文」(可滚动、`Esc` 返回),长回复里的表格与代码块
22
+ 不再被窗口宽度切碎。**覆盖层里也能复制**,取的是屏幕上那份正文,并在覆盖层底部回显「已复制 …」。
23
+ - 工具卡 / 子代理 / 改动卡的「全文」覆盖层里,复制键取的同样是**屏幕上那份正文**(工具全文、那份 diff、
24
+ 子代理日志),不再是卡片摘要。
25
+ - **拖选**照旧:被选中回复第一行前面的 `▶` 是装饰,拖过它不会把标记复制进去。
26
+
27
+ ## 底栏与链路
28
+
29
+ - 链路质量**在会话运行中持续重测**并按中位数读数:进会话那一瞬间的网络抖动不再把整场会话钉在最低档
30
+ (绘制节奏与每帧字节预算都跟着这档走)。
31
+ - 选择器把「可接入 / 已占用」两个词固定下来,并且**在首帧之前**就把"已经挂掉的窗口"判完——列表不会先
32
+ 显示「已占用」再改口。判定走一次真实往返(光标探针),因为断掉的 SSH 链路会让 launcher 还连着显示通道。
33
+ - 接管(把别人的窗口接过来)现在**要确认**,`Esc` 是取消而不是退出。
34
+
35
+ ## 空回合不再被误读为完成
36
+
37
+ 上游网关偶尔只回思考、`finish_reason: stop`、正文为零(实测某网关 10/35 个回合如此),
38
+ 宿主如实组装成"只有思考的助手消息",状态栏显示完成/空闲。现在转录里会补一行明确提示
39
+ (含 `finish_reason: stop` 与"按 Enter 继续"),回放/中断/报错的回合不提示。
40
+
41
+ ## 上游兼容
42
+
43
+ - `dsh` 声明窗口:`>=0.1.7-rc.1 <0.1.8`(`next`)与本条线并行;`0.1.7-rc.2` 已按完整流程验过并声明
44
+ `compatible`(干净树安装、`tsc` 0 错、套件全绿、六个真 PTY 探针全过),CI 里单独一条腿。
45
+ - CI 现有 5 条腿:`0.1.7-rc.1`(默认,提交的 manifest)、`0.1.7-rc.2`、`0.1.5-rc.3`、`0.1.5-rc.1`
46
+ (只跑类型与单元)、`test-windows`(ConPTY 真机)。默认腿新增一条 **Reply copy probe**:真 PTY 跑一轮
47
+ 脚本化模型,断言拖选复制、`/find` 高亮、以及**选中回复后按复制键拿到原文**。
48
+
49
+ ## 发版核对清单(执行时逐条打勾)
50
+
51
+ ```sh
52
+ # 0. 版本号(改这一处,然后重新生成锁)
53
+ # package.json "version": 0.7.4
54
+ npm install --package-lock-only --cache <可写目录> # 只重算锁,不动 node_modules
55
+
56
+ # 1. 门禁(本地)
57
+ npx tsc --noEmit -p tsconfig.json
58
+ node --test "tests/*.test.mjs" # 期望:1009 pass / 0 fail / 4 skip
59
+ node scripts/tui-mock-probe.mjs # 脚本化一轮:拖选 + 选中回复复制 + /find
60
+ node scripts/tui-mock-probe.mjs --busy # 窗口中途关闭
61
+ node scripts/probe-home.mjs --probe # boot/resize/diag/doctor/copy/preset/exit
62
+ node scripts/probe-home.mjs --probe --script tui-term-probe.mjs
63
+ node scripts/probe-home.mjs --probe --script tui-cut-probe.mjs
64
+ node scripts/probe-home.mjs --probe --script tui-rtt-probe.mjs
65
+ node scripts/probe-home.mjs --probe --script tui-drop-probe.mjs
66
+ npm pack --dry-run # 189 个文件、无 tgz 落盘
67
+
68
+ # 2. GitHub(先)
69
+ git commit -am "Release 0.7.4"
70
+ git push origin main
71
+ git tag -a v0.7.4 -m "0.7.4" && git push origin v0.7.4
72
+ # → 建 GitHub Release(正文用本文件"头等事"起的那几段 + 当日热度基线)
73
+ # → 等 CI 五条腿全 success(tag 与 main 都要看)
74
+
75
+ # 3. npm(CI 全绿之后;token 需先刷新)
76
+ npm whoami --registry https://registry.npmjs.org/ # 必须是登录态,否则不要往下走
77
+ npm publish --registry https://registry.npmjs.org/ --tag latest
78
+ npm dist-tag add dsh-ssh-tui@0.7.4 next # 0.7.3 时 latest 与 next 同值
79
+ ```
80
+
81
+ **注意**:本机 `~/.npmrc` 的默认 registry 是 npmmirror,发布必须显式
82
+ `--registry=https://registry.npmjs.org/`;`_authToken` 目前是过期状态(`npm whoami` → `E401` /
83
+ `ENEEDAUTH`),刷新之前不要执行第 3 步。
84
+
85
+ ## 已知限制(写进 Release)
86
+
87
+ - 真实 Windows 上仍只能人工确认的两条:宿主控制台窗口确实隐藏;`%USERPROFILE%` 与 `$HOME` 不一致的账户行为。
88
+ - 纯问答会话(尚无任何卡片)或刚 `/clear` 时,空输入 `↑` 仍是**历史召回**而不是选中回复——这是刻意保留的;
89
+ 那种会话用 `Alt+4` / `Ctrl+N` / `Ctrl+P` 选回复。选中后 `Enter` 正常打开全文。
90
+ - 把 `Ctrl+Shift+C` 折叠成 `Ctrl+C` 的老终端上,覆盖层里那个键等于关闭;请用 `ssh-tui.keys` 改绑 `copy`
91
+ (改绑后的键在覆盖层内同样生效)。