内容
# hwatu
[](https://github.com/hongnoul/hwatu/releases)
[](LICENSE)
[](https://github.com/hongnoul/hwatu/actions/workflows/ci.yml)
**编码代理的验证浏览器**。hwatu 将“页面看起来是否正确”转化为代理可以攀爬的数字,并将不可见的代理会话转化为屏幕上的真实窗口,以便在需要人类介入时使用。
您的代理在桌面上不存在的窗口中工作:不会占用焦点,不会污染 WM,支持任意并行度。它会衡量自己构建的内容:像素差异得分、动画曲线以数字形式呈现、动画中点的截图。当遇到 CAPTCHA 或需要人工判断时,`hwatu focus` 会将其实时会话、cookie、状态等全部实体化到您的 tiling WM 中。您只需操作十秒钟,它就会重新接管。

## 循环
一个代理迭代页面以达到设计参考的过程,包含真实的命令和输出:
```sh
hwatu --headless localhost:3000 # 它的窗口;您永远不会看到它
hwatu --headless staging.example.com # 参考
hwatu diff --id 2 --other 1 --heatmap /tmp/heat.png
# {"match_percent":85.13,"regions":[{"x":0,"y":160,"w":2048,...}]}
hwatu motion --id 1 # 参考的动画,以数字形式表示
# easing cubic-bezier(0.25,1,0.5,1), 300ms, marquee 29.78px/s ...
# ...代理编辑代码...
hwatu diff --id 2 --other 1
# {"match_percent":97.49} # 爬升优于猜测
```
我们对 stripe.com 首页的克隆运行了此循环:一个代理从 **85.1% 到 98.8% 像素匹配**,读取 `hwatu motion` 中的缓动函数,并比较使用 `hwatu seek` 固定的动画帧。自己重现:[scripts/demo/](scripts/demo/)。
完整的验证过程(打开无头,等待加载,评估,截图,关闭)的中位数为 **87 ms**。窗口在 **13 ms** 内从温暖的守护进程中生成。详细信息:[docs/benchmarks.md](docs/benchmarks.md)。
## 安装
```bash
curl -fsSL https://raw.githubusercontent.com/hongnoul/hwatu/main/scripts/install.sh | bash
```
一个静态二进制文件加上您的发行版的 `webkitgtk-6.0`(安装程序检查)。无需 Node、npm 包或 170 MB 浏览器下载。在 Arch 上:`yay -S hwatu`。从源代码:`cargo build --release`.
将其连接到任何 MCP 客户端(Claude Code、Cursor、...),只需一个条目:
```json
{ "mcpServers": { "hwatu": { "command": "hwatu", "args": ["mcp"] } } }
```
jcode 原生驱动 hwatu。所有内容也是一个以换行符分隔的 JSON 请求,通过 Unix 套接字,因此任何语言和脚本都可以使用零客户端库驱动它。完整的代理指南:[docs/agents.md](docs/agents.md)。
## 代理获取的内容
| 原语 | 它回答 |
|---|---|
| `snapshot` | 页面上有什么,我可以点击什么(JSON,~tokens not pixels) |
| `diff --other/--baseline` | 这两个渲染有多接近,它们在哪里不同,作为得分 + 区域 + 热图 |
| `motion` | 每个动画都是数字:持续时间、延迟、缓动、关键帧 |
| `seek` | 在时间 t 处固定所有动画;两个快照在同一时间处是字节相同的 |
| `expect` | 在一次调用中断言页面状态(轮询,结构化通过/失败) |
| `shot` / `shot --full` | 用户会看到什么(真正的 GPU-composited WebKit 渲染) |
| `click` / `type` / `scroll` / `upload` | 真实的指针/输入事件,结构化错误信息 |
| `console` | JS 错误,控制台输出,失败的请求自上次检查以来 |
| `challenge` | 这是 CAPTCHA / 反机器人墙吗,应该有人接管吗 |
| `resize` | 在视口宽度范围内验证响应式布局 |
| `focus <id>` | 将任何无头会话实体化为真实窗口供人类使用 |
歧义是一个错误,带有匹配计数,永远不是沉默的错误点击。
`snapshot` 的引用是实时元素句柄;过时是一个明确的错误,而不是谜团。
## vs 替代品
| | hwatu | Playwright(无头 Chromium) | chrome-devtools-mcp | Percy / Chromatic / Applitools | ditto & site cloners | tterm & browser-in-IDE cockpits |
|---|---|---|---|---|---|---|
| 构建用于 | 机器上的代理内部循环 | 跨浏览器 E2E 测试套件 | 代理的 DevTools 检查 | CI 视觉回归门控 | 一次性站点→代码生成 | 人类观察代理 |
| 像素验证 | `diff`:得分 + 区域 + 热图,87 ms 温暖通过 | `toHaveScreenshot` 基线(测试套件形状) | 仅截图 | 成熟,但云往返,按每次拍摄计费 | 无 - 永远不会渲染自己的输出 | 无 |
| 动画 | 读取为数字 (`motion`),固定飞行中 (`seek`) | 禁用或快进到最终状态 | 原始 CDP | 禁用以避免故障 | 在生成时捕获,由人工查看 | 无 |
| N 个代理的焦点窃取 | 从不 - 无头/后台是窗口属性 | 无头:好;头部:每个窗口都会弹出 | 好无头 | 不适用(云) | 不适用 | 它自己的窗格 |
| 会话中途人工接管 | `focus <id>`:相同的实时会话变为真实 WM 窗口 | 不可能无头;头部始终占用焦点 | 无 | 无 | 不适用 | 人类已经在观察 |
| CAPTCHA / 需要人类 | `challenge` 检测 + 结构化等待/恢复 | 手动变通方法 | 无 | 不适用 | 超出范围 | 人类在面板中解决 |
| 运行时依赖 | 1 MB 二进制 + 发行版 webkitgtk | Node + 包 + ~170 MB 浏览器每个版本 | Node + Chrome | SaaS 账户 | Node + Playwright + 服务 | 完整应用 |
| 代理的接口成本 | 一行 JSON / 短 CLI / MCP | 客户端库,会话对象 | CDP 上的 MCP 冗长 | API + 仪表板 | REST/MCP 作业 API | 不适用 |
诚实的警告,以免您在评论区发现它们:原始温暖延迟与 Playwright 持平(83 vs 82 ms),而 hwatu 的驻留内存 *高于* 无头 shell,因为每个 hwatu 窗口都是一个真正的 GPU-composited、WM-mappable 曲面,这是人工接管的代价,部分通过暂停空闲窗口回收(~56 MB 每窗口 120 s 后)。hwatu 渲染 WebKit,而不是 Chromium:适合“我的更改是否正确渲染”,不适合引擎特定的错误查找,在 CI 中保留 Playwright 矩阵以进行这些操作。当前仅限 Linux。全面的对比数据:[docs/benchmarks.md](docs/benchmarks.md)。
## 人工接管
其他结构无法复制的功能:**无头是一种窗口属性,而不是启动模式**。每个不可见的代理会话都是一个实时窗口,WM 只不过没有显示它。
```sh
id=$(hwatu --headless --json https://example.com | jq .id)
hwatu challenge --id "$id"
# {"status":"challenge","challenge_type":"turnstile","manual_required":true}
hwatu focus "$id" # 会话在您的 tiler 中出现,如它所处的那样
hwatu challenge --id "$id" --wait --timeout-ms 60000
# 人类点击复选框;{"status":"cleared"} - 代理恢复
```
相同的 cookie、相同的滚动位置、相同的半填表单。适用于 CAPTCHA、OAuth 同意、2FA 提示、支付确认和纯粹的“您觉得这看起来正确吗”。`challenge` 仅用于检测和人工接管,按设计:无求解器 API、无令牌注入、无指纹游戏。
代理默认情况下是无头的:hwatu 检测编码代理环境(`CLAUDECODE`、`JCODE_SOCKET`、`CURSOR_AGENT`,...),因此忘记的标志永远不会将窗口放在您的 WM 中。`--focus` 选项返回;`HWATU_AGENT_MODE` / `agent_mode` 在 `~/.config/hwatu/config.json` 中调整默认值。
## 对于另一侧的人类
hwatu 也是一个故意简化的 WebKit 浏览器,适用于 tiling WM:无标签(您的 WM 标签页就是标签页),无浏览器 chrome(按需召唤的 vim 样式底部栏),可重新映射的键绑定(`keys.conf`),内置原生广告拦截(EasyList 编译到 WebKit 的 content-extension 引擎,请求路径中无 JS),合理的下载,崩溃恢复会话,TLS 和权限提示为一行 y/n 栏提示。
人类侧面故意限制在人工接管质量。如果您想要链接提示、历史记录补全和密码管理器集成作为日常驱动程序,qutebrowser 是更好的选择,我们也这么说。范围和非目标:[docs/roadmap.md](docs/roadmap.md)。
```sh
hwatu # 启动器(自动启动守护进程)
hwatu example.com # 打开一个 URL
hwatu how to exit vim # 非 URL 变成网页搜索
hwatu list # 每个窗口:id,url,标题
hwatu adblock update # 获取 + 编译 EasyList/EasyPrivacy
hwatu update # 自我更新
hwatu quit # 停止守护进程
```
## 架构
```
hwatu <url> --unix socket--> hwatud (GTK 主循环)
├── 预热 WebView(立即采用,
│ 空闲时间预热下一个)
├── 窗口注册表(id -> WebView)
└── WebKit:共享网络进程,
每个站点网页进程
```
守护进程拥有 WebKitGTK 6 和一个预热的 WebView 池;客户端是一个 Unix 套接字往返,就像 `emacsclient`/`wezterm` 将编辑器分开一样。N 个窗口共享一个引擎(每个额外窗口 ~56 MB);未聚焦的窗口在 120 秒后挂起,并在需要时立即从池中恢复。如果守护进程非正常退出,下次启动时会重新打开每个窗口,位于其最后一个 URL。
Crates:`crates/ipc`(JSON 协议),`crates/hwatud`(守护进程,GTK4 + webkit6),`crates/hwatu`(客户端,无 GTK 链接)。
## 文档
- [docs/agents.md](docs/agents.md) — 完整的代理协议和指南
- [docs/benchmarks.md](docs/benchmarks.md) — 每个数字,测量,方法论
- [docs/roadmap.md](docs/roadmap.md) — 记录计划,优先级,非目标
- [scripts/demo/](scripts/demo/) — stripe 收敛演示,可重现
- [examples/clone/](examples/clone/) — 通用页面克隆管道
MIT 许可。Linux。WebKitGTK 6。