Sign In

@mearl/client

Package Overview
Dependencies
Maintainers
2
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mearl/client - npm Package Compare versions

Comparing version
2.7.1
to
2.7.2
+13
dist/commandSuggestions.d.ts
export interface SuggestableCommand {
readonly aliases?: readonly string[];
readonly description: string;
readonly examples?: readonly string[];
readonly name: string;
}
export interface CommandSuggestion {
command: SuggestableCommand;
score: number;
}
export declare function commandInput(args: readonly string[]): string;
export declare function findCommandSuggestions(args: readonly string[], commands: readonly SuggestableCommand[], limit?: number): CommandSuggestion[];
export declare function formatCommandSuggestions(commandName: string, suggestions: readonly CommandSuggestion[]): string[];
import { distance } from 'fastest-levenshtein';
const MIN_SIMILARITY = 0.5;
const MAX_SCORE_GAP = 0.1;
function normalizeCommand(value) {
return value
.normalize('NFKC')
.toLowerCase()
.replace(/[^a-z0-9]+/g, '_')
.replace(/^_+|_+$/g, '');
}
function leadingCommandParts(args) {
const parts = [];
for (const arg of args) {
if (arg.startsWith('-'))
break;
parts.push(arg);
}
return parts;
}
function commandQueries(args) {
const parts = leadingCommandParts(args);
const queries = new Set();
for (let length = parts.length; length > 0; length -= 1) {
const query = normalizeCommand(parts.slice(0, length).join('_'));
if (query)
queries.add(query);
}
return [...queries];
}
function similarity(left, right) {
const length = Math.max(left.length, right.length);
return length === 0 ? 0 : (length - distance(left, right)) / length;
}
export function commandInput(args) {
return leadingCommandParts(args).join(' ');
}
export function findCommandSuggestions(args, commands, limit = 3) {
const queries = commandQueries(args);
if (queries.length === 0 || limit <= 0)
return [];
const ranked = commands
.map(command => {
const keys = [command.name, ...(command.aliases ?? [])].map(normalizeCommand);
const score = Math.max(...queries.flatMap(query => keys.map(key => similarity(query, key))));
return { command, score };
})
.filter(suggestion => suggestion.score >= MIN_SIMILARITY)
.sort((left, right) => right.score - left.score || left.command.name.localeCompare(right.command.name));
const bestScore = ranked[0]?.score;
if (bestScore === undefined)
return [];
return ranked.filter(suggestion => bestScore - suggestion.score <= MAX_SCORE_GAP).slice(0, limit);
}
export function formatCommandSuggestions(commandName, suggestions) {
return suggestions.map(({ command }) => {
const invocation = command.examples?.[0] ?? command.name;
return ` ${commandName} ${invocation}\n ${command.description}`;
});
}
+6
-0
import { PAGE_ACT_ACTIONS } from './generated-browser-action-protocol.js';
const SEND_REQUEST_FILE_TRANSFER_TIMEOUT_MS = 120_000;
// AgentBay staging can spend up to 60s transferring bytes and then 120s syncing the session disk.
const PAGE_UPLOAD_FILE_TRANSFER_TIMEOUT_MS = 180_000;
// AgentBay context sync can poll for about 12 minutes before session deletion waits up to 5 more.

@@ -59,2 +61,6 @@ const BROWSER_CLOSE_TIMEOUT_SEC = 18 * 60;

: data.observe !== false;
if (action === 'page_upload') {
const actionTimeoutSec = observationEnabled ? resolveObservedActionTimeoutSec(data) : fallback;
return Math.max(fallback, actionTimeoutSec + Math.ceil(PAGE_UPLOAD_FILE_TRANSFER_TIMEOUT_MS / 1000));
}
if (observedAction && observationEnabled) {

@@ -61,0 +67,0 @@ return Math.max(fallback, resolveObservedActionTimeoutSec(data));

+52
-11

@@ -5,3 +5,4 @@ import { readFileSync, writeFileSync } from 'node:fs';

import { runUnifiedCheck, parseCheckTimeout } from './check.js';
import { COMMAND_MAP } from './commands.js';
import { commandInput, findCommandSuggestions, formatCommandSuggestions, } from './commandSuggestions.js';
import { COMMANDS, COMMAND_MAP } from './commands.js';
import { CLIENT_VERSION } from './socket.js';

@@ -13,3 +14,3 @@ import { UnifiedClient } from './unified.js';

title: 'API 调试',
names: ['capture_checkpoint', 'get_requests', 'get_logs', 'get_events', 'get_api_schema'],
names: ['tab_checkpoint', 'get_requests', 'get_logs', 'get_events', 'get_api_schema'],
},

