dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
中文 | English
一个 DSH(DeepSeek Harness)插件:探测本地 WSL 发行版和通过 SSH 连接的远程机器——CPU、内存、磁盘、GPU、Docker、进程和 systemd 服务——把结果写成一份原子更新的 JSON 快照,并以四种方式呈现这份快照。
| 界面 | 使用者 | 形态 |
|---|---|---|
remote_* agent 工具 | 模型 | 结构化 JSON + 一行结论 |
/wsx ... 会话命令 | 人 | 对齐的纯文本表格 |
| WinUI 3 桌面面板 | 人 | 置顶的原生窗口,实时 |
| MCP stdio 服务器 | 任意 MCP 客户端 | 与工具同一份实现 |
另外还有一个 GUI 内的网页面板(DSH 侧边栏里的 "remote targets" 图标),在浏览器里渲染同一份快照。
┌─ Remote targets ───────────────── [Probe now] [Open panel] ── ─ □ ✕ ┐
│ 3 targets · 2 online · 1 offline │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ ● WSL Ubuntu 24.04 WSL 127.0.0.1 82ms │ │
│ │ CPU 6.5% MEM 16.3% · 229M/1.4G load 1.12 .26 .09 │ │
│ │ / 2.8% · 1.0T free │ │
│ │ /mnt/c 86.7% · 33.8G free │ │
│ │ probed 2s ago │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ ✕ lab-gpu-01 SSH 192.168.1.50 │ │
│ │ connect timeout: waited 20s for 192.168.1.50 │ │
│ │ (is sshd reachable? is key auth working?) │ │
│ │ probed 12s ago · 3 consecutive failures │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
功能概要
allowMutations 把关;kill 另有独立的 confirm: true 门禁,并拒绝 PID 1。~/.ssh/config、ssh-agent 和 known_hosts 直接可用。dist/ 是一份自包含的 .NET 8 publish:目标机器不需要 .NET 运行时、不需要 Windows App Runtime,也不需要 MSIX。原生 WinUI 3 面板,显示一个在线的 WSL 目标和一个连接失败的 SSH 目标:

窄宽度下:

