@mearl/client 2.20.6 → 2.22.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.
Files changed (130) hide show
  1. package/README.md +153 -3
  2. package/dist/actionTimeouts.js +1 -1
  3. package/dist/artifacts.d.ts +1 -1
  4. package/dist/browserRegistry.d.ts +1 -1
  5. package/dist/check.d.ts +1 -1
  6. package/dist/cli.js +14 -8
  7. package/dist/commands.d.ts +23 -14
  8. package/dist/commands.js +24 -4
  9. package/dist/{generated-app-profile-definition.d.ts → generated/app-profile-definition.d.ts} +2 -2
  10. package/dist/{generated-app-profile-runtime-types.d.ts → generated/app-profile-runtime-types.d.ts} +2 -2
  11. package/dist/{generated-browser-action-protocol.d.ts → generated/browser-action-protocol.d.ts} +11 -1
  12. package/dist/{generated-browser-action-protocol.js → generated/browser-action-protocol.js} +3 -0
  13. package/dist/generated/plugin-diagnostics.d.ts +110 -0
  14. package/dist/generated/plugin-diagnostics.js +1 -0
  15. package/dist/generated/plugin-execution.d.ts +27 -0
  16. package/dist/generated/plugin-execution.js +1 -0
  17. package/dist/generated/plugin-models.d.ts +64 -0
  18. package/dist/generated/plugin-models.js +1 -0
  19. package/dist/generated/plugin-pages.d.ts +76 -0
  20. package/dist/generated/plugin-pages.js +59 -0
  21. package/dist/generated/plugin-recovery.d.ts +32 -0
  22. package/dist/generated/plugin-recovery.js +1 -0
  23. package/dist/generated/plugin-state.d.ts +50 -0
  24. package/dist/generated/plugin-state.js +1 -0
  25. package/dist/generated/plugin-tasks.d.ts +74 -0
  26. package/dist/generated/plugin-tasks.js +7 -0
  27. package/dist/generated/toolbox-icon.d.ts +10 -0
  28. package/dist/generated/toolbox-icon.js +37 -0
  29. package/dist/index.d.ts +15 -4
  30. package/dist/index.js +7 -1
  31. package/dist/pageEvalArguments.d.ts +1 -1
  32. package/dist/pageEvalArguments.js +1 -1
  33. package/dist/plugin-task-host.d.ts +1 -0
  34. package/dist/plugin-task-host.js +14 -0
  35. package/dist/plugin-worker.d.ts +1 -0
  36. package/dist/plugin-worker.js +17 -0
  37. package/dist/plugin.d.ts +1 -65
  38. package/dist/plugin.js +1 -391
  39. package/dist/pluginCliOutput.d.ts +2 -0
  40. package/dist/pluginCliOutput.js +7 -0
  41. package/dist/pluginModelSessionWorker.d.ts +23 -0
  42. package/dist/pluginModelSessionWorker.js +60 -0
  43. package/dist/plugins/clients/pluginHostConnection.d.ts +35 -0
  44. package/dist/plugins/clients/pluginHostConnection.js +256 -0
  45. package/dist/plugins/clients/pluginModels.d.ts +17 -0
  46. package/dist/plugins/clients/pluginModels.js +47 -0
  47. package/dist/plugins/clients/pluginPages.d.ts +21 -0
  48. package/dist/plugins/clients/pluginPages.js +46 -0
  49. package/dist/plugins/clients/pluginState.d.ts +26 -0
  50. package/dist/plugins/clients/pluginState.js +28 -0
  51. package/dist/plugins/clients/pluginTasks.d.ts +51 -0
  52. package/dist/plugins/clients/pluginTasks.js +181 -0
  53. package/dist/{officialPluginInstaller.js → plugins/officialPluginInstaller.js} +1 -3
  54. package/dist/{pluginPageBundle.js → plugins/pluginPageBundle.js} +74 -17
  55. package/dist/{pluginRegistry.d.ts → plugins/pluginRegistry.d.ts} +3 -4
  56. package/dist/{pluginRegistry.js → plugins/pluginRegistry.js} +16 -15
  57. package/dist/plugins/pluginTaskProtocol.d.ts +47 -0
  58. package/dist/plugins/pluginTaskProtocol.js +1 -0
  59. package/dist/plugins/pluginTaskWire.d.ts +4 -0
  60. package/dist/plugins/pluginTaskWire.js +42 -0
  61. package/dist/plugins/runtime/pluginCommandWorker.d.ts +2 -0
  62. package/dist/plugins/runtime/pluginCommandWorker.js +174 -0
  63. package/dist/plugins/runtime/pluginDiagnostics.d.ts +26 -0
  64. package/dist/plugins/runtime/pluginDiagnostics.js +309 -0
  65. package/dist/plugins/runtime/pluginExecution.d.ts +21 -0
  66. package/dist/plugins/runtime/pluginExecution.js +43 -0
  67. package/dist/plugins/runtime/pluginExecutor.d.ts +29 -0
  68. package/dist/plugins/runtime/pluginExecutor.js +223 -0
  69. package/dist/plugins/runtime/pluginExecutorProtocol.d.ts +55 -0
  70. package/dist/plugins/runtime/pluginExecutorProtocol.js +47 -0
  71. package/dist/plugins/runtime/pluginExecutorSupervisor.d.ts +4 -0
  72. package/dist/plugins/runtime/pluginExecutorSupervisor.js +63 -0
  73. package/dist/plugins/runtime/pluginHost.d.ts +20 -0
  74. package/dist/plugins/runtime/pluginHost.js +53 -0
  75. package/dist/plugins/runtime/pluginHostFiles.d.ts +1 -0
  76. package/dist/plugins/runtime/pluginHostFiles.js +21 -0
  77. package/dist/plugins/runtime/pluginHostPaths.d.ts +11 -0
  78. package/dist/plugins/runtime/pluginHostPaths.js +22 -0
  79. package/dist/plugins/runtime/pluginHostServer.d.ts +7 -0
  80. package/dist/plugins/runtime/pluginHostServer.js +252 -0
  81. package/dist/plugins/runtime/pluginModelCatalog.d.ts +14 -0
  82. package/dist/plugins/runtime/pluginModelCatalog.js +51 -0
  83. package/dist/plugins/runtime/pluginModelSessions.d.ts +16 -0
  84. package/dist/plugins/runtime/pluginModelSessions.js +234 -0
  85. package/dist/plugins/runtime/pluginPageManager.d.ts +57 -0
  86. package/dist/plugins/runtime/pluginPageManager.js +877 -0
  87. package/dist/plugins/runtime/pluginProcessTree.d.ts +2 -0
  88. package/dist/plugins/runtime/pluginProcessTree.js +31 -0
  89. package/dist/plugins/runtime/pluginRevision.d.ts +14 -0
  90. package/dist/plugins/runtime/pluginRevision.js +227 -0
  91. package/dist/plugins/runtime/pluginRuntimeCleanup.d.ts +2 -0
  92. package/dist/plugins/runtime/pluginRuntimeCleanup.js +55 -0
  93. package/dist/plugins/runtime/pluginStateManager.d.ts +24 -0
  94. package/dist/plugins/runtime/pluginStateManager.js +132 -0
  95. package/dist/plugins/runtime/pluginStateStore.d.ts +57 -0
  96. package/dist/plugins/runtime/pluginStateStore.js +396 -0
  97. package/dist/plugins/runtime/pluginTaskManager.d.ts +92 -0
  98. package/dist/plugins/runtime/pluginTaskManager.js +1080 -0
  99. package/dist/plugins/runtime/pluginTaskStore.d.ts +53 -0
  100. package/dist/plugins/runtime/pluginTaskStore.js +319 -0
  101. package/dist/plugins/runtime/pythonPlugin.d.ts +31 -0
  102. package/dist/{pythonPlugin.js → plugins/runtime/pythonPlugin.js} +236 -31
  103. package/dist/plugins/sdk/plugin.d.ts +118 -0
  104. package/dist/plugins/sdk/plugin.js +672 -0
  105. package/dist/{pluginAppProfiles.d.ts → plugins/sdk/pluginAppProfiles.d.ts} +1 -1
  106. package/dist/{pluginAppProfiles.js → plugins/sdk/pluginAppProfiles.js} +2 -2
  107. package/dist/plugins/sdk/pluginModelDefinition.d.ts +6 -0
  108. package/dist/plugins/sdk/pluginModelDefinition.js +75 -0
  109. package/dist/plugins/sdk/pluginPageDefinition.d.ts +3 -0
  110. package/dist/plugins/sdk/pluginPageDefinition.js +62 -0
  111. package/dist/plugins/sdk/pluginRecoveryDefinition.d.ts +3 -0
  112. package/dist/plugins/sdk/pluginRecoveryDefinition.js +56 -0
  113. package/dist/plugins/sdk/pluginStateDefinition.d.ts +4 -0
  114. package/dist/plugins/sdk/pluginStateDefinition.js +68 -0
  115. package/dist/runtimePaths.d.ts +4 -0
  116. package/dist/runtimePaths.js +8 -0
  117. package/dist/socket.d.ts +2 -2
  118. package/dist/socket.js +10 -6
  119. package/dist/unified.d.ts +3 -2
  120. package/dist/unified.js +83 -2
  121. package/package.json +7 -3
  122. package/dist/pythonPlugin.d.ts +0 -20
  123. /package/dist/{generated-app-profile-definition.js → generated/app-profile-definition.js} +0 -0
  124. /package/dist/{generated-app-profile-runtime-types.js → generated/app-profile-runtime-types.js} +0 -0
  125. /package/dist/{officialPluginCatalog.d.ts → plugins/officialPluginCatalog.d.ts} +0 -0
  126. /package/dist/{officialPluginCatalog.js → plugins/officialPluginCatalog.js} +0 -0
  127. /package/dist/{officialPluginInstaller.d.ts → plugins/officialPluginInstaller.d.ts} +0 -0
  128. /package/dist/{pluginPageBundle.d.ts → plugins/pluginPageBundle.d.ts} +0 -0
  129. /package/dist/{pluginSource.d.ts → plugins/pluginSource.d.ts} +0 -0
  130. /package/dist/{pluginSource.js → plugins/pluginSource.js} +0 -0
