dsh-ssh
其他 活跃维护

dsh-ssh

jmcc-guo/dsh-ssh

它提供SSH连接的创建、执行、列表、状态查看与断开全流程管理能力,内置类XShell风格的多标签Web终端界面,结合AI能力实现连接智能化管控,无需额外插件即可在浏览器端便捷管理多台服务器。

3
Stars 标星
0
Forks 分支
3
Watchers 关注
0
Open Issues
JavaScript
主要语言
MIT
开源协议
279 KB
仓库大小
1 个月前
最后推送
一键安装扩展 / 插件指令
dsh plugin --profile web add github:jmcc-guo/dsh-ssh
git clone https://github.com/jmcc-guo/dsh-ssh.git
git clone git@github.com:jmcc-guo/dsh-ssh.git
README.md main

@jmcc-guo/dsh-ssh

English · 简体中文

License: MIT
Node
Type

AI-managed SSH connections with a live multi-tab terminal panel for DeepSeek Harness.

SSH terminal panel + AI connection manager for DeepSeek Harness (DSH).

The AI agent can autonomously create, address, and tear down SSH connections
right from the conversation (ssh_connect / ssh_exec / ssh_list /
ssh_status / ssh_disconnect / ssh_exec_read / ssh_exec_kill /
ssh_delete), while a XShell/Uniterm-style multi-tab terminal panel in the
Web GUI shows every command — model and human — in real time on the same
screen.

Features

  • Model-driven connection lifecycle — the agent connects by name or by
    parameters, runs commands with exit codes and readable failures, lists and
    inspects AI-managed connections, disconnects and deletes them. The same
    server may hold several independent connections (each with its own name,
    session state and command queue).
  • Auto-save & reuse — every connection is persisted by name (unique
    across AI and user connections). After a DSH restart, ssh_exec on a saved
    name automatically re-establishes the connection from its saved settings.
  • Source model ai | user — connections created by the AI are ai;
    connections created from the Settings page are user. The AI can never see
    or touch user connections (ssh_list hides them; ssh_exec/ssh_status/
    ssh_disconnect/ssh_delete reject them explicitly). A one-way
    user → ai transfer (with explicit confirmation, also offered under the
    tab bar for a user-created active tab) grants the AI access to a live
    connection without disconnecting it.
  • Keep-alive & reconnect — idle keep-alive per connection; automatic
    reconnect only for unexpected drops
    (network blips, server resets) with
    exponential backoff (bounded attempts); every explicit disconnect stays
    down
    (tab close, ssh_disconnect, Settings "Disconnect" button). A
    "reconnected — the shell state was reset" notice is shown after an
    automatic reconnect.
  • Execution mutex (ai-source only) — while an AI command runs on an ai
    connection, keystrokes are dropped server-side with a visible "AI is
    executing…" hint; a model ssh_exec against a connection whose shared
    terminal shell is still active waits until the shell falls quiet (no
    output/input for shellQuietWaitMs, default 2 s) or returns a readable
    "busy" result instead of interleaving output. user connections are never
    mutexed.
  • Tab semantics — closing a tab disconnects immediately (no confirmation
    dialog); AI ssh_disconnect keeps the tab open showing "disconnected" (one
    click to reconnect); a model connect that has no tab re-opens one
    automatically. The tab bar's "+" opens saved connections — each pick
    opens a fresh tab/session, even when that connection is already connected
    in another tab
    — or starts a manual entry.
  • Live terminal column — while the terminal panel is open it lives in
    DSH's native right details column (the conversation shrinks instead of
    being covered); closing it restores the original right column (tool
    details) untouched, and a slim SSH rail on the right edge reopens the
    panel. Each connection owns one real interactive shell (PTY): the
    login banner (motd / Last login), the remote prompt user@host:path$,
    input echo and cd updates all come from the remote shell, exactly like a
    native SSH client. There is no input box and no copy control — click
    the terminal and type; keystrokes go straight into the remote shell
    (arrows, Tab, Ctrl-C, paste, IME supported). A blinking block cursor shows
    while the terminal is focused. AI-run commands appear in the same
    scrollback with source tags. Multi-tab, ANSI colors, scrollback. The view
    stays pinned to the newest output as it arrives — even large output bursts
    follow to the bottom; scrolling up to read history is respected (no forced
    jump), but whenever the AI starts operating the connection the terminal
    slides back to the bottom so the AI's live output is visible.
  • Settings page — "SSH Connections" under Settings manages everything:
    create, edit (rename supported), delete connections and their credentials
    (passwords / private keys stored in the DSH credential store), and
    connect/disconnect. The terminal column itself contains no CRUD.
  • Credential hygiene — passwords and private keys live in the DSH
    credential store under generated references; the records file, logs and
    tool results never contain secret material; inline secrets in tool
    arguments are rejected with guidance; auth failures return scrubbed,
    readable reasons.
  • Settings — the dsh-ssh settings namespace (heartbeat, reconnect
    policy, timeouts, output caps, records path) can be overridden through the
    DSH settings system / profile patch.
  • Bilingual UI — Chinese and English copy.

Requirements

  • Node.js >= 18 (ESM)
  • pnpm (lockfile: pnpm-lock.yaml)
  • A DeepSeek Harness (DSH) installation with the web profile
  • For the test suite: a reachable SSH server (the included tests target a
    local WSL OpenSSH instance; see scripts/test-acceptance.mjs)

Install into a profile

