@mearl/client 2.19.2 → 2.20.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 +35 -3
- package/dist/actionTimeouts.js +2 -0
- package/dist/artifacts.d.ts +17 -0
- package/dist/artifacts.js +80 -0
- package/dist/check.js +16 -4
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +47 -3
- package/dist/commands.d.ts +138 -14
- package/dist/commands.js +276 -9
- package/dist/generated-browser-action-protocol.d.ts +122 -9
- package/dist/generated-browser-action-protocol.js +24 -6
- package/dist/index.d.ts +10 -1
- package/dist/index.js +15 -3
- package/dist/plugin.d.ts +5 -2
- package/dist/plugin.js +66 -21
- package/dist/pluginPageBundle.d.ts +1 -0
- package/dist/pluginPageBundle.js +54 -1
- package/dist/pluginRegistry.d.ts +5 -0
- package/dist/pluginRegistry.js +28 -0
- package/dist/pluginSource.d.ts +2 -0
- package/dist/pluginSource.js +8 -0
- package/dist/socket.d.ts +1 -0
- package/dist/socket.js +208 -52
- package/dist/unified.d.ts +17 -0
- package/dist/unified.js +119 -9
- package/package.json +3 -3
|
@@ -173,7 +173,7 @@ export interface BrowserInfoBase {
|
|
|
173
173
|
}
|
|
174
174
|
export type BrowserScreenshotKind = 'native' | 'reconstructed' | 'none';
|
|
175
175
|
/** Browser-targeted actions that an external provider may implement. */
|
|
176
|
-
export type BrowserCapabilityAction = ExtensionBrowserAction;
|
|
176
|
+
export type BrowserCapabilityAction = ExtensionBrowserAction | 'page_download';
|
|
177
177
|
/** Capabilities advertised by an external browser-control provider. */
|
|
178
178
|
export interface BrowserCapabilities {
|
|
179
179
|
actions: BrowserCapabilityAction[];
|
|
@@ -222,6 +222,52 @@ export interface BrowserReleaseResult {
|
|
|
222
222
|
affectedTabIds: number[];
|
|
223
223
|
failures?: BrowserReleaseFailure[];
|
|
224
224
|
}
|
|
225
|
+
export interface ControlSessionInfo {
|
|
226
|
+
sessionId: string;
|
|
227
|
+
browserId: string;
|
|
228
|
+
transport: BrowserTransport;
|
|
229
|
+
status: 'active' | 'user' | 'releasing' | 'released';
|
|
230
|
+
generation: number;
|
|
231
|
+
tabs: Array<{
|
|
232
|
+
tabId: number;
|
|
233
|
+
source: 'created' | 'attached';
|
|
234
|
+
title?: string;
|
|
235
|
+
url?: string;
|
|
236
|
+
}>;
|
|
237
|
+
reason?: string;
|
|
238
|
+
}
|
|
239
|
+
export interface BrowserReleaseParams {
|
|
240
|
+
/** Close only session-created pages; attached user pages are protected. */
|
|
241
|
+
closeCreatedTabs?: boolean;
|
|
242
|
+
keepTabIds?: number[];
|
|
243
|
+
/** Preserve the browser environment by default for a scoped release. */
|
|
244
|
+
preserveEnvironment?: boolean;
|
|
245
|
+
keepArtifactIds?: string[];
|
|
246
|
+
}
|
|
247
|
+
export interface BrowserControlParams {
|
|
248
|
+
operation: 'handoff' | 'resume';
|
|
249
|
+
tabId?: number;
|
|
250
|
+
reason?: string;
|
|
251
|
+
}
|
|
252
|
+
export interface BrowserArtifact {
|
|
253
|
+
artifactId: string;
|
|
254
|
+
filename: string;
|
|
255
|
+
bytes: number;
|
|
256
|
+
location: 'browser';
|
|
257
|
+
/** Global browser route is attached by the unified client. */
|
|
258
|
+
browserId?: string;
|
|
259
|
+
}
|
|
260
|
+
export interface PageDownloadParams {
|
|
261
|
+
tabId: number;
|
|
262
|
+
trigger: Omit<PageClickParams, 'tabId'>;
|
|
263
|
+
/** Restrict the expected download URL; useful when the browser cannot identify its source. */
|
|
264
|
+
url?: string;
|
|
265
|
+
timeout?: number;
|
|
266
|
+
}
|
|
267
|
+
export interface PageDownloadResult {
|
|
268
|
+
download: BrowserArtifact;
|
|
269
|
+
action: unknown;
|
|
270
|
+
}
|
|
225
271
|
export interface CookieCopyScope {
|
|
226
272
|
/** Maintained authentication-domain presets to include. */
|
|
227
273
|
presets?: Array<'mearl-services'>;
|
|
@@ -595,7 +641,14 @@ export interface PageNavigateParams {
|
|
|
595
641
|
export interface PageUploadParams {
|
|
596
642
|
tabId: number;
|
|
597
643
|
selector: string;
|
|
598
|
-
|
|
644
|
+
/** Absolute paths available to the browser host. */
|
|
645
|
+
filePaths?: string[];
|
|
646
|
+
/** SDK/CLI stages caller-local files before dispatch. Mutually exclusive with filePaths. */
|
|
647
|
+
files?: Array<{
|
|
648
|
+
filePath: string;
|
|
649
|
+
} | {
|
|
650
|
+
artifactId: string;
|
|
651
|
+
}>;
|
|
599
652
|
observe?: PageObservationWaitOptions | false;
|
|
600
653
|
diagnostics?: PageDiagnosticsSetting;
|
|
601
654
|
}
|
|
@@ -616,9 +669,10 @@ export interface SetTimezoneResult {
|
|
|
616
669
|
timezone: string | null;
|
|
617
670
|
}
|
|
618
671
|
export interface GetCookieParams {
|
|
619
|
-
|
|
672
|
+
/** Select the tab's cookie store; omitted for the browser's default store. */
|
|
673
|
+
tabId?: number;
|
|
620
674
|
name: string;
|
|
621
|
-
/** Cookie lookup URL; defaults to the
|
|
675
|
+
/** Cookie lookup URL; required without tabId, otherwise defaults to the tab URL. */
|
|
622
676
|
url?: string;
|
|
623
677
|
domain?: string;
|
|
624
678
|
path?: string;
|
|
@@ -633,9 +687,12 @@ export interface GetCookieResult {
|
|
|
633
687
|
path?: string;
|
|
634
688
|
}
|
|
635
689
|
export interface SetCookieParams {
|
|
636
|
-
|
|
690
|
+
/** Select the tab's cookie store; required when reload is true. */
|
|
691
|
+
tabId?: number;
|
|
637
692
|
name: string;
|
|
638
693
|
value: string;
|
|
694
|
+
/** Cookie target URL; required when tabId is omitted. */
|
|
695
|
+
url?: string;
|
|
639
696
|
domain?: string;
|
|
640
697
|
path?: string;
|
|
641
698
|
reload?: boolean;
|
|
@@ -715,9 +772,19 @@ export interface BrowserActionMap {
|
|
|
715
772
|
response: SelectBrowserResult;
|
|
716
773
|
};
|
|
717
774
|
browser_release: {
|
|
718
|
-
request:
|
|
775
|
+
request: BrowserReleaseParams;
|
|
719
776
|
response: BrowserReleaseResult;
|
|
720
777
|
};
|
|
778
|
+
browser_control: {
|
|
779
|
+
request: BrowserControlParams;
|
|
780
|
+
response: {
|
|
781
|
+
session: ControlSessionInfo;
|
|
782
|
+
};
|
|
783
|
+
};
|
|
784
|
+
page_download: {
|
|
785
|
+
request: PageDownloadParams;
|
|
786
|
+
response: PageDownloadResult;
|
|
787
|
+
};
|
|
721
788
|
browser_launch: {
|
|
722
789
|
request: BrowserLaunchParams;
|
|
723
790
|
response: ManagedBrowserInfo;
|
|
@@ -829,10 +896,12 @@ export interface BrowserActionMap {
|
|
|
829
896
|
}
|
|
830
897
|
export type BrowserAction = keyof BrowserActionMap;
|
|
831
898
|
export type BrowserActionRequest<A extends BrowserAction> = BrowserActionMap[A]['request'];
|
|
832
|
-
export type BrowserActionResponse<A extends BrowserAction> = BrowserActionMap[A]['response']
|
|
899
|
+
export type BrowserActionResponse<A extends BrowserAction> = BrowserActionMap[A]['response'] & {
|
|
900
|
+
session?: ControlSessionInfo;
|
|
901
|
+
};
|
|
833
902
|
export type BrowserCommandAction = Exclude<BrowserAction, 'browser_select_browser'>;
|
|
834
|
-
export type ExtensionBrowserAction = Exclude<BrowserCommandAction, 'browser_list' | 'browser_launch' | 'browser_close'>;
|
|
835
|
-
export declare const TAB_ID_REQUIRED_ACTIONS: readonly ["tab_checkpoint", "capture_checkpoint", "get_requests", "get_logs", "get_events", "set_mock", "get_mocks", "page_screenshot", "tab_close", "page_click", "page_drag", "page_type", "page_scroll", "page_hover", "page_eval", "page_inspect", "page_snapshot", "page_press", "page_wait", "page_navigate", "page_upload", "page_frames", "set_device_emulation", "set_app_profile", "set_timezone", "
|
|
903
|
+
export type ExtensionBrowserAction = Exclude<BrowserCommandAction, 'browser_list' | 'browser_launch' | 'browser_close' | 'browser_control' | 'page_download'>;
|
|
904
|
+
export declare const TAB_ID_REQUIRED_ACTIONS: readonly ["tab_checkpoint", "capture_checkpoint", "get_requests", "get_logs", "get_events", "set_mock", "get_mocks", "page_screenshot", "tab_close", "page_click", "page_drag", "page_type", "page_scroll", "page_hover", "page_eval", "page_inspect", "page_snapshot", "page_press", "page_wait", "page_navigate", "page_upload", "page_download", "page_frames", "set_device_emulation", "set_app_profile", "set_timezone", "run_actions"];
|
|
836
905
|
export type TabIdRequiredAction = (typeof TAB_ID_REQUIRED_ACTIONS)[number];
|
|
837
906
|
export declare function actionRequiresTabId(action: unknown): action is TabIdRequiredAction;
|
|
838
907
|
export declare function validateRequiredActionTabId(action: string, data: unknown): void;
|
|
@@ -853,6 +922,8 @@ export declare const BROWSER_ACTIONS: {
|
|
|
853
922
|
readonly browser_list: true;
|
|
854
923
|
readonly browser_select_browser: true;
|
|
855
924
|
readonly browser_release: true;
|
|
925
|
+
readonly browser_control: true;
|
|
926
|
+
readonly page_download: true;
|
|
856
927
|
readonly browser_launch: true;
|
|
857
928
|
readonly browser_close: true;
|
|
858
929
|
readonly page_screenshot: true;
|
|
@@ -895,6 +966,8 @@ export declare const BROWSER_COMMAND_ACTIONS: {
|
|
|
895
966
|
readonly send_mtop_request: true;
|
|
896
967
|
readonly browser_list: true;
|
|
897
968
|
readonly browser_release: true;
|
|
969
|
+
readonly browser_control: true;
|
|
970
|
+
readonly page_download: true;
|
|
898
971
|
readonly browser_launch: true;
|
|
899
972
|
readonly browser_close: true;
|
|
900
973
|
readonly page_screenshot: true;
|
|
@@ -962,6 +1035,46 @@ export declare const EXTENSION_BROWSER_ACTIONS: {
|
|
|
962
1035
|
readonly request_domain_permission: true;
|
|
963
1036
|
readonly run_actions: true;
|
|
964
1037
|
};
|
|
1038
|
+
export declare const BROWSER_CAPABILITY_ACTIONS: {
|
|
1039
|
+
readonly page_download: true;
|
|
1040
|
+
readonly tab_checkpoint: true;
|
|
1041
|
+
readonly capture_checkpoint: true;
|
|
1042
|
+
readonly get_requests: true;
|
|
1043
|
+
readonly get_logs: true;
|
|
1044
|
+
readonly get_events: true;
|
|
1045
|
+
readonly set_mock: true;
|
|
1046
|
+
readonly get_mocks: true;
|
|
1047
|
+
readonly set_rule: true;
|
|
1048
|
+
readonly get_rules: true;
|
|
1049
|
+
readonly send_request: true;
|
|
1050
|
+
readonly send_mtop_request: true;
|
|
1051
|
+
readonly browser_release: true;
|
|
1052
|
+
readonly page_screenshot: true;
|
|
1053
|
+
readonly tab_open: true;
|
|
1054
|
+
readonly tab_close: true;
|
|
1055
|
+
readonly tab_list: true;
|
|
1056
|
+
readonly tab_group: true;
|
|
1057
|
+
readonly page_click: true;
|
|
1058
|
+
readonly page_drag: true;
|
|
1059
|
+
readonly page_type: true;
|
|
1060
|
+
readonly page_scroll: true;
|
|
1061
|
+
readonly page_hover: true;
|
|
1062
|
+
readonly page_eval: true;
|
|
1063
|
+
readonly page_inspect: true;
|
|
1064
|
+
readonly page_snapshot: true;
|
|
1065
|
+
readonly page_press: true;
|
|
1066
|
+
readonly page_wait: true;
|
|
1067
|
+
readonly page_navigate: true;
|
|
1068
|
+
readonly page_upload: true;
|
|
1069
|
+
readonly page_frames: true;
|
|
1070
|
+
readonly set_device_emulation: true;
|
|
1071
|
+
readonly set_app_profile: true;
|
|
1072
|
+
readonly set_timezone: true;
|
|
1073
|
+
readonly get_cookie: true;
|
|
1074
|
+
readonly set_cookie: true;
|
|
1075
|
+
readonly request_domain_permission: true;
|
|
1076
|
+
readonly run_actions: true;
|
|
1077
|
+
};
|
|
965
1078
|
export declare function isBrowserAction(value: unknown): value is BrowserAction;
|
|
966
1079
|
export declare function isBrowserCommandAction(value: unknown): value is BrowserCommandAction;
|
|
967
1080
|
export declare function isBrowserCapabilityAction(value: unknown): value is BrowserCapabilityAction;
|
|
@@ -72,12 +72,11 @@ export const TAB_ID_REQUIRED_ACTIONS = [
|
|
|
72
72
|
'page_wait',
|
|
73
73
|
'page_navigate',
|
|
74
74
|
'page_upload',
|
|
75
|
+
'page_download',
|
|
75
76
|
'page_frames',
|
|
76
77
|
'set_device_emulation',
|
|
77
78
|
'set_app_profile',
|
|
78
79
|
'set_timezone',
|
|
79
|
-
'get_cookie',
|
|
80
|
-
'set_cookie',
|
|
81
80
|
'run_actions',
|
|
82
81
|
];
|
|
83
82
|
const TAB_ID_REQUIRED_ACTION_SET = new Set(TAB_ID_REQUIRED_ACTIONS);
|
|
@@ -85,11 +84,22 @@ export function actionRequiresTabId(action) {
|
|
|
85
84
|
return typeof action === 'string' && TAB_ID_REQUIRED_ACTION_SET.has(action);
|
|
86
85
|
}
|
|
87
86
|
export function validateRequiredActionTabId(action, data) {
|
|
88
|
-
|
|
87
|
+
const cookieAction = action === 'get_cookie' || action === 'set_cookie';
|
|
88
|
+
if (!actionRequiresTabId(action) && !cookieAction)
|
|
89
89
|
return;
|
|
90
|
-
const
|
|
91
|
-
? data
|
|
90
|
+
const input = data && typeof data === 'object' && !Array.isArray(data)
|
|
91
|
+
? data
|
|
92
92
|
: undefined;
|
|
93
|
+
const tabId = input?.tabId;
|
|
94
|
+
if (cookieAction && tabId === undefined) {
|
|
95
|
+
if (action === 'set_cookie' && input?.reload === true) {
|
|
96
|
+
throw new Error('set_cookie: reload requires tabId');
|
|
97
|
+
}
|
|
98
|
+
if (typeof input?.url !== 'string' || !input.url.trim()) {
|
|
99
|
+
throw new Error(`${action}: specify url or tabId`);
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
93
103
|
if (typeof tabId !== 'number' || !Number.isSafeInteger(tabId) || tabId < 0) {
|
|
94
104
|
throw new Error(`${action} requires a numeric tabId. Call tab_open or tab_list to obtain a tabId.`);
|
|
95
105
|
}
|
|
@@ -119,6 +129,8 @@ export const BROWSER_ACTIONS = {
|
|
|
119
129
|
browser_list: true,
|
|
120
130
|
browser_select_browser: true,
|
|
121
131
|
browser_release: true,
|
|
132
|
+
browser_control: true,
|
|
133
|
+
page_download: true,
|
|
122
134
|
browser_launch: true,
|
|
123
135
|
browser_close: true,
|
|
124
136
|
page_screenshot: true,
|
|
@@ -161,6 +173,8 @@ export const BROWSER_COMMAND_ACTIONS = {
|
|
|
161
173
|
send_mtop_request: true,
|
|
162
174
|
browser_list: true,
|
|
163
175
|
browser_release: true,
|
|
176
|
+
browser_control: true,
|
|
177
|
+
page_download: true,
|
|
164
178
|
browser_launch: true,
|
|
165
179
|
browser_close: true,
|
|
166
180
|
page_screenshot: true,
|
|
@@ -228,6 +242,10 @@ export const EXTENSION_BROWSER_ACTIONS = {
|
|
|
228
242
|
request_domain_permission: true,
|
|
229
243
|
run_actions: true,
|
|
230
244
|
};
|
|
245
|
+
export const BROWSER_CAPABILITY_ACTIONS = {
|
|
246
|
+
...EXTENSION_BROWSER_ACTIONS,
|
|
247
|
+
page_download: true,
|
|
248
|
+
};
|
|
231
249
|
export function isBrowserAction(value) {
|
|
232
250
|
return typeof value === 'string' && Object.hasOwn(BROWSER_ACTIONS, value);
|
|
233
251
|
}
|
|
@@ -235,7 +253,7 @@ export function isBrowserCommandAction(value) {
|
|
|
235
253
|
return typeof value === 'string' && Object.hasOwn(BROWSER_COMMAND_ACTIONS, value);
|
|
236
254
|
}
|
|
237
255
|
export function isBrowserCapabilityAction(value) {
|
|
238
|
-
return
|
|
256
|
+
return typeof value === 'string' && Object.hasOwn(BROWSER_CAPABILITY_ACTIONS, value);
|
|
239
257
|
}
|
|
240
258
|
export function isExtensionBrowserAction(value) {
|
|
241
259
|
return typeof value === 'string' && Object.hasOwn(EXTENSION_BROWSER_ACTIONS, value);
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
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
9
|
export type { PluginInvocation, PluginInvokeOptions, PluginProgress, StructuredPluginProgress, } from './plugin.js';
|
|
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 { APP_PROFILE_NAMES, BROWSER_DEVICE_PRESET_NAMES, BROWSER_ACTIONS, BROWSER_CAPABILITY_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';
|
|
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';
|
|
@@ -24,6 +24,7 @@ export declare function sendMtopRequest(params: Protocol.SendMtopRequestParams,
|
|
|
24
24
|
export declare function browserList(options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'browser_list'>>;
|
|
25
25
|
export declare function selectBrowser(params?: Protocol.SelectBrowserParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'browser_select_browser'>>;
|
|
26
26
|
export declare function browserRelease(options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'browser_release'>>;
|
|
27
|
+
export declare function browserRelease(params: Protocol.BrowserReleaseParams, options: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'browser_release'>>;
|
|
27
28
|
export declare function browserLaunch(params: Protocol.BrowserLaunchParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'browser_launch'>>;
|
|
28
29
|
export declare function browserClose(params: Protocol.BrowserCloseParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'browser_close'>>;
|
|
29
30
|
export declare function setRule(params: Protocol.SetRuleParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'set_rule'>>;
|
|
@@ -52,3 +53,11 @@ export declare function setTimezone(params: Protocol.SetTimezoneParams, options?
|
|
|
52
53
|
export declare function getCookie(params: Protocol.GetCookieParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'get_cookie'>>;
|
|
53
54
|
export declare function setCookie(params: Protocol.SetCookieParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'set_cookie'>>;
|
|
54
55
|
export declare function runActions(params: Protocol.RunActionsParams, options?: UnifiedInvokeOptions): Promise<Protocol.BrowserActionResponse<'run_actions'>>;
|
|
56
|
+
export declare function browserControl(params: Protocol.BrowserControlParams, options?: UnifiedInvokeOptions): Promise<any>;
|
|
57
|
+
export declare function pageDownload(params: Protocol.PageDownloadParams, options?: UnifiedInvokeOptions): Promise<any>;
|
|
58
|
+
export declare function saveArtifact(artifact: Protocol.BrowserArtifact & {
|
|
59
|
+
browserId?: string;
|
|
60
|
+
}, outputPath: string, options?: UnifiedInvokeOptions): Promise<{
|
|
61
|
+
outputPath: string;
|
|
62
|
+
bytes: number;
|
|
63
|
+
}>;
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* then routes actions using the global browser ids returned by browser_list.
|
|
6
6
|
*/
|
|
7
7
|
export { UnifiedClient, UnifiedClient as MearlClient, decodeBrowserTarget, invoke, invokePlugin, listCloudConnectors, } from './unified.js';
|
|
8
|
-
export { APP_PROFILE_NAMES, BROWSER_DEVICE_PRESET_NAMES, BROWSER_ACTIONS, BROWSER_COMMAND_ACTIONS, EXTENSION_BROWSER_ACTIONS, PAGE_ACT_ACTIONS, SEND_REQUEST_MAX_FILES, SEND_REQUEST_MAX_TOTAL_BASE64_FILE_BYTES, SEND_REQUEST_MAX_TOTAL_FILE_BYTES, isBrowserAction, isBrowserCapabilityAction, isBrowserCommandAction, isExtensionBrowserAction, } from './generated-browser-action-protocol.js';
|
|
8
|
+
export { APP_PROFILE_NAMES, BROWSER_DEVICE_PRESET_NAMES, BROWSER_ACTIONS, BROWSER_CAPABILITY_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) {
|
|
11
11
|
return invoke('tab_checkpoint', params, options);
|
|
@@ -41,8 +41,10 @@ export function browserList(options) {
|
|
|
41
41
|
export function selectBrowser(params = {}, options) {
|
|
42
42
|
return invoke('browser_select_browser', params, options);
|
|
43
43
|
}
|
|
44
|
-
export function browserRelease(options) {
|
|
45
|
-
return
|
|
44
|
+
export function browserRelease(paramsOrOptions = {}, options) {
|
|
45
|
+
return options
|
|
46
|
+
? invoke('browser_release', paramsOrOptions, options)
|
|
47
|
+
: invoke('browser_release', {}, paramsOrOptions);
|
|
46
48
|
}
|
|
47
49
|
export function browserLaunch(params, options) {
|
|
48
50
|
return invoke('browser_launch', params, options);
|
|
@@ -128,3 +130,13 @@ export function setCookie(params, options) {
|
|
|
128
130
|
export function runActions(params, options) {
|
|
129
131
|
return invoke('run_actions', params, options);
|
|
130
132
|
}
|
|
133
|
+
export function browserControl(params, options) {
|
|
134
|
+
return invoke('browser_control', params, options);
|
|
135
|
+
}
|
|
136
|
+
export function pageDownload(params, options) {
|
|
137
|
+
return invoke('page_download', params, options);
|
|
138
|
+
}
|
|
139
|
+
export async function saveArtifact(artifact, outputPath, options = {}) {
|
|
140
|
+
const { exportArtifact } = await import('./artifacts.js');
|
|
141
|
+
return exportArtifact({ invoke }, artifact, outputPath, options);
|
|
142
|
+
}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type OfficialPluginInstaller } from './officialPluginInstaller.js';
|
|
2
|
-
import { type PluginToolboxContribution } from './pluginRegistry.js';
|
|
2
|
+
import { type PluginToolboxContribution, type PluginRegistration } from './pluginRegistry.js';
|
|
3
3
|
import type { UnifiedInvokeOptions } from './unified.js';
|
|
4
4
|
export { assertPluginName, findPluginDirectoryEntry, findPluginEntry, findPluginRegistration, listPluginNames, registerPlugin, resolvePluginEntry, unregisterPlugin, PluginError, readPluginPageBundleChunk, type PluginRegistration, type PluginRegistrationOptions, type PluginToolboxAgentConfig, type PluginPageBundleRegistration, type PluginToolboxContribution, type PluginToolboxLocale, type PluginToolboxSandboxPage, } from './pluginRegistry.js';
|
|
5
5
|
export { findOfficialPlugin, listOfficialPluginNames, OFFICIAL_PLUGIN_MANAGER, OFFICIAL_PLUGINS, type OfficialPluginDefinition, type OfficialPluginName, } from './officialPluginCatalog.js';
|
|
@@ -22,11 +22,14 @@ export interface PluginExecutionContext {
|
|
|
22
22
|
}
|
|
23
23
|
export interface PluginApp {
|
|
24
24
|
readonly options: Readonly<PluginOptions>;
|
|
25
|
-
|
|
25
|
+
/** Examples omit the `mearl <plugin>` prefix, which help adds when displayed. */
|
|
26
|
+
command(signature: string, handler: PluginHandler, description?: string, examples?: string[]): void;
|
|
26
27
|
invoke(action: string, payload?: Record<string, any>, options?: UnifiedInvokeOptions): Promise<any>;
|
|
27
28
|
progress(progress: PluginProgress): void;
|
|
28
29
|
}
|
|
29
30
|
export type PluginHandler = (input: Record<string, any>) => unknown | Promise<unknown>;
|
|
31
|
+
/** 工具列表仅检查入口;页面资源只在打开对应工具时检查。 */
|
|
32
|
+
export declare function refreshRegisteredPlugin(name: string, includePage?: boolean): Promise<PluginRegistration | undefined>;
|
|
30
33
|
/** 注册和执行复用同一宿主能力边界;仅 handler 执行期间开放浏览器调用。 */
|
|
31
34
|
export declare function loadPlugin(entry: string, context: Pick<PluginApp, 'invoke' | 'options' | 'progress'>, pluginName?: string): Promise<{
|
|
32
35
|
name: string | undefined;
|
package/dist/plugin.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { readFileSync
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, extname, resolve } from 'node:path';
|
|
3
3
|
import { pathToFileURL } from 'node:url';
|
|
4
4
|
import { Command } from 'commander';
|
|
5
5
|
import { COMMAND_MAP } from './commands.js';
|
|
6
6
|
import { findOfficialPlugin } from './officialPluginCatalog.js';
|
|
7
7
|
import { ensureOfficialPluginRegistered, } from './officialPluginInstaller.js';
|
|
8
|
-
import { findPluginRegistration, normalizePluginToolboxContribution, PluginError, } from './pluginRegistry.js';
|
|
8
|
+
import { findPluginRegistration, pluginRegistrationNeedsRefresh, registerPlugin, pluginDirectory, normalizePluginToolboxContribution, PluginError, } from './pluginRegistry.js';
|
|
9
|
+
import { pluginFileVersion } from './pluginSource.js';
|
|
9
10
|
import { describePythonPlugin, runPythonPluginCommand } from './pythonPlugin.js';
|
|
10
11
|
export { assertPluginName, findPluginDirectoryEntry, findPluginEntry, findPluginRegistration, listPluginNames, registerPlugin, resolvePluginEntry, unregisterPlugin, PluginError, readPluginPageBundleChunk, } from './pluginRegistry.js';
|
|
11
12
|
export { findOfficialPlugin, listOfficialPluginNames, OFFICIAL_PLUGIN_MANAGER, OFFICIAL_PLUGINS, } from './officialPluginCatalog.js';
|
|
@@ -67,14 +68,61 @@ function missingPluginEntryMessage(entry, pluginName) {
|
|
|
67
68
|
`卸载命令:npx @mearl/setup plugin uninstall ${name}`,
|
|
68
69
|
].join('\n');
|
|
69
70
|
}
|
|
71
|
+
const registrationRefreshes = new Map();
|
|
72
|
+
/** 工具列表仅检查入口;页面资源只在打开对应工具时检查。 */
|
|
73
|
+
export async function refreshRegisteredPlugin(name, includePage = false) {
|
|
74
|
+
const key = `${pluginDirectory()}/${name}`;
|
|
75
|
+
const pending = registrationRefreshes.get(key);
|
|
76
|
+
if (pending) {
|
|
77
|
+
await pending;
|
|
78
|
+
return refreshRegisteredPlugin(name, includePage);
|
|
79
|
+
}
|
|
80
|
+
const registration = findPluginRegistration(name);
|
|
81
|
+
if (!registration)
|
|
82
|
+
return undefined;
|
|
83
|
+
const entryChanged = pluginRegistrationNeedsRefresh(registration);
|
|
84
|
+
if (!entryChanged && (!includePage || !pluginRegistrationNeedsRefresh(registration, true))) {
|
|
85
|
+
return registration;
|
|
86
|
+
}
|
|
87
|
+
const refresh = (async () => {
|
|
88
|
+
const entryVersion = pluginFileVersion(registration.entry);
|
|
89
|
+
const loaded = entryChanged
|
|
90
|
+
? await loadPlugin(registration.entry, {
|
|
91
|
+
options: {},
|
|
92
|
+
invoke: async () => {
|
|
93
|
+
throw new Error('注册时不能执行浏览器操作');
|
|
94
|
+
},
|
|
95
|
+
progress: () => {
|
|
96
|
+
throw new Error('注册时不能报告执行进度');
|
|
97
|
+
},
|
|
98
|
+
}, name)
|
|
99
|
+
: registration;
|
|
100
|
+
// 异步加载期间其他进程可能卸载或重新绑定同名插件。
|
|
101
|
+
const current = findPluginRegistration(name);
|
|
102
|
+
if (JSON.stringify(current) !== JSON.stringify(registration))
|
|
103
|
+
return current;
|
|
104
|
+
if (pluginFileVersion(registration.entry) !== entryVersion) {
|
|
105
|
+
throw new PluginError('PLUGIN_SOURCE_CHANGED', `插件更新进行中,请重新打开工具: ${name}`);
|
|
106
|
+
}
|
|
107
|
+
registerPlugin(name, registration.entry, loaded.description, {
|
|
108
|
+
toolbox: loaded.toolbox,
|
|
109
|
+
...(registration.managedBy ? { managedBy: registration.managedBy } : {}),
|
|
110
|
+
});
|
|
111
|
+
return findPluginRegistration(name);
|
|
112
|
+
})();
|
|
113
|
+
registrationRefreshes.set(key, refresh);
|
|
114
|
+
try {
|
|
115
|
+
return await refresh;
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
registrationRefreshes.delete(key);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
70
121
|
/** 注册和执行复用同一宿主能力边界;仅 handler 执行期间开放浏览器调用。 */
|
|
71
122
|
export async function loadPlugin(entry, context, pluginName) {
|
|
72
123
|
let entryVersion;
|
|
73
124
|
try {
|
|
74
|
-
|
|
75
|
-
if (!stat.isFile())
|
|
76
|
-
throw new Error('not a file');
|
|
77
|
-
entryVersion = `${stat.mtimeMs}-${stat.size}`;
|
|
125
|
+
entryVersion = pluginFileVersion(entry);
|
|
78
126
|
}
|
|
79
127
|
catch {
|
|
80
128
|
throw new PluginError('PLUGIN_ENTRY_MISSING', missingPluginEntryMessage(entry, pluginName));
|
|
@@ -87,11 +135,15 @@ export async function loadPlugin(entry, context, pluginName) {
|
|
|
87
135
|
let toolbox;
|
|
88
136
|
const app = Object.freeze({
|
|
89
137
|
options: Object.freeze({ ...context.options }),
|
|
90
|
-
command(signature, handler, description = '') {
|
|
138
|
+
command(signature, handler, description = '', examples = []) {
|
|
91
139
|
if (!registering)
|
|
92
140
|
throw new Error('命令只能在入口注册阶段声明');
|
|
93
141
|
const [name, ...args] = signature.trim().split(/\s+/);
|
|
94
|
-
if (!/^[a-z][a-z0-9-]*$/.test(name) ||
|
|
142
|
+
if (!/^[a-z][a-z0-9-]*$/.test(name) ||
|
|
143
|
+
commands.has(name) ||
|
|
144
|
+
typeof handler !== 'function' ||
|
|
145
|
+
!Array.isArray(examples) ||
|
|
146
|
+
examples.some((example) => typeof example !== 'string' || !example.trim())) {
|
|
95
147
|
throw new Error(`无效或重复的命令: ${signature}`);
|
|
96
148
|
}
|
|
97
149
|
const command = new Command(name).description(description);
|
|
@@ -104,7 +156,7 @@ export async function loadPlugin(entry, context, pluginName) {
|
|
|
104
156
|
const names = command.registeredArguments.map(argument => argument.name());
|
|
105
157
|
if (new Set(names).size !== names.length)
|
|
106
158
|
throw new Error(`参数名称重复: ${signature}`);
|
|
107
|
-
commands.set(name, { command, signature, handler });
|
|
159
|
+
commands.set(name, { command, signature, handler, examples: [...examples] });
|
|
108
160
|
},
|
|
109
161
|
invoke(action, payload = {}, options = {}) {
|
|
110
162
|
if (!executing)
|
|
@@ -175,20 +227,13 @@ export async function loadPlugin(entry, context, pluginName) {
|
|
|
175
227
|
},
|
|
176
228
|
help(namespace, name) {
|
|
177
229
|
const selected = name ? [find(name)] : [...commands.values()];
|
|
178
|
-
const
|
|
179
|
-
const input = Object.fromEntries(example.registeredArguments.map(argument => [
|
|
180
|
-
argument.name(),
|
|
181
|
-
argument.variadic ? [`<${argument.name()}>`] : `<${argument.name()}>`,
|
|
182
|
-
]));
|
|
230
|
+
const examples = selected.flatMap(({ examples }) => examples);
|
|
183
231
|
return [
|
|
232
|
+
'用法:',
|
|
184
233
|
...selected.map(({ command, signature }) => ` ${namespace} ${signature} ${command.description()}`),
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
'常用选项: --browser --timeout --compact --output',
|
|
189
|
-
'连接或传输排障运行 mearl check --help。',
|
|
190
|
-
'插件选项: --name=value 传入字符串业务字段;--all 传入 all=true;--verbose; -h, --help; -v, --version',
|
|
191
|
-
'同名宿主选项及非字符串字段使用 --payload;业务字段与校验规则见对应 skill。',
|
|
234
|
+
...(examples.length
|
|
235
|
+
? ['', '示例:', ...examples.map(example => ` ${namespace} ${example}`)]
|
|
236
|
+
: []),
|
|
192
237
|
].join('\n');
|
|
193
238
|
},
|
|
194
239
|
async run(positionals, payload, namedInput = {}) {
|
package/dist/pluginPageBundle.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { readFileSync, realpathSync } from 'node:fs';
|
|
3
3
|
import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import { pluginFileVersion } from './pluginSource.js';
|
|
4
5
|
export const PLUGIN_PAGE_CHUNK_BYTES = 384 * 1024;
|
|
5
6
|
const MAX_PAGE_SOURCE_BYTES = 16 * 1024 * 1024;
|
|
6
7
|
const MAX_PAGE_BUNDLE_BYTES = 24 * 1024 * 1024;
|
|
@@ -31,6 +32,40 @@ const PAGE_BRIDGE_SCRIPT = String.raw `(() => {
|
|
|
31
32
|
let resolveReady;
|
|
32
33
|
const ready = new Promise(resolve => { resolveReady = resolve; });
|
|
33
34
|
|
|
35
|
+
let pageModalOpen = null;
|
|
36
|
+
let modalRestoreFrame = null;
|
|
37
|
+
const syncPageModal = () => {
|
|
38
|
+
if (!port) return;
|
|
39
|
+
const open = Boolean(document.querySelector?.('dialog:modal'));
|
|
40
|
+
if (open && modalRestoreFrame !== null) {
|
|
41
|
+
cancelAnimationFrame(modalRestoreFrame);
|
|
42
|
+
modalRestoreFrame = null;
|
|
43
|
+
}
|
|
44
|
+
if (open === pageModalOpen) return;
|
|
45
|
+
if (!open && pageModalOpen === true) {
|
|
46
|
+
if (modalRestoreFrame !== null) return;
|
|
47
|
+
// Closing a dialog restores focus and can scroll the anchors before the next paint.
|
|
48
|
+
modalRestoreFrame = requestAnimationFrame(() => {
|
|
49
|
+
modalRestoreFrame = null;
|
|
50
|
+
if (document.querySelector?.('dialog:modal')) return;
|
|
51
|
+
const positions = [];
|
|
52
|
+
for (const { presentation } of inlineAgentPanels.values()) {
|
|
53
|
+
if (!presentation.element.isConnected) continue;
|
|
54
|
+
const rect = presentation.element.getBoundingClientRect();
|
|
55
|
+
if (![rect.top, rect.left, rect.width, rect.height].every(Number.isFinite)) continue;
|
|
56
|
+
const bounds = { top: rect.top, left: rect.left, width: rect.width, height: rect.height };
|
|
57
|
+
presentation.bounds = bounds;
|
|
58
|
+
positions.push({ key: presentation.key, bounds });
|
|
59
|
+
}
|
|
60
|
+
pageModalOpen = false;
|
|
61
|
+
port.postMessage({ type: 'page-modal', open: false, positions });
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
pageModalOpen = open;
|
|
66
|
+
port.postMessage({ type: 'page-modal', open });
|
|
67
|
+
};
|
|
68
|
+
|
|
34
69
|
const requestHost = async (type, payload = {}) => {
|
|
35
70
|
await ready;
|
|
36
71
|
const requestId = String(++requestSequence);
|
|
@@ -468,6 +503,19 @@ const PAGE_BRIDGE_SCRIPT = String.raw `(() => {
|
|
|
468
503
|
port.start();
|
|
469
504
|
resolveReady(context);
|
|
470
505
|
port.postMessage({ type: 'ready' });
|
|
506
|
+
// Dialogs in the sandbox cannot cover Agent overlays in the parent document.
|
|
507
|
+
syncPageModal();
|
|
508
|
+
const modalObserver = typeof MutationObserver === 'function'
|
|
509
|
+
? new MutationObserver(syncPageModal)
|
|
510
|
+
: null;
|
|
511
|
+
modalObserver?.observe(document.documentElement, {
|
|
512
|
+
attributes: true,
|
|
513
|
+
attributeFilter: ['open'],
|
|
514
|
+
childList: true,
|
|
515
|
+
subtree: true,
|
|
516
|
+
});
|
|
517
|
+
document.addEventListener('toggle', syncPageModal, true);
|
|
518
|
+
document.addEventListener('close', syncPageModal, true);
|
|
471
519
|
};
|
|
472
520
|
window.addEventListener('message', connect);
|
|
473
521
|
window.parent.postMessage({ type: 'mearl-plugin-ready' }, '*');
|
|
@@ -578,6 +626,7 @@ export function buildPluginPageBundle(entry, pageEntry) {
|
|
|
578
626
|
const root = realpathSync(pluginRoot(entry));
|
|
579
627
|
let sourceBytes = 0;
|
|
580
628
|
const countedFiles = new Set();
|
|
629
|
+
const sources = {};
|
|
581
630
|
const resolveLocalFile = (reference, fromFile) => {
|
|
582
631
|
const cleanReference = reference.trim().split(/[?#]/, 1)[0];
|
|
583
632
|
if (!cleanReference || isEmbeddedReference(cleanReference))
|
|
@@ -585,14 +634,17 @@ export function buildPluginPageBundle(entry, pageEntry) {
|
|
|
585
634
|
if (isRemoteReference(cleanReference) || isAbsolute(cleanReference)) {
|
|
586
635
|
throw new Error(`toolbox.page 不允许远程或绝对资源: ${reference}`);
|
|
587
636
|
}
|
|
588
|
-
const
|
|
637
|
+
const sourcePath = resolve(dirname(fromFile), cleanReference);
|
|
638
|
+
const resolved = realpathSync(sourcePath);
|
|
589
639
|
const relativePath = relative(root, resolved);
|
|
590
640
|
if (relativePath === '..' || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) {
|
|
591
641
|
throw new Error(`toolbox.page 资源超出插件目录: ${reference}`);
|
|
592
642
|
}
|
|
643
|
+
sources[sourcePath] = pluginFileVersion(sourcePath);
|
|
593
644
|
return resolved;
|
|
594
645
|
};
|
|
595
646
|
const readAsset = (path) => {
|
|
647
|
+
sources[path] ??= pluginFileVersion(path);
|
|
596
648
|
const content = readFileSync(path);
|
|
597
649
|
if (!countedFiles.has(path)) {
|
|
598
650
|
countedFiles.add(path);
|
|
@@ -676,5 +728,6 @@ export function buildPluginPageBundle(entry, pageEntry) {
|
|
|
676
728
|
hash: createHash('sha256').update(buffer).digest('hex'),
|
|
677
729
|
size: buffer.byteLength,
|
|
678
730
|
chunks: Math.ceil(buffer.byteLength / PLUGIN_PAGE_CHUNK_BYTES),
|
|
731
|
+
sources,
|
|
679
732
|
};
|
|
680
733
|
}
|
package/dist/pluginRegistry.d.ts
CHANGED
|
@@ -37,7 +37,12 @@ export interface PluginRegistration {
|
|
|
37
37
|
managedBy?: string;
|
|
38
38
|
toolbox?: PluginToolboxContribution;
|
|
39
39
|
pageBundle?: PluginPageBundleRegistration;
|
|
40
|
+
source?: {
|
|
41
|
+
entry: string;
|
|
42
|
+
page?: Record<string, string>;
|
|
43
|
+
};
|
|
40
44
|
}
|
|
45
|
+
export declare function pluginRegistrationNeedsRefresh(registration: PluginRegistration, includePage?: boolean): boolean;
|
|
41
46
|
export interface PluginRegistrationOptions {
|
|
42
47
|
managedBy?: string;
|
|
43
48
|
toolbox?: PluginToolboxContribution;
|