@mearl/client 2.18.0 → 2.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/check.js CHANGED
@@ -1,4 +1,45 @@
1
1
  import { UnifiedClient, normalizeBrowserSelector, } from './unified.js';
2
+ const UPDATE_SOURCES = {
3
+ client: 'https://registry.npmjs.org/@mearl%2Fclient/latest',
4
+ 'native-host': 'https://registry.npmjs.org/@mearl%2Fnative-host/latest',
5
+ 'cloud-connector': 'https://registry.npmjs.org/@mearl%2Fcloud-connector/latest',
6
+ extension: 'https://mearl.alibaba-inc.com/config.json',
7
+ };
8
+ export function parseReleaseVersion(value) {
9
+ if (typeof value !== 'string')
10
+ return null;
11
+ const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-beta\.(0|[1-9]\d*))?$/.exec(value);
12
+ if (!match)
13
+ return null;
14
+ const parts = [Number(match[1]), Number(match[2]), Number(match[3]), Number(match[4] ?? 0)];
15
+ if (!parts.every(Number.isSafeInteger))
16
+ return null;
17
+ return [...parts.slice(0, 3), match[4] === undefined ? 1 : 0, parts[3]];
18
+ }
19
+ export function isNewerRelease(latest, installed) {
20
+ const latestParts = parseReleaseVersion(latest);
21
+ const installedParts = parseReleaseVersion(installed);
22
+ if (!latestParts || !installedParts)
23
+ return false;
24
+ for (let index = 0; index < latestParts.length; index++) {
25
+ if (latestParts[index] !== installedParts[index]) {
26
+ return latestParts[index] > installedParts[index];
27
+ }
28
+ }
29
+ return false;
30
+ }
31
+ async function fetchLatestVersion(component) {
32
+ const response = await fetch(UPDATE_SOURCES[component], {
33
+ signal: AbortSignal.timeout(1500),
34
+ });
35
+ if (!response.ok)
36
+ throw new Error(`HTTP ${response.status}`);
37
+ const payload = (await response.json());
38
+ const version = component === 'extension' ? payload.latestVersion : payload.version;
39
+ if (!parseReleaseVersion(version))
40
+ throw new Error('Invalid release version');
41
+ return version;
42
+ }
2
43
  function formatError(error) {
3
44
  return error instanceof Error ? error.message : String(error);
4
45
  }
@@ -98,6 +139,9 @@ export async function runUnifiedCheck(options, dependencies = {}) {
98
139
  connectTimeoutSec: timeoutSec,
99
140
  requestTimeoutSec: timeoutSec,
100
141
  });
142
+ const latestVersions = options.checkUpdates
143
+ ? Promise.allSettled(Object.keys(UPDATE_SOURCES).map(component => (dependencies.fetchLatestVersion ?? fetchLatestVersion)(component)))
144
+ : null;
101
145
  const environment = [
102
146
  { status: 'ok', label: `Unified client v${options.clientVersion}` },
103
147
  ];
@@ -301,6 +345,67 @@ export async function runUnifiedCheck(options, dependencies = {}) {
301
345
  });
302
346
  }
303
347
  }));
348
+ let updates;
349
+ if (latestVersions) {
350
+ const latestResults = await latestVersions;
351
+ const components = Object.keys(UPDATE_SOURCES);
352
+ const installed = new Map();
353
+ const addInstalled = (component, version) => {
354
+ if (!version)
355
+ return;
356
+ const versions = installed.get(component) ?? new Set();
357
+ versions.add(version);
358
+ installed.set(component, versions);
359
+ };
360
+ addInstalled('client', options.clientVersion);
361
+ for (const check of browserChecks) {
362
+ addInstalled('native-host', check.versions?.nativeHost);
363
+ addInstalled('extension', check.versions?.extension);
364
+ }
365
+ for (const connector of connectors)
366
+ addInstalled('cloud-connector', connector.version);
367
+ updates = components.flatMap((component, index) => {
368
+ const result = latestResults[index];
369
+ return [...(installed.get(component) ?? [])].map(version => ({
370
+ component,
371
+ installed: version,
372
+ ...(result.status === 'fulfilled' ? { latest: result.value } : {}),
373
+ status: result.status === 'rejected' ||
374
+ !parseReleaseVersion(version) ||
375
+ !parseReleaseVersion(result.value)
376
+ ? 'unavailable'
377
+ : isNewerRelease(result.value, version)
378
+ ? 'available'
379
+ : 'current',
380
+ }));
381
+ });
382
+ const available = updates.filter(update => update.status === 'available');
383
+ const unavailable = updates.filter(update => update.status === 'unavailable');
384
+ const packageUpdateAvailable = available.some(update => update.component === 'client' || update.component === 'native-host');
385
+ const extensionUpdateAvailable = available.some(update => update.component === 'extension');
386
+ const connectorUpdateAvailable = available.some(update => update.component === 'cloud-connector');
387
+ environment.push({
388
+ status: available.length || unavailable.length ? 'warning' : 'ok',
389
+ label: available.length
390
+ ? `${available.length} Mearl update${available.length === 1 ? '' : 's'} available`
391
+ : unavailable.length
392
+ ? 'Mearl update check incomplete'
393
+ : 'Mearl packages are current',
394
+ details: [
395
+ ...available.map(update => `${update.component}: ${update.installed} → ${update.latest}`),
396
+ ...(packageUpdateAvailable
397
+ ? ['Update on the affected machine: npx @mearl/setup update']
398
+ : []),
399
+ ...(extensionUpdateAvailable
400
+ ? ['Update a setup-managed extension: npx @mearl/setup extension']
401
+ : []),
402
+ ...(connectorUpdateAvailable
403
+ ? ['Update cloud-connector with its package manager on the connector machine.']
404
+ : []),
405
+ ...unavailable.map(update => `${update.component}: latest version unavailable`),
406
+ ],
407
+ });
408
+ }
304
409
  const hasError = environment.some(item => item.status === 'error') ||
