qichuang321/dsh-web-terminal
Project Overview项目介绍
This is a DeepSeek Harness plugin that adds an embedded web terminal dock to DSH's web GUI. It supports toggle hide/show, draggable resize, persistent sessions, full ConPTY emulation, and TUI plugin integration. Use it when running terminal or TUI apps alongside DSH chat. It is currently only prebuilt for win32-x64, with some Windows-specific limitations.
这是DeepSeek Harness的网页终端插件,为DSH网页界面添加可隐藏、可拖动调整大小的分屏终端底座。支持持久化会话、全仿真ConPTY终端,可集成各类基于终端设计的TUI插件,供聊天同时运行终端程序使用。注意当前仅提供win32-x64预编译版本,存在部分Windows特定限制。
请帮我了解并安装插件:【dsh-web-terminal】【https://github.com/qichuang321/dsh-web-terminal】
Send this message to DSH in your current session. CLI install commands may not be accurate across systems — DSH will figure it out for you.把上面这条消息直接发给当前会话里的 DSH,让它帮你了解并安装。安装命令不一定准确,发给 DSH 更稳。
Or use CLI install (for developers)或使用命令行安装(适合开发者)
CLI Install命令行安装
dsh plugin --profile web add link:D:\Deepseek Harness\dsh-web-terminal
把 qichuang321/dsh-web-terminal 加入你的 DSH 配置(web profile)即可启用。
READMEREADME
dsh-web-terminal
A terminal dock merged into the DSH web GUI: hideable, split-screen with the chat column when visible, and integrable with terminal-designed TUI plugins.
┌─────────────────────────────┐
│ session header [ 终端 ⇧ ] │ ← toggle button (conversation.session.header.actions)
├─────────────────────────────┤
│ conversation (chat) │ ← stays mounted & stateful
│ ──────────────────────── │ ← drag handle (resize)
│ [Shell] [Panel] ×│ ← dock tab bar
│ xterm.js terminal / TUI │ ← full-emulation ConPTY
└─────────────────────────────┘
- Hide / show: header button or `Ctrl+`` — the dock, the xterm instance and the PTY session all survive the toggle (nothing is torn down; VS Code style).
- Split-screen: the dock is absolutely docked into the bottom band of the
center column; the chat keeps its own scrollport above; height is draggable
(120–900 px) and persisted in
localStorage(dsh.webTerminal.v1). - TUI integration: any terminal-designed TUI runs unchanged inside the PTY (vim, htop, tianshu-tui, …), plus an extension API so plugins can register their own views.
Install
dsh plugin --profile web add link:D:\Deepseek Harness\dsh-web-terminal
# restart the web GUI, then use the header toggle or Ctrl+`
The plugin mounts via cordis.patch.yml (bundle patch layer, dsh-ssh pattern):
host half = lib/index.js (node-pty PTY + WebSocket bridge + registry service),
browser half = lib/client.js (the dock).
Architecture
| Piece | File | Notes |
|---|---|---|
| Plugin entry (host) | src/index.ts |
webServer inject, routes + webTerminal service under ctx.effect |
| Routes + WS terminal | src/routes.ts |
loopback fence, WebSocketServer({noServer:true}), 1MB/512KB backpressure |
| PTY layer | src/pty.ts |
node-pty direct (^1.1.0, prebuilt win32-x64 ConPTY), TERM/COLORTERM env, powershell.exe default |
| Shared protocol | src/protocol.ts |
frame types + WT_API paths + clamps |
| TUI registry | src/tui-registry.ts |
webTerminal.registerView(def) extension seam |
| Client entry | src/client/index.ts |
dock mount, header toggle slot, `Ctrl+`` |
| Dock UI | src/client/dock.tsx |
xterm + FitAddon, tabs, drag resize, TUI channel |
| TUI channel | src/client/tui-channel.ts |
window CustomEvent channel |
| Demo adapter | src/examples/* |
one dom view (demo:panel, proves the CustomEvent channel) |
Wire protocol (/api/dsh-web-terminal/*, loopback-fenced)
server→client: {type:'ready',view,cols,rows} | {type:'output',data}
| {type:'title',title?} | {type:'exit',code,error?}
client→server: {type:'input',data} | {type:'resize',cols,rows}
TUI plugin integration API
Host side — any DSH plugin registers a view inside its own ctx.effect:
const webTerminal = ctx.get('webTerminal')
ctx.effect(() => webTerminal?.registerView({
id: 'my-plugin:my-tui', // namespaced 'pluginId:viewId'
label: 'My TUI',
order: 10,
spawn: ({ cols, rows, env }) => ({
command: 'npx',
args: ['--yes', 'my-tui'],
shell: true, // run through the default Windows shell
env: { ...env, MY_FLAG: '1' },
}),
}), 'my-plugin: tui view')
Client side — the browser half of the same plugin:
import { dispatchRegister, dispatchUnregister, onTuiEvent, TUI_EVENTS } from 'dsh-web-terminal/client'
ctx.effect(() => {
dispatchRegister({ id: 'my-plugin:my-tui', label: 'My TUI', kind: 'pty' })
return () => dispatchUnregister('my-plugin:my-tui')
}, 'my-plugin: tui client')
Events: adapter→dock register / unregister; dock→adapter ready (re-register,
self-heal), activate ({id,container} — dom views render here), deactivate,
view-state ({id,state:'running'|'exit'|'error',code?,error?}).
Security
- Every route + the WS upgrade carry the loopback-only fence (remoteAddress ∈
{127.0.0.1, ::1, ::ffff:127.0.0.1}, Host localhost,
sec-fetch-siteand Origin checks) — LAN-exposed dsh web deployments must not serve these. - Client frames are strictly validated; only
input/resizeare accepted, cols/rows clamped. - The PTY is killed on socket close/error, tab close, dock unmount, and plugin
dispose (
ctx.effectdisposer); no fs access from the browser.
Windows caveats
node-ptynever setsTERMfrom thenameoption on Windows — the plugin setsTERM=xterm-256color/COLORTERM=truecolorexplicitly in the env.kill(signal)throws on Windows — barekill()only; Ctrl+C is\u0003.- Default shell:
powershell.exe(pwsh if installed,cmd.exelast resort).cmd.exemay needchcp 65001for UTF-8 TUI output. - ConPTY emits CRLF natively — the bridge never converts EOL (
convertEol:false).
Build
pnpm install # node-pty ^1.1.0 prebuilt win32-x64 — no compiler needed
pnpm build # tsc typecheck + tsdown → lib/index.js + lib/client.js
chuspeeism/dashi-taskboard
ccch1mneyyy/working-activity
Aisland-SJL/dsh-worktable
zhoushoujianwork/easyeda-agent
morluto/rea
linhay/harmony-next.skills