jazhu/dsh-hillstone-cli-ops ↗★ 0
dsh-hillstone-cli-ops
提供Hillstone网络设备管理与SSH运维终端。 适合需要管理网络设备,并允许助手调用工具进行自动化运维的用户。
安裝
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:jazhu/dsh-hillstone-cli-ops說明文件
閱讀完整 README ↗dsh-hillstone-cli-ops
A DeepSeek Harness (DSH) plugin that puts Hillstone / StoneOS network devices into the right sidebar: manage them, open a real SSH terminal, read the per-connection audit trail — and give the assistant seven tools so it can do all of that itself.
The plugin is a two-half DSH plugin: a Node host half (SSH, encryption, session
logs, the agent tools) and a browser client half (the sidebar tab and an xterm.js
terminal). Both are built from one src/ tree.
What you get
A 「设备运维」 tab in the right sidebar with three sub-tabs:
| Tab | What it does |
|---|---|
| 设备管理 | CRUD devices (name / IP / account / password / SSH port / device type / web port / note). 新增, 复制 and 编辑 all run in one centred dialog. |
| 终端 | One xterm.js terminal per live connection. Input goes to the host, device output streams back over SSE. |
| 日志 | The per-connection audit trail: who connected to which device, what was typed, which commands were run, when and why the session ended. |
Seven agent tools, so the assistant can operate devices without you:
hillstone_list_devices, hillstone_open_terminal, hillstone_send_input,
hillstone_get_output, hillstone_close_terminal, hillstone_list_sessions,
hillstone_run_and_analyze.
hillstone_run_and_analyze runs a batch of commands and hands the output to the
host LLM with a natural-language task ("检查接口状态和 CPU 负载"). By default it
reuses a terminal session an operator already has open, so you can watch the
agent work in the tab; with no live session it falls back to a one-shot exec
channel.
Install
git clone https://github.com/jazhu/dsh-hillstone-cli-ops
cd dsh-hillstone-cli-ops
pnpm install
pnpm run build # regenerates dist/ (not committed)
Then point DSH at the directory — either as a local bundle in the plugin manager, or by installing the package into your profile:
pnpm pack && ./dsh-hillstone-cli-ops-1.0.0.tgz
Restart the DSH main process afterwards. Disabling and re-enabling a plugin does not rebuild the host fiber in a running process, so new host code (SSH handling, the tools, the log writer) only takes effect on a real restart.
Requirements: Node 22+, a DSH build that exposes the tools and
sidebarRightTabs services, and ssh2 / xterm (installed by pnpm install).
Design notes
A few things here were decided from measurement, not from the obvious guess.
The pager is not optional on StoneOS
Measured on a real SG-6000: show cpu detail produces 9172 characters and stops
13 times at --More-- \0, and the device emits zero SGR sequences.
The part that bites is the second one: the pager eats anything typed into it. A command sent while the pager is waiting is silently swallowed — the pager just flushes the rest of the page and returns to the prompt. So a tool that writes a command and then waits for a prompt hangs until it times out, having never run the command.
hillstone_run_and_analyze therefore watches for --More-- and answers it with
a space, on the same write chain as any other input, and reports the number of
pages it had to advance. hillstone_send_input submits a bare command
automatically (see below) so the model does not have to remember the trailing
carriage return.
We do not send terminal length 0 on connect. The SG-6000 rejects it with
^-----unrecognized keyword and paginates anyway, so it achieved nothing except
putting a bogus device error at the top of every session.
send_input submits, but knows when not to
A model that passes a bare command and waits for output is the overwhelmingly
common case, and without the \r the text just sits in the device's line
buffer. So hillstone_send_input appends a carriage return — except when the
input is obviously not a command line:
data | behaviour |
|---|---|
| multi-character, no control characters, not blank | appends \r |
a single key (" " to page, "q") or anything containing control characters | sent verbatim |
anything, with an explicit submit: true / false | the explicit value wins |
submit also accepts "true" / "false", because a JSON-ish caller can hand
over a string and submit: "false" must not be read as truthy.
The output cursor counts bytes
hillstone_get_output is the read half of hillstone_send_input: the agent
polls with a since cursor instead of re-reading everything or sleeping blindly.
That cursor is a byte offset, not a character offset. The transcript is
capped and re-sliced when it overflows, so a string index would shift underneath
a polling agent; a cursor landing inside a multi-byte character is walked back to
the lead byte rather than producing a replacement glyph. The since /oplog//.jsonl: a header
line, one line per input / command / event entry, and a final line
recording the end reason. No end line means the session is still active — a
log that only records successes is not an audit trail, so the log is opened
before the SSH handshake and a refused port or a bad password is recorded too.
Device output is deliberately not logged: a show tech-support dump would
inflate the log by three orders of magnitude, and the device keeps its own logs.
Days are keyed in local time, and 30 days are kept.
Development
node node_modules/typescript/bin/tsc --noEmit # typecheck (build.mjs does NOT gate on this)
node build.mjs # esbuild dual bundle
node test/terminal-regression.mjs # offline end-to-end
node test/bundle-gate.mjs # greps the built product
Run them in that order. bundle-gate asserts against dist/, so a failed build
would otherwise leave it passing on the previous artefacts — it compares the
mtimes of dist/* against src/** + build.mjs and reports MISS … (STALE).
test/terminal-regression.mjs boots the real dist/index.mjs against a
self-hosted fake StoneOS over real ssh2, including a fake pager that stops at
--More-- and eats whatever is typed there — the same failure mode as the
real box. It also boots a second host instance with a recording tool registry,
because the tool surface is what an agent actually uses, and the browser's HTTP
API is not.
License
MIT — see LICENSE.