@mearl/client 3.0.0-beta.4 → 3.0.0-beta.7
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 +95 -16
- package/dist/actionTimeouts.js +1 -1
- package/dist/autoUpdate.js +2 -1
- package/dist/capabilityHelp.d.ts +4 -0
- package/dist/capabilityHelp.js +69 -0
- package/dist/check.d.ts +5 -6
- package/dist/check.js +4 -7
- package/dist/cli.js +22 -56
- package/dist/commandHelp.d.ts +3 -0
- package/dist/commandHelp.js +79 -0
- package/dist/commandSuggestions.d.ts +3 -0
- package/dist/commandSuggestions.js +16 -5
- package/dist/commands.d.ts +78 -62
- package/dist/commands.js +67 -49
- package/dist/index.d.ts +6 -2
- package/dist/index.js +10 -4
- package/dist/nativeHost.js +1 -1
- package/dist/plugins/clients/pluginHostConnection.d.ts +2 -0
- package/dist/plugins/clients/pluginHostConnection.js +3 -1
- package/dist/plugins/officialPluginInstaller.js +2 -1
- package/dist/plugins/pluginRegistry.d.ts +1 -1
- package/dist/plugins/pluginRegistry.js +12 -4
- package/dist/plugins/runtime/pluginCommandWorker.js +8 -6
- package/dist/plugins/runtime/pluginExecutor.d.ts +5 -0
- package/dist/plugins/runtime/pluginExecutor.js +15 -2
- package/dist/plugins/runtime/pluginExecutorProtocol.d.ts +4 -0
- package/dist/plugins/runtime/pluginExecutorSupervisor.js +2 -1
- package/dist/plugins/runtime/pluginHostServer.js +4 -0
- package/dist/plugins/runtime/pluginPageManager.js +0 -4
- package/dist/plugins/runtime/pluginRevision.js +0 -51
- package/dist/plugins/runtime/pluginStateStore.js +3 -8
- package/dist/plugins/runtime/pluginTaskManager.js +4 -8
- package/dist/plugins/sdk/plugin.js +1 -1
- package/dist/plugins/sdk/pluginModelDefinition.js +0 -6
- package/dist/plugins/sdk/pluginPageDefinition.d.ts +0 -1
- package/dist/plugins/sdk/pluginPageDefinition.js +2 -18
- package/dist/plugins/sdk/pluginRecoveryDefinition.js +0 -3
- package/dist/plugins/sdk/pluginStateDefinition.d.ts +0 -1
- package/dist/plugins/sdk/pluginStateDefinition.js +0 -11
- package/dist/runtimePaths.d.ts +1 -0
- package/dist/runtimePaths.js +1 -0
- package/dist/script.d.ts +30 -0
- package/dist/script.js +261 -0
- package/dist/scriptCli.d.ts +1 -0
- package/dist/scriptCli.js +94 -0
- package/dist/scriptErrors.d.ts +8 -0
- package/dist/scriptErrors.js +21 -0
- package/dist/scriptWorker.d.ts +1 -0
- package/dist/scriptWorker.js +50 -0
- package/dist/sdkMethods.d.ts +3 -0
- package/dist/sdkMethods.js +9 -0
- package/dist/socket.js +34 -9
- package/dist/unified.js +11 -1
- package/dist/unifiedConfig.d.ts +4 -0
- package/dist/unifiedConfig.js +11 -0
- package/package.json +13 -11
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { executeScript } from './script.js';
|
|
5
|
+
export async function runScriptCli(args, commandName) {
|
|
6
|
+
const program = new Command()
|
|
7
|
+
.name(`${commandName} run`)
|
|
8
|
+
.description('执行 Node.js 脚本;预注入 mearl SDK,支持顶层 await、条件和循环')
|
|
9
|
+
.option('--file <path>', '读取 JavaScript 文件;省略时从 stdin 读取')
|
|
10
|
+
.option('--session <new-or-id>', '首次建立或复用控制会话')
|
|
11
|
+
.option('--browser <id-or-name>', '指定浏览器')
|
|
12
|
+
.option('--connector <id-or-name>', '限制到远端 connector')
|
|
13
|
+
.option('--local', '仅使用本机浏览器')
|
|
14
|
+
.option('--cdp', '强制本机 CDP')
|
|
15
|
+
.option('--server <url>', '覆盖 cloud-server 地址')
|
|
16
|
+
.option('--timeout <seconds>', '整个脚本的执行时限(默认 60 秒)', '60')
|
|
17
|
+
.allowExcessArguments(false)
|
|
18
|
+
.exitOverride()
|
|
19
|
+
.addHelpText('after', `\n示例:\n ${commandName} run --file task.mjs\n ${commandName} run <<'JS'\n console.log(await mearl.tabList());\n JS\n\n脚本拥有当前宿主的 Node.js 权限;使用 await import() 加载模块。\n调用 Skill 命令:await mearl.invokePlugin('notes', { command: 'search', input: { query: 'project' } })。\n用 console.log(await mearl.help('pageClick')) 查询共享帮助,mearl.structure(value) 查看结果结构。\n每次运行重建 JS 变量;复用 session 和 tabId 可继续同一浏览器任务。\n`);
|
|
20
|
+
let source;
|
|
21
|
+
let options;
|
|
22
|
+
try {
|
|
23
|
+
program.parse(args, { from: 'user' });
|
|
24
|
+
options = program.opts();
|
|
25
|
+
const timeout = Number(options.timeout);
|
|
26
|
+
if (!Number.isFinite(timeout) || timeout <= 0 || timeout * 1000 > 2_147_483_647)
|
|
27
|
+
throw new Error('--timeout 必须是有效范围内的大于 0 的秒数');
|
|
28
|
+
if ((options.local || options.cdp) && options.connector)
|
|
29
|
+
throw new Error('--local/--cdp 不能与 --connector 同时使用');
|
|
30
|
+
if (options.file)
|
|
31
|
+
source = await readFile(resolve(options.file), 'utf8');
|
|
32
|
+
else {
|
|
33
|
+
if (process.stdin.isTTY)
|
|
34
|
+
throw new Error('通过 stdin 提交脚本,或使用 --file <path>');
|
|
35
|
+
const chunks = [];
|
|
36
|
+
let size = 0;
|
|
37
|
+
for await (const chunk of process.stdin) {
|
|
38
|
+
const buffer = Buffer.from(chunk);
|
|
39
|
+
size += buffer.length;
|
|
40
|
+
if (size > 1024 * 1024)
|
|
41
|
+
throw new Error('脚本不能超过 1 MiB');
|
|
42
|
+
chunks.push(buffer);
|
|
43
|
+
}
|
|
44
|
+
source = Buffer.concat(chunks).toString('utf8');
|
|
45
|
+
}
|
|
46
|
+
if (!source.trim() || Buffer.byteLength(source) > 1024 * 1024)
|
|
47
|
+
throw new Error('脚本必须是非空且不超过 1 MiB 的 JavaScript');
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (error.code === 'commander.helpDisplayed')
|
|
51
|
+
return 0;
|
|
52
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
53
|
+
return 2;
|
|
54
|
+
}
|
|
55
|
+
const controller = new AbortController();
|
|
56
|
+
const abort = () => controller.abort();
|
|
57
|
+
process.once('SIGINT', abort);
|
|
58
|
+
process.once('SIGTERM', abort);
|
|
59
|
+
try {
|
|
60
|
+
const result = await executeScript(source, {
|
|
61
|
+
executionTimeoutSec: Number(options.timeout),
|
|
62
|
+
signal: controller.signal,
|
|
63
|
+
source: 'cli',
|
|
64
|
+
...(options.session ? { session: options.session } : {}),
|
|
65
|
+
...(options.browser ? { browser: options.browser } : {}),
|
|
66
|
+
...(options.connector ? { connector: options.connector } : {}),
|
|
67
|
+
...(options.local || options.cdp ? { local: true } : {}),
|
|
68
|
+
...(options.cdp ? { transport: 'cdp' } : {}),
|
|
69
|
+
...(options.server ? { serverUrl: options.server } : {}),
|
|
70
|
+
});
|
|
71
|
+
if (result.stdout)
|
|
72
|
+
process.stdout.write(result.stdout);
|
|
73
|
+
if (result.stderr)
|
|
74
|
+
process.stderr.write(result.stderr);
|
|
75
|
+
if (result.session)
|
|
76
|
+
console.error(JSON.stringify({ session: result.session }));
|
|
77
|
+
if (!result.success)
|
|
78
|
+
console.error(JSON.stringify({
|
|
79
|
+
error: result.error,
|
|
80
|
+
executionStopped: result.executionStopped,
|
|
81
|
+
mayHaveLateEffects: result.mayHaveLateEffects,
|
|
82
|
+
pendingActions: result.pendingActions,
|
|
83
|
+
}));
|
|
84
|
+
return result.success ? 0 : 1;
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
88
|
+
return 1;
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
process.removeListener('SIGINT', abort);
|
|
92
|
+
process.removeListener('SIGTERM', abort);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function serializeScriptError(error: unknown, fallback?: string): {
|
|
2
|
+
stack?: string | undefined;
|
|
3
|
+
attempt?: {} | null | undefined;
|
|
4
|
+
stage?: string | undefined;
|
|
5
|
+
details?: Record<string, unknown> | undefined;
|
|
6
|
+
code: string;
|
|
7
|
+
message: string;
|
|
8
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { serializePluginExecutionError } from './plugins/runtime/pluginExecutorProtocol.js';
|
|
2
|
+
export function serializeScriptError(error, fallback = 'SCRIPT_ERROR') {
|
|
3
|
+
const value = error;
|
|
4
|
+
const diagnostic = serializePluginExecutionError({
|
|
5
|
+
message: typeof value?.message === 'string' ? value.message : String(error),
|
|
6
|
+
code: typeof value?.code === 'string' ? value.code : fallback,
|
|
7
|
+
details: {
|
|
8
|
+
...value?.details,
|
|
9
|
+
...(value?.attempt !== undefined ? { attempt: value.attempt } : {}),
|
|
10
|
+
},
|
|
11
|
+
...(typeof value?.stage === 'string' ? { stage: value.stage } : {}),
|
|
12
|
+
});
|
|
13
|
+
return {
|
|
14
|
+
code: diagnostic.code ?? fallback,
|
|
15
|
+
message: diagnostic.message,
|
|
16
|
+
...(diagnostic.details ? { details: diagnostic.details } : {}),
|
|
17
|
+
...(typeof diagnostic.metadata?.stage === 'string' ? { stage: diagnostic.metadata.stage } : {}),
|
|
18
|
+
...(diagnostic.details?.attempt !== undefined ? { attempt: diagnostic.details.attempt } : {}),
|
|
19
|
+
...(typeof value?.stack === 'string' ? { stack: value.stack.slice(0, 16384) } : {}),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { getStructure } from '@mearl/browser-core';
|
|
2
|
+
import { serializeScriptError } from './scriptErrors.js';
|
|
3
|
+
const pending = new Map();
|
|
4
|
+
let sequence = 0;
|
|
5
|
+
let session;
|
|
6
|
+
const call = (method, args) => new Promise((resolve, reject) => {
|
|
7
|
+
const id = ++sequence;
|
|
8
|
+
pending.set(id, { resolve, reject });
|
|
9
|
+
process.send({ type: 'call', id, method, args });
|
|
10
|
+
});
|
|
11
|
+
process.on('disconnect', () => process.exit(1));
|
|
12
|
+
process.on('message', async (message) => {
|
|
13
|
+
if (message.type === 'response') {
|
|
14
|
+
const entry = pending.get(message.id);
|
|
15
|
+
if (!entry)
|
|
16
|
+
return;
|
|
17
|
+
pending.delete(message.id);
|
|
18
|
+
session = message.session ?? session;
|
|
19
|
+
if (message.error)
|
|
20
|
+
entry.reject(Object.assign(new Error(message.error.message), message.error));
|
|
21
|
+
else
|
|
22
|
+
entry.resolve(message.result);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (message.type !== 'start')
|
|
26
|
+
return;
|
|
27
|
+
session = message.session;
|
|
28
|
+
const api = Object.fromEntries(message.methods.map((method) => [method, (...args) => call(method, args)]));
|
|
29
|
+
api.structure = getStructure;
|
|
30
|
+
Object.defineProperty(api, 'session', { get: () => session, enumerable: true });
|
|
31
|
+
Object.defineProperty(globalThis, 'mearl', { value: Object.freeze(api), configurable: true });
|
|
32
|
+
let failure;
|
|
33
|
+
try {
|
|
34
|
+
const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
|
|
35
|
+
const fn = new AsyncFunction('mearl', '"use strict";\n' + message.code + '\n//# sourceURL=mearl-script.js');
|
|
36
|
+
await fn(api);
|
|
37
|
+
if (pending.size)
|
|
38
|
+
throw Object.assign(new Error('Script finished with unawaited SDK calls'), {
|
|
39
|
+
code: 'SCRIPT_PENDING_CALLS',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
failure = serializeScriptError(error);
|
|
44
|
+
}
|
|
45
|
+
await Promise.all([
|
|
46
|
+
new Promise(resolve => process.stdout.write('', resolve)),
|
|
47
|
+
new Promise(resolve => process.stderr.write('', resolve)),
|
|
48
|
+
]);
|
|
49
|
+
process.send(failure ? { type: 'error', error: failure } : { type: 'done' });
|
|
50
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { BROWSER_COMMAND_ACTIONS } from '@mearl/browser-core/browserActionProtocol';
|
|
2
|
+
/** Method names and signature exceptions shared by script dispatch and command help. */
|
|
3
|
+
export const SDK_METHOD_ACTIONS = new Map(Object.keys(BROWSER_COMMAND_ACTIONS).map(action => [
|
|
4
|
+
action.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()),
|
|
5
|
+
action,
|
|
6
|
+
]));
|
|
7
|
+
SDK_METHOD_ACTIONS.set('selectBrowser', 'browser_select_browser');
|
|
8
|
+
SDK_METHOD_ACTIONS.set('captureCheckpoint', 'tab_checkpoint');
|
|
9
|
+
export const SDK_OPTIONS_ONLY_METHODS = new Set(['browserList', 'tabList', 'getRules']);
|
package/dist/socket.js
CHANGED
|
@@ -13,13 +13,13 @@ import os from 'node:os';
|
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import { CLIENT_VERSION } from './runtimePaths.js';
|
|
15
15
|
import { StringDecoder } from 'node:string_decoder';
|
|
16
|
-
import { ensureProviderControlHost, listInstalledProviders } from '@mearl/provider/host';
|
|
17
|
-
import { MEARL_PROVIDER_PROTOCOL_VERSION } from '@mearl/provider';
|
|
18
|
-
import { readLiveProvider } from '@mearl/provider/runtime';
|
|
16
|
+
import { assertProviderControlSessionMode, ensureProviderControlHost, listInstalledProviders } from '@mearl/provider/host';
|
|
17
|
+
import { MEARL_PROVIDER_PROTOCOL_VERSION, assertProviderProtocolVersion, validateProviderLaunchForm } from '@mearl/provider';
|
|
18
|
+
import { readLiveProvider, ensureInstalledProvidersRunning } from '@mearl/provider/runtime';
|
|
19
19
|
import { resolveFileRefs } from './fileRefs.js';
|
|
20
20
|
import { listLiveBrowsers, findBrowser, describeBrowser, } from './browserRegistry.js';
|
|
21
21
|
import { resolveActionTimeoutSec } from './actionTimeouts.js';
|
|
22
|
-
import { isBrowserCapabilityAction } from '@mearl/browser-core';
|
|
22
|
+
import { isBrowserCapabilityAction, parseBrowserListParams } from '@mearl/browser-core';
|
|
23
23
|
export { CLIENT_VERSION };
|
|
24
24
|
const MAX_SOCKET_BUFFER_SIZE = 64 * 1024 * 1024;
|
|
25
25
|
/**
|
|
@@ -133,6 +133,7 @@ function isOrdinaryLocalBrowser(browser) {
|
|
|
133
133
|
function supportsAction(browser, action, data) {
|
|
134
134
|
if (browser.transport === 'provider') {
|
|
135
135
|
if (action === 'browser_control' ||
|
|
136
|
+
(action === 'get_capabilities' && isConnectedBrowser(browser)) ||
|
|
136
137
|
(action === 'browser_release' && data[SESSION_OPTION]))
|
|
137
138
|
return true;
|
|
138
139
|
if (action.startsWith('__artifact_'))
|
|
@@ -375,6 +376,29 @@ export function invoke(action, data = {}, options = {}) {
|
|
|
375
376
|
const resolvedData = resolveFileRefs(data);
|
|
376
377
|
Object.defineProperty(resolvedData, SESSION_OPTION, { value: options.controlSessionId });
|
|
377
378
|
if (action === 'browser_list') {
|
|
379
|
+
const discovery = parseBrowserListParams(resolvedData);
|
|
380
|
+
if (discovery) {
|
|
381
|
+
return (async () => {
|
|
382
|
+
if (transport || options.socketPath || controlSource !== 'local' || options.controlSessionId)
|
|
383
|
+
throw new Error('Provider discovery requires the local source without a transport, socket or session override');
|
|
384
|
+
const provider = listInstalledProviders().find(provider => provider.providerId === discovery.provider);
|
|
385
|
+
if (!provider?.browserType.dynamicLaunchForm)
|
|
386
|
+
throw new Error(`Provider does not support dynamic launch forms: ${discovery.provider}`);
|
|
387
|
+
const warnings = await ensureInstalledProvidersRunning([discovery.provider]);
|
|
388
|
+
if (warnings.length)
|
|
389
|
+
throw new Error(warnings.join('; '));
|
|
390
|
+
const live = readLiveProvider(discovery.provider);
|
|
391
|
+
if (!live)
|
|
392
|
+
throw new Error(`Provider did not start: ${discovery.provider}`);
|
|
393
|
+
assertProviderProtocolVersion(live.providerProtocolVersion, discovery.provider);
|
|
394
|
+
const form = await invokeOnce('browser_providers', { operation: 'launch_form', ...discovery.launchForm }, timeoutSec, undefined, live.socketPath, 'local');
|
|
395
|
+
validateProviderLaunchForm(form);
|
|
396
|
+
return decorateBrowserListResult({
|
|
397
|
+
...browserListFromProviderRecords(listLiveBrowsers().filter(browser => browser.provider?.id === discovery.provider)),
|
|
398
|
+
discovery: { provider: discovery.provider, launchForm: form },
|
|
399
|
+
});
|
|
400
|
+
})();
|
|
401
|
+
}
|
|
378
402
|
return dispatchInvoke(action, resolvedData, timeoutSec, transport, undefined, controlSource, options.socketPath).then(result => decorateBrowserListResult(transport === 'cdp' ? cdpOnlyBrowserList(result) : result));
|
|
379
403
|
}
|
|
380
404
|
const selector = browser ?? defaultBrowser ?? process.env.MEARL_BROWSER ?? undefined;
|
|
@@ -509,7 +533,7 @@ async function dispatchInvoke(action, data, timeoutSec, transport, selector, con
|
|
|
509
533
|
throw new Error(`Browser ${describeBrowser(match)} does not support ${action}`);
|
|
510
534
|
}
|
|
511
535
|
try {
|
|
512
|
-
return await invokeOnce(action, data, timeoutSec, transport, match.socketPath, controlSource, match.transport === 'provider' ? match.browserId : undefined);
|
|
536
|
+
return await invokeOnce(action, data, timeoutSec, transport, match.socketPath, controlSource, match.transport === 'provider' ? match.browserId : undefined, match.capabilities?.controlSessionMode);
|
|
513
537
|
}
|
|
514
538
|
catch (error) {
|
|
515
539
|
if (isConnError(error) || isSocketSandboxError(error)) {
|
|
@@ -526,7 +550,7 @@ async function dispatchInvoke(action, data, timeoutSec, transport, selector, con
|
|
|
526
550
|
if (candidates.length > 0) {
|
|
527
551
|
const target = candidates[0];
|
|
528
552
|
try {
|
|
529
|
-
return await invokeOnce(action, data, timeoutSec, transport, target.socketPath, controlSource, target.transport === 'provider' ? target.browserId : undefined);
|
|
553
|
+
return await invokeOnce(action, data, timeoutSec, transport, target.socketPath, controlSource, target.transport === 'provider' ? target.browserId : undefined, target.capabilities?.controlSessionMode);
|
|
530
554
|
}
|
|
531
555
|
catch (error) {
|
|
532
556
|
if (isSocketSandboxError(error)) {
|
|
@@ -574,7 +598,7 @@ async function invokeViaLegacy(action, data, timeoutSec, transport, controlSourc
|
|
|
574
598
|
!(transport === 'cdp' && browser.transport === 'provider') &&
|
|
575
599
|
supportsAction(browser, action, data));
|
|
576
600
|
if (target) {
|
|
577
|
-
return invokeOnce(action, data, timeoutSec, transport, target.socketPath, controlSource, target.transport === 'provider' ? target.browserId : undefined);
|
|
601
|
+
return invokeOnce(action, data, timeoutSec, transport, target.socketPath, controlSource, target.transport === 'provider' ? target.browserId : undefined, target.capabilities?.controlSessionMode);
|
|
578
602
|
}
|
|
579
603
|
return invokeViaLegacy(action, data, timeoutSec, transport, controlSource,
|
|
580
604
|
/* isRetry */ true, localOnly);
|
|
@@ -585,9 +609,10 @@ async function invokeViaLegacy(action, data, timeoutSec, transport, controlSourc
|
|
|
585
609
|
throw new Error(formatConnectError(err, SOCKET_PATH), { cause: error });
|
|
586
610
|
}
|
|
587
611
|
}
|
|
588
|
-
async function invokeOnce(action, data, timeoutSec, transport, socketPath, controlSource, providerTarget) {
|
|
612
|
+
async function invokeOnce(action, data, timeoutSec, transport, socketPath, controlSource, providerTarget, controlSessionMode) {
|
|
589
613
|
if (providerTarget) {
|
|
590
614
|
const host = await ensureProviderControlHost(Math.min(timeoutSec * 1000, 10_000));
|
|
615
|
+
assertProviderControlSessionMode(host, controlSessionMode);
|
|
591
616
|
socketPath = host.socketPath;
|
|
592
617
|
}
|
|
593
618
|
const id = `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
@@ -686,7 +711,7 @@ function formatConnectError(error, socketPath) {
|
|
|
686
711
|
if (error.code === 'ENOENT') {
|
|
687
712
|
return [
|
|
688
713
|
`未找到 Socket: ${socketPath}`,
|
|
689
|
-
'native host 自动拉起失败。先运行 mearl check --local;缺少命令时运行 npx
|
|
714
|
+
'native host 自动拉起失败。先运行 mearl check --local;缺少命令时运行 npx @mearl/setup --local --yes。',
|
|
690
715
|
'自动拉起只使用默认 daemon 参数;自定义 CDP/Profile/浏览器参数需手工启动,参见 mearl-native-host --help。',
|
|
691
716
|
'',
|
|
692
717
|
'【方式一:浏览器插件模式】',
|
package/dist/unified.js
CHANGED
|
@@ -3,7 +3,7 @@ import { stageFile, exportArtifact } from './artifacts.js';
|
|
|
3
3
|
import WebSocket from 'ws';
|
|
4
4
|
import { DEFAULT_CONNECT_TIMEOUT, DEFAULT_HEARTBEAT_INTERVAL, DEFAULT_REQUEST_TIMEOUT, MAX_BUFFER_SIZE, isHeartbeatMessage, resolveCloudConnectorSelector, } from '@mearl/cloud-types';
|
|
5
5
|
import { resolveActionTimeoutSec } from './actionTimeouts.js';
|
|
6
|
-
import { validateRequiredActionTabId, } from '@mearl/browser-core';
|
|
6
|
+
import { validateRequiredActionTabId, parseBrowserListParams, } from '@mearl/browser-core';
|
|
7
7
|
import { invoke as invokeLocalBrowser, resolveLocalActionTarget } from './socket.js';
|
|
8
8
|
import { loadCloudServerConfig, loadLocalConnectorId } from './unifiedConfig.js';
|
|
9
9
|
const RESPONSE_GRACE_MS = 1_000;
|
|
@@ -146,6 +146,9 @@ export class UnifiedClient {
|
|
|
146
146
|
}, options);
|
|
147
147
|
}
|
|
148
148
|
async invoke(action, data = {}, options = {}) {
|
|
149
|
+
const discovery = action === 'browser_list' ? parseBrowserListParams(data) : undefined;
|
|
150
|
+
if (discovery && (!options.local || options.connector || options.transport || options.session || options.controlSessionId))
|
|
151
|
+
throw new Error('Provider discovery requires explicit --local without connector, transport, socket or session overrides');
|
|
149
152
|
if (action === 'plugin_state') {
|
|
150
153
|
const { PluginStateClient } = await import('./plugins/clients/pluginState.js');
|
|
151
154
|
const state = new PluginStateClient();
|
|
@@ -350,6 +353,12 @@ export class UnifiedClient {
|
|
|
350
353
|
if (action === 'connector_list')
|
|
351
354
|
return this.listConnectors(timeoutSec);
|
|
352
355
|
if (action === 'browser_list') {
|
|
356
|
+
if (parseBrowserListParams(data)) {
|
|
357
|
+
const result = await invokeLocalBrowser('browser_list', data, { timeoutSec, controlSource: 'local' });
|
|
358
|
+
if (!result?.discovery)
|
|
359
|
+
throw new Error('Provider launch-form discovery is not supported by this host');
|
|
360
|
+
return this.applyConfiguredDefault(this.decorateListing(result, { kind: 'local' }));
|
|
361
|
+
}
|
|
353
362
|
if (options.local || options.transport === 'cdp') {
|
|
354
363
|
return this.listLocalBrowsers(timeoutSec, options.transport);
|
|
355
364
|
}
|
|
@@ -543,6 +552,7 @@ export class UnifiedClient {
|
|
|
543
552
|
})
|
|
544
553
|
: null,
|
|
545
554
|
browsers,
|
|
555
|
+
...(result?.discovery ? { discovery: result.discovery } : {}),
|
|
546
556
|
...(resultWarnings.length > 0 ? { warnings: resultWarnings } : {}),
|
|
547
557
|
};
|
|
548
558
|
}
|
package/dist/unifiedConfig.d.ts
CHANGED
|
@@ -6,3 +6,7 @@ export declare function loadCliAutoUpdateIntervalDays(configPath?: string): numb
|
|
|
6
6
|
export declare function getCloudServerConfigPath(): string;
|
|
7
7
|
export declare function loadCloudServerConfig(): CloudServerConfig | null;
|
|
8
8
|
export declare function loadLocalConnectorId(): string | null;
|
|
9
|
+
export declare function resolveServer(explicit?: string): {
|
|
10
|
+
serverUrl?: string;
|
|
11
|
+
serverUrlSource?: string;
|
|
12
|
+
};
|
package/dist/unifiedConfig.js
CHANGED
|
@@ -56,3 +56,14 @@ export function loadLocalConnectorId() {
|
|
|
56
56
|
return null;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
+
export function resolveServer(explicit) {
|
|
60
|
+
if (explicit)
|
|
61
|
+
return { serverUrl: explicit, serverUrlSource: '--server' };
|
|
62
|
+
if (process.env.MEARL_SERVER_URL) {
|
|
63
|
+
return { serverUrl: process.env.MEARL_SERVER_URL, serverUrlSource: 'MEARL_SERVER_URL' };
|
|
64
|
+
}
|
|
65
|
+
const config = loadCloudServerConfig();
|
|
66
|
+
return config?.server
|
|
67
|
+
? { serverUrl: config.server, serverUrlSource: getCloudServerConfigPath() }
|
|
68
|
+
: {};
|
|
69
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mearl/client",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.7",
|
|
4
4
|
"description": "Unified Mearl SDK & CLI for local and remote browsers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -37,6 +37,10 @@
|
|
|
37
37
|
"./plugin": {
|
|
38
38
|
"types": "./dist/plugin.d.ts",
|
|
39
39
|
"import": "./dist/plugin.js"
|
|
40
|
+
},
|
|
41
|
+
"./script": {
|
|
42
|
+
"types": "./dist/script.d.ts",
|
|
43
|
+
"import": "./dist/script.js"
|
|
40
44
|
}
|
|
41
45
|
},
|
|
42
46
|
"bin": {
|
|
@@ -54,22 +58,20 @@
|
|
|
54
58
|
],
|
|
55
59
|
"license": "Apache-2.0",
|
|
56
60
|
"engines": {
|
|
57
|
-
"node": ">=
|
|
61
|
+
"node": ">=20.18.0"
|
|
58
62
|
},
|
|
59
63
|
"publishConfig": {
|
|
60
64
|
"registry": "https://registry.npmjs.org"
|
|
61
65
|
},
|
|
62
66
|
"dependencies": {
|
|
63
|
-
"
|
|
64
|
-
"ajv": "^8.18.0",
|
|
65
|
-
"commander": "^15.0.0",
|
|
67
|
+
"commander": "^14.0.3",
|
|
66
68
|
"fastest-levenshtein": "^1.0.16",
|
|
67
69
|
"ws": "^8.18.0",
|
|
68
|
-
"@mearl/
|
|
69
|
-
"@mearl/
|
|
70
|
-
"@mearl/
|
|
71
|
-
"@mearl/agent-runtime": "3.0.0-beta.
|
|
72
|
-
"@mearl/browser-core": "3.0.0-beta.
|
|
70
|
+
"@mearl/daemon-core": "3.0.0-beta.7",
|
|
71
|
+
"@mearl/cloud-types": "3.0.0-beta.7",
|
|
72
|
+
"@mearl/provider": "3.0.0-beta.7",
|
|
73
|
+
"@mearl/agent-runtime": "3.0.0-beta.7",
|
|
74
|
+
"@mearl/browser-core": "3.0.0-beta.7"
|
|
73
75
|
},
|
|
74
76
|
"devDependencies": {
|
|
75
77
|
"@types/node": "^24.9.1",
|
|
@@ -78,7 +80,7 @@
|
|
|
78
80
|
},
|
|
79
81
|
"repository": {
|
|
80
82
|
"type": "git",
|
|
81
|
-
"url": "git+https://github.com/
|
|
83
|
+
"url": "git+https://github.com/mearlhq/mearl.git",
|
|
82
84
|
"directory": "packages/client"
|
|
83
85
|
},
|
|
84
86
|
"scripts": {
|