@sparkleideas/plugins 3.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +401 -0
- package/__tests__/collection-manager.test.ts +332 -0
- package/__tests__/dependency-graph.test.ts +434 -0
- package/__tests__/enhanced-plugin-registry.test.ts +488 -0
- package/__tests__/plugin-registry.test.ts +368 -0
- package/__tests__/ruvector-bridge.test.ts +2429 -0
- package/__tests__/ruvector-integration.test.ts +1602 -0
- package/__tests__/ruvector-migrations.test.ts +1099 -0
- package/__tests__/ruvector-quantization.test.ts +846 -0
- package/__tests__/ruvector-streaming.test.ts +1088 -0
- package/__tests__/sdk.test.ts +325 -0
- package/__tests__/security.test.ts +348 -0
- package/__tests__/utils/ruvector-test-utils.ts +860 -0
- package/examples/plugin-creator/index.ts +636 -0
- package/examples/plugin-creator/plugin-creator.test.ts +312 -0
- package/examples/ruvector/README.md +288 -0
- package/examples/ruvector/attention-patterns.ts +394 -0
- package/examples/ruvector/basic-usage.ts +288 -0
- package/examples/ruvector/docker-compose.yml +75 -0
- package/examples/ruvector/gnn-analysis.ts +501 -0
- package/examples/ruvector/hyperbolic-hierarchies.ts +557 -0
- package/examples/ruvector/init-db.sql +119 -0
- package/examples/ruvector/quantization.ts +680 -0
- package/examples/ruvector/self-learning.ts +447 -0
- package/examples/ruvector/semantic-search.ts +576 -0
- package/examples/ruvector/streaming-large-data.ts +507 -0
- package/examples/ruvector/transactions.ts +594 -0
- package/examples/ruvector-plugins/hook-pattern-library.ts +486 -0
- package/examples/ruvector-plugins/index.ts +79 -0
- package/examples/ruvector-plugins/intent-router.ts +354 -0
- package/examples/ruvector-plugins/mcp-tool-optimizer.ts +424 -0
- package/examples/ruvector-plugins/reasoning-bank.ts +657 -0
- package/examples/ruvector-plugins/ruvector-plugins.test.ts +518 -0
- package/examples/ruvector-plugins/semantic-code-search.ts +498 -0
- package/examples/ruvector-plugins/shared/index.ts +20 -0
- package/examples/ruvector-plugins/shared/vector-utils.ts +257 -0
- package/examples/ruvector-plugins/sona-learning.ts +445 -0
- package/package.json +97 -0
- package/src/collections/collection-manager.ts +661 -0
- package/src/collections/index.ts +56 -0
- package/src/collections/official/index.ts +1040 -0
- package/src/core/base-plugin.ts +416 -0
- package/src/core/plugin-interface.ts +215 -0
- package/src/hooks/index.ts +685 -0
- package/src/index.ts +378 -0
- package/src/integrations/agentic-flow.ts +743 -0
- package/src/integrations/index.ts +88 -0
- package/src/integrations/ruvector/ARCHITECTURE.md +1245 -0
- package/src/integrations/ruvector/attention-advanced.ts +1040 -0
- package/src/integrations/ruvector/attention-executor.ts +782 -0
- package/src/integrations/ruvector/attention-mechanisms.ts +757 -0
- package/src/integrations/ruvector/attention.ts +1063 -0
- package/src/integrations/ruvector/gnn.ts +3050 -0
- package/src/integrations/ruvector/hyperbolic.ts +1948 -0
- package/src/integrations/ruvector/index.ts +394 -0
- package/src/integrations/ruvector/migrations/001_create_extension.sql +135 -0
- package/src/integrations/ruvector/migrations/002_create_vector_tables.sql +259 -0
- package/src/integrations/ruvector/migrations/003_create_indices.sql +328 -0
- package/src/integrations/ruvector/migrations/004_create_functions.sql +598 -0
- package/src/integrations/ruvector/migrations/005_create_attention_functions.sql +654 -0
- package/src/integrations/ruvector/migrations/006_create_gnn_functions.sql +728 -0
- package/src/integrations/ruvector/migrations/007_create_hyperbolic_functions.sql +762 -0
- package/src/integrations/ruvector/migrations/index.ts +35 -0
- package/src/integrations/ruvector/migrations/migrations.ts +647 -0
- package/src/integrations/ruvector/quantization.ts +2036 -0
- package/src/integrations/ruvector/ruvector-bridge.ts +2000 -0
- package/src/integrations/ruvector/self-learning.ts +2376 -0
- package/src/integrations/ruvector/streaming.ts +1737 -0
- package/src/integrations/ruvector/types.ts +1945 -0
- package/src/providers/index.ts +643 -0
- package/src/registry/dependency-graph.ts +568 -0
- package/src/registry/enhanced-plugin-registry.ts +994 -0
- package/src/registry/plugin-registry.ts +604 -0
- package/src/sdk/index.ts +563 -0
- package/src/security/index.ts +594 -0
- package/src/types/index.ts +446 -0
- package/src/workers/index.ts +700 -0
- package/tmp.json +0 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +23 -0
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Registry
|
|
3
|
+
*
|
|
4
|
+
* Manages plugin lifecycle, dependency resolution, and extension point collection.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { EventEmitter } from 'events';
|
|
8
|
+
import type {
|
|
9
|
+
PluginContext,
|
|
10
|
+
PluginConfig,
|
|
11
|
+
PluginMetadata,
|
|
12
|
+
IEventBus,
|
|
13
|
+
ILogger,
|
|
14
|
+
ServiceContainer,
|
|
15
|
+
AgentTypeDefinition,
|
|
16
|
+
TaskTypeDefinition,
|
|
17
|
+
MCPToolDefinition,
|
|
18
|
+
CLICommandDefinition,
|
|
19
|
+
MemoryBackendFactory,
|
|
20
|
+
HookDefinition,
|
|
21
|
+
WorkerDefinition,
|
|
22
|
+
LLMProviderDefinition,
|
|
23
|
+
HealthCheckResult,
|
|
24
|
+
} from '../types/index.js';
|
|
25
|
+
import type { IPlugin, PluginFactory } from '../core/plugin-interface.js';
|
|
26
|
+
import { validatePlugin, PLUGIN_EVENTS } from '../core/plugin-interface.js';
|
|
27
|
+
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Registry Types
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
32
|
+
export interface PluginRegistryConfig {
|
|
33
|
+
coreVersion: string;
|
|
34
|
+
dataDir: string;
|
|
35
|
+
logger?: ILogger;
|
|
36
|
+
eventBus?: IEventBus;
|
|
37
|
+
defaultConfig?: Partial<PluginConfig>;
|
|
38
|
+
maxPlugins?: number;
|
|
39
|
+
loadTimeout?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface PluginEntry {
|
|
43
|
+
plugin: IPlugin;
|
|
44
|
+
config: PluginConfig;
|
|
45
|
+
loadTime: Date;
|
|
46
|
+
initTime?: Date;
|
|
47
|
+
error?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface RegistryStats {
|
|
51
|
+
total: number;
|
|
52
|
+
initialized: number;
|
|
53
|
+
failed: number;
|
|
54
|
+
agentTypes: number;
|
|
55
|
+
taskTypes: number;
|
|
56
|
+
mcpTools: number;
|
|
57
|
+
cliCommands: number;
|
|
58
|
+
hooks: number;
|
|
59
|
+
workers: number;
|
|
60
|
+
providers: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// Default Implementations
|
|
65
|
+
// ============================================================================
|
|
66
|
+
|
|
67
|
+
class DefaultEventBus implements IEventBus {
|
|
68
|
+
private emitter = new EventEmitter();
|
|
69
|
+
|
|
70
|
+
emit(event: string, data?: unknown): void {
|
|
71
|
+
this.emitter.emit(event, data);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
on(event: string, handler: (data?: unknown) => void | Promise<void>): () => void {
|
|
75
|
+
this.emitter.on(event, handler);
|
|
76
|
+
return () => this.off(event, handler);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
off(event: string, handler: (data?: unknown) => void | Promise<void>): void {
|
|
80
|
+
this.emitter.off(event, handler);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
once(event: string, handler: (data?: unknown) => void | Promise<void>): () => void {
|
|
84
|
+
this.emitter.once(event, handler);
|
|
85
|
+
return () => this.off(event, handler);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
class DefaultLogger implements ILogger {
|
|
90
|
+
private context: Record<string, unknown> = {};
|
|
91
|
+
|
|
92
|
+
constructor(context?: Record<string, unknown>) {
|
|
93
|
+
if (context) this.context = context;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
debug(message: string, ...args: unknown[]): void {
|
|
97
|
+
console.debug(`[DEBUG]`, message, ...args, this.context);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
info(message: string, ...args: unknown[]): void {
|
|
101
|
+
console.info(`[INFO]`, message, ...args, this.context);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
warn(message: string, ...args: unknown[]): void {
|
|
105
|
+
console.warn(`[WARN]`, message, ...args, this.context);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
error(message: string, ...args: unknown[]): void {
|
|
109
|
+
console.error(`[ERROR]`, message, ...args, this.context);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
child(context: Record<string, unknown>): ILogger {
|
|
113
|
+
return new DefaultLogger({ ...this.context, ...context });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
class DefaultServiceContainer implements ServiceContainer {
|
|
118
|
+
private services = new Map<string, unknown>();
|
|
119
|
+
|
|
120
|
+
get<T>(key: string): T | undefined {
|
|
121
|
+
return this.services.get(key) as T | undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
set<T>(key: string, value: T): void {
|
|
125
|
+
this.services.set(key, value);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
has(key: string): boolean {
|
|
129
|
+
return this.services.has(key);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
delete(key: string): boolean {
|
|
133
|
+
return this.services.delete(key);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// Plugin Registry
|
|
139
|
+
// ============================================================================
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Central registry for plugin management.
|
|
143
|
+
*
|
|
144
|
+
* Features:
|
|
145
|
+
* - Plugin loading and unloading
|
|
146
|
+
* - Dependency resolution
|
|
147
|
+
* - Extension point collection
|
|
148
|
+
* - Health monitoring
|
|
149
|
+
* - Lifecycle management
|
|
150
|
+
*/
|
|
151
|
+
export class PluginRegistry extends EventEmitter {
|
|
152
|
+
// =========================================================================
|
|
153
|
+
// Properties
|
|
154
|
+
// =========================================================================
|
|
155
|
+
|
|
156
|
+
private readonly plugins = new Map<string, PluginEntry>();
|
|
157
|
+
private readonly config: PluginRegistryConfig;
|
|
158
|
+
private readonly logger: ILogger;
|
|
159
|
+
private readonly eventBus: IEventBus;
|
|
160
|
+
private readonly services: ServiceContainer;
|
|
161
|
+
private initialized = false;
|
|
162
|
+
|
|
163
|
+
// Extension point caches
|
|
164
|
+
private agentTypesCache: AgentTypeDefinition[] = [];
|
|
165
|
+
private taskTypesCache: TaskTypeDefinition[] = [];
|
|
166
|
+
private mcpToolsCache: MCPToolDefinition[] = [];
|
|
167
|
+
private cliCommandsCache: CLICommandDefinition[] = [];
|
|
168
|
+
private memoryBackendsCache: MemoryBackendFactory[] = [];
|
|
169
|
+
private hooksCache: HookDefinition[] = [];
|
|
170
|
+
private workersCache: WorkerDefinition[] = [];
|
|
171
|
+
private providersCache: LLMProviderDefinition[] = [];
|
|
172
|
+
|
|
173
|
+
// =========================================================================
|
|
174
|
+
// Constructor
|
|
175
|
+
// =========================================================================
|
|
176
|
+
|
|
177
|
+
constructor(config: PluginRegistryConfig) {
|
|
178
|
+
super();
|
|
179
|
+
this.config = config;
|
|
180
|
+
this.logger = config.logger ?? new DefaultLogger({ component: 'PluginRegistry' });
|
|
181
|
+
this.eventBus = config.eventBus ?? new DefaultEventBus();
|
|
182
|
+
this.services = new DefaultServiceContainer();
|
|
183
|
+
|
|
184
|
+
// Register self in services
|
|
185
|
+
this.services.set('pluginRegistry', this);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// =========================================================================
|
|
189
|
+
// Plugin Loading
|
|
190
|
+
// =========================================================================
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Register a plugin.
|
|
194
|
+
*/
|
|
195
|
+
async register(
|
|
196
|
+
plugin: IPlugin | PluginFactory,
|
|
197
|
+
config?: Partial<PluginConfig>
|
|
198
|
+
): Promise<void> {
|
|
199
|
+
// Resolve factory if needed
|
|
200
|
+
const resolvedPlugin = typeof plugin === 'function' ? await plugin() : plugin;
|
|
201
|
+
|
|
202
|
+
// Validate plugin
|
|
203
|
+
if (!validatePlugin(resolvedPlugin)) {
|
|
204
|
+
throw new Error('Invalid plugin: does not implement IPlugin interface');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const name = resolvedPlugin.metadata.name;
|
|
208
|
+
|
|
209
|
+
// Check for duplicates
|
|
210
|
+
if (this.plugins.has(name)) {
|
|
211
|
+
throw new Error(`Plugin ${name} already registered`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Check max plugins
|
|
215
|
+
if (this.config.maxPlugins && this.plugins.size >= this.config.maxPlugins) {
|
|
216
|
+
throw new Error(`Maximum plugin limit (${this.config.maxPlugins}) reached`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Create config
|
|
220
|
+
const pluginConfig: PluginConfig = {
|
|
221
|
+
enabled: true,
|
|
222
|
+
priority: 50,
|
|
223
|
+
settings: {},
|
|
224
|
+
...this.config.defaultConfig,
|
|
225
|
+
...config,
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// Store entry
|
|
229
|
+
const entry: PluginEntry = {
|
|
230
|
+
plugin: resolvedPlugin,
|
|
231
|
+
config: pluginConfig,
|
|
232
|
+
loadTime: new Date(),
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
this.plugins.set(name, entry);
|
|
236
|
+
this.eventBus.emit(PLUGIN_EVENTS.LOADED, { plugin: name });
|
|
237
|
+
this.logger.info(`Plugin registered: ${name} v${resolvedPlugin.metadata.version}`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Unregister a plugin.
|
|
242
|
+
*/
|
|
243
|
+
async unregister(name: string): Promise<void> {
|
|
244
|
+
const entry = this.plugins.get(name);
|
|
245
|
+
if (!entry) {
|
|
246
|
+
throw new Error(`Plugin ${name} not found`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Shutdown if initialized
|
|
250
|
+
if (entry.plugin.state === 'initialized') {
|
|
251
|
+
await entry.plugin.shutdown();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
this.plugins.delete(name);
|
|
255
|
+
this.invalidateCaches();
|
|
256
|
+
this.logger.info(`Plugin unregistered: ${name}`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// =========================================================================
|
|
260
|
+
// Initialization
|
|
261
|
+
// =========================================================================
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Initialize all registered plugins.
|
|
265
|
+
*/
|
|
266
|
+
async initialize(): Promise<void> {
|
|
267
|
+
if (this.initialized) {
|
|
268
|
+
throw new Error('Registry already initialized');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Resolve dependencies and get load order
|
|
272
|
+
const loadOrder = this.resolveDependencies();
|
|
273
|
+
|
|
274
|
+
// Initialize plugins in order
|
|
275
|
+
for (const name of loadOrder) {
|
|
276
|
+
const entry = this.plugins.get(name)!;
|
|
277
|
+
if (!entry.config.enabled) {
|
|
278
|
+
this.logger.info(`Plugin ${name} is disabled, skipping initialization`);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
const context = this.createPluginContext(entry);
|
|
284
|
+
await this.initializeWithTimeout(entry.plugin, context);
|
|
285
|
+
entry.initTime = new Date();
|
|
286
|
+
this.collectExtensionPoints(entry.plugin);
|
|
287
|
+
this.logger.info(`Plugin initialized: ${name}`);
|
|
288
|
+
} catch (error) {
|
|
289
|
+
entry.error = error instanceof Error ? error.message : String(error);
|
|
290
|
+
this.logger.error(`Failed to initialize plugin ${name}: ${entry.error}`);
|
|
291
|
+
// Continue with other plugins
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
this.initialized = true;
|
|
296
|
+
this.logger.info(`Registry initialized with ${this.plugins.size} plugins`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Shutdown all plugins.
|
|
301
|
+
*/
|
|
302
|
+
async shutdown(): Promise<void> {
|
|
303
|
+
// Shutdown in reverse order
|
|
304
|
+
const names = Array.from(this.plugins.keys()).reverse();
|
|
305
|
+
|
|
306
|
+
for (const name of names) {
|
|
307
|
+
const entry = this.plugins.get(name)!;
|
|
308
|
+
if (entry.plugin.state === 'initialized') {
|
|
309
|
+
try {
|
|
310
|
+
await entry.plugin.shutdown();
|
|
311
|
+
this.logger.info(`Plugin shutdown: ${name}`);
|
|
312
|
+
} catch (error) {
|
|
313
|
+
this.logger.error(`Error shutting down plugin ${name}: ${error}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
this.invalidateCaches();
|
|
319
|
+
this.initialized = false;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// =========================================================================
|
|
323
|
+
// Dependency Resolution
|
|
324
|
+
// =========================================================================
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Resolve dependencies and return load order.
|
|
328
|
+
*/
|
|
329
|
+
private resolveDependencies(): string[] {
|
|
330
|
+
const visited = new Set<string>();
|
|
331
|
+
const visiting = new Set<string>();
|
|
332
|
+
const order: string[] = [];
|
|
333
|
+
|
|
334
|
+
const visit = (name: string): void => {
|
|
335
|
+
if (visited.has(name)) return;
|
|
336
|
+
if (visiting.has(name)) {
|
|
337
|
+
throw new Error(`Circular dependency detected: ${name}`);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const entry = this.plugins.get(name);
|
|
341
|
+
if (!entry) {
|
|
342
|
+
throw new Error(`Missing dependency: ${name}`);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
visiting.add(name);
|
|
346
|
+
|
|
347
|
+
const deps = entry.plugin.metadata.dependencies ?? [];
|
|
348
|
+
for (const dep of deps) {
|
|
349
|
+
visit(dep);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
visiting.delete(name);
|
|
353
|
+
visited.add(name);
|
|
354
|
+
order.push(name);
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
for (const name of this.plugins.keys()) {
|
|
358
|
+
visit(name);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return order;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// =========================================================================
|
|
365
|
+
// Extension Points
|
|
366
|
+
// =========================================================================
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Collect extension points from a plugin.
|
|
370
|
+
*/
|
|
371
|
+
private collectExtensionPoints(plugin: IPlugin): void {
|
|
372
|
+
if (plugin.registerAgentTypes) {
|
|
373
|
+
const types = plugin.registerAgentTypes();
|
|
374
|
+
if (types) this.agentTypesCache.push(...types);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (plugin.registerTaskTypes) {
|
|
378
|
+
const types = plugin.registerTaskTypes();
|
|
379
|
+
if (types) this.taskTypesCache.push(...types);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (plugin.registerMCPTools) {
|
|
383
|
+
const tools = plugin.registerMCPTools();
|
|
384
|
+
if (tools) this.mcpToolsCache.push(...tools);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (plugin.registerCLICommands) {
|
|
388
|
+
const commands = plugin.registerCLICommands();
|
|
389
|
+
if (commands) this.cliCommandsCache.push(...commands);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (plugin.registerMemoryBackends) {
|
|
393
|
+
const backends = plugin.registerMemoryBackends();
|
|
394
|
+
if (backends) this.memoryBackendsCache.push(...backends);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (plugin.registerHooks) {
|
|
398
|
+
const hooks = plugin.registerHooks();
|
|
399
|
+
if (hooks) this.hooksCache.push(...hooks);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (plugin.registerWorkers) {
|
|
403
|
+
const workers = plugin.registerWorkers();
|
|
404
|
+
if (workers) this.workersCache.push(...workers);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (plugin.registerProviders) {
|
|
408
|
+
const providers = plugin.registerProviders();
|
|
409
|
+
if (providers) this.providersCache.push(...providers);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Invalidate extension point caches.
|
|
415
|
+
*/
|
|
416
|
+
private invalidateCaches(): void {
|
|
417
|
+
this.agentTypesCache = [];
|
|
418
|
+
this.taskTypesCache = [];
|
|
419
|
+
this.mcpToolsCache = [];
|
|
420
|
+
this.cliCommandsCache = [];
|
|
421
|
+
this.memoryBackendsCache = [];
|
|
422
|
+
this.hooksCache = [];
|
|
423
|
+
this.workersCache = [];
|
|
424
|
+
this.providersCache = [];
|
|
425
|
+
|
|
426
|
+
// Recollect from initialized plugins
|
|
427
|
+
for (const entry of this.plugins.values()) {
|
|
428
|
+
if (entry.plugin.state === 'initialized') {
|
|
429
|
+
this.collectExtensionPoints(entry.plugin);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// =========================================================================
|
|
435
|
+
// Getters
|
|
436
|
+
// =========================================================================
|
|
437
|
+
|
|
438
|
+
getAgentTypes(): AgentTypeDefinition[] {
|
|
439
|
+
return [...this.agentTypesCache];
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
getTaskTypes(): TaskTypeDefinition[] {
|
|
443
|
+
return [...this.taskTypesCache];
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
getMCPTools(): MCPToolDefinition[] {
|
|
447
|
+
return [...this.mcpToolsCache];
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
getCLICommands(): CLICommandDefinition[] {
|
|
451
|
+
return [...this.cliCommandsCache];
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
getMemoryBackends(): MemoryBackendFactory[] {
|
|
455
|
+
return [...this.memoryBackendsCache];
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
getHooks(): HookDefinition[] {
|
|
459
|
+
return [...this.hooksCache];
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
getWorkers(): WorkerDefinition[] {
|
|
463
|
+
return [...this.workersCache];
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
getProviders(): LLMProviderDefinition[] {
|
|
467
|
+
return [...this.providersCache];
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
getPlugin(name: string): IPlugin | undefined {
|
|
471
|
+
return this.plugins.get(name)?.plugin;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
getPluginEntry(name: string): PluginEntry | undefined {
|
|
475
|
+
return this.plugins.get(name);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
listPlugins(): PluginMetadata[] {
|
|
479
|
+
return Array.from(this.plugins.values()).map(e => e.plugin.metadata);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// =========================================================================
|
|
483
|
+
// Health Check
|
|
484
|
+
// =========================================================================
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Run health checks on all plugins.
|
|
488
|
+
*/
|
|
489
|
+
async healthCheck(): Promise<Map<string, HealthCheckResult>> {
|
|
490
|
+
const results = new Map<string, HealthCheckResult>();
|
|
491
|
+
|
|
492
|
+
for (const [name, entry] of this.plugins) {
|
|
493
|
+
if (entry.plugin.state !== 'initialized') {
|
|
494
|
+
results.set(name, {
|
|
495
|
+
healthy: false,
|
|
496
|
+
status: 'unhealthy',
|
|
497
|
+
message: `Plugin not initialized: ${entry.plugin.state}`,
|
|
498
|
+
checks: {},
|
|
499
|
+
timestamp: new Date(),
|
|
500
|
+
});
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
try {
|
|
505
|
+
if (entry.plugin.healthCheck) {
|
|
506
|
+
results.set(name, await entry.plugin.healthCheck());
|
|
507
|
+
} else {
|
|
508
|
+
results.set(name, {
|
|
509
|
+
healthy: true,
|
|
510
|
+
status: 'healthy',
|
|
511
|
+
checks: {},
|
|
512
|
+
timestamp: new Date(),
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
} catch (error) {
|
|
516
|
+
results.set(name, {
|
|
517
|
+
healthy: false,
|
|
518
|
+
status: 'unhealthy',
|
|
519
|
+
message: error instanceof Error ? error.message : String(error),
|
|
520
|
+
checks: {},
|
|
521
|
+
timestamp: new Date(),
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
return results;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// =========================================================================
|
|
530
|
+
// Stats
|
|
531
|
+
// =========================================================================
|
|
532
|
+
|
|
533
|
+
getStats(): RegistryStats {
|
|
534
|
+
let initialized = 0;
|
|
535
|
+
let failed = 0;
|
|
536
|
+
|
|
537
|
+
for (const entry of this.plugins.values()) {
|
|
538
|
+
if (entry.plugin.state === 'initialized') initialized++;
|
|
539
|
+
if (entry.plugin.state === 'error' || entry.error) failed++;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return {
|
|
543
|
+
total: this.plugins.size,
|
|
544
|
+
initialized,
|
|
545
|
+
failed,
|
|
546
|
+
agentTypes: this.agentTypesCache.length,
|
|
547
|
+
taskTypes: this.taskTypesCache.length,
|
|
548
|
+
mcpTools: this.mcpToolsCache.length,
|
|
549
|
+
cliCommands: this.cliCommandsCache.length,
|
|
550
|
+
hooks: this.hooksCache.length,
|
|
551
|
+
workers: this.workersCache.length,
|
|
552
|
+
providers: this.providersCache.length,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// =========================================================================
|
|
557
|
+
// Helpers
|
|
558
|
+
// =========================================================================
|
|
559
|
+
|
|
560
|
+
private createPluginContext(entry: PluginEntry): PluginContext {
|
|
561
|
+
return {
|
|
562
|
+
config: entry.config,
|
|
563
|
+
eventBus: this.eventBus,
|
|
564
|
+
logger: this.logger.child({ plugin: entry.plugin.metadata.name }),
|
|
565
|
+
services: this.services,
|
|
566
|
+
coreVersion: this.config.coreVersion,
|
|
567
|
+
dataDir: this.config.dataDir,
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
private async initializeWithTimeout(
|
|
572
|
+
plugin: IPlugin,
|
|
573
|
+
context: PluginContext
|
|
574
|
+
): Promise<void> {
|
|
575
|
+
const timeout = this.config.loadTimeout ?? 30000;
|
|
576
|
+
|
|
577
|
+
await Promise.race([
|
|
578
|
+
plugin.initialize(context),
|
|
579
|
+
new Promise<never>((_, reject) =>
|
|
580
|
+
setTimeout(() => reject(new Error('Initialization timeout')), timeout)
|
|
581
|
+
),
|
|
582
|
+
]);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// ============================================================================
|
|
587
|
+
// Default Registry Instance
|
|
588
|
+
// ============================================================================
|
|
589
|
+
|
|
590
|
+
let defaultRegistry: PluginRegistry | null = null;
|
|
591
|
+
|
|
592
|
+
export function getDefaultRegistry(): PluginRegistry {
|
|
593
|
+
if (!defaultRegistry) {
|
|
594
|
+
defaultRegistry = new PluginRegistry({
|
|
595
|
+
coreVersion: '3.0.0',
|
|
596
|
+
dataDir: process.cwd(),
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
return defaultRegistry;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
export function setDefaultRegistry(registry: PluginRegistry): void {
|
|
603
|
+
defaultRegistry = registry;
|
|
604
|
+
}
|