@mearl/client 2.9.6 → 2.11.0

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
@@ -69,7 +69,9 @@ mearl browser_launch --payload '{"name":"account-b","query":"test_account"}'
69
69
  mearl browser_launch --payload '{"name":"cookie-copy","copyCookieDomains":["example.com"]}'
70
70
  mearl browser_launch --payload '{"name":"cloud-login","provider":"agentbay","persistent":true,"userAgentMode":"desktop","copyCookieDomains":["example.com"],"url":"https://example.com/account"}'
71
71
  mearl browser_launch --browser '<控制浏览器 browserId>' --payload '{"name":"cloud-login","provider":"agentbay","reuse":"require","copyCookieDomains":["example.com"]}'
72
+ mearl browser_launch --browser '<本地来源 browserId>' --payload '{"sessionId":"<其他设备创建的 AgentBay sessionId>","copyCookieDomains":["example.com"]}'
72
73
  mearl browser_launch --payload '{"name":"cloud-tools","provider":"agentbay","copyCookieDomains":"mearl-services"}'
74
+ mearl browser_launch --payload '{"name":"cloud-tools-plus","provider":"agentbay","copyCookieDomains":{"presets":["mearl-services"],"domains":["figma.com"]}}'
73
75
  mearl browser_launch --payload '{"name":"figma","userAgentMode":"desktop","copyCookieDomains":["figma.com"]}'
74
76
  mearl browser_list
75
77
  mearl browser_release
@@ -120,9 +122,9 @@ mearl browser_release
120
122
 
121
123
  `browser_list` 统一列出普通浏览器和托管浏览器。`type` 区分 `regular` / `managed`,`status` 区分 `connected` / `running_disconnected` / `stopped`;只有 `connected` 的浏览器可作为操作目标。
122
124
 
123
- `browser_release` 仅在用户明确要求结束调试、退出接管或清理会话资源时使用,不作为普通任务收尾。它会释放当前目标浏览器的 debugger/CDP 控制和会话级临时状态(包括设备/时区模拟),保留浏览器及全部标签页;后续浏览器操作可自动重新建立控制。
125
+ 按 `sessionId` 接入的 AgentBay 浏览器用 `browser_release` 收尾:它会移除本机 attachment 并退出对应宿主,但不会关闭云端 Session。其他浏览器仅在用户明确要求结束调试、退出接管或清理会话资源时调用;它会释放 debugger/CDP 控制和会话级临时状态(包括设备/时区模拟),保留浏览器及全部标签页,后续浏览器操作可自动重新建立控制。
124
126
 