@@ -1,7 +1,7 @@
1
1
  import { readFileSync, realpathSync } from 'node:fs';
2
2
  import { basename, dirname, isAbsolute, relative, resolve } from 'node:path';
3
- import { validateAppProfileContribution, } from './generated-app-profile-definition.js';
4
- import { pluginFileVersion } from './pluginSource.js';
3
+ import { validateAppProfileContribution, } from '../../generated/app-profile-definition.js';
4
+ import { pluginFileVersion } from '../pluginSource.js';
5
5
  /** Profile resources share the Skill root and metadata refresh rules of toolbox resources. */
6
6
  export function loadPluginAppProfiles(entry, value) {
7
7
  if (value === undefined)
@@ -0,0 +1,6 @@
1
+ import type { PluginModelCapability, PluginModelRequest, PluginModelResult } from '../../generated/plugin-models.js';
2
+ export declare function pluginModelCapabilities(): {
3
+ providers: PluginModelCapability[];
4
+ };
5
+ export declare function normalizePluginModel(value: unknown): PluginModelRequest;
6
+ export declare function validatePluginModelResult(request: PluginModelRequest, value: unknown): PluginModelResult;
@@ -0,0 +1,75 @@
1
+ import { CODEX_MODEL_CAPABILITY } from '@mearl/agent-runtime/capabilities';
2
+ import { compilePluginStateSchema } from './pluginStateDefinition.js';
3
+ export function pluginModelCapabilities() {
4
+ return { providers: [CODEX_MODEL_CAPABILITY] };
5
+ }
6
+ export function normalizePluginModel(value) {
7
+ const request = value;
8
+ if (!request ||
9
+ typeof request !== 'object' ||
10
+ Array.isArray(request) ||
11
+ typeof request.prompt !== 'string' ||
12
+ !request.prompt.trim() ||
13
+ Buffer.byteLength(JSON.stringify(request)) > 256 * 1024 ||
14
+ (request.instructions !== undefined && typeof request.instructions !== 'string') ||
15
+ (request.conversation !== undefined &&
16
+ (typeof request.conversation !== 'string' ||
17
+ !/^[a-zA-Z0-9:_-]{1,80}$/.test(request.conversation))) ||
18
+ (request.model !== undefined &&
19
+ (typeof request.model !== 'string' || !request.model.trim() || request.model.length > 256)) ||
20
+ (request.reasoningEffort !== undefined &&
21
+ !['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'].includes(request.reasoningEffort)) ||
22
+ Object.keys(request).some(key => ![
23
+ 'provider',
24
+ 'mode',
25
+ 'prompt',
26
+ 'instructions',
27
+ 'model',
28
+ 'reasoningEffort',
29
+ 'outputSchema',
30
+ 'permissions',
31
+ 'conversation',
32
+ ].includes(key)))
33
+ throw new Error('Invalid model execution request');
34
+ const capability = pluginModelCapabilities().providers.find(item => item.provider === request.provider);
35
+ if (!capability?.modes.includes(request.mode) ||
36
+ (request.mode === 'agent' && !capability.permissions.includes(request.permissions)) ||
37
+ (request.mode === 'generate' && request.permissions !== undefined))
38
+ throw Object.assign(new Error('Requested model mode or permissions are unsupported; choose an advertised capability explicitly'), { code: 'PLUGIN_MODEL_CAPABILITY_UNSUPPORTED' });
39
+ if (request.outputSchema !== undefined) {
40
+ if (!capability.structuredOutput ||
41
+ !request.outputSchema ||
42
+ typeof request.outputSchema !== 'object' ||
43
+ Array.isArray(request.outputSchema) ||
44
+ request.outputSchema.$async ||
45
+ Buffer.byteLength(JSON.stringify(request.outputSchema)) > 64 * 1024)
46
+ throw new Error('Invalid model output schema');
47
+ compilePluginStateSchema(request.outputSchema);
48
+ }
49
+ return JSON.parse(JSON.stringify(request));
50
+ }
51
+ export function validatePluginModelResult(request, value) {
52
+ const result = value;
53
+ if (!result ||
54
+ typeof result.text !== 'string' ||
55
+ result.provider !== request.provider ||
56
+ result.mode !== request.mode)
57
+ throw new Error('Invalid model execution result');
58
+ if (request.outputSchema) {
59
+ let output;
60
+ try {
61
+ output = JSON.parse(result.text);
62
+ }
63
+ catch {
64
+ throw Object.assign(new Error('Model returned invalid JSON'), {
65
+ code: 'PLUGIN_MODEL_SCHEMA_INVALID',
66
+ });
67
+ }
68
+ if (!compilePluginStateSchema(request.outputSchema)(output))
69
+ throw Object.assign(new Error('Model result does not match the output schema'), {
70
+ code: 'PLUGIN_MODEL_SCHEMA_INVALID',
71
+ });
72
+ return { ...result, output };
73
+ }
74
+ return result;
75
+ }
@@ -0,0 +1,3 @@
1
+ import type { PluginPageRuntime } from '../../generated/plugin-pages.js';
2
+ export declare function normalizePluginPageRuntime(value: unknown): PluginPageRuntime | undefined;
3
+ export declare function compilePluginPageSchema(schema: Record<string, unknown>): import("ajv/dist/core.js").AnyValidateFunction<unknown>;
@@ -0,0 +1,62 @@
1
+ import { parse } from 'acorn';
2
+ import { Ajv } from 'ajv';
3
+ import { isPluginPageMatchPattern } from '../../generated/plugin-pages.js';
4
+ const validators = new WeakMap();
5
+ export function normalizePluginPageRuntime(value) {
6
+ if (value === undefined)
7
+ return undefined;
8
+ const runtime = value;
9
+ if (!runtime ||
10
+ runtime.version !== 1 ||
11
+ !Array.isArray(runtime.matches) ||
12
+ !runtime.matches.length ||
13
+ runtime.matches.length > 64 ||
14
+ !runtime.matches.every(isPluginPageMatchPattern) ||
15
+ typeof runtime.code !== 'string' ||
16
+ Buffer.byteLength(runtime.code) > 384 * 1024 ||
17
+ Buffer.byteLength(JSON.stringify(runtime)) > 448 * 1024)
18
+ throw new Error('Invalid plugin pageRuntime declaration');
19
+ const program = parse(`(${runtime.code}\n)`, { ecmaVersion: 'latest' });
20
+ const statement = program.body[0];
21
+ if (program.body.length !== 1 ||
22
+ statement.type !== 'ExpressionStatement' ||
23
+ !['ArrowFunctionExpression', 'FunctionExpression'].includes(statement.expression?.type ?? ''))
24
+ throw new Error('pageRuntime.code must be a self-contained function expression');
25
+ if (runtime.events !== undefined &&
26
+ (!runtime.events ||
27
+ typeof runtime.events !== 'object' ||
28
+ Array.isArray(runtime.events) ||
29
+ Object.keys(runtime.events).length > 32))
30
+ throw new Error('Invalid page runtime events');
31
+ for (const [name, event] of Object.entries(runtime.events ?? {})) {
32
+ if (!/^[a-z][a-z0-9-]{0,63}$/.test(name) ||
33
+ !event ||
34
+ !/^[a-z][a-z0-9-]*$/.test(event.command) ||
35
+ !event.schema ||
36
+ typeof event.schema !== 'object' ||
37
+ Array.isArray(event.schema) ||
38
+ (event.policy !== undefined && !['serial', 'latest', 'parallel'].includes(event.policy)) ||
39
+ (event.concurrency !== undefined &&
40
+ (!Number.isSafeInteger(event.concurrency) ||
41
+ event.concurrency < 1 ||
42
+ event.concurrency > 8)) ||
43
+ (event.maxPending !== undefined &&
44
+ (!Number.isSafeInteger(event.maxPending) ||
45
+ event.maxPending < 1 ||
46
+ event.maxPending > 64)) ||
47
+ event.schema.$async)
48
+ throw new Error(`Invalid page event binding: ${name}`);
49
+ compilePluginPageSchema(event.schema);
50
+ }
51
+ if (runtime.initialize !== undefined && !/^[a-z][a-z0-9-]*$/.test(runtime.initialize))
52
+ throw new Error('Invalid page runtime initialize command');
53
+ return JSON.parse(JSON.stringify(runtime));
54
+ }
55
+ export function compilePluginPageSchema(schema) {
56
+ let validate = validators.get(schema);
57
+ if (!validate) {
58
+ validate = new Ajv({ strict: true, allErrors: false }).compile(schema);
59
+ validators.set(schema, validate);
60
+ }
61
+ return validate;
62
+ }
@@ -0,0 +1,3 @@
1
+ import type { PluginTaskRecoveryDeclaration } from '../../generated/plugin-recovery.js';
2
+ export declare function normalizePluginTaskRecovery(value: unknown): PluginTaskRecoveryDeclaration | undefined;
3
+ export declare function pluginRecoverySignature(value: unknown): string;
@@ -0,0 +1,56 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { compilePluginStateSchema } from './pluginStateDefinition.js';
3
+ export function normalizePluginTaskRecovery(value) {
4
+ if (value === undefined)
5
+ return;
6
+ const declaration = JSON.parse(JSON.stringify(value));
7
+ if (!declaration ||
8
+ declaration.version !== 1 ||
9
+ !declaration.commands ||
10
+ typeof declaration.commands !== 'object' ||
11
+ Array.isArray(declaration.commands) ||
12
+ Object.keys(declaration.commands).length > 32 ||
13
+ Buffer.byteLength(JSON.stringify(declaration)) > 128 * 1024)
14
+ throw new Error('Invalid taskRecovery declaration');
15
+ for (const [command, definition] of Object.entries(declaration.commands)) {
16
+ if (!/^[a-z][a-z0-9-]*$/.test(command) ||
17
+ !definition ||
18
+ !Number.isSafeInteger(definition.checkpointVersion) ||
19
+ definition.checkpointVersion < 1 ||
20
+ !definition.schema ||
21
+ typeof definition.schema !== 'object' ||
22
+ Array.isArray(definition.schema) ||
23
+ definition.schema.$async)
24
+ throw new Error('Invalid task checkpoint definition');
25
+ compilePluginStateSchema(definition.schema);
26
+ if (definition.migrateFrom !== undefined) {
27
+ if (!Array.isArray(definition.migrateFrom) ||
28
+ definition.migrateFrom.length < 1 ||
29
+ definition.migrateFrom.length > 16)
30
+ throw new Error('Invalid checkpoint migration sources');
31
+ const versions = new Set();
32
+ for (const source of definition.migrateFrom) {
33
+ if (!source ||
34
+ !Number.isSafeInteger(source.version) ||
35
+ source.version < 1 ||
36
+ source.version >= definition.checkpointVersion ||
37
+ versions.has(source.version) ||
38
+ !source.schema ||
39
+ typeof source.schema !== 'object' ||
40
+ Array.isArray(source.schema) ||
41
+ source.schema.$async)
42
+ throw new Error('Checkpoint migrations require distinct older versions and schemas');
43
+ compilePluginStateSchema(source.schema);
44
+ versions.add(source.version);
45
+ }
46
+ }
47
+ }
48
+ return declaration;
49
+ }
50
+ export function pluginRecoverySignature(value) {
51
+ return createHash('sha256')
52
+ .update(JSON.stringify(value, (_key, item) => item && typeof item === 'object' && !Array.isArray(item)
53
+ ? Object.fromEntries(Object.entries(item).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)))
54
+ : item))
55
+ .digest('hex');
56
+ }
@@ -0,0 +1,4 @@
1
+ import type { PluginStateDeclaration } from '../../generated/plugin-state.js';
2
+ export declare function compilePluginStateSchema(schema: Record<string, unknown>): import("ajv/dist/core.js").AnyValidateFunction<unknown>;
3
+ export declare function normalizePluginState(value: unknown): PluginStateDeclaration | undefined;
4
+ export declare function pluginStateSignature(collection: import('../../generated/plugin-state.js').PluginStateCollection): string;
@@ -0,0 +1,68 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { Ajv } from 'ajv';
3
+ const validators = new WeakMap();
4
+ export function compilePluginStateSchema(schema) {
5
+ let validate = validators.get(schema);
6
+ if (!validate) {
7
+ validate = new Ajv({ strict: true }).compile(schema);
8
+ validators.set(schema, validate);
9
+ }
10
+ return validate;
11
+ }
12
+ export function normalizePluginState(value) {
13
+ if (value === undefined)
14
+ return;
15
+ const declaration = JSON.parse(JSON.stringify(value));
16
+ if (!declaration ||
17
+ declaration.version !== 1 ||
18
+ !declaration.collections ||
19
+ Array.isArray(declaration.collections) ||
20
+ Object.keys(declaration.collections).length > 32 ||
21
+ Buffer.byteLength(JSON.stringify(declaration)) > 128 * 1024)
22
+ throw new Error('Invalid stateStorage declaration');
23
+ for (const [name, collection] of Object.entries(declaration.collections)) {
24
+ if (!/^[a-z][a-z0-9-]{0,63}$/.test(name) ||
25
+ !collection ||
26
+ !['installation', 'host', 'browser-session', 'instance'].includes(collection.scope) ||
27
+ !['persistent', 'cache', 'memory'].includes(collection.storage) ||
28
+ !Number.isSafeInteger(collection.schemaVersion) ||
29
+ collection.schemaVersion < 1 ||
30
+ !collection.schema ||
31
+ typeof collection.schema !== 'object' ||
32
+ Array.isArray(collection.schema) ||
33
+ collection.schema.$async)
34
+ throw new Error(`Invalid state collection: ${name}`);
35
+ if (['browser-session', 'instance'].includes(collection.scope) &&
36
+ !['cache', 'memory'].includes(collection.storage))
37
+ throw new Error('Document state requires cache or memory storage');
38
+ if (collection.storage === 'memory' &&
39
+ !['browser-session', 'instance'].includes(collection.scope))
40
+ throw new Error('Memory state requires a browser session or instance scope');
41
+ if (collection.maxEntries !== undefined &&
42
+ (!Number.isSafeInteger(collection.maxEntries) ||
43
+ collection.maxEntries < 1 ||
44
+ collection.maxEntries > 4096))
45
+ throw new Error('State maxEntries must be between 1 and 4096');
46
+ if (collection.maxBytes !== undefined &&
47
+ (!Number.isSafeInteger(collection.maxBytes) ||
48
+ collection.maxBytes < 1 ||
49
+ collection.maxBytes > (collection.storage === 'memory' ? 128 : 16) * 1024 * 1024))
50
+ throw new Error('State maxBytes exceeds its storage limit');
51
+ if (collection.ttlMs !== undefined &&
52
+ (!Number.isSafeInteger(collection.ttlMs) ||
53
+ collection.ttlMs < 1 ||
54
+ collection.ttlMs > 7 * 86400000))
55
+ throw new Error('State TTL must be between 1 ms and 7 days');
56
+ if (collection.storage === 'persistent' && collection.ttlMs !== undefined)
57
+ throw new Error('Persistent state does not accept TTL');
58
+ compilePluginStateSchema(collection.schema);
59
+ }
60
+ return declaration;
61
+ }
62
+ export function pluginStateSignature(collection) {
63
+ return createHash('sha256')
64
+ .update(JSON.stringify(collection, (_key, value) => value && typeof value === 'object' && !Array.isArray(value)
65
+ ? Object.fromEntries(Object.entries(value).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)))
66
+ : value))
67
+ .digest('hex');
68
+ }
@@ -0,0 +1,4 @@
1
+ export declare const CLIENT_VERSION: string;
2
+ export declare const PLUGIN_TASK_HOST_PATH: string;
3
+ export declare const PLUGIN_WORKER_PATH: string;
4
+ export declare const PLUGIN_MODEL_SESSION_WORKER_PATH: string;
@@ -0,0 +1,8 @@
1
+ import { createRequire } from 'node:module';
2
+ import { fileURLToPath } from 'node:url';
3
+ // Package metadata and process entries share this anchor in compiled and bundled hosts.
4
+ const require = createRequire(import.meta.url);
5
+ export const CLIENT_VERSION = require('../package.json').version;
6
+ export const PLUGIN_TASK_HOST_PATH = fileURLToPath(new URL('./plugin-task-host.js', import.meta.url));
7
+ export const PLUGIN_WORKER_PATH = fileURLToPath(new URL('./plugin-worker.js', import.meta.url));
8
+ export const PLUGIN_MODEL_SESSION_WORKER_PATH = fileURLToPath(new URL('./pluginModelSessionWorker.js', import.meta.url));
package/dist/socket.d.ts CHANGED
@@ -7,7 +7,8 @@
7
7
  * 当 Socket 不存在,或 legacy Socket 是无人监听的残留文件时,
