dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
dsh-single-terminal is a real-terminal drawer plugin for the DeepSeek Harness (DSH) host. It docks an interactive PTY terminal (xterm.js) to the bottom of the web app — type into it, Ctrl-C it, resize it, open as many tabs as you need.
node-pty; interactive REPLs,
full-screen programs and Ctrl-C all behave like a native terminal$SHELL / bash / zsh / fishbackdrop-filter blur) — the mode only decides docking
behavior. Open/close slides with the host's
easing curve and honors prefers-reduced-motion.cd needed; with no session / no workspace the
defaultCwd rules apply.Alt+C toggles the drawerScreenshot of the terminal drawer (dark theme following, frosted-glass style): see preview.md.
shell.overlay): a
>_ prompt tab stuck to the drawer's top-right outer edge — the terminal's
entry button. It rides the drawer's own slide animation: expanded it sticks
to the terminal's top-right corner like a drawer handle; minimized it settles
at the page's bottom-right corner. Clicking toggles the drawer; Alt+C
toggles from anywhere except while typing inside the terminal (there Alt+C
is passed to the shell as ESC c).conversation.session.header.utilities): a
null-rendering component that only syncs the current session's workspace
root into the controller (so new terminals start there); the header shows no
visible UI for this plugin.shell.overlay): a frame-level bottom drawer with
+ button whose dropdown lists all shells found on this machine
(unavailable ones are not listed), the default shell pinned first and
marked,−) button,padding-bottom on the frame
root element (no host hook exists for bottom docks); when the anchor cannot
be found it silently falls back to overlay.Schemastery Config (renders on the host Plugins settings page), and / or the
profile cordis.patch.yml:
- insert:
- id: dsh-single-terminal
name: dsh-single-terminal
config:
defaultShell: powershell # powershell | pwsh | cmd | gitbash | wsl | <custom id>
defaultCwd: home # home | workspace | absolute path
scrollbackLimit: 200000 # replay ring buffer, bytes per session
fontSize: 13
fontFamily: Consolas, "Cascadia Mono", "Courier New", monospace
customShells:
- id: nu
name: Nushell
command: nu # resolved through PATH
args: []
defaultShell — shell preselected in the + dropdown (pinned first and
marked "Default"); when unavailable it falls
back (powershell on Windows, $SHELL/bash on POSIX).defaultCwd — start directory when there is no workspace context: home
(default) starts in the user home; workspace is reserved (currently
resolves to home); an absolute path must exist. When the current session
belongs to a workspace the workspace root takes precedence (see above).customShells — extra launchers; command may be an absolute path or a
name resolved through PATH (with PATHEXT on Windows).# Local development
dsh plugin --profile web add ./dsh-single-terminal
# Published: npm / tarball / GitHub
dsh plugin --profile web add dsh-single-terminal
dsh plugin --profile web add ./dsh-single-terminal-0.1.0.tgz
dsh plugin --profile web add github:you/dsh-single-terminal#<sha>
dsh --profile web # start (restart required for the host half to load)
node-pty is a native dependency of the host half (
dependencies, kept external and loaded viacreateRequire). It ships prebuilds for common platforms; on unusual platforms a C/C++ toolchain is needed forpnpm installto compile it. The browser half inlines xterm.js entirely — no runtime dependency there.
The build toolchain is tsc + tsdown (no vite): tsc -b type-checks and
emits declarations, while tsdown (Rolldown core) bundles the host half
(lib/index.js, ESM) and the browser half (lib/client.js, single-file CJS
__ModuleLoader__ factory with auto banner wrapping). Dependency management
uses pnpm 10 (the pnpm-lock.yaml is committed and CI installs with
--frozen-lockfile). Build artifacts are committed to git, so git installs
need no build:
pnpm install # install per pnpm-lock.yaml
pnpm run build # clean lib → tsc -b (declarations) → tsdown (both halves)
pnpm run verify # simulate the host module table to check lib/client.js (optional)
pnpm run release # check + build + verify + npm version patch + push tags (triggers the publish workflow)
Pushing a v* tag (pnpm run release bumps the patch version, rebuilds, and
tags/pushes automatically) triggers
.github/workflows/publish.yml — a single
release job that: sets up Node 26 → pnpm install --frozen-lockfile →
pnpm run check → pnpm run build → pnpm run verify → pnpm pack →
creates a GitHub Release (auto-generated changelog, tarball attached) →
publishes to npm via Trusted Publishing (OIDC --provenance, no
NPM_TOKEN secret; the npm package must have this repository configured as a
Trusted Publisher).
Requirements: Node ≥ 22.19 (or ≥ 24) + pnpm 10 (the packageManager field
pins the pnpm version).
pnpm install # includes node-pty + ws (runtime) and @xterm/* (bundled into the client)
pnpm run check # whole-tree TypeScript type check (tsc -b)
pnpm run build # clean lib → tsc -b (declarations) → tsdown (both halves)
pnpm run watch # tsdown watch mode
pnpm run verify # simulate the host seed table to check lib/client.js loads
├── src/ # Source
│ ├── host/ # Host half: index.ts (entry, ws route + config), hub.ts (session hub + frame protocol), shells.ts (registry + probing), types.ts
│ └── client/ # Browser half: plugin.tsx (slots), drawer.tsx (drawer + entry handle), term.tsx, controller.ts, ws.ts, styles.ts, theme.ts, scope.tsx (session bridge), i18n.ts ...
├── lib/ # Build artifacts (committed: git installs need no build)
│ ├── index.js # Host half (tsdown, ESM)
│ ├── client.js # Browser half (tsdown → __ModuleLoader__ factory, xterm inlined)
│ └── types/ # Type declarations (generated by tsc -b)
├── assets/preview/ # Screenshots referenced by README / preview.md
├── scripts/ # verify-client.mjs (host-seed simulation check), gen-xterm-css.mjs (regenerates src/client/xterm-css.ts)
├── tsdown.config.ts # tsdown build config (node half + client bundle banner wrapper)
├── tsconfig.json # solution: references tsconfig.host.json / tsconfig.client.json
├── cordis.patch.yml # Bundle patch: plugin row referenced by package name (no paths)
├── package.json # dsh.bundle + dsh.client(web) manifests + peerDependencies
├── README.md # This file (English)
├── README.zh.md # 中文文档
└── preview.md # Screenshot preview (references assets/preview/*.png)
subprocess terminal
primitive (SubprocessTerminalHandle) intentionally exposes no resize,
which a resize-following terminal needs; a plugin-owned node-pty gets the
full write / resize / kill control surface with the same ConPTY/forkpty
substrate the host uses./api/dsh-single-terminal.ws)
registered through ctx.webServer.registerUpgrade, gated by
ctx.connection.requestRejection (same trusted-host fence as the host API
gateway). The client connects same-origin and rides the dsh-auth cookie.Map independent of sockets —
page refresh / reconnect re-lists, adopts live sessions and attaches
with a replay of the ring buffer. Exited sessions are pruned so dead tabs
never resurrect. Multiple browser tabs may attach to one session (output is
broadcast, input is merged).open / input / resize / close / list / attach / ping, host → client hello / shells / opened / data / replay / exit / error / pong. input/resize are size-capped and
clamped server-side.pty.kill() and additionally
taskkill /T /F on the session pid — ConPTY closure alone can leave
PowerShell (+PSReadLine) alive; POSIX kills the foreground process group
(kill(-pid)).--dsw-alias-*, defined on body and flipped by
body[data-ds-dark-theme]), so light / dark / custom themes apply without
plugin-side logic. The xterm palette is computed at runtime: alias token
values are read via a hidden probe element (getComputedStyle), the
background is re-composed with the frosted alpha, and a MutationObserver
on the body attribute re-applies the palette — theme switches (including
custom themes projected by the host's ThemePresenter) update live.allowTransparency works there, while WebGL canvases are opaque). Both
modes share the frosted style, so the plugin stays on the DOM renderer with
a translucent terminal background under the frosted blur in every mode —
the WebGL addon was removed rather than swapped at runtime.deepseek-harness project is not modified; all UI sits in
existing slots (shell.overlay for the drawer and its entry handle,
conversation.session.header.utilities for the invisible session bridge).