@mearl/client 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
@@ -63,15 +63,10 @@ npx @mearl/setup update --local
63
63
  # 查看某个 action 的帮助
64
64
  mearl <action> --help
65
65
 
66
- # 启动两个隔离的 headless Chrome,并分别登录不同 TDBank 账号
67
- mearl browser_launch --payload '{"name":"account-a","accountId":12345}'
68
- mearl browser_launch --payload '{"name":"account-b","query":"test_account"}'
66
+ # 启动两个隔离的 headless Chrome
67
+ mearl browser_launch --payload '{"name":"browser-a","url":"https://example.com"}'
68
+ mearl browser_launch --payload '{"name":"browser-b","persistent":true}'
69
69
  mearl browser_launch --payload '{"name":"cookie-copy","copyCookieDomains":["example.com"]}'
70
- mearl browser_launch --payload '{"name":"cloud-login","provider":"agentbay","persistent":true,"userAgentMode":"desktop","copyCookieDomains":["example.com"],"url":"https://example.com/account"}'
71
- mearl browser_launch --browser '<控制浏览器 browserId>' --payload '{"name":"cloud-login","provider":"agentbay","reuse":"require","copyCookieDomains":["example.com"]}'
72
- mearl browser_launch --browser '<本地来源 browserId>' --payload '{"sessionId":"<其他设备创建的 AgentBay sessionId>","copyCookieDomains":["example.com"]}'
73
- mearl browser_launch --payload '{"name":"cloud-tools","provider":"agentbay","copyCookieDomains":"mearl-services"}'
74
- mearl browser_launch --payload '{"name":"cloud-tools-plus","provider":"agentbay","copyCookieDomains":{"presets":["mearl-services"],"domains":["figma.com"]}}'
75
70
  mearl browser_launch --payload '{"name":"figma","userAgentMode":"desktop","copyCookieDomains":["figma.com"]}'
76
71
  mearl browser_list
77
72
  mearl browser_release
@@ -79,16 +74,19 @@ mearl browser_release
79
74
 
80
75
  ### 外部浏览器 Provider
81
76
 
82
- Provider 浏览器与扩展、CDP 和 AgentBay 浏览器一起出现在 `browser_list` 中,并使用同一个
77
+ Provider 浏览器与扩展、CDP 浏览器一起出现在 `browser_list` 中,并使用同一个
83
78
  全局 `browserId` 参与路由。Provider 的安装、更新和卸载由 `@mearl/setup` 管理:
84
79
 
85
80
  ```bash
86
81
  npx @mearl/setup provider install @ali/mearl-provider-brow
82
+ npx @mearl/setup provider install @ali/mearl-provider-agentbay
87
83
 
88
- # 让 Brow 创建一个配对实例;debugId 可省略并自动生成
89
- mearl browser_launch --payload '{"provider":"brow","providerOptions":{"debugId":"my-device"}}'
84
+ # 查看 Provider 当前版本的能力、参数与使用流程
85
+ mearl brow
86
+ mearl agentbay --help
90
87
 
91
- # 扫码后确认同一个实例已经连接
88
+ # Provider 启动参数统一放在 providerOptions
89
+ mearl browser_launch --payload '{"provider":"brow","providerOptions":{}}'
92
90
  mearl browser_list
93
91
 
94
92
  # 后续操作使用 browser_list 返回的全局 Browser ID
@@ -130,12 +128,13 @@ Mearl 默认聚合当前机器和已配置 cloud-server 下的浏览器,并通
130
128
  `browser_list --help` 和 `browser_launch --help` 会按场景展示宿主选择参数;连接或传输排障运行
131
129
  `mearl check --help`。其中 `--json` 仅用于输出机器可读的检查结果。
132
130
 
133
- ## Skill CLI 插件
131
+ ## Skill 插件
134
132
 
135
133
  安装 skill 并自动注册命令,或直接注册已有入口:
136
134
 
137
135
  ```bash
138
136
  npx @mearl/setup plugin install trip/mearl --skill yuque-doc-fetch --yes
137
+ npx @mearl/setup plugin install trip/mearl --skill buc-request --yes
139
138
  npx @mearl/setup plugin yuque /absolute/path/to/yuque-doc-fetch
140
139
  npx @mearl/setup plugin uninstall yuque
141
140
  mearl yuque read "https://aliyuque.antfin.com/group/book/doc"
@@ -146,7 +145,7 @@ mearl yuque --version
146
145
 
147
146
  目录默认使用 `scripts/main.mjs`。入口默认导出 `register(app)`,可导出简短的 `description`
148
147
  显示在 `mearl` 插件列表中,并通过 `app.command(signature, handler, description?)`
149
- 注册动作、`app.invoke(action, payload, options?)` 复用当前浏览器上下文,并用 `app.progress(message)` 报告可选进度。CLI 默认只输出最终结果,传入
148
+ 注册动作、`app.invoke(action, payload, options?)` 复用当前浏览器上下文,并用 `app.progress(message)` 报告可选进度。调用另一个插件时,payload 使用 `{ command, input }` envelope;嵌套调用继承宿主选项并拒绝循环依赖。CLI 默认只输出最终结果,传入
150
149
  `--verbose` 时才把进度写入 stderr。handler 返回文本或 JSON,宿主统一处理 `--output`、连接和错误。写命令在用户调用后直接执行,业务模块负责目标、状态、幂等和结果校验。
151
150
  自动注册默认以 skill 安装目录名作为命令名,也可在入口导出 `name` 指定短名称。
152
151
  setup 会补齐缺失或低于自身版本的 client、native-host;安装参数透传给 Ali Skills,并固定全局共享安装。
