@mearl/client 2.18.0 → 2.19.1

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/dist/index.d.ts CHANGED
@@ -11,14 +11,14 @@ export { APP_PROFILE_NAMES, BROWSER_DEVICE_PRESET_NAMES, BROWSER_ACTIONS, BROWSE
11
11
  export type { ActionStep, AppProfileName, BrowserAction, BrowserActionMap, BrowserActionRequest, BrowserActionResponse, BrowserCapabilities, BrowserCapabilityAction, BrowserCloseParams, BrowserCommandAction, BrowserInfo, BrowserInfoBase, BrowserJsonValue, BrowserLaunchParams, BrowserProviderOperation, BrowserListResult, BrowserPairingInfo, BrowserReleaseFailure, BrowserReleaseResult, BrowserProviderDefinition, BrowserProviderIdentity, BrowserProviderInputSchema, BrowserProviderListResult, BrowserReference, BrowserDetails, BrowserSource, BrowserStatus, BrowserScreenshotKind, BrowserTransport, CaptureCheckpointParams, CaptureCheckpointResult, BrowserDevicePresetName, BrowserEmulationOptions, ExtensionBrowserAction, GetCookieParams, GetCookieResult, GetEventsParams, GetLogsParams, GetMocksParams, GetRequestsParams, ManagedBrowserInfo, PageClickParams, PageDragEndpoint, PageDragParams, PageDragPoint, PageActAction, PageDiagnosticsOptions, PageDiagnosticsSetting, PageEvalParams, PageFramesParams, PageHoverParams, PageInspectFilter, PageInspectParams, PageNavigateParams, PageObservationWaitOptions, PagePressParams, PageScreenshotParams, PageScrollParams, PageSnapshotParams, PageSnapshotQuery, PageTypeParams, PageUploadParams, PageWaitParams, RegularBrowserInfo, RequestDomainPermissionParams, RulesResult, RunActionName, RunActionsParams, SelectBrowserParams, SelectBrowserResult, SendRequestFile, SendRequestFormValue, SendMtopRequestParams, SendRequestParams, SetAppProfileParams, SetCookieParams, SetCookieResult, SetDeviceEmulationParams, SetMockParams, SetRuleParams, SetTimezoneParams, SetTimezoneResult, TabCloseParams, TabOpenParams, } from './generated-browser-action-protocol.js';
12
12
  import { type UnifiedInvokeOptions } from './unified.js';
13
13
  import type * as Protocol from './generated-browser-action-protocol.js';
14
- export declare function tabCheckpoint(params?: Protocol.CaptureCheckpointParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'tab_checkpoint'>>;
14
+ export declare function tabCheckpoint(params: Protocol.CaptureCheckpointParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'tab_checkpoint'>>;
15
15
  /** Compatibility wrapper for tabCheckpoint. */
16
- export declare function captureCheckpoint(params?: Protocol.CaptureCheckpointParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'tab_checkpoint'>>;
17
- export declare function getRequests(params?: Protocol.GetRequestsParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_requests'>>;
18
- export declare function getLogs(params?: Protocol.GetLogsParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_logs'>>;
19
- export declare function getEvents(params?: Protocol.GetEventsParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_events'>>;
16
+ export declare function captureCheckpoint(params: Protocol.CaptureCheckpointParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'tab_checkpoint'>>;
17
+ export declare function getRequests(params: Protocol.GetRequestsParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_requests'>>;
18
+ export declare function getLogs(params: Protocol.GetLogsParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_logs'>>;
19
+ export declare function getEvents(params: Protocol.GetEventsParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_events'>>;
20
20
  export declare function setMock(params: Protocol.SetMockParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'set_mock'>>;
21
- export declare function getMocks(params?: Protocol.GetMocksParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_mocks'>>;
21
+ export declare function getMocks(params: Protocol.GetMocksParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_mocks'>>;
22
22
  export declare function sendRequest(params: Protocol.SendRequestParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'send_request'>>;
23
23
  export declare function sendMtopRequest(params: Protocol.SendMtopRequestParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'send_mtop_request'>>;
24
24
  export declare function browserList(options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'browser_list'>>;
@@ -32,6 +32,7 @@ export declare function pageScreenshot(params: Protocol.PageScreenshotParams, op
32
32
  export declare function tabOpen(params: Protocol.TabOpenParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'tab_open'>>;
33
33
  export declare function tabClose(params: Protocol.TabCloseParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'tab_close'>>;
34
34
  export declare function tabList(options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'tab_list'>>;
35
+ export declare function tabGroup(params: Protocol.TabGroupParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'tab_group'>>;
35
36
  export declare function pageClick(params: Protocol.PageClickParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'page_click'>>;
36
37
  export declare function pageDrag(params: Protocol.PageDragParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'page_drag'>>;
37
38
  export declare function pageType(params: Protocol.PageTypeParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'page_type'>>;
package/dist/index.js CHANGED
@@ -7,26 +7,26 @@
7
7
  export { UnifiedClient, UnifiedClient as MearlClient, decodeBrowserTarget, invoke, invokePlugin, listCloudConnectors, } from './unified.js';
8
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, isBrowserCapabilityAction, isBrowserCommandAction, isExtensionBrowserAction, } from './generated-browser-action-protocol.js';
9
9
  import { invoke } from './unified.js';
10
- export function tabCheckpoint(params = {}, options) {
10
+ export function tabCheckpoint(params, options) {
11
11
  return invoke('tab_checkpoint', params, options);
12
12
  }
13
13
  /** Compatibility wrapper for tabCheckpoint. */
14
- export function captureCheckpoint(params = {}, options) {
14
+ export function captureCheckpoint(params, options) {
15
15
  return tabCheckpoint(params, options);
16
16
  }
17
- export function getRequests(params = {}, options) {
17
+ export function getRequests(params, options) {
18
18
  return invoke('get_requests', params, options);
19
19
  }
20
- export function getLogs(params = {}, options) {
20
+ export function getLogs(params, options) {
21
21
  return invoke('get_logs', params, options);
22
22
  }
23
- export function getEvents(params = {}, options) {
23
+ export function getEvents(params, options) {
24
24
  return invoke('get_events', params, options);
25
25
  }
26
26
  export function setMock(params, options) {
27
27
  return invoke('set_mock', params, options);
28
28
  }
29
- export function getMocks(params = {}, options) {
29
+ export function getMocks(params, options) {
30
30
  return invoke('get_mocks', params, options);
31
31
  }
32
32
  export function sendRequest(params, options) {
@@ -68,6 +68,9 @@ export function tabClose(params, options) {
68
68
  export function tabList(options) {
69
69
  return invoke('tab_list', {}, options);
70
70
  }
71
+ export function tabGroup(params, options) {
72
+ return invoke('tab_group', params, options);
73
+ }
71
74
  export function pageClick(params, options) {
72
75
  return invoke('page_click', params, options);
73
76
  }
@@ -9,9 +9,13 @@ export declare const OFFICIAL_PLUGINS: {
9
9
  readonly buc: {
10
10
  readonly description: "使用 BUC 身份请求内部服务";
11
11
  readonly installArgs: readonly ["trip/mearl", "--skill", "buc-request", "--yes"];
12
- readonly setup: "alibaba";
13
12
  readonly skill: "buc-request";
14
13
  };
14
+ readonly authx: {
15
+ readonly description: "使用 AuthX 身份请求内部服务";
16
+ readonly installArgs: readonly ["trip/mearl", "--skill", "authx-request", "--yes"];
17
+ readonly skill: "authx-request";
18
+ };
15
19
  readonly tdbank: {
16
20
  readonly description: "查询、借用和切换 TDBank 测试账号";
17
21
  readonly installArgs: readonly ["trip/mearl", "--skill", "tdbank-account", "--yes"];
@@ -3,9 +3,13 @@ export const OFFICIAL_PLUGINS = {
3
3
  buc: {
4
4
  description: '使用 BUC 身份请求内部服务',
5
5
  installArgs: ['trip/mearl', '--skill', 'buc-request', '--yes'],
6
- setup: 'alibaba',
7
6
  skill: 'buc-request',
8
7
  },
8
+ authx: {
9
+ description: '使用 AuthX 身份请求内部服务',
10
+ installArgs: ['trip/mearl', '--skill', 'authx-request', '--yes'],
11
+ skill: 'authx-request',
12
+ },
9
13
  tdbank: {
10
14
  description: '查询、借用和切换 TDBank 测试账号',
11
15
  installArgs: ['trip/mearl', '--skill', 'tdbank-account', '--yes'],
@@ -84,6 +84,32 @@ const PAGE_BRIDGE_SCRIPT = String.raw `(() => {
84
84
  await requestHost('clipboard-write', { text });
85
85
  };
86
86
 
87
+ const openExternalLink = async url => {
88
+ if (typeof url !== 'string' || !url || url.length > 8192) {
89
+ throw new Error('External link must be a non-empty URL no longer than 8192 characters');
90
+ }
91
+ await requestHost('open-external-link', { url });
92
+ };
93
+
94
+ const handleExternalLinkClick = event => {
95
+ if (event.defaultPrevented || event.button !== 0) return;
96
+ const link = event.target?.closest?.('a[href]');
97
+ if (!link || String(link.target).toLowerCase() !== '_blank' || link.hasAttribute?.('download')) {
98
+ return;
99
+ }
100
+ let url;
101
+ try {
102
+ url = new URL(link.href);
103
+ } catch {
104
+ return;
105
+ }
106
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') return;
107
+ event.preventDefault();
108
+ void openExternalLink(url.href).catch(error => {
109
+ console.error('Failed to open external link', error);
110
+ });
111
+ };
112
+
87
113
  const toast = async (message, options = {}) => {
88
114
  if (typeof message !== 'string' || !message.trim() || message.length > 4000) {
89
115
  throw new Error('Toast message must be a non-empty string no longer than 4000 characters');
@@ -106,16 +132,213 @@ const PAGE_BRIDGE_SCRIPT = String.raw `(() => {
106
132
  throw new Error('Agent panel options must be an object');
107
133
  }
108
134
  const layout = options.layout;
109
- if (layout !== undefined && layout !== 'drawer' && layout !== 'workspace') {
110
- throw new Error('Agent panel layout must be drawer or workspace');
135
+ if (layout !== undefined && layout !== 'drawer' && layout !== 'workspace' && layout !== 'inline') {
136
+ throw new Error('Agent panel layout must be drawer, workspace, or inline');
111
137
  }
112
138
  return layout;
113
139
  };
114
140
 
141
+ const agentPanelKey = (options, required = false) => {
142
+ const key = typeof options.key === 'string' ? options.key.trim() : '';
143
+ if ((required || options.key !== undefined) && (!key || key.length > 128)) {
144
+ throw new Error('Agent panel key must be a non-empty string no longer than 128 characters');
145
+ }
146
+ return key || null;
147
+ };
148
+
149
+ const agentPanelCwd = options => {
150
+ const cwd = options.cwd;
151
+ if (
152
+ cwd !== undefined &&
153
+ (typeof cwd !== 'string' ||
154
+ !cwd.trim() ||
155
+ cwd.length > 4096 ||
156
+ !/^(?:\/|[a-zA-Z]:[\\/])/.test(cwd.trim()))
157
+ ) {
158
+ throw new Error('Agent panel cwd must be an absolute path no longer than 4096 characters');
159
+ }
160
+ return cwd === undefined ? null : cwd.trim();
161
+ };
162
+
163
+ const agentPanelContext = options => {
164
+ const context = options.context;
165
+ if (
166
+ context !== undefined &&
167
+ (typeof context !== 'string' || !context.trim() || context.length > 65536)
168
+ ) {
169
+ throw new Error('Agent panel context must be a non-empty string no longer than 65536 characters');
170
+ }
171
+ return context === undefined ? null : context.trim();
172
+ };
173
+
174
+ const inlineAgentPanels = new Map();
175
+
176
+ const stopInlineAgentPanelTracking = key => {
177
+ if (key) {
178
+ inlineAgentPanels.get(key)?.cleanup();
179
+ inlineAgentPanels.delete(key);
180
+ return;
181
+ }
182
+ for (const tracked of inlineAgentPanels.values()) tracked.cleanup();
183
+ inlineAgentPanels.clear();
184
+ };
185
+
186
+ const inlineAgentPanelAnchor = (options, key) => {
187
+ const anchor = options.anchor;
188
+ if (!(anchor instanceof Element)) {
189
+ throw new Error('Inline Agent panel anchor must be an element');
190
+ }
191
+ const rect = anchor.getBoundingClientRect();
192
+ if (![rect.top, rect.left, rect.width, rect.height].every(Number.isFinite)) {
193
+ throw new Error('Inline Agent panel anchor returned invalid bounds');
194
+ }
195
+ return {
196
+ element: anchor,
197
+ key,
198
+ bounds: { top: rect.top, left: rect.left, width: rect.width, height: rect.height },
199
+ };
200
+ };
201
+
202
+ const trackInlineAgentPanel = presentation => {
203
+ stopInlineAgentPanelTracking(presentation.key);
204
+ let frame = null;
205
+ const update = () => {
206
+ frame = null;
207
+ if (!presentation.element.isConnected || !port) return;
208
+ const rect = presentation.element.getBoundingClientRect();
209
+ if (![rect.top, rect.left, rect.width, rect.height].every(Number.isFinite)) return;
210
+ const bounds = { top: rect.top, left: rect.left, width: rect.width, height: rect.height };
211
+ if (
212
+ bounds.top === presentation.bounds.top &&
213
+ bounds.left === presentation.bounds.left &&
214
+ bounds.width === presentation.bounds.width &&
215
+ bounds.height === presentation.bounds.height
216
+ ) return;
217
+ presentation.bounds = bounds;
218
+ port.postMessage({
219
+ type: 'agent-panel-position',
220
+ key: presentation.key,
221
+ bounds,
222
+ });
223
+ };
224
+ const schedule = () => {
225
+ if (frame !== null) return;
226
+ frame = requestAnimationFrame(update);
227
+ };
228
+ const observer = typeof ResizeObserver === 'function' ? new ResizeObserver(schedule) : null;
229
+ observer?.observe(presentation.element);
230
+ observer?.observe(document.documentElement);
231
+ const mutationObserver = typeof MutationObserver === 'function'
232
+ ? new MutationObserver(schedule)
233
+ : null;
234
+ mutationObserver?.observe(document.documentElement, {
235
+ attributes: true,
236
+ childList: true,
237
+ characterData: true,
238
+ subtree: true,
239
+ });
240
+ window.addEventListener('resize', schedule);
241
+ document.addEventListener('scroll', schedule, true);
242
+ const cleanup = () => {
243
+ if (frame !== null) cancelAnimationFrame(frame);
244
+ observer?.disconnect();
245
+ mutationObserver?.disconnect();
246
+ window.removeEventListener('resize', schedule);
247
+ document.removeEventListener('scroll', schedule, true);
248
+ };
249
+ inlineAgentPanels.set(presentation.key, { presentation, cleanup });
250
+ };
251
+
252
+ const scrollInlineAgentPage = (presentation, deltaX, deltaY) => {
253
+ const scrollAxis = (axis, delta) => {
254
+ if (!delta) return;
255
+ let element = presentation.element;
256
+ while (element) {
257
+ const style = window.getComputedStyle(element);
258
+ const overflow = axis === 'x' ? style.overflowX : style.overflowY;
259
+ const scrollSize = axis === 'x' ? element.scrollWidth : element.scrollHeight;
260
+ const clientSize = axis === 'x' ? element.clientWidth : element.clientHeight;
261
+ if (/^(auto|scroll|overlay)$/.test(overflow) && scrollSize > clientSize) {
262
+ const scrollPosition = axis === 'x' ? element.scrollLeft : element.scrollTop;
263
+ const maximum = scrollSize - clientSize;
264
+ if ((delta < 0 && scrollPosition > 0) || (delta > 0 && scrollPosition < maximum - 1)) {
265
+ if (axis === 'x') element.scrollLeft += delta;
266
+ else element.scrollTop += delta;
267
+ return;
268
+ }
269
+ }
270
+ element = element.parentElement;
271
+ }
272
+ window.scrollBy(axis === 'x' ? delta : 0, axis === 'y' ? delta : 0);
273
+ };
274
+ scrollAxis('x', deltaX);
275
+ scrollAxis('y', deltaY);
276
+ };
277
+
278
+ const agentPanelPresentation = (options, layout, key) => {
279
+ if (layout !== 'inline') return null;
280
+ return inlineAgentPanelAnchor(options, key);
281
+ };
282
+
115
283
  const setAgentPanelVisible = async (visible, options = {}) => {
116
284
  if (typeof visible !== 'boolean') throw new Error('Agent panel visibility must be a boolean');
117
285
  const layout = agentPanelLayout(options);
118
- await requestHost('agent-panel', { visible, ...(layout ? { layout } : {}) });
286
+ const key = agentPanelKey(options, visible && layout === 'inline');
287
+ const cwd = visible ? agentPanelCwd(options) : null;
288
+ const context = visible ? agentPanelContext(options) : null;
289
+ const presentation = visible ? agentPanelPresentation(options, layout, key) : null;
290
+ const request = requestHost('agent-panel', {
291
+ visible,
292
+ ...(layout ? { layout } : {}),
293
+ ...(key ? { key } : {}),
294
+ ...(cwd ? { cwd } : {}),
295
+ ...(context ? { context } : {}),
296
+ ...(presentation ? { bounds: presentation.bounds } : {}),
297
+ });
298
+ if (presentation) {
299
+ await ready;
300
+ trackInlineAgentPanel(presentation);
301
+ } else if (!visible && key) {
302
+ stopInlineAgentPanelTracking(key);
303
+ } else {
304
+ stopInlineAgentPanelTracking();
305
+ }
306
+ try {
307
+ await request;
308
+ } catch (error) {
309
+ if (presentation) stopInlineAgentPanelTracking(presentation.key);
310
+ throw error;
311
+ }
312
+ };
313
+
314
+ const agentGuideText = (value, label, maximumLength) => {
315
+ const normalized = typeof value === 'string' ? value.trim() : '';
316
+ if (!normalized || normalized.length > maximumLength) {
317
+ throw new Error(label + ' must be a non-empty string no longer than ' + maximumLength + ' characters');
318
+ }
319
+ return normalized;
320
+ };
321
+
322
+ const setAgentGuides = async guides => {
323
+ if (!Array.isArray(guides) || guides.length === 0 || guides.length > 6) {
324
+ throw new Error('Agent guides must contain between 1 and 6 items');
325
+ }
326
+ const normalized = guides.map((guide, index) => {
327
+ if (!guide || typeof guide !== 'object' || Array.isArray(guide)) {
328
+ throw new Error('Agent guide ' + index + ' must be an object');
329
+ }
330
+ const icon = guide.icon === undefined ? undefined : agentGuideText(guide.icon, 'Agent guide icon', 40);
331
+ if (icon && !/^[a-z][a-z0-9-]*$/.test(icon)) {
332
+ throw new Error('Agent guide icon must use kebab-case');
333
+ }
334
+ return {
335
+ title: agentGuideText(guide.title, 'Agent guide title', 80),
336
+ description: agentGuideText(guide.description, 'Agent guide description', 160),
337
+ prompt: agentGuideText(guide.prompt, 'Agent guide prompt', 4000),
338
+ ...(icon ? { icon } : {}),
339
+ };
340
+ });
341
+ await requestHost('agent-guides', { guides: normalized });
119
342
  };
120
343
 
121
344
  const runAgentTask = async (prompt, options = {}) => {
@@ -126,7 +349,13 @@ const PAGE_BRIDGE_SCRIPT = String.raw `(() => {
126
349
  throw new Error('Agent task options must be an object');
127
350
  }
128
351
  const cwd = options.cwd;
352
+ const ephemeral = options.ephemeral;
129
353
  const layout = agentPanelLayout(options);
354
+ const key = agentPanelKey(options, layout === 'inline');
355
+ const presentation = agentPanelPresentation(options, layout, key);
356
+ if (ephemeral !== undefined && ephemeral !== false) {
357
+ throw new Error('Temporary Agent conversations are not supported');
358
+ }
130
359
  if (
131
360
  cwd !== undefined &&
132
361
  (typeof cwd !== 'string' ||
@@ -136,11 +365,25 @@ const PAGE_BRIDGE_SCRIPT = String.raw `(() => {
136
365
  ) {
137
366
  throw new Error('Agent task cwd must be an absolute path no longer than 4096 characters');
138
367
  }
139
- await requestHost('agent-task', {
368
+ const request = requestHost('agent-task', {
140
369
  prompt: prompt.trim(),
141
370
  ...(cwd !== undefined ? { cwd: cwd.trim() } : {}),
142
371
  ...(layout ? { layout } : {}),
372
+ ...(key ? { key } : {}),
373
+ ...(presentation ? { bounds: presentation.bounds } : {}),
143
374
  });
375
+ if (presentation) {
376
+ await ready;
377
+ trackInlineAgentPanel(presentation);
378
+ } else {
379
+ stopInlineAgentPanelTracking();
380
+ }
381
+ try {
382
+ await request;
383
+ } catch (error) {
384
+ if (presentation) stopInlineAgentPanelTracking(presentation.key);
385
+ throw error;
386
+ }
144
387
  };
145
388
 
146
389
  const setQueryParam = async (name, value) => {
@@ -155,7 +398,7 @@ const PAGE_BRIDGE_SCRIPT = String.raw `(() => {
155
398
  return query;
156
399
  };
157
400
 
158
- const api = { copyText, invoke, operation, ready: () => ready, runAgentTask, runningOperations, selectDirectory, setAgentPanelVisible, setQueryParam, start, toast };
401
+ const api = { copyText, invoke, operation, ready: () => ready, runAgentTask, runningOperations, selectDirectory, setAgentGuides, setAgentPanelVisible, setQueryParam, start, toast };
159
402
  const ui = globalThis.__mearlToolboxUi;
160
403
  if (ui !== undefined) {
161
404
  if (!ui || typeof ui !== 'object' || ui.runtime !== 'preact') {
@@ -166,6 +409,7 @@ const PAGE_BRIDGE_SCRIPT = String.raw `(() => {
166
409
  }
167
410
  Object.defineProperty(api, 'context', { enumerable: true, get: () => context });
168
411
  Object.defineProperty(window, 'mearl', { configurable: false, value: Object.freeze(api) });
412
+ document.addEventListener('click', handleExternalLinkClick);
169
413
 
170
414
  const applyContext = nextContext => {
171
415
  const theme =
@@ -198,6 +442,22 @@ const PAGE_BRIDGE_SCRIPT = String.raw `(() => {
198
442
  applyContext(response.context);
199
443
  return;
200
444
  }
445
+ if (
446
+ response?.type === 'agent-panel-wheel' &&
447
+ typeof response.key === 'string' &&
448
+ inlineAgentPanels.has(response.key) &&
449
+ typeof response.deltaX === 'number' &&
450
+ Number.isFinite(response.deltaX) &&
451
+ typeof response.deltaY === 'number' &&
452
+ Number.isFinite(response.deltaY)
453
+ ) {
454
+ scrollInlineAgentPage(
455
+ inlineAgentPanels.get(response.key).presentation,
456
+ response.deltaX,
457
+ response.deltaY,
458
+ );
459
+ return;
460
+ }
201
461
  if (response?.type !== 'result' || typeof response.requestId !== 'string') return;
202
462
  const request = pending.get(response.requestId);
203
463
  if (!request) return;
@@ -1,6 +1,8 @@
1
1
  export interface CloudServerConfig {
2
2
  server: string;
3
3
  }
4
+ /** Number of local calendar days between CLI update checks; null disables them. */
5
+ export declare function loadCliAutoUpdateIntervalDays(configPath?: string): number | null;
4
6
  export declare function getCloudServerConfigPath(): string;
5
7
  export declare function loadCloudServerConfig(): CloudServerConfig | null;
6
8
  export declare function loadLocalConnectorId(): string | null;
@@ -2,6 +2,29 @@ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
3
  import { join } from 'node:path';
4
4
  const MEARL_CONFIG_DIR = join(homedir(), '.mearl');
5
+ /** Number of local calendar days between CLI update checks; null disables them. */
6
+ export function loadCliAutoUpdateIntervalDays(configPath = join(MEARL_CONFIG_DIR, 'config.json')) {
7
+ try {
8
+ const config = JSON.parse(readFileSync(configPath, 'utf8'));
9
+ if (config.cliAutoUpdate === false)
10
+ return null;
11
+ if (config.cliAutoUpdate === undefined)
12
+ return 1;
13
+ if (typeof config.cliAutoUpdate === 'object' &&
14
+ config.cliAutoUpdate !== null &&
15
+ !Array.isArray(config.cliAutoUpdate)) {
16
+ const { intervalDays } = config.cliAutoUpdate;
17
+ if (typeof intervalDays === 'number' &&
18
+ Number.isSafeInteger(intervalDays) &&
19
+ intervalDays > 0)
20
+ return intervalDays;
21
+ }
22
+ }
23
+ catch {
24
+ // A missing or invalid config uses the daily default.
25
+ }
26
+ return 1;
27
+ }
5
28
  export function getCloudServerConfigPath() {
6
29
  return join(MEARL_CONFIG_DIR, 'cloud-server.json');
7
30
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mearl/client",
3
- "version": "2.18.0",
3
+ "version": "2.19.1",
4
4
  "description": "Unified Mearl SDK & CLI for local and remote browsers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -59,8 +59,8 @@
59
59
  "commander": "^15.0.0",
60
60
  "fastest-levenshtein": "^1.0.16",
61
61
  "ws": "^8.18.0",
62
- "@mearl/cloud-types": "2.18.0",
63
- "@mearl/provider": "2.18.0"
62
+ "@mearl/cloud-types": "2.19.1",
63
+ "@mearl/provider": "2.19.1"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@types/node": "^24.9.1",