Chrome 浏览器中继
Chrome 浏览器中继让 AI 能够控制你真实的 Chrome 浏览器——保留你已有的登录状态、Cookies 和会话。与内置浏览器工具(会打开一个干净的 Electron 窗口)不同,Chrome Relay 操作的是你日常使用的 Chrome。
工作原理
AI 对话 → ChromeRelay 工具 → Alma 服务器 → WebSocket → Chrome 扩展 → chrome.debugger → 你的标签页Alma 附带一个 Chrome 扩展,通过 Chrome DevTools Protocol (CDP) 将你的浏览器桥接到 AI。扩展通过本地 WebSocket 连接 Alma,AI 通过该通道发送导航、点击、输入和截图等命令。
设置
方式一:一键启动(推荐)
- 打开 Alma → 设置 → Chrome Relay
- 复制页面上显示的 Auth Token
- 点击 Launch Chrome with Extension
- 等待连接状态变为绿色
WARNING
点击前请先关闭所有 Chrome 窗口。--load-extension 参数在 Chrome 未运行时效果最好。
方式二:手动安装
如果一键启动不生效(比如 Chrome 已在运行):
- 打开 Alma 设置 → Chrome Relay
- 展开 Manual Install 部分,复制 Extension Path
- 打开 Chrome,地址栏输入
chrome://extensions - 开启右上角的开发者模式
- 点击加载已解压的扩展程序,粘贴复制的路径
- 点击 Chrome 工具栏上的扩展图标打开弹窗
- 如未自动连接,点弹窗底部的 Settings
- 输入 Port(默认
23001)和从 Alma 复制的 Auth Token - 点击 Save,扩展会自动重连
验证连接
- 扩展图标显示绿色 ON 徽章
- Alma 设置 → Chrome Relay 显示绿色 Connected 状态
- 已 Attach 的标签页会在扩展弹窗和 Alma 设置中列出
可用工具
| 工具 | 描述 |
|---|---|
| ChromeRelayListTabs | 列出所有打开的 Chrome 标签页(ID、URL、标题、是否活跃) |
| ChromeRelayNavigate | 导航到 URL 或新建标签页,返回最终的 URL 与标题 |
| ChromeRelayClick | 用 ref 或 CSS 选择器点击元素,并回报究竟点到了什么 |
| ChromeRelayType | 在输入框中输入文字,可选按 Enter,并回报真正写进去的值 |
| ChromeRelayScreenshot | 截取标签页截图用于视觉分析 |
| ChromeRelayRead | 将页面可见内容读取为 Markdown(使用 Readability) |
| ChromeRelayReadDom | 快照所有可交互元素,附带 ref、角色、可访问名和现成的选择器 |
| ChromeRelayEval | 在页面上下文中执行 JavaScript,会等待 Promise 完成 |
| ChromeRelayScroll | 上下滚动页面 |
| ChromeRelayBack | 浏览器后退 |
| ChromeRelayForward | 浏览器前进 |
如何定位元素
ChromeRelayReadDom 是 AI 操作页面时依据的那张地图。每一行都给足了辨认和下手所需的信息: ref、可访问性角色与名称、元素本身的 tag,以及按元素类型补充的 id、链接的 href、 表单控件的 name 和 type,最后还有一个可以直接拿去用的 sel= 选择器。
e11 link "Pull requests" <a#pr-nav> href=/pulls sel=#pr-nav
e12 textbox "搜索或跳转…" <input> type=search name=q sel=input[name="q"]
e13 button "登录" <button> type=submit sel=button[type="submit"]像 e12 这样的 ref 可以直接传给 ChromeRelayClick 或 ChromeRelayType 的 selector 参数,不需要自己照着描述拼 CSS。sel= 则留给那些希望用稳定、可复用选择器的场合。
够不着的元素仍会列出,但会打上标记——判定规则与 Click 完全一致,地图和动作不会各说各话:
e21 button "订阅" <button#sub> sel=#sub [hidden: covered at its click point by <div.modal-overlay>]被标记的元素依然存在于页面上(弹层关掉、菜单展开之后可能就能点了),但此刻点击会被拒绝, 除非传 allowHidden。仅仅是滚动到屏幕外的元素不会被标记。
WARNING
ref 只在生成它的那次快照里有效。页面一旦跳转或重新渲染,旧的 ref 就过期了——工具会直接 提示 ref 已过期,而不是稀里糊涂点到别的东西上,此时重新跑一次 ChromeRelayReadDom 即可。
操作的回执说了什么
每个操作都会回报它实际做成了什么,而不只是"没有报错"。这一点很关键:点错元素的"成功", 和点对元素的"成功",长得一模一样。
点击
ChromeRelayClick 会告诉你选择器匹配到了几个元素、点的是第几个(matchedIndex,默认第一个)、 被点的到底是什么元素、这次点击有没有引发跳转,以及有没有因此打开新标签页:
{
"success": true,
"matchCount": 3,
"matchedIndex": 0,
"element": { "tag": "a", "id": "repo-link", "text": "yetone/alma", "href": "/yetone/alma" },
"navigated": { "from": "https://github.com/search?q=alma", "to": "https://github.com/yetone/alma" },
"openedTabs": []
}matchCount 偏大说明选择器太松——这次点的是第一个匹配项,未必是你想要的那个。
如果点击打开了新标签页,它会出现在 openedTabs 里,带上 id、url 和 title。 但这个新标签页尚未 attach:Chrome 要求逐个标签页授权,所以 AI 只能看到它存在, 在你授权之前无法操作它。
隐藏元素
默认情况下,Click 会拒绝那些真人根本点不到的元素。判定看的是「够不够得着」,而不只是元素 自己的 CSS:display: none、visibility: hidden、opacity: 0、transform: scale(0)、 尺寸为 0,点击位置被上层元素盖住,以及被停在文档之外(left: -9999px 这种无障碍 常用写法,怎么滚都到不了)。
只是需要滚动才能看到的元素不在此列——无论是在首屏之外,还是滚出了自己所在的滚动容器, Click 都会先滚过去再点。
它返回 success: false 并说清原因,而不是把事件默默丢进虚空:
{
"success": false,
"matchCount": 1,
"element": { "tag": "button", "id": "hidden-submit", "visible": false, "hiddenReason": "display:none" },
"error": "Element is not visible (display:none) — a real user could not click it. Pass allowHidden: true to click it anyway."
}有些页面确实要靠隐藏控件干活(屏幕外的文件输入框、自定义组件等)。这时传 allowHidden: true 即可照点不误——回执会注明这一次点的是隐藏元素。
输入
ChromeRelayType 不再给一句笼统的错误,而是区分具体的失败原因:元素不存在、元素不可输入 (不是 input、textarea 或 contenteditable)、被 disabled、被 readonly, 以及值写进去了却没生效。成功时它会回报字段里最终的值——这正是确认受控 React 组件是否接受 输入的办法:
{ "success": true, "element": { "tag": "input", "name": "q", "type": "search" }, "value": "TypeScript 最佳实践" }执行 JavaScript
ChromeRelayEval 会等待 Promise resolve。await fetch(...)、new Promise(...) 之类的异步代码拿到的是真正的结果,而不是一个空的 {}。回执还带上结果的 JS 类型, 这样字符串 "null" 和真正的 null 才分得清:
{ "result": "{\"login\":\"yetone\",\"public_repos\":42}", "type": "object" }读取与导航
ChromeRelayRead 在提取正文前会先剔除 display: none 和 visibility: hidden 的内容, 口径与 ChromeRelayReadDom 判定的可见性保持一致——不会再冒出页面上根本看不见的链接。
ChromeRelayNavigate 返回标签页真实的最终 URL 和标题,新建标签页时同样如此。
使用示例
利用已有的登录状态浏览
"帮我看看 GitHub 上有没有新的通知"
"打开我的 Gmail,帮我总结最新的邮件"
"去我的 Jira 看板,列出未关闭的工单"AI 使用你现有的 Chrome 会话,无需重新登录。
与页面交互
"在 Google 搜索 'TypeScript 最佳实践'"
"点击第一条搜索结果"
"帮我填写这个联系表单"读取和提取内容
"读取当前标签页的文章并总结"
"列出这个页面上所有的链接"
"对当前页面截个图"Chrome Relay vs 内置浏览器
| Chrome Relay | 内置浏览器 | |
|---|---|---|
| 浏览器 | 你真实的 Chrome | Electron 窗口 |
| 会话/Cookies | 保留已有登录 | 干净会话 |
| 需要扩展 | 是 | 否 |
| 适用场景 | 需要已登录账户的操作 | 通用浏览 |
TIP
如果你需要操作已登录的网站(Gmail、GitHub、Jira 等),使用 Chrome Relay。对于不需要认证的通用浏览或搜索,内置浏览器工具即可。
设置项
所有 Chrome Relay 设置位于 设置 → Chrome Relay:
| 设置项 | 描述 |
|---|---|
| Connection Status | 显示扩展是否已连接 |
| Auth Token | 用于认证扩展的令牌(可复制、重新生成) |
| Launch Chrome | 一键启动带扩展的 Chrome |
| Manual Install | 手动安装的分步说明 |
| Attached Tabs | 已建立调试器会话的标签页列表 |
常见问题
扩展显示 OFF(红色徽章)
- 确认 Alma 正在运行且端口 23001 可用
- 检查 Alma 设置和扩展 Options 中的 Auth Token 是否一致
- 点击扩展弹窗中的 Reconnect
点击 "Launch Chrome with Extension" 没反应
- 先完全关闭 Chrome(包括后台进程)
- macOS 上可在活动监视器中检查是否有残留的 Chrome 进程
- 改用手动安装方式
AI 没有使用 ChromeRelay 工具
- 在提示中明确说"用我的 Chrome 浏览器"或"在我真实的浏览器里操作"
- AI 会根据上下文区分 ChromeRelay(真实 Chrome)和 Browser(Electron 窗口)
明明看得见的元素,点击却返回 success: false
多半是页面上还有一个同样匹配的隐藏元素——先看回执里的 matchCount。如果匹配到多个, 第一个可能是隐藏的副本(导航菜单常常同时渲染桌面版和移动版)。重新跑一次 ChromeRelayReadDom,改用目标元素的 ref。
提示 ref 已过期
说明快照之后页面变了。重新跑 ChromeRelayReadDom 取新的 ref;ref 不会跨页面跳转继续有效。
点击打开了新标签页,但 AI 用不了
新标签页默认未 attach。在扩展弹窗或 Alma 设置 → Chrome Relay → Attached Tabs 中授权该标签页后,AI 才能操作它。
扩展频繁断开
- Chrome 可能会挂起 Service Worker;扩展使用 keep-alive 定时器来防止这种情况
- 如问题持续,检查防火墙对 localhost 连接的设置
- 尝试在 Alma 设置中重新生成 Auth Token
重新生成 Token 后扩展断开
这是正常行为。重新生成后:
- 打开扩展弹窗 → Settings
- 输入新的 Token
- 点击 Save
