@mearl/provider 2.14.0 → 2.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @mearl/provider
2
2
 
3
- `@mearl/provider` 是 Mearl 的统一浏览器 Provider 契约与运行时。内置的 Local Chrome、AgentBay
4
- 和外部 Provider 使用相同的 `BrowserProviderDefinition`;区别只在于运行时位于 Native Host
5
- 进程内,还是通过本地 Provider Socket 接入。
3
+ `@mearl/provider` 是 Mearl 的统一浏览器 Provider 契约与运行时。内置 Local Chrome 和外部
4
+ Provider 使用相同的 `BrowserProviderDefinition`;区别只在于运行时位于 Native Host 进程内,
5
+ 还是通过本地 Provider Socket 接入。
6
6
 
7
7
  ## 核心模型
8
8
 
@@ -38,6 +38,7 @@ pnpm add @mearl/provider
38
38
  "entry": "./dist/runtime.js",
39
39
  "browserType": {
40
40
  "name": "Device",
41
+ "help": ["Pair a device, then use its browserId with Mearl actions."],
41
42
  "inputSchema": {
42
43
  "type": "object",
43
44
  "properties": {
@@ -55,7 +56,8 @@ pnpm add @mearl/provider
55
56
  ```
56
57
 
57
58
  Schema 支持 string、number、boolean、array 和递归 object;Provider 配置也可通过
58
- `browserType.configuration.inputSchema` 声明。`local` `agentbay` 是内置保留 ID。
59
+ `browserType.configuration.inputSchema` 声明。`browserType.help` `mearl <providerId>`
60
+ `mearl <providerId> --help` 展示,让 Provider 的操作说明随独立包版本演进。`local` 是内置保留 ID。
59
61
 
60
62
  ## 最小外部运行时
61
63
 
@@ -112,14 +114,19 @@ Runtime 会统一校验 schema、状态机、操作能力和页面 action capabi
112
114
  公共 `browser_launch` 对所有 Provider 接收 `{ provider, providerOptions }`,返回与
113
115
  `browser_list.browsers[]` 相同的 `ManagedBrowserInfo`(对象形式的 Provider identity、状态、
114
116
  持久化策略、操作和安全元数据)。管理器内部字段、控制连接和临时访问地址不进入该结果。
115
- Local/AgentBay 的 CLI 顶层参数是便捷输入,由协调层转换;不得与 `providerOptions` 混用。
117
+ Local 的 CLI 顶层参数是便捷输入;外部 Provider 参数必须放在 `providerOptions`,两者不得混用。
116
118
 
117
- 内置浏览器保留针对本地启动、云端创建和接管的专用交互;外部 Provider 根据
118
- `browserType.inputSchema` 生成输入项。两者提交同一结构的 `providerOptions`。
119
+ 内置浏览器保留本地启动交互;外部 Provider 根据 `browserType.inputSchema` 生成输入项。
120
+ 两者提交同一结构的 `providerOptions`。
119
121
  Schema 的顶层 `oneOf` 可声明多个完整对象约束,
120
122
  例如创建与接管会话;每个分支用 `title` 命名,Runtime 要求输入恰好匹配一个分支,
121
123
  `not` 可排除不允许的字段组合。`default` 是表单初始值,不会隐式修改调用方参数。
122
- 对象字段 `copyCookieDomains` 约定使用 `{ presets?, domains? }` Cookie 范围结构;内置浏览器表单保留范围选择与授权交互。
124
+ 对象字段 `copyCookieDomains` 约定使用 `{ presets?, domains? }` Cookie 范围结构。
125
+ 为这个字段声明 `component: "cookie-sync"` 后,Mearl 浏览器管理器会使用统一的 Cookie
126
+ 范围选择组件,并在提交前申请所选站点权限;未声明组件时仍按普通 object 字段渲染。
127
+ Provider 可用 `component: "switch"` 渲染紧凑布尔开关;`formHidden` 隐藏仅供 CLI/API
128
+ 使用的高级字段,`formWidth` 控制字段宽度,`formDefault` 设置不影响运行时默认值的表单初值,
129
+ `formHideOptional` 只隐藏可选标识而不改变校验,`formVisibleWhen` 根据另一个字段是否为空控制显示与提交。
123
130
  `browserType.documentationUrl` 声明 HTTP(S) 文档地址;`configuration.inputSchema` 声明配置参数,
124
131
  配置只传给所选 Provider 的 `configure`,不存入页面本地存储。
125
132
 
@@ -131,9 +138,9 @@ Schema 的顶层 `oneOf` 可声明多个完整对象约束,
131
138
 
132
139
  ## 内置运行时
133
140
 
134
- Native Host 使用 `createProviderRuntime(definition)` 直接运行内置 Provider,不需要创建 Socket
141
+ Native Host 使用 `createProviderRuntime(definition)` 直接运行内置 Local Provider,不需要创建 Socket
135
142
  或注册安装记录。内置与外部实现共享同一份 schema 校验、状态机和生命周期派发逻辑;需要宿主服务的进程内调用可通过 `ProviderContext.host` 注入,Provider 合同本身不依赖 Extension、Native Messaging 或
136
- AgentBay SDK。
143
+ 具体浏览器服务 SDK。
137
144
 
138
145
  外部 Provider 的安装、发现与启动仍由 `@mearl/setup` 管理:
139
146
 
@@ -1,3 +1,3 @@
1
- export declare const PROVIDER_ACTIONS: readonly ["tab_checkpoint", "capture_checkpoint", "get_requests", "get_logs", "get_events", "get_api_schema", "set_mock", "get_mocks", "set_rule", "get_rules", "send_request", "send_mtop_request", "tdbank_account", "browser_release", "page_screenshot", "page_selected_element", "tab_open", "tab_close", "tab_list", "page_click", "page_drag", "page_type", "page_scroll", "page_hover", "page_eval", "page_snapshot", "page_press", "page_wait", "page_navigate", "page_upload", "page_frames", "set_device_emulation", "set_app_profile", "set_timezone", "get_cookie", "set_cookie", "record_start", "record_stop", "request_domain_permission", "get_user_info", "run_actions"];
1
+ export declare const PROVIDER_ACTIONS: readonly ["tab_checkpoint", "capture_checkpoint", "get_requests", "get_logs", "get_events", "set_mock", "get_mocks", "set_rule", "get_rules", "send_request", "send_mtop_request", "browser_release", "page_screenshot", "page_selected_element", "tab_open", "tab_close", "tab_list", "page_click", "page_drag", "page_type", "page_scroll", "page_hover", "page_eval", "page_snapshot", "page_press", "page_wait", "page_navigate", "page_upload", "page_frames", "set_device_emulation", "set_app_profile", "set_timezone", "get_cookie", "set_cookie", "record_start", "record_stop", "request_domain_permission", "run_actions"];
2
2
  export type ProviderAction = (typeof PROVIDER_ACTIONS)[number];
3
3
  export declare function isProviderAction(value: unknown): value is ProviderAction;
@@ -6,14 +6,12 @@ export const PROVIDER_ACTIONS = [
6
6
  'get_requests',
7
7
  'get_logs',
8
8
  'get_events',
9
- 'get_api_schema',
10
9
  'set_mock',
11
10
  'get_mocks',
12
11
  'set_rule',
13
12
  'get_rules',
14
13
  'send_request',
15
14
  'send_mtop_request',
16
- 'tdbank_account',
17
15
  'browser_release',
18
16
  'page_screenshot',
19
17
  'page_selected_element',
@@ -40,7 +38,6 @@ export const PROVIDER_ACTIONS = [
40
38
  'record_start',
41
39
  'record_stop',
42
40
  'request_domain_permission',
43
- 'get_user_info',
44
41
  'run_actions',
45
42
  ];
46
43
  const providerActionSet = new Set(PROVIDER_ACTIONS);
@@ -35,7 +35,20 @@ export function isProviderInputSchema(value, depth = 0) {
35
35
  const schema = value;
36
36
  if (!isOptionalString(schema.title) ||
37
37
  !isOptionalString(schema.description) ||
38
- (schema.default !== undefined && !isJsonValue(schema.default))) {
38
+ (schema.component !== undefined &&
39
+ !['cookie-sync', 'switch'].includes(String(schema.component))) ||
40
+ (schema.default !== undefined && !isJsonValue(schema.default)) ||
41
+ (schema.formDefault !== undefined && !isJsonValue(schema.formDefault)) ||
42
+ (schema.formHidden !== undefined && typeof schema.formHidden !== 'boolean') ||
43
+ (schema.formWidth !== undefined && !['half', 'full'].includes(String(schema.formWidth))) ||
44
+ (schema.formHideOptional !== undefined && typeof schema.formHideOptional !== 'boolean') ||
45
+ (schema.formVisibleWhen !== undefined &&
46
+ (!schema.formVisibleWhen ||
47
+ typeof schema.formVisibleWhen !== 'object' ||
48
+ Array.isArray(schema.formVisibleWhen) ||
49
+ typeof schema.formVisibleWhen.field !== 'string' ||
50
+ !schema.formVisibleWhen.field ||
51
+ typeof schema.formVisibleWhen.empty !== 'boolean'))) {
39
52
  return false;
40
53
  }
41
54
  if (schema.type === 'string') {
@@ -98,6 +111,12 @@ export function isProviderInputSchema(value, depth = 0) {
98
111
  if (!Object.values(properties).every(item => isProviderInputSchema(item, depth + 1))) {
99
112
  return false;
100
113
  }
114
+ if (Object.values(properties).some(item => {
115
+ const condition = item.formVisibleWhen;
116
+ return condition !== undefined && !(String(condition.field) in properties);
117
+ })) {
118
+ return false;
119
+ }
101
120
  if (schema.not !== undefined &&
102
121
  (!isProviderInputSchema(schema.not, depth + 1) || schema.not.type !== 'object'))
103
122
  return false;
@@ -113,6 +132,10 @@ export function isProviderInputSchema(value, depth = 0) {
113
132
  return false;
114
133
  }
115
134
  }
135
+ if (schema.component === 'cookie-sync' && schema.type !== 'object')
136
+ return false;
137
+ if (schema.component === 'switch' && schema.type !== 'boolean')
138
+ return false;
116
139
  if (schema.default !== undefined) {
117
140
  try {
118
141
  validateSchemaValue(value, schema.default, 'default');
@@ -121,6 +144,14 @@ export function isProviderInputSchema(value, depth = 0) {
121
144
  return false;
122
145
  }
123
146
  }
147
+ if (schema.formDefault !== undefined) {
148
+ try {
149
+ validateSchemaValue(value, schema.formDefault, 'formDefault');
150
+ }
151
+ catch {
152
+ return false;
153
+ }
154
+ }
124
155
  return true;
125
156
  }
126
157
  function fail(path, message) {
@@ -326,11 +357,6 @@ export function validateProviderBrowser(browser) {
326
357
  !Number.isFinite(Date.parse(details.createdAt)))) ||
327
358
  (details.cdpPort !== undefined &&
328
359
  (!Number.isInteger(details.cdpPort) || details.cdpPort < 1 || details.cdpPort > 65535)) ||
329
- (details.account !== undefined &&
330
- (!details.account ||
331
- typeof details.account.nickname !== 'string' ||
332
- !isOptionalString(details.account.accountId) ||
333
- !isOptionalString(details.account.loginId))) ||
334
360
  (details.copiedCookies !== undefined &&
335
361
  (!details.copiedCookies ||
336
362
  !strings(details.copiedCookies.domains) ||
package/dist/registry.js CHANGED
@@ -11,9 +11,14 @@ export const PROVIDER_SOCKETS_SUBDIR = 'providers/sockets';
11
11
  export const PROVIDER_DATA_SUBDIR = 'providers/data';
12
12
  export const BROWSERS_SUBDIR = 'browsers';
13
13
  export const PROVIDER_START_TOKEN_ENV = 'MEARL_PROVIDER_START_TOKEN';
14
- const RESERVED_PROVIDER_IDS = new Set(['local', 'agentbay']);
14
+ const RESERVED_PROVIDER_IDS = new Set(['local']);
15
15
  export function providerBrowserKey(providerId, browserId) {
16
- return `provider:${encodeURIComponent(providerId)}:${encodeURIComponent(browserId)}`;
16
+ const redundantPrefix = `managed:${providerId}:`;
17
+ const scopedBrowserId = browserId.toLowerCase().startsWith(redundantPrefix.toLowerCase())
18
+ ? browserId.slice(redundantPrefix.length)
19
+ : browserId;
20
+ const encodedBrowserId = encodeURIComponent(scopedBrowserId).replace(/%3A/gi, ':');
21
+ return `provider:${encodeURIComponent(providerId)}:${encodedBrowserId}`;
17
22
  }
18
23
  export function providerSocketPath(providerId) {
19
24
  const suffix = hashKey(providerId);
@@ -144,6 +149,10 @@ function isProviderBrowserType(value) {
144
149
  (typeof browserType.documentationUrl !== 'string' ||
145
150
  !/^https?:\/\//.test(browserType.documentationUrl)))
146
151
  return false;
152
+ if (browserType.help !== undefined &&
153
+ (!Array.isArray(browserType.help) ||
154
+ browserType.help.some(item => typeof item !== 'string' || !item.trim())))
155
+ return false;
147
156
  if (!isProviderObjectSchema(browserType.inputSchema))
148
157
  return false;
149
158
  return (browserType.configuration === undefined ||
package/dist/server.js CHANGED
@@ -12,6 +12,15 @@ const activeProviderIds = new Set();
12
12
  function errorMessage(error) {
13
13
  return error instanceof Error ? error.message : String(error);
14
14
  }
15
+ function isProviderHostContextData(value) {
16
+ if (!value || typeof value !== 'object' || Array.isArray(value))
17
+ return false;
18
+ const host = value;
19
+ return ((host.userAgent === undefined || typeof host.userAgent === 'string') &&
20
+ (host.cookies === undefined ||
21
+ (Array.isArray(host.cookies) &&
22
+ host.cookies.every(cookie => cookie && typeof cookie === 'object' && !Array.isArray(cookie)))));
23
+ }
15
24
  function isProviderActionRequest(value) {
16
25
  if (!value || typeof value !== 'object' || Array.isArray(value))
17
26
  return false;
@@ -26,7 +35,30 @@ function isProviderActionRequest(value) {
26
35
  !Array.isArray(request.data))) &&
27
36
  (request.browserId === undefined || typeof request.browserId === 'string') &&
28
37
  (request.version === undefined || typeof request.version === 'string') &&
29
- (request.controlSource === undefined || typeof request.controlSource === 'string'));
38
+ (request.controlSource === undefined || typeof request.controlSource === 'string') &&
39
+ (request.host === undefined || isProviderHostContextData(request.host)));
40
+ }
41
+ function providerContext(request) {
42
+ const hostData = request.host;
43
+ return {
44
+ controlSource: request.controlSource,
45
+ ...(hostData
46
+ ? {
47
+ host: {
48
+ async request(method) {
49
+ if (method === 'export_cookies')
50
+ return (hostData.cookies ?? []);
51
+ if (method === 'get_browser_user_agent') {
52
+ if (!hostData.userAgent)
53
+ throw new Error('Host browser user agent is unavailable');
54
+ return hostData.userAgent;
55
+ }
56
+ throw new Error(`Unsupported Provider host service: ${method}`);
57
+ },
58
+ },
59
+ }
60
+ : {}),
61
+ };
30
62
  }
31
63
  export function createProviderServer(options) {
32
64
  const runtime = createProviderRuntime(options.definition);
@@ -49,24 +81,25 @@ export function createProviderServer(options) {
49
81
  throw new Error('browserId is required when the provider has multiple browsers');
50
82
  }
51
83
  async function dispatch(request) {
84
+ const context = providerContext(request);
52
85
  if (request.action === 'browser_launch') {
53
86
  if (request.browserId)
54
87
  throw new Error('browser_launch does not accept browserId');
55
- return runtime.launch(request.data ?? {}, { controlSource: request.controlSource });
88
+ return runtime.launch(request.data ?? {}, context);
56
89
  }
57
90
  if (request.action === 'browser_provider_configure') {
58
91
  if (request.browserId)
59
92
  throw new Error('Provider configuration does not accept browserId');
60
- return runtime.configure(request.data ?? {}, { controlSource: request.controlSource });
93
+ return runtime.configure(request.data ?? {}, context);
61
94
  }
62
95
  if (request.action === 'browser_manager_start') {
63
- return runtime.start({ browserId: await providerBrowserId(request.browserId) }, { controlSource: request.controlSource });
96
+ return runtime.start({ browserId: await providerBrowserId(request.browserId) }, context);
64
97
  }
65
98
  if (request.action === 'browser_manager_sync_cookies') {
66
99
  return runtime.syncCookies({
67
100
  browserId: await providerBrowserId(request.browserId),
68
101
  scope: request.data?.scope,
69
- }, { controlSource: request.controlSource });
102
+ }, context);
70
103
  }
71
104
  if (request.action !== 'get_versions' &&
72
105
  request.action !== 'browser_close' &&
@@ -76,11 +109,11 @@ export function createProviderServer(options) {
76
109
  }
77
110
  const browserId = await providerBrowserId(request.browserId);
78
111
  if (request.action === 'browser_close') {
79
- return runtime.close({ browserId, deleteData: request.data?.deleteProfile === true }, { controlSource: request.controlSource });
112
+ return runtime.close({ browserId, deleteData: request.data?.deleteProfile === true }, context);
80
113
  }
81
114
  if (request.action === 'get_browser_access_url') {
82
115
  return {
83
- browserAccess: await runtime.getAccess({ browserId }, { controlSource: request.controlSource }),
116
+ browserAccess: await runtime.getAccess({ browserId }, context),
84
117
  };
85
118
  }
86
119
  if (request.action === 'get_versions') {
@@ -101,7 +134,7 @@ export function createProviderServer(options) {
101
134
  if (request.action === 'browser_release') {
102
135
  const found = (await runtime.listBrowsers()).find(browser => browser.browserId === browserId);
103
136
  if (found?.operations.includes('detach')) {
104
- return runtime.detach({ browserId }, { controlSource: request.controlSource });
137
+ return runtime.detach({ browserId }, context);
105
138
  }
106
139
  }
107
140
  touchProviderBrowser(providerBrowserKey(descriptor.providerId, browserId));
package/dist/types.d.ts CHANGED
@@ -24,10 +24,27 @@ export interface ProviderDescriptor {
24
24
  protocolVersion?: number;
25
25
  builtin?: boolean;
26
26
  }
27
+ export type ProviderInputComponent = 'cookie-sync' | 'switch';
28
+ export interface ProviderInputVisibility {
29
+ field: string;
30
+ empty: boolean;
31
+ }
27
32
  interface ProviderSchemaBase {
28
33
  title?: string;
29
34
  description?: string;
30
35
  default?: JsonValue;
36
+ /** Optional Mearl form component used to edit this schema value. */
37
+ component?: ProviderInputComponent;
38
+ /** Presentation-only default used by Mearl forms without changing runtime semantics. */
39
+ formDefault?: JsonValue;
40
+ /** Excludes an advanced runtime option from generated Mearl forms. */
41
+ formHidden?: boolean;
42
+ /** Places compact fields side by side in generated Mearl forms. */
43
+ formWidth?: 'half' | 'full';
44
+ /** Hides the optional badge without changing schema validation. */
45
+ formHideOptional?: boolean;
46
+ /** Conditionally displays and submits this field based on another form value. */
47
+ formVisibleWhen?: ProviderInputVisibility;
31
48
  }
32
49
  export interface ProviderStringSchema extends ProviderSchemaBase {
33
50
  type: 'string';
@@ -68,6 +85,8 @@ export interface ProviderConfigurationDefinition {
68
85
  export interface ProviderBrowserType {
69
86
  name: string;
70
87
  documentationUrl?: string;
88
+ /** Provider-owned usage guidance rendered by `mearl <providerId>`. */
89
+ help?: readonly string[];
71
90
  inputSchema: ProviderObjectSchema;
72
91
  configuration?: ProviderConfigurationDefinition;
73
92
  }
@@ -91,11 +110,6 @@ export type ProviderBrowserDetails = JsonObject & {
91
110
  headless?: boolean;
92
111
  createdAt?: string;
93
112
  cdpPort?: number;
94
- account?: {
95
- nickname: string;
96
- accountId?: string;
97
- loginId?: string;
98
- };
99
113
  copiedCookies?: {
100
114
  domains: string[];
101
115
  count: number;
@@ -143,6 +157,7 @@ export interface ProviderActionRequest {
143
157
  browserId?: string;
144
158
  version?: string;
145
159
  controlSource?: string;
160
+ host?: ProviderHostContextData;
146
161
  }
147
162
  export interface ProviderActionContext {
148
163
  browserId: string;
@@ -153,6 +168,11 @@ export interface ProviderActionContext {
153
168
  export interface ProviderHostServices {
154
169
  request<T = JsonValue>(method: string, data?: JsonObject): Promise<T>;
155
170
  }
171
+ /** Host-derived launch data carried over the local Provider protocol. */
172
+ export interface ProviderHostContextData {
173
+ cookies?: JsonObject[];
174
+ userAgent?: string;
175
+ }
156
176
  export interface ProviderContext {
157
177
  host?: ProviderHostServices;
158
178
  controlSource?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mearl/provider",
3
- "version": "2.14.0",
3
+ "version": "2.15.1",
4
4
  "description": "Unified contracts and runtime utilities for built-in and external Mearl browser providers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -35,7 +35,7 @@
35
35
  "registry": "https://registry.npmjs.org"
36
36
  },
37
37
  "dependencies": {
38
- "@mearl/daemon-core": "2.14.0"
38
+ "@mearl/daemon-core": "2.15.1"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^24.9.1",