@mearl/client 2.13.0 → 2.15.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.
@@ -64,14 +64,12 @@ export const BROWSER_ACTIONS = {
64
64
  get_requests: true,
65
65
  get_logs: true,
66
66
  get_events: true,
67
- get_api_schema: true,
68
67
  set_mock: true,
69
68
  get_mocks: true,
70
69
  set_rule: true,
71
70
  get_rules: true,
72
71
  send_request: true,
73
72
  send_mtop_request: true,
74
- tdbank_account: true,
75
73
  browser_list: true,
76
74
  browser_select_browser: true,
77
75
  browser_release: true,
@@ -102,7 +100,6 @@ export const BROWSER_ACTIONS = {
102
100
  record_start: true,
103
101
  record_stop: true,
104
102
  request_domain_permission: true,
105
- get_user_info: true,
106
103
  run_actions: true,
107
104
  };
108
105
  export const BROWSER_COMMAND_ACTIONS = {
@@ -111,14 +108,12 @@ export const BROWSER_COMMAND_ACTIONS = {
111
108
  get_requests: true,
112
109
  get_logs: true,
113
110
  get_events: true,
114
- get_api_schema: true,
115
111
  set_mock: true,
116
112
  get_mocks: true,
117
113
  set_rule: true,
118
114
  get_rules: true,
119
115
  send_request: true,
120
116
  send_mtop_request: true,
121
- tdbank_account: true,
122
117
  browser_list: true,
123
118
  browser_release: true,
124
119
  browser_launch: true,
@@ -148,7 +143,6 @@ export const BROWSER_COMMAND_ACTIONS = {
148
143
  record_start: true,
149
144
  record_stop: true,
150
145
  request_domain_permission: true,
151
- get_user_info: true,
152
146
  run_actions: true,
153
147
  };
154
148
  export const EXTENSION_BROWSER_ACTIONS = {
@@ -157,14 +151,12 @@ export const EXTENSION_BROWSER_ACTIONS = {
157
151
  get_requests: true,
158
152
  get_logs: true,
159
153
  get_events: true,
160
- get_api_schema: true,
161
154
  set_mock: true,
162
155
  get_mocks: true,
163
156
  set_rule: true,
164
157
  get_rules: true,
165
158
  send_request: true,
166
159
  send_mtop_request: true,
167
- tdbank_account: true,
168
160
  browser_release: true,
169
161
  page_screenshot: true,
170
162
  page_selected_element: true,
@@ -191,7 +183,6 @@ export const EXTENSION_BROWSER_ACTIONS = {
191
183
  record_start: true,
192
184
  record_stop: true,
193
185
  request_domain_permission: true,
194
- get_user_info: true,
195
186
  run_actions: true,
196
187
  };
197
188
  export function isBrowserAction(value) {
package/dist/index.d.ts CHANGED
@@ -4,10 +4,11 @@
4
4
  * Discovers browser targets on this machine and every configured cloud connector,
5
5
  * then routes actions using the global browser ids returned by browser_list.
6
6
  */
7
- export { UnifiedClient, UnifiedClient as MearlClient, decodeBrowserTarget, invoke, listCloudConnectors, } from './unified.js';
7
+ export { UnifiedClient, UnifiedClient as MearlClient, decodeBrowserTarget, invoke, invokePlugin, listCloudConnectors, } from './unified.js';
8
8
  export type { UnifiedClientOptions, UnifiedInvokeOptions, UnifiedInvokeOptions as InvokeOptions, } from './unified.js';
9
+ export type { PluginInvocation, PluginInvokeOptions } from './plugin.js';
9
10
  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';
10
- export type { ActionStep, AppProfileName, BrowserAction, BrowserActionMap, BrowserActionRequest, BrowserActionResponse, BrowserCapabilities, BrowserCapabilityAction, BrowserCloseParams, BrowserCommandAction, BrowserInfo, BrowserInfoBase, BrowserLaunchParams, BrowserListResult, BrowserPairingInfo, BrowserReleaseFailure, BrowserReleaseResult, BrowserSource, BrowserStatus, BrowserScreenshotKind, BrowserTransport, 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
+ 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, 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, } from './generated-browser-action-protocol.js';
11
12
  import { type UnifiedInvokeOptions } from './unified.js';
12
13
  import type * as Protocol from './generated-browser-action-protocol.js';
13
14
  export declare function tabCheckpoint(params?: Protocol.CaptureCheckpointParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'tab_checkpoint'>>;
@@ -18,10 +19,8 @@ export declare function getLogs(params?: Protocol.GetLogsParams, options?: Unifi
18
19
  export declare function getEvents(params?: Protocol.GetEventsParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_events'>>;
19
20
  export declare function setMock(params: Protocol.SetMockParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'set_mock'>>;
20
21
  export declare function getMocks(params?: Protocol.GetMocksParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_mocks'>>;
21
- export declare function getApiSchema(params: Protocol.GetApiSchemaParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_api_schema'>>;
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
- export declare function tdbankAccount(params?: Protocol.TdbankAccountParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'tdbank_account'>>;
25
24
  export declare function browserList(options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'browser_list'>>;
26
25
  export declare function selectBrowser(params?: Protocol.SelectBrowserParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'browser_select_browser'>>;
27
26
  export declare function browserRelease(options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'browser_release'>>;
@@ -51,5 +50,4 @@ export declare function setAppProfile(params: Protocol.SetAppProfileParams, opti
51
50
  export declare function setTimezone(params: Protocol.SetTimezoneParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'set_timezone'>>;
52
51
  export declare function getCookie(params: Protocol.GetCookieParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_cookie'>>;
53
52
  export declare function setCookie(params: Protocol.SetCookieParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'set_cookie'>>;
54
- export declare function getUserInfo(params?: Protocol.GetUserInfoParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_user_info'>>;
55
53
  export declare function runActions(params: Protocol.RunActionsParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'run_actions'>>;
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Discovers browser targets on this machine and every configured cloud connector,
5
5
  * then routes actions using the global browser ids returned by browser_list.
6
6
  */
7
- export { UnifiedClient, UnifiedClient as MearlClient, decodeBrowserTarget, invoke, listCloudConnectors, } from './unified.js';
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
10
  export function tabCheckpoint(params = {}, options) {
@@ -29,18 +29,12 @@ export function setMock(params, options) {
29
29
  export function getMocks(params = {}, options) {
30
30
  return invoke('get_mocks', params, options);
31
31
  }
32
- export function getApiSchema(params, options) {
33
- return invoke('get_api_schema', params, options);
34
- }
35
32
  export function sendRequest(params, options) {
36
33
  return invoke('send_request', params, options);
37
34
  }
38
35
  export function sendMtopRequest(params, options) {
39
36
  return invoke('send_mtop_request', params, options);
40
37
  }
41
- export function tdbankAccount(params = {}, options) {
42
- return invoke('tdbank_account', params, options);
43
- }
44
38
  export function browserList(options) {
45
39
  return invoke('browser_list', {}, options);
46
40
  }
@@ -128,9 +122,6 @@ export function getCookie(params, options) {
128
122
  export function setCookie(params, options) {
129
123
  return invoke('set_cookie', params, options);
130
124
  }
131
- export function getUserInfo(params = {}, options) {
132
- return invoke('get_user_info', params, options);
133
- }
134
125
  export function runActions(params, options) {
135
126
  return invoke('run_actions', params, options);
136
127
  }
package/dist/plugin.d.ts CHANGED
@@ -1,18 +1,42 @@
1
+ import { type BucApi } from './buc.js';
1
2
  import type { UnifiedInvokeOptions } from './unified.js';
2
- export { assertPluginName, findPluginEntry, findPluginRegistration, listPluginNames, registerPlugin, resolvePluginEntry, unregisterPlugin, PluginError, type PluginRegistration, } from './pluginRegistry.js';
3
+ export { type BucApi, type BucIdentity, type BucProfile, type BucRequestAuth, type BucRequestFormValue, type BucRequestParams, type BucRequestResult, } from './buc.js';
4
+ export { assertPluginName, findPluginEntry, findPluginRegistration, listPluginNames, registerPlugin, resolvePluginEntry, unregisterPlugin, PluginError, type PluginRegistration, type PluginRegistrationOptions, } from './pluginRegistry.js';
3
5
  export type PluginOptions = UnifiedInvokeOptions;
6
+ export interface PluginInvocation {
7
+ command: string;
8
+ input: Record<string, any>;
9
+ }
10
+ export interface PluginInvokeOptions extends UnifiedInvokeOptions {
11
+ onProgress?: (message: string) => void;
12
+ }
13
+ export interface PluginExecutionContext {
14
+ buc?: BucApi;
15
+ invoke(action: string, payload?: Record<string, any>, options?: UnifiedInvokeOptions): Promise<any>;
16
+ }
4
17
  export interface PluginApp {
5
18
  readonly options: Readonly<PluginOptions>;
19
+ readonly buc: BucApi;
6
20
  command(signature: string, handler: PluginHandler, description?: string): void;
7
21
  invoke(action: string, payload?: Record<string, any>, options?: UnifiedInvokeOptions): Promise<any>;
8
22
  progress(message: string): void;
9
23
  }
10
24
  export type PluginHandler = (input: Record<string, any>) => unknown | Promise<unknown>;
11
25
  /** 注册和执行共享一个 app;仅 handler 执行期间开放浏览器调用。 */
12
- export declare function loadPlugin(entry: string, context: Pick<PluginApp, 'invoke' | 'options' | 'progress'>, pluginName?: string): Promise<{
26
+ export declare function loadPlugin(entry: string, context: Pick<PluginApp, 'invoke' | 'options' | 'progress'> & {
27
+ buc?: BucApi;
28
+ }, pluginName?: string): Promise<{
29
+ name: string | undefined;
30
+ description: string | undefined;
31
+ version: string | undefined;
32
+ help(namespace: string, name?: string): string;
33
+ run(positionals: string[], payload?: unknown): Promise<unknown>;
34
+ }>;
35
+ export declare function loadRegisteredPlugin(name: string, context: PluginExecutionContext, options?: PluginInvokeOptions): Promise<{
13
36
  name: string | undefined;
14
37
  description: string | undefined;
15
38
  version: string | undefined;
16
39
  help(namespace: string, name?: string): string;
17
40
  run(positionals: string[], payload?: unknown): Promise<unknown>;
18
41
  }>;
42
+ export declare function invokeRegisteredPlugin(name: string, invocation: PluginInvocation, context: PluginExecutionContext, options?: PluginInvokeOptions): Promise<unknown>;
package/dist/plugin.js CHANGED
@@ -2,7 +2,8 @@ import { readFileSync, statSync } from 'node:fs';
2
2
  import { dirname, resolve } from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
4
  import { Command } from 'commander';
5
- import { PluginError } from './pluginRegistry.js';
5
+ import { buc } from './buc.js';
6
+ import { findPluginRegistration, PluginError } from './pluginRegistry.js';
6
7
  export { assertPluginName, findPluginEntry, findPluginRegistration, listPluginNames, registerPlugin, resolvePluginEntry, unregisterPlugin, PluginError, } from './pluginRegistry.js';
7
8
  function readPluginVersion(entry) {
8
9
  try {
@@ -31,9 +32,12 @@ function missingPluginEntryMessage(entry, pluginName) {
31
32
  }
32
33
  /** 注册和执行共享一个 app;仅 handler 执行期间开放浏览器调用。 */
33
34
  export async function loadPlugin(entry, context, pluginName) {
35
+ let entryVersion;
34
36
  try {
35
- if (!statSync(entry).isFile())
37
+ const stat = statSync(entry);
38
+ if (!stat.isFile())
36
39
  throw new Error('not a file');
40
+ entryVersion = `${stat.mtimeMs}-${stat.size}`;
37
41
  }
38
42
  catch {
39
43
  throw new PluginError('PLUGIN_ENTRY_MISSING', missingPluginEntryMessage(entry, pluginName));
@@ -45,6 +49,23 @@ export async function loadPlugin(entry, context, pluginName) {
45
49
  let description;
46
50
  const app = Object.freeze({
47
51
  options: Object.freeze({ ...context.options }),
52
+ buc: Object.freeze({
53
+ request(params) {
54
+ if (!executing)
55
+ throw new Error('app.buc.request 只能在命令执行期间调用');
56
+ return (context.buc ?? buc).request(params);
57
+ },
58
+ whoami() {
59
+ if (!executing)
60
+ throw new Error('app.buc.whoami 只能在命令执行期间调用');
61
+ return (context.buc ?? buc).whoami();
62
+ },
63
+ profile() {
64
+ if (!executing)
65
+ throw new Error('app.buc.profile 只能在命令执行期间调用');
66
+ return (context.buc ?? buc).profile();
67
+ },
68
+ }),
48
69
  command(signature, handler, description = '') {
49
70
  if (!registering)
50
71
  throw new Error('命令只能在入口注册阶段声明');
@@ -78,7 +99,9 @@ export async function loadPlugin(entry, context, pluginName) {
78
99
  },
79
100
  });
80
101
  try {
81
- const module = await import(pathToFileURL(entry).href);
102
+ const moduleUrl = pathToFileURL(entry);
103
+ moduleUrl.searchParams.set('mearl-version', entryVersion);
104
+ const module = await import(moduleUrl.href);
82
105
  if (module.name !== undefined && typeof module.name !== 'string')
83
106
  throw new Error('入口的 name 导出必须为字符串');
84
107
  name = module.name;
@@ -171,3 +194,29 @@ export async function loadPlugin(entry, context, pluginName) {
171
194
  },
172
195
  };
173
196
  }
197
+ export async function loadRegisteredPlugin(name, context, options = {}) {
198
+ const registration = findPluginRegistration(name);
199
+ if (!registration) {
200
+ throw new PluginError('PLUGIN_NOT_REGISTERED', `插件未注册: ${name}`);
201
+ }
202
+ const { onProgress, ...invokeOptions } = options;
203
+ return loadPlugin(registration.entry, {
204
+ options: invokeOptions,
205
+ ...(context.buc ? { buc: context.buc } : {}),
206
+ progress: onProgress ?? (() => undefined),
207
+ async invoke(action, payload, overrides) {
208
+ const result = await context.invoke(action, payload, {
209
+ ...invokeOptions,
210
+ ...overrides,
211
+ });
212
+ if (result?.action?.success === false) {
213
+ throw new Error(result.action.error || `${action} failed`);
214
+ }
215
+ return result;
216
+ },
217
+ }, name);
218
+ }
219
+ export async function invokeRegisteredPlugin(name, invocation, context, options = {}) {
220
+ const plugin = await loadRegisteredPlugin(name, context, options);
221
+ return plugin.run([], invocation);
222
+ }
@@ -5,11 +5,15 @@ export declare class PluginError extends Error {
5
5
  export interface PluginRegistration {
6
6
  entry: string;
7
7
  description?: string;
8
+ managedBy?: string;
9
+ }
10
+ export interface PluginRegistrationOptions {
11
+ managedBy?: string;
8
12
  }
9
13
  export declare function pluginDirectory(): string;
10
14
  export declare function assertPluginName(name: string): void;
11
15
  export declare function resolvePluginEntry(target: string): string;
12
- export declare function registerPlugin(name: string, target: string, description?: string): string;
16
+ export declare function registerPlugin(name: string, target: string, description?: string, options?: PluginRegistrationOptions): string;
13
17
  export declare function unregisterPlugin(name: string): boolean;
14
18
  export declare function findPluginRegistration(name: string): PluginRegistration | undefined;
15
19
  export declare function findPluginEntry(name: string): string | undefined;
@@ -40,7 +40,7 @@ export function resolvePluginEntry(target) {
40
40
  throw new PluginError('PLUGIN_ENTRY_MISSING', `入口不存在: ${absolute}(目录默认使用 scripts/main.mjs)`);
41
41
  }
42
42
  }
43
- export function registerPlugin(name, target, description) {
43
+ export function registerPlugin(name, target, description, options = {}) {
44
44
  assertPluginName(name);
45
45
  const entry = resolvePluginEntry(target);
46
46
  const directory = pluginDirectory();
@@ -49,7 +49,12 @@ export function registerPlugin(name, target, description) {
49
49
  // 每个插件独立保存,多个名称并发注册不会覆盖彼此;同名注册原子替换绑定。
50
50
  const temporary = `${file}.${randomUUID()}.tmp`;
51
51
  const summary = description?.trim();
52
- const record = { entry, ...(summary ? { description: summary } : {}) };
52
+ const managedBy = options.managedBy?.trim();
53
+ const record = {
54
+ entry,
55
+ ...(summary ? { description: summary } : {}),
56
+ ...(managedBy ? { managedBy } : {}),
57
+ };
53
58
  try {
54
59
  writeFileSync(temporary, `${JSON.stringify(record)}\n`, { mode: 0o600, flag: 'wx' });
55
60
  renameSync(temporary, file);
@@ -92,7 +97,14 @@ export function findPluginRegistration(name) {
92
97
  const description = typeof record.description === 'string' && record.description.trim()
93
98
  ? record.description.trim()
94
99
  : undefined;
95
- return { entry: record.entry, ...(description ? { description } : {}) };
100
+ const managedBy = typeof record.managedBy === 'string' && record.managedBy.trim()
101
+ ? record.managedBy.trim()
102
+ : undefined;
103
+ return {
104
+ entry: record.entry,
105
+ ...(description ? { description } : {}),
106
+ ...(managedBy ? { managedBy } : {}),
107
+ };
96
108
  }
97
109
  catch {
98
110
  throw new PluginError('PLUGIN_REGISTRY_INVALID', `无效的插件记录: ${file}`);
package/dist/socket.js CHANGED
@@ -77,14 +77,16 @@ function browserListFromProviderRecords(browsers) {
77
77
  .map(browser => ({
78
78
  browserId: browser.browserId,
79
79
  name: browser.browserName,
80
- type: 'regular',
81
- status: browser.status === 'pairing' ? 'pairing' : 'connected',
80
+ type: 'managed',
81
+ status: browser.status ?? 'running_disconnected',
82
82
  transport: 'provider',
83
- providerId: browser.providerId,
84
- providerName: browser.providerName,
85
- providerVersion: browser.providerVersion,
86
- providerProtocolVersion: browser.providerProtocolVersion,
83
+ provider: browser.provider,
84
+ ownership: browser.ownership,
85
+ persistence: browser.persistence,
86
+ operations: browser.operations,
87
87
  pairing: browser.pairing,
88
+ references: browser.references,
89
+ details: browser.details,
88
90
  }));
89
91
  return {
90
92
  count: providerBrowsers.length,
@@ -99,11 +101,23 @@ function mergeWarnings(result, warnings) {
99
101
  const merged = [...new Set([...resultWarnings, ...warnings])];
100
102
  return { ...result, ...(merged.length > 0 ? { warnings: merged } : {}) };
101
103
  }
102
- function supportsAction(browser, action) {
103
- if (browser.transport === 'provider' &&
104
- (action === 'get_versions' || action === 'browser_close')) {
104
+ function isConnectedBrowser(browser) {
105
+ return browser.status === undefined || browser.status === 'connected';
106
+ }
107
+ function supportsAction(browser, action, data) {
108
+ if (action === 'get_versions')
105
109
  return true;
110
+ if (action === 'get_browser_access_url') {
111
+ return browser.transport !== 'provider' || browser.operations?.includes('access') === true;
112
+ }
113
+ if (action === 'browser_close') {
114
+ if (browser.transport !== 'provider')
115
+ return true;
116
+ const required = data.deleteProfile === true ? 'deleteData' : 'close';
117
+ return browser.operations?.includes(required) === true;
106
118
  }
119
+ if (!isConnectedBrowser(browser))
120
+ return false;
107
121
  return (browser.transport !== 'provider' ||
108
122
  browser.capabilities?.actions.some(capability => capability === action) === true);
109
123
  }
@@ -117,7 +131,7 @@ function selectBrowserResult(data) {
117
131
  };
118
132
  }
119
133
  const browsers = listLiveBrowsers();
120
- const controllableBrowsers = browsers.filter(browser => browser.status !== 'pairing');
134
+ const controllableBrowsers = browsers.filter(isConnectedBrowser);
121
135
  const match = findBrowser(controllableBrowsers, selector);
122
136
  if (!match)
123
137
  throw new Error(formatNoMatchError(selector, browsers));
@@ -218,20 +232,20 @@ async function dispatchInvoke(action, data, timeoutSec, transport, selector, con
218
232
  }
219
233
  }
220
234
  const browsers = listLiveBrowsers();
221
- const controllableBrowsers = browsers.filter(browser => browser.status !== 'pairing');
235
+ const controllableBrowsers = browsers.filter(isConnectedBrowser);
222
236
  // Explicit target: must match a live browser; do not silently fall back.
223
237
  if (selector) {
224
238
  const match = findBrowser(browsers, selector);
225
239
  if (!match) {
226
240
  throw new Error(formatNoMatchError(selector, browsers));
227
241
  }
228
- if (match.status === 'pairing' && action !== 'get_versions' && action !== 'browser_close') {
229
- throw new Error(`Browser ${describeBrowser(match)} is waiting for device pairing`);
230
- }
231
242
  if (transport === 'cdp' && match.transport === 'provider') {
232
243
  throw new Error(`Browser ${describeBrowser(match)} does not support the CDP transport`);
233
244
  }
234
- if (!supportsAction(match, action)) {
245
+ if (match.status === 'pairing' && action !== 'get_versions' && action !== 'browser_close') {
246
+ throw new Error(`Browser ${describeBrowser(match)} is waiting for device pairing`);
247
+ }
248
+ if (!supportsAction(match, action, data)) {
235
249
  throw new Error(`Browser ${describeBrowser(match)} does not support ${action}`);
236
250
  }
237
251
  try {
@@ -248,7 +262,8 @@ async function dispatchInvoke(action, data, timeoutSec, transport, selector, con
248
262
  }
249
263
  // No explicit target: prefer the most-recently-focused live browser; on a
250
264
  // stale socket, fall through to the legacy path + auto-spawn.
251
- const routableBrowsers = controllableBrowsers.filter(browser => !(transport === 'cdp' && browser.transport === 'provider') && supportsAction(browser, action));
265
+ const routableBrowsers = controllableBrowsers.filter(browser => !(transport === 'cdp' && browser.transport === 'provider') &&
266
+ supportsAction(browser, action, data));
252
267
  if (routableBrowsers.length > 0) {
253
268
  const target = routableBrowsers[0];
254
269
  try {
@@ -288,9 +303,9 @@ async function invokeViaLegacy(action, data, timeoutSec, transport, controlSourc
288
303
  /* isRetry */ true);
289
304
  }
290
305
  const browsers = listLiveBrowsers();
291
- const target = browsers.find(browser => browser.status !== 'pairing' &&
306
+ const target = browsers.find(browser => isConnectedBrowser(browser) &&
292
307
  !(transport === 'cdp' && browser.transport === 'provider') &&
293
- supportsAction(browser, action));
308
+ supportsAction(browser, action, data));
294
309
  if (target) {
295
310
  return invokeOnce(action, data, timeoutSec, transport, target.socketPath, controlSource, target.providerBrowserId);
296
311
  }
package/dist/unified.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type CloudConnectorListResult } from '@mearl/cloud-types';
2
+ import type { PluginInvocation, PluginInvokeOptions } from './plugin.js';
2
3
  export interface UnifiedInvokeOptions {
3
4
  timeoutSec?: number;
4
5
  /** Global browser id, source-local browser id, or unique browser name. */
@@ -36,6 +37,7 @@ export declare class UnifiedClient {
36
37
  private closing;
37
38
  constructor(options?: UnifiedClientOptions);
38
39
  disconnect(): void;
40
+ invokePlugin(name: string, invocation: PluginInvocation, options?: PluginInvokeOptions): Promise<unknown>;
39
41
  invoke(action: string, data?: Record<string, any>, options?: UnifiedInvokeOptions): Promise<any>;
40
42
  private invokeExplicitLocal;
41
43
  private invokeLaunchWithoutControlBrowser;
@@ -61,5 +63,6 @@ export declare class UnifiedClient {
61
63
  private rejectAllPending;
62
64
  }
63
65
  export declare function invoke(action: string, data?: Record<string, any>, options?: UnifiedInvokeOptions): Promise<any>;
66
+ export declare function invokePlugin(name: string, invocation: PluginInvocation, options?: PluginInvokeOptions): Promise<unknown>;
64
67
  export declare function listCloudConnectors(): Promise<CloudConnectorListResult>;
65
68
  export {};
package/dist/unified.js CHANGED
@@ -13,8 +13,10 @@ function encodeBrowserTarget(target) {
13
13
  }
14
14
  function omitTransientBrowserSecrets(browser) {
15
15
  const safeBrowser = { ...browser };
16
- delete safeBrowser.agentbayAccessUrl;
17
- delete safeBrowser.accessUrl;
16
+ for (const key of Object.keys(safeBrowser)) {
17
+ if (/accessUrl$/i.test(key))
18
+ delete safeBrowser[key];
19
+ }
18
20
  return safeBrowser;
19
21
  }
20
22
  export function decodeBrowserTarget(selector) {
@@ -117,6 +119,12 @@ export class UnifiedClient {
117
119
  this.ws?.close();
118
120
  this.ws = null;
119
121
  }
122
+ async invokePlugin(name, invocation, options = {}) {
123
+ const { invokeRegisteredPlugin } = await import('./plugin.js');
124
+ return invokeRegisteredPlugin(name, invocation, {
125
+ invoke: (action, payload, invokeOptions) => this.invoke(action, payload, invokeOptions),
126
+ }, options);
127
+ }
120
128
  async invoke(action, data = {}, options = {}) {
121
129
  const timeoutSec = options.timeoutSec ?? resolveActionTimeoutSec(action, data, this.options.requestTimeoutSec);
122
130
  if (action === 'connector_list')
@@ -420,17 +428,7 @@ export class UnifiedClient {
420
428
  decorateActionResult(action, result, source) {
421
429
  if (action !== 'browser_launch' || typeof result?.browserId !== 'string')
422
430
  return result;
423
- const safeResult = omitTransientBrowserSecrets(result);
424
- const originalBrowserId = result.browserId;
425
- return {
426
- ...safeResult,
427
- browserId: encodeBrowserTarget({
428
- browser: originalBrowserId,
429
- ...(source.connectorId ? { connector: source.connectorId } : {}),
430
- }),
431
- originalBrowserId,
432
- source,
433
- };
431
+ return decorateBrowser(result, source);
434
432
  }
435
433
  async ensureConnected() {
436
434
  if (this.ws?.readyState === WebSocket.OPEN)
@@ -579,6 +577,20 @@ export async function invoke(action, data = {}, options = {}) {
579
577
  client.disconnect();
580
578
  }
581
579
  }
580
+ export async function invokePlugin(name, invocation, options = {}) {
581
+ const serverUrl = process.env.MEARL_SERVER_URL ?? loadCloudServerConfig()?.server;
582
+ const client = new UnifiedClient({
583
+ ...(serverUrl ? { serverUrl } : {}),
584
+ defaultBrowser,
585
+ defaultConnector,
586
+ });
587
+ try {
588
+ return await client.invokePlugin(name, invocation, options);
589
+ }
590
+ finally {
591
+ client.disconnect();
592
+ }
593
+ }
582
594
  export function listCloudConnectors() {
583
595
  return invoke('connector_list', {});
584
596
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mearl/client",
3
- "version": "2.13.0",
3
+ "version": "2.15.0",
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.13.0",
63
- "@mearl/provider": "2.13.0"
62
+ "@mearl/cloud-types": "2.15.0",
63
+ "@mearl/provider": "2.15.0"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@types/node": "^24.9.1",