305
410
  browserChecks.some(check => check.item.status === 'error');
306
411
  const listedBrowsers = browserList?.browsers ?? [];
@@ -336,6 +441,7 @@ export async function runUnifiedCheck(options, dependencies = {}) {
336
441
  connectors,
337
442
  environment,
338
443
  ok: !hasError,
444
+ ...(updates ? { updates } : {}),
339
445
  };
340
446
  writeJson(JSON.stringify(result, null, 2));
341
447
  return result;
package/dist/cli.d.ts CHANGED
@@ -10,6 +10,7 @@ export interface ParsedArgs {
10
10
  forceCdp: boolean;
11
11
  json: boolean;
12
12
  local: boolean;
13
+ updates: boolean;
13
14
  outputPath?: string;
14
15
  payload: Record<string, any>;
15
16
  serverUrl?: string;
package/dist/cli.js CHANGED
@@ -21,7 +21,7 @@ const CATEGORIES = [
21
21
  title: '网络请求',
22
22
  names: ['send_request', 'send_mtop_request', 'request_domain_permission'],
23
23
  },
24
- { title: '标签页', names: ['tab_open', 'tab_list', 'tab_close'] },
24
+ { title: '标签页', names: ['tab_open', 'tab_list', 'tab_group', 'tab_close'] },
25
25
  {
26
26
  title: '页面感知',
27
27
  names: ['page_snapshot', 'page_inspect', 'page_screenshot', 'page_frames'],
@@ -77,6 +77,7 @@ const PLUGIN_HOST_OPTIONS = new Set([
77
77
  '--help',
78
78
  '--json',
79
79
  '--local',
80
+ '--updates',
80
81
  '--output',
81
82
  '--payload',
82
83
  '--payload-file',
@@ -266,6 +267,7 @@ function printCheckHelp(commandName) {
266
267
  ' --server <url> 覆盖 cloud-server WebSocket URL',
267
268
  ' --timeout <seconds> 单项检查超时时间,默认 5 秒',
268
269
  ' --json 输出机器可读 JSON',
270
+ ' --updates 查询已安装组件的发布版本',
269
271
  ' -h, --help 查看帮助',
270
272
  ].join('\n'));
271
273
  }
@@ -409,6 +411,7 @@ export function parseClientArgs(args, plugin = false) {
409
411
  .option('--browser <id-or-name>')
410
412
  .option('--connector <id-or-name>')
411
413
  .option('--local')
414
+ .option('--updates')
412
415
  .option('--cdp')
413
416
  .option('--server <url>');
414
417
  if (plugin)
@@ -442,6 +445,8 @@ export function parseClientArgs(args, plugin = false) {
442
445
  const json = options.json === true;
443
446
  if (json && action !== 'check')
444
447
  throw new Error('--json 仅适用于 check');
448
+ if (options.updates && action !== 'check')
449
+ throw new Error('--updates 仅适用于 check');
445
450
  const parsedPayload = parsePayload(options.payload, options.payloadFile);
446
451
  const { payload, outputPath } = normalizeScreenshotOutput(action, parsedPayload, options.output);
447
452
  const fallbackTimeout = resolveActionTimeoutSec(action, payload, 60);
@@ -459,6 +464,7 @@ export function parseClientArgs(args, plugin = false) {
459
464
  forceCdp,
460
465
  json,
461
466
  local,
467
+ updates: options.updates === true,
462
468
  ...(outputPath ? { outputPath } : {}),
463
469
  ...(options.browser ? { browser: options.browser } : {}),
464
470
  ...(connector ? { connector } : {}),
@@ -645,6 +651,7 @@ export async function runMearlCli(args, options = {}) {
645
651
  ...(parsed.connector ? { connector: parsed.connector } : {}),
646
652
  ...(parsed.local ? { local: true } : {}),
647
653
  ...(parsed.forceCdp ? { transport: 'cdp' } : {}),
654
+ ...(parsed.updates ? { checkUpdates: true } : {}),
648
655
  };
649
656
  if (parsed.json) {
650
657
  const result = await runUnifiedCheck({ ...checkOptions, json: true });
@@ -24,6 +24,7 @@ export declare const COMMANDS: readonly [{
24
24
  readonly params: [{
25
25
  readonly name: "tabId";
26
26
  readonly type: "number";
27
+ readonly required: true;
27
28
  readonly description: "目标标签页 ID,从 tab_open 获取";
28
29
  }];
29
30
  readonly examples: ["tab_checkpoint --payload '{\"tabId\":12345}'"];
@@ -33,6 +34,7 @@ export declare const COMMANDS: readonly [{
33
34
  readonly params: [{
34
35
  readonly name: "tabId";
35
36
  readonly type: "number";
37
+ readonly required: true;
36
38
  readonly description: "目标标签页 ID,从 tab_list 获取";
37
39
  }, {
38
40
  readonly name: "count";
@@ -75,13 +77,14 @@ export declare const COMMANDS: readonly [{
75
77
  readonly type: "boolean";
76
78
  readonly description: "只返回数据结构,不返回叶子值";
77
79
  }];
78
- readonly examples: ["get_requests --payload '{\"count\":5}'", "get_requests --payload '{\"source\":\"mtop\",\"filter\":\"cart\"}'"];
80
+ readonly examples: ["get_requests --payload '{\"tabId\":12345,\"count\":5}'", "get_requests --payload '{\"tabId\":12345,\"source\":\"mtop\",\"filter\":\"cart\"}'"];
79
81
  }, {
80
82
  readonly name: "get_logs";
81
83
  readonly description: "获取浏览器控制台日志";
82
84
  readonly params: [{
83
85
  readonly name: "tabId";
84
86
  readonly type: "number";
87
+ readonly required: true;
85
88
  readonly description: "目标标签页 ID,从 tab_list 获取";
86
89
  }, {
87
90
  readonly name: "limit";
@@ -104,13 +107,14 @@ export declare const COMMANDS: readonly [{
104
107
  readonly type: "string";
105
108
  readonly description: "只返回指定 tab checkpoint 或同类查询 cursor 之后的日志";
106
109
  }];
107
- readonly examples: ["get_logs --payload '{\"limit\":10,\"level\":\"error\"}'"];
110
+ readonly examples: ["get_logs --payload '{\"tabId\":12345,\"limit\":10,\"level\":\"error\"}'"];
108
111
  }, {
109
112
  readonly name: "get_events";
110
113
  readonly description: "获取 RUM/aplus/ARMS 埋点事件";
111
114
  readonly params: [{
112
115
  readonly name: "tabId";
113
116
  readonly type: "number";
117
+ readonly required: true;
114
118
  readonly description: "目标标签页 ID,从 tab_list 获取";
115
119
  }, {
116
120
  readonly name: "source";
@@ -141,13 +145,14 @@ export declare const COMMANDS: readonly [{
141
145
  readonly type: "boolean";
142
146
  readonly description: "返回完整 URL、_raw 和 _context;默认 false";
143
147
  }];
144
- readonly examples: ["get_events --payload '{\"source\":\"aplus\",\"event_type\":\"CLK\",\"limit\":10}'", "get_events --payload '{\"filter\":\"order\",\"includeRaw\":true,\"limit\":1}'", "get_events --payload '{\"tabId\":12345,\"source\":\"all\",\"limit\":20}'"];
148
+ readonly examples: ["get_events --payload '{\"tabId\":12345,\"source\":\"aplus\",\"event_type\":\"CLK\",\"limit\":10}'", "get_events --payload '{\"tabId\":12345,\"filter\":\"order\",\"includeRaw\":true,\"limit\":1}'", "get_events --payload '{\"tabId\":12345,\"source\":\"all\",\"limit\":20}'"];
145
149
  }, {
146
150
  readonly name: "set_mock";
147
151
  readonly description: "设置 API mock 数据";
148
152
  readonly params: [{
149
153
  readonly name: "tabId";
150
154
  readonly type: "number";
155
+ readonly required: true;
151
156
  readonly description: "目标标签页 ID,从 tab_list 获取";
152
157
  }, {
153
158
  readonly name: "apiName";
@@ -175,20 +180,21 @@ export declare const COMMANDS: readonly [{
175
180
  readonly type: "object";
176
181
  readonly description: "请求 query/body 字段子集匹配;$index 从 0 开始指定调用顺序";
177
182
  }];
178
- readonly examples: ["set_mock --payload '{\"apiName\":\"mtop.cart.query\",\"mockData\":{\"ret\":[\"SUCCESS::调用成功\"]}}'", "set_mock --payload-file ./mock-data.json"];
183
+ readonly examples: ["set_mock --payload '{\"tabId\":12345,\"apiName\":\"mtop.cart.query\",\"mockData\":{\"ret\":[\"SUCCESS::调用成功\"]}}'", "set_mock --payload-file ./mock-data.json"];
179
184
  }, {
180
185
  readonly name: "get_mocks";
181
186
  readonly description: "查看当前生效的 mock 列表";
182
187
  readonly params: [{
183
188
  readonly name: "tabId";
184
189
  readonly type: "number";
190
+ readonly required: true;
185
191
  readonly description: "目标标签页 ID,从 tab_list 获取";
186
192
  }, {
187
193
  readonly name: "apiName";
188
194
  readonly type: "string";
189
195
  readonly description: "按 API 名称或正则表达式过滤";
190
196
  }];
191
- readonly examples: ["get_mocks"];
197
+ readonly examples: ["get_mocks --payload '{\"tabId\":12345}'"];
192
198
  }, {
193
199
  readonly name: "set_rule";
194
200
  readonly description: "添加 Chrome declarativeNetRequest 规则(重定向/修改请求头/拦截请求)";
@@ -452,6 +458,26 @@ export declare const COMMANDS: readonly [{
452
458
  readonly description: "列出目标浏览器所有窗口中的标签页";
453
459
  readonly aliases: readonly ["tabs", "list_tabs", "show_tabs"];
454
460
  readonly examples: ["tab_list"];
461
+ }, {
462
+ readonly name: "tab_group";
463
+ readonly description: "把同一窗口中的标签页加入分组,或通过 ungroup 取消分组并保留标签页;仅扩展模式支持";
464
+ readonly params: [{
465
+ readonly name: "tabIds";
466
+ readonly type: "number[]";
467
+ readonly required: true;
468
+ readonly description: "标签页 ID,分组最多 100 个,取消分组最多 1000 个";
469
+ }, {
470
+ readonly name: "title";
471
+ readonly type: "string";
472
+ readonly required: false;
473
+ readonly description: "分组标题;取消分组时省略";
474
+ }, {
475
+ readonly name: "ungroup";
476
+ readonly type: "boolean";
477
+ readonly required: false;
478
+ readonly description: "为 true 时取消分组并保留标签页";
479
+ }];
480
+ readonly examples: ["tab_group --payload '{\"tabIds\":[123,124],\"title\":\"项目文档\"}'", "tab_group --payload '{\"tabIds\":[123,124],\"ungroup\":true}'"];
455
481
  }, {
456
482
  readonly name: "page_click";
457
483
  readonly description: "点击页面元素,支持 CSS/@ref、可见文本或视口坐标定位";
package/dist/commands.js CHANGED
@@ -17,14 +17,26 @@ export const COMMANDS = [
17
17
  {
18
18
  name: 'tab_checkpoint',
19
19
  description: '为目标标签页建立日志、请求和埋点增量边界',
20
- params: [{ name: 'tabId', type: 'number', description: '目标标签页 ID,从 tab_open 获取' }],
20
+ params: [
21
+ {
22
+ name: 'tabId',
23
+ type: 'number',
24
+ required: true,
25
+ description: '目标标签页 ID,从 tab_open 获取',
26
+ },
27
+ ],
21
28
  examples: [`tab_checkpoint --payload '{"tabId":12345}'`],
22
29
  },
23
30
  {
24
31
  name: 'get_requests',
25
32
  description: '获取最近的网络请求(mtop 或普通 xhr/fetch)',
26
33
  params: [
27
- { name: 'tabId', type: 'number', description: '目标标签页 ID,从 tab_list 获取' },
34
+ {
35
+ name: 'tabId',
36
+ type: 'number',
37
+ required: true,
38
+ description: '目标标签页 ID,从 tab_list 获取',
39
+ },
28
40
  { name: 'count', type: 'number', description: '返回条数,默认 10' },
29
41
  {
30
42
  name: 'source',
@@ -45,15 +57,20 @@ export const COMMANDS = [
45
57
  { name: 'keysOnly', type: 'boolean', description: '只返回数据结构,不返回叶子值' },
46
58
  ],
47
59
  examples: [
48
- `get_requests --payload '{"count":5}'`,
49
- `get_requests --payload '{"source":"mtop","filter":"cart"}'`,
60
+ `get_requests --payload '{"tabId":12345,"count":5}'`,
61
+ `get_requests --payload '{"tabId":12345,"source":"mtop","filter":"cart"}'`,
50
62
  ],
51
63
  },
52
64
  {
53
65
  name: 'get_logs',
54
66
  description: '获取浏览器控制台日志',
55
67
  params: [
56
- { name: 'tabId', type: 'number', description: '目标标签页 ID,从 tab_list 获取' },
68
+ {
69
+ name: 'tabId',
70
+ type: 'number',
71
+ required: true,
72
+ description: '目标标签页 ID,从 tab_list 获取',
73
+ },
57
74
  { name: 'limit', type: 'number', description: '返回最新 N 条,默认 100' },
58
75
  { name: 'level', type: 'string', description: '按日志级别过滤:log/info/warn/error/all' },
59
76
  { name: 'filter', type: 'string', description: '按内容过滤,支持正则' },
@@ -64,13 +81,18 @@ export const COMMANDS = [
64
81
  description: '只返回指定 tab checkpoint 或同类查询 cursor 之后的日志',
65
82
  },
66
83
  ],
67
- examples: [`get_logs --payload '{"limit":10,"level":"error"}'`],
84
+ examples: [`get_logs --payload '{"tabId":12345,"limit":10,"level":"error"}'`],
68
85
  },
69
86
  {
70
87
  name: 'get_events',
71
88
  description: '获取 RUM/aplus/ARMS 埋点事件',
72
89
  params: [
73
- { name: 'tabId', type: 'number', description: '目标标签页 ID,从 tab_list 获取' },
90
+ {
91
+ name: 'tabId',
92
+ type: 'number',
93
+ required: true,
94
+ description: '目标标签页 ID,从 tab_list 获取',
95
+ },
74
96
  {
75
97
  name: 'source',
76
98
  type: '"rum" | "aplus" | "arms" | "all"',
@@ -88,8 +110,8 @@ export const COMMANDS = [
88
110
  },
89
111
  ],
90
112
  examples: [
91
- `get_events --payload '{"source":"aplus","event_type":"CLK","limit":10}'`,
92
- `get_events --payload '{"filter":"order","includeRaw":true,"limit":1}'`,
113
+ `get_events --payload '{"tabId":12345,"source":"aplus","event_type":"CLK","limit":10}'`,
114
+ `get_events --payload '{"tabId":12345,"filter":"order","includeRaw":true,"limit":1}'`,
93
115
  `get_events --payload '{"tabId":12345,"source":"all","limit":20}'`,
94
116
  ],
95
117
  },
@@ -98,7 +120,12 @@ export const COMMANDS = [
98
120
  name: 'set_mock',
99
121
  description: '设置 API mock 数据',
100
122
  params: [
101
- { name: 'tabId', type: 'number', description: '目标标签页 ID,从 tab_list 获取' },
123
+ {
124
+ name: 'tabId',
125
+ type: 'number',
126
+ required: true,
127
+ description: '目标标签页 ID,从 tab_list 获取',
128
+ },
102
129
  { name: 'apiName', type: 'string', required: true, description: 'API 名称或正则表达式' },
103
130
  { name: 'mockData', type: 'object', description: '完整 mock 响应数据(与 fields 二选一)' },
104
131
  {
@@ -123,7 +150,7 @@ export const COMMANDS = [
123
150
  },
124
151
  ],
125
152
  examples: [
126
- `set_mock --payload '{"apiName":"mtop.cart.query","mockData":{"ret":["SUCCESS::调用成功"]}}'`,
153
+ `set_mock --payload '{"tabId":12345,"apiName":"mtop.cart.query","mockData":{"ret":["SUCCESS::调用成功"]}}'`,
127
154
  `set_mock --payload-file ./mock-data.json`,
128
155
  ],
129
156
  },
@@ -131,10 +158,15 @@ export const COMMANDS = [
131
158
  name: 'get_mocks',
132
159
  description: '查看当前生效的 mock 列表',
133
160
  params: [
134
- { name: 'tabId', type: 'number', description: '目标标签页 ID,从 tab_list 获取' },
161
+ {
162
+ name: 'tabId',
163
+ type: 'number',
164
+ required: true,
165
+ description: '目标标签页 ID,从 tab_list 获取',
166
+ },
135
167
  { name: 'apiName', type: 'string', description: '按 API 名称或正则表达式过滤' },
136
168
  ],
137
- examples: ['get_mocks'],
169
+ examples: [`get_mocks --payload '{"tabId":12345}'`],
138
170
  },
139
171
  {
140
172
  name: 'set_rule',
@@ -389,6 +421,29 @@ export const COMMANDS = [
389
421
  aliases: ['tabs', 'list_tabs', 'show_tabs'],
390
422
  examples: ['tab_list'],
391
423
  },
424
+ {
425
+ name: 'tab_group',
426
+ description: '把同一窗口中的标签页加入分组,或通过 ungroup 取消分组并保留标签页;仅扩展模式支持',
427
+ params: [
428
+ {
429
+ name: 'tabIds',
430
+ type: 'number[]',
431
+ required: true,
432
+ description: '标签页 ID,分组最多 100 个,取消分组最多 1000 个',
433
+ },
434
+ { name: 'title', type: 'string', required: false, description: '分组标题;取消分组时省略' },
435
+ {
436
+ name: 'ungroup',
437
+ type: 'boolean',
438
+ required: false,
439
+ description: '为 true 时取消分组并保留标签页',
440
+ },
441
+ ],
442
+ examples: [
443
+ `tab_group --payload '{"tabIds":[123,124],"title":"项目文档"}'`,
444
+ `tab_group --payload '{"tabIds":[123,124],"ungroup":true}'`,
445
+ ],
446
+ },
392
447
  // ── 页面操作 ──────────────────────────────────────────────────────────────
393
448
  {
394
449
  name: 'page_click',
@@ -1,5 +1,5 @@
1
1
  export interface GetRequestsParams {
2
- tabId?: number;
2
+ tabId: number;
3
3
  count?: number;
4
4
  filter?: string;
5
5
  /** Relative time window in seconds. */
@@ -15,7 +15,7 @@ export interface GetRequestsParams {
15
15
  source?: 'mtop' | 'requests' | 'all';
16
16
  }
17
17
  export interface GetLogsParams {
18
- tabId?: number;
18
+ tabId: number;
19
19
  level?: 'log' | 'warn' | 'error' | 'info' | 'all';
20
20
  /** Relative time window in seconds. */
21
21
  since?: number;
@@ -25,7 +25,7 @@ export interface GetLogsParams {
25
25
  filter?: string;
26
26
  }
27
27
  export interface CaptureCheckpointParams {
28
- tabId?: number;
28
+ tabId: number;
29
29
  }
30
30
  export interface CaptureCheckpointResult {
31
31
  tabId: number;
@@ -34,7 +34,7 @@ export interface CaptureCheckpointResult {
34
34
  captureStatus: 'armed' | 'active';
35
35
  }
36
36
  export interface GetEventsParams {
37
- tabId?: number;
37
+ tabId: number;
38
38
  source?: 'rum' | 'aplus' | 'arms' | 'all';
39
39
  event_type?: string;
40
40
  filter?: string;
@@ -45,7 +45,7 @@ export interface GetEventsParams {
45
45
  includeRaw?: boolean;
46
46
  }
47
47
  export interface SetMockParams {
48
- tabId?: number;
48
+ tabId: number;
49
49
  apiName: string;
50
50
  mockData?: unknown;
51
51
  fields?: Array<{
@@ -57,7 +57,7 @@ export interface SetMockParams {
57
57
  conditions?: Record<string, unknown>;
58
58
  }
59
59
  export interface GetMocksParams {
60
- tabId?: number;
60
+ tabId: number;
61
61
  apiName?: string;
62
62
  }
63
63
  export declare const SEND_REQUEST_MAX_FILES = 20;
@@ -348,7 +348,7 @@ export interface PageScreenshotParams {
348
348
  format?: 'png' | 'jpeg';
349
349
  quality?: number;
350
350
  }
351
- export declare const BROWSER_DEVICE_PRESET_NAMES: readonly ["iphone-17-pro", "iphone-17e", "pixel-11", "ipad-mini-a17-pro", "galaxy-s26", "pura-x-max", "iphone-15-pro", "iphone-se", "pixel-8", "ipad-mini", "galaxy-s23"];
351
+ export declare const BROWSER_DEVICE_PRESET_NAMES: readonly ["iphone-duo-folded", "iphone-duo-unfolded", "iphone-17-pro", "iphone-17e", "pixel-11", "ipad-mini-a17-pro", "galaxy-s26", "pura-x-max", "iphone-15-pro", "iphone-se", "pixel-8", "ipad-mini", "galaxy-s23"];
352
352
  export type BrowserDevicePresetName = (typeof BROWSER_DEVICE_PRESET_NAMES)[number];
353
353
  export interface BrowserEmulationOptions {
354
354
  preset?: BrowserDevicePresetName;
@@ -365,7 +365,7 @@ export interface BrowserEmulationOptions {
365
365
  /** App 容器横屏时将根页面与业务页面分置左右 WebView;仅与 deviceShell、App 档案同时生效。 */
366
366
  splitScreen?: boolean;
367
367
  }
368
- export type AppBridgeConfigBehavior = 'navigate' | 'closeAll' | 'closeBackData' | 'postMessage' | 'notifySubscribe' | 'notifyUnsubscribe' | 'notifyPost' | 'sharedFileRead' | 'sharedFileWrite' | 'sharedDataGet' | 'sharedDataSet' | 'legacyBroadcastCreate' | 'legacyBroadcastPost' | 'legacyBroadcastClose' | 'memoryGet' | 'memorySet' | 'memoryDelete' | 'availabilityMap' | 'userInfo' | 'networkInfo' | 'geolocation' | 'permission' | 'imagePicker' | 'prefetch' | 'fptTrack' | 'mtopStream' | 'previewFile' | 'toast' | 'unsupportedNativeInput' | 'payment' | 'userTrack' | 'calendar' | 'networkQualityMonitor' | 'beaconRequest' | 'voiceRecognition';
368
+ export type AppBridgeConfigBehavior = 'navigate' | 'closeAll' | 'closeBackData' | 'postMessage' | 'notifySubscribe' | 'notifyUnsubscribe' | 'notifyPost' | 'sharedFileRead' | 'sharedFileWrite' | 'sharedDataGet' | 'sharedDataSet' | 'legacyBroadcastCreate' | 'legacyBroadcastPost' | 'legacyBroadcastClose' | 'memoryGet' | 'memorySet' | 'memoryDelete' | 'availabilityMap' | 'userInfo' | 'authCode' | 'networkInfo' | 'geolocation' | 'permission' | 'imagePicker' | 'prefetch' | 'fptTrack' | 'mtopStream' | 'previewFile' | 'toast' | 'unsupportedNativeInput' | 'payment' | 'userTrack' | 'calendar' | 'networkQualityMonitor' | 'beaconRequest' | 'voiceRecognition';
369
369
  /** 单个桥方法的声明式覆盖;行为、固定回包、成功空实现三选一。 */
370
370
  export interface AppBridgeMethodConfig {
371
371
  behavior?: AppBridgeConfigBehavior;
@@ -403,6 +403,15 @@ export interface TabOpenParams {
403
403
  /** 与 appProfile 配合使用;按方法覆盖档案内置桥配置 */
404
404
  bridgeConfig?: AppBridgeConfig;
405
405
  }
406
+ export type TabGroupParams = {
407
+ tabIds: number[];
408
+ title: string;
409
+ ungroup?: false;
410
+ } | {
411
+ tabIds: number[];
412
+ ungroup: true;
413
+ title?: never;
414
+ };
406
415
  /**
407
416
  * 客户端容器模拟可用档案名。
408
417
  *
@@ -421,6 +430,8 @@ export interface SetAppProfileParams {
421
430
  profile?: AppProfileName;
422
431
  os?: 'ios' | 'android';
423
432
  appVersion?: string;
433
+ /** 支付宝容器语言;其他容器忽略。 */
434
+ language?: 'zh-CN' | 'zh-TW' | 'zh-HK' | 'en-US';
424
435
  deviceModel?: string;
425
436
  /** 按方法覆盖档案内置桥配置,仅支持受控行为、固定 JSON 回包和成功空实现 */
426
437
  bridgeConfig?: AppBridgeConfig;
@@ -725,6 +736,10 @@ export interface BrowserActionMap {
725
736
  request: undefined;
726
737
  response: unknown;
727
738
  };
739
+ tab_group: {
740
+ request: TabGroupParams;
741
+ response: unknown;
742
+ };
728
743
  page_click: {
729
744
  request: PageClickParams;
730
745
  response: unknown;
@@ -811,7 +826,7 @@ export type BrowserActionRequest<A extends BrowserAction> = BrowserActionMap[A][
811
826
  export type BrowserActionResponse<A extends BrowserAction> = BrowserActionMap[A]['response'];
812
827
  export type BrowserCommandAction = Exclude<BrowserAction, 'browser_select_browser'>;
813
828
  export type ExtensionBrowserAction = Exclude<BrowserCommandAction, 'browser_list' | 'browser_launch' | 'browser_close'>;
814
- export declare const TAB_ID_REQUIRED_ACTIONS: readonly ["page_screenshot", "tab_close", "page_click", "page_drag", "page_type", "page_scroll", "page_hover", "page_eval", "page_inspect", "page_snapshot", "page_press", "page_wait", "page_navigate", "page_upload", "page_frames", "set_device_emulation", "set_app_profile", "set_timezone", "get_cookie", "set_cookie", "run_actions"];
829
+ export declare const TAB_ID_REQUIRED_ACTIONS: readonly ["tab_checkpoint", "capture_checkpoint", "get_requests", "get_logs", "get_events", "set_mock", "get_mocks", "page_screenshot", "tab_close", "page_click", "page_drag", "page_type", "page_scroll", "page_hover", "page_eval", "page_inspect", "page_snapshot", "page_press", "page_wait", "page_navigate", "page_upload", "page_frames", "set_device_emulation", "set_app_profile", "set_timezone", "get_cookie", "set_cookie", "run_actions"];
815
830
  export type TabIdRequiredAction = (typeof TAB_ID_REQUIRED_ACTIONS)[number];
816
831
  export declare function actionRequiresTabId(action: unknown): action is TabIdRequiredAction;
817
832
  export declare function validateRequiredActionTabId(action: string, data: unknown): void;
@@ -838,6 +853,7 @@ export declare const BROWSER_ACTIONS: {
838
853
  readonly tab_open: true;
839
854
  readonly tab_close: true;
840
855
  readonly tab_list: true;
856
+ readonly tab_group: true;
841
857
  readonly page_click: true;
842
858
  readonly page_drag: true;
843
859
  readonly page_type: true;
@@ -879,6 +895,7 @@ export declare const BROWSER_COMMAND_ACTIONS: {
879
895
  readonly tab_open: true;
880
896
  readonly tab_close: true;
881
897
  readonly tab_list: true;
898
+ readonly tab_group: true;
882
899
  readonly page_click: true;
883
900
  readonly page_drag: true;
884
901
  readonly page_type: true;
@@ -917,6 +934,7 @@ export declare const EXTENSION_BROWSER_ACTIONS: {
917
934
  readonly tab_open: true;
918
935
  readonly tab_close: true;
919
936
  readonly tab_list: true;
937
+ readonly tab_group: true;
920
938
  readonly page_click: true;
921
939
  readonly page_drag: true;
922
940
  readonly page_type: true;
@@ -7,6 +7,8 @@ export const SEND_REQUEST_MAX_TOTAL_FILE_BYTES = 40 * 1024 * 1024;
7
7
  // the 10 MiB cloud message limit.
8
8
  export const SEND_REQUEST_MAX_TOTAL_BASE64_FILE_BYTES = 7 * 1024 * 1024;
9
9
  export const BROWSER_DEVICE_PRESET_NAMES = [
10
+ 'iphone-duo-folded',
11
+ 'iphone-duo-unfolded',
10
12
  'iphone-17-pro',
11
13
  'iphone-17e',
12
14
  'pixel-11',
@@ -49,6 +51,13 @@ export const APP_PROFILE_NAMES = [
49
51
  'easybike',
50
52
  ];
51
53
  export const TAB_ID_REQUIRED_ACTIONS = [
54
+ 'tab_checkpoint',
55
+ 'capture_checkpoint',
56
+ 'get_requests',
57
+ 'get_logs',
58
+ 'get_events',
59
+ 'set_mock',
60
+ 'get_mocks',
52
61
  'page_screenshot',
53
62
  'tab_close',
54
63
  'page_click',
@@ -116,6 +125,7 @@ export const BROWSER_ACTIONS = {
116
125
  tab_open: true,
117
126
  tab_close: true,
118
127
  tab_list: true,
128
+ tab_group: true,
119
129
  page_click: true,
120
130
  page_drag: true,
121
131
  page_type: true,
@@ -157,6 +167,7 @@ export const BROWSER_COMMAND_ACTIONS = {
157
167
  tab_open: true,
158
168
  tab_close: true,
159
169
  tab_list: true,
170
+ tab_group: true,
160
171
  page_click: true,
161
172
  page_drag: true,
162
173
  page_type: true,
@@ -195,6 +206,7 @@ export const EXTENSION_BROWSER_ACTIONS = {
195
206
  tab_open: true,
196
207
  tab_close: true,
197
208
  tab_list: true,
209
+ tab_group: true,
198
210
  page_click: true,
199
211
  page_drag: true,
200
212
  page_type: true,