@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
@@ -0,0 +1,234 @@
1
+ import { fork } from 'node:child_process';
2
+ import { createHash } from 'node:crypto';
3
+ import { PLUGIN_MODEL_SESSION_WORKER_PATH } from '../../runtimePaths.js';
4
+ import { processIdentity } from '@mearl/daemon-core';
5
+ import { pluginExecutionError } from './pluginExecutorProtocol.js';
6
+ import { stopPluginProcessTree } from './pluginProcessTree.js';
7
+ import { validatePluginModelResult } from '../sdk/pluginModelDefinition.js';
8
+ /** Opt-in conversations retain a supervised process and thread within one execution owner. */
9
+ export class PluginModelSessions {
10
+ idleMs;
11
+ limit;
12
+ workerPath;
13
+ sessions = new Map();
14
+ closing = new Set();
15
+ closed = false;
16
+ constructor(idleMs = 5 * 60_000, limit = 2, workerPath = PLUGIN_MODEL_SESSION_WORKER_PATH) {
17
+ this.idleMs = idleMs;
18
+ this.limit = limit;
19
+ this.workerPath = workerPath;
20
+ }
21
+ execute(owner, ...args) {
22
+ const [plugin, , context, options = {}] = args;
23
+ const request = options.model;
24
+ const configuration = Object.fromEntries(Object.entries(request)
25
+ .filter(([key]) => !['prompt', 'outputSchema'].includes(key))
26
+ .sort(([a], [b]) => a.localeCompare(b)));
27
+ const key = createHash('sha256')
28
+ .update(JSON.stringify([
29
+ owner,
30
+ plugin,
31
+ context.invocation?.installationId,
32
+ context.invocation?.revision,
33
+ options.cwd,
34
+ Object.entries(options.environment ?? {}).sort(([a], [b]) => a.localeCompare(b)),
35
+ configuration,
36
+ ]))
37
+ .digest('hex');
38
+ options.signal?.throwIfAborted();
39
+ if (this.closed)
40
+ return Promise.reject(new Error('Model conversations are closed'));
41
+ let session = this.sessions.get(key);
42
+ if (!session || session.closed) {
43
+ if (this.sessions.size >= this.limit) {
44
+ const idle = [...this.sessions.values()].find(value => !value.queued);
45
+ if (!idle)
46
+ return Promise.reject(new Error('Model conversation limit reached'));
47
+ void this.remove(idle).catch(error => console.error('Model conversation cleanup failed:', error));
48
+ }
49
+ const child = fork(this.workerPath, [], {
50
+ stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
51
+ serialization: 'advanced',
52
+ execArgv: [],
53
+ detached: process.platform !== 'win32',
54
+ cwd: options.cwd,
55
+ env: options.environment,
56
+ });
57
+ // The supervisor remains responsible for process-tree cleanup if its host disconnects.
58
+ const exited = new Promise(resolve => child.once('close', () => resolve()));
59
+ session = { owner, child, tail: Promise.resolve(), queued: 0, closed: false, exited };
60
+ const current = session;
61
+ child.on('error', () => {
62
+ void this.remove(current).catch(error => console.error('Model conversation cleanup failed:', error));
63
+ });
64
+ child.once('exit', () => {
65
+ void this.remove(current).catch(error => console.error('Model conversation cleanup failed:', error));
66
+ });
67
+ this.sessions.set(key, session);
68
+ }
69
+ this.sessions.delete(key);
70
+ this.sessions.set(key, session);
71
+ clearTimeout(session.timer);
72
+ const current = session;
73
+ current.queued++;
74
+ let started = false;
75
+ const operation = current.tail
76
+ .catch(() => { })
77
+ .then(async () => {
78
+ options.signal?.throwIfAborted();
79
+ if (current.closed)
80
+ throw new Error('Model conversation has ended');
81
+ started = true;
82
+ return this.run(current, args);
83
+ });
84
+ current.tail = operation.finally(() => {
85
+ current.queued--;
86
+ if (!current.queued && !current.closed) {
87
+ current.timer = setTimeout(() => {
88
+ void this.remove(current).catch(error => console.error('Model conversation cleanup failed:', error));
89
+ }, this.idleMs);
90
+ current.timer.unref();
91
+ }
92
+ });
93
+ void current.tail.catch(() => { });
94
+ if (!options.signal)
95
+ return operation;
96
+ const signal = options.signal;
97
+ return new Promise((resolve, reject) => {
98
+ const abort = () => {
99
+ if (!started)
100
+ reject(Object.assign(new Error('Model execution cancelled'), {
101
+ code: 'PLUGIN_EXECUTION_CANCELLED',
102
+ }));
103
+ };
104
+ signal.addEventListener('abort', abort, { once: true });
105
+ if (signal.aborted)
106
+ abort();
107
+ void operation
108
+ .then(resolve, reject)
109
+ .finally(() => signal.removeEventListener('abort', abort));
110
+ });
111
+ }
112
+ run(session, args) {
113
+ const options = args[3], request = options.model, child = session.child;
114
+ return new Promise((resolve, reject) => {
115
+ let finished = false, stderr = '';
116
+ let timer;
117
+ const finish = async (failure, result) => {
118
+ if (finished)
119
+ return;
120
+ finished = true;
121
+ clearTimeout(timer);
122
+ options.signal?.removeEventListener('abort', abort);
123
+ child.removeListener('message', message);
124
+ child.removeListener('error', failed);
125
+ child.removeListener('exit', exited);
126
+ child.stderr?.removeListener('data', capture);
127
+ try {
128
+ if (failure)
129
+ await this.remove(session);
130
+ options.onExecutorStopped?.();
131
+ if (failure)
132
+ reject(failure);
133
+ else
134
+ resolve(result);
135
+ }
136
+ catch (error) {
137
+ reject(error);
138
+ }
139
+ };
140
+ const failed = (error) => {
141
+ void finish(error);
142
+ };
143
+ const exited = () => failed(new Error(stderr || 'Model conversation executor exited'));
144
+ const abort = () => failed(Object.assign(new Error('Model execution cancelled'), {
145
+ code: 'PLUGIN_EXECUTION_CANCELLED',
146
+ }));
147
+ const capture = (chunk) => {
148
+ stderr = (stderr + chunk.toString()).slice(-65536);
149
+ };
150
+ const message = (event) => {
151
+ if (finished)
152
+ return;
153
+ try {
154
+ if (event.type === 'result')
155
+ void finish(undefined, validatePluginModelResult(request, event.result));
156
+ else if (event.type === 'error')
157
+ failed(pluginExecutionError(event.error));
158
+ else if (event.type === 'diagnostic')
159
+ options.onDiagnostic?.(event.metadata);
160
+ else if (event.type === 'delta')
161
+ options.onProgress?.({
162
+ message: 'model.delta',
163
+ data: { delta: event.delta, index: event.index, ...event.metadata, partial: true },
164
+ });
165
+ }
166
+ catch (error) {
167
+ void finish(error);
168
+ }
169
+ };
170
+ child.on('message', message);
171
+ child.once('error', failed);
172
+ child.once('exit', exited);
173
+ child.stderr?.on('data', capture);
174
+ options.signal?.addEventListener('abort', abort, { once: true });
175
+ if (options.executionTimeoutMs)
176
+ timer = setTimeout(() => failed(Object.assign(new Error('Model execution deadline exceeded'), {
177
+ code: 'PLUGIN_EXECUTION_DEADLINE',
178
+ })), options.executionTimeoutMs);
179
+ try {
180
+ if (options.signal?.aborted) {
181
+ abort();
182
+ return;
183
+ }
184
+ if (child.pid)
185
+ options.onExecutorStarted?.({
186
+ pid: child.pid,
187
+ processIdentity: processIdentity(child.pid),
188
+ });
189
+ child.send({ request }, error => {
190
+ if (error)
191
+ failed(error);
192
+ });
193
+ }
194
+ catch (error) {
195
+ void finish(error);
196
+ }
197
+ });
198
+ }
199
+ remove(session) {
200
+ if (session.closing)
201
+ return session.closing;
202
+ session.closed = true;
203
+ clearTimeout(session.timer);
204
+ for (const [key, value] of this.sessions)
205
+ if (value === session)
206
+ this.sessions.delete(key);
207
+ session.closing = (session.child.pid ? stopPluginProcessTree(session.child.pid) : Promise.resolve())
208
+ .then(() => {
209
+ let timer;
210
+ const timeout = new Promise((_, reject) => {
211
+ timer = setTimeout(() => reject(new Error('Model conversation did not exit')), 2000);
212
+ });
213
+ return Promise.race([session.exited, timeout]).finally(() => clearTimeout(timer));
214
+ })
215
+ .catch(cause => {
216
+ throw Object.assign(new Error('Unable to confirm model conversation cleanup', { cause }), {
217
+ code: 'PLUGIN_EXECUTOR_CLEANUP_FAILED',
218
+ details: { executionStopped: false },
219
+ });
220
+ });
221
+ this.closing.add(session);
222
+ void session.closing.finally(() => this.closing.delete(session)).catch(() => { });
223
+ return session.closing;
224
+ }
225
+ async release(owner) {
226
+ await Promise.all([...new Set([...this.sessions.values(), ...this.closing])]
227
+ .filter(value => value.owner === owner)
228
+ .map(value => this.remove(value)));
229
+ }
230
+ async close() {
231
+ this.closed = true;
232
+ await Promise.all([...new Set([...this.sessions.values(), ...this.closing])].map(value => this.remove(value)));
233
+ }
234
+ }
@@ -0,0 +1,57 @@
1
+ import type { PluginDiagnosticPage } from '../../generated/plugin-diagnostics.js';
2
+ import type { PluginStateManager } from './pluginStateManager.js';
3
+ import type { PluginInvocationIdentity } from '../../generated/plugin-execution.js';
4
+ import type { PluginRevision } from './pluginRevision.js';
5
+ import type { PluginOptions } from '../sdk/plugin.js';
6
+ import type { PluginPageTarget } from '../../generated/plugin-pages.js';
7
+ import type { PluginTaskBridge, PluginTaskManager } from './pluginTaskManager.js';
8
+ interface Execution {
9
+ cwd: string;
10
+ environment: Record<string, string>;
11
+ signal?: AbortSignal;
12
+ revision?: PluginRevision;
13
+ expectedTarget?: PluginPageTarget;
14
+ parentIdentity?: PluginInvocationIdentity;
15
+ }
16
+ /** The browser owns document identity; this service owns revisions and event execution. */
17
+ export declare class PluginPageManager {
18
+ private readonly tasks;
19
+ private readonly states;
20
+ private readonly releaseModels?;
21
+ private readonly instances;
22
+ private readonly changes;
23
+ private readonly heartbeat;
24
+ private readonly reconciling;
25
+ private closed;
26
+ private readonly describing;
27
+ constructor(tasks: PluginTaskManager, states: PluginStateManager, releaseModels?: ((owner: string) => Promise<void>) | undefined);
28
+ resolveStateTarget(plugin: string, instanceId: unknown, bridge: PluginTaskBridge, options: PluginOptions): Promise<{
29
+ target: {
30
+ browserSessionId: string;
31
+ tabId: number;
32
+ frameId: string | number;
33
+ documentId: string;
34
+ url: string;
35
+ };
36
+ assertActive: () => void;
37
+ }>;
38
+ diagnosticSnapshot(): PluginDiagnosticPage[];
39
+ private reconcile;
40
+ private bounded;
41
+ private invoke;
42
+ private unavailable;
43
+ private validate;
44
+ private own;
45
+ private bind;
46
+ private append;
47
+ private waitForChange;
48
+ private invalidate;
49
+ call(plugin: string, input: Record<string, unknown>, bridge: PluginTaskBridge, options?: PluginOptions, execution?: Execution): Promise<any>;
50
+ receive(plugin: string, input: Record<string, unknown>, bridge: PluginTaskBridge, options?: PluginOptions): Promise<unknown>;
51
+ private drain;
52
+ private execute;
53
+ private start;
54
+ private wait;
55
+ close(): Promise<void>;
56
+ }
57
+ export {};