8
8
  * 自动尝试拉起 native host,稍后重试连接。
9
9
  */
10
- export declare const CLIENT_VERSION: string;
10
+ import { CLIENT_VERSION } from './runtimePaths.js';
11
+ export { CLIENT_VERSION };
11
12
  /**
12
13
  * Legacy well-known socket. New native-hosts publish per-browser sockets in the
13
14
  * registry and we address those; this path is a fallback for pre-registry hosts
@@ -46,4 +47,3 @@ export declare function resolveLocalActionTarget(action: string, data: Record<st
46
47
  * legacy well-known socket,并在 socket 不存在或已残留时自动拉起 native host。
47
48
  */
48
49
  export declare function invoke(action: string, data?: Record<string, any>, options?: InvokeOptions): Promise<any>;
49
- export {};
package/dist/socket.js CHANGED
@@ -11,16 +11,16 @@ import net from 'node:net';
11
11
  import fs from 'node:fs';
12
12
  import os from 'node:os';
13
13
  import path from 'node:path';
14
- import { createRequire } from 'node:module';
14
+ import { CLIENT_VERSION } from './runtimePaths.js';
15
15
  import { StringDecoder } from 'node:string_decoder';
16
16
  import { ensureProviderControlHost, listInstalledProviders } from '@mearl/provider/host';
