@mearl/client 2.8.2 → 2.9.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 +21 -16
- package/dist/check.js +10 -1
- package/dist/cli.d.ts +16 -0
- package/dist/cli.js +81 -58
- package/dist/commands.d.ts +34 -10
- package/dist/commands.js +36 -8
- package/dist/generated-browser-action-protocol.d.ts +22 -2
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -69,6 +69,7 @@ mearl browser_launch --payload '{"name":"account-b","query":"test_account"}'
|
|
|
69
69
|
mearl browser_launch --payload '{"name":"cookie-copy","copyCookieDomains":["example.com"]}'
|
|
70
70
|
mearl browser_launch --payload '{"name":"cloud-login","provider":"agentbay","persistent":true,"userAgentMode":"desktop","copyCookieDomains":["example.com"],"url":"https://example.com/account"}'
|
|
71
71
|
mearl browser_launch --browser '<控制浏览器 browserId>' --payload '{"name":"cloud-login","provider":"agentbay","reuse":"require","copyCookieDomains":["example.com"]}'
|
|
72
|
+
mearl browser_launch --payload '{"name":"cloud-tools","provider":"agentbay","copyCookieDomains":"mearl-services"}'
|
|
72
73
|
mearl browser_launch --payload '{"name":"figma","userAgentMode":"desktop","copyCookieDomains":["figma.com"]}'
|
|
73
74
|
mearl browser_list
|
|
74
75
|
mearl browser_release
|
|
@@ -84,40 +85,44 @@ mearl browser_release
|
|
|
84
85
|
| `--payload <json>` | 直接传入 JSON 参数 |
|
|
85
86
|
| `--payload-file <path>` | 从文件读取 JSON 参数 |
|
|
86
87
|
| `--timeout <seconds>` | 请求超时时间 |
|
|
87
|
-
| `--compact` | 紧凑输出
|
|
88
|
+
| `--compact` | stdout 紧凑输出 |
|
|
88
89
|
| `--output <path>` | 将结果写入文件(如截图) |
|
|
89
90
|
| `--browser <id\|名称>` | 指定目标浏览器 |
|
|
90
91
|
| `--connector <id\|名称>` | 限制到一台远端机器 |
|
|
91
92
|
| `--local` | 只使用当前机器 |
|
|
93
|
+
| `--cdp` | 当前机器强制使用 CDP |
|
|
92
94
|
| `--server <url>` | 覆盖 cloud-server 地址 |
|
|
93
95
|
|
|
96
|
+
`check` 有独立的选项说明,运行 `mearl check --help` 查看。其中 `--json` 仅用于输出机器可读的检查结果。
|
|
97
|
+
|
|
94
98
|
## 支持的操作
|
|
95
99
|
|
|
96
|
-
| 分类
|
|
97
|
-
|
|
|
98
|
-
| API 调试
|
|
99
|
-
| Mock & 规则
|
|
100
|
-
|
|
|
101
|
-
| 标签页
|
|
102
|
-
| 页面操作
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
-
|
|
|
106
|
-
|
|
|
107
|
-
|
|
|
108
|
-
|
|
|
100
|
+
| 分类 | Actions |
|
|
101
|
+
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
102
|
+
| API 调试 | `tab_checkpoint` `get_requests` `get_logs` `get_events` `get_api_schema` |
|
|
103
|
+
| Mock & 规则 | `set_mock` `get_mocks` `set_rule` `get_rules` |
|
|
104
|
+
| 网络请求 | `send_request` `send_mtop_request` `request_domain_permission` |
|
|
105
|
+
| 标签页 | `tab_open` `tab_close` `tab_list` |
|
|
106
|
+
| 页面操作 | `page_click` `page_drag` `page_type` `page_hover` `page_scroll` `page_eval` `page_press` `page_wait` `page_navigate` `page_upload` |
|
|
107
|
+
| 组合调用 | `run_actions` |
|
|
108
|
+
| 页面感知 | `page_snapshot` `page_screenshot` `page_selected_element` `page_frames` |
|
|
109
|
+
| 环境与 Cookie | `set_device_emulation` `set_app_profile` `set_timezone` `get_cookie` `set_cookie` |
|
|
110
|
+
| 用户信息 | `get_user_info` |
|
|
111
|
+
| 录制 | `record_start` `record_stop` |
|
|
112
|
+
| TDBank | `tdbank_account` |
|
|
113
|
+
| 浏览器 | `browser_list` `browser_release` `browser_launch` `browser_close` |
|
|
109
114
|
|
|
110
115
|
页面交互动作(`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` 默认裸执行,可按需开启观察或诊断。
|
|
111
116
|
|
|
112
117
|
`page_snapshot` 默认返回完整 AX Tree;长列表可传 `mode: "viewport"`,只需要当前视口内的控件时传 `mode: "interactive"`,已知 CSS 区域时传 `rootSelector`,已有 ref 时传 `rootRef`(可用 `ancestorDepth` 向上补充上下文),只查找特定文案或角色时传 `query`。视口内缺少 AX 控件语义时,`interactive` 会自动回退到 viewport,并返回 `fallbackMode: "viewport"`。`maxNodes` / `maxChars` 截断会同时保留首尾内容。
|
|
113
118
|
|
|
114
|
-
重复文本点击可用 `page_click.scope` 限定 CSS / ref
|
|
119
|
+
重复文本点击可用 `page_click.scope` 限定 CSS / ref 子树;浏览器扩展后端中,容器或 iframe 内的点坐标使用 `coordinateSpace: "active-frame"`,CSS/text 查询可用 `frameId` 显式指定 frame。点击结果通过 `requestedTarget`、`resolvedTarget`、`dispatchTarget`、`hitTarget` 和 `coordinates` 区分请求语义、DOM 解析、派发节点与可信输入实际命中;ref 指向滚动容器内的子节点时,可用 `page_scroll.containerPolicy: "nearest"` 自动解析最近可滚动祖先。
|
|
115
120
|
|
|
116
121
|
`browser_list` 统一列出普通浏览器和托管浏览器。`type` 区分 `regular` / `managed`,`status` 区分 `connected` / `running_disconnected` / `stopped`;只有 `connected` 的浏览器可作为操作目标。
|
|
117
122
|
|
|
118
123
|
`browser_release` 仅在用户明确要求结束调试、退出接管或清理会话资源时使用,不作为普通任务收尾。它会释放当前目标浏览器的 debugger/CDP 控制和会话级临时状态(包括设备/时区模拟),保留浏览器及全部标签页;后续浏览器操作可自动重新建立控制。
|
|
119
124
|
|
|
120
|
-
托管浏览器使用独立 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` 只复用且不会意外创建计费会话。`copyCookieDomains`
|
|
125
|
+
托管浏览器使用独立 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` 只复用且不会意外创建计费会话。`copyCookieDomains` 传数组时把控制浏览器指定域的 Cookie 写入目标,传 `"mearl-services"` 时同步 Mearl 依赖平台登录态。Cookie 值不会经过 cloud-server,也不会出现在命令结果或日志中;`userAgentMode: "desktop"` 让本地实例使用匹配本机 Chrome 版本的桌面 UA,AgentBay 则复用当前控制浏览器的桌面 UA。`browser_list` 仅通过 `agentbayImageId`、`agentbayContextId` 和 `agentbayContextName` 返回 AgentBay 实例的稳定配置。无影浏览器串流入口包含临时访问凭据且会过期,因此不会写入实例记录或列表结果;需要时运行 `mearl check --browser <id|名称>` 实时获取,并按敏感信息处理。完整设计见 [托管浏览器与 TDBank 多账号设计](../../docs/managed-browsers.md)。
|
|
121
126
|
|
|
122
127
|
## 架构
|
|
123
128
|
|
package/dist/check.js
CHANGED
|
@@ -190,6 +190,13 @@ export async function runUnifiedCheck(options, dependencies = {}) {
|
|
|
190
190
|
const browserChecks = [];
|
|
191
191
|
await Promise.all(targets.map(async (browser) => {
|
|
192
192
|
try {
|
|
193
|
+
if (options.transport === 'cdp') {
|
|
194
|
+
await client.invoke('tab_list', {}, {
|
|
195
|
+
timeoutSec,
|
|
196
|
+
browser: browser.browserId,
|
|
197
|
+
transport: 'cdp',
|
|
198
|
+
});
|
|
199
|
+
}
|
|
193
200
|
const versions = (await client.invoke('get_versions', {}, {
|
|
194
201
|
timeoutSec,
|
|
195
202
|
browser: browser.browserId,
|
|
@@ -243,7 +250,9 @@ export async function runUnifiedCheck(options, dependencies = {}) {
|
|
|
243
250
|
status: accessUrlWarning ? 'warning' : 'ok',
|
|
244
251
|
label: accessUrlWarning
|
|
245
252
|
? 'Browser command path ready; AgentBay access URL unavailable'
|
|
246
|
-
:
|
|
253
|
+
: options.transport === 'cdp'
|
|
254
|
+
? 'CDP command path'
|
|
255
|
+
: 'Browser command path',
|
|
247
256
|
details,
|
|
248
257
|
},
|
|
249
258
|
versions: publicVersions,
|
package/dist/cli.d.ts
CHANGED
|
@@ -2,4 +2,20 @@ export interface MearlCliOptions {
|
|
|
2
2
|
commandName?: string;
|
|
3
3
|
clientVersion?: string;
|
|
4
4
|
}
|
|
5
|
+
export interface ParsedArgs {
|
|
6
|
+
action: string;
|
|
7
|
+
browser?: string;
|
|
8
|
+
compact: boolean;
|
|
9
|
+
connector?: string;
|
|
10
|
+
forceCdp: boolean;
|
|
11
|
+
json: boolean;
|
|
12
|
+
local: boolean;
|
|
13
|
+
outputPath?: string;
|
|
14
|
+
payload: Record<string, any>;
|
|
15
|
+
serverUrl?: string;
|
|
16
|
+
serverUrlSource?: string;
|
|
17
|
+
timeoutSec: number;
|
|
18
|
+
timeoutOption?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function parseClientArgs(args: string[]): ParsedArgs;
|
|
5
21
|
export declare function runMearlCli(args: string[], options?: MearlCliOptions): Promise<number>;
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
|
+
import { Command, CommanderError } from 'commander';
|
|
3
4
|
import { resolveActionTimeoutSec } from './actionTimeouts.js';
|
|
4
5
|
import { runUnifiedCheck, parseCheckTimeout } from './check.js';
|
|
5
6
|
import { commandInput, findCommandSuggestions, formatCommandSuggestions, } from './commandSuggestions.js';
|
|
@@ -14,7 +15,7 @@ const CATEGORIES = [
|
|
|
14
15
|
},
|
|
15
16
|
{ title: 'Mock & 规则', names: ['set_mock', 'get_mocks', 'set_rule', 'get_rules'] },
|
|
16
17
|
{
|
|
17
|
-
title: '
|
|
18
|
+
title: '网络请求',
|
|
18
19
|
names: ['send_request', 'send_mtop_request', 'request_domain_permission'],
|
|
19
20
|
},
|
|
20
21
|
{ title: '标签页', names: ['tab_open', 'tab_close', 'tab_list'] },
|
|
@@ -59,10 +60,6 @@ const BUILTIN_COMMANDS = [
|
|
|
59
60
|
];
|
|
60
61
|
const SUGGESTABLE_COMMANDS = [...COMMANDS, ...BUILTIN_COMMANDS];
|
|
61
62
|
const ADVANCED_BUILTIN_COMMANDS = new Set(['connector_list']);
|
|
62
|
-
function optionValue(args, name) {
|
|
63
|
-
const index = args.indexOf(name);
|
|
64
|
-
return index >= 0 ? args[index + 1] : undefined;
|
|
65
|
-
}
|
|
66
63
|
function printUsage(commandName, version) {
|
|
67
64
|
const lines = [
|
|
68
65
|
`${commandName} v${version}`,
|
|
@@ -70,18 +67,17 @@ function printUsage(commandName, version) {
|
|
|
70
67
|
'用法:',
|
|
71
68
|
` ${commandName} <action> [选项]`,
|
|
72
69
|
` ${commandName} <action> --help`,
|
|
73
|
-
` ${commandName} check [
|
|
70
|
+
` ${commandName} check [选项]`,
|
|
74
71
|
'',
|
|
75
72
|
'统一目标发现:',
|
|
76
|
-
' browser_list
|
|
77
|
-
'
|
|
73
|
+
' browser_list 默认聚合当前机器及已配置 cloud-server 下的浏览器。',
|
|
74
|
+
' 后续操作通过 --browser <browserId> 指定目标,客户端会自动选择本地或远端传输。',
|
|
78
75
|
'',
|
|
79
76
|
'通用选项:',
|
|
80
77
|
' --payload <json> 直接传入 JSON 参数',
|
|
81
78
|
' --payload-file <path> 从文件读取整个 JSON 参数',
|
|
82
79
|
' --timeout <seconds> 超时时间(默认按 action 推导)',
|
|
83
|
-
' --compact 输出紧凑格式 JSON',
|
|
84
|
-
' --json check 输出机器可读 JSON',
|
|
80
|
+
' --compact stdout 输出紧凑格式 JSON',
|
|
85
81
|
' --output <path> 保存输出或截图',
|
|
86
82
|
' --browser <id|名称> 指定 browser_list 返回的浏览器',
|
|
87
83
|
' --connector <id|名称> 限制到一台远端 connector',
|
|
@@ -89,6 +85,10 @@ function printUsage(commandName, version) {
|
|
|
89
85
|
' --cdp 当前机器强制使用 CDP(隐含 --local)',
|
|
90
86
|
' --server <url> 覆盖 cloud-server WebSocket URL',
|
|
91
87
|
'',
|
|
88
|
+
'其他选项:',
|
|
89
|
+
' -h, --help 查看帮助',
|
|
90
|
+
' -v, --version 查看版本',
|
|
91
|
+
'',
|
|
92
92
|
'内置命令:',
|
|
93
93
|
' check 检查指定浏览器,未指定时检查全部已连接浏览器',
|
|
94
94
|
];
|
|
@@ -97,12 +97,31 @@ function printUsage(commandName, version) {
|
|
|
97
97
|
for (const name of category.names) {
|
|
98
98
|
const command = COMMAND_MAP.get(name);
|
|
99
99
|
if (command)
|
|
100
|
-
lines.push(` ${name.padEnd(26)}${command.description}`);
|
|
100
|
+
lines.push(` ${name.padEnd(26)}${command.summary ?? command.description}`);
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
lines.push('', `运行 ${commandName} <action> --help 查看 action 参数。`);
|
|
104
104
|
console.error(lines.join('\n'));
|
|
105
105
|
}
|
|
106
|
+
function printCheckHelp(commandName) {
|
|
107
|
+
console.error([
|
|
108
|
+
'check',
|
|
109
|
+
' 检查指定浏览器,未指定时检查全部已连接浏览器',
|
|
110
|
+
'',
|
|
111
|
+
'用法:',
|
|
112
|
+
` ${commandName} check [选项]`,
|
|
113
|
+
'',
|
|
114
|
+
'选项:',
|
|
115
|
+
' --browser <id|名称> 只检查指定浏览器',
|
|
116
|
+
' --connector <id|名称> 限制到一台远端 connector',
|
|
117
|
+
' --local 只检查当前机器,不能与 --connector 同时使用',
|
|
118
|
+
' --cdp 当前机器强制使用 CDP(隐含 --local)',
|
|
119
|
+
' --server <url> 覆盖 cloud-server WebSocket URL',
|
|
120
|
+
' --timeout <seconds> 单项检查超时时间,默认 5 秒',
|
|
121
|
+
' --json 输出机器可读 JSON',
|
|
122
|
+
' -h, --help 查看帮助',
|
|
123
|
+
].join('\n'));
|
|
124
|
+
}
|
|
106
125
|
function printAdvancedCommandHelp(commandName, action) {
|
|
107
126
|
if (action !== 'connector_list')
|
|
108
127
|
return;
|
|
@@ -115,12 +134,13 @@ function printAdvancedCommandHelp(commandName, action) {
|
|
|
115
134
|
].join('\n'));
|
|
116
135
|
}
|
|
117
136
|
function printCommandHelp(commandName, command) {
|
|
137
|
+
const payloadUsage = command.params?.length ? ' [--payload <json> | --payload-file <path>]' : '';
|
|
118
138
|
const lines = [
|
|
119
139
|
`${command.name}`,
|
|
120
140
|
` ${command.description}`,
|
|
121
141
|
'',
|
|
122
142
|
'用法:',
|
|
123
|
-
` ${commandName} ${command.name} [
|
|
143
|
+
` ${commandName} ${command.name}${payloadUsage} [通用选项]`,
|
|
124
144
|
'',
|
|
125
145
|
];
|
|
126
146
|
if (command.params?.length) {
|
|
@@ -136,6 +156,7 @@ function printCommandHelp(commandName, command) {
|
|
|
136
156
|
for (const example of command.examples)
|
|
137
157
|
lines.push(` ${commandName} ${example}`);
|
|
138
158
|
}
|
|
159
|
+
lines.push('', `运行 ${commandName} --help 查看通用选项。`);
|
|
139
160
|
console.error(lines.join('\n'));
|
|
140
161
|
}
|
|
141
162
|
function printUnknownCommand(commandName, args) {
|
|
@@ -159,9 +180,7 @@ function parsePositiveTimeout(value, fallback) {
|
|
|
159
180
|
}
|
|
160
181
|
return parsed;
|
|
161
182
|
}
|
|
162
|
-
function parsePayload(
|
|
163
|
-
const payloadRaw = optionValue(args, '--payload');
|
|
164
|
-
const payloadFile = optionValue(args, '--payload-file');
|
|
183
|
+
function parsePayload(payloadRaw, payloadFile) {
|
|
165
184
|
if (payloadRaw !== undefined && payloadFile !== undefined) {
|
|
166
185
|
throw new Error('--payload 与 --payload-file 不能同时使用');
|
|
167
186
|
}
|
|
@@ -183,33 +202,7 @@ function parsePayload(args) {
|
|
|
183
202
|
}
|
|
184
203
|
return {};
|
|
185
204
|
}
|
|
186
|
-
function
|
|
187
|
-
const optionsWithValues = new Set([
|
|
188
|
-
'--payload',
|
|
189
|
-
'--payload-file',
|
|
190
|
-
'--timeout',
|
|
191
|
-
'--output',
|
|
192
|
-
'--browser',
|
|
193
|
-
'--connector',
|
|
194
|
-
'--server',
|
|
195
|
-
]);
|
|
196
|
-
const flags = new Set(['--compact', '--json', '--local', '--cdp', '--help', '-h']);
|
|
197
|
-
for (let index = 1; index < args.length; index += 1) {
|
|
198
|
-
const arg = args[index];
|
|
199
|
-
if (optionsWithValues.has(arg)) {
|
|
200
|
-
if (!args[index + 1] || args[index + 1].startsWith('--')) {
|
|
201
|
-
throw new Error(`缺少 ${arg} 的参数值`);
|
|
202
|
-
}
|
|
203
|
-
index += 1;
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
if (flags.has(arg))
|
|
207
|
-
continue;
|
|
208
|
-
throw new Error(`未知参数: ${arg}`);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
function resolveServer(args) {
|
|
212
|
-
const explicit = optionValue(args, '--server');
|
|
205
|
+
function resolveServer(explicit) {
|
|
213
206
|
if (explicit)
|
|
214
207
|
return { serverUrl: explicit, serverUrlSource: '--server' };
|
|
215
208
|
if (process.env.MEARL_SERVER_URL) {
|
|
@@ -220,31 +213,59 @@ function resolveServer(args) {
|
|
|
220
213
|
? { serverUrl: config.server, serverUrlSource: getCloudServerConfigPath() }
|
|
221
214
|
: {};
|
|
222
215
|
}
|
|
223
|
-
function
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
216
|
+
export function parseClientArgs(args) {
|
|
217
|
+
const program = new Command()
|
|
218
|
+
.name('mearl')
|
|
219
|
+
.helpOption(false)
|
|
220
|
+
.allowExcessArguments(false)
|
|
221
|
+
.exitOverride()
|
|
222
|
+
.configureOutput({ writeErr: () => { }, writeOut: () => { } })
|
|
223
|
+
.argument('<action>')
|
|
224
|
+
.option('--payload <json>')
|
|
225
|
+
.option('--payload-file <path>')
|
|
226
|
+
.option('--timeout <seconds>')
|
|
227
|
+
.option('--compact')
|
|
228
|
+
.option('--json')
|
|
229
|
+
.option('--output <path>')
|
|
230
|
+
.option('--browser <id-or-name>')
|
|
231
|
+
.option('--connector <id-or-name>')
|
|
232
|
+
.option('--local')
|
|
233
|
+
.option('--cdp')
|
|
234
|
+
.option('--server <url>');
|
|
235
|
+
try {
|
|
236
|
+
program.parse(['node', 'mearl', ...args]);
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
if (error instanceof CommanderError) {
|
|
240
|
+
throw new Error(error.message.replace(/^error:\s*/i, ''), { cause: error });
|
|
241
|
+
}
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
const action = program.processedArgs[0];
|
|
245
|
+
const options = program.opts();
|
|
246
|
+
const json = options.json === true;
|
|
227
247
|
if (json && action !== 'check')
|
|
228
248
|
throw new Error('--json 仅适用于 check');
|
|
229
|
-
const payload = parsePayload(
|
|
249
|
+
const payload = parsePayload(options.payload, options.payloadFile);
|
|
230
250
|
const fallbackTimeout = resolveActionTimeoutSec(action, payload, 60);
|
|
231
|
-
const forceCdp =
|
|
232
|
-
const local = forceCdp ||
|
|
233
|
-
const connector =
|
|
251
|
+
const forceCdp = options.cdp === true;
|
|
252
|
+
const local = forceCdp || options.local === true;
|
|
253
|
+
const connector = options.connector;
|
|
234
254
|
if (local && connector)
|
|
235
255
|
throw new Error('--local/--cdp 不能与 --connector 同时使用');
|
|
236
256
|
return {
|
|
237
257
|
action,
|
|
238
258
|
payload,
|
|
239
|
-
timeoutSec: parsePositiveTimeout(
|
|
240
|
-
|
|
259
|
+
timeoutSec: parsePositiveTimeout(options.timeout, fallbackTimeout),
|
|
260
|
+
...(options.timeout !== undefined ? { timeoutOption: options.timeout } : {}),
|
|
261
|
+
compact: options.compact === true,
|
|
241
262
|
forceCdp,
|
|
242
263
|
json,
|
|
243
264
|
local,
|
|
244
|
-
...(
|
|
245
|
-
...(
|
|
265
|
+
...(options.output ? { outputPath: options.output } : {}),
|
|
266
|
+
...(options.browser ? { browser: options.browser } : {}),
|
|
246
267
|
...(connector ? { connector } : {}),
|
|
247
|
-
...resolveServer(
|
|
268
|
+
...resolveServer(options.server),
|
|
248
269
|
};
|
|
249
270
|
}
|
|
250
271
|
function screenshotData(action, result) {
|
|
@@ -285,10 +306,12 @@ export async function runMearlCli(args, options = {}) {
|
|
|
285
306
|
const command = COMMAND_MAP.get(args[0]);
|
|
286
307
|
if (command)
|
|
287
308
|
printCommandHelp(commandName, command);
|
|
309
|
+
else if (args[0] === 'check')
|
|
310
|
+
printCheckHelp(commandName);
|
|
288
311
|
else if (ADVANCED_BUILTIN_COMMANDS.has(args[0])) {
|
|
289
312
|
printAdvancedCommandHelp(commandName, args[0]);
|
|
290
313
|
}
|
|
291
|
-
else if (args[0]?.startsWith('-')
|
|
314
|
+
else if (args[0]?.startsWith('-')) {
|
|
292
315
|
printUsage(commandName, clientVersion);
|
|
293
316
|
}
|
|
294
317
|
else {
|
|
@@ -305,14 +328,14 @@ export async function runMearlCli(args, options = {}) {
|
|
|
305
328
|
}
|
|
306
329
|
let parsed;
|
|
307
330
|
try {
|
|
308
|
-
parsed =
|
|
331
|
+
parsed = parseClientArgs(args);
|
|
309
332
|
}
|
|
310
333
|
catch (error) {
|
|
311
334
|
console.error(error instanceof Error ? error.message : String(error));
|
|
312
335
|
return 2;
|
|
313
336
|
}
|
|
314
337
|
if (parsed.action === 'check') {
|
|
315
|
-
const timeoutSec = parseCheckTimeout(
|
|
338
|
+
const timeoutSec = parseCheckTimeout(parsed.timeoutOption);
|
|
316
339
|
if (timeoutSec === null) {
|
|
317
340
|
console.error('--timeout 必须是正整数秒');
|
|
318
341
|
return 2;
|
package/dist/commands.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export interface ParamDef {
|
|
|
8
8
|
export interface CommandDef {
|
|
9
9
|
name: BrowserCommandAction;
|
|
10
10
|
description: string;
|
|
11
|
+
/** 顶层命令列表使用的简短描述;完整说明保留在 action help。 */
|
|
12
|
+
summary?: string;
|
|
11
13
|
/** CLI 中用于推荐 canonical action 的常见表达,不会作为可执行 action。 */
|
|
12
14
|
aliases?: readonly string[];
|
|
13
15
|
/** payload 字段说明 */
|
|
@@ -372,10 +374,12 @@ export declare const COMMANDS: readonly [{
|
|
|
372
374
|
}, {
|
|
373
375
|
readonly name: "browser_list";
|
|
374
376
|
readonly description: "列出所有浏览器及其连接状态、类型和托管实例信息;AgentBay 可包含镜像与持久化 Context";
|
|
377
|
+
readonly summary: "列出本地及远端浏览器与连接状态";
|
|
375
378
|
readonly examples: ["browser_list"];
|
|
376
379
|
}, {
|
|
377
380
|
readonly name: "browser_release";
|
|
378
381
|
readonly description: "按用户明确要求释放调试控制和会话级临时效果(含设备/时区模拟),保留浏览器和全部标签页;不用于普通任务收尾";
|
|
382
|
+
readonly summary: "释放浏览器调试控制及会话级临时效果";
|
|
379
383
|
readonly examples: ["browser_release", "browser_release --browser chrome-a"];
|
|
380
384
|
}, {
|
|
381
385
|
readonly name: "browser_launch";
|
|
@@ -439,10 +443,10 @@ export declare const COMMANDS: readonly [{
|
|
|
439
443
|
readonly description: "CDP 控制浏览器中用于读取 TDBank Cookie 的标签页";
|
|
440
444
|
}, {
|
|
441
445
|
readonly name: "copyCookieDomains";
|
|
442
|
-
readonly type: "string[]";
|
|
443
|
-
readonly description: "
|
|
446
|
+
readonly type: "string[] | \"mearl-services\"";
|
|
447
|
+
readonly description: "从控制浏览器复制 Cookie:传域名数组,或传 mearl-services 同步 Mearl 依赖平台登录态";
|
|
444
448
|
}];
|
|
445
|
-
readonly examples: ["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 --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\"]}'"];
|
|
449
|
+
readonly examples: ["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 --payload '{\"name\":\"cloud-tools\",\"provider\":\"agentbay\",\"copyCookieDomains\":\"mearl-services\"}'", "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\"]}'"];
|
|
446
450
|
}, {
|
|
447
451
|
readonly name: "browser_close";
|
|
448
452
|
readonly description: "关闭指定托管浏览器并按策略保留或清理本地 Profile / AgentBay Context";
|
|
@@ -510,6 +514,10 @@ export declare const COMMANDS: readonly [{
|
|
|
510
514
|
readonly name: "simulateSystemBars";
|
|
511
515
|
readonly type: "boolean";
|
|
512
516
|
readonly description: "与 appProfile 配合;是否模拟顶部状态/导航栏与底部手势栏,默认 true";
|
|
517
|
+
}, {
|
|
518
|
+
readonly name: "bridgeConfig";
|
|
519
|
+
readonly type: "object";
|
|
520
|
+
readonly description: "与 appProfile 配合;声明式覆盖桥方法,形如 {methods:{api:{behavior|response|noop,silent?}}}";
|
|
513
521
|
}];
|
|
514
522
|
readonly examples: ["tab_open --payload '{\"url\":\"https://example.com\"}'", "tab_open --payload '{\"url\":\"https://example.com/page\",\"reuse\":\"prefer\",\"match\":{\"ignoreSearch\":true}}'", "tab_open --payload '{\"url\":\"https://m.example.com\",\"emulation\":{\"preset\":\"iphone-15-pro\"}}'", "tab_open --payload '{\"url\":\"https://market.m.taobao.com/app/trip/rx-home/pages/home\",\"appProfile\":\"fliggy\"}'", "tab_open --payload '{\"url\":\"https://example.com\",\"appProfile\":\"fliggy\",\"os\":\"android\"}'", "tab_open --payload '{\"url\":\"https://example.com\",\"group\":\"验证任务\"}'"];
|
|
515
523
|
}, {
|
|
@@ -525,7 +533,7 @@ export declare const COMMANDS: readonly [{
|
|
|
525
533
|
readonly examples: ["tab_close --payload '{\"tabId\":123}'"];
|
|
526
534
|
}, {
|
|
527
535
|
readonly name: "tab_list";
|
|
528
|
-
readonly description: "
|
|
536
|
+
readonly description: "列出目标浏览器所有窗口中的标签页";
|
|
529
537
|
readonly aliases: readonly ["tabs", "list_tabs", "show_tabs"];
|
|
530
538
|
readonly examples: ["tab_list"];
|
|
531
539
|
}, {
|
|
@@ -542,7 +550,15 @@ export declare const COMMANDS: readonly [{
|
|
|
542
550
|
}, {
|
|
543
551
|
readonly name: "point";
|
|
544
552
|
readonly type: "{ x: number; y: number }";
|
|
545
|
-
readonly description: "按视口坐标点击(CSS
|
|
553
|
+
readonly description: "按视口坐标点击(CSS 像素)。默认使用 outer-viewport;容器或 iframe 内坐标配合 coordinateSpace: \"active-frame\"";
|
|
554
|
+
}, {
|
|
555
|
+
readonly name: "frameId";
|
|
556
|
+
readonly type: "string";
|
|
557
|
+
readonly description: "浏览器扩展后端:显式指定 CSS/text 查询或 active-frame 坐标所属的 frame;省略时容器模式自动使用当前业务 frame";
|
|
558
|
+
}, {
|
|
559
|
+
readonly name: "coordinateSpace";
|
|
560
|
+
readonly type: "\"outer-viewport\" | \"active-frame\"";
|
|
561
|
+
readonly description: "仅配合 point 使用;默认 outer-viewport;浏览器扩展后端的 active-frame 会换算 iframe 偏移与缩放后派发可信输入";
|
|
546
562
|
}, {
|
|
547
563
|
readonly name: "role";
|
|
548
564
|
readonly type: "string";
|
|
@@ -565,7 +581,7 @@ export declare const COMMANDS: readonly [{
|
|
|
565
581
|
readonly required: true;
|
|
566
582
|
readonly description: "目标标签页 ID";
|
|
567
583
|
}];
|
|
568
|
-
readonly examples: ["page_click --payload '{\"text\":\"提交\"}'", "page_click --payload '{\"text\":\"退款明细\",\"role\":\"button\"}'", "page_click --payload '{\"text\":\"确定\",\"scope\":\"@e12\"}'", "page_click --payload '{\"selector\":\"@e3\"}'", "page_click --payload '{\"selector\":\".submit-btn\"}'", "page_click --payload '{\"selector\":\".gesture-btn\",\"clickMode\":\"touch\"}'", "page_click --payload '{\"point\":{\"x\":336,\"y\":117}}'"];
|
|
584
|
+
readonly examples: ["page_click --payload '{\"text\":\"提交\"}'", "page_click --payload '{\"text\":\"退款明细\",\"role\":\"button\"}'", "page_click --payload '{\"text\":\"确定\",\"scope\":\"@e12\"}'", "page_click --payload '{\"selector\":\"@e3\"}'", "page_click --payload '{\"selector\":\".submit-btn\"}'", "page_click --payload '{\"selector\":\".gesture-btn\",\"clickMode\":\"touch\"}'", "page_click --payload '{\"point\":{\"x\":336,\"y\":117}}'", "page_click --payload '{\"point\":{\"x\":196,\"y\":240},\"coordinateSpace\":\"active-frame\"}'"];
|
|
569
585
|
}, {
|
|
570
586
|
readonly name: "page_drag";
|
|
571
587
|
readonly description: "拖动页面元素或视口坐标,适用于轮播图、滑块和拖放";
|
|
@@ -818,7 +834,7 @@ export declare const COMMANDS: readonly [{
|
|
|
818
834
|
readonly examples: ["page_snapshot", "page_snapshot --payload '{\"tabId\":1,\"mode\":\"viewport\"}'", "page_snapshot --payload '{\"tabId\":1,\"rootSelector\":\"[role=dialog]\"}'", "page_snapshot --payload '{\"tabId\":1,\"rootRef\":\"@e8\",\"ancestorDepth\":1}'", "page_snapshot --payload '{\"tabId\":1,\"query\":{\"text\":\"保存\",\"role\":\"button\"}}'"];
|
|
819
835
|
}, {
|
|
820
836
|
readonly name: "page_screenshot";
|
|
821
|
-
readonly description: "
|
|
837
|
+
readonly description: "获取当前视口或指定元素截图";
|
|
822
838
|
readonly outputFlag: true;
|
|
823
839
|
readonly params: [{
|
|
824
840
|
readonly name: "selector";
|
|
@@ -860,7 +876,7 @@ export declare const COMMANDS: readonly [{
|
|
|
860
876
|
readonly examples: ["page_selected_element", "page_selected_element --output ./element.png"];
|
|
861
877
|
}, {
|
|
862
878
|
readonly name: "get_user_info";
|
|
863
|
-
readonly description: "
|
|
879
|
+
readonly description: "获取当前浏览器登录的阿里员工信息";
|
|
864
880
|
readonly params: [{
|
|
865
881
|
readonly name: "detail";
|
|
866
882
|
readonly type: "boolean";
|
|
@@ -879,6 +895,7 @@ export declare const COMMANDS: readonly [{
|
|
|
879
895
|
}, {
|
|
880
896
|
readonly name: "record_stop";
|
|
881
897
|
readonly description: "停止录制并返回完整结果(操作 + 关联请求);未在录制时不报错,返回 wasRecording: false";
|
|
898
|
+
readonly summary: "停止录制并返回操作和关联请求";
|
|
882
899
|
readonly params: [{
|
|
883
900
|
readonly name: "tabId";
|
|
884
901
|
readonly type: "number";
|
|
@@ -888,6 +905,7 @@ export declare const COMMANDS: readonly [{
|
|
|
888
905
|
}, {
|
|
889
906
|
readonly name: "set_device_emulation";
|
|
890
907
|
readonly description: "把目标 tab 切换为移动端模拟(视口/UA/触摸),等价 DevTools 设备工具栏,作用域 per-tab;需保持调试控制才持续生效";
|
|
908
|
+
readonly summary: "设置或清除目标标签页的设备模拟";
|
|
891
909
|
readonly params: [{
|
|
892
910
|
readonly name: "enabled";
|
|
893
911
|
readonly type: "boolean";
|
|
@@ -939,6 +957,7 @@ export declare const COMMANDS: readonly [{
|
|
|
939
957
|
}, {
|
|
940
958
|
readonly name: "set_app_profile";
|
|
941
959
|
readonly description: "把目标 tab 切换为 App 容器模拟态(客户端 UA + JSBridge 托管 + 页面跳转 iframe 容器栈 + scheme 唤端映射 + 自适应系统标题栏),系统栏由 iframe 外层统一渲染,并根据页面协议与桥调用切换普通、沉浸或隐藏状态;用于桌面浏览器近似实机验证 App 内 H5,需保持调试控制才持续生效";
|
|
960
|
+
readonly summary: "设置或清除目标标签页的 App 容器模拟";
|
|
942
961
|
readonly params: [{
|
|
943
962
|
readonly name: "enabled";
|
|
944
963
|
readonly type: "boolean";
|
|
@@ -968,6 +987,10 @@ export declare const COMMANDS: readonly [{
|
|
|
968
987
|
readonly name: "emulation";
|
|
969
988
|
readonly type: "object";
|
|
970
989
|
readonly description: "覆盖档案推荐的设备模拟参数,字段同 set_device_emulation 去掉 enabled";
|
|
990
|
+
}, {
|
|
991
|
+
readonly name: "bridgeConfig";
|
|
992
|
+
readonly type: "object";
|
|
993
|
+
readonly description: "声明式覆盖桥方法,形如 {methods:{api:{behavior|response|noop,silent?}}};不执行配置脚本";
|
|
971
994
|
}, {
|
|
972
995
|
readonly name: "reload";
|
|
973
996
|
readonly type: "boolean";
|
|
@@ -1051,7 +1074,7 @@ export declare const COMMANDS: readonly [{
|
|
|
1051
1074
|
readonly examples: ["set_cookie --payload '{\"tabId\":12345,\"name\":\"feature_flag\",\"value\":\"enabled\"}'", "set_cookie --payload '{\"tabId\":12345,\"name\":\"edith_switch_br.rx-vehicle-search_home\",\"value\":\"3.6.63\",\"domain\":\"taobao.com\",\"reload\":true}'"];
|
|
1052
1075
|
}, {
|
|
1053
1076
|
readonly name: "page_frames";
|
|
1054
|
-
readonly description: "
|
|
1077
|
+
readonly description: "获取页面主 frame 与所有 iframe 列表";
|
|
1055
1078
|
readonly params: [{
|
|
1056
1079
|
readonly name: "tabId";
|
|
1057
1080
|
readonly type: "number";
|
|
@@ -1061,7 +1084,7 @@ export declare const COMMANDS: readonly [{
|
|
|
1061
1084
|
readonly examples: ["page_frames --payload '{\"tabId\":12345}'"];
|
|
1062
1085
|
}, {
|
|
1063
1086
|
readonly name: "request_domain_permission";
|
|
1064
|
-
readonly description: "
|
|
1087
|
+
readonly description: "申请访问指定域名的权限(必要时弹窗让用户授权)";
|
|
1065
1088
|
readonly params: [{
|
|
1066
1089
|
readonly name: "domain";
|
|
1067
1090
|
readonly type: "string";
|
|
@@ -1072,6 +1095,7 @@ export declare const COMMANDS: readonly [{
|
|
|
1072
1095
|
}, {
|
|
1073
1096
|
readonly name: "run_actions";
|
|
1074
1097
|
readonly description: "批量顺序执行多个 action,减少往返延迟。遇错即停,返回已完成步骤结果;步骤内的页面动作不带内置观察";
|
|
1098
|
+
readonly summary: "批量顺序执行多个 action,遇错即停";
|
|
1075
1099
|
readonly params: [{
|
|
1076
1100
|
readonly name: "actions";
|
|
1077
1101
|
readonly type: "Array<{action: string, data?: object}>";
|
package/dist/commands.js
CHANGED
|
@@ -266,11 +266,13 @@ export const COMMANDS = [
|
|
|
266
266
|
{
|
|
267
267
|
name: 'browser_list',
|
|
268
268
|
description: '列出所有浏览器及其连接状态、类型和托管实例信息;AgentBay 可包含镜像与持久化 Context',
|
|
269
|
+
summary: '列出本地及远端浏览器与连接状态',
|
|
269
270
|
examples: ['browser_list'],
|
|
270
271
|
},
|
|
271
272
|
{
|
|
272
273
|
name: 'browser_release',
|
|
273
274
|
description: '按用户明确要求释放调试控制和会话级临时效果(含设备/时区模拟),保留浏览器和全部标签页;不用于普通任务收尾',
|
|
275
|
+
summary: '释放浏览器调试控制及会话级临时效果',
|
|
274
276
|
examples: ['browser_release', 'browser_release --browser chrome-a'],
|
|
275
277
|
},
|
|
276
278
|
{
|
|
@@ -325,8 +327,8 @@ export const COMMANDS = [
|
|
|
325
327
|
},
|
|
326
328
|
{
|
|
327
329
|
name: 'copyCookieDomains',
|
|
328
|
-
type: 'string[]',
|
|
329
|
-
description: '
|
|
330
|
+
type: 'string[] | "mearl-services"',
|
|
331
|
+
description: '从控制浏览器复制 Cookie:传域名数组,或传 mearl-services 同步 Mearl 依赖平台登录态',
|
|
330
332
|
},
|
|
331
333
|
],
|
|
332
334
|
examples: [
|
|
@@ -334,6 +336,7 @@ export const COMMANDS = [
|
|
|
334
336
|
`browser_launch --payload '{"name":"cloud-public","provider":"agentbay","imageId":"browser_latest","url":"https://example.com"}'`,
|
|
335
337
|
`browser_launch --payload '{"name":"cloud-login","provider":"agentbay","persistent":true,"copyCookieDomains":["example.com"],"url":"https://example.com/account"}'`,
|
|
336
338
|
`browser_launch --payload '{"name":"cloud-login","provider":"agentbay","reuse":"require","copyCookieDomains":["example.com"]}'`,
|
|
339
|
+
`browser_launch --payload '{"name":"cloud-tools","provider":"agentbay","copyCookieDomains":"mearl-services"}'`,
|
|
337
340
|
`browser_launch --payload '{"name":"account-a","headless":true,"accountId":12345}'`,
|
|
338
341
|
`browser_launch --payload '{"name":"figma","userAgentMode":"desktop","copyCookieDomains":["figma.com"]}'`,
|
|
339
342
|
`browser_launch --payload '{"name":"account-b","query":"test_account","url":"https://www.taobao.com"}'`,
|
|
@@ -420,6 +423,11 @@ export const COMMANDS = [
|
|
|
420
423
|
type: 'boolean',
|
|
421
424
|
description: '与 appProfile 配合;是否模拟顶部状态/导航栏与底部手势栏,默认 true',
|
|
422
425
|
},
|
|
426
|
+
{
|
|
427
|
+
name: 'bridgeConfig',
|
|
428
|
+
type: 'object',
|
|
429
|
+
description: '与 appProfile 配合;声明式覆盖桥方法,形如 {methods:{api:{behavior|response|noop,silent?}}}',
|
|
430
|
+
},
|
|
423
431
|
],
|
|
424
432
|
examples: [
|
|
425
433
|
`tab_open --payload '{"url":"https://example.com"}'`,
|
|
@@ -439,7 +447,7 @@ export const COMMANDS = [
|
|
|
439
447
|
},
|
|
440
448
|
{
|
|
441
449
|
name: 'tab_list',
|
|
442
|
-
description: '
|
|
450
|
+
description: '列出目标浏览器所有窗口中的标签页',
|
|
443
451
|
aliases: ['tabs', 'list_tabs', 'show_tabs'],
|
|
444
452
|
examples: ['tab_list'],
|
|
445
453
|
},
|
|
@@ -461,7 +469,17 @@ export const COMMANDS = [
|
|
|
461
469
|
{
|
|
462
470
|
name: 'point',
|
|
463
471
|
type: '{ x: number; y: number }',
|
|
464
|
-
description: '按视口坐标点击(CSS
|
|
472
|
+
description: '按视口坐标点击(CSS 像素)。默认使用 outer-viewport;容器或 iframe 内坐标配合 coordinateSpace: "active-frame"',
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
name: 'frameId',
|
|
476
|
+
type: 'string',
|
|
477
|
+
description: '浏览器扩展后端:显式指定 CSS/text 查询或 active-frame 坐标所属的 frame;省略时容器模式自动使用当前业务 frame',
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
name: 'coordinateSpace',
|
|
481
|
+
type: '"outer-viewport" | "active-frame"',
|
|
482
|
+
description: '仅配合 point 使用;默认 outer-viewport;浏览器扩展后端的 active-frame 会换算 iframe 偏移与缩放后派发可信输入',
|
|
465
483
|
},
|
|
466
484
|
{
|
|
467
485
|
name: 'role',
|
|
@@ -495,6 +513,7 @@ export const COMMANDS = [
|
|
|
495
513
|
`page_click --payload '{"selector":".submit-btn"}'`,
|
|
496
514
|
`page_click --payload '{"selector":".gesture-btn","clickMode":"touch"}'`,
|
|
497
515
|
`page_click --payload '{"point":{"x":336,"y":117}}'`,
|
|
516
|
+
`page_click --payload '{"point":{"x":196,"y":240},"coordinateSpace":"active-frame"}'`,
|
|
498
517
|
],
|
|
499
518
|
},
|
|
500
519
|
{
|
|
@@ -755,7 +774,7 @@ export const COMMANDS = [
|
|
|
755
774
|
},
|
|
756
775
|
{
|
|
757
776
|
name: 'page_screenshot',
|
|
758
|
-
description: '
|
|
777
|
+
description: '获取当前视口或指定元素截图',
|
|
759
778
|
outputFlag: true,
|
|
760
779
|
params: [
|
|
761
780
|
{ name: 'selector', type: 'string', description: 'CSS 选择器,提供后只截取该元素区域' },
|
|
@@ -789,7 +808,7 @@ export const COMMANDS = [
|
|
|
789
808
|
},
|
|
790
809
|
{
|
|
791
810
|
name: 'get_user_info',
|
|
792
|
-
description: '
|
|
811
|
+
description: '获取当前浏览器登录的阿里员工信息',
|
|
793
812
|
params: [
|
|
794
813
|
{
|
|
795
814
|
name: 'detail',
|
|
@@ -809,6 +828,7 @@ export const COMMANDS = [
|
|
|
809
828
|
{
|
|
810
829
|
name: 'record_stop',
|
|
811
830
|
description: '停止录制并返回完整结果(操作 + 关联请求);未在录制时不报错,返回 wasRecording: false',
|
|
831
|
+
summary: '停止录制并返回操作和关联请求',
|
|
812
832
|
params: [{ name: 'tabId', type: 'number', description: '目标标签页 ID,从 tab_list 获取' }],
|
|
813
833
|
examples: ['record_stop'],
|
|
814
834
|
},
|
|
@@ -816,6 +836,7 @@ export const COMMANDS = [
|
|
|
816
836
|
{
|
|
817
837
|
name: 'set_device_emulation',
|
|
818
838
|
description: '把目标 tab 切换为移动端模拟(视口/UA/触摸),等价 DevTools 设备工具栏,作用域 per-tab;需保持调试控制才持续生效',
|
|
839
|
+
summary: '设置或清除目标标签页的设备模拟',
|
|
819
840
|
params: [
|
|
820
841
|
{ name: 'enabled', type: 'boolean', required: true, description: 'true=启用,false=清除' },
|
|
821
842
|
{
|
|
@@ -846,6 +867,7 @@ export const COMMANDS = [
|
|
|
846
867
|
{
|
|
847
868
|
name: 'set_app_profile',
|
|
848
869
|
description: '把目标 tab 切换为 App 容器模拟态(客户端 UA + JSBridge 托管 + 页面跳转 iframe 容器栈 + scheme 唤端映射 + 自适应系统标题栏),系统栏由 iframe 外层统一渲染,并根据页面协议与桥调用切换普通、沉浸或隐藏状态;用于桌面浏览器近似实机验证 App 内 H5,需保持调试控制才持续生效',
|
|
870
|
+
summary: '设置或清除目标标签页的 App 容器模拟',
|
|
849
871
|
params: [
|
|
850
872
|
{ name: 'enabled', type: 'boolean', required: true, description: 'true=启用,false=清除' },
|
|
851
873
|
{
|
|
@@ -870,6 +892,11 @@ export const COMMANDS = [
|
|
|
870
892
|
type: 'object',
|
|
871
893
|
description: '覆盖档案推荐的设备模拟参数,字段同 set_device_emulation 去掉 enabled',
|
|
872
894
|
},
|
|
895
|
+
{
|
|
896
|
+
name: 'bridgeConfig',
|
|
897
|
+
type: 'object',
|
|
898
|
+
description: '声明式覆盖桥方法,形如 {methods:{api:{behavior|response|noop,silent?}}};不执行配置脚本',
|
|
899
|
+
},
|
|
873
900
|
{ name: 'reload', type: 'boolean', description: '应用/清除后是否重载页面,默认 true' },
|
|
874
901
|
{ name: 'tabId', type: 'number', required: true, description: '目标标签页 ID' },
|
|
875
902
|
],
|
|
@@ -953,13 +980,13 @@ export const COMMANDS = [
|
|
|
953
980
|
// ── 其他 ──────────────────────────────────────────────────────────────────
|
|
954
981
|
{
|
|
955
982
|
name: 'page_frames',
|
|
956
|
-
description: '
|
|
983
|
+
description: '获取页面主 frame 与所有 iframe 列表',
|
|
957
984
|
params: [{ name: 'tabId', type: 'number', required: true, description: '目标标签页 ID' }],
|
|
958
985
|
examples: [`page_frames --payload '{"tabId":12345}'`],
|
|
959
986
|
},
|
|
960
987
|
{
|
|
961
988
|
name: 'request_domain_permission',
|
|
962
|
-
description: '
|
|
989
|
+
description: '申请访问指定域名的权限(必要时弹窗让用户授权)',
|
|
963
990
|
params: [
|
|
964
991
|
{
|
|
965
992
|
name: 'domain',
|
|
@@ -974,6 +1001,7 @@ export const COMMANDS = [
|
|
|
974
1001
|
{
|
|
975
1002
|
name: 'run_actions',
|
|
976
1003
|
description: '批量顺序执行多个 action,减少往返延迟。遇错即停,返回已完成步骤结果;步骤内的页面动作不带内置观察',
|
|
1004
|
+
summary: '批量顺序执行多个 action,遇错即停',
|
|
977
1005
|
params: [
|
|
978
1006
|
{
|
|
979
1007
|
name: 'actions',
|
|
@@ -219,8 +219,8 @@ export interface BrowserLaunchParams {
|
|
|
219
219
|
site?: string | number;
|
|
220
220
|
query?: string;
|
|
221
221
|
tabId?: number;
|
|
222
|
-
/** Copy
|
|
223
|
-
copyCookieDomains?: string[];
|
|
222
|
+
/** Copy explicit domains or the fixed Mearl-service preset from the control browser. */
|
|
223
|
+
copyCookieDomains?: string[] | 'mearl-services';
|
|
224
224
|
}
|
|
225
225
|
export interface BrowserCloseParams {
|
|
226
226
|
browser: string;
|
|
@@ -271,6 +271,18 @@ export interface BrowserEmulationOptions {
|
|
|
271
271
|
orientation?: 'portrait' | 'landscape';
|
|
272
272
|
touch?: boolean;
|
|
273
273
|
}
|
|
274
|
+
export type AppBridgeConfigBehavior = 'navigate' | 'closeAll' | 'closeBackData' | 'postMessage' | 'notifySubscribe' | 'notifyUnsubscribe' | 'notifyPost' | 'sharedFileRead' | 'sharedFileWrite' | 'sharedDataGet' | 'sharedDataSet' | 'legacyBroadcastCreate' | 'legacyBroadcastPost' | 'legacyBroadcastClose' | 'memoryGet' | 'memorySet' | 'memoryDelete' | 'availabilityMap' | 'userInfo' | 'networkInfo' | 'geolocation' | 'permission' | 'prefetch' | 'fptTrack' | 'mtopStream' | 'previewFile' | 'toast' | 'unsupportedNativeInput' | 'payment' | 'userTrack' | 'calendar' | 'networkQualityMonitor' | 'beaconRequest';
|
|
275
|
+
/** 单个桥方法的声明式覆盖;行为、固定回包、成功空实现三选一。 */
|
|
276
|
+
export interface AppBridgeMethodConfig {
|
|
277
|
+
behavior?: AppBridgeConfigBehavior;
|
|
278
|
+
response?: unknown;
|
|
279
|
+
noop?: boolean;
|
|
280
|
+
silent?: boolean;
|
|
281
|
+
}
|
|
282
|
+
/** 追加或覆盖 App 档案中的桥方法。配置保持 JSON 可序列化,不执行配置提供的代码。 */
|
|
283
|
+
export interface AppBridgeConfig {
|
|
284
|
+
methods: Record<string, AppBridgeMethodConfig>;
|
|
285
|
+
}
|
|
274
286
|
export interface TabOpenParams {
|
|
275
287
|
url: string;
|
|
276
288
|
active?: boolean;
|
|
@@ -297,6 +309,8 @@ export interface TabOpenParams {
|
|
|
297
309
|
os?: 'ios' | 'android';
|
|
298
310
|
/** 与 appProfile 配合使用;是否模拟顶部状态/导航栏与底部手势栏,默认 true */
|
|
299
311
|
simulateSystemBars?: boolean;
|
|
312
|
+
/** 与 appProfile 配合使用;按方法覆盖档案内置桥配置 */
|
|
313
|
+
bridgeConfig?: AppBridgeConfig;
|
|
300
314
|
}
|
|
301
315
|
/**
|
|
302
316
|
* 客户端容器模拟可用档案名。
|
|
@@ -319,6 +333,8 @@ export interface SetAppProfileParams {
|
|
|
319
333
|
deviceModel?: string;
|
|
320
334
|
/** 是否模拟顶部状态/导航栏与底部手势栏,默认 true */
|
|
321
335
|
simulateSystemBars?: boolean;
|
|
336
|
+
/** 按方法覆盖档案内置桥配置,仅支持受控行为、固定 JSON 回包和成功空实现 */
|
|
337
|
+
bridgeConfig?: AppBridgeConfig;
|
|
322
338
|
/** 覆盖档案推荐的设备模拟参数(视口/UA 默认取档案值) */
|
|
323
339
|
emulation?: BrowserEmulationOptions;
|
|
324
340
|
/** 默认 true:应用/清除后刷新页面使注入生效 */
|
|
@@ -350,6 +366,10 @@ export interface PageClickParams {
|
|
|
350
366
|
x: number;
|
|
351
367
|
y: number;
|
|
352
368
|
};
|
|
369
|
+
/** CSS/text 查询的目标 frame;省略时容器模式自动使用当前业务 frame。 */
|
|
370
|
+
frameId?: string;
|
|
371
|
+
/** point 的坐标空间;默认 outer-viewport 以保持兼容。 */
|
|
372
|
+
coordinateSpace?: 'outer-viewport' | 'active-frame';
|
|
353
373
|
role?: string;
|
|
354
374
|
exact?: boolean;
|
|
355
375
|
scope?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mearl/client",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.1",
|
|
4
4
|
"description": "Unified Mearl SDK & CLI for local and remote browsers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -45,13 +45,17 @@
|
|
|
45
45
|
"socket-client"
|
|
46
46
|
],
|
|
47
47
|
"license": "ISC",
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=22.12.0"
|
|
50
|
+
},
|
|
48
51
|
"publishConfig": {
|
|
49
52
|
"registry": "https://registry.npmjs.org"
|
|
50
53
|
},
|
|
51
54
|
"dependencies": {
|
|
55
|
+
"commander": "^15.0.0",
|
|
52
56
|
"fastest-levenshtein": "^1.0.16",
|
|
53
57
|
"ws": "^8.18.0",
|
|
54
|
-
"@mearl/cloud-types": "2.
|
|
58
|
+
"@mearl/cloud-types": "2.9.1"
|
|
55
59
|
},
|
|
56
60
|
"devDependencies": {
|
|
57
61
|
"@types/node": "^24.9.1",
|