@sparkleideas/shared 3.0.0-alpha.7

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 (96) hide show
  1. package/README.md +323 -0
  2. package/__tests__/hooks/bash-safety.test.ts +289 -0
  3. package/__tests__/hooks/file-organization.test.ts +335 -0
  4. package/__tests__/hooks/git-commit.test.ts +336 -0
  5. package/__tests__/hooks/index.ts +23 -0
  6. package/__tests__/hooks/session-hooks.test.ts +357 -0
  7. package/__tests__/hooks/task-hooks.test.ts +193 -0
  8. package/docs/EVENTS_IMPLEMENTATION_SUMMARY.md +388 -0
  9. package/docs/EVENTS_QUICK_REFERENCE.md +470 -0
  10. package/docs/EVENTS_README.md +352 -0
  11. package/package.json +39 -0
  12. package/src/core/config/defaults.ts +207 -0
  13. package/src/core/config/index.ts +15 -0
  14. package/src/core/config/loader.ts +271 -0
  15. package/src/core/config/schema.ts +188 -0
  16. package/src/core/config/validator.ts +209 -0
  17. package/src/core/event-bus.ts +236 -0
  18. package/src/core/index.ts +22 -0
  19. package/src/core/interfaces/agent.interface.ts +251 -0
  20. package/src/core/interfaces/coordinator.interface.ts +363 -0
  21. package/src/core/interfaces/event.interface.ts +267 -0
  22. package/src/core/interfaces/index.ts +19 -0
  23. package/src/core/interfaces/memory.interface.ts +332 -0
  24. package/src/core/interfaces/task.interface.ts +223 -0
  25. package/src/core/orchestrator/event-coordinator.ts +122 -0
  26. package/src/core/orchestrator/health-monitor.ts +214 -0
  27. package/src/core/orchestrator/index.ts +89 -0
  28. package/src/core/orchestrator/lifecycle-manager.ts +263 -0
  29. package/src/core/orchestrator/session-manager.ts +279 -0
  30. package/src/core/orchestrator/task-manager.ts +317 -0
  31. package/src/events/domain-events.ts +584 -0
  32. package/src/events/event-store.test.ts +387 -0
  33. package/src/events/event-store.ts +588 -0
  34. package/src/events/example-usage.ts +293 -0
  35. package/src/events/index.ts +90 -0
  36. package/src/events/projections.ts +561 -0
  37. package/src/events/state-reconstructor.ts +349 -0
  38. package/src/events.ts +367 -0
  39. package/src/hooks/INTEGRATION.md +658 -0
  40. package/src/hooks/README.md +532 -0
  41. package/src/hooks/example-usage.ts +499 -0
  42. package/src/hooks/executor.ts +379 -0
  43. package/src/hooks/hooks.test.ts +421 -0
  44. package/src/hooks/index.ts +131 -0
  45. package/src/hooks/registry.ts +333 -0
  46. package/src/hooks/safety/bash-safety.ts +604 -0
  47. package/src/hooks/safety/file-organization.ts +473 -0
  48. package/src/hooks/safety/git-commit.ts +623 -0
  49. package/src/hooks/safety/index.ts +46 -0
  50. package/src/hooks/session-hooks.ts +559 -0
  51. package/src/hooks/task-hooks.ts +513 -0
  52. package/src/hooks/types.ts +357 -0
  53. package/src/hooks/verify-exports.test.ts +125 -0
  54. package/src/index.ts +195 -0
  55. package/src/mcp/connection-pool.ts +438 -0
  56. package/src/mcp/index.ts +183 -0
  57. package/src/mcp/server.ts +774 -0
  58. package/src/mcp/session-manager.ts +428 -0
  59. package/src/mcp/tool-registry.ts +566 -0
  60. package/src/mcp/transport/http.ts +557 -0
  61. package/src/mcp/transport/index.ts +294 -0
  62. package/src/mcp/transport/stdio.ts +324 -0
  63. package/src/mcp/transport/websocket.ts +484 -0
  64. package/src/mcp/types.ts +565 -0
  65. package/src/plugin-interface.ts +663 -0
  66. package/src/plugin-loader.ts +638 -0
  67. package/src/plugin-registry.ts +604 -0
  68. package/src/plugins/index.ts +34 -0
  69. package/src/plugins/official/hive-mind-plugin.ts +330 -0
  70. package/src/plugins/official/index.ts +24 -0
  71. package/src/plugins/official/maestro-plugin.ts +508 -0
  72. package/src/plugins/types.ts +108 -0
  73. package/src/resilience/bulkhead.ts +277 -0
  74. package/src/resilience/circuit-breaker.ts +326 -0
  75. package/src/resilience/index.ts +26 -0
  76. package/src/resilience/rate-limiter.ts +420 -0
  77. package/src/resilience/retry.ts +224 -0
  78. package/src/security/index.ts +39 -0
  79. package/src/security/input-validation.ts +265 -0
  80. package/src/security/secure-random.ts +159 -0
  81. package/src/services/index.ts +16 -0
  82. package/src/services/v3-progress.service.ts +505 -0
  83. package/src/types/agent.types.ts +144 -0
  84. package/src/types/index.ts +22 -0
  85. package/src/types/mcp.types.ts +300 -0
  86. package/src/types/memory.types.ts +263 -0
  87. package/src/types/swarm.types.ts +255 -0
  88. package/src/types/task.types.ts +205 -0
  89. package/src/types.ts +367 -0
  90. package/src/utils/secure-logger.d.ts +69 -0
  91. package/src/utils/secure-logger.d.ts.map +1 -0
  92. package/src/utils/secure-logger.js +208 -0
  93. package/src/utils/secure-logger.js.map +1 -0
  94. package/src/utils/secure-logger.ts +257 -0
  95. package/tmp.json +0 -0
  96. package/tsconfig.json +9 -0
