claude-flow-novice 1.5.11 → 1.5.13
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/.claude-flow-novice/dist/mcp/auth.js +347 -0
- package/.claude-flow-novice/dist/mcp/claude-code-wrapper.js +717 -0
- package/.claude-flow-novice/dist/mcp/claude-flow-tools.js +1365 -0
- package/.claude-flow-novice/dist/mcp/client.js +201 -0
- package/.claude-flow-novice/dist/mcp/index.js +192 -0
- package/.claude-flow-novice/dist/mcp/integrate-wrapper.js +85 -0
- package/.claude-flow-novice/dist/mcp/lifecycle-manager.js +348 -0
- package/.claude-flow-novice/dist/mcp/load-balancer.js +386 -0
- package/.claude-flow-novice/dist/mcp/mcp-config-manager.js +1362 -0
- package/.claude-flow-novice/dist/mcp/mcp-server-novice-simplified.js +583 -0
- package/.claude-flow-novice/dist/mcp/mcp-server-novice.js +723 -0
- package/.claude-flow-novice/dist/mcp/mcp-server-sdk.js +649 -0
- package/.claude-flow-novice/dist/mcp/mcp-server.js +2256 -0
- package/.claude-flow-novice/dist/mcp/orchestration-integration.js +800 -0
- package/.claude-flow-novice/dist/mcp/performance-monitor.js +489 -0
- package/.claude-flow-novice/dist/mcp/protocol-manager.js +376 -0
- package/.claude-flow-novice/dist/mcp/router.js +220 -0
- package/.claude-flow-novice/dist/mcp/ruv-swarm-tools.js +671 -0
- package/.claude-flow-novice/dist/mcp/ruv-swarm-wrapper.js +254 -0
- package/.claude-flow-novice/dist/mcp/server-with-wrapper.js +32 -0
- package/.claude-flow-novice/dist/mcp/server-wrapper-mode.js +26 -0
- package/.claude-flow-novice/dist/mcp/server.js +539 -0
- package/.claude-flow-novice/dist/mcp/session-manager.js +338 -0
- package/.claude-flow-novice/dist/mcp/sparc-modes.js +455 -0
- package/.claude-flow-novice/dist/mcp/swarm-tools.js +903 -0
- package/.claude-flow-novice/dist/mcp/tools.js +426 -0
- package/.claude-flow-novice/dist/src/cli/commands/swarm.js +23 -1
- package/.claude-flow-novice/dist/src/cli/commands/swarm.js.map +1 -1
- package/.claude-flow-novice/dist/src/cli/simple-commands/init/templates/CLAUDE.md +40 -101
- package/.claude-flow-novice/dist/src/coordination/swarm-coordinator-factory.js +36 -0
- package/.claude-flow-novice/dist/src/coordination/swarm-coordinator-factory.js.map +1 -0
- package/.claude-flow-novice/dist/src/validators/index.js +12 -0
- package/.claude-flow-novice/dist/src/validators/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/validators/swarm-init-validator.js +261 -0
- package/.claude-flow-novice/dist/src/validators/swarm-init-validator.js.map +1 -0
- package/.claude-flow-novice/dist/src/validators/todowrite-batching-validator.js +204 -0
- package/.claude-flow-novice/dist/src/validators/todowrite-batching-validator.js.map +1 -0
- package/.claude-flow-novice/dist/src/validators/todowrite-integration.js +189 -0
- package/.claude-flow-novice/dist/src/validators/todowrite-integration.js.map +1 -0
- package/CLAUDE.md +98 -102
- package/package.json +2 -2
- package/src/cli/simple-commands/init/templates/CLAUDE.md +4 -40
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication and authorization for MCP
|
|
3
|
+
*/ import { createHash, timingSafeEqual } from 'node:crypto';
|
|
4
|
+
/**
|
|
5
|
+
* Authentication manager implementation
|
|
6
|
+
*/ export class AuthManager {
|
|
7
|
+
config;
|
|
8
|
+
logger;
|
|
9
|
+
revokedTokens = new Set();
|
|
10
|
+
tokenStore = new Map();
|
|
11
|
+
constructor(config, logger){
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.logger = logger;
|
|
14
|
+
// Start token cleanup timer
|
|
15
|
+
if (config.enabled) {
|
|
16
|
+
setInterval(()=>{
|
|
17
|
+
this.cleanupExpiredTokens();
|
|
18
|
+
}, 300000); // Clean up every 5 minutes
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async authenticate(credentials) {
|
|
22
|
+
if (!this.config.enabled) {
|
|
23
|
+
return {
|
|
24
|
+
success: true,
|
|
25
|
+
user: 'anonymous',
|
|
26
|
+
permissions: [
|
|
27
|
+
'*'
|
|
28
|
+
]
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
this.logger.debug('Authenticating credentials', {
|
|
32
|
+
method: this.config.method,
|
|
33
|
+
hasCredentials: !!credentials
|
|
34
|
+
});
|
|
35
|
+
try {
|
|
36
|
+
switch(this.config.method){
|
|
37
|
+
case 'token':
|
|
38
|
+
return await this.authenticateToken(credentials);
|
|
39
|
+
case 'basic':
|
|
40
|
+
return await this.authenticateBasic(credentials);
|
|
41
|
+
case 'oauth':
|
|
42
|
+
return await this.authenticateOAuth(credentials);
|
|
43
|
+
default:
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
error: `Unsupported authentication method: ${this.config.method}`
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
} catch (error) {
|
|
50
|
+
this.logger.error('Authentication error', error);
|
|
51
|
+
return {
|
|
52
|
+
success: false,
|
|
53
|
+
error: error instanceof Error ? error instanceof Error ? error.message : String(error) : 'Authentication failed'
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
authorize(session, permission) {
|
|
58
|
+
if (!this.config.enabled || !session.authenticated) {
|
|
59
|
+
return !this.config.enabled; // If auth disabled, allow all
|
|
60
|
+
}
|
|
61
|
+
const permissions = session.authData?.permissions || [];
|
|
62
|
+
// Check for wildcard permission
|
|
63
|
+
if (permissions.includes('*')) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
// Check for exact permission match
|
|
67
|
+
if (permissions.includes(permission)) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
// Check for prefix-based permissions (e.g., "tools.*" matches "tools.list")
|
|
71
|
+
for (const perm of permissions){
|
|
72
|
+
if (perm.endsWith('*') && permission.startsWith(perm.slice(0, -1))) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
this.logger.warn('Authorization denied', {
|
|
77
|
+
sessionId: session.id,
|
|
78
|
+
user: session.authData?.user,
|
|
79
|
+
permission,
|
|
80
|
+
userPermissions: permissions
|
|
81
|
+
});
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
async validateToken(token) {
|
|
85
|
+
if (this.revokedTokens.has(token)) {
|
|
86
|
+
return {
|
|
87
|
+
valid: false,
|
|
88
|
+
error: 'Token has been revoked'
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const tokenData = this.tokenStore.get(token);
|
|
92
|
+
if (!tokenData) {
|
|
93
|
+
return {
|
|
94
|
+
valid: false,
|
|
95
|
+
error: 'Invalid token'
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (tokenData.expiresAt < new Date()) {
|
|
99
|
+
this.tokenStore.delete(token);
|
|
100
|
+
return {
|
|
101
|
+
valid: false,
|
|
102
|
+
error: 'Token has expired'
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
valid: true,
|
|
107
|
+
user: tokenData.user,
|
|
108
|
+
permissions: tokenData.permissions,
|
|
109
|
+
expiresAt: tokenData.expiresAt
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
async generateToken(userId, permissions) {
|
|
113
|
+
const token = this.createSecureToken();
|
|
114
|
+
const now = new Date();
|
|
115
|
+
const expiresAt = new Date(now.getTime() + (this.config.sessionTimeout || 3600000));
|
|
116
|
+
this.tokenStore.set(token, {
|
|
117
|
+
user: userId,
|
|
118
|
+
permissions,
|
|
119
|
+
createdAt: now,
|
|
120
|
+
expiresAt
|
|
121
|
+
});
|
|
122
|
+
this.logger.info('Token generated', {
|
|
123
|
+
userId,
|
|
124
|
+
permissions,
|
|
125
|
+
expiresAt
|
|
126
|
+
});
|
|
127
|
+
return token;
|
|
128
|
+
}
|
|
129
|
+
async revokeToken(token) {
|
|
130
|
+
this.revokedTokens.add(token);
|
|
131
|
+
this.tokenStore.delete(token);
|
|
132
|
+
this.logger.info('Token revoked', {
|
|
133
|
+
token: token.substring(0, 8) + '...'
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
async authenticateToken(credentials) {
|
|
137
|
+
const token = this.extractToken(credentials);
|
|
138
|
+
if (!token) {
|
|
139
|
+
return {
|
|
140
|
+
success: false,
|
|
141
|
+
error: 'Token not provided'
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// Check if it's a stored token (generated by us)
|
|
145
|
+
const validation = await this.validateToken(token);
|
|
146
|
+
if (validation.valid) {
|
|
147
|
+
return {
|
|
148
|
+
success: true,
|
|
149
|
+
user: validation.user,
|
|
150
|
+
permissions: validation.permissions,
|
|
151
|
+
token
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// Check against configured static tokens
|
|
155
|
+
if (this.config.tokens && this.config.tokens.length > 0) {
|
|
156
|
+
const isValid = this.config.tokens.some((validToken)=>{
|
|
157
|
+
return this.timingSafeEqual(token, validToken);
|
|
158
|
+
});
|
|
159
|
+
if (isValid) {
|
|
160
|
+
return {
|
|
161
|
+
success: true,
|
|
162
|
+
user: 'token-user',
|
|
163
|
+
permissions: [
|
|
164
|
+
'*'
|
|
165
|
+
],
|
|
166
|
+
token
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
success: false,
|
|
172
|
+
error: 'Invalid token'
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
async authenticateBasic(credentials) {
|
|
176
|
+
const { username, password } = this.extractBasicAuth(credentials);
|
|
177
|
+
if (!username || !password) {
|
|
178
|
+
return {
|
|
179
|
+
success: false,
|
|
180
|
+
error: 'Username and password required'
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (!this.config.users || this.config.users.length === 0) {
|
|
184
|
+
return {
|
|
185
|
+
success: false,
|
|
186
|
+
error: 'No users configured'
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
const user = this.config.users.find((u)=>u.username === username);
|
|
190
|
+
if (!user) {
|
|
191
|
+
return {
|
|
192
|
+
success: false,
|
|
193
|
+
error: 'Invalid username or password'
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// Verify password
|
|
197
|
+
const isValidPassword = this.verifyPassword(password, user.password);
|
|
198
|
+
if (!isValidPassword) {
|
|
199
|
+
return {
|
|
200
|
+
success: false,
|
|
201
|
+
error: 'Invalid username or password'
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
// Generate a session token
|
|
205
|
+
const token = await this.generateToken(username, user.permissions);
|
|
206
|
+
return {
|
|
207
|
+
success: true,
|
|
208
|
+
user: username,
|
|
209
|
+
permissions: user.permissions,
|
|
210
|
+
token
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
async authenticateOAuth(credentials) {
|
|
214
|
+
// TODO: Implement OAuth authentication
|
|
215
|
+
// This would typically involve:
|
|
216
|
+
// 1. Validating JWT tokens
|
|
217
|
+
// 2. Checking token expiration
|
|
218
|
+
// 3. Extracting user info and permissions from token claims
|
|
219
|
+
this.logger.warn('OAuth authentication not yet implemented');
|
|
220
|
+
return {
|
|
221
|
+
success: false,
|
|
222
|
+
error: 'OAuth authentication not implemented'
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
extractToken(credentials) {
|
|
226
|
+
if (typeof credentials === 'string') {
|
|
227
|
+
return credentials;
|
|
228
|
+
}
|
|
229
|
+
if (typeof credentials === 'object' && credentials !== null) {
|
|
230
|
+
const creds = credentials;
|
|
231
|
+
if (typeof creds.token === 'string') {
|
|
232
|
+
return creds.token;
|
|
233
|
+
}
|
|
234
|
+
if (typeof creds.authorization === 'string') {
|
|
235
|
+
const match = creds.authorization.match(/^Bearer\s+(.+)$/i);
|
|
236
|
+
return match ? match[1] : null;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
extractBasicAuth(credentials) {
|
|
242
|
+
if (typeof credentials === 'object' && credentials !== null) {
|
|
243
|
+
const creds = credentials;
|
|
244
|
+
if (typeof creds.username === 'string' && typeof creds.password === 'string') {
|
|
245
|
+
return {
|
|
246
|
+
username: creds.username,
|
|
247
|
+
password: creds.password
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
if (typeof creds.authorization === 'string') {
|
|
251
|
+
const match = creds.authorization.match(/^Basic\s+(.+)$/i);
|
|
252
|
+
if (match) {
|
|
253
|
+
try {
|
|
254
|
+
const decoded = atob(match[1]);
|
|
255
|
+
const colonIndex = decoded.indexOf(':');
|
|
256
|
+
if (colonIndex >= 0) {
|
|
257
|
+
return {
|
|
258
|
+
username: decoded.substring(0, colonIndex),
|
|
259
|
+
password: decoded.substring(colonIndex + 1)
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
} catch {
|
|
263
|
+
// Invalid base64
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return {};
|
|
269
|
+
}
|
|
270
|
+
verifyPassword(providedPassword, storedPassword) {
|
|
271
|
+
// For now, using simple hash comparison
|
|
272
|
+
// In production, use proper password hashing like bcrypt
|
|
273
|
+
const hashedProvided = this.hashPassword(providedPassword);
|
|
274
|
+
const hashedStored = this.hashPassword(storedPassword);
|
|
275
|
+
return this.timingSafeEqual(hashedProvided, hashedStored);
|
|
276
|
+
}
|
|
277
|
+
hashPassword(password) {
|
|
278
|
+
return createHash('sha256').update(password).digest('hex');
|
|
279
|
+
}
|
|
280
|
+
timingSafeEqual(a, b) {
|
|
281
|
+
const encoder = new TextEncoder();
|
|
282
|
+
const bufferA = encoder.encode(a);
|
|
283
|
+
const bufferB = encoder.encode(b);
|
|
284
|
+
if (bufferA.length !== bufferB.length) {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
return timingSafeEqual(bufferA, bufferB);
|
|
288
|
+
}
|
|
289
|
+
createSecureToken() {
|
|
290
|
+
// Generate a secure random token
|
|
291
|
+
const timestamp = Date.now().toString(36);
|
|
292
|
+
const random1 = Math.random().toString(36).substring(2, 15);
|
|
293
|
+
const random2 = Math.random().toString(36).substring(2, 15);
|
|
294
|
+
const hash = createHash('sha256').update(`${timestamp}${random1}${random2}`).digest('hex').substring(0, 32);
|
|
295
|
+
return `mcp_${timestamp}_${hash}`;
|
|
296
|
+
}
|
|
297
|
+
cleanupExpiredTokens() {
|
|
298
|
+
const now = new Date();
|
|
299
|
+
let cleaned = 0;
|
|
300
|
+
for (const [token, data] of this.tokenStore.entries()){
|
|
301
|
+
if (data.expiresAt < now) {
|
|
302
|
+
this.tokenStore.delete(token);
|
|
303
|
+
cleaned++;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (cleaned > 0) {
|
|
307
|
+
this.logger.debug('Cleaned up expired tokens', {
|
|
308
|
+
count: cleaned
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Permission constants for common operations
|
|
315
|
+
*/ export const Permissions = {
|
|
316
|
+
// System operations
|
|
317
|
+
SYSTEM_INFO: 'system.info',
|
|
318
|
+
SYSTEM_HEALTH: 'system.health',
|
|
319
|
+
SYSTEM_METRICS: 'system.metrics',
|
|
320
|
+
// Tool operations
|
|
321
|
+
TOOLS_LIST: 'tools.list',
|
|
322
|
+
TOOLS_INVOKE: 'tools.invoke',
|
|
323
|
+
TOOLS_DESCRIBE: 'tools.describe',
|
|
324
|
+
// Agent operations
|
|
325
|
+
AGENTS_LIST: 'agents.list',
|
|
326
|
+
AGENTS_SPAWN: 'agents.spawn',
|
|
327
|
+
AGENTS_TERMINATE: 'agents.terminate',
|
|
328
|
+
AGENTS_INFO: 'agents.info',
|
|
329
|
+
// Task operations
|
|
330
|
+
TASKS_LIST: 'tasks.list',
|
|
331
|
+
TASKS_CREATE: 'tasks.create',
|
|
332
|
+
TASKS_CANCEL: 'tasks.cancel',
|
|
333
|
+
TASKS_STATUS: 'tasks.status',
|
|
334
|
+
// Memory operations
|
|
335
|
+
MEMORY_READ: 'memory.read',
|
|
336
|
+
MEMORY_WRITE: 'memory.write',
|
|
337
|
+
MEMORY_QUERY: 'memory.query',
|
|
338
|
+
MEMORY_DELETE: 'memory.delete',
|
|
339
|
+
// Administrative operations
|
|
340
|
+
ADMIN_CONFIG: 'admin.config',
|
|
341
|
+
ADMIN_LOGS: 'admin.logs',
|
|
342
|
+
ADMIN_SESSIONS: 'admin.sessions',
|
|
343
|
+
// Wildcard permission
|
|
344
|
+
ALL: '*'
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
//# sourceMappingURL=auth.js.map
|