17
+ import { MEARL_PROVIDER_PROTOCOL_VERSION } from '@mearl/provider';
17
18
  import { readLiveProvider } from '@mearl/provider/runtime';
18
19
  import { resolveFileRefs } from './fileRefs.js';
19
20
  import { listLiveBrowsers, findBrowser, describeBrowser, } from './browserRegistry.js';
20
21
  import { resolveActionTimeoutSec } from './actionTimeouts.js';
21
- import { isBrowserCapabilityAction } from './generated-browser-action-protocol.js';
22
- const require = createRequire(import.meta.url);
23
- export const CLIENT_VERSION = require('../package.json').version;
22
+ import { isBrowserCapabilityAction } from './generated/browser-action-protocol.js';
23
+ export { CLIENT_VERSION };
24
24
  const MAX_SOCKET_BUFFER_SIZE = 64 * 1024 * 1024;
25
25
  /**
26
26
  * Legacy well-known socket. New native-hosts publish per-browser sockets in the
@@ -581,6 +581,7 @@ async function invokeOnce(action, data, timeoutSec, transport, socketPath, contr
581
581
  socketPath = host.socketPath;
582
582
  }
583
583
  const id = `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
584
+ const deadline = Date.now() + Math.ceil(timeoutSec * 1000);
584
585
  return new Promise((resolve, reject) => {
585
586
  if (process.platform !== 'win32' && fs.existsSync(socketPath) && process.getuid) {
586
587
  const ownerUid = fs.statSync(socketPath).uid;
@@ -616,14 +617,17 @@ async function invokeOnce(action, data, timeoutSec, transport, socketPath, contr
616
617
  action,
617
618
  data,
618
619
  version: CLIENT_VERSION,
620
+ protocolVersion: MEARL_PROVIDER_PROTOCOL_VERSION,
619
621
  controlSource: toControlSourceWireValue(controlSource),
620
622
  };
621
623
  if (data[SESSION_OPTION])
622
624
  message.controlSessionId = data[SESSION_OPTION];
623
625
  if (transport)
624
626
  message.transport = transport;
625
- if (providerTarget)
627
+ if (providerTarget) {
626
628
  message.providerTarget = providerTarget;
629
+ message.deadline = deadline;
630
+ }
627
631
  socket.write(JSON.stringify(message) + '\n');
628
632
  });
629
633
  socket.on('data', chunk => {
@@ -681,7 +685,7 @@ function formatConnectError(error, socketPath) {
681
685
  '3. 确认 Chrome 浏览器正在运行,如仍无法连接可在 chrome://extensions 重新加载插件',
682
686
  '',
683
687
  '【方式二:CDP 远程调试模式(Chrome 145+,无需插件)】',
684
- ' 访问 chrome://inspect/#remote-debugging,勾选 "Discover network targets"',
688
+ ' 访问 chrome://inspect/#remote-debugging,勾选 "Allow remote debugging for this browser instance"',
685
689
  ' Chrome 会弹出授权提示,点击允许,native host 自动读取 DevToolsActivePort 文件连接',
686
690
  ' 确认有普通网页已打开(非 chrome:// 页面)',
687
691
  ].join('\n');
package/dist/unified.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { BrowserArtifact } from './generated-browser-action-protocol.js';
1
+ import type { BrowserArtifact } from './generated/browser-action-protocol.js';
2
2
  import { type CloudConnectorListResult } from '@mearl/cloud-types';
3
- import type { PluginInvocation, PluginInvokeOptions } from './plugin.js';
3
+ import type { PluginInvocation, PluginInvokeOptions } from './plugins/sdk/plugin.js';
4
4
  export interface UnifiedInvokeOptions {
5
5
  /** Use new on the first tab_open/page_snapshot, then reuse the returned id. */
