@mearl/client 2.2.0 → 2.2.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
@@ -40,6 +40,9 @@ mearl get_requests --payload '{"count":5}'
40
40
  # 代理一个 HTTP 请求
41
41
  mearl send_request --payload '{"url":"https://api.example.com","method":"GET"}'
42
42
 
43
+ # 以 multipart/form-data 上传本地文件
44
+ mearl send_request --payload '{"url":"https://api.example.com/upload","method":"POST","formData":{"folder":"assets"},"files":[{"fieldName":"file","filePath":"/absolute/path/image.png"}]}'
45
+
43
46
  # 截图并保存
44
47
  mearl page_screenshot --output ./screenshot.png
45
48
 
@@ -49,6 +52,9 @@ mearl set_mock --payload-file ./mock-data.json
49
52
  # 环境自检
50
53
  mearl check
51
54
 
55
+ # 更新本地环境
56
+ npx @mearl/setup update --local
57
+
52
58
  # 查看某个 action 的帮助
53
59
  mearl <action> --help
54
60
 
@@ -88,7 +94,7 @@ mearl browser_release
88
94
  | TDBank | `tdbank_account` |
89
95
  | 浏览器 | `browser_list` `browser_release` `browser_launch` `browser_close` |
90
96
 
91
- 页面交互动作(`page_click` / `page_type` / `page_hover` / `page_scroll` / `page_press` / `page_upload`)默认内置观察:同一次调用内执行动作并等待异步稳定,返回 `{ action, observation }`;传 `observe: false` 可关闭观察仅执行裸动作。观察结果不替代完整页面理解:`mode: "delta"` 只返回主文档中的 `effects.notifications`、`effects.interactives` 和 `effects.focus` 等高置信度信号,并明确携带 `scope: "main-document"`;可交互节点会尽量携带真实 backend `node.ref`,后续动作优先使用 ref,缺少 ref 时使用 `node.selector`。`mode: "navigation"` 且 `ready: true` 时,页面已通过网络静默、骨架状态或保守的内容稳定判定,可在新页面重建快照。通常仅在 `fullSnapshotRecommended` 为 true 时根据 `snapshotReasons` 回退;滚动后若下一步需要读取新视口内容,可按需获取 viewport 快照。`page_eval` 默认裸执行,只有显式传入 `observe` 对象时才启用观察。
97
+ 页面交互动作(`page_click` / `page_type` / `page_hover` / `page_scroll` / `page_press` / `page_upload`)默认内置观察:同一次调用内执行动作并等待异步稳定,返回 `{ action, observation }`;传 `observe: false` 可关闭观察仅执行裸动作。观察结果不替代完整页面理解:`mode: "delta"` 只返回主文档中的 `effects.notifications`、`effects.interactives` 和 `effects.focus` 等高置信度信号,并明确携带 `scope: "main-document"`;可交互节点会尽量携带真实 backend `node.ref`,后续动作优先使用 ref,缺少 ref 时使用 `node.selector`。`mode: "navigation"` 且 `ready: true` 时,页面已通过网络静默、骨架状态或保守的内容稳定判定,可在新页面重建快照。动作直接打开新标签页时,`observation.openedTabs` 返回新标签页的 `tabId`、URL、标题和加载状态,可直接把该 `tabId` 用于后续操作,不必调用 `tab_list`。通常仅在 `fullSnapshotRecommended` 为 true 时根据 `snapshotReasons` 回退;滚动后若下一步需要读取新视口内容,可按需获取 viewport 快照。`page_eval` 默认裸执行,只有显式传入 `observe` 对象时才启用观察。
92
98
 
93
99
  `page_snapshot` 默认返回完整 AX Tree;长列表可传 `mode: "viewport"`,只需要当前视口内的控件时传 `mode: "interactive"`,已知 CSS 区域时传 `rootSelector`,已有 ref 时传 `rootRef`(可用 `ancestorDepth` 向上补充上下文),只查找特定文案或角色时传 `query`。视口内缺少 AX 控件语义时,`interactive` 会自动回退到 viewport,并返回 `fallbackMode: "viewport"`。`maxNodes` / `maxChars` 截断会同时保留首尾内容。
94
100
 
@@ -1,4 +1,5 @@
1
1
  import { PAGE_ACT_ACTIONS } from './generated-browser-action-protocol.js';
