@hyzyn/dsh-tty 0.19.1 → 0.19.3

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 CHANGED
@@ -179,12 +179,12 @@ lacks that service the box is switched back off and disabled, with a note that o
179
179
  empty or 22 (the default)** — matching the connecting side's `spec.port ?? 22` (empty means 22), so
180
180
  “empty / `22` / `" 22 "`” all collapse into one key and the same account never ends up with two names or one
181
181
  password stored twice; a non-default port does take part (the same host on another port is often a different
182
- box behind NAT). `field` is `PASSWORD` / `PASSPHRASE`; e.g. `hsadmin@192.168.80.248:22` →
182
+ box behind NAT). `field` is `PASSWORD` / `PASSPHRASE`; e.g. `hsadmin@192.0.2.10:22` →
183
183
  `DSH_TTY_HSADMIN_192_168_80_248_PASSWORD`. It is **derived from the resource identity and carries no hash** — the
184
184
  same school as git-credential-store's `protocol://username@host` and docker credential helpers'
185
185
  `ServerURL` + `Username`: host and username **are ASCII identifiers already**, so nothing needs sanitizing and
186
186
  nothing needs a hash to disambiguate. The old hash-based version was patching over "sanitize a human label into a
187
- key": the reference grammar accepts ASCII only, so `HS 248` / `HS-248` / `HS_248` collapse to exactly the same
187
+ key": the reference grammar accepts ASCII only, so `HS 248` / `lab-a` / `HS_248` collapse to exactly the same
188
188
  string and only a hash could stop them silently overwriting each other. A resource identity has no such trap — a
189
189
  collision can only happen for **the same host, the same user, the same port**, which is the same password by
190
190
  definition (sharing it is correct behaviour). **The connection name never takes part in the key**, so renaming a
@@ -491,12 +491,24 @@ session belongs to (visually aligned with FinalShell’s session monitor bar):
491
491
  session exit, orphan reaping, plugin disable and configuration off all stop the meter and close the remote
492
492
  channel, leaving no timers or remote loops.
493
493
  - **Best-effort**: a field that cannot be obtained is omitted (the frontend shows “n/a”); a collection
494
- failure silently stops the meter and hides the whole status bar (it collapses after 3s without a new
495
- frame) — it never writes to the PTY and never raises an error. Progress-bar thresholds:
496
- <70 normal / 70~90 yellow / >90 red.
494
+ failure silently stops the meter and hides the whole status bar (it collapses after **8s** without a new
495
+ frame — not 3s: when a sampling subprocess on the host occasionally slows down, frames get further apart,
496
+ and too tight a window makes the whole bar blink) — it never writes to the PTY and never raises an error.
497
+ Progress-bar thresholds: <70 normal / 70~90 yellow / >90 red.
498
+ - **A slow command cannot stall the cadence**: subprocess-backed fields (df / netstat / vm_stat) are awaited
499
+ only on the very first sample; afterwards the sampler uses the last known value and refreshes in the
500
+ background, so a command that hangs on some machine only leaves that one field briefly stale instead of
501
+ stretching the one-frame-per-second timeline (measured: 3000ms → 3~12ms per frame).
497
502
  - **Hot effect**: turning `statsEnabled` off stops collection at once (the status bar disappears and no more
498
503
  stats frames go over the WS), and turning it back on restores automatically from the subscriptions still in
499
504
  place — no restart and no need to reopen tabs.
505
+ - **Fixed slots + incremental repaint**: every value owns a fixed-width character slot (right
506
+ aligned, so even the widest form only takes that one slot), and the items are built once — afterwards only
507
+ text that actually changed is written. Hence `CPU 5% → 12%`, `TCP 36 → 1024` and
508
+ `memory 9.2 GB → 17.8 GB` no longer push every following item sideways (which showed up as the whole bar
509
+ jumping once a second), the progress-bar width can finally animate through its CSS transition, and
510
+ horizontally scrolling to `Network` in a narrow window is no longer snapped back to the start by the next
511
+ refresh.
500
512
 
501
513
  ## Configuration (Settings → Plugins → “Terminal Panel”, saving takes effect immediately)
502
514
 
