Mastra BrowserViewer 实战:用 Playwright 托管 Chrome、CDP URL 与屏幕直播驱动 CLI 浏览器工具
2026/9/14 5:41:56 网站建设 项目流程

Mastra BrowserViewer 实战:用 Playwright 托管 Chrome、CDP URL 与屏幕直播驱动 CLI 浏览器工具

【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra

@mastra/browser-viewer是 Mastra 浏览器体系中的 CLI 型浏览器 Provider:它用 Playwright 启动并全权管理一个 Chrome 实例,对外暴露 Chrome DevTools Protocol(CDP)WebSocket 地址,供agent-browserbrowser-usebrowse等命令行工具作为次级客户端连接,同时提供屏幕直播(screencast)、输入注入和线程级浏览器隔离能力。本文基于该包的 README、完整源码与仓库内参考文档展开,读完后你可以独立配置一个 BrowserViewer 接入 Workspace、理解 CDP URL 的发现与注入机制,并掌握thread/shared两种作用域、外部浏览器连接与直播流的实现细节。

一、定位:CLI 驱动的浏览器自动化

在 Mastra 中,Agent 驱动浏览器有两条路线:通过 SDK 对象编程(如AgentBrowserStagehand),或通过命令行工具操作。BrowserViewer属于后者。源码顶部的注释直接说明了它的设计目标(browser/browser-viewer.ts#L1-L12):

  • 直接建立页面级 CDP 会话(修复 screencast 的 sessionId 问题);
  • 完整的浏览器生命周期控制;
  • 可预测的 CDP URL,便于注入到 CLI 命令;
  • 线程(Thread)作用域的浏览器隔离。

类定义在 browser/browser-viewer.ts#L49,关键只读属性揭示了它的身份(browser/browser-viewer.ts#L50-L59):

属性值 / 说明
id构造时生成为browser-viewer-{timestamp}
name固定为'BrowserViewer'
provider固定为'browser-viewer'
providerType固定为'cli',用于区别于 SDK 型 Provider
cli当前配置使用的 CLI(CLIProvider类型)

与 SDK 型 Provider 的一个本质区别是:getTools()直接返回空对象(browser/browser-viewer.ts#L406-L414)。CLI Agent 不需要 SDK 工具,它通过workspace_execute_command配合 skills 来驱动 CLI 命令,Mastra 只负责浏览器侧的直播、输入注入与生命周期。

完整的集成指南(Workspace 快速上手、CLI 安装方式)见仓库内文档 browser-viewer 集成文档,API 级参考见 BrowserViewer 参考文档。

二、安装与基础用法

安装方式(见 browser/browser-viewer/README.md):

npm install @mastra/browser-viewer

包元信息(browser/browser-viewer/package.json):

  • 当前版本0.2.3,依赖playwright-core@^1.61.1typed-emitter
  • @mastra/core的 peer 要求为>=1.26.0-0 <2.0.0-0zod兼容^3.25.0 || ^4.0.0
  • 要求 Node.js>=22.13.0

README 给出的最简用法——独立启动浏览器并获取 CDP URL:

import { BrowserViewer } from '@mastra/browser-viewer'; const viewer = new BrowserViewer({ cli: 'agent-browser', // Agent 将使用的 CLI headless: false, // 显示浏览器窗口 }); // 启动浏览器 await viewer.launch(); // 获取供 CLI 连接的 CDP URL const cdpUrl = viewer.getCdpUrl(); console.log(cdpUrl); // ws://127.0.0.1:9222/devtools/browser/...

典型的生产用法则是把BrowserViewer交给Workspace,再挂到 Agent 上(示例整理自 browser-viewer.ts#L36-L47 的 JSDoc 与仓库内集成文档):

import { Workspace, LocalSandbox } from '@mastra/core/workspace'; import { BrowserViewer } from '@mastra/browser-viewer'; const workspace = new Workspace({ sandbox: new LocalSandbox({ workingDirectory: './workspace' }), browser: new BrowserViewer({ cli: 'browser-use', headless: false, }), });

当 Agent 通过workspace_execute_command执行浏览器 CLI 命令时,Mastra 会自动:检测 CLI 命令 → 若浏览器未运行则通过 Playwright 启动 Chrome → 把 CDP URL 以正确的标志注入命令 → 开始向 Studio 推流 screencast。

三、配置参数详解

BrowserViewerConfig继承自 core 包的BrowserConfigBase,再附加两个 CLI 专属字段(browser-viewer/types.ts#L15-L29):

参数类型默认值说明
cli'agent-browser' \| 'browser-use' \| 'browse' \| 'browse-cli'必填Agent 使用的 CLI,CLI 通过 CDP URL 连接 Chrome
cdpPortnumber0(自动分配可用端口)Chrome 远程调试端口,仅在自行启动 Chrome 时生效
headlessbooleantrue是否无头运行(测试 browser-viewer.test.ts#L46-L49 验证了默认值)
scope'thread' \| 'shared''thread';提供cdpUrl时默认'shared'浏览器实例作用域,详见第五节
cdpUrlstring \| (() => string \| Promise<string>)-连接已存在的浏览器而非新启动一个
viewport{ width, height } \| 'window'{ width: 1280, height: 720 }视口尺寸;'window'表示不做视口模拟、跟随真实窗口
executablePathstringPlaywright 自带 Chromium自定义 Chrome 可执行文件路径
timeoutnumber10000浏览器操作默认超时(毫秒)
onLaunch/onClose生命周期回调-浏览器就绪 / 关闭前触发
screencastScreencastOptions-直播流参数(格式、质量、尺寸)

基础字段的定义与约束在 packages/core/src/browser/browser.ts#L233-L307。其中两个值得注意的约束:

  1. cdpUrlscope: 'thread'互斥——线程隔离需要为每个线程启动独立浏览器进程,连接已有浏览器时做不到。构造器中的处理逻辑印证了这一点(browser-viewer.ts#L64-L76):
// 提供 cdpUrl 时 scope 默认 'shared',否则默认 'thread' const effectiveScope = config.cdpUrl ? (config.scope ?? 'shared') : (config.scope ?? 'thread');
  1. 构造时 CLI 专属字段(clicdpPort)会被剔除后再传入基类(browser-viewer.ts#L71),因为基类BrowserConfig是判别联合,不识别这些字段。

四、核心机制:Playwright 托管 Chrome 与 CDP URL 发现

真正的启动逻辑在 thread-manager.ts 的launchBrowser中(thread-manager.ts#L182-L290),调用链如下:

  1. chromium.launchServer(...):以--remote-debugging-port=${cdpPort}--no-first-run--no-default-browser-check参数启动 Chrome 进程。端口默认0,由操作系统自动分配;
  2. discoverCdpUrl(browserServer):从 Chrome 的DevToolsActivePort文件中发现真实的 CDP WebSocket 地址(thread-manager.ts#L433-L484)。该文件第一行是端口号、第二行是/devtools/browser/<guid>路径,拼出形如ws://127.0.0.1:52481/devtools/browser/abc...的 URL 供外部 CLI 连接。由于 Chrome 启动初期可能仍在写文件,这里做了最长 1500ms 的重试轮询;
  3. chromium.connect(browserServer.wsEndpoint()):Playwright 自身也连接该浏览器,用于 screencast 与会话管理;
  4. browser.newContext(...)创建上下文并打开初始页面。注意视口处理(thread-manager.ts#L217-L222):viewport: 'window'时传null给 Playwright,语义正是“禁用模拟、跟随真实窗口”;
  5. context.newCDPSession(page)为活动页面建立 CDP 会话,供 screencast 和输入注入使用。

此外还有一个可靠性增强:通过browser.newBrowserCDPSession()打开Target.setDiscoverTargets,监听Target.targetDestroyed(thread-manager.ts#L249-L280)。当页面级 CDP 看不到全局事件时(CLI 会自己创建页面),浏览器级会话能观察到所有 target 的销毁;一旦剩余页面 target 归零即判定浏览器已关闭。

五、作用域模型:threadshared

BrowserViewerThreadManager支持两种会话组织方式(thread-manager.ts#L48-L64):

  • thread(默认):每个线程一个独立 Chrome 进程。threadSessions是一个threadId -> BrowserViewerSession的 Map,懒创建——线程首次调用ensureReady()/launch(threadId)时才启动(browser-viewer.ts#L157-L199)。适合并行 Agent 需要互相隔离的浏览器状态;
  • shared:所有线程共享单个 Chrome。doLaunch()中走createSharedSession()一次性启动(browser-viewer.ts#L122-L135)。

getCdpUrl(threadId?)按作用域路由到对应会话的cdpUrl(browser-viewer.ts#L114-L116)。线程隔离的会话状态查询、活动页解析(resolveActivePage取最近打开的页面)与断连清理逻辑均在该 thread-manager 中实现,关闭入口为closeAll()(thread-manager.ts#L655-L665)。

六、连接已有浏览器:cdpUrlconnectToExternalCdp

BrowserViewer 有两种“不自己启动浏览器”的路径:

  1. 构造时提供cdpUrl:可以是字符串或异步函数。doLaunch()解析 URL 后走connectToExistingcreateSharedSessionFromCdp(browser-viewer.ts#L126-L130),内部用chromium.connectOverCDP(cdpUrl)建立连接(thread-manager.ts#L568-L594)。此时会话的browserServernull,表示进程不归本包所有,清理时不会杀掉外部浏览器;
  2. 运行时调用connectToExternalCdp(cdpUrl, threadId?)(browser-viewer.ts#L210-L225):适用于 Agent 使用自己的外部浏览器端点(例如云端浏览器服务),Mastra 仅连接以启用 screencast,不管理其生命周期。实现上它会先关闭该线程的既有会话,避免泄漏浏览器进程(thread-manager.ts#L551-L562)。

用法示例:

// 连接本地已开启远程调试的 Chrome const viewer = new BrowserViewer({ cli: 'browser-use', cdpUrl: 'ws://127.0.0.1:9222/devtools/browser/abc123', }); // 或运行中接入外部端点 await viewer.connectToExternalCdp('wss://cloud.example.com/session', 'thread-123');

七、屏幕直播与输入注入

startScreencast

startScreencast(options?)(browser-viewer.ts#L279-L382)返回一个ScreencastStream。默认直播参数写死在源码中:

  • format: 'jpeg'quality: 80maxWidth: 1280maxHeight: 720everyNthFrame: 1

其设计要点是CDP 会话按需提供且不缓存CdpSessionProvider.getCdpSession每次调用都通过createFreshCdpSession(threadId)当前活动页面新建 CDP 会话(browser-viewer.ts#L282-L306)。源码注释解释原因——CDP 会话是页面作用域的,tab 切换后必须重新附着到当前页面,而不是启动时的初始页面。配套的 tab 切换处理会监听 context 的page事件与每个页面的close/framenavigated事件,在 100ms 延迟后自动stream.reconnect(),并在流停止时统一解绑监听器(browser-viewer.ts#L317-L378)。

injectMouseEvent / injectKeyboardEvent

Studio 的实时交互通过 CDP 的Input.dispatchMouseEvent/Input.dispatchKeyEvent实现(browser-viewer.ts#L388-L404):

await viewer.injectMouseEvent({ type: 'mousePressed', x: 100, y: 200, button: 'left' }); await viewer.injectKeyboardEvent({ type: 'keyDown', key: 'Enter', code: 'Enter' });

底层getCdpSessionForThread带了一个按pageUrl判定的缓存(thread-manager.ts#L329-L370):同一活动页复用会话,页面 URL 变化或页面关闭则重建;若创建会话失败(页面已在获取与创建之间关闭),会触发断连清理流程。

八、支持的 CLI 与 CDP 注入方式

CLIProvider类型定义支持四种取值(browser-viewer/types.ts#L10)。各 CLI 需在 workspace 环境中单独安装,并各自发布一个 skill 教会 Agent 其命令与工作流。根据仓库内集成/参考文档(集成文档、参考文档):

cli取值安装CDP 注入方式
agent-browsernpm install -g agent-browser+npx skills add vercel-labs/agent-browser--cdp标志
browser-usepip install browser-use+npx skills add browser-use/browser-use --skill browser-use直接 stdin 调用(browser-use/browseruse/browser/bu)时设置BU_CDP_WS与线程隔离的BU_NAME环境变量;遗留子命令保留--cdp-url--session注入
browse/browse-clinpm install -g browse+browse skills install--ws标志

当 CLI 命令经workspace_execute_command执行时,Mastra 按cli配置自动选择正确的注入方式,无需 Agent 手写连接参数。仓库内的 browser/agent-browser 包则是 SDK 型 Provider(供对比),二者分工明确:BrowserViewer管 Chrome 与 CDP,CLI 工具执行具体的浏览操作。

九、行为验证:从测试用例看契约

包内测试(browser-viewer/src/tests/browser-viewer.test.ts)覆盖了几个关键契约:

  • 无浏览器运行时,getBrowserState()getCurrentUrl()均返回null,而非抛错;
  • getBrowserState('thread-1')会透传线程 ID 给底层的按线程状态查询;
  • headless默认true,显式传false时生效(对应 README 中headless: false显示浏览器窗口的用法);
  • cli同时支持现值browse与遗留值browse-cli

getBrowserStateForThread返回{ tabs, activeTabIndex },其中activeTabIndex取最后一个页面(最近打开者),与 thread-manager 中resolveActivePage的取页策略保持一致(browser-viewer.ts#L235-L254)。

十、仓库文件索引

路径内容
browser/browser-viewer/README.md包 README(本文主文档)
browser/browser-viewer/src/browser-viewer.tsBrowserViewer主类:生命周期、CDP、screencast、输入注入
browser/browser-viewer/src/thread-manager.ts线程会话管理、Chrome 启动与 CDP URL 发现
browser/browser-viewer/src/types.tsCLIProviderBrowserViewerConfig类型
browser/browser-viewer/src/tests/browser-viewer.test.ts行为测试
browser/browser-viewer/package.json版本、依赖与引擎要求
docs/src/content/en/integrations/browsers/browser-viewer.mdx集成指南(Quickstart、CLI 安装)
docs/src/content/en/reference/browser/browser-viewer.mdx完整 API 参考(参数表、方法签名)
packages/core/src/browser/browser.ts基类BrowserConfigBase配置约束

【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询