2
+ const SEND_REQUEST_FILE_TRANSFER_TIMEOUT_MS = 120_000;
2
3
  function boundedNumber(value, fallback, minimum, maximum) {
3
4
  const numeric = Number(value);
4
5
  if (!Number.isFinite(numeric))
@@ -16,7 +17,10 @@ export function resolveObservedActionTimeoutSec(data) {
16
17
  }
17
18
  export function resolveActionTimeoutSec(action, data, fallback = 15) {
18
19
  if (action === 'send_request') {
19
- return Math.max(fallback, Math.ceil(((data.timeout ?? 30000) + 5000) / 1000));
20
+ const transferTimeoutMs = Array.isArray(data.files) && data.files.length > 0
21
+ ? SEND_REQUEST_FILE_TRANSFER_TIMEOUT_MS
22
+ : 0;
23
+ return Math.max(fallback, Math.ceil(((data.timeout ?? 30000) + transferTimeoutMs + 5000) / 1000));
20
24
  }
21
25
  if (action === 'send_mtop_request') {
22
26
  return Math.max(fallback, Math.ceil(((data.timeout ?? 10000) + 5000) / 1000));
@@ -33,6 +37,16 @@ export function resolveActionTimeoutSec(action, data, fallback = 15) {
33
37
  return Math.max(fallback, 90);
34
38
  if (action === 'browser_close' || action === 'tdbank_account')
35
39
  return Math.max(fallback, 20);
40
+ if (action === 'run_actions') {
41
+ const actions = Array.isArray(data.actions) ? data.actions : [{}];
42
+ const timeoutSec = actions.reduce((total, step) => {
43
+ const stepAction = String(step?.action ?? '');
44
+ if (!stepAction || stepAction === 'run_actions')
45
+ return total + 15;
46
+ return total + resolveActionTimeoutSec(stepAction, step?.data ?? {}, 15);
47
+ }, 0);
48
+ return Math.max(fallback, timeoutSec);
49
+ }
36
50
  // 页面交互动作默认内置观察,超时需覆盖观察与导航窗口;observe:false 时是裸动作,用默认超时
37
51
  const observedAction = OBSERVED_PAGE_ACTIONS.has(action);
38
52
  const observationEnabled = action === 'page_eval'
@@ -41,9 +55,5 @@ export function resolveActionTimeoutSec(action, data, fallback = 15) {
41
55
  if (observedAction && observationEnabled) {
42
56
  return Math.max(fallback, resolveObservedActionTimeoutSec(data));
43
57
  }
44
- if (action === 'run_actions') {
45
- const steps = Array.isArray(data.actions) ? data.actions.length : 1;
46
- return Math.max(fallback, steps * 15);
47
- }
48
58
  return fallback;
49
59
  }
@@ -164,7 +164,7 @@ export declare const COMMANDS: readonly [{
164
164
  readonly examples: ["get_rules"];
165
165
  }, {
166
166
  readonly name: "send_request";
167
- readonly description: "发送 HTTP 请求,自动携带浏览器 Cookie";
167
+ readonly description: "发送 HTTP 请求,自动携带浏览器 Cookie,支持 multipart 文件上传";
168
168
  readonly params: [{
169
169
  readonly name: "url";
170
170
  readonly type: "string";
@@ -186,20 +186,24 @@ export declare const COMMANDS: readonly [{
186
186
  readonly name: "body";
187
187
  readonly type: "any";
188
188
  readonly description: "请求体(传对象时自动 JSON 序列化)";
189
+ }, {
190
+ readonly name: "formData";
191
+ readonly type: "object";
192
+ readonly description: "multipart 文本字段(值支持 string/number/boolean)";
193
+ }, {
194
+ readonly name: "files";
195
+ readonly type: "SendRequestFile[]";
196
+ readonly description: "multipart 文件,最多 20 个、总计不超过 40 MiB;Base64 文件总计不超过 7 MiB。每项传 fieldName 和绝对 filePath,或传 base64 + filename";
189
197
  }, {
190
198
  readonly name: "withCookies";
191
199
  readonly type: "boolean";
192
200
  readonly description: "是否附加浏览器 Cookie,默认 true";
193
- }, {
194
- readonly name: "cookieDomain";
195
- readonly type: "string";
196
- readonly description: "指定 Cookie 域名,不填时从 url 提取";
197
201
  }, {
198
202
  readonly name: "timeout";
199
203
  readonly type: "number";
200
204
  readonly description: "超时毫秒数,默认 30000";
201
205
  }];
202
- readonly examples: ["send_request --payload '{\"url\":\"https://api.example.com/data\",\"method\":\"GET\"}'"];
206
+ readonly examples: ["send_request --payload '{\"url\":\"https://api.example.com/data\",\"method\":\"GET\"}'", "send_request --payload '{\"url\":\"https://api.example.com/upload\",\"method\":\"POST\",\"formData\":{\"folder\":\"assets\"},\"files\":[{\"fieldName\":\"file\",\"filePath\":\"/absolute/path/image.png\"}]}'"];
203
207
  }, {
204
208
  readonly name: "send_mtop_request";
205
209
  readonly description: "在页面上下文中发起 mtop 请求,自动处理签名和 token";
@@ -351,6 +355,10 @@ export declare const COMMANDS: readonly [{
351
355
  readonly name: "active";
352
356
  readonly type: "boolean";
353
357
  readonly description: "是否激活新 Tab,默认 false";
358
+ }, {
359
+ readonly name: "openerTabId";
360
+ readonly type: "number";
361
+ readonly description: "来源 Tab ID;用于把目标 Tab 关联到来源侧边栏对话";
354
362
  }, {
355
363
  readonly name: "closeExisting";
356
364
  readonly type: "boolean";
@@ -393,7 +401,7 @@ export declare const COMMANDS: readonly [{
393
401
  readonly examples: ["tab_list"];
394
402
  }, {
395
403
  readonly name: "page_click";
396
- readonly description: "点击页面元素,支持 selector / text / point 三种定位方式;auto 在可见页按设备模拟状态使用可信 mouse/touch,隐藏页使用 DOM fallback";
404
+ readonly description: "点击页面元素,支持 selector / text / point 三种定位方式;auto 在可见页按设备模拟状态使用可信 mouse/touch,隐藏页使用 DOM fallback;点击直接打开新标签页时 observation.openedTabs 返回目标信息";
397
405
  readonly params: [{
398
406
  readonly name: "selector";
399
407
  readonly type: "string";
package/dist/commands.js CHANGED
@@ -125,18 +125,30 @@ export const COMMANDS = [
125
125
  // ── 网络代理 ──────────────────────────────────────────────────────────────
126
126
  {
127
127
  name: 'send_request',
128
- description: '发送 HTTP 请求,自动携带浏览器 Cookie',
128
+ description: '发送 HTTP 请求,自动携带浏览器 Cookie,支持 multipart 文件上传',
129
129
  params: [
130
130
  { name: 'url', type: 'string', required: true, description: '完整目标 URL' },
131
131
  { name: 'method', type: 'string', description: 'HTTP 方法,默认 GET' },
132
132
  { name: 'headers', type: 'object', description: '自定义请求头' },
133
133
  { name: 'params', type: 'object', description: 'URL 查询参数' },
134
134
  { name: 'body', type: 'any', description: '请求体(传对象时自动 JSON 序列化)' },
135
+ {
136
+ name: 'formData',
137
+ type: 'object',
138
+ description: 'multipart 文本字段(值支持 string/number/boolean)',
139
+ },
140
+ {
141
+ name: 'files',
142
+ type: 'SendRequestFile[]',
143
+ description: 'multipart 文件,最多 20 个、总计不超过 40 MiB;Base64 文件总计不超过 7 MiB。每项传 fieldName 和绝对 filePath,或传 base64 + filename',
144
+ },
135
145
  { name: 'withCookies', type: 'boolean', description: '是否附加浏览器 Cookie,默认 true' },
136
- { name: 'cookieDomain', type: 'string', description: '指定 Cookie 域名,不填时从 url 提取' },
137
146
  { name: 'timeout', type: 'number', description: '超时毫秒数,默认 30000' },
138
147
  ],
139
- examples: [`send_request --payload '{"url":"https://api.example.com/data","method":"GET"}'`],
148
+ examples: [
149
+ `send_request --payload '{"url":"https://api.example.com/data","method":"GET"}'`,
150
+ `send_request --payload '{"url":"https://api.example.com/upload","method":"POST","formData":{"folder":"assets"},"files":[{"fieldName":"file","filePath":"/absolute/path/image.png"}]}'`,
151
+ ],
140
152
  },
141
153
  {
142
154
  name: 'send_mtop_request',
@@ -259,6 +271,11 @@ export const COMMANDS = [
259
271
  params: [
260
272
  { name: 'url', type: 'string', required: true, description: '要打开的 URL' },
261
273
  { name: 'active', type: 'boolean', description: '是否激活新 Tab,默认 false' },
274
+ {
275
+ name: 'openerTabId',
276
+ type: 'number',
277
+ description: '来源 Tab ID;用于把目标 Tab 关联到来源侧边栏对话',
278
+ },
262
279
  {
263
280
  name: 'closeExisting',
264
281
  type: 'boolean',
@@ -311,7 +328,7 @@ export const COMMANDS = [
311
328
  // ── 页面操作 ──────────────────────────────────────────────────────────────
312
329
  {
313
330
  name: 'page_click',
314
- description: '点击页面元素,支持 selector / text / point 三种定位方式;auto 在可见页按设备模拟状态使用可信 mouse/touch,隐藏页使用 DOM fallback',
331
+ description: '点击页面元素,支持 selector / text / point 三种定位方式;auto 在可见页按设备模拟状态使用可信 mouse/touch,隐藏页使用 DOM fallback;点击直接打开新标签页时 observation.openedTabs 返回目标信息',
315
332
  params: [
316
333
  {
317
334
  name: 'selector',
@@ -48,14 +48,38 @@ export interface GetApiSchemaParams {
48
48
  version?: string;
49
49
  fields?: Array<'schema' | 'hsf'>;
50
50
  }
51
+ export declare const SEND_REQUEST_MAX_FILES = 20;
52
+ export declare const SEND_REQUEST_MAX_TOTAL_FILE_BYTES: number;
53
+ export declare const SEND_REQUEST_MAX_TOTAL_BASE64_FILE_BYTES: number;
54
+ export type SendRequestFormValue = string | number | boolean;
55
+ interface SendRequestFileMetadata {
56
+ /** multipart/form-data field name. */
57
+ fieldName: string;
58
+ /** Defaults to a MIME type inferred from filename, then application/octet-stream. */
59
+ contentType?: string;
60
+ }
61
+ export type SendRequestFile = SendRequestFileMetadata & ({
62
+ /** Absolute path on the machine running Mearl native-host. */
63
+ filePath: string;
64
+ base64?: never;
65
+ /** Defaults to the basename of filePath. */
66
+ filename?: string;
67
+ } | {
68
+ filePath?: never;
69
+ /** Base64 payload; decoded Base64-backed files are limited to 7 MiB total. */
70
+ base64: string;
71
+ /** Multipart filename for the Base64 payload. */
72
+ filename: string;
73
+ });
51
74
  export interface SendRequestParams {
52
75
  url: string;
53
76
  method?: string;
54
77
  headers?: Record<string, string>;
55
78
  params?: Record<string, string>;
56
79
  body?: unknown;
80
+ formData?: Record<string, SendRequestFormValue>;
81
+ files?: SendRequestFile[];
57
82
  withCookies?: boolean;
58
- cookieDomain?: string;
59
83
  responseType?: 'json' | 'text' | 'auto';
60
84
  timeout?: number;
61
85
  }
@@ -203,6 +227,8 @@ export interface BrowserEmulationOptions {
203
227
  export interface TabOpenParams {
204
228
  url: string;
205
229
  active?: boolean;
230
+ /** Source tab whose Agent conversation should own the opened or reused tab. */
231
+ openerTabId?: number;
206
232
  closeExisting?: boolean;
207
233
  reuse?: 'never' | 'prefer' | 'require';
208
234
  match?: {
@@ -675,3 +701,4 @@ export declare const EXTENSION_BROWSER_ACTIONS: {
675
701
  export declare function isBrowserAction(value: unknown): value is BrowserAction;
676
702
  export declare function isBrowserCommandAction(value: unknown): value is BrowserCommandAction;
677
703
  export declare function isExtensionBrowserAction(value: unknown): value is ExtensionBrowserAction;
704
+ export {};
@@ -1,5 +1,11 @@
1
1
  // Generated by scripts/generate-client-action-protocol.mjs.
2
2
  // Do not edit directly; update browser-core/src/browserActionProtocol.ts.
3
+ export const SEND_REQUEST_MAX_FILES = 20;
4
+ // Bounds multipart buffering in native-host and the extension service worker.
5
+ export const SEND_REQUEST_MAX_TOTAL_FILE_BYTES = 40 * 1024 * 1024;
6
+ // Base64 expands by 4/3; 7 MiB leaves roughly 680 KiB for JSON framing under
7
+ // the 10 MiB cloud message limit.
8
+ export const SEND_REQUEST_MAX_TOTAL_BASE64_FILE_BYTES = 7 * 1024 * 1024;
3
9
  export const PAGE_ACT_ACTIONS = [
4
10
  'page_click',
5
11
  'page_type',
package/dist/index.d.ts CHANGED
@@ -6,8 +6,8 @@
6
6
  */
7
7
  export { invoke } from './socket.js';
8
8
  export type { InvokeOptions } from './socket.js';
9
- export { BROWSER_ACTIONS, BROWSER_COMMAND_ACTIONS, EXTENSION_BROWSER_ACTIONS, PAGE_ACT_ACTIONS, isBrowserAction, isBrowserCommandAction, isExtensionBrowserAction, } from './generated-browser-action-protocol.js';
10
- export type { ActionStep, BrowserAction, BrowserActionMap, BrowserActionRequest, BrowserActionResponse, BrowserCloseParams, BrowserCommandAction, BrowserInfo, BrowserInfoBase, BrowserLaunchParams, BrowserListResult, BrowserReleaseFailure, BrowserReleaseResult, BrowserStatus, BrowserDevicePresetName, BrowserEmulationOptions, ExtensionBrowserAction, GetApiSchemaParams, GetCookieParams, GetCookieResult, GetEventsParams, GetLogsParams, GetMocksParams, GetRequestsParams, GetUserInfoParams, ManagedBrowserAccount, ManagedBrowserInfo, PageClickParams, PageActAction, PageEvalParams, PageFramesParams, PageHoverParams, PageNavigateParams, PageObservationWaitOptions, PagePressParams, PageScreenshotParams, PageScrollParams, PageSelectedElementParams, PageSnapshotParams, PageSnapshotQuery, PageTypeParams, PageUploadParams, PageWaitParams, RecordParams, RegularBrowserInfo, RequestDomainPermissionParams, RulesResult, RunActionName, RunActionsParams, SelectBrowserParams, SelectBrowserResult, SendMtopRequestParams, SendRequestParams, SetCookieParams, SetCookieResult, SetDeviceEmulationParams, SetMockParams, SetRuleParams, SetTimezoneParams, SetTimezoneResult, TabCloseParams, TabOpenParams, TdbankAccountParams, } from './generated-browser-action-protocol.js';
9
+ export { 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
+ export type { ActionStep, BrowserAction, BrowserActionMap, BrowserActionRequest, BrowserActionResponse, BrowserCloseParams, BrowserCommandAction, BrowserInfo, BrowserInfoBase, BrowserLaunchParams, BrowserListResult, BrowserReleaseFailure, BrowserReleaseResult, BrowserStatus, BrowserDevicePresetName, BrowserEmulationOptions, ExtensionBrowserAction, GetApiSchemaParams, GetCookieParams, GetCookieResult, GetEventsParams, GetLogsParams, GetMocksParams, GetRequestsParams, GetUserInfoParams, ManagedBrowserAccount, ManagedBrowserInfo, PageClickParams, PageActAction, 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, SetCookieParams, SetCookieResult, SetDeviceEmulationParams, SetMockParams, SetRuleParams, SetTimezoneParams, SetTimezoneResult, TabCloseParams, TabOpenParams, TdbankAccountParams, } from './generated-browser-action-protocol.js';
11
11
  import type * as Protocol from './generated-browser-action-protocol.js';
12
12
  export declare function getRequests(params?: Protocol.GetRequestsParams): Promise<Protocol.BrowserActionResponse<'get_requests'>>;
13
13
  export declare function getLogs(params?: Protocol.GetLogsParams): Promise<Protocol.BrowserActionResponse<'get_logs'>>;
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * 提供类型安全的具名方法和通用的 invoke 底层方法。
6
6
  */
7
7
  export { invoke } from './socket.js';
8
- export { BROWSER_ACTIONS, BROWSER_COMMAND_ACTIONS, EXTENSION_BROWSER_ACTIONS, PAGE_ACT_ACTIONS, isBrowserAction, isBrowserCommandAction, isExtensionBrowserAction, } from './generated-browser-action-protocol.js';
8
+ export { 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 './socket.js';
10
10
  export function getRequests(params = {}) {
11
11
  return invoke('get_requests', params);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mearl/client",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "Client SDK & CLI for Mearl — communicate with Chrome Extension via Unix Socket",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",