6
6
  session?: string;
@@ -51,6 +51,7 @@ export declare class UnifiedClient {
51
51
  outputPath: string;
52
52
  bytes: number;
53
53
  }>;
54
+ pinBrowserTarget(options?: UnifiedInvokeOptions): Promise<UnifiedInvokeOptions>;
54
55
  private pinPageEvalTarget;
55
56
  private invokeRouted;
56
57
  private invokeExplicitLocal;
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 './generated-browser-action-protocol.js';
6
+ import { validateRequiredActionTabId, } from './generated/browser-action-protocol.js';
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;
@@ -124,12 +124,72 @@ export class UnifiedClient {
124
124
  this.ws = null;
125
125
  }
126
126
  async invokePlugin(name, invocation, options = {}) {
127
- const { invokeRegisteredPlugin } = await import('./plugin.js');
127
+ const { invokeRegisteredPlugin } = await import('./plugins/sdk/plugin.js');
128
128
  return invokeRegisteredPlugin(name, invocation, {
129
129
  invoke: (action, payload, invokeOptions) => this.invoke(action, payload, invokeOptions),
130
+ runModel: async (plugin, request, execution) => {
131
+ const { PluginModelClient } = await import('./plugins/clients/pluginModels.js');
132
+ const models = new PluginModelClient({ source: options.source ?? 'sdk' });
133
+ try {
134
+ return await models.run(plugin, request, {
135
+ signal: execution.signal,
136
+ onProgress: progress => {
137
+ if (progress !== undefined)
138
+ execution.onProgress?.(progress);
139
+ },
140
+ });
141
+ }
142
+ finally {
143
+ models.disconnect();
144
+ }
145
+ },
130
146
  }, options);
131
147
  }
