@mearl/client
Advanced tools
+16
-0
@@ -5,2 +5,18 @@ export interface MearlCliOptions { | ||
| } | ||
| export interface ParsedArgs { | ||
| action: string; | ||
| browser?: string; | ||
| compact: boolean; | ||
| connector?: string; | ||
| forceCdp: boolean; | ||
| json: boolean; | ||
| local: boolean; | ||
| outputPath?: string; | ||
| payload: Record<string, any>; | ||
| serverUrl?: string; | ||
| serverUrlSource?: string; | ||
| timeoutSec: number; | ||
| timeoutOption?: string; | ||
| } | ||
| export declare function parseClientArgs(args: string[]): ParsedArgs; | ||
| export declare function runMearlCli(args: string[], options?: MearlCliOptions): Promise<number>; |
+46
-49
| import { readFileSync, writeFileSync } from 'node:fs'; | ||
| import { resolve } from 'node:path'; | ||
| import { Command, CommanderError } from 'commander'; | ||
| import { resolveActionTimeoutSec } from './actionTimeouts.js'; | ||
@@ -62,6 +63,2 @@ import { runUnifiedCheck, parseCheckTimeout } from './check.js'; | ||
| const ADVANCED_BUILTIN_COMMANDS = new Set(['connector_list']); | ||
| function optionValue(args, name) { | ||
| const index = args.indexOf(name); | ||
| return index >= 0 ? args[index + 1] : undefined; | ||
| } | ||
| function printUsage(commandName, version) { | ||
@@ -163,5 +160,3 @@ const lines = [ | ||
| } | ||
| function parsePayload(args) { | ||
| const payloadRaw = optionValue(args, '--payload'); | ||
| const payloadFile = optionValue(args, '--payload-file'); | ||
| function parsePayload(payloadRaw, payloadFile) { | ||
| if (payloadRaw !== undefined && payloadFile !== undefined) { | ||
@@ -188,29 +183,3 @@ throw new Error('--payload 与 --payload-file 不能同时使用'); | ||
| } | ||
| function validateOptions(args) { | ||
| const optionsWithValues = new Set([ | ||
| '--payload', | ||
| '--payload-file', | ||
| '--timeout', | ||
| '--output', | ||
| '--browser', | ||
| '--connector', | ||
| '--server', | ||
| ]); | ||
| const flags = new Set(['--compact', '--json', '--local', '--cdp', '--help', '-h']); | ||
| for (let index = 1; index < args.length; index += 1) { | ||
| const arg = args[index]; | ||
| if (optionsWithValues.has(arg)) { | ||
| if (!args[index + 1] || args[index + 1].startsWith('--')) { | ||
| throw new Error(`缺少 ${arg} 的参数值`); | ||
| } | ||
| index += 1; | ||
| continue; | ||
| } | ||
| if (flags.has(arg)) | ||
| continue; | ||
| throw new Error(`未知参数: ${arg}`); | ||
| } | ||
| } | ||
| function resolveServer(args) { | ||
| const explicit = optionValue(args, '--server'); | ||
| function resolveServer(explicit) { | ||
| if (explicit) | ||
@@ -226,13 +195,40 @@ return { serverUrl: explicit, serverUrlSource: '--server' }; | ||
| } | ||
| function parseArgs(args) { | ||
| validateOptions(args); | ||
| const action = args[0]; | ||
| const json = args.includes('--json'); | ||
| export function parseClientArgs(args) { | ||
| const program = new Command() | ||
| .name('mearl') | ||
| .helpOption(false) | ||
| .allowExcessArguments(false) | ||
| .exitOverride() | ||
| .configureOutput({ writeErr: () => { }, writeOut: () => { } }) | ||
| .argument('<action>') | ||
| .option('--payload <json>') | ||
| .option('--payload-file <path>') | ||
| .option('--timeout <seconds>') | ||
| .option('--compact') | ||
| .option('--json') | ||
| .option('--output <path>') | ||
| .option('--browser <id-or-name>') | ||
| .option('--connector <id-or-name>') | ||
| .option('--local') | ||
| .option('--cdp') | ||
| .option('--server <url>'); | ||
| try { | ||
| program.parse(['node', 'mearl', ...args]); | ||
| } | ||
| catch (error) { | ||
| if (error instanceof CommanderError) { | ||
| throw new Error(error.message.replace(/^error:\s*/i, ''), { cause: error }); | ||
| } | ||
| throw error; | ||
| } | ||
| const action = program.processedArgs[0]; | ||
| const options = program.opts(); | ||
| const json = options.json === true; | ||
| if (json && action !== 'check') | ||
| throw new Error('--json 仅适用于 check'); | ||
| const payload = parsePayload(args); | ||
| const payload = parsePayload(options.payload, options.payloadFile); | ||
| const fallbackTimeout = resolveActionTimeoutSec(action, payload, 60); | ||
| const forceCdp = args.includes('--cdp'); | ||
| const local = forceCdp || args.includes('--local'); | ||
| const connector = optionValue(args, '--connector'); | ||
| const forceCdp = options.cdp === true; | ||
| const local = forceCdp || options.local === true; | ||
| const connector = options.connector; | ||
| if (local && connector) | ||
@@ -243,11 +239,12 @@ throw new Error('--local/--cdp 不能与 --connector 同时使用'); | ||
| payload, | ||
| timeoutSec: parsePositiveTimeout(optionValue(args, '--timeout'), fallbackTimeout), | ||
| compact: args.includes('--compact'), | ||
| timeoutSec: parsePositiveTimeout(options.timeout, fallbackTimeout), | ||
| ...(options.timeout !== undefined ? { timeoutOption: options.timeout } : {}), | ||
| compact: options.compact === true, | ||
| forceCdp, | ||
| json, | ||
| local, | ||
| ...(optionValue(args, '--output') ? { outputPath: optionValue(args, '--output') } : {}), | ||
| ...(optionValue(args, '--browser') ? { browser: optionValue(args, '--browser') } : {}), | ||
| ...(options.output ? { outputPath: options.output } : {}), | ||
| ...(options.browser ? { browser: options.browser } : {}), | ||
| ...(connector ? { connector } : {}), | ||
| ...resolveServer(args), | ||
| ...resolveServer(options.server), | ||
| }; | ||
@@ -313,3 +310,3 @@ } | ||
| try { | ||
| parsed = parseArgs(args); | ||
| parsed = parseClientArgs(args); | ||
| } | ||
@@ -321,3 +318,3 @@ catch (error) { | ||
| if (parsed.action === 'check') { | ||
| const timeoutSec = parseCheckTimeout(optionValue(args, '--timeout')); | ||
| const timeoutSec = parseCheckTimeout(parsed.timeoutOption); | ||
| if (timeoutSec === null) { | ||
@@ -324,0 +321,0 @@ console.error('--timeout 必须是正整数秒'); |
+21
-5
@@ -442,6 +442,6 @@ import { type BrowserCommandAction } from './generated-browser-action-protocol.js'; | ||
| readonly name: "copyCookieDomains"; | ||
| readonly type: "string[]"; | ||
| readonly description: "从控制浏览器复制到本地新实例或 AgentBay 新建/复用实例的 Cookie 域名列表,可与本地 TDBank 登录组合"; | ||
| readonly type: "string[] | \"mearl-services\""; | ||
| readonly description: "从控制浏览器复制 Cookie:传域名数组,或传 mearl-services 同步 Mearl 依赖平台登录态"; | ||
| }]; | ||
| readonly examples: ["browser_launch --payload '{\"name\":\"cloud-a\",\"provider\":\"agentbay\",\"userAgentMode\":\"desktop\",\"url\":\"https://example.com\"}'", "browser_launch --payload '{\"name\":\"cloud-public\",\"provider\":\"agentbay\",\"imageId\":\"browser_latest\",\"url\":\"https://example.com\"}'", "browser_launch --payload '{\"name\":\"cloud-login\",\"provider\":\"agentbay\",\"persistent\":true,\"copyCookieDomains\":[\"example.com\"],\"url\":\"https://example.com/account\"}'", "browser_launch --payload '{\"name\":\"cloud-login\",\"provider\":\"agentbay\",\"reuse\":\"require\",\"copyCookieDomains\":[\"example.com\"]}'", "browser_launch --payload '{\"name\":\"account-a\",\"headless\":true,\"accountId\":12345}'", "browser_launch --payload '{\"name\":\"figma\",\"userAgentMode\":\"desktop\",\"copyCookieDomains\":[\"figma.com\"]}'", "browser_launch --payload '{\"name\":\"account-b\",\"query\":\"test_account\",\"url\":\"https://www.taobao.com\"}'", "browser_launch --payload '{\"name\":\"with-cookies\",\"copyCookieDomains\":[\"example.com\",\"sub.example.org\"]}'"]; | ||
| readonly examples: ["browser_launch --payload '{\"name\":\"cloud-a\",\"provider\":\"agentbay\",\"userAgentMode\":\"desktop\",\"url\":\"https://example.com\"}'", "browser_launch --payload '{\"name\":\"cloud-public\",\"provider\":\"agentbay\",\"imageId\":\"browser_latest\",\"url\":\"https://example.com\"}'", "browser_launch --payload '{\"name\":\"cloud-login\",\"provider\":\"agentbay\",\"persistent\":true,\"copyCookieDomains\":[\"example.com\"],\"url\":\"https://example.com/account\"}'", "browser_launch --payload '{\"name\":\"cloud-login\",\"provider\":\"agentbay\",\"reuse\":\"require\",\"copyCookieDomains\":[\"example.com\"]}'", "browser_launch --payload '{\"name\":\"cloud-tools\",\"provider\":\"agentbay\",\"copyCookieDomains\":\"mearl-services\"}'", "browser_launch --payload '{\"name\":\"account-a\",\"headless\":true,\"accountId\":12345}'", "browser_launch --payload '{\"name\":\"figma\",\"userAgentMode\":\"desktop\",\"copyCookieDomains\":[\"figma.com\"]}'", "browser_launch --payload '{\"name\":\"account-b\",\"query\":\"test_account\",\"url\":\"https://www.taobao.com\"}'", "browser_launch --payload '{\"name\":\"with-cookies\",\"copyCookieDomains\":[\"example.com\",\"sub.example.org\"]}'"]; | ||
| }, { | ||
@@ -514,2 +514,6 @@ readonly name: "browser_close"; | ||
| readonly description: "与 appProfile 配合;是否模拟顶部状态/导航栏与底部手势栏,默认 true"; | ||
| }, { | ||
| readonly name: "bridgeConfig"; | ||
| readonly type: "object"; | ||
| readonly description: "与 appProfile 配合;声明式覆盖桥方法,形如 {methods:{api:{behavior|response|noop,silent?}}}"; | ||
| }]; | ||
@@ -547,4 +551,12 @@ 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\":\"验证任务\"}'"]; | ||
| readonly type: "{ x: number; y: number }"; | ||
| readonly description: "按视口坐标点击(CSS 像素)。selector/text 都不适用的兜底场景"; | ||
| readonly description: "按视口坐标点击(CSS 像素)。默认使用 outer-viewport;容器或 iframe 内坐标配合 coordinateSpace: \"active-frame\""; | ||
| }, { | ||
| readonly name: "frameId"; | ||
| readonly type: "string"; | ||
| readonly description: "浏览器扩展后端:显式指定 CSS/text 查询或 active-frame 坐标所属的 frame;省略时容器模式自动使用当前业务 frame"; | ||
| }, { | ||
| readonly name: "coordinateSpace"; | ||
| readonly type: "\"outer-viewport\" | \"active-frame\""; | ||
| readonly description: "仅配合 point 使用;默认 outer-viewport;浏览器扩展后端的 active-frame 会换算 iframe 偏移与缩放后派发可信输入"; | ||
| }, { | ||
| readonly name: "role"; | ||
@@ -571,3 +583,3 @@ readonly type: "string"; | ||
| }]; | ||
| readonly examples: ["page_click --payload '{\"text\":\"提交\"}'", "page_click --payload '{\"text\":\"退款明细\",\"role\":\"button\"}'", "page_click --payload '{\"text\":\"确定\",\"scope\":\"@e12\"}'", "page_click --payload '{\"selector\":\"@e3\"}'", "page_click --payload '{\"selector\":\".submit-btn\"}'", "page_click --payload '{\"selector\":\".gesture-btn\",\"clickMode\":\"touch\"}'", "page_click --payload '{\"point\":{\"x\":336,\"y\":117}}'"]; | ||
| readonly examples: ["page_click --payload '{\"text\":\"提交\"}'", "page_click --payload '{\"text\":\"退款明细\",\"role\":\"button\"}'", "page_click --payload '{\"text\":\"确定\",\"scope\":\"@e12\"}'", "page_click --payload '{\"selector\":\"@e3\"}'", "page_click --payload '{\"selector\":\".submit-btn\"}'", "page_click --payload '{\"selector\":\".gesture-btn\",\"clickMode\":\"touch\"}'", "page_click --payload '{\"point\":{\"x\":336,\"y\":117}}'", "page_click --payload '{\"point\":{\"x\":196,\"y\":240},\"coordinateSpace\":\"active-frame\"}'"]; | ||
| }, { | ||
@@ -976,2 +988,6 @@ readonly name: "page_drag"; | ||
| }, { | ||
| readonly name: "bridgeConfig"; | ||
| readonly type: "object"; | ||
| readonly description: "声明式覆盖桥方法,形如 {methods:{api:{behavior|response|noop,silent?}}};不执行配置脚本"; | ||
| }, { | ||
| readonly name: "reload"; | ||
@@ -978,0 +994,0 @@ readonly type: "boolean"; |
+25
-3
@@ -328,4 +328,4 @@ import { APP_PROFILE_NAMES, } from './generated-browser-action-protocol.js'; | ||
| name: 'copyCookieDomains', | ||
| type: 'string[]', | ||
| description: '从控制浏览器复制到本地新实例或 AgentBay 新建/复用实例的 Cookie 域名列表,可与本地 TDBank 登录组合', | ||
| type: 'string[] | "mearl-services"', | ||
| description: '从控制浏览器复制 Cookie:传域名数组,或传 mearl-services 同步 Mearl 依赖平台登录态', | ||
| }, | ||
@@ -338,2 +338,3 @@ ], | ||
| `browser_launch --payload '{"name":"cloud-login","provider":"agentbay","reuse":"require","copyCookieDomains":["example.com"]}'`, | ||
| `browser_launch --payload '{"name":"cloud-tools","provider":"agentbay","copyCookieDomains":"mearl-services"}'`, | ||
| `browser_launch --payload '{"name":"account-a","headless":true,"accountId":12345}'`, | ||
@@ -425,2 +426,7 @@ `browser_launch --payload '{"name":"figma","userAgentMode":"desktop","copyCookieDomains":["figma.com"]}'`, | ||
| }, | ||
| { | ||
| name: 'bridgeConfig', | ||
| type: 'object', | ||
| description: '与 appProfile 配合;声明式覆盖桥方法,形如 {methods:{api:{behavior|response|noop,silent?}}}', | ||
| }, | ||
| ], | ||
@@ -467,5 +473,15 @@ examples: [ | ||
| type: '{ x: number; y: number }', | ||
| description: '按视口坐标点击(CSS 像素)。selector/text 都不适用的兜底场景', | ||
| description: '按视口坐标点击(CSS 像素)。默认使用 outer-viewport;容器或 iframe 内坐标配合 coordinateSpace: "active-frame"', | ||
| }, | ||
| { | ||
| name: 'frameId', | ||
| type: 'string', | ||
| description: '浏览器扩展后端:显式指定 CSS/text 查询或 active-frame 坐标所属的 frame;省略时容器模式自动使用当前业务 frame', | ||
| }, | ||
| { | ||
| name: 'coordinateSpace', | ||
| type: '"outer-viewport" | "active-frame"', | ||
| description: '仅配合 point 使用;默认 outer-viewport;浏览器扩展后端的 active-frame 会换算 iframe 偏移与缩放后派发可信输入', | ||
| }, | ||
| { | ||
| name: 'role', | ||
@@ -502,2 +518,3 @@ type: 'string', | ||
| `page_click --payload '{"point":{"x":336,"y":117}}'`, | ||
| `page_click --payload '{"point":{"x":196,"y":240},"coordinateSpace":"active-frame"}'`, | ||
| ], | ||
@@ -878,2 +895,7 @@ }, | ||
| }, | ||
| { | ||
| name: 'bridgeConfig', | ||
| type: 'object', | ||
| description: '声明式覆盖桥方法,形如 {methods:{api:{behavior|response|noop,silent?}}};不执行配置脚本', | ||
| }, | ||
| { name: 'reload', type: 'boolean', description: '应用/清除后是否重载页面,默认 true' }, | ||
@@ -880,0 +902,0 @@ { name: 'tabId', type: 'number', required: true, description: '目标标签页 ID' }, |
@@ -222,4 +222,4 @@ export interface GetRequestsParams { | ||
| tabId?: number; | ||
| /** Copy matching cookies from the selected control browser into a new or reused instance. */ | ||
| copyCookieDomains?: string[]; | ||
| /** Copy explicit domains or the fixed Mearl-service preset from the control browser. */ | ||
| copyCookieDomains?: string[] | 'mearl-services'; | ||
| } | ||
@@ -275,2 +275,14 @@ export interface BrowserCloseParams { | ||
| } | ||
| export type AppBridgeConfigBehavior = 'navigate' | 'memoryGet' | 'memorySet' | 'memoryDelete' | 'availabilityMap' | 'userInfo' | 'networkInfo' | 'geolocation' | 'permission' | 'prefetch' | 'fptTrack' | 'mtopStream' | 'previewFile' | 'toast' | 'unsupportedNativeInput' | 'payment' | 'userTrack' | 'calendar' | 'networkQualityMonitor' | 'beaconRequest'; | ||
| /** 单个桥方法的声明式覆盖;行为、固定回包、成功空实现三选一。 */ | ||
| export interface AppBridgeMethodConfig { | ||
| behavior?: AppBridgeConfigBehavior; | ||
| response?: unknown; | ||
| noop?: boolean; | ||
| silent?: boolean; | ||
| } | ||
| /** 追加或覆盖 App 档案中的桥方法。配置保持 JSON 可序列化,不执行配置提供的代码。 */ | ||
| export interface AppBridgeConfig { | ||
| methods: Record<string, AppBridgeMethodConfig>; | ||
| } | ||
| export interface TabOpenParams { | ||
@@ -302,2 +314,4 @@ url: string; | ||
| simulateSystemBars?: boolean; | ||
| /** 与 appProfile 配合使用;按方法覆盖档案内置桥配置 */ | ||
| bridgeConfig?: AppBridgeConfig; | ||
| } | ||
@@ -325,2 +339,4 @@ /** | ||
| simulateSystemBars?: boolean; | ||
| /** 按方法覆盖档案内置桥配置,仅支持受控行为、固定 JSON 回包和成功空实现 */ | ||
| bridgeConfig?: AppBridgeConfig; | ||
| /** 覆盖档案推荐的设备模拟参数(视口/UA 默认取档案值) */ | ||
@@ -357,2 +373,6 @@ emulation?: BrowserEmulationOptions; | ||
| }; | ||
| /** CSS/text 查询的目标 frame;省略时容器模式自动使用当前业务 frame。 */ | ||
| frameId?: string; | ||
| /** point 的坐标空间;默认 outer-viewport 以保持兼容。 */ | ||
| coordinateSpace?: 'outer-viewport' | 'active-frame'; | ||
| role?: string; | ||
@@ -359,0 +379,0 @@ exact?: boolean; |
+6
-2
| { | ||
| "name": "@mearl/client", | ||
| "version": "2.8.2", | ||
| "version": "2.9.0", | ||
| "description": "Unified Mearl SDK & CLI for local and remote browsers", | ||
@@ -48,2 +48,5 @@ "type": "module", | ||
| "license": "ISC", | ||
| "engines": { | ||
| "node": ">=22.12.0" | ||
| }, | ||
| "publishConfig": { | ||
@@ -53,5 +56,6 @@ "registry": "https://registry.npmjs.org" | ||
| "dependencies": { | ||
| "commander": "^15.0.0", | ||
| "fastest-levenshtein": "^1.0.16", | ||
| "ws": "^8.18.0", | ||
| "@mearl/cloud-types": "2.8.2" | ||
| "@mearl/cloud-types": "2.9.0" | ||
| }, | ||
@@ -58,0 +62,0 @@ "devDependencies": { |
+3
-2
@@ -72,2 +72,3 @@ # @mearl/client | ||
| mearl browser_launch --browser '<控制浏览器 browserId>' --payload '{"name":"cloud-login","provider":"agentbay","reuse":"require","copyCookieDomains":["example.com"]}' | ||
| mearl browser_launch --payload '{"name":"cloud-tools","provider":"agentbay","copyCookieDomains":"mearl-services"}' | ||
| mearl browser_launch --payload '{"name":"figma","userAgentMode":"desktop","copyCookieDomains":["figma.com"]}' | ||
@@ -115,3 +116,3 @@ mearl browser_list | ||
| 重复文本点击可用 `page_click.scope` 限定 CSS / ref 子树;ref 指向滚动容器内的子节点时,可用 `page_scroll.containerPolicy: "nearest"` 自动解析最近可滚动祖先。点击结果的 `resolvedTarget` 和滚动结果的前后位置、边界字段可用于诊断实际目标与效果。 | ||
| 重复文本点击可用 `page_click.scope` 限定 CSS / ref 子树;浏览器扩展后端中,容器或 iframe 内的点坐标使用 `coordinateSpace: "active-frame"`,CSS/text 查询可用 `frameId` 显式指定 frame。点击结果通过 `requestedTarget`、`resolvedTarget`、`dispatchTarget`、`hitTarget` 和 `coordinates` 区分请求语义、DOM 解析、派发节点与可信输入实际命中;ref 指向滚动容器内的子节点时,可用 `page_scroll.containerPolicy: "nearest"` 自动解析最近可滚动祖先。 | ||
@@ -122,3 +123,3 @@ `browser_list` 统一列出普通浏览器和托管浏览器。`type` 区分 `regular` / `managed`,`status` 区分 `connected` / `running_disconnected` / `stopped`;只有 `connected` 的浏览器可作为操作目标。 | ||
| 托管浏览器使用独立 Profile。控制浏览器需先登录 TDBank;生成的 SSO 地址在本地内部传递,本地实例可以使用 `headless: true`(默认)完成测试账号登录。AgentBay 固定为非 headless,传 `headless: true` 会被拒绝;`imageId` 可指定镜像别名或具体镜像 ID,省略时使用已验证的 Linux Browser Use 内网镜像。`persistent: true` 会让本地实例保留 Profile,让 AgentBay 同名实例绑定稳定的云端 Browser Context;正常关闭时同步 Cookie、LocalStorage、IndexedDB 等状态,下次启动无需再次复制本地登录态。`deleteProfile: true` 会显式删除对应 Profile 或 Context。AgentBay 的 `reuse` 支持 `never`(默认)、`prefer` 和 `require`:`prefer` 存在运行实例时复用、否则创建,`require` 只复用且不会意外创建计费会话。`copyCookieDomains` 可在首次启动或复用运行实例时把控制浏览器指定域的 Cookie 写入目标,Cookie 值不会经过 cloud-server,也不会出现在命令结果或日志中;`userAgentMode: "desktop"` 让本地实例使用匹配本机 Chrome 版本的桌面 UA,AgentBay 则复用当前控制浏览器的桌面 UA。`browser_list` 仅通过 `agentbayImageId`、`agentbayContextId` 和 `agentbayContextName` 返回 AgentBay 实例的稳定配置。无影浏览器串流入口包含临时访问凭据且会过期,因此不会写入实例记录或列表结果;需要时运行 `mearl check --browser <id|名称>` 实时获取,并按敏感信息处理。完整设计见 [托管浏览器与 TDBank 多账号设计](../../docs/managed-browsers.md)。 | ||
| 托管浏览器使用独立 Profile。控制浏览器需先登录 TDBank;生成的 SSO 地址在本地内部传递,本地实例可以使用 `headless: true`(默认)完成测试账号登录。AgentBay 固定为非 headless,传 `headless: true` 会被拒绝;`imageId` 可指定镜像别名或具体镜像 ID,省略时使用已验证的 Linux Browser Use 内网镜像。`persistent: true` 会让本地实例保留 Profile,让 AgentBay 同名实例绑定稳定的云端 Browser Context;正常关闭时同步 Cookie、LocalStorage、IndexedDB 等状态,下次启动无需再次复制本地登录态。`deleteProfile: true` 会显式删除对应 Profile 或 Context。AgentBay 的 `reuse` 支持 `never`(默认)、`prefer` 和 `require`:`prefer` 存在运行实例时复用、否则创建,`require` 只复用且不会意外创建计费会话。`copyCookieDomains` 传数组时把控制浏览器指定域的 Cookie 写入目标,传 `"mearl-services"` 时同步 Mearl 依赖平台登录态。Cookie 值不会经过 cloud-server,也不会出现在命令结果或日志中;`userAgentMode: "desktop"` 让本地实例使用匹配本机 Chrome 版本的桌面 UA,AgentBay 则复用当前控制浏览器的桌面 UA。`browser_list` 仅通过 `agentbayImageId`、`agentbayContextId` 和 `agentbayContextName` 返回 AgentBay 实例的稳定配置。无影浏览器串流入口包含临时访问凭据且会过期,因此不会写入实例记录或列表结果;需要时运行 `mearl check --browser <id|名称>` 实时获取,并按敏感信息处理。完整设计见 [托管浏览器与 TDBank 多账号设计](../../docs/managed-browsers.md)。 | ||
@@ -125,0 +126,0 @@ ## 架构 |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
258224
1.87%5639
1.31%145
0.69%16
-5.88%4
33.33%+ Added
+ Added
+ Added
- Removed
Updated