DSH Plugins Marketplace

DSH Plugins

Plugins

/

dsh-terminal

e

dsh-terminal

Manifest valid

Workspace-aware web terminal plugin for the DeepSeek Harness (dsh). Runs a streaming PTY terminal at /terminal and embeds a split-pane terminal dock powered by xterm.js.

UI (client)

dsh-terminal

Workspace-aware web terminal plugin for the DeepSeek Harness (dsh). Loaded as a host plugin by the web profile: it serves a terminal page at /terminal (streaming PTY output over SSE, keystrokes via POST) and embeds a split-pane terminal dock into the chat column so you can run nvim/lazygit on the workspace you're working in without leaving the page.

Demo

A quick tour of the workspace-aware terminal dock.

What makes it "workspace-aware"

  • One PTY per workspace root, kept alive across SSE disconnects — switching conversations preserves each workspace's terminal and scrollback.
  • Sessions are bounded by an LRU cap (default 8) and an idle reaper, so never-again-visited terminals are eventually freed.
  • The dock targets whatever chat is active by asking the server for its authoritative workspace root (validated against the real workspace registry).

Installation

Install directly from the GitHub repository:

dsh plugin --profile web add github:emircanerkul/dsh-terminal
dsh web

Or clone it locally and install from the local directory:

git clone https://github.com/emircanerkul/dsh-terminal.git
cd dsh-terminal
npm install
npm run check
dsh plugin --profile web add .
dsh web

Loading

