# dsh-vps-manager

**在 DSH 中和 SSH 一样无感地使用 VPS，除了精选的部分常用命令之外，使用时还能让对话中的大模型对 VPS 进行操作。**

[English](README.md) | 中文

在 DeepSeek Harness（DSH）里管理你的 VPS：用命令查看服务器状态（不走模型、不花 token），跟 AI 说一句话让它登上服务器干活，常见软件和系统维护按菜谱完成。

所有操作都通过 SSH 密钥登录，并共用同一套执行机制：
- 会改东西的操作跑成远端任务，断线后在服务器上继续
- 同一台机器上的改动一个接一个执行，不会同时进行
- 改配置文件前自动备份
- 改防火墙和 SSH 前，先在服务器上设好自动恢复
- 每次执行都记审计日志

## 目录

- [三种用法](#三种用法)
- [运行要求](#运行要求)
- [安装](#安装)
- [添加机器](#添加机器)
- [在对话里选机器](#在对话里选机器)
- [对话里的终端](#对话里的终端)
- [命令](#命令)
- [跟 AI 说话](#跟-ai-说话)
- [菜谱](#菜谱)
- [设置页](#设置页)
- [它防不住什么](#它防不住什么)
- [数据放在哪](#数据放在哪)
- [开发](#开发)

---

## 三种用法

| | 看信息 | 跟 AI 说话 | 按菜谱安装与维护 |
|---|---|---|---|
| 怎么用 | `/vps-sysinfo` 这类命令 | 「给这台装个 nginx，把 a.com 反代到 3000 端口」 | `/vps-install <菜谱id>` 看计划，`/vps-yes` 执行 |
| 走不走模型 | 不走 | 走 | 不走 |
| 花费 | 零 token | 正常消耗 token | 零 token |
| 能做什么 | 固定的只读查询 | SSH 能做的都能做，按风险分级确认 | 29 条可重复执行的菜谱 |

---

## 运行要求

- DeepSeek Harness 0.1.5-rc.2（在 DSH Desktop 2.0.10 上测试）
- macOS 或 Linux。插件依赖 OpenSSH 的连接复用，Windows 自带的 OpenSSH 不支持
- Node.js >= 22.13
- **只支持 SSH 密钥登录。** 不支持密码登录，也不支持需要输密码的 `sudo`
- 会改系统的操作要求远端用户是 `root` 或有免密 `sudo`；只读查询不需要

---

## 安装

**插件市场**：在 DSH 的插件市场（dshmarket）里搜索 `dsh-vps-manager`，点安装。DSH Desktop 和 `dsh web` 都可以。

**命令行**：DSH Desktop 点菜单栏（Windows 上是任务栏托盘）的 DSH 图标 →「打开 DSH 终端」；命令行版 DSH（`dsh web`）用普通终端。执行：

```bash
dsh plugin add dsh-vps-manager
```

想装 GitHub 上的最新代码（可能比 npm 上发布的版本新），把包名换成 `github:AIcivilization/dsh-vps-manager`。

装好后**重启 DSH**，插件在 DSH 启动时加载。

卸载：打开 **DSH 设置 → VPS 管理**，拉到最下面点「卸载…」（见[设置页](#设置页)）。也可以在终端执行 `dsh plugin remove dsh-vps-manager` 后重启 DSH；这样只移除插件，机器清单、钥匙、SSH 配置和审计日志都会留着。

---

## 添加机器

打开 **DSH 设置 → VPS 管理 →「+ 添加机器」**，向导分四步：

1. **填写信息**：地址、端口、用户名、别名、备注、分组
2. **生成钥匙**：生成专用钥匙 `~/.ssh/dsh_vps_ed25519`，不动你原有的钥匙
3. **放公钥到服务器**，三种方式任选，插件全程接触不到你的密码：
   - 复制公钥，粘到服务商后台的「SSH 密钥」里
   - 复制一行命令，在已经能登录的服务器上执行
   - 一键打开终端，`ssh-copy-id` 命令已填好，你自己输密码
4. **保存并测试连接**：连接配置写入 `~/.ssh/config.d/dsh-vps.conf`，在 `~/.ssh/config` 最顶部加一行 `Include`（改之前备份为 `~/.ssh/config.dsh-bak`），然后体检一次

`~/.ssh/config` 里已有的机器，可以用「从 ~/.ssh/config 导入」直接接管。插件不会改写你自己写的 `~/.ssh/config` 内容。

---

## 在对话里选机器

对话头部有一个 **VPS 开关**：`VPS` 后面先是终端按钮 `>_`，再是每台机器一个小方块。方块里写着编号（第一台 1，第二台 2，依次类推），和设置页里的一致。

- **方块的颜色是真实的连接状态**，不只是「选没选」：
  - **灰**：这个对话没选这台
  - **黄（闪）**：选了，正在连接
  - **绿**：选了，而且刚测过连得上
  - **红**：选了，但连不上。输入框下方写明原因（比如「SSH 配置里找不到这台机器」），旁边有「重试」；鼠标停在方块上也能看到原因

  打开开关、打开这个对话、切回 DSH 窗口时都会现测一次（走复用连接时几十毫秒）；命令、AI 工具、终端每次连服务器的成败也会记下来，方块跟着变
- **点一个方块**：这个对话进入 **VPS 模式**。之后的 `/vps-` 命令都作用在这台机器上；插件还会告诉模型「这个对话在操作哪台、是什么系统（包管理器、init、权限）、80/443 端口被哪个程序占着、在跑哪些服务和容器」，以及几条必须遵守的规矩（名字不许猜、systemd 管的服务只用 systemctl、查不到不许改成重启重装），模型按这个系统写命令，你说「这台」「服务器」它就知道指哪台
- **VPS 模式下模型不能用本机 bash**：一调用就会被拦下，并提示它改用 VPS 工具。这样模型不会把服务器上的问题拿到你的电脑上去查。读文件、搜索这类工具不受影响
- **再点一次**：退出 VPS 模式，方块变灰，本机 bash 恢复。没有绑定时**没有任何默认机器**：命令不执行，AI 必须写明要操作哪台
- 每个对话同一时间只能绑定一台；**绑定只对当前对话有效**：一个窗口开着 VPS 模式操作服务器，另一个窗口照常写本机代码，互不影响
- 也可以用命令：`/vps-use <别名>` 绑定，`/vps-use off` 解除
- 还没体检过的机器，打开开关时会在后台自动体检一次，让模型拿到系统信息

输入框下方平时不显示任何内容，只有这三种情况才出现一行提示：这台机器上有任务在跑、机器连不上、磁盘用到 85% 以上。

---

## 对话里的终端

对话头部 `VPS` 后面的 **`>_`** 按钮就是终端。绑定机器后点一下，输入框下方出现一个真正的终端，跟在 SSH 里操作一样（只有一台机器时，没绑定也可以直接点，会顺手绑上）：

- 菜单脚本、`top`、`htop`、`vim`、`docker exec -it`、`mysql` 这类要反复按键的程序都能用，Ctrl+C、方向键、中文正常
- 终端右上角三个圆按钮：
  - **红色 ×**：结束这个终端，服务器上的 shell 和里面正在跑的程序一起结束
  - **黄色 −**：最小化成输入框下方的一条横栏（显示「终端在后台运行 · 已开 N 分钟」），终端在后台继续跑；点横栏恢复
  - **绿色**：最大化，终端撑满对话区，输入框被推到最上面；再点一次（或双击标题栏）恢复原来大小
- 顶部 `>_` 仍是开关：没开 → 打开；开着 → 最小化；最小化 → 恢复。最小化时它的右上角有个小绿点
- **最小化、切到别的对话再回来，终端和里面的内容都还在**
- **刷新页面或网络断开**：服务器把终端保留一段时间（默认 10 分钟，可在设置里改），期间回来会自动接上，断开期间的输出也补回来；超过时间才结束
- 普通大小时右下角可以拖动调高度，全屏程序会按新尺寸重画；高度会记住
- 关掉 VPS 开关或换到另一台机器，这个对话的终端随之结束
- 这里敲的内容和输出**不经过 AI**；想让 AI 看结果，用 `/vps-sh` 执行
- 每次打开、结束记一行审计日志（不记录按键内容）

**设置**（DSH 设置 → VPS 管理 → 终端）：颜色方案（跟随系统 / 暗色 / 白色，跟随系统即与 DSH 的外观一致）、字号（11–20）、断线后保留多久（5 分钟 / 10 分钟 / 30 分钟 / 1 小时）、是否允许从其他设备打开。

**DSH Desktop 和 `dsh web` 都能用**，终端连接跟着页面地址走（`https` 页面自动用加密连接）。连接要过三道检查：DSH 自己的登录校验、来源必须是 DSH 页面本身、页面里的插件令牌。另外**默认只能在运行 DSH 的这台电脑上打开**：用局域网地址或反向代理访问 `dsh web` 时，要先在 DSH 设置 → VPS 管理 → 终端 里勾选「允许从其他设备打开 VPS 终端」。终端等于这台服务器的完整操作权限，只在你信任访问途径时打开。

实现上不需要本机编译任何原生模块：服务器那头的伪终端由 `ssh -tt` 申请，窗口大小改变时另开一条 SSH 连接调整。终端显示用的是 [xterm.js](https://xtermjs.org)（MIT 许可，随插件附带，第一次打开终端时才加载）。

---

## 命令

命令不走模型、不花 token。DSH 会把命令结果收成一行，所以每条结果的第一行就是结论。

**两条使用规则：**

1. 命令作用在**当前对话绑定的机器**上；没有绑定就不执行。
2. **下表里没写参数的命令，只发命令名本身。** DSH 只会把声明了参数的命令后面的文字交给插件。在不收参数的命令后面加字，整句会被当成普通消息发给模型。需要确认的操作都是先出计划，再单独发 `/vps-yes`。

**自己敲的命令会附给 AI**：DSH 本身不会把命令结果交给模型。插件会记下你用 `/vps-sh` 执行的最近几条命令和输出（令牌、密码、私钥等先打码），在你下次跟 AI 说话时一起附上。所以你可以先自己查，再直接问「看看上面为什么报错」。不想附上的那条，在命令前加 `--private`。

### 入口

| 命令 | 作用 |
|---|---|
| `/vps-help` | 全部命令与用法 |
| `/vps-yes` | 确认刚才的计划：重启、安装，或被拦下的高危命令。只对当前对话有效，5 分钟内有效，执行一次后作废 |

### 看信息

| 命令 | 作用 |
|---|---|
| `/vps-sysinfo` | 系统、CPU、内存、磁盘、负载、公网 IP |
| `/vps-disk` | 挂载点、inode、占用最大的目录（扫描有时间上限） |
| `/vps-ports` | 监听端口和对应进程 |
| `/vps-services` | 正在运行和失败的服务 |
| `/vps-net` | 网卡、累计流量、连接数 |
| `/vps-docker` | 容器列表与磁盘占用 |
| `/vps-ping` | 主机名、系统、负载、运行时长 |
| `/vps-logs <服务名>` | 某个服务最近 100 行日志 |
| `/vps-q <菜谱id>` | 运行任意一条查询菜谱，如 `ip-info`、`top-procs`、`cron-list`、`cert-expiry`、`firewall-status`、`updates`、`login-history` |
| `/vps-sh <命令>` | 在机器上执行命令，输出直接显示在对话里：同一个对话里记住 `cd` 到的目录；`top`、`tail -f`、`journalctl -f`、`less`、`watch` 这类会一直刷新或翻页的命令自动改成一次性输出，`vim`、进入交互 shell 这类做不了的会提示改用[终端](#对话里的终端)；判定为高危的先拦下，发 `/vps-yes` 才执行。前面加 `--bg` 放到后台跑，加 `--private` 表示这条输出不附给 AI |

### 机器

| 命令 | 作用 |
|---|---|
| `/vps-list` | 已登记的机器与状态，★ 标出当前对话绑定的那台 |
| `/vps-use <别名>` | 把当前对话绑定到一台机器；`/vps-use off` 解除绑定 |
| `/vps-probe` | 重新体检：系统、init、包管理器、权限、CPU、内存、磁盘 |
| `/vps-reboot` | 重启前先检查：为什么该重启（内核或 libc 更新待生效）、现在适不适合重启（包管理器正在装东西、或插件有任务在跑时会拦下）、会停掉哪些容器、它们能不能自己启动。发 `/vps-yes` 才重启，之后等机器回来，报告内核变化、容器状态和失败的服务；5 分钟后还连不上就提示你去服务商后台查看 |

### 安装与任务

| 命令 | 作用 |
|---|---|
| `/vps-recipes` | 菜谱清单，按安装 / 配置 / 查询分组 |
| `/vps-install <菜谱id> [key=value …]` | 出计划：检测结果、参数与默认值、步骤、脚本原文。发 `/vps-yes` 执行 |
| `/vps-tasks` | 这台机器上的远端任务列表 |
| `/vps-task <任务号> [--stop]` | 一个任务的状态和日志；加 `--stop` 终止它 |

### 排查

| 命令 | 作用 |
|---|---|
| `/vps-doctor` | 插件版本、当前对话绑定的机器、连通测试、最近几次执行 |

---

## 跟 AI 说话

插件给模型注册了 5 个工具，并附带一份操作规则（skill `vps-operator`，模型需要时自己读取）：

| 工具 | 作用 |
|---|---|
| `vps_hosts` | 列出已登记的机器 |
| `vps_exec` | 在机器上执行脚本 |
| `vps_write_file` | 写远端文件：先备份，校验不通过自动还原 |
| `vps_task` | 远端任务：列表、状态、日志、终止 |
| `vps_recipe` | 菜谱：列表、查看、运行，以及把刚做成的事存成菜谱 |

### 分级确认

插件在每段脚本执行前判定风险级别。**模型自己声明的级别只能往高调，不能往低压。**

| 级别 | 例子 |
|---|---|
| **只读** | `df -h`、`systemctl status`、`docker ps`、`journalctl` |
| **改动** | `apt-get install`、`sed -i`、`systemctl restart`、写文件 |
| **高危** | `rm -rf`、`mkfs`、改防火墙（`ufw`、`iptables`）、`passwd`、`reboot`、杀进程（`kill`、`pkill`、`killall`）、`curl … \| sh` |

要不要弹确认框，取决于这台机器的确认档位（优先级：机器 > 分组 > 全局）：

| 档位 | 只读 | 改动 | 高危 |
|---|---|---|---|
| **谨慎**（默认） | 自动 | 问 | 问 |
| **放手** | 自动 | 自动 | 问 |
| **全自动** | 自动 | 自动 | 自动 |
| 没有审批界面的环境 | 自动 | **拒绝** | **拒绝** |

### 三道保护

- **改文件先备份。** `vps_write_file` 先把原文件备份到服务器的 `~/.cache/dsh-vps/backups/`，再原子写入，然后运行你给的校验命令（比如 `nginx -t`），不通过就自动还原
- **连通性保险。** 改防火墙、SSH、网络配置之前，先在服务器上设一个定时恢复（默认 120 秒），改完后用全新的连接测试。连不上就等它到时自动恢复，你不会被锁在门外
- **长操作不怕断线。** 会改东西的操作跑成远端任务，断线后在服务器上继续，可以再接回来查看。取消只是停止等待，不会杀掉任务；终止任务必须明确发起

---

## 菜谱

内置 29 条，每条都可以重复执行：

- **安装（7 条）**：Docker、Nginx、fail2ban、常用命令行工具、Portainer、Uptime Kuma、Nginx Proxy Manager
- **配置（6 条）**：系统更新、系统清理、虚拟内存（swap）、时区、BBR 拥塞控制、自动安全更新
- **查询（16 条）**：系统信息、磁盘、端口、服务、网络、容器、服务日志、连通与负载、机器体检、出口 IP、资源占用最多的进程、定时任务、证书到期、防火墙状态、可升级的包、登录记录

安装和配置类菜谱都由四部分组成：
- **detect**：判断是否已经装好，或已经是目标状态
- **plan**：写给人看的执行步骤
- **run**：执行
- **verify**：证明真的能用，比如用 `docker info` 而不是 `docker --version`

已经是目标状态的菜谱，执行时会跳过安装，直接验证。

参数用 `key=value` 传入，计划页会列出每个参数和它的默认值：

```text
/vps-install setup-swap size_mb=4096
/vps-yes
```

### 添加自己的菜谱

- **让 AI 存。** AI 帮你做成一件可以复用的事之后，说一句「存成菜谱」。它会把具体值抽成参数，补上 `detect` 和 `verify`；插件检查有没有夹带密码或密钥，然后保存为 `$DSH_HOME/vps-manager/recipes/my-<id>.yml`
- **自己写 YAML 文件**，放进这个目录

自定义菜谱的 id 带 `my-` 前缀，不能覆盖内置菜谱。要修改，就用同一个 id 再存一遍；要删除，删掉对应的文件即可。

自定义菜谱按不可信内容对待：风险级别取「菜谱声明的」和「插件静态判定的」中更严的那个；新增或内容改动后，第一次运行至少按「改动」级别确认一次。

---

## 设置页

**DSH 设置 → VPS 管理**，纯界面操作，不经过模型：

- **机器列表**：编号（与对话头部的方块一致）、地址、权限、分组、备注；可一键测试连通
- **添加机器**，以及**从 ~/.ssh/config 导入**
- **单台机器设置**：别名、地址、端口、用户名、跳板机、放公钥、主机指纹、确认档位、移除机器
- **基础配置**：填写想要的状态（时区、虚拟内存大小、BBR、自动安全更新、fail2ban、常用命令行工具），保存时只执行和当前状态不一样的项，每项作为一个远端任务执行，显示进度
- **全局设置**：默认确认档位、连通性保险时长；当 DSH 的 Web 服务对局域网开放时，是否允许在设置页执行会改动服务器的操作
- **终端**：颜色方案（跟随系统 / 暗色 / 白色）、字号、断线后保留多久、是否允许从其他设备打开[终端](#对话里的终端)（默认不允许）
- **数据位置**
- **卸载**：勾选要做的事，确认后执行，每一步显示结果。在 DSH Desktop 上可以直接移除插件，完成后一键重启 DSH；其他环境会给出要在终端执行的命令
  - 移除插件本身（默认勾选）
  - 移除 SSH 连接配置（默认勾选）：去掉 `~/.ssh/config` 顶部插件加的 `Include` 行，`config.d/dsh-vps.conf` 改名留作备份，两者都能找回
  - 清理服务器上的插件目录 `~/.cache/dsh-vps`（有任务在跑的机器会跳过）
  - 撤销插件钥匙在服务器上的登录权限（从 `authorized_keys` 删掉那一行，先备份）。如果这把钥匙是你登录某台服务器的唯一方式，撤销后就登不上了
  - 删除插件专用钥匙、删除插件数据（机器清单、审计日志、自定义菜谱），删了不能恢复

  服务器上的项会先做，因为本机的配置和钥匙一删就连不上服务器了。除了前两项，其他默认都不勾选

设置页的后端接口只接受同源的 JSON 请求，并校验 DSH 每次启动时随机生成的 token；**接口不提供任意命令执行，也不能随意改文件。** 当 DSH 的 Web 服务绑定在 `0.0.0.0` 时，会改动服务器的操作默认关闭。

---

## 它防不住什么

- **分级确认防的是 AI 失误，不防存心绕过的 AI。** 静态判定识别不了所有变形写法，模型也可以用 DSH 自带的 bash 工具直接 ssh 到服务器
- **DSH 的沙箱管不到这个插件自己启动的 ssh 进程**
- **终端里的操作不分级确认。** 那是你自己在敲命令，跟直接用 SSH 一样，插件不拦截
- **没有通用回滚。** 能兜底的只有文件备份、连通性保险和可重复执行的菜谱。重装系统、升级大版本、动分区之前，请先到服务商后台打快照

---

## 数据放在哪

| 位置 | 内容 |
|---|---|
| `$DSH_HOME/vps-manager/hosts.yml` | 机器清单、分组、确认档位（可以手工编辑） |
| `$DSH_HOME/vps-manager/state.json` | 体检结果、各对话绑定的机器 |
| `$DSH_HOME/vps-manager/recipes/` | 你自己的菜谱 |
| `$DSH_HOME/vps-manager/audit/` | 审计日志：每次执行一行 JSON（来源、机器、动作、档位、结果），按月分文件，保留 6 个月 |
| `~/.ssh/config.d/dsh-vps.conf` | 插件维护的 SSH 连接配置 |
| `~/.ssh/dsh_vps_ed25519` | 插件生成的专用钥匙 |
| 服务器上的 `~/.cache/dsh-vps/` | 远端任务目录、日志、文件备份 |

插件不读取私钥内容，密码也从不经过插件。

---

## 开发

```bash
npm install
npm test
```

194 个测试，不需要真实服务器：用本机的 `sh -s` 代替远端 `sshd`，覆盖载荷协议、远端任务、并发锁、备份还原、风险判定、VPS 模式、卸载、命令、设置页接口、终端连接（鉴权、本机限制、输入输出、窗口大小、断线保留与接回、结束清理）与界面渲染。装了 DSH Desktop 的机器上，还会拿 DSH 自带的 `dsh-tools`、`dsh-skill`、`dsh-user-approval` 核对工具定义、返回值、skill 字段和审批结果词汇。

---

## 许可

[MIT](LICENSE)

附带的第三方代码：`lib/vendor/xterm/` 下是 [xterm.js](https://github.com/xtermjs/xterm.js) 6.0.0 与 addon-fit 0.11.0，MIT 许可，版权归 xterm.js 作者，许可原文见 [lib/vendor/xterm/LICENSE](lib/vendor/xterm/LICENSE)。
