claude-flow-novice 2.15.2 → 2.15.3

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 (98) hide show
  1. package/.claude/hooks/cfn-BACKUP_USAGE.md +243 -243
  2. package/.claude/hooks/cfn-invoke-security-validation.sh +69 -69
  3. package/.claude/hooks/cfn-post-edit-cfn-retrospective.sh +78 -78
  4. package/.claude/hooks/cfn-post-edit.config.json +44 -44
  5. package/.claude/skills/agent-lifecycle/SKILL.md +60 -0
  6. package/.claude/skills/agent-lifecycle/execute-lifecycle-hook.sh +573 -0
  7. package/.claude/skills/agent-lifecycle/simple-audit.sh +31 -0
  8. package/.claude/skills/cfn-hybrid-routing/check-dependencies.sh +51 -51
  9. package/.claude/skills/cfn-loop-validation/orchestrate-cfn-loop.sh +252 -252
  10. package/.claude/skills/cfn-redis-coordination/agent-recovery.sh +74 -74
  11. package/.claude/skills/cfn-redis-coordination/get-context.sh +112 -112
  12. package/.claude/skills/cfn-transparency-middleware/middleware-config.sh +28 -28
  13. package/.claude/skills/cfn-transparency-middleware/performance-benchmark.sh +78 -78
  14. package/.claude/skills/cfn-transparency-middleware/test-integration.sh +161 -161
  15. package/.claude/skills/cfn-transparency-middleware/test-transparency-skill.sh +367 -367
  16. package/.claude/skills/cfn-transparency-middleware/tests/input-validation.sh +92 -92
  17. package/.claude/skills/cfn-transparency-middleware/wrap-agent.sh +131 -131
  18. package/claude-assets/hooks/cfn-BACKUP_USAGE.md +243 -243
  19. package/claude-assets/hooks/cfn-invoke-security-validation.sh +69 -69
  20. package/claude-assets/hooks/cfn-post-edit-cfn-retrospective.sh +78 -78
  21. package/claude-assets/hooks/cfn-post-edit.config.json +44 -44
  22. package/claude-assets/hooks/cfn-post-execution/memory-cleanup.sh +19 -19
  23. package/claude-assets/hooks/cfn-pre-execution/memory-check.sh +19 -19
  24. package/claude-assets/skills/agent-lifecycle/execute-lifecycle-hook.sh +572 -572
  25. package/claude-assets/skills/agent-lifecycle/simple-audit.sh +30 -30
  26. package/claude-assets/skills/cfn-automatic-memory-persistence/persist-agent-output.sh +48 -48
  27. package/claude-assets/skills/cfn-automatic-memory-persistence/query-agent-history.sh +34 -34
  28. package/claude-assets/skills/cfn-deliverable-validation/confidence-calculator.sh +261 -261
  29. package/claude-assets/skills/cfn-expert-update/update-expert.sh +345 -345
  30. package/claude-assets/skills/cfn-hybrid-routing/check-dependencies.sh +51 -51
  31. package/claude-assets/skills/cfn-intervention-detector/detect-intervention.sh +110 -110
  32. package/claude-assets/skills/cfn-intervention-orchestrator/execute-intervention.sh +58 -58
  33. package/claude-assets/skills/cfn-loop-validation/orchestrate-cfn-loop.sh +252 -252
  34. package/claude-assets/skills/cfn-loop2-output-processing/process-validator-output.sh +275 -275
  35. package/claude-assets/skills/cfn-memory-management/check-memory.sh +159 -159
  36. package/claude-assets/skills/cfn-memory-management/cleanup-memory.sh +196 -196
  37. package/claude-assets/skills/cfn-node-heap-sizer/task-mode-heap-limiter.sh +325 -325
  38. package/claude-assets/skills/cfn-playbook-auto-update/auto-update-playbook.sh +85 -85
  39. package/claude-assets/skills/cfn-redis-coordination/agent-recovery.sh +74 -74
  40. package/claude-assets/skills/cfn-redis-coordination/get-context.sh +112 -112
  41. package/claude-assets/skills/cfn-scope-simplifier/simplify-scope.sh +67 -67
  42. package/claude-assets/skills/cfn-specialist-injection/recommend-specialist.sh +56 -56
  43. package/claude-assets/skills/cfn-standardized-error-handling/capture-agent-error.sh +86 -86
  44. package/claude-assets/skills/cfn-standardized-error-handling/test-error-handling.sh +165 -165
  45. package/claude-assets/skills/cfn-task-config-init/initialize-config.sh +264 -264
  46. package/claude-assets/skills/cfn-task-decomposition/task-decomposer.sh +278 -278
  47. package/claude-assets/skills/cfn-transparency-middleware/middleware-config.sh +28 -28
  48. package/claude-assets/skills/cfn-transparency-middleware/performance-benchmark.sh +78 -78
  49. package/claude-assets/skills/cfn-transparency-middleware/test-integration.sh +161 -161
  50. package/claude-assets/skills/cfn-transparency-middleware/test-transparency-skill.sh +367 -367
  51. package/claude-assets/skills/cfn-transparency-middleware/tests/input-validation.sh +92 -92
  52. package/claude-assets/skills/cfn-transparency-middleware/wrap-agent.sh +131 -131
  53. package/claude-assets/skills/docker-build/SKILL.md +96 -203
  54. package/claude-assets/skills/docker-build/build.sh +73 -73
  55. package/claude-assets/skills/integration/agent-handoff.sh +494 -0
  56. package/claude-assets/skills/integration/file-operations.sh +414 -0
  57. package/claude-assets/skills/workflow-codification/APPROVAL_WORKFLOW.md +806 -0
  58. package/claude-assets/skills/workflow-codification/COST_TRACKING.md +637 -0
  59. package/claude-assets/skills/workflow-codification/EDGE_CASE_TRACKING.md +404 -0
  60. package/claude-assets/skills/workflow-codification/README_PHASE4.md +457 -0
  61. package/claude-assets/skills/workflow-codification/SKILL.md +110 -0
  62. package/claude-assets/skills/workflow-codification/analyze-patterns.sh +899 -0
  63. package/claude-assets/skills/workflow-codification/approval-workflow.sh +514 -0
  64. package/claude-assets/skills/workflow-codification/generate-skill-update.sh +525 -0
  65. package/claude-assets/skills/workflow-codification/review-skill.sh +643 -0
  66. package/claude-assets/skills/workflow-codification/templates/email-notification.txt +114 -0
  67. package/claude-assets/skills/workflow-codification/templates/slack-notification.md +85 -0
  68. package/claude-assets/skills/workflow-codification/test-integration.sh +281 -0
  69. package/claude-assets/skills/workflow-codification/track-cost-savings.sh +445 -0
  70. package/claude-assets/skills/workflow-codification/track-edge-case.sh +323 -0
  71. package/dist/cli/config-manager.js +91 -109
  72. package/dist/cli/config-manager.js.map +1 -1
  73. package/dist/integration/DatabaseHandoff.js +507 -0
  74. package/dist/integration/DatabaseHandoff.js.map +1 -0
  75. package/dist/integration/StandardAdapter.js +291 -0
  76. package/dist/integration/StandardAdapter.js.map +1 -0
  77. package/dist/lib/agent-output-parser.js +518 -0
  78. package/dist/lib/agent-output-parser.js.map +1 -0
  79. package/dist/lib/agent-output-validator.js +950 -0
  80. package/dist/lib/agent-output-validator.js.map +1 -0
  81. package/dist/lib/artifact-registry.js +443 -0
  82. package/dist/lib/artifact-registry.js.map +1 -0
  83. package/dist/lib/config-validator.js +687 -0
  84. package/dist/lib/config-validator.js.map +1 -0
  85. package/dist/types/agent-output.js +44 -0
  86. package/dist/types/agent-output.js.map +1 -0
  87. package/dist/types/config.js +28 -0
  88. package/dist/types/config.js.map +1 -0
  89. package/package.json +2 -1
  90. package/scripts/artifact-cleanup.sh +392 -0
  91. package/scripts/deploy-production.sh +355 -355
  92. package/scripts/docker-playwright-fix.sh +311 -311
  93. package/scripts/docker-rebuild-all-agents.sh +127 -127
  94. package/scripts/memory-leak-prevention.sh +305 -305
  95. package/scripts/migrate-artifacts.sh +563 -0
  96. package/scripts/migrate-yaml-to-json.sh +465 -0
  97. package/scripts/run-marketing-tests.sh +42 -42
  98. package/scripts/update_paths.sh +46 -46
