Bundle
dsh-terminal-tabs
Terminals view tab for the DSH web UI: live count of background shells/subagents per session, Chinese purpose titles, collapsible command bodies with streaming output, and one-click kill
- Source
- bbboy31
- License
- MIT
- Updated
- Updated 2 days ago
Readme
# dsh-terminal-tabs
[English](README.md) | [中文](README.zh.md)
Turn the commands your model runs — in the background **and** in the foreground — 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.view` seat, sibling of Chat / Trajectory; selection is managed by the framework.
- **Live label** — the tab text re-resolves on every projection from the host-pushed `jobsBySession` mirror, 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.
- **Foreground commands too** — the plain `pwsh`/`bash` tool calls that finish in milliseconds and never become registry jobs are still captured: the node half wraps `ctx.shell.run()` and attributes each run to the session that initiated it, then serves them through `/dsh-terminal-tabs/foreground`. Running rows pulse green with a ticking duration; settled rows show the exit code and elapsed time, with the captured output tail under a chevron. The page merges them below the background rows.
- **Collapsible body** — the command renders one ellipsised line; the chevron flips down/up and unwraps the full text.
- **Live output** — expanding a background 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 `ShellProcessRead` shape (`{ delta }`); if that contract changes upstream, output display degrades to empty while close/kill keeps working.
- Foreground runs are recorded through a separate `ctx.shell.run()` wrap; an unattributable run (no initiating agent) passes through untouched. Settled entries keep the last 16 KiB of combined output.
## 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 |
| `foregroundPath` | `/dsh-terminal-tabs/foreground` | Absolute pathname of the foreground-command ledger route |
| `includeKinds` | `["bash","pwsh","subagent"]` | Job kinds shown in the UI |
## Install
```sh
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
```sh
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.
## License
[MIT](LICENSE)
Install
dsh plugin --profile web add github:bbboy31/dsh-terminal-tabs
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-terminal-tabs from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.