@mearl/provider 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 +4 -0
- package/dist/control-host.d.ts +11 -0
- package/dist/control-host.js +75 -0
- package/dist/generated-provider-actions.d.ts +1 -1
- package/dist/generated-provider-actions.js +1 -0
- package/dist/host.d.ts +2 -0
- package/dist/host.js +1 -0
- package/dist/server.js +2 -0
- package/dist/types.d.ts +8 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@ Provider 使用相同的 `BrowserProviderDefinition`;区别只在于运行时
|
|
|
6
6
|
|
|
7
7
|
## 核心模型
|
|
8
8
|
|
|
9
|
+
控制会话由 Mearl 管理。Provider 继续接收目标实例、页面与普通 action,无需处理控制会话 ID、资源归属或人工交接状态。带 `--session` 的释放只结束 Mearl 任务控制;实例关闭、退出接管与 Cookie 同步仍使用 Provider 的生命周期方法。上传与下载分别按 `capabilities.actions` 中的 `page_upload`、`page_download` 声明使用。
|
|
10
|
+
|
|
11
|
+
`page_upload` 接收执行宿主上的 `filePaths`,Mearl 负责中转调用方文件与浏览器产物。`page_download` 接收 `{ tabId, trigger, url?, timeout? }`;Provider 先监听下载,再执行点击,等待文件完成后返回 `ProviderDownloadResult`:`{ filename, filePath, action, owned? }`。`filePath` 是本地 Provider 进程宿主上的绝对路径,云端文件由 Provider 取回;`action` 是点击结果,`owned:true` 允许 Mearl 在导入后删除该中转文件。Mearl 将结果转为统一的浏览器产物,支持调用方保存、后续上传与会话清理。下载超时或来源不确定时返回错误,不重复点击。
|
|
12
|
+
|
|
9
13
|
Provider v2 分开表达实例状态与控制方式:
|
|
10
14
|
|
|
11
15
|
- `status`:实例处于 `connected`、`pairing`、`running_disconnected` 或 `stopped`。
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type DaemonRecord } from '@mearl/daemon-core';
|
|
2
|
+
export interface ProviderControlHostRecord extends DaemonRecord {
|
|
3
|
+
socketPath: string;
|
|
4
|
+
token: string;
|
|
5
|
+
ready: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function providerControlHostFile(): string;
|
|
8
|
+
export declare function providerControlSocketPath(pid: number): string;
|
|
9
|
+
export declare function readProviderControlHost(): ProviderControlHostRecord | null;
|
|
10
|
+
/** A single host owns Provider sessions across independent CLI and connector processes. */
|
|
11
|
+
export declare function ensureProviderControlHost(timeoutMs?: number): Promise<ProviderControlHostRecord>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { isAlive, keyedConfigFile, claimRecord, readRecord, removeInvalidRecord, removeRecordIfPid, writeRecordIfPid, } from '@mearl/daemon-core';
|
|
6
|
+
export function providerControlHostFile() {
|
|
7
|
+
return keyedConfigFile('control', 'providers');
|
|
8
|
+
}
|
|
9
|
+
export function providerControlSocketPath(pid) {
|
|
10
|
+
return process.platform === 'win32'
|
|
11
|
+
? `\\\\.\\pipe\\mearl-provider-control-${pid}`
|
|
12
|
+
: join(tmpdir(), `mearl-provider-control-${pid}.sock`);
|
|
13
|
+
}
|
|
14
|
+
export function readProviderControlHost() {
|
|
15
|
+
const record = readRecord(providerControlHostFile());
|
|
16
|
+
return record?.ready && typeof record.socketPath === 'string' && isAlive(record.pid)
|
|
17
|
+
? record
|
|
18
|
+
: null;
|
|
19
|
+
}
|
|
20
|
+
let startup;
|
|
21
|
+
/** A single host owns Provider sessions across independent CLI and connector processes. */
|
|
22
|
+
export function ensureProviderControlHost(timeoutMs = 10_000) {
|
|
23
|
+
if (startup)
|
|
24
|
+
return startup;
|
|
25
|
+
const pending = (async () => {
|
|
26
|
+
const file = providerControlHostFile();
|
|
27
|
+
const deadline = Date.now() + timeoutMs;
|
|
28
|
+
while (Date.now() < deadline) {
|
|
29
|
+
const ready = readProviderControlHost();
|
|
30
|
+
if (ready)
|
|
31
|
+
return ready;
|
|
32
|
+
const previous = readRecord(file);
|
|
33
|
+
if (!previous || !isAlive(previous.pid)) {
|
|
34
|
+
if (previous)
|
|
35
|
+
removeRecordIfPid(file, previous.pid);
|
|
36
|
+
else
|
|
37
|
+
removeInvalidRecord(file);
|
|
38
|
+
const token = randomUUID();
|
|
39
|
+
if (claimRecord(file, { pid: process.pid, socketPath: '', token, ready: false })) {
|
|
40
|
+
try {
|
|
41
|
+
const entry = process.env.MEARL_NATIVE_HOST_ENTRY;
|
|
42
|
+
const child = spawn(entry ? process.execPath : 'mearl-native-host', [...(entry ? [entry] : []), '--provider-control', token], { detached: true, stdio: 'ignore', windowsHide: true });
|
|
43
|
+
await new Promise((resolve, reject) => {
|
|
44
|
+
child.once('error', reject);
|
|
45
|
+
child.once('spawn', resolve);
|
|
46
|
+
});
|
|
47
|
+
child.unref();
|
|
48
|
+
if (!child.pid)
|
|
49
|
+
throw new Error('Provider control host did not report its pid');
|
|
50
|
+
writeRecordIfPid(file, process.pid, {
|
|
51
|
+
pid: child.pid,
|
|
52
|
+
socketPath: providerControlSocketPath(child.pid),
|
|
53
|
+
token,
|
|
54
|
+
ready: false,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
removeRecordIfPid(file, process.pid);
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
64
|
+
}
|
|
65
|
+
throw new Error('Provider control host startup timed out');
|
|
66
|
+
})();
|
|
67
|
+
startup = pending;
|
|
68
|
+
void pending
|
|
69
|
+
.finally(() => {
|
|
70
|
+
if (startup === pending)
|
|
71
|
+
startup = undefined;
|
|
72
|
+
})
|
|
73
|
+
.catch(() => { });
|
|
74
|
+
return pending;
|
|
75
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const PROVIDER_ACTIONS: readonly ["tab_checkpoint", "capture_checkpoint", "get_requests", "get_logs", "get_events", "set_mock", "get_mocks", "set_rule", "get_rules", "send_request", "send_mtop_request", "browser_release", "page_screenshot", "tab_open", "tab_close", "tab_list", "tab_group", "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", "get_cookie", "set_cookie", "request_domain_permission", "run_actions"];
|
|
1
|
+
export declare const PROVIDER_ACTIONS: readonly ["tab_checkpoint", "capture_checkpoint", "get_requests", "get_logs", "get_events", "set_mock", "get_mocks", "set_rule", "get_rules", "send_request", "send_mtop_request", "browser_release", "page_screenshot", "tab_open", "tab_close", "tab_list", "tab_group", "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", "get_cookie", "set_cookie", "request_domain_permission", "run_actions", "page_download"];
|
|
2
2
|
export type ProviderAction = (typeof PROVIDER_ACTIONS)[number];
|
|
3
3
|
export declare function isProviderAction(value: unknown): value is ProviderAction;
|
package/dist/host.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { listInstalledProviders, registerProviderPackage, stopProvider, uninstallProvider, } from './registry.js';
|
|
2
2
|
export type { InstalledProviderManifest, ProviderPackageMetadata } from './types.js';
|
|
3
|
+
export { ensureProviderControlHost, readProviderControlHost, providerControlHostFile, providerControlSocketPath, } from './control-host.js';
|
|
4
|
+
export type { ProviderControlHostRecord } from './control-host.js';
|
package/dist/host.js
CHANGED
package/dist/server.js
CHANGED
|
@@ -81,6 +81,8 @@ export function createProviderServer(options) {
|
|
|
81
81
|
throw new Error('browserId is required when the provider has multiple browsers');
|
|
82
82
|
}
|
|
83
83
|
async function dispatch(request) {
|
|
84
|
+
if (request.controlSessionId)
|
|
85
|
+
throw new Error('CONTROL_SESSION_UNSUPPORTED: this Provider does not implement control sessions');
|
|
84
86
|
const context = providerContext(request);
|
|
85
87
|
if (request.action === 'browser_launch') {
|
|
86
88
|
if (request.browserId)
|
package/dist/types.d.ts
CHANGED
|
@@ -11,6 +11,14 @@ export interface ProviderCapabilities {
|
|
|
11
11
|
actions: ProviderAction[];
|
|
12
12
|
screenshot: ProviderScreenshotKind;
|
|
13
13
|
}
|
|
14
|
+
/** Completed download staged on the local Provider process host for Mearl transfer. */
|
|
15
|
+
export interface ProviderDownloadResult {
|
|
16
|
+
filename: string;
|
|
17
|
+
filePath: string;
|
|
18
|
+
action: JsonValue;
|
|
19
|
+
/** Allows Mearl to remove the staged file after importing it. */
|
|
20
|
+
owned?: boolean;
|
|
21
|
+
}
|
|
14
22
|
export interface ProviderPairingInfo {
|
|
15
23
|
kind: 'qr';
|
|
16
24
|
url: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mearl/provider",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.20.0",
|
|
4
4
|
"description": "Unified contracts and runtime utilities for built-in and external Mearl browser providers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"registry": "https://registry.npmjs.org"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@mearl/daemon-core": "2.
|
|
38
|
+
"@mearl/daemon-core": "2.20.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^24.9.1",
|