开始 / Getting Started
它怎么工作
一条 chrome-use 命令从终端出发,要经过四个进程才碰到页面:CLI 本身、每个会话一个的守护进程、
Chrome 为扩展拉起的中继进程、浏览器里的 ab-connect 扩展。这一页把整条链路拆开:每一站在哪个进程里、说什么协议、
状态放在谁手上。读完你就能解释 relay-cdp-url 是什么文件、@ref 为什么会失效、
以及为什么装一个扩展而不是开调试端口。
一条命令走过的四站
以 chrome-use open https://example.com 为例,完整链路是:
每一段都是标准协议,没有私有魔法:CLI 到守护进程是本机套接字,守护进程到中继是普通 CDP,
中继到扩展是 Chrome 原生消息,扩展到页面是 chrome.debugger(和 DevTools 同一套)。
CLI 很薄,状态都在守护进程里
chrome-use 这个二进制在 CLI 模式下不持有任何浏览器状态。它把命令序列化成一行 JSON,
写进 Unix 域套接字,读回一行 JSON,打印,退出。套接字路径是 ~/.chrome-use/<session>.sock
(有 XDG_RUNTIME_DIR 时用 $XDG_RUNTIME_DIR/chrome-use/,见 cli/src/native/daemon.rs
的 get_daemon_socket_dir)。Windows 没有 Unix 套接字,改用 127.0.0.1 上按会话名哈希出的 TCP 端口。
第一次对某个会话发命令时,CLI 发现套接字不存在,就先 fork 出守护进程再重试。守护进程旁边还有几个
sidecar 文件:<session>.pid、<session>.version、<session>.stream(实时流服务的端口)。
守护进程持有三样跨命令的东西:
- 到浏览器的 CDP 连接(
CdpClient) - @ref 到 DOM 节点的映射表(
RefMap,见下文 snapshot 一节) chrome-use script的 JS 引擎(boa,纯 Rust)。引擎跑在守护进程里而不是页面里,所以页面硬跳转时脚本状态不丢,详见 单次成型脚本。
batch
和 script 能「一次往返跑完多步」的底座。守护进程闲置约 10 分钟自动退出,清掉它开的临时标签。
中继进程是 Chrome 自己拉起来的
第三站最容易让人迷糊:中继不是你启动的,是 Chrome 启动的。
装扩展时,chrome-use extension install 往 Chrome 的原生消息宿主目录写一份 manifest
(宿主名 com.leeguoo.chrome_use,兼容旧名 com.agent_browser.connect),里面登记了
chrome-use 二进制的路径和允许对话的扩展 id。之后 ab-connect 扩展一调用 connectNative,
Chrome 就以隐藏的 __nm-host 模式拉起 chrome-use(cli/src/connect.rs 的 run_nm_host),
两边用 stdio 说原生消息协议:4 字节小端长度前缀加一个 JSON 帧。
这个 nm-host 进程做两件事:
- 在 127.0.0.1 上随机绑一个端口,开一个 WebSocket 端点
ws://127.0.0.1:<port>/<guid>,把 URL 写进~/.chrome-use/relay-cdp-url,文件权限 0600。guid 不可猜、文件只有当前用户可读,所以本机其他进程拿不到入口。守护进程读这个文件,把它当成一个普通 Chrome 的 CDP 端点去连。 - 在扩展协议和标准 CDP 之间做翻译,并本地应答
Target.getTargets这类发现命令,避免每次都往扩展绕一圈(cli/src/native/relay.rs)。
~/.chrome-use/relay-cdp-url 不存在,多半是中继没起来(扩展没连上);
nm-host.log 里 stdin EOF 一般是 MV3 后台 worker 被 Chrome 回收了。
细节见 故障排查。
扩展端只做一件事
ab-connect 是个 MV3 扩展,核心就是拿 chrome.debugger 按标签页执行 CDP 命令,把结果通过原生消息交回中继。
权限足迹是 7 项,没有 <all_urls>。两条对指纹重要的性质:
- 它只附加 agent 自己的标签。你正在看的页面不会被 attach,也就不会出现「chrome-use 已开始调试此浏览器」的横幅(扩展 0.5.5 起,只 attach agent 拥有的标签)。
- 这条路径不注入任何 JS 补丁。页面看到的就是你真实的 Chrome、真实的 cookie、真实的指纹,所以 CreepJS 测出来是 0% bot。反检测靠的不是伪装,是根本没有东西需要伪装(见 反检测与隐身)。
换句话说,扩展是整条链路里唯一碰得到页面的进程,而它碰页面的方式和 DevTools 是同一套协议。
为什么装扩展,而不是开 --remote-debugging-port
驱动真实 Chrome 的传统做法是加 --remote-debugging-port=9222 然后直连 CDP。这条路从 Chrome 136 起废了一半:
每次有客户端连上来,Chrome 都弹一个阻塞式的「Allow remote debugging?」确认框。agent 每连一次你点一次,自动化无从谈起。
而且端口要在启动 Chrome 前就开好,等于要求用户换一种方式启动日常浏览器。
原生消息没有这个问题,因为信任关系是反过来的:不是外部进程去敲 Chrome 的端口,而是 Chrome 根据宿主 manifest 里 登记的扩展 id,主动拉起并认证这个宿主。装扩展点一次「添加至 Chrome」,之后每次使用零确认。本机安全性由两层兜住: 只有 manifest 里登记的扩展能启动宿主;中继的 WebSocket URL 带不可猜的 guid,写在 0600 权限的文件里。
两种模式
上面讲的四站链路是默认模式:连你真实的、已登录的 Chrome。还有第二条路,--launch:
# 默认:走扩展中继,用你的真实 Chrome
$ chrome-use open https://example.com
# 另开一个隔离的隐身 Chromium
$ chrome-use --launch open https://example.com
| 扩展中继(默认) | --launch | |
|---|---|---|
| 浏览器 | 你正在用的那个 Chrome | 守护进程自己拉起的独立 Chromium |
| 登录态 | 全部现成 | 空的临时 profile(--profile 可指定持久 profile) |
| 链路 | daemon → 中继 → 扩展 → chrome.debugger | daemon 直连 CDP,没有中继和扩展 |
| 反检测 | 不需要:本来就是真浏览器,零注入 | 原生覆盖(改 UA、时区、webdriver 等,见 stealth.rs),不打 JS 补丁 |
| 适用 | 需要登录态、过反爬、用户随时接管 | 干净环境测试、并行跑一次性任务 |
两种模式共用同一个守护进程架构和同一套命令,区别只在守护进程连的是谁。
每个 --session 一个彩色标签组
多个 agent 共用一个真实 Chrome 时,隔离靠标签组:
$ chrome-use --session research open https://a.com # research 组
$ chrome-use --session shopping open https://b.com # shopping 组,互不可见
守护进程启动时把会话名记进 DAEMON_SESSION,它开的每个标签都落进这个会话专属的彩色 Chrome 标签组。
隔离在中继层强制执行(relay.rs 的 client_groups):守护进程连上中继后先声明自己的组,
此后它的 Target.getTargets 只返回本组的标签。它看不见你的标签,也看不见其他 agent 的标签,自然也就点不到。
细节都有归属规则:新标签打上创建者的组标签;页面弹出的 pop-up 继承 opener 的组,跨进程跳转(比如 OAuth 弹窗)不会跑丢;
chrome-use adopt 是唯一的跨组通道,收养后那一个标签被重新打进收养者的组,其余照旧不可见。会话与持久化细节见
会话与持久化。
snapshot 和 @ref 从哪里来
chrome-use snapshot -i 输出的那棵文本树不是 HTML,是无障碍树(accessibility tree)。守护进程通过 CDP 的
Accessibility.getFullAXTree 拿到整棵 AX 树(cli/src/native/snapshot.rs),过滤出 button、link、
textbox 等可交互角色,渲染成带缩进的文本:
button "提交" [ref=e12]
textbox "邮箱" [ref=e13]
每个 [ref=eN] 是守护进程在生成快照时分配的编号,背后是 RefMap(cli/src/native/element.rs):
ref 映射到 CDP 的 backend_node_id,也就是那个 DOM 节点在浏览器进程里的稳定句柄。chrome-use click @e12
时,守护进程查表拿到节点 id,直接对节点发操作,不需要 CSS 选择器,也不需要坐标。由此推出两条使用规则:
- ref 是守护进程的内存状态。守护进程重启(比如闲置退出)后旧 ref 全部作废,重新
snapshot即可。 - 页面大改后 ref 可能指向已不存在的节点。
RefMap存了每个 ref 的角色、名称等指纹,--heal能据此把 ref 重新定位到最接近的节点。定位细节见 定位元素。
出身
chrome-use 最初基于 vercel-labs/agent-browser(Apache-2.0)。CLI 骨架和部分命令语义来自上游, 但隐身与扩展中继架构、反检测、humanize、多 agent 隔离、以及本页讲的整条 nm-host 链路都是本项目自己长出来的, 现在是独立项目。完整的分叉说明见 血统与上游。
AGENT_BROWSER_DEBUG=1 让守护进程把日志写到 ~/.chrome-use/<session>.log;
cat ~/.chrome-use/relay-cdp-url 看中继的实时端点;chrome-use doctor 把每一站的健康状况报一遍。
排障见 故障排查,扩展安装见 驱动真实 Chrome,反检测数据见 反检测与隐身。