dsh-terminal-tabs
Terminals view tab for DeepSeek Harness web UI — live background job count, streaming output, one-click kill
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 26, 2026
- Updated
- Aug 26, 2026
Introduction
dsh-terminal-tabs
Turn the background commands your model starts into something visible and closable inside the DeepSeek Harness web UI: a Terminals view tab next to Chat / Trajectory whose label tracks how many background shells and subagents the current session has running (命令行1, 命令行×3). Open it for a management page — one row per running command with a Chinese purpose title, a collapsible one-line command body, start clock plus ticking duration, a live output pane, and a Close button that actually kills the process.
Features
- Real view tab — registered through the official
conversation.viewseat, sibling of Chat / Trajectory; selection is managed by the framework. - Live label — the tab text re-resolves on every projection from the host-pushed
jobsBySessionmirror, scoped to whichever session you are viewing. Zero observation RPC. - Purpose titles — a local heuristic dictionary maps each command to a short Chinese phrase (
cd→ 打开文件夹 "Open folder",git status→ 查看 Git 状态 "Git status",Start-Sleep→ 等待 "Wait",npm run dev→ 启动开发服务 "Start dev server"). Subagents show as 子代理任务 "Subagent task"; unknown commands fall back to 执行命令 "Run command" while the raw text stays fully visible. - Collapsible body — the command renders one ellipsised line; the chevron flips down/up and unwraps the full text.
- Live output — expanding a row opens a dark scrollback fed by a private tee on the spawned process: newest ~10 lines auto-follow, wheel up browses history, returning to the bottom resumes following.
- Timing — start wall-clock (HH:MM:SS) and elapsed time (秒 → 分秒 → 时分) tick every second.
- One-click close — kills the job through the node half's HTTP route; the jobs registry ownership fence still applies, so cross-session requests are rejected.
- Stable numbering — rows keep their number while open; re-entering the page renumbers compactly. Background subagents are included, marked with an indigo dot.
How live output works (boundaries)
The browser never touches the model's consuming cursor. The node half wraps ctx.shell.start() at the instance level: every successfully spawned process gets an internal pump (300 ms) that drains the pipe into a per-process ring buffer (48 KiB cap). The wrapper is the process's single consuming cursor; a model-side read returns everything accumulated since its previous read, byte-for-byte — only the cadence changes. Consequences:
- Background jobs started before the wrapper mounted have no captured output ("(暂无输出)").
- Persistent PTY terminal sessions (
ctx.terminals) do not flow through this channel. - The wrapper understands the current
ShellProcessReadshape ({ delta }); if that contract changes upstream, output display degrades to empty while close/kill keeps working.
Configuration (cordis.yml config row; all optional)
| Key | Default | Meaning |
|---|---|---|
routePath | /dsh-terminal-tabs/close | Absolute pathname of the kill route |
tailPath | /dsh-terminal-tabs/tail | Absolute pathname of the output-tail route |
includeKinds | ["bash","pwsh","subagent"] | Job kinds shown in the UI |
Install
dsh plugin --profile web add github:bbboy31/dsh-terminal-tabs
Pure-JavaScript dual-plane plugin (node half index.js, browser half client.js) — no build step. After installing, refresh the web page.
Uninstall
dsh plugin --profile web remove dsh-terminal-tabs
Or use 插件市场 → installed list → 卸载 (two-step confirm). Uninstalling removes the routes, tab, and stylesheet with the fiber — no residue.
Development note: the Loader caches imported modules by package name — editing the node half requires a package rename or a process restart to take effect; client-half edits apply on page refresh.