OpenCLI Browser Bridge 完整指南:5 分钟装好让 AI Agent 驱动已登录 Chrome 的浏览器扩展
OpenCLI Browser Bridge 完整指南:5 分钟装好让 AI Agent 驱动已登录 Chrome 的浏览器扩展
【免费下载链接】OpenCLIMake Any Website into CLI & Use your logged-in browser by AI agent.项目地址: https://gitcode.com/gh_mirrors/ope/OpenCLI
OpenCLI Browser Bridge 是一个 Chrome 浏览器扩展:它把你已登录的浏览器接到本地 daemon,AI Agent 就能通过 CLI 发号施令,直接操作真实页面、复用你的真实登录态。本文覆盖安装验证、权限清单、命令执行链路、标签页租约与故障排查,读完即可自助完成全部配置。
为什么 AI Agent 需要一个通往"已登录 Chrome"的桥梁
对 AI Agent 来说,最有价值的数据往往藏在登录墙后面:个人收件箱、工作台、站点的会员区。每次任务都开一个全新的浏览器环境,意味着反复输账号、反复过风控,效率极低;更现实的路径是直接复用用户正在使用的那个浏览器会话。
OpenCLI 的解法是一条轻量链路:Browser Bridge 扩展 + 本地 daemon。daemon 按需自动启动,扩展常驻在浏览器里,两者通过 localhost 上的 WebSocket 对话。扩展本身不承载任何业务逻辑,官方定位就是"传输层 + 浏览器控制层"——在某个标签页里跑一段 JS、跳转某个 URL、读取指定域名的 Cookie、截屏、等待下载,这些动作全部来自 CLI 的显式指令,扩展只负责忠实执行。
只有落在真实浏览器里执行,AI Agent 才拥有和人一样的"身份":真实的登录态、真实的 Cookie、真实的页面行为。这也是打通需要认证的站点适配器(browser-backed adapters)的关键——没有这座桥,CLI 只能触达无需登录的公开接口。
5 分钟装好 OpenCLI Browser Bridge 并验证连接
两种安装方式
- 方式 A(推荐):在 Chrome Web Store 安装名为OpenCLI的扩展;
- 方式 B(手动):拿到
opencli-extension-v{version}.zip发行包并解压,打开chrome://extensions,开启开发者模式,点击"加载已解压的扩展程序",选中解压目录。
从 extension/manifest.json 可以看到基本规格:Manifest V3,后台是dist/background.js,以 module 类型 service worker 运行;extension/package.json 声明了opencli.compatRange: ">=1.7.0",即与 CLI 版本的兼容范围。
用 opencli doctor 验证连接
在仓库根目录执行:
opencli doctor它检查两件事:扩展是否装好、daemon 是否可达。链路打通的标志是:扩展的 service worker 已经用 WebSocket 连上 daemon,并完成hello握手——握手消息里带着扩展版本号与兼容范围,daemon 据此就能向 CLI 上报版本不匹配的问题。
弹窗面板与多配置文件
扩展弹窗(extension/popup.html)是一块极简状态面板,只有三项信息:
- 连接状态:绿点已连接、橙点连接中、红点未连接;
- daemon 版本;
- Profile(contextId):当前浏览器配置文件的标识,可一键复制。
每个 Chrome 配置文件都跑一个独立的扩展实例,OpenCLI 靠它区分不同的浏览器会话。管理多个配置文件时,可以在 CLI 侧给 contextId 起别名:
opencli profile list opencli profile rename <contextId> work opencli profile use work opencli --profile work browser main state浏览器扩展权限清单:chrome debugger 权限详解
extension/manifest.json 里声明的核心权限:debugger、tabs、cookies、tabGroups、downloads,再加activeTab、alarms、storage和<all_urls>主机权限。每一项都对应一项实际能力:
| 权限 | 换来的能力 | 源码出处 |
|---|---|---|
debugger | 求值、截屏、输入、网络捕获等 CDP 指令全靠它下发,作用于扩展控制或绑定的标签页 | extension/src/cdp.ts 的chrome.debugger.attach/sendCommand封装 |
tabs/tabGroups | 开、管专用自动化容器窗口与标签组,并把选中标签的元数据喂回 CLI | extension/src/background.ts 中 owned 容器与 tab group 的创建、收敛逻辑 |
cookies | 让需要发起认证请求的浏览器型适配器读到对应 Cookie | handleCookies按 domain/url 过滤后调用chrome.cookies.getAll |
downloads | 把下载的四个状态喂给opencli browser wait download,Agent 得以等文件落盘 | extension/src/cdp.ts 的waitForDownload |
chrome debugger 权限详解:URL 白名单与附加重试
chrome.debugger光凭这一个权限就能附加到任意 http/https 标签页,不需要主机权限。但扩展主动收窄了可附加范围:isDebuggableUrl白名单只放行http://、https://、about:blank、data:,chrome://与chrome-extension://页面一律不碰。
附加动作自带重试:普通命令最多重试 2 次、间隔 500ms;浏览器命令走激进模式,最多 5 次、间隔 1500ms——因为 1Password、Playwright MCP Bridge 这类扩展可能临时占用chrome.debugger。每次附加前还会先发一条无害探针(Runtime.evaluate求值1)确认附加仍然有效,避免陈旧缓存造成误判。
cookies:为什么读取必须带作用域
Cookie 读取被刻意设计成"必须带作用域":命令没提供domain或url时直接报错:
Cookie scope required: provide domain or url to avoid dumping all cookies这条硬约束挡住了"一条命令倾倒全部 Cookie"的用法。读取成功后返回 name、value、domain、path、secure、httpOnly、expirationDate 字段,供需要登录态的浏览器型适配器发起认证请求。
downloads:只旁观全过程,不碰任何东西
opencli browser wait download借助chrome.downloads观察下载从 started 到 in-progress、completed 或 failed 的全过程,让 Agent 在自动化流程触发下载后阻塞等待。实现上有三个关键点(extension/src/cdp.ts 的waitForDownload):
- 模式过滤:按命令给定的 filename / URL / finalUrl / mime 子串过滤(
matchesDownloadPattern),只盯本次命令关心的那条下载; - 超时保护:默认 30s,可用
timeoutMs覆盖,超时即返回interrupted状态与错误信息; - 不干预:OpenCLI 只做观察、过滤与等待,不修改、不重定向、不持久化浏览器的下载历史。
面向商店审核的官方说明(建议直接复用为商店申请材料):
This extension uses
chrome.downloadsto surface download lifecycle (started / in-progress / completed / failed) to the OpenCLI command-line tool, so agents can wait for downloads triggered during an automation workflow. The command filters by a user-provided filename or URL pattern and timeout. We do not modify, redirect, or persist user download history.
跟着一条命令走:从 CLI 到 Chrome 的完整链路
协议常量与结构化契约
通信参数集中在 extension/src/protocol.ts:
| 常量 | 取值 | 作用 |
|---|---|---|
DAEMON_HOST | localhost | 只与本机对话 |
DAEMON_PORT | 19825 | daemon 默认端口 |
DAEMON_WS_URL | ws://localhost:19825/ext | 命令通道(WebSocket) |
DAEMON_PING_URL | http://localhost:19825/ping | 建连前的轻量健康检查 |
Command携带唯一请求 ID(id)、动作类型(action)、页面标识(page,即 CDP targetId)、会话名(session)、表面(surface: 'browser' \| 'adapter');Result回传id、ok、data,失败时附带稳定的机器可读错误码errorCode与可选的恢复提示errorHint。这份契约的意义在于:CLI 侧不用对错误文本做正则匹配,就能判断"重试是否安全"。
协议动作清单包括:exec、navigate、tabs、cookies、screenshot、close-window、sessions、set-file-input、insert-text、bind、network-capture-start、network-capture-read、wait-download、cdp、frames。其中exec是"万能通道":固定动作之外的一切需求,本质都是借它把一段 JS 送进页面上下文执行。
五步分发链路
一条命令从 daemon 到页面执行,要走完五步:
- WebSocket
onmessage收到 JSON 命令; executeWithJournal进入命令日志层;handleCommand按action分发,先解析或创建标签页租约;- extension/src/cdp.ts 的 CDP 封装执行真实的浏览器操作;
- 结果(含
page标识)经 WebSocket 送回 daemon。
命令日志:安全重试的底层保障
CLI 在传输失败时会用同一个命令 ID发起重试,于是 extension/src/journal.ts 的executeWithJournal以 ID 为键实现"每个 ID 恰好执行一次":
- 命令正在跑 → 重复投递直接挂到 in-flight Promise 上,不并发重复执行;
- 命令已跑完 → 原样重放记录下来的
Result; - 命令开始了却没跑完(比如 service worker 中途死亡)→ 返回
command_lost错误码,并附提示"先检查浏览器状态再重试,不要盲目重跑 navigate/click/type/eval 这类写操作"。
日志落在chrome.storage.session:能扛过 service worker 重启,浏览器退出即清除——恰好覆盖"重试可能再来要一次结果"的整个生命周期。上限 64 条;超过 64KB 的结果不做记录,重放时如实返回result_evicted。
机器可读错误码与重试安全性
classifyExtensionError把原始错误映射为稳定错误码:
| 错误码 | 什么时候出现 | 重试安全性 |
|---|---|---|
attach_failed/tab_gone | 附加阶段就失败,页面代码一行都没跑 | 安全,可发起新的逻辑尝试 |
target_navigated | 执行期间页面文档发生了变更 | 由页面层决定是否 settle-retry |
detached_mid_command/cdp_timeout | 执行到一半死亡,结果无法判定 | 危险,盲目重跑可能把写操作应用两遍 |
连接稳定性:MV3 环境下如何不掉线
extension/src/background.ts 的connectAttempt是一套为 MV3 service worker 量身定做的韧性设计:
先探活,再开 WebSocket。每次建连前先用 HTTP 探测/ping,并且显式credentials: 'omit'。为什么?不加这个参数,浏览器会把 localhost 上庞大的 Cookie jar 附在请求头里,可能顶穿 Node 默认的请求头上限,让 daemon 回一个 431,重连循环就此静默卡死。
翻倍退避加随机抖动。重连间隔 1s → 2s → 4s …,封顶 15s,外加 0–500ms 随机抖动;一旦连接成功立即重置计数。
20s 心跳:空闲连接不算数。应用层每 20s 发一条ping消息。原因是 Chrome 116+ 只在 WebSocket 有活动时延长 service worker 生命周期,挂着不动的 OPEN 连接不算活动——没有心跳,worker 就会在 30s 空闲回收与 30s keepalive alarm 之间"走钢丝"。
chrome.alarms 当最后防线。MV3 生产环境 alarm 最小间隔约 30s,worker 被空闲回收后,chrome.alarms会周期性把它唤醒重连,保证链路不会永久断裂。
标签页租约:会话如何绑定到标签页
opencli browser <session> <command>系列命令的会话连续性,靠的是扩展内部的标签页租约(TargetLease)来维护"CLI 会话 → 浏览器标签页"的映射。
三种租约形态
- owned(自持):会话拥有自己的标签页,位于专用自动化容器窗口内——interactive 角色对应前台的"OpenCLI Browser"标签组(橙色),adapter 角色对应后台窗口;
- bound(借用):用
bind动作把会话绑到用户自己的标签页上(即opencli browser bind),能操作用户已登录的页面,但不能新建、关闭或切换标签(返回bound_tab_mutation_blocked); - preferredTabId:租约锚定到具体标签页 ID,标签一关,租约随之释放。
空闲超时策略
getIdleTimeout按场景区分空闲释放节奏:
| 场景 | 空闲超时 | 为什么这样定 |
|---|---|---|
浏览器交互命令(browser:*/operate:*) | 10 分钟 | 人的操作节奏慢,不能回收太狠 |
| 适配器驱动的自动化 | 30 秒 | 后台资源要快速释放 |
| bound 借用标签 / persistent 站点会话 | 永不超时 | 持有到解除绑定或关闭为止 |
还有一层关键保护:活跃命令计数(activeCommandCounts)会阻塞空闲释放——租约上还有命令在跑时,绝不在命令脚下拆标签页;等命令跑完,才从"完成时刻"重新起算空闲窗口。
worker 重启后的自愈
租约注册表(opencli_target_lease_registry_v2)落在chrome.storage.session而不是storage.local:所有 window/tab/group ID 都是单次浏览器会话内有效的运行时数字,跨重启持久化既恢复不了,还可能撞上用户新建的窗口和标签。storage.session恰好能在 service worker 重启时存活、浏览器退出时清除。
启动时reconcileTargetLeaseRegistry恢复容器窗口与租约,并用四层分组发现(内存缓存 → 分组台账 → 租约持有标签所在组 → 全盘扫描空标题孤儿组)收敛标签组,专门修复"worker 在tabs.group与tabGroups.update之间崩溃"留下的无标题孤儿组。
进阶能力一览:从 60 秒 CDP 护栏到网络捕获
extension/src/cdp.ts 提供的 CDP 封装远不止基础求值:
60 秒超时护栏。chrome.debugger自身没有超时:页面被原生对话框(alert/confirm/print/beforeunload)阻塞时,Runtime.evaluate会永久挂起,拖垮该标签后续所有命令。因此sendDebuggerCommand默认带 60s 截止时间,健康检查探针只有 2s;同时扩展会从命令的绝对截止时间deadlineAt推导自己的 CDP 时限并预留 5s 余量,让更具体的错误先于 daemon 的通用定时器到达 CLI。
跨域 iframe 里执行代码。对跨域 iframe,扩展用Target.setAutoAttach+Target.getTargets解析出 iframe 子 target,再对子 target 直接Runtime.evaluate(evaluateInFrame);配合frames动作枚举跨域 frame 列表,即可按索引逐个操作。
文件上传的绕行方案。setFileInputFiles走Page.setInterceptFileChooserDialog拦截文件选择器:Chrome 在经chrome.debugger附加时拒绝普通 nodeId 方式设置文件(crbug 928255),唯一可行路径是从拦截到的Page.fileChooserOpened事件里取backendNodeId,再调DOM.setFileInputFiles。本地文件由 Chrome 直接读取,不必把大体积 base64 塞进消息通道。
网络捕获。network-capture-start/network-capture-read借 CDP Network 域记录请求与响应:请求体上限 1MB、响应体上限 8MB,超限截断并标注truncated与真实大小;遇到 HTTP 30x 重定向会保留原始 POST body(重定向 hop 复用同一 requestId 时不做覆盖);readNetworkCapture读取后即清空,避免同一条记录被重复消费。
opencli doctor 排障速查
- 🔌连接不上?先到
chrome://extensions确认扩展已安装且启用;看到 "Extension not connected" 时,先跑opencli doctor检查 daemon 与扩展的握手状态; - 🧩attach 失败?报错
attach failed: Cannot access a chrome-extension:// URL说明有其他扩展干扰,临时禁用其他扩展再试(源码在重试失败时就会给出这条提示); - 弹窗圆点怎么读?红点未连接、橙点连接中、绿点已连接,顺手可以核对 daemon 版本与当前 Profile(contextId);
- 想看扩展内部发生了什么?扩展会把
console.log/warn/error经 WebSocket 转发给 daemon(forwardLog),CLI 侧能看到连接、重连、租约创建与释放等内部事件。
写在最后
权限清单不长,debugger、tabs、cookies、downloads各换回一项真实能力,没有多余申请;命令通道外面套着指数退避与命令日志两道保险,AI Agent 可以明确判断"这次重试安不安全";标签页租约随空闲超时自动回收,活跃命令脚下又绝不会拆台。这套"最小权限面 + 高可靠命令通道 + 重试安全 + 租约回收"的取舍,就是让 AI Agent 稳定驾驭你已登录浏览器背后的全部成本。
【免费下载链接】OpenCLIMake Any Website into CLI & Use your logged-in browser by AI agent.项目地址: https://gitcode.com/gh_mirrors/ope/OpenCLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考