窄宽度下已知的布局问题。 这张窄截图是诚实的,不是美化的:窗口被压窄时,指标行会以别扭的方式折行,过长的错误字符串会溢出卡片而不是干净地省略,每个目标的标题也可能挤到状态徽标上。面板在这些宽度下能用,但看得出没打磨完。目前也没有紧凑的“单个数字”模式。修这些问题在待办列表上;请把这两张截图当作当前状态,而不是预期状态。
| 要求 | |
|---|---|
| 操作系统(面板) | Windows 10 1809(build 17763)或更新 —— 这是 WinUI 3 的要求 |
| 操作系统(其余部分) | 任何能跑 Node 的平台:/wsx、工具、MCP 和 Skills 都与平台无关 |
| DSH | 带插件系统的版本(bundle 机制 + dsh.client 客户端模块) |
| 远程目标 | 任何运行 sshd 的类 Unix 主机(Linux / macOS / WSL) |
| 使用它 | 什么都不用装 —— dist/ 是自包含的;不需要 .NET 运行时,也不需要 Windows App Runtime |
| 开发它 | .NET 8 SDK(构建窗口)+ Node.js 20(跑测试);不需要 Visual Studio |
非 Windows 主机: 插件的 host 那半边能加载,命令和工具都能用,但面板是 WinUI 3 程序,所以没有浮窗。网页面板正常工作。
| 部分 | 位置 | 职责 |
|---|---|---|
| Host 插件 | lib/index.js | 接线、调度、采集、写快照、注册命令/工具/路由 |
| 传输 | lib/ssh.js | 两条通道:系统 OpenSSH(复用 ~/.ssh/config 和 agent)与 wsl.exe |
| 采集 | scripts/probe.sh | 一份自包含的只读脚本,喂给目标的 POSIX shell |
| 解析 | lib/probe.js | 把分节文本转成结构化指标 |
| 调度 | lib/manager.js | 并发上限、离线退避、冷启动错峰、心跳写入 |
| 桌面面板 | app/ | WinUI 3(C#/.NET 8)程序;读取快照并渲染 |
| 网页面板 | lib/client.js | DSH 侧边栏图标 + 中栏实时面板 + "open desktop panel" 按钮 |
| MCP | bin/mcp-server.js | 零依赖的 MCP stdio 服务器 |
| Skills | skills/ | 两份给 agent 的操作手册 |
Host 和窗口之间通过一个状态文件通信,而不是 HTTP:
$DSH_HOME/remote-panel/state.json # %USERPROFILE%\.dsh\remote-panel\state.json by default
它用临时文件 + rename 写入,所以读取方永远不会看到半份 JSON 文档。旁边 host 还会写 config.resolved.json(归一化后的配置),独立运行的 MCP 服务器因此能看到与 DSH 完全相同的目标列表。
DSH 的 web 服务器有一道信任栅栏:非 loopback 的 Host 头和未认证请求会被直接拒绝。插件确实可以注册路由,但一个外部的原生进程仍然得自己解决认证和端口发现问题。状态文件不需要认证、不需要端口,同一个用户就能读,而且天然支持两种启动顺序——“窗口在 host 之后启动”和“host 重启后窗口重连”。
例外是网页面板:它活在浏览器里,那本来就是同源环境,所以它使用 host 注册的
/dsh-remote-panel/*路由。这些路由会开真实的 SSH 连接并返回机器信息,所以 host 在它们前面放了一道 loopback 信任栅栏:逐段校验 Host,拒绝Sec-Fetch-Site: cross-site,只要带了Origin就必须同源,不是 POST 的一律 405,任何异常都按失败关闭(fail closed)。
首次安装的分步指南见 INSTALL.md。
简版:这是一个标准的 DSH bundle 包 —— package.json 的 dsh.bundle.patch 指向 cordis.patch.yml,由它把 host 插件、skills 和 MCP 服务器插入 profile 的配置树。
# Plugin Manager: add a package, "link:" + the absolute path of this folder
link:C:\path\to\dsh-remote-panel
# or the command line
dsh plugin --profile desktop add link:C:\path\to\dsh-remote-panel # local dev: edits apply without reinstalling
dsh plugin --profile desktop add C:\path\to\dsh-remote-panel # copy it into the profile
bundle 变更要生效,必须重启 DSH。
目标写在 profile 的 cordis.patch.yml 里,位于 dsh-remote-panel 那一行的 config.targets 下。改完重启 DSH。
- id: dsh-remote-panel
name: dsh-remote-panel
config:
targets:
# A local WSL distribution
- kind: wsl
name: WSL Ubuntu 24.04
distro: Ubuntu-24.04
channel: auto # auto = try SSH first, fall back to wsl.exe
user: root # optional: the default user for a WSL target is "root"
tags: [local]
services: [ssh, docker, cron]
# A remote machine (IP or hostname)
- kind: ssh
name: lab-gpu-01
host: 192.168.1.50
port: 22
user: ubuntu
identityFile: ~/.ssh/id_ed25519
tags: [lab, gpu]
# Or just use a Host alias from ~/.ssh/config
- kind: ssh
name: prod-web-1
host: prod-web-1
user: deploy
目标可以用 id、name、主机名,或其中任意一个的唯一片段来选中。省略 id 时,它由 kind + user + host 推导出来——例如 ssh-ubuntu-192.168.1.50。
| 选项 | 默认值 | 说明 |
|---|---|---|
enabled | true | false 会停止所有探测。插件仍然加载,命令/工具/面板仍然可用,但每个目标都停在 unknown。 |
probeIntervalMs | 15000 | 探测间隔(钳制在 3000–3600000)。 |
timeoutMs | 60000 | 全局单次探测超时(钳制在 3000–600000)。WSL 冷启动要花 18–88s —— 如果你会盯着 WSL,别调低这个值。 |
targets[].timeoutMs | 继承全局值 | 单目标覆盖(同样是 3000–600000 的钳制)。只收紧某一个目标——比如一直温热的 WSL 发行版,或纯 SSH 部署。 |
connectTimeoutMs | 10000 | 建连超时(ssh 的 ConnectTimeout)。真正能逮住“机器确实挂了”的就是它,所以全局那 60s 永远不会让你白等。 |
probeOnStart | true | 插件启动时探测一次(错峰进行)。 |
maxConcurrentProbes | 4 | 同时探测多少个目标(钳制在 1–32)。 |
allowMutations | true | 写操作总开关;false 即保持只读。 |
collectDocker | true | 采集开关——关掉能减轻目标端负载。 |
collectProcesses | true | |
collectServices | true | |
collectGpu | true | |
historyLength | 40 | 每个目标保留多少条历史样本用于趋势线(钳制在 0–600)。一次失败仍会推入一个 latencyMs=0 的断点。 |
flushIntervalMs | 500 | 调度器 tick 周期(钳制在 100–60000)。 |
heartbeatMs | 2000 | 什么都没变时多久重写一次快照——面板靠它区分“没变化”和“host 没了”(钳制在 500–300000)。 |
autoLaunch | true | DSH 启动时打开面板窗口。 |
appPath | '' | 面板 exe 的显式路径;留空表示“在包内查找”。 |
stateFile | $DSH_HOME/remote-panel/state.json | 快照写入的位置。 |
targetsFile | — | 从一个 JSON 文件读取额外的目标数组。 |
ssh.executable | 自动检测 | 显式指定 ssh.exe。 |
ssh.user | '' | kind: ssh 目标的默认用户。 |
ssh.port | 22 | kind: ssh 目标的默认端口。 |
ssh.identityFile | '' | 默认身份文件。 |
ssh.commonFlags | [] | 追加到每次 ssh 调用后的额外 argv。 |
ssh.controlMaster | true | 连接复用。热探测从 ~300ms 降到 ~80ms。 |
ssh.controlPersistSec | 60 | 复用的 master 保持存活多久(钳制在 0–3600)。 |
wsl.executable | wsl.exe | |
wsl.defaultUser | '' | 目标没有 user 时使用。 |
单目标选项:kind(wsl | ssh)、id、name、distro、host、port、user、channel(auto | ssh | wsl)、identityFile、sshConfigHost、strictHostKeyChecking、timeoutMs、tags、enabled、services。kind: wsl 目标还可以设 sshPort(默认 2222)——那是通过 SSH 访问该 WSL 目标时使用的端口,这类目标的 host 侧永远是 127.0.0.1。
为什么
timeoutMs默认是 60000。 最后一个wsl.exe会话结束约一分钟后,WSL 会把整个发行版拆掉,所以冷启动的第一次探测要花 18–88s。早先 20s 的默认值让健康的 WSL 目标每隔几轮就抖成 “offline”——数据是对的,只是还没到。真正的浪费(等一台根本连不上的机器)改由connectTimeoutMs吸收,所以 60s 只作用于“连得上但很慢”的目标——而这正是 WSL 冷启动的样子。已经温热的部署可以用targets[].timeoutMs收紧单个目标。
kind: wsl 目标有两条到达路径;由 channel 决定:
channel | 行为 | 适用场景 |
|---|---|---|
auto(默认) | 先试 SSH,失败则回落到 wsl.exe | 大多数情况 |
ssh | 只用 SSH | 发行版里跑了 sshd |
wsl | 只用 wsl.exe | 没有 sshd,且当前会话拥有完整访问权限 |
wsl.exe 会被拒绝DSH 的 Windows 沙箱把子进程降到 Low 完整性级别,而 WSL 服务拒绝这类调用者:
| 方式 | 受限沙箱下 |
|---|---|
wsl.exe -l -v、wsl.exe -d ... -- cmd | Wsl/E_ACCESSDENIED |
\\wsl.localhost\Ubuntu-24.04\... | 拒绝访问 |
| SSH 到 WSL 内部的 sshd | 可用,受限和完整访问下都可用 |
这就是 channel: auto 优先走 SSH 的原因。插件把这个特定的失败当成一种可诊断的状态,而不是泛泛的错误:一旦看到 Wsl/E_ACCESSDENIED,它会明说,并指向 SSH 路线。wsl-link skill 会在发行版里部署一个 sshd(监听 127.0.0.1:2222,仅 loopback,仅公钥认证)。
最后一个 wsl.exe 会话结束约一分钟后,WSL 会把整个发行版拆掉——即使 SSH 正在用它。所以空闲期之后的第一次调用要付冷启动的代价,紧接着的那次不到一秒。这不是故障。
要么接受它(全局默认 timeoutMs: 60000 已经覆盖了),要么在 %USERPROFILE%\.wslconfig 的 [wsl2] 下加 vmIdleTimeout=3600000 把它消掉——代价是发行版会长期占着约 1.4 GB 内存。
当一次探测耗时 10 秒或更多,面板会把那张卡片标为冷启动,这样第一次采样慢就不会被误当成链路坏了。
插件总是带上 ssh -o BatchMode=yes(以及 NumberOfPasswordPrompts=0)。因此它永远不可能弹密码提示——在没有 TTY 的子进程里,密码提示只会一直挂到探测超时。如果某个目标一直 offline,先检查免密登录:
ssh -o BatchMode=yes -o ConnectTimeout=10 -o NumberOfPasswordPrompts=0 -- user@192.168.1.50 'echo OK'
更深入的参考资料(与本 README 一同编写):
/wsx 子命令及其示例输出/wsx ... 命令/wsx status overview of local WSL and remote machines
/wsx status [target] status overview (optionally one target)
/wsx list list all configured targets and their channel
/wsx probe [target] probe now (all targets when none is given)
/wsx docker [target] list containers
/wsx services [target] list key service states
/wsx ps [target] [n] list processes by CPU
/wsx exec <target> <cmd> run a command on the target
/wsx panel show / raise the desktop panel window
/wsx open open the plugin data directory and the state file
/wsx help the command list above
/wsx list 还会打印一行工具注册路径,说明这些工具是通过官方的 defineTool 助手注册的(附带额外的 schema 校验),还是回落到裸 JSON Schema。两者都能用。
在开发这个插件的机器上,跑的是回落(
raw)路径,原因值得记下来:@deepseek-ai/*这些包只存在于app.asar内部,没有被解包到磁盘,所以第三方插件的 ESMimport('@deepseek-ai/dsh-tools')在 Node 里解析不了(Cannot find package '@deepseek-ai/dsh-tools')。于是插件用
projectParameters()把自己的参数方言翻译成标准 JSON Schema,再走ctx.tools.register的 raw 路径注册——这条路径只用 Node 内置能力,因此永远可用。那行注册路径会如实报告正在走哪条路;看到raw不是故障。 两条路径的行为、参数校验和安全门禁完全一致。
七个 remote_* 工具,与 MCP 服务器共用同一份实现和同一套门禁:
| 工具 | 用途 |
|---|---|
remote_status | 目标总览;refresh: true 立即重新探测 |
remote_exec | 在目标上执行命令,返回 stdout/stderr/退出码 |
remote_files | list / read / upload / download |
remote_docker | ps / logs / start / stop / restart / pause / unpause |
remote_services | status / start / stop / restart / reload |
remote_processes | list(按 cpu/mem)和 kill |
remote_panel | 显示 WinUI 3 面板窗口 |
cordis.patch.yml 里已经有一行,把这套相同的能力通过 MCP stdio 暴露出去:
- id: mcp-remote-panel
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: remote_panel
transport: stdio
command: !!js process.execPath
args: !!js "process.env.DSH_PROFILE_DIR ? [process.env.DSH_PROFILE_DIR + '/node_modules/dsh-remote-panel/bin/mcp-server.js'] : []"
这些工具会以 mcp__remote_panel__<tool name> 的形式出现。不想要 MCP 就整块删掉——不影响别的任何东西。(真实文件里还设了 cwd、failOnStartupError: false 和 toolCallTimeoutMs;上面这段摘录裁到只剩理解接线方式所需的部分。)
不用 DSH,手动验证 MCP 服务器:
$env:DSH_WSX_CONFIG = '{"config":{"targets":[{"kind":"wsl","distro":"Ubuntu-24.04","channel":"wsl","user":"root"}]}}'
'{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node .\bin\mcp-server.js
stdout 上只能有协议 JSON(按换行分隔);所有诊断信息都走 stderr。 给那个文件加日志时请写 stderr——stdout 上多一个字符就会破坏协议。
目标列表来自 host 插件写出的 config.resolved.json,所以 MCP 侧和 DSH 侧看到的目标永远一致;配置合并逻辑没有在那里重新实现一遍。
两个 skill,装好之后 agent 可以加载:
| Skill | 用途 |
|---|---|
remote-panel | 什么时候该用哪个界面、典型工具用法、WSL 的两条通道和冷启动 |
remote-panel-troubleshooting | 分层诊断:配置 → 链路 → 采集 → 状态文件 → MCP → 被拒的写操作 |
它们由 install.ps1(以及 INSTALL.md 里的手动步骤)装进 DSH 默认的 skill 扫描根目录 $DSH_HOME/skills/,而不是通过 bundle patch。cordis.patch.yml 里的长注释解释了为什么 bundle patch 在这里无法启用 skill-filesystem(web-app 层禁用了那一行,而非 insert 的 patch 只赋值它携带的键,因此永远清不掉继承来的 disabled)。对 skill 内容的修改实时生效——那个目录被监视着;代码和配置的改动需要重启。
.\build.ps1 # Release build (for development)
.\build.ps1 -Pack # publish into dist\ (the shipping location)
.\build.ps1 -Clean # clear bin/obj/dist
你不需要 Visual Studio。 关键在于 csproj 里的 EnableMsixTooling=true:它决定用哪套 PRI 工具链。设成 false 会回落到 MrtCore.PriGen.targets,它依赖 Microsoft.Build.Packaging.Pri.Tasks.dll——一个只在 Visual Studio 里才有的程序集——于是在干净的 .NET SDK 上必然以 MSB4062: ... ExpandPriContent 失败。设成 true 时,改用 Windows App SDK NuGet 包内随附的独立工具链。打包本身则由 WindowsPackageType=None 关掉。
dist/ 是一份自包含发布,带着完整的 .NET 和 Windows App SDK 运行时(视剪裁情况约 163 MB / 约 324 个文件)。换来的是:目标机器不需要 .NET 运行时、不需要 Windows App Runtime,也不需要 MSIX 注册——拷过去就能跑。
build.ps1 -Pack 还会剪掉 .pdb 文件和未使用的本地化 satellite 目录,然后跑一次无头自检(test/run-checks.ps1,用面板的 --dump-status)来确认发布出来的 exe 真的能启动,而不只是躺在磁盘上。
发布出来的 exe 是未签名的,所以第一次启动时 Windows 会弹一个“未知发布者——你确定要运行这个软件吗?”的安全警告。build.ps1 -Pack 会在发布之后给它签名;你也可以单独运行:
.\sign-panel.ps1 # create/reuse a code-signing certificate, trust it, sign the exe
.\sign-panel.ps1 -Uninstall # revoke: remove the signature and the certificate
它做四件事,而且只写当前用户的 HKCU 存储——不碰机器级证书存储,也不需要管理员权限:
CurrentUser\My 里创建(或复用)一张自签名的代码签名证书;CurrentUser\Root 和 CurrentUser\TrustedPublisher;dist\WsxPanel.exe 签名(SHA-256);诚实的局限。
- 自签名并不能可靠地去掉“未知发布者”警告。 在常见情况下,它能在本机为本用户去掉警告,但它不是真实信任链的替代品;只有商业代码签名证书才能可靠地为他人去掉这个警告,也只有它才能产生 Windows 信任的发布者名称。
- 把证书放进“受信任的根”意味着这个用户信任由该证书签名的任何东西。这是自签名固有的性质,不是脚本的疏忽。
-Uninstall会撤销它(签名字节仍留在 exe 里,但失去信任链,等同于未签名)。- 它只对本机的当前用户有效。把
dist/拷到另一台机器,警告就会回来。- 重新构建会覆盖 exe,从而使签名失效,所以签名必须是构建的最后一步——这正是
build.ps1 -Pack已经采用的顺序。
node test\run-all.mjs # or: node test\run-all.mjs Ubuntu-22.04
五个套件,在一台有可用本地 WSL 发行版的 Windows 主机上测得共 139 项检查:
| 套件 | 需要 WSL | 覆盖内容 | 检查数 |
|---|---|---|---|
unit.test.mjs | 否 | 命令注入面、配置归一化、探测解析(含 CPU 公式)、调度(心跳/错峰/门禁)、目标选择、schema 方言投影、ssh/scp argv 构造、原子写入 | 69 |
client.test.mjs | 否 | 浏览器那半边的契约:模块 id、slot 配对、主题 token、路由 | 10 |
lossless.test.mjs | 否 | remote_* 的返回值必须是无损 JSON——一个 undefined 就会让 DSH 拒绝整个调用 | 10 |
smoke.test.mjs | 是 | 真实路径 buildProbeCommand → runRemote → parseProbeOutput,外加只读的目录列举/文件读取和一次目标级超时 | 26 |
mcp.test.mjs | 是 | MCP 协议:initialize / tools/list / tools/call / 错误码 / 从垃圾输入中恢复 | 24 |
三个离线套件在任何地方都能跑。另外两个会驱动一个真实的发行版:它们用内联配置跑通协议,然后对活的目标调用工具,所以需要一个可用的本地 WSL 发行版。如果没有发行版响应,run-all.mjs 会把那些套件报成 FAIL 而不是跳过,这样汇总行反映的是现实,而不是悄悄通过。
任何套件也可以单独运行:
node --test test\unit.test.mjs
node --test test\client.test.mjs
node --test test\lossless.test.mjs
node test\smoke.test.mjs Ubuntu-24.04
node test\mcp.test.mjs Ubuntu-24.04
如果你
PATH上的node不是 DSH 用的那个,就显式使用那个解释器——先跑node --version确认你在 Node 20 或更新版本上。
run-all.mjs 覆盖 Node 那半边。面板本身由两个 PowerShell 自检覆盖,两者都需要 Windows 桌面会话(它们不属于 run-all.mjs):
pwsh -File .\test\run-checks.ps1 -Exe .\dist\WsxPanel.exe # headless: asserts --dump-status output
pwsh -File .\test\gui-smoke.ps1 -Exe .\dist\WsxPanel.exe -Shot .\docs\panel-default.png
run-checks.ps1 生成一份形状真实的状态文件,并断言窗口会显示的那行状态(它与窗口共用 reader 和 formatter,所以这不是一个仿制品)。gui-smoke.ps1 真正启动窗口,断言它活着且拥有真实的主窗口句柄,可选地截图,并打印新增的日志行。build.ps1 -Pack 在发布之后会自动运行 run-checks.ps1。
dsh-remote-panel/
├── package.json bundle manifest (dsh.bundle.patch → cordis.patch.yml)
├── cordis.patch.yml the bundle patch itself (host plugin row, MCP row, rationale)
├── install.ps1 install/uninstall helper (also copies the skills)
├── build.ps1 build/publish the WinUI 3 panel
├── sign-panel.ps1 self-sign (and revoke) the panel exe
├── release.ps1 maintainer-only: privacy scan, zip + tgz, SHA256 (not shipped)
├── README.md this file
├── INSTALL.md first-time install guide
├── CHANGELOG.md version history
├── LICENSE MIT + third-party notices for the bundled runtimes
├── .gitignore build output, runtime state, packaging artifacts
├── lib/ host plugin: index, config, manager, state, ssh, probe,
│ ops, remote-ops, tools, commands, client, panel, format, util
├── bin/mcp-server.js zero-dependency MCP stdio server
├── scripts/probe.sh the read-only collection script sent to the target
├── app/ WinUI 3 (C#/.NET 8) panel sources
├── dist/ self-contained panel publish (shipping location)
├── skills/ the two agent Skills
├── locale/ panel title/description strings (en, zh)
├── docs/ screenshots, the snapshot JSON Schema, reference docs
└── test/ the five Node suites plus the PowerShell panel self-checks
预编译的发布归档为用户做了裁剪:它包含 lib/、bin/、scripts/、skills/、dist/、docs/、locale/、各个 manifest、三个 PowerShell 脚本(install.ps1、build.ps1、sign-panel.ps1)以及文档——但不含 app/ 源码、test/ 和 release.ps1。因此要跑测试需要仓库,而不是发布归档。
/proc、df 和 ps。不支持 Windows 目标(WinRM,或 SSH 到 Windows)。nvidia-smi)。remote_exec 不在 allowMutations 的覆盖范围内。 它接收的是任意命令,插件无法判断那条命令是否只读。责任在调用方。sftp-server 就能传输任意大小的文件。prod 匹配到两台机器时,命令和工具都会要求更具体的指定。从 /wsx list 开始(它会报告工具注册路径和任何配置问题),然后按 skills/remote-panel-troubleshooting/SKILL.md 走一遍——它是逐层写的,所以能把“连不上”定位到链条上具体的一环。最常见的三种情况:
| 症状 | 首先怀疑 |
|---|---|
某个目标一直是 offline | 没有配置免密登录(插件从不弹密码,所以密码认证和超时无法区分) |
| 某个 WSL 目标偶尔超时 | 冷启动(看 latencyMs:第一次大、之后小,就正是它) |
| 面板一片空白 | dist/WsxPanel.exe 从来没构建过;/wsx panel 会明确说明 |
INSTALL.md 专门列出了首次运行时的失败情况。
本插件采用 MIT 许可 —— 见 LICENSE。package.json 声明 "license": "MIT",两者一致。
重新分发之前有一点值得一读:dist/ 里预编译的面板是一份自包含的 .NET 8 publish,因此它打包了上面那份 MIT 许可不覆盖的运行时组件——.NET 运行时(MIT,© .NET Foundation and Contributors)、Windows App SDK / WinUI 3(Microsoft Software License Terms),以及 Microsoft.Windows.SDK.NET.dll(MIT,© Microsoft Corporation)。LICENSE 在第三方声明(third-party notices)一节里列出了这些。如果你把 dist/ 连同自己的作品一起分发,请一并带上这些声明;从源码重新构建(build.ps1 -Pack)会复现同一套组件。
A DSH (DeepSeek Harness) plugin that probes local WSL distributions and remote machines over SSH — CPU, memory, disk, GPU, Docker, processes and systemd services — writes the result into one atomic JSON snapshot, and surfaces that snapshot in four ways.
| Surface | Consumer | Shape |
|---|---|---|
remote_* agent tools | the model | structured JSON + a one-line verdict |
/wsx ... session commands | humans | aligned plain-text tables |
| WinUI 3 desktop panel | humans | always-on-top native window, live |
| MCP stdio server | any MCP client | the same implementation as the tools |
Plus an in-GUI web panel (the "remote targets" icon in the DSH sidebar) that renders the same snapshot in the browser.
┌─ Remote targets ───────────────── [Probe now] [Open panel] ── ─ □ ✕ ┐
│ 3 targets · 2 online · 1 offline │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ ● WSL Ubuntu 24.04 WSL 127.0.0.1 82ms │ │
│ │ CPU 6.5% MEM 16.3% · 229M/1.4G load 1.12 .26 .09 │ │
│ │ / 2.8% · 1.0T free │ │
│ │ /mnt/c 86.7% · 33.8G free │ │
│ │ probed 2s ago │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ ✕ lab-gpu-01 SSH 192.168.1.50 │ │
│ │ connect timeout: waited 20s for 192.168.1.50 │ │
│ │ (is sshd reachable? is key auth working?) │ │
│ │ probed 12s ago · 3 consecutive failures │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
Feature summary
allowMutations; kill has its
own independent confirm: true gate and refuses PID 1.~/.ssh/config, ssh-agent and known_hosts just work.dist/ is a self-contained .NET 8 publish: the target
machine needs no .NET runtime, no Windows App Runtime and no MSIX.The native WinUI 3 panel, showing one online WSL target and one failed SSH target:

At a narrow width:

Known layout issues at narrow widths. The narrow screenshot is honest, not flattering: when the window is squeezed, metric rows wrap awkwardly, long error strings overflow their card instead of eliding cleanly, and the per-target header can crowd the status badge. The panel is usable at those widths but visibly unpolished. There is also no compact "single number" mode. Fixing this is on the list; treat these screenshots as the current state rather than the intended one.
| Requirement | |
|---|---|
| OS (for the panel) | Windows 10 1809 (build 17763) or newer — a WinUI 3 requirement |
| OS (for everything else) | any platform Node runs on: /wsx, the tools, MCP and Skills are platform-independent |
| DSH | a version with the plugin system (bundle mechanism + dsh.client client modules) |
| Remote targets | any Unix-like host running sshd (Linux / macOS / WSL) |
| To use it | nothing to install — dist/ is self-contained; no .NET runtime, no Windows App Runtime |
| To develop it | .NET 8 SDK (to build the window) + Node.js 20 (to run the tests); Visual Studio is not required |
Hosts other than Windows: the host half of the plugin loads and both the commands and the tools work, but the panel is a WinUI 3 program, so there is no floating window. The web panel works normally.
| Part | Location | Role |
|---|---|---|
| Host plugin | lib/index.js | wiring, scheduling, collection, snapshot writes, command/tool/route registration |
| Transport | lib/ssh.js | two channels: system OpenSSH (reusing ~/.ssh/config and the agent) and wsl.exe |
| Collection | `scrip |