@@ -158,19 +157,38 @@ setup 会补齐缺失或低于自身版本的 client、native-host;安装参
158
157
  插件接受位置参数或包含 command/input 的 `--payload`,两种形式不能混用。JSON 保留业务字段类型;正文等文件输入
159
158
  按具体 skill 的字段约定处理。插件不使用上表中内置 action 的 `--payload-file` 入口。
160
159
 
160
+ 注册后的插件也可从 SDK 调用;实例方法 `client.invokePlugin` 复用既有连接和默认浏览器:
161
+
162
+ ```typescript
163
+ import { invokePlugin } from '@mearl/client';
164
+
165
+ const result = await invokePlugin(
166
+ 'yuque',
167
+ { command: 'read', input: { url: 'https://aliyuque.antfin.com/group/book/doc' } },
168
+ { browser: 'work' },
169
+ );
170
+ ```
171
+
172
+ MCP Server 通过固定工具 `mearl_plugin` 接受同样的 `plugin`、`command` 和 `input`,不会把插件子命令动态扩展成 MCP 工具。SDK 与 MCP 不接受任意入口路径;调用官方目录中尚未注册的插件时,会通过匹配当前 client 版本的 setup 自动安装并重试一次。未知名称仍返回 `PLUGIN_NOT_REGISTERED`。
173
+
161
174
  注册记录在 `~/.mearl/plugins/<name>.json`,保存入口路径和可选的简短描述;skill 业务代码原地更新直接生效,换路径或更新描述后重新注册。
162
175
  `plugin uninstall <name>` 会对 `~/.agents/skills` 中的全局 Skill 调用 `ali-skills remove --global --yes`,
163
176
  成功后清除相关注册;即使入口目录已被手动删除,也会完成 Ali Skills 跟踪记录的清理。
164
177
  手工路径只解除注册并保留源码。
165
178
  `PLUGIN_NOT_REGISTERED` 表示插件尚未注册。`PLUGIN_ENTRY_MISSING` 会显示失效入口,并给出带有实际插件名的
166
179
  卸载清理命令。其他错误按实际原因处理。
167
- 编写规范见 [Mearl Skill Creator 的 CLI 插件规范](../skills/mearl-skill-creator/references/cli-plugin.md)。
180
+ 编写规范见 [Mearl Skill Creator 的 Skill 插件规范](../skills/mearl-skill-creator/references/cli-plugin.md)。
181
+
182
+ 需要 BUC 身份的内部 HTTP 接口通过官方 `buc` 插件调用。CLI 使用 `mearl buc request`,业务插件使用
183
+ `app.invoke('buc', { command: 'request', input })`;当前身份和人员资料分别使用 `whoami` 与 `profile`
184
+ 子命令。BUC 插件按请求创建一次性凭据,只允许访问 HTTPS `alibaba-inc.com` 域名,过滤认证字段与
185
+ Cookie,并始终在运行 CLI、SDK 或 MCP Server 的机器上执行,不随浏览器或 connector 转发。
168
186
 
169
187
  ## 支持的操作
170
188
 
171
189
  | 分类 | Actions |
172
190
  | ------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
173
- | API 调试 | `tab_checkpoint` `get_requests` `get_logs` `get_events` `get_api_schema` |
191
+ | API 调试 | `tab_checkpoint` `get_requests` `get_logs` `get_events` |
174
192
  | Mock & 规则 | `set_mock` `get_mocks` `set_rule` `get_rules` |
175
193
  | 网络请求 | `send_request` `send_mtop_request` `request_domain_permission` |
176
194
  | 标签页 | `tab_open` `tab_close` `tab_list` |
@@ -178,9 +196,7 @@ setup 会补齐缺失或低于自身版本的 client、native-host;安装参
178
196
  | 组合调用 | `run_actions` |
179
197
  | 页面感知 | `page_snapshot` `page_screenshot` `page_selected_element` `page_frames` |
180
198
  | 环境与 Cookie | `set_device_emulation` `set_app_profile` `set_timezone` `get_cookie` `set_cookie` |
181
- | 用户信息 | `get_user_info` |
182
199
  | 录制 | `record_start` `record_stop` |
183
- | TDBank | `tdbank_account` |
184
200
  | 浏览器 | `browser_list` `browser_release` `browser_launch` `browser_close` |
185
201
 
186
202
  页面交互动作(`page_click` / `page_drag` / `page_type` / `page_hover` / `page_scroll` / `page_press` / `page_upload`)默认等待异步稳定并返回 `{ action, observation }`。参数、ref 或目标解析在派发前失败时返回 `action.stage: "precondition"`,省略无意义的 `observation` / `diagnostics`,CLI 退出码为 1。传 `observe: false` 可执行裸动作;需要同一动作期间的新增 error logs 和业务 requests 时传 `diagnostics: true`,埋点通过 `diagnostics.events` 按需开启。`page_eval` 默认裸执行,可按需开启观察或诊断。
@@ -193,9 +209,11 @@ setup 会补齐缺失或低于自身版本的 client、native-host;安装参
193
209
  `regular` / `managed`,`status` 区分 `connected` / `pairing` / `running_disconnected` /
194
210
  `stopped`;只有 `connected` 的浏览器可作为常规操作目标。
195
211
 
