在侧栏管理山石设备并开 SSH 终端与审计
30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/5 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add jazhu/dsh-hillstone-cli-ops该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · browser
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 29 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读写/删除本地文件发起外部网络请求
查看扫描证据(共 33 处,展示前 17 条)
- 发起网络请求dist/index.mjs:5015
if ("function" === typeof fetch && !W.startsWith("file://")) return fetch(W, { credentials: "same-origin" }).then(function(a) { - 发起网络请求dist/index.mjs:5131
return J || "function" !== typeof WebAssembly.instantiateStreaming || W.startsWith(V) || W.startsWith("file://") || "function" !== typeof fetch ? d(c) - 写入本地文件dist/index.mjs:20217
mkdirSync(dataDir, { recursive: true }); - 读取本地文件dist/index.mjs:20294
if (!existsSync(devFile) && !existsSync(polFile)) return { devices: 0, policies: 0 }; - 读取本地文件dist/index.mjs:20299
if (existsSync(devFile)) { - 读取本地文件dist/index.mjs:20300
const parsed = parse(readFileSync(devFile, "utf-8")); - 读取本地文件dist/index.mjs:20303
if (existsSync(polFile)) { - 读取本地文件dist/index.mjs:20304
const parsed = parse(readFileSync(polFile, "utf-8")); - 读取本地文件dist/index.mjs:20328
if (existsSync(f)) renameSync(f, f + ".imported"); - 写入本地文件dist/index.mjs:20328
if (existsSync(f)) renameSync(f, f + ".imported"); - 写入本地文件dist/index.mjs:20389
mkdirSync2(userDataDir, { recursive: true }); - 写入本地文件dist/index.mjs:20536
mkdirSync3(dayDir, { recursive: true }); - 写入本地文件dist/index.mjs:20558
appendFileSync(this.file, `${JSON.stringify(rec)} - 写入本地文件dist/index.mjs:20623
appendFileSync(this.file, `${JSON.stringify({ t: "end", at: ( new Date()).toISOString(), reason, entries: this.seq })} - 删除本地文件递归dist/index.mjs:20640
rmSync(join3(logDir, name2), { recursive: true, force: true }); - 发起网络请求dist/client.js:6305
const r = await fetch(API + "/_session"); - 发起网络请求dist/client.js:6316
const res = await fetch(API + path, {
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 设备增删改查
- 实时SSH终端
- 按连接审计日志
DSH 插件:在右侧边栏管理 Hillstone/StoneOS 设备——设备增删改查、实时 SSH 终端、按连接审计日志,还有七个智能体工具。
README
由 DeepSeek 最新模型翻译生成dsh-hillstone-cli-ops
一个 DeepSeek Harness(DSH)插件,把 Hillstone / StoneOS 网络设备搬进 DSH 的右侧栏:管理设备、打开一个真实的 SSH 终端、查看每次连接的审计日志,同时给助手注册 7 个工具,让它自己就能做这些事。
English README
本插件由两半组成:Node 宿主半边(SSH、加密、会话日志、agent 工具)和浏览器客户端半边(右侧栏标签页 + xterm.js 终端)。两半都由同一份 src/ 源码构建。
你得到什么
右侧栏的一个「设备运维」标签页,内含三个子标签:
| 子标签 | 做什么 |
| 设备管理 | 设备的增删改查(名称 / IP / 账号 / 密码 / SSH 端口 / 设备类型 / Web 端口 / 备注)。新增、复制、编辑都走同一个弹窗。每张卡片上有一个 WebUI 登录 按钮,见下文。 |
| 终端 | 每条活动连接对应一个 xterm.js 终端。输入发给宿主,设备输出经 SSE 流回面板。新连接出现时面板直接切到「终端」,你落在实时会话上,而不是设备列表。 |
| 日志 | 每次连接的审计记录:谁连了哪台设备、输入了什么、执行了哪些命令、会话何时因何结束。
展开完整文档(README 剩余 1 节)
dsh-hillstone-cli-ops
一个 DeepSeek Harness(DSH)插件,把 Hillstone / StoneOS 网络设备搬进 DSH 的右侧栏:管理设备、打开一个真实的 SSH 终端、查看每次连接的审计日志,同时给助手注册 7 个工具,让它自己就能做这些事。
English README
本插件由两半组成:Node 宿主半边(SSH、加密、会话日志、agent 工具)和浏览器客户端半边(右侧栏标签页 + xterm.js 终端)。两半都由同一份 src/ 源码构建。
你得到什么
右侧栏的一个「设备运维」标签页,内含三个子标签:
| 子标签 | 做什么 |
| 设备管理 | 设备的增删改查(名称 / IP / 账号 / 密码 / SSH 端口 / 设备类型 / Web 端口 / 备注)。新增、复制、编辑都走同一个弹窗。每张卡片上有一个 WebUI 登录 按钮,见下文。 |
| 终端 | 每条活动连接对应一个 xterm.js 终端。输入发给宿主,设备输出经 SSE 流回面板。新连接出现时面板直接切到「终端」,你落在实时会话上,而不是设备列表。 |
| 日志 | 每次连接的审计记录:谁连了哪台设备、输入了什么、执行了哪些命令、会话何时因何结束。 |
7 个 agent 工具,让助手不必等你也能操作设备:
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 批量执行命令,把输出连同一句自然语言目标(「检查接口状态和 CPU 负载」)交给宿主 LLM 分析。默认复用运维人员已经打开的终端会话,因此你可以在标签页里看着 agent 干活;没有活动会话时它会退回到一次性 exec 通道。
WebUI 登录
每张设备卡片上都有一个 WebUI 登录 按钮。它会在一个真实浏览器窗口里打开设备的管理界面,用该设备已保存的账号登录,然后把窗口留在你的桌面上——之后你就在浏览器里自己操作。
https://<设备 IP>:<Web 端口,未填则 443>/
用之前有三件事值得知道:
- 密码不会到达面板。 宿主解密后直接填进表单,只回传一个结论——已登录 / 需人工完成 / 失败。
本插件的浏览器半边根本看不到密码。
- 它只尝试一次,这是有意为之。 密码错一次之后设备就会开始要求图形验证码,而一旦开始要求,之后任何自动尝试都不可能成功。所以失败被报为「需人工完成」并把窗口留着让你手工完成,而不是重试到锁死。
- 浏览器 profile 是持久的(<数据目录>/webui/<设备 id>),所以对同一台设备第二次点击通常直接打开已登录的会话,不再碰密码。
这个按钮会在运行 DSH 的那台机器上启动一个有界面的 Chromium——它不是无头检查,运行期间会占用一部分屏幕。
浏览器用的是 Playwright 自带的 Chromium。pnpm install 会把 Playwright 钉在一个其浏览器版本与普通桌面环境已缓存内容相匹配的版本上,因此不需要额外下载;如果你升级了 Playwright 而它要求新浏览器,执行一次 npx playwright install chromium 即可。
安装
git clone https://github.com/jazhu/dsh-hillstone-cli-ops
cd dsh-hillstone-cli-ops
pnpm install
pnpm run build # 重新生成 dist/(不入库)
然后把 DSH 指向这个目录——既可以在插件管理器里作为本地 bundle,也可以把包装进你的 profile:
pnpm pack && <你的 dsh 插件安装命令> ./dsh-hillstone-cli-ops-1.0.0.tgz
装完请重启 DSH 主进程。 停用再启用插件不会重建运行中进程的宿主 fiber,所以新的宿主代码(SSH 处理、工具、日志写入)只有在真正重启后才生效。
环境要求:Node 22+;一个暴露了 tools、slots、sidebarRightTabs、sidebarRight、 uiWorkspace 这些服务的 DSH 版本;以及 ssh2 / xterm(由 pnpm install 装好)。
package.json 在 dsh.client.inject 里列出客户端需要的服务,这个列表是有约束力的:它正是 loader 在任何代码运行之前读取的东西。客户端半边自己的 export const inject 与这份 manifest 是 同一个事实的两个独立陈述,所以 test/bundle-gate.mjs 会比对它们,一旦漂移就让门禁失败。
你新增服务时记得两处一起改。
设计说明
这里有若干决定是从实测中得来的,而不是从显而易见的猜测。
StoneOS 上的分页器不是可选项
在真实的 SG-6000 上实测:show cpu detail 产生 9172 个字符,在 --More-- \0 处停了 13 次,而设备不输出任何 SGR 颜色序列。
真正咬人的是第二点:分页器会吞掉任何输入的东西。 在分页器等待期间发送的命令会被静默吞掉——分页器只是把本页剩余内容刷完然后回到提示符。于是「写入命令然后等提示符」的工具会一直挂到超时,而那条命令从未被执行。
因此 hillstone_run_and_analyze 会监测 --More-- 并用一个空格应答它(走与其他输入同一条写队列),同时报告它翻了多少页。hillstone_send_input 会自动提交一条裸命令(见下文),这样模型不必记住那个结尾的回车。
连接时我们不发送 terminal length 0。SG-6000 会以 ^-----unrecognized keyword 拒绝它并且照样分页,所以它除了在每个会话顶部放一条假设备错误之外,什么也没换来。
只登录一次,因为第二次不可能成功
设备的登录接口在密码错误时不仅返回一个错误,还会开始要求图形验证码。这个验证码不是会自行解除的软锁定——从那一刻起它就长在表单上,而且接口对正确密码也不豁免它。于是会自动重试的登录第二次必然失败,而且看起来像是「密码不对」,真相却是「第一次错了,现在需要一个人」。
所以登录只跑一次,结论从 POST /rest/login 的响应体读出({"success":true…},或者 exception.message 加上验证码要求),然后把窗口交出去。面板把这一档标为需人工完成而不是失败,因为验证码是等着人来做的事,不是故障。
同一份实测还带来两个较小的决定:结论取自响应体而不是刮页面,因为登录后的页面上有几百个毫不相干的表单元素;以及 Playwright 用动态 import() 加载,这样装不上时只让一次按钮点击失败并给出可读的错误信息,而不是在加载期把整个宿主插件拖垮。
send_input 会提交,但知道什么时候不该
「模型传一条裸命令然后等输出」是压倒性的常见情况,没有 \r 的话文本就只是躺在设备的行缓冲里。
所以 hillstone_send_input 会补一个回车——除非这段输入明显不是一条命令行:
| data | 行为 |
| 多字符、不含控制字符、不为空白 | 补 \r |
| 单个按键(翻页的 " "、退出的 "q")或任何含控制字符的内容 | 原样发送 |
| 任何内容,但显式给了 submit: true / false | 显式值优先 |
submit 也接受字符串 "true" / "false",因为一个偏 JSON 的调用方可能递过来字符串,而 submit: "false" 不能被当成真值。
输出游标按字节计
hillstone_get_output 是 hillstone_send_input 的读取半边:agent 用 since 游标轮询,而不是重读全部内容或盲目 sleep。这个游标是字节偏移,不是字符偏移。输出记录有上限并在溢出时被重新切片,所以字符串下标会在轮询的 agent 脚下发生位移;落在多字节字符中间的游标会被回退到首字节,而不是吐出一个替换字符。判定「游标太旧」的条件是 since < dropped——与一个不断增长的总长相比,会让任何新输出一到就看起来所有游标都过期了。
违反契约的 ToolDefinition 不会抛错
DSH 的 ToolDefinition 要求 output.schema 与 output.render(args, value),并要求 parameters 是一张扁平属性表(字段 → { type, description, required? }),而不是一个 JSON Schema 对象。违反它不会在注册时报错——工具只是永远不出现在 Tool.listTools 里。
插件看起来一切正常,agent 却报告自己没法操作设备,日志里也没有任何一句说明原因。
所以回归套件和 bundle 门禁断言的是契约本身,不只是工具名:每个工具都必须有 description、一张扁平参数表、一个 execute,以及一个能把真实结果渲染成可读文本块的 output.render,并且渲染失败时是给出可读文本而不是抛异常。
密码永远不到达浏览器
设备密码用每个 profile 各自的密钥静态加密,只在宿主进程内解密。复制按钮是服务端的:宿主复制的是密文,所以复制一台设备绝不会把明文密码搬进页面;编辑副本时密码框留空即保留继承来的那个。
按「复制」不会创建任何东西。它打开的是用源设备预填好的新增弹窗,只有按「保存」才写入记录——所以「取消」真的是取消,而不是留下一条没人要的 -副本。表单是以覆盖项的形式发给宿主的,所以一个改过地址、账号或设备类型的副本,会严格按运维人员输入的内容创建,而他们没动的每个字段仍然来自源设备(密码框留空意为「继承」,不是「置空」)。
侧边栏属于会话,而不是属于应用
连接建立时,插件会打开自己的右侧栏标签页。陷阱在于 DSH 的右栏是按会话的:每一次 openTab / toggleExpanded / close 都会经过 sessions.onScreen 解析,所以它总是落在用户 当前正在看的那个对话上。agent 在你读另一个对话时去连一台设备,过去就会把终端弹到那里。
于是每条连接都带上「是谁发起的」这个身份——宿主侧的 originSessionId,取自工具的 exec.agent.id 或由页面发送——并把展开动作路由到那个会话:先试会话内的 openTabIn(它不会把你挪走),只有当那个会话从未有过右栏时,才用 uiWorkspace.openSession 把主栏切过去并在那边打开标签页。这个切换是通过订阅发布屏幕会话的,所以 openTab 必须等到下一个 tick——同一个 tick 里做会重新制造出正在修的那个 bug。而重复打开一个已存在的标签页只会聚焦它、不会展开右栏,因此还有 isExpanded() / toggleExpanded() 的兜底。
连接落在「终端」上,而不是「设备管理」上
打开面板只完成一半工作。标签页的 body 是由侧栏挂载的,所以如果在标签页还关着的时候察觉到连接,就没有人在听——那个通知掉进了空处,面板带着设备列表出现,而那恰恰是唯一一个完全说明不了「agent 现在在设备上干什么」的视图。
所以「显示终端」这个意图是在打开标签页之前被 park 住的,以它所属的会话为键,由 body 在挂载时认领。这个顺序的两半都重要。先 park 覆盖的是「openTab 同步挂载 body」的情形;先问覆盖的是「标签页从来没打开过」的情形,而 m04040 意味着应答的那个 body 可能是另一个对话的、晚一个 tick 才到。
早期版本 park 的是单个布尔值。在常见情形下(一个会话、一个面板)它看着是对的,但它会被「恰好先挂载的那个面板」认领走——这就是同一个错会话 bug 低一层的重现。按会话分桶的 map 才让它不再回来。
标签页对自己的描述
右栏会在任何人打开一个标签页之前展示它的 guide 条目,所以那段文字是本插件对一个从没见过这个面板的用户所做的描述。它漂移了很久——在面板已经长出搜索、分页、存活探测、审计日志和上面那个自动跳页之后,它还在只宣传「设备管理与 SSH 终端」。过期的说明比没有说明更糟:没有任何迹象提示你即将打开的东西和标签写的不一样。
有两件事让它保持诚实。文案现在与面板一致,并且门禁会断言它,所以删掉这段描述会让构建失败。
那条断言在匹配前会先解码产物里的 \uXXXX 转义——esbuild 以 charset: ascii 产出,把转义手工抄进正则曾经做出了一条会漏掉产物里已经存在的字符串的检查。
固定端口上的本地 API
浏览器半边无法直接调用宿主的服务,所以宿主开了一个回环 HTTP 服务(127.0.0.1:18783,前缀 /ops-api)。它由一个按会话生成的 bearer token 守护,页面从 GET /ops-api/_session 取这个 token,而该端点只回应来自回环、且没有 Origin 或 Origin 是本地的请求——否则用户在使用 harness 期间访问的任何一个网页都能够到这个 API。页面在 X-Ops-Token 里携带该 token。
每次连接、每天一个日志文件
每次连接会写 <dataDir>/oplog/<YYYY-MM-DD>/<connId>.jsonl:一行头、每条 input / command / event 一行、以及最后一行记录结束原因。没有结束行意味着会话仍在活动 ——只记录成功的日志不叫审计记录,所以日志在 SSH 握手之前就打开了,端口被拒或密码错误同样会被记录。设备输出被刻意不记录:一次 show tech-support 转储会让日志膨胀三个数量级,而设备自己留有日志。日期按本地时间分键,保留 30 天。
开发
node node_modules/typescript/bin/tsc --noEmit # 类型检查(build.mjs 不以此为门禁)
node build.mjs # esbuild 双产物构建
node test/terminal-regression.mjs # 离线端到端
node test/bundle-gate.mjs # 对构建产物做特征断言
请按这个顺序运行。bundle-gate 断言的是 dist/,所以一次失败的构建否则会让它在上一次的产物上照样通过——它会比较 dist/ 与 src/* + build.mjs 的 mtime,并报 MISS … (STALE)。
test/terminal-regression.mjs 会拿真实的 dist/index.mjs 对上一个自建的假 StoneOS(走真实 ssh2),其中包含一个会在 --More-- 处停下、并吞掉在那里输入的任何东西的假分页器——与真机相同的失效模式。它还会起第二个宿主实例、配一个会录下注册内容的工具注册表,因为工具面才是 agent 真正用到的东西,而浏览器的 HTTP API 不是。
许可
MIT —— 见 LICENSE。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
jazhu/dsh-hillstone-cli-ops
在侧栏管理山石设备并开 SSH 终端与审计
相关插件 · 浏览器
智能体插件框架deepseek-ai/deepseek-harness
DeepSeek Harnessdsh是由 DeepSeek AI 开发的开源 agent harness智能体框架。
★ 270,422已汉化DSH
71.9
多设备 Agent 看板YaoApp/yao
✨ 所有 Agent 和工作区集中管理,覆盖你的每一台设备。任务看板追踪进度,桌面端、移动端、浏览器、API…
★ 8,147已汉化DSH
71.2
可扩展侧边栏工作台omdsh-dev/DSH-better-sidebar
DSH 的双工作台:原生右侧栏 + 自绘底部面板,并把 ctx.betterSidebar 服务开放给所有插件
★ 4,761已汉化DSH
71.1
浏览器自动化桥Tencent/BrowserSkill
你的 Agent。你的浏览器。连接,就能协作。
★ 9,967已汉化DSH
71
AI 小说创作工作台EthanYoQ/AI-Novel-Writer
AI 小说作家 / AI Novel Writer
★ 1,591已汉化DSH
70.7
智能体浏览器引擎platonai/Browser4
DeepSeek Harness 集成
★ 1,175已汉化DSH
70.4
这是你的仓库?认领条目