@mearl/provider 2.19.3 → 2.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/provider-runtime.d.ts +2 -1
- package/dist/provider-runtime.js +78 -1
- package/dist/registry.js +12 -1
- package/dist/server.js +10 -0
- package/dist/types.d.ts +43 -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`。
|
|
@@ -132,6 +136,31 @@ Provider 可用 `component: "switch"` 渲染紧凑布尔开关;`formHidden`
|
|
|
132
136
|
`browserType.documentationUrl` 声明 HTTP(S) 文档地址;`configuration.inputSchema` 声明配置参数,
|
|
133
137
|
配置只传给所选 Provider 的 `configure`,不存入页面本地存储。
|
|
134
138
|
|
|
139
|
+
Provider 需要查询设备池、已有会话或动态候选项时,声明 `browserType.dynamicLaunchForm: true`
|
|
140
|
+
并实现只读的 `getLaunchForm({ variant?, values }, context)`。
|
|
141
|
+
`browserType.launchForm` 可提供无网络依赖的初始表单,弹窗立即渲染其字段和模式;
|
|
142
|
+
各 `variants[]` 可用 `inputSchema/values/refreshOn/dynamicFields` 覆盖该模式的初值。
|
|
143
|
+
`dynamicFields` 指定异步查询期间需要禁用的字段,其余字段与模式选择保持可操作;加载状态在表单状态行显示。
|
|
144
|
+
初次查询携带静态表单默认值;查询只传入可解析的字段,最终启动仍严格校验全部输入。
|
|
145
|
+
用户在等待期间输入的内容会保留,切换模式或环境会使旧请求失效。
|
|
146
|
+
管理器通过内部
|
|
147
|
+
`browser_provider_launch_form` 请求获取 `ProviderLaunchForm`,不识别 Provider ID 或业务字段:
|
|
148
|
+
|
|
149
|
+
- `inputSchema` 描述当前模式的用户输入,`required` 决定必填标识与提交校验。
|
|
150
|
+
- `variants` 的 `id/title/description` 描述连接方式,`variant` 指定当前方式。
|
|
151
|
+
- `values` 是 Provider 给出的固定启动参数,提交时与用户输入合并;只包含非敏感参数。
|
|
152
|
+
- 字符串字段的 `options` 使用 `{ value, label, description?, disabled? }` 描述候选项。
|
|
153
|
+
禁用项只供展示;运行时的最终资源状态由 Provider 的 `launch` 再次核验。
|
|
154
|
+
- `refreshOn` 指定需要重新查询表单的字段;字段变化会清空其他选项字段的选择。
|
|
155
|
+
刷新时保留已有输入,选项字段仅保留仍有效的选择;切换模式重新使用该模式默认值。
|
|
156
|
+
- `message` 展示查询结果,`blocked` 阻止无资源、无有效会话等情况下的提交。
|
|
157
|
+
|
|
158
|
+
查询过程不得创建或租用资源,也不得返回凭据。Runtime 会校验返回的 schema、候选项、
|
|
159
|
+
模式与固定参数。最终 `launch` 仍使用 `browserType.inputSchema` 校验,动态表单不改变 CLI/API
|
|
160
|
+
参数契约。未声明动态表单的 Provider 继续使用静态 schema。
|
|
161
|
+
开发联调时,Provider 应链接包含该公开契约的本地 `@mearl/provider` 构建;分发时需配套发布
|
|
162
|
+
包含动态表单能力的 SDK、Mearl 宿主与 Provider。
|
|
163
|
+
|
|
135
164
|
`details` 中的通用键具有固定类型:`headless` 为布尔值,`createdAt` 为可解析时间字符串,
|
|
136
165
|
`cdpPort` 为有效端口,`account` 包含 `nickname` 和可选的字符串 `accountId/loginId`,
|
|
137
166
|
`copiedCookies` 包含字符串数组 `domains` 和非负整数 `count`。其余键允许非敏感 JSON,界面同样展示。
|
|
@@ -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/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from './types.js';
|
|
2
2
|
export { PROVIDER_ACTIONS, isProviderAction } from './generated-provider-actions.js';
|
|
3
3
|
export { providerDataDir } from './registry.js';
|
|
4
|
-
export { createProviderRuntime, validateProviderBrowser, validateProviderInput, } from './provider-runtime.js';
|
|
4
|
+
export { createProviderRuntime, validateProviderBrowser, validateProviderInput, validateProviderLaunchForm, } from './provider-runtime.js';
|
|
5
5
|
export { createProviderServer } from './server.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from './types.js';
|
|
2
2
|
export { PROVIDER_ACTIONS, isProviderAction } from './generated-provider-actions.js';
|
|
3
3
|
export { providerDataDir } from './registry.js';
|
|
4
|
-
export { createProviderRuntime, validateProviderBrowser, validateProviderInput, } from './provider-runtime.js';
|
|
4
|
+
export { createProviderRuntime, validateProviderBrowser, validateProviderInput, validateProviderLaunchForm, } from './provider-runtime.js';
|
|
5
5
|
export { createProviderServer } from './server.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { BrowserProviderDefinition, JsonObject, ProviderBrowser, ProviderInputSchema, ProviderObjectSchema, ProviderRuntime } from './types.js';
|
|
1
|
+
import type { BrowserProviderDefinition, JsonObject, ProviderBrowser, ProviderInputSchema, ProviderObjectSchema, ProviderRuntime, ProviderLaunchForm } from './types.js';
|
|
2
2
|
export declare function isProviderInputSchema(value: unknown, depth?: number): value is ProviderInputSchema;
|
|
3
3
|
export declare function validateProviderInput(schema: ProviderObjectSchema, value: JsonObject): void;
|
|
4
4
|
export declare function validateProviderBrowser(browser: ProviderBrowser): void;
|
|
5
|
+
export declare function validateProviderLaunchForm(form: ProviderLaunchForm): void;
|
|
5
6
|
export declare function createProviderRuntime(definition: BrowserProviderDefinition): ProviderRuntime;
|
package/dist/provider-runtime.js
CHANGED
|
@@ -71,7 +71,18 @@ export function isProviderInputSchema(value, depth = 0) {
|
|
|
71
71
|
Number(schema.minLength) <= Number(schema.maxLength)) &&
|
|
72
72
|
isOptionalString(schema.pattern) &&
|
|
73
73
|
validPattern &&
|
|
74
|
-
isOptionalString(schema.placeholder)
|
|
74
|
+
isOptionalString(schema.placeholder) &&
|
|
75
|
+
(schema.options === undefined ||
|
|
76
|
+
(Array.isArray(schema.options) &&
|
|
77
|
+
schema.options.every(option => option &&
|
|
78
|
+
typeof option === 'object' &&
|
|
79
|
+
!Array.isArray(option) &&
|
|
80
|
+
typeof option.value === 'string' &&
|
|
81
|
+
typeof option.label === 'string' &&
|
|
82
|
+
isOptionalString(option.description) &&
|
|
83
|
+
(option.disabled === undefined || typeof option.disabled === 'boolean') &&
|
|
84
|
+
(schema.enum === undefined || schema.enum.includes(option.value))) &&
|
|
85
|
+
new Set(schema.options.map(option => option.value)).size === schema.options.length));
|
|
75
86
|
if (!valid)
|
|
76
87
|
return false;
|
|
77
88
|
}
|
|
@@ -387,6 +398,13 @@ function validateDefinition(definition) {
|
|
|
387
398
|
(typeof definition.browserType.documentationUrl !== 'string' ||
|
|
388
399
|
!/^https?:\/\//.test(definition.browserType.documentationUrl)))
|
|
389
400
|
throw new Error('Provider documentation URL must use HTTP(S)');
|
|
401
|
+
if (definition.browserType.dynamicLaunchForm !== undefined &&
|
|
402
|
+
typeof definition.browserType.dynamicLaunchForm !== 'boolean')
|
|
403
|
+
throw new Error('Provider dynamic launch form declaration is invalid');
|
|
404
|
+
if (definition.browserType.dynamicLaunchForm && !definition.getLaunchForm)
|
|
405
|
+
throw new Error('Provider does not implement getLaunchForm');
|
|
406
|
+
if (definition.browserType.launchForm !== undefined)
|
|
407
|
+
validateProviderLaunchForm(definition.browserType.launchForm);
|
|
390
408
|
const configuration = definition.browserType.configuration;
|
|
391
409
|
if (configuration &&
|
|
392
410
|
(!isProviderInputSchema(configuration.inputSchema) ||
|
|
@@ -394,6 +412,45 @@ function validateDefinition(definition) {
|
|
|
394
412
|
throw new Error('Provider configuration schema is invalid');
|
|
395
413
|
}
|
|
396
414
|
}
|
|
415
|
+
export function validateProviderLaunchForm(form) {
|
|
416
|
+
if (!form ||
|
|
417
|
+
!isProviderInputSchema(form.inputSchema) ||
|
|
418
|
+
form.inputSchema.type !== 'object' ||
|
|
419
|
+
!isOptionalString(form.variant) ||
|
|
420
|
+
!isOptionalString(form.message) ||
|
|
421
|
+
(form.blocked !== undefined && typeof form.blocked !== 'boolean') ||
|
|
422
|
+
(form.values !== undefined &&
|
|
423
|
+
(!form.values ||
|
|
424
|
+
typeof form.values !== 'object' ||
|
|
425
|
+
Array.isArray(form.values) ||
|
|
426
|
+
!isJsonValue(form.values))) ||
|
|
427
|
+
(form.dynamicFields !== undefined &&
|
|
428
|
+
(!Array.isArray(form.dynamicFields) ||
|
|
429
|
+
form.dynamicFields.some(field => typeof field !== 'string' || !(field in form.inputSchema.properties)))) ||
|
|
430
|
+
(form.refreshOn !== undefined &&
|
|
431
|
+
(!Array.isArray(form.refreshOn) ||
|
|
432
|
+
form.refreshOn.some(field => typeof field !== 'string' || !(field in form.inputSchema.properties)))) ||
|
|
433
|
+
(form.variants !== undefined &&
|
|
434
|
+
(!Array.isArray(form.variants) ||
|
|
435
|
+
!form.variants.length ||
|
|
436
|
+
form.variants.some(variant => !variant ||
|
|
437
|
+
typeof variant.id !== 'string' ||
|
|
438
|
+
!variant.id ||
|
|
439
|
+
typeof variant.title !== 'string' ||
|
|
440
|
+
!isOptionalString(variant.description)) ||
|
|
441
|
+
new Set(form.variants.map(variant => variant.id)).size !== form.variants.length ||
|
|
442
|
+
!form.variants.some(variant => variant.id === form.variant)))) {
|
|
443
|
+
throw new Error('Provider returned an invalid launch form');
|
|
444
|
+
}
|
|
445
|
+
for (const variant of form.variants ?? []) {
|
|
446
|
+
validateProviderLaunchForm({
|
|
447
|
+
inputSchema: variant.inputSchema === undefined ? form.inputSchema : variant.inputSchema,
|
|
448
|
+
values: variant.values === undefined ? form.values : variant.values,
|
|
449
|
+
refreshOn: variant.refreshOn === undefined ? form.refreshOn : variant.refreshOn,
|
|
450
|
+
dynamicFields: variant.dynamicFields === undefined ? form.dynamicFields : variant.dynamicFields,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
}
|
|
397
454
|
export function createProviderRuntime(definition) {
|
|
398
455
|
validateDefinition(definition);
|
|
399
456
|
let publishedBrowsers = [];
|
|
@@ -445,6 +502,26 @@ export function createProviderRuntime(definition) {
|
|
|
445
502
|
}
|
|
446
503
|
publishedBrowsers = [...browsers];
|
|
447
504
|
},
|
|
505
|
+
async getLaunchForm(request, context = {}) {
|
|
506
|
+
if (!definition.browserType.dynamicLaunchForm || !definition.getLaunchForm)
|
|
507
|
+
throw new Error('Provider does not support dynamic launch forms');
|
|
508
|
+
if (!request ||
|
|
509
|
+
!isOptionalString(request.variant) ||
|
|
510
|
+
!request.values ||
|
|
511
|
+
typeof request.values !== 'object' ||
|
|
512
|
+
Array.isArray(request.values) ||
|
|
513
|
+
!isJsonValue(request.values))
|
|
514
|
+
throw new Error('Invalid launch form request');
|
|
515
|
+
const form = await definition.getLaunchForm(request, context);
|
|
516
|
+
validateProviderLaunchForm(form);
|
|
517
|
+
for (const [key, value] of Object.entries(form.values ?? {})) {
|
|
518
|
+
const schema = definition.browserType.inputSchema.properties[key];
|
|
519
|
+
if (!schema)
|
|
520
|
+
throw new Error(`Launch form returned an unknown parameter: ${key}`);
|
|
521
|
+
validateSchemaValue(schema, value, key);
|
|
522
|
+
}
|
|
523
|
+
return form;
|
|
524
|
+
},
|
|
448
525
|
async launch(options, context = {}) {
|
|
449
526
|
validateProviderInput(definition.browserType.inputSchema, options);
|
|
450
527
|
const browser = await definition.launch(options, context);
|
package/dist/registry.js
CHANGED
|
@@ -3,7 +3,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
3
3
|
import { isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
4
4
|
import { claimRecord, ensureDir, hashKey, isAlive, keyedConfigFile, keyedDir, listRecordFiles, readLiveRecords, readRecord, removeInvalidRecord, removeRecordIfPid, writeRecord, writeRecordIfPid, } from '@mearl/daemon-core';
|
|
5
5
|
import { MEARL_PROVIDER_PROTOCOL_VERSION, } from './types.js';
|
|
6
|
-
import { isProviderInputSchema } from './provider-runtime.js';
|
|
6
|
+
import { validateProviderLaunchForm, isProviderInputSchema } from './provider-runtime.js';
|
|
7
7
|
export const INSTALLED_PROVIDERS_SUBDIR = 'providers/installed';
|
|
8
8
|
export const LIVE_PROVIDERS_SUBDIR = 'providers/live';
|
|
9
9
|
export const STARTING_PROVIDERS_SUBDIR = 'providers/starting';
|
|
@@ -155,6 +155,17 @@ function isProviderBrowserType(value) {
|
|
|
155
155
|
return false;
|
|
156
156
|
if (!isProviderObjectSchema(browserType.inputSchema))
|
|
157
157
|
return false;
|
|
158
|
+
if (browserType.dynamicLaunchForm !== undefined &&
|
|
159
|
+
typeof browserType.dynamicLaunchForm !== 'boolean')
|
|
160
|
+
return false;
|
|
161
|
+
if (browserType.launchForm !== undefined) {
|
|
162
|
+
try {
|
|
163
|
+
validateProviderLaunchForm(browserType.launchForm);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
158
169
|
return (browserType.configuration === undefined ||
|
|
159
170
|
(Boolean(browserType.configuration) &&
|
|
160
171
|
typeof browserType.configuration === 'object' &&
|
package/dist/server.js
CHANGED
|
@@ -81,7 +81,17 @@ 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);
|
|
87
|
+
if (request.action === 'browser_provider_launch_form') {
|
|
88
|
+
if (request.browserId)
|
|
89
|
+
throw new Error('Provider launch form does not accept browserId');
|
|
90
|
+
return runtime.getLaunchForm({
|
|
91
|
+
variant: request.data?.variant,
|
|
92
|
+
values: request.data?.values,
|
|
93
|
+
}, context);
|
|
94
|
+
}
|
|
85
95
|
if (request.action === 'browser_launch') {
|
|
86
96
|
if (request.browserId)
|
|
87
97
|
throw new Error('browser_launch does not accept 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;
|
|
@@ -54,6 +62,37 @@ export interface ProviderStringSchema extends ProviderSchemaBase {
|
|
|
54
62
|
maxLength?: number;
|
|
55
63
|
pattern?: string;
|
|
56
64
|
placeholder?: string;
|
|
65
|
+
options?: readonly ProviderInputOption[];
|
|
66
|
+
}
|
|
67
|
+
export interface ProviderInputOption {
|
|
68
|
+
value: string;
|
|
69
|
+
label: string;
|
|
70
|
+
description?: string;
|
|
71
|
+
disabled?: boolean;
|
|
72
|
+
}
|
|
73
|
+
export interface ProviderLaunchFormRequest {
|
|
74
|
+
variant?: string;
|
|
75
|
+
values: JsonObject;
|
|
76
|
+
}
|
|
77
|
+
export interface ProviderLaunchFormVariant {
|
|
78
|
+
id: string;
|
|
79
|
+
title: string;
|
|
80
|
+
description?: string;
|
|
81
|
+
inputSchema?: ProviderObjectSchema;
|
|
82
|
+
values?: JsonObject;
|
|
83
|
+
refreshOn?: readonly string[];
|
|
84
|
+
dynamicFields?: readonly string[];
|
|
85
|
+
}
|
|
86
|
+
/** Read-only presentation data; values are fixed launch parameters, never credentials. */
|
|
87
|
+
export interface ProviderLaunchForm {
|
|
88
|
+
inputSchema: ProviderObjectSchema;
|
|
89
|
+
variants?: readonly ProviderLaunchFormVariant[];
|
|
90
|
+
variant?: string;
|
|
91
|
+
values?: JsonObject;
|
|
92
|
+
refreshOn?: readonly string[];
|
|
93
|
+
dynamicFields?: readonly string[];
|
|
94
|
+
message?: string;
|
|
95
|
+
blocked?: boolean;
|
|
57
96
|
}
|
|
58
97
|
export interface ProviderNumberSchema extends ProviderSchemaBase {
|
|
59
98
|
type: 'number';
|
|
@@ -89,6 +128,8 @@ export interface ProviderBrowserType {
|
|
|
89
128
|
help?: readonly string[];
|
|
90
129
|
inputSchema: ProviderObjectSchema;
|
|
91
130
|
configuration?: ProviderConfigurationDefinition;
|
|
131
|
+
dynamicLaunchForm?: boolean;
|
|
132
|
+
launchForm?: ProviderLaunchForm;
|
|
92
133
|
}
|
|
93
134
|
export type ProviderBrowserStatus = 'connected' | 'pairing' | 'running_disconnected' | 'stopped';
|
|
94
135
|
export type ProviderOwnership = 'owned' | 'attached';
|
|
@@ -202,6 +243,7 @@ export interface BrowserProviderDefinition {
|
|
|
202
243
|
syncCookies?: (request: ProviderSyncCookiesRequest, context: ProviderContext) => Promise<unknown>;
|
|
203
244
|
getAccess?: (request: ProviderInstanceRequest, context: ProviderContext) => Promise<ProviderAccess>;
|
|
204
245
|
configure?: (options: JsonObject, context: ProviderContext) => Promise<unknown>;
|
|
246
|
+
getLaunchForm?: (request: ProviderLaunchFormRequest, context: ProviderContext) => Promise<ProviderLaunchForm>;
|
|
205
247
|
handleAction?: (context: ProviderActionContext) => Promise<unknown>;
|
|
206
248
|
}
|
|
207
249
|
export interface ProviderRuntime {
|
|
@@ -215,6 +257,7 @@ export interface ProviderRuntime {
|
|
|
215
257
|
syncCookies(request: ProviderSyncCookiesRequest, context?: ProviderContext): Promise<unknown>;
|
|
216
258
|
getAccess(request: ProviderInstanceRequest, context?: ProviderContext): Promise<ProviderAccess>;
|
|
217
259
|
configure(options: JsonObject, context?: ProviderContext): Promise<unknown>;
|
|
260
|
+
getLaunchForm(request: ProviderLaunchFormRequest, context?: ProviderContext): Promise<ProviderLaunchForm>;
|
|
218
261
|
invokeAction(context: ProviderActionContext): Promise<unknown>;
|
|
219
262
|
}
|
|
220
263
|
export interface ProviderServerOptions {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mearl/provider",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.20.1",
|
|
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.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^24.9.1",
|