On this page
@mearl/provider
@mearl/provider defines Mearl’s unified browser Provider contracts and runtime. Built-in Local Chrome and external
Providers share BrowserProviderDefinition; the runtime either runs inside Native Host
or connects through a local Provider Socket.
The action catalog directly reuses @mearl/browser-core/browserActionProtocol, without a separately generated copy.
Core model
Mearl manages control sessions by default. Providers receive target instances, pages, and ordinary actions, without handling session IDs, resource ownership, or human handoff state. Release with --session ends only Mearl task control; instance closure, detachment, and Cookie synchronization use Provider lifecycle methods. Upload and download require page_upload and page_download, respectively, in capabilities.actions.
Mearl appends session to ordinary action responses with routing and control state only; browser_control handoff also includes session pages. Text refs from page_snapshot are directly actionable. Providers retain locator mappings in their runtime rather than exposing internal node-ID dictionaries.
page_upload receives filePaths on the execution host; Mearl stages caller files and browser artifacts. page_download receives { tabId, trigger, url?, timeout? }: listen for downloads before clicking, wait for completion, then return ProviderDownloadResult: { filename, filePath, action, owned? }. filePath is absolute on the local Provider process host; Providers retrieve remote files. action is the click result; owned:true lets Mearl delete the staged file after import. Mearl converts it to a browser artifact for saving, later upload, and session cleanup. Return an error on timeout or uncertain provenance without clicking again.
Provider v2 separates instance status from control mode:
status:connected,pairing,running_disconnected, orstopped.control: Page actions are handled by the Provider, by the host’s Extension/CDP, or have no current control channel.
Instances also declare ownership, persistence, currently permitted resource operations, stable
references, and publicly displayable details. Provider identity is independent of control transport, and persistent instances
need not pretend to be connected browsers.
External Provider installation
External Provider packages declare @mearl/provider as a runtime dependency:
pnpm add @mearl/provider
package.json#mearl.provider declares the protocol entry, browser type, and structured launch arguments:
{
"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"
}
}
}
}
}
}
}
Schemas support string, number, boolean, array, and recursive object. Provider configuration can use
browserType.configuration.inputSchema. mearl <providerId> and
mearl <providerId> --help display browserType.help, so operation guidance evolves with the package version. If a same-name Skill plugin is registered,
the CLI appends a plugin command summary automatically; do not duplicate that list in browserType.help.
local is a reserved built-in ID.
Providers can declare UI translations in locales on the browser type, form fields, options,
launch variants, and dynamic form messages. For example, a field can set title: "任务名称"
and locales: { en: { title: "Task name" } }. The extension tries the regional locale
(such as zh_CN), then the base language (such as zh), then the default text.
Browser references can declare detail labels with label and locales, and use summary: true
to show the identifier in the collapsed row.
Minimal external runtime
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'],
},
]);
Pairing devices use status: "pairing" with pairing; stopped persistent instances use
status: "stopped" and control: { kind: "none" }. Running instances without connected control use
status: "running_disconnected"。
The runtime validates schemas, state machines, operations, and page-action capabilities before calling implementations.
capabilities.actions must explicitly list implemented and verified actions. Do not derive it from all actions minus exclusions, which could falsely advertise new SDK actions. Forwarding adapters may automatically reconnect and retry only if a connection failure proves the request was never sent. Sent requests with missing responses and business execution failures must not be replayed automatically.
Optional implementations such as start, detach, getAccess, and syncCookies must match declared instance operations;
the Provider runtime coordinates these lifecycle methods while browser actions use the unified Mearl protocol.
Public browser_launch accepts { provider, providerOptions } for all Providers and returns
the same ManagedBrowserInfo as browser_list.browsers[]: object-form Provider identity, state,
persistence policy, operations, and security metadata. Internal manager fields, control connections, and temporary access URLs are excluded.
Local top-level CLI arguments are convenience inputs. External Provider arguments must use providerOptions; the two cannot be mixed.
Built-in browsers retain local launch interactions; external Provider inputs are generated from browserType.inputSchema.
Both submit the same providerOptions structure.
Top-level schema oneOf can declare multiple complete object constraints,
such as creating or attaching sessions. Each branch has a title; the runtime requires input to match exactly one branch.
not excludes forbidden combinations. default initializes forms without implicitly modifying caller arguments.
Object field copyCookieDomains uses the { presets?, domains? } Cookie scope convention.
With component: "cookie-sync", the browser manager uses the shared Cookie
scope selector and requests selected-site permissions before submission. Without it, the field renders as an ordinary object.
component: "switch" renders compact boolean switches. formHidden hides advanced CLI/API-only
fields; formWidth controls width; formDefault initializes forms without changing runtime defaults;
formHideOptional hides only the optional marker, not validation; formVisibleWhen controls display and submission based on another field’s emptiness.
browserType.documentationUrl declares an HTTP(S) documentation URL. configuration.inputSchema declares configuration inputs,
passed only to the selected Provider’s configure, without page-local persistence.
Providers querying device pools, existing sessions, or dynamic options declare browserType.dynamicLaunchForm: true
and implement read-only getLaunchForm({ variant?, values }, context).
browserType.launchForm can supply an initial form without network access, rendering fields and modes immediately.
Each variants[] entry can override mode defaults through inputSchema/values/refreshOn/dynamicFields.
dynamicFields identifies fields disabled during asynchronous queries; other fields and mode selection remain usable. A form status row shows loading.
The initial query carries static defaults and sends only parseable fields; final launch still validates all inputs strictly.
Input entered while waiting is retained. Mode or environment changes invalidate previous requests.
The manager uses the internal
browser_providers launch_form operation to obtain ProviderLaunchForm, without recognizing Provider IDs or business fields:
inputSchemadescribes user inputs for the current mode;requiredcontrols required markers and submission validation.variantsusesid/title/descriptionfor connection modes, withvariantselecting the current mode.valuescontains fixed, non-sensitive Provider launch arguments, merged with user inputs on submission.- String-field
optionsuse{ value, label, description?, disabled? }. Disabled options are display-only. Providerlaunchrevalidates final resource state. refreshOnidentifies fields requiring a form query; changes clear selections in other option fields. Refresh retains inputs and still-valid option selections; switching modes reapplies that mode’s defaults.messagedisplays query results;blockedprevents submission without resources or valid sessions.
Queries must not create or rent resources or return credentials. The runtime validates returned schemas, options,
modes, and fixed arguments. Final launch still validates against browserType.inputSchema; dynamic forms do not change CLI/API
argument contracts. Providers without dynamic forms use static schemas.
For local integration, link a local @mearl/provider build containing this public contract. Distribution requires matching releases
of the SDK, Mearl host, and Provider supporting dynamic forms.
Common details keys have fixed types: headless is boolean, createdAt is a parseable timestamp,
cdpPort is a valid port, account contains nickname and optional string accountId/loginId,
and copiedCookies contains string-array domains and nonnegative integer count. Other keys may contain non-sensitive JSON and are also displayed.
references.kind identifies resource categories, id is opaque, and label is the display name.
Common categories include profile/session/image/context/context-name; Providers may define others.
Built-in runtime
Native Host runs Local directly with createProviderRuntime(definition), without a Socket
or installation record. Built-in and external implementations share schema validation, state machines, and lifecycle dispatch. In-process host services can use ProviderContext.host; Provider contracts do not depend on Extension, Native Messaging, or
specific browser service SDKs.
Launching, configuring, or querying dynamic forms for a specific Provider starts only that Provider; full discovery checks all installed Providers. Mearl’s control host manages queues and actions against caller absolute deadlines. Callers without deadlines use action-derived waits; persistent closure and long waits have no fixed two-minute limit. Adapter forwarding must cover the action’s time budget and preserve uncertain-execution semantics when waiting fails.
setup manages external Provider installation, discovery, and startup:
npx @mearl/setup provider install <package>
npx @mearl/setup provider update <provider-id>
npx @mearl/setup provider uninstall <provider-id>
Socket protocol
The current Provider protocol version is 3; request envelopes carry protocolVersion. Installation metadata,
runtime descriptors, running processes, and callers must match. Update the relevant packages and restart processes on mismatch.
browser_providers:operation: "configure"carriesoptions;operation: "launch_form"carriesvariant?andvalues.browser_instances:operation: "start" | "detach" | "sync_cookies"; synchronization carriesscope, and envelopebrowserIdselects the instance.browser_launch,browser_close, andget_browser_access_urlretain their separate responsibilities.- Mearl’s control process handles
browser_sessions, without forwarding to external Providers. Session release and instancedetachare separate.
The SDK dispatches Socket requests to methods such as configure/getLaunchForm/start/detach/syncCookies.
The ordinary browser_release action does not detach instances; Mearl converts public CLI release without a session to detach.
Providers bundling the SDK must update it and rebuild. Editing installation metadata cannot replace running code.
External task control and page enhancement
Providers managing their own task ownership and human handoffs can declare
["external"] in browserType.controlSessionModes and return controlSessionMode: "external" in instance control.capabilities.
The SDK checks Mearl host support at startup and before operations; unsupported hosts reject explicitly.
An instance’s control mode is fixed within a runtime. External mode rejects Mearl --session; external tools own page ownership and handoff.
Providers omitting this field retain default behavior.
To support unified handoff, implement BrowserProviderDefinition.control(request, context) and advertise the supported handoff, resume and finish operations on each instance. Mearl forwards browser_control without creating a Mearl session. Requests contain the Provider-local browserId, operation, optional tabId and reason; only finish accepts keepTabIds. Return { controlSessionMode: "external", operation, control }, where control is respectively user, agent or finished. Providers must validate control ownership and retained pages, reject unsupported parameters, and keep handoff preflight responsive while page operations wait. Resume only after the user explicitly returns control.
External Providers can bind Mearl Extension environment enhancement to authorized pages through
createProviderEnhancementClient(providerId, browserId), calling bind, invoke, and release. Only device, App, and time-zone enhancement is allowed,
as advertised by the Extension. The current Extension exposes only time zones.
The channel applies domain authorization and page-control conflict checks without registering automation pages or granting ordinary page actions, arbitrary code execution, or closure.
Independent packages use @mearl/provider-<id>. The independent @mearl/provider-ego uses external control mode and Ego’s native channel for Spaces, pages and human handoff. Its device and App emulation does not require a browser extension. Follow the Provider’s own installation instructions and capability limits.
Package metadata mearl.provider.extensionAgentDefault.macBundleIds accepts exact browser application Bundle IDs, such as ["com.example.browser"]. A supporting macOS Native Host matches only the browser that launched it. Matching extensions default to disabled Agent access; an explicit local user choice takes precedence. The declaration is captured at registration, so register again after changing it. It neither requires a running Provider process nor grants Space control. Do not declare general browsers such as Chrome unless the Provider owns all Agent access to that browser application.
Implement read-only getEnhancementTarget({ browserId, tabId }, context). When a user controls the page, return
{ tabId, ready: false }; when Agent control is confirmed, return
{ tabId, ready: true, mainFrameId, url }. A current physical targetId may replace mainFrameId.
Identity must come from the current browser target. When both are given, the host verifies both. Target-ID-only mode requires host target verification support.
URL narrows candidates but cannot alone prove page identity.
This callback and listBrowsers must be reentrant while handleAction awaits enhancement, without acquiring that operation’s lock or claiming user pages.
Calls may carry the original request’s absolute deadline, outside the business payload. Binding handles are scoped to pages, connection generations, and allowed actions;
process restarts or page identity changes require revalidation. After a mutation response is lost, later writes are isolated and cannot be replayed or rerouted through ordinary actions.
Explicit rebinding only revalidates identity; the execution host must resolve environment recovery conflicts that remain unconfirmed.
Installed App resources
External-control Providers can read locally accepted App definitions and runtimes using
createProviderAppResourceClient(providerId, browserId). catalog(params?, deadline?) returns catalog summaries only;
resolve({providerTabId, profile, options?}, deadline?) revalidates page control and identity, then returns compiled configuration,
capability/installation revisions, and exact runtime bindings and source. It does not install pages or execute business host services.
The full Native capability service resolves resources; Skill catalogs read registered snapshots without re-executing source files for discovery.
A resource protocol declaration only indicates an available reader. Explicit reads refresh and validate installation; slow or failed module initialization does not block ordinary Provider control-host startup.
Profiles containing bridges or globals without an explicit runtime use installed app.defaultPageRuntime. Missing runtimes are rejected,
without downgrading the profile into a fully prepared UA configuration. The SDK rechecks definition revisions, page proof, and source SHA-256.
The separate resource message limit is 16 MiB to accommodate JSON-encoded runtimes; ordinary enhancement remains limited to 1 MiB.
Providers store and reference verified code within their own instance/page scopes, preserving existing quotas and cleanup rules. A Space listing is not the full physical browser target list and cannot justify reclaiming others’ resources. Obtaining resources does not establish backend support for root containers, bridge callbacks, or real authorization. Advertise App capabilities only after individual verification. Hosts without the resource protocol declaration reject explicitly, without falling back to ordinary actions or arbitrary Node execution.