@@ -50,2 +51,11 @@ { title: 'Mock & 规则', names: ['set_mock', 'get_mocks', 'set_rule', 'get_rules'] },

];
const BUILTIN_COMMANDS = [
{
name: 'check',
description: '检查指定浏览器,未指定时检查全部已连接浏览器',
examples: ['check'],
},
];
const SUGGESTABLE_COMMANDS = [...COMMANDS, ...BUILTIN_COMMANDS];
const ADVANCED_BUILTIN_COMMANDS = new Set(['connector_list']);
function optionValue(args, name) {

@@ -82,3 +92,2 @@ const index = args.indexOf(name);

' check 检查指定浏览器,未指定时检查全部已连接浏览器',
' connector_list 列出 cloud-server 下的 connector',
];

@@ -96,2 +105,13 @@ for (const category of CATEGORIES) {

}
function printAdvancedCommandHelp(commandName, action) {
if (action !== 'connector_list')
return;
console.error([
'connector_list',
' 列出 cloud-server 下的 connector(高级诊断)',
'',
'用法:',
` ${commandName} connector_list [--server <url>] [--timeout <seconds>]`,
].join('\n'));
}
function printCommandHelp(commandName, command) {

@@ -110,3 +130,3 @@ const lines = [

const required = parameter.required ? '必填' : '可选';
lines.push(` ${parameter.name.padEnd(22)}${required.padEnd(6)}${parameter.type.padEnd(28)}${parameter.description}`);
lines.push(` ${parameter.name.padEnd(22)}${required.padEnd(6)}${parameter.type.padEnd(28)} ${parameter.description}`);
}

@@ -122,2 +142,14 @@ lines.push('');

}
function printUnknownCommand(commandName, args) {
const input = commandInput(args) || args[0] || '';
const suggestions = findCommandSuggestions(args, SUGGESTABLE_COMMANDS);
const lines = [`不支持的命令: ${input}`];
if (suggestions.length > 0) {
lines.push('', '你可能想用:', ...formatCommandSuggestions(commandName, suggestions));
}
else {
lines.push(`运行 ${commandName} --help 查看所有可用命令`);
}
console.error(lines.join('\n'));
}
function parsePositiveTimeout(value, fallback) {

@@ -240,2 +272,4 @@ if (value === undefined)

const clientVersion = options.clientVersion ?? CLIENT_VERSION;
if (args[0] === 'capture_checkpoint')
args = ['tab_checkpoint', ...args.slice(1)];
if (args.includes('--version') || args.includes('-v')) {

@@ -253,6 +287,18 @@ console.log(clientVersion);

printCommandHelp(commandName, command);
else
else if (ADVANCED_BUILTIN_COMMANDS.has(args[0])) {
printAdvancedCommandHelp(commandName, args[0]);
}
else if (args[0]?.startsWith('-') || BUILTIN_COMMANDS.some(item => item.name === args[0])) {
printUsage(commandName, clientVersion);
}
else {
printUnknownCommand(commandName, args);
return 2;
}
return 0;
}
if (args[0] !== 'check' && args[0] !== 'connector_list' && !COMMAND_MAP.has(args[0])) {
printUnknownCommand(commandName, args);
return 2;
}
let parsed;

@@ -284,7 +330,2 @@ try {

}
if (parsed.action !== 'connector_list' && !COMMAND_MAP.has(parsed.action)) {
console.error(`不支持的 action: ${parsed.action}`);
console.error(`运行 ${commandName} --help 查看所有可用命令`);
return 2;
}
if (parsed.action === 'page_selected_element' && parsed.outputPath) {

@@ -311,3 +352,3 @@ parsed.payload = { ...parsed.payload, includeScreenshot: true };

}
return 0;
return result?.action?.success === false ? 1 : 0;
}

