Skip to main content
Glama
ai-browser-bridge

ai-browser-bridge

by love530love

自主浏览器 · AI Browser Bridge

默认用途:让已有 AI Agent 直接操作浏览器,无需配置模型。 终端 Agent 可立即使用 接入说明 和 agent.ps1;MCP 客户端使用本机生成的工具配置。插件内模型任务页只是可选功能。

Agent 操作契约:新 MCP 连接的初始化说明自动包含 操作 Skill,也可通过 MCP 资源 bridge://skills/ai-browser-bridge 读取同一份内容。已有客户端需要重新连接。初始化会传递说明,但客户端是否展示、模型是否遵守取决于客户端;这不代替服务端租约、权限和动作验证。无需重新加载 Chrome 扩展。

一个可自行修改、运行的 Chrome Manifest V3 扩展,将网页提供给支持 MCP、HTTP 或命令行的 AI。当前源码版本 0.37.4(部署状态以 doctor 为准),默认允许所有 HTTP / HTTPS 网站,也可切换为指定网站列表。

0.25.0 补的是一闪而过、或者悬停才存在的那层 UI。toast 活 2–3 秒,任何「等你问我的时候再扫一遍」都必然错过,所以 browser_transient 不是更快的扫描,而是扩展在 isolated world 里装一个常驻 MutationObserver,在提示还在屏幕上时就记下来:read 能查到已经消失的 toast(带 firstSeenAt / goneAt / preExisting),watch 等新的提示出现,clear 只清记录。tooltip 有两条路:声明式的(title / data-tooltip / aria-describedby)由 browser_popup action:"probe" 直接读;纯 CSS :hover 的只能让真实指针去问——合成 mouseover 不移动 :hover 状态,所以 action:"reveal" 走 CDP Input.dispatchMouseEvent,settle 之后再读新出现的浮层。popconfirm 看起来就是个普通按钮,点两次就删两次,所以 action:"open" 点一次并返回气泡里的按钮,action:"choose" 按 choice:"confirm"|"cancel" 或确切文本选,每个派发过点击的结果都是 retryable:false。tooltip 常挂在 <span>/图标上,browser_find_element 不会给 ref,因此 browser_popup 支持 selector 自己定位,probe 不带参数时还会列出全页声明了 tooltip 的元素。真机实测(Chrome 156)三处坑写进了代码:el.textContent = x 是替换文本节点而不是 characterData 变更(第一版因此漏掉全部 aria-live);visible() 的 per-call 缓存会把「点击前不可见」记到「点击后」(open 因此误报 not_opened);用几何距离猜「这个气泡属于哪个触发源」会认错爹(改成只认 aria-expanded + aria-controls,其余靠 opened/closed_by_click/unchanged 三态如实报告)。另外 extension/worker.js 有一行显式的工具分发白名单,新工具漏加就是真机 Unknown command 而 532 个单测全绿,所以加了一条静态测试直接读那行源码。

0.23.0 是拿两个成熟实现当尺子量出来的:chrome-devtools-mcp 与 playwright-mcp。逐条比对后我们缺的只剩七块,而且都是「以前根本做不到」而不是「没做」——它们全在 CDP 里,而当时的 CDP 通道只走 attach-per-call 的一次性附件,任何 override 都会在调用返回的瞬间被撤销。这七块现在是 7 个工具:browser_emulation(设备/地理/时区/UA/视口/色彩/CPU/网络条件,每字段开会话前校验)、browser_dialog(alert/confirm/prompt——对话框冻住渲染进程,只有它能动作;status 是只读的,没有会话时 dialogOpen 回 null 而不是 false)、browser_ax_snapshot(屏幕阅读器那棵树,ignored 包装被 splice 掉、子节点全部上提)、browser_drag(原生拦截在这台机器上没有触发,所以补了 mode:"synthetic":在页面里构造 DataTransfer 派发 dragstart/drop,真机把页面计数从 0 推到 1)、browser_pdf_save(printToPDF 走下载管理器落盘;MV3 worker 没有 URL.createObjectURL,所以用 data URL,并回读真实文件名)、browser_init_script(文档脚本之前运行,从下次导航生效,Chrome 无法枚举只能自己记 id;真机 add → 刷新 → 变量为 1 → remove → 刷新 → undefined)、browser_network_rules(阻断/打桩/失败/清除;Fetch.requestPaused 是推送式的,每个被暂停的请求都必须被答复,不匹配的立刻 continue;真机 mock 后 fetch 返回 MOCKED-BY-BRIDGE)。三条只有真机才暴露的结论写进了代码:override 的 clear 命令被接受不等于 override 消失(视口 390×844 在扩展重载、服务重启后仍在,只有导航才还原),所以 clear 用「set 前的 baseline + 回读页面」判定 verified / stillInEffect;locale 改的是 Intl 而不是 navigator.language;开着拖拽拦截时发起拖拽的 mousePressed 不返回,所以所有输入派发都是有上限的 fire-and-observe。

0.22.0 把范围从「页面」扩到「浏览器本身」。此前所有工具都活在文档里,而人类工程师有一大半操作在文档之外:恢复关掉的标签、翻书签、清历史、看下载、启停扩展、改站点权限、读 cookie、调隐私开关、摆窗口。这些 Chrome 都给扩展 API,之前一个都没开。现在 13 个权限 + 21 个工具全部打开,规则是每开一个权限必须在同一版里有一个工具——只加权限不加工具,等于安装时多一条警告而没有多一分能力。读写刻意分家(browser_cookies 只读、browser_cookies_write 才写),因为确认串必须「声明即必填、只有一个值」,否则要么逼调用方在只读时也闭眼填确认,要么写就没有闸门。10 个新确认串在服务端入队前就拒。硬边界写进代码:桥扩展拒绝停用自己(那会切断承载该请求的连接);browser_cookies 没有「读全部」模式,必须指名 url 或 domain,返回标 secrets:true;browser_clear_browsing_data 必须显式列 dataTypes,因为 cookies 会登出所有站点;建窗口默认 focused:false;代理 rules 的 scheme 不一致时拒绝而不是悄悄挑一个;所有读回来的标题/URL/文件名都标 untrusted browser metadata。真机已验证到写闭环:书签 create → get → remove(verified:true),browser_extensions 读到 35 个扩展,browser_session_restore 列出了 25 条最近关闭记录、其中 6 条是 20 标签窗口。

0.21.0 把「哪个浏览器是我的」这个问题交给了唯一知道答案的人。0.20.0 的打分器把稳定版 Default 排在前面,用户当场否掉并选了 Canary / Default;随后按写入时间取证才发现两份 profile 是同一个账号的克隆(Login Data 都是 192KB),Canary 那份已经 78 天没写入、真正在写的是稳定版那份——所以「记一次」之后还必须能发现「记错了」。现在 config.json 里的 browserTarget: { channel, exePath, userDataDir, profileDirectory } 记一次、之后逐字使用,脚本回 targetSource: "explicit";只有没记录时才退回启发式,doctor 会提醒。记录也会过期,所以同一轮又加了活跃度证据:-DryRun 报 wouldLaunch.ageMinutes / profileName / accounts 与全渠道最新的 liveCandidate,目标陈旧时给 warnings: ["recorded_target_looks_stale"] 且 doctor 直接判红。登录账号只能证明「我用过一次」,只有写入时间能证明「我在用」。 ⚠️ 当前定论(后续 mtime 取证复核后):browserTarget = stable / Default(D:\Program Files\Google\Chrome\Application\chrome.exe);上文「用户选了 Canary / Default」是被推翻的中间结论,切勿照抄,更不要按注册表顺序取 chrome.exe(本机 HKCU 指向 Canary SxS,HKLM 才是稳定版,各渠道 user-data 独立)。 另外两条实测结论:--load-extension 在 Chrome 156/159 上被忽略(不能当兜底);SW_SHOWNOACTIVATE 不足以阻止 Chrome 抢前台(它对第一个窗口会显式 ShowWindow),所以改成「抢了就把焦点还回去」——AttachThreadInput + SetForegroundWindow 恢复原前台窗口,再测一次并如实报 focusRestored。诊断也修了:Chrome 退出时会把扩展记录压缩成只剩 disable_reasons、path 消失,只按路径匹配会误报「未安装」,现在 id 也参与匹配。

0.20.0 修的是用户直接指出的两件事:不要再开一个没有历史信息的浏览器,以及不要把浏览器界面弹到前台打断正在做别的事的人。前者查清了根因——本机 HKCU\...\App Paths\chrome.exe 指向 Chrome SxS(Canary 渠道),而 Windows 解析 chrome 时 HKCU 优先于 HKLM,每个渠道又有自己的 user-data 目录,所以「起了一个没登录、没书签、没历史的浏览器」不是玄学而是选错渠道。现在脚本对每个已安装渠道的 profile 做磁盘取证(last_used profile 是否存在、有无书签、Login Data 是否空壳)打分选二进制,得分全为 0 时拒绝启动而不是开一个空壳;服务侧启动前先探测可见窗口(有窗口就绝不启动),启动后比对 userDataDir,不一致直接 launched_wrong_profile 且不继续原调用。后者改成 CreateProcess + SW_SHOWNOACTIVATE,窗口出现在当前窗口后面;并且不承诺、只测量——启动前后各取一次 GetForegroundWindow,抢了焦点就如实报 activated: true。browser_reload_extension 的 UIA 回退同样不再自动抢前台,改为需要显式 allowFocus: true。doctor 新增 auto-start-target,用 -DryRun 在不启动任何东西的前提下回答「会起哪个浏览器、用哪份 profile」。