# from npm (recommended)
dsh plugin --profile web add @jmcc-guo/dsh-ssh

# or directly from GitHub
dsh plugin --profile web add "github:jmcc-guo/dsh-ssh#v0.3.1"

# or from a local checkout
dsh plugin --profile web add <path-to-this-repo>

The bundle patch (cordis.patch.yml) mounts the dsh-ssh row. Override
config in the profile patch with the same row id:

- id: dsh-ssh
  config:
    heartbeatIntervalMs: 20000
    reconnectMaxAttempts: 8
    outputLimitBytes: 2097152

Restart the profile process afterwards (plugin-set changes and the client
bundle graph are composed at boot).

Troubleshooting

ssh2 is bundled — no build steps needed

ssh2 and its full dependency closure are shipped bundled inside the
package
(bundleDependencies), so installing @jmcc-guo/dsh-ssh never runs
dependency build scripts: no allowBuilds edits, no
ERR_PNPM_IGNORED_BUILDS. ssh2 runs on its pure-JS implementation, which is
fully functional (verified against a real SSH server); the optional native
crypto binding is intentionally not compiled.

Peer-dependency warnings

pnpm peers check may report "missing peer" for the @deepseek-ai/* packages
even though DSH provides them: with the hoisted profile layout, external
plugins resolve host packages at runtime from the shared profiles/node_modules
tree, which pnpm's static peer check does not cross. The warnings are benign —
the plugin loads fine (verified at runtime).

Settings namespace (dsh-ssh)

Key Default Meaning
heartbeatIntervalMs 30000 ssh2 keep-alive interval
keepaliveCountMax 3 keep-alive failures before the connection is considered dead
connectTimeoutMs 15000 SSH handshake / TCP connect timeout
reconnectBaseDelayMs 2000 first auto-reconnect delay (doubles per attempt)
reconnectMaxDelayMs 60000 backoff cap
reconnectMaxAttempts 5 max automatic reconnect attempts
execTimeoutMs 120000 default ssh_exec completion wait
busyWaitTimeoutMs 20000 default mutex wait when the connection is busy
reconnectWaitTimeoutMs 30000 default wait while reconnecting
shellQuietWaitMs 2000 shared-shell silence required before AI may run
outputLimitBytes 1048576 per-connection terminal buffer cap
execOutputMaxBytes 200000 cap on output returned to the model per command
recordsPath $DSH_HOME/storages/dsh-ssh/connections.json records file override

Model tools

  • ssh_connect — create a new AI connection (host/port/user + auth by
    credential reference or key file path) or re-establish an existing one.
  • ssh_exec — run a command on an AI connection by name; auto-reconnects
    when offline, waits through reconnect/busy states (with timeouts), returns
    output + exit code; long commands return an execId for
    ssh_exec_read / ssh_exec_kill.
  • ssh_exec_read — incremental output of a running (or finished) command.
  • ssh_exec_kill — terminate a running command (SIGINT through the pty).
  • ssh_list — AI-visible connections with live status (never user ones).
  • ssh_status — detailed status of one AI connection.
  • ssh_disconnect — explicit disconnect (no auto-reconnect; optional
    delete); the panel tab stays open showing "disconnected".
  • ssh_delete — delete a saved AI connection record (disconnects first).

Secret rule for the model: never pass passwords or private keys inline in
tool arguments (they are recorded verbatim in the session log and rejected).
Use auth.passwordRef / auth.privateKeyRef (a stored credential or
environment variable) or auth.privateKeyPath (a key file on the host).
New secrets can be stored through the panel's connection form, which routes
them into the DSH credential store.

Security notes

  • The panel channel (/ssh/ws) applies the harness browser-trust fence:
    loopback/trusted-host Host, same-origin Origin, cross-site fetch-metadata
    rejection.
  • Secrets never leave the credential store: the records file holds
    references only; error messages are scrubbed; logs contain no secrets.
  • Commands run through real PTYs on the remote host: ANSI output works,
    interactive programs work, and termination is a genuine SIGINT to the
    foreground process group. User keystrokes flow through the shared shell's
    PTY; while an AI command runs on an ai-source connection the host drops
    keystrokes (input mutex).

Repository layout

lib/index.js            plugin entry: config schema, manager + tools + panel channel wiring
lib/manager.js          SshManager — connection lifecycle, keep-alive/reconnect, mutex, PTY shells
lib/tools.js            model tools (ssh_connect / ssh_exec / ssh_exec_read / ssh_exec_kill / ...)
lib/ws.js               panel WebSocket channel (/ssh/ws) with the browser-trust fence
lib/store.js            persisted connection records
lib/client.js           Web GUI client: multi-tab terminal panel + settings UI
cordis.patch.yml        bundle patch that mounts the dsh-ssh row
scripts/                test suites (see below)

Development / tests

scripts/ contains the acceptance suite and helpers (requires a reachable
SSH server; the included tests target a WSL OpenSSH instance):

node scripts/test-acceptance.mjs   # 65-check manager-level acceptance suite
node scripts/smoke.mjs             # quick smoke test
node scripts/test-panel-ws.mjs     # panel WebSocket channel drive (test web instance on :3081)
node scripts/test-rename.mjs       # focused rename test (no SSH server needed)

Contributing

Issues and pull requests are welcome. Keep the model-facing surface (tool
names, parameter semantics, result shapes) backward compatible, and make sure
secrets never end up in logs, records or tool results.

License

MIT