@mearl/client 2.21.0 → 3.0.0-beta.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/LICENSE +201 -0
- package/README.md +57 -27
- package/dist/actionTimeouts.js +1 -1
- package/dist/artifacts.d.ts +1 -1
- package/dist/autoUpdate.js +1 -2
- package/dist/browserRegistry.d.ts +1 -1
- package/dist/check.d.ts +1 -1
- package/dist/check.js +4 -3
- package/dist/cli.js +4 -3
- package/dist/commands.d.ts +20 -20
- package/dist/commands.js +17 -15
- package/dist/index.d.ts +12 -15
- package/dist/index.js +7 -7
- package/dist/local.d.ts +3 -1
- package/dist/local.js +2 -1
- package/dist/pageEvalArguments.d.ts +1 -1
- package/dist/pageEvalArguments.js +1 -1
- package/dist/plugin-task-host.js +1 -1
- package/dist/plugin-worker.js +2 -2
- package/dist/plugin.d.ts +2 -118
- package/dist/plugin.js +2 -672
- package/dist/pluginModelSessionWorker.d.ts +2 -2
- package/dist/pluginModelSessionWorker.js +4 -4
- package/dist/{pluginHostConnection.d.ts → plugins/clients/pluginHostConnection.d.ts} +2 -2
- package/dist/{pluginHostConnection.js → plugins/clients/pluginHostConnection.js} +6 -7
- package/dist/{pluginModels.d.ts → plugins/clients/pluginModels.d.ts} +3 -3
- package/dist/{pluginPages.d.ts → plugins/clients/pluginPages.d.ts} +3 -3
- package/dist/{pluginPages.js → plugins/clients/pluginPages.js} +1 -1
- package/dist/{pluginState.d.ts → plugins/clients/pluginState.d.ts} +3 -3
- package/dist/{pluginTasks.d.ts → plugins/clients/pluginTasks.d.ts} +4 -4
- package/dist/{pluginTasks.js → plugins/clients/pluginTasks.js} +4 -4
- package/dist/plugins/officialPluginCatalog.d.ts +25 -0
- package/dist/plugins/officialPluginCatalog.js +125 -0
- package/dist/{officialPluginInstaller.js → plugins/officialPluginInstaller.js} +7 -4
- package/dist/{pluginRegistry.d.ts → plugins/pluginRegistry.d.ts} +1 -1
- package/dist/{pluginRegistry.js → plugins/pluginRegistry.js} +3 -3
- package/dist/{pluginTaskProtocol.d.ts → plugins/pluginTaskProtocol.d.ts} +2 -2
- package/dist/{pluginCommandWorker.js → plugins/runtime/pluginCommandWorker.js} +3 -3
- package/dist/{pluginDiagnostics.d.ts → plugins/runtime/pluginDiagnostics.d.ts} +4 -4
- package/dist/{pluginDiagnostics.js → plugins/runtime/pluginDiagnostics.js} +2 -2
- package/dist/{pluginExecution.d.ts → plugins/runtime/pluginExecution.d.ts} +3 -3
- package/dist/{pluginExecution.js → plugins/runtime/pluginExecution.js} +1 -1
- package/dist/{pluginExecutor.d.ts → plugins/runtime/pluginExecutor.d.ts} +3 -3
- package/dist/{pluginExecutor.js → plugins/runtime/pluginExecutor.js} +4 -4
- package/dist/{pluginExecutorProtocol.d.ts → plugins/runtime/pluginExecutorProtocol.d.ts} +4 -4
- package/dist/{pluginHost.d.ts → plugins/runtime/pluginHost.d.ts} +3 -3
- package/dist/{pluginHostPaths.js → plugins/runtime/pluginHostPaths.js} +1 -1
- package/dist/{pluginHostServer.js → plugins/runtime/pluginHostServer.js} +3 -3
- package/dist/{pluginModelCatalog.d.ts → plugins/runtime/pluginModelCatalog.d.ts} +1 -1
- package/dist/{pluginModelSessions.js → plugins/runtime/pluginModelSessions.js} +3 -3
- package/dist/{pluginPageManager.d.ts → plugins/runtime/pluginPageManager.d.ts} +4 -4
- package/dist/{pluginPageManager.js → plugins/runtime/pluginPageManager.js} +5 -5
- package/dist/{pluginRevision.d.ts → plugins/runtime/pluginRevision.d.ts} +1 -1
- package/dist/{pluginRevision.js → plugins/runtime/pluginRevision.js} +3 -2
- package/dist/{pluginRuntimeCleanup.js → plugins/runtime/pluginRuntimeCleanup.js} +2 -2
- package/dist/{pluginStateManager.d.ts → plugins/runtime/pluginStateManager.d.ts} +2 -2
- package/dist/{pluginStateManager.js → plugins/runtime/pluginStateManager.js} +2 -2
- package/dist/{pluginStateStore.d.ts → plugins/runtime/pluginStateStore.d.ts} +1 -1
- package/dist/{pluginStateStore.js → plugins/runtime/pluginStateStore.js} +1 -1
- package/dist/{pluginTaskManager.d.ts → plugins/runtime/pluginTaskManager.d.ts} +12 -12
- package/dist/{pluginTaskManager.js → plugins/runtime/pluginTaskManager.js} +6 -6
- package/dist/{pluginTaskStore.d.ts → plugins/runtime/pluginTaskStore.d.ts} +4 -4
- package/dist/{pluginTaskStore.js → plugins/runtime/pluginTaskStore.js} +1 -1
- package/dist/{pythonPlugin.d.ts → plugins/runtime/pythonPlugin.d.ts} +2 -2
- package/dist/{pythonPlugin.js → plugins/runtime/pythonPlugin.js} +2 -2
- package/dist/plugins/sdk/plugin.d.ts +115 -0
- package/dist/plugins/sdk/plugin.js +669 -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/{pluginModelDefinition.d.ts → plugins/sdk/pluginModelDefinition.d.ts} +1 -1
- package/dist/{pluginPageDefinition.d.ts → plugins/sdk/pluginPageDefinition.d.ts} +1 -1
- package/dist/{pluginPageDefinition.js → plugins/sdk/pluginPageDefinition.js} +1 -1
- package/dist/{pluginRecoveryDefinition.d.ts → plugins/sdk/pluginRecoveryDefinition.d.ts} +1 -1
- package/dist/{pluginStateDefinition.d.ts → plugins/sdk/pluginStateDefinition.d.ts} +2 -2
- package/dist/plugins/skillManifest.d.ts +10 -0
- package/dist/plugins/skillManifest.js +56 -0
- package/dist/runtimePaths.d.ts +4 -0
- package/dist/runtimePaths.js +8 -0
- package/dist/socket.d.ts +4 -2
- package/dist/socket.js +17 -6
- package/dist/unified.d.ts +2 -2
- package/dist/unified.js +7 -7
- package/package.json +9 -8
- package/dist/generated-app-profile-definition.d.ts +0 -130
- package/dist/generated-app-profile-definition.js +0 -458
- package/dist/generated-app-profile-runtime-types.d.ts +0 -213
- package/dist/generated-app-profile-runtime-types.js +0 -1
- package/dist/generated-browser-action-protocol.d.ts +0 -1147
- package/dist/generated-browser-action-protocol.js +0 -283
- package/dist/generated-plugin-diagnostics.d.ts +0 -110
- package/dist/generated-plugin-diagnostics.js +0 -1
- package/dist/generated-plugin-execution.d.ts +0 -27
- package/dist/generated-plugin-execution.js +0 -1
- package/dist/generated-plugin-models.d.ts +0 -64
- package/dist/generated-plugin-models.js +0 -1
- package/dist/generated-plugin-pages.d.ts +0 -76
- package/dist/generated-plugin-pages.js +0 -59
- package/dist/generated-plugin-recovery.d.ts +0 -32
- package/dist/generated-plugin-recovery.js +0 -1
- package/dist/generated-plugin-state.d.ts +0 -50
- package/dist/generated-plugin-state.js +0 -1
- package/dist/generated-plugin-tasks.d.ts +0 -74
- package/dist/generated-plugin-tasks.js +0 -7
- package/dist/generated-toolbox-icon.d.ts +0 -10
- package/dist/generated-toolbox-icon.js +0 -37
- package/dist/officialPluginCatalog.d.ts +0 -28
- package/dist/officialPluginCatalog.js +0 -29
- /package/dist/{pluginModels.js → plugins/clients/pluginModels.js} +0 -0
- /package/dist/{pluginState.js → plugins/clients/pluginState.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/{pluginPageBundle.js → plugins/pluginPageBundle.js} +0 -0
- /package/dist/{pluginSource.d.ts → plugins/pluginSource.d.ts} +0 -0
- /package/dist/{pluginSource.js → plugins/pluginSource.js} +0 -0
- /package/dist/{pluginTaskProtocol.js → plugins/pluginTaskProtocol.js} +0 -0
- /package/dist/{pluginTaskWire.d.ts → plugins/pluginTaskWire.d.ts} +0 -0
- /package/dist/{pluginTaskWire.js → plugins/pluginTaskWire.js} +0 -0
- /package/dist/{pluginCommandWorker.d.ts → plugins/runtime/pluginCommandWorker.d.ts} +0 -0
- /package/dist/{pluginExecutorProtocol.js → plugins/runtime/pluginExecutorProtocol.js} +0 -0
- /package/dist/{pluginExecutorSupervisor.d.ts → plugins/runtime/pluginExecutorSupervisor.d.ts} +0 -0
- /package/dist/{pluginExecutorSupervisor.js → plugins/runtime/pluginExecutorSupervisor.js} +0 -0
- /package/dist/{pluginHost.js → plugins/runtime/pluginHost.js} +0 -0
- /package/dist/{pluginHostFiles.d.ts → plugins/runtime/pluginHostFiles.d.ts} +0 -0
- /package/dist/{pluginHostFiles.js → plugins/runtime/pluginHostFiles.js} +0 -0
- /package/dist/{pluginHostPaths.d.ts → plugins/runtime/pluginHostPaths.d.ts} +0 -0
- /package/dist/{pluginHostServer.d.ts → plugins/runtime/pluginHostServer.d.ts} +0 -0
- /package/dist/{pluginModelCatalog.js → plugins/runtime/pluginModelCatalog.js} +0 -0
- /package/dist/{pluginModelSessions.d.ts → plugins/runtime/pluginModelSessions.d.ts} +0 -0
- /package/dist/{pluginProcessTree.d.ts → plugins/runtime/pluginProcessTree.d.ts} +0 -0
- /package/dist/{pluginProcessTree.js → plugins/runtime/pluginProcessTree.js} +0 -0
- /package/dist/{pluginRuntimeCleanup.d.ts → plugins/runtime/pluginRuntimeCleanup.d.ts} +0 -0
- /package/dist/{pluginModelDefinition.js → plugins/sdk/pluginModelDefinition.js} +0 -0
- /package/dist/{pluginRecoveryDefinition.js → plugins/sdk/pluginRecoveryDefinition.js} +0 -0
- /package/dist/{pluginStateDefinition.js → plugins/sdk/pluginStateDefinition.js} +0 -0
package/dist/plugin.js
CHANGED
|
@@ -1,672 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { normalizePluginPageRuntime } from './pluginPageDefinition.js';
|
|
4
|
-
import { loadPluginAppProfiles } from './pluginAppProfiles.js';
|
|
5
|
-
export { invokePageEvalWithArgs } from './pageEvalArguments.js';
|
|
6
|
-
import { readFileSync } from 'node:fs';
|
|
7
|
-
import { dirname, extname, resolve } from 'node:path';
|
|
8
|
-
import { pathToFileURL } from 'node:url';
|
|
9
|
-
import { Command } from 'commander';
|
|
10
|
-
import { COMMAND_MAP } from './commands.js';
|
|
11
|
-
import { findOfficialPlugin } from './officialPluginCatalog.js';
|
|
12
|
-
import { ensureOfficialPluginRegistered, } from './officialPluginInstaller.js';
|
|
13
|
-
import { findPluginRegistration, pluginRegistrationNeedsRefresh, registerPlugin, pluginDirectory, normalizePluginToolboxContribution, PluginError, } from './pluginRegistry.js';
|
|
14
|
-
import { pluginFileVersion } from './pluginSource.js';
|
|
15
|
-
import { describePythonPlugin, runPythonPluginCommand } from './pythonPlugin.js';
|
|
16
|
-
import { createPluginInvocationIdentity, } from './pluginExecution.js';
|
|
17
|
-
export { executeRegisteredPlugin, } from './pluginExecutor.js';
|
|
18
|
-
export { PluginTaskClient } from './pluginTasks.js';
|
|
19
|
-
export { cleanupPluginRuntime } from './pluginRuntimeCleanup.js';
|
|
20
|
-
export { PluginPageClient } from './pluginPages.js';
|
|
21
|
-
export { assertPluginName, findPluginDirectoryEntry, findPluginEntry, findPluginRegistration, listPluginNames, pluginDirectory, registerPlugin, resolvePluginEntry, unregisterPlugin, PluginError, readPluginPageBundleChunk, } from './pluginRegistry.js';
|
|
22
|
-
export { findOfficialPlugin, listOfficialPluginNames, OFFICIAL_PLUGIN_MANAGER, OFFICIAL_PLUGINS, } from './officialPluginCatalog.js';
|
|
23
|
-
function readPluginVersion(entry) {
|
|
24
|
-
try {
|
|
25
|
-
const content = readFileSync(resolve(dirname(entry), '..', 'SKILL.md'), 'utf8');
|
|
26
|
-
const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1];
|
|
27
|
-
const versionLine = frontmatter?.split(/\r?\n/).find(line => /^version:\s*/.test(line));
|
|
28
|
-
return versionLine?.match(/^version:\s*["']?([^"'#\s]+)["']?\s*$/)?.[1];
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
return undefined;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
function isObject(value) {
|
|
35
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
36
|
-
}
|
|
37
|
-
function isPluginInvocation(value) {
|
|
38
|
-
return isObject(value) && typeof value.command === 'string' && isObject(value.input);
|
|
39
|
-
}
|
|
40
|
-
function normalizePluginProgress(progress) {
|
|
41
|
-
if (typeof progress === 'string') {
|
|
42
|
-
if (!progress)
|
|
43
|
-
throw new Error('app.progress 需要非空消息');
|
|
44
|
-
return progress;
|
|
45
|
-
}
|
|
46
|
-
if (!isObject(progress) || typeof progress.message !== 'string' || !progress.message) {
|
|
47
|
-
throw new Error('app.progress 需要非空消息');
|
|
48
|
-
}
|
|
49
|
-
if (progress.data !== undefined && !isObject(progress.data)) {
|
|
50
|
-
throw new Error('app.progress data 必须是对象');
|
|
51
|
-
}
|
|
52
|
-
let data;
|
|
53
|
-
if (progress.data) {
|
|
54
|
-
try {
|
|
55
|
-
const serialized = JSON.stringify(progress.data);
|
|
56
|
-
const parsed = serialized === undefined ? undefined : JSON.parse(serialized);
|
|
57
|
-
if (!isObject(parsed))
|
|
58
|
-
throw new Error('invalid object');
|
|
59
|
-
data = Object.freeze(parsed);
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
|
-
throw new Error('app.progress data 必须是可 JSON 序列化的对象');
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return Object.freeze({
|
|
66
|
-
message: progress.message,
|
|
67
|
-
...(data ? { data } : {}),
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
function missingPluginEntryMessage(entry, pluginName) {
|
|
71
|
-
const name = pluginName ?? '<name>';
|
|
72
|
-
return [
|
|
73
|
-
'插件入口已失效',
|
|
74
|
-
'',
|
|
75
|
-
`插件:${name}`,
|
|
76
|
-
`入口:${entry}`,
|
|
77
|
-
'',
|
|
78
|
-
`卸载命令:npx @mearl/setup plugin uninstall ${name}`,
|
|
79
|
-
].join('\n');
|
|
80
|
-
}
|
|
81
|
-
const registrationRefreshes = new Map();
|
|
82
|
-
/** 工具列表仅检查入口;页面资源只在打开对应工具时检查。 */
|
|
83
|
-
export async function refreshRegisteredPlugin(name, includePage = false) {
|
|
84
|
-
const key = `${pluginDirectory()}/${name}`;
|
|
85
|
-
const pending = registrationRefreshes.get(key);
|
|
86
|
-
if (pending) {
|
|
87
|
-
await pending;
|
|
88
|
-
return refreshRegisteredPlugin(name, includePage);
|
|
89
|
-
}
|
|
90
|
-
const registration = findPluginRegistration(name);
|
|
91
|
-
if (!registration)
|
|
92
|
-
return undefined;
|
|
93
|
-
const entryChanged = pluginRegistrationNeedsRefresh(registration);
|
|
94
|
-
if (!entryChanged && (!includePage || !pluginRegistrationNeedsRefresh(registration, true))) {
|
|
95
|
-
return registration;
|
|
96
|
-
}
|
|
97
|
-
const refresh = (async () => {
|
|
98
|
-
const entryVersion = pluginFileVersion(registration.entry);
|
|
99
|
-
const loaded = entryChanged
|
|
100
|
-
? await loadPlugin(registration.entry, {
|
|
101
|
-
options: {},
|
|
102
|
-
invoke: async () => {
|
|
103
|
-
throw new Error('注册时不能执行浏览器操作');
|
|
104
|
-
},
|
|
105
|
-
progress: () => {
|
|
106
|
-
throw new Error('注册时不能报告执行进度');
|
|
107
|
-
},
|
|
108
|
-
}, name)
|
|
109
|
-
: registration;
|
|
110
|
-
// 异步加载期间其他进程可能卸载或重新绑定同名插件。
|
|
111
|
-
const current = findPluginRegistration(name);
|
|
112
|
-
if (JSON.stringify(current) !== JSON.stringify(registration))
|
|
113
|
-
return current;
|
|
114
|
-
if (pluginFileVersion(registration.entry) !== entryVersion) {
|
|
115
|
-
throw new PluginError('PLUGIN_SOURCE_CHANGED', `插件更新进行中,请重新打开工具: ${name}`);
|
|
116
|
-
}
|
|
117
|
-
registerPlugin(name, registration.entry, loaded.description, {
|
|
118
|
-
hasPublicCommands: 'commandHelp' in loaded ? Boolean(loaded.commandHelp(name)) : loaded.hasPublicCommands,
|
|
119
|
-
toolbox: loaded.toolbox,
|
|
120
|
-
appProfiles: loaded.appProfiles,
|
|
121
|
-
profileSources: 'profileSources' in loaded ? loaded.profileSources : loaded.source?.profiles,
|
|
122
|
-
...(registration.managedBy ? { managedBy: registration.managedBy } : {}),
|
|
123
|
-
});
|
|
124
|
-
return findPluginRegistration(name);
|
|
125
|
-
})();
|
|
126
|
-
registrationRefreshes.set(key, refresh);
|
|
127
|
-
try {
|
|
128
|
-
return await refresh;
|
|
129
|
-
}
|
|
130
|
-
finally {
|
|
131
|
-
registrationRefreshes.delete(key);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
/** 注册和执行复用同一宿主能力边界;仅 handler 执行期间开放浏览器调用。 */
|
|
135
|
-
export async function loadPlugin(entry, context, pluginName) {
|
|
136
|
-
let entryVersion;
|
|
137
|
-
try {
|
|
138
|
-
entryVersion = pluginFileVersion(entry);
|
|
139
|
-
}
|
|
140
|
-
catch {
|
|
141
|
-
throw new PluginError('PLUGIN_ENTRY_MISSING', missingPluginEntryMessage(entry, pluginName));
|
|
142
|
-
}
|
|
143
|
-
const commands = new Map();
|
|
144
|
-
const recoveries = new Map();
|
|
145
|
-
const migrations = new Map();
|
|
146
|
-
let executing = false;
|
|
147
|
-
let checkpointDeclaration;
|
|
148
|
-
let invocationGeneration = 0;
|
|
149
|
-
let registering = true;
|
|
150
|
-
let name;
|
|
151
|
-
let description;
|
|
152
|
-
let toolbox;
|
|
153
|
-
let pageRuntime;
|
|
154
|
-
let stateStorage;
|
|
155
|
-
let taskRecovery;
|
|
156
|
-
let appProfiles;
|
|
157
|
-
let profileSources;
|
|
158
|
-
const registerCommand = (signature, handler, description = '', examples = [], toolboxOnly = false, runtimeOnly = false) => {
|
|
159
|
-
if (!registering)
|
|
160
|
-
throw new Error('命令只能在入口注册阶段声明');
|
|
161
|
-
const [name, ...args] = signature.trim().split(/\s+/);
|
|
162
|
-
if (!/^[a-z][a-z0-9-]*$/.test(name) ||
|
|
163
|
-
commands.has(name) ||
|
|
164
|
-
typeof handler !== 'function' ||
|
|
165
|
-
!Array.isArray(examples) ||
|
|
166
|
-
examples.some((example) => typeof example !== 'string' || !example.trim())) {
|
|
167
|
-
throw new Error(`无效或重复的命令: ${signature}`);
|
|
168
|
-
}
|
|
169
|
-
const command = new Command(name).description(description);
|
|
170
|
-
for (const argument of args) {
|
|
171
|
-
if (!/^(?:<[a-zA-Z][\w-]*(?:\.\.\.)?>|\[[a-zA-Z][\w-]*(?:\.\.\.)?\])$/.test(argument)) {
|
|
172
|
-
throw new Error(`无效的位置参数: ${argument}`);
|
|
173
|
-
}
|
|
174
|
-
command.argument(argument);
|
|
175
|
-
}
|
|
176
|
-
const names = command.registeredArguments.map(argument => argument.name());
|
|
177
|
-
if (new Set(names).size !== names.length)
|
|
178
|
-
throw new Error(`参数名称重复: ${signature}`);
|
|
179
|
-
commands.set(name, {
|
|
180
|
-
command,
|
|
181
|
-
signature,
|
|
182
|
-
handler,
|
|
183
|
-
examples: [...examples],
|
|
184
|
-
toolboxOnly,
|
|
185
|
-
runtimeOnly,
|
|
186
|
-
});
|
|
187
|
-
};
|
|
188
|
-
const app = Object.freeze({
|
|
189
|
-
options: Object.freeze({ ...context.options }),
|
|
190
|
-
command: (signature, handler, description = '', examples = []) => registerCommand(signature, handler, description, examples),
|
|
191
|
-
runtimeCommand: (signature, handler, description = '', examples = []) => registerCommand(signature, handler, description, examples, false, true),
|
|
192
|
-
recoveryCommand(command, handler) {
|
|
193
|
-
if (!registering ||
|
|
194
|
-
!/^[a-z][a-z0-9-]*$/.test(command) ||
|
|
195
|
-
typeof handler !== 'function' ||
|
|
196
|
-
recoveries.has(command))
|
|
197
|
-
throw new Error('Invalid or duplicate recovery command');
|
|
198
|
-
recoveries.set(command, handler);
|
|
199
|
-
},
|
|
200
|
-
checkpointMigration(command, handler) {
|
|
201
|
-
if (!registering ||
|
|
202
|
-
!/^[a-z][a-z0-9-]*$/.test(command) ||
|
|
203
|
-
typeof handler !== 'function' ||
|
|
204
|
-
migrations.has(command))
|
|
205
|
-
throw new Error('Invalid or duplicate checkpoint migration');
|
|
206
|
-
migrations.set(command, (input, commandContext) => handler(input, commandContext));
|
|
207
|
-
},
|
|
208
|
-
checkpoint(data, expectedSequence) {
|
|
209
|
-
if (!executing || !context.invocation?.taskId)
|
|
210
|
-
throw new PluginError('PLUGIN_TASK_REQUIRED', 'Checkpoints require a managed task');
|
|
211
|
-
context.signal?.throwIfAborted();
|
|
212
|
-
return context.invoke('plugin_task_checkpoint', {
|
|
213
|
-
plugin: pluginName ?? name,
|
|
214
|
-
data,
|
|
215
|
-
expectedSequence,
|
|
216
|
-
expectedDeclaration: checkpointDeclaration,
|
|
217
|
-
});
|
|
218
|
-
},
|
|
219
|
-
state(collection, operation, input = {}) {
|
|
220
|
-
if (!executing)
|
|
221
|
-
throw new Error('app.state is only available during command execution');
|
|
222
|
-
context.signal?.throwIfAborted();
|
|
223
|
-
if (!stateStorage || !Object.hasOwn(stateStorage.collections, collection))
|
|
224
|
-
throw new PluginError('PLUGIN_STATE_NOT_DECLARED', 'Plugin state collection is not declared');
|
|
225
|
-
return context.invoke('plugin_state', {
|
|
226
|
-
...input,
|
|
227
|
-
plugin: pluginName ?? name,
|
|
228
|
-
collection,
|
|
229
|
-
operation,
|
|
230
|
-
expectedDeclaration: pluginStateSignature(stateStorage.collections[collection]),
|
|
231
|
-
});
|
|
232
|
-
},
|
|
233
|
-
model(request, options = {}) {
|
|
234
|
-
if (!executing)
|
|
235
|
-
throw new Error('app.model is only available during command execution');
|
|
236
|
-
context.signal?.throwIfAborted();
|
|
237
|
-
const generation = invocationGeneration;
|
|
238
|
-
const observing = () => executing && generation === invocationGeneration && !context.signal?.aborted;
|
|
239
|
-
if (context.runModel)
|
|
240
|
-
return context.runModel(pluginName ?? name, request, {
|
|
241
|
-
signal: context.signal,
|
|
242
|
-
onProgress: progress => {
|
|
243
|
-
if (!observing())
|
|
244
|
-
return;
|
|
245
|
-
context.progress(progress);
|
|
246
|
-
options.onProgress?.(progress);
|
|
247
|
-
},
|
|
248
|
-
});
|
|
249
|
-
const payload = {
|
|
250
|
-
plugin: pluginName ?? name,
|
|
251
|
-
operation: 'run',
|
|
252
|
-
request,
|
|
253
|
-
};
|
|
254
|
-
return (options.onProgress
|
|
255
|
-
? context.invoke('plugin_model', payload, {}, progress => {
|
|
256
|
-
if (observing())
|
|
257
|
-
options.onProgress?.(progress);
|
|
258
|
-
})
|
|
259
|
-
: context.invoke('plugin_model', payload));
|
|
260
|
-
},
|
|
261
|
-
page(operation, input = {}) {
|
|
262
|
-
if (!executing)
|
|
263
|
-
throw new Error('app.page is only available during command execution');
|
|
264
|
-
context.signal?.throwIfAborted();
|
|
265
|
-
return context.invoke('plugin_page', { ...input, plugin: pluginName ?? name, operation });
|
|
266
|
-
},
|
|
267
|
-
toolboxCommand: (signature, handler, description = '', examples = []) => registerCommand(signature, handler, description, examples, true),
|
|
268
|
-
invoke(action, payload = {}, options = {}) {
|
|
269
|
-
if (!executing)
|
|
270
|
-
throw new Error('app.invoke 只能在命令执行期间调用');
|
|
271
|
-
context.signal?.throwIfAborted();
|
|
272
|
-
return context.invoke(action, payload, options);
|
|
273
|
-
},
|
|
274
|
-
progress(progress) {
|
|
275
|
-
if (!executing)
|
|
276
|
-
throw new Error('app.progress 只能在命令执行期间调用');
|
|
277
|
-
context.progress(normalizePluginProgress(progress));
|
|
278
|
-
},
|
|
279
|
-
});
|
|
280
|
-
try {
|
|
281
|
-
if (extname(entry).toLowerCase() === '.py') {
|
|
282
|
-
const descriptor = await describePythonPlugin(entry, app.options);
|
|
283
|
-
name = descriptor.name;
|
|
284
|
-
description = descriptor.description;
|
|
285
|
-
toolbox = normalizePluginToolboxContribution(descriptor.toolbox);
|
|
286
|
-
pageRuntime = normalizePluginPageRuntime(descriptor.pageRuntime);
|
|
287
|
-
stateStorage = normalizePluginState(descriptor.stateStorage);
|
|
288
|
-
taskRecovery = normalizePluginTaskRecovery(descriptor.taskRecovery);
|
|
289
|
-
({ appProfiles, profileSources } = loadPluginAppProfiles(entry, descriptor.appProfiles));
|
|
290
|
-
for (const command of descriptor.commands) {
|
|
291
|
-
const commandName = command.signature.trim().split(/\s+/)[0];
|
|
292
|
-
if (command.migrationOnly) {
|
|
293
|
-
app.checkpointMigration(commandName, (input, commandContext) => runPythonPluginCommand(entry, commandName, input, app, commandContext, 'migration', descriptor));
|
|
294
|
-
continue;
|
|
295
|
-
}
|
|
296
|
-
if (command.recoveryOnly) {
|
|
297
|
-
app.recoveryCommand(commandName, (input, commandContext) => runPythonPluginCommand(entry, commandName, input, app, commandContext, true, descriptor));
|
|
298
|
-
continue;
|
|
299
|
-
}
|
|
300
|
-
const register = command.runtimeOnly
|
|
301
|
-
? app.runtimeCommand
|
|
302
|
-
: command.toolboxOnly
|
|
303
|
-
? app.toolboxCommand
|
|
304
|
-
: app.command;
|
|
305
|
-
register(command.signature, (input, commandContext) => runPythonPluginCommand(entry, commandName, input, app, commandContext, false, descriptor, command.runtimeOnly), command.description);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
const moduleUrl = pathToFileURL(entry);
|
|
310
|
-
moduleUrl.searchParams.set('mearl-version', entryVersion);
|
|
311
|
-
const module = await import(moduleUrl.href);
|
|
312
|
-
if (module.name !== undefined && typeof module.name !== 'string')
|
|
313
|
-
throw new Error('入口的 name 导出必须为字符串');
|
|
314
|
-
name = module.name;
|
|
315
|
-
if (module.description !== undefined && typeof module.description !== 'string')
|
|
316
|
-
throw new Error('入口的 description 导出必须为字符串');
|
|
317
|
-
description = module.description;
|
|
318
|
-
toolbox = normalizePluginToolboxContribution(module.toolbox);
|
|
319
|
-
pageRuntime = normalizePluginPageRuntime(module.pageRuntime);
|
|
320
|
-
stateStorage = normalizePluginState(module.stateStorage);
|
|
321
|
-
taskRecovery = normalizePluginTaskRecovery(module.taskRecovery);
|
|
322
|
-
({ appProfiles, profileSources } = loadPluginAppProfiles(entry, module.appProfiles));
|
|
323
|
-
if (module.default !== undefined && typeof module.default !== 'function')
|
|
324
|
-
throw new Error('入口必须默认导出 register(app) 函数');
|
|
325
|
-
if (typeof module.default === 'function')
|
|
326
|
-
await module.default(app);
|
|
327
|
-
else if (!appProfiles)
|
|
328
|
-
throw new Error('入口必须默认导出 register(app) 函数');
|
|
329
|
-
}
|
|
330
|
-
if (commands.size === 0 && !appProfiles)
|
|
331
|
-
throw new Error('入口未注册任何命令');
|
|
332
|
-
for (const command of Object.keys(taskRecovery?.commands ?? {})) {
|
|
333
|
-
if (!commands.has(command) ||
|
|
334
|
-
commands.get(command)?.runtimeOnly ||
|
|
335
|
-
commands.get(command)?.toolboxOnly ||
|
|
336
|
-
!recoveries.has(command))
|
|
337
|
-
throw new Error(`Recovery requires a command and recoveryCommand: ${command}`);
|
|
338
|
-
}
|
|
339
|
-
for (const command of recoveries.keys())
|
|
340
|
-
if (!Object.hasOwn(taskRecovery?.commands ?? {}, command))
|
|
341
|
-
throw new Error(`Undeclared recovery command: ${command}`);
|
|
342
|
-
for (const [command, definition] of Object.entries(taskRecovery?.commands ?? {})) {
|
|
343
|
-
if (Boolean(definition.migrateFrom?.length) !== migrations.has(command))
|
|
344
|
-
throw new Error(`Checkpoint migrations require a declaration and checkpointMigration: ${command}`);
|
|
345
|
-
}
|
|
346
|
-
for (const command of migrations.keys())
|
|
347
|
-
if (!Object.hasOwn(taskRecovery?.commands ?? {}, command))
|
|
348
|
-
throw new Error(`Undeclared checkpoint migration: ${command}`);
|
|
349
|
-
const runtimeCommands = new Set(Object.values(pageRuntime?.events ?? {}).map(event => event.command));
|
|
350
|
-
if (pageRuntime?.initialize)
|
|
351
|
-
runtimeCommands.add(pageRuntime.initialize);
|
|
352
|
-
for (const [name, command] of commands) {
|
|
353
|
-
if (command.runtimeOnly && !runtimeCommands.has(name))
|
|
354
|
-
throw new Error(`runtimeCommand is not declared in pageRuntime.events: ${name}`);
|
|
355
|
-
}
|
|
356
|
-
for (const name of runtimeCommands)
|
|
357
|
-
if (!commands.has(name) || commands.get(name)?.toolboxOnly)
|
|
358
|
-
throw new Error(`Invalid page runtime command: ${name}`);
|
|
359
|
-
const toolboxCommands = toolbox?.page.commands ?? [];
|
|
360
|
-
for (const [name, command] of commands) {
|
|
361
|
-
if (command.toolboxOnly && !toolboxCommands.includes(name)) {
|
|
362
|
-
throw new Error(`toolboxCommand 未声明在 toolbox.page.commands 中: ${name}`);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
for (const command of toolboxCommands) {
|
|
366
|
-
if (!commands.has(command))
|
|
367
|
-
throw new Error(`toolbox 命令未注册: ${command}`);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
catch (error) {
|
|
371
|
-
throw new PluginError('PLUGIN_LOAD_FAILED', `${entry}: ${error instanceof Error ? error.message : String(error)}`);
|
|
372
|
-
}
|
|
373
|
-
finally {
|
|
374
|
-
registering = false;
|
|
375
|
-
}
|
|
376
|
-
const find = (name, toolboxPage = false) => {
|
|
377
|
-
const command = commands.get(name);
|
|
378
|
-
if (!command)
|
|
379
|
-
throw new PluginError('PLUGIN_COMMAND_NOT_FOUND', `未知子命令: ${name}`);
|
|
380
|
-
if (toolboxPage === 'runtime') {
|
|
381
|
-
if (!(pageRuntime?.initialize === name ||
|
|
382
|
-
Object.values(pageRuntime?.events ?? {}).some(event => event.command === name)) ||
|
|
383
|
-
command.toolboxOnly)
|
|
384
|
-
throw new PluginError('PLUGIN_COMMAND_NOT_ALLOWED', 'Page runtime command is not declared');
|
|
385
|
-
}
|
|
386
|
-
else if (command.runtimeOnly) {
|
|
387
|
-
throw new PluginError('PLUGIN_COMMAND_RUNTIME_ONLY', `命令仅供页面运行时调用: ${name}`);
|
|
388
|
-
}
|
|
389
|
-
else if (toolboxPage) {
|
|
390
|
-
if (!toolbox?.page.commands.includes(name)) {
|
|
391
|
-
throw new PluginError('PLUGIN_COMMAND_NOT_ALLOWED', `工具页面不允许调用命令: ${name}`);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
else if (command.toolboxOnly) {
|
|
395
|
-
throw new PluginError('PLUGIN_COMMAND_TOOLBOX_ONLY', `命令仅供工具页面调用: ${name}`);
|
|
396
|
-
}
|
|
397
|
-
return command;
|
|
398
|
-
};
|
|
399
|
-
const publicCommands = () => [...commands.values()].filter(command => !command.toolboxOnly && !command.runtimeOnly);
|
|
400
|
-
const run = async (positionals, payload, namedInput = {}, toolboxPage = false) => {
|
|
401
|
-
if (payload !== undefined && positionals.length > 0) {
|
|
402
|
-
throw new PluginError('PLUGIN_INVALID_INPUT', '位置参数调用与 --payload 调用不能混用');
|
|
403
|
-
}
|
|
404
|
-
if (payload !== undefined && Object.keys(namedInput).length > 0) {
|
|
405
|
-
throw new PluginError('PLUGIN_INVALID_INPUT', '插件业务选项与 --payload 调用不能混用');
|
|
406
|
-
}
|
|
407
|
-
if (payload !== undefined &&
|
|
408
|
-
(!isObject(payload) || typeof payload.command !== 'string' || !isObject(payload.input))) {
|
|
409
|
-
throw new PluginError('PLUGIN_INVALID_INPUT', '--payload 必须为 {"command":"...","input":{...}}');
|
|
410
|
-
}
|
|
411
|
-
const envelope = payload;
|
|
412
|
-
const commandName = envelope?.command ?? positionals[0] ?? '';
|
|
413
|
-
const hidden = toolboxPage === 'recovery' || toolboxPage === 'migration';
|
|
414
|
-
const selected = find(commandName, hidden ? false : toolboxPage);
|
|
415
|
-
const command = selected.command;
|
|
416
|
-
const handler = toolboxPage === 'recovery'
|
|
417
|
-
? recoveries.get(commandName)
|
|
418
|
-
: toolboxPage === 'migration'
|
|
419
|
-
? migrations.get(commandName)
|
|
420
|
-
: selected.handler;
|
|
421
|
-
if (!handler || (toolboxPage === 'recovery' && !context.invocation?.recovery))
|
|
422
|
-
throw new PluginError('PLUGIN_TASK_RECOVERY_UNAVAILABLE', 'Recovery handler requires a managed recovery attempt');
|
|
423
|
-
if (toolboxPage === 'recovery' &&
|
|
424
|
-
context.invocation?.recovery?.declarationHash !==
|
|
425
|
-
pluginRecoverySignature(taskRecovery.commands[commandName]))
|
|
426
|
-
throw new PluginError('PLUGIN_RECOVERY_DECLARATION_CHANGED', 'Recovery declaration differs from the admitted format');
|
|
427
|
-
const positionalInput = envelope
|
|
428
|
-
? { ...envelope.input }
|
|
429
|
-
: Object.fromEntries(command.registeredArguments
|
|
430
|
-
.map((argument, index) => [
|
|
431
|
-
argument.name(),
|
|
432
|
-
argument.variadic ? positionals.slice(index + 1) : positionals[index + 1],
|
|
433
|
-
])
|
|
434
|
-
.filter(([, value]) => value !== undefined));
|
|
435
|
-
for (const name of Object.keys(namedInput)) {
|
|
436
|
-
if (Object.hasOwn(positionalInput, name)) {
|
|
437
|
-
throw new PluginError('PLUGIN_INVALID_INPUT', `参数不能重复传入: ${name}`);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
const input = { ...positionalInput, ...namedInput };
|
|
441
|
-
if (!envelope &&
|
|
442
|
-
!command.registeredArguments.some(argument => argument.variadic) &&
|
|
443
|
-
positionals.length - 1 > command.registeredArguments.length) {
|
|
444
|
-
throw new PluginError('PLUGIN_INVALID_INPUT', '位置参数过多');
|
|
445
|
-
}
|
|
446
|
-
for (const argument of hidden ? [] : command.registeredArguments) {
|
|
447
|
-
const value = Object.hasOwn(input, argument.name()) ? input[argument.name()] : undefined;
|
|
448
|
-
if (argument.required &&
|
|
449
|
-
(value === undefined ||
|
|
450
|
-
value === null ||
|
|
451
|
-
value === '' ||
|
|
452
|
-
(Array.isArray(value) && value.length === 0))) {
|
|
453
|
-
throw new PluginError('PLUGIN_INVALID_INPUT', `缺少必填参数: ${argument.name()}`);
|
|
454
|
-
}
|
|
455
|
-
if (argument.variadic && value !== undefined && !Array.isArray(value)) {
|
|
456
|
-
throw new PluginError('PLUGIN_INVALID_INPUT', `${argument.name()} 必须为数组`);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
if (executing)
|
|
460
|
-
throw new PluginError('PLUGIN_BUSY', '同一命令实例不能并发执行');
|
|
461
|
-
const signal = context.signal ?? new AbortController().signal;
|
|
462
|
-
signal.throwIfAborted();
|
|
463
|
-
const identity = context.invocation ??
|
|
464
|
-
createPluginInvocationIdentity(pluginName ?? name ?? 'unregistered', context.source ?? 'sdk', context.parentInvocation);
|
|
465
|
-
const generation = ++invocationGeneration;
|
|
466
|
-
const assertActive = () => {
|
|
467
|
-
if (!executing || generation !== invocationGeneration)
|
|
468
|
-
throw new PluginError('PLUGIN_INVOCATION_ENDED', '命令调用已结束');
|
|
469
|
-
signal.throwIfAborted();
|
|
470
|
-
};
|
|
471
|
-
const commandContext = Object.freeze({
|
|
472
|
-
identity,
|
|
473
|
-
signal,
|
|
474
|
-
...(identity.recovery ? { recovery: structuredClone(identity.recovery) } : {}),
|
|
475
|
-
checkpoint: (data, expectedSequence) => {
|
|
476
|
-
assertActive();
|
|
477
|
-
return app.checkpoint(data, expectedSequence);
|
|
478
|
-
},
|
|
479
|
-
...(identity.page
|
|
480
|
-
? {
|
|
481
|
-
page: Object.freeze({
|
|
482
|
-
send: (message, options = {}) => {
|
|
483
|
-
assertActive();
|
|
484
|
-
return app.page('send', {
|
|
485
|
-
instanceId: identity.page.instanceId,
|
|
486
|
-
message,
|
|
487
|
-
...(options.timeoutSec !== undefined ? { timeoutSec: options.timeoutSec } : {}),
|
|
488
|
-
});
|
|
489
|
-
},
|
|
490
|
-
get: () => {
|
|
491
|
-
assertActive();
|
|
492
|
-
return app.page('get', { instanceId: identity.page.instanceId });
|
|
493
|
-
},
|
|
494
|
-
}),
|
|
495
|
-
}
|
|
496
|
-
: {}),
|
|
497
|
-
invoke: (action, payload, options) => {
|
|
498
|
-
assertActive();
|
|
499
|
-
return app.invoke(action, payload, options);
|
|
500
|
-
},
|
|
501
|
-
state: (collection, operation, input) => {
|
|
502
|
-
assertActive();
|
|
503
|
-
return app.state(collection, operation, {
|
|
504
|
-
...(identity.page ? { instanceId: identity.page.instanceId } : {}),
|
|
505
|
-
...input,
|
|
506
|
-
});
|
|
507
|
-
},
|
|
508
|
-
model: (request, options) => {
|
|
509
|
-
assertActive();
|
|
510
|
-
return app.model(request, options);
|
|
511
|
-
},
|
|
512
|
-
progress: progress => {
|
|
513
|
-
assertActive();
|
|
514
|
-
app.progress(progress);
|
|
515
|
-
},
|
|
516
|
-
});
|
|
517
|
-
checkpointDeclaration =
|
|
518
|
-
taskRecovery && Object.hasOwn(taskRecovery.commands, commandName)
|
|
519
|
-
? pluginRecoverySignature(taskRecovery.commands[commandName])
|
|
520
|
-
: undefined;
|
|
521
|
-
executing = true;
|
|
522
|
-
context.onInvocation?.(identity);
|
|
523
|
-
try {
|
|
524
|
-
return await handler(input, commandContext);
|
|
525
|
-
}
|
|
526
|
-
finally {
|
|
527
|
-
executing = false;
|
|
528
|
-
checkpointDeclaration = undefined;
|
|
529
|
-
context.onInvocation?.(undefined);
|
|
530
|
-
}
|
|
531
|
-
};
|
|
532
|
-
return {
|
|
533
|
-
name,
|
|
534
|
-
description,
|
|
535
|
-
toolbox,
|
|
536
|
-
pageRuntime,
|
|
537
|
-
stateStorage,
|
|
538
|
-
taskRecovery,
|
|
539
|
-
appProfiles,
|
|
540
|
-
profileSources,
|
|
541
|
-
version: readPluginVersion(entry),
|
|
542
|
-
commandHelp(namespace) {
|
|
543
|
-
return publicCommands()
|
|
544
|
-
.map(({ command, signature }) => ` ${namespace} ${signature} ${command.description()}`)
|
|
545
|
-
.join('\n');
|
|
546
|
-
},
|
|
547
|
-
help(namespace, name) {
|
|
548
|
-
const selected = name ? [find(name)] : publicCommands();
|
|
549
|
-
if (!selected.length) {
|
|
550
|
-
const location = toolbox
|
|
551
|
-
? `请在 Mearl ${toolbox.displayMode === 'popup' ? 'Popup' : '工具箱'} 中打开「${toolbox.title}」使用。`
|
|
552
|
-
: '此插件未提供公开 CLI 命令。';
|
|
553
|
-
return `${namespace} 未提供 CLI 命令。\n${location}`;
|
|
554
|
-
}
|
|
555
|
-
const examples = selected.flatMap(({ examples }) => examples);
|
|
556
|
-
return [
|
|
557
|
-
'用法:',
|
|
558
|
-
...selected.map(({ command, signature }) => ` ${namespace} ${signature} ${command.description()}`),
|
|
559
|
-
...(examples.length
|
|
560
|
-
? ['', '示例:', ...examples.map(example => ` ${namespace} ${example}`)]
|
|
561
|
-
: []),
|
|
562
|
-
].join('\n');
|
|
563
|
-
},
|
|
564
|
-
run(positionals, payload, namedInput = {}) {
|
|
565
|
-
return run(positionals, payload, namedInput);
|
|
566
|
-
},
|
|
567
|
-
runRuntime(invocation) {
|
|
568
|
-
return run([], invocation, {}, 'runtime');
|
|
569
|
-
},
|
|
570
|
-
runRecovery(invocation) {
|
|
571
|
-
return run([], invocation, {}, 'recovery');
|
|
572
|
-
},
|
|
573
|
-
runMigration(invocation) {
|
|
574
|
-
return run([], invocation, {}, 'migration');
|
|
575
|
-
},
|
|
576
|
-
runToolbox(invocation) {
|
|
577
|
-
return run([], invocation, {}, true);
|
|
578
|
-
},
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
async function loadRegisteredPluginWithState(name, context, options = {}, state = { stack: [] }) {
|
|
582
|
-
if (state.stack.includes(name)) {
|
|
583
|
-
throw new PluginError('PLUGIN_INVOCATION_CYCLE', `插件调用形成循环: ${[...state.stack, name].join(' -> ')}`);
|
|
584
|
-
}
|
|
585
|
-
let registration = context.resolveRegistration
|
|
586
|
-
? context.resolveRegistration(name)
|
|
587
|
-
: findPluginRegistration(name);
|
|
588
|
-
if (!registration && findOfficialPlugin(name)) {
|
|
589
|
-
await (context.ensurePlugin ?? ensureOfficialPluginRegistered)(name, options.onProgress ? message => options.onProgress?.(message) : undefined);
|
|
590
|
-
registration = findPluginRegistration(name);
|
|
591
|
-
}
|
|
592
|
-
if (!registration) {
|
|
593
|
-
throw new PluginError('PLUGIN_NOT_REGISTERED', `插件未注册: ${name}`);
|
|
594
|
-
}
|
|
595
|
-
const { onProgress, signal, source, ...invokeOptions } = options;
|
|
596
|
-
const childState = { stack: [...state.stack, name] };
|
|
597
|
-
let activeInvocation;
|
|
598
|
-
return loadPlugin(registration.entry, {
|
|
599
|
-
options: invokeOptions,
|
|
600
|
-
source: source ?? context.source,
|
|
601
|
-
parentInvocation: context.parentInvocation,
|
|
602
|
-
invocation: context.invocation,
|
|
603
|
-
signal,
|
|
604
|
-
runModel: context.runModel,
|
|
605
|
-
onInvocation: identity => {
|
|
606
|
-
activeInvocation = identity;
|
|
607
|
-
},
|
|
608
|
-
progress: onProgress ?? (() => undefined),
|
|
609
|
-
async invoke(action, payload, overrides, progress) {
|
|
610
|
-
const nestedOptions = {
|
|
611
|
-
...invokeOptions,
|
|
612
|
-
...overrides,
|
|
613
|
-
};
|
|
614
|
-
if (COMMAND_MAP.has(action) || action === 'connector_list') {
|
|
615
|
-
const result = await context.invoke(action, payload, nestedOptions);
|
|
616
|
-
if (result?.action?.success === false) {
|
|
617
|
-
throw new Error(result.action.error || `${action} failed`);
|
|
618
|
-
}
|
|
619
|
-
return result;
|
|
620
|
-
}
|
|
621
|
-
const pluginInvocation = isPluginInvocation(payload);
|
|
622
|
-
if (findPluginRegistration(action) || findOfficialPlugin(action) || pluginInvocation) {
|
|
623
|
-
if (!pluginInvocation) {
|
|
624
|
-
throw new PluginError('PLUGIN_INVALID_INPUT', `调用插件 ${action} 需要 {"command":"...","input":{...}}`);
|
|
625
|
-
}
|
|
626
|
-
if (activeInvocation?.taskId) {
|
|
627
|
-
if (!findPluginRegistration(action) && findOfficialPlugin(action))
|
|
628
|
-
await (context.ensurePlugin ?? ensureOfficialPluginRegistered)(action);
|
|
629
|
-
return context.invoke('plugin_invoke_child', { plugin: action, invocation: payload }, nestedOptions);
|
|
630
|
-
}
|
|
631
|
-
return invokeRegisteredPluginWithState(action, payload, { ...context, invocation: undefined, parentInvocation: activeInvocation }, {
|
|
632
|
-
...nestedOptions,
|
|
633
|
-
...(onProgress ? { onProgress } : {}),
|
|
634
|
-
...(signal ? { signal } : {}),
|
|
635
|
-
...(source ? { source } : {}),
|
|
636
|
-
}, childState);
|
|
637
|
-
}
|
|
638
|
-
const result = await (progress
|
|
639
|
-
? context.invoke(action, payload, nestedOptions, progress)
|
|
640
|
-
: context.invoke(action, payload, nestedOptions));
|
|
641
|
-
if (result?.action?.success === false) {
|
|
642
|
-
throw new Error(result.action.error || `${action} failed`);
|
|
643
|
-
}
|
|
644
|
-
return result;
|
|
645
|
-
},
|
|
646
|
-
}, name);
|
|
647
|
-
}
|
|
648
|
-
export function loadRegisteredPlugin(name, context, options = {}) {
|
|
649
|
-
return loadRegisteredPluginWithState(name, context, options);
|
|
650
|
-
}
|
|
651
|
-
async function invokeRegisteredPluginWithState(name, invocation, context, options = {}, state = { stack: [] }) {
|
|
652
|
-
const plugin = await loadRegisteredPluginWithState(name, context, options, state);
|
|
653
|
-
return plugin.run([], invocation);
|
|
654
|
-
}
|
|
655
|
-
export function invokeRegisteredPlugin(name, invocation, context, options = {}) {
|
|
656
|
-
return invokeRegisteredPluginWithState(name, invocation, context, options);
|
|
657
|
-
}
|
|
658
|
-
/** Entry point for the native toolbox service; nested plugin calls retain public-command access. */
|
|
659
|
-
export async function invokeRegisteredToolboxPlugin(name, invocation, context, options = {}) {
|
|
660
|
-
const plugin = await loadRegisteredPluginWithState(name, context, options);
|
|
661
|
-
return plugin.runToolbox(invocation);
|
|
662
|
-
}
|
|
663
|
-
export async function invokeRegisteredRuntimePlugin(name, invocation, context, options = {}) {
|
|
664
|
-
const plugin = await loadRegisteredPluginWithState(name, context, options);
|
|
665
|
-
return plugin.runRuntime(invocation);
|
|
666
|
-
}
|
|
667
|
-
export async function invokeRegisteredRecoveryPlugin(name, invocation, context, options = {}) {
|
|
668
|
-
const plugin = await loadRegisteredPluginWithState(name, context, options);
|
|
669
|
-
return plugin.runRecovery(invocation);
|
|
670
|
-
}
|
|
671
|
-
export { PluginStateClient } from './pluginState.js';
|
|
672
|
-
export { PluginModelClient } from './pluginModels.js';
|
|
1
|
+
export * from './plugins/sdk/plugin.js';
|
|
2
|
+
export * from './plugins/skillManifest.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { PluginModelDiagnostic, PluginModelRequest, PluginModelResult } from '
|
|
2
|
-
import type { PluginExecutionError } from '
|
|
1
|
+
import type { PluginModelDiagnostic, PluginModelRequest, PluginModelResult } from '@mearl/browser-core';
|
|
2
|
+
import type { PluginExecutionError } from '@mearl/browser-core';
|
|
3
3
|
export type ModelSessionRequest = {
|
|
4
4
|
request: PluginModelRequest;
|
|
5
5
|
};
|