claude-flow-novice 2.18.7 → 2.18.9

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 (33) hide show
  1. package/.claude/cfn-extras/skills/advanced-features/cfn-event-bus/README.md +2 -2
  2. package/.claude/cfn-extras/skills/advanced-features/cfn-event-bus/SKILL.md +2 -2
  3. package/.claude/cfn-extras/skills/advanced-features/cfn-event-bus/config.json +1 -1
  4. package/package.json +1 -1
  5. package/.claude/core/agent-manager.js +0 -80
  6. package/.claude/core/agent-manager.js.map +0 -1
  7. package/.claude/core/config.js +0 -1241
  8. package/.claude/core/config.js.map +0 -1
  9. package/.claude/core/event-bus.js +0 -136
  10. package/.claude/core/event-bus.js.map +0 -1
  11. package/.claude/core/index.js +0 -6
  12. package/.claude/core/index.js.map +0 -1
  13. package/.claude/core/json-persistence.js +0 -112
  14. package/.claude/core/json-persistence.js.map +0 -1
  15. package/.claude/core/logger.js +0 -245
  16. package/.claude/core/logger.js.map +0 -1
  17. package/.claude/core/orchestrator-fixed.js +0 -236
  18. package/.claude/core/orchestrator-fixed.js.map +0 -1
  19. package/.claude/core/orchestrator.js +0 -1136
  20. package/.claude/core/orchestrator.js.map +0 -1
  21. package/.claude/core/persistence.js +0 -185
  22. package/.claude/core/persistence.js.map +0 -1
  23. package/.claude/core/project-manager.js +0 -80
  24. package/.claude/core/project-manager.js.map +0 -1
  25. package/.claude/core/slash-command.js +0 -24
  26. package/.claude/core/version.js +0 -35
  27. package/.claude/core/version.js.map +0 -1
  28. package/.claude/helpers/checkpoint-manager.sh +0 -251
  29. package/.claude/helpers/github-safe.js +0 -106
  30. package/.claude/helpers/github-setup.sh +0 -28
  31. package/.claude/helpers/quick-start.sh +0 -19
  32. package/.claude/helpers/setup-mcp.sh +0 -18
  33. package/.claude/helpers/standard-checkpoint-hooks.sh +0 -179
