@mearl/client 3.0.0-beta.7 → 3.0.0-beta.9
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 +15 -11
- package/dist/actionTimeouts.js +0 -3
- package/dist/bin.js +12 -5
- package/dist/capabilityHelp.js +0 -1
- package/dist/cli.js +8 -1
- package/dist/commands.d.ts +0 -11
- package/dist/commands.js +0 -13
- package/dist/guide.d.ts +46 -0
- package/dist/guide.js +80 -0
- package/dist/guides/api-browser-setup.md +93 -0
- package/dist/guides/api-page-automation.md +140 -0
- package/dist/guides/api-page-debugging.md +39 -0
- package/dist/guides/api-page-environment.md +37 -0
- package/dist/guides/api-request-debugging.md +54 -0
- package/dist/guides/browser-tool-cooperation.md +23 -0
- package/dist/guides/core.md +60 -0
- package/dist/guides/related-skills.md +32 -0
- package/dist/guides/scripting.md +21 -0
- package/dist/guides/troubleshooting.md +170 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +0 -3
- package/dist/plugins/pluginRegistry.js +9 -3
- package/dist/plugins/runtime/pluginDiagnostics.js +1 -1
- package/dist/plugins/runtime/pluginHostServer.js +1 -1
- package/dist/plugins/runtime/pluginPageManager.js +2 -2
- package/dist/plugins/runtime/pluginStateManager.js +1 -1
- package/dist/plugins/runtime/pluginTaskManager.js +2 -2
- package/dist/plugins/sdk/plugin.d.ts +2 -0
- package/dist/plugins/sdk/plugin.js +17 -9
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# @mearl/client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
CLI、TypeScript SDK 与脚本入口共用浏览器路由和插件运行时。运行时连接与安装由 `@mearl/setup` 管理。
|
|
4
|
+
|
|
5
|
+
首次使用当前版本可运行 `mearl guide`,默认只读取核心工作流。`mearl guide --list` 列出专题;例如 `mearl guide run` 读取 SDK 组合调用,`mearl guide environment` 读取设备与 App 指引。指南只读 npm 包内资源,不连接浏览器、加载插件、安装环境或触发自动更新。动作参数继续使用 `mearl <action> --help`,目标的已安装能力继续按现有目录查询。
|
|
6
|
+
|
|
7
|
+
指南正文唯一维护在本包的 `guides/`,构建时复制到 `dist/guides/`,`pnpm check:skills` 校验一致性。CLI 输出将本地文档链接转换为下一条指南命令。`skills/mearl/` 只分发精简的 `SKILL.md`,不附带正文副本;仅使用 MCP、无法执行 CLI 时,直接使用当前工具说明与 schema。组织发行版的安装、认证与业务补充仍由其独立 Skill 提供。
|
|
4
8
|
|
|
5
9
|
统一浏览器 SDK 与 CLI。它会发现当前机器及已配置 cloud-server 下所有 connector 的浏览器,并根据 `browser_list` 返回的全局 `browserId` 自动选择本地 Socket 或云端 WebSocket 链路。
|
|
6
10
|
|
|
@@ -9,9 +13,9 @@
|
|
|
9
13
|
## 安装
|
|
10
14
|
|
|
11
15
|
```bash
|
|
12
|
-
npm install @mearl/client
|
|
16
|
+
npm install @mearl/client@beta
|
|
13
17
|
# 或
|
|
14
|
-
pnpm add @mearl/client
|
|
18
|
+
pnpm add @mearl/client@beta
|
|
15
19
|
```
|
|
16
20
|
|
|
17
21
|
## SDK 使用
|
|
@@ -60,7 +64,7 @@ mearl check
|
|
|
60
64
|
mearl check --local --json
|
|
61
65
|
|
|
62
66
|
# 更新本地环境
|
|
63
|
-
npx @mearl/setup update --local
|
|
67
|
+
npx @mearl/setup@beta update --local
|
|
64
68
|
|
|
65
69
|
# 查看某个 action 的帮助
|
|
66
70
|
mearl <action> --help
|
|
@@ -242,7 +246,7 @@ Mearl 默认聚合当前机器和已配置 cloud-server 下的浏览器,并通
|
|
|
242
246
|
client、native-host、cloud-connector 和扩展的最新发布版本。发布版本查询最长等待约 1.5 秒,
|
|
243
247
|
无法访问发布源时会提示无法确认更新,不影响浏览器可用性检查。`--json` 输出机器可读的检查结果,
|
|
244
248
|
与 `--updates` 同用时包含更新状态。发现更新时会提示适用的命令:client/native-host 使用
|
|
245
|
-
`npx @mearl/setup update`,setup 管理的扩展使用 `npx @mearl/setup extension`。
|
|
249
|
+
`npx @mearl/setup@beta update`,setup 管理的扩展使用 `npx @mearl/setup extension`。
|
|
246
250
|
|
|
247
251
|
全局安装的 `mearl` CLI 默认在每天首次使用后启动独立后台进程,查询最新发布版本,并比较
|
|
248
252
|
client、native-host、cloud-server 和 MCP server 的已安装版本。发现更新时调用
|
|
@@ -309,7 +313,7 @@ const result = await invokePlugin(
|
|
|
309
313
|
);
|
|
310
314
|
```
|
|
311
315
|
|
|
312
|
-
MCP Server 通过固定工具 `plugin_invoke`
|
|
316
|
+
MCP Server 通过固定工具 `plugin_invoke` 接受 `plugin`、`command` 和 `input`。SDK 与 MCP 使用已注册插件;安装宿主管理的插件目录可触发匹配当前 client 版本的 setup 安装与一次重试,其他未注册名称返回 `PLUGIN_NOT_REGISTERED`。
|
|
313
317
|
|
|
314
318
|
长命令使用本机持久任务服务。服务按注册目录隔离并自动启动,CLI、SDK、MCP 与工具箱共享任务记录;调用方退出不取消执行。服务重启后将未完成任务标记为 `interrupted`,不自动重放。后台任务固定接受时的源码快照、工作目录与环境;第三方 JS 依赖应预先打包进 Skill,状态文件放在源码目录之外。快照只固定源码和相对资源,不预解析 JS 依赖;缺失依赖和语法错误由执行器加载代码时报告。
|
|
315
319
|
|
|
@@ -435,9 +439,9 @@ Preact、Hooks 和 HTM 运行时。页面从 `window.mearl.ui` 读取 `html`、`
|
|
|
435
439
|
|
|
436
440
|
工具箱面向人,CLI 面向 Agent:复用业务操作、保持结果一致即可,不要求共享中间态。表单草稿、选择、预览等纯 UI 状态默认放浏览器内存;确需刷新或重新打开后恢复时再用浏览器存储。sandbox 页不能假定可直接访问 `localStorage`、IndexedDB 或 `chrome.storage`,应使用宿主提供的能力。只有业务记录、关闭页面后继续的任务、安全重试回执或受消息大小限制的资源传输才需要 Node 状态,并说明其生命周期及清理方式。
|
|
437
441
|
|
|
438
|
-
|
|
442
|
+
Skill 插件可提供自定义请求与数据处理命令。CLI 使用 `mearl <plugin> <command>`,业务插件通过 `app.invoke(plugin, { command, input })` 复用同一操作;插件名称、认证方式、请求字段和上传约定以对应 Skill 的契约为准。
|
|
439
443
|
|
|
440
|
-
Node 插件在运行 CLI、SDK 或 MCP Server 的宿主上执行,不随浏览器或 connector
|
|
444
|
+
Node 插件在运行 CLI、SDK 或 MCP Server 的宿主上执行,不随浏览器或 connector 转发。插件读取的文件路径属于该执行宿主;需要浏览器执行的操作应使用明确的浏览器目标。安装宿主可通过能力目录提供按需安装信息。
|
|
441
445
|
|
|
442
446
|
## 支持的操作
|
|
443
447
|
|
|
@@ -445,7 +449,7 @@ Node 插件在运行 CLI、SDK 或 MCP Server 的宿主上执行,不随浏览
|
|
|
445
449
|
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
446
450
|
| API 调试 | `tab_checkpoint` `get_requests` `get_logs` `get_events` |
|
|
447
451
|
| Mock & 规则 | `set_mock` `get_mocks` `set_rule` `get_rules` |
|
|
448
|
-
| 网络请求 | `send_request` `
|
|
452
|
+
| 网络请求 | `send_request` `request_permission` |
|
|
449
453
|
| 标签页 | `tab_open` `tab_list` `tab_close` |
|
|
450
454
|
| 页面感知 | `page_snapshot` `page_inspect` `page_screenshot` `page_frames` |
|
|
451
455
|
| 页面操作 | `page_click` `page_drag` `page_type` `page_hover` `page_scroll` `page_press` `page_wait` `page_navigate` `page_upload` `page_eval` |
|
|
@@ -586,7 +590,7 @@ SDK 使用 `tasks.checkpoint(taskId)` / `tasks.resume(taskId)`。当前仅支持
|
|
|
586
590
|
|
|
587
591
|
插件 CLI 调用失败以非零退出码和 stderr JSON `{ "error": { "code", "message" } }` 返回,保留 SDK/MCP 的 code,不输出任意异常附加字段。`PLUGIN_TASK_WAIT_TIMEOUT` 仅停止等待;`PLUGIN_EXECUTION_CANCELLED` / `PLUGIN_EXECUTION_DEADLINE` 表示执行取消或到期;`PLUGIN_PAGE_TARGET_CHANGED` / `PLUGIN_PAGE_INVALIDATED` 要求重新获取页面实例;`PLUGIN_STATE_CONFLICT` 需重新读取 CAS 版本;`PLUGIN_MODEL_CAPABILITY_UNSUPPORTED` 表示声明能力不支持;`PLUGIN_INVOCATION_CYCLE` / `PLUGIN_INVOCATION_DEPTH` 拒绝循环或过深组合。进程树无法确认停止时保留失败记录并拒绝恢复。
|
|
588
592
|
|
|
589
|
-
|
|
593
|
+
工具箱内显式页面 attach 缺少站点授权时会申请权限,由用户确认后继续;后台页面运行时不会自动弹窗申请。
|
|
590
594
|
|
|
591
595
|
模型列表可通过 SDK `PluginModelClient.list()` 或工具箱 `host.model.list` 查询。模型项携带可用时的 `reasoningEfforts` 和 `defaultReasoningEffort`,界面按模型支持范围提供思考级别。宿主合并并发发现请求,已有模型目录立即返回,过期后后台刷新;刷新失败保留可用目录并短暂退避,不创建推理会话。状态批量读取使用 `get-many` 和 `keys`(SDK `getMany`、工具箱 `host.state.getMany`),一次读取同一 revision 的最多 128 个键,含缺失键的 CAS version,结果上限 1 MiB。
|
|
592
596
|
|
|
@@ -597,7 +601,7 @@ SDK 使用 `tasks.checkpoint(taskId)` / `tasks.resume(taskId)`。当前仅支持
|
|
|
597
601
|
|
|
598
602
|
## 受管理插件目录
|
|
599
603
|
|
|
600
|
-
安装宿主可通过 client/plugin 的 replaceOfficialPluginCatalog(revision, plugins) 保存已选 preset 的插件目录。revision 为确切清单摘要,每项声明 description、skill 和安装器参数 installArgs,可选 setup 分组。目录以原子文件替换保存于当前插件注册目录的 .official-catalog.json
|
|
604
|
+
安装宿主可通过 client/plugin 的 replaceOfficialPluginCatalog(revision, plugins) 保存已选 preset 的插件目录。revision 为确切清单摘要,每项声明 description、skill 和安装器参数 installArgs,可选 setup 分组。目录以原子文件替换保存于当前插件注册目录的 .official-catalog.json;目录默认为空。安装源码与 preset 的信任校验由安装宿主负责,目录本身不触发安装。
|
|
601
605
|
|
|
602
606
|
findOfficialPlugin/listOfficialPluginNames 每次读取当前目录,继续复用同一 setup 入口完成缺失插件安装。readOfficialPluginCatalog 向安装状态页报告损坏;普通插件和浏览器命令忽略损坏的可选目录,不影响已注册能力。OFFICIAL_PLUGINS 兼容导出反映本进程最近读取的目录。
|
|
603
607
|
|
package/dist/actionTimeouts.js
CHANGED
|
@@ -26,9 +26,6 @@ export function resolveActionTimeoutSec(action, data, fallback = 15) {
|
|
|
26
26
|
: 0;
|
|
27
27
|
return Math.max(fallback, Math.ceil(((data.timeout ?? 30000) + transferTimeoutMs + 5000) / 1000));
|
|
28
28
|
}
|
|
29
|
-
if (action === 'send_protocol_request') {
|
|
30
|
-
return Math.max(fallback, Math.ceil(((data.timeout ?? 10000) + 5000) / 1000));
|
|
31
|
-
}
|
|
32
29
|
if (action === 'page_download')
|
|
33
30
|
return Math.max(fallback, Math.ceil((Number(data.timeout ?? 30000) + 10000) / 1000));
|
|
34
31
|
if (action === 'page_wait') {
|
package/dist/bin.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import { CLIENT_VERSION } from './runtimePaths.js';
|
|
3
|
+
const args = process.argv.slice(2);
|
|
4
|
+
if (args[0] === 'guide') {
|
|
5
|
+
const { runGuideCli } = await import('./guide.js');
|
|
6
|
+
process.exitCode = await runGuideCli(args.slice(1));
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
const { runMearlCli } = await import('./cli.js');
|
|
10
|
+
process.exitCode = await runMearlCli(args);
|
|
11
|
+
const { scheduleCliAutoUpdate } = await import('./autoUpdate.js');
|
|
12
|
+
scheduleCliAutoUpdate(CLIENT_VERSION);
|
|
13
|
+
}
|
package/dist/capabilityHelp.js
CHANGED
|
@@ -42,7 +42,6 @@ export function formatCapabilityHelp(catalog, detailed = false) {
|
|
|
42
42
|
if (!catalog)
|
|
43
43
|
return '当前浏览器能力暂不可读;以下为静态帮助。连接后再次运行 --help 可查看已安装 App。';
|
|
44
44
|
const lines = [
|
|
45
|
-
...(catalog.actions.includes('send_protocol_request') ? ['已安装的可选协议命令:send_protocol_request(沿用 --payload 参数;--help 查看用法)', ''] : []),
|
|
46
45
|
'当前浏览器已安装的 App:',
|
|
47
46
|
];
|
|
48
47
|
if (!catalog.apps.entries.length)
|
package/dist/cli.js
CHANGED
|
@@ -61,6 +61,7 @@ const CATEGORIES = [
|
|
|
61
61
|
},
|
|
62
62
|
];
|
|
63
63
|
const BUILTIN_COMMANDS = [
|
|
64
|
+
{ name: 'guide', description: '按需读取与当前 CLI 配套的本地指南', examples: ['guide', 'guide environment'] },
|
|
64
65
|
{
|
|
65
66
|
name: 'run',
|
|
66
67
|
description: '执行预注入 mearl SDK 的 Node.js 脚本',
|
|
@@ -128,6 +129,7 @@ function printUsage(commandName, version) {
|
|
|
128
129
|
` ${commandName} <action> --help`,
|
|
129
130
|
` ${commandName} check [选项]`,
|
|
130
131
|
` ${commandName} run [--file <path>] [选项]`,
|
|
132
|
+
` ${commandName} guide [topic]`,
|
|
131
133
|
'',
|
|
132
134
|
'常用选项:',
|
|
133
135
|
' --payload <json> 直接传入 JSON 参数',
|
|
@@ -143,6 +145,7 @@ function printUsage(commandName, version) {
|
|
|
143
145
|
' -v, --version 查看版本',
|
|
144
146
|
'',
|
|
145
147
|
'内置命令:',
|
|
148
|
+
` ${'guide'.padEnd(TOP_LEVEL_COMMAND_NAME_WIDTH)}读取本地核心流程或单个专题;--list 查看主题`,
|
|
146
149
|
` ${'run'.padEnd(TOP_LEVEL_COMMAND_NAME_WIDTH)}执行 Node.js 脚本,支持浏览器 API 与 Skill 命令`,
|
|
147
150
|
` ${'check'.padEnd(TOP_LEVEL_COMMAND_NAME_WIDTH)}检查指定浏览器,未指定时检查全部已连接浏览器`,
|
|
148
151
|
];
|
|
@@ -513,7 +516,7 @@ async function runPluginCli(args, commandName) {
|
|
|
513
516
|
console.log(plugin.version ?? 'unknown');
|
|
514
517
|
return 0;
|
|
515
518
|
}
|
|
516
|
-
if (parsed.help || (!parsed.pluginArgs?.length && !parsed.hasPayload)) {
|
|
519
|
+
if (parsed.help || (!plugin.defaultCommand && !parsed.pluginArgs?.length && !parsed.hasPayload && !Object.keys(parsed.pluginInput ?? {}).length)) {
|
|
517
520
|
console.error(plugin.help(`${commandName} ${parsed.action}`, parsed.pluginArgs?.[0]));
|
|
518
521
|
return 0;
|
|
519
522
|
}
|
|
@@ -563,6 +566,10 @@ function writeOutput(action, result, outputPath) {
|
|
|
563
566
|
export async function runMearlCli(args, options = {}) {
|
|
564
567
|
const commandName = options.commandName ?? 'mearl';
|
|
565
568
|
const clientVersion = options.clientVersion ?? CLIENT_VERSION;
|
|
569
|
+
if (args[0] === 'guide') {
|
|
570
|
+
const { runGuideCli } = await import('./guide.js');
|
|
571
|
+
return runGuideCli(args.slice(1), commandName, clientVersion);
|
|
572
|
+
}
|
|
566
573
|
if (args[0] === 'run') {
|
|
567
574
|
const { runScriptCli } = await import('./scriptCli.js');
|
|
568
575
|
return runScriptCli(args.slice(1), commandName);
|
package/dist/commands.d.ts
CHANGED
|
@@ -368,17 +368,6 @@ export declare const COMMANDS: readonly [{
|
|
|
368
368
|
readonly notes: readonly ["需要托管认证的接口使用相应能力插件;不要手工传入其管理的凭据。", "body 不能与 formData/files 组合;bodyFile 以原始字节发送,不包 multipart boundary,不能用于 GET/HEAD。", "bodyFile.filePath 属于 native-host,最大 40 MiB;base64 解码后最大 7 MiB。multipart 不手工设置 Content-Type,由 Mearl 生成 boundary。", "filePath 属于请求执行宿主;无法引用远端路径时用 base64。"];
|
|
369
369
|
readonly result: readonly ["返回 status、statusText、headers、body、bodyType、attachedCookieCount;bodyType 为 json/text/base64,二进制 body 需解码后保存。"];
|
|
370
370
|
readonly examples: ["send_request --payload '{\"url\":\"https://api.example.com/data\",\"method\":\"GET\"}'", "send_request --payload '{\"url\":\"https://api.example.com/upload\",\"method\":\"POST\",\"formData\":{\"folder\":\"assets\"},\"files\":[{\"fieldName\":\"file\",\"filePath\":\"/absolute/path/image.png\"}]}'", "send_request --payload '{\"url\":\"https://api.example.com/upload\",\"method\":\"POST\",\"bodyFile\":{\"filePath\":\"/absolute/path/image.png\",\"contentType\":\"image/png\"}}'"];
|
|
371
|
-
}, {
|
|
372
|
-
readonly name: "send_protocol_request";
|
|
373
|
-
readonly description: "通过已安装的请求协议能力,使用目标浏览器 Cookie 发送请求";
|
|
374
|
-
readonly params: [{
|
|
375
|
-
readonly name: "timeout";
|
|
376
|
-
readonly type: "number";
|
|
377
|
-
readonly description: "宿主超时毫秒数;协议能力负责校验具体参数";
|
|
378
|
-
}];
|
|
379
|
-
readonly notes: readonly ["需要安装提供 request.protocol 的能力包;参数、签名、网关与响应协议由能力包定义。", "使用 --payload 传递协议文档声明的参数;核心不设置业务参数默认值。"];
|
|
380
|
-
readonly result: readonly ["返回 success、response、requestData;业务结果由协议定义。"];
|
|
381
|
-
readonly examples: ["send_protocol_request --payload '{\"operation\":\"example\",\"data\":{}}'"];
|
|
382
371
|
}, {
|
|
383
372
|
readonly name: "browser_list";
|
|
384
373
|
readonly description: "列出所有浏览器及其连接状态、类型和 Provider 托管实例信息";
|
package/dist/commands.js
CHANGED
|
@@ -368,19 +368,6 @@ export const COMMANDS = [
|
|
|
368
368
|
`send_request --payload '{"url":"https://api.example.com/upload","method":"POST","bodyFile":{"filePath":"/absolute/path/image.png","contentType":"image/png"}}'`,
|
|
369
369
|
],
|
|
370
370
|
},
|
|
371
|
-
{
|
|
372
|
-
name: 'send_protocol_request',
|
|
373
|
-
description: '通过已安装的请求协议能力,使用目标浏览器 Cookie 发送请求',
|
|
374
|
-
params: [
|
|
375
|
-
{ name: 'timeout', type: 'number', description: '宿主超时毫秒数;协议能力负责校验具体参数' },
|
|
376
|
-
],
|
|
377
|
-
notes: [
|
|
378
|
-
'需要安装提供 request.protocol 的能力包;参数、签名、网关与响应协议由能力包定义。',
|
|
379
|
-
'使用 --payload 传递协议文档声明的参数;核心不设置业务参数默认值。',
|
|
380
|
-
],
|
|
381
|
-
result: ['返回 success、response、requestData;业务结果由协议定义。'],
|
|
382
|
-
examples: [`send_protocol_request --payload '{"operation":"example","data":{}}'`],
|
|
383
|
-
},
|
|
384
371
|
// ── 浏览器 ────────────────────────────────────────────────────────────────
|
|
385
372
|
{
|
|
386
373
|
name: 'browser_list',
|
package/dist/guide.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** One catalog drives CLI navigation and the build-time resource copy. */
|
|
2
|
+
export declare const GUIDE_TOPICS: readonly [{
|
|
3
|
+
readonly id: "core";
|
|
4
|
+
readonly file: "core.md";
|
|
5
|
+
readonly title: "核心工作流与按需导航";
|
|
6
|
+
}, {
|
|
7
|
+
readonly id: "automation";
|
|
8
|
+
readonly file: "api-page-automation.md";
|
|
9
|
+
readonly title: "控件、文件、弹窗与结果判读";
|
|
10
|
+
}, {
|
|
11
|
+
readonly id: "run";
|
|
12
|
+
readonly file: "scripting.md";
|
|
13
|
+
readonly title: "SDK、代码组合、超时与不确定结果";
|
|
14
|
+
}, {
|
|
15
|
+
readonly id: "debugging";
|
|
16
|
+
readonly file: "api-page-debugging.md";
|
|
17
|
+
readonly title: "布局、遮挡、iframe 与视觉验证";
|
|
18
|
+
}, {
|
|
19
|
+
readonly id: "environment";
|
|
20
|
+
readonly file: "api-page-environment.md";
|
|
21
|
+
readonly title: "设备、App、时区与 Cookie";
|
|
22
|
+
}, {
|
|
23
|
+
readonly id: "diagnostics";
|
|
24
|
+
readonly file: "api-request-debugging.md";
|
|
25
|
+
readonly title: "接口、日志、埋点、权限与 Mock";
|
|
26
|
+
}, {
|
|
27
|
+
readonly id: "browsers";
|
|
28
|
+
readonly file: "api-browser-setup.md";
|
|
29
|
+
readonly title: "目标、会话、远端与 Provider";
|
|
30
|
+
}, {
|
|
31
|
+
readonly id: "cooperation";
|
|
32
|
+
readonly file: "browser-tool-cooperation.md";
|
|
33
|
+
readonly title: "与其他浏览器工具协作";
|
|
34
|
+
}, {
|
|
35
|
+
readonly id: "extensions";
|
|
36
|
+
readonly file: "related-skills.md";
|
|
37
|
+
readonly title: "专用 Skill 与扩展开发入口";
|
|
38
|
+
}, {
|
|
39
|
+
readonly id: "troubleshooting";
|
|
40
|
+
readonly file: "troubleshooting.md";
|
|
41
|
+
readonly title: "安装、连接与环境排查";
|
|
42
|
+
}];
|
|
43
|
+
export declare function renderGuideLinks(source: string, commandName?: string): string;
|
|
44
|
+
/** Only packaged files are read; unknown IDs never become filesystem paths. */
|
|
45
|
+
export declare function readGuide(id: string, commandName?: string, version?: string): Promise<string>;
|
|
46
|
+
export declare function runGuideCli(args: string[], commandName?: string, version?: string): Promise<number>;
|
package/dist/guide.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { CLIENT_VERSION } from './runtimePaths.js';
|
|
3
|
+
/** One catalog drives CLI navigation and the build-time resource copy. */
|
|
4
|
+
export const GUIDE_TOPICS = [
|
|
5
|
+
{ id: 'core', file: 'core.md', title: '核心工作流与按需导航' },
|
|
6
|
+
{ id: 'automation', file: 'api-page-automation.md', title: '控件、文件、弹窗与结果判读' },
|
|
7
|
+
{ id: 'run', file: 'scripting.md', title: 'SDK、代码组合、超时与不确定结果' },
|
|
8
|
+
{ id: 'debugging', file: 'api-page-debugging.md', title: '布局、遮挡、iframe 与视觉验证' },
|
|
9
|
+
{ id: 'environment', file: 'api-page-environment.md', title: '设备、App、时区与 Cookie' },
|
|
10
|
+
{ id: 'diagnostics', file: 'api-request-debugging.md', title: '接口、日志、埋点、权限与 Mock' },
|
|
11
|
+
{ id: 'browsers', file: 'api-browser-setup.md', title: '目标、会话、远端与 Provider' },
|
|
12
|
+
{ id: 'cooperation', file: 'browser-tool-cooperation.md', title: '与其他浏览器工具协作' },
|
|
13
|
+
{ id: 'extensions', file: 'related-skills.md', title: '专用 Skill 与扩展开发入口' },
|
|
14
|
+
{ id: 'troubleshooting', file: 'troubleshooting.md', title: '安装、连接与环境排查' },
|
|
15
|
+
];
|
|
16
|
+
export function renderGuideLinks(source, commandName = 'mearl') {
|
|
17
|
+
return source.replace(/\[([^\]\n]+)\]\(([^)\s]+)\)/g, (original, label, href) => {
|
|
18
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith('#'))
|
|
19
|
+
return original;
|
|
20
|
+
const [file, section] = href.replace(/^\.\//, '').split('#');
|
|
21
|
+
const topic = GUIDE_TOPICS.find(topic => topic.file === file);
|
|
22
|
+
if (!topic)
|
|
23
|
+
throw new Error(`指南包含未打包的本地链接:${href}`);
|
|
24
|
+
return `${label}(\`${commandName} guide ${topic.id}\`${section ? `,小节 \`${section}\`` : ''})`;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/** Only packaged files are read; unknown IDs never become filesystem paths. */
|
|
28
|
+
export async function readGuide(id, commandName = 'mearl', version = CLIENT_VERSION) {
|
|
29
|
+
const topic = GUIDE_TOPICS.find(topic => topic.id === id);
|
|
30
|
+
if (!topic)
|
|
31
|
+
throw new Error(`未知指南主题:${id}。用 ${commandName} guide --list 查看主题。`);
|
|
32
|
+
let source;
|
|
33
|
+
try {
|
|
34
|
+
source = readFileSync(new URL(`./guides/${topic.file}`, import.meta.url), 'utf8');
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
throw new Error(`当前安装缺少指南资源:${topic.id}。请通过当前发行版的安装入口修复安装包;参数可用 ${commandName} <action> --help 查询。`, { cause: error });
|
|
38
|
+
}
|
|
39
|
+
let management;
|
|
40
|
+
if (source.includes('{{setup}}')) {
|
|
41
|
+
const { readInstallationManagement, setupManagementCommand } = await import('@mearl/daemon-core/installation');
|
|
42
|
+
const installed = await readInstallationManagement();
|
|
43
|
+
const command = setupManagementCommand(installed.setup ?? { package: '@mearl/setup', version: 'beta' });
|
|
44
|
+
source = source.replaceAll('{{setup}}', command);
|
|
45
|
+
management = installed.source ? `管理入口:${command};沿用本机已选 preset。` : `首次安装入口:${command};本机尚未选择 preset,组织发行环境使用其首次安装指引。`;
|
|
46
|
+
}
|
|
47
|
+
return [
|
|
48
|
+
`[mearl:guide] CLI ${version} · ${topic.id}`,
|
|
49
|
+
'本指南随 CLI 分发;扩展、远端宿主与已安装能力仍须按目标实际帮助核验。',
|
|
50
|
+
...(management ? [management] : []),
|
|
51
|
+
'',
|
|
52
|
+
renderGuideLinks(source.trim(), commandName),
|
|
53
|
+
'',
|
|
54
|
+
`其他主题:${commandName} guide --list;具体参数:${commandName} <action> --help。`,
|
|
55
|
+
`[mearl:guide] end ${topic.id}`,
|
|
56
|
+
].join('\n');
|
|
57
|
+
}
|
|
58
|
+
export async function runGuideCli(args, commandName = 'mearl', version = CLIENT_VERSION) {
|
|
59
|
+
const list = () => GUIDE_TOPICS.map(topic => ` ${topic.id.padEnd(18)}${topic.title}`).join('\n');
|
|
60
|
+
if (args.length === 1 && (args[0] === '--help' || args[0] === '-h')) {
|
|
61
|
+
console.log(`${commandName} guide — CLI ${version}\n\n用法:${commandName} guide [topic]\n ${commandName} guide --list\n\n默认只读 core;按需读取单个专题。只读本地资源,不连接浏览器或检查更新。\n\n${list()}`);
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
if (args.length === 1 && args[0] === '--list') {
|
|
65
|
+
console.log(`${commandName} guide — CLI ${version}\n${list()}`);
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
if (args.length > 1 || args[0]?.startsWith('-') || !GUIDE_TOPICS.some(topic => topic.id === (args[0] ?? 'core'))) {
|
|
69
|
+
console.error(`未知指南主题或选项:${args.join(' ')}。用 ${commandName} guide --list 查看主题。`);
|
|
70
|
+
return 2;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
console.log(await readGuide(args[0] ?? 'core', commandName, version));
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
78
|
+
return 1;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Mearl API 场景:浏览器初始化与管理
|
|
2
|
+
|
|
3
|
+
需要管理控制会话、选择远端 connector、在同一 connector 下选择浏览器,或列出、启动、关闭、释放普通及 Provider 托管浏览器实例时读取本文件。设备、时区和 Cookie 操作见 [页面测试环境准备](./api-page-environment.md)。
|
|
4
|
+
|
|
5
|
+
## 目录
|
|
6
|
+
|
|
7
|
+
- [浏览器实例](#browser-instances)
|
|
8
|
+
- [控制会话与人工交接](#control-sessions)
|
|
9
|
+
- [目标宿主与高级路由参数](#cloud-connectors)
|
|
10
|
+
- [可选浏览器 Provider](#browser-providers)
|
|
11
|
+
|
|
12
|
+
<a id="browser-instances"></a>
|
|
13
|
+
|
|
14
|
+
## 浏览器实例
|
|
15
|
+
|
|
16
|
+
`browser_list` 返回普通浏览器和 Provider 托管浏览器。顶层包含 `count`、`connectedCount`、`defaultBrowserId`、`browsers`,部分来源发现失败时还会返回 `warnings`。公共字段包括 `browserId`、`name`、`type` 和 `status`:
|
|
17
|
+
|
|
18
|
+
- `type` 为 `regular` 或 `managed`。
|
|
19
|
+
- `status` 为 `connected`、`pairing`、`running_disconnected` 或 `stopped`;只有 `connected` 可作为页面操作目标。
|
|
20
|
+
- Provider 实例还会返回 `provider`、`ownership`、`persistence`、`operations`、`references`、`details` 和可选的 `pairing`。用 `operations` 判断当前可执行的生命周期操作。
|
|
21
|
+
|
|
22
|
+
`browser_launch` 默认启动本地独立 Chrome。外部 Provider 使用以下通用结构,具体 `providerOptions` 必须通过 `mearl <providerId>` 查询:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
mearl browser_launch --payload '{"provider":"<providerId>","providerOptions":{}}'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
本机与远端 connector 同时存在时,用 `--local`、`--connector <id|name>`,或用 `--browser <global-id>` 同时指定宿主和 Cookie/UA 的来源浏览器。启动响应中的全局 `browserId` 用于首次操作和交付;建立控制会话后,后续操作由 `--session` 固定目标,无会话时继续传选定的 `--browser`。
|
|
29
|
+
|
|
30
|
+
`browser_close` 关闭托管实例;`deleteProfile: true` 表示同时删除 Provider 持久化数据。是否支持关闭、删除、启动、同步 Cookie、detach 或访问入口,以实例的 `operations` 和 Provider 命令帮助为准。
|
|
31
|
+
|
|
32
|
+
不带 `--session` 的 `browser_release` 通过 `--browser <id|名称>` 选择普通浏览器,只在用户明确要求结束调试、退出接管或清理环境时调用。它会释放 debugger/CDP 控制,清理依赖当前调试连接的 Mock、设备/时区模拟与持续采集,保留浏览器和标签页;存在活动控制会话时拒绝浏览器级释放。用户要求的终态依赖这些效果时保持控制。会话任务的收尾见[控制会话](#control-sessions);Provider 的 detach 或其他收尾遵循 `mearl <providerId>`。
|
|
33
|
+
|
|
34
|
+
浏览器访问入口不会出现在 `browser_list` 中。只有用户明确需要时才运行 `mearl check --browser <id|名称>`,并把返回的 `browserAccess.url` 按敏感信息处理,不写入日志或转发给无关方。
|
|
35
|
+
|
|
36
|
+
<a id="control-sessions"></a>
|
|
37
|
+
|
|
38
|
+
## 控制会话与人工交接
|
|
39
|
+
|
|
40
|
+
扩展免打扰默认开启;`tab_open active:true` 为 HTTP(S) 页面启用后台焦点模拟,新页在首次导航前生效,不切换标签页或窗口。关闭免打扰后,`active:true` 才会真实激活并恢复最小化窗口。普通页面操作不会自动启用模拟。`activation.mode` 区分 `background` / `foreground`;模拟后的 `document.visibilityState` 可为 `visible`,不能据此判断窗口在前台。交给用户、释放控制或扩展连接断开时恢复,`preserveEnvironment` 不保留此临时状态;交还后按需用 `reuse:"require", active:true` 再次激活。未带会话时通过 `browser_release` 清理。纯 CDP 和外部 Provider 沿用各自激活能力。
|
|
41
|
+
|
|
42
|
+
首次 `tab_open` / `page_snapshot` / `page_screenshot` 传 `--session new`,后续命令传 `--session <sessionId>`,使用响应的完整 `session.sessionId`。会话绑定浏览器与传输,页面操作同时指定 `tabId`;仅支持视觉感知的 Provider 可从截图建立会话。
|
|
43
|
+
|
|
44
|
+
扩展新建的后台页面默认进入折叠的 `Mearl` 标签组,前台新页进入展开的 `Mearl · 待操作` 组;两个管理组复用同一个已有窗口,页面打开的后续标签继承源组状态。复用用户的普通页面保留原窗口和分组,`tab_open.group` 可指定其他分组。接管时会话内的管理页移入待操作组,交还后收回 `Mearl` 组;用户正在查看组内其他页面或窗口没有其他页面时保留展开状态。纯 CDP 和外部 Provider 沿用各自的页面组织方式。
|
|
45
|
+
|
|
46
|
+
常规响应的 session 只包含 ID、浏览器、传输、状态、代次和可选 reason,不重复标签页清单。页面列表用 tab_list 查询;browser_control 的交接结果还包含会话页面的 tabs。CLI、SDK、MCP 使用同一结构。
|
|
47
|
+
|
|
48
|
+
会话自动记录新建与复用页面,串行执行同页面操作,并阻止其他会话或无会话调用占用其页面。不同任务使用独立会话;会话不隔离 Cookie 与存储,账号或浏览器共享资源冲突时使用独立浏览器。Extension、CDP 和采用 Mearl 控制的 Provider 共用这些规则;声明外部控制的 Provider(如 Ego)拒绝 Mearl 会话,交接和收尾沿用原工具。
|
|
49
|
+
|
|
50
|
+
`browser_control` 的 `operation:"handoff"` 将控制权交给用户,`tabId` 指定会话页面,`reason` 说明需要用户处理的事项。用户在 Mearl Popup 浏览器管理页接管和交还;无扩展 UI 的环境,在用户明确确认交还后调用 `operation:"resume"`。
|
|
51
|
+
|
|
52
|
+
- `CONTROL_SESSION_PAUSED`:停止派发动作,交还后重新快照或截图,根据当前状态继续,不重放未完成批次;已派发的操作可能仍然生效。
|
|
53
|
+
- `CONTROL_SESSION_NOT_FOUND`:会话不存在或已失效,在原浏览器以普通调用执行 `tab_list`,获取当前 `tabId` 并检查操作结果,再按任务需要建立新会话,避免重复写操作。
|
|
54
|
+
|
|
55
|
+
任务结束调用 `browser_release --session <sessionId>`,释放该会话的控制归属并清理临时文件,默认保留页面、Mock 与模拟环境。payload 支持以下保留与清理选项:
|
|
56
|
+
|
|
57
|
+
| 参数 | 行为 |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| `closeCreatedTabs:true` | 关闭确认不再需要的会话新建页;复用的用户页面始终保留 |
|
|
60
|
+
| `keepTabIds` | 配合关闭新建页时保留结果页面 |
|
|
61
|
+
| `preserveEnvironment:false` | 清理会话页面的 Mock 与模拟环境 |
|
|
62
|
+
| `keepArtifactIds` | 保留指定浏览器文件产物,其他会话临时文件清理 |
|
|
63
|
+
|
|
64
|
+
会话释放保留 Provider 实例;关闭、退出接管和同步 Cookie 使用原有实例操作,存在活动控制会话时先释放所属会话。
|
|
65
|
+
|
|
66
|
+
<a id="cloud-connectors"></a>
|
|
67
|
+
|
|
68
|
+
## 目标宿主与高级路由参数
|
|
69
|
+
|
|
70
|
+
默认不传宿主、连接或传输覆盖参数。`browser_list` 会同时查询当前机器和全部远端 connector,结果名称带机器前缀,`source` 包含远端机器的 `connectorId` 和 `connectorName`,全局 `browserId` 可跨 CLI 调用并自动选择本地或远端传输。需要指定目标时优先传这个全局 ID;完整实例名称跨来源唯一时也可由客户端解析。建立控制会话后,后续 action 只需传 `--session <sessionId>` 和页面参数。不要依赖名称片段的模糊匹配。
|
|
71
|
+
|
|
72
|
+
| 参数 | 作用 | 使用时机 |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| `--local` | 只使用当前机器 | 限定本机清单、检查,或让 `browser_launch` 落到本机 |
|
|
75
|
+
| `--connector <id\|name>` | 只使用一台远端 connector | 限定远端清单、检查或启动宿主;名称或 ID 片段必须唯一 |
|
|
76
|
+
| `--cdp` | 本机强制使用 CDP,并隐含 `--local` | 需要验证或绕过 Extension 控制链路时 |
|
|
77
|
+
| `--server <url>` | 覆盖当前调用使用的 cloud-server URL | 自动配置不可用或临时排障时 |
|
|
78
|
+
|
|
79
|
+
`--local` / `--cdp` 不能与 `--connector` 同时使用。全局远端 `browserId` 已包含 connector,普通页面 action 不重复传 `--connector`。`--server` 的解析顺序见 `mearl check --help`,连接排障见[环境安装与连接排查](./troubleshooting.md#cloud-agent)。高级诊断命令 `mearl connector_list` 只用于查看没有可列出浏览器的 connector,普通浏览器操作无需先调用它。
|
|
80
|
+
|
|
81
|
+
<a id="browser-providers"></a>
|
|
82
|
+
|
|
83
|
+
## 可选浏览器 Provider
|
|
84
|
+
|
|
85
|
+
Provider 的参数、配置和生命周期随独立包演进,Skill 只保留能力与安装入口。安装后必须读取 Provider 自带的当前帮助,不从本文件推断字段。
|
|
86
|
+
|
|
87
|
+
支持的平台和设备来源以已安装 Provider 的能力声明及配套 Skill 为准。
|
|
88
|
+
|
|
89
|
+
只询问能力或配置时不创建实例。执行任务前先运行 `mearl browser_list`:已有符合目标且 `status: "connected"` 的实例时直接复用;需要创建、配对、接入或收尾时,遵循对应 Provider 命令帮助。实例返回的全局 `browserId` 用于选择目标,无会话时后续统一页面 action 继续使用该 ID。
|
|
90
|
+
|
|
91
|
+
用户只要求移动视口、移动 UA、触摸事件或机型样式时使用设备模拟,无需创建外部 Provider 实例。通过 connector 接入远端浏览器属于控制链路,平台实例由对应 Provider 管理。
|
|
92
|
+
|
|
93
|
+
已有 Ego 任务的接入、只读候选查询及页面目标映射见[与其他浏览器工具协作](./browser-tool-cooperation.md)。该流程仅查询明确选择的本机 Provider,不广播查询全部来源。
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Mearl 场景:常规网页自动化
|
|
2
|
+
|
|
3
|
+
普通操作遵循 [核心工作流](./core.md)。本文件用于复杂控件、派发差异、文件操作或增量结果不足以决策的情况。布局、iframe、脚本与视觉验证见[页面排障](./api-page-debugging.md),设备/App 联动见[环境准备](./api-page-environment.md)。
|
|
4
|
+
|
|
5
|
+
## 打开与复用页面
|
|
6
|
+
|
|
7
|
+
`tabId` 标识当前浏览器与传输中的页面,取得后持续复用;控制会话通过 `--session` 固定目标与控制归属,页面动作仍传 `tabId`。`opened` / `reused` 区分新页与既有页,复用页应保留。移动态或 App 态首屏需在新建时设置;这些配置不应用于复用页,已有页使用对应设置 action。
|
|
8
|
+
|
|
9
|
+
需要激活已有页面时,对同 URL 使用 `reuse: "require", active: true`,避免另外新开页面。激活方式与临时状态恢复见[浏览器控制与人工交接](./api-browser-setup.md#control-sessions)。
|
|
10
|
+
|
|
11
|
+
## 快照范围与非语义控件
|
|
12
|
+
|
|
13
|
+
优先 viewport、局部根节点或 query,避免长页面完整输出;每次快照后只使用本次返回的新 ref。
|
|
14
|
+
|
|
15
|
+
快照文本中的 @eN 可直接用于后续操作,内部节点映射只保存在运行时;无名称、无 ref 且无状态的分组会折叠,控件状态与文本内容保留。
|
|
16
|
+
|
|
17
|
+
AX 空或稀疏时显式使用 dom;interactive 无 AX 控件时只回退到 AX viewport,不会自动切换来源。后台弹层仍在过渡时,等待明确可见条件,或用 full + query 核验结构。
|
|
18
|
+
|
|
19
|
+
自定义复选框、单选框或开关没有原生/ARIA 语义时,AX 可能只返回相邻文案。以文案 ref 为根,用 dom 和 ancestorDepth 1–3 获取局部容器,从其中浏览器标记的 clickable ref 定位控件,不直接点击静态文案或猜 CSS。用观察、局部快照或 inspect 验证状态。
|
|
20
|
+
|
|
21
|
+
<a id="observe"></a>
|
|
22
|
+
|
|
23
|
+
## 动作结果判读
|
|
24
|
+
|
|
25
|
+
交互 action 默认返回动作与增量观察。`page_eval` 默认裸执行,`run_actions` 的步骤不带内置观察。
|
|
26
|
+
|
|
27
|
+
`observe: false` 且未开启 diagnostics 时返回裸动作结果,没有 `action` 或 `observation`;开启 diagnostics 时返回 `{ action, diagnostics }`。按实际返回结构判断,不将缺少 `action.success` 当作失败。
|
|
28
|
+
|
|
29
|
+
- **未派发**:`action.stage: "precondition"` 时按 error 修正输入;过期 ref 重建最小快照。此时没有 observation/diagnostics。
|
|
30
|
+
- **确认不确定**:`dispatch-uncertain` 时先看 attempt 中的请求目标、解析目标、命中、坐标、派发模式和输入序列确认状态,再用 observation 或最小快照判断是否已生效,确认未生效后才重试。attempt 本身不证明效果。
|
|
31
|
+
- **目标差异**:`resolvedTarget`、`dispatchTarget`、`hitTarget` 只在目标存在有效差异时展开;兼容字段 tag/text 或 strategy/distance 的省略不表示缺少派发。无法确定可点击祖先时,actionableAncestorCandidates 仅供检查,不会自动改点。
|
|
32
|
+
- **通知**:effects.notifications 是高置信度通知;transient 表示已出现后消失,只读取文案,不继续点击。
|
|
33
|
+
- **控件与焦点**:effects.interactives 和 effects.focus 的 node.ref/selector 可继续操作。recreated 是同一语义控件被框架重建,不是新业务入口;状态未变时不会重复返回 value/checked。
|
|
34
|
+
- **普通文本**:effects.texts 按需返回短小可见文本的 selector 和当前 text,例如独立计数或金额;不推断业务含义,也不代表可点击。最多 8 项、每项 160 字符;隐藏、遮挡、复杂结构或超限时仍按快照建议核验。
|
|
35
|
+
- **快照建议**:按 fullSnapshotRecommended 和 snapshotReasons 补最小范围内容。已有通知/控件仍可能遗漏未分类结构;changed=true 而 effects 为空只说明没有高置信度信号,不能直接判失败。
|
|
36
|
+
- **导航**:mode=navigation 中 loaded 只表示文档完成,ready 才表示可继续。network-idle 表示相关请求静默;content-stable 是长连接情况下按内容与 skeleton/busy 状态判断的就绪兜底。
|
|
37
|
+
- **上下文重建**:mode=unavailable 时重新建立快照。delta 的 URL/标题未变化时可省略;同页请求用 network.ready/settledBy/pendingRequests 判断。
|
|
38
|
+
- **新页面**:openedTabs 只报告当前目标 Tab 直接打开的页面。ready=true 时直接对其 tabId 建立快照,无需 tab_list。
|
|
39
|
+
- **观察稳定**:quiet 已有限等待仍运行的动画;无限动画不阻塞。no-change 不自动要求快照;timeout 结合 snapshotReasons 回退。
|
|
40
|
+
|
|
41
|
+
DOM 增量覆盖主文档(scope=main-document);iframe、Shadow DOM、Canvas 或纯视觉变化可能不在覆盖范围。按目标补局部快照,跨域 iframe 或视觉任务用截图。
|
|
42
|
+
|
|
43
|
+
## page_click
|
|
44
|
+
|
|
45
|
+
文本多匹配时按候选 role/name 缩小范围或取 ref;原生 select 可按 option 文本点击,无需脚本改值。
|
|
46
|
+
|
|
47
|
+
auto/dom 对原生 option 执行选择并触发 input/change,返回 `selectionState.valueBefore/valueAfter/selectedAfter`。这是事件派发后的即时状态;`selectedAfter` 为 false 表示选项未保持选中(例如页面处理器恢复了旧值),先检查页面再决定下一步。后续异步变化仍需按业务状态核验。禁用的 select、option、optgroup 不会被强行选择。自定义 ARIA option 仍走普通点击。
|
|
48
|
+
|
|
49
|
+
selector/ref 目标被嵌套容器裁剪时会先滚入可见区;仍裁剪或被弹层覆盖则返回前置条件错误。用 page_inspect 的 hitTest.occludedBy 确认遮挡。派发前的命中检查允许目标自己的祖先/后代。
|
|
50
|
+
|
|
51
|
+
auto 在可见页按设备使用可信 mouse/touch,在隐藏页回退 DOM;响应的 dispatchMode、pointerType、fallbackReason 说明实际路径。显式 touch 要求可见,显式 mouse 会强制尝试可信输入;只有任务明确依赖真实用户手势时才激活。
|
|
52
|
+
|
|
53
|
+
## page_drag
|
|
54
|
+
|
|
55
|
+
起点自动滚入视口,但终点需同时可见;否则先滚动。坐标适合 Canvas 或组件内手势,ref 适合可见元素拖放。DOM 回退属于不可信事件;页面拒绝不可信事件时再接管为可见页可信输入。
|
|
56
|
+
|
|
57
|
+
## page_type
|
|
58
|
+
|
|
59
|
+
填写表单后根据 observation 检查目标值及联动控件;不要仅以输入成功判断表单已提交。
|
|
60
|
+
|
|
61
|
+
## page_hover
|
|
62
|
+
|
|
63
|
+
hover 展开菜单后需根据内容选择下一步时逐步执行;目标已确定且紧接点击时可用 run_actions。
|
|
64
|
+
|
|
65
|
+
## page_press
|
|
66
|
+
|
|
67
|
+
先确认焦点;隐藏页 DOM 输入只实现部分默认行为,依赖编辑、选择、方向键或页面滚动时用可见页 keyboard。用 defaultAction 判断是否模拟了所需语义。
|
|
68
|
+
|
|
69
|
+
原生 alert/confirm/prompt/beforeunload 会阻塞页面 JS。内置扩展与 CDP 路径通过 `page_snapshot` 返回 `dialog.id/type/message/defaultPrompt`,不运行 DOM 快照;中断回执中的弹窗文本有长度上限,完整读取时按需提高快照 `maxChars`。确认含义后用现有按键接口显式处理:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
mearl page_press --payload '{"tabId":12345,"dialogId":"<dialog.id>","key":"Escape"}'
|
|
73
|
+
mearl page_press --payload '{"tabId":12345,"dialogId":"<dialog.id>","key":"Enter","promptText":"仅用于 prompt"}'
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
普通 Enter 不处理原生弹窗;过期 ID 会拒绝,不自动接受或重试。该分支返回裸按键结果 `dispatchMode:"native-dialog"`,不进入 DOM 观察。处理后重新读页面:页面中已派发的 JS 可能继续执行,工具不会回放旧动作。动作已确认成功、仅后续观察被弹窗打断时,保留 `action.success:true` 并附带 `dialog`;没有确认回执时按 `dispatch-uncertain` 核验业务状态。
|
|
77
|
+
|
|
78
|
+
只能读取监测连接收到的弹窗事件。首次附加前已打开、由其他调试连接持有的弹窗可能阻塞 Chrome 的 Page.enable;此时有界超时并提示人工关闭后重试,不猜测弹窗内容或盲目接受。弹窗文本存储上限 65536 字符,超出时返回 `truncated:true`。
|
|
79
|
+
|
|
80
|
+
接受 prompt 时省略 `promptText` 会保留预填默认值,显式传 `""` 才清空;默认值超出存储上限时必须显式指定 `promptText`,避免提交被截断的值。
|
|
81
|
+
|
|
82
|
+
## page_scroll
|
|
83
|
+
|
|
84
|
+
用 moved、atTop/atBottom 判断是否移动或到达边界;自动发现内部容器时复用 containerSelector。ref 指向子节点时可使用 nearest 找可滚动祖先。需要读取滚动后的新视口时补 viewport 快照。
|
|
85
|
+
|
|
86
|
+
## page_wait
|
|
87
|
+
|
|
88
|
+
没有前置动作,或 observation 表明仍需等待且已有明确条件时再调用;以 selector/text/condition 表达业务就绪,固定时间只用于任务本身要求经过一段时间的情况。
|
|
89
|
+
|
|
90
|
+
服务端渲染页面的节点出现不等于客户端已可交互;需要时等待已确认的业务就绪信号(例如倒计时开始更新),避免输入被后续初始化覆盖。
|
|
91
|
+
|
|
92
|
+
等待结束后需要立即点击已确认目标时,在同一次 `mearl run` 中 await 等待并紧接点击,不把两个动作拆到不同模型轮次:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
mearl run --session '<sessionId>' --timeout 240 <<'JS'
|
|
96
|
+
const tabId = 12345;
|
|
97
|
+
const ready = await mearl.pageWait({
|
|
98
|
+
tabId,
|
|
99
|
+
condition: '!!document.querySelector("form button[type=submit]:not(:disabled)")',
|
|
100
|
+
timeout: 200000,
|
|
101
|
+
interval: 100,
|
|
102
|
+
});
|
|
103
|
+
if (!ready.waited) throw new Error('Target did not become ready');
|
|
104
|
+
console.log(await mearl.pageClick({ tabId, selector: 'form button[type=submit]:not(:disabled)' }));
|
|
105
|
+
JS
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
整体 `--timeout` 要覆盖等待时限及后续动作;条件等待最多 300000ms。这会省去模型往返,但两次动作仍有传输与派发延迟,不保证原子执行。点击后检查动作结果与业务确认状态;失败或派发不确定时不要自动重放整段脚本。
|
|
109
|
+
|
|
110
|
+
## page_navigate
|
|
111
|
+
|
|
112
|
+
在同一 tabId 中导航、刷新或前进/后退,完成后建立新快照。
|
|
113
|
+
|
|
114
|
+
<a id="file-operations"></a>
|
|
115
|
+
|
|
116
|
+
## page_upload
|
|
117
|
+
|
|
118
|
+
找到 input[type=file] 后直接上传,隐藏节点也可用。文件来源按以下方式指定:
|
|
119
|
+
|
|
120
|
+
- 调用方文件:`files:[{filePath:"/absolute/path"}]`,自动中转到浏览器宿主。
|
|
121
|
+
- 浏览器产物:`files:[{artifactId}]`,使用 `page_download` 返回的产物;会话产物继续使用所属会话。
|
|
122
|
+
- 浏览器或 Provider 宿主文件:`filePaths:["/absolute/path"]`,路径属于执行上传的宿主;外部平台的文件中转按 Provider 帮助执行。
|
|
123
|
+
|
|
124
|
+
`files` 与 `filePaths` 互斥。确认页面显示附件或上传成功的业务状态;外部 Provider 的文件能力按其当前帮助执行。
|
|
125
|
+
|
|
126
|
+
## page_download
|
|
127
|
+
|
|
128
|
+
先监听下载,再执行 `trigger` 点击并等待下载完成。`trigger` 使用不含 `tabId` 的 `page_click` 参数;目标页面由顶层 `tabId` 指定。
|
|
129
|
+
|
|
130
|
+
`--output <绝对路径>` 将文件保存到调用方;省略时返回 `download.artifactId`,文件保留在浏览器宿主,可用于后续 `page_upload`。扩展无法通过 referrer 确认下载来源时,传入已知 `url`。下载超时或归属不确定时,先检查浏览器下载状态,再决定后续操作,避免重复点击。
|
|
131
|
+
|
|
132
|
+
## 文件产物与传输
|
|
133
|
+
|
|
134
|
+
文件中转的单文件上限为 256 MiB。Extension/CDP 支持上传与下载;外部 Provider 按 `capabilities.actions` 声明的 `page_upload`、`page_download` 使用对应能力。无会话产物空闲一小时回收,会话产物默认随 `browser_release --session <sessionId>` 清理,需要保留时传 `keepArtifactIds`。宿主退出清理暂存文件,保存到调用方的结果文件保留。
|
|
135
|
+
|
|
136
|
+
## run_actions
|
|
137
|
+
|
|
138
|
+
仅批量执行无需中间判断的紧密序列。失败时只检查已完成步骤与 failedStep;后续步骤未执行,不能将部分成功当作整个任务完成。
|
|
139
|
+
|
|
140
|
+
连续填写等已知步骤可在同一次 `mearl run` 中使用 `observe:false`,最后检查提交前的表单状态或结果页;需要根据中间结果选目标时保留逐步观察。快照 `query` 必须是对象,例如 `{"query":{"text":"提交","exact":false}}`;切换标签用 `tab_open` 的 `reuse:"require",active:true`,刷新用 `page_navigate` 的 `refresh:true`,原生选项用 `page_click` 的 `role:"option"`。
|