0.19.1 补的是 0.19.0 真机验证时撞见的一个更根本的问题:扩展被 Chrome 停用了,而服务只能看到「没连上」。doctor 现在多一条 bridge-extension-registered,直接从 User Data\<profile>\Secure Preferences 读出扩展在哪个 profile、是否启用、disable_reasons 是多少——因为「没安装 / 装了但被停用 / 装了但没起来」是三件不同的事,修法完全不同。本机实测:Default 里 enabled: false、disable_reasons: [16777216]、开发者模式关闭;154 上一直可用,156 上第一次真正重启后被停用(强怀疑,未做 154 对照)。同时修掉 0.19.0 的一个分支错误:浏览器起来了但扩展没连时,结果被降级成 extension_disconnected,恰好把「浏览器已经起来了」这个事实藏掉;现在返回 chrome_launched_extension_not_connected 并带上 extensionState。

0.19.0 把「浏览器没开」从一个回答变成一个动作。此前任何需要扩展的调用在浏览器关着时都返回 extension_disconnected,让 agent 自己去求人——而这是基座自己就能做的事。现在没有 chrome 进程在跑时,第一次需要扩展的调用会先启动浏览器,再把它自己重跑一次,结果里带 autoLaunched(启动了谁、连上没有、进程与窗口句柄)。规则窄得刻意:只在一个 chrome 进程都不存在时启动;浏览器在跑却没回连是另一个问题(扩展被暂停、worker 被回收),再起一个实例等于第二个 profile,那是丢 cookie 而不是修好它,所以那种情况返回 chrome_running_extension_not_connected 并说明该重新加载扩展。顺手拆掉第二个死角:浏览器重启后旧 tabId 必然失效,现在启动后若发现该 id 不在当前标签里,直接返回 tab_gone_after_browser_launch + 当前标签列表 + 下一步,而不是一句「tab not found」。启动用服务进程完成(工具调用里起的 Chrome 会被沙箱连树回收,实测),参数不含任何 profile 标志,所以拿到的就是人双击图标时那份 cookie 与登录态。默认开,配置项 autoLaunchBrowser,AIB_AUTO_LAUNCH=0 可关;测试默认关闭。