@@ -0,0 +1,557 @@
1
+ /**
2
+ * V3 MCP HTTP Transport
3
+ *
4
+ * HTTP/REST transport for MCP communication:
5
+ * - Express-based with optimized middleware
6
+ * - WebSocket support for real-time notifications
7
+ * - Connection pooling for client connections
8
+ * - Security headers with helmet
9
+ *
10
+ * Performance Targets:
11
+ * - Request handling: <20ms overhead
12
+ * - WebSocket message: <5ms
13
+ */
14
+
15
+ import { EventEmitter } from 'events';
16
+ import express, { Express, Request, Response, NextFunction } from 'express';
17
+ import { createServer, Server } from 'http';
18
+ import { WebSocketServer, WebSocket } from 'ws';
19
+ import cors from 'cors';
20
+ import helmet from 'helmet';
21
+ import {
22
+ ITransport,
23
+ TransportType,
24
+ MCPRequest,
25
+ MCPResponse,
26
+ MCPNotification,
27
+ RequestHandler,
28
+ NotificationHandler,
29
+ TransportHealthStatus,
30
+ ILogger,
31
+ AuthConfig,
32
+ } from '../types.js';
33
+
34
+ /**
35
+ * HTTP Transport Configuration
36
+ */
37
+ export interface HttpTransportConfig {
38
+ host: string;
39
+ port: number;
40
+ tlsEnabled?: boolean;
41
+ tlsCert?: string;
42
+ tlsKey?: string;
43
+ corsEnabled?: boolean;
44
+ corsOrigins?: string[];
45
+ auth?: AuthConfig;
46
+ maxRequestSize?: string;
47
+ requestTimeout?: number;
48
+ }
49
+
50
+ /**
51
+ * HTTP Transport Implementation
52
+ */
53
+ export class HttpTransport extends EventEmitter implements ITransport {
54
+ public readonly type: TransportType = 'http';
55
+
56
+ private requestHandler?: RequestHandler;
57
+ private notificationHandler?: NotificationHandler;
58
+ private app: Express;
59
+ private server?: Server;
60
+ private wss?: WebSocketServer;
61
+ private running = false;
62
+ private activeConnections = new Set<WebSocket>();
63
+
64
+ // Statistics
65
+ private messagesReceived = 0;
66
+ private messagesSent = 0;
67
+ private errors = 0;
68
+ private httpRequests = 0;
69
+ private wsMessages = 0;
70
+
71
+ constructor(
72
+ private readonly logger: ILogger,
73
+ private readonly config: HttpTransportConfig
74
+ ) {
75
+ super();
76
+ this.app = express();
77
+ this.setupMiddleware();
78
+ this.setupRoutes();
79
+ }
80
+
81
+ /**
82
+ * Start the transport
83
+ */
84
+ async start(): Promise<void> {
85
+ if (this.running) {
86
+ throw new Error('HTTP transport already running');
87
+ }
88
+
89
+ this.logger.info('Starting HTTP transport', {
90
+ host: this.config.host,
91
+ port: this.config.port,
92
+ });
93
+
94
+ // Create HTTP server
95
+ this.server = createServer(this.app);
96
+
97
+ // Create WebSocket server
98
+ this.wss = new WebSocketServer({
99
+ server: this.server,
100
+ path: '/ws',
101
+ });
102
+
103
+ this.setupWebSocketHandlers();
104
+
105
+ // Start server
106
+ await new Promise<void>((resolve, reject) => {
107
+ this.server!.listen(this.config.port, this.config.host, () => {
108
+ resolve();
109
+ });
110
+ this.server!.on('error', reject);
111
+ });
112
+
113
+ this.running = true;
114
+ this.logger.info('HTTP transport started', {
115
+ url: `http://${this.config.host}:${this.config.port}`,
116
+ });
117
+ }
118
+
119
+ /**
120
+ * Stop the transport
121
+ */
122
+ async stop(): Promise<void> {
123
+ if (!this.running) {
124
+ return;
125
+ }
126
+
127
+ this.logger.info('Stopping HTTP transport');
128
+ this.running = false;
129
+
130
+ // Close all WebSocket connections
131
+ for (const ws of this.activeConnections) {
132
+ try {
133
+ ws.close(1000, 'Server shutting down');
134
+ } catch {
135
+ // Ignore errors
136
+ }
137
+ }
138
+ this.activeConnections.clear();
139
+
140
+ // Close WebSocket server
141
+ if (this.wss) {
142
+ this.wss.close();
143
+ this.wss = undefined;
144
+ }
145
+
146
+ // Close HTTP server
147
+ if (this.server) {
148
+ await new Promise<void>((resolve) => {
149
+ this.server!.close(() => resolve());
150
+ });
151
+ this.server = undefined;
152
+ }
153
+
154
+ this.logger.info('HTTP transport stopped');
155
+ }
156
+
157
+ /**
158
+ * Register request handler
159
+ */
160
+ onRequest(handler: RequestHandler): void {
161
+ this.requestHandler = handler;
162
+ }
163
+
164
+ /**
165
+ * Register notification handler
166
+ */
167
+ onNotification(handler: NotificationHandler): void {
168
+ this.notificationHandler = handler;
169
+ }
170
+
171
+ /**
172
+ * Get health status
173
+ */
174
+ async getHealthStatus(): Promise<TransportHealthStatus> {
175
+ return {
176
+ healthy: this.running,
177
+ metrics: {
178
+ messagesReceived: this.messagesReceived,
179
+ messagesSent: this.messagesSent,
180
+ errors: this.errors,
181
+ httpRequests: this.httpRequests,
182
+ wsMessages: this.wsMessages,
183
+ activeConnections: this.activeConnections.size,
184
+ },
185
+ };
186
+ }
187
+
188
+ /**
189
+ * Send notification to all connected WebSocket clients
190
+ */
191
+ async sendNotification(notification: MCPNotification): Promise<void> {
192
+ const message = JSON.stringify(notification);
193
+
194
+ for (const ws of this.activeConnections) {
195
+ try {
196
+ if (ws.readyState === WebSocket.OPEN) {
197
+ ws.send(message);
198
+ this.messagesSent++;
199
+ }
200
+ } catch (error) {
201
+ this.logger.error('Failed to send notification', { error });
202
+ this.errors++;
203
+ }
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Setup Express middleware
209
+ */
210
+ private setupMiddleware(): void {
211
+ // Security headers
212
+ this.app.use(helmet({
213
+ contentSecurityPolicy: false, // Allow for flexibility
214
+ }));
215
+
216
+ // CORS - Secure defaults (no wildcard in production)
217
+ if (this.config.corsEnabled !== false) {
218
+ const allowedOrigins = this.config.corsOrigins;
219
+
220
+ // SECURITY: Reject wildcard CORS in production unless explicitly configured
221
+ if (!allowedOrigins || allowedOrigins.length === 0) {
222
+ this.logger.warn('CORS: No origins configured, restricting to same-origin only');
223
+ }
224
+
225
+ this.app.use(cors({
226
+ origin: (origin, callback) => {
227
+ // Allow requests with no origin (same-origin, curl, etc.)
228
+ if (!origin) {
229
+ callback(null, true);
230
+ return;
231
+ }
232
+
233
+ // Check against allowed origins
234
+ if (allowedOrigins && allowedOrigins.length > 0) {
235
+ if (allowedOrigins.includes(origin) || allowedOrigins.includes('*')) {
236
+ callback(null, true);
237
+ } else {
238
+ callback(new Error(`CORS: Origin '${origin}' not allowed`));
239
+ }
240
+ } else {
241
+ // No origins configured - reject cross-origin requests
242
+ callback(new Error('CORS: Cross-origin requests not allowed'));
243
+ }
244
+ },
245
+ credentials: true,
246
+ maxAge: 86400,
247
+ methods: ['GET', 'POST', 'OPTIONS'],
248
+ allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-ID'],
249
+ }));
250
+ }
251
+
252
+ // Body parsing
253
+ this.app.use(express.json({
254
+ limit: this.config.maxRequestSize || '10mb',
255
+ }));
256
+
257
+ // Request timeout
258
+ if (this.config.requestTimeout) {
259
+ this.app.use((req, res, next) => {
260
+ res.setTimeout(this.config.requestTimeout!, () => {
261
+ res.status(408).json({
262
+ jsonrpc: '2.0',
263
+ id: null,
264
+ error: { code: -32000, message: 'Request timeout' },
265
+ });
266
+ });
267
+ next();
268
+ });
269
+ }
270
+
271
+ // Request logging
272
+ this.app.use((req, res, next) => {
273
+ const startTime = performance.now();
274
+ res.on('finish', () => {
275
+ const duration = performance.now() - startTime;
276
+ this.logger.debug('HTTP request', {
277
+ method: req.method,
278
+ path: req.path,
279
+ status: res.statusCode,
280
+ duration: `${duration.toFixed(2)}ms`,
281
+ });
282
+ });
283
+ next();
284
+ });
285
+ }
286
+
287
+ /**
288
+ * Setup Express routes
289
+ */
290
+ private setupRoutes(): void {
291
+ // Health check
292
+ this.app.get('/health', (req, res) => {
293
+ res.json({
294
+ status: 'ok',
295
+ timestamp: new Date().toISOString(),
296
+ connections: this.activeConnections.size,
297
+ });
298
+ });
299
+
300
+ // MCP JSON-RPC endpoint
301
+ this.app.post('/rpc', async (req, res) => {
302
+ await this.handleHttpRequest(req, res);
303
+ });
304
+
305
+ // Alternative MCP endpoint
306
+ this.app.post('/mcp', async (req, res) => {
307
+ await this.handleHttpRequest(req, res);
308
+ });
309
+
310
+ // Server info
311
+ this.app.get('/info', (req, res) => {
312
+ res.json({
313
+ name: 'Claude-Flow MCP Server V3',
314
+ version: '3.0.0',
315
+ transport: 'http',
316
+ capabilities: {
317
+ jsonrpc: true,
318
+ websocket: true,
319
+ },
320
+ });
321
+ });
322
+
323
+ // 404 handler
324
+ this.app.use((req, res) => {
325
+ res.status(404).json({
326
+ error: 'Not found',
327
+ path: req.path,
328
+ });
329
+ });
330
+
331
+ // Error handler
332
+ this.app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
333
+ this.logger.error('Express error', { error: err });
334
+ this.errors++;
335
+ res.status(500).json({
336
+ jsonrpc: '2.0',
337
+ id: null,
338
+ error: { code: -32603, message: 'Internal error' },
339
+ });
340
+ });
341
+ }
342
+
343
+ /**
344
+ * Setup WebSocket handlers
345
+ */
346
+ private setupWebSocketHandlers(): void {
347
+ if (!this.wss) return;
348
+
349
+ this.wss.on('connection', (ws, req) => {
350
+ this.activeConnections.add(ws);
351
+ this.logger.info('WebSocket client connected', {
352
+ total: this.activeConnections.size,
353
+ });
354
+
355
+ ws.on('message', async (data) => {
356
+ await this.handleWebSocketMessage(ws, data.toString());
357
+ });
358
+
359
+ ws.on('close', () => {
360
+ this.activeConnections.delete(ws);
361
+ this.logger.info('WebSocket client disconnected', {
362
+ total: this.activeConnections.size,
363
+ });
364
+ });
365
+
366
+ ws.on('error', (error) => {
367
+ this.logger.error('WebSocket error', { error });
368
+ this.errors++;
369
+ this.activeConnections.delete(ws);
370
+ });
371
+ });
372
+ }
373
+
374
+ /**
375
+ * Handle HTTP request
376
+ */
377
+ private async handleHttpRequest(req: Request, res: Response): Promise<void> {
378
+ this.httpRequests++;
379
+ this.messagesReceived++;
380
+
381
+ // Validate authentication (ALWAYS check, not just when explicitly enabled)
382
+ // SECURITY: Auth should be opt-out, not opt-in
383
+ const requiresAuth = this.config.auth?.enabled !== false; // Default to requiring auth
384
+
385
+ if (requiresAuth && this.config.auth) {
386
+ const authResult = this.validateAuth(req);
387
+ if (!authResult.valid) {
388
+ this.logger.warn('Authentication failed', {
389
+ ip: req.ip,
390
+ path: req.path,
391
+ error: authResult.error,
392
+ });
393
+ res.status(401).json({
394
+ jsonrpc: '2.0',
395
+ id: null,
396
+ error: { code: -32001, message: 'Unauthorized' },
397
+ });
398
+ return;
399
+ }
400
+ } else if (requiresAuth && !this.config.auth) {
401
+ // No auth configured but auth is required - warn and continue (development mode)
402
+ this.logger.warn('No authentication configured - running in development mode');
403
+ }
404
+
405
+ const message = req.body;
406
+
407
+ // Validate JSON-RPC format
408
+ if (message.jsonrpc !== '2.0') {
409
+ res.status(400).json({
410
+ jsonrpc: '2.0',
411
+ id: message.id || null,
412
+ error: { code: -32600, message: 'Invalid JSON-RPC version' },
413
+ });
414
+ return;
415
+ }
416
+
417
+ if (!message.method) {
418
+ res.status(400).json({
419
+ jsonrpc: '2.0',
420
+ id: message.id || null,
421
+ error: { code: -32600, message: 'Missing method' },
422
+ });
423
+ return;
424
+ }
425
+
426
+ // Handle notification vs request
427
+ if (message.id === undefined) {
428
+ // Notification
429
+ if (this.notificationHandler) {
430
+ await this.notificationHandler(message as MCPNotification);
431
+ }
432
+ res.status(204).end();
433
+ } else {
434
+ // Request
435
+ if (!this.requestHandler) {
436
+ res.status(500).json({
437
+ jsonrpc: '2.0',
438
+ id: message.id,
439
+ error: { code: -32603, message: 'No request handler' },
440
+ });
441
+ return;
442
+ }
443
+
444
+ try {
445
+ const response = await this.requestHandler(message as MCPRequest);
446
+ res.json(response);
447
+ this.messagesSent++;
448
+ } catch (error) {
449
+ this.errors++;
450
+ res.status(500).json({
451
+ jsonrpc: '2.0',
452
+ id: message.id,
453
+ error: {
454
+ code: -32603,
455
+ message: error instanceof Error ? error.message : 'Internal error',
456
+ },
457
+ });
458
+ }
459
+ }
460
+ }
461
+
462
+ /**
463
+ * Handle WebSocket message
464
+ */
465
+ private async handleWebSocketMessage(ws: WebSocket, data: string): Promise<void> {
466
+ this.wsMessages++;
467
+ this.messagesReceived++;
468
+
469
+ try {
470
+ const message = JSON.parse(data);
471
+
472
+ if (message.jsonrpc !== '2.0') {
473
+ ws.send(JSON.stringify({
474
+ jsonrpc: '2.0',
475
+ id: message.id || null,
476
+ error: { code: -32600, message: 'Invalid JSON-RPC version' },
477
+ }));
478
+ return;
479
+ }
480
+
481
+ if (message.id === undefined) {
482
+ // Notification
483
+ if (this.notificationHandler) {
484
+ await this.notificationHandler(message as MCPNotification);
485
+ }
486
+ } else {
487
+ // Request
488
+ if (!this.requestHandler) {
489
+ ws.send(JSON.stringify({
490
+ jsonrpc: '2.0',
491
+ id: message.id,
492
+ error: { code: -32603, message: 'No request handler' },
493
+ }));
494
+ return;
495
+ }
496
+
497
+ const response = await this.requestHandler(message as MCPRequest);
498
+ ws.send(JSON.stringify(response));
499
+ this.messagesSent++;
500
+ }
501
+ } catch (error) {
502
+ this.errors++;
503
+ this.logger.error('WebSocket message error', { error });
504
+
505
+ try {
506
+ const parsed = JSON.parse(data);
507
+ ws.send(JSON.stringify({
508
+ jsonrpc: '2.0',
509
+ id: parsed.id || null,
510
+ error: { code: -32700, message: 'Parse error' },
511
+ }));
512
+ } catch {
513
+ ws.send(JSON.stringify({
514
+ jsonrpc: '2.0',
515
+ id: null,
516
+ error: { code: -32700, message: 'Parse error' },
517
+ }));
518
+ }
519
+ }
520
+ }
521
+
522
+ /**
523
+ * Validate authentication
524
+ */
525
+ private validateAuth(req: Request): { valid: boolean; error?: string } {
526
+ const auth = req.headers.authorization;
527
+
528
+ if (!auth) {
529
+ return { valid: false, error: 'Authorization header required' };
530
+ }
531
+
532
+ const tokenMatch = auth.match(/^Bearer\s+(.+)$/i);
533
+ if (!tokenMatch) {
534
+ return { valid: false, error: 'Invalid authorization format' };
535
+ }
536
+
537
+ const token = tokenMatch[1];
538
+
539
+ if (this.config.auth?.tokens?.length) {
540
+ if (!this.config.auth.tokens.includes(token)) {
541
+ return { valid: false, error: 'Invalid token' };
542
+ }
543
+ }
544
+
545
+ return { valid: true };
546
+ }
547
+ }
548
+
549
+ /**
550
+ * Create HTTP transport
551
+ */
552
+ export function createHttpTransport(
553
+ logger: ILogger,
554
+ config: HttpTransportConfig
555
+ ): HttpTransport {
556
+ return new HttpTransport(logger, config);
557
+ }