cyjyyd/dsh-ssh-tui

dsh-ssh-tui

跳板机与高延迟 SSH 上的 DeepSeek Harness 终端:纯 ANSI、增量重绘;SSH 掉线后会话不丢,可用 --resume 接回。

Uncategorizeddsh-plugin-marketharness-engineeringterminal

DSH Relay

A resilient terminal workspace for DeepSeek Harness. 远端优先 · 可重连 · 就在终端里

SSH 断了。会话还在。接回来继续。

“Relay” 说的是这件事:显示可以消失,会话不必跟着消失。宿主进程活着,同一句命令接回来 就回到原来的会话 —— 转写、计划卡片、审批、排队中的消息都在原处。 这是远程优先的终端工作区,不只是“一个 SSH 风格的界面”。

断线时正在跑的那一轮会怎样?取决于 `/disconnect`:
默认 pause —— 当前这一轮在窗口离开时停下,会话本身不丢,接回来接着用;
显式设成 continue —— 当前这一轮在显示端离开后继续在后台跑完。
两种策略下宿主都不退出。演示动图用的是 continue(字幕里标了),别把它当成默认行为。
npm 包名没有变,安装命令也没有变。 发布坐标仍然是 dsh-ssh-tui;
DSH Relay 是产品展示名。见 docs/decisions/brand-dsh-relay.md。
npm i -g @deepseek-ai/dsh
dsh plugin --profile tui add dsh-ssh-tui@latest
dsh --profile tui

需要 Node.js ≥ 22.19 和 DeepSeek Harness CLI。断开之后接回来:dsh --profile tui --resume。

*实拍,不是摆拍:真 PTY、真宿主、真 SIGHUP、真 resume(npm run screenshots:reconnect 可重放)。 演示显式设了 /disconnect continue,所以画面上这一轮在窗口消失后跑完了;换成默认的 pause, 同一段会停在中断处,会话照样接得回来。*

English: README.en.md

已测,不是号称

在 100×20 下实测,每个动作 40 次取中位(npm run bench;本节由 npm run bench:report 生成, 不是手抄的):

动作 · 毫秒 · 字节 · 寻址行 · 整屏清屏 流式输出时的一次重绘 · 1.69 · 501 · 3 · 0 等待卡片时钟走一格 · 1.37 · 465 · 3 · 0 报告屏翻一页(PgDn / PgUp) · 1.37 · 2457 · 17 · 0 菜单里移动一次光标 · 0.99 · 588 · 4 · 0 向导字段里打一个字符 · 0.21 · 140 · 1 · 0

五个动作合计 0 次整屏清屏。这条是这一版的性能承诺:不管终端多慢、链路多差, 画面按行增量重绘,不会因为一次 tick 把整屏重画一遍。

跑法就是上面两条命令;这两条断言在 CI 的每一条腿上都会重跑(Linux 与 Windows、四个宿主版本)。 链路上的承诺另有真 PTY 探针:断链重接、断线后宿主存活、带历史的 resume、以及"终端不回光标位置" 时的链路显示,各自一个脚本,全部在 CI 里跑(npm run freeze 会把它们列出来)。

你在我们没覆盖过的终端上试过 DSH Relay 吗? 提一份终端兼容报告 —— 不用读代码,填一张表就行, 结果会进 docs/terminals.md 的「社区实测」区。

如果 DSH Relay 让你的远程会话更可靠了, 给仓库点个 star 能帮别的终端用户找到它。

30 秒开始

上面三条命令装好就进工作区。之后常用的几条:

恢复旧会话:

dsh --profile tui --resume

更新:

dsh plugin --profile tui add dsh-ssh-tui@latest
dsh plugin 由 profile 内的 pnpm 管理依赖。更新时请显式带 @latest,否则已有 lockfile 可能继续沿用旧版本。

Windows 用户见 Windows 指南。

为什么会有这个 TUI

DeepSeek Harness 已经有 Web、headless 和不断扩展的插件生态。

dsh-ssh-tui 不试图把浏览器搬进终端。

