内容
# HyprPilot
程序控制 Hyprland 以适应 AI 代理和人类。键入 Rust IPC 客户端,长期运行的守护进程,具有撤销和快照,CLI,MCP 服务器,输入合成和屏幕捕获 + OCR。
> 状态:**v1.0.0** —首个稳定版本。键入 IPC 客户端,带有撤销/快照的守护进程,CLI,stdio MCP 服务器,输入合成(打字,键入组合键和鼠标 via `wlr-virtual-pointer-v1`),以及屏幕捕获(`grim`)+ OCR(`tesseract`)在 `Vision` 能力组中(由于截图可能捕获私人内容,因此从默认配置文件中排除)。
## 安装
在 Arch 和基于 Arch 的发行版上,从 AUR 安装:
```sh
yay -S hyprpilot-mcp # 或:paru -S hyprpilot-mcp
```
这将安装 `hyprpilot`、`hyprpilot-daemon` 和 `hyprpilot-mcp` 二进制文件。视觉和输入后端(`grim`、`slurp`、`tesseract`、`wtype`、`ydotool`)是可选依赖项 — 安装您需要的。
要从源代码构建,请参阅下面的[快速入门(CLI)](#快速入门-CLI)。
## Crates
- `hyprpilot-core` — 异步 Hyprland IPC 客户端(控制套接字 + 事件套接字),键入查询和调度程序,错误类型。无守护进程感知。
- `hyprpilot-daemon` — 通过 Unix 套接字公开 JSON-RPC 的长期运行进程。记录可逆操作在撤销堆栈中。
- `hyprpilot-cli` — `hyprpilot` 二进制文件。查询、窗口/工作区操作、守护进程控制和撤销的子命令。
- `hyprpilot-mcp` — `hyprpilot-mcp` stdio MCP 服务器。通过键入工具和能力配置文件向 MCP 主机(Claude Desktop,Claude Code)公开 Hyprland 控制权,具有 dry-run 默认值。
- `hyprpilot-input` — 输入合成(打字,键入组合键,鼠标)通过 `wtype`、`ydotool` 和 Hyprland 的 `sendshortcut`。
- `hyprpilot-vision` — 通过 `grim`(wlr-screencopy)和 `tesseract` 进行屏幕捕获和 OCR。守护进程无关:图像字节很大且无状态,因此 CLI 和 MCP 服务器直接调用此 crate。
## 快速入门(CLI)
```sh
cargo build --release
# 直接查询(无需守护进程)
./target/release/hyprpilot query active-window
./target/release/hyprpilot query clients
# 窗口操作(直接到 Hyprland)
./target/release/hyprpilot win cycle
./target/release/hyprpilot ws switch 2
# 守护进程支持的撤销
./target/release/hyprpilot-daemon &
./target/release/hyprpilot --use-daemon ws send +1
./target/release/hyprpilot undo
./target/release/hyprpilot daemon stop
```
## 快速入门(MCP for 代理)
1. 运行守护进程(在 MCP 会话期间持续运行,拥有撤销状态):
```sh
./target/release/hyprpilot-daemon &
```
2. 将 MCP 服务器注册到您的主机。示例 Claude Desktop 配置文件(`~/.config/Claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"hyprland": {
"command": "/absolute/path/to/target/release/hyprpilot-mcp",
"args": ["--profile", "default"]
}
}
}
```
3. 重启主机。它将默认发现约 24 个工具(读取、窗口、工作区、撤销组)。破坏性和进程工具被隐藏,除非您切换到更宽松的配置文件。
### 能力配置文件
配置文件位于 `~/.config/hyprpilot/profiles/<name>.toml`。内置配置文件,无需文件:
- `default` — 读取 + 窗口 + 工作区 + 撤销。隐藏杀死、关闭、执行。
- `unrestricted` — 每个工具。请谨慎使用。
配置文件格式:
```toml
# ~/.config/hyprpilot/profiles/strict.toml
allow = ["read", "window", "workspace", "undo"]
allow_tools = ["kill_active"] # 在组外添加特定工具
deny_tools = ["focus_window"] # 从组中删除特定工具
```
工具组:`read`、`window`、`workspace`、`destructive`、`process`、`undo`。
### Dry-run
变异工具接受 `dry_run: bool` 参数,默认为 `true`。当 `dry_run=true` 时,服务器返回预览(“将会 …”)并且 **不会** 触摸守护进程。代理必须传递 `dry_run=false` 才能实际变异。
这层机制建立在能力配置文件之上;两者必须同意,然后修改才会到达守护进程。
### 快照
捕获已知的良好布局并稍后恢复到它。快照是 `$XDG_STATE_HOME/hyprpilot/snapshots/<name>.json` 下的 JSON 文件。
```sh
./target/release/hyprpilot snapshot save before-meeting
./target/release/hyprpilot snapshot list
./target/release/hyprpilot snapshot diff before-meeting # 预览恢复
./target/release/hyprpilot snapshot restore before-meeting
# 自动保存 `_pre-restore-<unix_ts>` 快照,以便恢复本身是可逆的。
./target/release/hyprpilot snapshot delete before-meeting
```
恢复是尽力而为的。它通过地址、PID、然后 `(initial_class, initial_title)` 匹配活动窗口到快照条目。对于每个匹配项,差异计算在这些维度上:
- **工作区** — 始终。
- **浮动** 状态 — 始终。
- **浮动窗口几何** — 精确(x,y)和(w,h),通过 `movewindowpixel` 和 `resizewindowpixel`。平铺窗口由布局驱动,因此几何差异被抑制。
- **全屏模式** — 通过 `fullscreenstate`,无焦点干扰。
- **固定状态** — 仅适用于浮动窗口(Hyprland 拒绝固定平铺窗口)。
- **活动焦点** — 快照的聚焦窗口最后被重新聚焦,以便覆盖早期操作的任何焦点副作用。
未来工作是重新生成丢失的窗口(快照条目,其进程消失)。快照中存在但在实际窗口中不存在的窗口将保持不变;恢复永远不会具有破坏性。
### 持久撤销
守护进程的撤销堆栈持久化到 `$XDG_STATE_HOME/hyprpilot/undo.json`,每次推送/弹出时。守护进程重启后,`hyprpilot undo` 仍然有效,即使守护进程进程死亡。格式错误的撤销文件在启动时被公开;撤销堆栈为空,而不是中止。
### 输入合成(v0.5)
输入合成类型、按键组合、移动和点击鼠标。构建在外部工具之上,加上 Hyprland 的原生 `sendshortcut` 用于每窗口按键传递。
操作后端路由:
| 操作 | 后端 | 原因 |
|---|---|---|
| `input type` | `wtype`(虚拟键盘) | 快速批量文本输入聚焦窗口;绑定触发与纯文本无关。 |
| `input keys` | `ydotool`(uinput→libinput),回退到 `wtype` | **ydotool 事件到达 Hyprland 的全局绑定匹配器,因此 `super+T` 触发其绑定。** wtype 的虚拟键盘事件从绑定匹配器中过滤掉(仅限聚焦客户端)。 |
| `input mouse-move` / `mouse-click` | `ydotool` | 指针合成。 |
| `input shortcut` | Hyprland `sendshortcut` | 每窗口,无焦点更改,无外部后端。 |
> **`input keys` 需要 `ydotoold` 运行** 以触发绑定。hyprpilot 在 `$YDOTOOL_SOCKET`、然后 `$XDG_RUNTIME_DIR/.ydotool_socket`、最后 `/tmp/.ydotool_socket` 处查找其套接字。如果 ydotoold 无法访问,`input keys` 降级为 wtype — 和弦仍然到达聚焦窗口,但 **不会** 触发合成器绑定。运行 ydotoold 作为 `systemd --user` 服务,以便它始终可用。
**这是 HyprPilot 中最危险的工具界面。** 可以输入的代理可以执行任意 shell 命令在任何聚焦终端中。两个独立的门控适用于任何输入调用到达线路之前:
1. **守护进程环境变量。** 守护进程拒绝每个 `Input*` 请求,除非使用 `HYPRPILOT_DANGEROUS_INPUT_OK=1` 启动:
```sh
HYPRPILOT_DANGEROUS_INPUT_OK=1 hyprpilot-daemon &
```
2. **能力配置文件。** `input` 组不在内置的 `default` 配置文件中。要授予它,请使用内置的 `unrestricted` 配置文件或编写自定义配置文件 — 请参阅 `docs/profile-with-input.example.toml`:
```sh
hyprpilot-mcp --profile unrestricted
# 或使用自定义配置文件:
hyprpilot-mcp --profile my-input-profile
```
两个门控必须对齐,然后代理才能合成输入。
```sh
# CLI 示例(需要上述两个门控)
hyprpilot input type "hello world"
hyprpilot input keys ctrl+shift+t
hyprpilot input shortcut ctrl+t class:firefox
hyprpilot input mouse-move 100 200 --absolute
hyprpilot input mouse-click left
```
`input_shortcut` 使用 Hyprland 的 `sendshortcut` 调度程序,严格来说比 `input_keys` 更安全 — 它通过选择器定位特定窗口,而不是让焦点发生在任何地方。
后端在守护进程启动时检测;如果 `wtype` 或 `ydotool` 丢失,则相应的工具返回 `input_backend_missing` 以及缺失的后端名称。
### 声明性规则
守护进程订阅 Hyprland 的事件套接字,并将每个事件与 `$XDG_CONFIG_HOME/hyprpilot/rules.toml` 中声明的规则进行匹配。每个规则将事件类型与字段相等谓词和一个操作列表配对,以匹配时触发。
```toml
[[rule]]
name = "slack to scratchpad"
on = "openwindow"
when = { class = "Slack" }
do = ["move_to_workspace_silent special:scratch"]
```
语义:
- **第一个匹配项获胜。** 规则从上到下评估;第一个 `on` 匹配且 `when` 谓词全部成立的规则会触发。
- **仅相等谓词** 在 v0.4 中(无正则表达式、范围、否定)。
- **重入保护。** 在引擎执行操作时,传入事件会跳过规则匹配,并在操作完成后出现约 100 毫秒的尾随。防止规则 → 操作 → 回声 → 规则循环。
- **每个操作的故障隔离。** 失败的调度程序记录警告,规则的其余操作仍继续运行。
通过 CLI 或 MCP 检视和验证:
```sh
hyprpilot rules path # 守护进程读取的路径
hyprpilot rules list # 当前磁盘规则
hyprpilot rules validate # 解析 + 编译,在重启前
```
参见 `docs/rules.example.toml` 以获取完整的操作语法参考。
编辑 `rules.toml` 时,守护进程运行时无效果 — 引擎使用其在启动时加载的版本。重启守护进程以重新加载。
### 屏幕捕获 + OCR(v0.6)
`hyprpilot-vision` crate 调用 `grim`(wlr-screencopy)进行捕获,调用 `tesseract` 进行 OCR。两个二进制文件在构造时检测;缺失 → 类型错误。
```sh
# CLI — 图像字节转到 --output 或 stdout。
hyprpilot capture full --monitor eDP-1 --output /tmp/screen.png
hyprpilot capture region 0 0 1920 1080 --output /tmp/region.jpeg --format jpeg
# 对区域进行 OCR。
hyprpilot ocr region 100 200 800 400 --lang eng --psm 6
```
MCP 工具(组:`vision`,仅需选择加入,不在默认配置文件中):
| 工具 | 结果 |
| --- | --- |
| `screenshot` | `Content::Image` 块(base64 PNG/JPEG);整个合成器 |
| `screenshot_region` | `Content::Image`;矩形区域 |
| `screenshot_monitor` | `Content::Image`;单个命名监视器 |
| `ocr_screen` | `Content::Text`;从整个屏幕提取的文本 |
| `ocr_region` | `Content::Text`;从区域提取的文本 |
视觉功能从 `Profile::default_safe()` 中排除,因为截图可以捕获私人内容。要授予它,请在自定义配置文件的 `allow` 中列出 `vision` (参见 `docs/profile.example.toml`),或使用 `--profile unrestricted` 进行受信任的本地开发。
两个后端都对滥用宽容:
- 缺失二进制 → `backend 'grim' is not installed (or not on $PATH)`。
- 零尺寸区域 → `invalid region: width and height must be > 0`。
- Tesseract 在空白图像中找不到文本 → 空字符串(成功),不是错误。
## 设计
- 合成器的 IPC 是事实的来源。我们不缓存。
- 变异操作在 API 边界处键入。用户代码中没有字符串类型的调度。
- 守护进程对于 CLI 是可选的;对于 MCP 服务器是必需的(撤销状态必须在 MCP 主机的生命周期中存活下来)。
- 连接时检测 Hyprland 版本;与活动版本不兼容的命令会快速失败并出现类型错误。
## 里程碑
- v0.1(已完成):核心 + 守护进程 + CLI,~15 个调度程序,内存撤销。
- v0.2(已完成):MCP 服务器,能力配置文件,dry-run。
- v0.3(已完成):快照(捕获/列出/差异/恢复/删除),守护进程重启后持久撤销。
- v0.4(已完成):
- **恢复完整性**:`snapshot_restore` 中的几何、全屏、固定、活动焦点。
- **规则引擎**:守护进程端对 socket2 事件的反应,带有 TOML 规则文件,优先匹配语义,重入保护执行。
- v0.5(已完成):
- **输入合成**:通过 `wtype`、`ydotool` 和 Hyprland 的 `sendshortcut` 进行输入。由环境变量 + 能力配置文件控制。
- v0.6(进行中):
- **屏幕捕获**:`grim` 支持的 PNG / JPEG 捕获(完整、按监视器、区域)通过新的 `hyprpilot-vision` crate。
- **OCR**:`tesseract` 支持的文本提取。
- **MCP `Content::Image`** 带有 base64 PNG/JPEG 和 5 个新工具在 `ToolGroup::Vision`(仅需选择加入)。