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,276 @@
1
+ /**
2
+ * Persistence layer for Claude-Flow using SQLite
3
+ */
4
+
5
+ import { DB } from "https://deno.land/x/sqlite@v3.8/mod.ts";
6
+ import { join } from "https://deno.land/std@0.224.0/path/mod.ts";
7
+ import { ensureDir } from "https://deno.land/std@0.224.0/fs/mod.ts";
8
+
9
+ export interface PersistedAgent {
10
+ id: string;
11
+ type: string;
12
+ name: string;
13
+ status: string;
14
+ capabilities: string;
15
+ systemPrompt: string;
16
+ maxConcurrentTasks: number;
17
+ priority: number;
18
+ createdAt: number;
19
+ }
20
+
21
+ export interface PersistedTask {
22
+ id: string;
23
+ type: string;
24
+ description: string;
25
+ status: string;
26
+ priority: number;
27
+ dependencies: string;
28
+ metadata: string;
29
+ assignedAgent?: string;
30
+ progress: number;
31
+ error?: string;
32
+ createdAt: number;
33
+ completedAt?: number;
34
+ }
35
+
36
+ export class PersistenceManager {
37
+ private db: DB;
38
+ private dbPath: string;
39
+
40
+ constructor(dataDir: string = "./memory") {
41
+ this.dbPath = join(dataDir, "claude-flow.db");
42
+ }
43
+
44
+ async initialize(): Promise<void> {
45
+ // Ensure directory exists
46
+ await ensureDir(join(this.dbPath, ".."));
47
+
48
+ // Open database
49
+ this.db = new DB(this.dbPath);
50
+
51
+ // Create tables if they don't exist
52
+ this.createTables();
53
+ }
54
+
55
+ private createTables(): void {
56
+ // Agents table
57
+ this.db.execute(`
58
+ CREATE TABLE IF NOT EXISTS agents (
59
+ id TEXT PRIMARY KEY,
60
+ type TEXT NOT NULL,
61
+ name TEXT NOT NULL,
62
+ status TEXT NOT NULL,
63
+ capabilities TEXT NOT NULL,
64
+ system_prompt TEXT NOT NULL,
65
+ max_concurrent_tasks INTEGER NOT NULL,
66
+ priority INTEGER NOT NULL,
67
+ created_at INTEGER NOT NULL
68
+ )
69
+ `);
70
+
71
+ // Tasks table
72
+ this.db.execute(`
73
+ CREATE TABLE IF NOT EXISTS tasks (
74
+ id TEXT PRIMARY KEY,
75
+ type TEXT NOT NULL,
76
+ description TEXT NOT NULL,
77
+ status TEXT NOT NULL,
78
+ priority INTEGER NOT NULL,
79
+ dependencies TEXT NOT NULL,
80
+ metadata TEXT NOT NULL,
81
+ assigned_agent TEXT,
82
+ progress INTEGER DEFAULT 0,
83
+ error TEXT,
84
+ created_at INTEGER NOT NULL,
85
+ completed_at INTEGER
86
+ )
87
+ `);
88
+
89
+ // Sessions table for terminal sessions
90
+ this.db.execute(`
91
+ CREATE TABLE IF NOT EXISTS sessions (
92
+ id TEXT PRIMARY KEY,
93
+ agent_id TEXT NOT NULL,
94
+ terminal_id TEXT NOT NULL,
95
+ status TEXT NOT NULL,
96
+ created_at INTEGER NOT NULL,
97
+ FOREIGN KEY (agent_id) REFERENCES agents(id)
98
+ )
99
+ `);
100
+ }
101
+
102
+ // Agent operations
103
+ async saveAgent(agent: PersistedAgent): Promise<void> {
104
+ this.db.query(
105
+ `INSERT OR REPLACE INTO agents
106
+ (id, type, name, status, capabilities, system_prompt, max_concurrent_tasks, priority, created_at)
107
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
108
+ [
109
+ agent.id,
110
+ agent.type,
111
+ agent.name,
112
+ agent.status,
113
+ agent.capabilities,
114
+ agent.systemPrompt,
115
+ agent.maxConcurrentTasks,
116
+ agent.priority,
117
+ agent.createdAt,
118
+ ]
119
+ );
120
+ }
121
+
122
+ async getAgent(id: string): Promise<PersistedAgent | null> {
123
+ const rows = this.db.query<[string, string, string, string, string, string, number, number, number]>(
124
+ "SELECT * FROM agents WHERE id = ?",
125
+ [id]
126
+ );
127
+
128
+ if (rows.length === 0) return null;
129
+
130
+ const [id_, type, name, status, capabilities, systemPrompt, maxConcurrentTasks, priority, createdAt] = rows[0];
131
+ return {
132
+ id: id_,
133
+ type,
134
+ name,
135
+ status,
136
+ capabilities,
137
+ systemPrompt,
138
+ maxConcurrentTasks,
139
+ priority,
140
+ createdAt,
141
+ };
142
+ }
143
+
144
+ async getActiveAgents(): Promise<PersistedAgent[]> {
145
+ const rows = this.db.query<[string, string, string, string, string, string, number, number, number]>(
146
+ "SELECT * FROM agents WHERE status IN ('active', 'idle') ORDER BY created_at DESC"
147
+ );
148
+
149
+ return rows.map(([id, type, name, status, capabilities, systemPrompt, maxConcurrentTasks, priority, createdAt]) => ({
150
+ id,
151
+ type,
152
+ name,
153
+ status,
154
+ capabilities,
155
+ systemPrompt,
156
+ maxConcurrentTasks,
157
+ priority,
158
+ createdAt,
159
+ }));
160
+ }
161
+
162
+ async updateAgentStatus(id: string, status: string): Promise<void> {
163
+ this.db.query("UPDATE agents SET status = ? WHERE id = ?", [status, id]);
164
+ }
165
+
166
+ // Task operations
167
+ async saveTask(task: PersistedTask): Promise<void> {
168
+ this.db.query(
169
+ `INSERT OR REPLACE INTO tasks
170
+ (id, type, description, status, priority, dependencies, metadata, assigned_agent, progress, error, created_at, completed_at)
171
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
172
+ [
173
+ task.id,
174
+ task.type,
175
+ task.description,
176
+ task.status,
177
+ task.priority,
178
+ task.dependencies,
179
+ task.metadata,
180
+ task.assignedAgent || null,
181
+ task.progress,
182
+ task.error || null,
183
+ task.createdAt,
184
+ task.completedAt || null,
185
+ ]
186
+ );
187
+ }
188
+
189
+ async getTask(id: string): Promise<PersistedTask | null> {
190
+ const rows = this.db.query<[string, string, string, string, number, string, string, string | null, number, string | null, number, number | null]>(
191
+ "SELECT * FROM tasks WHERE id = ?",
192
+ [id]
193
+ );
194
+
195
+ if (rows.length === 0) return null;
196
+
197
+ const [id_, type, description, status, priority, dependencies, metadata, assignedAgent, progress, error, createdAt, completedAt] = rows[0];
198
+ return {
199
+ id: id_,
200
+ type,
201
+ description,
202
+ status,
203
+ priority,
204
+ dependencies,
205
+ metadata,
206
+ assignedAgent: assignedAgent || undefined,
207
+ progress,
208
+ error: error || undefined,
209
+ createdAt,
210
+ completedAt: completedAt || undefined,
211
+ };
212
+ }
213
+
214
+ async getActiveTasks(): Promise<PersistedTask[]> {
215
+ const rows = this.db.query<[string, string, string, string, number, string, string, string | null, number, string | null, number, number | null]>(
216
+ "SELECT * FROM tasks WHERE status IN ('pending', 'in_progress', 'assigned') ORDER BY priority DESC, created_at ASC"
217
+ );
218
+
219
+ return rows.map(([id, type, description, status, priority, dependencies, metadata, assignedAgent, progress, error, createdAt, completedAt]) => ({
220
+ id,
221
+ type,
222
+ description,
223
+ status,
224
+ priority,
225
+ dependencies,
226
+ metadata,
227
+ assignedAgent: assignedAgent || undefined,
228
+ progress,
229
+ error: error || undefined,
230
+ createdAt,
231
+ completedAt: completedAt || undefined,
232
+ }));
233
+ }
234
+
235
+ async updateTaskStatus(id: string, status: string, assignedAgent?: string): Promise<void> {
236
+ if (assignedAgent) {
237
+ this.db.query(
238
+ "UPDATE tasks SET status = ?, assigned_agent = ? WHERE id = ?",
239
+ [status, assignedAgent, id]
240
+ );
241
+ } else {
242
+ this.db.query("UPDATE tasks SET status = ? WHERE id = ?", [status, id]);
243
+ }
244
+ }
245
+
246
+ async updateTaskProgress(id: string, progress: number): Promise<void> {
247
+ this.db.query("UPDATE tasks SET progress = ? WHERE id = ?", [progress, id]);
248
+ }
249
+
250
+ // Statistics
251
+ async getStats(): Promise<{
252
+ totalAgents: number;
253
+ activeAgents: number;
254
+ totalTasks: number;
255
+ pendingTasks: number;
256
+ completedTasks: number;
257
+ }> {
258
+ const [totalAgents] = this.db.query<[number]>("SELECT COUNT(*) FROM agents")[0];
259
+ const [activeAgents] = this.db.query<[number]>("SELECT COUNT(*) FROM agents WHERE status IN ('active', 'idle')")[0];
260
+ const [totalTasks] = this.db.query<[number]>("SELECT COUNT(*) FROM tasks")[0];
261
+ const [pendingTasks] = this.db.query<[number]>("SELECT COUNT(*) FROM tasks WHERE status IN ('pending', 'in_progress', 'assigned')")[0];
262
+ const [completedTasks] = this.db.query<[number]>("SELECT COUNT(*) FROM tasks WHERE status = 'completed'")[0];
263
+
264
+ return {
265
+ totalAgents,
266
+ activeAgents,
267
+ totalTasks,
268
+ pendingTasks,
269
+ completedTasks,
270
+ };
271
+ }
272
+
273
+ close(): void {
274
+ this.db.close();
275
+ }
276
+ }
@@ -0,0 +1,438 @@
1
+ /**
2
+ * Authentication and authorization for MCP
3
+ */
4
+
5
+ import { MCPAuthConfig, MCPSession } from '../utils/types.ts';
6
+ import { ILogger } from '../core/logger.ts';
7
+ import { MCPError } from '../utils/errors.ts';
8
+ import { createHash, timingSafeEqual } from 'node:crypto';
9
+
10
+ export interface IAuthManager {
11
+ authenticate(credentials: unknown): Promise<AuthResult>;
12
+ authorize(session: MCPSession, permission: string): boolean;
13
+ validateToken(token: string): Promise<TokenValidation>;
14
+ generateToken(userId: string, permissions: string[]): Promise<string>;
15
+ revokeToken(token: string): Promise<void>;
16
+ }
17
+
18
+ export interface AuthResult {
19
+ success: boolean;
20
+ user?: string;
21
+ permissions?: string[];
22
+ token?: string;
23
+ error?: string;
24
+ }
25
+
26
+ export interface TokenValidation {
27
+ valid: boolean;
28
+ user?: string;
29
+ permissions?: string[];
30
+ expiresAt?: Date;
31
+ error?: string;
32
+ }
33
+
34
+ /**
35
+ * Authentication manager implementation
36
+ */
37
+ export class AuthManager implements IAuthManager {
38
+ private revokedTokens = new Set<string>();
39
+ private tokenStore = new Map<string, {
40
+ user: string;
41
+ permissions: string[];
42
+ createdAt: Date;
43
+ expiresAt: Date;
44
+ }>();
45
+
46
+ constructor(
47
+ private config: MCPAuthConfig,
48
+ private logger: ILogger,
49
+ ) {
50
+ // Start token cleanup timer
51
+ if (config.enabled) {
52
+ setInterval(() => {
53
+ this.cleanupExpiredTokens();
54
+ }, 300000); // Clean up every 5 minutes
55
+ }
56
+ }
57
+
58
+ async authenticate(credentials: unknown): Promise<AuthResult> {
59
+ if (!this.config.enabled) {
60
+ return {
61
+ success: true,
62
+ user: 'anonymous',
63
+ permissions: ['*'],
64
+ };
65
+ }
66
+
67
+ this.logger.debug('Authenticating credentials', {
68
+ method: this.config.method,
69
+ hasCredentials: !!credentials,
70
+ });
71
+
72
+ try {
73
+ switch (this.config.method) {
74
+ case 'token':
75
+ return await this.authenticateToken(credentials);
76
+ case 'basic':
77
+ return await this.authenticateBasic(credentials);
78
+ case 'oauth':
79
+ return await this.authenticateOAuth(credentials);
80
+ default:
81
+ return {
82
+ success: false,
83
+ error: `Unsupported authentication method: ${this.config.method}`,
84
+ };
85
+ }
86
+ } catch (error) {
87
+ this.logger.error('Authentication error', error);
88
+ return {
89
+ success: false,
90
+ error: error instanceof Error ? error.message : 'Authentication failed',
91
+ };
92
+ }
93
+ }
94
+
95
+ authorize(session: MCPSession, permission: string): boolean {
96
+ if (!this.config.enabled || !session.authenticated) {
97
+ return !this.config.enabled; // If auth disabled, allow all
98
+ }
99
+
100
+ const permissions = session.authData?.permissions || [];
101
+
102
+ // Check for wildcard permission
103
+ if (permissions.includes('*')) {
104
+ return true;
105
+ }
106
+
107
+ // Check for exact permission match
108
+ if (permissions.includes(permission)) {
109
+ return true;
110
+ }
111
+
112
+ // Check for prefix-based permissions (e.g., "tools.*" matches "tools.list")
113
+ for (const perm of permissions) {
114
+ if (perm.endsWith('*') && permission.startsWith(perm.slice(0, -1))) {
115
+ return true;
116
+ }
117
+ }
118
+
119
+ this.logger.warn('Authorization denied', {
120
+ sessionId: session.id,
121
+ user: session.authData?.user,
122
+ permission,
123
+ userPermissions: permissions,
124
+ });
125
+
126
+ return false;
127
+ }
128
+
129
+ async validateToken(token: string): Promise<TokenValidation> {
130
+ if (this.revokedTokens.has(token)) {
131
+ return {
132
+ valid: false,
133
+ error: 'Token has been revoked',
134
+ };
135
+ }
136
+
137
+ const tokenData = this.tokenStore.get(token);
138
+ if (!tokenData) {
139
+ return {
140
+ valid: false,
141
+ error: 'Invalid token',
142
+ };
143
+ }
144
+
145
+ if (tokenData.expiresAt < new Date()) {
146
+ this.tokenStore.delete(token);
147
+ return {
148
+ valid: false,
149
+ error: 'Token has expired',
150
+ };
151
+ }
152
+
153
+ return {
154
+ valid: true,
155
+ user: tokenData.user,
156
+ permissions: tokenData.permissions,
157
+ expiresAt: tokenData.expiresAt,
158
+ };
159
+ }
160
+
161
+ async generateToken(userId: string, permissions: string[]): Promise<string> {
162
+ const token = this.createSecureToken();
163
+ const now = new Date();
164
+ const expiresAt = new Date(now.getTime() + (this.config.sessionTimeout || 3600000));
165
+
166
+ this.tokenStore.set(token, {
167
+ user: userId,
168
+ permissions,
169
+ createdAt: now,
170
+ expiresAt,
171
+ });
172
+
173
+ this.logger.info('Token generated', {
174
+ userId,
175
+ permissions,
176
+ expiresAt,
177
+ });
178
+
179
+ return token;
180
+ }
181
+
182
+ async revokeToken(token: string): Promise<void> {
183
+ this.revokedTokens.add(token);
184
+ this.tokenStore.delete(token);
185
+ this.logger.info('Token revoked', { token: token.substring(0, 8) + '...' });
186
+ }
187
+
188
+ private async authenticateToken(credentials: unknown): Promise<AuthResult> {
189
+ const token = this.extractToken(credentials);
190
+ if (!token) {
191
+ return {
192
+ success: false,
193
+ error: 'Token not provided',
194
+ };
195
+ }
196
+
197
+ // Check if it's a stored token (generated by us)
198
+ const validation = await this.validateToken(token);
199
+ if (validation.valid) {
200
+ return {
201
+ success: true,
202
+ user: validation.user!,
203
+ permissions: validation.permissions!,
204
+ token,
205
+ };
206
+ }
207
+
208
+ // Check against configured static tokens
209
+ if (this.config.tokens && this.config.tokens.length > 0) {
210
+ const isValid = this.config.tokens.some((validToken) => {
211
+ return this.timingSafeEqual(token, validToken);
212
+ });
213
+
214
+ if (isValid) {
215
+ return {
216
+ success: true,
217
+ user: 'token-user',
218
+ permissions: ['*'], // Static tokens get all permissions
219
+ token,
220
+ };
221
+ }
222
+ }
223
+
224
+ return {
225
+ success: false,
226
+ error: 'Invalid token',
227
+ };
228
+ }
229
+
230
+ private async authenticateBasic(credentials: unknown): Promise<AuthResult> {
231
+ const { username, password } = this.extractBasicAuth(credentials);
232
+ if (!username || !password) {
233
+ return {
234
+ success: false,
235
+ error: 'Username and password required',
236
+ };
237
+ }
238
+
239
+ if (!this.config.users || this.config.users.length === 0) {
240
+ return {
241
+ success: false,
242
+ error: 'No users configured',
243
+ };
244
+ }
245
+
246
+ const user = this.config.users.find((u) => u.username === username);
247
+ if (!user) {
248
+ return {
249
+ success: false,
250
+ error: 'Invalid username or password',
251
+ };
252
+ }
253
+
254
+ // Verify password
255
+ const isValidPassword = this.verifyPassword(password, user.password);
256
+ if (!isValidPassword) {
257
+ return {
258
+ success: false,
259
+ error: 'Invalid username or password',
260
+ };
261
+ }
262
+
263
+ // Generate a session token
264
+ const token = await this.generateToken(username, user.permissions);
265
+
266
+ return {
267
+ success: true,
268
+ user: username,
269
+ permissions: user.permissions,
270
+ token,
271
+ };
272
+ }
273
+
274
+ private async authenticateOAuth(credentials: unknown): Promise<AuthResult> {
275
+ // TODO: Implement OAuth authentication
276
+ // This would typically involve:
277
+ // 1. Validating JWT tokens
278
+ // 2. Checking token expiration
279
+ // 3. Extracting user info and permissions from token claims
280
+
281
+ this.logger.warn('OAuth authentication not yet implemented');
282
+ return {
283
+ success: false,
284
+ error: 'OAuth authentication not implemented',
285
+ };
286
+ }
287
+
288
+ private extractToken(credentials: unknown): string | null {
289
+ if (typeof credentials === 'string') {
290
+ return credentials;
291
+ }
292
+
293
+ if (typeof credentials === 'object' && credentials !== null) {
294
+ const creds = credentials as Record<string, unknown>;
295
+
296
+ if (typeof creds.token === 'string') {
297
+ return creds.token;
298
+ }
299
+
300
+ if (typeof creds.authorization === 'string') {
301
+ const match = creds.authorization.match(/^Bearer\s+(.+)$/i);
302
+ return match ? match[1] : null;
303
+ }
304
+ }
305
+
306
+ return null;
307
+ }
308
+
309
+ private extractBasicAuth(credentials: unknown): { username?: string; password?: string } {
310
+ if (typeof credentials === 'object' && credentials !== null) {
311
+ const creds = credentials as Record<string, unknown>;
312
+
313
+ if (typeof creds.username === 'string' && typeof creds.password === 'string') {
314
+ return {
315
+ username: creds.username,
316
+ password: creds.password,
317
+ };
318
+ }
319
+
320
+ if (typeof creds.authorization === 'string') {
321
+ const match = creds.authorization.match(/^Basic\s+(.+)$/i);
322
+ if (match) {
323
+ try {
324
+ const decoded = atob(match[1]);
325
+ const colonIndex = decoded.indexOf(':');
326
+ if (colonIndex >= 0) {
327
+ return {
328
+ username: decoded.substring(0, colonIndex),
329
+ password: decoded.substring(colonIndex + 1),
330
+ };
331
+ }
332
+ } catch {
333
+ // Invalid base64
334
+ }
335
+ }
336
+ }
337
+ }
338
+
339
+ return {};
340
+ }
341
+
342
+ private verifyPassword(providedPassword: string, storedPassword: string): boolean {
343
+ // For now, using simple hash comparison
344
+ // In production, use proper password hashing like bcrypt
345
+ const hashedProvided = this.hashPassword(providedPassword);
346
+ const hashedStored = this.hashPassword(storedPassword);
347
+
348
+ return this.timingSafeEqual(hashedProvided, hashedStored);
349
+ }
350
+
351
+ private hashPassword(password: string): string {
352
+ return createHash('sha256').update(password).digest('hex');
353
+ }
354
+
355
+ private timingSafeEqual(a: string, b: string): boolean {
356
+ const encoder = new TextEncoder();
357
+ const bufferA = encoder.encode(a);
358
+ const bufferB = encoder.encode(b);
359
+
360
+ if (bufferA.length !== bufferB.length) {
361
+ return false;
362
+ }
363
+
364
+ return timingSafeEqual(bufferA, bufferB);
365
+ }
366
+
367
+ private createSecureToken(): string {
368
+ // Generate a secure random token
369
+ const timestamp = Date.now().toString(36);
370
+ const random1 = Math.random().toString(36).substring(2, 15);
371
+ const random2 = Math.random().toString(36).substring(2, 15);
372
+ const hash = createHash('sha256')
373
+ .update(`${timestamp}${random1}${random2}`)
374
+ .digest('hex')
375
+ .substring(0, 32);
376
+
377
+ return `mcp_${timestamp}_${hash}`;
378
+ }
379
+
380
+ private cleanupExpiredTokens(): void {
381
+ const now = new Date();
382
+ let cleaned = 0;
383
+
384
+ for (const [token, data] of this.tokenStore.entries()) {
385
+ if (data.expiresAt < now) {
386
+ this.tokenStore.delete(token);
387
+ cleaned++;
388
+ }
389
+ }
390
+
391
+ if (cleaned > 0) {
392
+ this.logger.debug('Cleaned up expired tokens', { count: cleaned });
393
+ }
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Permission constants for common operations
399
+ */
400
+ export const Permissions = {
401
+ // System operations
402
+ SYSTEM_INFO: 'system.info',
403
+ SYSTEM_HEALTH: 'system.health',
404
+ SYSTEM_METRICS: 'system.metrics',
405
+
406
+ // Tool operations
407
+ TOOLS_LIST: 'tools.list',
408
+ TOOLS_INVOKE: 'tools.invoke',
409
+ TOOLS_DESCRIBE: 'tools.describe',
410
+
411
+ // Agent operations
412
+ AGENTS_LIST: 'agents.list',
413
+ AGENTS_SPAWN: 'agents.spawn',
414
+ AGENTS_TERMINATE: 'agents.terminate',
415
+ AGENTS_INFO: 'agents.info',
416
+
417
+ // Task operations
418
+ TASKS_LIST: 'tasks.list',
419
+ TASKS_CREATE: 'tasks.create',
420
+ TASKS_CANCEL: 'tasks.cancel',
421
+ TASKS_STATUS: 'tasks.status',
422
+
423
+ // Memory operations
424
+ MEMORY_READ: 'memory.read',
425
+ MEMORY_WRITE: 'memory.write',
426
+ MEMORY_QUERY: 'memory.query',
427
+ MEMORY_DELETE: 'memory.delete',
428
+
429
+ // Administrative operations
430
+ ADMIN_CONFIG: 'admin.config',
431
+ ADMIN_LOGS: 'admin.logs',
432
+ ADMIN_SESSIONS: 'admin.sessions',
433
+
434
+ // Wildcard permission
435
+ ALL: '*',
436
+ } as const;
437
+
438
+ export type Permission = typeof Permissions[keyof typeof Permissions];