@mearl/client 3.0.0-beta.1 → 3.0.0-beta.10

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.
Files changed (72) hide show
  1. package/README.md +333 -246
  2. package/README.zh-CN.md +612 -0
  3. package/dist/actionTimeouts.js +0 -3
  4. package/dist/autoUpdate.js +3 -1
  5. package/dist/bin.js +12 -5
  6. package/dist/capabilityHelp.d.ts +4 -0
  7. package/dist/capabilityHelp.js +68 -0
  8. package/dist/check.d.ts +5 -6
  9. package/dist/check.js +2 -5
  10. package/dist/cli.js +29 -56
  11. package/dist/commandHelp.d.ts +3 -0
  12. package/dist/commandHelp.js +79 -0
  13. package/dist/commandSuggestions.d.ts +3 -0
  14. package/dist/commandSuggestions.js +16 -5
  15. package/dist/commands.d.ts +70 -65
  16. package/dist/commands.js +60 -55
  17. package/dist/guide.d.ts +46 -0
  18. package/dist/guide.js +80 -0
  19. package/dist/guides/api-browser-setup.md +93 -0
  20. package/dist/guides/api-page-automation.md +140 -0
  21. package/dist/guides/api-page-debugging.md +39 -0
  22. package/dist/guides/api-page-environment.md +37 -0
  23. package/dist/guides/api-request-debugging.md +54 -0
  24. package/dist/guides/browser-tool-cooperation.md +23 -0
  25. package/dist/guides/core.md +60 -0
  26. package/dist/guides/related-skills.md +51 -0
  27. package/dist/guides/scripting.md +21 -0
  28. package/dist/guides/troubleshooting.md +170 -0
  29. package/dist/index.d.ts +5 -2
  30. package/dist/index.js +8 -5
  31. package/dist/plugins/clients/pluginHostConnection.d.ts +2 -0
  32. package/dist/plugins/clients/pluginHostConnection.js +3 -1
  33. package/dist/plugins/officialPluginInstaller.js +2 -1
  34. package/dist/plugins/pluginRegistry.d.ts +1 -1
  35. package/dist/plugins/pluginRegistry.js +21 -7
  36. package/dist/plugins/runtime/pluginCommandWorker.js +8 -6
  37. package/dist/plugins/runtime/pluginDiagnostics.js +1 -1
  38. package/dist/plugins/runtime/pluginExecutor.d.ts +5 -0
  39. package/dist/plugins/runtime/pluginExecutor.js +15 -2
  40. package/dist/plugins/runtime/pluginExecutorProtocol.d.ts +4 -0
  41. package/dist/plugins/runtime/pluginExecutorSupervisor.js +2 -1
  42. package/dist/plugins/runtime/pluginHostServer.js +5 -1
  43. package/dist/plugins/runtime/pluginPageManager.js +2 -6
  44. package/dist/plugins/runtime/pluginRevision.js +0 -51
  45. package/dist/plugins/runtime/pluginStateManager.js +1 -1
  46. package/dist/plugins/runtime/pluginStateStore.js +3 -8
  47. package/dist/plugins/runtime/pluginTaskManager.js +5 -9
  48. package/dist/plugins/sdk/plugin.d.ts +9 -0
  49. package/dist/plugins/sdk/plugin.js +22 -9
  50. package/dist/plugins/sdk/pluginModelDefinition.js +0 -6
  51. package/dist/plugins/sdk/pluginPageDefinition.d.ts +0 -1
  52. package/dist/plugins/sdk/pluginPageDefinition.js +2 -18
  53. package/dist/plugins/sdk/pluginRecoveryDefinition.js +0 -3
  54. package/dist/plugins/sdk/pluginStateDefinition.d.ts +0 -1
  55. package/dist/plugins/sdk/pluginStateDefinition.js +0 -11
  56. package/dist/runtimePaths.d.ts +1 -0
  57. package/dist/runtimePaths.js +1 -0
  58. package/dist/script.d.ts +30 -0
  59. package/dist/script.js +261 -0
  60. package/dist/scriptCli.d.ts +1 -0
  61. package/dist/scriptCli.js +94 -0
  62. package/dist/scriptErrors.d.ts +8 -0
  63. package/dist/scriptErrors.js +21 -0
  64. package/dist/scriptWorker.d.ts +1 -0
  65. package/dist/scriptWorker.js +50 -0
  66. package/dist/sdkMethods.d.ts +3 -0
  67. package/dist/sdkMethods.js +9 -0
  68. package/dist/socket.js +33 -8
  69. package/dist/unified.js +11 -1
  70. package/dist/unifiedConfig.d.ts +4 -0
  71. package/dist/unifiedConfig.js +11 -0
  72. package/package.json +14 -12
@@ -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,3 @@
1
+ /** Method names and signature exceptions shared by script dispatch and command help. */
2
+ export declare const SDK_METHOD_ACTIONS: Map<string, string>;
3
+ export declare const SDK_OPTIONS_ONLY_METHODS: Set<string>;
@@ -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)}`;
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
  }
@@ -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
+ };
@@ -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.1",
3
+ "version": "3.0.0-beta.10",
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": ">=22.12.0"
61
+ "node": ">=20.18.0"
58
62
  },
59
63
  "publishConfig": {
60
64
  "registry": "https://registry.npmjs.org"
61
65
  },
62
66
  "dependencies": {
63
- "acorn": "^8.16.0",
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/daemon-core": "3.0.0-beta.1",
69
- "@mearl/cloud-types": "3.0.0-beta.1",
70
- "@mearl/provider": "3.0.0-beta.1",
71
- "@mearl/agent-runtime": "3.0.0-beta.1",
72
- "@mearl/browser-core": "3.0.0-beta.1"
70
+ "@mearl/cloud-types": "3.0.0-beta.10",
71
+ "@mearl/provider": "3.0.0-beta.10",
72
+ "@mearl/daemon-core": "3.0.0-beta.10",
73
+ "@mearl/agent-runtime": "3.0.0-beta.10",
74
+ "@mearl/browser-core": "3.0.0-beta.10"
73
75
  },
74
76
  "devDependencies": {
75
77
  "@types/node": "^24.9.1",
@@ -78,11 +80,11 @@
78
80
  },
79
81
  "repository": {
80
82
  "type": "git",
81
- "url": "git+https://github.com/F-loat/mearl.git",
83
+ "url": "git+https://github.com/mearlhq/mearl.git",
82
84
  "directory": "packages/client"
83
85
  },
84
86
  "scripts": {
85
- "build": "rm -rf dist/ && tsc && chmod +x dist/bin.js",
87
+ "build": "rm -rf dist/ && tsc && node build-guides.mjs && chmod +x dist/bin.js",
86
88
  "dev": "tsc --watch",
87
89
  "typecheck": "tsc --noEmit",
88
90
  "test": "vitest run"