claude-flow-novice 2.18.7 → 2.18.8
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/package.json +1 -1
- package/.claude/core/agent-manager.js +0 -80
- package/.claude/core/agent-manager.js.map +0 -1
- package/.claude/core/config.js +0 -1241
- package/.claude/core/config.js.map +0 -1
- package/.claude/core/event-bus.js +0 -136
- package/.claude/core/event-bus.js.map +0 -1
- package/.claude/core/index.js +0 -6
- package/.claude/core/index.js.map +0 -1
- package/.claude/core/json-persistence.js +0 -112
- package/.claude/core/json-persistence.js.map +0 -1
- package/.claude/core/logger.js +0 -245
- package/.claude/core/logger.js.map +0 -1
- package/.claude/core/orchestrator-fixed.js +0 -236
- package/.claude/core/orchestrator-fixed.js.map +0 -1
- package/.claude/core/orchestrator.js +0 -1136
- package/.claude/core/orchestrator.js.map +0 -1
- package/.claude/core/persistence.js +0 -185
- package/.claude/core/persistence.js.map +0 -1
- package/.claude/core/project-manager.js +0 -80
- package/.claude/core/project-manager.js.map +0 -1
- package/.claude/core/slash-command.js +0 -24
- package/.claude/core/version.js +0 -35
- package/.claude/core/version.js.map +0 -1
- package/.claude/helpers/checkpoint-manager.sh +0 -251
- package/.claude/helpers/github-safe.js +0 -106
- package/.claude/helpers/github-setup.sh +0 -28
- package/.claude/helpers/quick-start.sh +0 -19
- package/.claude/helpers/setup-mcp.sh +0 -18
- package/.claude/helpers/standard-checkpoint-hooks.sh +0 -179
package/.claude/core/config.js
DELETED
|
@@ -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
|