The web profile mounts this plugin through a patch layer and resolves it as a package by name (so DSH's runtime client-plugin discovery can find its dsh.client half — the Settings → Plugins card). In cordis.patch.yml:

- insert:
    - id: terminal
      name: 'dsh-terminal'

with dsh-terminal linked into the profile's shared install so both the loader and the client-modules scanner resolve it:

ln -s /absolute/path/to/dshterm ~/.dsh/profiles/node_modules/dsh-terminal

The plugin declares a client half in package.json (dsh.client + exports["./client"], implemented in client.js) that contributes a Settings → Plugins card; the host serves the config through GET/POST /terminal/shortcuts and keeps it in a small file store (~/.dsh-terminal/shortcuts.json), so the plugin stays self-contained and never waits on the shared settings service. See src/index.js for the plugin row (inject: ['webServer', 'sandboxPolicy']).

Layout

terminal.mjs        entry point (re-exports src/index.js — the mounted path)
src/               host-side server code
  index.js         plugin assembly + teardown, idle-reaper timer
  constants.js     limits, MIME table, asset manifest, path roots, knobs
  palette.js       the single fixed terminal palette (no theme switcher)
  pty.js           single node-pty accessor
  sessions.js      PTY lifecycle manager (LRU + idle reaper)
  http.js          body reader / static server / JSON-text response helpers
  workspace.js     workspace-root resolution helpers
  auth.js          page-token mint + ?token= check helpers
  page.js          /terminal page HTML builder
  routes/          all HTTP routes + the chat-column embed tap (split by area)
    index.js         installRoutes() wiring + shared route helpers
    terminal.js      /terminal, /stream, /input, /resize, /kill
    api.js           /bin, /workspace, /debug, /sessions
    assets.js        /terminal/assets/*
    embed-tap.js     chat-column embed injection
web/               browser-side assets served at /terminal/assets/*
  embed.js         the chat-column SPLIT-PANE dock (client LRU of iframes)
  terminal/        terminal page bootstrap + vendored xterm
  fonts/           Nerd Font used for nvim/lazygit PUA icons
test/              unit tests (TerminalSessions) + a module smoke harness

Config / static-asset map

src/constants.js holds the knob values (MAX_SESSIONS, MAX_CLIENTS_PER_SESSION, IDLE_MS, REAPER_MS, MAX_INPUT_BYTES, MAX_RESIZE_BYTES) and the ASSETS manifest that maps public /terminal/assets/<name> URLs to files under web/.

HTTP surface (authed = ?token= from the /terminal page)

| Method | Path | Purpose | |--------|--------------------|-----------------------------------------------------| | GET | /terminal | the terminal page (mints the auth token) | | GET | /terminal/stream | SSE: PTY output for the workspace | | POST | /terminal/input | keystrokes into the PTY | | POST | /terminal/resize | resize the PTY | | GET | /terminal/bin | is a command (lazygit/nvim) on PATH? | | GET | /terminal/proc | the executable currently occupying this workspace's terminal (or null) | | GET | /terminal/debug | last embed-reported detection diagnostic | | GET | /terminal/workspace| authoritative active workspace root + list | | GET | /terminal/sessions| live per-workspace PTY debug listing | | GET | /terminal/shortcuts| effective dock hotkeys (Settings → Plugins → Terminal) | | POST | /terminal/kill | kill one workspace's terminal | | GET | /terminal/assets/*| static files (embed, bootstrap, xterm, fonts) |

Keyboard shortcuts

The dock registers global shortcuts that fire whatever has focus — chat, sidebar, or the terminal itself (the config is relayed into the terminal page, which captures matching combos before xterm/lazygit see them and asks the dock to act):

| Action | Default | Notes | |-------------------|----------------|----------------------------------------------| | Toggle dock | Ctrl+\`` | restores the last size/position (persisted) | | Toggle popup mode | Ctrl+Shift+`| open/close the full-screen floating modal | | Open lazygit |Ctrl+Shift+G | →lazygit, only at an idle shell prompt | | Open nvim | Ctrl+Shift+E | →nvim .`, only at an idle shell prompt |

Toggling back open restores the previous size and split because those are persisted per workspace. The launchers first ask /terminal/proc, which scans this workspace's PTY process tree for any non-shell program (lazygit, nvim, vim, htop, …). If one is running — whatever it is — the shortcut does NOT type the new command; instead it shows a short toast telling you to close the running app first (Ctrl+C / :q). So pressing Ctrl+Shift+E while lazygit is up won't type nvim . over it — it toasts "running lazygit — close it first". A lazygit in another workspace or a separate terminal never counts: each /terminal/proc call walks only the active workspace's PTY.

Shortcuts are layout-independent: each matches event.key or event.code (the physical key, and multiple codes are accepted). On US the key left of 1 is Backquote; on a UK/ISO ("British PC") layout that key reports IntlBackslash (yielding key="0" under Ctrl), so the toggle matches both, so Ctrl+ ` toggles the dock there too. mod is one of ctrl | meta | alt | any; note Cmd+ ` is the OS "cycle windows" shortcut on macOS, so a meta default would never reach the page — ctrl is the safe cross-platform default. Each binding is set by key (character), code (physical key; several codes may be given), or both. Toggle/modal accept both the US Backquote and the UK/ISO IntlBackslash physical codes.

Configure bindings from Settings → Plugins → Terminal. The Settings card reads the current bindings from GET /terminal/shortcuts and saves them via POST /terminal/shortcuts; the host persists them to ~/.dsh-terminal/shortcuts.json. The dock fetches the effective config from GET /terminal/shortcuts on every page load and merges it over the defaults in web/embed.js (no localStorage override path any more). web/embed.js is loaded fresh per page, so after saving in the settings panel a plain browser refresh picks the new bindings up.

Development

npm run check   # syntax-check every source + web asset
npm test        # unit tests for PTY lifecycle (TerminalSessions)
node test/smoke.mjs  # mount the plugin against a mock ctx (wiring smoke test)

Reload behaviour (important):

  • web/embed.js is injected as a tiny loader that pulls /terminal/assets/embed.js fresh from disk on every page load — embed edits go live on a plain browser refresh.
  • web/terminal/boot.js is read once at module load and baked into the /terminal page, so boot.js edits require a web-profile restart.
  • Server-side edits under src/** also require a web-profile restart.
  • The Settings → Plugins → Terminal card comes from the dsh.client half, so it appears only after a web-profile restart realigns loader entry names and the browser loads the new client bundle (refresh the page too). From then on, saved bindings go live in the dock on a refresh.

Sponsor

Erklab

Sponsored by erklab — Architected with production-grade systems using AI-driven velocity and human-centered precision.

Similar plugins

relay-dsh-plugin-terminal

by yangbobo2021

Provider-neutral interactive terminal bottom-view plugin for DeepSeek Harness Workbench.

Manifest valid

0

MIT

JavaScript

Sep 3, 2026

dsh plugin --profile web add relay-dsh-plugin-terminal

by giiiiiithub

Real PTY terminal panel for the DSH Web UI: host half spawns shells with node-pty (cmd.exe default on Windows), rendered in the browser with xterm.js; multi-tab sessions, dock/floating window, clipboa

UI & ExperienceManifest valid

0

122/wk

MIT

JavaScript

Aug 15, 2026

dsh plugin --profile web add dsh-terminal

by hoshinoick811

MobaXterm-style SSH/SFTP tool plugin for DeepSeek Harness (DSH): saved connection profiles, persistent multiplexed sessions, remote command execution, SFTP file transfer, and a web GUI console with fi

Manifest valid

4

MIT

JavaScript

Sep 6, 2026

dsh plugin --profile web add dsh-tool-ssh

Bottom multi-tab terminal panel (node-pty + xterm.js) pinned to the viewport bottom, always below the input box.

UI & ExperienceManifest valid

0

156/wk

dsh plugin --profile web add dsh-plugin-terminal

by maxwell-feng

DeepSeek Harness plugin: back the native web_search / web_fetch tools with your self-hosted SearXNG instance — keyless, private, no third-party search vendor.

Tools & CapabilitiesManifest valid

8

892/wk

MIT

TypeScript

Sep 13, 2026

dsh plugin --profile web add dsh-searxng-web

by houlain

DeepSeek Harness plugin: workspace file browser + code viewer + session change diff + per-hunk partial revert (Trae-style). Windows verified only; Linux/macOS untested — use with caution.

Manifest valid

0

MIT

TypeScript

Aug 16, 2026

dsh plugin --profile web add dsh-workspace-studio