dsh-ssh-tui 0.8.0 → 0.8.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/README.en.md +34 -0
- package/README.md +435 -577
- package/docs/display-mode.md +122 -0
- package/docs/remote-ops.md +51 -0
- package/docs/terminals.md +53 -0
- package/lib/attach.js +4 -4
- package/lib/attach.js.map +1 -1
- package/lib/auth-failure.js +36 -0
- package/lib/auth-failure.js.map +1 -1
- package/lib/commands.js +2 -0
- package/lib/commands.js.map +1 -1
- package/lib/dialogs.js +43 -0
- package/lib/dialogs.js.map +1 -1
- package/lib/display-mode.js +147 -0
- package/lib/display-mode.js.map +1 -0
- package/lib/display-sock.js +361 -21
- package/lib/display-sock.js.map +1 -1
- package/lib/footer.js +6 -9
- package/lib/footer.js.map +1 -1
- package/lib/glyph-measure.js +92 -0
- package/lib/glyph-measure.js.map +1 -0
- package/lib/i18n/en.js +18 -2
- package/lib/i18n/en.js.map +1 -1
- package/lib/i18n/zh.js +18 -2
- package/lib/i18n/zh.js.map +1 -1
- package/lib/index.js +73 -5
- package/lib/index.js.map +1 -1
- package/lib/paint.js +22 -9
- package/lib/paint.js.map +1 -1
- package/lib/picker.js +14 -13
- package/lib/picker.js.map +1 -1
- package/lib/plan.js +11 -11
- package/lib/plan.js.map +1 -1
- package/lib/platform.js +96 -0
- package/lib/platform.js.map +1 -1
- package/lib/session-blank.js +81 -0
- package/lib/session-blank.js.map +1 -0
- package/lib/session-list.js +102 -81
- package/lib/session-list.js.map +1 -1
- package/lib/startup.js +7 -0
- package/lib/startup.js.map +1 -1
- package/lib/subagent-model.js +8 -7
- package/lib/subagent-model.js.map +1 -1
- package/lib/term-text.js +296 -28
- package/lib/term-text.js.map +1 -1
- package/lib/terminal-input.js +132 -10
- package/lib/terminal-input.js.map +1 -1
- package/lib/theme.js +318 -0
- package/lib/theme.js.map +1 -0
- package/lib/tool-present.js +11 -9
- package/lib/tool-present.js.map +1 -1
- package/lib/tui.js +420 -38
- package/lib/tui.js.map +1 -1
- package/lib/types/attach.d.ts +6 -2
- package/lib/types/auth-failure.d.ts +30 -0
- package/lib/types/commands.d.ts +6 -0
- package/lib/types/dialogs.d.ts +36 -0
- package/lib/types/display-mode.d.ts +99 -0
- package/lib/types/display-sock.d.ts +75 -0
- package/lib/types/footer.d.ts +1 -1
- package/lib/types/glyph-measure.d.ts +41 -0
- package/lib/types/index.d.ts +13 -0
- package/lib/types/plan.d.ts +5 -2
- package/lib/types/platform.d.ts +83 -0
- package/lib/types/session-blank.d.ts +51 -0
- package/lib/types/session-list.d.ts +34 -0
- package/lib/types/startup.d.ts +6 -0
- package/lib/types/subagent-model.d.ts +7 -6
- package/lib/types/term-text.d.ts +55 -23
- package/lib/types/terminal-input.d.ts +37 -0
- package/lib/types/theme.d.ts +109 -0
- package/lib/types/tool-present.d.ts +2 -2
- package/lib/types/tui.d.ts +98 -1
- package/package.json +68 -66
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# 管道宿主模式(`DSH_TUI_DISPLAY=stdio`)
|
|
2
|
+
|
|
3
|
+
> 这个插件是**终端 UI**:它要在一个真实 TTY 里画。桌面版 Harness 的启动器是 Electron-as-Node,
|
|
4
|
+
> 没有 console,所以**它不需要这个插件**,插件在那边保持惰性(`docs/desktop.md`)。
|
|
5
|
+
> 这份文档讲的是**另一条路**:宿主自己会开终端控件(PTY 面板、xterm.js、GUI 里嵌的终端),
|
|
6
|
+
> 它可以把插件当成"一个在管道上说话的子进程"来用。
|
|
7
|
+
|
|
8
|
+
## 一句话
|
|
9
|
+
|
|
10
|
+
父进程声明 `DSH_TUI_DISPLAY=stdio`,插件就不再要求 TTY:它把 stdin 当**输入通道**、stdout 当
|
|
11
|
+
**画面通道**、并接受父进程用 `CSI 8 ; rows ; cols t` 报告的面板尺寸。父进程只要会"喂字节 + 解 ANSI",
|
|
12
|
+
就能承载完整 TUI——包括会话续接、断线重连、`/exit`。
|
|
13
|
+
|
|
14
|
+
## 两个入口(机制 + 语法糖)
|
|
15
|
+
|
|
16
|
+
| 入口 | 用途 |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `DSH_TUI_DISPLAY=stdio`(环境变量) | **机制**。父进程设置它,等于声明"stdin/stdout 是显示通道",插件的无终端保护会照此放行 |
|
|
19
|
+
| `--display stdio`(命令行) | **语法糖**。写在 profile 行里时 `--help` 会显示它,拼错是报错而不是静默惰性 |
|
|
20
|
+
|
|
21
|
+
两者同时存在时**环境变量优先**(父进程描述的是它马上要用的通道;标志可能来自包装脚本的残留)。
|
|
22
|
+
不认识的值会打印一行说明,然后按没有终端处理——不会静默变惰性。
|
|
23
|
+
|
|
24
|
+
## 父进程要做的五件事
|
|
25
|
+
|
|
26
|
+
1. **管道起进程**(不要 PTY)。`dsh --profile tui` 用 `spawn(..., { stdio: ['pipe','pipe','pipe'] })`;
|
|
27
|
+
顺带一句话:能开 PTY 的宿主**不需要这个模式**——直接给真 TTY,走默认路径即可。
|
|
28
|
+
2. **声明初始尺寸**:管道不报告尺寸,插件读 `COLUMNS` / `LINES`(没给就是 80×24)。
|
|
29
|
+
建议同时给 `TERM=xterm-256color`、`COLORTERM=truecolor`:能力表(alt-screen、颜色、鼠标、
|
|
30
|
+
OSC 8)都从环境推出来,`TERM` 缺失会退化成"哑终端"。
|
|
31
|
+
3. **把按键写成字节**到子进程 stdin(UTF-8)。字节流就是终端输入:普通字符、`\r`、`\x03`(Ctrl+C)、
|
|
32
|
+
方向键的 `ESC [ A` 之类都原样可用。
|
|
33
|
+
4. **把 stdout 解成画面**:输出就是 ANSI(绝对定位 + 只画脏行 + `DEC 2026` 同步刷新)。
|
|
34
|
+
喂给 xterm.js 之类即可;**不要**自己做行缓冲或重排——它们假定了固定宽度和行地址。
|
|
35
|
+
5. **报告尺寸变化**:管道没有 `resize` 事件,也没有 `SIGWINCH`。面板变大变小时,往 stdin 写
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
CSI 8 ; <rows> ; <cols> t 例:\x1b[8;40;120t
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
这就是终端回答"窗口多少格"的序列,插件把它当尺寸帧转给绘制的 Host,并以新宽度重画。
|
|
42
|
+
它**不会**被当成输入(半个到达的序列会被暂存到下次读取,不会把 `ESC` 泄进提示词)。
|
|
43
|
+
一次写完整条序列:拆成两次写时,前半段会被暂存,但"暂存"只保证不泄成按键。
|
|
44
|
+
|
|
45
|
+
## 光标探测(可选,但建议)
|
|
46
|
+
|
|
47
|
+
插件会通过 `CSI 6n` 问光标位置,用来(1)测链路往返、(2)判断 `①`/`—` 这类**歧义宽度**字形在这个
|
|
48
|
+
字体里占几格。父进程如果像终端模拟器那样回答:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
CSI <row> ; <col> R 例:\x1b[1;1R
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
那么接入是快的,并且宽度能按真实字体判定。**不回答也能用**:测量会在采样预算(约 0.7s)后放弃,
|
|
55
|
+
歧义宽度退回按 locale 推断,字形探针直接跳过。需要彻底关掉字形探针(`DSH_TUI_NO_GLYPH_PROBE=1`)
|
|
56
|
+
的场合是"断言 attach 字节流"的测试。
|
|
57
|
+
|
|
58
|
+
## 退出与清理
|
|
59
|
+
|
|
60
|
+
- 用户在 TUI 里 `/exit`,或按 Ctrl+C,子进程自己收尾:恢复管道上的终端模式(管道上没有可恢复的
|
|
61
|
+
东西,所以是空操作)、写 goodbye 帧、退出码 0。
|
|
62
|
+
- 插件会**短暂保持 raw 模式一个往返**再放开终端,用来吞掉"还在路上的光标回答"——否则慢链路上
|
|
63
|
+
这个回答会被 tty 回显成 `^[[25;1R` 文本留在提示符旁。宿主不需要配合,只要别在这几百毫秒里
|
|
64
|
+
强杀进程。
|
|
65
|
+
- 会话本身在 Host 进程里,窗口/管道断开不等于会话结束:重新按同一个 session id 接入即可续上。
|
|
66
|
+
|
|
67
|
+
## 最小示例(Node)
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
import { spawn } from 'node:child_process'
|
|
71
|
+
|
|
72
|
+
const child = spawn(process.execPath, [cliPath, '--profile', 'tui', `--resume=${sessionId}`], {
|
|
73
|
+
env: {
|
|
74
|
+
...process.env,
|
|
75
|
+
DSH_HOME: home,
|
|
76
|
+
DSH_TUI_DISPLAY: 'stdio', // 父进程声明:我在管道上说话
|
|
77
|
+
TERM: 'xterm-256color',
|
|
78
|
+
COLORTERM: 'truecolor',
|
|
79
|
+
COLUMNS: '100', // 管道不报尺寸,父进程声明
|
|
80
|
+
LINES: '30',
|
|
81
|
+
},
|
|
82
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
// 画面 → 终端控件
|
|
86
|
+
child.stdout.on('data', bytes => terminal.write(bytes))
|
|
87
|
+
// 按键 → 子进程
|
|
88
|
+
terminal.onData(text => child.stdin.write(text))
|
|
89
|
+
// 探测 → 像终端模拟器那样回答(不回答也能跑,只是慢)
|
|
90
|
+
let seen = ''
|
|
91
|
+
child.stdout.on('data', chunk => {
|
|
92
|
+
seen += chunk
|
|
93
|
+
let at = seen.indexOf('\x1b[6n')
|
|
94
|
+
while (at !== -1) {
|
|
95
|
+
child.stdin.write('\x1b[1;1R')
|
|
96
|
+
seen = seen.slice(at + 4)
|
|
97
|
+
at = seen.indexOf('\x1b[6n')
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
// 面板尺寸变化 → 报告给子进程
|
|
101
|
+
resizeObserver(size => child.stdin.write(`\x1b[8;${size.rows};${size.cols}t`))
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## 可执行的规格
|
|
105
|
+
|
|
106
|
+
| 位置 | 覆盖内容 |
|
|
107
|
+
|---|---|
|
|
108
|
+
| `tests/stdio-pipe-e2e.test.mjs` | 真 relay + 真 Host,**两端都是管道**:attach 不报 `setRawMode`、初始尺寸、按键、`CSI 8 t` 重画、goodbye 退出 |
|
|
109
|
+
| `tests/display-mode.test.mjs` | 模式解析(env/标志/拼错)、尺寸报告的解析与跨读暂存 |
|
|
110
|
+
| `scripts/tui-stdio-probe.mjs` | 端到端:起真 profile、走管道,断言画面、输入回显、`CSI 8 t` 后按新宽度重画、`/exit` 干净退出。CI 的 Linux 与 Windows 两条腿都跑 |
|
|
111
|
+
| `tests/no-terminal.test.mjs` | 没有终端时插件惰性(桌面版那条路),以及 `requireTerminal: true` 是唯一会抛错的路径 |
|
|
112
|
+
|
|
113
|
+
## 限制(写清楚,免得宿主踩)
|
|
114
|
+
|
|
115
|
+
- **没有真实 `resize`**:只能靠 `CSI 8 ; rows ; cols t`。不发的宿主会一直停在 `COLUMNS`/`LINES` 声明的尺寸。
|
|
116
|
+
- **没有 `SIGWINCH`**:同上。
|
|
117
|
+
- **能力表来自环境**:`TERM`/`COLORTERM` 没给,就没有 alt-screen、没有颜色、没有鼠标——画面能跑,
|
|
118
|
+
但不是用户期待的样子。用 `DSH_TUI_TERM_CAPS` 可以显式覆盖(见 `docs/terminals.md`)。
|
|
119
|
+
- **只读诊断**:`DSH_TUI_LINE_MODE=1` 是给日志型面板的另一条路——每个事件一行纯文本,不画屏。
|
|
120
|
+
- **桌面版进程本身**:如果宿主是那个 Electron-as-Node 启动器,**它开不出这条通道**(没有 console,
|
|
121
|
+
但真正的问题在它自己的子进程模型)。这种情况请用 npm 安装的 CLI 在真终端/SSH 里跑
|
|
122
|
+
`dsh --profile tui`,见 `docs/desktop.md`。
|
package/docs/remote-ops.md
CHANGED
|
@@ -215,6 +215,55 @@ AUTH 类错误**,所以一次瞬时 403 会直接终结整轮。要让它在"
|
|
|
215
215
|
重试**每次发送最多一次**(重试自己那一轮不会再触发),且只对"提供商拒绝 + 本机有凭据"这一类生效——
|
|
216
216
|
本机缺凭据时重试毫无意义。
|
|
217
217
|
|
|
218
|
+
## 4.9 两种 400:一种是我们能压的,一种是上游的 bug
|
|
219
|
+
|
|
220
|
+
2026-09-29 在一个长会话里数出 **6 次 400**,全部落在同一条路由(`command-code` + `deepseek/deepseek-v4.1-flash`,
|
|
221
|
+
effort=max),但**分两族、两个不同的成因**:
|
|
222
|
+
|
|
223
|
+
| 族 | 正文 | 次数与时间(UTC) | 归属 |
|
|
224
|
+
|---|---|---|---|
|
|
225
|
+
| A | `json: unknown field "summary"`(HTTP 400 `InvalidParameter`) | 1 次(09-28 01:45,turn 26) | pi-ai 的 `openai-responses` 适配器在设了强度时会发 `reasoning.summary`,而这家的 Go 网关不认这个字段 |
|
|
226
|
+
| B | `The reasoning_text in the thinking mode must be passed back to the API.`(`invalid_request_error`) | **5 次**(09-29 07:24 turn 36;14:02 / 14:05 / 14:13 / 14:16 turn 45–48) | **上游 harness 的 bug**:多轮会话把上一轮的 reasoning 块丢了,而思考模式下网关要求回传 |
|
|
227
|
+
|
|
228
|
+
**A 族**:全局 CLI 里的 pi-ai 副本被本机打过一个补丁(一直在,`.bak` 也在),
|
|
229
|
+
`dist/api/openai-responses.js` 第 260 行:
|
|
230
|
+
|
|
231
|
+
```js
|
|
232
|
+
- summary: options?.reasoningSummary || "auto", // 原文:没设也会发 "auto"
|
|
233
|
+
+ ...(options?.reasoningSummary ? { summary: options.reasoningSummary } : {}), // 补丁:只在显式设置时才发
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
单轮探针也验证过:带 `reasoning:{effort:"max"}`(不含 `summary`)打 `/provider/v1/responses` 返回 200。
|
|
237
|
+
**升级 dsh 族会覆盖这个文件,补丁会丢**——补丁丢了的症状就是 A 族 400 重新出现,照上面这行重新打即可
|
|
238
|
+
(打之前先 `cp openai-responses.js openai-responses.js.bak`)。
|
|
239
|
+
|
|
240
|
+
**B 族不是我们的问题,而且已被上游记录**:
|
|
241
|
+
|
|
242
|
+
- <https://github.com/deepseek-ai/deepseek-harness/discussions/1780>(切到第三方模型后多轮对话报同一个错)
|
|
243
|
+
- <https://github.com/deepseek-ai/deepseek-harness/discussions/231>(多轮会话丢 reasoning 块 → 400,
|
|
244
|
+
openai-responses 自定义提供商 —— 与我们的路由形态一致)
|
|
245
|
+
|
|
246
|
+
本机实验支持"是客户端丢块,不是网关挑剔":同一模型、同一 `effort=max`,第一轮拿到 `reasoning` 项(id `rs_…`),
|
|
247
|
+
第二轮**带上/不带**它都成功(两轮短对话测不出差异);失败只出现在长会话的**续写**上,正是"历史重建时丢块"的特征。
|
|
248
|
+
|
|
249
|
+
绕开办法:把这条路由的**思考关掉**(`/effort off`,没有思考就没有要回传的东西),或改用同一网关的
|
|
250
|
+
**`command-code-messages`**(anthropic-messages 风味,思考块带签名、harness 会正确回传),或者等上游修。
|
|
251
|
+
|
|
252
|
+
**插件会在失败时直接说这句话**(与 A 族同理):B 族 400 一到,TUI 会补一行
|
|
253
|
+
「这是上游的已知缺陷…绕开办法:`/effort off` 或 `command-code-messages`」并附 #1780/#231——它**不会**触发
|
|
254
|
+
`/retryauth` 的自动重试,因为这个 400 是确定性的(重发必然再失败)。
|
|
255
|
+
|
|
256
|
+
**A 族要主动维护**:补丁在依赖里,升级族就会丢。升级后跑一次
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
node scripts/patch-pi-ai-summary.mjs --check # 退出码 1 = 有文件需要修
|
|
260
|
+
node scripts/patch-pi-ai-summary.mjs # 修,自动留 <file>.bak(幂等)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
它覆盖 `openai-responses.js` 与 `azure-openai-responses.js`,并会在"上游改了形状、既不匹配原文也不匹配补丁后"
|
|
264
|
+
时**拒绝动手**并让你先读文件(而不是假装已经好了)。2026-09-29 首次运行修好了 `azure-*`(三处安装各一份)
|
|
265
|
+
与 `/usr/lib/node_modules` 那份未打的副本。
|
|
266
|
+
|
|
218
267
|
## 5. 明确不做(以及为什么)
|
|
219
268
|
|
|
220
269
|
- **内置 `--daemon` 常驻模式**:Host 不是服务。它是"某个会话的写者",靠 idle-exit 把写锁交还;把它变成常驻服务会让 Web UI 与其它窗口长期打不开同一会话。
|
|
@@ -225,6 +274,8 @@ AUTH 类错误**,所以一次瞬时 403 会直接终结整轮。要让它在"
|
|
|
225
274
|
|
|
226
275
|
- `/diag`:这条会话的通道、锁、Host 身份、判定链("会接入后台 Host,不要另开第二个窗口")。
|
|
227
276
|
- `/retryauth [on|off]`:要不要在"提供商侧鉴权失败"时自动重试一次(见 4.8);`/doctor` 看路由与凭据是否就位。
|
|
277
|
+
- 桌面版(官方 GUI 应用)与终端配置的关系、两个上游缺陷:见 [`desktop.md`](desktop.md)。
|
|
278
|
+
- **400 先分族**:`unknown field "summary"` 是本地 pi-ai 补丁丢了(4.9 A 族),`reasoning_text must be passed back` 是上游 harness 的 bug(4.9 B 族)。
|
|
228
279
|
- `/doctor`:profile 组合、依赖、兼容与 `dsh-scope` 副本数;`/doctor --fix` 修 profile 补丁(写前备份)。
|
|
229
280
|
- 真机验收脚本:`node scripts/tui-probe.mjs`(启动/缩放//diag//doctor//copy error//preset/鼠标模式/退出)、
|
|
230
281
|
`node scripts/tui-drop-probe.mjs`(杀掉窗口再接管,断言转录保留、可输入、无乱码)与
|
package/docs/terminals.md
CHANGED
|
@@ -124,3 +124,56 @@ Windows 与 Linux 的差异只在**改键**文件里:`/keys` 写出的键名
|
|
|
124
124
|
- **`/diag` 的终端行来自 Host 的环境**:重新接入(reattach)后它描述的是 Host 启动时所处的终端。
|
|
125
125
|
退出时的「离开备用屏」序列因此**无条件发出**——它落在没进过备用屏的终端上会被忽略,
|
|
126
126
|
而漏发会把用户留在无法滚动的屏幕里。
|
|
127
|
+
|
|
128
|
+
## 字符宽度:哪些算两格,以及为什么
|
|
129
|
+
|
|
130
|
+
终端里"一个字符占几格"没有统一答案,这个 TUI 的口径是:
|
|
131
|
+
|
|
132
|
+
| 类别 | 例子 | 预算 | 说明 |
|
|
133
|
+
|---|---|---|---|
|
|
134
|
+
| CJK / 全角 | `中`、`,`、`(` | 2 | 无争议 |
|
|
135
|
+
| emoji 符号 | `✖`、`⚠`、`▶`、`✅` | 2 | 终端用彩色 emoji 字形画它,比等宽字体宽;`pinEmojiCells` 用 VS15 + 预留空格把它**钉**在 2 格里 |
|
|
136
|
+
| **带圈/带括号的字母数字** | `①`-`⑳`、`⑴`、`⒈`、`ⓐ`、`❶`、`➀`、`Ⅰ` | **看终端** | 见下 |
|
|
137
|
+
| 本 TUI 自己的装饰 | `─`、`●`、`❯`、`·`、`▸`、`✓`、`⠋` | **1** | 它们来自等宽字体,按 2 格算会画出半宽横线、把光标挪到文字右边一格 |
|
|
138
|
+
|
|
139
|
+
**这两行(带圈字符、标点)是 2026-09-29 修的一个真实缺陷**:模型会写 `①`-`⑩`,而 CJK 字体的终端把它们画成两格,我们却按一格预算——于是每一行都比实际短一格。
|
|
140
|
+
与 emoji 不同,这类字符**没有变体选择符**可以"钉"宽度,只能跟着终端走。判定规则:
|
|
141
|
+
|
|
142
|
+
1. `DSH_TUI_AMBIGUOUS_WIDTH=1|2` 明确指定(覆盖一切);
|
|
143
|
+
2. 否则看 locale:`LC_ALL` / `LC_CTYPE` / `LANG` 是 **zh / ja / ko** ⇒ 按 2 格(该终端极可能用 CJK 字体);
|
|
144
|
+
3. 其它 ⇒ 按 1 格。
|
|
145
|
+
|
|
146
|
+
**UI 语言不参与判定**:一个中文读者在纯西文终端上看到的仍是窄字形,输入中文并不会改变字体度量。
|
|
147
|
+
若你的终端在英文 locale 下也把这类字符画成两格(或在中文 locale 下画成一格),用上面那个环境变量显式纠正即可。
|
|
148
|
+
|
|
149
|
+
### 先实测,再回落(2026-09-30 起)
|
|
150
|
+
|
|
151
|
+
locale 只是**猜测**,而"① 渲染错位"报了三次都出在猜上。现在由**显示中继**(持有终端的那一侧)直接问终端:
|
|
152
|
+
把 `①—…“”·•Ⅰ` 打到刚清空的一行上,发 `CSI 6n`,读回光标列——那个列就是终端刚刚花掉的格数(`src/glyph-measure.ts`)。
|
|
153
|
+
|
|
154
|
+
测量走 `TerminalInputPump.askPosition`,**应答由 pump 吞掉**——这不是实现细节:没人消费的光标应答会被终端回显成字面量 `^[[1;5R`,
|
|
155
|
+
第一版把探测放在启动器里裸写,就在进入 TUI 的过程中把这类异常字符重新漏了出来。测完的结论经**协议帧 `FRAME_METRICS`(12)** 交给
|
|
156
|
+
正在绘制的 Host,所以已在运行的旧 Host、resume 会话也能当场采纳并整体重绘(旧帧的行是按旧表算的,必须重绘而不是增量)。
|
|
157
|
+
|
|
158
|
+
- 终端推进 **2 格** → 按 2 格预算,不补空格;
|
|
159
|
+
- 终端推进 **1 格**(字形却画得更宽 → 就是你看到的"①和后一个字挤在一起") → 仍按 2 格预算,但**补一个预留空格**:
|
|
160
|
+
渲染时在字形后插一个空格,让终端实际花掉两格。这正是 emoji 那套"钉住"的另一半;圈号没有变体选择符可用,所以只剩这半招。
|
|
161
|
+
**宽度契约**:钉住前后 `displayWidth` 必须相等(那个空格算作同一个两格单元),否则填/截/光标会集体偏一格。
|
|
162
|
+
- 终端不答或答得既不匹配"全宽"也不匹配"全窄"(混排字体) → 什么都不决定,回落 locale 猜测。
|
|
163
|
+
- 想让探针完全不写字节(给断言 attach 字节序列的 harness):`DSH_TUI_NO_GLYPH_PROBE=1`。
|
|
164
|
+
|
|
165
|
+
另外两处必须留意:**绘制发生在分离 Host 里,而 Host 的 stdout 是中继 socket、不是终端**——所以实测由持有终端的窗口进程做,
|
|
166
|
+
结论经 `DSH_TUI_AMBIGUOUS_WIDTH` 传给 Host(spawn 时注入,见 `hostChildEnv`);没终端时一律按窄的一侧算(管道、日志、测试)。
|
|
167
|
+
手工纠正仍然可用: `DSH_TUI_AMBIGUOUS_WIDTH=1|2`。
|
|
168
|
+
|
|
169
|
+
涉及范围(`src/term-text.ts` 的 `AMBIGUOUS_WIDE_RANGES`),两类:
|
|
170
|
+
|
|
171
|
+
1. **圈号/字母数字**:Enclosed Alphanumerics(U+2460–U+24FF,`①`-`⑳`/`⑴`/`⒈`/`ⓐ`)、Dingbat 圈号(U+2776–U+2793,`❶`/`➀`)、罗马数字(U+2160–U+217F,`Ⅰ`)、
|
|
172
|
+
Enclosed Alphanumeric Supplement(含区域指示符,U+1F100–U+1F1FF)、Enclosed Ideographic Supplement(U+1F200–U+1F2FF)。
|
|
173
|
+
2. **CJK 全角标点**:`—` `–`(U+2013–U+2014)、`‘ ’ “ ”`(U+2018–U+201D)、`…`(U+2025–U+2026)、`•`(U+2022)、`‹ ›`(U+2039–U+203A)、`·`(U+00B7)。
|
|
174
|
+
这一类在中文语料里出现得比圈号多几个数量级——实测某会话里 `—` 出现 **31855 次**。
|
|
175
|
+
|
|
176
|
+
**刻意不算宽**的:Box-drawing(`─`)、几何装饰(`●` `▸`)、`❯` `✓` `✗` `★` 等 —— 它们是本 TUI 自己的字符或早已按真实终端实测为窄字形,
|
|
177
|
+
按两格算会把横线画成半宽、把光标挪到文字右边。
|
|
178
|
+
|
|
179
|
+
顺带修掉的一个连带缺陷:`foldInputView`(输入行折叠)原来把省略号标记硬写成 1 格,而 `…` 现在是 2 格 —— 折叠后的行会**溢出预算**、光标偏移一格。现在按标记的实测宽度计算。
|
package/lib/attach.js
CHANGED
|
@@ -70,7 +70,7 @@ export function createAttacher(deps) {
|
|
|
70
70
|
recoveryWindow.push(current);
|
|
71
71
|
return true;
|
|
72
72
|
};
|
|
73
|
-
const spawnHostAndRelay = async (sessionId, recover) => {
|
|
73
|
+
const spawnHostAndRelay = async (sessionId, recover, options = {}) => {
|
|
74
74
|
const live = deps.locksDisabled?.() === true ? undefined : await deps.inspectLiveHost(sessionId);
|
|
75
75
|
if (live?.kind === 'attachable') {
|
|
76
76
|
// The user asked for this session and is waiting: say so.
|
|
@@ -80,7 +80,7 @@ export function createAttacher(deps) {
|
|
|
80
80
|
if (live?.kind === 'zombie') {
|
|
81
81
|
throw new Error(deps.messages.zombie(sessionId, live.pid));
|
|
82
82
|
}
|
|
83
|
-
const spawned = deps.spawnHost(sessionId);
|
|
83
|
+
const spawned = deps.spawnHost(sessionId, options);
|
|
84
84
|
// The Host boots with the TTY in cooked mode: quiet it first so a reply
|
|
85
85
|
// still in flight cannot be echoed over the boot splash, and keep whatever
|
|
86
86
|
// the user types meanwhile instead of dropping it.
|
|
@@ -206,7 +206,7 @@ export function createAttacher(deps) {
|
|
|
206
206
|
};
|
|
207
207
|
return {
|
|
208
208
|
attachExisting,
|
|
209
|
-
attachOrSpawn: async (sessionId, recover = true) => {
|
|
209
|
+
attachOrSpawn: async (sessionId, recover = true, options = {}) => {
|
|
210
210
|
// A session another window is holding is not taken over by asking: this
|
|
211
211
|
// path is what a script or a `--resume=<id>` launch reaches, and there is
|
|
212
212
|
// nobody to confirm with. Attaching anyway silently kicked the window the
|
|
@@ -218,7 +218,7 @@ export function createAttacher(deps) {
|
|
|
218
218
|
if (live?.kind === 'attachable' && live.state === 'attached') {
|
|
219
219
|
throw new Error(deps.messages.attached(sessionId, live.pid));
|
|
220
220
|
}
|
|
221
|
-
await spawnHostAndRelay(sessionId, recover);
|
|
221
|
+
await spawnHostAndRelay(sessionId, recover, options);
|
|
222
222
|
},
|
|
223
223
|
get recoveries() {
|
|
224
224
|
return recoveryWindow.length;
|
package/lib/attach.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attach.js","sourceRoot":"","sources":["../src/attach.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,oFAAoF;AACpF,MAAM,CAAC,MAAM,yBAAyB,GAAG,KAAK,CAAA;AAC9C,6EAA6E;AAC7E,MAAM,CAAC,MAAM,uBAAuB,GAAG,KAAK,CAAA;AAC5C,8EAA8E;AAC9E,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAA;AAC9C,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAA;AACrC,4EAA4E;AAC5E,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAA;AAC3C,wEAAwE;AACxE,MAAM,gBAAgB,GAAG,GAAG,CAAA;AAE5B;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,CAAA;AAE3E;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,SAAuB,IAAI,CAAC,MAAM;IAC/E,MAAM,IAAI,GAAG,mBAAmB,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,CAAA;IACzE,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;IACxG,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,GAAG,GAAG,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAA;AAClD,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,kBAAkB,CAAC,KAAc,EAAE,SAAiB;IAClE,IAAI,SAAS,IAAI,yBAAyB;QAAE,OAAO,KAAK,CAAA;IACxD,MAAM,IAAI,GAAI,KAA2C,EAAE,IAAI,CAAA;IAC/D,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IACpC,OAAO,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,YAAY,IAAI,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,sBAAsB,CAAA;AAChH,CAAC;AA0FD,MAAM,UAAU,cAAc,CAAC,IAAkB;IAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;IAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,EAAE,QAAQ,IAAI,wBAAwB,CAAA;IACjE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,KAAK,IAAI,oBAAoB,CAAA;IACvD,MAAM,cAAc,GAAa,EAAE,CAAA;IAEnC,MAAM,eAAe,GAAG,GAAY,EAAE;QACpC,MAAM,OAAO,GAAG,GAAG,EAAE,CAAA;QACrB,OAAO,cAAc,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzF,cAAc,CAAC,KAAK,EAAE,CAAA;QACxB,CAAC;QACD,IAAI,cAAc,CAAC,MAAM,IAAI,KAAK;YAAE,OAAO,KAAK,CAAA;QAChD,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC5B,OAAO,IAAI,CAAA;IACb,CAAC,CAAA;IAED,MAAM,iBAAiB,GAAG,KAAK,
|
|
1
|
+
{"version":3,"file":"attach.js","sourceRoot":"","sources":["../src/attach.ts"],"names":[],"mappings":"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"}
|
package/lib/auth-failure.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Two failure classes that look alike in the transcript and need different fixes.
|
|
3
|
+
*
|
|
4
|
+
* 1. **Authentication** — `code: "AUTH"`, and the message says which side said
|
|
5
|
+
* no (below).
|
|
6
|
+
* 2. **Reasoning replay** — a 400 from a thinking-mode gateway that wants the
|
|
7
|
+
* previous turn's reasoning passed back; the harness drops it when it rebuilds
|
|
8
|
+
* a long multi-turn history. Reported upstream (deepseek-harness #1780, #231),
|
|
9
|
+
* so the advice is a workaround, not a fix.
|
|
10
|
+
*
|
|
2
11
|
* Where an authentication failure came from.
|
|
3
12
|
*
|
|
4
13
|
* A turn can die with `code: "AUTH"` for two very different reasons, and the
|
|
@@ -89,4 +98,31 @@ export function classifyAuthFailure(message) {
|
|
|
89
98
|
return { origin: 'provider', ...(status === undefined ? {} : { status }) };
|
|
90
99
|
return undefined;
|
|
91
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* A thinking-mode gateway refusing a follow-up because the reasoning is missing.
|
|
103
|
+
*
|
|
104
|
+
* Seen on the 0.2.0-rc line with the command-code route and `effort: max`
|
|
105
|
+
* (five times in one long session, always on a *continuation*, never on the
|
|
106
|
+
* first request of a turn):
|
|
107
|
+
*
|
|
108
|
+
* {"type":"invalid_request_error","code":"invalid_request_error",
|
|
109
|
+
* "message":"The `reasoning_text` in the thinking mode must be passed back
|
|
110
|
+
* to the API."}
|
|
111
|
+
*
|
|
112
|
+
* Both a live probe and the upstream reports agree on the shape: the client is
|
|
113
|
+
* expected to send the previous assistant turn's reasoning items back, and the
|
|
114
|
+
* harness does not do that once a session grows long. Nothing the reader can do
|
|
115
|
+
* fixes the client, but two workarounds exist — no thinking on that route, or
|
|
116
|
+
* the same gateway's Anthropic-flavoured route, which carries signed thinking
|
|
117
|
+
* blocks the harness does replay.
|
|
118
|
+
* @param message - the failure text the host attached to the turn.
|
|
119
|
+
* @returns true when this is that failure.
|
|
120
|
+
*/
|
|
121
|
+
export function isReasoningReplayFailure(message) {
|
|
122
|
+
const text = String(message ?? '');
|
|
123
|
+
if (text === '')
|
|
124
|
+
return false;
|
|
125
|
+
return /reasoning_text[^.]{0,80}must be passed back/iu.test(text)
|
|
126
|
+
|| /thinking mode must be passed back/iu.test(text);
|
|
127
|
+
}
|
|
92
128
|
//# sourceMappingURL=auth-failure.js.map
|
package/lib/auth-failure.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-failure.js","sourceRoot":"","sources":["../src/auth-failure.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"auth-failure.js","sourceRoot":"","sources":["../src/auth-failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAWH;;;;;;GAMG;AACH,MAAM,iBAAiB,GAAsB;IAC3C,yBAAyB;IACzB,6BAA6B;IAC7B,wBAAwB;IACxB,uBAAuB;IACvB,iBAAiB;IACjB,iCAAiC;IACjC,0BAA0B;IAC1B,gCAAgC;IAChC,qBAAqB;IACrB,iBAAiB;CAClB,CAAA;AAED,6DAA6D;AAC7D,MAAM,cAAc,GAAsB;IACxC,wCAAwC;IACxC,6EAA6E;IAC7E,6CAA6C;IAC7C,kDAAkD;IAClD,OAAO;IACP,+BAA+B;CAChC,CAAA;AAED,wEAAwE;AACxE,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,6EAA6E;IAC7E,wBAAwB;IACxB,MAAM,KAAK,GAAG,qCAAqC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACjE,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAA;IACpC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAA;IAClD,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAA;AACrD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAe;IACjD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;IAClC,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,SAAS,CAAA;IACxC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC7B,0EAA0E;IAC1E,uEAAuE;IACvE,IAAI,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAA;IAC/H,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAA;IAC3E,IAAI,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAA;IACrI,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAe;IACtD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;IAClC,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,KAAK,CAAA;IAC7B,OAAO,+CAA+C,CAAC,IAAI,CAAC,IAAI,CAAC;WAC5D,qCAAqC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACvD,CAAC"}
|
package/lib/commands.js
CHANGED
|
@@ -19,6 +19,8 @@ export const LOCAL_COMMANDS = [
|
|
|
19
19
|
{ name: 'diag', key: 'cmd.diag' },
|
|
20
20
|
{ name: 'disconnect', key: 'cmd.disconnect' },
|
|
21
21
|
{ name: 'retryauth', key: 'cmd.retryauth' },
|
|
22
|
+
{ name: 'theme', key: 'cmd.theme' },
|
|
23
|
+
{ name: 'cleanup', key: 'cmd.cleanup' },
|
|
22
24
|
{ name: 'notify', key: 'cmd.notify' },
|
|
23
25
|
{ name: 'approval', key: 'cmd.approval' },
|
|
24
26
|
{ name: 'view', key: 'cmd.view' },
|
package/lib/commands.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
|
package/lib/dialogs.js
CHANGED
|
@@ -90,12 +90,55 @@ export function moveQuestionCursor(dialog, delta) {
|
|
|
90
90
|
? (delta >= 0 ? visible[0] : visible[visible.length - 1])
|
|
91
91
|
: visible[Math.max(0, Math.min(visible.length - 1, at + delta))];
|
|
92
92
|
dialog.cursor = next ?? dialog.cursor;
|
|
93
|
+
dialog.onCursor?.(dialog.cursor);
|
|
93
94
|
if (dialog.question.multiSelect !== true) {
|
|
94
95
|
dialog.selected.clear();
|
|
95
96
|
dialog.selected.add(dialog.cursor);
|
|
96
97
|
}
|
|
97
98
|
return true;
|
|
98
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Which options a fresh question dialog starts with.
|
|
102
|
+
*
|
|
103
|
+
* The rule that caused a real mis-answer: a **multi-select** list used to inherit
|
|
104
|
+
* the single-select default of "the first option is already chosen", so a reader
|
|
105
|
+
* who moved the highlight to the row they wanted and pressed Enter answered with
|
|
106
|
+
* the first row instead — the `●` never left it. A multi-select starts empty
|
|
107
|
+
* unless the caller asks for a specific preselection; `questionSubmit` then falls
|
|
108
|
+
* back to the highlighted row, which is what pressing Enter means.
|
|
109
|
+
* @param question - the question being asked.
|
|
110
|
+
* @param preselected - an index the caller wants chosen up front, if any.
|
|
111
|
+
* @returns the indexes to select initially.
|
|
112
|
+
*/
|
|
113
|
+
export function initialQuestionSelection(question, preselected) {
|
|
114
|
+
const count = question.options?.length ?? 0;
|
|
115
|
+
if (count === 0)
|
|
116
|
+
return [];
|
|
117
|
+
const wanted = preselected !== undefined && preselected >= 0 && preselected < count ? preselected : undefined;
|
|
118
|
+
if (question.multiSelect === true)
|
|
119
|
+
return wanted === undefined ? [] : [wanted];
|
|
120
|
+
return [wanted ?? 0];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The circle drawn beside one option.
|
|
124
|
+
*
|
|
125
|
+
* `●` marks **what Enter will submit**, which is why it sits on the highlighted
|
|
126
|
+
* row while nothing is ticked — the mark follows the cursor, so what the reader
|
|
127
|
+
* sees is what the answer will be. A ticked row in a multi-select list carries
|
|
128
|
+
* `✓` instead: several rows can be chosen at once, and the circle cannot be in
|
|
129
|
+
* two places without lying about one of them.
|
|
130
|
+
* @param dialog - the open question dialog.
|
|
131
|
+
* @param index - the option index being drawn.
|
|
132
|
+
* @returns the marker for that row.
|
|
133
|
+
*/
|
|
134
|
+
export function questionOptionMarker(dialog, index) {
|
|
135
|
+
const toggled = dialog.selected.has(index);
|
|
136
|
+
if (dialog.question.multiSelect !== true)
|
|
137
|
+
return index === dialog.cursor ? '●' : '○';
|
|
138
|
+
if (toggled)
|
|
139
|
+
return '✓';
|
|
140
|
+
return index === dialog.cursor && dialog.selected.size === 0 ? '●' : '○';
|
|
141
|
+
}
|
|
99
142
|
/** Select option `index`: toggles in a multi-select list, replaces otherwise. */
|
|
100
143
|
export function selectQuestionOption(dialog, index) {
|
|
101
144
|
dialog.cursor = index;
|
package/lib/dialogs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialogs.js","sourceRoot":"","sources":["../src/dialogs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dialogs.js","sourceRoot":"","sources":["../src/dialogs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the TUI's bytes go when the parent is not a terminal.
|
|
3
|
+
*
|
|
4
|
+
* The plugin paints through a display relay, and a relay normally *is* a
|
|
5
|
+
* terminal: it writes ANSI to stdout, listens for `resize`, and puts stdin in raw
|
|
6
|
+
* mode. A GUI, a browser terminal or a test harness has none of those, but it can
|
|
7
|
+
* provide the same three things over pipes — bytes in, bytes out, and a size it
|
|
8
|
+
* reports as a protocol frame. This module is the switch that says "the parent is
|
|
9
|
+
* such a relay, do not require a TTY".
|
|
10
|
+
*
|
|
11
|
+
* Two entries, one mechanism and one sugar (as chosen when the mode was
|
|
12
|
+
* designed):
|
|
13
|
+
*
|
|
14
|
+
* - `DSH_TUI_DISPLAY=stdio` in the environment — the mechanism. Setting it *is*
|
|
15
|
+
* the parent's statement that it is a relay, so it is also what the no-TTY
|
|
16
|
+
* guard consults.
|
|
17
|
+
* - `--display stdio` on the command line — the sugar, declared in the profile's
|
|
18
|
+
* own grammar so it shows up in `--help` and a typo is an error rather than a
|
|
19
|
+
* silently inert plugin.
|
|
20
|
+
*
|
|
21
|
+
* @module dsh-ssh-tui/display-mode
|
|
22
|
+
*/
|
|
23
|
+
/** The environment variable that selects the display mode. */
|
|
24
|
+
export const DISPLAY_MODE_ENV = 'DSH_TUI_DISPLAY';
|
|
25
|
+
/**
|
|
26
|
+
* Parse one mode word.
|
|
27
|
+
* @param value - a value from the environment or a flag.
|
|
28
|
+
* @returns the mode, or undefined for anything else (the caller reports it).
|
|
29
|
+
*/
|
|
30
|
+
export function parseDisplayMode(value) {
|
|
31
|
+
const raw = String(value ?? '').trim().toLowerCase();
|
|
32
|
+
return raw === 'stdio' || raw === 'tty' ? raw : undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The `--display <mode>` value in an argument vector.
|
|
36
|
+
*
|
|
37
|
+
* Accepts both spellings a person may type (`--display stdio`,
|
|
38
|
+
* `--display=stdio`) and only ever reads the pair it owns — everything else is
|
|
39
|
+
* left where it is, because the same vector is parsed by the profile's own
|
|
40
|
+
* grammar and by the host.
|
|
41
|
+
* @param argv - an argument vector (defaults to this process's).
|
|
42
|
+
* @returns the value found, if any.
|
|
43
|
+
*/
|
|
44
|
+
export function displayModeFromArgv(argv = process.argv) {
|
|
45
|
+
for (let at = 0; at < argv.length; at += 1) {
|
|
46
|
+
const arg = argv[at] ?? '';
|
|
47
|
+
if (arg.startsWith('--display='))
|
|
48
|
+
return arg.slice('--display='.length);
|
|
49
|
+
if (arg === '--display') {
|
|
50
|
+
const next = argv[at + 1];
|
|
51
|
+
// A missing value is not silently "the default": leave it to the grammar,
|
|
52
|
+
// which turns it into a usage error.
|
|
53
|
+
return next === undefined || next.startsWith('-') ? '' : next;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Resolve the mode request from the environment and the command line.
|
|
60
|
+
*
|
|
61
|
+
* The environment wins when both are present: it is the mechanism, and a parent
|
|
62
|
+
* that sets it is describing the channel it is about to speak on, whereas a flag
|
|
63
|
+
* is a convenience the reader may have left in a wrapper script.
|
|
64
|
+
* @param env - the environment to read.
|
|
65
|
+
* @param argv - the argument vector to read.
|
|
66
|
+
* @returns the requested mode, plus the offending value when one was useless.
|
|
67
|
+
*/
|
|
68
|
+
export function requestedDisplayMode(env = process.env, argv = process.argv) {
|
|
69
|
+
const fromEnv = env[DISPLAY_MODE_ENV];
|
|
70
|
+
if (fromEnv !== undefined && String(fromEnv).trim() !== '') {
|
|
71
|
+
const mode = parseDisplayMode(fromEnv);
|
|
72
|
+
return mode === undefined ? { invalid: String(fromEnv).trim() } : { mode };
|
|
73
|
+
}
|
|
74
|
+
const fromArgv = displayModeFromArgv(argv);
|
|
75
|
+
if (fromArgv === undefined || fromArgv === '')
|
|
76
|
+
return {};
|
|
77
|
+
const mode = parseDisplayMode(fromArgv);
|
|
78
|
+
return mode === undefined ? { invalid: fromArgv } : { mode };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* A window-size report: `CSI 8 ; rows ; cols t`.
|
|
82
|
+
*
|
|
83
|
+
* This is the sequence a terminal sends when it is asked for its size, and it is
|
|
84
|
+
* also how a pipe parent says "the panel is now this big". A pipe has no
|
|
85
|
+
* `resize` event and no SIGWINCH, so without this the embedder's only way to
|
|
86
|
+
* resize the TUI would be to restart it.
|
|
87
|
+
*/
|
|
88
|
+
export const WINDOW_SIZE_REPORT = /\u001b\[8;(\d+);(\d+)t/gu;
|
|
89
|
+
/** Longest run that can still grow into a window-size report. */
|
|
90
|
+
const PARTIAL_REPORT_MAX = 16;
|
|
91
|
+
/** `ESC`, `ESC [`, `ESC [ 8`, `ESC [ 8 ; 24`, … — but not a finished report. */
|
|
92
|
+
const PARTIAL_REPORT = /^\u001b(?:\[(?:8(?:;(?:\d*(?:;\d*)?)?)?)?)?$/u;
|
|
93
|
+
/**
|
|
94
|
+
* Split a parent's input into keystrokes and size reports.
|
|
95
|
+
*
|
|
96
|
+
* A report can arrive split across two reads (`ESC [ 8 ; 2` then `4 ; 80 t`),
|
|
97
|
+
* and forwarding half of it as typing would send escape bytes to the model's
|
|
98
|
+
* prompt. The tail is held until the next read, exactly like the cursor-reply
|
|
99
|
+
* filter holds a half-arrived reply — with the same deadline that filter gets:
|
|
100
|
+
* a lone `ESC` matches the prefix, and the byte after it may never come, so a
|
|
101
|
+
* caller that forwards typing **must** release a {@link pending} run when its
|
|
102
|
+
* window passes (`flush()`). Waiting for the next read instead swallows the
|
|
103
|
+
* user's Escape key, and delivers it glued to whatever key follows as an Alt
|
|
104
|
+
* chord. `TerminalInputGuard` is the same filter with that timer attached.
|
|
105
|
+
* @returns a filter for one input stream.
|
|
106
|
+
*/
|
|
107
|
+
export function createParentResizeFilter() {
|
|
108
|
+
let held = '';
|
|
109
|
+
return {
|
|
110
|
+
push(text) {
|
|
111
|
+
const sizes = [];
|
|
112
|
+
const combined = held + text;
|
|
113
|
+
held = '';
|
|
114
|
+
let forward = '';
|
|
115
|
+
let cursor = 0;
|
|
116
|
+
for (const match of combined.matchAll(WINDOW_SIZE_REPORT)) {
|
|
117
|
+
const at = match.index ?? 0;
|
|
118
|
+
forward += combined.slice(cursor, at);
|
|
119
|
+
cursor = at + match[0].length;
|
|
120
|
+
// `CSI 8 ; rows ; cols t`: rows is the first number, columns the second.
|
|
121
|
+
const rows = Number(match[1]);
|
|
122
|
+
const columns = Number(match[2]);
|
|
123
|
+
// A zero dimension is a malformed report, not a request to hide the TUI.
|
|
124
|
+
if (rows > 0 && columns > 0)
|
|
125
|
+
sizes.push({ columns, rows });
|
|
126
|
+
}
|
|
127
|
+
const rest = combined.slice(cursor);
|
|
128
|
+
// Only the run from the last ESC can still grow into a report; everything
|
|
129
|
+
// before it is typing this read already settled.
|
|
130
|
+
const escapeAt = rest.lastIndexOf('\u001b');
|
|
131
|
+
const candidate = escapeAt === -1 ? '' : rest.slice(escapeAt);
|
|
132
|
+
const tail = candidate.length <= PARTIAL_REPORT_MAX && PARTIAL_REPORT.test(candidate) ? candidate : '';
|
|
133
|
+
held = tail;
|
|
134
|
+
return { forward: forward + rest.slice(0, rest.length - tail.length), sizes };
|
|
135
|
+
},
|
|
136
|
+
flush() {
|
|
137
|
+
const tail = held;
|
|
138
|
+
held = '';
|
|
139
|
+
return tail;
|
|
140
|
+
},
|
|
141
|
+
/** Is a run held that could still grow into a report? */
|
|
142
|
+
get pending() {
|
|
143
|
+
return held !== '';
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=display-mode.js.map
|