@mearl/client
Advanced tools
+51
-0
@@ -0,2 +1,48 @@ | ||
| import type { CloudConnectorInfo } from '@mearl/cloud-types'; | ||
| import { type UnifiedClientOptions, type UnifiedInvokeOptions } from './unified.js'; | ||
| export interface UnifiedCheckVersions { | ||
| nativeHost?: string; | ||
| extension?: string | null; | ||
| browser?: string | null; | ||
| } | ||
| interface BrowserInfo { | ||
| browserId: string; | ||
| extensionVersion?: string; | ||
| name: string; | ||
| nativeHostVersion?: string; | ||
| originalBrowserId?: string; | ||
| originalName?: string; | ||
| status: string; | ||
| type?: string; | ||
| provider?: 'local' | 'agentbay'; | ||
| transport?: 'extension' | 'cdp'; | ||
| } | ||
| interface CheckItem { | ||
| status: 'ok' | 'warning' | 'error'; | ||
| label: string; | ||
| details?: string[]; | ||
| } | ||
| export interface ExtensionInstallationInfo { | ||
| extensionId: string; | ||
| setupManagedPath: string | null; | ||
| } | ||
| export interface UnifiedCheckBrowserResult extends BrowserInfo { | ||
| agentbayAccessUrl?: string; | ||
| check?: { | ||
| errors?: string[]; | ||
| label: string; | ||
| status: CheckItem['status']; | ||
| warnings?: string[]; | ||
| }; | ||
| extensionInstallation?: ExtensionInstallationInfo; | ||
| selected: boolean; | ||
| versions?: UnifiedCheckVersions; | ||
| } | ||
| export interface UnifiedCheckResult { | ||
| browsers: UnifiedCheckBrowserResult[]; | ||
| clientVersion: string; | ||
| connectors: CloudConnectorInfo[]; | ||
| environment: CheckItem[]; | ||
| ok: boolean; | ||
| } | ||
| interface CheckClient { | ||
@@ -15,9 +61,14 @@ invoke(action: string, data?: Record<string, any>, options?: UnifiedInvokeOptions): Promise<any>; | ||
| transport?: 'cdp'; | ||
| json?: boolean; | ||
| } | ||
| export interface UnifiedCheckDependencies { | ||
| createClient?: (options: UnifiedClientOptions) => CheckClient; | ||
| writeJson?: (json: string) => void; | ||
| writeLine?: (line: string) => void; | ||
| } | ||
| export declare function parseCheckTimeout(value: string | undefined): number | null; | ||
| export declare function runUnifiedCheck(options: UnifiedCheckOptions & { | ||
| json: true; | ||
| }, dependencies?: UnifiedCheckDependencies): Promise<UnifiedCheckResult>; | ||
| export declare function runUnifiedCheck(options: UnifiedCheckOptions, dependencies?: UnifiedCheckDependencies): Promise<boolean>; | ||
| export {}; |
+64
-3
@@ -76,2 +76,3 @@ import { UnifiedClient } from './unified.js'; | ||
| const writeLine = dependencies.writeLine ?? (line => console.error(line)); | ||
| const writeJson = dependencies.writeJson ?? (json => process.stdout.write(`${json}\n`)); | ||
| const createClient = dependencies.createClient ?? | ||
@@ -198,2 +199,7 @@ ((clientOptions) => new UnifiedClient(clientOptions)); | ||
| })); | ||
| const publicVersions = { | ||
| ...(versions.nativeHost ? { nativeHost: versions.nativeHost } : {}), | ||
| ...(versions.extension !== undefined ? { extension: versions.extension } : {}), | ||
| ...(versions.browser !== undefined ? { browser: versions.browser } : {}), | ||
| }; | ||
| const details = [ | ||
@@ -204,2 +210,14 @@ versions.nativeHost ? `native-host v${versions.nativeHost}` : null, | ||
| ].filter((value) => value !== null); | ||
| let extensionInstallation; | ||
| if (options.local && | ||
| browser.transport === 'extension' && | ||
| typeof versions.extensionId === 'string' && | ||
| typeof versions.extension === 'string' && | ||
| (typeof versions.setupManagedPath === 'string' || versions.setupManagedPath === null)) { | ||
| extensionInstallation = { | ||
| extensionId: versions.extensionId, | ||
| setupManagedPath: versions.setupManagedPath, | ||
| }; | ||
| } | ||
| let agentbayAccessUrl; | ||
| let accessUrlWarning; | ||
@@ -215,2 +233,3 @@ if (browser.provider === 'agentbay' && options.browser) { | ||
| } | ||
| agentbayAccessUrl = access.agentbayAccessUrl; | ||
| details.push(`AgentBay access URL (temporary, sensitive): ${access.agentbayAccessUrl}`); | ||
@@ -224,3 +243,5 @@ } | ||
| browserChecks.push({ | ||
| ...(agentbayAccessUrl ? { agentbayAccessUrl } : {}), | ||
| browser, | ||
| ...(extensionInstallation ? { extensionInstallation } : {}), | ||
| item: { | ||
@@ -233,2 +254,4 @@ status: accessUrlWarning ? 'warning' : 'ok', | ||
| }, | ||
| versions: publicVersions, | ||
| ...(accessUrlWarning ? { warnings: [accessUrlWarning] } : {}), | ||
| }); | ||
@@ -240,2 +263,5 @@ } | ||
| browser, | ||
| ...(message.includes('Unknown action') | ||
| ? { warnings: ['Update @mearl/native-host for complete diagnostics.'] } | ||
| : { errors: [message] }), | ||
| item: message.includes('Unknown action') | ||
@@ -255,2 +281,40 @@ ? { | ||
| })); | ||
| const hasError = environment.some(item => item.status === 'error') || | ||
| browserChecks.some(check => check.item.status === 'error'); | ||
| const listedBrowsers = browserList?.browsers ?? []; | ||
| if (options.json) { | ||
| const result = { | ||
| browsers: listedBrowsers.map(browser => { | ||
| const check = browserChecks.find(item => item.browser.browserId === browser.browserId); | ||
| const browserIdentity = { ...browser }; | ||
| delete browserIdentity.extensionVersion; | ||
| delete browserIdentity.nativeHostVersion; | ||
| return { | ||
| ...browserIdentity, | ||
| selected: targets.some(target => target.browserId === browser.browserId), | ||
| ...(check?.agentbayAccessUrl ? { agentbayAccessUrl: check.agentbayAccessUrl } : {}), | ||
| ...(check?.item | ||
| ? { | ||
| check: { | ||
| label: check.item.label, | ||
| status: check.item.status, | ||
| ...(check.errors ? { errors: check.errors } : {}), | ||
| ...(check.warnings ? { warnings: check.warnings } : {}), | ||
| }, | ||
| } | ||
| : {}), | ||
| ...(check?.extensionInstallation | ||
| ? { extensionInstallation: check.extensionInstallation } | ||
| : {}), | ||
| ...(check?.versions ? { versions: check.versions } : {}), | ||
| }; | ||
| }), | ||
| clientVersion: options.clientVersion, | ||
| connectors, | ||
| environment, | ||
| ok: !hasError, | ||
| }; | ||
| writeJson(JSON.stringify(result, null, 2)); | ||
| return result; | ||
| } | ||
| writeLine(''); | ||
@@ -264,3 +328,2 @@ printSection(writeLine, 'Environment', environment); | ||
| } | ||
| const listedBrowsers = browserList?.browsers ?? []; | ||
| if (listedBrowsers.length > 0) { | ||
@@ -283,4 +346,2 @@ writeLine('Browsers'); | ||
| } | ||
| const hasError = environment.some(item => item.status === 'error') || | ||
| browserChecks.some(check => check.item.status === 'error'); | ||
| if (hasError) { | ||
@@ -287,0 +348,0 @@ writeLine('⚠️ Mearl checks failed. See the browser or source details above.'); |
+13
-4
@@ -84,2 +84,3 @@ import { readFileSync, writeFileSync } from 'node:fs'; | ||
| ' --compact 输出紧凑格式 JSON', | ||
| ' --json check 输出机器可读 JSON', | ||
| ' --output <path> 保存输出或截图', | ||
@@ -196,3 +197,3 @@ ' --browser <id|名称> 指定 browser_list 返回的浏览器', | ||
| ]); | ||
| const flags = new Set(['--compact', '--local', '--cdp', '--help', '-h']); | ||
| const flags = new Set(['--compact', '--json', '--local', '--cdp', '--help', '-h']); | ||
| for (let index = 1; index < args.length; index += 1) { | ||
@@ -227,2 +228,5 @@ const arg = args[index]; | ||
| const action = args[0]; | ||
| const json = args.includes('--json'); | ||
| if (json && action !== 'check') | ||
| throw new Error('--json 仅适用于 check'); | ||
| const payload = parsePayload(args); | ||
@@ -241,2 +245,3 @@ const fallbackTimeout = resolveActionTimeoutSec(action, payload, 60); | ||
| forceCdp, | ||
| json, | ||
| local, | ||
@@ -319,3 +324,3 @@ ...(optionValue(args, '--output') ? { outputPath: optionValue(args, '--output') } : {}), | ||
| } | ||
| const ok = await runUnifiedCheck({ | ||
| const checkOptions = { | ||
| clientVersion, | ||
@@ -329,4 +334,8 @@ timeoutSec, | ||
| ...(parsed.forceCdp ? { transport: 'cdp' } : {}), | ||
| }); | ||
| return ok ? 0 : 1; | ||
| }; | ||
| if (parsed.json) { | ||
| const result = await runUnifiedCheck({ ...checkOptions, json: true }); | ||
| return result.ok ? 0 : 1; | ||
| } | ||
| return (await runUnifiedCheck(checkOptions)) ? 0 : 1; | ||
| } | ||
@@ -333,0 +342,0 @@ if (parsed.action === 'page_selected_element' && parsed.outputPath) { |
+14
-2
@@ -505,4 +505,12 @@ import { type BrowserCommandAction } from './generated-browser-action-protocol.js'; | ||
| readonly description: "开 tab 即进入 App 容器模拟态(客户端 UA + JSBridge 托管 + iframe 容器栈 + 自适应系统标题栏,首屏即容器态)。系统栏由 iframe 外层统一渲染,并根据页面协议与桥调用切换普通、沉浸或隐藏状态;档案名同 set_app_profile,如 \"fliggy\""; | ||
| }, { | ||
| readonly name: "os"; | ||
| readonly type: "\"ios\" | \"android\""; | ||
| readonly description: "与 appProfile 配合;容器系统,默认 ios"; | ||
| }, { | ||
| readonly name: "simulateSystemBars"; | ||
| readonly type: "boolean"; | ||
| readonly description: "与 appProfile 配合;是否模拟顶部状态/导航栏与底部手势栏,默认 true"; | ||
| }]; | ||
| 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\":\"验证任务\"}'"]; | ||
| 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\":\"验证任务\"}'"]; | ||
| }, { | ||
@@ -957,2 +965,6 @@ readonly name: "tab_close"; | ||
| }, { | ||
| readonly name: "simulateSystemBars"; | ||
| readonly type: "boolean"; | ||
| readonly description: "是否模拟顶部状态/导航栏与底部手势栏,默认 true"; | ||
| }, { | ||
| readonly name: "emulation"; | ||
@@ -971,3 +983,3 @@ readonly type: "object"; | ||
| }]; | ||
| 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}'"]; | ||
| 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}'"]; | ||
| }, { | ||
@@ -974,0 +986,0 @@ readonly name: "set_timezone"; |
+17
-0
@@ -413,2 +413,12 @@ import { APP_PROFILE_NAMES, } from './generated-browser-action-protocol.js'; | ||
| }, | ||
| { | ||
| name: 'os', | ||
| type: '"ios" | "android"', | ||
| description: '与 appProfile 配合;容器系统,默认 ios', | ||
| }, | ||
| { | ||
| name: 'simulateSystemBars', | ||
| type: 'boolean', | ||
| description: '与 appProfile 配合;是否模拟顶部状态/导航栏与底部手势栏,默认 true', | ||
| }, | ||
| ], | ||
@@ -420,2 +430,3 @@ examples: [ | ||
| `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":"验证任务"}'`, | ||
@@ -855,2 +866,7 @@ ], | ||
| { | ||
| name: 'simulateSystemBars', | ||
| type: 'boolean', | ||
| description: '是否模拟顶部状态/导航栏与底部手势栏,默认 true', | ||
| }, | ||
| { | ||
| name: 'emulation', | ||
@@ -865,2 +881,3 @@ type: 'object', | ||
| `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"}'`, | ||
@@ -867,0 +884,0 @@ `set_app_profile --payload '{"enabled":false}'`, |
@@ -296,2 +296,6 @@ export interface GetRequestsParams { | ||
| appProfile?: AppProfileName; | ||
| /** 与 appProfile 配合使用;容器系统,默认 ios */ | ||
| os?: 'ios' | 'android'; | ||
| /** 与 appProfile 配合使用;是否模拟顶部状态/导航栏与底部手势栏,默认 true */ | ||
| simulateSystemBars?: boolean; | ||
| } | ||
@@ -317,2 +321,4 @@ /** | ||
| deviceModel?: string; | ||
| /** 是否模拟顶部状态/导航栏与底部手势栏,默认 true */ | ||
| simulateSystemBars?: boolean; | ||
| /** 覆盖档案推荐的设备模拟参数(视口/UA 默认取档案值) */ | ||
@@ -319,0 +325,0 @@ emulation?: BrowserEmulationOptions; |
+2
-2
| { | ||
| "name": "@mearl/client", | ||
| "version": "2.8.1", | ||
| "version": "2.8.2", | ||
| "description": "Unified Mearl SDK & CLI for local and remote browsers", | ||
@@ -54,3 +54,3 @@ "type": "module", | ||
| "ws": "^8.18.0", | ||
| "@mearl/cloud-types": "2.8.1" | ||
| "@mearl/cloud-types": "2.8.2" | ||
| }, | ||
@@ -57,0 +57,0 @@ "devDependencies": { |
+1
-0
@@ -58,2 +58,3 @@ # @mearl/client | ||
| mearl check | ||
| mearl check --local --json | ||
@@ -60,0 +61,0 @@ # 更新本地环境 |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
AI-detected potential security risk
Supply chain riskAI has determined that this package may contain potential security issues or vulnerabilities.
253494
2.93%5566
2.92%144
0.7%1
-50%17
6.25%+ Added
- Removed
Updated