196
- 按 `sessionId` 接入的 AgentBay 浏览器用 `browser_release` 收尾:它会移除本机 attachment 并退出对应宿主,但不会关闭云端 Session。其他浏览器仅在用户明确要求结束调试、退出接管或清理会话资源时调用;它会释放 debugger/CDP 控制和会话级临时状态(包括设备/时区模拟),保留浏览器及全部标签页,后续浏览器操作可自动重新建立控制。
197
-
198
- 托管浏览器使用独立 Profile。控制浏览器需先登录 TDBank;生成的 SSO 地址在本地内部传递,本地实例可以使用 `headless: true`(默认)完成测试账号登录。AgentBay 固定为非 headless,传 `headless: true` 会被拒绝;`imageId` 可指定镜像别名或具体镜像 ID,省略时使用已验证的 Linux Browser Use 内网镜像。`persistent: true` 会让本地实例保留 Profile,让 AgentBay 同名实例绑定稳定的云端 Browser Context;正常关闭时同步 Cookie、LocalStorage、IndexedDB 等状态,下次启动无需再次复制本地登录态。`deleteProfile: true` 会显式删除对应 Profile 或 Context。AgentBay 的 `reuse` 支持 `never`(默认)、`prefer` 和 `require`:`prefer` 存在运行实例时复用、否则创建,`require` 只复用且不会意外创建计费会话。另一台设备已知确切 Session 时,可传 `sessionId` 直接通过 AgentBay CDP 接入,不需要 cloud-server;当前设备需配置能访问该 Session 的 `AGENTBAY_KEY`。provider-local ID 为 `managed:agentbay:session:<sessionId>`,跨 CLI 调用使用启动响应中的全局 `browserId`,且本机只持有 attachment。`copyCookieDomains` 传数组时复制指定域,传 `"mearl-services"` 时同步 Mearl 依赖平台登录态;需要同时复制基础登录态和额外站点时传 `{ "presets": ["mearl-services"], "domains": [...] }`。Cookie 值不会经过 cloud-server,也不会出现在命令结果或日志中;`userAgentMode: "desktop"` 让本地实例使用匹配本机 Chrome 版本的桌面 UA,AgentBay 则复用当前控制浏览器的桌面 UA。`browser_list` 仅返回浏览器的稳定配置,不携带 AgentBay 串流入口或沙箱 noVNC 地址。需要时显式运行 `mearl check --browser <id|名称>`,由统一的浏览器访问入口能力实时返回 `browserAccess`;AgentBay、OpenSandbox 和 AONE Sandbox 地址都应按敏感信息处理。完整设计见 [托管浏览器与 TDBank 多账号设计](../../docs/managed-browsers.md)。
212
+ `browser_release` 仅在用户明确要求结束调试、退出接管或清理会话资源时调用;它会释放
213
+ debugger/CDP 控制和会话级临时状态,保留浏览器及全部标签页。托管实例的启动、关闭、持久化、
214
+ 接入和登录态同步语义由各 Provider 声明;运行 `mearl <providerId>` 获取当前说明。访问入口不在
215
+ `browser_list` 中,需要时显式运行 `mearl check --browser <id|名称>`,并把返回的
216
+ `browserAccess.url` 按敏感信息处理。
199
217
 
200
218
  ## 架构
201
219
 
@@ -1,8 +1,8 @@
1
1
  import { PAGE_ACT_ACTIONS } from './generated-browser-action-protocol.js';
2
2
  const SEND_REQUEST_FILE_TRANSFER_TIMEOUT_MS = 120_000;
3
- // AgentBay staging can spend up to 60s transferring bytes and then 120s syncing the session disk.
3
+ // Remote Providers may stage files before forwarding a page upload.
4
4
  const PAGE_UPLOAD_FILE_TRANSFER_TIMEOUT_MS = 180_000;
5
- // AgentBay context sync can poll for about 12 minutes before session deletion waits up to 5 more.
5
+ // Persistent Providers may need to synchronize remote state before closing an instance.
6
6
  const BROWSER_CLOSE_TIMEOUT_SEC = 18 * 60;
