@hyzyn/dsh-tty 0.12.0 → 0.16.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 +153 -4
- package/client.js +242 -50
- package/lib/index.js +42 -4
- package/lib/index.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 +2 -2
package/README.md
CHANGED
|
@@ -28,7 +28,10 @@ SSH 侧同理(远程 tmux);0.12.0 起做了一轮**界面视觉 overhaul**
|
|
|
28
28
|
工具(见「开发」);0.11.0 起支持 **SSH 连接测试**——设置卡片连接簿
|
|
29
29
|
条目行内「测试」与 SSH 连接对话框「试连」,按 TCP → 主机密钥(TOFU)→
|
|
30
30
|
认证 逐段诊断连接(见下文 SSH 连接);SFTP 传输增加**可视化进度条**
|
|
31
|
-
(上传/下载百分比,服务端直传为不定进度脉冲,见下文 SFTP
|
|
31
|
+
(上传/下载百分比,服务端直传为不定进度脉冲,见下文 SFTP);0.13.0 / 0.14.0 /
|
|
32
|
+
0.15.0 / 0.16.0 依次开放客户端服务扩展点——连接栏按钮 `ttyConnbar`、命令标签
|
|
33
|
+
`ttyTerminal.open`、就地嵌入终端 `ttyTerminal.mount`、面板内挂载位 `ttyPanel`
|
|
34
|
+
(其他插件把自己的界面挂在终端右侧,终端保持可见,见下文「客户端服务」)。
|
|
32
35
|
|
|
33
36
|

|
|
34
37
|
|
|
@@ -143,10 +146,16 @@ SSH 会话同表调度:`tty_list` 里 `kind: 'ssh'` 的条目按 `target`
|
|
|
143
146
|
不动终端、不占会话名额,直接对 SSH 连接做远程文件操作(`ssh2` 的 sftp
|
|
144
147
|
subsystem,宿主半体 `src/sftp.ts`):
|
|
145
148
|
|
|
146
|
-

|
|
147
150
|
|
|
148
|
-

|
|
149
152
|
|
|
153
|
+
- **落点(0.16.0)**:终端面板开着且挂载位空着时,文件浏览**挂在终端下方**——路径栏 /
|
|
154
|
+
列表 / 传输进度占满整宽(文件列表是横向宽表,下方全宽比右侧窄栏好用,终端也保住
|
|
155
|
+
宽度不会折行;双栏两栏并排更需要这个宽度),终端继续可见可用。高度可拖、可折叠成
|
|
156
|
+
一条标题栏(折叠不关面板、不中断浏览);挂载位已被别的面板(如容器面板)占用、或
|
|
157
|
+
面板没开时,退回原来的居中对话框,**不会把别人的面板挤掉**。标题 / 折叠 / ✕ 由
|
|
158
|
+
tty 的挂载位提供;
|
|
150
159
|
- **入口**:① 标签栏「+」菜单的连接簿条目带 📂(按该条目打开文件浏览);
|
|
151
160
|
② SSH 连接对话框填好主机/认证后点「文件浏览」(不落连接簿也能浏览);
|
|
152
161
|
- **操作**:目录浏览(路径框回车跳转、`..(上级目录)`、单击文件即下载)、
|
|
@@ -313,11 +322,151 @@ tmux server(专用 socket `dsh-tty`,与用户自己的 tmux 完全隔离)
|
|
|
313
322
|
| `endOnPageClose` | `false` | 页面(最后一个连接)断开且保活期结束时,是否连 tmux 持久会话一起结束。默认 `false` = 留存可恢复;`true` = 页面关了就不保活(保活期内刷新仍可无缝接回) |
|
|
314
323
|
| `sftpLimits` | `{maxDownloadMb: 1024, maxUploadMb: 2048, maxUploadFiles: 1000}` | SFTP 传输限制(浏览器侧保护,均为 **0 = 不限**):`maxDownloadMb` 单文件下载上限(超限中止并提示用双栏 `⇦`/终端 scp)、`maxUploadMb` 单文件上传上限、`maxUploadFiles` 一次批量/拖拽上传的文件数上限;大文件请走双栏 `⇨/⇦` 服务端直传(字节不经过浏览器,不占内存) |
|
|
315
324
|
|
|
325
|
+
## 连接栏扩展点(客户端服务 `ttyConnbar`,0.13.0)
|
|
326
|
+
|
|
327
|
+
其他插件可以在 SSH 连接栏(SFTP / 隧道按钮那一行)追加自己的上下文按钮,而不需要
|
|
328
|
+
tty 认识它——tty 只暴露一个通用客户端服务。**内置动作(重新打开 / SFTP / 隧道)也
|
|
329
|
+
走同一条注册通道**,显示顺序 = 注册顺序;未注册任何扩展时行为与之前完全一致。
|
|
330
|
+
|
|
331
|
+
```js
|
|
332
|
+
// 消费方(如 dsh-docker)在自己的客户端半体里可选注入:tty 没装就不会触发
|
|
333
|
+
ctx.inject(['ttyConnbar'], (c) => {
|
|
334
|
+
const dispose = c.ttyConnbar.addAction(({ tab, spec, bookName, addAction }) => {
|
|
335
|
+
// 每次 renderConnbar 都会调用一次;自行决定这次要不要加按钮
|
|
336
|
+
if (spec.t !== 'ssh') return
|
|
337
|
+
addAction(iconSvg, '容器', '打开该主机的 Docker 容器面板', () => { /* 打开自己的面板 */ })
|
|
338
|
+
})
|
|
339
|
+
// 卸载时调用 dispose()
|
|
340
|
+
})
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
| 成员 | 说明 |
|
|
344
|
+
| --- | --- |
|
|
345
|
+
| `addAction(factory)` | 注册按钮工厂;返回注销函数。`factory` 收到 `{tab, spec, bookName, addAction}`:`spec` 是会话的 spawnSpec(`{t:'ssh', name?, host, port, username, ...}`),`bookName` 是连接簿条目名(内联连接为 `''`),`addAction(icon, label, title, onClick)` 用 tty 的按钮样式追加一个按钮 |
|
|
346
|
+
| `requestRender()` | 请 tty 重新渲染连接栏(消费方异步拿到新数据后需要按钮立刻出现时用) |
|
|
347
|
+
|
|
348
|
+
- 只在 **SSH 标签**上触发;本地标签的连接栏本身是隐藏的。
|
|
349
|
+
- 工厂抛错只记 `console.warn`,不影响连接栏与内置按钮。
|
|
350
|
+
- 服务名 `ttyConnbar` 未声明在 tty 的 `Context` 类型面上,消费方用字符串注入即可;
|
|
351
|
+
tty 未安装或版本 < 0.13.0 时注入不会触发,消费方需按可选依赖处理。
|
|
352
|
+
|
|
353
|
+
### 终端命令标签(客户端服务 `ttyTerminal`,0.14.0)
|
|
354
|
+
|
|
355
|
+
比连接栏按钮更进一步的扩展点:让其他插件**开一个标签直接跑一条命令**(典型用途
|
|
356
|
+
是 dsh-docker 的卡片「终端」按钮 → `docker exec -it <容器> sh`)。
|
|
357
|
+
|
|
358
|
+
```js
|
|
359
|
+
ctx.inject(['ttyTerminal'], (c) => {
|
|
360
|
+
c.ttyTerminal.open({
|
|
361
|
+
command: "docker exec -it 'ems-consumer-test' sh", // 必填,单行,≤2000 字符
|
|
362
|
+
book: 'HS-248', // 二选一:连接簿条目名 → SSH 标签
|
|
363
|
+
// spec: { host, port, username, auth, agentForward }, // 内联 SSH 字段
|
|
364
|
+
// (都不传 = 本地标签,用 cwd 指定工作目录)
|
|
365
|
+
label: 'ems-consumer-test · exec',
|
|
366
|
+
cwd: '/optional/local/cwd',
|
|
367
|
+
})
|
|
368
|
+
})
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
- 命令标签**不做 tmux 持久化**(命令短命,attach 无意义),也不走登录 shell;
|
|
372
|
+
SSH 侧用 `conn.exec(command, {pty})`,本地侧用 `sh -c 'export TERM=…; exec <command>'`。
|
|
373
|
+
- **命令标签会自动重开**:宿主重启 / 断线重连后 sid 已失效,客户端对
|
|
374
|
+
`spawnSpec.command` 的标签按原规格重新执行命令(普通非持久标签维持「点击重试」
|
|
375
|
+
的旧行为)。页面刷新后同样按原命令恢复。
|
|
376
|
+
- 命令来自**宿主侧插件**(不是远程用户输入),信任级与插件本身相同;tty 只校验
|
|
377
|
+
形状:非空、单行、长度 ≤2000(换行会破坏本地 `-c` 包装层)。
|
|
378
|
+
- 服务名 `ttyTerminal` 同样未声明在 `Context` 类型面上,按可选依赖注入;tty 未安装
|
|
379
|
+
或版本 < 0.14.0 时不会触发(dsh-docker 会退化为「复制命令」)。
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
### 就地嵌入终端(`ttyTerminal.mount`,0.15.0)
|
|
383
|
+
|
|
384
|
+
`open` 是「借 tty 的弹窗开一个标签」——用户的面板会被弹窗盖住/被顶到后面;如果消费方
|
|
385
|
+
希望**在自己的面板里就地放一块终端**(典型是 dsh-docker 的终端抽屉:看着容器日志直接
|
|
386
|
+
进容器敲命令,上下文不断),用 `mount`:
|
|
387
|
+
|
|
388
|
+
```js
|
|
389
|
+
ctx.inject(['ttyTerminal'], (c) => {
|
|
390
|
+
if (Number(c.ttyTerminal.version ?? 0) < 2) { /* 老版本:退回 open */ }
|
|
391
|
+
const dispose = c.ttyTerminal.mount(hostEl, {
|
|
392
|
+
command: "docker exec -it 'ems-consumer-test' sh", // 与 open 同一套 options
|
|
393
|
+
book: 'HS-248', // book > spec > 本地
|
|
394
|
+
label: 'ems-consumer-test · exec',
|
|
395
|
+
})
|
|
396
|
+
// 收起自己的抽屉时:
|
|
397
|
+
// dispose()
|
|
398
|
+
})
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
- `hostEl` 需是 `HTMLElement`:tty 往里塞一个绝对定位的 `.tt_term`,所以挂载点要
|
|
402
|
+
`position: relative` 且有确定尺寸(尺寸变化会被 ResizeObserver 接住并同步给 PTY)。
|
|
403
|
+
- 嵌入终端与标签**共用同一条 WebSocket 与会话表**,但语义是「别人面板里的一块终端」:
|
|
404
|
+
不进标签栏、不写 sessionStorage、不参与 tty 面板的显隐;**tty 面板关闭不会波及它**
|
|
405
|
+
(反过来说:嵌入会话在跑时,tty 的连接不会被关掉)。
|
|
406
|
+
- 断线自动重连、宿主重启后按原命令重跑、退出后点击遮罩重开,全部沿用既有逻辑;
|
|
407
|
+
`dispose()` 结束会话并卸载 DOM。挂载是**冷启动安全**的——tty 面板没开、连接还没建,
|
|
408
|
+
`mount` 也会把连接拉起来(创建帧先排队,`onopen` 后补发)。
|
|
409
|
+
- 嵌入终端没有 tty 面板头部的搜索/清屏/复制工具栏,Ctrl+F 交还浏览器。
|
|
410
|
+
|
|
411
|
+
### 面板内挂载位(客户端服务 `ttyPanel`,0.16.0)
|
|
412
|
+
|
|
413
|
+
> tty 自己的 SFTP 文件浏览也走这条通道(0.16.0):挂载位空着时挂在右侧,
|
|
414
|
+
> 被别的面板占用时退回对话框。
|
|
415
|
+
|
|
416
|
+
`mount` 解决的是「消费方给宿主,tty 往里塞终端」;`ttyPanel` 是它的**镜像**——tty 在
|
|
417
|
+
终端面板里给消费方一块位置,让消费方把自己的界面挂进来。典型场景:dsh-docker 从 SSH
|
|
418
|
+
连接栏点「容器」,容器面板挂在终端**右侧**,终端继续可见、可点、可输入,而不是被整屏
|
|
419
|
+
弹窗盖住(0.15 之前那正是用户的痛点)。
|
|
420
|
+
|
|
421
|
+
```js
|
|
422
|
+
ctx.inject(['ttyPanel'], (c) => {
|
|
423
|
+
// 面板没开(或 tty < 0.16)时走自己的弹窗
|
|
424
|
+
if (Number(c.ttyPanel.version ?? 0) < 1 || c.ttyPanel.isOpen() !== true) { /* fallback */ }
|
|
425
|
+
const pane = c.ttyPanel.mountPane({
|
|
426
|
+
title: 'Docker 容器', // 面板标题
|
|
427
|
+
hint: 'prod-web-01', // 标题右侧灰字(可选)
|
|
428
|
+
side: 'right', // 'right'(默认,竖向列表 / 列表+详情)| 'bottom'(横向宽表)
|
|
429
|
+
size: 520, // 初始尺寸 px:right = 宽度(默认 460)、bottom = 高度(默认 320)
|
|
430
|
+
min: 360, // 最小尺寸 px(可选,默认 280 / 160)
|
|
431
|
+
onClose: () => { /* tty 收掉面板时回调:在这里 unmount 自己的 React root */ },
|
|
432
|
+
})
|
|
433
|
+
createRoot(pane.element).render(<MyPanel />)
|
|
434
|
+
// 自己收起时:pane.dispose()
|
|
435
|
+
})
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
| 成员 | 说明 |
|
|
439
|
+
| --- | --- |
|
|
440
|
+
| `isOpen()` | 终端面板是否正开着(最小化不算)。消费方据此决定「挂进来」还是「走自己的弹窗」 |
|
|
441
|
+
| `mountPane(options)` | 在面板右侧 / 下方挂一块位置并返回 handle;**同时只挂一个**,后来的 `mountPane` 会先收掉前一个(并回调它的 `onClose`)。`side:'bottom'` 时占满宽度、由高度定尺寸(拖上边缘),折叠成一条标题栏 |
|
|
442
|
+
| `handle.element` | 消费方 render 的宿主(flex 纵向、已 `overflow:hidden`,撑满正文区) |
|
|
443
|
+
| `handle.setTitle(text)` / `setHint(text)` | 改标题 / 灰字 |
|
|
444
|
+
| `handle.expand() / collapse() / toggle() / isCollapsed()` | 折叠成 32px 窄条(竖排标题 + 展开/关闭按钮),终端立刻拿回宽度 |
|
|
445
|
+
| `handle.dispose()` | 消费方主动收起(幂等,不会再触发 `onClose`) |
|
|
446
|
+
|
|
447
|
+
- **生命周期**:侧栏的 DOM 长在终端弹窗里——最小化 / 恢复跟着面板走,消费方不用管;
|
|
448
|
+
面板被关闭(✕ / 宿主卸载)时 tty **先调 `onClose`**(消费方在这里 unmount),随后才摘 DOM。
|
|
449
|
+
- **拖尺寸**:右侧 pane 拖左边缘、下方 pane 拖上边缘(上限 = 面板卡片长边的 72%,
|
|
450
|
+
给终端留位置),尺寸在同一次页面会话内按方向分别记住。终端区的尺寸变化由既有
|
|
451
|
+
ResizeObserver 接住,自动 refit 并把新行列数同步给 PTY。
|
|
452
|
+
- **视口锚定**:pane 开合 / 拖尺寸会改变终端区高度,xterm 自己挪视口会让内容
|
|
453
|
+
「被往上顶」。refit 时按用户当时的意图锚定——本来贴着底部(在看最新输出)就
|
|
454
|
+
继续贴底,翻在历史里就锁住原来那几行,不会跳走。
|
|
455
|
+
- **方向怎么挑**:竖向列表 / 列表+详情(如容器面板)用 `right`;横向宽表(如 SFTP
|
|
456
|
+
文件列表、本地↔远程双栏)用 `bottom` —— 全宽摆得下更多列,也不挤终端宽度。
|
|
457
|
+
- 标题栏(标题 / 折叠 / ✕)由 tty 提供,消费方只管自己的正文;`onClose` 抛错只记
|
|
458
|
+
`console.warn`,不影响面板关闭。
|
|
459
|
+
|
|
460
|
+
> 契约版本:`ttyConnbar.version === 1`、`ttyTerminal.version === 2`(1 = 只有 `open`,
|
|
461
|
+
> 2 = 增加 `mount`)、`ttyPanel.version === 1`。消费方**按版本号判断能力**,不要用
|
|
462
|
+
> `typeof fn === 'function'` 之外的假设;老版本 tty 上 `inject` 依然会触发,但没有
|
|
463
|
+
> 对应字段。
|
|
464
|
+
|
|
316
465
|
## 帧协议(/api/dsh-tty/ws,JSON 文本帧;v3 = 单连接多会话 + 断线重连)
|
|
317
466
|
|
|
318
467
|
| 方向 | 帧 | 说明 |
|
|
319
468
|
| --- | --- | --- |
|
|
320
|
-
| C→S | `{t:'spawn', sid?, cols?, rows?, cwd?, persist?, persistName?}` | 创建会话;sid 缺省由宿主生成,cwd 缺省用配置兜底;`persist` + 稳定 `persistName`(0.10.0)= tmux 持久会话(`dsh-<名>`,需 persistence=tmux
|
|
469
|
+
| 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)= 直接跑一条命令(不做持久化) |
|
|
321
470
|
| 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 托管) |
|
|
322
471
|
| C→S | `{t:'input', sid?, d}` | 按键/粘贴数据 |
|
|
323
472
|
| C→S | `{t:'resize', sid?, cols, rows}` | 面板尺寸变化 |
|