125
- 托管浏览器使用独立 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)。
127
+ 托管浏览器使用独立 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` 只复用且不会意外创建计费会话。另一台设备已知确切 Session 时,可传 `sessionId` 直接通过 AgentBay CDP 接入,不需要 cloud-server;当前设备需配置能访问该 Session 的 `AGENTBAY_KEY`。provider-local ID 为 `managed:agentbay:session:<sessionId>`,跨 CLI 调用使用启动响应中的全局 `browserId`,且本机只持有 attachment。`copyCookieDomains` 传数组时复制指定域,传 `"mearl-services"` 时同步 Mearl 依赖平台登录态;需要同时复制基础登录态和额外站点时传 `{ "presets": ["mearl-services"], "domains": [...] }`。Cookie 值不会经过 cloud-server,也不会出现在命令结果或日志中;`userAgentMode: "desktop"` 让本地实例使用匹配本机 Chrome 版本的桌面 UA,AgentBay 则复用当前控制浏览器的桌面 UA。`browser_list` 仅返回浏览器的稳定配置,不携带 AgentBay 串流入口或沙箱 noVNC 地址。需要时显式运行 `mearl check --browser <id|名称>`,由统一的浏览器访问入口能力实时返回 `browserAccess`;AgentBay、OpenSandbox 和 AONE Sandbox 地址都应按敏感信息处理。完整设计见 [托管浏览器与 TDBank 多账号设计](../../docs/managed-browsers.md)。
126
128
 
127
129
  ## 架构
128
130
 
package/dist/check.d.ts CHANGED
@@ -5,6 +5,16 @@ export interface UnifiedCheckVersions {
5
5
  extension?: string | null;
6
6
  browser?: string | null;
7
7
  }
8
+ export type BrowserAccessCapability = {
9
+ kind: 'agentbay';
10
+ } | {
11
+ kind: 'sandbox-novnc';
12
+ environment: 'opensandbox' | 'aone';
13
+ port: number;
14
+ };
15
+ export type BrowserAccess = BrowserAccessCapability & {
16
+ url: string;
17
+ };
8
18
  interface BrowserInfo {
9
19
  browserId: string;
10
20
  extensionVersion?: string;
@@ -27,7 +37,7 @@ export interface ExtensionInstallationInfo {
27
37
  setupManagedPath: string | null;
28
38
  }
29
39
  export interface UnifiedCheckBrowserResult extends BrowserInfo {
30
- agentbayAccessUrl?: string;
40
+ browserAccess?: BrowserAccess;
31
41
  check?: {
32
42
  errors?: string[];
33
43
  label: string;
package/dist/check.js CHANGED
@@ -2,6 +2,20 @@ import { UnifiedClient, normalizeBrowserSelector, } from './unified.js';
2
2
  function formatError(error) {
3
3
  return error instanceof Error ? error.message : String(error);
4
4
  }
5
+ function browserAccessLabel(access) {
6
+ if (access.kind === 'agentbay')
7
+ return 'AgentBay, temporary';
8
+ return access.environment === 'opensandbox' ? 'OpenSandbox noVNC' : 'AONE Sandbox noVNC';
9
+ }
10
+ async function getBrowserAccess(client, browser, capability, timeoutSec) {
11
+ const result = (await client.invoke('get_browser_access_url', {}, { timeoutSec, browser: browser.browserId }));
12
+ if (!result.browserAccess?.url)
13
+ throw new Error('Browser host did not return an access URL');
14
+ if (result.browserAccess.kind !== capability.kind) {
15
+ throw new Error('Browser host returned an unexpected access URL type');
16
+ }
17
+ return result.browserAccess;
18
+ }
5
19
  function redactServerUrl(serverUrl) {
6
20
  try {
7
21
  const parsed = new URL(serverUrl);
@@ -223,33 +237,27 @@ export async function runUnifiedCheck(options, dependencies = {}) {
223
237
  setupManagedPath: versions.setupManagedPath,
224
238
  };
225
239
  }
226
- let agentbayAccessUrl;
240
+ let browserAccess;
227
241
  let accessUrlWarning;
228
- if (browser.provider === 'agentbay' && options.browser) {
242
+ const browserAccessCapability = versions.browserAccess;
243
+ if (browserAccessCapability && options.browser) {
229
244
  try {
230
- const access = (await client.invoke('get_agentbay_access_url', {}, {
231
- timeoutSec,
232
- browser: browser.browserId,
233
- }));
234
- if (!access.agentbayAccessUrl) {
235
- throw new Error('AgentBay did not return a browser access URL');
236
- }
237
- agentbayAccessUrl = access.agentbayAccessUrl;
238
- details.push(`AgentBay access URL (temporary, sensitive): ${access.agentbayAccessUrl}`);
245
+ browserAccess = await getBrowserAccess(client, browser, browserAccessCapability, timeoutSec);
246
+ details.push(`Browser access URL (${browserAccessLabel(browserAccess)}; sensitive): ${browserAccess.url}`);
239
247
  }
240
248
  catch (error) {
241
249
  accessUrlWarning = formatError(error);
242
- details.push(`AgentBay access URL unavailable: ${accessUrlWarning}`);
250
+ details.push(`Browser access URL unavailable: ${accessUrlWarning}`);
243
251
  }
244
252
  }
245
253
  browserChecks.push({
246
- ...(agentbayAccessUrl ? { agentbayAccessUrl } : {}),
254
+ ...(browserAccess ? { browserAccess } : {}),
247
255
  browser,
248
256
  ...(extensionInstallation ? { extensionInstallation } : {}),
249
257
  item: {
250
258
  status: accessUrlWarning ? 'warning' : 'ok',
251
259
  label: accessUrlWarning
252
- ? 'Browser command path ready; AgentBay access URL unavailable'
260
+ ? 'Browser command path ready; browser access URL unavailable'
253
261
  : options.transport === 'cdp'
254
262
  ? 'CDP command path'
255
263
  : 'Browser command path',
@@ -293,7 +301,7 @@ export async function runUnifiedCheck(options, dependencies = {}) {
293
301
  return {
294
302
  ...browserIdentity,
295
303
  selected: targets.some(target => target.browserId === browser.browserId),
296
- ...(check?.agentbayAccessUrl ? { agentbayAccessUrl: check.agentbayAccessUrl } : {}),
304
+ ...(check?.browserAccess ? { browserAccess: check.browserAccess } : {}),
297
305
  ...(check?.item
298
306
  ? {
299
307
  check: {
@@ -386,21 +386,24 @@ export declare const COMMANDS: readonly [{
386
386
  readonly examples: ["browser_list"];
387
387
  }, {
388
388
  readonly name: "browser_release";
389
- readonly description: "按用户明确要求释放调试控制和会话级临时效果(含设备/时区模拟),保留浏览器和全部标签页;不用于普通任务收尾";
390
- readonly summary: "释放浏览器调试控制及会话级临时效果";
389
+ readonly description: "sessionId 接入的 AgentBay 浏览器用它解除本机 attachment且不关闭云端 Session;其他浏览器仅按用户明确要求释放调试控制和会话级临时效果(含设备/时区模拟),不用于普通任务收尾";
390
+ readonly summary: "释放浏览器控制或解除 Session attachment";
391
391
  readonly examples: ["browser_release", "browser_release --browser chrome-a"];
392
392
  }, {
393
393
  readonly name: "browser_launch";
394
- readonly description: "启动本地 Chrome,或创建/显式复用 AgentBay 云浏览器,并通过 CDP 接入 Mearl";
394
+ readonly description: "启动本地 Chrome、创建/复用同名 AgentBay 云浏览器,或用 sessionId 接入现有云端 Session";
395
395
  readonly params: [{
396
396
  readonly name: "name";
397
397
  readonly type: "string";
398
- readonly required: true;
399
- readonly description: "实例名称,最多 64 个字符";
398
+ readonly description: "实例名称,创建浏览器时必填;按 sessionId 接入时可省略";
400
399
  }, {
401
400
  readonly name: "provider";
402
401
  readonly type: "\"local\" | \"agentbay\"";
403
- readonly description: "运行环境,默认 local;agentbay 从 AGENTBAY_KEY 创建云浏览器";
402
+ readonly description: "运行环境,默认 local;传 sessionId 时自动使用 agentbay,否则 agentbay 从 AGENTBAY_KEY 创建云浏览器";
403
+ }, {
404
+ readonly name: "sessionId";
405
+ readonly type: "string";
406
+ readonly description: "显式接入现有 AgentBay Session;不会创建、初始化或取得其生命周期所有权";
404
407
  }, {
405
408
  readonly name: "reuse";
406
409
  readonly type: "\"never\" | \"prefer\" | \"require\"";
@@ -451,10 +454,10 @@ export declare const COMMANDS: readonly [{
451
454
  readonly description: "CDP 控制浏览器中用于读取 TDBank Cookie 的标签页";
452
455
  }, {
453
456
  readonly name: "copyCookieDomains";
454
- readonly type: "string[] | \"mearl-services\"";
455
- readonly description: "从控制浏览器复制 Cookie:传域名数组,或传 mearl-services 同步 Mearl 依赖平台登录态";
457
+ readonly type: "string[] | \"mearl-services\" | { presets?: [\"mearl-services\"]; domains?: string[] }";
458
+ readonly description: "从控制浏览器复制 Cookie:可传域名数组、Mearl 登录态预设,或组合预设与额外域名";
456
459
  }];
457
- 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\"]}'"];
460
+ 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 --browser '<本地浏览器 browserId>' --payload '{\"sessionId\":\"<AgentBay sessionId>\",\"copyCookieDomains\":[\"example.com\"]}'", "browser_launch --payload '{\"name\":\"cloud-tools\",\"provider\":\"agentbay\",\"copyCookieDomains\":\"mearl-services\"}'", "browser_launch --payload '{\"name\":\"cloud-tools\",\"provider\":\"agentbay\",\"copyCookieDomains\":{\"presets\":[\"mearl-services\"],\"domains\":[\"figma.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\"]}'"];
458
461
  }, {
459
462
  readonly name: "browser_close";
460
463
  readonly description: "关闭指定托管浏览器并按策略保留或清理本地 Profile / AgentBay Context";
@@ -509,25 +512,21 @@ export declare const COMMANDS: readonly [{
509
512
  }, {
510
513
  readonly name: "emulation";
511
514
  readonly type: "object";
512
- readonly description: "开 tab 即进入移动端模拟态(首屏即移动态、无需 reload)。字段同 set_device_emulation 去掉 enabled,如 {\"preset\":\"iphone-15-pro\"}";
515
+ readonly description: "开 tab 即进入移动端模拟态(首屏即移动态、无需 reload)。字段同 set_device_emulation 去掉 enabled/reload;deviceShell=true 时页面从首屏起在 iframe WebView 中渲染设备系统外壳";
513
516
  }, {
514
517
  readonly name: "appProfile";
515
518
  readonly type: "string";
516
- readonly description: "开 tab 即进入 App 容器模拟态(客户端 UA + JSBridge 托管 + iframe 容器栈 + 自适应系统标题栏,首屏即容器态)。系统栏由 iframe 外层统一渲染,并根据页面协议与桥调用切换普通、沉浸或隐藏状态;档案名同 set_app_profile,如 \"fliggy\"";
519
+ readonly description: "开 tab 即注入 App 客户端环境与 JSBridge;档案名同 set_app_profile,如 \"fliggy\"。需要 iframe WebView 和系统栏时同时传 emulation.deviceShell=true";
517
520
  }, {
518
521
  readonly name: "os";
519
522
  readonly type: "\"ios\" | \"android\"";
520
523
  readonly description: "与 appProfile 配合;容器系统,默认 ios";
521
- }, {
522
- readonly name: "simulateSystemBars";
523
- readonly type: "boolean";
524
- readonly description: "与 appProfile 配合;是否模拟顶部状态/导航栏与底部手势栏,默认 true";
525
524
  }, {
526
525
  readonly name: "bridgeConfig";
527
526
  readonly type: "object";
528
527
  readonly description: "与 appProfile 配合;声明式覆盖桥方法,形如 {methods:{api:{behavior|response|noop,silent?}}}";
529
528
  }];
530
- 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\":\"验证任务\"}'"];
529
+ 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-17-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\":\"验证任务\"}'"];
531
530
  }, {
532
531
  readonly name: "tab_close";
533
532
  readonly description: "关闭指定标签页";
@@ -912,7 +911,7 @@ export declare const COMMANDS: readonly [{
912
911
  readonly examples: ["record_stop"];
913
912
  }, {
914
913
  readonly name: "set_device_emulation";
915
- readonly description: "把目标 tab 切换为移动端模拟(视口/UA/触摸),等价 DevTools 设备工具栏,作用域 per-tab;需保持调试控制才持续生效";
914
+ readonly description: "把目标 tab 切换为移动端模拟(视口/DPR/UA/触摸/视觉比例),等价 DevTools 设备工具栏,作用域 per-tab;需保持调试控制才持续生效";
916
915
  readonly summary: "设置或清除目标标签页的设备模拟";
917
916
  readonly params: [{
918
917
  readonly name: "enabled";
@@ -921,8 +920,8 @@ export declare const COMMANDS: readonly [{
921
920
  readonly description: "true=启用,false=清除";
922
921
  }, {
923
922
  readonly name: "preset";
924
- readonly type: "\"iphone-15-pro\" | \"iphone-se\" | \"pixel-8\" | \"ipad-mini\" | \"galaxy-s23\"";
925
- readonly description: "内置设备预设,提供 width/height/DPR/UA 默认值";
923
+ readonly type: string;
924
+ readonly description: "内置设备预设,提供 width/height/DPR/UA 默认值;旧机型 ID 继续兼容";
926
925
  }, {
927
926
  readonly name: "width";
928
927
  readonly type: "number";
@@ -935,6 +934,10 @@ export declare const COMMANDS: readonly [{
935
934
  readonly name: "deviceScaleFactor";
936
935
  readonly type: "number";
937
936
  readonly description: "DPR(覆盖 preset)";
937
+ }, {
938
+ readonly name: "scale";
939
+ readonly type: "number";
940
+ readonly description: "画面视觉比例,不改变视口尺寸或 DPR;必须为有限正数,默认 1";
938
941
  }, {
939
942
  readonly name: "mobile";
940
943
  readonly type: "boolean";
@@ -950,7 +953,11 @@ export declare const COMMANDS: readonly [{
950
953
  }, {
951
954
  readonly name: "touch";
952
955
  readonly type: "boolean";
953
- readonly description: "是否启用触摸事件,默认 mobile 为真时启用";
956
+ readonly description: "是否启用触摸事件,默认从 preset 推断;无 preset 时跟随 mobile";
957
+ }, {
958
+ readonly name: "deviceShell";
959
+ readonly type: "boolean";
960
+ readonly description: "是否使用 iframe WebView 并渲染顶部状态栏与底部操作区,默认 false";
954
961
  }, {
955
962
  readonly name: "reload";
956
963
  readonly type: "boolean";
@@ -961,10 +968,10 @@ export declare const COMMANDS: readonly [{
961
968
  readonly required: true;
962
969
  readonly description: "目标标签页 ID";
963
970
  }];
964
- 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}'"];
971
+ readonly examples: ["set_device_emulation --payload '{\"enabled\":true,\"preset\":\"iphone-17-pro\"}'", "set_device_emulation --payload '{\"enabled\":true,\"preset\":\"pixel-11\",\"orientation\":\"landscape\"}'", "set_device_emulation --payload '{\"enabled\":false}'"];
965
972
  }, {
966
973
  readonly name: "set_app_profile";
967
- readonly description: "把目标 tab 切换为 App 容器模拟态(客户端 UA + JSBridge 托管 + 页面跳转 iframe 容器栈 + scheme 唤端映射 + 自适应系统标题栏),系统栏由 iframe 外层统一渲染,并根据页面协议与桥调用切换普通、沉浸或隐藏状态;用于桌面浏览器近似实机验证 App 内 H5,需保持调试控制才持续生效";
974
+ readonly description: "设置 App 客户端环境与 JSBridge;传 emulation 时由同一个命令联动设备模拟,省略时不改变设备状态";
968
975
  readonly summary: "设置或清除目标标签页的 App 容器模拟";
969
976
  readonly params: [{
970
977
  readonly name: "enabled";
@@ -987,18 +994,14 @@ export declare const COMMANDS: readonly [{
987
994
  readonly name: "deviceModel";
988
995
  readonly type: "string";
989
996
  readonly description: "Android 机型名(拼入 UA 与 __DEVICE_MODEL__)";
990
- }, {
991
- readonly name: "simulateSystemBars";
992
- readonly type: "boolean";
993
- readonly description: "是否模拟顶部状态/导航栏与底部手势栏,默认 true";
994
- }, {
995
- readonly name: "emulation";
996
- readonly type: "object";
997
- readonly description: "覆盖档案推荐的设备模拟参数,字段同 set_device_emulation 去掉 enabled";
998
997
  }, {
999
998
  readonly name: "bridgeConfig";
1000
999
  readonly type: "object";
1001
1000
  readonly description: "声明式覆盖桥方法,形如 {methods:{api:{behavior|response|noop,silent?}}};不执行配置脚本";
1001
+ }, {
1002
+ readonly name: "emulation";
1003
+ readonly type: "object";
1004
+ readonly description: "可选设备联动参数,字段同 set_device_emulation 去掉 enabled/reload;字段覆盖档案推荐设备,deviceShell 默认 true。省略时不设置或清除设备";
1002
1005
  }, {
1003
1006
  readonly name: "reload";
1004
1007
  readonly type: "boolean";
@@ -1009,7 +1012,7 @@ export declare const COMMANDS: readonly [{
1009
1012
  readonly required: true;
1010
1013
  readonly description: "目标标签页 ID";
1011
1014
  }];
1012
- 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}'"];
1015
+ readonly examples: ["set_app_profile --payload '{\"enabled\":true,\"profile\":\"fliggy\"}'", "set_app_profile --payload '{\"enabled\":true,\"profile\":\"fliggy\",\"emulation\":{}}'", "set_app_profile --payload '{\"enabled\":true,\"profile\":\"alipay\",\"emulation\":{\"preset\":\"pixel-11\",\"deviceShell\":true}}'", "set_app_profile --payload '{\"enabled\":false}'", "set_app_profile --payload '{\"enabled\":false,\"emulation\":{}}'"];
1013
1016
  }, {
1014
1017
  readonly name: "set_timezone";
1015
1018
  readonly description: "覆盖目标 tab 的时区,传空字符串恢复浏览器默认时区";
package/dist/commands.js CHANGED
@@ -1,6 +1,7 @@
1
- import { APP_PROFILE_NAMES, } from './generated-browser-action-protocol.js';
1
+ import { APP_PROFILE_NAMES, BROWSER_DEVICE_PRESET_NAMES, } from './generated-browser-action-protocol.js';
2
2
  /** 容器档案名列表(拼进参数说明,新增档案无需改本文件) */
3
3
  const APP_PROFILE_NAME_LIST = APP_PROFILE_NAMES.join('/');
4
+ const DEVICE_PRESET_TYPE = BROWSER_DEVICE_PRESET_NAMES.map(name => JSON.stringify(name)).join(' | ');
4
5
  const PAGE_DIAGNOSTICS_PARAM = {
5
6
  name: 'diagnostics',
6
7
  type: 'object | boolean',
@@ -289,19 +290,28 @@ export const COMMANDS = [
289
290
  },
290
291
  {
291
292
  name: 'browser_release',
292
- description: '按用户明确要求释放调试控制和会话级临时效果(含设备/时区模拟),保留浏览器和全部标签页;不用于普通任务收尾',
293
- summary: '释放浏览器调试控制及会话级临时效果',
293
+ description: 'sessionId 接入的 AgentBay 浏览器用它解除本机 attachment且不关闭云端 Session;其他浏览器仅按用户明确要求释放调试控制和会话级临时效果(含设备/时区模拟),不用于普通任务收尾',
294
+ summary: '释放浏览器控制或解除 Session attachment',
294
295
  examples: ['browser_release', 'browser_release --browser chrome-a'],
295
296
  },
296
297
  {
297
298
  name: 'browser_launch',
298
- description: '启动本地 Chrome,或创建/显式复用 AgentBay 云浏览器,并通过 CDP 接入 Mearl',
299
+ description: '启动本地 Chrome、创建/复用同名 AgentBay 云浏览器,或用 sessionId 接入现有云端 Session',
299
300
  params: [
300
- { name: 'name', type: 'string', required: true, description: '实例名称,最多 64 个字符' },
301
+ {
302
+ name: 'name',
303
+ type: 'string',
304
+ description: '实例名称,创建浏览器时必填;按 sessionId 接入时可省略',
305
+ },
301
306
  {
302
307
  name: 'provider',
303
308
  type: '"local" | "agentbay"',
304
- description: '运行环境,默认 local;agentbay 从 AGENTBAY_KEY 创建云浏览器',
309
+ description: '运行环境,默认 local;传 sessionId 时自动使用 agentbay,否则 agentbay 从 AGENTBAY_KEY 创建云浏览器',
310
+ },
311
+ {
312
+ name: 'sessionId',
313
+ type: 'string',
314
+ description: '显式接入现有 AgentBay Session;不会创建、初始化或取得其生命周期所有权',
305
315
  },
306
316
  {
307
317
  name: 'reuse',
@@ -345,8 +355,8 @@ export const COMMANDS = [
345
355
  },
346
356
  {
347
357
  name: 'copyCookieDomains',
348
- type: 'string[] | "mearl-services"',
349
- description: '从控制浏览器复制 Cookie:传域名数组,或传 mearl-services 同步 Mearl 依赖平台登录态',
358
+ type: 'string[] | "mearl-services" | { presets?: ["mearl-services"]; domains?: string[] }',
359
+ description: '从控制浏览器复制 Cookie:可传域名数组、Mearl 登录态预设,或组合预设与额外域名',
350
360
  },
351
361
  ],
352
362
  examples: [
@@ -354,7 +364,9 @@ export const COMMANDS = [
354
364
  `browser_launch --payload '{"name":"cloud-public","provider":"agentbay","imageId":"browser_latest","url":"https://example.com"}'`,
355
365
  `browser_launch --payload '{"name":"cloud-login","provider":"agentbay","persistent":true,"copyCookieDomains":["example.com"],"url":"https://example.com/account"}'`,
356
366
  `browser_launch --payload '{"name":"cloud-login","provider":"agentbay","reuse":"require","copyCookieDomains":["example.com"]}'`,
367
+ `browser_launch --browser '<本地浏览器 browserId>' --payload '{"sessionId":"<AgentBay sessionId>","copyCookieDomains":["example.com"]}'`,
357
368
  `browser_launch --payload '{"name":"cloud-tools","provider":"agentbay","copyCookieDomains":"mearl-services"}'`,
369
+ `browser_launch --payload '{"name":"cloud-tools","provider":"agentbay","copyCookieDomains":{"presets":["mearl-services"],"domains":["figma.com"]}}'`,
358
370
  `browser_launch --payload '{"name":"account-a","headless":true,"accountId":12345}'`,
359
371
  `browser_launch --payload '{"name":"figma","userAgentMode":"desktop","copyCookieDomains":["figma.com"]}'`,
360
372
  `browser_launch --payload '{"name":"account-b","query":"test_account","url":"https://www.taobao.com"}'`,
@@ -424,23 +436,18 @@ export const COMMANDS = [
424
436
  {
425
437
  name: 'emulation',
426
438
  type: 'object',
427
- description: '开 tab 即进入移动端模拟态(首屏即移动态、无需 reload)。字段同 set_device_emulation 去掉 enabled,如 {"preset":"iphone-15-pro"}',
439
+ description: '开 tab 即进入移动端模拟态(首屏即移动态、无需 reload)。字段同 set_device_emulation 去掉 enabled/reload;deviceShell=true 时页面从首屏起在 iframe WebView 中渲染设备系统外壳',
428
440
  },
429
441
  {
430
442
  name: 'appProfile',
431
443
  type: 'string',
432
- description: '开 tab 即进入 App 容器模拟态(客户端 UA + JSBridge 托管 + iframe 容器栈 + 自适应系统标题栏,首屏即容器态)。系统栏由 iframe 外层统一渲染,并根据页面协议与桥调用切换普通、沉浸或隐藏状态;档案名同 set_app_profile,如 "fliggy"',
444
+ description: '开 tab 即注入 App 客户端环境与 JSBridge;档案名同 set_app_profile,如 "fliggy"。需要 iframe WebView 和系统栏时同时传 emulation.deviceShell=true',
433
445
  },
434
446
  {
435
447
  name: 'os',
436
448
  type: '"ios" | "android"',
437
449
  description: '与 appProfile 配合;容器系统,默认 ios',
438
450
  },
439
- {
440
- name: 'simulateSystemBars',
441
- type: 'boolean',
442
- description: '与 appProfile 配合;是否模拟顶部状态/导航栏与底部手势栏,默认 true',
443
- },
444
451
  {
445
452
  name: 'bridgeConfig',
446
453
  type: 'object',
@@ -450,7 +457,7 @@ export const COMMANDS = [
450
457
  examples: [
451
458
  `tab_open --payload '{"url":"https://example.com"}'`,
452
459
  `tab_open --payload '{"url":"https://example.com/page","reuse":"prefer","match":{"ignoreSearch":true}}'`,
453
- `tab_open --payload '{"url":"https://m.example.com","emulation":{"preset":"iphone-15-pro"}}'`,
460
+ `tab_open --payload '{"url":"https://m.example.com","emulation":{"preset":"iphone-17-pro"}}'`,
454
461
  `tab_open --payload '{"url":"https://market.m.taobao.com/app/trip/rx-home/pages/home","appProfile":"fliggy"}'`,
455
462
  `tab_open --payload '{"url":"https://example.com","appProfile":"fliggy","os":"android"}'`,
456
463
  `tab_open --payload '{"url":"https://example.com","group":"验证任务"}'`,
@@ -853,18 +860,23 @@ export const COMMANDS = [
853
860
  // ── 移动端模拟 ────────────────────────────────────────────────────────────
854
861
  {
855
862
  name: 'set_device_emulation',
856
- description: '把目标 tab 切换为移动端模拟(视口/UA/触摸),等价 DevTools 设备工具栏,作用域 per-tab;需保持调试控制才持续生效',
863
+ description: '把目标 tab 切换为移动端模拟(视口/DPR/UA/触摸/视觉比例),等价 DevTools 设备工具栏,作用域 per-tab;需保持调试控制才持续生效',
857
864
  summary: '设置或清除目标标签页的设备模拟',
858
865
  params: [
859
866
  { name: 'enabled', type: 'boolean', required: true, description: 'true=启用,false=清除' },
860
867
  {
861
868
  name: 'preset',
862
- type: '"iphone-15-pro" | "iphone-se" | "pixel-8" | "ipad-mini" | "galaxy-s23"',
863
- description: '内置设备预设,提供 width/height/DPR/UA 默认值',
869
+ type: DEVICE_PRESET_TYPE,
870
+ description: '内置设备预设,提供 width/height/DPR/UA 默认值;旧机型 ID 继续兼容',
864
871
  },
865
872
  { name: 'width', type: 'number', description: '视口宽度(覆盖 preset)' },
866
873
  { name: 'height', type: 'number', description: '视口高度(覆盖 preset)' },
867
874
  { name: 'deviceScaleFactor', type: 'number', description: 'DPR(覆盖 preset)' },
875
+ {
876
+ name: 'scale',
877
+ type: 'number',
878
+ description: '画面视觉比例,不改变视口尺寸或 DPR;必须为有限正数,默认 1',
879
+ },
868
880
  { name: 'mobile', type: 'boolean', description: '是否走移动渲染模式,默认从 preset 推断' },
869
881
  { name: 'userAgent', type: 'string', description: '自定义 UA(覆盖 preset)' },
870
882
  {
@@ -872,19 +884,28 @@ export const COMMANDS = [
872
884
  type: '"portrait" | "landscape"',
873
885
  description: '横竖屏,默认 portrait',
874
886
  },
875
- { name: 'touch', type: 'boolean', description: '是否启用触摸事件,默认 mobile 为真时启用' },
887
+ {
888
+ name: 'touch',
889
+ type: 'boolean',
890
+ description: '是否启用触摸事件,默认从 preset 推断;无 preset 时跟随 mobile',
891
+ },
892
+ {
893
+ name: 'deviceShell',
894
+ type: 'boolean',
895
+ description: '是否使用 iframe WebView 并渲染顶部状态栏与底部操作区,默认 false',
896
+ },
876
897
  { name: 'reload', type: 'boolean', description: '启用后是否重载页面,默认 true' },
877
898
  { name: 'tabId', type: 'number', required: true, description: '目标标签页 ID' },
878
899
  ],
879
900
  examples: [
880
- `set_device_emulation --payload '{"enabled":true,"preset":"iphone-15-pro"}'`,
881
- `set_device_emulation --payload '{"enabled":true,"preset":"pixel-8","orientation":"landscape"}'`,
901
+ `set_device_emulation --payload '{"enabled":true,"preset":"iphone-17-pro"}'`,
902
+ `set_device_emulation --payload '{"enabled":true,"preset":"pixel-11","orientation":"landscape"}'`,
882
903
  `set_device_emulation --payload '{"enabled":false}'`,
883
904
  ],
884
905
  },
885
906
  {
886
907
  name: 'set_app_profile',
887
- description: '把目标 tab 切换为 App 容器模拟态(客户端 UA + JSBridge 托管 + 页面跳转 iframe 容器栈 + scheme 唤端映射 + 自适应系统标题栏),系统栏由 iframe 外层统一渲染,并根据页面协议与桥调用切换普通、沉浸或隐藏状态;用于桌面浏览器近似实机验证 App 内 H5,需保持调试控制才持续生效',
908
+ description: '设置 App 客户端环境与 JSBridge;传 emulation 时由同一个命令联动设备模拟,省略时不改变设备状态',
888
909
  summary: '设置或清除目标标签页的 App 容器模拟',
889
910
  params: [
890
911
  { name: 'enabled', type: 'boolean', required: true, description: 'true=启用,false=清除' },
@@ -901,28 +922,24 @@ export const COMMANDS = [
901
922
  description: 'Android 机型名(拼入 UA 与 __DEVICE_MODEL__)',
902
923
  },
903
924
  {
904
- name: 'simulateSystemBars',
905
- type: 'boolean',
906
- description: '是否模拟顶部状态/导航栏与底部手势栏,默认 true',
907
- },
908
- {
909
- name: 'emulation',
925
+ name: 'bridgeConfig',
910
926
  type: 'object',
911
- description: '覆盖档案推荐的设备模拟参数,字段同 set_device_emulation 去掉 enabled',
927
+ description: '声明式覆盖桥方法,形如 {methods:{api:{behavior|response|noop,silent?}}};不执行配置脚本',
912
928
  },
913
929
  {
914
- name: 'bridgeConfig',
930
+ name: 'emulation',
915
931
  type: 'object',
916
- description: '声明式覆盖桥方法,形如 {methods:{api:{behavior|response|noop,silent?}}};不执行配置脚本',
932
+ description: '可选设备联动参数,字段同 set_device_emulation 去掉 enabled/reload;字段覆盖档案推荐设备,deviceShell 默认 true。省略时不设置或清除设备',
917
933
  },
918
934
  { name: 'reload', type: 'boolean', description: '应用/清除后是否重载页面,默认 true' },
919
935
  { name: 'tabId', type: 'number', required: true, description: '目标标签页 ID' },
920
936
  ],
921
937
  examples: [
922
938
  `set_app_profile --payload '{"enabled":true,"profile":"fliggy"}'`,
923
- `set_app_profile --payload '{"enabled":true,"profile":"fliggy","simulateSystemBars":false}'`,
924
- `set_app_profile --payload '{"enabled":true,"profile":"alipay","os":"android","deviceModel":"Pixel 8 Pro"}'`,
939
+ `set_app_profile --payload '{"enabled":true,"profile":"fliggy","emulation":{}}'`,
940
+ `set_app_profile --payload '{"enabled":true,"profile":"alipay","emulation":{"preset":"pixel-11","deviceShell":true}}'`,
925
941
  `set_app_profile --payload '{"enabled":false}'`,
942
+ `set_app_profile --payload '{"enabled":false,"emulation":{}}'`,
926
943
  ],
927
944
  },
928
945
  {
@@ -157,6 +157,8 @@ export interface ManagedBrowserInfo extends BrowserInfoBase {
157
157
  persistent: boolean;
158
158
  cdpPort?: number;
159
159
  agentbaySessionId?: string;
160
+ /** Whether this host owns the AgentBay session lifecycle or only attaches to it. */
161
+ agentbaySessionMode?: 'owned' | 'attached';
160
162
  /** AgentBay image used to create the session. */
161
163
  agentbayImageId?: string;
162
164
  /** Persistent AgentBay browser context ID when enabled for this instance. */
@@ -199,12 +201,26 @@ export interface BrowserReleaseResult {
199
201
  hadActiveControl: boolean;
200
202
  transports: Array<'extension' | 'cdp'>;
201
203
  affectedTabIds: number[];
204
+ /** True when browser_release also removed a sessionId-based local AgentBay attachment. */
205
+ detached?: boolean;
206
+ /** Attached sessions remain alive in AgentBay after the local attachment is released. */
207
+ sessionClosed?: boolean;
208
+ agentbaySessionId?: string;
202
209
  failures?: BrowserReleaseFailure[];
203
210
  }
211
+ export interface CookieCopyScope {
212
+ /** Maintained authentication-domain presets to include. */
213
+ presets?: Array<'mearl-services'>;
214
+ /** Additional domains or URLs to copy alongside the selected presets. */
215
+ domains?: string[];
216
+ }
204
217
  export interface BrowserLaunchParams {
205
- name: string;
206
- /** Browser runtime provider. Defaults to local Chrome. */
218
+ /** Required when creating a browser; optional when attaching by AgentBay sessionId. */
219
+ name?: string;
220
+ /** Browser runtime provider. Defaults to local Chrome unless sessionId is present. */
207
221
  provider?: 'local' | 'agentbay';
222
+ /** Attach to an exact live AgentBay session without creating or owning it. */
223
+ sessionId?: string;
208
224
  /** AgentBay running-session reuse policy. Defaults to never. */
209
225
  reuse?: 'never' | 'prefer' | 'require';
210
226
  /** AgentBay image alias or concrete image ID. Only supported by the AgentBay provider. */
@@ -221,8 +237,8 @@ export interface BrowserLaunchParams {
221
237
  site?: string | number;
222
238
  query?: string;
223
239
  tabId?: number;
224
- /** Copy explicit domains or the fixed Mearl-service preset from the control browser. */
225
- copyCookieDomains?: string[] | 'mearl-services';
240
+ /** Copy explicit domains, the Mearl-service preset, or a combination of both. */
241
+ copyCookieDomains?: string[] | 'mearl-services' | CookieCopyScope;
226
242
  }
227
243
  export interface BrowserCloseParams {
228
244
  browser: string;
@@ -262,16 +278,20 @@ export interface PageSelectedElementParams {
262
278
  includeOuterHTML?: boolean;
263
279
  includeScreenshot?: boolean;
264
280
  }
265
- export type BrowserDevicePresetName = 'iphone-15-pro' | 'iphone-se' | 'pixel-8' | 'ipad-mini' | 'galaxy-s23';
281
+ 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"];
282
+ export type BrowserDevicePresetName = (typeof BROWSER_DEVICE_PRESET_NAMES)[number];
266
283
  export interface BrowserEmulationOptions {
267
284
  preset?: BrowserDevicePresetName;
268
285
  width?: number;
269
286
  height?: number;
270
287
  deviceScaleFactor?: number;
288
+ scale?: number;
271
289
  mobile?: boolean;
272
290
  userAgent?: string;
273
291
  orientation?: 'portrait' | 'landscape';
274
292
  touch?: boolean;
293
+ /** 在 iframe WebView 外渲染设备系统栏;默认 false,保持纯 CDP 设备模拟。 */
294
+ deviceShell?: boolean;
275
295
  }
276
296
  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' | 'prefetch' | 'fptTrack' | 'mtopStream' | 'previewFile' | 'toast' | 'unsupportedNativeInput' | 'payment' | 'userTrack' | 'calendar' | 'networkQualityMonitor' | 'beaconRequest';
277
297
  /** 单个桥方法的声明式覆盖;行为、固定回包、成功空实现三选一。 */
@@ -302,15 +322,12 @@ export interface TabOpenParams {
302
322
  emulation?: BrowserEmulationOptions;
303
323
  group?: string;
304
324
  /**
305
- * 客户端容器模拟档案名(如 'fliggy')。打开后即按该 App 的 WebView 环境渲染:
306
- * 移动视口 + 容器 UA + JSBridge 托管 + 自适应系统标题栏。系统栏由 iframe 外层统一渲染,
307
- * 并根据页面协议与桥调用切换普通、沉浸或隐藏状态。可用档案见 APP_PROFILE_NAMES。
325
+ * 客户端容器模拟档案名(如 'fliggy')。只注入容器 UA、环境变量、JSBridge 与桥协议;
326
+ * 设备视口和 iframe 系统外壳由 emulation.deviceShell 独立控制。可用档案见 APP_PROFILE_NAMES。
308
327
  */
309
328
  appProfile?: AppProfileName;
310
329
  /** 与 appProfile 配合使用;容器系统,默认 ios */
311
330
  os?: 'ios' | 'android';
312
- /** 与 appProfile 配合使用;是否模拟顶部状态/导航栏与底部手势栏,默认 true */
313
- simulateSystemBars?: boolean;
314
331
  /** 与 appProfile 配合使用;按方法覆盖档案内置桥配置 */
315
332
  bridgeConfig?: AppBridgeConfig;
316
333
  }
@@ -333,11 +350,12 @@ export interface SetAppProfileParams {
333
350
  os?: 'ios' | 'android';
334
351
  appVersion?: string;
335
352
  deviceModel?: string;
336
- /** 是否模拟顶部状态/导航栏与底部手势栏,默认 true */
337
- simulateSystemBars?: boolean;
338
353
  /** 按方法覆盖档案内置桥配置,仅支持受控行为、固定 JSON 回包和成功空实现 */
339
354
  bridgeConfig?: AppBridgeConfig;
340
- /** 覆盖档案推荐的设备模拟参数(视口/UA 默认取档案值) */
355
+ /**
356
+ * 可选设备联动参数。传入时显式字段覆盖档案推荐设备,空对象使用推荐设备且默认开启
357
+ * deviceShell;省略时不设置或清除该 tab 的设备状态。
358
+ */
341
359
  emulation?: BrowserEmulationOptions;
342
360
  /** 默认 true:应用/清除后刷新页面使注入生效 */
343
361
  reload?: boolean;
@@ -6,6 +6,20 @@ export const SEND_REQUEST_MAX_TOTAL_FILE_BYTES = 40 * 1024 * 1024;
6
6
  // Base64 expands by 4/3; 7 MiB leaves roughly 680 KiB for JSON framing under
7
7
  // the 10 MiB cloud message limit.
8
8
  export const SEND_REQUEST_MAX_TOTAL_BASE64_FILE_BYTES = 7 * 1024 * 1024;
9
+ export const BROWSER_DEVICE_PRESET_NAMES = [
10
+ 'iphone-17-pro',
11
+ 'iphone-17e',
12
+ 'pixel-11',
13
+ 'ipad-mini-a17-pro',
14
+ 'galaxy-s26',
15
+ 'pura-x-max',
16
+ // 旧名称保留具体机型语义,供已有调用与历史回归基线继续使用。
17
+ 'iphone-15-pro',
18
+ 'iphone-se',
19
+ 'pixel-8',
20
+ 'ipad-mini',
21
+ 'galaxy-s23',
22
+ ];
9
23
  /**
10
24
  * 客户端容器模拟可用档案名。
11
25
  *
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
  export { UnifiedClient, UnifiedClient as MearlClient, decodeBrowserTarget, invoke, listCloudConnectors, } from './unified.js';
8
8
  export type { UnifiedClientOptions, UnifiedInvokeOptions, UnifiedInvokeOptions as InvokeOptions, } from './unified.js';
9
- export { APP_PROFILE_NAMES, BROWSER_ACTIONS, BROWSER_COMMAND_ACTIONS, EXTENSION_BROWSER_ACTIONS, PAGE_ACT_ACTIONS, SEND_REQUEST_MAX_FILES, SEND_REQUEST_MAX_TOTAL_BASE64_FILE_BYTES, SEND_REQUEST_MAX_TOTAL_FILE_BYTES, isBrowserAction, isBrowserCommandAction, isExtensionBrowserAction, } from './generated-browser-action-protocol.js';
9
+ export { APP_PROFILE_NAMES, BROWSER_DEVICE_PRESET_NAMES, BROWSER_ACTIONS, BROWSER_COMMAND_ACTIONS, EXTENSION_BROWSER_ACTIONS, PAGE_ACT_ACTIONS, SEND_REQUEST_MAX_FILES, SEND_REQUEST_MAX_TOTAL_BASE64_FILE_BYTES, SEND_REQUEST_MAX_TOTAL_FILE_BYTES, isBrowserAction, isBrowserCommandAction, isExtensionBrowserAction, } from './generated-browser-action-protocol.js';
10
10
  export type { ActionStep, AppProfileName, BrowserAction, BrowserActionMap, BrowserActionRequest, BrowserActionResponse, BrowserCloseParams, BrowserCommandAction, BrowserInfo, BrowserInfoBase, BrowserLaunchParams, BrowserListResult, BrowserReleaseFailure, BrowserReleaseResult, BrowserSource, BrowserStatus, CaptureCheckpointParams, CaptureCheckpointResult, BrowserDevicePresetName, BrowserEmulationOptions, ExtensionBrowserAction, GetApiSchemaParams, GetCookieParams, GetCookieResult, GetEventsParams, GetLogsParams, GetMocksParams, GetRequestsParams, GetUserInfoParams, ManagedBrowserAccount, ManagedBrowserInfo, PageClickParams, PageDragEndpoint, PageDragParams, PageDragPoint, PageActAction, PageDiagnosticsOptions, PageDiagnosticsSetting, PageEvalParams, PageFramesParams, PageHoverParams, PageNavigateParams, PageObservationWaitOptions, PagePressParams, PageScreenshotParams, PageScrollParams, PageSelectedElementParams, PageSnapshotParams, PageSnapshotQuery, PageTypeParams, PageUploadParams, PageWaitParams, RecordParams, RegularBrowserInfo, RequestDomainPermissionParams, RulesResult, RunActionName, RunActionsParams, SelectBrowserParams, SelectBrowserResult, SendRequestFile, SendRequestFormValue, SendMtopRequestParams, SendRequestParams, SetAppProfileParams, SetCookieParams, SetCookieResult, SetDeviceEmulationParams, SetMockParams, SetRuleParams, SetTimezoneParams, SetTimezoneResult, TabCloseParams, TabOpenParams, TdbankAccountParams, } from './generated-browser-action-protocol.js';
11
11
  import { type UnifiedInvokeOptions } from './unified.js';
12
12
  import type * as Protocol from './generated-browser-action-protocol.js';
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * then routes actions using the global browser ids returned by browser_list.
6
6
  */
7
7
  export { UnifiedClient, UnifiedClient as MearlClient, decodeBrowserTarget, invoke, listCloudConnectors, } from './unified.js';
8
- export { APP_PROFILE_NAMES, BROWSER_ACTIONS, BROWSER_COMMAND_ACTIONS, EXTENSION_BROWSER_ACTIONS, PAGE_ACT_ACTIONS, SEND_REQUEST_MAX_FILES, SEND_REQUEST_MAX_TOTAL_BASE64_FILE_BYTES, SEND_REQUEST_MAX_TOTAL_FILE_BYTES, isBrowserAction, isBrowserCommandAction, isExtensionBrowserAction, } from './generated-browser-action-protocol.js';
8
+ export { APP_PROFILE_NAMES, BROWSER_DEVICE_PRESET_NAMES, BROWSER_ACTIONS, BROWSER_COMMAND_ACTIONS, EXTENSION_BROWSER_ACTIONS, PAGE_ACT_ACTIONS, SEND_REQUEST_MAX_FILES, SEND_REQUEST_MAX_TOTAL_BASE64_FILE_BYTES, SEND_REQUEST_MAX_TOTAL_FILE_BYTES, isBrowserAction, isBrowserCommandAction, isExtensionBrowserAction, } from './generated-browser-action-protocol.js';
9
9
  import { invoke } from './unified.js';
10
10
  export function tabCheckpoint(params = {}, options) {
11
11
  return invoke('tab_checkpoint', params, options);
package/dist/socket.js CHANGED
@@ -11,6 +11,7 @@ import fs from 'node:fs';
11
11
  import os from 'node:os';
12
12
  import path from 'node:path';
13
13
  import { createRequire } from 'node:module';
14
+ import { StringDecoder } from 'node:string_decoder';
14
15
  import { resolveFileRefs } from './fileRefs.js';
15
16
  import { listLiveBrowsers, findBrowser, describeBrowser } from './browserRegistry.js';
16
17
  import { resolveActionTimeoutSec } from './actionTimeouts.js';
@@ -255,6 +256,7 @@ function invokeOnce(action, data, timeoutSec, transport, socketPath, controlSour
255
256
  const socket = net.createConnection(socketPath);
256
257
  let settled = false;
257
258
  let buffer = '';
259
+ const decoder = new StringDecoder('utf8');
258
260
  const settle = (error, result) => {
259
261
  if (settled)
260
262
  return;
@@ -283,7 +285,7 @@ function invokeOnce(action, data, timeoutSec, transport, socketPath, controlSour
283
285
  socket.write(JSON.stringify(message) + '\n');
284
286
  });
285
287
  socket.on('data', chunk => {
286
- buffer += chunk.toString('utf-8');
288
+ buffer += decoder.write(chunk);
287
289
  if (Buffer.byteLength(buffer, 'utf-8') > MAX_SOCKET_BUFFER_SIZE) {
288
290
  settle(new Error(`Socket 响应超过 ${MAX_SOCKET_BUFFER_SIZE} 字节限制`));
289
291
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mearl/client",
3
- "version": "2.9.6",
3
+ "version": "2.11.0",
4
4
  "description": "Unified Mearl SDK & CLI for local and remote browsers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -55,7 +55,7 @@
55
55
  "commander": "^15.0.0",
56
56
  "fastest-levenshtein": "^1.0.16",
57
57
  "ws": "^8.18.0",
58
- "@mearl/cloud-types": "2.9.6"
58
+ "@mearl/cloud-types": "2.11.0"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@types/node": "^24.9.1",