本页内容
@mearl/provider
@mearl/provider 是 Mearl 的统一浏览器 Provider 契约与运行时。内置 Local Chrome 和外部
Provider 使用相同的 BrowserProviderDefinition;区别只在于运行时位于 Native Host 进程内,
还是通过本地 Provider Socket 接入。
action 清单直接复用 @mearl/browser-core/browserActionProtocol 的能力动作合同,不维护独立生成副本。
核心模型
默认由 Mearl 管理控制会话。Provider 继续接收目标实例、页面与普通 action,无需处理控制会话 ID、资源归属或人工交接状态。带 --session 的释放只结束 Mearl 任务控制;实例关闭、退出接管与 Cookie 同步仍使用 Provider 的生命周期方法。上传与下载分别按 capabilities.actions 中的 page_upload、page_download 声明使用。
普通 action 响应中的 session 由 Mearl 附加,只包含路由与控制状态;browser_control 交接时还包含会话页面清单。page_snapshot 的文本 ref 可直接用于后续操作,Provider 应在自身运行时保存其定位映射,不在响应中输出内部节点 ID 字典。
page_upload 接收执行宿主上的 filePaths,Mearl 负责中转调用方文件与浏览器产物。page_download 接收 { tabId, trigger, url?, timeout? };Provider 先监听下载,再执行点击,等待文件完成后返回 ProviderDownloadResult:{ filename, filePath, action, owned? }。filePath 是本地 Provider 进程宿主上的绝对路径,云端文件由 Provider 取回;action 是点击结果,owned:true 允许 Mearl 在导入后删除该中转文件。Mearl 将结果转为统一的浏览器产物,支持调用方保存、后续上传与会话清理。下载超时或来源不确定时返回错误,不重复点击。
Provider v2 分开表达实例状态与控制方式:
status:实例处于connected、pairing、running_disconnected或stopped。control:页面操作由 Provider 自己处理、由宿主的 Extension/CDP 处理,或当前没有控制通道。
实例还会声明 ownership、persistence、当前允许的资源 operations、稳定
references 和可公开展示的 details。因此 Provider identity 不再等同于控制传输,持久化实例
也不需要伪装成已连接浏览器。
外部 Provider 安装
外部 Provider 包将 @mearl/provider 声明为运行依赖:
pnpm add @mearl/provider
包的 package.json#mearl.provider 声明协议入口、浏览器类型和结构化启动参数:
{
"name": "@example/mearl-provider-device",
"version": "1.0.0",
"type": "module",
"mearl": {
"provider": {
"providerId": "example-device",
"name": "Example device provider",
"protocolVersion": 3,
"entry": "./dist/runtime.js",
"browserType": {
"name": "Device",
"help": ["Pair a device, then use its browserId with Mearl actions."],
"inputSchema": {
"type": "object",
"properties": {
"code": {
"type": "string",
"title": "Pairing code",
"placeholder": "Generated by default"
}
}
}
}
}
}
}
Schema 支持 string、number、boolean、array 和递归 object;Provider 配置也可通过
browserType.configuration.inputSchema 声明。browserType.help 由 mearl <providerId> 和
mearl <providerId> --help 展示,让 Provider 的操作说明随独立包版本演进。同名 Skill 插件已注册时,
CLI 会在其后自动追加插件命令摘要,Provider 不需要在 browserType.help 中重复维护命令清单。
local 是内置保留 ID。
Provider 可在浏览器类型、表单字段、选项、连接方式和动态表单提示上通过 locales 声明
多语言文案。例如,字段可设置 title: "任务名称" 和 locales: { en: { title: "Task name" } }。
扩展依次按地区语言(如 zh_CN)、基础语言(如 zh)和字段默认值选择文案。
实例引用可通过 label 和 locales 声明详情标签,通过 summary: true 在折叠行展示标识。
最小外部运行时
import { createProviderServer, type BrowserProviderDefinition } from '@mearl/provider';
const definition: BrowserProviderDefinition = {
descriptor: {
providerId: 'example-device',
name: 'Example device provider',
version: '1.0.0',
},
browserType: {
name: 'Device',
inputSchema: {
type: 'object',
properties: { code: { type: 'string', title: 'Pairing code' } },
},
},
launch: async options => createDevice(String(options.code ?? '')),
close: async ({ browserId }) => removeDevice(browserId),
handleAction: async ({ browserId, action, data }) =>
dispatchDeviceAction(browserId, action, data),
};
const server = createProviderServer({ definition });
await server.start();
server.updateBrowsers([
{
browserId: 'device-1',
name: 'Example device',
status: 'connected',
control: {
kind: 'provider',
capabilities: {
actions: ['tab_list', 'page_snapshot', 'page_click'],
screenshot: 'none',
},
},
ownership: 'owned',
persistence: 'persistent',
operations: ['close'],
},
]);
配对中的设备使用 status: "pairing" 并提供 pairing;停止的持久化实例使用
status: "stopped" 和 control: { kind: "none" }。运行但尚未接通控制链路时使用
status: "running_disconnected"。
Runtime 会统一校验 schema、状态机、操作能力和页面 action capability,校验通过后才调用实现。
capabilities.actions 应明确列出适配器已经实现并验证的动作,避免通过全量动作减排除项生成清单,使 SDK 新增动作被误报为可用。适配器转发请求时,只有能证明请求尚未发送的连接失败才可自动重连重试;已经发送、响应丢失或业务执行失败的动作不应自动重放。
start、detach、getAccess、syncCookies 等可选实现必须与实例声明的 operation 对应;
这些生命周期方法由 Provider runtime 协调,浏览器动作使用统一的 Mearl 协议。
公共 browser_launch 对所有 Provider 接收 { provider, providerOptions },返回与
browser_list.browsers[] 相同的 ManagedBrowserInfo(对象形式的 Provider identity、状态、
持久化策略、操作和安全元数据)。管理器内部字段、控制连接和临时访问地址不进入该结果。
Local 的 CLI 顶层参数是便捷输入;外部 Provider 参数必须放在 providerOptions,两者不得混用。
内置浏览器保留本地启动交互;外部 Provider 根据 browserType.inputSchema 生成输入项。
两者提交同一结构的 providerOptions。
Schema 的顶层 oneOf 可声明多个完整对象约束,
例如创建与接管会话;每个分支用 title 命名,Runtime 要求输入恰好匹配一个分支,
not 可排除不允许的字段组合。default 是表单初始值,不会隐式修改调用方参数。
对象字段 copyCookieDomains 约定使用 { presets?, domains? } Cookie 范围结构。
为这个字段声明 component: "cookie-sync" 后,Mearl 浏览器管理器会使用统一的 Cookie
范围选择组件,并在提交前申请所选站点权限;未声明组件时仍按普通 object 字段渲染。
Provider 可用 component: "switch" 渲染紧凑布尔开关;formHidden 隐藏仅供 CLI/API
使用的高级字段,formWidth 控制字段宽度,formDefault 设置不影响运行时默认值的表单初值,
formHideOptional 只隐藏可选标识而不改变校验,formVisibleWhen 根据另一个字段是否为空控制显示与提交。
browserType.documentationUrl 声明 HTTP(S) 文档地址;configuration.inputSchema 声明配置参数,
配置只传给所选 Provider 的 configure,不存入页面本地存储。
Provider 需要查询设备池、已有会话或动态候选项时,声明 browserType.dynamicLaunchForm: true
并实现只读的 getLaunchForm({ variant?, values }, context)。
browserType.launchForm 可提供无网络依赖的初始表单,弹窗立即渲染其字段和模式;
各 variants[] 可用 inputSchema/values/refreshOn/dynamicFields 覆盖该模式的初值。
dynamicFields 指定异步查询期间需要禁用的字段,其余字段与模式选择保持可操作;加载状态在表单状态行显示。
初次查询携带静态表单默认值;查询只传入可解析的字段,最终启动仍严格校验全部输入。
用户在等待期间输入的内容会保留,切换模式或环境会使旧请求失效。
管理器通过内部
browser_providers 的 launch_form operation 获取 ProviderLaunchForm,不识别 Provider ID 或业务字段:
inputSchema描述当前模式的用户输入,required决定必填标识与提交校验。variants的id/title/description描述连接方式,variant指定当前方式。values是 Provider 给出的固定启动参数,提交时与用户输入合并;只包含非敏感参数。- 字符串字段的
options使用{ value, label, description?, disabled? }描述候选项。 禁用项只供展示;运行时的最终资源状态由 Provider 的launch再次核验。 refreshOn指定需要重新查询表单的字段;字段变化会清空其他选项字段的选择。 刷新时保留已有输入,选项字段仅保留仍有效的选择;切换模式重新使用该模式默认值。message展示查询结果,blocked阻止无资源、无有效会话等情况下的提交。
查询过程不得创建或租用资源,也不得返回凭据。Runtime 会校验返回的 schema、候选项、
模式与固定参数。最终 launch 仍使用 browserType.inputSchema 校验,动态表单不改变 CLI/API
参数契约。未声明动态表单的 Provider 继续使用静态 schema。
开发联调时,Provider 应链接包含该公开契约的本地 @mearl/provider 构建;分发时需配套发布
包含动态表单能力的 SDK、Mearl 宿主与 Provider。
details 中的通用键具有固定类型:headless 为布尔值,createdAt 为可解析时间字符串,
cdpPort 为有效端口,account 包含 nickname 和可选的字符串 accountId/loginId,
copiedCookies 包含字符串数组 domains 和非负整数 count。其余键允许非敏感 JSON,界面同样展示。
references 的 kind 表示资源类别,id 为不透明标识,label 为展示名;
通用类别包括 profile/session/image/context/context-name,也允许 Provider 自定义类别。
内置运行时
Native Host 使用 createProviderRuntime(definition) 直接运行内置 Local Provider,不需要创建 Socket
或注册安装记录。内置与外部实现共享同一份 schema 校验、状态机和生命周期派发逻辑;需要宿主服务的进程内调用可通过 ProviderContext.host 注入,Provider 合同本身不依赖 Extension、Native Messaging 或
具体浏览器服务 SDK。
指定 Provider 的启动、配置和动态表单查询只启动该 Provider;完整浏览器发现仍检查所有已安装 Provider。Mearl 的 Provider 控制宿主按调用方绝对期限管理排队及后续动作,旧调用方使用按动作推导的等待时间,持久化关闭和长时间等待不受固定两分钟限制。适配器自己的转发链路也应覆盖对应动作的时间预算,并在等待失败时保留执行结果不确定的语义。
外部 Provider 的安装、发现与启动仍由 @mearl/setup 管理:
npx @mearl/setup provider install <package>
npx @mearl/setup provider update <provider-id>
npx @mearl/setup provider uninstall <provider-id>
Socket 协议
当前 Provider 协议版本为 3,请求 envelope 携带 protocolVersion。安装元数据、
运行时 descriptor、运行中进程与调用方必须使用相同协议;不匹配时更新相关包并重启进程。
browser_providers:operation: "configure"携带options,operation: "launch_form"携带variant?和values。browser_instances:operation: "start" | "detach" | "sync_cookies";同步携带scope,目标实例由 envelope 的browserId选择。browser_launch、browser_close、get_browser_access_url保留各自职责。browser_sessions由 Mearl 控制进程处理,不转交外部 Provider;会话释放与实例detach分开执行。
SDK 负责将 Socket 请求分发到 configure/getLaunchForm/start/detach/syncCookies 等方法。
browser_release 普通 action 不承担实例解绑;公开 CLI 的无会话释放由 Mearl 转换为 detach。
打包 SDK 的 Provider 必须更新 SDK 并重新构建,修改安装元数据不能替换运行中的旧代码。
外部任务控制与页面增强
已自行管理任务归属、人工交接的 Provider,可在 browserType.controlSessionModes 中声明
["external"],并在实例的 control.capabilities 中返回 controlSessionMode: "external"。
SDK 在服务启动和操作前确认 Mearl 控制宿主支持此模式;旧宿主会明确拒绝。
同一运行时内实例的控制模式固定。该模式不接受 Mearl --session,页面归属和交接仍由外部工具负责。
未声明该字段的 Provider 保持原有行为。
支持统一交接入口时,实现 BrowserProviderDefinition.control(request, context),并在实例的 operations 中声明 handoff、resume、finish 中实际支持的操作。Mearl 将 browser_control 转发给该方法,不创建 Mearl 会话。请求包含 Provider 内的 browserId、operation,以及可选的 tabId、reason;只有 finish 可带 keepTabIds。返回 { controlSessionMode: "external", operation, control },其中 control 分别为 user、agent、finished。Provider 必须校验控制权和保留页归属,拒绝不支持的参数,并让交接预检在页面操作等待期间仍可响应。只有用户明确交还后才可 resume。
外部 Provider 可用 createProviderEnhancementClient(providerId, browserId) 为已获准页面绑定 Mearl
Extension 的环境增强,依次调用 bind、invoke、release。这条通道只允许设备、App、时区类增强,
具体范围取决于 Extension 返回的能力;当前 Extension 实现仅开放时区。
它沿用域授权和现有页面控制冲突检查,不登记自动化页面,不提供普通页面操作、任意代码执行或关闭权限。
独立 Provider 包使用 @mearl/provider-<id> 命名。EgoLite 的独立 @mearl/provider-ego 使用外部控制模式,经 Ego 原生通道管理 Space、页面和人工交接;设备和 App 模拟不依赖浏览器扩展。安装及能力限制以 Provider 自身说明为准。
包元数据 mearl.provider.extensionAgentDefault.macBundleIds 可声明浏览器应用的精确 Bundle ID 列表,例如 ["com.example.browser"]。支持该协议的 macOS Native Host 仅匹配启动它的浏览器;命中时扩展默认关闭 Agent 接入,用户的本地明确选择优先。声明随 Provider 注册保存,变更后需重新注册;它不依赖 Provider 进程在线,也不代表 Mearl 获得任何 Space 控制权。不要为通用 Chrome 等应用声明独占默认值,除非该 Provider 确实承担该浏览器所有 Agent 接入。
实现必须提供只读的 getEnhancementTarget({ browserId, tabId }, context):页面由用户控制时返回
{ tabId, ready: false };确认 Agent 可操作时返回
{ tabId, ready: true, mainFrameId, url },也可用当前物理 targetId 代替 mainFrameId。
身份必须来自当前浏览器目标;提供两项时,当前宿主同时核验两项。仅提供 target ID 时,需要宿主具备相应的目标核验能力。
URL 仅用于缩小候选范围,不能单独证明双方是同一页面。
此回调和 listBrowsers 必须能在 handleAction 等待增强结果期间重入,不应获取该操作持有的锁或认领用户页面。
调用可传入原请求的绝对 deadline,它不属于业务 payload。绑定句柄有页面、连接代次和动作范围限制;
进程重启或页面身份变化后需要重新核验。变更响应丢失后,后续写入会被隔离,不能自动重放或改走普通 action。
显式重新绑定只完成核验;无法确认的环境仍须由执行宿主处理恢复冲突。
已安装 App 资源
外部控制 Provider 可通过 createProviderAppResourceClient(providerId, browserId) 读取本机已接受的
App 定义与运行时。catalog(params?, deadline?) 只返回目录摘要;
resolve({providerTabId, profile, options?}, deadline?) 在重新核验页面控制和身份后返回编译配置、
能力/安装 revision,以及必要运行时的确切 binding 与源码。它不执行页面安装或业务宿主服务。
资源由完整 Native 能力服务解析,Skill 目录读取已注册快照;不会为发现目录重新执行 Skill 源文件。
资源协议声明只表示读取口存在;显式读取时才刷新和校验安装,能力模块的慢初始化或失败不阻塞普通 Provider 控制宿主启动。
含桥或全局变量且未显式指定运行时的端模拟配置,沿用已安装的 app.defaultPageRuntime;缺少运行时直接拒绝,
不将其降级为已完整准备的 UA 配置。SDK 会再次核验定义 revision、页面证明和源码 SHA-256。
独立的资源消息上限为 16 MiB,以容纳合法运行时的 JSON 编码;普通增强通道仍限 1 MiB。
Provider 应在自己的实例/页面范围中保存并引用已核验代码,保持原有资源配额与清理规则; Space 清单不等于整个物理浏览器的目标清单,不能据此回收别人的资源。 获得资源不代表后端已支持其根容器、桥回调或真实授权,只有逐项验证后才声明对应 App 能力。 旧宿主缺少资源协议声明时明确拒绝,不回退到普通 action 或任意 Node 执行。