@@ -0,0 +1,687 @@
1
+ /**
2
+ * CFN Configuration Validator
3
+ * Validates JSON configurations against the CFN schema with detailed error reporting
4
+ *
5
+ * @version 1.0.0
6
+ * @description Type-safe validation library with 95%+ accuracy
7
+ */ /**
8
+ * ConfigValidator: Main validation class
9
+ * Provides schema validation, error reporting, and env var export functionality
10
+ */ export class ConfigValidator {
11
+ schema;
12
+ initialized = true;
13
+ constructor(schema){
14
+ if (schema) {
15
+ this.schema = schema;
16
+ } else {
17
+ // Use the default schema embedded below
18
+ this.schema = this.getDefaultSchema();
19
+ }
20
+ }
21
+ /**
22
+ * Get default schema definition
23
+ */ getDefaultSchema() {
24
+ return {
25
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
26
+ $id: 'https://claude-flow-novice.local/schemas/cfn-config-v1.json',
27
+ title: 'CFN Configuration Schema v1.0',
28
+ description: 'Canonical JSON schema for all Claude Flow Novice configuration files',
29
+ version: '1.0.0',
30
+ type: 'object'
31
+ };
32
+ }
33
+ /**
34
+ * Main validation method using built-in validators
35
+ */ validate(config) {
36
+ const errors = [];
37
+ const warnings = [];
38
+ if (typeof config !== 'object' || config === null) {
39
+ return {
40
+ valid: false,
41
+ errors: [
42
+ {
43
+ field: 'root',
44
+ message: 'Configuration must be a valid JSON object',
45
+ path: '/',
46
+ code: 'INVALID_TYPE'
47
+ }
48
+ ],
49
+ warnings,
50
+ configType: 'unknown'
51
+ };
52
+ }
53
+ const configObj = config;
54
+ const configType = this.detectConfigType(configObj);
55
+ // Validate based on detected type
56
+ switch(configType){
57
+ case 'agent-whitelist':
58
+ return this.validateAgentWhitelist(configObj);
59
+ case 'mcp-servers':
60
+ return this.validateMCPServers(configObj);
61
+ case 'skill-requirements':
62
+ return this.validateSkillRequirements(configObj);
63
+ case 'runtime-contract':
64
+ return this.validateRuntimeContract(configObj);
65
+ case 'team':
66
+ return this.validateTeamConfig(configObj);
67
+ default:
68
+ return {
69
+ valid: false,
70
+ errors: [
71
+ {
72
+ field: 'root',
73
+ message: 'Unknown configuration type. Must include agents, servers, tools, variables, or team',
74
+ path: '/',
75
+ code: 'UNKNOWN_CONFIG_TYPE'
76
+ }
77
+ ],
78
+ warnings,
79
+ configType: 'unknown'
80
+ };
81
+ }
82
+ }
83
+ /**
84
+ * Validate JSON string
85
+ */ validateJSON(jsonString) {
86
+ try {
87
+ const config = JSON.parse(jsonString);
88
+ return this.validate(config);
89
+ } catch (error) {
90
+ return {
91
+ valid: false,
92
+ errors: [
93
+ {
94
+ field: 'json',
95
+ message: `Failed to parse JSON: ${error instanceof Error ? error.message : String(error)}`,
96
+ path: 'root',
97
+ code: 'JSON_PARSE_ERROR'
98
+ }
99
+ ],
100
+ warnings: [],
101
+ configType: 'unknown'
102
+ };
103
+ }
104
+ }
105
+ /**
106
+ * Validate Agent Whitelist Configuration
107
+ */ validateAgentWhitelist(config) {
108
+ const errors = [];
109
+ const warnings = [];
110
+ // Check version
111
+ if (!config.version || typeof config.version !== 'string') {
112
+ errors.push({
113
+ field: 'version',
114
+ message: 'Missing required field: version (string)',
115
+ path: '/version',
116
+ code: 'MISSING_REQUIRED'
117
+ });
118
+ } else if (!/^\d+\.\d+\.\d+$/.test(config.version)) {
119
+ errors.push({
120
+ field: 'version',
121
+ message: 'Invalid version format. Expected X.Y.Z format',
122
+ path: '/version',
123
+ value: config.version,
124
+ code: 'INVALID_FORMAT'
125
+ });
126
+ }
127
+ // Check agents array
128
+ if (!Array.isArray(config.agents)) {
129
+ errors.push({
130
+ field: 'agents',
131
+ message: 'Missing required field: agents (array)',
132
+ path: '/agents',
133
+ code: 'MISSING_REQUIRED'
134
+ });
135
+ } else {
136
+ for(let i = 0; i < config.agents.length; i++){
137
+ const agent = config.agents[i];
138
+ if (!agent.type || typeof agent.type !== 'string' || agent.type.length === 0) {
139
+ errors.push({
140
+ field: `agents[${i}].type`,
141
+ message: 'Agent type is required and must be non-empty string',
142
+ path: `/agents/${i}/type`,
143
+ code: 'INVALID_AGENT'
144
+ });
145
+ }
146
+ if (!agent.displayName || typeof agent.displayName !== 'string') {
147
+ errors.push({
148
+ field: `agents[${i}].displayName`,
149
+ message: 'Agent displayName is required',
150
+ path: `/agents/${i}/displayName`,
151
+ code: 'INVALID_AGENT'
152
+ });
153
+ }
154
+ if (!Array.isArray(agent.skills)) {
155
+ errors.push({
156
+ field: `agents[${i}].skills`,
157
+ message: 'Agent skills must be an array',
158
+ path: `/agents/${i}/skills`,
159
+ code: 'INVALID_AGENT'
160
+ });
161
+ }
162
+ }
163
+ }
164
+ if (!config.lastUpdated) {
165
+ warnings.push('Agent configuration missing lastUpdated field (recommended)');
166
+ }
167
+ return {
168
+ valid: errors.length === 0,
169
+ errors,
170
+ warnings,
171
+ configType: 'agent-whitelist'
172
+ };
173
+ }
174
+ /**
175
+ * Validate MCP Servers Configuration
176
+ */ validateMCPServers(config) {
177
+ const errors = [];
178
+ const warnings = [];
179
+ // Check version
180
+ if (!config.version || typeof config.version !== 'string') {
181
+ errors.push({
182
+ field: 'version',
183
+ message: 'Missing required field: version',
184
+ path: '/version',
185
+ code: 'MISSING_REQUIRED'
186
+ });
187
+ }
188
+ // Check servers object
189
+ if (!config.servers || typeof config.servers !== 'object') {
190
+ errors.push({
191
+ field: 'servers',
192
+ message: 'Missing required field: servers (object)',
193
+ path: '/servers',
194
+ code: 'MISSING_REQUIRED'
195
+ });
196
+ } else {
197
+ const servers = config.servers;
198
+ for(const serverName in servers){
199
+ if (Object.prototype.hasOwnProperty.call(servers, serverName)) {
200
+ const serverConfig = servers[serverName];
201
+ if (!serverConfig.endpoint || typeof serverConfig.endpoint !== 'string') {
202
+ errors.push({
203
+ field: `servers.${serverName}.endpoint`,
204
+ message: 'Server endpoint is required',
205
+ path: `/servers/${serverName}/endpoint`,
206
+ code: 'INVALID_SERVER'
207
+ });
208
+ } else if (!this.isValidURL(serverConfig.endpoint)) {
209
+ errors.push({
210
+ field: `servers.${serverName}.endpoint`,
211
+ message: 'Server endpoint must be a valid URL',
212
+ path: `/servers/${serverName}/endpoint`,
213
+ value: serverConfig.endpoint,
214
+ code: 'INVALID_URL'
215
+ });
216
+ }
217
+ if (!Array.isArray(serverConfig.requiredSkills)) {
218
+ errors.push({
219
+ field: `servers.${serverName}.requiredSkills`,
220
+ message: 'Server requiredSkills must be an array',
221
+ path: `/servers/${serverName}/requiredSkills`,
222
+ code: 'INVALID_SERVER'
223
+ });
224
+ }
225
+ if (!serverConfig.auth || typeof serverConfig.auth !== 'object') {
226
+ errors.push({
227
+ field: `servers.${serverName}.auth`,
228
+ message: 'Server auth is required',
229
+ path: `/servers/${serverName}/auth`,
230
+ code: 'INVALID_SERVER'
231
+ });
232
+ }
233
+ if (serverConfig.timeoutMs !== undefined && typeof serverConfig.timeoutMs !== 'number') {
234
+ errors.push({
235
+ field: `servers.${serverName}.timeoutMs`,
236
+ message: 'timeoutMs must be a number',
237
+ path: `/servers/${serverName}/timeoutMs`,
238
+ code: 'INVALID_TYPE'
239
+ });
240
+ } else if (serverConfig.timeoutMs !== undefined && serverConfig.timeoutMs < 1000) {
241
+ errors.push({
242
+ field: `servers.${serverName}.timeoutMs`,
243
+ message: 'timeoutMs must be at least 1000ms',
244
+ path: `/servers/${serverName}/timeoutMs`,
245
+ value: serverConfig.timeoutMs,
246
+ code: 'CONSTRAINT_VIOLATION'
247
+ });
248
+ }
249
+ }
250
+ }
251
+ }
252
+ return {
253
+ valid: errors.length === 0,
254
+ errors,
255
+ warnings,
256
+ configType: 'mcp-servers'
257
+ };
258
+ }
259
+ /**
260
+ * Validate Skill Requirements Configuration
261
+ */ validateSkillRequirements(config) {
262
+ const errors = [];
263
+ const warnings = [];
264
+ if (!config.version || typeof config.version !== 'string') {
265
+ errors.push({
266
+ field: 'version',
267
+ message: 'Missing required field: version',
268
+ path: '/version',
269
+ code: 'MISSING_REQUIRED'
270
+ });
271
+ }
272
+ if (!config.tools || typeof config.tools !== 'object') {
273
+ errors.push({
274
+ field: 'tools',
275
+ message: 'Missing required field: tools (object)',
276
+ path: '/tools',
277
+ code: 'MISSING_REQUIRED'
278
+ });
279
+ } else {
280
+ const tools = config.tools;
281
+ for(const toolName in tools){
282
+ if (Object.prototype.hasOwnProperty.call(tools, toolName)) {
283
+ const toolConfig = tools[toolName];
284
+ if (!toolConfig.displayName || typeof toolConfig.displayName !== 'string') {
285
+ errors.push({
286
+ field: `tools.${toolName}.displayName`,
287
+ message: 'Tool displayName is required',
288
+ path: `/tools/${toolName}/displayName`,
289
+ code: 'INVALID_TOOL'
290
+ });
291
+ }
292
+ if (!Array.isArray(toolConfig.requiredSkills) || toolConfig.requiredSkills.length === 0) {
293
+ errors.push({
294
+ field: `tools.${toolName}.requiredSkills`,
295
+ message: 'Tool requiredSkills must be a non-empty array',
296
+ path: `/tools/${toolName}/requiredSkills`,
297
+ code: 'INVALID_TOOL'
298
+ });
299
+ }
300
+ }
301
+ }
302
+ }
303
+ return {
304
+ valid: errors.length === 0,
305
+ errors,
306
+ warnings,
307
+ configType: 'skill-requirements'
308
+ };
309
+ }
310
+ /**
311
+ * Validate Runtime Contract Configuration
312
+ */ validateRuntimeContract(config) {
313
+ const errors = [];
314
+ const warnings = [];
315
+ if (!config.version || typeof config.version !== 'string') {
316
+ errors.push({
317
+ field: 'version',
318
+ message: 'Missing required field: version',
319
+ path: '/version',
320
+ code: 'MISSING_REQUIRED'
321
+ });
322
+ }
323
+ if (config.variables && typeof config.variables === 'object') {
324
+ const variables = config.variables;
325
+ for(const varName in variables){
326
+ if (Object.prototype.hasOwnProperty.call(variables, varName)) {
327
+ const varConfig = variables[varName];
328
+ if (!varConfig.description || typeof varConfig.description !== 'string') {
329
+ errors.push({
330
+ field: `variables.${varName}.description`,
331
+ message: 'Variable description is required',
332
+ path: `/variables/${varName}/description`,
333
+ code: 'INVALID_VARIABLE'
334
+ });
335
+ }
336
+ const varType = varConfig.type;
337
+ const validTypes = [
338
+ 'string',
339
+ 'integer',
340
+ 'number',
341
+ 'boolean'
342
+ ];
343
+ if (!varConfig.type || validTypes.indexOf(varType) === -1) {
344
+ errors.push({
345
+ field: `variables.${varName}.type`,
346
+ message: 'Variable type must be one of: string, integer, number, boolean',
347
+ path: `/variables/${varName}/type`,
348
+ code: 'INVALID_VARIABLE'
349
+ });
350
+ }
351
+ }
352
+ }
353
+ }
354
+ return {
355
+ valid: errors.length === 0,
356
+ errors,
357
+ warnings,
358
+ configType: 'runtime-contract'
359
+ };
360
+ }
361
+ /**
362
+ * Validate Team Configuration
363
+ */ validateTeamConfig(config) {
364
+ const errors = [];
365
+ const warnings = [];
366
+ if (!config.team || typeof config.team !== 'object') {
367
+ errors.push({
368
+ field: 'team',
369
+ message: 'Missing required field: team (object)',
370
+ path: '/team',
371
+ code: 'MISSING_REQUIRED'
372
+ });
373
+ return {
374
+ valid: false,
375
+ errors,
376
+ warnings,
377
+ configType: 'team'
378
+ };
379
+ }
380
+ const team = config.team;
381
+ if (!team.id || typeof team.id !== 'string' || team.id.length === 0) {
382
+ errors.push({
383
+ field: 'team.id',
384
+ message: 'Team id is required and must be non-empty',
385
+ path: '/team/id',
386
+ code: 'INVALID_TEAM'
387
+ });
388
+ } else if (!/^[a-z0-9-]+$/.test(team.id)) {
389
+ errors.push({
390
+ field: 'team.id',
391
+ message: 'Team id must contain only lowercase letters, numbers, and hyphens',
392
+ path: '/team/id',
393
+ value: team.id,
394
+ code: 'INVALID_FORMAT'
395
+ });
396
+ }
397
+ if (!team.name || typeof team.name !== 'string') {
398
+ errors.push({
399
+ field: 'team.name',
400
+ message: 'Team name is required',
401
+ path: '/team/name',
402
+ code: 'INVALID_TEAM'
403
+ });
404
+ }
405
+ // Validate workspace if present (supports both diskQuota and disk_quota)
406
+ if (team.workspace && typeof team.workspace === 'object') {
407
+ const workspace = team.workspace;
408
+ const diskQuota = this.getPropertyValue(workspace, 'diskQuota');
409
+ if (diskQuota !== undefined && !this.isValidDiskQuota(diskQuota)) {
410
+ errors.push({
411
+ field: 'team.workspace.diskQuota',
412
+ message: 'Invalid disk quota format. Expected format: <number><UNIT> (e.g., 100GB)',
413
+ path: '/team/workspace/diskQuota',
414
+ value: diskQuota,
415
+ code: 'INVALID_FORMAT'
416
+ });
417
+ }
418
+ }
419
+ // Validate resources if present (supports both camelCase and snake_case naming)
420
+ if (team.resources && typeof team.resources === 'object') {
421
+ const resources = team.resources;
422
+ const cpuCores = this.getPropertyValue(resources, 'cpuCores');
423
+ const maxAgents = this.getPropertyValue(resources, 'maxAgents');
424
+ // Validate cpuCores (supports cpuCores and cpu_cores)
425
+ if (cpuCores !== undefined) {
426
+ if (typeof cpuCores !== 'number' || cpuCores < 0) {
427
+ errors.push({
428
+ field: 'team.resources.cpuCores',
429
+ message: 'cpuCores must be a non-negative number',
430
+ path: '/team/resources/cpuCores',
431
+ value: cpuCores,
432
+ code: 'INVALID_TYPE'
433
+ });
434
+ }
435
+ }
436
+ // Validate maxAgents (supports maxAgents and max_agents)
437
+ if (maxAgents !== undefined) {
438
+ if (typeof maxAgents !== 'number' || maxAgents < 1 || !Number.isInteger(maxAgents)) {
439
+ errors.push({
440
+ field: 'team.resources.maxAgents',
441
+ message: 'maxAgents must be a positive integer',
442
+ path: '/team/resources/maxAgents',
443
+ value: maxAgents,
444
+ code: 'INVALID_TYPE'
445
+ });
446
+ }
447
+ }
448
+ }
449
+ // Validate network if present
450
+ if (team.network && typeof team.network === 'object') {
451
+ const network = team.network;
452
+ if (network.coordinatorIp && !this.isValidIPv4(network.coordinatorIp)) {
453
+ errors.push({
454
+ field: 'team.network.coordinatorIp',
455
+ message: 'Invalid IPv4 format',
456
+ path: '/team/network/coordinatorIp',
457
+ value: network.coordinatorIp,
458
+ code: 'INVALID_FORMAT'
459
+ });
460
+ }
461
+ }
462
+ return {
463
+ valid: errors.length === 0,
464
+ errors,
465
+ warnings,
466
+ configType: 'team'
467
+ };
468
+ }
469
+ /**
470
+ * Detect configuration type
471
+ */ detectConfigType(config) {
472
+ // Check for agent whitelist (has 'agents' array)
473
+ if ('agents' in config && Array.isArray(config.agents)) {
474
+ return 'agent-whitelist';
475
+ }
476
+ // Check for MCP servers (has 'servers' object)
477
+ if ('servers' in config && typeof config.servers === 'object') {
478
+ return 'mcp-servers';
479
+ }
480
+ // Check for skill requirements (has 'tools' object)
481
+ if ('tools' in config && typeof config.tools === 'object') {
482
+ return 'skill-requirements';
483
+ }
484
+ // Check for runtime contract (has 'variables' object)
485
+ if ('variables' in config && typeof config.variables === 'object') {
486
+ return 'runtime-contract';
487
+ }
488
+ // Check for team config (has 'team' object)
489
+ if ('team' in config && typeof config.team === 'object') {
490
+ return 'team';
491
+ }
492
+ return 'unknown';
493
+ }
494
+ /**
495
+ * Export configuration as environment variables
496
+ * Handles type preservation and validation
497
+ */ exportEnvVars(config) {
498
+ if (typeof config !== 'object' || config === null) {
499
+ throw new Error('Configuration must be an object');
500
+ }
501
+ const configObj = config;
502
+ if (!('variables' in configObj)) {
503
+ throw new Error('Configuration is not a valid runtime contract');
504
+ }
505
+ const envMap = {};
506
+ const variables = configObj.variables;
507
+ if (typeof variables !== 'object' || variables === null) {
508
+ throw new Error('Variables must be an object');
509
+ }
510
+ const varsObj = variables;
511
+ for(const key in varsObj){
512
+ if (Object.prototype.hasOwnProperty.call(varsObj, key)) {
513
+ const variable = varsObj[key];
514
+ const varObj = variable;
515
+ if (varObj.value !== null && varObj.value !== undefined) {
516
+ const varType = varObj.type;
517
+ envMap[key] = this.coerceToCorrectType(varObj.value, varType);
518
+ }
519
+ }
520
+ }
521
+ return envMap;
522
+ }
523
+ /**
524
+ * Coerce value to correct type without loss
525
+ */ coerceToCorrectType(value, type) {
526
+ if (type === 'integer' && typeof value === 'string') {
527
+ return parseInt(value, 10);
528
+ }
529
+ if (type === 'number' && typeof value === 'string') {
530
+ return parseFloat(value);
531
+ }
532
+ if (type === 'boolean' && typeof value === 'string') {
533
+ return value.toLowerCase() === 'true';
534
+ }
535
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
536
+ return value;
537
+ }
538
+ return String(value);
539
+ }
540
+ /**
541
+ * Format validation errors for display
542
+ */ formatErrors(result) {
543
+ if (result.valid) {
544
+ return 'Configuration is valid.';
545
+ }
546
+ let output = `Validation failed with ${result.errors.length} error(s):\n\n`;
547
+ for (const error of result.errors){
548
+ output += `[${error.code}] ${error.field || 'root'}\n`;
549
+ output += ` ${error.message}\n`;
550
+ if (error.value !== undefined) {
551
+ output += ` Current value: ${JSON.stringify(error.value)}\n`;
552
+ }
553
+ output += '\n';
554
+ }
555
+ if (result.warnings.length > 0) {
556
+ output += `\nWarnings (${result.warnings.length}):\n`;
557
+ for (const warning of result.warnings){
558
+ output += ` - ${warning}\n`;
559
+ }
560
+ }
561
+ return output;
562
+ }
563
+ /**
564
+ * Helper: Validate URL format
565
+ */ isValidURL(url) {
566
+ if (typeof url !== 'string') return false;
567
+ try {
568
+ new URL(url);
569
+ return true;
570
+ } catch {
571
+ return false;
572
+ }
573
+ }
574
+ /**
575
+ * Helper: Validate disk quota format
576
+ */ isValidDiskQuota(quota) {
577
+ if (typeof quota !== 'string') return false;
578
+ return /^\d+[KMGTPE]B$/.test(quota);
579
+ }
580
+ /**
581
+ * Helper: Validate IPv4 format
582
+ */ isValidIPv4(ip) {
583
+ if (typeof ip !== 'string') return false;
584
+ const ipv4Pattern = /^(\d{1,3}\.){3}\d{1,3}$/;
585
+ if (!ipv4Pattern.test(ip)) return false;
586
+ const parts = ip.split('.');
587
+ return parts.every((part)=>{
588
+ const num = parseInt(part, 10);
589
+ return num >= 0 && num <= 255;
590
+ });
591
+ }
592
+ /**
593
+ * Helper: Normalize field name (support both snake_case and camelCase)
594
+ * Examples: disk_quota → diskQuota, cpu_cores → cpuCores, maxAgents → maxAgents
595
+ */ normalizeFieldName(name) {
596
+ if (!name.includes('_')) return name;
597
+ return name.replace(/_([a-z])/g, (_, letter)=>letter.toUpperCase());
598
+ }
599
+ /**
600
+ * Helper: Get property value supporting both naming conventions
601
+ * @param obj Object to search
602
+ * @param field Field name in any convention (camelCase or snake_case)
603
+ * @returns Value if found, undefined otherwise
604
+ */ getPropertyValue(obj, field) {
605
+ // Try camelCase version first
606
+ if (field in obj) return obj[field];
607
+ // Try snake_case version
608
+ const snakeCase = field.replace(/[A-Z]/g, (letter)=>`_${letter.toLowerCase()}`);
609
+ if (snakeCase in obj) return obj[snakeCase];
610
+ // Try normalizing if it's snake_case input
611
+ const camelCase = this.normalizeFieldName(field);
612
+ if (camelCase in obj) return obj[camelCase];
613
+ return undefined;
614
+ }
615
+ }
616
+ /**
617
+ * Singleton instance for global usage
618
+ */ let validatorInstance = null;
619
+ /**
620
+ * Get or create validator instance
621
+ */ export function getValidator(schema) {
622
+ if (!validatorInstance) {
623
+ validatorInstance = new ConfigValidator(schema);
624
+ }
625
+ return validatorInstance;
626
+ }
627
+ /**
628
+ * Validate configuration object
629
+ */ export function validateConfig(config) {
630
+ return getValidator().validate(config);
631
+ }
632
+ /**
633
+ * Validate JSON string
634
+ */ export function validateJSON(jsonString) {
635
+ return getValidator().validateJSON(jsonString);
636
+ }
637
+ /**
638
+ * Export environment variables from runtime contract
639
+ */ export function exportEnvVars(config) {
640
+ return getValidator().exportEnvVars(config);
641
+ }
642
+ /**
643
+ * Check if configuration is valid (boolean shortcut)
644
+ */ export function isValidConfig(config) {
645
+ return getValidator().validate(config).valid;
646
+ }
647
+ /**
648
+ * Reset validator instance (useful for testing)
649
+ */ export function resetValidator() {
650
+ validatorInstance = null;
651
+ }
652
+ /**
653
+ * Validate multiple configuration files efficiently
654
+ * @param filePaths Array of file paths to validate
655
+ * @returns Map of file path to validation result
656
+ * @throws Error if file cannot be read
657
+ */ export function validateConfigFiles(filePaths) {
658
+ const results = {};
659
+ const validator = getValidator();
660
+ for (const filePath of filePaths){
661
+ try {
662
+ // Dynamically import fs module to read file
663
+ const fs = require('fs');
664
+ const content = fs.readFileSync(filePath, 'utf-8');
665
+ const config = JSON.parse(content);
666
+ results[filePath] = validator.validate(config);
667
+ } catch (error) {
668
+ results[filePath] = {
669
+ valid: false,
670
+ errors: [
671
+ {
672
+ field: 'file',
673
+ message: `Failed to read or parse file: ${error instanceof Error ? error.message : String(error)}`,
674
+ path: filePath,
675
+ code: 'FILE_READ_ERROR'
676
+ }
677
+ ],
678
+ warnings: [],
679
+ configType: 'unknown'
680
+ };
681
+ }
682
+ }
683
+ return results;
684
+ }
685
+ export default ConfigValidator;
686
+
687
+ //# sourceMappingURL=config-validator.js.map