@@ -559,7 +571,7 @@ single command** (the typical use is dsh-docker’s card “Terminal” button
559
571
  ctx.inject(['ttyTerminal'], (c) => {
560
572
  c.ttyTerminal.open({
561
573
  command: "docker exec -it 'ems-consumer-test' sh", // required, single line, ≤2000 characters
562
- book: 'HS-248', // one of the two: connection-book entry name → SSH tab
574
+ book: 'lab-a', // one of the two: connection-book entry name → SSH tab
563
575
  // spec: { host, port, username, auth, agentForward }, // inline SSH fields
564
576
  // (neither = local tab, with cwd giving the working directory)
565
577
  label: 'ems-consumer-test · exec',
@@ -592,7 +604,7 @@ ctx.inject(['ttyTerminal'], (c) => {
592
604
  if (Number(c.ttyTerminal.version ?? 0) < 2) { /* older version: fall back to open */ }
593
605
  const dispose = c.ttyTerminal.mount(hostEl, {
594
606
  command: "docker exec -it 'ems-consumer-test' sh", // the same set of options as open
595
- book: 'HS-248', // book > spec > local
607
+ book: 'lab-a', // book > spec > local
596
608
  label: 'ems-consumer-test · exec',
597
609
  })
598
610
  // When collapsing your own drawer:
@@ -628,8 +640,8 @@ A mount slot is **connection-scoped**: its credentials / target come from the te
628
640
  So since 0.19.0 every pane records its **owner tab** (`options.ownerSid`, defaulting to the active tab at
629
641
  mount time): switching to another tab **hides** the pane (`data-dock-hidden`; its DOM and your rendered tree
630
642
  survive, in-flight transfers keep running) and switching back restores the scene; closing the owner tab tears
631
- the pane down. Without this, the pane stayed put across a tab switch — its title read `SFTP · cdc-test-161`
632
- while the active tab was `192.168.80.248`, leaving **another host**’s file listing on screen (worst case:
643
+ the pane down. Without this, the pane stayed put across a tab switch — its title read `SFTP · lab-b`
644
+ while the active tab was `192.0.2.10`, leaving **another host**’s file listing on screen (worst case:
633
645
  uploading to the wrong host). Passing `ownerSid: null` means “owned by no tab” (always visible); that is what
634
646
  happens automatically when the panel is open with no tabs at all, and a consumer can use it to declare “this
635
647
  pane has nothing to do with tabs, do not hide it on a switch”.
package/README.md CHANGED
@@ -166,12 +166,12 @@ SSH 会话同表调度:`tty_list` 里 `kind: 'ssh'` 的条目按 `target`
166
166
  - **名字**(规则恒定):`DSH_TTY_<用户名>_<主机>[_<端口>]_<字段>`,**端口留空或 22(默认)时省略**
167
167
  —— 与连接侧的 `spec.port ?? 22` 一致(留空即 22),所以"留空 / `22` / `" 22 "`"三种写法归成同一个键,
168
168
  同一个账号不会有两个名字、同一个密码不会存两份;非默认端口进键(同一主机不同端口常是 NAT 后面的
169
- 不同盒子)。字段 = `PASSWORD` / `PASSPHRASE`;例 `hsadmin@192.168.80.248:22` →
169
+ 不同盒子)。字段 = `PASSWORD` / `PASSPHRASE`;例 `hsadmin@192.0.2.10:22` →
170
170
  `DSH_TTY_HSADMIN_192_168_80_248_PASSWORD`。**按资源身份派生、不带哈希**——与
171
171
  git-credential-store 的 `protocol://username@host`、docker credential helpers 的
172
172
  `ServerURL` + `Username` 同一派:host 与 username **本来就是 ASCII 标识符**,不需要清洗、
173
173
  也就不需要哈希兜底。曾经的哈希版是在补救"把人类标签清洗成键":引用文法只认 ASCII,
174
- `HS 248` / `HS-248` / `HS_248` 折出来完全一样,只能靠哈希避免静默覆盖 ✗。资源身份没有这个
174
+ `HS 248` / `lab-a` / `HS_248` 折出来完全一样,只能靠哈希避免静默覆盖 ✗。资源身份没有这个
175
175
  死结——撞名只可能发生在**同一主机、同一用户、同一端口**,而那本来就该是同一个密码(共享是
176
176
  正确行为)。**连接名完全不参与键**,所以改连接名/改备注都不会换键。空主机或空用户名则拒绝
177
177
  存入(键的全部来源,缺一就退化成常量)。代价是可读性弱于人类标签:本对话框的「存入」**只按
@@ -436,10 +436,19 @@ tmux server(专用 socket `dsh-tty`,与用户自己的 tmux 完全隔离)
436
436
  宿主首个订阅才起采集;退订清零、会话退出、孤儿回收、插件禁用、配置关闭
437
437
  都会停表并关掉远端 channel,不留定时器/远端循环。
438
438
  - **best-effort**:某项拿不到就省略该字段(前端显示「无」),采集失败静默停表、
439
- 状态条整条隐藏(3s 收不到新帧即收起)——绝不写 PTY、绝不弹错误。进度条阈值
439
+ 状态条整条隐藏(8s 收不到新帧即收起——**不是 3s**:宿主某个采集子进程偶发变慢时帧间隔
440
+ 会被拉长,窗口太紧就会整条一闪一闪)——绝不写 PTY、绝不弹错误。进度条阈值
440
441
  配色:<70 正常 / 70~90 黄 / >90 红。
442
+ - **慢命令不拖垮节奏**:走子进程的字段(df / netstat / vm_stat)只在首次采样时等一次,
443
+ 之后一律「用上一次的值 + 到点后台刷新」,某台机器某个命令卡住只会让那一格短暂显示旧值,
444
+ 不会把「每秒一帧」的时间轴拖散(实测 3000ms/帧 → 3~12ms/帧)。
441
445
  - **热生效**:关掉 `statsEnabled` 立刻停采集(状态条消失、WS 不再发 stats 帧),
442
446
  重新打开后按仍存留的订阅自动恢复,无需重启或用例重开标签。
447
+ - **固定槽位 + 增量刷新**:每个值的字符槽位固定(右对齐,最宽的一档照样只占
448
+ 自己那一格),条目只建一次、之后只写真的变了的文本。所以 `CPU 5% → 12%`、
449
+ `TCP 36 → 1024`、`内存 9.2 GB → 17.8 GB` 都不会把后面的条目推着横移(曾表现为整条
450
+ 每秒跳一次),进度条宽度也能真正走 CSS 过渡;窄窗口下横向滚到右侧看 `网络` 时,
451
+ 滚动位置不会被下一秒的刷新弹回开头。
443
452
 
444
453
  ## 配置(设置 → 插件 → 终端面板,保存即热生效)
445
454
 
@@ -516,7 +525,7 @@ ctx.inject(['ttyConnbar'], (c) => {
516
525
  ctx.inject(['ttyTerminal'], (c) => {
517
526
  c.ttyTerminal.open({
518
527
  command: "docker exec -it 'ems-consumer-test' sh", // 必填,单行,≤2000 字符
519
- book: 'HS-248', // 二选一:连接簿条目名 → SSH 标签
528
+ book: 'lab-a', // 二选一:连接簿条目名 → SSH 标签
520
529
  // spec: { host, port, username, auth, agentForward }, // 内联 SSH 字段
521
530
  // (都不传 = 本地标签,用 cwd 指定工作目录)
522
531
  label: 'ems-consumer-test · exec',
@@ -546,7 +555,7 @@ ctx.inject(['ttyTerminal'], (c) => {
546
555
  if (Number(c.ttyTerminal.version ?? 0) < 2) { /* 老版本:退回 open */ }
547
556
  const dispose = c.ttyTerminal.mount(hostEl, {
548
557
  command: "docker exec -it 'ems-consumer-test' sh", // 与 open 同一套 options
549
- book: 'HS-248', // book > spec > 本地
558
+ book: 'lab-a', // book > spec > 本地
550
559
  label: 'ems-consumer-test · exec',
551
560
  })
552
561
  // 收起自己的抽屉时:
@@ -578,7 +587,7 @@ ctx.inject(['ttyTerminal'], (c) => {
578
587
  pane 记一个「归属标签」(`options.ownerSid`,默认 = 挂载那一刻的活动标签):切到别的
579
588
  标签时整块**收起**(`data-dock-hidden`,DOM 与你 render 的树都保活,在途传输继续跑),
580
589
  切回来恢复现场;归属标签被关掉时 pane 一并收掉。不这么做的话,切完标签面板还停在原处
581
- ——标题写着 `SFTP · cdc-test-161`、底下活动标签却是 `192.168.80.248`,面板里躺着**另一台
590
+ ——标题写着 `SFTP · lab-b`、底下活动标签却是 `192.0.2.10`,面板里躺着**另一台
582
591
  主机**的文件列表(最坏的情况是往错主机上传)。显式传 `ownerSid: null` 表示「不隶属任何
583
592
  标签」(一直可见)——面板开着但一个标签都没有时自动落进这一档;消费方也可以用它声明
584
593
  「这块与标签无关,别跟着切」。
@@ -781,7 +790,7 @@ node scripts/preview.mjs --theme=light # 浅色主题
781
790
  - **SSH 密码 / 口令建议 `env:VAR` 引用**:连接簿随 settings 文件落盘,
782
791
  `password` / `passphrase` 明文入库有泄露面;建议 `env:VAR` +
783
792
  dsh-env-manager 托管,或直接用 `agent` 认证(凭证不落盘)。
784
- - **服务器状态条(0.17.0)边界**:远端靠两跳兜底:先 POSIX(要 `/proc` + `awk`),一帧未出则回退 PowerShell——**Windows 远端**由此覆盖(温度多数组件没有、显示「无」,磁盘取系统盘 `%SystemDrive%`,TCP 走 `netstat -an`,网速走 `Get-NetAdapterStatistics` 差值;只输出整数,从根上避开区域小数点与科学计数法),**macOS/BSD 远端**两跳都不成立、一个字段都拿不到、状态条整条隐藏;非 Linux 远端的每次(重新)订阅会多一跳注定失败的 exec(约百毫秒,不影响出帧延迟);Linux 上 CPU 温度只在暴露 `/sys/class/thermal` 的机器上有(多数云主机/虚拟机没有,显示「无」);磁盘固定取 `/`(Windows 取系统盘)所在文件系统(不做多挂载点);远端脚本经 `sh -c` 单引号包裹(登录 shell 是 fish/csh 也能跑);本地会话的指标属于**宿主机器**(多个本地标签共享同一份采样,CPU/网速差值窗口连续),macOS 没有 /proc、`ss`、sysfs,改用 os/netstat/vm_stat,故温度恒为「无」,内存按 vm_stat 的 active+wired+compressed 计(os.freemem 会把文件缓存算成已用、长期 99%);指标是**瞬时值**,没有历史曲线,也不提供 agent 工具(agent 侧仍用 tty_* 命令粒度能力)。
793
+ - **服务器状态条(0.17.0)边界**:远端靠两跳兜底:先 POSIX(要 `/proc` + `awk`),一帧未出则回退 PowerShell——**Windows 远端**由此覆盖(温度多数组件没有、显示「无」,磁盘取系统盘 `%SystemDrive%`,TCP 走 `netstat -an`,网速走 `Get-NetAdapterStatistics` 差值;只输出整数,从根上避开区域小数点与科学计数法),**macOS/BSD 远端**两跳都不成立、一个字段都拿不到、状态条整条隐藏;非 Linux 远端的每次(重新)订阅会多一跳注定失败的 exec(约百毫秒,不影响出帧延迟);Linux 上 CPU 温度只在暴露 `/sys/class/thermal` 的机器上有(多数云主机/虚拟机没有,显示「无」);磁盘固定取 `/`(Windows 取系统盘)所在文件系统(不做多挂载点);远端脚本经 `sh -c` 单引号包裹(登录 shell 是 fish/csh 也能跑);本地会话的指标属于**宿主机器**(多个本地标签共享同一份采样,CPU/网速差值窗口连续),macOS 没有 /proc、`ss`、sysfs,改用 os/netstat/vm_stat(网速那条是 `netstat -ibn`——**必须带 `-n`**:不带时 netstat 会对接口地址做反查,DNS 不响应就挂几十秒,D50 的根因就是它),故温度恒为「无」,内存按 vm_stat 的 active+wired+compressed 计(os.freemem 会把文件缓存算成已用、长期 99%);指标是**瞬时值**,没有历史曲线,也不提供 agent 工具(agent 侧仍用 tty_* 命令粒度能力)。
785
794
  - **SSH 会话没有本地 pid**:ssh2 shell channel 不是本机进程,`ready.pid`
786
795
  为 `null`、`tty_list` 显示 `target` 而非 pid,本机 `ps` / `kill` 对远程
787
796
  进程无效——关闭请用标签 ✕ 或 `kill` 帧(关闭的是 ssh2 channel)。