ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

OpenCLI Browser Bridge 完整指南:5 分钟装好让 AI Agent 驱动已登录 Chrome 的浏览器扩展

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开、管专用自动化容器窗口与标签组,并把选中标签的元数据喂回 CLIextension/src/background.ts 中 owned 容器与 tab group 的创建、收敛逻辑
cookies让需要发起认证请求的浏览器型适配器读到对应 CookiehandleCookies按 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):

  1. 模式过滤:按命令给定的 filename / URL / finalUrl / mime 子串过滤(matchesDownloadPattern),只盯本次命令关心的那条下载;
  2. 超时保护:默认 30s,可用timeoutMs覆盖,超时即返回interrupted状态与错误信息;
  3. 不干预:OpenCLI 只做观察、过滤与等待,不修改、不重定向、不持久化浏览器的下载历史。

面向商店审核的官方说明(建议直接复用为商店申请材料):

This extension useschrome.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_HOSTlocalhost只与本机对话
DAEMON_PORT19825daemon 默认端口
DAEMON_WS_URLws://localhost:19825/ext命令通道(WebSocket)
DAEMON_PING_URLhttp://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 到页面执行,要走完五步:

  1. WebSocketonmessage收到 JSON 命令;
  2. executeWithJournal进入命令日志层;
  3. handleCommand按action分发,先解析或创建标签页租约;
  4. extension/src/cdp.ts 的 CDP 封装执行真实的浏览器操作;
  5. 结果(含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),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进