它解决另一类问题:

连接不可靠

SSH 会断、笔记本会合盖、网络会切换、跳板机会超时。

正在运行的回合不应该因为显示终端消失,就和整个 Harness Host 一起死亡。

dsh-ssh-tui 将 Host 与显示端分离:

Harness session
      │
      ▼
   TUI Host
      │
      ├── 当前 SSH / terminal
      │
      └── 断线后重新 attach

忙碌中的 Host 可以在显示端离开后继续存在;重新连接后,同一条命令即可接回。

终端环境并不统一

Linux PTY、Windows ConPTY、SSH、旧控制台、不同 OSC / mouse / clipboard 能力,并不是同一种终端。

本项目不会假定:

process.stdin/stdout == terminal == display == host

而是显式区分:

Harness Host
    ↓
Display Transport
    ↓
Terminal Capability

TTY 是一种能力,不是宿主存在的前提。

这也是为什么在没有真实终端的 Host 中,插件应该安全地不激活,而不是让宿主一起崩掉。

Harness 本身仍在快速变化

本项目把 兼容性当成功能维护,而不是发版后顺便测试。

当前声明窗口:

DeepSeek Harness · 状态 0.2.1-rc.2 · ✅ 支持,默认开发线(提交的 manifest / 锁 / test-windows 都装它) 0.2.0-rc.1 / 0.2.0-rc.2 · ✅ 支持 0.1.7 整线(含 0.1.7-rc.1 / 0.1.7-rc.2) · ❌ 自 dsh-ssh-tui 0.8.4-rc.1 起摘除(dshReleases 明确表态 incompatible) 0.1.5 及更早 · ❌ 自 dsh-ssh-tui 0.8.0 起不再支持

声明范围:

>=0.2.0-rc.1 <0.2.1
|| >=0.2.1-rc.2 <0.2.2

两个 comparator 不是笔误:node-semver 只让 prerelease 被同一 major.minor.patch 的 comparator 接纳。

发布前会针对支持线重新安装依赖并运行类型检查、测试和真实终端探针。

不是:

“安装没有报错,所以应该能用。”

而是尽量回答:

“这个 Harness 版本、这个 Host、这个终端路径,我们实际验证过什么?”

同一个 Harness,终端里看到完整过程

官方 headless 很适合一次性任务:

dsh --profile headless "..."

它完成任务后将最终回复写到 stdout。

如果你需要持续观察思考、工具、diff、子代理、计划和审批,则可以进入 TUI。

下面是同一个任务:

在 TUI 中:

  • reasoning 可折叠、可实时展开;
  • 工具调用以卡片呈现;
  • edit 显示 git 风格 diff;
  • 子代理独立显示;
  • plan / approval / ask-user 直接进入交互界面;
  • 模型输出持续流式显示,而不是等整个任务结束。

为弱网而设计

TUI 使用纯 ANSI 和增量重绘,不依赖浏览器或重量级远程 UI。

下面是在 2 kB/s 限速下回放真实绘制事件:

该样例可以直接复现:

npm run screenshots:slow

对应数据保存在:

docs/screenshots/slow-link.json

目标不是做一个网络 benchmark。

目标是确保:

链路越差,界面可以降级;任务本身不能跟着失去可用性。

核心能力

Terminal-native workspace

  • 纯 ANSI 渲染;
  • Markdown、标题、列表、引用、代码块;
  • 工具卡片与 git 风格 diff;
  • reasoning 折叠与实时查看;
  • plan / approval / ask-user-question;
  • 子代理独立状态卡;
  • 状态行按一眼扫读的顺序回答五个问题:链路健康、正在做什么、tok/s、额度余量、上下文占用(宽度不够时按优先级逐项收敛,窄屏更简洁而不是更拥挤);轮数 / 步数 / 模型时间 / 工具时间 / 缓存命中移出常驻行,仍可在 /status 与 /diag 查看;
  • /model、/mode、/submodel 等 Harness 能力直接进入终端。