@@ -314,0 +355,0 @@ catch (error) {

+30
-49

@@ -11,2 +11,4 @@ import type { BrowserCommandAction } from './generated-browser-action-protocol.js';

description: string;
/** CLI 中用于推荐 canonical action 的常见表达,不会作为可执行 action。 */
aliases?: readonly string[];
/** payload 字段说明 */

@@ -19,4 +21,4 @@ params?: ParamDef[];

export declare const COMMANDS: readonly [{
readonly name: "capture_checkpoint";
readonly description: "为目标标签页建立 console/network 增量边界";
readonly name: "tab_checkpoint";
readonly description: "为目标标签页建立日志、请求和埋点增量边界";
readonly params: [{

@@ -27,3 +29,3 @@ readonly name: "tabId";

}];
readonly examples: ["capture_checkpoint --payload '{\"tabId\":12345}'"];
readonly examples: ["tab_checkpoint --payload '{\"tabId\":12345}'"];
}, {

@@ -55,3 +57,3 @@ readonly name: "get_requests";

readonly type: "string";
readonly description: "只返回指定 capture cursor 之后的请求";
readonly description: "只返回指定 tab checkpoint 或同类查询 cursor 之后的请求";
}, {

@@ -89,3 +91,3 @@ readonly name: "includeBody";

readonly type: "string";
readonly description: "只返回指定 capture cursor 之后的日志";
readonly description: "只返回指定 tab checkpoint 或同类查询 cursor 之后的日志";
}];

@@ -117,2 +119,6 @@ readonly examples: ["get_logs --payload '{\"limit\":10,\"level\":\"error\"}'"];

}, {
readonly name: "after";
readonly type: "string";
readonly description: "只返回指定 tab checkpoint 之后的事件";
}, {
readonly name: "limit";

@@ -392,2 +398,3 @@ readonly type: "number";

readonly description: "打开或按 URL 规则复用 Tab,并等待新页面加载完成";
readonly aliases: readonly ["tab_new", "new_tab", "tab_create", "create_tab"];
readonly params: [{

@@ -435,2 +442,3 @@ readonly name: "url";

readonly description: "关闭指定标签页";
readonly aliases: readonly ["tab_remove", "remove_tab", "tab_delete", "delete_tab"];
readonly params: [{

@@ -446,6 +454,7 @@ readonly name: "tabId";

readonly description: "获取当前窗口所有标签页列表";
readonly aliases: readonly ["tabs", "list_tabs", "show_tabs"];
readonly examples: ["tab_list"];
}, {
readonly name: "page_click";
readonly description: "点击页面元素,支持 selector / text / point 三种定位方式;auto 在可见页按设备模拟状态使用可信 mouse/touch,隐藏页使用 DOM fallback;点击直接打开新标签页时 observation.openedTabs 返回目标信息";
readonly description: "点击页面元素,支持 CSS/@ref、可见文本或视口坐标定位";
readonly params: [{

@@ -478,8 +487,4 @@ readonly name: "selector";

readonly type: "\"auto\" | \"dom\" | \"mouse\" | \"touch\"";
readonly description: "点击模式;auto 在可见页按设备模拟状态选择可信 mouse/touch、隐藏页走 DOM,checkbox/radio 校验状态;其他值强制指定派发方式";
}, {
readonly name: "observe";
readonly type: "object | false";
readonly description: "观察选项(quietMs/firstChangeTimeoutMs/timeoutMs/navigationGraceMs/navigationTimeoutMs/networkIdleMs/contentReadyAfterMs);传 false 只关闭 DOM/导航观察,默认诊断仍会返回";
}, ParamDef, {
readonly description: "默认 auto:可见页使用匹配设备的可信输入,隐藏页使用 DOM;dom/mouse/touch 可强制指定派发方式";
}, ParamDef, ParamDef, {
readonly name: "tabId";

@@ -493,3 +498,3 @@ readonly type: "number";

readonly name: "page_drag";
readonly description: "在页面内执行拖动;from/to 均可使用视口坐标或 CSS/@ref,auto 在可见页使用可信 mouse/touch、隐藏页使用 DOM 模拟,并默认观察拖动后的页面变化";
readonly description: "拖动页面元素或视口坐标,适用于轮播图、滑块和拖放";
readonly params: [{

@@ -508,3 +513,3 @@ readonly name: "from";

readonly type: "\"auto\" | \"dom\" | \"mouse\" | \"touch\"";
readonly description: "拖动输入类型;默认 auto,可见页按设备使用可信输入、隐藏页使用 DOM 模拟;dom 可显式强制后台模拟";
readonly description: "默认 auto:可见页使用匹配设备的可信输入,隐藏页使用 DOM;dom/mouse/touch 可强制指定派发方式";
}, {

@@ -514,7 +519,3 @@ readonly name: "durationMs";

readonly description: "拖动持续时间,0–5000ms,默认 400ms;内部自动分段移动";
}, {
readonly name: "observe";
readonly type: "object | false";
readonly description: "观察选项;传 false 只关闭 DOM/导航观察,默认诊断仍会返回";
}, ParamDef, {
}, ParamDef, ParamDef, {
readonly name: "tabId";

@@ -528,3 +529,3 @@ readonly type: "number";

readonly name: "page_type";
readonly description: "向输入框填写文本,兼容 React 受控组件;默认观察校验提示等异步变化";
readonly description: "向输入框填写文本,兼容 React 受控组件";
readonly params: [{

@@ -544,7 +545,3 @@ readonly name: "selector";

readonly description: "是否先清空原有内容,默认 true";
}, {
readonly name: "observe";
readonly type: "object | false";
readonly description: "观察选项;传 false 只关闭 DOM/导航观察,默认诊断仍会返回";
}, ParamDef, {
}, ParamDef, ParamDef, {
readonly name: "tabId";

@@ -558,3 +555,3 @@ readonly type: "number";

readonly name: "page_hover";
readonly description: "将鼠标悬停在元素上,默认观察下拉菜单、提示信息等 hover 后变化";
readonly description: "将鼠标悬停在页面元素上";
readonly params: [{

@@ -565,7 +562,3 @@ readonly name: "selector";

readonly description: "原生 CSS 选择器,或 page_snapshot / 页面动作 observation 返回的 @eN ref(必须带 @ 前缀)";
}, {
readonly name: "observe";
readonly type: "object | false";
readonly description: "观察选项;传 false 只关闭 DOM/导航观察,默认诊断仍会返回";
}, ParamDef, {
}, ParamDef, ParamDef, {
readonly name: "tabId";

@@ -579,3 +572,3 @@ readonly type: "number";

readonly name: "page_scroll";
readonly description: "滚动页面或指定容器,默认观察懒加载等滚动后变化";
readonly description: "滚动页面或指定容器";
readonly params: [{

@@ -598,7 +591,3 @@ readonly name: "direction";

readonly description: "默认 self,要求 selector 本身可滚动;nearest 会在其不可滚动时使用最近的可滚动祖先";
}, {
readonly name: "observe";
readonly type: "object | false";
readonly description: "观察选项;传 false 只关闭 DOM/导航观察,默认诊断仍会返回";
}, ParamDef, {
}, ParamDef, ParamDef, {
readonly name: "tabId";

@@ -635,3 +624,3 @@ readonly type: "number";

readonly name: "page_press";
readonly description: "在页面中按下键盘按键,默认观察提交、关闭弹窗等结果";
readonly description: "在页面中按下键盘按键或组合键";
readonly params: [{

@@ -646,7 +635,3 @@ readonly name: "key";

readonly description: "修饰键数组:ctrl/alt/shift/meta/cmd";
}, {
readonly name: "observe";
readonly type: "object | false";
readonly description: "观察选项;传 false 只关闭 DOM/导航观察,默认诊断仍会返回";
}, ParamDef, {
}, ParamDef, ParamDef, {
readonly name: "tabId";

@@ -712,3 +697,3 @@ readonly type: "number";

readonly name: "page_upload";
readonly description: "向 <input type=\"file\"> 元素上传文件,默认观察上传状态变化";
readonly description: "向 <input type=\"file\"> 元素上传一个或多个文件";
readonly params: [{

@@ -724,7 +709,3 @@ readonly name: "selector";

readonly description: "本地文件路径数组";
}, {
readonly name: "observe";
readonly type: "object | false";
readonly description: "观察选项;传 false 只关闭 DOM/导航观察,默认诊断仍会返回";
}, ParamDef, {
}, ParamDef, ParamDef, {
readonly name: "tabId";

@@ -731,0 +712,0 @@ readonly type: "number";

const PAGE_DIAGNOSTICS_PARAM = {
name: 'diagnostics',
type: 'object | boolean',
description: '交互动作默认在执行前原子建立边界,并返回新增 error logs/全部业务 requests;传 false 关闭,或传对象自定义过滤',
description: '默认关闭;传 true 返回动作期间新增的 error logs/业务 requests,或传对象自定义通道与过滤',
};
const PAGE_OBSERVE_PARAM = {
name: 'observe',
type: 'object | false',
description: '默认开启 DOM/导航观察;传 false 关闭,不影响显式请求的 diagnostics',
};
export const COMMANDS = [
// ── API 调试 ──────────────────────────────────────────────────────────────
{
name: 'capture_checkpoint',
description: '为目标标签页建立 console/network 增量边界',
name: 'tab_checkpoint',
description: '为目标标签页建立日志、请求和埋点增量边界',
params: [{ name: 'tabId', type: 'number', description: '目标标签页 ID,从 tab_open 获取' }],
examples: [`capture_checkpoint --payload '{"tabId":12345}'`],
examples: [`tab_checkpoint --payload '{"tabId":12345}'`],
},

@@ -27,3 +32,7 @@ {

{ name: 'since', type: 'number', description: '只返回最近 N 秒内的请求' },
{ name: 'after', type: 'string', description: '只返回指定 capture cursor 之后的请求' },
{
name: 'after',
type: 'string',
description: '只返回指定 tab checkpoint 或同类查询 cursor 之后的请求',
},
{ name: 'includeBody', type: 'boolean', description: '是否包含请求/响应体,默认 true' },

@@ -45,3 +54,7 @@ ],

{ name: 'since', type: 'number', description: '只返回最近 N 秒内的日志' },
{ name: 'after', type: 'string', description: '只返回指定 capture cursor 之后的日志' },
{
name: 'after',
type: 'string',
description: '只返回指定 tab checkpoint 或同类查询 cursor 之后的日志',
},
],

@@ -63,2 +76,3 @@ examples: [`get_logs --payload '{"limit":10,"level":"error"}'`],

{ name: 'since', type: 'number', description: '只返回最近 N 秒内的事件' },
{ name: 'after', type: 'string', description: '只返回指定 tab checkpoint 之后的事件' },
{ name: 'limit', type: 'number', description: '返回条数,默认 20' },

@@ -317,2 +331,3 @@ {

description: '打开或按 URL 规则复用 Tab,并等待新页面加载完成',
aliases: ['tab_new', 'new_tab', 'tab_create', 'create_tab'],
params: [

@@ -367,2 +382,3 @@ { name: 'url', type: 'string', required: true, description: '要打开的 URL' },

description: '关闭指定标签页',
aliases: ['tab_remove', 'remove_tab', 'tab_delete', 'delete_tab'],
params: [{ name: 'tabId', type: 'number', required: true, description: '要关闭的标签页 ID' }],

@@ -374,2 +390,3 @@ examples: [`tab_close --payload '{"tabId":123}'`],

description: '获取当前窗口所有标签页列表',
aliases: ['tabs', 'list_tabs', 'show_tabs'],
examples: ['tab_list'],

@@ -380,3 +397,3 @@ },

name: 'page_click',
description: '点击页面元素,支持 selector / text / point 三种定位方式;auto 在可见页按设备模拟状态使用可信 mouse/touch,隐藏页使用 DOM fallback;点击直接打开新标签页时 observation.openedTabs 返回目标信息',
description: '点击页面元素,支持 CSS/@ref、可见文本或视口坐标定位',
params: [

@@ -416,9 +433,5 @@ {

type: '"auto" | "dom" | "mouse" | "touch"',
description: '点击模式;auto 在可见页按设备模拟状态选择可信 mouse/touch、隐藏页走 DOM,checkbox/radio 校验状态;其他值强制指定派发方式',
description: '默认 auto:可见页使用匹配设备的可信输入,隐藏页使用 DOM;dom/mouse/touch 可强制指定派发方式',
},
{
name: 'observe',
type: 'object | false',
description: '观察选项(quietMs/firstChangeTimeoutMs/timeoutMs/navigationGraceMs/navigationTimeoutMs/networkIdleMs/contentReadyAfterMs);传 false 只关闭 DOM/导航观察,默认诊断仍会返回',
},
PAGE_OBSERVE_PARAM,
PAGE_DIAGNOSTICS_PARAM,

@@ -439,3 +452,3 @@ { name: 'tabId', type: 'number', required: true, description: '目标标签页 ID' },

name: 'page_drag',
description: '在页面内执行拖动;from/to 均可使用视口坐标或 CSS/@ref,auto 在可见页使用可信 mouse/touch、隐藏页使用 DOM 模拟,并默认观察拖动后的页面变化',
description: '拖动页面元素或视口坐标,适用于轮播图、滑块和拖放',
params: [

@@ -457,3 +470,3 @@ {

type: '"auto" | "dom" | "mouse" | "touch"',
description: '拖动输入类型;默认 auto,可见页按设备使用可信输入、隐藏页使用 DOM 模拟;dom 可显式强制后台模拟',
description: '默认 auto:可见页使用匹配设备的可信输入,隐藏页使用 DOM;dom/mouse/touch 可强制指定派发方式',
},

@@ -465,7 +478,3 @@ {

},
{
name: 'observe',
type: 'object | false',
description: '观察选项;传 false 只关闭 DOM/导航观察,默认诊断仍会返回',
},
PAGE_OBSERVE_PARAM,
PAGE_DIAGNOSTICS_PARAM,

@@ -481,3 +490,3 @@ { name: 'tabId', type: 'number', required: true, description: '目标标签页 ID' },

name: 'page_type',
description: '向输入框填写文本,兼容 React 受控组件;默认观察校验提示等异步变化',
description: '向输入框填写文本,兼容 React 受控组件',
params: [

@@ -492,7 +501,3 @@ {

{ name: 'clearFirst', type: 'boolean', description: '是否先清空原有内容,默认 true' },
{
name: 'observe',
type: 'object | false',
description: '观察选项;传 false 只关闭 DOM/导航观察,默认诊断仍会返回',
},
PAGE_OBSERVE_PARAM,
PAGE_DIAGNOSTICS_PARAM,

@@ -505,3 +510,3 @@ { name: 'tabId', type: 'number', required: true, description: '目标标签页 ID' },

name: 'page_hover',
description: '将鼠标悬停在元素上,默认观察下拉菜单、提示信息等 hover 后变化',
description: '将鼠标悬停在页面元素上',
params: [

@@ -514,7 +519,3 @@ {

},
{
name: 'observe',
type: 'object | false',
description: '观察选项;传 false 只关闭 DOM/导航观察,默认诊断仍会返回',
},
PAGE_OBSERVE_PARAM,
PAGE_DIAGNOSTICS_PARAM,

@@ -527,3 +528,3 @@ { name: 'tabId', type: 'number', required: true, description: '目标标签页 ID' },

name: 'page_scroll',
description: '滚动页面或指定容器,默认观察懒加载等滚动后变化',
description: '滚动页面或指定容器',
params: [

@@ -547,7 +548,3 @@ {

},
{
name: 'observe',
type: 'object | false',
description: '观察选项;传 false 只关闭 DOM/导航观察,默认诊断仍会返回',
},
PAGE_OBSERVE_PARAM,
PAGE_DIAGNOSTICS_PARAM,

@@ -585,3 +582,3 @@ { name: 'tabId', type: 'number', required: true, description: '目标标签页 ID' },

name: 'page_press',
description: '在页面中按下键盘按键,默认观察提交、关闭弹窗等结果',
description: '在页面中按下键盘按键或组合键',
params: [

@@ -595,7 +592,3 @@ {

{ name: 'modifiers', type: 'string[]', description: '修饰键数组:ctrl/alt/shift/meta/cmd' },
{
name: 'observe',
type: 'object | false',
description: '观察选项;传 false 只关闭 DOM/导航观察,默认诊断仍会返回',
},
PAGE_OBSERVE_PARAM,
PAGE_DIAGNOSTICS_PARAM,

@@ -660,3 +653,3 @@ { name: 'tabId', type: 'number', required: true, description: '目标标签页 ID' },

name: 'page_upload',
description: '向 <input type="file"> 元素上传文件,默认观察上传状态变化',
description: '向 <input type="file"> 元素上传一个或多个文件',
params: [

@@ -670,7 +663,3 @@ {

{ name: 'filePaths', type: 'string[]', required: true, description: '本地文件路径数组' },
{
name: 'observe',
type: 'object | false',
description: '观察选项;传 false 只关闭 DOM/导航观察,默认诊断仍会返回',
},
PAGE_OBSERVE_PARAM,
PAGE_DIAGNOSTICS_PARAM,

@@ -677,0 +666,0 @@ { name: 'tabId', type: 'number', required: true, description: '目标标签页 ID' },

@@ -7,3 +7,3 @@ export interface GetRequestsParams {

since?: number;
/** Opaque cursor returned by capture_checkpoint or a previous query. */
/** Opaque cursor returned by tab_checkpoint or a previous query. */
after?: string;

@@ -23,3 +23,3 @@ includeBody?: boolean;

since?: number;
/** Opaque cursor returned by capture_checkpoint or a previous query. */
/** Opaque cursor returned by tab_checkpoint or a previous query. */
after?: string;

@@ -44,2 +44,4 @@ limit?: number;

since?: number;
/** Opaque cursor returned by tab_checkpoint or a previous event query. */
after?: string;
limit?: number;

@@ -305,2 +307,3 @@ includeRaw?: boolean;

requests?: boolean | Pick<GetRequestsParams, 'count' | 'filter' | 'includeBody' | 'includeHeaders' | 'maxBodySize' | 'fields' | 'keysOnly' | 'source'>;
events?: boolean | Pick<GetEventsParams, 'source' | 'event_type' | 'filter' | 'limit' | 'includeRaw'>;
}

@@ -478,2 +481,7 @@ export type PageDiagnosticsSetting = PageDiagnosticsOptions | boolean;

export interface BrowserActionMap {
tab_checkpoint: {
request: CaptureCheckpointParams;
response: CaptureCheckpointResult;
};
/** Compatibility alias for tab_checkpoint. */
capture_checkpoint: {

@@ -660,2 +668,3 @@ request: CaptureCheckpointParams;

export declare const BROWSER_ACTIONS: {
readonly tab_checkpoint: true;
readonly capture_checkpoint: true;

@@ -706,2 +715,3 @@ readonly get_requests: true;

export declare const BROWSER_COMMAND_ACTIONS: {
readonly tab_checkpoint: true;
readonly capture_checkpoint: true;

@@ -751,2 +761,3 @@ readonly get_requests: true;

export declare const EXTENSION_BROWSER_ACTIONS: {
readonly tab_checkpoint: true;
readonly capture_checkpoint: true;

@@ -753,0 +764,0 @@ readonly get_requests: true;

@@ -20,2 +20,3 @@ // Generated by scripts/generate-client-action-protocol.mjs.

export const BROWSER_ACTIONS = {
tab_checkpoint: true,
capture_checkpoint: true,

@@ -66,2 +67,3 @@ get_requests: true,

export const BROWSER_COMMAND_ACTIONS = {
tab_checkpoint: true,
capture_checkpoint: true,

@@ -111,2 +113,3 @@ get_requests: true,

export const EXTENSION_BROWSER_ACTIONS = {
tab_checkpoint: true,
capture_checkpoint: true,

@@ -113,0 +116,0 @@ get_requests: true,

@@ -13,3 +13,5 @@ /**

import type * as Protocol from './generated-browser-action-protocol.js';
export declare function captureCheckpoint(params?: Protocol.CaptureCheckpointParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'capture_checkpoint'>>;
export declare function tabCheckpoint(params?: Protocol.CaptureCheckpointParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'tab_checkpoint'>>;
/** Compatibility wrapper for tabCheckpoint. */
export declare function captureCheckpoint(params?: Protocol.CaptureCheckpointParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'tab_checkpoint'>>;
export declare function getRequests(params?: Protocol.GetRequestsParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_requests'>>;

@@ -16,0 +18,0 @@ export declare function getLogs(params?: Protocol.GetLogsParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_logs'>>;

@@ -10,4 +10,8 @@ /**

import { invoke } from './unified.js';
export function tabCheckpoint(params = {}, options) {
return invoke('tab_checkpoint', params, options);
}
/** Compatibility wrapper for tabCheckpoint. */
export function captureCheckpoint(params = {}, options) {
return invoke('capture_checkpoint', params, options);
return tabCheckpoint(params, options);
}

@@ -14,0 +18,0 @@ export function getRequests(params = {}, options) {

@@ -208,3 +208,8 @@ import WebSocket from 'ws';

}
throw new Error('The browser host for browser_launch is ambiguous. Pass --local, --connector <id|name>, or --browser <global-id>.');
const availableHosts = [
...(localAvailable ? ['this machine [--local]'] : []),
...connectors.map(connector => `${connector.name} [${connector.connectorId}]`),
].join(', ');
throw new Error('The browser host for browser_launch is ambiguous. ' +
`Pass --local, --connector <id|name>, or --browser <global-id>. Available: ${availableHosts}`);
}

@@ -265,7 +270,10 @@ async selectDefaultBrowser(data, options, timeoutSec) {

const selection = resolveCloudConnectorSelector(connectors, selector);
const available = connectors
.map(connector => `${connector.name} [${connector.connectorId}]`)
.join(', ');
if (selection.status === 'ambiguous') {
throw new Error(`Cloud connector selector "${selector}" is ambiguous. Use an exact connectorId.`);
throw new Error(`Cloud connector selector "${selector}" is ambiguous. Use an exact connectorId. Available: ${available}`);
}
if (selection.status !== 'matched') {
throw new Error(`No cloud connector matches "${selector}".`);
throw new Error(`No cloud connector matches "${selector}". Available: ${available}`);
}

@@ -272,0 +280,0 @@ return selection.connector;

{
"name": "@mearl/client",
"version": "2.7.1",
"version": "2.7.2",
"description": "Unified Mearl SDK & CLI for local and remote browsers",

@@ -51,4 +51,5 @@ "type": "module",

"dependencies": {
"fastest-levenshtein": "^1.0.16",
"ws": "^8.18.0",
"@mearl/cloud-types": "2.7.1"
"@mearl/cloud-types": "2.7.2"
},

@@ -55,0 +56,0 @@ "devDependencies": {

@@ -76,2 +76,5 @@ # @mearl/client

输入不存在或拼写有误的 action 时,CLI 会基于可用命令和常见别名直接返回最多三个候选及调用示例,
不连接浏览器,也不会自动执行推荐命令。例如 `mearl tab new` 会优先推荐 `mearl tab_open`。
**通用选项:**

@@ -95,3 +98,3 @@

| ----------- | --------------------------------------------------------------------------------------------------------------------- |
| API 调试 | `capture_checkpoint` `get_requests` `get_logs` `get_events` `get_api_schema` |
| API 调试 | `tab_checkpoint` `get_requests` `get_logs` `get_events` `get_api_schema` |
| Mock & 规则 | `set_mock` `get_mocks` `set_rule` `get_rules` |

@@ -108,7 +111,7 @@ | 网络代理 | `send_request` `send_mtop_request` |

页面交互动作(`page_click` / `page_drag` / `page_type` / `page_hover` / `page_scroll` / `page_press` / `page_upload`)默认内置观察与原子诊断:同一次调用内执行动作、等待异步稳定,并返回 `{ action, observation, diagnostics }`;`diagnostics` 默认包含新增 error logs 和全部业务 requests。传 `observe: false` 只关闭 DOM/导航观察并保留诊断;同时传 `diagnostics: false` 才仅执行裸动作。观察结果不替代完整页面理解:`mode: "delta"` 只返回主文档中的 `effects.notifications`、`effects.interactives` 和 `effects.focus` 等高置信度信号,并明确携带 `scope: "main-document"`;可交互节点会尽量携带真实 backend `node.ref`,后续动作优先使用 ref,缺少 ref 时使用 `node.selector`。`mode: "navigation"` 且 `ready: true` 时,页面已通过网络静默、骨架状态或保守的内容稳定判定,可在新页面重建快照。动作直接打开新标签页时,`observation.openedTabs` 返回新标签页的 `tabId`、URL、标题和加载状态,可直接把该 `tabId` 用于后续操作,不必调用 `tab_list`。通常仅在 `fullSnapshotRecommended` 为 true 时根据 `snapshotReasons` 回退;滚动后若下一步需要读取新视口内容,可按需获取 viewport 快照。`page_eval` 默认裸执行;显式传 `observe` 对象可启用观察,传 `diagnostics: true` 或对象可启用原子诊断。
页面交互动作(`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` 默认裸执行,可按需开启观察或诊断。
`page_snapshot` 默认返回完整 AX Tree;长列表可传 `mode: "viewport"`,只需要当前视口内的控件时传 `mode: "interactive"`,已知 CSS 区域时传 `rootSelector`,已有 ref 时传 `rootRef`(可用 `ancestorDepth` 向上补充上下文),只查找特定文案或角色时传 `query`。视口内缺少 AX 控件语义时,`interactive` 会自动回退到 viewport,并返回 `fallbackMode: "viewport"`。`maxNodes` / `maxChars` 截断会同时保留首尾内容。
重复文本点击可用 `page_click.scope` 限定 CSS / ref 子树;ref 指向滚动容器内的子节点时,可用 `page_scroll.containerPolicy: "nearest"` 自动解析最近可滚动祖先。`page_click.clickMode` 默认 `auto`:可见桌面页派发可信 mouse,移动模拟页派发可信 touch;隐藏页使用 DOM fallback,返回 `dispatchMode: "dom"` 和 `fallbackReason: "page-hidden"`,且不切换标签或还原窗口。可用 `dom` / `mouse` / `touch` 覆盖自动策略;可信输入返回实际 `pointerType`。点击结果的 `resolvedTarget` 和滚动结果的前后位置、边界字段可用于诊断实际派发目标与滚动效果。
重复文本点击可用 `page_click.scope` 限定 CSS / ref 子树;ref 指向滚动容器内的子节点时,可用 `page_scroll.containerPolicy: "nearest"` 自动解析最近可滚动祖先。点击结果的 `resolvedTarget` 和滚动结果的前后位置、边界字段可用于诊断实际目标与效果。

@@ -115,0 +118,0 @@ `browser_list` 统一列出普通浏览器和托管浏览器。`type` 区分 `regular` / `managed`,`status` 区分 `connected` / `running_disconnected` / `stopped`;只有 `connected` 的浏览器可作为操作目标。