@@ -1,1241 +0,0 @@
1
- /**
2
- * Enterprise Configuration Management for Claude-Flow
3
- * Features: Security masking, change tracking, multi-format support, credential management
4
- */ import { promises as fs } from 'fs';
5
- import { join } from 'path';
6
- import { homedir } from 'os';
7
- import { createHash, randomBytes, createCipheriv, createDecipheriv } from 'crypto';
8
- import { safeParseJSON } from '../utils/helpers.js';
9
- import { ConfigError, ValidationError } from '../utils/errors.js';
10
- /**
11
- * Security classifications for configuration paths
12
- */ const SECURITY_CLASSIFICATIONS = {
13
- credentials: {
14
- level: 'secret',
15
- encrypted: true
16
- },
17
- 'credentials.apiKey': {
18
- level: 'secret',
19
- maskPattern: '****...****',
20
- encrypted: true
21
- },
22
- 'credentials.token': {
23
- level: 'secret',
24
- maskPattern: '****...****',
25
- encrypted: true
26
- },
27
- 'credentials.password': {
28
- level: 'secret',
29
- maskPattern: '********',
30
- encrypted: true
31
- },
32
- 'mcp.apiKey': {
33
- level: 'confidential',
34
- maskPattern: '****...****'
35
- },
36
- 'logging.destination': {
37
- level: 'internal'
38
- },
39
- orchestrator: {
40
- level: 'internal'
41
- },
42
- terminal: {
43
- level: 'public'
44
- }
45
- };
46
- /**
47
- * Sensitive configuration paths that should be masked in output
48
- */ const SENSITIVE_PATHS = [
49
- 'credentials',
50
- 'apiKey',
51
- 'token',
52
- 'password',
53
- 'secret',
54
- 'key',
55
- 'auth'
56
- ];
57
- /**
58
- * Format parsers for different configuration file types
59
- */ const FORMAT_PARSERS = {
60
- json: {
61
- parse: JSON.parse,
62
- stringify: (obj)=>JSON.stringify(obj, null, 2),
63
- extension: '.json'
64
- },
65
- yaml: {
66
- parse: (content)=>{
67
- // Simple YAML parser for basic key-value pairs
68
- const lines = content.split('\n');
69
- const result = {};
70
- let current = result;
71
- const stack = [
72
- result
73
- ];
74
- for (const line of lines){
75
- const trimmed = line.trim();
76
- if (!trimmed || trimmed.startsWith('#')) continue;
77
- const indent = line.length - line.trimStart().length;
78
- const colonIndex = trimmed.indexOf(':');
79
- if (colonIndex === -1) continue;
80
- const key = trimmed.substring(0, colonIndex).trim();
81
- const value = trimmed.substring(colonIndex + 1).trim();
82
- // Simple value parsing
83
- let parsedValue = value;
84
- if (value === 'true') parsedValue = true;
85
- else if (value === 'false') parsedValue = false;
86
- else if (!isNaN(Number(value)) && value !== '') parsedValue = Number(value);
87
- else if (value.startsWith('"') && value.endsWith('"')) {
88
- parsedValue = value.slice(1, -1);
89
- }
90
- current[key] = parsedValue;
91
- }
92
- return result;
93
- },
94
- stringify: (obj)=>{
95
- const stringify = (obj, indent = 0)=>{
96
- const spaces = ' '.repeat(indent);
97
- let result = '';
98
- for (const [key, value] of Object.entries(obj)){
99
- if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
100
- result += `${spaces}${key}:\n${stringify(value, indent + 1)}`;
101
- } else {
102
- const formattedValue = typeof value === 'string' ? `"${value}"` : String(value);
103
- result += `${spaces}${key}: ${formattedValue}\n`;
104
- }
105
- }
106
- return result;
107
- };
108
- return stringify(obj);
109
- },
110
- extension: '.yaml'
111
- },
112
- toml: {
113
- parse: (content)=>{
114
- // Simple TOML parser for basic sections and key-value pairs
115
- const lines = content.split('\n');
116
- const result = {};
117
- let currentSection = result;
118
- for (const line of lines){
119
- const trimmed = line.trim();
120
- if (!trimmed || trimmed.startsWith('#')) continue;
121
- // Section header
122
- if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
123
- const sectionName = trimmed.slice(1, -1);
124
- currentSection = result[sectionName] = {};
125
- continue;
126
- }
127
- // Key-value pair
128
- const equalsIndex = trimmed.indexOf('=');
129
- if (equalsIndex === -1) continue;
130
- const key = trimmed.substring(0, equalsIndex).trim();
131
- const value = trimmed.substring(equalsIndex + 1).trim();
132
- // Simple value parsing
133
- let parsedValue = value;
134
- if (value === 'true') parsedValue = true;
135
- else if (value === 'false') parsedValue = false;
136
- else if (!isNaN(Number(value)) && value !== '') parsedValue = Number(value);
137
- else if (value.startsWith('"') && value.endsWith('"')) {
138
- parsedValue = value.slice(1, -1);
139
- }
140
- currentSection[key] = parsedValue;
141
- }
142
- return result;
143
- },
144
- stringify: (obj)=>{
145
- let result = '';
146
- for (const [section, values] of Object.entries(obj)){
147
- if (typeof values === 'object' && values !== null && !Array.isArray(values)) {
148
- result += `[${section}]\n`;
149
- for (const [key, value] of Object.entries(values)){
150
- const formattedValue = typeof value === 'string' ? `"${value}"` : String(value);
151
- result += `${key} = ${formattedValue}\n`;
152
- }
153
- result += '\n';
154
- }
155
- }
156
- return result;
157
- },
158
- extension: '.toml'
159
- }
160
- };
161
- /**
162
- * Default configuration values
163
- */ const DEFAULT_CONFIG = {
164
- orchestrator: {
165
- maxConcurrentAgents: 10,
166
- taskQueueSize: 100,
167
- healthCheckInterval: 30000,
168
- shutdownTimeout: 30000
169
- },
170
- terminal: {
171
- type: 'auto',
172
- poolSize: 5,
173
- recycleAfter: 10,
174
- healthCheckInterval: 60000,
175
- commandTimeout: 300000
176
- },
177
- memory: {
178
- backend: 'hybrid',
179
- cacheSizeMB: 100,
180
- syncInterval: 5000,
181
- conflictResolution: 'crdt',
182
- retentionDays: 30
183
- },
184
- coordination: {
185
- maxRetries: 3,
186
- retryDelay: 1000,
187
- deadlockDetection: true,
188
- resourceTimeout: 60000,
189
- messageTimeout: 30000
190
- },
191
- mcp: {
192
- transport: 'stdio',
193
- port: 3000,
194
- tlsEnabled: false
195
- },
196
- logging: {
197
- level: 'info',
198
- format: 'json',
199
- destination: 'console'
200
- },
201
- credentials: {
202
- },
203
- security: {
204
- encryptionEnabled: true,
205
- auditLogging: true,
206
- maskSensitiveValues: true,
207
- allowEnvironmentOverrides: true
208
- }
209
- };
210
- /**
211
- * Configuration manager
212
- */ export class ConfigManager {
213
- static instance;
214
- config;
215
- configPath;
216
- profiles = new Map();
217
- currentProfile;
218
- userConfigDir;
219
- changeHistory = [];
220
- encryptionKey;
221
- validationRules = new Map();
222
- formatParsers = FORMAT_PARSERS;
223
- constructor(){
224
- this.config = deepClone(DEFAULT_CONFIG);
225
- this.userConfigDir = this.getUserConfigDir();
226
- this.setupValidationRules();
227
- // Encryption will be initialized via init() method
228
- }
229
- /**
230
- * Gets the singleton instance
231
- */ static getInstance() {
232
- if (!ConfigManager.instance) {
233
- ConfigManager.instance = new ConfigManager();
234
- }
235
- return ConfigManager.instance;
236
- }
237
- /**
238
- * Initialize async components
239
- */ async init() {
240
- await this.initializeEncryption();
241
- }
242
- /**
243
- * Initializes encryption for sensitive configuration values
244
- */ async initializeEncryption() {
245
- try {
246
- const keyFile = join(this.userConfigDir, '.encryption-key');
247
- // Check if key file exists (simplified for demo)
248
- try {
249
- await fs.access(keyFile);
250
- // In a real implementation, this would be more secure
251
- this.encryptionKey = randomBytes(32);
252
- } catch {
253
- this.encryptionKey = randomBytes(32);
254
- // Store key securely (in production, use proper key management)
255
- }
256
- } catch (error) {
257
- console.warn('Failed to initialize encryption:', error.message);
258
- }
259
- }
260
- /**
261
- * Sets up validation rules for configuration paths
262
- */ setupValidationRules() {
263
- // Orchestrator validation rules
264
- this.validationRules.set('orchestrator.maxConcurrentAgents', {
265
- type: 'number',
266
- required: true,
267
- min: 1,
268
- max: 100,
269
- validator: (value, config)=>{
270
- if (value > config.terminal?.poolSize * 2) {
271
- return 'maxConcurrentAgents should not exceed 2x terminal pool size';
272
- }
273
- return null;
274
- }
275
- });
276
- this.validationRules.set('orchestrator.taskQueueSize', {
277
- type: 'number',
278
- required: true,
279
- min: 1,
280
- max: 10000,
281
- dependencies: [
282
- 'orchestrator.maxConcurrentAgents'
283
- ],
284
- validator: (value, config)=>{
285
- const maxAgents = config.orchestrator?.maxConcurrentAgents || 1;
286
- if (value < maxAgents * 10) {
287
- return 'taskQueueSize should be at least 10x maxConcurrentAgents';
288
- }
289
- return null;
290
- }
291
- });
292
- // Terminal validation rules
293
- this.validationRules.set('terminal.type', {
294
- type: 'string',
295
- required: true,
296
- values: [
297
- 'auto',
298
- 'vscode',
299
- 'native'
300
- ]
301
- });
302
- this.validationRules.set('terminal.poolSize', {
303
- type: 'number',
304
- required: true,
305
- min: 1,
306
- max: 50
307
- });
308
- // Memory validation rules
309
- this.validationRules.set('memory.backend', {
310
- type: 'string',
311
- required: true,
312
- values: [
313
- 'sqlite',
314
- 'markdown',
315
- 'hybrid'
316
- ]
317
- });
318
- this.validationRules.set('memory.cacheSizeMB', {
319
- type: 'number',
320
- required: true,
321
- min: 1,
322
- max: 10000,
323
- validator: (value)=>{
324
- if (value > 1000) {
325
- return 'Large cache sizes may impact system performance';
326
- }
327
- return null;
328
- }
329
- });
330
- // Security validation rules
331
- this.validationRules.set('security.encryptionEnabled', {
332
- type: 'boolean',
333
- required: true
334
- });
335
- // Credentials validation
336
- this.validationRules.set('credentials.apiKey', {
337
- type: 'string',
338
- pattern: /^[a-zA-Z0-9_-]+$/,
339
- validator: (value)=>{
340
- if (value && value.length < 16) {
341
- return 'API key should be at least 16 characters long';
342
- }
343
- return null;
344
- }
345
- });
346
- }
347
- /**
348
- * Loads configuration from various sources
349
- */ async load(configPath) {
350
- if (configPath !== undefined) {
351
- this.configPath = configPath;
352
- }
353
- // Start with defaults
354
- let config = deepClone(DEFAULT_CONFIG);
355
- // Load from file if specified
356
- if (configPath) {
357
- const fileConfig = await this.loadFromFile(configPath);
358
- config = deepMergeConfig(config, fileConfig);
359
- }
360
- // Load from environment variables
361
- const envConfig = this.loadFromEnv();
362
- config = deepMergeConfig(config, envConfig);
363
- // Validate the final configuration
364
- this.validate(config);
365
- this.config = config;
366
- return config;
367
- }
368
- /**
369
- * Gets the current configuration with optional security masking
370
- */ get(maskSensitive = false) {
371
- const config = deepClone(this.config);
372
- if (maskSensitive && this.config.security?.maskSensitiveValues) {
373
- return this.maskSensitiveValues(config);
374
- }
375
- return config;
376
- }
377
- /**
378
- * Gets configuration with security masking applied
379
- */ getSecure() {
380
- return this.get(true);
381
- }
382
- /**
383
- * Gets all configuration values (alias for get method for backward compatibility)
384
- */ async getAll() {
385
- return this.get();
386
- }
387
- /**
388
- * Updates configuration values with change tracking
389
- */ update(updates, options = {}) {
390
- const oldConfig = deepClone(this.config);
391
- // Track changes before applying
392
- this.trackChanges(oldConfig, updates, options);
393
- // Apply updates
394
- this.config = deepMergeConfig(this.config, updates);
395
- // Validate the updated configuration
396
- this.validateWithDependencies(this.config);
397
- return this.get();
398
- }
399
- /**
400
- * Loads default configuration
401
- */ loadDefault() {
402
- this.config = deepClone(DEFAULT_CONFIG);
403
- }
404
- /**
405
- * Saves configuration to file with format support
406
- */ async save(path, format) {
407
- const savePath = path || this.configPath;
408
- if (!savePath) {
409
- throw new ConfigError('No configuration file path specified');
410
- }
411
- const detectedFormat = format || this.detectFormat(savePath);
412
- const parser = this.formatParsers[detectedFormat];
413
- if (!parser) {
414
- throw new ConfigError(`Unsupported format for saving: ${detectedFormat}`);
415
- }
416
- // Get configuration without sensitive values for saving
417
- const configToSave = this.getConfigForSaving();
418
- const content = parser.stringify(configToSave);
419
- await fs.writeFile(savePath, content, 'utf8');
420
- // Record the save operation
421
- this.recordChange({
422
- timestamp: new Date().toISOString(),
423
- path: 'CONFIG_SAVED',
424
- oldValue: null,
425
- newValue: savePath,
426
- source: 'file'
427
- });
428
- }
429
- /**
430
- * Gets configuration suitable for saving (excludes runtime-only values)
431
- */ getConfigForSaving() {
432
- const config = deepClone(this.config);
433
- // Remove encrypted credentials from the saved config
434
- // They should be stored separately in a secure location
435
- if (config.credentials) {
436
- delete config.credentials;
437
- }
438
- return config;
439
- }
440
- /**
441
- * Gets user configuration directory
442
- */ getUserConfigDir() {
443
- const home = homedir();
444
- // DISABLED: Return .claude instead of creating .claude-flow directory
445
- // return join(home, '.claude-flow');
446
- return join(home, '.claude');
447
- }
448
- /**
449
- * Creates user config directory if it doesn't exist
450
- */ async ensureUserConfigDir() {
451
- // DISABLED: Prevent creation of .claude-flow directory
452
- // try {
453
- // await fs.mkdir(this.userConfigDir, {
454
- // recursive: true
455
- // });
456
- // } catch (error) {
457
- // if (error.code !== 'EEXIST') {
458
- // throw new ConfigError(`Failed to create config directory: ${error.message}`);
459
- // }
460
- // }
461
- console.warn('ensureUserConfigDir() disabled to prevent .claude-flow directory creation');
462
- }
463
- /**
464
- * Loads all profiles from the profiles directory
465
- */ async loadProfiles() {
466
- // DISABLED: Prevent loading profiles from .claude-flow directory
467
- console.warn('loadProfiles() disabled to prevent .claude-flow directory access');
468
- return;
469
- // const profilesDir = join(this.userConfigDir, 'profiles');
470
- // try {
471
- // const entries = await fs.readdir(profilesDir, {
472
- // withFileTypes: true
473
- // });
474
- // for (const entry of entries){
475
- // if (entry.isFile() && entry.name.endsWith('.json')) {
476
- // const profileName = entry.name.replace('.json', '');
477
- // const profilePath = join(profilesDir, entry.name);
478
- // try {
479
- // const content = await fs.readFile(profilePath, 'utf8');
480
- // const profileConfig = safeParseJSON(content);
481
- // if (profileConfig) {
482
- // this.profiles.set(profileName, profileConfig);
483
- // }
484
- // } catch (error) {
485
- // console.warn(`Failed to load profile ${profileName}: ${error.message}`);
486
- // }
487
- // }
488
- // }
489
- // } catch (error) {
490
- // // Profiles directory doesn't exist - this is okay
491
- // }
492
- }
493
- /**
494
- * Applies a named profile
495
- */ async applyProfile(profileName) {
496
- await this.loadProfiles();
497
- const profile = this.profiles.get(profileName);
498
- if (!profile) {
499
- throw new ConfigError(`Profile '${profileName}' not found`);
500
- }
501
- this.config = deepMergeConfig(this.config, profile);
502
- this.currentProfile = profileName;
503
- this.validate(this.config);
504
- }
505
- /**
506
- * Saves current configuration as a profile
507
- */ async saveProfile(profileName, config) {
508
- // DISABLED: Prevent saving profiles to .claude-flow directory
509
- console.warn('saveProfile() disabled to prevent .claude-flow directory creation');
510
- return;
511
- // await this.ensureUserConfigDir();
512
- // const profilesDir = join(this.userConfigDir, 'profiles');
513
- // await fs.mkdir(profilesDir, {
514
- // recursive: true
515
- // });
516
- // const profileConfig = config || this.config;
517
- // const profilePath = join(profilesDir, `${profileName}.json`);
518
- // const content = JSON.stringify(profileConfig, null, 2);
519
- // await fs.writeFile(profilePath, content, 'utf8');
520
- // this.profiles.set(profileName, profileConfig);
521
- }
522
- /**
523
- * Deletes a profile
524
- */ async deleteProfile(profileName) {
525
- // DISABLED: Prevent profile operations on .claude-flow directory
526
- console.warn('deleteProfile() disabled to prevent .claude-flow directory operations');
527
- this.profiles.delete(profileName);
528
- return;
529
- // const profilePath = join(this.userConfigDir, 'profiles', `${profileName}.json`);
530
- // try {
531
- // await fs.unlink(profilePath);
532
- // this.profiles.delete(profileName);
533
- // } catch (error) {
534
- // if (error.code === 'ENOENT') {
535
- // throw new ConfigError(`Profile '${profileName}' not found`);
536
- // }
537
- // throw new ConfigError(`Failed to delete profile: ${error.message}`);
538
- // }
539
- }
540
- /**
541
- * Lists all available profiles
542
- */ async listProfiles() {
543
- // DISABLED: Prevent listing profiles from .claude-flow directory
544
- console.warn('listProfiles() disabled to prevent .claude-flow directory access');
545
- return [];
546
- // await this.loadProfiles();
547
- // return Array.from(this.profiles.keys());
548
- }
549
- /**
550
- * Gets a specific profile configuration
551
- */ async getProfile(profileName) {
552
- // DISABLED: Prevent getting profiles from .claude-flow directory
553
- console.warn('getProfile() disabled to prevent .claude-flow directory access');
554
- return undefined;
555
- // await this.loadProfiles();
556
- // return this.profiles.get(profileName);
557
- }
558
- /**
559
- * Gets the current active profile name
560
- */ getCurrentProfile() {
561
- return this.currentProfile;
562
- }
563
- /**
564
- * Sets a configuration value by path with change tracking and validation
565
- */ set(path, value, options = {}) {
566
- const oldValue = this.getValue(path);
567
- // Record the change
568
- this.recordChange({
569
- timestamp: new Date().toISOString(),
570
- path,
571
- oldValue,
572
- newValue: value,
573
- user: options.user,
574
- reason: options.reason,
575
- source: options.source || 'cli'
576
- });
577
- // Encrypt sensitive values
578
- if (this.isSensitivePath(path) && this.config.security?.encryptionEnabled) {
579
- value = this.encryptValue(value);
580
- }
581
- const keys = path.split('.');
582
- let current = this.config;
583
- for(let i = 0; i < keys.length - 1; i++){
584
- const key = keys[i];
585
- if (!(key in current)) {
586
- current[key] = {};
587
- }
588
- current = current[key];
589
- }
590
- current[keys[keys.length - 1]] = value;
591
- // Validate the path-specific rule and dependencies
592
- this.validatePath(path, value);
593
- this.validateWithDependencies(this.config);
594
- }
595
- /**
596
- * Gets a configuration value by path with decryption for sensitive values
597
- */ getValue(path, decrypt = true) {
598
- const keys = path.split('.');
599
- let current = this.config;
600
- for (const key of keys){
601
- if (current && typeof current === 'object' && key in current) {
602
- current = current[key];
603
- } else {
604
- return undefined;
605
- }
606
- }
607
- // Decrypt sensitive values if requested
608
- if (decrypt && this.isSensitivePath(path) && this.isEncryptedValue(current)) {
609
- try {
610
- return this.decryptValue(current);
611
- } catch (error) {
612
- console.warn(`Failed to decrypt value at path ${path}:`, error.message);
613
- return current;
614
- }
615
- }
616
- return current;
617
- }
618
- /**
619
- * Resets configuration to defaults
620
- */ reset() {
621
- this.config = deepClone(DEFAULT_CONFIG);
622
- delete this.currentProfile;
623
- }
624
- /**
625
- * Gets configuration schema for validation
626
- */ getSchema() {
627
- return {
628
- orchestrator: {
629
- maxConcurrentAgents: {
630
- type: 'number',
631
- min: 1,
632
- max: 100
633
- },
634
- taskQueueSize: {
635
- type: 'number',
636
- min: 1,
637
- max: 10000
638
- },
639
- healthCheckInterval: {
640
- type: 'number',
641
- min: 1000,
642
- max: 300000
643
- },
644
- shutdownTimeout: {
645
- type: 'number',
646
- min: 1000,
647
- max: 300000
648
- }
649
- },
650
- terminal: {
651
- type: {
652
- type: 'string',
653
- values: [
654
- 'auto',
655
- 'vscode',
656
- 'native'
657
- ]
658
- },
659
- poolSize: {
660
- type: 'number',
661
- min: 1,
662
- max: 50
663
- },
664
- recycleAfter: {
665
- type: 'number',
666
- min: 1,
667
- max: 1000
668
- },
669
- healthCheckInterval: {
670
- type: 'number',
671
- min: 1000,
672
- max: 3600000
673
- },
674
- commandTimeout: {
675
- type: 'number',
676
- min: 1000,
677
- max: 3600000
678
- }
679
- },
680
- memory: {
681
- backend: {
682
- type: 'string',
683
- values: [
684
- 'sqlite',
685
- 'markdown',
686
- 'hybrid'
687
- ]
688
- },
689
- cacheSizeMB: {
690
- type: 'number',
691
- min: 1,
692
- max: 10000
693
- },
694
- syncInterval: {
695
- type: 'number',
696
- min: 1000,
697
- max: 300000
698
- },
699
- conflictResolution: {
700
- type: 'string',
701
- values: [
702
- 'crdt',
703
- 'timestamp',
704
- 'manual'
705
- ]
706
- },
707
- retentionDays: {
708
- type: 'number',
709
- min: 1,
710
- max: 3650
711
- }
712
- },
713
- coordination: {
714
- maxRetries: {
715
- type: 'number',
716
- min: 0,
717
- max: 100
718
- },
719
- retryDelay: {
720
- type: 'number',
721
- min: 100,
722
- max: 60000
723
- },
724
- deadlockDetection: {
725
- type: 'boolean'
726
- },
727
- resourceTimeout: {
728
- type: 'number',
729
- min: 1000,
730
- max: 3600000
731
- },
732
- messageTimeout: {
733
- type: 'number',
734
- min: 1000,
735
- max: 300000
736
- }
737
- },
738
- mcp: {
739
- transport: {
740
- type: 'string',
741
- values: [
742
- 'stdio',
743
- 'http',
744
- 'websocket'
745
- ]
746
- },
747
- port: {
748
- type: 'number',
749
- min: 1,
750
- max: 65535
751
- },
752
- tlsEnabled: {
753
- type: 'boolean'
754
- }
755
- },
756
- logging: {
757
- level: {
758
- type: 'string',
759
- values: [
760
- 'debug',
761
- 'info',
762
- 'warn',
763
- 'error'
764
- ]
765
- },
766
- format: {
767
- type: 'string',
768
- values: [
769
- 'json',
770
- 'text'
771
- ]
772
- },
773
- destination: {
774
- type: 'string',
775
- values: [
776
- 'console',
777
- 'file'
778
- ]
779
- }
780
- }
781
- };
782
- }
783
- /**
784
- * Validates a value against schema
785
- */ validateValue(value, schema, path) {
786
- if (schema.type === 'number') {
787
- if (typeof value !== 'number' || isNaN(value)) {
788
- throw new ValidationError(`${path}: must be a number`);
789
- }
790
- if (schema.min !== undefined && value < schema.min) {
791
- throw new ValidationError(`${path}: must be at least ${schema.min}`);
792
- }
793
- if (schema.max !== undefined && value > schema.max) {
794
- throw new ValidationError(`${path}: must be at most ${schema.max}`);
795
- }
796
- } else if (schema.type === 'string') {
797
- if (typeof value !== 'string') {
798
- throw new ValidationError(`${path}: must be a string`);
799
- }
800
- if (schema.values && !schema.values.includes(value)) {
801
- throw new ValidationError(`${path}: must be one of [${schema.values.join(', ')}]`);
802
- }
803
- } else if (schema.type === 'boolean') {
804
- if (typeof value !== 'boolean') {
805
- throw new ValidationError(`${path}: must be a boolean`);
806
- }
807
- }
808
- }
809
- /**
810
- * Gets configuration diff between current and default
811
- */ getDiff() {
812
- const defaultConfig = DEFAULT_CONFIG;
813
- const diff = {};
814
- const findDifferences = (current, defaults, path = '')=>{
815
- for(const key in current){
816
- const currentValue = current[key];
817
- const defaultValue = defaults[key];
818
- const fullPath = path ? `${path}.${key}` : key;
819
- if (typeof currentValue === 'object' && currentValue !== null && !Array.isArray(currentValue)) {
820
- if (typeof defaultValue === 'object' && defaultValue !== null) {
821
- const nestedDiff = {};
822
- findDifferences(currentValue, defaultValue, fullPath);
823
- if (Object.keys(nestedDiff).length > 0) {
824
- if (!path) {
825
- diff[key] = nestedDiff;
826
- }
827
- }
828
- }
829
- } else if (currentValue !== defaultValue) {
830
- const pathParts = fullPath.split('.');
831
- let target = diff;
832
- for(let i = 0; i < pathParts.length - 1; i++){
833
- if (!target[pathParts[i]]) {
834
- target[pathParts[i]] = {};
835
- }
836
- target = target[pathParts[i]];
837
- }
838
- target[pathParts[pathParts.length - 1]] = currentValue;
839
- }
840
- }
841
- };
842
- findDifferences(this.config, defaultConfig);
843
- return diff;
844
- }
845
- /**
846
- * Exports configuration with metadata
847
- */ export() {
848
- return {
849
- version: '1.0.0',
850
- exported: new Date().toISOString(),
851
- profile: this.currentProfile,
852
- config: this.config,
853
- diff: this.getDiff()
854
- };
855
- }
856
- /**
857
- * Imports configuration from export
858
- */ import(data) {
859
- if (!data.config) {
860
- throw new ConfigError('Invalid configuration export format');
861
- }
862
- this.validateWithDependencies(data.config);
863
- this.config = data.config;
864
- this.currentProfile = data.profile;
865
- // Record the import operation
866
- this.recordChange({
867
- timestamp: new Date().toISOString(),
868
- path: 'CONFIG_IMPORTED',
869
- oldValue: null,
870
- newValue: data.version || 'unknown',
871
- source: 'file'
872
- });
873
- }
874
- /**
875
- * Loads configuration from file with format detection
876
- */ async loadFromFile(path) {
877
- try {
878
- const content = await fs.readFile(path, 'utf8');
879
- const format = this.detectFormat(path, content);
880
- const parser = this.formatParsers[format];
881
- if (!parser) {
882
- throw new ConfigError(`Unsupported configuration format: ${format}`);
883
- }
884
- const config = parser.parse(content);
885
- if (!config) {
886
- throw new ConfigError(`Invalid ${format.toUpperCase()} in configuration file: ${path}`);
887
- }
888
- return config;
889
- } catch (error) {
890
- if (error.code === 'ENOENT') {
891
- // File doesn't exist, use defaults
892
- return {};
893
- }
894
- throw new ConfigError(`Failed to load configuration from ${path}: ${error.message}`);
895
- }
896
- }
897
- /**
898
- * Detects configuration file format
899
- */ detectFormat(path, content) {
900
- const ext = path.split('.').pop()?.toLowerCase();
901
- if (ext === 'yaml' || ext === 'yml') return 'yaml';
902
- if (ext === 'toml') return 'toml';
903
- if (ext === 'json') return 'json';
904
- // Try to detect from content
905
- if (content) {
906
- const trimmed = content.trim();
907
- if (trimmed.startsWith('{') || trimmed.startsWith('[')) return 'json';
908
- if (trimmed.includes('=') && trimmed.includes('[')) return 'toml';
909
- if (trimmed.includes(':') && !trimmed.includes('=')) return 'yaml';
910
- }
911
- // Default to JSON
912
- return 'json';
913
- }
914
- /**
915
- * Loads configuration from environment variables
916
- */ loadFromEnv() {
917
- const config = {};
918
- // Orchestrator settings
919
- const maxAgents = process.env.CLAUDE_FLOW_MAX_AGENTS;
920
- if (maxAgents) {
921
- if (!config.orchestrator) {
922
- config.orchestrator = {};
923
- }
924
- config.orchestrator = {
925
- ...DEFAULT_CONFIG.orchestrator,
926
- ...config.orchestrator,
927
- maxConcurrentAgents: parseInt(maxAgents, 10)
928
- };
929
- }
930
- // Terminal settings
931
- const terminalType = process.env.CLAUDE_FLOW_TERMINAL_TYPE;
932
- if (terminalType === 'vscode' || terminalType === 'native' || terminalType === 'auto') {
933
- config.terminal = {
934
- ...DEFAULT_CONFIG.terminal,
935
- ...config.terminal,
936
- type: terminalType
937
- };
938
- }
939
- // Memory settings
940
- const memoryBackend = process.env.CLAUDE_FLOW_MEMORY_BACKEND;
941
- if (memoryBackend === 'sqlite' || memoryBackend === 'markdown' || memoryBackend === 'hybrid') {
942
- config.memory = {
943
- ...DEFAULT_CONFIG.memory,
944
- ...config.memory,
945
- backend: memoryBackend
946
- };
947
- }
948
- // MCP settings
949
- const mcpTransport = process.env.CLAUDE_FLOW_MCP_TRANSPORT;
950
- if (mcpTransport === 'stdio' || mcpTransport === 'http' || mcpTransport === 'websocket') {
951
- config.mcp = {
952
- ...DEFAULT_CONFIG.mcp,
953
- ...config.mcp,
954
- transport: mcpTransport
955
- };
956
- }
957
- const mcpPort = process.env.CLAUDE_FLOW_MCP_PORT;
958
- if (mcpPort) {
959
- config.mcp = {
960
- ...DEFAULT_CONFIG.mcp,
961
- ...config.mcp,
962
- port: parseInt(mcpPort, 10)
963
- };
964
- }
965
- // Logging settings
966
- const logLevel = process.env.CLAUDE_FLOW_LOG_LEVEL;
967
- if (logLevel === 'debug' || logLevel === 'info' || logLevel === 'warn' || logLevel === 'error') {
968
- config.logging = {
969
- ...DEFAULT_CONFIG.logging,
970
- ...config.logging,
971
- level: logLevel
972
- };
973
- }
974
- return config;
975
- }
976
- /**
977
- * Validates configuration with dependency checking
978
- */ validateWithDependencies(config) {
979
- const errors = [];
980
- const warnings = [];
981
- // Validate all paths with rules
982
- for (const [path, rule] of this.validationRules.entries()){
983
- const value = this.getValueByPath(config, path);
984
- try {
985
- this.validatePath(path, value, config);
986
- } catch (error) {
987
- errors.push(error.message);
988
- }
989
- }
990
- // Additional cross-field validations
991
- if (config.orchestrator.maxConcurrentAgents > config.terminal.poolSize * 3) {
992
- warnings.push('High agent-to-terminal ratio may cause resource contention');
993
- }
994
- if (config.memory.cacheSizeMB > 1000 && config.memory.backend === 'sqlite') {
995
- warnings.push('Large cache size with SQLite backend may impact performance');
996
- }
997
- if (config.mcp.transport === 'http' && !config.mcp.tlsEnabled) {
998
- warnings.push('HTTP transport without TLS is not recommended for production');
999
- }
1000
- // Log warnings
1001
- if (warnings.length > 0 && config.logging?.level === 'debug') {
1002
- console.warn('Configuration warnings:', warnings);
1003
- }
1004
- // Throw errors
1005
- if (errors.length > 0) {
1006
- throw new ValidationError(`Configuration validation failed:\n${errors.join('\n')}`);
1007
- }
1008
- }
1009
- /**
1010
- * Validates a specific configuration path
1011
- */ validatePath(path, value, config) {
1012
- const rule = this.validationRules.get(path);
1013
- if (!rule) return;
1014
- const currentConfig = config || this.config;
1015
- // Required validation
1016
- if (rule.required && (value === undefined || value === null)) {
1017
- throw new ValidationError(`${path} is required`);
1018
- }
1019
- if (value === undefined || value === null) return;
1020
- // Type validation
1021
- if (rule.type === 'number' && (typeof value !== 'number' || isNaN(value))) {
1022
- throw new ValidationError(`${path} must be a number`);
1023
- }
1024
- if (rule.type === 'string' && typeof value !== 'string') {
1025
- throw new ValidationError(`${path} must be a string`);
1026
- }
1027
- if (rule.type === 'boolean' && typeof value !== 'boolean') {
1028
- throw new ValidationError(`${path} must be a boolean`);
1029
- }
1030
- // Range validation
1031
- if (typeof value === 'number') {
1032
- if (rule.min !== undefined && value < rule.min) {
1033
- throw new ValidationError(`${path} must be at least ${rule.min}`);
1034
- }
1035
- if (rule.max !== undefined && value > rule.max) {
1036
- throw new ValidationError(`${path} must be at most ${rule.max}`);
1037
- }
1038
- }
1039
- // Values validation
1040
- if (rule.values && !rule.values.includes(value)) {
1041
- throw new ValidationError(`${path} must be one of: ${rule.values.join(', ')}`);
1042
- }
1043
- // Pattern validation
1044
- if (rule.pattern && typeof value === 'string' && !rule.pattern.test(value)) {
1045
- throw new ValidationError(`${path} does not match required pattern`);
1046
- }
1047
- // Custom validator
1048
- if (rule.validator) {
1049
- const result = rule.validator(value, currentConfig);
1050
- if (result) {
1051
- throw new ValidationError(`${path}: ${result}`);
1052
- }
1053
- }
1054
- }
1055
- /**
1056
- * Gets a value from a configuration object by path
1057
- */ getValueByPath(obj, path) {
1058
- const keys = path.split('.');
1059
- let current = obj;
1060
- for (const key of keys){
1061
- if (current && typeof current === 'object' && key in current) {
1062
- current = current[key];
1063
- } else {
1064
- return undefined;
1065
- }
1066
- }
1067
- return current;
1068
- }
1069
- /**
1070
- * Legacy validate method for backward compatibility
1071
- */ validate(config) {
1072
- this.validateWithDependencies(config);
1073
- }
1074
- /**
1075
- * Masks sensitive values in configuration
1076
- */ maskSensitiveValues(config) {
1077
- const maskedConfig = deepClone(config);
1078
- // Recursively mask sensitive paths
1079
- const maskObject = (obj, path = '')=>{
1080
- if (!obj || typeof obj !== 'object') return obj;
1081
- const masked = {};
1082
- for (const [key, value] of Object.entries(obj)){
1083
- const currentPath = path ? `${path}.${key}` : key;
1084
- if (this.isSensitivePath(currentPath)) {
1085
- const classification = SECURITY_CLASSIFICATIONS[currentPath];
1086
- masked[key] = classification?.maskPattern || '****';
1087
- } else if (typeof value === 'object' && value !== null) {
1088
- masked[key] = maskObject(value, currentPath);
1089
- } else {
1090
- masked[key] = value;
1091
- }
1092
- }
1093
- return masked;
1094
- };
1095
- return maskObject(maskedConfig);
1096
- }
1097
- /**
1098
- * Tracks changes to configuration
1099
- */ trackChanges(oldConfig, updates, options) {
1100
- // Simple implementation for tracking changes
1101
- for (const [key, value] of Object.entries(updates)){
1102
- this.recordChange({
1103
- timestamp: new Date().toISOString(),
1104
- path: key,
1105
- oldValue: oldConfig[key],
1106
- newValue: value,
1107
- user: options.user,
1108
- reason: options.reason,
1109
- source: options.source || 'cli'
1110
- });
1111
- }
1112
- }
1113
- /**
1114
- * Records a configuration change
1115
- */ recordChange(change) {
1116
- this.changeHistory.push(change);
1117
- // Keep only last 1000 changes
1118
- if (this.changeHistory.length > 1000) {
1119
- this.changeHistory.shift();
1120
- }
1121
- }
1122
- /**
1123
- * Checks if a path contains sensitive information
1124
- */ isSensitivePath(path) {
1125
- return SENSITIVE_PATHS.some((sensitive)=>path.toLowerCase().includes(sensitive.toLowerCase()));
1126
- }
1127
- /**
1128
- * Encrypts a sensitive value
1129
- */ encryptValue(value) {
1130
- if (!this.encryptionKey) {
1131
- return value; // Return original if encryption not available
1132
- }
1133
- try {
1134
- // Simplified encryption - in production use proper encryption
1135
- const iv = randomBytes(16);
1136
- const key = createHash('sha256').update(this.encryptionKey).digest();
1137
- const cipher = createCipheriv('aes-256-cbc', key, iv);
1138
- let encrypted = cipher.update(JSON.stringify(value), 'utf8', 'hex');
1139
- encrypted += cipher.final('hex');
1140
- return `encrypted:${iv.toString('hex')}:${encrypted}`;
1141
- } catch (error) {
1142
- console.warn('Failed to encrypt value:', error.message);
1143
- return value;
1144
- }
1145
- }
1146
- /**
1147
- * Decrypts a sensitive value
1148
- */ decryptValue(encryptedValue) {
1149
- if (!this.encryptionKey || !this.isEncryptedValue(encryptedValue)) {
1150
- return encryptedValue;
1151
- }
1152
- try {
1153
- const parts = encryptedValue.replace('encrypted:', '').split(':');
1154
- if (parts.length !== 2) return encryptedValue; // Handle old format
1155
- const iv = Buffer.from(parts[0], 'hex');
1156
- const encrypted = parts[1];
1157
- const key = createHash('sha256').update(this.encryptionKey).digest();
1158
- const decipher = createDecipheriv('aes-256-cbc', key, iv);
1159
- let decrypted = decipher.update(encrypted, 'hex', 'utf8');
1160
- decrypted += decipher.final('utf8');
1161
- return JSON.parse(decrypted);
1162
- } catch (error) {
1163
- console.warn('Failed to decrypt value:', error.message);
1164
- return encryptedValue;
1165
- }
1166
- }
1167
- /**
1168
- * Checks if a value is encrypted
1169
- */ isEncryptedValue(value) {
1170
- return typeof value === 'string' && value.startsWith('encrypted:');
1171
- }
1172
- }
1173
- // Export singleton instance
1174
- export const configManager = ConfigManager.getInstance();
1175
- // Helper function to load configuration
1176
- export async function loadConfig(path) {
1177
- return await configManager.load(path);
1178
- }
1179
- function deepClone(obj) {
1180
- return JSON.parse(JSON.stringify(obj));
1181
- }
1182
- export { SENSITIVE_PATHS, SECURITY_CLASSIFICATIONS };
1183
- // Custom deepMerge for Config type
1184
- function deepMergeConfig(target, ...sources) {
1185
- const result = deepClone(target);
1186
- for (const source of sources){
1187
- if (!source) continue;
1188
- // Merge each section
1189
- if (source.orchestrator) {
1190
- result.orchestrator = {
1191
- ...result.orchestrator,
1192
- ...source.orchestrator
1193
- };
1194
- }
1195
- if (source.terminal) {
1196
- result.terminal = {
1197
- ...result.terminal,
1198
- ...source.terminal
1199
- };
1200
- }
1201
- if (source.memory) {
1202
- result.memory = {
1203
- ...result.memory,
1204
- ...source.memory
1205
- };
1206
- }
1207
- if (source.coordination) {
1208
- result.coordination = {
1209
- ...result.coordination,
1210
- ...source.coordination
1211
- };
1212
- }
1213
- if (source.mcp) {
1214
- result.mcp = {
1215
- ...result.mcp,
1216
- ...source.mcp
1217
- };
1218
- }
1219
- if (source.logging) {
1220
- result.logging = {
1221
- ...result.logging,
1222
- ...source.logging
1223
- };
1224
- }
1225
- if (source.credentials) {
1226
- result.credentials = {
1227
- ...result.credentials,
1228
- ...source.credentials
1229
- };
1230
- }
1231
- if (source.security) {
1232
- result.security = {
1233
- ...result.security,
1234
- ...source.security
1235
- };
1236
- }
1237
- }
1238
- return result;
1239
- }
1240
-
1241
- //# sourceMappingURL=config.js.map