chrome-agent 0.15.0

Browser automation for AI agents. Single binary, zero deps, CDP direct to Chrome.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
# chrome-agent

[![Crates.io](https://img.shields.io/crates/v/chrome-agent)](https://crates.io/crates/chrome-agent)
[![npm](https://img.shields.io/npm/v/chrome-agent)](https://www.npmjs.com/package/chrome-agent)
[![CI](https://github.com/sderosiaux/chrome-agent/actions/workflows/ci.yml/badge.svg)](https://github.com/sderosiaux/chrome-agent/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Rust 2024](https://img.shields.io/badge/Rust-2024_edition-orange)](https://doc.rust-lang.org/edition-guide/rust-2024/)

<p align="center">
  <img src="docs/hero-logo.png" alt="chrome-agent — Browser automation for AI agents" width="500">
</p>

<p align="center">
  <strong>用一个 3 MB 的二进制文件,把网页变成 Agent 能直接用的记录。</strong>
</p>

<p align="center">
  <a href="README.md">English</a> | <a href="README.cn.md">简体中文</a>
</p>

> **免责声明:** 这是一个独立的社区驱动项目,与 Google 或 Chrome 团队没有任何关联、认可或赞助关系。

> 用户不是你,是你的 LLM。
>
> 你不需要读这份 README,你的 Agent 才需要。安装后运行 `chrome-agent --help`,让 LLM 自己搞定。CLI 内嵌了完整的使用指南,每条错误都附带下一步操作提示,`--json` 给 Agent 结构化数据,你不用写任何适配器。这个页面只是因为 GitHub 需要一个。

## 别人做不到的那一件事

任何浏览器工具都能把页面交给模型。问题在于交过去的形状。

```bash
chrome-agent goto news.ycombinator.com
chrome-agent --json extract --limit 30
```

```json
{"ok":true,"count":30,"pattern":"TR.athing.submission","items":[
  {"title":"PGSimCity - How PostgreSQL Works",
   "url":"https://nikolays.github.io/PGSimCity/",
   "fields":["PGSimCity - How PostgreSQL Works (nikolays.github.io)"]},
  ...
]}
```

不写选择器,也不花一次模型调用去找出这些行。模式是结构化识别出来的,用 MDR/DEPTA 风格的启发式规则给同级相似度、内容异质性和文本链接比打分。

在这个页面上用 [`scripts/measure.sh`](scripts/measure.sh) 测得的结果,你可以自己跑一遍:

| 你交给模型的东西 | token | 模型拿到什么 |
|---|---|---|
| `extract --limit 30` | **1,571** | 30 条新闻,带 URL 的记录 |
| `inspect`(无障碍树) | 5,652 | 整棵树,新闻混在页面结构里 |
| 原始 HTML | 8,727 | 全部内容,包括没人会读的标记 |

三者包含同样的 30 条新闻。只有第一种把它们作为记录交出去。另外两种交的是页面,让模型自己在里面找新闻,而这个代价要付两次:一次在输入 token,一次在解析它们的推理上。

差距有多大取决于页面。在一个本身就是列表的博客归档页上,`extract` 返回约 12,500 token,而树是约 16,100,因为可剥离的外围标记很少。优势来自记录被大量其他标记包围的页面。

其他工具要么让 Agent 写逐站点的选择器(下次部署就失效),要么花一次模型调用去读 DOM,而后者正是这类工具本该省掉的经常性开销。

## 和 agent-browser 有什么不同?

[agent-browser](https://github.com/vercel-labs/agent-browser)(Vercel)是最接近的东西,同样是为 Agent 打造的 Rust CLI,而且遥遥领先:功能更多、用户更多、几乎每天发版。如果你要的是平台,用它。两个诚实的区别:

| | chrome-agent | agent-browser |
|---|---|---|
| **重复记录提取** | `extract`,结构化,不调用 LLM | 无内置;`read` 返回可读文本 |
| **反机器人检测** | 7 项内置 CDP 补丁,`--connect` 连真实 Chrome | 核心无隐身能力,委托给付费云服务商 |
| **进程模型** | 一条命令,一个连接,执行完退出 | 后台守护进程 |
| **元素 ID** | `backendNodeId`,同一页面下次 inspect 仍然有效 | 顺序 `@e1`,每次快照重新分配 |
| **浏览器原生下载** | 不支持,只有 `download --url` | 支持 |
| **MCP 服务** |||

它确实领先的地方:agent-browser 有点击触发的下载、加密的凭证保险库、云服务商集成和 MCP 模式。它同样复用你真实的 Chrome 配置文件,和这里的 `--copy-cookies` 一样,所以登录态访问对双方都不是差异点。

## 什么情况下别用 chrome-agent

借用 [ripgrep](https://github.com/BurntSushi/ripgrep) 的做法,因为浪费你一下午最快的方式,就是一份只列优点的 README。

- **你需要测试框架。** 用 Playwright。断言、重试、trace viewer、测试运行器,还有微软在维护。
- **你需要有支持的产品。** 这是一个人的项目。没有 SLA,没有路线图承诺,没有企业支持。
- **你需要 MCP。** 这里没有 MCP 服务。如果你的编码 Agent 不能执行 shell,这不是合适的工具。
- **你需要浏览器集群。** 没有云、没有代理池、没有验证码破解。Browserbase、Steel、Browserless 做这个。
- **目标站点用 DataDome 或 Kasada。** `--stealth` 过不去。你得用 `--connect` 连真实 Chrome,即便如此也不保证。
- **你需要 Firefox 或 Safari。** 这里说的是 CDP,只支持 Chrome。

## 它建立在什么之上

- **单一二进制,零运行时。** 无 Node,无 npm,无需下载 Playwright。Linux 构建是静态的(musl),任何发行版都能跑,不用对 glibc 版本。
- **错误即指令。** 每次失败都带一个 `hint` 指出下一步:`{"ok":false,"error":"...","hint":"run inspect"}`- **稳定的元素 ID。** uid 来自 Chrome 的 `backendNodeId`,所以 `n82` 下次 inspect 仍指向同一个节点。导航之后它们会变,而 `diff` 会告诉你什么时候发生了,而不是假装比较两个不同的页面。
- **会话持久化。** Chrome 在调用之间保持运行,所以一条命令的代价是一个连接,不是启动一个浏览器。
- **并行 Agent 不会互相干扰。** `--browser agent1``--browser agent2`,独立的 Chrome 实例和独立的会话状态。

```
chrome-agent(3 MB Rust 二进制文件)
    | CDP over WebSocket
    v
Chrome(无头模式,无 Node.js,无运行时依赖)
```

## 安装

```bash
# 为 AI Agent 安装 -- 会安装一个 SKILL.md,你的 Agent 会自动读取
npx skills add sderosiaux/chrome-agent

# 或者只安装二进制文件
npm install -g chrome-agent    # 预编译版
npx chrome-agent --help        # 免安装
cargo install chrome-agent     # 从源码编译
```

## 快速上手

```bash
# 导航并查看页面
chrome-agent goto https://example.com --inspect

# 通过 uid 点击
chrome-agent click n12 --inspect

# 填写表单
chrome-agent fill --uid n20 "user@test.com"

# CSS 选择器同样可用
chrome-agent click --selector "button.submit"
chrome-agent fill --selector "input[name=email]" "hello@test.com"

# 文章内容(Readability — 类似 Firefox 阅读模式)
chrome-agent read

# 可见文本,限定范围并截断
chrome-agent text --selector "main" --truncate 500

# 执行 JS
chrome-agent eval "document.title"

# 截图(返回文件路径,非二进制数据)
chrome-agent screenshot
```

## 命令列表

### 导航

| 命令 | 功能 |
|---------|------------|
| `goto <url> [--inspect] [--max-depth N] [--header "K: V"]` | 导航。缺少 `https://` 时自动补全。`--header`(可重复)发送额外的 HTTP 请求头。 |
| `back` | 浏览器后退。 |
| `forward` | 浏览器前进。 |
| `close [--purge]` | 停止浏览器。`--purge` 删除 cookie/配置。 |

### 检查

| 命令 | 功能 |
|---------|------------|
| `inspect [--verbose] [--max-depth N] [--uid nN] [--filter "role,role"] [--scroll] [--limit N] [--urls] [--max-chars N] [--offset K]` | 带 UID 的无障碍树。`--scroll --limit` 用于无限滚动。`--urls` 解析链接 href。`--max-chars`/`--offset` 限制并分页输出。 |
| `diff` | 查看上次 inspect 以来的变化。 |
| `screenshot [--filename name] [--format jpeg\|png] [--quality N] [--max-width N] [--uid nN\|--selector "css"]` | 截图保存到文件。JPEG/quality/max-width 缩小体积;`--uid`/`--selector` 裁剪到单个元素。 |
| `pdf [--filename name] [--landscape] [--background]` | 将当前页面打印为 PDF 文件。 |
| `tabs` | 列出打开的标签页。 |

### 交互

| 命令 | 功能 |
|---------|------------|
| `click <uid> [--inspect]` | 通过 uid 点击。无盒模型时回退到 JS `.click()`|
| `click --selector "css" [--inspect]` | 通过 CSS 选择器点击。 |
| `click --xy 100,200` | 通过坐标点击。 |
| `dblclick <uid> [--inspect]` | 双击。同样支持 `--selector``--xy`|
| `fill --uid <uid> <value> [--inspect]` | 通过 uid 填写输入框。 |
| `fill --selector "css" <value>` | 通过选择器填写。 |
| `fill-form <uid=val>...` | 批量填写。 |
| `select --uid <uid> <value>` | 按值或可见文本选择下拉选项。 |
| `select --selector "css" <value>` | 通过 CSS 选择器选择。 |
| `check <uid>` | 确保复选框/单选框为选中状态(幂等)。 |
| `uncheck <uid>` | 确保复选框/单选框为未选中状态(幂等)。 |
| `upload --uid <uid> <file>...` | 上传文件到文件输入框。 |
| `upload --selector "css" <file>...` | 通过 CSS 选择器上传。 |
| `drag <from-uid> <to-uid>` | 拖拽元素到另一个元素。 |
| `type <text> [--selector "css"]` | 在聚焦元素中输入文本。 |
| `press <key>` | Enter、Tab、Escape 等按键。 |
| `scroll <down\|up\|uid>` | 滚动页面或将元素滚动到可见区域。 |
| `hover <uid>` | 悬停。 |
| `wait <text\|url\|selector> <pattern>` | 等待条件满足。 |
| `wait network-idle [--idle-ms N] [--timeout N]` | 等待网络静默 `--idle-ms`(默认 500)后返回。比固定 sleep 更适合 SPA/XHR 稳定。 |

### 内容提取

| 命令 | 功能 |
|---------|------------|
| `read [--html] [--truncate N]` | 通过 Mozilla Readability 提取文章。 |
| `text [uid] [--selector "css"] [--truncate N]` | 获取页面或元素的可见文本。 |
| `eval <expression> [--selector "css"]` | 在页面上下文中执行 JS。`el` = 匹配的元素。 |
| `extract [--selector "css"] [--limit N] [--scroll] [--a11y]` | 自动检测重复数据。`--a11y` 用于 React SPA(如 X.com)。 |
| `download <url> [--out path] [--timeout N]` | 在页面内 fetch 下载 URL,因此 cookie/登录态自动带上(可下载需登录的文件)。返回 `{path,bytes,mime}`|

### 监控

| 命令 | 功能 |
|---------|------------|
| `network [--filter "pattern"] [--body] [--live N] [--abort "pattern"]` | 网络请求和 API 响应。`--abort` 拦截匹配的请求。 |
| `console [--level error] [--clear]` | console.log/warn/error + JS 异常。 |

### 高级

| 命令 | 功能 |
|---------|------------|
| `frame <selector\|main>` |`eval`/`inspect` 切换进 iframe(或切回主页面)。仅在单个 `pipe`/`batch` 进程内持续有效。 |
| `batch` | 从 stdin 的 JSON 数组执行多条命令。 |
| `pipe` | 持久化 JSON stdin/stdout 连接。 |

## 全局参数

```
--browser <name>         命名浏览器配置(默认:"default")
--page <name>            命名标签页(默认:"default")
--connect <auto|url>     连接到正在运行的 Chrome(必须带值:"auto",或 ws:// / http:// URL)
--headed                 显示浏览器窗口(默认:无头模式)
--stealth                反检测补丁(Cloudflare、Turnstile)
--copy-cookies           使用真实 Chrome 配置的 cookie
--timeout <seconds>      命令超时时间(默认:30)
--max-depth <N>          限制 inspect 深度
--ignore-https-errors    接受自签名证书
--json                   结构化 JSON 输出
--dialog <mode>          JS 对话框策略:accept(默认)、dismiss 或 manual
--dialog-text <text>     当 --dialog accept 时,为 prompt() 对话框提交的文本
```

JS 对话框(`alert`/`confirm`/`prompt`/`beforeunload`)默认自动应答(`--dialog accept`)—— 否则原生对话框会阻塞页面且没有任何 DOM 信号,Agent 的下一条命令会挂起。使用 `--dialog dismiss` 取消它们,或用 `--dialog manual` 退出自动应答。

## 核心循环:inspect、操作、inspect

```bash
chrome-agent goto https://app.com/login --inspect
# uid=n52 textbox "Email" focusable
# uid=n58 textbox "Password" focusable
# uid=n63 button "Sign In" focusable

chrome-agent fill --uid n52 "user@test.com"
chrome-agent fill --uid n58 "password123"
chrome-agent click n63 --inspect
# uid=n101 heading "Dashboard" level=1
```

只要 DOM 节点存在,UID 在多次 inspect 之间保持不变。

## 内容提取

按 token 消耗从少到多排列:

```bash
# 文章(Readability,类似 Firefox 阅读模式)
chrome-agent read

# 重复数据 -- 商品、搜索结果、信息流。无需选择器。
chrome-agent extract
# 使用 MDR/DEPTA 启发式算法,自动发现数据模式。

# React SPA(X.com 等)-- 使用无障碍树替代 DOM
chrome-agent extract --a11y --scroll --limit 20

# 限定范围的可见文本
chrome-agent text --selector "[role=main]" --truncate 1000

# API 响应 -- 跳过 DOM
chrome-agent network --filter "api" --body
```

## 表单:下拉菜单、复选框、文件上传

```bash
# 按值或可见文本选择下拉选项
chrome-agent select --uid n15 "California"

# 幂等的复选框控制
chrome-agent check n20     # 已选中则不操作
chrome-agent uncheck n20   # 已取消选中则不操作

# 文件上传
chrome-agent upload --uid n30 /path/to/document.pdf

# 双击(文本选择、特殊控件)
chrome-agent dblclick n42
```

## iframe

`frame` 切换会把 `eval` 和 `inspect` 绑定到该 iframe —— 但**只在同一个进程内有效**,所以要通过 `pipe`(或 `batch`)驱动,绝不能用分开的 CLI 调用:

```bash
printf '%s\n' \
  '{"cmd":"frame","target":"#payment-iframe"}' \
  '{"cmd":"inspect"}' \
  '{"cmd":"fill","uid":"n42","value":"4242424242424242"}' \
  '{"cmd":"frame","target":"main"}' | chrome-agent pipe
```

- 精确指定目标 iframe(例如 `iframe[src*="checkout"]`);裸写 `iframe` 会匹配 DOM 顺序中的第一个,往往是广告的 `about:blank` 槽位。
- `frame` 只作用于 `eval`/`inspect`****作用于 `--selector` 定位。切换后先 `inspect` 拿到 iframe 内的 uid,再按 uid 操作(uid 跨 frame 均可解析)。
- 每个独立的 `chrome-agent <cmd>` 都会打开一个全新连接,因此 `chrome-agent frame …` 后再单独执行 `chrome-agent inspect` 会丢失切换状态。请使用 `pipe`/`batch`
## 批量模式

从 stdin 的 JSON 数组执行命令序列,无需每条命令单独启动进程:

```bash
echo '[
  {"cmd":"goto","url":"https://example.com"},
  {"cmd":"inspect","filter":"button"},
  {"cmd":"click","uid":"n42"}
]' | chrome-agent batch
```

每条命令输出一行 JSON。比每条命令单独启动进程快约 10 倍。

## 隐身模式

`--stealth` 通过 CDP 修补 7 项自动化指纹:

- `navigator.webdriver` 设为 `undefined`
- `chrome.runtime` 模拟
- Permissions API 修复
- WebGL 渲染器掩码
- User-Agent 清理
- 输入坐标泄漏补丁
- `Runtime.enable` 从不调用

这些是 CDP 层面的补丁(`Page.addScriptToEvaluateOnNewDocument`),不是 Chrome 启动参数。

对于使用更强保护(DataDome、Kasada)且会检测 Chromium 二进制文件指纹的网站,请连接到真实的 Chrome:

```bash
google-chrome --remote-debugging-port=9222 &
chrome-agent --connect http://127.0.0.1:9222 goto https://www.leboncoin.fr --inspect
```

| 防护等级 | 解决方案 |
|---|---|
| 无防护 | `chrome-agent goto ...` |
| Cloudflare/Turnstile | `chrome-agent --stealth goto ...` |
| 需要登录的网站 | `chrome-agent --stealth --copy-cookies goto ...` |
| DataDome/Kasada | `chrome-agent --connect` 连接到真实 Chrome |

## 已登录网站

`--copy-cookies` 从你的 Chrome 配置中复制 cookie 数据库。两个 Chrome 实例使用同一个 macOS Keychain,因此加密的 cookie 直接可用。

```bash
chrome-agent --stealth --copy-cookies goto x.com/home --inspect
# 你的时间线、你的私信,无需登录流程。

chrome-agent --copy-cookies goto mail.google.com --inspect
chrome-agent --copy-cookies goto github.com/notifications --inspect
```

你的真实 Chrome 不受影响。

## 网络捕获与请求拦截

```bash
# 已加载的资源(隐身安全,使用 Performance API)
chrome-agent network --filter "api"

# 实时流量及响应体
chrome-agent network --live 5 --body --filter "graphql"

# 拦截追踪/广告请求(使用 Fetch 域拦截)
chrome-agent network --abort "*tracking*" --live 30

# 控制台输出
chrome-agent console --level error    # 仅错误 + 异常
```

控制台捕获使用注入的拦截器,而非 `Runtime.enable`。

## 带链接 URL 的 inspect

Agent 在决定点击哪个链接时,通常需要 URL 而不仅仅是文本:

```bash
chrome-agent inspect --urls --filter link
# uid=n82 link "Pricing" url="https://example.com/pricing"
# uid=n97 link "Docs" url="https://docs.example.com"
```

## Pipe 模式

对于需要连续发送多条命令的 Agent,pipe 模式保持单一连接:

```bash
echo '{"cmd":"goto","url":"https://example.com","inspect":true}
{"cmd":"click","uid":"n12","inspect":true}
{"cmd":"read"}' | chrome-agent pipe
```

每条响应一行 JSON。比每条命令启动一个进程快约 10 倍。

## JSON 模式

```bash
chrome-agent --json goto https://example.com --inspect
# {"ok":true,"url":"...","title":"...","snapshot":"uid=n1 heading..."}

chrome-agent --json eval "1+1"
# {"ok":true,"result":2}

# 错误返回退出码 1,但 JSON 仍在 stdout(可解析):
chrome-agent --json click n99
# {"ok":false,"error":"Element uid=n99 not found.","hint":"Run 'chrome-agent inspect'"}
```

## 多标签页与并行 Agent

```bash
# 同一浏览器中的多个标签页
chrome-agent --page main goto https://app.com
chrome-agent --page docs goto https://docs.app.com
chrome-agent --page main eval "document.title"   # "App"

# 多个 Agent,各自拥有独立的 Chrome
chrome-agent --browser agent1 goto https://example.com
chrome-agent --browser agent2 goto https://other.com
```

## 与 AI Agent 配合使用

```bash
# 安装技能文件(Claude Code、Cursor、Copilot 等)
npx skills add sderosiaux/chrome-agent

# 或者让你的 Agent 运行:
chrome-agent --help
# 帮助输出包含完整的 LLM 使用指南。
```

Claude Code 权限配置:

```json
{
  "permissions": {
    "allow": ["Bash(chrome-agent *)"]
  }
}
```

## 对比

|  | chrome-agent | agent-browser (Vercel) | Playwright MCP |
|---|---|---|---|
| 语言 | Rust | Rust | TypeScript |
| 二进制 | 3 MB,零运行时 | 3 MB CLI + 仪表盘 + 云服务商 | Node + Playwright |
| 启动速度 | ~10ms(会话复用) | 守护进程(首次后快速) | 冷启动 |
| 页面代价(HN 首页) | 5,652 token(`inspect`),1,571(`extract`,全部 30 条记录) | 此处未测量 | 此处未测量 |
| UID 稳定性 | `backendNodeId`(跨 inspect 稳定) | 顺序 `@e1, @e2`(每次快照重新分配) | 不适用(选择器) |
| 操作 + 观察 | `--inspect` 参数(1 次调用) | 单独 snapshot 调用 | 单独调用 |
| 隐身 | 7 项原生 CDP 补丁 | 委托给云服务商 ||
| 阅读模式 | `read`(Readability.js) |||
| 数据提取 | `extract`(自动检测重复数据) |||
| 代码量 | ~22.2K 行 Rust 代码(src/,不含空行与纯注释行;由测试实测) | ~40K 行(对方公布的数字,本仓库无法核实) | Playwright |
| 设计目标 | 最少 token,最大自主性 | 功能完整平台 | 浏览器测试 |

## 许可证

MIT

> 本文档由社区维护。如有翻译问题,欢迎提交 PR。