0.18.0 把 DevTools 的打开与关闭接了回来。0.9 撤下它时写明的两个前提是「绑定到目标窗口、可回读的原生动作」和「充分的负证据」,0.14.0 的 expand 与 0.16.0 的 invoke 把这两样补齐,这一轮把它们接成链路:browser_devtools 的 open / close 走浏览器自己的菜单(More tools → Developer tools),不合成 F12,因此没有焦点竞态。对外是「请求一个状态」而不是切换——菜单项本身是 toggle,所以先 observe,已在目标状态就 dispatched:false 什么都不派发;幂等性由此而来,这也是「uncertain 之后允许重试」在这里成立、在裸 invoke 上不成立的唯一原因。打开只认正证据(devtools:// 文档 + ≥2 个面板 TabItem),关闭必须同时有负证据(走查完整 partial:false 且没有 DevTools 文档)——「没看到打开」本身也描述了「遍历提前放弃」。真机验收 npm run test:devtools 从「只能 PARTIAL」变成 11/11:ensure-closed → open → open again(必须不派发)→ 选/重选 Console·Network·Elements → close 全链自驱。

0.17.0 修的是上一轮暴露的一个盲区:基座看不见自己造成的东西。invoke 一个菜单项(比如 New tab)能打开 chrome:// 面,而 browser_tabs 会过滤非 http(s) 标签,于是「我打开了什么」既查不到也关不掉。现在这类标签会被列出并标 restricted:true,restrictedReason 区分 non_http(内部页)与 origin_not_allowed(无权读的站点)——但只给身份不给内容:title 恒为 null,内部页只留 scheme://host/path(去掉 query 和 fragment),无权站点连 URL 都不给,因为那条 URL 就是内容。可见不等于可读(browser_read 仍报 Site not allowed),而 browser_close 在 force:true 时能关掉它——关闭是标签生命周期操作,不是读取内容。真机验收从 17 条涨到 21/21,多出来的 4 条正是「可见 / 无 force 仍被拒 / 带 force 能关 / 跑完不留残留」,验收脚本现在会自己收尾。

0.16.0 让原生 UI 第一次选中一个菜单项:browser_native_action 增加 invoke,并强制要求 expect.afterInvoke:"control_gone"。理由和 expand/collapse 完全不同——expand/collapse 幂等,所以「不确定就重试」是安全指令;invoke 不幂等,同一个菜单项选两次命令就跑两次,而 UI Automation 只能观测到「控件还在不在树里」,命令执行了什么这一层看不见。所以 success 只表示声明的后置条件被观测到了,永远不表示命令成功;invoke 也不提供 failed(项还在只说明没观测到效果,不说明命令没跑)。菜单关闭时树会重建、runtime id 会换,因此必须连续两次遍历都找不到「同名+同角色+同 automationId」才算 gone。真机 17/17:invoke New tab Ctrl+T 后菜单自己折叠、该项消失,窗口标题变成 New Tab——标题是另一个 API 读到的,这才证明命令真的跑了(第一版断言恒真,已改掉)。

0.15.0 起因是一件基座此前完全看不见的事:它不知道自己驱动的是哪个 Chrome。没有 chrome.* API 报浏览器版本,navigator.userAgent 被 UA 减缩成 154.0.0.0(补丁号被抹掉,正好抹掉升级最想确认的那几位),Runtime.evaluate 又是 awaitPromise:false 拿不到 UA-CH 的 Promise。三条路都不通之后答案在 service worker 里的 UA-CH:/status 现在有 browserVersion / browserMajor / browserIdentitySource,实测 154.0.8037.93。配套新增 scripts/chrome-compat-baseline.js——把升级前的原生控件 automationId 抓成基线、升级后 --diff 出 GONE / NEW / CHANGED,并回答「新版本下载好了没有」(实测 PENDING UPDATE: 156.0.8078.49)。同时把密码管理器 UIA 的定位从「按 id」改成「id 优先 → 角色+名称兜底 → 歧义即拒绝」,因为 Find-ById 失败与「控件不存在」不可区分,而这条流程会把密码显示出来。156 尚未实际应用,所以不宣称「已适配 156」,验收清单写在 docs/CHROME-VERSION-ADAPTATION.md。

0.14.0 让原生 Chrome UI 从「状态可读」走到「单个控件可写且可证」:browser_native_action 只做 expand / collapse,故意没有 Toggle——toggle 派发两次回到原点,所以「结果不确定」时根本无法重试;而 expand/collapse 构造上幂等(已在该状态就不派发,返回 alreadyInState:true),这才让「不确定之后重试一次」变成安全指令。ref 不算身份,必须再重复一个 name 或 automationId,不符即拒绝且不派发。成功只认「从控件自己读回的状态」,pattern 不抛异常不算成功。它走绑定到元素的 ExpandCollapsePattern,不是 SendKeys('{F12}'),因此没有焦点 TOCTOU。实测还纠正了一条旧结论:Chrome 自绘菜单是窗口的子 HWND,展开时快照能看到完整的 MenuItem 树(111 → 166 控件、45 个菜单项),所以「不含 popup」只对独立顶层窗口成立。真机 npm run test:native 13/13。

0.13.0 只改一件事,但它是个反复被提的坑:browser_otp_fetch 要不要按「几分钟内」过滤。答案是不要——时区猜错、locale 不认识、或者一行写着「昨天 23:58」,都会静默藏掉那个能用的码,而回复只会说「没找到码」,和「邮箱里确实没有」长得一模一样。所以改成证据:每个候选带 ageHint(just_now / minutes / hours / days / weeks_or_more / absolute_date / clock_only / not_stated + 页面原话),并把这个理由写进返回值本身。

0.12.0 做的是度量,不是加能力。0.11 说 recipe 能让弱 agent 把事做完,但那句话当时不可证伪:所有验收都在回答「这个能力对不对」,没有一条在回答「一个读不懂页面的调用方能不能把任务做完」。新增的 test/bench/ 把三个数字变成可复现的:完成率、确认率、危险动作数(点错目标、重复提交),而且 completed 与 danger 一律取自 fixture 服务端自己的记录,不采信调用方的自述。首次实测:blind 完成 2/5 并造成 2 次实际损害,guarded 完成 4/5 且零损害,recipe 用更少的步数达到同样结果。它还当场抓出两个真实缺陷——普通「Sign out」链接不被识别导致 untilPhase:"logged_in" 在最需要它的应用上失效,以及 click_verified 在导航型提交上报 uncertain(现在报 navigated 并说明期望值从未被求值)。两个都修了。详见 调研文档第 5 节。

0.11.0 回答的是另一个问题:一个看不懂页面的 agent,凭什么能把事做完? 先做调研再动手,结论写在 让「很弱的 agent」也能把事做完:弱 agent 做不完事,通常不是因为不够聪明,而是因为每一步都要它做决定。所以这一轮把决定挪走——browser_recipe_save / browser_recipe_run / browser_recipe_list / browser_recipe_delete 让流程想过一次就能按数据重放。每步存的不是动作也不是选择器,而是一个耐久描述符(tag / role / accessible name / 可见文本 / id / name 属性 / selector),运行时按七级身份阶梯重新解析(role+name → name → id → name-attribute → selector → text → fuzzy-name),所以跨得过框架重建(id 每次都换)、跨得过快照淘汰(ref 会死,accessible name 不会)。四条硬规则都是「宁可拒绝也不猜」:命中多个可见元素就拒绝、变量在 step 1 之前全量校验、secret 步骤拒绝存字面量(否则 recipe 文件就是明文密码库)、拒绝记录导航/上传/坐标步骤。调研里明确不采纳的也有理由:AI 兜底定位(基座必须在无模型时可用,且模型判断不可复现)、set-of-marks 截图标注(真实账号的像素不该进上下文)、云端反爬与 CAPTCHA。详见 Chrome 能力矩阵。

0.10.0 补的是自动登录的最后一段空缺:提交之后「等相位」。0.9.0 让密码框可以写,但提交那一下仍然读不出来——browser_login_state 是单帧快照,而提交恰恰是单帧必然不够的时刻:服务端跳转或 SPA 换视图,紧接着的一次读取正好落在两份文档之间,只能回 unknown。调用方于是分不清「服务器慢」和「密码被拒」,而分不清这两件事的代价是再提交一次(可能触发锁定或再发一条验证码)。现在它接受 untilPhase / timeoutMs / pollMs:轮询写在 service worker 而不是页面里,因为页面内 await 一个 Promise 会被正在等待的那次导航销毁,跨不过整页跳转;每次轮询都是对当前文档的一次独立注入。同一个拦截必须先拆:worker.js 对所有命令都做「pendingUrl 存在就拒绝」,而这条命令正是应该在导航中被调用的,第一版因此真机报 Navigation in progress,现在只有 browser_login_state + untilPhase 豁免。相位表新增 identifier(邮箱框 + 下一步、没有密码框的两步登录首屏;原先落 unknown,而 unknown 的引导是「去看 shadow root」,等于把人指去找组件而不是去填用户名),logged_in 增加一条弱证据(没有登录表单 + 存在账号/头像元素,标 phaseConfidence:"weak",明说是负向推断),并排掉「自身文字写着 Sign in 的账号元素」这类假阳性。等待超时不写成失败:note 明说服务器慢、验证码、未建模的第二步、错误横幅在这里长得一样,同时给出 phasesSeen 让人自己判断;匹配到也不吹,相位是 DOM 读数而非认证证明。详见 Chrome 能力矩阵。

0.9.0 把 DevTools 的 Application 面板与 Network 重放变成可直接调用的能力:browser_storage_read / browser_storage_write 覆盖 localStorage、sessionStorage、cookie 与 IndexedDB 结构,browser_replay_request 是 Network 面板右键 Replay 的等价物。这一轮最有价值的产出是一个否定事实:第一版按标准做法用 DOMStorage.enable + getDOMStorageItems 读 localStorage,真机回 {"code":-32601,"message":"'DOMStorage.enable' wasn't found"},IndexedDB 同样回 'IndexedDB.enable' wasn't found;而同一轮 Network.getCookies 一直成功——扩展的 chrome.debugger.sendCommand 只转发 CDP 域的一个子集。因此 cookie 走 Network,web storage 与 IndexedDB 改走 Runtime.evaluate 在页面上下文读写:页面自己的存储按定义就是被测文档的存储,没有 storageId 可以填错,每个表达式还回传 location.origin 与请求比对,页面中途导航就拒绝作答(reason:"origin_mismatch"),边界比填 storageId 更紧。发现式读取只回键名与值长度(cookie 只回属性),要取值必须显式给 key,此时 cookie 带 sensitive:true;写入要 confirmation:"modify_site_storage_once" 且只回 verified:false——命令被接受不等于页面读到了新值。两处刻意拒绝(清空某源全部 cookie、IndexedDB 的三种写)一次 CDP 都不发;IndexedDB 读取先查 indexedDB.databases(),名单里没有就拒绝,因为 open 在库不存在时会创建一个。重放由页面上下文 fetch 发出,自动带页面当前 cookie 与 CORS 位置,超时是 status:"uncertain" + reason:"replay_timeout" 而不是失败,且绝不自动重试。同一轮修掉一个真正的死锁:browser_login_state 把密码框注册成合法 ref 交给调用方,但共享守卫的 sensitive() 与 browser_fill 的输入类型白名单都会拒绝它,自动登录在最后一步永远走不完;现在 browser_fill / browser_fill_verified 接受 secret:true,只对「操作名是这两个 fill + 元素确实是 input[type=password] + 不在 [data-ai-private] 子树内」放行,sensitive() 一行未改,type=hidden 与第三方声明的私有区域仍拒绝,返回值只有 characters 与布尔 valueMatches(valueEchoSuppressed:true),不回显值。详见 Chrome 能力矩阵。

0.8.0 补上了 0.7.0 留下的那个缺口:从 Chrome 自己的密码管理器读回单个站点密码(browser_password_manager_reveal)。用 Windows UI Automation 驱动 chrome://password-manager/passwords,像人一样搜站点、开条目、点「显示密码」,把明文读回来——这不是绕过浏览器,而是使用浏览器:CDP 对任何 chrome:// 页面硬阻断(Cannot access a chrome:// URL),内容脚本进不去(match_origin_as_fallback 只覆盖 about:/data:/blob:/filesystem:),chrome.passwordsPrivate 只发给组件扩展。边界写死在代码里:一次一个站点、条目名精确匹配、详情页再按 scheme+host+port 绑一次 origin、审计只记站点哈希不记值、Chrome 把揭示交给操作系统提示时只报 waiting_for_user_auth 并请用户完成而绝不绕过。同一轮里试做 live 脚本编辑(browser_debugger 的 set_script_source),结果是一个必须说清的否定结论:V8 已把 Debugger.setScriptSource 换成无条件返回 ServerError("setScriptSource functionality no longer available") 的空壳,换任何参数都不会变,因此该动作如实返回 reason:"script_edit_removed_by_v8"、blockedBy:"v8_inspector" 并附替代路径,而不是假装能改。现场验收 56/56。详见 Chrome 能力矩阵。

0.7.0 做的是自动登录闭环:browser_login_state 判断当前处于凭据、验证码还是已登录阶段(只报字段有没有值,不回传任何值),browser_otp_fetch 从已登录的邮箱标签里抽验证码并刻意返回多个候选、每个带发件人与主题,browser_credential_lookup 读本机 Chrome 已保存的登录。同时把可访问名计算按 ARIA 规范重写(补上原先完全没读的 aria-labelledby)。一个必须说清的事实:在这台机器的 Chrome 154 上,读密码库读不到已保存密码——同一个 profile、同一把 key 能解开该 profile 自己的 cookies(12/12),但 177 条登录行一条都不通过认证,另用一台没有 app-bound key 的 Canary 复现同样结果,说明 Chrome 对密码用的不是这把 key。因此失败被拆成三种处置相反的原因(app_bound_encryption / password_key_not_available / legacy_key_mismatch),并附 keyValidation 用同 profile 的 cookie 反证 key。现场验收 30/30。详见 Chrome 能力矩阵。

0.6.0 把 F12 的断点调试真正做进来:新增 browser_debugger,每个标签一条长活 CDP 调试会话,断点跨调用存活,支持单步、栈帧查看与在指定帧上求值。代价是明确的——会话开着时该标签显示调试提示条,且手开 DevTools 无法同时附加。核心安全性质是「暂停的页面绝不能被丢下」:所有关闭路径先 resume 再 detach,无人查看的暂停态按最后交互时间起算 TTL 后自动恢复;暂停期间其它页面工具不再静默超时,而是回结构化 waiting/tab_paused_by_debugger,browser_health 仍能作答。现场验收 38/38。详见 Chrome 能力矩阵。

0.5.25 把「复用优先」补到最后两个缺口:browser_reload_extension 在扩展已连接时改为扩展自我重载(chrome.runtime.reload,不抢焦点、不开标签、不发按键),只有扩展断连时才回退到需要前台的 Windows UI Automation 并如实标注;新增 browser_launch,仅在没有任何 chrome 进程时启动,且启动参数只有 URL、不带任何 profile 或隐身标志,因此沿用用户默认 profile 与登录态。详见 Chrome 能力矩阵。

0.5.24 让所有页面动作默认不依赖窗口/标签焦点:标签置于后台、窗口不在最上层时,读写、CDP 指针派发与坐标动作仍照常完成(后台实测 25/25,npm run test:focus)。坐标工具新增可选 requireForeground 用于显式恢复严格模式;browser_prepare_coordinate 用 evidenceQuality 提示截图是否可能是旧帧。详见 Chrome 能力矩阵 的「焦点独立性契约」。

0.5.18 增加断线错误暂存、重连补传和落盘确认,以及鼠标移入后的再次校验、弹窗表单值变化检测。日志保留范围与错误清理流程见 扩展错误维护。

0.5.17 坐标指针动作新增必需步骤:browser_prepare_coordinate → 核对返回目标 → 使用一次性 coordinateToken 调用 browser_action。旧客户端需刷新工具列表;服务与扩展需配套升级。

浮层、坐标与交易结果的当前保障及待完成项见 操作可靠性说明。点击已派发不等于业务成功;故障诊断对租用等操作返回待核验凭据,不授权自动重放。

能做什么

AI Browser Bridge 是一个本机优先的浏览器操控编排层,服务与扩展配套运行。它不内置云模型;任意支持 MCP、HTTP 或 CLI 的 Agent 都可以接入。

  • 读取网页正文、交互控件、页面状态和受限截图。

  • 点击、填写、选择、滚动、上传、下载、导航和关闭标签页,并对关键写操作做结果验证。密码框默认拒绝填写,browser_fill / browser_fill_verified 需显式 secret:true 才放行,且不回显写入的值。

  • 处理 iframe、重型 SPA、虚拟滚动、下拉框、广告、引导层、客服浮窗和透明遮挡。

  • 通过 DOM、Accessibility、Debugger、截图和坐标适配器进行分层兜底;没有 GPT 时仍可使用本地确定性工具链。

  • 下载意图有独立路由和终态观察(0.32.0):查看记录走下载列表,等待已有记录用精确 downloadId 的 browser_download_wait(最长 20 秒),上传与页面下载不再混为一条路径。页面下载要先定位链接并显式点击,再用 URL/文件名/时间证据核对 Chrome 下载记录;当前没有可靠的一键 click→downloadId 关联,多候选就报告歧义。verified:true 只证明 Chrome 报 complete 且文件存在,不代表内容可信或已安全扫描,工具不会自动打开文件。

  • 坐标与 DOM ref 现在能无缝交接(0.31.0):browser_prepare_coordinate 返回 target.hitRef,并在目标为语义控件或可验证的 cursor:pointer 卡片时另给 clickRef + 证据。若 DOM 点击语义相同,优先 browser_click({tabId, ref:clickRef}),免去截图像素→坐标→DOM 目标的往返;坐标 token 仍留给必须复现真实指针行为的场景。browser_pick 找不到选择器时会在不点击的前提下扫描精确可见卡片,返回可复用 ref、匹配质量、几何和可点击证据;只有一个完全匹配的 cursor:pointer 卡片时才附带经 schema 校验的 browser_click nextStep;同名语义按钮、多个相同项或子串命中只引导读上下文,不会猜着点。共享 cursor:pointer 父容器包着多张卡片时会定位到各自卡片,不会把整组当一张;坐标命中标题文字时会提升到最近的卡片级节点。普通 checkbox/input 不会被误当 picker 触发点击。

  • 停靠式 DevTools 可观察、可选择 Elements/Console/Network 等面板,也可由基座打开与关闭:browser_devtools 的 open / close 走浏览器自己的菜单(More tools → Developer tools),不合成 F12,没有焦点竞态;对外是请求一个状态而非切换,已在目标状态就 dispatched:false。关闭要求走查完整(partial:false)才算数。分离(detached)DevTools 只报 detachedDevtools,尚不能绑定。

  • browser_devtools_capture 通过 CDP 在单次 attach 的有界窗口内只读捕获 Console 消息、异常、Network 请求行、Sources 脚本列表和 Performance 指标;空结果不证明没有发生,网络记录不含 header/cookie/body,缓冲溢出以 dropped/truncated 计数上报。

  • browser_storage_read / browser_storage_write 是 DevTools Application 面板的等价物,覆盖 localStorage、sessionStorage、cookie 与 IndexedDB 结构。拆成读、写两个工具而不是一个带 action 的:读是真的只读,能走 read 优先级、超时降级成可重试;写则必须带 confirmation:"modify_site_storage_once"。发现式读取只回键名与值长度(cookie 只回属性),要取值必须显式指定 key,此时 cookie 返回带 sensitive:true;写入回 verified:false,要验证就再读一次。cookie 走 Network.getCookies,web storage 与 IndexedDB 走 Runtime.evaluate——实测 DOMStorage.enable 与 IndexedDB.enable 都是 -32601 wasn't found,扩展只转发 CDP 域的一个子集。IndexedDB 只读结构:库名先用 indexedDB.databases() 查名单,名单里没有就拒绝(open 会创建库),读记录用返回的 recordReadTemplate 交给 browser_debugger 的 evaluate。清空某源全部 cookie、IndexedDB 的三种写都被刻意拒绝,且一次 CDP 都不发。

  • browser_replay_request 是 Network 面板右键 Replay 的等价物,由页面上下文 fetch 发出,所以自动带页面当前 cookie 与 CORS 位置;调用方自己给 method/url/headers/body,需 confirmation:"replay_request_once"。超时回 status:"uncertain" + reason:"replay_timeout" 而不是 failed(请求可能已到服务器,只是响应丢了),绝不自动重试;Cookie/Host/Content-Length/Connection/Transfer-Encoding/Referer 作为请求头直接拒绝,只接受 http/https。带一次性 CSRF token 的请求重放通常 4xx,那是服务端拒绝过期凭据,不是桥的故障。

  • browser_password_manager_reveal 读回 Chrome 自己保存的单个站点密码:用 Windows UI Automation 打开 chrome://password-manager/passwords、搜索、打开条目、点「显示密码」,再把明文交给调用方。一次只处理一个站点,条目名精确匹配(绝不做子串匹配),详情页再按 scheme+host+port 绑一次 origin 挡 lookalike;siteCandidates 会处理 com.cn/co.uk 这类多段公共后缀与 IPv4。返回 success / waiting_for_user_auth / not_found(附 visibleEntries)/ uncertain / unavailable / denied;Chrome 把揭示交给操作系统提示时只报 waiting_for_user_auth 并要求用户自己完成,之后用 action:"read" 再取,绝不绕过。代价是 focusRequired: true、method: "windows_ui_automation",会短暂占用前台;打开用地址栏 ^l + Alt+Enter 新标签(命令行传 chrome:// 会被 Chrome 拒绝,只会开一个 New Tab),关标签前重读地址栏确认仍停在密码管理器,否则保留并说明。明文只回给调用方,不落盘不写日志,审计只记工具名与站点哈希前 16 位,结果里的 handling 说明这一点。

  • browser_debugger 的 action:"set_script_source" 已接线,但被浏览器拒绝,这是实测结论而不是没做:Chrome 对 Debugger.setScriptSource 返回 ServerError("setScriptSource functionality no longer available"),该错误来自 V8 里的一个无条件空壳(V8DebuggerAgentImpl::setScriptSource,看任何参数之前就返回)。因此该动作回 reason:"script_edit_removed_by_v8" + blockedBy:"v8_inspector" + measuredEvidence + 三条替代路径,applied 恒为 false,且绝不重试——换 scriptId/source/dryRun 都不可能改变结果。要改运行中页面的行为,用 action:"evaluate" 重定义函数或全局变量:立刻生效,但改的是行为不是脚本文本,行号与 source map 不变。

  • browser_clipboard / browser_clipboard_write 读、写系统剪贴板,三条通道各自诚实:strategy:"native" 走本项目自己的原生宿主直接碰操作系统剪贴板,不碰页面、不抬窗口、不受浏览器可见性约束,写入能读回校验;"offscreen" 用扩展的不可见文档,不碰任何页面,但这个 Chrome 上读不了(navigator.clipboard.readText() 在 offscreen 文档里抛 Document is not focused),写会返回 true 却可能什么都没写进去(实测用独立回读证伪);"cdp" 借一个真实标签合成 Ctrl+V,需要该文档可见——完全被覆盖的 Chrome 窗口,活动标签的 visibilityState 就是 hidden,这时返回 retryable:false + clipboard_needs_visible_document + retryWith:{allowFocus:true},而不是报「剪贴板是空的」。读取恒带 secrets:true 与不可信内容标记,写入需 confirmation:"modify_clipboard_once"。

  • browser_navigation 是 webNavigation 的落点:这个权限只有事件、没有操作,而事件本身就是能力。action:"status"(只读,不建会话)/ "timeline"(事件流)/ "chain"(一次导航的重定向链:startUrl / finalUrl / hops / redirects / historyUpdates / durationMs)。它回答的是「这个标签页刚才真正去了哪些 URL」——包括被 beforeunload 拦下、被 301 链重定向、被 SPA 用 history API 改掉的。finalUrl 是最后一个 commit 的文档,不等于浏览器最后停在哪。

  • browser_native_messaging / browser_native_ports / browser_native_host / browser_native_host_install 是 nativeMessaging 的落点,也是唯一离开浏览器沙箱的通道。宿主是本项目自己的 Node 程序(native-host/host.js),支持 ping、system.info、clipboard.read/write、shell.run,并写审计日志。发送需 confirmation:"run_native_host_once",安装需 modify_native_host_once。三种失败要求三种相反动作:native_host_not_registered(Chrome 什么都没启动,去装)、native_host_no_reply(收下了没答,结果未知,别重发)、native_host_closed(答复前退出)。

  • browser_identity 用 identity 权限:action:"profile" 返回登录这个 Chrome 配置的账号(需要 identity.email,本版已申请,真机返回真实邮箱);action:"token" 在没有 oauth2 段时明确回 oauth2_not_configured + manifestField:"oauth2";action:"launch" 由调用方自带 client_id,只接受 https(回环地址除外)。

  • browser_certificates / browser_certificates_write 管证书。扩展没有任何证书 API(chrome.certificateProvider 只对厂商/企业组件扩展开放),而 Windows 上的 Chrome 不维护自己的证书库,它用的就是操作系统的存储——所以路径不是去驱动 chrome://settings/certificates,而是直接读那个存储:stores(7 个存储 + 可读计数)、list、inspect(含证书链与操作系统自己的信任裁决)。写(import / remove,确认串 modify_certificates_once)用回读证明:成功意味着指纹可证地存在或已消失。把证书加进 Root/TrustedPublisher 时 Windows 会弹自己的同意框,那个框就是安全边界,命中时返回 waiting_for_user_auth(请用户去点,然后用 list 复查,不是重试导入)。

  • browser_native_action 有六种动作,按「能证明什么」分组:expand / collapse / toggle(必须带 desiredState,只接受「请求一个状态」,不接受翻转——翻转两次回到原点,不确定的结果没法重试)/ select(按可见名字选一项,控件本身是条目或控件是容器都支持)/ value(设值并回读,判定用完整字符串,回显截断到 200 字符)这五种都是幂等请求:先读当前状态,已经在目标状态就 alreadyInState:true 且 dispatched:false。invoke 不是幂等的,所以必须带 expect.afterInvoke:"control_gone" 这个后置条件,且不确定时绝不重发。全部通过 UIA pattern 绑定到元素,不合成全局按键。

  • 一个实测边界:chrome:// 页面的内容对 UI Automation 不可见(把窗口切到 chrome://extensions 后抓原生树:88 个控件、Document 0 个、带 TogglePattern 的 0 个,全是浏览器外壳)。所以原生动作层能驱动浏览器外壳(工具栏、菜单、标签条),不能驱动 chrome:// 页面里的开关——那需要 Chrome 的无障碍引擎被启用。chrome://password-manager 之所以能读,是因为它走地址栏输入 + 键盘,不是 UIA 元素绑定。

  • browser_eval(0.30.0)在页面里执行一段表达式并取回结果,这是最后一层兜底,也是最快的一层。主世界走 chrome.scripting.executeScript({world:'MAIN'})——不开面板、不弹 banner、不抬窗口、后台标签也能用;只有页面 CSP 拒绝 eval 时才自动降级到 CDP Runtime.evaluate,返回体的 channel 说明走的是哪条。它存在的原因是:结构化工具(read / find_element / pick / shadow_tree)永远只能覆盖「我们已经想到的」页面形态,React 把 onClick 挂在 <div> 上、标记里一个属性都不留时,工具会扫不到,而 browser_eval 一次调用就能点中并读回结果。mode:"read" 用 const 遮蔽 fetch/XMLHttpRequest/window.open 等作为护栏,但这是防误触不是安全沙箱(readOnlyIsGuardrail:true),表达式仍可经 iframe 绕开;密码框只回 valueLength 与 secret:true,value 恒为 null。

  • 仍未实现:DevTools Console 面板内输入执行(页面内求值已由 browser_eval 覆盖,所以通常不再需要)、分离 DevTools 绑定、下载落盘核验、性能录制、Lighthouse 与 ServiceWorker 调试;从捕获记录直接重放也不支持(browser_devtools_capture 刻意丢弃 header 与 body,是只读诊断而非请求存档),browser_replay_request 需要调用方自己给请求;live 脚本编辑受浏览器限制不可达(见上);chrome:// 页面内容的元素级操作见上一条。

  • 支持多 Agent 队列、优先级、等待状态、标签页写租约、租约续期和服务重启恢复。

  • 支持上传目录白名单、SHA256 校验、Agent 作用域、origin 白名单、内容边界和审计脱敏。

  • 提供 doctor、browser_health、browser_execution_profile、网络/性能诊断和本地滚动日志。

  • 真实 Chrome 本地 fixture 验收可通过 npm run test:devtools 运行;该用例仅操作并关闭自身创建的测试标签,并自己把 DevTools 打开再关回去(ensure-closed → open → open again → select/reselect → close)。

  • 默认本机监听 127.0.0.1,不直接暴露公网;高风险网页内容按不可信输入处理。

完整能力基线、限制、验收指标和升级目标见 产品路线图;新 Agent 接入规则见 AGENTS.md。

发布与版本管理

  • 发布历史:GitHub Releases。源码与本机部署版本以 package.json 和 doctor 为准;推送提交不等于创建 Release。

  • 所有版本:GitHub Releases

  • 版本号必须同时更新 package.json、package-lock.json、extension/manifest.json 和 CHANGELOG.md,再创建同名 Git tag 与 GitHub Release。

  • Release 必须包含变更、验证结果、升级步骤和已知限制;只有“已实现并有测试或实测证据”的能力才可列入已完成清单。

当前版本升级指南

0.5.10:在 0.5.9 基础上增加 SPA 零尺寸布局诊断、视口回退和坐标动作安全等待,避免客服浮窗等图标控件被盲点。完整历史见 CHANGELOG.md。

下一阶段的一次性重构与 Python 数据自动化接入方案见 一次性升级与验收方案。该文件区分已实现能力、实施批次和验收证据;计划内容不会自动视为当前版本功能。

远程沙箱与 VNC 登录边界

AI Browser Bridge 默认是本机桥接,不是公网浏览器网关:HTTP 服务只监听 127.0.0.1,Host 必须匹配 127.0.0.1:端口,普通网站 Origin 会被拒绝。隔离沙箱里的 agent 不能直接接管你电脑上已经登录的 Chrome。

如果 agent 在云端/沙箱里工作,推荐让它在沙箱内运行自己的一套 bridge + Chrome + extension,并通过 VNC/noVNC 让用户在那台沙箱 Chrome 登录目标网站。登录态只留在沙箱 Chrome 中。不要建议用户把本机 19387 端口直接暴露到公网;如确需远程访问,应另行设计带认证、审计、来源限制和明确用户授权的隧道层。

历史:从 0.1.0 更新到 0.1.1

如果加载的是本项目的 extension 文件夹,在 chrome://extensions 找到“自主浏览器”,点击重新加载,然后重新打开扩展面板。配对密钥和已保存的网站列表保留;未设置过 allSites 的旧配置默认启用所有网站。确认“允许所有网站(默认)”已勾选,点击“保存并连接”。如 Chrome 显示新增网站权限提示,按提示确认。若从其他目录解压安装,请用新扩展包更新那个目录后重新加载。

0.1.1 的连接状态和最近操作通过事件自动更新,无需持续点击刷新。原 0.1.0 桥接服务与新版扩展兼容,不必为了升级扩展断开正在使用的服务。

没有内置模型、开发者云端、遥测或 AutoGLM 依赖。浏览器控制在本机完成;接入的 AI 若运行在云端,返回给它的网页内容仍会进入该 AI 的服务。普通封闭聊天窗口不会因为安装扩展就自动获得工具能力。

Related MCP server: Orvima

默认无人值守策略

默认目标是让 WorkBuddy、Copilot、Codex、Qoder 等任意 MCP/HTTP Agent 接上就能稳定使用,不要求用户反复值守:

  • 首次只需要加载扩展、运行 pair.ps1 并保存连接;之后保存配对密钥,扩展和服务会后台重连。

  • 默认允许所有 HTTP / HTTPS 网站,减少小白用户逐站配置成本;需要收紧时再切到 origin 白名单。

  • 多 Agent 冲突、队列满和扩展临时断线默认返回 status:"waiting"、retryable:true、suggestedDelayMs、nextPollTool 和 recommendedNextAction,调用方应等待/轮询,不应把它当成任务失败。

  • 读类工具有 DOM/时间预算并返回 diagnostics;重页面可先用 browser_find_text 定位关键词,也可用 browser_read mode:"cheap" 获取轻量摘要。外层读超时只失败当前读请求,桥保持在线;写类工具超时仍按未知结果处理,避免自动重放误操作。

  • 任意 agent 首次接入先读 browser_agent_guide;使用 browser_queue_status 查看 active/queued/connected 状态;等待恢复时调用 browser_wait_until_ready;共享 tab 的多步写任务用 browser_claim_tab,无人值守场景建议带 wait:true,长任务可用 browser_renew_tab 心跳,写工具带同一个 agent 会自动续租,结束后 browser_release_tab。

  • 上传仍要求本机 allowlisted upload root 和 SHA256,这是少数必须显式配置的安全边界。

已实现

  • 网页可见正文提取、交互元素编号、点击、文本输入、主页面滚动。

  • 在显式许可的本机目录内,按完整 SHA256 将文件绑定到最新 file-input ref;只选择文件,不自动点击提交。

  • 标签页列举、新建、导航、关闭;通过临时 debugger 连接截图,完成后释放。

  • Chrome 中文控制面板:配对、网站授权、连接状态、最近操作、暂停与重新连接。

  • MCP stdio、带认证的本机 HTTP API、CLI 三种入口;不绑定模型厂商。

  • 默认允许所有 HTTP / HTTPS 网站;关闭“允许所有网站”后按完整 origin 列表检查。拒绝 file:、javascript:、Chrome 内部页和带账户信息的 URL;浏览器保护的页面仍受 Chrome 限制。

  • 命令全局顺序执行;失效元素引用与改变了标签/链接的元素会拒绝操作。多客户端共享同一标签页时,可用 browser_claim_tab 获取强制写租约;租约有效期间,写操作必须传入匹配的 agent,读操作仍可观察。

  • 可选的 Chrome 内置模型协助:browser_ai_status 检测本机 Chrome 内置 AI,browser_local_judge 用它在本地把一个拟执行动作判为 allow/warn/block/unsure,模型不出本机、也不执行动作。治理可配置 judge:"require",让某个 agent 的写操作必须先拿到 allow 裁决;模型不可用则拒绝写入(fail-closed)。

  • AI 辅助发现(0.35.0):当所有确定性规则失效时(SPA role-less 元素、遮挡页面、陌生 UI),nextStepFor 返回 nextStep.aiAssisted === true,Agent 自动调用 browser_local_judge({mode: 'discovery', ...}) 获取页面语义解析、工具推荐和操作引导。AI 是可选增强器,绝不影响离线可用性;模型不可用时回退到确定性规则。详见 AGENTS.md、AGENT-QUICKSTART.md 和 skills/ai-browser-bridge/SKILL.md。

  • 智能调度器(0.35.0,0.35.1 完成运行时回归):priority-engine.js / load-balancer.js / ai-scheduler.js 提供动态优先级评分、任务分解建议、多 Agent 冲突检测与跨实例负载均衡,作为增强层挂在现有串行队列之上。简单读取优先于写操作与导航;任务分解是建议(subTaskSuggestions),不会凭空生成可执行浏览器命令;跨实例探活与路由带 Bearer token,走真实的 GET /status / POST /call 契约,401 视为「端口在监听」而非实例死亡。

  • 扩展占用者可见(0.35.1):/status 与 browser_queue_status 返回 extensionOwner { pid, processName, nonChromeHost, ... },明确「当前是哪个浏览器进程占着唯一的扩展连接」。当多个装了扩展的浏览器同时存在(系统 Chrome / Codex 内置浏览器 / Canary)时,不再出现「我的 Chrome 没反应」却无从排查的情况。browser_reload_extension 会在 errorList 里返回 addressVerified / observedButtonNames 等原始证据,只有地址栏已验证为 chrome://extensions 且文档可读时才判定错误列表已清空。清错的 UIA 扫描会按 extensionOwner.pid 精确绑定到持有连接的那个浏览器进程;若该宿主没有可见窗口(实测 Codex/ChatGPT 内置浏览器),结果带 hostBinding 明确说明是哪个进程、为什么没清,而不是误报成功或让人去开另一个 Chrome 窗口。

  • 多宿主并发基座(0.36.0):扩展连接不再独占——系统 Chrome、Canary/Dev、Agent 内置浏览器可同时连到同一服务,各自持有独立 hostId、独立 in-flight 槽位与独立能力画像,任一宿主卡住或断线都不影响其他宿主。所有工具新增可选 hostId(把命令与 tab 租约绑定到指定浏览器;tab id 只在单个浏览器内唯一)与 commandId(幂等键,重发只回放结果、不重放写操作)。browser_hosts 列出全部宿主、协议/扩展/Chrome 版本、socket 占用进程与能力 hints(WebMCP Origin Trial 149+、outputSchema 159.0.8140.0+)。握手向后兼容未带 hostId 的旧扩展;显式指定宿主掉线时返回 waiting 而非静默改派;不设能力禁区,版本与能力差异只作为 hints,绝不因「宿主不是 chrome.exe」拒绝连接。完整排期与极端场景库见 docs/HIGH-CONCURRENCY-ROADMAP.md。

  • 心跳巡检(0.37.0):node src/cli.js heartbeat 一次采集服务/宿主/队列/审计指标并与源码 buildId 比对,异常时生成 .local/heartbeat/incidents/*.md 排障报告;--watch --interval-ms 60000 可由用户自行常驻。clean 需要 buildId、版本、连接与零 findings 全部有证据,否则一律 uncertain;finding 连续 3 轮才升级 critical,宿主变化按 hostId 双快照对比。只自动执行两个白名单动作(源码陈旧时经看门狗重启服务、扩展落后时按 hostId 重载),且必须服务完全空闲、通过冷却与单实例锁、动作后复核;其余异常只报告不动作。critical / uncertain / skipped 返回非零退出码,供定时调度器识别。详见 docs/HIGH-CONCURRENCY-ROADMAP.md 的 SLO 与故障注入部分。

  • 可选的多 agent 治理:用 node src/cli.js agent set <name> '{"scopes":["read"],"origins":["example.com"],"maxLeaseMs":60000}' 注册具名 agent 后,超出作用域的写入/上传/认领租约、白名单外的导航都会被服务直接拒绝并返回 status:"denied"、reason、retryable:false,租约时长会被钳制到上限;未注册或未具名的调用保持默认开放,不会削弱单机使用。所有工具都接受可选 agent,建议读操作也带上以便审计归属。同一份策略还可配 tools.allow/tools.deny(工具级白/黑名单)、judge:"require"(写前过本机模型,默认 judgeTtlMs 120s)、contentBoundaries:true(读输出加不可信内容边界)。策略存放于 .local/agents.json,运维改动无需重启。

  • 断线和超时不自动重放写入;断线后的新请求返回 waiting 恢复指引,已派发或排队的未知结果命令仍失败返回。暂停不撤销已经执行的操作。扩展保存配对密钥后会在后台自动重连,不需要 AI 自动打开扩展设置页。

  • 日志仅记录操作名称、标签页编号、调用方 agent 名、时间和成功/失败,不记录输入内容、正文、截图或密钥。

本机启动与安装

要求:Node.js 22+、PowerShell 7(pwsh.exe)、Chrome 120+。不需要会命令行:每个 .ps1 都配了同名 .bat,双击即可运行。

PowerShell 7 是常规双击入口的硬依赖,不是可选项。 7 个入口 .bat(start / status / doctor / pair / stop / install-autostart / uninstall-autostart)都调用 pwsh.exe,而且 start.ps1 / scripts/watch-service.ps1 用到 PowerShell 7(.NET 5+)才有的 [Convert]::ToHexString。缺了它,start.bat 根本起不来。现在这 7 个入口都会先检测:缺依赖时给出中文安装指引、把时间 / 脚本名 / 原因写进 .local/launcher-error.log、保留窗口并退出非零码,不会一闪而过。唯一例外是 doctor-lite.bat:它只调用 node.exe,专门用于诊断「连 doctor.bat 都打不开」的 pwsh 缺失场景。

未安装时任选其一:

  • 在线:winget install --id Microsoft.PowerShell --source winget

  • 离线 / 国内网络:从 PowerShell GitHub Releases 下载 PowerShell-*-win-x64.zip 便携版,解压到任意目录,把该目录加入用户级 PATH,然后重新双击。winget 的 MSIX 包在部分环境会在 StagePackageAsync 阶段中断;GitHub 单连接下载可能很慢,可改用支持多线程分段下载的工具。

如果 where.exe pwsh 有结果但双击仍失败,先运行 doctor-lite.bat:它只用 node.exe,完全不经过 pwsh.exe,所以能在 PowerShell 7 缺失或 PATH 异常时给出宿主环境报告(pwsh / node / 自启注册 / 端口占用 / 代理 / 服务四态),每项都带可复制的修复命令。

本机已安装依赖;迁移到另一台电脑时先在项目目录运行 npm ci。

  1. 双击 start.bat。服务只监听 127.0.0.1:19387,后台运行。再次启动会先检查已有服务,不会重复启动。

  2. 在你希望使用的 Chrome 用户配置中打开 chrome://extensions,打开右上角的开发者模式开关,点左上角“加载已解压的扩展程序”,选择本项目的 extension 文件夹。

  3. 双击 pair.bat,密钥会复制到剪贴板(不会打印)。点击浏览器工具栏右上角的拼图图标 → 自主浏览器(若被折叠,先点拼图图标把它固定出来),粘贴配对密钥。

  4. 默认勾选“允许所有网站”,无需填写网站列表,点击“保存并连接”,状态会自动更新为“已连接”。若只想操作部分网站,取消勾选并逐行填写源地址,例如 https://example.com,再保存。路径不能填写;不同端口属于不同 origin。

  5. 双击 status.bat。只有 connected: true 才表明当前 Chrome 扩展实际连接成功。

瞄一眼就知道通不通:工具栏角标

角标

含义

该做什么

ON(绿)

已连接

直接用

...(黄)

正在连接

稍等几秒

OFF(灰)

已被暂停

点图标 → “重新连接”

?(红)

配对密钥不匹配

重跑 pair.bat 再粘贴

2(橙)

服务已被另一个 Chrome 配置占用

先在那边断开

!(红)

服务没起来或连接断开

双击 start.bat;扩展会后台自动重连

出问题时还会弹一次系统通知(同一类问题 10 分钟内至多一次),不用盯着角标等。

现有 AutoGLM 无需卸载。建议不要让两个自动化工具同时操作同一个标签页。每个服务只连接一个 Chrome 用户配置;第二个配置会被拒绝,并在角标上显示 2,不再只是无意义地转圈。

扩展更新后,在 Chrome 扩展页点“重新加载”。已保存配对密钥且未暂停时,扩展会在后台自动重连;连接成功时每 20 秒发送一个纯本机保活消息,不会调用模型或消耗模型 token。AI 客户端诊断断线时只应读取 npm run status 或 GET /status,不要自动打开 chrome-extension://.../panel.html。

开机 / 关机后的必要操作

服务注册为登录自启后,日常使用不需要任何手动操作。下面分「一次性设置」和「日常」两部分。

一次性设置(每台机器只做一次)

双击 install-autostart.bat。

启动入口使用 PowerShell 7 并遵守机器原有的脚本执行策略。若被策略或安全软件拦截,应保留报错并排查,不自动添加排除项、降低策略或恢复隔离文件。参见 2026-10-19 告警记录。

默认在用户「启动」文件夹放一个快捷方式,运行隐藏的 PowerShell 7 健康守护程序。可以在「任务管理器 → 启动应用」里禁用;组织安全软件仍可能限制启动。

两种方式均隐藏运行。如需注册表方式:

.\install-autostart.ps1 -Method Registry

安装会立即启动守护程序,并注册以后登录时启动。它约每 10 秒检查认证状态,服务未监听时尝试启动,失败退避至最多 60 秒;命名互斥锁避免重复守护。当前状态写入 .local/watchdog-status.json,状态变化写入可轮转的 .local/watchdog.jsonl。安装命令完成不等于连接验收通过,使用 npm run doctor 确认。

安装时还会写两份可审计的清单:

  • .local/runtime.json:本次解析到的 pwsh.exe / node.exe 绝对路径。start.ps1 / agent.ps1 / scripts/watch-service.ps1 都优先读它,文件失效才回退 Get-Command。这样即便登录时便携版 pwsh 或 AI 工具链注入的临时 node 改变了 PATH,服务重启后也不会选到另一个二进制。doctor-lite.bat 会报告当前实际使用的路径。

  • .local/autostart-registry.json:本次真正注册成功的条目(startup-shortcut 或 registry-run),含目标路径。用 .\install-autostart.ps1 -StatusOnly 可只读列出所有注册项,包括检测到但不由项目管理的外部计划任务(标 external,不会自动删除)。

运行期调用、连接恢复、队列等待、权限拒绝和错误分类会自动追加到 .local\logs\audit.jsonl;单文件超过 10 MB 时滚动为 audit.1.jsonl。这些日志用于下一次版本升级的回归分析,不包含页面正文、输入值、Cookie 或 Authorization。

撤销:双击 uninstall-autostart.bat。它按 .local/autostart-registry.json 逐项清理并输出「已移除 / 未找到」,最后把 entries 清空(文件保留作为证据),保留配对密钥和你的配置。若你自己额外注册过计划任务(例如 AI Browser Bridge Watchdog),卸载器只会如实报告并给出清理命令,不会擅自删除——否则「卸载了却没卸干净」会反过来变成「删了不该删的东西」。

此外,扩展只需要加载 extension/ 并用 pair.bat 配对一次;密钥保存在扩展里,之后自动重连。

每次开机 / 重新登录后

什么都不用做。登录 Windows 时服务自动起来,打开 Chrome 后扩展自动连上,不需要再跑 start.bat 或 pair.bat。

想确认就双击 status.bat,看到 "connected": true 即可。

关机 / 重启 / 合盖

守护程序运行且依赖正常时,休眠唤醒后继续检查与恢复;恢复期间 AI 报 waiting 属正常,不要重复提交不确定的写操作。端口被占用、配置损坏或扩展被禁用不能承诺自动恢复,日志会区分这些情况;守护不会杀死未知进程或修改配对信息。

主动停止:双击 stop.bat,同时设置持久暂停标记,避免被守护立即重启。运行 start.bat 或重新安装自启动可解除暂停。卸载自启动会使守护在下次检查时退出。

状态不对时的排查顺序

先双击 doctor-lite.bat(只用 Node,不依赖 PowerShell 7),它检查宿主环境:pwsh / node 的实际路径与版本、.local/runtime.json 是否失效、自启注册了哪些项、19387 端口被谁占用、NO_PROXY 是否覆盖本机、以及服务处于「未安装 / 未启动 / 端口占用或无响应 / 运行中」哪一种状态。服务起来之后,再双击 doctor.bat 做运行时体检(buildId、版本一致性、扩展连接、工具 schema)。两者不在同一层,缺一不可。

现象

处理

双击 .bat 一闪而过

看 .local/launcher-error.log;再 where.exe pwsh,缺则按上文装 PowerShell 7

服务未监听、fetch failed

查看 .local/watchdog-status.json;可双击 start.bat,反复失败检查 .local/server-error.log

端口有监听但 /status 不通

Get-NetTCPConnection -LocalPort 19387 查 OwningProcess;可能是别的进程占用或代理吞掉本机请求(本机探活已显式 -NoProxy)

守护把服务拉起来了吗

看 .local/watchdog.jsonl 的 starting-service → startup-completed → connected 状态迁移

服务在跑但 connected: false

扩展被暂停(角标 OFF):点图标 → “重新连接”;或看 doctor 的 extension-connected

角标 ?

配对密钥不匹配,重跑 pair.bat 再粘贴

角标 2

另一个 Chrome 配置已占用,先在那边断开

扩展卡着红色 Errors

只是历史错误残留,不影响使用。等几分钟或在 chrome://extensions 点 Reload

面板一直显示「未连上本机服务」

说明扩展从未握手成功过:先 start.bat,再 doctor-lite.bat;自动重连仍在后台运行

换电脑 / 重装系统

npm ci → 装 PowerShell 7 → start.bat → install-autostart.bat → pair.bat

卸载自启后守护还在拉服务

.\install-autostart.ps1 -StatusOnly 列出全部注册项;外部手工注册的计划任务项目不会自动删除,报告会给出清理命令

关于 PowerShell 版本(2026-11-10 更正):这里曾写「所有 .ps1 都不再要求 PowerShell 7,Windows PowerShell 5.1 也能直接运行」,这是错的。.ps1 里的 [Convert]::ToHexString 等属于 PowerShell 7 / .NET 5+,5.1 跑不了;而且用户实际双击的是 .bat,它只认 pwsh.exe。脚本引擎兼容性与启动器依赖是两件事,不要把前者当成后者已解决。因此也不做「.bat 悄悄回退 powershell.exe」:那会把一步可见的失败换成更难查的运行时异常。

重启后验收清单

重启并登录 Windows 后,30 秒内按顺序确认:

.\status.bat                          # 期望 "connected": true
.\doctor.bat                          # 期望 "ok": true,extension-connected / version-match 均为 true
type .local\watchdog-status.json      # 期望 "state": "connected"
.\install-autostart.ps1 -StatusOnly   # 期望列出本次注册的条目,无「幽灵注册」

Chrome 面板应显示「已连接」、角标 ON。若 30 秒内未恢复,先看 .local/watchdog.jsonl 的状态迁移,再看 .local/server-error.log,最后跑 doctor-lite.bat 区分「服务没起来」与「端口被占用」。

代理环境:本机请求固定走 127.0.0.1,脚本已用 -NoProxy 绕过代理。若在企业网络或使用代理工具,建议把用户级 NO_PROXY 设为 127.0.0.1,localhost,::1(setx NO_PROXY "127.0.0.1,localhost,::1");HTTP_PROXY 与 http_proxy 大小写重复会让 Windows PowerShell 5.1 的部分命令报「已添加具有相同键的项」。

让其他 AI 接入

MCP 客户端

npm run setup 会生成 .local/mcp-config.json,包含当前机器的 Node 可执行文件路径与 MCP 入口,不含密钥。将其中的服务器条目加入支持 stdio MCP 的客户端配置并重载连接。不同客户端的外围配置格式可能不同。

{
  "mcpServers": {
    "ai-browser-bridge": {
      "command": "node",
      "args": ["K:/PythonProjects5/AI-Browser-Bridge/src/mcp.js"]
    }
  }
}

MCP 入口不会启动云模型。客户端自己决定下一步调用哪个工具。tools/list 暴露 68 个工具,browser_screenshot 返回 MCP 图片内容。

CLI 客户端

node src/cli.js tools
node src/cli.js call browser_tabs
'{"url":"https://example.com"}' | node src/cli.js call browser_open --stdin
'{"tabId":123}' | node src/cli.js call browser_read --stdin

必须使用实际返回的 tabId 和 ref,不能照抄示例中的 123。元素 ref 与最近一次快照绑定;导航或页面变化后重新读取。

HTTP / 自己的 AI 程序

端点:GET /status、GET /tools、POST /call。请求头使用 Authorization: Bearer <agentToken>,令牌从本机 .local/config.json 读取。不要把配对密钥当成 agentToken,也不要把令牌贴到对话中。

{"name":"browser_read","arguments":{"tabId":123,"maxChars":12000}}

服务拒绝网页 Origin 请求、错误 Host 和未认证请求,不提供浏览器可用的跨域开放接口。远程 AI 客户端需要本机适配器;本版本不提供公网网关。

验证与边界

npm test
npm run test:e2e
npm run test:bench
npm run test:native

npm run test:native 是唯一会真的操作你窗口的验收:它在你的 Chrome 里展开一个菜单按钮、用独立快照确认、再折叠回去,结束时不留打开的菜单。它只挑浏览器自己画的菜单(书签 / 应用菜单),不碰扩展工具栏按钮——那会运行别人的代码。拒绝路径(不存在的 ref、名字不符的 ref、只给 role 的 ref)全部不派发。没有可用的菜单按钮时它报 BLOCKED 并以退出码 2 结束,那不是通过。

npm run test:bench 是能力之外的另一种验收:5 个任务 × 3 种调用风格(blind / guarded / recipe),度量「一个读不懂页面的调用方能不能把事做完、会不会弄坏东西」,输出完成率、确认率、步数与危险动作数。它不调用任何模型——注入的是失败模式,不是推理能力。数字和「这个套件不证明什么」都写在 调研文档第 5 节。

自动测试使用独立临时 Chromium 配置和本地测试网页,不读取个人 Chrome 数据。端到端测试通过 MCP 完成打开、读取、填写、点击、截图、导航、暂停、恢复及关闭,并由独立 HTTP 客户端验证第二条接入路径。截图与报告在 output/playwright/。

本机测试脚本会优先使用已存在的 Playwright Chromium;其他机器可运行 npx playwright install chromium,或用 AIB_TEST_CHROMIUM 指向测试专用 Chromium。不要指向你的日常 Chrome 用户数据目录。

明确边界:

  • 测试通过不等于已经装入你的日常 Chrome。实际连接以 npm run status 为准。

  • 当前 DOM 操作限主框架和普通 DOM;尚未覆盖 iframe、Shadow DOM 的结构化定位、验证码和复杂富文本编辑器。文件上传支持主框架普通 DOM 的 file input;坐标操作可触达可见区域,但不提供 Canvas 结构化解析。

  • 原有输入/点击使用 DOM 事件;新增键盘和坐标路径使用 CDP 可信事件。截图使用 debugger 权限,Chrome 可能显示调试提示;与其他调试器冲突时返回错误。

  • 内置任务期间持有全局操作租约;外部客户端编排同一标签页的多步工作流时,应先 browser_claim_tab,后续写工具传入同一个 agent,完成验证后 browser_release_tab。

  • 指定网站模式约束工具的直接目标,不是网络防火墙。网页自身的脚本仍可能提交表单、发网络请求或跳转到其他网站;指定模式下未授权页不能继续读写,所有网站模式可继续操作其他 HTTP / HTTPS 页。截图包含页面中可见的嵌入内容。

  • 文本提取不读取密码输入值,忽略隐藏文本与 data-ai-private 标记区域;截图没有自动脱敏。不能将其理解为完整敏感信息检测。

  • 页面内容属于不可信数据,AI 客户端不得将网页中的指令当作用户授权。点击可能导致提交、发消息或购买,客户端必须遵守用户授权范围。

  • browser_local_judge 只是可选的本地 Chrome 内置 AI 建议源。它不执行动作,也不替代用户授权;如果 Chrome 没有暴露 Built-in AI API、模型未下载或会话创建失败,会返回 verdict: "unavailable",调用方必须按无裁判可用处理。首次本地模型会话可能较慢,服务端对该工具使用 120 秒专用超时。调用方必须检查 schemaValid、parseWarning 和 needsHumanConfirm,不得把非标准模型输出当作批准。

  • 已有插件任务助手,模型在本机配置文件设置;尚无完整多轮聊天和模型管理界面。外部 AI 仍可独立调用工具。没有声称已全面超过 AutoGLM。

文件与运行状态

位置

用途

extension/

可直接加载的 Chrome 插件源码

src/server.js

本机 HTTP / WebSocket 桥接、排队、鉴权

src/mcp.js

MCP stdio 适配

src/cli.js

命令行入口

.local/

本机密钥、日志、PID、接入配置,不进入分发包

各 .bat

双击入口:start / stop / pair / status / doctor / install-autostart / uninstall-autostart

doctor-lite.bat

零 pwsh 依赖的宿主环境诊断(只用 node.exe),服务没起来 / PowerShell 7 缺失时先跑它

scripts/check-runtime.bat

各 .bat 的公共前置检查;失败写 .local/launcher-error.log

scripts/doctor-host-env.mjs

doctor-lite 的检查实现,输出中文报告或 --json

scripts/resolve-runtime.ps1

优先读 .local/runtime.json 的 pwsh / node 绝对路径,失效才回退 PATH

install-autostart.ps1

注册登录自启(默认启动文件夹,-Method Registry 用注册表);-StatusOnly 只读列出注册项;写 runtime.json 与 autostart-registry.json

uninstall-autostart.ps1

按 autostart-registry.json 逐项撤销并输出「已移除 / 未找到」;不擅自删除外部计划任务

.local/runtime.json · .local/autostart-registry.json

固化的二进制路径与本次注册项清单,供诊断与卸载对账

test/

服务测试和真实扩展端到端测试

docs/HANDOFF.md

后续开发交接与已知限制

停止后台服务:.\stop.ps1。不设置开机自启、不修改现有 AI 客户端配置。若要完全停用,也可在 Chrome 中禁用扩展。

技术依据

给新 Agent 的浏览器接入提示词

将下面的提示词放在新 Agent 的任务前置说明中。它让 AI Browser Bridge 成为默认入口,同时保留在桥接不可用或能力不足时安全降级的空间:

浏览器任务默认优先使用 AI Browser Bridge。

先执行 browser_health、browser_tabs、browser_observe。
如果桥接可用,继续使用 Bridge 完成任务,并遵守其队列、租约和验证规则。

如果 Bridge 暂时不可用:
1. 不要反复打开扩展设置或无限重连;
2. 判断任务是否可以安全等待;
3. 底层调试、网络拦截、性能分析在 Bridge 内已经有对应工具(`browser_debugger`、`browser_network_rules`、`browser_performance`、`browser_devtools_capture`)。一次定位失败不构成改换通道的理由;只有 Bridge 确实不可用、且用户明确授权了具体替代通道时,才可在该授权范围内切换,并按 AGENTS.md 的失败收容规则记录原因;
4. 切换工具前记录原因,并避免同时对同一标签页执行写操作;
5. 对点击、填写、上传、提交等不确定写操作,先重新读取并验证,禁止盲目重放。

根据任务目标和页面状态选择最合适的工具,不要为了遵守工具偏好而牺牲任务完成度。

完整的工具顺序、iframe 和遮挡处理、多 Agent 排队规则见本项目的 AGENTS.md。 产品能力基线与长期升级目标见 docs/PRODUCT-ROADMAP.md。

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      B
      quality
      B
      maintenance
      Turns Chrome into a local MCP server, giving AI agents 51 browser tools to inspect DOM, automate clicks, run JavaScript, audit security and accessibility, mock APIs, extract data, record demos, and more from any MCP client.
      92
      459 npm
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables any MCP-capable AI client to drive the user's own Chrome or Edge browser through a set of browse_* tools — navigating, clicking, typing, extracting, and managing tabs with their existing logins on their own machine. Runs locally and self-hosted with a live viewport and pause/approve controls so every verified step stays under user watch.
      4
      MIT
    • A
      license
      B
      quality
      B
      maintenance
      Enables AI agents to control your installed Google Chrome through MCP or HTTP using compact text snapshots, exact pixel coordinates, and a visible cursor. It provides browser tools for navigation, clicking, typing, scrolling, tabs, forms, waiting, recording, and human handoff.
      35
      105 npm
      3
      MIT
    • A
      license
      Not graded
      quality
      A
      maintenance
      Enables AI coding tools to drive the user's real, already-logged-in Chrome through an MCP stdio interface, exposing 25 tools for navigating, clicking, typing, reading pages, taking screenshots, capturing network traffic, and accessing cookies and storage. The bundled Chrome extension talks to a local bridge instead of an open remote-debugging port, so logged-in sessions and bot-detection-proof human browsing work without extra setup.
      1 npm
      ISC