@hyzyn/dsh-tty 0.11.0 → 0.15.0
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.md +130 -5
- package/client.js +1868 -38
- package/lib/index.js +87 -11
- package/lib/index.js.map +1 -1
- package/lib/sftp.d.ts +52 -2
- package/lib/sftp.js +197 -12
- package/lib/sftp.js.map +1 -1
- package/lib/shell-integration.d.ts +7 -0
- package/lib/shell-integration.js +10 -0
- package/lib/shell-integration.js.map +1 -1
- package/lib/ssh.d.ts +7 -0
- package/lib/ssh.js +16 -1
- package/lib/ssh.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -21,7 +21,11 @@ xterm.js 全交互终端(node-pty 真实 PTY,WebGL 渲染器加速),支
|
|
|
21
21
|
面板头部压缩为「标签行 + SSH 连接栏」两行;0.10.0 起支持**会话持久化
|
|
22
22
|
(tmux)**——「持久终端」标签由 tmux server(专用 socket)托管,断线保活
|
|
23
23
|
超时、甚至宿主重启后重开标签即按名接回原现场(正在跑的程序原样存活),
|
|
24
|
-
SSH 侧同理(远程 tmux);0.
|
|
24
|
+
SSH 侧同理(远程 tmux);0.12.0 起做了一轮**界面视觉 overhaul**——样式表
|
|
25
|
+
独立成 `client-src/tty.css` 并引入 `--tt-*` 令牌层(圆角/控件高度/间距/
|
|
26
|
+
动效统一,颜色全部派生自 DSH 皮肤 token,明暗主题自动跟随)、图标全面矢量
|
|
27
|
+
化、遮罩/toast/SFTP/连接栏等交互面重排,并新增 `pnpm preview` 截图回归
|
|
28
|
+
工具(见「开发」);0.11.0 起支持 **SSH 连接测试**——设置卡片连接簿
|
|
25
29
|
条目行内「测试」与 SSH 连接对话框「试连」,按 TCP → 主机密钥(TOFU)→
|
|
26
30
|
认证 逐段诊断连接(见下文 SSH 连接);SFTP 传输增加**可视化进度条**
|
|
27
31
|
(上传/下载百分比,服务端直传为不定进度脉冲,见下文 SFTP)。
|
|
@@ -154,8 +158,14 @@ subsystem,宿主半体 `src/sftp.ts`):
|
|
|
154
158
|
- **传输进度条(0.11.0)**:底部状态行右侧新增细进度条 + 百分比——上传按
|
|
155
159
|
XHR 流式进度(多文件带 `i/n · 文件名` 标签);下载改为流式读
|
|
156
160
|
`response.body`,按响应 `content-length` 实时算百分比(无长度时降级为已传
|
|
157
|
-
|
|
158
|
-
|
|
161
|
+
字节文本);
|
|
162
|
+
- **取消传输(0.12.0)**:进度条右侧 ✕——**上传**中断在途 XHR(批次里剩余
|
|
163
|
+
文件一并跳过);**下载**用 `AbortController` 打断流式读取;**双栏 ⇨/⇦
|
|
164
|
+
直传**改为服务端任务化(start 返回 jobId → 400ms 轮询真实字节进度 → ✕ 打
|
|
165
|
+
cancel 中止),不再是一个打不断的同步 HTTP 请求。取消后**半截文件自动
|
|
166
|
+
删除**(上传的远端残留文件 / 下载的本机残留文件;清理失败只记日志),状态行
|
|
167
|
+
走「已取消」而非红色失败态;关闭文件浏览对话框也会收掉在途传输,不留
|
|
168
|
+
「看不见但还在写远端」的后台搬运;
|
|
159
169
|
- **连接管理**:懒连接池——首次操作才建 SSH 连接,空闲 120 秒自动回收,
|
|
160
170
|
断开后下次操作自动重连;连接簿条目在每次(重)连接时实时解析(改密码
|
|
161
171
|
后自动用新凭证);TOFU 与终端会话/隧道共用同一份 `hostKeys` 钉扎,指纹
|
|
@@ -303,11 +313,101 @@ tmux server(专用 socket `dsh-tty`,与用户自己的 tmux 完全隔离)
|
|
|
303
313
|
| `endOnPageClose` | `false` | 页面(最后一个连接)断开且保活期结束时,是否连 tmux 持久会话一起结束。默认 `false` = 留存可恢复;`true` = 页面关了就不保活(保活期内刷新仍可无缝接回) |
|
|
304
314
|
| `sftpLimits` | `{maxDownloadMb: 1024, maxUploadMb: 2048, maxUploadFiles: 1000}` | SFTP 传输限制(浏览器侧保护,均为 **0 = 不限**):`maxDownloadMb` 单文件下载上限(超限中止并提示用双栏 `⇦`/终端 scp)、`maxUploadMb` 单文件上传上限、`maxUploadFiles` 一次批量/拖拽上传的文件数上限;大文件请走双栏 `⇨/⇦` 服务端直传(字节不经过浏览器,不占内存) |
|
|
305
315
|
|
|
316
|
+
## 连接栏扩展点(客户端服务 `ttyConnbar`,0.13.0)
|
|
317
|
+
|
|
318
|
+
其他插件可以在 SSH 连接栏(SFTP / 隧道按钮那一行)追加自己的上下文按钮,而不需要
|
|
319
|
+
tty 认识它——tty 只暴露一个通用客户端服务。**内置动作(重新打开 / SFTP / 隧道)也
|
|
320
|
+
走同一条注册通道**,显示顺序 = 注册顺序;未注册任何扩展时行为与之前完全一致。
|
|
321
|
+
|
|
322
|
+
```js
|
|
323
|
+
// 消费方(如 dsh-docker)在自己的客户端半体里可选注入:tty 没装就不会触发
|
|
324
|
+
ctx.inject(['ttyConnbar'], (c) => {
|
|
325
|
+
const dispose = c.ttyConnbar.addAction(({ tab, spec, bookName, addAction }) => {
|
|
326
|
+
// 每次 renderConnbar 都会调用一次;自行决定这次要不要加按钮
|
|
327
|
+
if (spec.t !== 'ssh') return
|
|
328
|
+
addAction(iconSvg, '容器', '打开该主机的 Docker 容器面板', () => { /* 打开自己的面板 */ })
|
|
329
|
+
})
|
|
330
|
+
// 卸载时调用 dispose()
|
|
331
|
+
})
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
| 成员 | 说明 |
|
|
335
|
+
| --- | --- |
|
|
336
|
+
| `addAction(factory)` | 注册按钮工厂;返回注销函数。`factory` 收到 `{tab, spec, bookName, addAction}`:`spec` 是会话的 spawnSpec(`{t:'ssh', name?, host, port, username, ...}`),`bookName` 是连接簿条目名(内联连接为 `''`),`addAction(icon, label, title, onClick)` 用 tty 的按钮样式追加一个按钮 |
|
|
337
|
+
| `requestRender()` | 请 tty 重新渲染连接栏(消费方异步拿到新数据后需要按钮立刻出现时用) |
|
|
338
|
+
|
|
339
|
+
- 只在 **SSH 标签**上触发;本地标签的连接栏本身是隐藏的。
|
|
340
|
+
- 工厂抛错只记 `console.warn`,不影响连接栏与内置按钮。
|
|
341
|
+
- 服务名 `ttyConnbar` 未声明在 tty 的 `Context` 类型面上,消费方用字符串注入即可;
|
|
342
|
+
tty 未安装或版本 < 0.13.0 时注入不会触发,消费方需按可选依赖处理。
|
|
343
|
+
|
|
344
|
+
### 终端命令标签(客户端服务 `ttyTerminal`,0.14.0)
|
|
345
|
+
|
|
346
|
+
比连接栏按钮更进一步的扩展点:让其他插件**开一个标签直接跑一条命令**(典型用途
|
|
347
|
+
是 dsh-docker 的卡片「终端」按钮 → `docker exec -it <容器> sh`)。
|
|
348
|
+
|
|
349
|
+
```js
|
|
350
|
+
ctx.inject(['ttyTerminal'], (c) => {
|
|
351
|
+
c.ttyTerminal.open({
|
|
352
|
+
command: "docker exec -it 'ems-consumer-test' sh", // 必填,单行,≤2000 字符
|
|
353
|
+
book: 'HS-248', // 二选一:连接簿条目名 → SSH 标签
|
|
354
|
+
// spec: { host, port, username, auth, agentForward }, // 内联 SSH 字段
|
|
355
|
+
// (都不传 = 本地标签,用 cwd 指定工作目录)
|
|
356
|
+
label: 'ems-consumer-test · exec',
|
|
357
|
+
cwd: '/optional/local/cwd',
|
|
358
|
+
})
|
|
359
|
+
})
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
- 命令标签**不做 tmux 持久化**(命令短命,attach 无意义),也不走登录 shell;
|
|
363
|
+
SSH 侧用 `conn.exec(command, {pty})`,本地侧用 `sh -c 'export TERM=…; exec <command>'`。
|
|
364
|
+
- **命令标签会自动重开**:宿主重启 / 断线重连后 sid 已失效,客户端对
|
|
365
|
+
`spawnSpec.command` 的标签按原规格重新执行命令(普通非持久标签维持「点击重试」
|
|
366
|
+
的旧行为)。页面刷新后同样按原命令恢复。
|
|
367
|
+
- 命令来自**宿主侧插件**(不是远程用户输入),信任级与插件本身相同;tty 只校验
|
|
368
|
+
形状:非空、单行、长度 ≤2000(换行会破坏本地 `-c` 包装层)。
|
|
369
|
+
- 服务名 `ttyTerminal` 同样未声明在 `Context` 类型面上,按可选依赖注入;tty 未安装
|
|
370
|
+
或版本 < 0.14.0 时不会触发(dsh-docker 会退化为「复制命令」)。
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
### 就地嵌入终端(`ttyTerminal.mount`,0.15.0)
|
|
374
|
+
|
|
375
|
+
`open` 是「借 tty 的弹窗开一个标签」——用户的面板会被弹窗盖住/被顶到后面;如果消费方
|
|
376
|
+
希望**在自己的面板里就地放一块终端**(典型是 dsh-docker 的终端抽屉:看着容器日志直接
|
|
377
|
+
进容器敲命令,上下文不断),用 `mount`:
|
|
378
|
+
|
|
379
|
+
```js
|
|
380
|
+
ctx.inject(['ttyTerminal'], (c) => {
|
|
381
|
+
if (Number(c.ttyTerminal.version ?? 0) < 2) { /* 老版本:退回 open */ }
|
|
382
|
+
const dispose = c.ttyTerminal.mount(hostEl, {
|
|
383
|
+
command: "docker exec -it 'ems-consumer-test' sh", // 与 open 同一套 options
|
|
384
|
+
book: 'HS-248', // book > spec > 本地
|
|
385
|
+
label: 'ems-consumer-test · exec',
|
|
386
|
+
})
|
|
387
|
+
// 收起自己的抽屉时:
|
|
388
|
+
// dispose()
|
|
389
|
+
})
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
- `hostEl` 需是 `HTMLElement`:tty 往里塞一个绝对定位的 `.tt_term`,所以挂载点要
|
|
393
|
+
`position: relative` 且有确定尺寸(尺寸变化会被 ResizeObserver 接住并同步给 PTY)。
|
|
394
|
+
- 嵌入终端与标签**共用同一条 WebSocket 与会话表**,但语义是「别人面板里的一块终端」:
|
|
395
|
+
不进标签栏、不写 sessionStorage、不参与 tty 面板的显隐;**tty 面板关闭不会波及它**
|
|
396
|
+
(反过来说:嵌入会话在跑时,tty 的连接不会被关掉)。
|
|
397
|
+
- 断线自动重连、宿主重启后按原命令重跑、退出后点击遮罩重开,全部沿用既有逻辑;
|
|
398
|
+
`dispose()` 结束会话并卸载 DOM。挂载是**冷启动安全**的——tty 面板没开、连接还没建,
|
|
399
|
+
`mount` 也会把连接拉起来(创建帧先排队,`onopen` 后补发)。
|
|
400
|
+
- 嵌入终端没有 tty 面板头部的搜索/清屏/复制工具栏,Ctrl+F 交还浏览器。
|
|
401
|
+
|
|
402
|
+
> 契约版本:`ttyConnbar.version === 1`、`ttyTerminal.version === 2`(1 = 只有 `open`,
|
|
403
|
+
> 2 = 增加 `mount`)。消费方**按版本号判断能力**,不要用 `typeof fn === 'function'`
|
|
404
|
+
> 之外的假设;老版本 tty 上 `inject` 依然会触发,但没有 `mount` 字段。
|
|
405
|
+
|
|
306
406
|
## 帧协议(/api/dsh-tty/ws,JSON 文本帧;v3 = 单连接多会话 + 断线重连)
|
|
307
407
|
|
|
308
408
|
| 方向 | 帧 | 说明 |
|
|
309
409
|
| --- | --- | --- |
|
|
310
|
-
| C→S | `{t:'spawn', sid?, cols?, rows?, cwd?, persist?, persistName?}` | 创建会话;sid 缺省由宿主生成,cwd 缺省用配置兜底;`persist` + 稳定 `persistName`(0.10.0)= tmux 持久会话(`dsh-<名>`,需 persistence=tmux
|
|
410
|
+
| C→S | `{t:'spawn', sid?, cols?, rows?, cwd?, persist?, persistName?, command?}` | 创建会话;sid 缺省由宿主生成,cwd 缺省用配置兜底;`persist` + 稳定 `persistName`(0.10.0)= tmux 持久会话(`dsh-<名>`,需 persistence=tmux);`command`(0.14.0)= 直接跑一条命令(不做持久化) |
|
|
311
411
|
| C→S | `{t:'ssh', sid?, cols?, rows?, name? \| host, username, …, persist?, persistName?}` | 创建 SSH 会话(ssh2 原生);`name` 引用连接簿条目作基底,内联 `host/port/username/auth/keyPath/passphrase/password/agentForward` 可逐项覆盖;`persist` 语义同 spawn(远程 tmux 托管) |
|
|
312
412
|
| C→S | `{t:'input', sid?, d}` | 按键/粘贴数据 |
|
|
313
413
|
| C→S | `{t:'resize', sid?, cols, rows}` | 面板尺寸变化 |
|
|
@@ -340,11 +440,36 @@ pnpm --filter @hyzyn/dsh-tty integration # 集成测试:真实插件 × 真
|
|
|
340
440
|
pnpm --filter @hyzyn/dsh-tty live # 对运行中的 dsh web 做存活冒烟
|
|
341
441
|
pnpm --filter @hyzyn/dsh-tty tui # TUI 冒烟:vim/nano 全屏渲染
|
|
342
442
|
pnpm --filter @hyzyn/dsh-tty ssh-smoke # SSH 冒烟:内存 SSH server(ssh2.Server)× 真实 spawnSsh 端到端(需先 build)
|
|
443
|
+
pnpm --filter @hyzyn/dsh-tty preview # 视觉预览:headless Chrome 逐场景截图(见下)
|
|
343
444
|
```
|
|
344
445
|
|
|
345
|
-
浏览器半体源码在 `client-src/index.js
|
|
446
|
+
浏览器半体源码在 `client-src/index.js`,样式表独立成 `client-src/tty.css`(由
|
|
447
|
+
esbuild 的 text loader 内联进 `client.js`)。构建产物 `client.js`(含 xterm 内核),
|
|
346
448
|
改客户端后需重新 `pnpm build` 并刷新页面(可能需硬刷新)。
|
|
347
449
|
|
|
450
|
+
### 视觉预览 / 截图回归(`scripts/preview.mjs`)
|
|
451
|
+
|
|
452
|
+
改样式不该靠「刷新页面看一眼」:脚本把 `client.js` 装进一个纯静态夹具页
|
|
453
|
+
(`scripts/preview/harness.html` + `mock-host.js` 伪造的 DSH 宿主:module
|
|
454
|
+
loader / fetch / WebSocket),用 headless Chrome 把 14 个界面状态逐个渲染并
|
|
455
|
+
截图到 `packages/tty/.preview/shots/`:
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
node scripts/preview.mjs # 全场景
|
|
459
|
+
node scripts/preview.mjs local menu ssh # 指定场景
|
|
460
|
+
node scripts/preview.mjs --list # 列出场景
|
|
461
|
+
node scripts/preview.mjs --theme=light # 浅色主题
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
覆盖:本地终端 / 多标签 + SSH 连接栏 / 「+」菜单 / SSH 对话框(新建、编辑)/
|
|
465
|
+
设置卡片 / SFTP(单窗体、双栏)/ 最小化徽标 / 退出与错误遮罩 / 隧道弹层 /
|
|
466
|
+
搜索框 / toast。夹具还会把 `--dsw-*` 皮肤变量与真实界面一并渲染,因此能验
|
|
467
|
+
「明暗主题切换后是否还有白色面板」这类问题。产物目录 `.preview/` 已 gitignore。
|
|
468
|
+
|
|
469
|
+
> 夹具需要 Chrome/Chromium(默认找 playwright 缓存的 Chrome for Testing,
|
|
470
|
+
> 也可用 `CHROME_PATH` 指定)。若宿主环境限制了 Chrome 的沙箱(子进程被
|
|
471
|
+
> 拒),需要放开后运行,否则浏览器起不来。
|
|
472
|
+
|
|
348
473
|
## 已知限制
|
|
349
474
|
|
|
350
475
|
- **resize 为内部耦合**:DSH 的 `spawnTerminal` handle 未暴露 resize,
|