@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.
- package/README.md +153 -3
- package/dist/actionTimeouts.js +1 -1
- package/dist/artifacts.d.ts +1 -1
- package/dist/browserRegistry.d.ts +1 -1
- package/dist/check.d.ts +1 -1
- package/dist/cli.js +14 -8
- package/dist/commands.d.ts +23 -14
- package/dist/commands.js +24 -4
- package/dist/{generated-app-profile-definition.d.ts → generated/app-profile-definition.d.ts} +2 -2
- package/dist/{generated-app-profile-runtime-types.d.ts → generated/app-profile-runtime-types.d.ts} +2 -2
- package/dist/{generated-browser-action-protocol.d.ts → generated/browser-action-protocol.d.ts} +11 -1
- package/dist/{generated-browser-action-protocol.js → generated/browser-action-protocol.js} +3 -0
- package/dist/generated/plugin-diagnostics.d.ts +110 -0
- package/dist/generated/plugin-diagnostics.js +1 -0
- package/dist/generated/plugin-execution.d.ts +27 -0
- package/dist/generated/plugin-execution.js +1 -0
- package/dist/generated/plugin-models.d.ts +64 -0
- package/dist/generated/plugin-models.js +1 -0
- package/dist/generated/plugin-pages.d.ts +76 -0
- package/dist/generated/plugin-pages.js +59 -0
- package/dist/generated/plugin-recovery.d.ts +32 -0
- package/dist/generated/plugin-recovery.js +1 -0
- package/dist/generated/plugin-state.d.ts +50 -0
- package/dist/generated/plugin-state.js +1 -0
- package/dist/generated/plugin-tasks.d.ts +74 -0
- package/dist/generated/plugin-tasks.js +7 -0
- package/dist/generated/toolbox-icon.d.ts +10 -0
- package/dist/generated/toolbox-icon.js +37 -0
- package/dist/index.d.ts +15 -4
- package/dist/index.js +7 -1
- package/dist/pageEvalArguments.d.ts +1 -1
- package/dist/pageEvalArguments.js +1 -1
- package/dist/plugin-task-host.d.ts +1 -0
- package/dist/plugin-task-host.js +14 -0
- package/dist/plugin-worker.d.ts +1 -0
- package/dist/plugin-worker.js +17 -0
- package/dist/plugin.d.ts +1 -65
- package/dist/plugin.js +1 -391
- package/dist/pluginCliOutput.d.ts +2 -0
- package/dist/pluginCliOutput.js +7 -0
- package/dist/pluginModelSessionWorker.d.ts +23 -0
- package/dist/pluginModelSessionWorker.js +60 -0
- package/dist/plugins/clients/pluginHostConnection.d.ts +35 -0
- package/dist/plugins/clients/pluginHostConnection.js +256 -0
- package/dist/plugins/clients/pluginModels.d.ts +17 -0
- package/dist/plugins/clients/pluginModels.js +47 -0
- package/dist/plugins/clients/pluginPages.d.ts +21 -0
- package/dist/plugins/clients/pluginPages.js +46 -0
- package/dist/plugins/clients/pluginState.d.ts +26 -0
- package/dist/plugins/clients/pluginState.js +28 -0
- package/dist/plugins/clients/pluginTasks.d.ts +51 -0
- package/dist/plugins/clients/pluginTasks.js +181 -0
- package/dist/{officialPluginInstaller.js → plugins/officialPluginInstaller.js} +1 -3
- package/dist/{pluginPageBundle.js → plugins/pluginPageBundle.js} +74 -17
- package/dist/{pluginRegistry.d.ts → plugins/pluginRegistry.d.ts} +3 -4
- package/dist/{pluginRegistry.js → plugins/pluginRegistry.js} +16 -15
- package/dist/plugins/pluginTaskProtocol.d.ts +47 -0
- package/dist/plugins/pluginTaskProtocol.js +1 -0
- package/dist/plugins/pluginTaskWire.d.ts +4 -0
- package/dist/plugins/pluginTaskWire.js +42 -0
- package/dist/plugins/runtime/pluginCommandWorker.d.ts +2 -0
- package/dist/plugins/runtime/pluginCommandWorker.js +174 -0
- package/dist/plugins/runtime/pluginDiagnostics.d.ts +26 -0
- package/dist/plugins/runtime/pluginDiagnostics.js +309 -0
- package/dist/plugins/runtime/pluginExecution.d.ts +21 -0
- package/dist/plugins/runtime/pluginExecution.js +43 -0
- package/dist/plugins/runtime/pluginExecutor.d.ts +29 -0
- package/dist/plugins/runtime/pluginExecutor.js +223 -0
- package/dist/plugins/runtime/pluginExecutorProtocol.d.ts +55 -0
- package/dist/plugins/runtime/pluginExecutorProtocol.js +47 -0
- package/dist/plugins/runtime/pluginExecutorSupervisor.d.ts +4 -0
- package/dist/plugins/runtime/pluginExecutorSupervisor.js +63 -0
- package/dist/plugins/runtime/pluginHost.d.ts +20 -0
- package/dist/plugins/runtime/pluginHost.js +53 -0
- package/dist/plugins/runtime/pluginHostFiles.d.ts +1 -0
- package/dist/plugins/runtime/pluginHostFiles.js +21 -0
- package/dist/plugins/runtime/pluginHostPaths.d.ts +11 -0
- package/dist/plugins/runtime/pluginHostPaths.js +22 -0
- package/dist/plugins/runtime/pluginHostServer.d.ts +7 -0
- package/dist/plugins/runtime/pluginHostServer.js +252 -0
- package/dist/plugins/runtime/pluginModelCatalog.d.ts +14 -0
- package/dist/plugins/runtime/pluginModelCatalog.js +51 -0
- package/dist/plugins/runtime/pluginModelSessions.d.ts +16 -0
- package/dist/plugins/runtime/pluginModelSessions.js +234 -0
- package/dist/plugins/runtime/pluginPageManager.d.ts +57 -0
- package/dist/plugins/runtime/pluginPageManager.js +877 -0
- package/dist/plugins/runtime/pluginProcessTree.d.ts +2 -0
- package/dist/plugins/runtime/pluginProcessTree.js +31 -0
- package/dist/plugins/runtime/pluginRevision.d.ts +14 -0
- package/dist/plugins/runtime/pluginRevision.js +227 -0
- package/dist/plugins/runtime/pluginRuntimeCleanup.d.ts +2 -0
- package/dist/plugins/runtime/pluginRuntimeCleanup.js +55 -0
- package/dist/plugins/runtime/pluginStateManager.d.ts +24 -0
- package/dist/plugins/runtime/pluginStateManager.js +132 -0
- package/dist/plugins/runtime/pluginStateStore.d.ts +57 -0
- package/dist/plugins/runtime/pluginStateStore.js +396 -0
- package/dist/plugins/runtime/pluginTaskManager.d.ts +92 -0
- package/dist/plugins/runtime/pluginTaskManager.js +1080 -0
- package/dist/plugins/runtime/pluginTaskStore.d.ts +53 -0
- package/dist/plugins/runtime/pluginTaskStore.js +319 -0
- package/dist/plugins/runtime/pythonPlugin.d.ts +31 -0
- package/dist/{pythonPlugin.js → plugins/runtime/pythonPlugin.js} +236 -31
- package/dist/plugins/sdk/plugin.d.ts +118 -0
- package/dist/plugins/sdk/plugin.js +672 -0
- package/dist/{pluginAppProfiles.d.ts → plugins/sdk/pluginAppProfiles.d.ts} +1 -1
- package/dist/{pluginAppProfiles.js → plugins/sdk/pluginAppProfiles.js} +2 -2
- package/dist/plugins/sdk/pluginModelDefinition.d.ts +6 -0
- package/dist/plugins/sdk/pluginModelDefinition.js +75 -0
- package/dist/plugins/sdk/pluginPageDefinition.d.ts +3 -0
- package/dist/plugins/sdk/pluginPageDefinition.js +62 -0
- package/dist/plugins/sdk/pluginRecoveryDefinition.d.ts +3 -0
- package/dist/plugins/sdk/pluginRecoveryDefinition.js +56 -0
- package/dist/plugins/sdk/pluginStateDefinition.d.ts +4 -0
- package/dist/plugins/sdk/pluginStateDefinition.js +68 -0
- package/dist/runtimePaths.d.ts +4 -0
- package/dist/runtimePaths.js +8 -0
- package/dist/socket.d.ts +2 -2
- package/dist/socket.js +10 -6
- package/dist/unified.d.ts +3 -2
- package/dist/unified.js +83 -2
- package/package.json +7 -3
- package/dist/pythonPlugin.d.ts +0 -20
- /package/dist/{generated-app-profile-definition.js → generated/app-profile-definition.js} +0 -0
- /package/dist/{generated-app-profile-runtime-types.js → generated/app-profile-runtime-types.js} +0 -0
- /package/dist/{officialPluginCatalog.d.ts → plugins/officialPluginCatalog.d.ts} +0 -0
- /package/dist/{officialPluginCatalog.js → plugins/officialPluginCatalog.js} +0 -0
- /package/dist/{officialPluginInstaller.d.ts → plugins/officialPluginInstaller.d.ts} +0 -0
- /package/dist/{pluginPageBundle.d.ts → plugins/pluginPageBundle.d.ts} +0 -0
- /package/dist/{pluginSource.d.ts → plugins/pluginSource.d.ts} +0 -0
- /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 '
|
|
4
|
-
import { pluginFileVersion } from '
|
|
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,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
|
-
|
|
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 {
|
|
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
|
|
22
|
-
|
|
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,勾选 "
|
|
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
|
|
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
|
|
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.
|
|
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/
|
|
67
|
-
"@mearl/
|
|
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",
|
package/dist/pythonPlugin.d.ts
DELETED
|
@@ -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>;
|
|
File without changes
|
/package/dist/{generated-app-profile-runtime-types.js → generated/app-profile-runtime-types.js}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|