@mearl/client 2.8.0 → 2.8.2

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 CHANGED
@@ -55,6 +55,7 @@ mearl set_mock --payload-file ./mock-data.json
55
55
 
56
56
  # 环境自检
57
57
  mearl check
58
+ mearl check --local --json
58
59
 
59
60
  # 更新本地环境
60
61
  npx @mearl/setup update --local
package/dist/check.d.ts CHANGED
@@ -1,4 +1,50 @@
1
+ import type { CloudConnectorInfo } from '@mearl/cloud-types';
1
2
  import { type UnifiedClientOptions, type UnifiedInvokeOptions } from './unified.js';
3
+ export interface UnifiedCheckVersions {
4
+ nativeHost?: string;
5
+ extension?: string | null;
6
+ browser?: string | null;
7
+ }
8
+ interface BrowserInfo {
9
+ browserId: string;
10
+ extensionVersion?: string;
11
+ name: string;
12
+ nativeHostVersion?: string;
13
+ originalBrowserId?: string;
14
+ originalName?: string;
15
+ status: string;
16
+ type?: string;
17
+ provider?: 'local' | 'agentbay';
18
+ transport?: 'extension' | 'cdp';
19
+ }
20
+ interface CheckItem {
21
+ status: 'ok' | 'warning' | 'error';
22
+ label: string;
23
+ details?: string[];
24
+ }
25
+ export interface ExtensionInstallationInfo {
26
+ extensionId: string;
27
+ setupManagedPath: string | null;
28
+ }
29
+ export interface UnifiedCheckBrowserResult extends BrowserInfo {
30
+ agentbayAccessUrl?: string;
31
+ check?: {
32
+ errors?: string[];
33
+ label: string;
34
+ status: CheckItem['status'];
35
+ warnings?: string[];
36
+ };
37
+ extensionInstallation?: ExtensionInstallationInfo;
38
+ selected: boolean;
39
+ versions?: UnifiedCheckVersions;
40
+ }
41
+ export interface UnifiedCheckResult {
42
+ browsers: UnifiedCheckBrowserResult[];
43
+ clientVersion: string;
44
+ connectors: CloudConnectorInfo[];
45
+ environment: CheckItem[];
46
+ ok: boolean;
47
+ }
2
48
  interface CheckClient {
3
49
  invoke(action: string, data?: Record<string, any>, options?: UnifiedInvokeOptions): Promise<any>;
4
50
  disconnect(): void;
@@ -12,11 +58,16 @@ export interface UnifiedCheckOptions {
12
58
  connector?: string;
13
59
  local?: boolean;
14
60
  transport?: 'cdp';
61
+ json?: boolean;
15
62
  }
16
63
  export interface UnifiedCheckDependencies {
17
64
  createClient?: (options: UnifiedClientOptions) => CheckClient;
65
+ writeJson?: (json: string) => void;
18
66
  writeLine?: (line: string) => void;
19
67
  }
20
68
  export declare function parseCheckTimeout(value: string | undefined): number | null;
69
+ export declare function runUnifiedCheck(options: UnifiedCheckOptions & {
70
+ json: true;
71
+ }, dependencies?: UnifiedCheckDependencies): Promise<UnifiedCheckResult>;
21
72
  export declare function runUnifiedCheck(options: UnifiedCheckOptions, dependencies?: UnifiedCheckDependencies): Promise<boolean>;
22
73
  export {};
package/dist/check.js CHANGED
@@ -73,6 +73,7 @@ export function parseCheckTimeout(value) {
73
73
  export async function runUnifiedCheck(options, dependencies = {}) {
74
74
  const timeoutSec = options.timeoutSec ?? 5;
75
75
  const writeLine = dependencies.writeLine ?? (line => console.error(line));
76
+ const writeJson = dependencies.writeJson ?? (json => process.stdout.write(`${json}\n`));
76
77
  const createClient = dependencies.createClient ??
77
78
  ((clientOptions) => new UnifiedClient(clientOptions));
78
79
  const client = createClient({
@@ -194,11 +195,28 @@ export async function runUnifiedCheck(options, dependencies = {}) {
194
195
  browser: browser.browserId,
195
196
  ...(options.transport ? { transport: options.transport } : {}),
196
197
  }));
198
+ const publicVersions = {
199
+ ...(versions.nativeHost ? { nativeHost: versions.nativeHost } : {}),
200
+ ...(versions.extension !== undefined ? { extension: versions.extension } : {}),
201
+ ...(versions.browser !== undefined ? { browser: versions.browser } : {}),
202
+ };
197
203
  const details = [
198
204
  versions.nativeHost ? `native-host v${versions.nativeHost}` : null,
199
205
  versions.extension ? `extension v${versions.extension}` : null,
200
206
  versions.browser ? `Chrome ${versions.browser}` : null,
201
207
  ].filter((value) => value !== null);
208
+ let extensionInstallation;
209
+ if (options.local &&
210
+ browser.transport === 'extension' &&
211
+ typeof versions.extensionId === 'string' &&
212
+ typeof versions.extension === 'string' &&
213
+ (typeof versions.setupManagedPath === 'string' || versions.setupManagedPath === null)) {
214
+ extensionInstallation = {
215
+ extensionId: versions.extensionId,
216
+ setupManagedPath: versions.setupManagedPath,
217
+ };
218
+ }
219
+ let agentbayAccessUrl;
202
220
  let accessUrlWarning;
203
221
  if (browser.provider === 'agentbay' && options.browser) {
204
222
  try {
@@ -209,6 +227,7 @@ export async function runUnifiedCheck(options, dependencies = {}) {
209
227
  if (!access.agentbayAccessUrl) {
210
228
  throw new Error('AgentBay did not return a browser access URL');
211
229
  }
230
+ agentbayAccessUrl = access.agentbayAccessUrl;
212
231
  details.push(`AgentBay access URL (temporary, sensitive): ${access.agentbayAccessUrl}`);
213
232
  }
214
233
  catch (error) {
@@ -217,7 +236,9 @@ export async function runUnifiedCheck(options, dependencies = {}) {
217
236
  }
218
237
  }
219
238
  browserChecks.push({
239
+ ...(agentbayAccessUrl ? { agentbayAccessUrl } : {}),
220
240
  browser,
241
+ ...(extensionInstallation ? { extensionInstallation } : {}),
221
242
  item: {
222
243
  status: accessUrlWarning ? 'warning' : 'ok',
223
244
  label: accessUrlWarning
@@ -225,12 +246,17 @@ export async function runUnifiedCheck(options, dependencies = {}) {
225
246
  : 'Browser command path',
226
247
  details,
227
248
  },
249
+ versions: publicVersions,
250
+ ...(accessUrlWarning ? { warnings: [accessUrlWarning] } : {}),
228
251
  });
229
252
  }
230
253
  catch (error) {
231
254
  const message = formatError(error);
232
255
  browserChecks.push({
233
256
  browser,
257
+ ...(message.includes('Unknown action')
258
+ ? { warnings: ['Update @mearl/native-host for complete diagnostics.'] }
259
+ : { errors: [message] }),
234
260
  item: message.includes('Unknown action')
235
261
  ? {
236
262
  status: 'warning',
@@ -245,6 +271,44 @@ export async function runUnifiedCheck(options, dependencies = {}) {
245
271
  });
246
272
  }
247
273
  }));
274
+ const hasError = environment.some(item => item.status === 'error') ||
275
+ browserChecks.some(check => check.item.status === 'error');
276
+ const listedBrowsers = browserList?.browsers ?? [];
277
+ if (options.json) {
278
+ const result = {
279
+ browsers: listedBrowsers.map(browser => {
280
+ const check = browserChecks.find(item => item.browser.browserId === browser.browserId);
281
+ const browserIdentity = { ...browser };
282
+ delete browserIdentity.extensionVersion;
283
+ delete browserIdentity.nativeHostVersion;
284
+ return {
285
+ ...browserIdentity,
286
+ selected: targets.some(target => target.browserId === browser.browserId),
287
+ ...(check?.agentbayAccessUrl ? { agentbayAccessUrl: check.agentbayAccessUrl } : {}),
288
+ ...(check?.item
289
+ ? {
290
+ check: {
291
+ label: check.item.label,
292
+ status: check.item.status,
293
+ ...(check.errors ? { errors: check.errors } : {}),
294
+ ...(check.warnings ? { warnings: check.warnings } : {}),
295
+ },
296
+ }
297
+ : {}),
298
+ ...(check?.extensionInstallation
299
+ ? { extensionInstallation: check.extensionInstallation }
300
+ : {}),
301
+ ...(check?.versions ? { versions: check.versions } : {}),
302
+ };
303
+ }),
304
+ clientVersion: options.clientVersion,
305
+ connectors,
306
+ environment,
307
+ ok: !hasError,
308
+ };
309
+ writeJson(JSON.stringify(result, null, 2));
310
+ return result;
311
+ }
248
312
  writeLine('');
249
313
  printSection(writeLine, 'Environment', environment);
250
314
  if (connectors.length > 0) {
@@ -253,7 +317,6 @@ export async function runUnifiedCheck(options, dependencies = {}) {
253
317
  writeLine(formatConnector(connector));
254
318
  writeLine('');
255
319
  }
256
- const listedBrowsers = browserList?.browsers ?? [];
257
320
  if (listedBrowsers.length > 0) {
258
321
  writeLine('Browsers');
259
322
  for (const browser of listedBrowsers) {
@@ -271,8 +334,6 @@ export async function runUnifiedCheck(options, dependencies = {}) {
271
334
  }
272
335
  writeLine('');
273
336
  }
274
- const hasError = environment.some(item => item.status === 'error') ||
275
- browserChecks.some(check => check.item.status === 'error');
276
337
  if (hasError) {
277
338
  writeLine('⚠️ Mearl checks failed. See the browser or source details above.');
278
339
  return false;
package/dist/cli.js CHANGED
@@ -81,6 +81,7 @@ function printUsage(commandName, version) {
81
81
  ' --payload-file <path> 从文件读取整个 JSON 参数',
82
82
  ' --timeout <seconds> 超时时间(默认按 action 推导)',
83
83
  ' --compact 输出紧凑格式 JSON',
84
+ ' --json check 输出机器可读 JSON',
84
85
  ' --output <path> 保存输出或截图',
85
86
  ' --browser <id|名称> 指定 browser_list 返回的浏览器',
86
87
  ' --connector <id|名称> 限制到一台远端 connector',
@@ -192,7 +193,7 @@ function validateOptions(args) {
192
193
  '--connector',
193
194
  '--server',
194
195
  ]);
195
- const flags = new Set(['--compact', '--local', '--cdp', '--help', '-h']);
196
+ const flags = new Set(['--compact', '--json', '--local', '--cdp', '--help', '-h']);
196
197
  for (let index = 1; index < args.length; index += 1) {
197
198
  const arg = args[index];
198
199
  if (optionsWithValues.has(arg)) {
@@ -222,6 +223,9 @@ function resolveServer(args) {
222
223
  function parseArgs(args) {
223
224
  validateOptions(args);
224
225
  const action = args[0];
226
+ const json = args.includes('--json');
227
+ if (json && action !== 'check')
228
+ throw new Error('--json 仅适用于 check');
225
229
  const payload = parsePayload(args);
226
230
  const fallbackTimeout = resolveActionTimeoutSec(action, payload, 60);
227
231
  const forceCdp = args.includes('--cdp');
@@ -235,6 +239,7 @@ function parseArgs(args) {
235
239
  timeoutSec: parsePositiveTimeout(optionValue(args, '--timeout'), fallbackTimeout),
236
240
  compact: args.includes('--compact'),
237
241
  forceCdp,
242
+ json,
238
243
  local,
239
244
  ...(optionValue(args, '--output') ? { outputPath: optionValue(args, '--output') } : {}),
240
245
  ...(optionValue(args, '--browser') ? { browser: optionValue(args, '--browser') } : {}),
@@ -312,7 +317,7 @@ export async function runMearlCli(args, options = {}) {
312
317
  console.error('--timeout 必须是正整数秒');
313
318
  return 2;
314
319
  }
315
- const ok = await runUnifiedCheck({
320
+ const checkOptions = {
316
321
  clientVersion,
317
322
  timeoutSec,
318
323
  ...(parsed.serverUrl ? { serverUrl: parsed.serverUrl } : {}),
@@ -321,8 +326,12 @@ export async function runMearlCli(args, options = {}) {
321
326
  ...(parsed.connector ? { connector: parsed.connector } : {}),
322
327
  ...(parsed.local ? { local: true } : {}),
323
328
  ...(parsed.forceCdp ? { transport: 'cdp' } : {}),
324
- });
325
- return ok ? 0 : 1;
329
+ };
330
+ if (parsed.json) {
331
+ const result = await runUnifiedCheck({ ...checkOptions, json: true });
332
+ return result.ok ? 0 : 1;
333
+ }
334
+ return (await runUnifiedCheck(checkOptions)) ? 0 : 1;
326
335
  }
327
336
  if (parsed.action === 'page_selected_element' && parsed.outputPath) {
328
337
  parsed.payload = { ...parsed.payload, includeScreenshot: true };
@@ -501,9 +501,17 @@ export declare const COMMANDS: readonly [{
501
501
  }, {
502
502
  readonly name: "appProfile";
503
503
  readonly type: "string";
504
- readonly description: "开 tab 即进入 App 容器模拟态(客户端 UA + JSBridge 托管 + iframe 容器栈,首屏即容器态)。档案名同 set_app_profile,如 \"fliggy\"";
504
+ readonly description: "开 tab 即进入 App 容器模拟态(客户端 UA + JSBridge 托管 + iframe 容器栈 + 自适应系统标题栏,首屏即容器态)。系统栏由 iframe 外层统一渲染,并根据页面协议与桥调用切换普通、沉浸或隐藏状态;档案名同 set_app_profile,如 \"fliggy\"";
505
+ }, {
506
+ readonly name: "os";
507
+ readonly type: "\"ios\" | \"android\"";
508
+ readonly description: "与 appProfile 配合;容器系统,默认 ios";
509
+ }, {
510
+ readonly name: "simulateSystemBars";
511
+ readonly type: "boolean";
512
+ readonly description: "与 appProfile 配合;是否模拟顶部状态/导航栏与底部手势栏,默认 true";
505
513
  }];
506
- readonly examples: ["tab_open --payload '{\"url\":\"https://example.com\"}'", "tab_open --payload '{\"url\":\"https://example.com/page\",\"reuse\":\"prefer\",\"match\":{\"ignoreSearch\":true}}'", "tab_open --payload '{\"url\":\"https://m.example.com\",\"emulation\":{\"preset\":\"iphone-15-pro\"}}'", "tab_open --payload '{\"url\":\"https://market.m.taobao.com/app/trip/rx-home/pages/home\",\"appProfile\":\"fliggy\"}'", "tab_open --payload '{\"url\":\"https://example.com\",\"group\":\"验证任务\"}'"];
514
+ readonly examples: ["tab_open --payload '{\"url\":\"https://example.com\"}'", "tab_open --payload '{\"url\":\"https://example.com/page\",\"reuse\":\"prefer\",\"match\":{\"ignoreSearch\":true}}'", "tab_open --payload '{\"url\":\"https://m.example.com\",\"emulation\":{\"preset\":\"iphone-15-pro\"}}'", "tab_open --payload '{\"url\":\"https://market.m.taobao.com/app/trip/rx-home/pages/home\",\"appProfile\":\"fliggy\"}'", "tab_open --payload '{\"url\":\"https://example.com\",\"appProfile\":\"fliggy\",\"os\":\"android\"}'", "tab_open --payload '{\"url\":\"https://example.com\",\"group\":\"验证任务\"}'"];
507
515
  }, {
508
516
  readonly name: "tab_close";
509
517
  readonly description: "关闭指定标签页";
@@ -930,7 +938,7 @@ export declare const COMMANDS: readonly [{
930
938
  readonly examples: ["set_device_emulation --payload '{\"enabled\":true,\"preset\":\"iphone-15-pro\"}'", "set_device_emulation --payload '{\"enabled\":true,\"preset\":\"pixel-8\",\"orientation\":\"landscape\"}'", "set_device_emulation --payload '{\"enabled\":false}'"];
931
939
  }, {
932
940
  readonly name: "set_app_profile";
933
- readonly description: "把目标 tab 切换为 App 容器模拟态(客户端 UA + JSBridge 托管 + 页面跳转 iframe 容器栈 + scheme 唤端映射),用于桌面浏览器近似实机验证 App 内 H5;需保持调试控制才持续生效";
941
+ readonly description: "把目标 tab 切换为 App 容器模拟态(客户端 UA + JSBridge 托管 + 页面跳转 iframe 容器栈 + scheme 唤端映射 + 自适应系统标题栏),系统栏由 iframe 外层统一渲染,并根据页面协议与桥调用切换普通、沉浸或隐藏状态;用于桌面浏览器近似实机验证 App 内 H5,需保持调试控制才持续生效";
934
942
  readonly params: [{
935
943
  readonly name: "enabled";
936
944
  readonly type: "boolean";
@@ -952,6 +960,10 @@ export declare const COMMANDS: readonly [{
952
960
  readonly name: "deviceModel";
953
961
  readonly type: "string";
954
962
  readonly description: "Android 机型名(拼入 UA 与 __DEVICE_MODEL__)";
963
+ }, {
964
+ readonly name: "simulateSystemBars";
965
+ readonly type: "boolean";
966
+ readonly description: "是否模拟顶部状态/导航栏与底部手势栏,默认 true";
955
967
  }, {
956
968
  readonly name: "emulation";
957
969
  readonly type: "object";
@@ -966,7 +978,7 @@ export declare const COMMANDS: readonly [{
966
978
  readonly required: true;
967
979
  readonly description: "目标标签页 ID";
968
980
  }];
969
- readonly examples: ["set_app_profile --payload '{\"enabled\":true,\"profile\":\"fliggy\"}'", "set_app_profile --payload '{\"enabled\":true,\"profile\":\"alipay\",\"os\":\"android\",\"deviceModel\":\"Pixel 8 Pro\"}'", "set_app_profile --payload '{\"enabled\":false}'"];
981
+ readonly examples: ["set_app_profile --payload '{\"enabled\":true,\"profile\":\"fliggy\"}'", "set_app_profile --payload '{\"enabled\":true,\"profile\":\"fliggy\",\"simulateSystemBars\":false}'", "set_app_profile --payload '{\"enabled\":true,\"profile\":\"alipay\",\"os\":\"android\",\"deviceModel\":\"Pixel 8 Pro\"}'", "set_app_profile --payload '{\"enabled\":false}'"];
970
982
  }, {
971
983
  readonly name: "set_timezone";
972
984
  readonly description: "覆盖目标 tab 的时区,传空字符串恢复浏览器默认时区";
package/dist/commands.js CHANGED
@@ -408,7 +408,17 @@ export const COMMANDS = [
408
408
  {
409
409
  name: 'appProfile',
410
410
  type: 'string',
411
- description: '开 tab 即进入 App 容器模拟态(客户端 UA + JSBridge 托管 + iframe 容器栈,首屏即容器态)。档案名同 set_app_profile,如 "fliggy"',
411
+ description: '开 tab 即进入 App 容器模拟态(客户端 UA + JSBridge 托管 + iframe 容器栈 + 自适应系统标题栏,首屏即容器态)。系统栏由 iframe 外层统一渲染,并根据页面协议与桥调用切换普通、沉浸或隐藏状态;档案名同 set_app_profile,如 "fliggy"',
412
+ },
413
+ {
414
+ name: 'os',
415
+ type: '"ios" | "android"',
416
+ description: '与 appProfile 配合;容器系统,默认 ios',
417
+ },
418
+ {
419
+ name: 'simulateSystemBars',
420
+ type: 'boolean',
421
+ description: '与 appProfile 配合;是否模拟顶部状态/导航栏与底部手势栏,默认 true',
412
422
  },
413
423
  ],
414
424
  examples: [
@@ -416,6 +426,7 @@ export const COMMANDS = [
416
426
  `tab_open --payload '{"url":"https://example.com/page","reuse":"prefer","match":{"ignoreSearch":true}}'`,
417
427
  `tab_open --payload '{"url":"https://m.example.com","emulation":{"preset":"iphone-15-pro"}}'`,
418
428
  `tab_open --payload '{"url":"https://market.m.taobao.com/app/trip/rx-home/pages/home","appProfile":"fliggy"}'`,
429
+ `tab_open --payload '{"url":"https://example.com","appProfile":"fliggy","os":"android"}'`,
419
430
  `tab_open --payload '{"url":"https://example.com","group":"验证任务"}'`,
420
431
  ],
421
432
  },
@@ -834,7 +845,7 @@ export const COMMANDS = [
834
845
  },
835
846
  {
836
847
  name: 'set_app_profile',
837
- description: '把目标 tab 切换为 App 容器模拟态(客户端 UA + JSBridge 托管 + 页面跳转 iframe 容器栈 + scheme 唤端映射),用于桌面浏览器近似实机验证 App 内 H5;需保持调试控制才持续生效',
848
+ description: '把目标 tab 切换为 App 容器模拟态(客户端 UA + JSBridge 托管 + 页面跳转 iframe 容器栈 + scheme 唤端映射 + 自适应系统标题栏),系统栏由 iframe 外层统一渲染,并根据页面协议与桥调用切换普通、沉浸或隐藏状态;用于桌面浏览器近似实机验证 App 内 H5,需保持调试控制才持续生效',
838
849
  params: [
839
850
  { name: 'enabled', type: 'boolean', required: true, description: 'true=启用,false=清除' },
840
851
  {
@@ -849,6 +860,11 @@ export const COMMANDS = [
849
860
  type: 'string',
850
861
  description: 'Android 机型名(拼入 UA 与 __DEVICE_MODEL__)',
851
862
  },
863
+ {
864
+ name: 'simulateSystemBars',
865
+ type: 'boolean',
866
+ description: '是否模拟顶部状态/导航栏与底部手势栏,默认 true',
867
+ },
852
868
  {
853
869
  name: 'emulation',
854
870
  type: 'object',
@@ -859,6 +875,7 @@ export const COMMANDS = [
859
875
  ],
860
876
  examples: [
861
877
  `set_app_profile --payload '{"enabled":true,"profile":"fliggy"}'`,
878
+ `set_app_profile --payload '{"enabled":true,"profile":"fliggy","simulateSystemBars":false}'`,
862
879
  `set_app_profile --payload '{"enabled":true,"profile":"alipay","os":"android","deviceModel":"Pixel 8 Pro"}'`,
863
880
  `set_app_profile --payload '{"enabled":false}'`,
864
881
  ],
@@ -289,9 +289,14 @@ export interface TabOpenParams {
289
289
  group?: string;
290
290
  /**
291
291
  * 客户端容器模拟档案名(如 'fliggy')。打开后即按该 App 的 WebView 环境渲染:
292
- * 移动视口 + 容器 UA + JSBridge 托管。可用档案见 APP_PROFILE_NAMES。
292
+ * 移动视口 + 容器 UA + JSBridge 托管 + 自适应系统标题栏。系统栏由 iframe 外层统一渲染,
293
+ * 并根据页面协议与桥调用切换普通、沉浸或隐藏状态。可用档案见 APP_PROFILE_NAMES。
293
294
  */
294
295
  appProfile?: AppProfileName;
296
+ /** 与 appProfile 配合使用;容器系统,默认 ios */
297
+ os?: 'ios' | 'android';
298
+ /** 与 appProfile 配合使用;是否模拟顶部状态/导航栏与底部手势栏,默认 true */
299
+ simulateSystemBars?: boolean;
295
300
  }
296
301
  /**
297
302
  * 客户端容器模拟可用档案名。
@@ -312,6 +317,8 @@ export interface SetAppProfileParams {
312
317
  os?: 'ios' | 'android';
313
318
  appVersion?: string;
314
319
  deviceModel?: string;
320
+ /** 是否模拟顶部状态/导航栏与底部手势栏,默认 true */
321
+ simulateSystemBars?: boolean;
315
322
  /** 覆盖档案推荐的设备模拟参数(视口/UA 默认取档案值) */
316
323
  emulation?: BrowserEmulationOptions;
317
324
  /** 默认 true:应用/清除后刷新页面使注入生效 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mearl/client",
3
- "version": "2.8.0",
3
+ "version": "2.8.2",
4
4
  "description": "Unified Mearl SDK & CLI for local and remote browsers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -51,7 +51,7 @@
51
51
  "dependencies": {
52
52
  "fastest-levenshtein": "^1.0.16",
53
53
  "ws": "^8.18.0",
54
- "@mearl/cloud-types": "2.8.0"
54
+ "@mearl/cloud-types": "2.8.2"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/node": "^24.9.1",