@mearl/client 3.0.0-beta.5 → 3.0.0-beta.7
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 +4 -4
- package/dist/actionTimeouts.js +1 -1
- package/dist/autoUpdate.js +2 -1
- package/dist/capabilityHelp.d.ts +4 -0
- package/dist/capabilityHelp.js +69 -0
- package/dist/check.js +1 -1
- package/dist/cli.js +9 -15
- package/dist/commandHelp.js +3 -3
- package/dist/commandSuggestions.d.ts +3 -0
- package/dist/commandSuggestions.js +16 -5
- package/dist/commands.d.ts +58 -52
- package/dist/commands.js +48 -35
- package/dist/index.d.ts +3 -2
- package/dist/index.js +6 -4
- package/dist/plugins/officialPluginInstaller.js +2 -1
- package/dist/plugins/runtime/pluginTaskManager.js +1 -1
- package/dist/script.js +2 -1
- package/dist/socket.js +32 -8
- package/dist/unified.js +11 -1
- package/dist/unifiedConfig.d.ts +4 -0
- package/dist/unifiedConfig.js +11 -0
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -224,8 +224,8 @@ stdout 在管道或重定向时默认输出紧凑 JSON,在交互终端默认
|
|
|
224
224
|
未指定字段时默认返回正文。
|
|
225
225
|
|
|
226
226
|
`send_request` 默认携带所选浏览器中匹配完整目标 URL 的普通 Cookie,以及目标 URL 第一方站点对应的 CHIPS Cookie。例如 `https://app.example.com/` 使用 `https://example.com` 分区,不依赖已打开页面。站点按协议和公共后缀列表(含私有后缀)确定;排除其他站点、其他协议、跨站祖先及不透明分区。保留不同路径下的同名 Cookie,不额外拼入父站点的 host-only Cookie。嵌入其他站点的第三方分区、页面签名或完整页面身份仍需使用目标页面中的 `page_eval` 请求。
|
|
227
|
-
响应中的 `Set-Cookie` 会更新原浏览器,后续调用可以使用刷新后的登录态;`withCookies:false` 同时关闭自动携带与响应回写。扩展由 Chrome 处理响应 Cookie;CDP 的 Node 请求在返回前通过原页面的浏览器连接回写,支持 HttpOnly、默认路径、过期时间和删除。CDP 响应存在 `Set-Cookie` 时附带 `cookieSync: { updatedCount, rejectedCount, failedCount }`,不返回 Cookie 值;无法确定分区的 Partitioned 响应 Cookie 计入拒绝数。同步失败不撤销已完成的 HTTP 操作,应检查状态而非直接重发业务请求。
|
|
228
|
-
扩展的临时凭据注入仅匹配原始完整请求 URL,重定向由 Chrome 按新 URL 选择 Cookie。CDP 的 Node 请求(含
|
|
227
|
+
响应中的 `Set-Cookie` 会更新原浏览器,后续调用可以使用刷新后的登录态;`withCookies:false` 同时关闭自动携带与响应回写。扩展由 Chrome 处理响应 Cookie;CDP 的 Node 请求在返回前通过原页面的浏览器连接回写,支持 HttpOnly、默认路径、过期时间和删除。CDP 响应存在 `Set-Cookie` 时附带 `cookieSync: { updatedCount, rejectedCount, failedCount }`,不返回 Cookie 值;无法确定分区的 Partitioned 响应 Cookie 计入拒绝数。同步失败不撤销已完成的 HTTP 操作,应检查状态而非直接重发业务请求。Protocol 普通请求及流式请求使用相同的响应刷新机制。
|
|
228
|
+
扩展的临时凭据注入仅匹配原始完整请求 URL,重定向由 Chrome 按新 URL 选择 Cookie。CDP 的 Node 请求(含 Protocol 流式请求)不自动跟随重定向,避免将原 URL 的 Cookie 或签名传到其他地址。
|
|
229
229
|
|
|
230
230
|
Mearl 默认聚合当前机器和已配置 cloud-server 下的浏览器,并通过 `browser_list` 返回的全局
|
|
231
231
|
`browserId` 自动路由。以下覆盖参数仅在限定宿主或连接、传输排障时使用,因此不出现在默认 help:
|
|
@@ -267,7 +267,7 @@ client、native-host、cloud-server 和 MCP server 的已安装版本。发现
|
|
|
267
267
|
安装 skill 并自动注册命令,或直接注册已有入口:
|
|
268
268
|
|
|
269
269
|
```bash
|
|
270
|
-
npx @mearl/setup plugin install https://github.com/
|
|
270
|
+
npx @mearl/setup plugin install https://github.com/mearlhq/mearl --skill browser-tab-manager --yes
|
|
271
271
|
npx @mearl/setup plugin /absolute/path/to/browser-tab-manager
|
|
272
272
|
npx @mearl/setup plugin uninstall tabs
|
|
273
273
|
mearl tabs summary
|
|
@@ -445,7 +445,7 @@ Node 插件在运行 CLI、SDK 或 MCP Server 的宿主上执行,不随浏览
|
|
|
445
445
|
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
446
446
|
| API 调试 | `tab_checkpoint` `get_requests` `get_logs` `get_events` |
|
|
447
447
|
| Mock & 规则 | `set_mock` `get_mocks` `set_rule` `get_rules` |
|
|
448
|
-
| 网络请求 | `send_request` `
|
|
448
|
+
| 网络请求 | `send_request` `send_protocol_request` `request_permission` |
|
|
449
449
|
| 标签页 | `tab_open` `tab_list` `tab_close` |
|
|
450
450
|
| 页面感知 | `page_snapshot` `page_inspect` `page_screenshot` `page_frames` |
|
|
451
451
|
| 页面操作 | `page_click` `page_drag` `page_type` `page_hover` `page_scroll` `page_press` `page_wait` `page_navigate` `page_upload` `page_eval` |
|
package/dist/actionTimeouts.js
CHANGED
|
@@ -26,7 +26,7 @@ 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 === '
|
|
29
|
+
if (action === 'send_protocol_request') {
|
|
30
30
|
return Math.max(fallback, Math.ceil(((data.timeout ?? 10000) + 5000) / 1000));
|
|
31
31
|
}
|
|
32
32
|
if (action === 'page_download')
|
package/dist/autoUpdate.js
CHANGED
|
@@ -4,6 +4,7 @@ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, realpathSync,
|
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import { delimiter, dirname, join, resolve, win32 } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { npmExecEnvironment } from '@mearl/daemon-core';
|
|
7
8
|
import { isNewerRelease, parseReleaseVersion } from './check.js';
|
|
8
9
|
import { loadCliAutoUpdateIntervalDays } from './unifiedConfig.js';
|
|
9
10
|
const CONFIG_DIR = join(homedir(), '.mearl');
|
|
@@ -207,7 +208,7 @@ export function setupUpdateCommand(version, packageRoot, profiles, updateClient)
|
|
|
207
208
|
if (!parseReleaseVersion(version))
|
|
208
209
|
throw new Error('A valid Mearl release is required');
|
|
209
210
|
const [major, minor] = version.split('.');
|
|
210
|
-
const env =
|
|
211
|
+
const env = npmExecEnvironment();
|
|
211
212
|
delete env.MEARL_AUTO_UPDATE_CLIENT_ROOT;
|
|
212
213
|
delete env.MEARL_AUTO_UPDATE_CLIENT_ONLY;
|
|
213
214
|
delete env.MEARL_AUTO_UPDATE_PROFILES;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { BrowserCapabilityCatalog } from '@mearl/browser-core';
|
|
2
|
+
/** Help reads an existing target with a short budget; it never installs an environment. */
|
|
3
|
+
export declare function readHelpCapabilities(args: string[]): Promise<BrowserCapabilityCatalog | undefined>;
|
|
4
|
+
export declare function formatCapabilityHelp(catalog: BrowserCapabilityCatalog | undefined, detailed?: boolean): string;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { resolveServer } from './unifiedConfig.js';
|
|
2
|
+
import { listLiveBrowsers } from './browserRegistry.js';
|
|
3
|
+
import { UnifiedClient } from './unified.js';
|
|
4
|
+
/** Help reads an existing target with a short budget; it never installs an environment. */
|
|
5
|
+
export async function readHelpCapabilities(args) {
|
|
6
|
+
const value = (flag) => {
|
|
7
|
+
const index = args.indexOf(flag);
|
|
8
|
+
return index < 0 ? args.find(arg => arg.startsWith(`${flag}=`))?.slice(flag.length + 1) : args[index + 1];
|
|
9
|
+
};
|
|
10
|
+
const browser = value('--browser');
|
|
11
|
+
const session = value('--session');
|
|
12
|
+
const connector = value('--connector');
|
|
13
|
+
const { serverUrl } = resolveServer(value('--server'));
|
|
14
|
+
const local = args.includes('--local') || args.includes('--cdp');
|
|
15
|
+
if (local && connector || session === 'new')
|
|
16
|
+
return;
|
|
17
|
+
if (!browser && !connector && (local || !serverUrl) && !session && !listLiveBrowsers().length)
|
|
18
|
+
return;
|
|
19
|
+
const client = new UnifiedClient({ connectTimeoutSec: 1, requestTimeoutSec: 1, ...(serverUrl ? { serverUrl } : {}) });
|
|
20
|
+
let timer;
|
|
21
|
+
try {
|
|
22
|
+
const result = await Promise.race([
|
|
23
|
+
client.invoke('get_capabilities', {}, {
|
|
24
|
+
...(browser ? { browser } : {}), ...(session ? { session } : {}),
|
|
25
|
+
...(connector ? { connector } : {}), ...(local ? { local: true } : {}),
|
|
26
|
+
...(args.includes('--cdp') ? { transport: 'cdp' } : {}),
|
|
27
|
+
}),
|
|
28
|
+
new Promise(resolve => { timer = setTimeout(() => resolve(undefined), 1500); }),
|
|
29
|
+
]);
|
|
30
|
+
if (result && Array.isArray(result.actions) && Array.isArray(result.apps?.entries))
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Static help remains usable when the target is offline or does not expose a catalog.
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
client.disconnect();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function formatCapabilityHelp(catalog, detailed = false) {
|
|
42
|
+
if (!catalog)
|
|
43
|
+
return '当前浏览器能力暂不可读;以下为静态帮助。连接后再次运行 --help 可查看已安装 App。';
|
|
44
|
+
const lines = [
|
|
45
|
+
...(catalog.actions.includes('send_protocol_request') ? ['已安装的可选协议命令:send_protocol_request(沿用 --payload 参数;--help 查看用法)', ''] : []),
|
|
46
|
+
'当前浏览器已安装的 App:',
|
|
47
|
+
];
|
|
48
|
+
if (!catalog.apps.entries.length)
|
|
49
|
+
lines.push(catalog.apps.status === 'available' ? ' 暂无 App 档案;设备模拟可独立使用。' : ' App 目录暂不可读,不代表未安装。');
|
|
50
|
+
for (const app of catalog.apps.entries) {
|
|
51
|
+
lines.push(` ${app.id} — ${app.label}${app.available ? '' : '(暂不可用)'}`);
|
|
52
|
+
if (!detailed)
|
|
53
|
+
continue;
|
|
54
|
+
lines.push(` 能力:${app.features.join(', ')}`);
|
|
55
|
+
if (app.usage?.skills?.length)
|
|
56
|
+
lines.push(` Skill:${app.usage.skills.join(', ')}`);
|
|
57
|
+
if (app.usage?.description)
|
|
58
|
+
lines.push(` ${app.usage.description}`);
|
|
59
|
+
if (app.usage?.limitations?.length)
|
|
60
|
+
lines.push(...app.usage.limitations.map(line => ` 限制:${line}`));
|
|
61
|
+
if (app.usage?.documentation?.length)
|
|
62
|
+
lines.push(...app.usage.documentation.map(doc => ` 说明:${doc.title} ${doc.url}`));
|
|
63
|
+
}
|
|
64
|
+
if (detailed)
|
|
65
|
+
lines.push(...catalog.limitations.map(line => ` ${line}`));
|
|
66
|
+
else
|
|
67
|
+
lines.push(' App 用法与限制:mearl set_app_profile --help');
|
|
68
|
+
return lines.join('\n');
|
|
69
|
+
}
|
package/dist/check.js
CHANGED
|
@@ -4,7 +4,7 @@ const UPDATE_SOURCES = {
|
|
|
4
4
|
client: 'https://registry.npmjs.org/@mearl%2Fclient/latest',
|
|
5
5
|
'native-host': 'https://registry.npmjs.org/@mearl%2Fnative-host/latest',
|
|
6
6
|
'cloud-connector': 'https://registry.npmjs.org/@mearl%2Fcloud-connector/latest',
|
|
7
|
-
extension: 'https://github.com/
|
|
7
|
+
extension: 'https://github.com/mearlhq/mearl/releases/latest/download/config.json',
|
|
8
8
|
};
|
|
9
9
|
export function parseReleaseVersion(value) {
|
|
10
10
|
if (typeof value !== 'string')
|
package/dist/cli.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readHelpCapabilities, formatCapabilityHelp } from './capabilityHelp.js';
|
|
1
2
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
3
|
import { resolve } from 'node:path';
|
|
3
4
|
import { Command, CommanderError } from 'commander';
|
|
@@ -10,7 +11,7 @@ import { formatCommandHelp } from './commandHelp.js';
|
|
|
10
11
|
import { CLIENT_VERSION } from './socket.js';
|
|
11
12
|
import { UnifiedClient } from './unified.js';
|
|
12
13
|
import { findPluginEntry, findPluginRegistration, findOfficialPlugin, listPluginNames, listOfficialPluginNames, loadRegisteredPlugin, PluginError, } from './plugins/sdk/plugin.js';
|
|
13
|
-
import {
|
|
14
|
+
import { resolveServer } from './unifiedConfig.js';
|
|
14
15
|
const TOP_LEVEL_COMMAND_NAME_WIDTH = 22;
|
|
15
16
|
const CATEGORIES = [
|
|
16
17
|
{
|
|
@@ -20,9 +21,9 @@ const CATEGORIES = [
|
|
|
20
21
|
{ title: 'Mock & 规则', names: ['set_mock', 'get_mocks', 'set_rule', 'get_rules'] },
|
|
21
22
|
{
|
|
22
23
|
title: '网络请求',
|
|
23
|
-
names: ['send_request', '
|
|
24
|
+
names: ['send_request', 'request_permission'],
|
|
24
25
|
},
|
|
25
|
-
{ title: '标签页', names: ['tab_open', 'tab_list', 'tab_group', 'tab_close'
|
|
26
|
+
{ title: '标签页', names: ['tab_open', 'tab_list', 'tab_group', 'tab_close'] },
|
|
26
27
|
{
|
|
27
28
|
title: '页面感知',
|
|
28
29
|
names: ['page_snapshot', 'page_inspect', 'page_screenshot', 'page_frames'],
|
|
@@ -351,17 +352,6 @@ function parsePayload(payloadRaw, payloadFile) {
|
|
|
351
352
|
}
|
|
352
353
|
return {};
|
|
353
354
|
}
|
|
354
|
-
function resolveServer(explicit) {
|
|
355
|
-
if (explicit)
|
|
356
|
-
return { serverUrl: explicit, serverUrlSource: '--server' };
|
|
357
|
-
if (process.env.MEARL_SERVER_URL) {
|
|
358
|
-
return { serverUrl: process.env.MEARL_SERVER_URL, serverUrlSource: 'MEARL_SERVER_URL' };
|
|
359
|
-
}
|
|
360
|
-
const config = loadCloudServerConfig();
|
|
361
|
-
return config?.server
|
|
362
|
-
? { serverUrl: config.server, serverUrlSource: getCloudServerConfigPath() }
|
|
363
|
-
: {};
|
|
364
|
-
}
|
|
365
355
|
function normalizeScreenshotOutput(action, payload, explicitOutput) {
|
|
366
356
|
if (action !== 'page_screenshot' || !Object.hasOwn(payload, 'output')) {
|
|
367
357
|
return { payload, ...(explicitOutput ? { outputPath: explicitOutput } : {}) };
|
|
@@ -612,8 +602,11 @@ export async function runMearlCli(args, options = {}) {
|
|
|
612
602
|
}
|
|
613
603
|
if (args.includes('--help') || args.includes('-h')) {
|
|
614
604
|
const command = COMMAND_MAP.get(args[0]);
|
|
615
|
-
if (command)
|
|
605
|
+
if (command) {
|
|
616
606
|
console.error(formatCommandHelp(commandName, command));
|
|
607
|
+
if (command.name === 'set_app_profile' || command.name === 'tab_open')
|
|
608
|
+
console.error(formatCapabilityHelp(await readHelpCapabilities(args), true));
|
|
609
|
+
}
|
|
617
610
|
else if (args[0] === 'check')
|
|
618
611
|
printCheckHelp(commandName);
|
|
619
612
|
else if (ADVANCED_BUILTIN_COMMANDS.has(args[0])) {
|
|
@@ -621,6 +614,7 @@ export async function runMearlCli(args, options = {}) {
|
|
|
621
614
|
}
|
|
622
615
|
else if (args[0]?.startsWith('-')) {
|
|
623
616
|
printUsage(commandName, clientVersion);
|
|
617
|
+
console.error(formatCapabilityHelp(await readHelpCapabilities(args)));
|
|
624
618
|
}
|
|
625
619
|
else {
|
|
626
620
|
printUnknownCommand(commandName, args);
|
package/dist/commandHelp.js
CHANGED
|
@@ -39,10 +39,10 @@ export function formatCommandHelp(commandName, command) {
|
|
|
39
39
|
];
|
|
40
40
|
const method = [...SDK_METHOD_ACTIONS].find(([, action]) => action === command.name)?.[0];
|
|
41
41
|
if (method)
|
|
42
|
-
lines.push('SDK:', ...(method === 'browserRelease'
|
|
42
|
+
lines.push('SDK:', ...(method === 'browserRelease' || method === 'browserList'
|
|
43
43
|
? [
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
` await mearl.${method}(options?)`,
|
|
45
|
+
` await mearl.${method}(params, options)`,
|
|
46
46
|
]
|
|
47
47
|
: [
|
|
48
48
|
` await mearl.${method}(${SDK_OPTIONS_ONLY_METHODS.has(method) ? 'options?' : 'params, options?'})`,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export interface SuggestableCommand {
|
|
2
2
|
readonly aliases?: readonly string[];
|
|
3
|
+
/** Suggestion-only expressions whose canonical invocation needs a specific payload. */
|
|
4
|
+
readonly aliasExamples?: Readonly<Record<string, string>>;
|
|
3
5
|
readonly description: string;
|
|
4
6
|
readonly examples?: readonly string[];
|
|
5
7
|
readonly name: string;
|
|
@@ -7,6 +9,7 @@ export interface SuggestableCommand {
|
|
|
7
9
|
export interface CommandSuggestion {
|
|
8
10
|
command: SuggestableCommand;
|
|
9
11
|
score: number;
|
|
12
|
+
example?: string;
|
|
10
13
|
}
|
|
11
14
|
export declare function commandInput(args: readonly string[]): string;
|
|
12
15
|
export declare function findCommandSuggestions(args: readonly string[], commands: readonly SuggestableCommand[], limit?: number): CommandSuggestion[];
|
|
@@ -40,9 +40,20 @@ export function findCommandSuggestions(args, commands, limit = 3) {
|
|
|
40
40
|
return [];
|
|
41
41
|
const ranked = commands
|
|
42
42
|
.map(command => {
|
|
43
|
-
const keys = [command.name, ...(command.aliases ?? [])].map(
|
|
44
|
-
const
|
|
45
|
-
|
|
43
|
+
const keys = [command.name, ...(command.aliases ?? [])].map(name => ({ name }));
|
|
44
|
+
for (const [name, example] of Object.entries(command.aliasExamples ?? {})) {
|
|
45
|
+
keys.push({ name, example });
|
|
46
|
+
}
|
|
47
|
+
let score = -1;
|
|
48
|
+
let example;
|
|
49
|
+
for (const key of keys) {
|
|
50
|
+
const candidate = Math.max(...queries.map(query => similarity(query, normalizeCommand(key.name))));
|
|
51
|
+
if (candidate > score) {
|
|
52
|
+
score = candidate;
|
|
53
|
+
example = key.example;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { command, score, ...(example ? { example } : {}) };
|
|
46
57
|
})
|
|
47
58
|
.filter(suggestion => suggestion.score >= MIN_SIMILARITY)
|
|
48
59
|
.sort((left, right) => right.score - left.score || left.command.name.localeCompare(right.command.name));
|
|
@@ -52,8 +63,8 @@ export function findCommandSuggestions(args, commands, limit = 3) {
|
|
|
52
63
|
return ranked.filter(suggestion => bestScore - suggestion.score <= MAX_SCORE_GAP).slice(0, limit);
|
|
53
64
|
}
|
|
54
65
|
export function formatCommandSuggestions(commandName, suggestions) {
|
|
55
|
-
return suggestions.map(({ command }) => {
|
|
56
|
-
const invocation = command.examples?.[0] ?? command.name;
|
|
66
|
+
return suggestions.map(({ command, example }) => {
|
|
67
|
+
const invocation = example ?? command.examples?.[0] ?? command.name;
|
|
57
68
|
return ` ${commandName} ${invocation}\n ${command.description}`;
|
|
58
69
|
});
|
|
59
70
|
}
|
package/dist/commands.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface CommandDef {
|
|
|
14
14
|
summary?: string;
|
|
15
15
|
/** CLI 中用于推荐 canonical action 的常见表达,不会作为可执行 action。 */
|
|
16
16
|
aliases?: readonly string[];
|
|
17
|
+
/** 仅用于错误建议;按表达给出 canonical action 的适用示例,不增加可执行别名。 */
|
|
18
|
+
aliasExamples?: Readonly<Record<string, string>>;
|
|
17
19
|
/** payload 字段说明 */
|
|
18
20
|
params?: ParamDef[];
|
|
19
21
|
/** 是否支持 --output */
|
|
@@ -86,7 +88,7 @@ export declare const COMMANDS: readonly [{
|
|
|
86
88
|
readonly examples: ["tab_checkpoint --payload '{\"tabId\":12345}'"];
|
|
87
89
|
}, {
|
|
88
90
|
readonly name: "get_requests";
|
|
89
|
-
readonly description: "
|
|
91
|
+
readonly description: "获取最近的网络请求(协议请求或普通 xhr/fetch)";
|
|
90
92
|
readonly params: [{
|
|
91
93
|
readonly name: "tabId";
|
|
92
94
|
readonly type: "number";
|
|
@@ -98,8 +100,8 @@ export declare const COMMANDS: readonly [{
|
|
|
98
100
|
readonly description: "返回条数,默认 10";
|
|
99
101
|
}, {
|
|
100
102
|
readonly name: "source";
|
|
101
|
-
readonly type: "\"
|
|
102
|
-
readonly description: "数据来源,不填时默认
|
|
103
|
+
readonly type: "\"protocol\" | \"requests\" | \"all\"";
|
|
104
|
+
readonly description: "数据来源,不填时默认 all;all 返回全部业务请求";
|
|
103
105
|
}, {
|
|
104
106
|
readonly name: "filter";
|
|
105
107
|
readonly type: "string";
|
|
@@ -133,9 +135,9 @@ export declare const COMMANDS: readonly [{
|
|
|
133
135
|
readonly type: "boolean";
|
|
134
136
|
readonly description: "只返回数据结构,不返回叶子值";
|
|
135
137
|
}];
|
|
136
|
-
readonly notes: readonly ["流式请求开始后即进入缓冲区;页面及 App 桥代发的
|
|
138
|
+
readonly notes: readonly ["流式请求开始后即进入缓冲区;页面及 App 桥代发的 协议 SSE 都归入 protocol。无法归属当前页面 target 的 Service Worker 请求不在采集范围内。"];
|
|
137
139
|
readonly result: readonly ["nextCursor 用于同类查询续读,hasMore 表示还有数据,gap 表示缓冲区淘汰造成缺口;checkpoint cursor 可分别用于三类查询的初始边界。", "省略正文时不返回 postData / responseBody 字段;指定 fields 后需要同时读取全文时显式传 includeBody:true。", "isPending 标记未完成请求;SSE 的 responseBody 保存按序到达的 event/data/timestamp/duration 分片。敏感请求头返回脱敏值。"];
|
|
138
|
-
readonly examples: ["get_requests --payload '{\"tabId\":12345,\"count\":5}'", "get_requests --payload '{\"tabId\":12345,\"source\":\"
|
|
140
|
+
readonly examples: ["get_requests --payload '{\"tabId\":12345,\"count\":5}'", "get_requests --payload '{\"tabId\":12345,\"source\":\"protocol\",\"filter\":\"cart\"}'"];
|
|
139
141
|
}, {
|
|
140
142
|
readonly name: "get_logs";
|
|
141
143
|
readonly description: "获取浏览器控制台日志";
|
|
@@ -242,7 +244,7 @@ export declare const COMMANDS: readonly [{
|
|
|
242
244
|
readonly description: "请求 query/body 字段子集匹配;$index 从 0 开始指定调用顺序";
|
|
243
245
|
}];
|
|
244
246
|
readonly notes: readonly ["启用时至少提供 mockData 或非空 fields;非空 fields 优先。fields 每项 { path: string, value: any },path 支持点号和数组索引。", "相同 apiName 的多条无条件 Mock 会覆盖;conditions 子集匹配 query/body(含 data 内层),不同条件可共存;$index 从 0 起控制调用顺序,不参与字段匹配。", "大字段可用顶层 @<绝对路径> 文件引用或 --payload-file;文件展开后仍受 Native Messaging 单条消息大小限制。"];
|
|
245
|
-
readonly examples: ["set_mock --payload '{\"tabId\":12345,\"apiName\":\"
|
|
247
|
+
readonly examples: ["set_mock --payload '{\"tabId\":12345,\"apiName\":\"cart.query\",\"mockData\":{\"ret\":[\"SUCCESS::调用成功\"]}}'", "set_mock --payload-file ./mock-data.json"];
|
|
246
248
|
}, {
|
|
247
249
|
readonly name: "get_mocks";
|
|
248
250
|
readonly description: "查看当前生效的 mock 列表";
|
|
@@ -307,7 +309,7 @@ export declare const COMMANDS: readonly [{
|
|
|
307
309
|
readonly type: "string";
|
|
308
310
|
readonly description: "规则说明";
|
|
309
311
|
}];
|
|
310
|
-
readonly notes: readonly ["扩展模式规则持久化;CDP 仅支持会话级 modifyHeaders 请求头 set,不支持响应头、initiatorDomains 或其他动作。Provider 是否支持以其能力声明为准。", "扩展模式会检查规则请求 URL 和显式来源域名的访问权限;缺失时仍保存规则,返回 warnings 和 missingOrigins,不自动弹窗。可在请求规则页面授权,或按提示调用
|
|
312
|
+
readonly notes: readonly ["扩展模式规则持久化;CDP 仅支持会话级 modifyHeaders 请求头 set,不支持响应头、initiatorDomains 或其他动作。Provider 是否支持以其能力声明为准。", "扩展模式会检查规则请求 URL 和显式来源域名的访问权限;缺失时仍保存规则,返回 warnings 和 missingOrigins,不自动弹窗。可在请求规则页面授权,或按提示调用 request_permission。复杂 URL 表达式无法完整推断域名时会提示手动检查。"];
|
|
311
313
|
readonly examples: ["set_rule --payload '{\"actionType\":\"block\",\"filter\":\"^https://example\\\\.com/ads/.*\"}'", "set_rule --payload '{\"ruleId\":42,\"enabled\":false}'"];
|
|
312
314
|
}, {
|
|
313
315
|
readonly name: "get_rules";
|
|
@@ -367,46 +369,30 @@ export declare const COMMANDS: readonly [{
|
|
|
367
369
|
readonly result: readonly ["返回 status、statusText、headers、body、bodyType、attachedCookieCount;bodyType 为 json/text/base64,二进制 body 需解码后保存。"];
|
|
368
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\"}}'"];
|
|
369
371
|
}, {
|
|
370
|
-
readonly name: "
|
|
371
|
-
readonly description: "
|
|
372
|
+
readonly name: "send_protocol_request";
|
|
373
|
+
readonly description: "通过已安装的请求协议能力,使用目标浏览器 Cookie 发送请求";
|
|
372
374
|
readonly params: [{
|
|
373
|
-
readonly name: "api";
|
|
374
|
-
readonly type: "string";
|
|
375
|
-
readonly required: true;
|
|
376
|
-
readonly description: "mtop API 名称";
|
|
377
|
-
}, {
|
|
378
|
-
readonly name: "data";
|
|
379
|
-
readonly type: "object";
|
|
380
|
-
readonly description: "请求数据";
|
|
381
|
-
}, {
|
|
382
|
-
readonly name: "version";
|
|
383
|
-
readonly type: "string";
|
|
384
|
-
readonly description: "API 版本,默认 1.0";
|
|
385
|
-
}, {
|
|
386
|
-
readonly name: "method";
|
|
387
|
-
readonly type: "string";
|
|
388
|
-
readonly description: "HTTP 方法,默认 GET";
|
|
389
|
-
}, {
|
|
390
|
-
readonly name: "appKey";
|
|
391
|
-
readonly type: "string";
|
|
392
|
-
readonly description: "签名使用的 appKey,默认 12574478";
|
|
393
|
-
}, {
|
|
394
|
-
readonly name: "env";
|
|
395
|
-
readonly type: "\"prod\" | \"pre\"";
|
|
396
|
-
readonly description: "请求环境,默认 prod";
|
|
397
|
-
}, {
|
|
398
375
|
readonly name: "timeout";
|
|
399
376
|
readonly type: "number";
|
|
400
|
-
readonly description: "
|
|
377
|
+
readonly description: "宿主超时毫秒数;协议能力负责校验具体参数";
|
|
401
378
|
}];
|
|
402
|
-
readonly notes: readonly ["
|
|
403
|
-
readonly result: readonly ["返回 success、response、requestData
|
|
404
|
-
readonly examples: ["
|
|
379
|
+
readonly notes: readonly ["需要安装提供 request.protocol 的能力包;参数、签名、网关与响应协议由能力包定义。", "使用 --payload 传递协议文档声明的参数;核心不设置业务参数默认值。"];
|
|
380
|
+
readonly result: readonly ["返回 success、response、requestData;业务结果由协议定义。"];
|
|
381
|
+
readonly examples: ["send_protocol_request --payload '{\"operation\":\"example\",\"data\":{}}'"];
|
|
405
382
|
}, {
|
|
406
383
|
readonly name: "browser_list";
|
|
407
384
|
readonly description: "列出所有浏览器及其连接状态、类型和 Provider 托管实例信息";
|
|
408
385
|
readonly summary: "列出本地及远端浏览器与连接状态";
|
|
409
|
-
readonly
|
|
386
|
+
readonly params: [{
|
|
387
|
+
readonly name: "provider";
|
|
388
|
+
readonly type: "string";
|
|
389
|
+
readonly description: "配合 launchForm,仅查询一个本机已安装 Provider 的启动候选;需 --local";
|
|
390
|
+
}, {
|
|
391
|
+
readonly name: "launchForm";
|
|
392
|
+
readonly type: "object";
|
|
393
|
+
readonly description: "现有动态启动表单请求:{variant?,values:{}},不创建或接入实例";
|
|
394
|
+
}];
|
|
395
|
+
readonly examples: ["browser_list", "browser_list --local"];
|
|
410
396
|
}, {
|
|
411
397
|
readonly name: "browser_release";
|
|
412
398
|
readonly description: "释放当前浏览器的调试控制和会话级临时效果(含设备/时区模拟);不用于普通任务收尾,Provider 实例的行为以 mearl <providerId> 为准";
|
|
@@ -489,6 +475,9 @@ export declare const COMMANDS: readonly [{
|
|
|
489
475
|
readonly name: "tab_open";
|
|
490
476
|
readonly description: "打开或按 URL 规则复用 Tab,并等待新页面加载完成";
|
|
491
477
|
readonly aliases: readonly ["tab_new", "new_tab", "tab_create", "create_tab"];
|
|
478
|
+
readonly aliasExamples: {
|
|
479
|
+
readonly tab_activate: "tab_open --payload '{\"url\":\"https://example.com\",\"reuse\":\"require\",\"active\":true}'";
|
|
480
|
+
};
|
|
492
481
|
readonly params: [{
|
|
493
482
|
readonly name: "url";
|
|
494
483
|
readonly type: "string";
|
|
@@ -529,7 +518,7 @@ export declare const COMMANDS: readonly [{
|
|
|
529
518
|
}, {
|
|
530
519
|
readonly name: "appProfile";
|
|
531
520
|
readonly type: "string";
|
|
532
|
-
readonly description: "按已安装档案应用 App 环境,ID
|
|
521
|
+
readonly description: "按已安装档案应用 App 环境,ID 与限制见当前动态帮助。需要 iframe WebView 和系统栏时同时传 emulation.deviceShell=true";
|
|
533
522
|
}, {
|
|
534
523
|
readonly name: "os";
|
|
535
524
|
readonly type: "\"ios\" | \"android\"";
|
|
@@ -581,6 +570,9 @@ export declare const COMMANDS: readonly [{
|
|
|
581
570
|
}, {
|
|
582
571
|
readonly name: "page_click";
|
|
583
572
|
readonly description: "点击页面元素,支持 CSS/@ref、可见文本或视口坐标定位";
|
|
573
|
+
readonly aliasExamples: {
|
|
574
|
+
readonly page_select: "page_click --payload '{\"tabId\":12345,\"text\":\"选项文案\",\"role\":\"option\"}'";
|
|
575
|
+
};
|
|
584
576
|
readonly params: [{
|
|
585
577
|
readonly name: "selector";
|
|
586
578
|
readonly type: "string";
|
|
@@ -624,7 +616,7 @@ export declare const COMMANDS: readonly [{
|
|
|
624
616
|
readonly description: "目标标签页 ID";
|
|
625
617
|
}];
|
|
626
618
|
readonly result: readonly ["默认返回 { action, observation };action.success 只表示动作成功,业务效果需结合 observation 和目标状态判断。", "observe=false 且未开启 diagnostics 时返回裸动作结果(如 { clicked: true }),没有 action/observation;开启 diagnostics 时返回 { action, diagnostics }。", "包装结果中的 action.stage=precondition 表示未派发,省略 observation/diagnostics;dispatch-uncertain 表示可能已派发,先查看 action.attempt 与可用观察或最小快照核验,避免重复提交。CLI 单次调用失败以退出码 1 结束;脚本需检查 action.success。", "observation.effects 返回通知、控件和焦点增量,可直接复用 node.ref/selector;changed=true 且 effects 为空不表示失败。", "effects.texts 按需返回普通可见文本的 selector/text,不代表可点击控件或业务成功;超限或覆盖不完整时仍提示补快照。", "fullSnapshotRecommended/snapshotReasons 提示是否补充快照;openedTabs 返回可继续操作的 tabId 和 ready。navigation 模式的 ready 表示新页面就绪。", "显式开启的诊断通道在 diagnostics 中返回动作期间增量,与 observe 独立。"];
|
|
627
|
-
readonly notes: readonly ["selector / text / point 必须且只能提供一个。隐藏页 auto 使用 DOM;只有明确依赖真实用户手势时才激活页面。", "原生 select 可按 option 文本点击;checkbox/radio 返回 controlState.checkedBefore/checkedAfter。"];
|
|
619
|
+
readonly notes: readonly ["selector / text / point 必须且只能提供一个。隐藏页 auto 使用 DOM;只有明确依赖真实用户手势时才激活页面。", "auto 对原生 option、checkbox/radio 使用状态感知的 DOM 路径;显式 mouse/touch 保留可信输入。", "原生 select 可按 option 文本点击;auto/dom 返回 selectionState.valueBefore/valueAfter/selectedAfter,检查选中状态是否保持。checkbox/radio 返回 controlState.checkedBefore/checkedAfter。"];
|
|
628
620
|
readonly examples: ["page_click --payload '{\"tabId\":12345,\"text\":\"提交\"}'", "page_click --payload '{\"tabId\":12345,\"text\":\"退款明细\",\"role\":\"button\"}'", "page_click --payload '{\"tabId\":12345,\"text\":\"确定\",\"scope\":\"@e12\"}'", "page_click --payload '{\"tabId\":12345,\"selector\":\"@e3\"}'", "page_click --payload '{\"tabId\":12345,\"selector\":\".submit-btn\"}'", "page_click --payload '{\"tabId\":12345,\"selector\":\".gesture-btn\",\"clickMode\":\"touch\"}'", "page_click --payload '{\"tabId\":12345,\"point\":{\"x\":336,\"y\":117}}'", "page_click --payload '{\"tabId\":12345,\"point\":{\"x\":196,\"y\":240},\"coordinateSpace\":\"active-frame\"}'"];
|
|
629
621
|
}, {
|
|
630
622
|
readonly name: "page_drag";
|
|
@@ -659,6 +651,9 @@ export declare const COMMANDS: readonly [{
|
|
|
659
651
|
}, {
|
|
660
652
|
readonly name: "page_type";
|
|
661
653
|
readonly description: "向输入框填写文本,兼容 React 受控组件";
|
|
654
|
+
readonly aliasExamples: {
|
|
655
|
+
readonly page_fill: "page_type --payload '{\"tabId\":12345,\"selector\":\"@e6\",\"text\":\"填写内容\",\"clearFirst\":true}'";
|
|
656
|
+
};
|
|
662
657
|
readonly params: [{
|
|
663
658
|
readonly name: "selector";
|
|
664
659
|
readonly type: "string";
|
|
@@ -820,6 +815,14 @@ export declare const COMMANDS: readonly [{
|
|
|
820
815
|
readonly name: "modifiers";
|
|
821
816
|
readonly type: "string[]";
|
|
822
817
|
readonly description: "修饰键数组:ctrl/alt/shift/meta/cmd";
|
|
818
|
+
}, {
|
|
819
|
+
readonly name: "dialogId";
|
|
820
|
+
readonly type: "string";
|
|
821
|
+
readonly description: "原生弹窗回执或 page_snapshot 中的 dialog.id;仅配合 Enter 接受或 Escape 取消";
|
|
822
|
+
}, {
|
|
823
|
+
readonly name: "promptText";
|
|
824
|
+
readonly type: "string";
|
|
825
|
+
readonly description: "仅用于 dialogId + Enter 接受原生 prompt 时填写文本";
|
|
823
826
|
}, {
|
|
824
827
|
readonly name: "pressMode";
|
|
825
828
|
readonly type: "\"auto\" | \"dom\" | \"keyboard\"";
|
|
@@ -830,7 +833,7 @@ export declare const COMMANDS: readonly [{
|
|
|
830
833
|
readonly required: true;
|
|
831
834
|
readonly description: "目标标签页 ID";
|
|
832
835
|
}];
|
|
833
|
-
readonly notes: readonly ["隐藏页 DOM 模式支持 Tab 焦点移动、原生控件 Enter/Space 激活和输入框 Enter 提交;编辑、选择、方向键和页面滚动等原生行为需可见页与 pressMode=keyboard。"];
|
|
836
|
+
readonly notes: readonly ["隐藏页 DOM 模式支持 Tab 焦点移动、原生控件 Enter/Space 激活和输入框 Enter 提交;编辑、选择、方向键和页面滚动等原生行为需可见页与 pressMode=keyboard。", "原生 alert/confirm/prompt/beforeunload 弹窗需显式传 dialogId;普通 Enter 不处理弹窗。弹窗处理绕过 DOM 观察,不自动重放被中断的动作;处理后重新读取页面。"];
|
|
834
837
|
readonly result: readonly ["默认返回 { action, observation };action.success 只表示动作成功,业务效果需结合 observation 和目标状态判断。", "observe=false 且未开启 diagnostics 时返回裸动作结果(如 { clicked: true }),没有 action/observation;开启 diagnostics 时返回 { action, diagnostics }。", "包装结果中的 action.stage=precondition 表示未派发,省略 observation/diagnostics;dispatch-uncertain 表示可能已派发,先查看 action.attempt 与可用观察或最小快照核验,避免重复提交。CLI 单次调用失败以退出码 1 结束;脚本需检查 action.success。", "observation.effects 返回通知、控件和焦点增量,可直接复用 node.ref/selector;changed=true 且 effects 为空不表示失败。", "effects.texts 按需返回普通可见文本的 selector/text,不代表可点击控件或业务成功;超限或覆盖不完整时仍提示补快照。", "fullSnapshotRecommended/snapshotReasons 提示是否补充快照;openedTabs 返回可继续操作的 tabId 和 ready。navigation 模式的 ready 表示新页面就绪。", "显式开启的诊断通道在 diagnostics 中返回动作期间增量,与 observe 独立。", "action.data.dispatchMode / fallbackReason / defaultAction 说明实际输入路径。"];
|
|
835
838
|
readonly examples: ["page_press --payload '{\"tabId\":12345,\"key\":\"Enter\"}'"];
|
|
836
839
|
}, {
|
|
@@ -874,11 +877,14 @@ export declare const COMMANDS: readonly [{
|
|
|
874
877
|
readonly required: true;
|
|
875
878
|
readonly description: "目标标签页 ID";
|
|
876
879
|
}];
|
|
877
|
-
readonly notes: readonly ["time / selector / text / condition 必须且只能提供一个;time 范围 0–60000ms。页面交互已有内置观察,仅在需要独立等待条件时使用。"];
|
|
880
|
+
readonly notes: readonly ["time / selector / text / condition 必须且只能提供一个;time 范围 0–60000ms。页面交互已有内置观察,仅在需要独立等待条件时使用。", "等待后须立即执行已知动作时,用 mearl run 在同一脚本中 await pageWait 后紧接 pageClick,避免模型往返。整体 --timeout 须覆盖等待及后续动作;等待失败不继续点击。"];
|
|
878
881
|
readonly examples: ["page_wait --payload '{\"tabId\":12345,\"time\":1000}'", "page_wait --payload '{\"tabId\":12345,\"selector\":\".dialog\",\"state\":\"visible\"}'", "page_wait --payload '{\"tabId\":12345,\"selector\":\".loading\",\"state\":\"hidden\"}'", "page_wait --payload '{\"tabId\":12345,\"text\":\"提交成功\"}'", "page_wait --payload '{\"tabId\":12345,\"condition\":\"window.appReady === true\",\"timeout\":30000}'"];
|
|
879
882
|
}, {
|
|
880
883
|
readonly name: "page_navigate";
|
|
881
884
|
readonly description: "在当前标签页内导航到 URL、刷新页面,或沿浏览历史后退/前进";
|
|
885
|
+
readonly aliasExamples: {
|
|
886
|
+
readonly page_reload: "page_navigate --payload '{\"tabId\":12345,\"refresh\":true}'";
|
|
887
|
+
};
|
|
882
888
|
readonly params: [{
|
|
883
889
|
readonly name: "url";
|
|
884
890
|
readonly type: "string";
|
|
@@ -1065,7 +1071,7 @@ export declare const COMMANDS: readonly [{
|
|
|
1065
1071
|
readonly examples: ["set_device_emulation --payload '{\"tabId\":12345,\"enabled\":true,\"preset\":\"iphone-17-pro\"}'", "set_device_emulation --payload '{\"tabId\":12345,\"enabled\":true,\"preset\":\"pixel-11\",\"orientation\":\"landscape\"}'", "set_device_emulation --payload '{\"tabId\":12345,\"enabled\":true,\"preset\":\"ipad-mini-a17-pro\",\"orientation\":\"landscape\",\"deviceShell\":true,\"splitScreen\":true}'", "set_device_emulation --payload '{\"tabId\":12345,\"enabled\":false}'"];
|
|
1066
1072
|
}, {
|
|
1067
1073
|
readonly name: "set_app_profile";
|
|
1068
|
-
readonly description: "设置已安装的 App 档案;完整 JSBridge
|
|
1074
|
+
readonly description: "设置已安装的 App 档案;完整 JSBridge 由能力包提供。可同时设置已安装的网络控件,传 emulation 时联动设备模拟";
|
|
1069
1075
|
readonly summary: "设置或清除目标标签页的 App 容器模拟";
|
|
1070
1076
|
readonly params: [{
|
|
1071
1077
|
readonly name: "enabled";
|
|
@@ -1075,7 +1081,7 @@ export declare const COMMANDS: readonly [{
|
|
|
1075
1081
|
}, {
|
|
1076
1082
|
readonly name: "profile";
|
|
1077
1083
|
readonly type: "string";
|
|
1078
|
-
readonly description: "已安装的容器档案 ID(enabled=true
|
|
1084
|
+
readonly description: "已安装的容器档案 ID(enabled=true 时必填);当前动态帮助直接展示目标浏览器目录";
|
|
1079
1085
|
}, {
|
|
1080
1086
|
readonly name: "os";
|
|
1081
1087
|
readonly type: "\"ios\" | \"android\"";
|
|
@@ -1101,9 +1107,9 @@ export declare const COMMANDS: readonly [{
|
|
|
1101
1107
|
readonly type: "object";
|
|
1102
1108
|
readonly description: "声明式覆盖桥方法,形如 {methods:{api:{behavior|response|noop,silent?}}};不执行配置脚本";
|
|
1103
1109
|
}, {
|
|
1104
|
-
readonly name: "
|
|
1105
|
-
readonly type: "
|
|
1106
|
-
readonly description: "
|
|
1110
|
+
readonly name: "networkControl";
|
|
1111
|
+
readonly type: "object | null";
|
|
1112
|
+
readonly description: "可选已安装网络控件 {id,value};null 清除,省略时保持当前设置。仅作用于目标 tab";
|
|
1107
1113
|
}, {
|
|
1108
1114
|
readonly name: "emulation";
|
|
1109
1115
|
readonly type: "object";
|
|
@@ -1119,8 +1125,8 @@ export declare const COMMANDS: readonly [{
|
|
|
1119
1125
|
readonly description: "目标标签页 ID";
|
|
1120
1126
|
}];
|
|
1121
1127
|
readonly notes: readonly ["只有用户明确要求 App/客户端/容器/JSBridge 模拟时启用。移动视口或普通 H5 调试使用 set_device_emulation。", "emulation={} 使用档案推荐设备;enabled=false 时省略 emulation 只清除 App 注入,传 {} 同时清除设备模拟。", "bridgeConfig.methods 按方法声明 behavior / response / noop 三选一,可传 silent;只支持受控行为、固定 JSON 回包或成功空实现,不执行配置脚本。"];
|
|
1122
|
-
readonly result: readonly ["返回 enabled、profile、label、userAgent、os、
|
|
1123
|
-
readonly examples: ["set_app_profile --payload '{\"tabId\":12345,\"enabled\":true,\"profile\":\"reader\"}'", "set_app_profile --payload '{\"tabId\":12345,\"enabled\":true,\"profile\":\"reader\",\"
|
|
1128
|
+
readonly result: readonly ["返回 enabled、profile、label、userAgent、os、networkControl、reloaded;联动设备时额外返回实际 emulation。"];
|
|
1129
|
+
readonly examples: ["set_app_profile --payload '{\"tabId\":12345,\"enabled\":true,\"profile\":\"reader\"}'", "set_app_profile --payload '{\"tabId\":12345,\"enabled\":true,\"profile\":\"reader\",\"networkControl\":{\"id\":\"environment\",\"value\":\"pre\"}}'", "set_app_profile --payload '{\"tabId\":12345,\"enabled\":false,\"networkControl\":null}'", "set_app_profile --payload '{\"tabId\":12345,\"enabled\":true,\"profile\":\"reader\",\"emulation\":{}}'", "set_app_profile --payload '{\"tabId\":12345,\"enabled\":true,\"profile\":\"wallet\",\"emulation\":{\"preset\":\"pixel-11\",\"deviceShell\":true}}'", "set_app_profile --payload '{\"tabId\":12345,\"enabled\":true,\"profile\":\"reader\",\"emulation\":{\"preset\":\"ipad-mini-a17-pro\",\"orientation\":\"landscape\",\"deviceShell\":true,\"splitScreen\":true}}'", "set_app_profile --payload '{\"tabId\":12345,\"enabled\":false}'", "set_app_profile --payload '{\"tabId\":12345,\"enabled\":false,\"emulation\":{}}'"];
|
|
1124
1130
|
}, {
|
|
1125
1131
|
readonly name: "set_timezone";
|
|
1126
1132
|
readonly description: "覆盖目标 tab 的时区,传空字符串恢复浏览器默认时区";
|
|
@@ -1213,7 +1219,7 @@ export declare const COMMANDS: readonly [{
|
|
|
1213
1219
|
readonly result: readonly ["frames[] 包含 frameId、url、name 和 isMainFrame;frameId 可用于 page_eval 等支持 frame 定位的 action。"];
|
|
1214
1220
|
readonly examples: ["page_frames --payload '{\"tabId\":12345}'"];
|
|
1215
1221
|
}, {
|
|
1216
|
-
readonly name: "
|
|
1222
|
+
readonly name: "request_permission";
|
|
1217
1223
|
readonly description: "申请访问指定域名的权限(必要时弹窗让用户授权)";
|
|
1218
1224
|
readonly params: [{
|
|
1219
1225
|
readonly name: "domain";
|
|
@@ -1223,7 +1229,7 @@ export declare const COMMANDS: readonly [{
|
|
|
1223
1229
|
}];
|
|
1224
1230
|
readonly notes: readonly ["domain 使用纯主机名,不传 URL。在 send_request 或 set_rule 提示缺少浏览器域名权限时申请。", "弹窗等待用户选择,90 秒超时或关闭按拒绝处理;granted=true 后才重试原请求。"];
|
|
1225
1231
|
readonly result: readonly ["返回 granted、domain,授权成功时包含 origin。"];
|
|
1226
|
-
readonly examples: ["
|
|
1232
|
+
readonly examples: ["request_permission --payload '{\"domain\":\"example.com\"}'"];
|
|
1227
1233
|
}, {
|
|
1228
1234
|
readonly name: "run_actions";
|
|
1229
1235
|
readonly description: "批量顺序执行多个 action,减少往返延迟。遇错即停,返回已完成步骤结果;步骤内的页面动作不带内置观察";
|
package/dist/commands.js
CHANGED
|
@@ -101,7 +101,7 @@ export const COMMANDS = [
|
|
|
101
101
|
},
|
|
102
102
|
{
|
|
103
103
|
name: 'get_requests',
|
|
104
|
-
description: '
|
|
104
|
+
description: '获取最近的网络请求(协议请求或普通 xhr/fetch)',
|
|
105
105
|
params: [
|
|
106
106
|
{
|
|
107
107
|
name: 'tabId',
|
|
@@ -112,8 +112,8 @@ export const COMMANDS = [
|
|
|
112
112
|
{ name: 'count', type: 'number', description: '返回条数,默认 10' },
|
|
113
113
|
{
|
|
114
114
|
name: 'source',
|
|
115
|
-
type: '"
|
|
116
|
-
description: '数据来源,不填时默认
|
|
115
|
+
type: '"protocol" | "requests" | "all"',
|
|
116
|
+
description: '数据来源,不填时默认 all;all 返回全部业务请求',
|
|
117
117
|
},
|
|
118
118
|
{ name: 'filter', type: 'string', description: '按 URL 或 API 名称过滤,支持正则' },
|
|
119
119
|
{ name: 'since', type: 'number', description: '只返回最近 N 秒内的请求' },
|
|
@@ -133,7 +133,7 @@ export const COMMANDS = [
|
|
|
133
133
|
{ name: 'keysOnly', type: 'boolean', description: '只返回数据结构,不返回叶子值' },
|
|
134
134
|
],
|
|
135
135
|
notes: [
|
|
136
|
-
'流式请求开始后即进入缓冲区;页面及 App 桥代发的
|
|
136
|
+
'流式请求开始后即进入缓冲区;页面及 App 桥代发的 协议 SSE 都归入 protocol。无法归属当前页面 target 的 Service Worker 请求不在采集范围内。',
|
|
137
137
|
],
|
|
138
138
|
result: [
|
|
139
139
|
'nextCursor 用于同类查询续读,hasMore 表示还有数据,gap 表示缓冲区淘汰造成缺口;checkpoint cursor 可分别用于三类查询的初始边界。',
|
|
@@ -142,7 +142,7 @@ export const COMMANDS = [
|
|
|
142
142
|
],
|
|
143
143
|
examples: [
|
|
144
144
|
`get_requests --payload '{"tabId":12345,"count":5}'`,
|
|
145
|
-
`get_requests --payload '{"tabId":12345,"source":"
|
|
145
|
+
`get_requests --payload '{"tabId":12345,"source":"protocol","filter":"cart"}'`,
|
|
146
146
|
],
|
|
147
147
|
},
|
|
148
148
|
{
|
|
@@ -249,7 +249,7 @@ export const COMMANDS = [
|
|
|
249
249
|
'大字段可用顶层 @<绝对路径> 文件引用或 --payload-file;文件展开后仍受 Native Messaging 单条消息大小限制。',
|
|
250
250
|
],
|
|
251
251
|
examples: [
|
|
252
|
-
`set_mock --payload '{"tabId":12345,"apiName":"
|
|
252
|
+
`set_mock --payload '{"tabId":12345,"apiName":"cart.query","mockData":{"ret":["SUCCESS::调用成功"]}}'`,
|
|
253
253
|
`set_mock --payload-file ./mock-data.json`,
|
|
254
254
|
],
|
|
255
255
|
},
|
|
@@ -303,7 +303,7 @@ export const COMMANDS = [
|
|
|
303
303
|
],
|
|
304
304
|
notes: [
|
|
305
305
|
'扩展模式规则持久化;CDP 仅支持会话级 modifyHeaders 请求头 set,不支持响应头、initiatorDomains 或其他动作。Provider 是否支持以其能力声明为准。',
|
|
306
|
-
'扩展模式会检查规则请求 URL 和显式来源域名的访问权限;缺失时仍保存规则,返回 warnings 和 missingOrigins,不自动弹窗。可在请求规则页面授权,或按提示调用
|
|
306
|
+
'扩展模式会检查规则请求 URL 和显式来源域名的访问权限;缺失时仍保存规则,返回 warnings 和 missingOrigins,不自动弹窗。可在请求规则页面授权,或按提示调用 request_permission。复杂 URL 表达式无法完整推断域名时会提示手动检查。',
|
|
307
307
|
],
|
|
308
308
|
examples: [
|
|
309
309
|
`set_rule --payload '{"actionType":"block","filter":"^https://example\\\\.com/ads/.*"}'`,
|
|
@@ -369,32 +369,28 @@ export const COMMANDS = [
|
|
|
369
369
|
],
|
|
370
370
|
},
|
|
371
371
|
{
|
|
372
|
-
name: '
|
|
373
|
-
description: '
|
|
372
|
+
name: 'send_protocol_request',
|
|
373
|
+
description: '通过已安装的请求协议能力,使用目标浏览器 Cookie 发送请求',
|
|
374
374
|
params: [
|
|
375
|
-
{ name: '
|
|
376
|
-
{ name: 'data', type: 'object', description: '请求数据' },
|
|
377
|
-
{ name: 'version', type: 'string', description: 'API 版本,默认 1.0' },
|
|
378
|
-
{ name: 'method', type: 'string', description: 'HTTP 方法,默认 GET' },
|
|
379
|
-
{ name: 'appKey', type: 'string', description: '签名使用的 appKey,默认 12574478' },
|
|
380
|
-
{ name: 'env', type: '"prod" | "pre"', description: '请求环境,默认 prod' },
|
|
381
|
-
{ name: 'timeout', type: 'number', description: '超时毫秒数,默认 10000' },
|
|
375
|
+
{ name: 'timeout', type: 'number', description: '宿主超时毫秒数;协议能力负责校验具体参数' },
|
|
382
376
|
],
|
|
383
377
|
notes: [
|
|
384
|
-
'
|
|
385
|
-
'
|
|
386
|
-
],
|
|
387
|
-
result: ['返回 success、response、requestData;业务成功需检查 response.ret。'],
|
|
388
|
-
examples: [
|
|
389
|
-
`send_mtop_request --payload '{"api":"mtop.trade.order.detail","data":{"orderId":"123"}}'`,
|
|
378
|
+
'需要安装提供 request.protocol 的能力包;参数、签名、网关与响应协议由能力包定义。',
|
|
379
|
+
'使用 --payload 传递协议文档声明的参数;核心不设置业务参数默认值。',
|
|
390
380
|
],
|
|
381
|
+
result: ['返回 success、response、requestData;业务结果由协议定义。'],
|
|
382
|
+
examples: [`send_protocol_request --payload '{"operation":"example","data":{}}'`],
|
|
391
383
|
},
|
|
392
384
|
// ── 浏览器 ────────────────────────────────────────────────────────────────
|
|
393
385
|
{
|
|
394
386
|
name: 'browser_list',
|
|
395
387
|
description: '列出所有浏览器及其连接状态、类型和 Provider 托管实例信息',
|
|
396
388
|
summary: '列出本地及远端浏览器与连接状态',
|
|
397
|
-
|
|
389
|
+
params: [
|
|
390
|
+
{ name: 'provider', type: 'string', description: '配合 launchForm,仅查询一个本机已安装 Provider 的启动候选;需 --local' },
|
|
391
|
+
{ name: 'launchForm', type: 'object', description: '现有动态启动表单请求:{variant?,values:{}},不创建或接入实例' },
|
|
392
|
+
],
|
|
393
|
+
examples: ['browser_list', 'browser_list --local'],
|
|
398
394
|
},
|
|
399
395
|
{
|
|
400
396
|
name: 'browser_release',
|
|
@@ -501,6 +497,9 @@ export const COMMANDS = [
|
|
|
501
497
|
name: 'tab_open',
|
|
502
498
|
description: '打开或按 URL 规则复用 Tab,并等待新页面加载完成',
|
|
503
499
|
aliases: ['tab_new', 'new_tab', 'tab_create', 'create_tab'],
|
|
500
|
+
aliasExamples: {
|
|
501
|
+
tab_activate: `tab_open --payload '{"url":"https://example.com","reuse":"require","active":true}'`,
|
|
502
|
+
},
|
|
504
503
|
params: [
|
|
505
504
|
{ name: 'url', type: 'string', required: true, description: '要打开的 URL' },
|
|
506
505
|
{
|
|
@@ -546,7 +545,7 @@ export const COMMANDS = [
|
|
|
546
545
|
{
|
|
547
546
|
name: 'appProfile',
|
|
548
547
|
type: 'string',
|
|
549
|
-
description: '按已安装档案应用 App 环境,ID
|
|
548
|
+
description: '按已安装档案应用 App 环境,ID 与限制见当前动态帮助。需要 iframe WebView 和系统栏时同时传 emulation.deviceShell=true',
|
|
550
549
|
},
|
|
551
550
|
{
|
|
552
551
|
name: 'os',
|
|
@@ -618,6 +617,9 @@ export const COMMANDS = [
|
|
|
618
617
|
{
|
|
619
618
|
name: 'page_click',
|
|
620
619
|
description: '点击页面元素,支持 CSS/@ref、可见文本或视口坐标定位',
|
|
620
|
+
aliasExamples: {
|
|
621
|
+
page_select: `page_click --payload '{"tabId":12345,"text":"选项文案","role":"option"}'`,
|
|
622
|
+
},
|
|
621
623
|
params: [
|
|
622
624
|
{
|
|
623
625
|
name: 'selector',
|
|
@@ -671,7 +673,8 @@ export const COMMANDS = [
|
|
|
671
673
|
result: PAGE_ACTION_RESULT,
|
|
672
674
|
notes: [
|
|
673
675
|
'selector / text / point 必须且只能提供一个。隐藏页 auto 使用 DOM;只有明确依赖真实用户手势时才激活页面。',
|
|
674
|
-
'
|
|
676
|
+
'auto 对原生 option、checkbox/radio 使用状态感知的 DOM 路径;显式 mouse/touch 保留可信输入。',
|
|
677
|
+
'原生 select 可按 option 文本点击;auto/dom 返回 selectionState.valueBefore/valueAfter/selectedAfter,检查选中状态是否保持。checkbox/radio 返回 controlState.checkedBefore/checkedAfter。',
|
|
675
678
|
],
|
|
676
679
|
examples: [
|
|
677
680
|
`page_click --payload '{"tabId":12345,"text":"提交"}'`,
|
|
@@ -726,6 +729,9 @@ export const COMMANDS = [
|
|
|
726
729
|
{
|
|
727
730
|
name: 'page_type',
|
|
728
731
|
description: '向输入框填写文本,兼容 React 受控组件',
|
|
732
|
+
aliasExamples: {
|
|
733
|
+
page_fill: `page_type --payload '{"tabId":12345,"selector":"@e6","text":"填写内容","clearFirst":true}'`,
|
|
734
|
+
},
|
|
729
735
|
params: [
|
|
730
736
|
{
|
|
731
737
|
name: 'selector',
|
|
@@ -911,6 +917,8 @@ export const COMMANDS = [
|
|
|
911
917
|
description: '按键名称:Enter/Tab/Escape/Backspace 等',
|
|
912
918
|
},
|
|
913
919
|
{ name: 'modifiers', type: 'string[]', description: '修饰键数组:ctrl/alt/shift/meta/cmd' },
|
|
920
|
+
{ name: 'dialogId', type: 'string', description: '原生弹窗回执或 page_snapshot 中的 dialog.id;仅配合 Enter 接受或 Escape 取消' },
|
|
921
|
+
{ name: 'promptText', type: 'string', description: '仅用于 dialogId + Enter 接受原生 prompt 时填写文本' },
|
|
914
922
|
{
|
|
915
923
|
name: 'pressMode',
|
|
916
924
|
type: '"auto" | "dom" | "keyboard"',
|
|
@@ -922,6 +930,7 @@ export const COMMANDS = [
|
|
|
922
930
|
],
|
|
923
931
|
notes: [
|
|
924
932
|
'隐藏页 DOM 模式支持 Tab 焦点移动、原生控件 Enter/Space 激活和输入框 Enter 提交;编辑、选择、方向键和页面滚动等原生行为需可见页与 pressMode=keyboard。',
|
|
933
|
+
'原生 alert/confirm/prompt/beforeunload 弹窗需显式传 dialogId;普通 Enter 不处理弹窗。弹窗处理绕过 DOM 观察,不自动重放被中断的动作;处理后重新读取页面。',
|
|
925
934
|
],
|
|
926
935
|
result: [
|
|
927
936
|
...PAGE_ACTION_RESULT,
|
|
@@ -961,6 +970,7 @@ export const COMMANDS = [
|
|
|
961
970
|
],
|
|
962
971
|
notes: [
|
|
963
972
|
'time / selector / text / condition 必须且只能提供一个;time 范围 0–60000ms。页面交互已有内置观察,仅在需要独立等待条件时使用。',
|
|
973
|
+
'等待后须立即执行已知动作时,用 mearl run 在同一脚本中 await pageWait 后紧接 pageClick,避免模型往返。整体 --timeout 须覆盖等待及后续动作;等待失败不继续点击。',
|
|
964
974
|
],
|
|
965
975
|
examples: [
|
|
966
976
|
`page_wait --payload '{"tabId":12345,"time":1000}'`,
|
|
@@ -973,6 +983,9 @@ export const COMMANDS = [
|
|
|
973
983
|
{
|
|
974
984
|
name: 'page_navigate',
|
|
975
985
|
description: '在当前标签页内导航到 URL、刷新页面,或沿浏览历史后退/前进',
|
|
986
|
+
aliasExamples: {
|
|
987
|
+
page_reload: `page_navigate --payload '{"tabId":12345,"refresh":true}'`,
|
|
988
|
+
},
|
|
976
989
|
params: [
|
|
977
990
|
{
|
|
978
991
|
name: 'url',
|
|
@@ -1172,14 +1185,14 @@ export const COMMANDS = [
|
|
|
1172
1185
|
},
|
|
1173
1186
|
{
|
|
1174
1187
|
name: 'set_app_profile',
|
|
1175
|
-
description: '设置已安装的 App 档案;完整 JSBridge
|
|
1188
|
+
description: '设置已安装的 App 档案;完整 JSBridge 由能力包提供。可同时设置已安装的网络控件,传 emulation 时联动设备模拟',
|
|
1176
1189
|
summary: '设置或清除目标标签页的 App 容器模拟',
|
|
1177
1190
|
params: [
|
|
1178
1191
|
{ name: 'enabled', type: 'boolean', required: true, description: 'true=启用,false=清除' },
|
|
1179
1192
|
{
|
|
1180
1193
|
name: 'profile',
|
|
1181
1194
|
type: 'string',
|
|
1182
|
-
description: '已安装的容器档案 ID(enabled=true
|
|
1195
|
+
description: '已安装的容器档案 ID(enabled=true 时必填);当前动态帮助直接展示目标浏览器目录',
|
|
1183
1196
|
},
|
|
1184
1197
|
{ name: 'os', type: '"ios" | "android"', description: '容器系统,默认 ios' },
|
|
1185
1198
|
{
|
|
@@ -1204,9 +1217,9 @@ export const COMMANDS = [
|
|
|
1204
1217
|
description: '声明式覆盖桥方法,形如 {methods:{api:{behavior|response|noop,silent?}}};不执行配置脚本',
|
|
1205
1218
|
},
|
|
1206
1219
|
{
|
|
1207
|
-
name: '
|
|
1208
|
-
type: '
|
|
1209
|
-
description: '
|
|
1220
|
+
name: 'networkControl',
|
|
1221
|
+
type: 'object | null',
|
|
1222
|
+
description: '可选已安装网络控件 {id,value};null 清除,省略时保持当前设置。仅作用于目标 tab',
|
|
1210
1223
|
},
|
|
1211
1224
|
{
|
|
1212
1225
|
name: 'emulation',
|
|
@@ -1222,12 +1235,12 @@ export const COMMANDS = [
|
|
|
1222
1235
|
'bridgeConfig.methods 按方法声明 behavior / response / noop 三选一,可传 silent;只支持受控行为、固定 JSON 回包或成功空实现,不执行配置脚本。',
|
|
1223
1236
|
],
|
|
1224
1237
|
result: [
|
|
1225
|
-
'返回 enabled、profile、label、userAgent、os、
|
|
1238
|
+
'返回 enabled、profile、label、userAgent、os、networkControl、reloaded;联动设备时额外返回实际 emulation。',
|
|
1226
1239
|
],
|
|
1227
1240
|
examples: [
|
|
1228
1241
|
`set_app_profile --payload '{"tabId":12345,"enabled":true,"profile":"reader"}'`,
|
|
1229
|
-
`set_app_profile --payload '{"tabId":12345,"enabled":true,"profile":"reader","
|
|
1230
|
-
`set_app_profile --payload '{"tabId":12345,"enabled":false,"
|
|
1242
|
+
`set_app_profile --payload '{"tabId":12345,"enabled":true,"profile":"reader","networkControl":{"id":"environment","value":"pre"}}'`,
|
|
1243
|
+
`set_app_profile --payload '{"tabId":12345,"enabled":false,"networkControl":null}'`,
|
|
1231
1244
|
`set_app_profile --payload '{"tabId":12345,"enabled":true,"profile":"reader","emulation":{}}'`,
|
|
1232
1245
|
`set_app_profile --payload '{"tabId":12345,"enabled":true,"profile":"wallet","emulation":{"preset":"pixel-11","deviceShell":true}}'`,
|
|
1233
1246
|
`set_app_profile --payload '{"tabId":12345,"enabled":true,"profile":"reader","emulation":{"preset":"ipad-mini-a17-pro","orientation":"landscape","deviceShell":true,"splitScreen":true}}'`,
|
|
@@ -1347,7 +1360,7 @@ export const COMMANDS = [
|
|
|
1347
1360
|
examples: [`page_frames --payload '{"tabId":12345}'`],
|
|
1348
1361
|
},
|
|
1349
1362
|
{
|
|
1350
|
-
name: '
|
|
1363
|
+
name: 'request_permission',
|
|
1351
1364
|
description: '申请访问指定域名的权限(必要时弹窗让用户授权)',
|
|
1352
1365
|
params: [
|
|
1353
1366
|
{
|
|
@@ -1362,7 +1375,7 @@ export const COMMANDS = [
|
|
|
1362
1375
|
'弹窗等待用户选择,90 秒超时或关闭按拒绝处理;granted=true 后才重试原请求。',
|
|
1363
1376
|
],
|
|
1364
1377
|
result: ['返回 granted、domain,授权成功时包含 origin。'],
|
|
1365
|
-
examples: [`
|
|
1378
|
+
examples: [`request_permission --payload '{"domain":"example.com"}'`],
|
|
1366
1379
|
},
|
|
1367
1380
|
// ── 组合调用 ────────────────────────────────────────────────────────────────
|
|
1368
1381
|
{
|
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { UnifiedClient, UnifiedClient as MearlClient, decodeBrowserTarget, invok
|
|
|
13
13
|
export type { UnifiedClientOptions, UnifiedInvokeOptions, UnifiedInvokeOptions as InvokeOptions, } from './unified.js';
|
|
14
14
|
export type { PluginInvocation, PluginInvokeOptions, PluginProgress, StructuredPluginProgress, } from './plugins/sdk/plugin.js';
|
|
15
15
|
export { APP_PROFILE_NAMES, BROWSER_DEVICE_PRESET_NAMES, BROWSER_ACTIONS, BROWSER_CAPABILITY_ACTIONS, BROWSER_COMMAND_ACTIONS, EXTENSION_BROWSER_ACTIONS, PAGE_ACT_ACTIONS, SEND_REQUEST_MAX_FILES, SEND_REQUEST_MAX_TOTAL_BASE64_FILE_BYTES, SEND_REQUEST_MAX_TOTAL_FILE_BYTES, isBrowserAction, isBrowserCapabilityAction, isBrowserCommandAction, isExtensionBrowserAction, } from '@mearl/browser-core';
|
|
16
|
-
export type { ActionStep, AppProfileName, BrowserAction, BrowserActionMap, BrowserActionRequest, BrowserActionResponse, BrowserCapabilities, BrowserCapabilityAction, BrowserCloseParams, BrowserCommandAction, BrowserInfo, BrowserInfoBase, BrowserJsonValue, BrowserLaunchParams, BrowserProviderOperation, BrowserListResult, BrowserPairingInfo, BrowserReleaseFailure, BrowserReleaseResult, BrowserProviderDefinition, BrowserProviderIdentity, BrowserProviderInputSchema, BrowserProviderListResult, BrowserReference, BrowserDetails, BrowserSource, BrowserStatus, BrowserScreenshotKind, BrowserTransport, CaptureCheckpointParams, CaptureCheckpointResult, BrowserDevicePresetName, BrowserEmulationOptions, ExtensionBrowserAction, GetCookieParams, GetCookieResult, GetEventsParams, GetLogsParams, GetMocksParams, GetRequestsParams, ManagedBrowserInfo, PageClickParams, PageDragEndpoint, PageDragParams, PageDragPoint, PageActAction, PageDiagnosticsOptions, PageDiagnosticsSetting, PageEvalParams, PageFramesParams, PageHoverParams, PageInspectFilter, PageInspectParams, PageNavigateParams, PageObservationWaitOptions, PagePressParams, PageScreenshotParams, PageScrollParams, PageSnapshotParams, PageSnapshotQuery, PageTypeParams, PageUploadParams, PageWaitParams, RegularBrowserInfo,
|
|
16
|
+
export type { ActionStep, AppProfileName, BrowserAction, BrowserActionMap, BrowserActionRequest, BrowserActionResponse, BrowserCapabilities, BrowserCapabilityAction, BrowserCloseParams, BrowserCommandAction, BrowserInfo, BrowserInfoBase, BrowserJsonValue, BrowserLaunchParams, BrowserProviderOperation, BrowserListResult, BrowserListParams, BrowserPairingInfo, BrowserReleaseFailure, BrowserReleaseResult, BrowserProviderDefinition, BrowserProviderIdentity, BrowserProviderInputSchema, BrowserProviderListResult, BrowserReference, BrowserDetails, BrowserSource, BrowserStatus, BrowserScreenshotKind, BrowserTransport, CaptureCheckpointParams, CaptureCheckpointResult, BrowserDevicePresetName, BrowserEmulationOptions, ExtensionBrowserAction, GetCookieParams, GetCookieResult, GetEventsParams, GetLogsParams, GetMocksParams, GetRequestsParams, ManagedBrowserInfo, PageClickParams, PageDragEndpoint, PageDragParams, PageDragPoint, PageActAction, PageDiagnosticsOptions, PageDiagnosticsSetting, PageEvalParams, PageFramesParams, PageHoverParams, PageInspectFilter, PageInspectParams, PageNavigateParams, PageObservationWaitOptions, PagePressParams, PageScreenshotParams, PageScrollParams, PageSnapshotParams, PageSnapshotQuery, PageTypeParams, PageUploadParams, PageWaitParams, RegularBrowserInfo, RequestPermissionParams, RulesResult, RunActionName, RunActionsParams, SelectBrowserParams, SelectBrowserResult, SendRequestFile, SendRequestBinaryBody, SendRequestFormValue, SendProtocolRequestParams, SendRequestParams, SetAppProfileParams, SetCookieParams, SetCookieResult, SetDeviceEmulationParams, SetMockParams, SetRuleParams, SetTimezoneParams, SetTimezoneResult, TabCloseParams, TabOpenParams, } from '@mearl/browser-core';
|
|
17
17
|
import { type UnifiedInvokeOptions } from './unified.js';
|
|
18
18
|
import type * as Protocol from '@mearl/browser-core';
|
|
19
19
|
export declare function tabCheckpoint(params: Protocol.CaptureCheckpointParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'tab_checkpoint'>>;
|
|
@@ -25,8 +25,9 @@ export declare function getEvents(params: Protocol.GetEventsParams, options?: Un
|
|
|
25
25
|
export declare function setMock(params: Protocol.SetMockParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'set_mock'>>;
|
|
26
26
|
export declare function getMocks(params: Protocol.GetMocksParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_mocks'>>;
|
|
27
27
|
export declare function sendRequest(params: Protocol.SendRequestParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'send_request'>>;
|
|
28
|
-
export declare function
|
|
28
|
+
export declare function sendProtocolRequest(params: Protocol.SendProtocolRequestParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'send_protocol_request'>>;
|
|
29
29
|
export declare function browserList(options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'browser_list'>>;
|
|
30
|
+
export declare function browserList(params: Protocol.BrowserListParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'browser_list'>>;
|
|
30
31
|
export declare function selectBrowser(params?: Protocol.SelectBrowserParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'browser_select_browser'>>;
|
|
31
32
|
export declare function browserRelease(options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'browser_release'>>;
|
|
32
33
|
export declare function browserRelease(params: Protocol.BrowserReleaseParams, options: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'browser_release'>>;
|
package/dist/index.js
CHANGED
|
@@ -36,11 +36,13 @@ export function getMocks(params, options) {
|
|
|
36
36
|
export function sendRequest(params, options) {
|
|
37
37
|
return invoke('send_request', params, options);
|
|
38
38
|
}
|
|
39
|
-
export function
|
|
40
|
-
return invoke('
|
|
39
|
+
export function sendProtocolRequest(params, options) {
|
|
40
|
+
return invoke('send_protocol_request', params, options);
|
|
41
41
|
}
|
|
42
|
-
export function browserList(options) {
|
|
43
|
-
return
|
|
42
|
+
export function browserList(paramsOrOptions = {}, options) {
|
|
43
|
+
return 'provider' in paramsOrOptions || 'launchForm' in paramsOrOptions
|
|
44
|
+
? invoke('browser_list', paramsOrOptions, options)
|
|
45
|
+
: invoke('browser_list', {}, paramsOrOptions);
|
|
44
46
|
}
|
|
45
47
|
export function selectBrowser(params = {}, options) {
|
|
46
48
|
return invoke('browser_select_browser', params, options);
|
|
@@ -2,6 +2,7 @@ import { closeSync, mkdirSync, openSync, rmSync, statSync, writeFileSync } from
|
|
|
2
2
|
import { CLIENT_VERSION } from '../runtimePaths.js';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { spawn } from 'node:child_process';
|
|
5
|
+
import { npmExecEnvironment } from '@mearl/daemon-core';
|
|
5
6
|
import { findOfficialPlugin, readOfficialPluginCatalog } from './officialPluginCatalog.js';
|
|
6
7
|
import { findPluginRegistration, pluginDirectory } from './pluginRegistry.js';
|
|
7
8
|
const INSTALL_TIMEOUT_MS = 180_000;
|
|
@@ -60,7 +61,7 @@ function appendOutput(current, chunk) {
|
|
|
60
61
|
function installWithSetup(name) {
|
|
61
62
|
return new Promise((resolve, reject) => {
|
|
62
63
|
const command = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
63
|
-
const child = spawn(command, ['-y', '--@mearl:registry=https://registry.npmjs.org', `@mearl/setup@${CLIENT_VERSION}`, 'plugin', 'ensure', name], { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
|
|
64
|
+
const child = spawn(command, ['-y', '--@mearl:registry=https://registry.npmjs.org', `@mearl/setup@${CLIENT_VERSION}`, 'plugin', 'ensure', name], { env: npmExecEnvironment(), stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
|
|
64
65
|
let stdout = '';
|
|
65
66
|
let stderr = '';
|
|
66
67
|
let settled = false;
|
|
@@ -884,7 +884,7 @@ export class PluginTaskManager {
|
|
|
884
884
|
});
|
|
885
885
|
}
|
|
886
886
|
if (restrictedTarget &&
|
|
887
|
-
!['send_request', '
|
|
887
|
+
!['send_request', 'send_protocol_request', 'browser_list', 'connector_list'].includes(action))
|
|
888
888
|
throw error('PLUGIN_PAGE_TARGET_CHANGED', 'Runtime tasks must access the document through context.page');
|
|
889
889
|
this.assertRouting(options, request.options);
|
|
890
890
|
if (client) {
|
package/dist/script.js
CHANGED
|
@@ -208,7 +208,8 @@ export async function executeScript(code, options = {}) {
|
|
|
208
208
|
session: sessionId,
|
|
209
209
|
...args[2],
|
|
210
210
|
});
|
|
211
|
-
const optionsOnly = OPTIONS_ONLY.has(method)
|
|
211
|
+
const optionsOnly = (OPTIONS_ONLY.has(method) && !(method === 'browserList' && args[0] &&
|
|
212
|
+
('provider' in args[0] || 'launchForm' in args[0]))) || (method === 'browserRelease' && args.length < 2);
|
|
212
213
|
return invoke(METHOD_ACTIONS.get(method), optionsOnly ? {} : (args[0] ?? {}), optionsOnly ? args[0] : args[1]);
|
|
213
214
|
})
|
|
214
215
|
.then(result => send({ type: 'response', id, result, session }), error => send({ type: 'response', id, error: serializeError(error), session }))
|
package/dist/socket.js
CHANGED
|
@@ -13,13 +13,13 @@ import os from 'node:os';
|
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import { CLIENT_VERSION } from './runtimePaths.js';
|
|
15
15
|
import { StringDecoder } from 'node:string_decoder';
|
|
16
|
-
import { ensureProviderControlHost, listInstalledProviders } from '@mearl/provider/host';
|
|
17
|
-
import { MEARL_PROVIDER_PROTOCOL_VERSION } from '@mearl/provider';
|
|
18
|
-
import { readLiveProvider } from '@mearl/provider/runtime';
|
|
16
|
+
import { assertProviderControlSessionMode, ensureProviderControlHost, listInstalledProviders } from '@mearl/provider/host';
|
|
17
|
+
import { MEARL_PROVIDER_PROTOCOL_VERSION, assertProviderProtocolVersion, validateProviderLaunchForm } from '@mearl/provider';
|
|
18
|
+
import { readLiveProvider, ensureInstalledProvidersRunning } from '@mearl/provider/runtime';
|
|
19
19
|
import { resolveFileRefs } from './fileRefs.js';
|
|
20
20
|
import { listLiveBrowsers, findBrowser, describeBrowser, } from './browserRegistry.js';
|
|
21
21
|
import { resolveActionTimeoutSec } from './actionTimeouts.js';
|
|
22
|
-
import { isBrowserCapabilityAction } from '@mearl/browser-core';
|
|
22
|
+
import { isBrowserCapabilityAction, parseBrowserListParams } from '@mearl/browser-core';
|
|
23
23
|
export { CLIENT_VERSION };
|
|
24
24
|
const MAX_SOCKET_BUFFER_SIZE = 64 * 1024 * 1024;
|
|
25
25
|
/**
|
|
@@ -376,6 +376,29 @@ export function invoke(action, data = {}, options = {}) {
|
|
|
376
376
|
const resolvedData = resolveFileRefs(data);
|
|
377
377
|
Object.defineProperty(resolvedData, SESSION_OPTION, { value: options.controlSessionId });
|
|
378
378
|
if (action === 'browser_list') {
|
|
379
|
+
const discovery = parseBrowserListParams(resolvedData);
|
|
380
|
+
if (discovery) {
|
|
381
|
+
return (async () => {
|
|
382
|
+
if (transport || options.socketPath || controlSource !== 'local' || options.controlSessionId)
|
|
383
|
+
throw new Error('Provider discovery requires the local source without a transport, socket or session override');
|
|
384
|
+
const provider = listInstalledProviders().find(provider => provider.providerId === discovery.provider);
|
|
385
|
+
if (!provider?.browserType.dynamicLaunchForm)
|
|
386
|
+
throw new Error(`Provider does not support dynamic launch forms: ${discovery.provider}`);
|
|
387
|
+
const warnings = await ensureInstalledProvidersRunning([discovery.provider]);
|
|
388
|
+
if (warnings.length)
|
|
389
|
+
throw new Error(warnings.join('; '));
|
|
390
|
+
const live = readLiveProvider(discovery.provider);
|
|
391
|
+
if (!live)
|
|
392
|
+
throw new Error(`Provider did not start: ${discovery.provider}`);
|
|
393
|
+
assertProviderProtocolVersion(live.providerProtocolVersion, discovery.provider);
|
|
394
|
+
const form = await invokeOnce('browser_providers', { operation: 'launch_form', ...discovery.launchForm }, timeoutSec, undefined, live.socketPath, 'local');
|
|
395
|
+
validateProviderLaunchForm(form);
|
|
396
|
+
return decorateBrowserListResult({
|
|
397
|
+
...browserListFromProviderRecords(listLiveBrowsers().filter(browser => browser.provider?.id === discovery.provider)),
|
|
398
|
+
discovery: { provider: discovery.provider, launchForm: form },
|
|
399
|
+
});
|
|
400
|
+
})();
|
|
401
|
+
}
|
|
379
402
|
return dispatchInvoke(action, resolvedData, timeoutSec, transport, undefined, controlSource, options.socketPath).then(result => decorateBrowserListResult(transport === 'cdp' ? cdpOnlyBrowserList(result) : result));
|
|
380
403
|
}
|
|
381
404
|
const selector = browser ?? defaultBrowser ?? process.env.MEARL_BROWSER ?? undefined;
|
|
@@ -510,7 +533,7 @@ async function dispatchInvoke(action, data, timeoutSec, transport, selector, con
|
|
|
510
533
|
throw new Error(`Browser ${describeBrowser(match)} does not support ${action}`);
|
|
511
534
|
}
|
|
512
535
|
try {
|
|
513
|
-
return await invokeOnce(action, data, timeoutSec, transport, match.socketPath, controlSource, match.transport === 'provider' ? match.browserId : undefined);
|
|
536
|
+
return await invokeOnce(action, data, timeoutSec, transport, match.socketPath, controlSource, match.transport === 'provider' ? match.browserId : undefined, match.capabilities?.controlSessionMode);
|
|
514
537
|
}
|
|
515
538
|
catch (error) {
|
|
516
539
|
if (isConnError(error) || isSocketSandboxError(error)) {
|
|
@@ -527,7 +550,7 @@ async function dispatchInvoke(action, data, timeoutSec, transport, selector, con
|
|
|
527
550
|
if (candidates.length > 0) {
|
|
528
551
|
const target = candidates[0];
|
|
529
552
|
try {
|
|
530
|
-
return await invokeOnce(action, data, timeoutSec, transport, target.socketPath, controlSource, target.transport === 'provider' ? target.browserId : undefined);
|
|
553
|
+
return await invokeOnce(action, data, timeoutSec, transport, target.socketPath, controlSource, target.transport === 'provider' ? target.browserId : undefined, target.capabilities?.controlSessionMode);
|
|
531
554
|
}
|
|
532
555
|
catch (error) {
|
|
533
556
|
if (isSocketSandboxError(error)) {
|
|
@@ -575,7 +598,7 @@ async function invokeViaLegacy(action, data, timeoutSec, transport, controlSourc
|
|
|
575
598
|
!(transport === 'cdp' && browser.transport === 'provider') &&
|
|
576
599
|
supportsAction(browser, action, data));
|
|
577
600
|
if (target) {
|
|
578
|
-
return invokeOnce(action, data, timeoutSec, transport, target.socketPath, controlSource, target.transport === 'provider' ? target.browserId : undefined);
|
|
601
|
+
return invokeOnce(action, data, timeoutSec, transport, target.socketPath, controlSource, target.transport === 'provider' ? target.browserId : undefined, target.capabilities?.controlSessionMode);
|
|
579
602
|
}
|
|
580
603
|
return invokeViaLegacy(action, data, timeoutSec, transport, controlSource,
|
|
581
604
|
/* isRetry */ true, localOnly);
|
|
@@ -586,9 +609,10 @@ async function invokeViaLegacy(action, data, timeoutSec, transport, controlSourc
|
|
|
586
609
|
throw new Error(formatConnectError(err, SOCKET_PATH), { cause: error });
|
|
587
610
|
}
|
|
588
611
|
}
|
|
589
|
-
async function invokeOnce(action, data, timeoutSec, transport, socketPath, controlSource, providerTarget) {
|
|
612
|
+
async function invokeOnce(action, data, timeoutSec, transport, socketPath, controlSource, providerTarget, controlSessionMode) {
|
|
590
613
|
if (providerTarget) {
|
|
591
614
|
const host = await ensureProviderControlHost(Math.min(timeoutSec * 1000, 10_000));
|
|
615
|
+
assertProviderControlSessionMode(host, controlSessionMode);
|
|
592
616
|
socketPath = host.socketPath;
|
|
593
617
|
}
|
|
594
618
|
const id = `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
package/dist/unified.js
CHANGED
|
@@ -3,7 +3,7 @@ import { stageFile, exportArtifact } from './artifacts.js';
|
|
|
3
3
|
import WebSocket from 'ws';
|
|
4
4
|
import { DEFAULT_CONNECT_TIMEOUT, DEFAULT_HEARTBEAT_INTERVAL, DEFAULT_REQUEST_TIMEOUT, MAX_BUFFER_SIZE, isHeartbeatMessage, resolveCloudConnectorSelector, } from '@mearl/cloud-types';
|
|
5
5
|
import { resolveActionTimeoutSec } from './actionTimeouts.js';
|
|
6
|
-
import { validateRequiredActionTabId, } from '@mearl/browser-core';
|
|
6
|
+
import { validateRequiredActionTabId, parseBrowserListParams, } from '@mearl/browser-core';
|
|
7
7
|
import { invoke as invokeLocalBrowser, resolveLocalActionTarget } from './socket.js';
|
|
8
8
|
import { loadCloudServerConfig, loadLocalConnectorId } from './unifiedConfig.js';
|
|
9
9
|
const RESPONSE_GRACE_MS = 1_000;
|
|
@@ -146,6 +146,9 @@ export class UnifiedClient {
|
|
|
146
146
|
}, options);
|
|
147
147
|
}
|
|
148
148
|
async invoke(action, data = {}, options = {}) {
|
|
149
|
+
const discovery = action === 'browser_list' ? parseBrowserListParams(data) : undefined;
|
|
150
|
+
if (discovery && (!options.local || options.connector || options.transport || options.session || options.controlSessionId))
|
|
151
|
+
throw new Error('Provider discovery requires explicit --local without connector, transport, socket or session overrides');
|
|
149
152
|
if (action === 'plugin_state') {
|
|
150
153
|
const { PluginStateClient } = await import('./plugins/clients/pluginState.js');
|
|
151
154
|
const state = new PluginStateClient();
|
|
@@ -350,6 +353,12 @@ export class UnifiedClient {
|
|
|
350
353
|
if (action === 'connector_list')
|
|
351
354
|
return this.listConnectors(timeoutSec);
|
|
352
355
|
if (action === 'browser_list') {
|
|
356
|
+
if (parseBrowserListParams(data)) {
|
|
357
|
+
const result = await invokeLocalBrowser('browser_list', data, { timeoutSec, controlSource: 'local' });
|
|
358
|
+
if (!result?.discovery)
|
|
359
|
+
throw new Error('Provider launch-form discovery is not supported by this host');
|
|
360
|
+
return this.applyConfiguredDefault(this.decorateListing(result, { kind: 'local' }));
|
|
361
|
+
}
|
|
353
362
|
if (options.local || options.transport === 'cdp') {
|
|
354
363
|
return this.listLocalBrowsers(timeoutSec, options.transport);
|
|
355
364
|
}
|
|
@@ -543,6 +552,7 @@ export class UnifiedClient {
|
|
|
543
552
|
})
|
|
544
553
|
: null,
|
|
545
554
|
browsers,
|
|
555
|
+
...(result?.discovery ? { discovery: result.discovery } : {}),
|
|
546
556
|
...(resultWarnings.length > 0 ? { warnings: resultWarnings } : {}),
|
|
547
557
|
};
|
|
548
558
|
}
|
package/dist/unifiedConfig.d.ts
CHANGED
|
@@ -6,3 +6,7 @@ export declare function loadCliAutoUpdateIntervalDays(configPath?: string): numb
|
|
|
6
6
|
export declare function getCloudServerConfigPath(): string;
|
|
7
7
|
export declare function loadCloudServerConfig(): CloudServerConfig | null;
|
|
8
8
|
export declare function loadLocalConnectorId(): string | null;
|
|
9
|
+
export declare function resolveServer(explicit?: string): {
|
|
10
|
+
serverUrl?: string;
|
|
11
|
+
serverUrlSource?: string;
|
|
12
|
+
};
|
package/dist/unifiedConfig.js
CHANGED
|
@@ -56,3 +56,14 @@ export function loadLocalConnectorId() {
|
|
|
56
56
|
return null;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
+
export function resolveServer(explicit) {
|
|
60
|
+
if (explicit)
|
|
61
|
+
return { serverUrl: explicit, serverUrlSource: '--server' };
|
|
62
|
+
if (process.env.MEARL_SERVER_URL) {
|
|
63
|
+
return { serverUrl: process.env.MEARL_SERVER_URL, serverUrlSource: 'MEARL_SERVER_URL' };
|
|
64
|
+
}
|
|
65
|
+
const config = loadCloudServerConfig();
|
|
66
|
+
return config?.server
|
|
67
|
+
? { serverUrl: config.server, serverUrlSource: getCloudServerConfigPath() }
|
|
68
|
+
: {};
|
|
69
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mearl/client",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.7",
|
|
4
4
|
"description": "Unified Mearl SDK & CLI for local and remote browsers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -58,20 +58,20 @@
|
|
|
58
58
|
],
|
|
59
59
|
"license": "Apache-2.0",
|
|
60
60
|
"engines": {
|
|
61
|
-
"node": ">=
|
|
61
|
+
"node": ">=20.18.0"
|
|
62
62
|
},
|
|
63
63
|
"publishConfig": {
|
|
64
64
|
"registry": "https://registry.npmjs.org"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"commander": "^
|
|
67
|
+
"commander": "^14.0.3",
|
|
68
68
|
"fastest-levenshtein": "^1.0.16",
|
|
69
69
|
"ws": "^8.18.0",
|
|
70
|
-
"@mearl/
|
|
71
|
-
"@mearl/
|
|
72
|
-
"@mearl/
|
|
73
|
-
"@mearl/
|
|
74
|
-
"@mearl/
|
|
70
|
+
"@mearl/daemon-core": "3.0.0-beta.7",
|
|
71
|
+
"@mearl/cloud-types": "3.0.0-beta.7",
|
|
72
|
+
"@mearl/provider": "3.0.0-beta.7",
|
|
73
|
+
"@mearl/agent-runtime": "3.0.0-beta.7",
|
|
74
|
+
"@mearl/browser-core": "3.0.0-beta.7"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
77
|
"@types/node": "^24.9.1",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
},
|
|
81
81
|
"repository": {
|
|
82
82
|
"type": "git",
|
|
83
|
-
"url": "git+https://github.com/
|
|
83
|
+
"url": "git+https://github.com/mearlhq/mearl.git",
|
|
84
84
|
"directory": "packages/client"
|
|
85
85
|
},
|
|
86
86
|
"scripts": {
|