Skip to content

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 通过该通道发送导航、点击、输入和截图等命令。

设置 ​

方式一:一键启动(推荐) ​

  1. 打开 Alma → 设置 → Chrome Relay
  2. 复制页面上显示的 Auth Token
  3. 点击 Launch Chrome with Extension
  4. 等待连接状态变为绿色

WARNING

点击前请先关闭所有 Chrome 窗口。--load-extension 参数在 Chrome 未运行时效果最好。

方式二:手动安装 ​

如果一键启动不生效(比如 Chrome 已在运行):

  1. 打开 Alma 设置 → Chrome Relay
  2. 展开 Manual Install 部分,复制 Extension Path
  3. 打开 Chrome,地址栏输入 chrome://extensions
  4. 开启右上角的开发者模式
  5. 点击加载已解压的扩展程序,粘贴复制的路径
  6. 点击 Chrome 工具栏上的扩展图标打开弹窗
  7. 如未自动连接,点弹窗底部的 Settings
  8. 输入 Port(默认 23001)和从 Alma 复制的 Auth Token
  9. 点击 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,默认第一个)、 被点的到底是什么元素、这次点击有没有引发跳转,以及有没有因此打开新标签页:

json
{
  "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 并说清原因,而不是把事件默默丢进虚空:

json
{
  "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 组件是否接受 输入的办法:

json
{ "success": true, "element": { "tag": "input", "name": "q", "type": "search" }, "value": "TypeScript 最佳实践" }

执行 JavaScript ​

ChromeRelayEval 会等待 Promise resolve。await fetch(...)、new Promise(...) 之类的异步代码拿到的是真正的结果,而不是一个空的 {}。回执还带上结果的 JS 类型, 这样字符串 "null" 和真正的 null 才分得清:

json
{ "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内置浏览器
浏览器你真实的 ChromeElectron 窗口
会话/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 后扩展断开 ​

这是正常行为。重新生成后:

  1. 打开扩展弹窗 → Settings
  2. 输入新的 Token
  3. 点击 Save