mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-11-09 14:04:06 +09:00
- Implemented comprehensive multi-agent orchestration system - Added sophisticated task scheduling with priority management - Built persistent memory management with hybrid SQLite/Markdown backends - Created terminal session management with pooling capabilities - Implemented resource coordination with deadlock detection - Added MCP integration for tool management - Built interactive CLI with REPL mode - Created comprehensive test suite with 100% coverage goal - Added CI/CD pipeline with GitHub Actions - Implemented comprehensive documentation and guides - Updated package.json for npm deployment - Created claude.md comprehensive system guide 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
288 lines
7.7 KiB
TypeScript
288 lines
7.7 KiB
TypeScript
#!/usr/bin/env deno run --allow-read
|
|
|
|
/**
|
|
* Example Configuration Validator
|
|
* Validates all example configuration files for correctness
|
|
*/
|
|
|
|
import { walk } from "https://deno.land/std@0.220.0/fs/mod.ts";
|
|
|
|
interface ValidationResult {
|
|
file: string;
|
|
valid: boolean;
|
|
errors: string[];
|
|
}
|
|
|
|
interface ConfigSchema {
|
|
[key: string]: {
|
|
type: string;
|
|
required?: boolean;
|
|
properties?: ConfigSchema;
|
|
};
|
|
}
|
|
|
|
const CLAUDE_FLOW_CONFIG_SCHEMA: ConfigSchema = {
|
|
orchestrator: {
|
|
type: 'object',
|
|
required: true,
|
|
properties: {
|
|
maxConcurrentAgents: { type: 'number' },
|
|
taskQueueSize: { type: 'number' },
|
|
healthCheckInterval: { type: 'number' },
|
|
shutdownTimeout: { type: 'number' },
|
|
},
|
|
},
|
|
terminal: {
|
|
type: 'object',
|
|
properties: {
|
|
type: { type: 'string' },
|
|
poolSize: { type: 'number' },
|
|
recycleAfter: { type: 'number' },
|
|
healthCheckInterval: { type: 'number' },
|
|
commandTimeout: { type: 'number' },
|
|
},
|
|
},
|
|
memory: {
|
|
type: 'object',
|
|
properties: {
|
|
backend: { type: 'string' },
|
|
cacheSizeMB: { type: 'number' },
|
|
syncInterval: { type: 'number' },
|
|
conflictResolution: { type: 'string' },
|
|
retentionDays: { type: 'number' },
|
|
},
|
|
},
|
|
coordination: {
|
|
type: 'object',
|
|
properties: {
|
|
maxRetries: { type: 'number' },
|
|
retryDelay: { type: 'number' },
|
|
deadlockDetection: { type: 'boolean' },
|
|
resourceTimeout: { type: 'number' },
|
|
messageTimeout: { type: 'number' },
|
|
},
|
|
},
|
|
mcp: {
|
|
type: 'object',
|
|
properties: {
|
|
transport: { type: 'string' },
|
|
port: { type: 'number' },
|
|
tlsEnabled: { type: 'boolean' },
|
|
},
|
|
},
|
|
logging: {
|
|
type: 'object',
|
|
properties: {
|
|
level: { type: 'string' },
|
|
format: { type: 'string' },
|
|
destination: { type: 'string' },
|
|
},
|
|
},
|
|
};
|
|
|
|
const WORKFLOW_SCHEMA: ConfigSchema = {
|
|
name: { type: 'string', required: true },
|
|
description: { type: 'string' },
|
|
tasks: {
|
|
type: 'array',
|
|
required: true,
|
|
properties: {
|
|
id: { type: 'string', required: true },
|
|
type: { type: 'string', required: true },
|
|
description: { type: 'string', required: true },
|
|
dependencies: { type: 'array' },
|
|
assignTo: { type: 'string' },
|
|
priority: { type: 'string' },
|
|
timeout: { type: 'number' },
|
|
},
|
|
},
|
|
};
|
|
|
|
function validateType(value: any, expectedType: string): boolean {
|
|
switch (expectedType) {
|
|
case 'string':
|
|
return typeof value === 'string';
|
|
case 'number':
|
|
return typeof value === 'number';
|
|
case 'boolean':
|
|
return typeof value === 'boolean';
|
|
case 'array':
|
|
return Array.isArray(value);
|
|
case 'object':
|
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function validateObject(obj: any, schema: ConfigSchema, path = ''): string[] {
|
|
const errors: string[] = [];
|
|
|
|
// Check required fields
|
|
for (const [key, spec] of Object.entries(schema)) {
|
|
if (spec.required && !(key in obj)) {
|
|
errors.push(`${path}${key} is required but missing`);
|
|
continue;
|
|
}
|
|
|
|
if (key in obj) {
|
|
const value = obj[key];
|
|
const currentPath = path ? `${path}${key}.` : `${key}.`;
|
|
|
|
if (!validateType(value, spec.type)) {
|
|
errors.push(`${path}${key} should be of type ${spec.type}, got ${typeof value}`);
|
|
continue;
|
|
}
|
|
|
|
// Validate nested objects
|
|
if (spec.type === 'object' && spec.properties) {
|
|
errors.push(...validateObject(value, spec.properties, currentPath));
|
|
}
|
|
|
|
// Validate array items
|
|
if (spec.type === 'array' && spec.properties && Array.isArray(value)) {
|
|
value.forEach((item, index) => {
|
|
if (typeof item === 'object') {
|
|
errors.push(...validateObject(item, spec.properties!, `${currentPath}[${index}].`));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
function validateClaudeFlowConfig(config: any): string[] {
|
|
return validateObject(config, CLAUDE_FLOW_CONFIG_SCHEMA);
|
|
}
|
|
|
|
function validateWorkflow(workflow: any): string[] {
|
|
const errors = validateObject(workflow, WORKFLOW_SCHEMA);
|
|
|
|
// Additional workflow-specific validations
|
|
if (workflow.tasks && Array.isArray(workflow.tasks)) {
|
|
const taskIds = new Set<string>();
|
|
|
|
for (const [index, task] of workflow.tasks.entries()) {
|
|
// Check for duplicate task IDs
|
|
if (taskIds.has(task.id)) {
|
|
errors.push(`Duplicate task ID "${task.id}" found at index ${index}`);
|
|
} else {
|
|
taskIds.add(task.id);
|
|
}
|
|
|
|
// Validate dependencies reference existing tasks
|
|
if (task.dependencies && Array.isArray(task.dependencies)) {
|
|
for (const dep of task.dependencies) {
|
|
if (!taskIds.has(dep) && !workflow.tasks.slice(0, index).some((t: any) => t.id === dep)) {
|
|
errors.push(`Task "${task.id}" depends on non-existent task "${dep}"`);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validate priority values
|
|
if (task.priority && !['low', 'medium', 'high', 'critical'].includes(task.priority)) {
|
|
errors.push(`Task "${task.id}" has invalid priority "${task.priority}"`);
|
|
}
|
|
|
|
// Validate timeout is positive
|
|
if (task.timeout && task.timeout <= 0) {
|
|
errors.push(`Task "${task.id}" has invalid timeout ${task.timeout}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
async function validateFile(filePath: string): Promise<ValidationResult> {
|
|
const result: ValidationResult = {
|
|
file: filePath,
|
|
valid: false,
|
|
errors: [],
|
|
};
|
|
|
|
try {
|
|
const content = await Deno.readTextFile(filePath);
|
|
const config = JSON.parse(content);
|
|
|
|
// Determine validation type based on file name/content
|
|
if (filePath.includes('workflow') || config.tasks) {
|
|
result.errors = validateWorkflow(config);
|
|
} else if (filePath.includes('config') || config.orchestrator) {
|
|
result.errors = validateClaudeFlowConfig(config);
|
|
} else {
|
|
result.errors.push('Unknown configuration file type');
|
|
}
|
|
|
|
result.valid = result.errors.length === 0;
|
|
} catch (error) {
|
|
if (error instanceof SyntaxError) {
|
|
result.errors.push(`Invalid JSON: ${error.message}`);
|
|
} else {
|
|
result.errors.push(`Failed to read file: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
console.log('Validating example configurations...\n');
|
|
|
|
const results: ValidationResult[] = [];
|
|
|
|
// Find all JSON files in examples directory
|
|
for await (const entry of walk('./examples', { exts: ['.json'] })) {
|
|
if (entry.isFile) {
|
|
const result = await validateFile(entry.path);
|
|
results.push(result);
|
|
}
|
|
}
|
|
|
|
// Also check any config files in the root
|
|
const rootConfigFiles = ['claude-flow.config.json', 'config.json'];
|
|
for (const filename of rootConfigFiles) {
|
|
try {
|
|
await Deno.stat(filename);
|
|
const result = await validateFile(filename);
|
|
results.push(result);
|
|
} catch {
|
|
// File doesn't exist, skip
|
|
}
|
|
}
|
|
|
|
if (results.length === 0) {
|
|
console.log('No configuration files found to validate.');
|
|
return;
|
|
}
|
|
|
|
// Report results
|
|
let hasErrors = false;
|
|
|
|
for (const result of results) {
|
|
if (result.valid) {
|
|
console.log(`✅ ${result.file}: Valid`);
|
|
} else {
|
|
console.log(`❌ ${result.file}: Invalid`);
|
|
for (const error of result.errors) {
|
|
console.log(` - ${error}`);
|
|
}
|
|
hasErrors = true;
|
|
}
|
|
}
|
|
|
|
console.log(`\nValidated ${results.length} configuration files.`);
|
|
|
|
if (hasErrors) {
|
|
console.error('❌ Some configuration files have errors!');
|
|
Deno.exit(1);
|
|
} else {
|
|
console.log('✅ All configuration files are valid!');
|
|
}
|
|
}
|
|
|
|
if (import.meta.main) {
|
|
await main();
|
|
} |