claude-flow 1.0.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 (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +612 -0
  3. package/bin/claude-flow +0 -0
  4. package/bin/claude-flow-simple +0 -0
  5. package/bin/claude-flow-typecheck +0 -0
  6. package/deno.json +84 -0
  7. package/package.json +45 -0
  8. package/scripts/check-links.ts +274 -0
  9. package/scripts/check-performance-regression.ts +168 -0
  10. package/scripts/claude-sparc.sh +562 -0
  11. package/scripts/coverage-report.ts +692 -0
  12. package/scripts/demo-task-system.ts +224 -0
  13. package/scripts/install.js +72 -0
  14. package/scripts/test-batch-tasks.ts +29 -0
  15. package/scripts/test-coordination-features.ts +238 -0
  16. package/scripts/test-mcp.ts +251 -0
  17. package/scripts/test-runner.ts +571 -0
  18. package/scripts/validate-examples.ts +288 -0
  19. package/src/cli/cli-core.ts +273 -0
  20. package/src/cli/commands/agent.ts +83 -0
  21. package/src/cli/commands/config.ts +442 -0
  22. package/src/cli/commands/help.ts +765 -0
  23. package/src/cli/commands/index.ts +963 -0
  24. package/src/cli/commands/mcp.ts +191 -0
  25. package/src/cli/commands/memory.ts +74 -0
  26. package/src/cli/commands/monitor.ts +403 -0
  27. package/src/cli/commands/session.ts +595 -0
  28. package/src/cli/commands/start.ts +156 -0
  29. package/src/cli/commands/status.ts +345 -0
  30. package/src/cli/commands/task.ts +79 -0
  31. package/src/cli/commands/workflow.ts +763 -0
  32. package/src/cli/completion.ts +553 -0
  33. package/src/cli/formatter.ts +310 -0
  34. package/src/cli/index.ts +211 -0
  35. package/src/cli/main.ts +23 -0
  36. package/src/cli/repl.ts +1050 -0
  37. package/src/cli/simple-cli.js +211 -0
  38. package/src/cli/simple-cli.ts +211 -0
  39. package/src/coordination/README.md +400 -0
  40. package/src/coordination/advanced-scheduler.ts +487 -0
  41. package/src/coordination/circuit-breaker.ts +366 -0
  42. package/src/coordination/conflict-resolution.ts +490 -0
  43. package/src/coordination/dependency-graph.ts +475 -0
  44. package/src/coordination/index.ts +63 -0
  45. package/src/coordination/manager.ts +460 -0
  46. package/src/coordination/messaging.ts +290 -0
  47. package/src/coordination/metrics.ts +585 -0
  48. package/src/coordination/resources.ts +322 -0
  49. package/src/coordination/scheduler.ts +390 -0
  50. package/src/coordination/work-stealing.ts +224 -0
  51. package/src/core/config.ts +627 -0
  52. package/src/core/event-bus.ts +186 -0
  53. package/src/core/json-persistence.ts +183 -0
  54. package/src/core/logger.ts +262 -0
  55. package/src/core/orchestrator-fixed.ts +312 -0
  56. package/src/core/orchestrator.ts +1234 -0
  57. package/src/core/persistence.ts +276 -0
  58. package/src/mcp/auth.ts +438 -0
  59. package/src/mcp/claude-flow-tools.ts +1280 -0
  60. package/src/mcp/load-balancer.ts +510 -0
  61. package/src/mcp/router.ts +240 -0
  62. package/src/mcp/server.ts +548 -0
  63. package/src/mcp/session-manager.ts +418 -0
  64. package/src/mcp/tools.ts +180 -0
  65. package/src/mcp/transports/base.ts +21 -0
  66. package/src/mcp/transports/http.ts +457 -0
  67. package/src/mcp/transports/stdio.ts +254 -0
  68. package/src/memory/backends/base.ts +22 -0
  69. package/src/memory/backends/markdown.ts +283 -0
  70. package/src/memory/backends/sqlite.ts +329 -0
  71. package/src/memory/cache.ts +238 -0
  72. package/src/memory/indexer.ts +238 -0
  73. package/src/memory/manager.ts +572 -0
  74. package/src/terminal/adapters/base.ts +29 -0
  75. package/src/terminal/adapters/native.ts +504 -0
  76. package/src/terminal/adapters/vscode.ts +340 -0
  77. package/src/terminal/manager.ts +308 -0
  78. package/src/terminal/pool.ts +271 -0
  79. package/src/terminal/session.ts +250 -0
  80. package/src/terminal/vscode-bridge.ts +242 -0
  81. package/src/utils/errors.ts +231 -0
  82. package/src/utils/helpers.ts +476 -0
  83. package/src/utils/types.ts +493 -0
@@ -0,0 +1,510 @@
1
+ /**
2
+ * Load balancer and rate limiting for MCP
3
+ */
4
+
5
+ import { MCPLoadBalancerConfig, MCPRequest, MCPResponse, MCPSession } from '../utils/types.ts';
6
+ import { ILogger } from '../core/logger.ts';
7
+ import { MCPError } from '../utils/errors.ts';
8
+
9
+ export interface RequestMetrics {
10
+ requestId: string;
11
+ sessionId: string;
12
+ method: string;
13
+ startTime: number;
14
+ endTime?: number;
15
+ success?: boolean;
16
+ error?: string;
17
+ }
18
+
19
+ export interface LoadBalancerMetrics {
20
+ totalRequests: number;
21
+ successfulRequests: number;
22
+ failedRequests: number;
23
+ rateLimitedRequests: number;
24
+ averageResponseTime: number;
25
+ requestsPerSecond: number;
26
+ circuitBreakerTrips: number;
27
+ lastReset: Date;
28
+ }
29
+
30
+ export interface ILoadBalancer {
31
+ shouldAllowRequest(session: MCPSession, request: MCPRequest): Promise<boolean>;
32
+ recordRequestStart(session: MCPSession, request: MCPRequest): RequestMetrics;
33
+ recordRequestEnd(metrics: RequestMetrics, response?: MCPResponse, error?: Error): void;
34
+ getMetrics(): LoadBalancerMetrics;
35
+ resetMetrics(): void;
36
+ isCircuitBreakerOpen(): boolean;
37
+ }
38
+
39
+ /**
40
+ * Circuit breaker state
41
+ */
42
+ enum CircuitBreakerState {
43
+ CLOSED = 'closed',
44
+ OPEN = 'open',
45
+ HALF_OPEN = 'half_open',
46
+ }
47
+
48
+ /**
49
+ * Rate limiter using token bucket algorithm
50
+ */
51
+ class RateLimiter {
52
+ private tokens: number;
53
+ private lastRefill: number;
54
+
55
+ constructor(
56
+ private maxTokens: number,
57
+ private refillRate: number, // tokens per second
58
+ ) {
59
+ this.tokens = maxTokens;
60
+ this.lastRefill = Date.now();
61
+ }
62
+
63
+ tryConsume(tokens = 1): boolean {
64
+ this.refill();
65
+
66
+ if (this.tokens >= tokens) {
67
+ this.tokens -= tokens;
68
+ return true;
69
+ }
70
+
71
+ return false;
72
+ }
73
+
74
+ private refill(): void {
75
+ const now = Date.now();
76
+ const timePassed = (now - this.lastRefill) / 1000;
77
+ const tokensToAdd = Math.floor(timePassed * this.refillRate);
78
+
79
+ if (tokensToAdd > 0) {
80
+ this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd);
81
+ this.lastRefill = now;
82
+ }
83
+ }
84
+
85
+ getTokens(): number {
86
+ this.refill();
87
+ return this.tokens;
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Circuit breaker implementation
93
+ */
94
+ class CircuitBreaker {
95
+ private state = CircuitBreakerState.CLOSED;
96
+ private failureCount = 0;
97
+ private lastFailureTime = 0;
98
+ private successCount = 0;
99
+
100
+ constructor(
101
+ private failureThreshold: number,
102
+ private recoveryTimeout: number, // milliseconds
103
+ private halfOpenMaxRequests = 3,
104
+ ) {}
105
+
106
+ canExecute(): boolean {
107
+ const now = Date.now();
108
+
109
+ switch (this.state) {
110
+ case CircuitBreakerState.CLOSED:
111
+ return true;
112
+
113
+ case CircuitBreakerState.OPEN:
114
+ if (now - this.lastFailureTime >= this.recoveryTimeout) {
115
+ this.state = CircuitBreakerState.HALF_OPEN;
116
+ this.successCount = 0;
117
+ return true;
118
+ }
119
+ return false;
120
+
121
+ case CircuitBreakerState.HALF_OPEN:
122
+ return this.successCount < this.halfOpenMaxRequests;
123
+
124
+ default:
125
+ return false;
126
+ }
127
+ }
128
+
129
+ recordSuccess(): void {
130
+ if (this.state === CircuitBreakerState.HALF_OPEN) {
131
+ this.successCount++;
132
+ if (this.successCount >= this.halfOpenMaxRequests) {
133
+ this.state = CircuitBreakerState.CLOSED;
134
+ this.failureCount = 0;
135
+ }
136
+ } else if (this.state === CircuitBreakerState.CLOSED) {
137
+ this.failureCount = 0;
138
+ }
139
+ }
140
+
141
+ recordFailure(): void {
142
+ this.failureCount++;
143
+ this.lastFailureTime = Date.now();
144
+
145
+ if (this.state === CircuitBreakerState.HALF_OPEN) {
146
+ this.state = CircuitBreakerState.OPEN;
147
+ } else if (this.state === CircuitBreakerState.CLOSED && this.failureCount >= this.failureThreshold) {
148
+ this.state = CircuitBreakerState.OPEN;
149
+ }
150
+ }
151
+
152
+ getState(): CircuitBreakerState {
153
+ return this.state;
154
+ }
155
+
156
+ getMetrics(): { state: string; failureCount: number; successCount: number } {
157
+ return {
158
+ state: this.state,
159
+ failureCount: this.failureCount,
160
+ successCount: this.successCount,
161
+ };
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Load balancer implementation
167
+ */
168
+ export class LoadBalancer implements ILoadBalancer {
169
+ private rateLimiter: RateLimiter;
170
+ private circuitBreaker: CircuitBreaker;
171
+ private sessionRateLimiters = new Map<string, RateLimiter>();
172
+ private metrics: LoadBalancerMetrics;
173
+ private requestTimes: number[] = [];
174
+ private requestsInLastSecond = 0;
175
+ private lastSecondTimestamp = 0;
176
+
177
+ constructor(
178
+ private config: MCPLoadBalancerConfig,
179
+ private logger: ILogger,
180
+ ) {
181
+ this.rateLimiter = new RateLimiter(
182
+ config.maxRequestsPerSecond,
183
+ config.maxRequestsPerSecond,
184
+ );
185
+
186
+ this.circuitBreaker = new CircuitBreaker(
187
+ config.circuitBreakerThreshold,
188
+ 30000, // 30 second recovery timeout
189
+ );
190
+
191
+ this.metrics = {
192
+ totalRequests: 0,
193
+ successfulRequests: 0,
194
+ failedRequests: 0,
195
+ rateLimitedRequests: 0,
196
+ averageResponseTime: 0,
197
+ requestsPerSecond: 0,
198
+ circuitBreakerTrips: 0,
199
+ lastReset: new Date(),
200
+ };
201
+
202
+ // Clean up old session rate limiters periodically
203
+ setInterval(() => {
204
+ this.cleanupSessionRateLimiters();
205
+ }, 300000); // Every 5 minutes
206
+ }
207
+
208
+ async shouldAllowRequest(session: MCPSession, request: MCPRequest): Promise<boolean> {
209
+ if (!this.config.enabled) {
210
+ return true;
211
+ }
212
+
213
+ // Check circuit breaker
214
+ if (!this.circuitBreaker.canExecute()) {
215
+ this.logger.warn('Request rejected by circuit breaker', {
216
+ sessionId: session.id,
217
+ method: request.method,
218
+ circuitState: this.circuitBreaker.getState(),
219
+ });
220
+ this.metrics.circuitBreakerTrips++;
221
+ return false;
222
+ }
223
+
224
+ // Check global rate limit
225
+ if (!this.rateLimiter.tryConsume()) {
226
+ this.logger.warn('Request rejected by global rate limiter', {
227
+ sessionId: session.id,
228
+ method: request.method,
229
+ remainingTokens: this.rateLimiter.getTokens(),
230
+ });
231
+ this.metrics.rateLimitedRequests++;
232
+ return false;
233
+ }
234
+
235
+ // Check per-session rate limit
236
+ const sessionRateLimiter = this.getSessionRateLimiter(session.id);
237
+ if (!sessionRateLimiter.tryConsume()) {
238
+ this.logger.warn('Request rejected by session rate limiter', {
239
+ sessionId: session.id,
240
+ method: request.method,
241
+ remainingTokens: sessionRateLimiter.getTokens(),
242
+ });
243
+ this.metrics.rateLimitedRequests++;
244
+ return false;
245
+ }
246
+
247
+ return true;
248
+ }
249
+
250
+ recordRequestStart(session: MCPSession, request: MCPRequest): RequestMetrics {
251
+ const requestMetrics: RequestMetrics = {
252
+ requestId: request.id.toString(),
253
+ sessionId: session.id,
254
+ method: request.method,
255
+ startTime: Date.now(),
256
+ };
257
+
258
+ this.metrics.totalRequests++;
259
+ this.updateRequestsPerSecond();
260
+
261
+ this.logger.debug('Request started', {
262
+ requestId: requestMetrics.requestId,
263
+ sessionId: session.id,
264
+ method: request.method,
265
+ });
266
+
267
+ return requestMetrics;
268
+ }
269
+
270
+ recordRequestEnd(metrics: RequestMetrics, response?: MCPResponse, error?: Error): void {
271
+ metrics.endTime = Date.now();
272
+ const duration = metrics.endTime - metrics.startTime;
273
+
274
+ // Update response time tracking
275
+ this.requestTimes.push(duration);
276
+ if (this.requestTimes.length > 1000) {
277
+ this.requestTimes.shift(); // Keep only last 1000 requests
278
+ }
279
+
280
+ const success = !error && (!response || !response.error);
281
+ metrics.success = success;
282
+ const errorMessage = error?.message || response?.error?.message;
283
+ if (errorMessage) {
284
+ metrics.error = errorMessage;
285
+ }
286
+
287
+ if (success) {
288
+ this.metrics.successfulRequests++;
289
+ this.circuitBreaker.recordSuccess();
290
+ } else {
291
+ this.metrics.failedRequests++;
292
+ this.circuitBreaker.recordFailure();
293
+ }
294
+
295
+ // Update average response time
296
+ this.metrics.averageResponseTime = this.calculateAverageResponseTime();
297
+
298
+ this.logger.debug('Request completed', {
299
+ requestId: metrics.requestId,
300
+ sessionId: metrics.sessionId,
301
+ method: metrics.method,
302
+ duration,
303
+ success,
304
+ error: metrics.error,
305
+ });
306
+ }
307
+
308
+ getMetrics(): LoadBalancerMetrics {
309
+ return { ...this.metrics };
310
+ }
311
+
312
+ resetMetrics(): void {
313
+ this.metrics = {
314
+ totalRequests: 0,
315
+ successfulRequests: 0,
316
+ failedRequests: 0,
317
+ rateLimitedRequests: 0,
318
+ averageResponseTime: 0,
319
+ requestsPerSecond: 0,
320
+ circuitBreakerTrips: 0,
321
+ lastReset: new Date(),
322
+ };
323
+ this.requestTimes = [];
324
+
325
+ this.logger.info('Load balancer metrics reset');
326
+ }
327
+
328
+ isCircuitBreakerOpen(): boolean {
329
+ return this.circuitBreaker.getState() === CircuitBreakerState.OPEN;
330
+ }
331
+
332
+ getDetailedMetrics(): {
333
+ loadBalancer: LoadBalancerMetrics;
334
+ circuitBreaker: { state: string; failureCount: number; successCount: number };
335
+ rateLimiter: { tokens: number; maxTokens: number };
336
+ sessions: number;
337
+ } {
338
+ return {
339
+ loadBalancer: this.getMetrics(),
340
+ circuitBreaker: this.circuitBreaker.getMetrics(),
341
+ rateLimiter: {
342
+ tokens: this.rateLimiter.getTokens(),
343
+ maxTokens: this.config.maxRequestsPerSecond,
344
+ },
345
+ sessions: this.sessionRateLimiters.size,
346
+ };
347
+ }
348
+
349
+ private getSessionRateLimiter(sessionId: string): RateLimiter {
350
+ let rateLimiter = this.sessionRateLimiters.get(sessionId);
351
+
352
+ if (!rateLimiter) {
353
+ // Create a per-session rate limiter (more restrictive than global)
354
+ const sessionLimit = Math.max(1, Math.floor(this.config.maxRequestsPerSecond / 10));
355
+ rateLimiter = new RateLimiter(sessionLimit, sessionLimit);
356
+ this.sessionRateLimiters.set(sessionId, rateLimiter);
357
+ }
358
+
359
+ return rateLimiter;
360
+ }
361
+
362
+ private calculateAverageResponseTime(): number {
363
+ if (this.requestTimes.length === 0) {
364
+ return 0;
365
+ }
366
+
367
+ const sum = this.requestTimes.reduce((acc, time) => acc + time, 0);
368
+ return sum / this.requestTimes.length;
369
+ }
370
+
371
+ private updateRequestsPerSecond(): void {
372
+ const now = Math.floor(Date.now() / 1000);
373
+
374
+ if (now !== this.lastSecondTimestamp) {
375
+ this.metrics.requestsPerSecond = this.requestsInLastSecond;
376
+ this.requestsInLastSecond = 1;
377
+ this.lastSecondTimestamp = now;
378
+ } else {
379
+ this.requestsInLastSecond++;
380
+ }
381
+ }
382
+
383
+ private cleanupSessionRateLimiters(): void {
384
+ // Remove rate limiters for sessions that haven't been used recently
385
+ const cutoffTime = Date.now() - 300000; // 5 minutes ago
386
+ let cleaned = 0;
387
+
388
+ for (const [sessionId, rateLimiter] of this.sessionRateLimiters.entries()) {
389
+ // If the rate limiter has full tokens, it hasn't been used recently
390
+ if (rateLimiter.getTokens() === this.config.maxRequestsPerSecond) {
391
+ this.sessionRateLimiters.delete(sessionId);
392
+ cleaned++;
393
+ }
394
+ }
395
+
396
+ if (cleaned > 0) {
397
+ this.logger.debug('Cleaned up session rate limiters', { count: cleaned });
398
+ }
399
+ }
400
+ }
401
+
402
+ /**
403
+ * Request queue for handling backpressure
404
+ */
405
+ export class RequestQueue {
406
+ private queue: Array<{
407
+ session: MCPSession;
408
+ request: MCPRequest;
409
+ resolve: (result: any) => void;
410
+ reject: (error: Error) => void;
411
+ timestamp: number;
412
+ }> = [];
413
+
414
+ private processing = false;
415
+ private maxQueueSize: number;
416
+ private requestTimeout: number;
417
+
418
+ constructor(
419
+ maxQueueSize = 1000,
420
+ requestTimeout = 30000, // 30 seconds
421
+ private logger: ILogger,
422
+ ) {
423
+ this.maxQueueSize = maxQueueSize;
424
+ this.requestTimeout = requestTimeout;
425
+
426
+ // Clean up expired requests periodically
427
+ setInterval(() => {
428
+ this.cleanupExpiredRequests();
429
+ }, 10000); // Every 10 seconds
430
+ }
431
+
432
+ async enqueue<T>(
433
+ session: MCPSession,
434
+ request: MCPRequest,
435
+ processor: (session: MCPSession, request: MCPRequest) => Promise<T>,
436
+ ): Promise<T> {
437
+ if (this.queue.length >= this.maxQueueSize) {
438
+ throw new MCPError('Request queue is full');
439
+ }
440
+
441
+ return new Promise<T>((resolve, reject) => {
442
+ this.queue.push({
443
+ session,
444
+ request,
445
+ resolve,
446
+ reject,
447
+ timestamp: Date.now(),
448
+ });
449
+
450
+ if (!this.processing) {
451
+ this.processQueue(processor);
452
+ }
453
+ });
454
+ }
455
+
456
+ private async processQueue<T>(
457
+ processor: (session: MCPSession, request: MCPRequest) => Promise<T>,
458
+ ): Promise<void> {
459
+ if (this.processing) {
460
+ return;
461
+ }
462
+
463
+ this.processing = true;
464
+
465
+ while (this.queue.length > 0) {
466
+ const item = this.queue.shift()!;
467
+
468
+ // Check if request has expired
469
+ if (Date.now() - item.timestamp > this.requestTimeout) {
470
+ item.reject(new MCPError('Request timeout'));
471
+ continue;
472
+ }
473
+
474
+ try {
475
+ const result = await processor(item.session, item.request);
476
+ item.resolve(result);
477
+ } catch (error) {
478
+ item.reject(error instanceof Error ? error : new Error(String(error)));
479
+ }
480
+ }
481
+
482
+ this.processing = false;
483
+ }
484
+
485
+ private cleanupExpiredRequests(): void {
486
+ const now = Date.now();
487
+ let cleaned = 0;
488
+
489
+ this.queue = this.queue.filter((item) => {
490
+ if (now - item.timestamp > this.requestTimeout) {
491
+ item.reject(new MCPError('Request timeout'));
492
+ cleaned++;
493
+ return false;
494
+ }
495
+ return true;
496
+ });
497
+
498
+ if (cleaned > 0) {
499
+ this.logger.warn('Cleaned up expired requests from queue', { count: cleaned });
500
+ }
501
+ }
502
+
503
+ getQueueSize(): number {
504
+ return this.queue.length;
505
+ }
506
+
507
+ isProcessing(): boolean {
508
+ return this.processing;
509
+ }
510
+ }