132
148
  async invoke(action, data = {}, options = {}) {
149
+ if (action === 'plugin_state') {
150
+ const { PluginStateClient } = await import('./plugins/clients/pluginState.js');
151
+ const state = new PluginStateClient();
152
+ try {
153
+ return await state.call(String(data.plugin ?? ''), data.collection, data.operation, data, options);
154
+ }
155
+ finally {
156
+ state.disconnect();
157
+ }
158
+ }
159
+ if (action === 'plugin_model') {
160
+ const { PluginModelClient } = await import('./plugins/clients/pluginModels.js');
161
+ const models = new PluginModelClient();
162
+ try {
163
+ if (data.operation === 'capabilities')
164
+ return await models.capabilities();
165
+ if (data.operation === 'list')
166
+ return await models.list(data.provider);
167
+ if (data.operation === 'start')
168
+ return await models.start(String(data.plugin ?? ''), data.request, data.taskOptions ?? {});
169
+ if (data.operation === 'run')
170
+ return await models.run(String(data.plugin ?? ''), data.request, data.taskOptions ?? {});
171
+ throw new Error('Invalid model operation');
172
+ }
173
+ finally {
174
+ models.disconnect();
175
+ }
176
+ }
177
+ if (action === 'plugin_page') {
178
+ const { PluginHostConnection } = await import('./plugins/clients/pluginHostConnection.js');
179
+ const { PluginPageClient } = await import('./plugins/clients/pluginPages.js');
180
+ const connection = new PluginHostConnection();
181
+ const pages = new PluginPageClient({}, connection);
182
+ try {
183
+ const pinned = ['attach', 'activation'].includes(data.operation)
184
+ ? await this.pinBrowserTarget(options)
185
+ : options;
186
+ return await pages.call(String(data.plugin ?? ''), data.operation, data, pinned);
187
+ }
188
+ finally {
189
+ pages.disconnect();
190
+ connection.disconnect();
191
+ }
192
+ }
133
193
  if (options.session && options.session !== 'new') {
134
194
  const session = await this.resolveControlSession(options.session, options);
135
195
  const selected = decodeBrowserTarget(options.browser);
@@ -209,6 +269,27 @@ export class UnifiedClient {
209
269
  saveArtifact(artifact, outputPath, options = {}) {
210
270
  return exportArtifact(this, artifact, outputPath, options);
211
271
  }
272
+ async pinBrowserTarget(options = {}) {
273
+ const selector = options.browser ?? this.defaultBrowser;
274
+ if (decodeBrowserTarget(selector))
275
+ return { ...options, browser: selector };
276
+ const listing = (await this.invoke('browser_list', {}, {
277
+ ...options,
278
+ ...(options.connector || !this.defaultConnector
279
+ ? {}
280
+ : { connector: this.defaultConnector }),
281
+ }));
282
+ const connected = listing.browsers.filter(browser => browser.status === 'connected');
283
+ const matches = selector
284
+ ? findBrowsers(connected, selector)
285
+ : connected.filter(browser => browser.browserId === listing.defaultBrowserId);
286
+ const target = matches.length ? matches : selector ? [] : connected;
287
+ if (target.length !== 1)
288
+ throw new Error('Plugin pages require an explicit, connected and unambiguous browser target');
289
+ const pinned = { ...options, browser: target[0].browserId };
290
+ delete pinned.connector;
291
+ return pinned;
292
+ }
212
293
  async pinPageEvalTarget(options, data) {
213
294
  const target = decodeBrowserTarget(options.browser);
214
295
  if (target && !options.connector)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mearl/client",
3
- "version": "2.20.6",
3
+ "version": "2.22.0",
4
4
  "description": "Unified Mearl SDK & CLI for local and remote browsers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -60,11 +60,15 @@
60
60
  "registry": "https://registry.npmjs.org"
61
61
  },
62
62
  "dependencies": {
63
+ "acorn": "^8.16.0",
64
+ "ajv": "^8.18.0",
63
65
  "commander": "^15.0.0",
64
66
  "fastest-levenshtein": "^1.0.16",
65
67
  "ws": "^8.18.0",
66
- "@mearl/cloud-types": "2.20.6",
67
- "@mearl/provider": "2.20.6"
68
+ "@mearl/daemon-core": "2.22.0",
69
+ "@mearl/cloud-types": "2.22.0",
70
+ "@mearl/provider": "2.22.0",
71
+ "@mearl/agent-runtime": "2.22.0"
68
72
  },
69
73
  "devDependencies": {
70
74
  "@types/node": "^24.9.1",
@@ -1,20 +0,0 @@
1
- import type { PluginOptions } from './plugin.js';
2
- export interface PythonPluginCommand {
3
- description: string;
4
- signature: string;
5
- toolboxOnly: boolean;
6
- }
7
- export interface PythonPluginDescriptor {
8
- commands: PythonPluginCommand[];
9
- description?: string;
10
- name?: string;
11
- toolbox?: unknown;
12
- appProfiles?: unknown;
13
- }
14
- export interface PythonPluginApp {
15
- readonly options: Readonly<PluginOptions>;
16
- invoke(action: string, payload?: Record<string, any>, options?: PluginOptions): Promise<any>;
17
- progress(message: string): void;
18
- }
19
- export declare function describePythonPlugin(entry: string, options: Readonly<PluginOptions>): Promise<PythonPluginDescriptor>;
20
- export declare function runPythonPluginCommand(entry: string, command: string, input: Record<string, any>, app: PythonPluginApp): Promise<unknown>;