7
7
  function boundedNumber(value, fallback, minimum, maximum) {
8
8
  const numeric = Number(value);
@@ -41,8 +41,6 @@ export function resolveActionTimeoutSec(action, data, fallback = 15) {
41
41
  return Math.max(fallback, 90);
42
42
  if (action === 'browser_close')
43
43
  return Math.max(fallback, BROWSER_CLOSE_TIMEOUT_SEC);
44
- if (action === 'tdbank_account')
45
- return Math.max(fallback, 20);
46
44
  if (action === 'run_actions') {
47
45
  const actions = Array.isArray(data.actions) ? data.actions : [{}];
48
46
  const timeoutSec = actions.reduce((total, step) => {
package/dist/check.d.ts CHANGED
@@ -15,8 +15,6 @@ export interface ProviderVersionInfo {
15
15
  protocolVersion: number;
16
16
  }
17
17
  export type BrowserAccessCapability = {
18
- kind: 'agentbay';
19
- } | {
20
18
  kind: 'sandbox-novnc';
21
19
  environment: 'opensandbox' | 'aone';
22
20
  port: number;
package/dist/check.js CHANGED
@@ -3,8 +3,6 @@ function formatError(error) {
3
3
  return error instanceof Error ? error.message : String(error);
4
4
  }
5
5
  function browserAccessLabel(access) {
6
- if (access.kind === 'agentbay')
7
- return 'AgentBay, temporary';
8
6
  if (access.kind === 'sandbox-novnc' && 'environment' in access) {
9
7
  return access.environment === 'opensandbox' ? 'OpenSandbox noVNC' : 'AONE Sandbox noVNC';
10
8
  }
package/dist/cli.js CHANGED
@@ -8,13 +8,13 @@ import { commandInput, findCommandSuggestions, formatCommandSuggestions, } from
8
8
  import { COMMANDS, COMMAND_MAP } from './commands.js';
9
9
  import { CLIENT_VERSION } from './socket.js';
10
10
  import { UnifiedClient } from './unified.js';
11
- import { findPluginEntry, findPluginRegistration, listPluginNames, loadPlugin, PluginError, } from './plugin.js';
11
+ import { findPluginEntry, findPluginRegistration, findOfficialPlugin, listPluginNames, listOfficialPluginNames, loadRegisteredPlugin, PluginError, } from './plugin.js';
12
12
  import { getCloudServerConfigPath, loadCloudServerConfig } from './unifiedConfig.js';
13
13
  const TOP_LEVEL_COMMAND_NAME_WIDTH = 22;
14
14
  const CATEGORIES = [
15
15
  {
16
16
  title: 'API 调试',
17
- names: ['tab_checkpoint', 'get_requests', 'get_logs', 'get_events', 'get_api_schema'],
17
+ names: ['tab_checkpoint', 'get_requests', 'get_logs', 'get_events'],
18
18
  },
19
19
  { title: 'Mock & 规则', names: ['set_mock', 'get_mocks', 'set_rule', 'get_rules'] },
20
20
  {
@@ -46,9 +46,7 @@ const CATEGORIES = [
46
46
  title: '环境与 Cookie',
47
47
  names: ['set_device_emulation', 'set_app_profile', 'set_timezone', 'get_cookie', 'set_cookie'],
48
48
  },
49
- { title: '用户信息', names: ['get_user_info'] },
50
49
  { title: '录制', names: ['record_start', 'record_stop'] },
51
- { title: 'TDBank', names: ['tdbank_account'] },
52
50
  {
53
51
  title: '浏览器',
54
52
  names: ['browser_list', 'browser_release', 'browser_launch', 'browser_close'],
@@ -61,7 +59,16 @@ const BUILTIN_COMMANDS = [
61
59
  examples: ['check'],
62
60
  },
63
61
  ];
64
- const SUGGESTABLE_COMMANDS = [...COMMANDS, ...BUILTIN_COMMANDS];
62
+ const OFFICIAL_PLUGIN_COMMANDS = listOfficialPluginNames().map(name => ({
63
+ name,
64
+ description: findOfficialPlugin(name).description,
65
+ examples: [name],
66
+ }));
67
+ const SUGGESTABLE_COMMANDS = [
68
+ ...COMMANDS,
69
+ ...BUILTIN_COMMANDS,
70
+ ...OFFICIAL_PLUGIN_COMMANDS,
71
+ ];
65
72
  const ADVANCED_BUILTIN_COMMANDS = new Set(['connector_list']);
66
73
  function printUsage(commandName, version) {
67
74
  const lines = [
@@ -115,6 +122,14 @@ function printUsage(commandName, version) {
115
122
  });
116
123
  lines.push('', '已注册插件:', ...entries);
117
124
  }
125
+ const installed = new Set(plugins);
126
+ const available = listOfficialPluginNames().filter(name => !installed.has(name));
127
+ if (available.length) {
128
+ lines.push('', '官方插件(首次调用自动安装):', ...available.map(name => {
129
+ const description = findOfficialPlugin(name).description;
130
+ return ` ${name.padEnd(TOP_LEVEL_COMMAND_NAME_WIDTH)}${description}`;
131
+ }));
132
+ }
118
133
  }
119
134
  catch (error) {
120
135
  lines.push('', error instanceof Error ? error.message : String(error));
@@ -126,6 +141,78 @@ function printUsage(commandName, version) {
126
141
  lines.push('', `运行 ${commandName} <action> --help 查看 action 参数。`);
127
142
  console.error(lines.join('\n'));
128
143
  }
144
+ function providerSchemaLine(name, schema, required, indent = 2) {
145
+ const detail = [
146
+ required ? '必填' : '可选',
147
+ schema.type,
148
+ schema.type === 'string' && schema.secret ? '敏感' : undefined,
149
+ schema.title,
150
+ schema.description,
151
+ schema.default === undefined ? undefined : `默认 ${JSON.stringify(schema.default)}`,
152
+ ].filter(Boolean);
153
+ return `${' '.repeat(indent)}${name.padEnd(Math.max(1, 24 - indent))}${detail.join(' · ')}`;
154
+ }
155
+ function providerSchemaLines(name, schema, required, indent = 2) {
156
+ const lines = [providerSchemaLine(name, schema, required, indent)];
157
+ if (schema.type === 'object') {
158
+ const childRequired = new Set(schema.required ?? []);
159
+ for (const [childName, childSchema] of Object.entries(schema.properties)) {
160
+ lines.push(...providerSchemaLines(childName, childSchema, childRequired.has(childName), indent + 2));
161
+ }
162
+ }
163
+ else if (schema.type === 'array' && schema.items.type === 'object') {
164
+ const childRequired = new Set(schema.items.required ?? []);
165
+ for (const [childName, childSchema] of Object.entries(schema.items.properties)) {
166
+ lines.push(...providerSchemaLines(childName, childSchema, childRequired.has(childName), indent + 2));
167
+ }
168
+ }
169
+ return lines;
170
+ }
171
+ function printProviderHelp(commandName, provider) {
172
+ const browserType = provider.browserType;
173
+ const lines = [
174
+ `${provider.providerId} — ${provider.name} v${provider.version}`,
175
+ '',
176
+ ...(browserType.help ?? []).map(line => String(line)),
177
+ ];
178
+ if (browserType.help?.length)
179
+ lines.push('');
180
+ lines.push('创建:', ` ${commandName} browser_launch --payload '{"provider":"${provider.providerId}","providerOptions":{}}'`, '', '启动参数(providerOptions):');
181
+ const required = new Set(browserType.inputSchema.required ?? []);
182
+ const properties = Object.entries(browserType.inputSchema.properties);
183
+ if (properties.length === 0)
184
+ lines.push(' (无)');
185
+ else {
186
+ for (const [name, schema] of properties) {
187
+ lines.push(...providerSchemaLines(name, schema, required.has(name)));
188
+ }
189
+ }
190
+ if (browserType.inputSchema.oneOf?.length) {
191
+ lines.push('', '可选模式:');
192
+ browserType.inputSchema.oneOf.forEach((schema, index) => {
193
+ const mode = schema.title || `模式 ${index + 1}`;
194
+ const requiredFields = schema.required?.length ? schema.required.join(', ') : '无必填字段';
195
+ lines.push(` ${mode}(必填:${requiredFields})`);
196
+ });
197
+ }
198
+ lines.push('', '发现与检查:', ` ${commandName} browser_list`, ` ${commandName} check --browser <browserId>`, '', '管理:', ` npx @mearl/setup provider update ${provider.providerId}`, ` npx @mearl/setup provider uninstall ${provider.providerId}`);
199
+ if (browserType.configuration) {
200
+ lines.push('', 'Provider 配置(在 Mearl 浏览器管理器中填写):');
201
+ const configurationRequired = new Set(browserType.configuration.inputSchema.required ?? []);
202
+ for (const [name, schema] of Object.entries(browserType.configuration.inputSchema.properties)) {
203
+ lines.push(...providerSchemaLines(name, schema, configurationRequired.has(name)));
204
+ }
205
+ }
206
+ if (browserType.documentationUrl) {
207
+ lines.push('', `文档:${browserType.documentationUrl}`);
208
+ }
209
+ console.error(lines.join('\n'));
210
+ }
211
+ function installedProvider(name) {
212
+ if (!name)
213
+ return undefined;
214
+ return listInstalledProviders().find(provider => provider.providerId === name);
215
+ }
129
216
  function printCheckHelp(commandName) {
130
217
  console.error([
131
218
  'check',
@@ -341,7 +428,7 @@ export function parseClientArgs(args, plugin = false) {
341
428
  : {}),
342
429
  };
343
430
  }
344
- async function runPluginCli(args, entry, commandName) {
431
+ async function runPluginCli(args, commandName) {
345
432
  let parsed;
346
433
  try {
347
434
  parsed = parseClientArgs(args, true);
@@ -359,23 +446,23 @@ async function runPluginCli(args, entry, commandName) {
359
446
  ...(parsed.local ? { local: true } : {}),
360
447
  ...(parsed.forceCdp ? { transport: 'cdp' } : {}),
361
448
  };
362
- const plugin = await loadPlugin(entry, {
363
- options: invokeOptions,
364
- progress(message) {
365
- if (parsed.verbose) {
366
- process.stderr.write(message.endsWith('\n') ? message : `${message}\n`);
367
- }
368
- },
449
+ const plugin = await loadRegisteredPlugin(parsed.action, {
369
450
  async invoke(action, payload, options) {
370
451
  client ??= new UnifiedClient({
371
452
  ...(parsed.serverUrl ? { serverUrl: parsed.serverUrl } : {}),
372
453
  });
373
- const result = await client.invoke(action, payload, { ...invokeOptions, ...options });
374
- if (result?.action?.success === false)
375
- throw new Error(result.action.error || `${action} failed`);
376
- return result;
454
+ return client.invoke(action, payload, options);
377
455
  },
378
- }, parsed.action);
456
+ }, {
457
+ ...invokeOptions,
458
+ ...(parsed.verbose
459
+ ? {
460
+ onProgress(message) {
461
+ process.stderr.write(message.endsWith('\n') ? message : `${message}\n`);
462
+ },
463
+ }
464
+ : {}),
465
+ });
379
466
  if (parsed.version) {
380
467
  console.log(plugin.version ?? 'unknown');
381
468
  return 0;
@@ -435,6 +522,12 @@ export async function runMearlCli(args, options = {}) {
435
522
  const clientVersion = options.clientVersion ?? CLIENT_VERSION;
436
523
  if (args[0] === 'capture_checkpoint')
437
524
  args = ['tab_checkpoint', ...args.slice(1)];
525
+ const provider = installedProvider(args[0]);
526
+ if (provider &&
527
+ (args.length === 1 || args.slice(1).every(arg => arg === '--help' || arg === '-h'))) {
528
+ printProviderHelp(commandName, provider);
529
+ return 0;
530
+ }
438
531
  if (args[0] &&
439
532
  !args[0].startsWith('-') &&
440
533
  !args[0].startsWith('__') &&
@@ -443,8 +536,8 @@ export async function runMearlCli(args, options = {}) {
443
536
  !COMMAND_MAP.has(args[0])) {
444
537
  try {
445
538
  const entry = findPluginEntry(args[0]);
446
- if (entry)
447
- return await runPluginCli(args, entry, commandName);
539
+ if (entry || findOfficialPlugin(args[0]))
540
+ return await runPluginCli(args, commandName);
448
541
  }
449
542
  catch (error) {
450
543
  console.error(error instanceof Error ? error.message : String(error));
@@ -142,24 +142,6 @@ export declare const COMMANDS: readonly [{
142
142
  readonly description: "返回完整 URL、_raw 和 _context;默认 false";
143
143
  }];
144
144
  readonly examples: ["get_events --payload '{\"source\":\"aplus\",\"event_type\":\"CLK\",\"limit\":10}'", "get_events --payload '{\"filter\":\"order\",\"includeRaw\":true,\"limit\":1}'", "get_events --payload '{\"tabId\":12345,\"source\":\"all\",\"limit\":20}'"];
145
- }, {
146
- readonly name: "get_api_schema";
147
- readonly description: "获取 API 接口 schema(出入参定义)";
148
- readonly params: [{
149
- readonly name: "api";
150
- readonly type: "string";
151
- readonly required: true;
152
- readonly description: "API 名称";
153
- }, {
154
- readonly name: "version";
155
- readonly type: "string";
156
- readonly description: "API 版本,用于精确匹配接口定义";
157
- }, {
158
- readonly name: "fields";
159
- readonly type: "(\"schema\" | \"hsf\")[]";
160
- readonly description: "指定返回内容;不填时返回全部可用内容";
161
- }];
162
- readonly examples: ["get_api_schema --payload '{\"api\":\"mtop.trade.order.detail\"}'"];
163
145
  }, {
164
146
  readonly name: "set_mock";
165
147
  readonly description: "设置 API mock 数据";
@@ -334,100 +316,43 @@ export declare const COMMANDS: readonly [{
334
316
  readonly description: "超时毫秒数,默认 10000";
335
317
  }];
336
318
  readonly examples: ["send_mtop_request --payload '{\"api\":\"mtop.trade.order.detail\",\"data\":{\"orderId\":\"123\"}}'"];
337
- }, {
338
- readonly name: "tdbank_account";
339
- readonly description: "TDBank 账号操作:获取扫码登录二维码、当前账号、账号列表、切换账号、快速借用并切换账号";
340
- readonly params: [{
341
- readonly name: "tabId";
342
- readonly type: "number";
343
- readonly description: "login 首次不传;后续传上次响应的 tabId;无法恢复待扫码状态时会返回新的 tabId;其他操作在 CDP 模式下用于读取 Cookie";
344
- }, {
345
- readonly name: "operation";
346
- readonly type: "\"current\" | \"list\" | \"login\" | \"switch\" | \"borrow\"";
347
- readonly description: "操作类型,默认 current";
348
- }, {
349
- readonly name: "refresh";
350
- readonly type: "boolean";
351
- readonly description: "list 时是否强制刷新账号列表";
352
- }, {
353
- readonly name: "pageIndex";
354
- readonly type: "number";
355
- readonly description: "list 时的页码,默认 1";
356
- }, {
357
- readonly name: "accountId";
358
- readonly type: "string | number";
359
- readonly description: "switch 时使用账号列表中的 id";
360
- }, {
361
- readonly name: "havanaId";
362
- readonly type: "string | number";
363
- readonly description: "switch 时直接指定 havanaId;TDBank 方式需配合 site,免密方式需配合 ssoKey";
364
- }, {
365
- readonly name: "site";
366
- readonly type: "string | number";
367
- readonly description: "TDBank 方式直接指定 havanaId 时必填";
368
- }, {
369
- readonly name: "ssoKey";
370
- readonly type: "string";
371
- readonly description: "switch 免密方式的秘钥;必须同时传 havanaId,site 固定为 taobao";
372
- }, {
373
- readonly name: "query";
374
- readonly type: "string";
375
- readonly description: "borrow 时输入要借用的账号 loginId/关键词";
376
- }, {
377
- readonly name: "identityId";
378
- readonly type: "string | number";
379
- readonly description: "login 返回 identity_required 后,传用户明确选择的 identityId";
380
- }];
381
- readonly examples: ["tdbank_account --payload '{\"operation\":\"login\"}'", "tdbank_account --payload '{\"operation\":\"login\",\"tabId\":12345}'", "tdbank_account --payload '{\"operation\":\"login\",\"tabId\":12345,\"identityId\":\"10001\"}'", "tdbank_account --payload '{\"operation\":\"current\"}'", "tdbank_account --payload '{\"operation\":\"list\",\"refresh\":true}'", "tdbank_account --payload '{\"operation\":\"switch\",\"accountId\":12345}'", "tdbank_account --payload '{\"operation\":\"switch\",\"havanaId\":\"1234567890\",\"ssoKey\":\"<secret>\"}'", "tdbank_account --payload '{\"operation\":\"borrow\",\"query\":\"test_account\"}'"];
382
319
  }, {
383
320
  readonly name: "browser_list";
384
- readonly description: "列出所有浏览器及其连接状态、类型和托管实例信息;AgentBay 可包含镜像与持久化 Context";
321
+ readonly description: "列出所有浏览器及其连接状态、类型和 Provider 托管实例信息";
385
322
  readonly summary: "列出本地及远端浏览器与连接状态";
386
323
  readonly examples: ["browser_list"];
387
324
  }, {
388
325
  readonly name: "browser_release";
389
- readonly description: "sessionId 接入的 AgentBay 浏览器用它解除本机 attachment且不关闭云端 Session;其他浏览器仅按用户明确要求释放调试控制和会话级临时效果(含设备/时区模拟),不用于普通任务收尾";
390
- readonly summary: "释放浏览器控制或解除 Session attachment";
326
+ readonly description: "释放当前浏览器的调试控制和会话级临时效果(含设备/时区模拟);不用于普通任务收尾,Provider 实例的行为以 mearl <providerId> 为准";
327
+ readonly summary: "释放浏览器调试控制和会话级临时效果";
391
328
  readonly examples: ["browser_release", "browser_release --browser chrome-a"];
392
329
  }, {
393
330
  readonly name: "browser_launch";
394
- readonly description: "启动本地 Chrome、创建/复用 AgentBay 云浏览器,或通过已注册 Provider 创建浏览器实例";
331
+ readonly description: "启动本地 Chrome,或通过已注册 Provider 创建浏览器实例";
395
332
  readonly params: [{
396
333
  readonly name: "name";
397
334
  readonly type: "string";
398
- readonly description: "实例名称,创建浏览器时必填;按 sessionId 接入时可省略";
335
+ readonly description: "本地实例名称;使用 local Provider 时必填";
399
336
  }, {
400
337
  readonly name: "provider";
401
338
  readonly type: "string";
402
- readonly description: "运行环境,默认 local;可传 agentbay 或 mearl --help 中展示的已注册 Provider ID";
339
+ readonly description: "运行环境,默认 local;可传 mearl --help 中展示的已注册 Provider ID";
403
340
  }, {
404
341
  readonly name: "providerOptions";
405
342
  readonly type: "Record<string, JSON value>";
406
343
  readonly description: "所选 Provider 声明的结构化启动参数;不能与顶层启动字段混用";
407
- }, {
408
- readonly name: "sessionId";
409
- readonly type: "string";
410
- readonly description: "显式接入现有 AgentBay Session;不会创建、初始化或取得其生命周期所有权";
411
- }, {
412
- readonly name: "reuse";
413
- readonly type: "\"never\" | \"prefer\" | \"require\"";
414
- readonly description: "AgentBay 运行实例复用策略:never 默认不复用;prefer 同名运行实例存在则复用、无记录则创建;require 只复用、不创建";
415
- }, {
416
- readonly name: "imageId";
417
- readonly type: "string";
418
- readonly description: "AgentBay 镜像别名或具体镜像 ID;默认使用已验证的 Linux Browser Use 内网镜像";
419
344
  }, {
420
345
  readonly name: "headless";
421
346
  readonly type: "boolean";
422
- readonly description: "本地 provider 是否无头运行,默认 true;AgentBay 固定为 false,不支持 true";
347
+ readonly description: "本地 Provider 是否无头运行,默认 true";
423
348
  }, {
424
349
  readonly name: "userAgentMode";
425
350
  readonly type: "\"default\" | \"desktop\"";
426
- readonly description: "UA 模式:default 使用 provider 默认 UA;desktop 本地使用匹配本机版本的 UA,AgentBay 复用控制浏览器的桌面 UA";
351
+ readonly description: "本地 UA 模式:default 使用默认 UA;desktop 使用匹配本机版本的 UA";
427
352
  }, {
428
353
  readonly name: "persistent";
429
354
  readonly type: "boolean";
430
- readonly description: "关闭后是否保留浏览器状态,默认 false;本地保留 Profile,AgentBay 同名实例复用云端 Context";
355
+ readonly description: "本地实例关闭后是否保留 Profile,默认 false";
431
356
  }, {
432
357
  readonly name: "url";
433
358
  readonly type: "string";
@@ -436,35 +361,15 @@ export declare const COMMANDS: readonly [{
436
361
  readonly name: "executablePath";
437
362
  readonly type: "string";
438
363
  readonly description: "自定义 Chrome 可执行文件路径";
439
- }, {
440
- readonly name: "accountId";
441
- readonly type: "string | number";
442
- readonly description: "登录账号列表中的 accountId";
443
- }, {
444
- readonly name: "havanaId";
445
- readonly type: "string | number";
446
- readonly description: "直接登录 havanaId;需要同时传 site";
447
- }, {
448
- readonly name: "site";
449
- readonly type: "string | number";
450
- readonly description: "havanaId 对应的站点";
451
- }, {
452
- readonly name: "query";
453
- readonly type: "string";
454
- readonly description: "搜索、借用并登录匹配的 TDBank 账号";
455
- }, {
456
- readonly name: "tabId";
457
- readonly type: "number";
458
- readonly description: "CDP 控制浏览器中用于读取 TDBank Cookie 的标签页";
459
364
  }, {
460
365
  readonly name: "copyCookieDomains";
461
366
  readonly type: "string[] | \"mearl-services\" | { presets?: [\"mearl-services\"]; domains?: string[] }";
462
367
  readonly description: "从控制浏览器复制 Cookie:可传域名数组、Mearl 登录态预设,或组合预设与额外域名";
463
368
  }];
464
- readonly examples: ["browser_launch --payload '{\"provider\":\"brow\",\"providerOptions\":{}}'", "browser_launch --payload '{\"name\":\"cloud-a\",\"provider\":\"agentbay\",\"userAgentMode\":\"desktop\",\"url\":\"https://example.com\"}'", "browser_launch --payload '{\"name\":\"cloud-public\",\"provider\":\"agentbay\",\"imageId\":\"browser_latest\",\"url\":\"https://example.com\"}'", "browser_launch --payload '{\"name\":\"cloud-login\",\"provider\":\"agentbay\",\"persistent\":true,\"copyCookieDomains\":[\"example.com\"],\"url\":\"https://example.com/account\"}'", "browser_launch --payload '{\"name\":\"cloud-login\",\"provider\":\"agentbay\",\"reuse\":\"require\",\"copyCookieDomains\":[\"example.com\"]}'", "browser_launch --browser '<本地浏览器 browserId>' --payload '{\"sessionId\":\"<AgentBay sessionId>\",\"copyCookieDomains\":[\"example.com\"]}'", "browser_launch --payload '{\"name\":\"cloud-tools\",\"provider\":\"agentbay\",\"copyCookieDomains\":\"mearl-services\"}'", "browser_launch --payload '{\"name\":\"cloud-tools\",\"provider\":\"agentbay\",\"copyCookieDomains\":{\"presets\":[\"mearl-services\"],\"domains\":[\"figma.com\"]}}'", "browser_launch --payload '{\"name\":\"account-a\",\"headless\":true,\"accountId\":12345}'", "browser_launch --payload '{\"name\":\"figma\",\"userAgentMode\":\"desktop\",\"copyCookieDomains\":[\"figma.com\"]}'", "browser_launch --payload '{\"name\":\"account-b\",\"query\":\"test_account\",\"url\":\"https://www.taobao.com\"}'", "browser_launch --payload '{\"name\":\"with-cookies\",\"copyCookieDomains\":[\"example.com\",\"sub.example.org\"]}'"];
369
+ readonly examples: ["browser_launch --payload '{\"provider\":\"brow\",\"providerOptions\":{}}'", "browser_launch --payload '{\"name\":\"figma\",\"userAgentMode\":\"desktop\",\"copyCookieDomains\":[\"figma.com\"]}'", "browser_launch --payload '{\"name\":\"with-cookies\",\"copyCookieDomains\":[\"example.com\",\"sub.example.org\"]}'"];
465
370
  }, {
466
371
  readonly name: "browser_close";
467
- readonly description: "关闭指定托管浏览器并按策略保留或清理本地 Profile / AgentBay Context";
372
+ readonly description: "关闭指定托管浏览器,并按 Provider 策略保留或清理持久化数据";
468
373
  readonly params: [{
469
374
  readonly name: "browser";
470
375
  readonly type: "string";
@@ -473,9 +378,9 @@ export declare const COMMANDS: readonly [{
473
378
  }, {
474
379
  readonly name: "deleteProfile";
475
380
  readonly type: "boolean";
476
- readonly description: "强制删除持久化本地 Profile 或 AgentBay Context,默认 false";
381
+ readonly description: "强制删除持久化数据,默认 false";
477
382
  }];
478
- readonly examples: ["browser_close --payload '{\"browser\":\"managed:account-a\"}'", "browser_close --payload '{\"browser\":\"managed:agentbay:cloud-a\"}'", "browser_close --payload '{\"browser\":\"account-b\",\"deleteProfile\":true}'"];
383
+ readonly examples: ["browser_close --payload '{\"browser\":\"managed:account-a\"}'", "browser_close --payload '{\"browser\":\"account-b\",\"deleteProfile\":true}'"];
479
384
  }, {
480
385
  readonly name: "tab_open";
481
386
  readonly description: "打开或按 URL 规则复用 Tab,并等待新页面加载完成";
@@ -885,15 +790,6 @@ export declare const COMMANDS: readonly [{
885
790
  readonly description: "目标标签页 ID";
886
791
  }];
887
792
  readonly examples: ["page_selected_element", "page_selected_element --output ./element.png"];
888
- }, {
889
- readonly name: "get_user_info";
890
- readonly description: "获取当前浏览器登录的阿里员工信息";
891
- readonly params: [{
892
- readonly name: "detail";
893
- readonly type: "boolean";
894
- readonly description: "是否获取完整用户信息,默认 false(只返回工号)";
895
- }];
896
- readonly examples: ["get_user_info", "get_user_info --payload '{\"detail\":true}'"];
897
793
  }, {
898
794
  readonly name: "record_start";
899
795
  readonly description: "开始录制用户操作";
@@ -1034,7 +930,7 @@ export declare const COMMANDS: readonly [{
1034
930
  readonly examples: ["set_timezone --payload '{\"tabId\":12345,\"timezone\":\"America/New_York\"}'", "set_timezone --payload '{\"tabId\":12345,\"timezone\":\"\"}'"];
1035
931
  }, {
1036
932
  readonly name: "get_cookie";
1037
- readonly description: "读取当前标签页的一个指定非分区 Cookie;拒绝返回 HttpOnly Cookie";
933
+ readonly description: "从目标浏览器读取一个指定非分区 Cookie;拒绝返回 HttpOnly Cookie";
1038
934
  readonly params: [{
1039
935
  readonly name: "tabId";
1040
936
  readonly type: "number";
@@ -1045,16 +941,20 @@ export declare const COMMANDS: readonly [{
1045
941
  readonly type: "string";
1046
942
  readonly required: true;
1047
943
  readonly description: "要读取的 Cookie 名称;不支持列举全部 Cookie";
944
+ }, {
945
+ readonly name: "url";
946
+ readonly type: "string";
947
+ readonly description: "可选 Cookie 查询 URL;省略时使用当前标签页 URL";
1048
948
  }, {
1049
949
  readonly name: "domain";
1050
950
  readonly type: "string";
1051
- readonly description: "可选精确域过滤,只允许当前 host 或其父域";
951
+ readonly description: "可选精确域过滤,只允许 Cookie 查询 URL 的 host 或其父域";
1052
952
  }, {
1053
953
  readonly name: "path";
1054
954
  readonly type: "string";
1055
955
  readonly description: "可选精确 path 过滤";
1056
956
  }];
1057
- readonly examples: ["get_cookie --payload '{\"tabId\":12345,\"name\":\"feature_flag\"}'", "get_cookie --payload '{\"tabId\":12345,\"name\":\"edith_switch_br.rx-vehicle-search_home\",\"domain\":\"taobao.com\"}'"];
957
+ readonly examples: ["get_cookie --payload '{\"tabId\":12345,\"name\":\"feature_flag\"}'", "get_cookie --payload '{\"tabId\":12345,\"name\":\"edith_switch_br.rx-vehicle-search_home\",\"domain\":\"taobao.com\"}'", "get_cookie --payload '{\"tabId\":12345,\"name\":\"tracknick\",\"url\":\"https://www.taobao.com/\",\"domain\":\"taobao.com\",\"path\":\"/\"}'"];
1058
958
  }, {
1059
959
  readonly name: "set_cookie";
1060
960
  readonly description: "设置当前标签页域下的一个指定非分区 session Cookie,可选刷新页面";