@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.
- package/README.md +43 -20
- package/dist/actionTimeouts.js +2 -4
- package/dist/browserRegistry.d.ts +8 -6
- package/dist/buc.d.ts +65 -0
- package/dist/buc.js +533 -0
- package/dist/check.d.ts +9 -9
- package/dist/check.js +9 -7
- package/dist/cli.js +94 -18
- package/dist/commands.d.ts +22 -122
- package/dist/commands.js +21 -125
- package/dist/generated-browser-action-protocol.d.ts +117 -94
- package/dist/generated-browser-action-protocol.js +0 -9
- package/dist/index.d.ts +3 -5
- package/dist/index.js +1 -10
- package/dist/plugin.d.ts +26 -2
- package/dist/plugin.js +52 -3
- package/dist/pluginRegistry.d.ts +5 -1
- package/dist/pluginRegistry.js +15 -3
- package/dist/socket.js +33 -18
- package/dist/unified.d.ts +3 -0
- package/dist/unified.js +25 -13
- package/package.json +3 -3
|
@@ -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,
|
|
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 {
|
|
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'
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
+
}
|
package/dist/pluginRegistry.d.ts
CHANGED
|
@@ -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;
|
package/dist/pluginRegistry.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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: '
|
|
81
|
-
status: browser.status
|
|
80
|
+
type: 'managed',
|
|
81
|
+
status: browser.status ?? 'running_disconnected',
|
|
82
82
|
transport: 'provider',
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
|
103
|
-
|
|
104
|
-
|
|
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(
|
|
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(
|
|
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 (
|
|
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') &&
|
|
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
|
|
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
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
63
|
-
"@mearl/provider": "2.
|
|
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",
|