tasq/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js

378 lines
13 KiB
JavaScript

/**
* Dual-Mode Orchestrator
* Runs Claude Code and Codex workers in parallel with shared memory
*/
import { spawn } from 'child_process';
import { EventEmitter } from 'events';
/**
* Orchestrates parallel execution of Claude Code and Codex workers
*/
export class DualModeOrchestrator extends EventEmitter {
config;
workers = new Map();
processes = new Map();
constructor(config) {
super();
this.config = {
projectPath: config.projectPath,
maxConcurrent: config.maxConcurrent ?? 4,
sharedNamespace: config.sharedNamespace ?? 'collaboration',
timeout: config.timeout ?? 300000, // 5 minutes
claudeCommand: config.claudeCommand ?? 'claude',
codexCommand: config.codexCommand ?? 'claude', // Both use claude CLI
};
}
/**
* Initialize shared memory for collaboration
*/
async initializeSharedMemory(taskContext) {
const { projectPath, sharedNamespace } = this.config;
// Initialize memory database
await this.runCommand('npx', ['claude-flow@alpha', 'memory', 'init', '--force'], projectPath);
// Store task context
await this.runCommand('npx', [
'claude-flow@alpha', 'memory', 'store',
'--key', 'task-context',
'--value', taskContext,
'--namespace', sharedNamespace
], projectPath);
this.emit('memory:initialized', { namespace: sharedNamespace, taskContext });
}
/**
* Spawn a headless worker
*/
async spawnWorker(config) {
const result = {
id: config.id,
platform: config.platform,
role: config.role,
status: 'pending',
memoryKeys: [],
};
this.workers.set(config.id, result);
// Wait for dependencies
if (config.dependsOn?.length) {
await this.waitForDependencies(config.dependsOn);
}
result.status = 'running';
result.startedAt = new Date();
this.emit('worker:started', { id: config.id, role: config.role, platform: config.platform });
try {
const output = await this.executeHeadless(config);
result.status = 'completed';
result.output = output;
result.completedAt = new Date();
this.emit('worker:completed', { id: config.id, output: output.slice(0, 200) });
}
catch (error) {
result.status = 'failed';
result.error = error instanceof Error ? error.message : String(error);
result.completedAt = new Date();
this.emit('worker:failed', { id: config.id, error: result.error });
}
}
/**
* Execute a headless Claude/Codex instance
*/
async executeHeadless(config) {
const { projectPath, timeout } = this.config;
const command = config.platform === 'claude' ? this.config.claudeCommand : this.config.codexCommand;
// Build the prompt with memory integration
const enhancedPrompt = this.buildCollaborativePrompt(config);
const args = [
'-p', enhancedPrompt,
'--output-format', 'text',
];
if (config.maxTurns) {
args.push('--max-turns', String(config.maxTurns));
}
if (config.model) {
args.push('--model', config.model);
}
return new Promise((resolve, reject) => {
let output = '';
let errorOutput = '';
const proc = spawn(command, args, {
cwd: projectPath,
env: { ...process.env, FORCE_COLOR: '0' },
stdio: ['pipe', 'pipe', 'pipe'],
});
this.processes.set(config.id, proc);
proc.stdout?.on('data', (data) => {
output += data.toString();
});
proc.stderr?.on('data', (data) => {
errorOutput += data.toString();
});
const timer = setTimeout(() => {
proc.kill('SIGTERM');
reject(new Error(`Worker ${config.id} timed out after ${timeout}ms`));
}, timeout);
proc.on('close', (code) => {
clearTimeout(timer);
this.processes.delete(config.id);
if (code === 0 || output.length > 0) {
resolve(output || errorOutput);
}
else {
reject(new Error(`Worker ${config.id} exited with code ${code}: ${errorOutput}`));
}
});
proc.on('error', (err) => {
clearTimeout(timer);
this.processes.delete(config.id);
reject(err);
});
});
}
/**
* Build a prompt that includes memory coordination instructions
*/
buildCollaborativePrompt(config) {
const { sharedNamespace, projectPath } = this.config;
return `You are a ${config.role.toUpperCase()} agent in a collaborative dual-mode swarm.
Platform: ${config.platform === 'claude' ? 'Claude Code' : 'OpenAI Codex'}
Working Directory: ${projectPath}
Shared Memory Namespace: ${sharedNamespace}
COLLABORATION PROTOCOL:
1. Search shared memory for context: npx claude-flow@alpha memory search --query "<relevant terms>" --namespace ${sharedNamespace}
2. Complete your assigned task
3. Store your results: npx claude-flow@alpha memory store --key "${config.id}-result" --value "<your summary>" --namespace ${sharedNamespace}
YOUR TASK:
${config.prompt}
Remember: Other agents depend on your results in shared memory. Be concise and store actionable outputs.`;
}
/**
* Wait for dependent workers to complete
*/
async waitForDependencies(deps) {
const checkInterval = 500;
const maxWait = this.config.timeout;
let waited = 0;
while (waited < maxWait) {
const allComplete = deps.every(depId => {
const worker = this.workers.get(depId);
return worker && (worker.status === 'completed' || worker.status === 'failed');
});
if (allComplete)
return;
await new Promise(resolve => setTimeout(resolve, checkInterval));
waited += checkInterval;
}
throw new Error(`Dependencies [${deps.join(', ')}] did not complete in time`);
}
/**
* Run a swarm of collaborative workers
*/
async runCollaboration(workers, taskContext) {
const startTime = Date.now();
const errors = [];
// Initialize shared memory
await this.initializeSharedMemory(taskContext);
// Group workers by dependency level
const levels = this.buildDependencyLevels(workers);
// Execute each level in parallel
for (const level of levels) {
const promises = level.map(worker => this.spawnWorker(worker).catch(err => {
errors.push(`${worker.id}: ${err.message}`);
}));
await Promise.all(promises);
}
// Collect shared memory results
const sharedMemory = await this.collectSharedMemory();
return {
success: errors.length === 0,
workers: Array.from(this.workers.values()),
sharedMemory,
totalDuration: Date.now() - startTime,
errors,
};
}
/**
* Build dependency levels for parallel execution
*/
buildDependencyLevels(workers) {
const levels = [];
const placed = new Set();
while (placed.size < workers.length) {
const level = [];
for (const worker of workers) {
if (placed.has(worker.id))
continue;
const depsReady = !worker.dependsOn ||
worker.dependsOn.every(dep => placed.has(dep));
if (depsReady) {
level.push(worker);
}
}
if (level.length === 0 && placed.size < workers.length) {
// Circular dependency detected, add remaining
for (const worker of workers) {
if (!placed.has(worker.id)) {
level.push(worker);
}
}
}
for (const worker of level) {
placed.add(worker.id);
}
if (level.length > 0) {
levels.push(level);
}
}
return levels;
}
/**
* Collect results from shared memory
*/
async collectSharedMemory() {
const { projectPath, sharedNamespace } = this.config;
try {
const output = await this.runCommand('npx', ['claude-flow@alpha', 'memory', 'list', '--namespace', sharedNamespace, '--format', 'json'], projectPath);
return JSON.parse(output);
}
catch {
return {};
}
}
/**
* Run a command and return output
*/
runCommand(command, args, cwd) {
return new Promise((resolve, reject) => {
const proc = spawn(command, args, { cwd, stdio: ['pipe', 'pipe', 'pipe'] });
let output = '';
let error = '';
proc.stdout?.on('data', (data) => { output += data.toString(); });
proc.stderr?.on('data', (data) => { error += data.toString(); });
proc.on('close', (code) => {
if (code === 0) {
resolve(output);
}
else {
reject(new Error(error || `Command failed with code ${code}`));
}
});
proc.on('error', reject);
});
}
/**
* Stop all running workers
*/
stopAll() {
for (const [id, proc] of this.processes) {
proc.kill('SIGTERM');
this.emit('worker:stopped', { id });
}
this.processes.clear();
}
}
/**
* Pre-built collaboration templates
*/
export const CollaborationTemplates = {
/**
* Feature development swarm
*/
featureDevelopment: (feature) => [
{
id: 'architect',
platform: 'claude',
role: 'architect',
prompt: `Design the architecture for: ${feature}. Define components, interfaces, and data flow.`,
maxTurns: 10,
},
{
id: 'coder',
platform: 'codex',
role: 'coder',
prompt: `Implement the feature based on the architecture. Write clean, typed code.`,
dependsOn: ['architect'],
maxTurns: 15,
},
{
id: 'tester',
platform: 'codex',
role: 'tester',
prompt: `Write comprehensive tests for the implementation. Target 80% coverage.`,
dependsOn: ['coder'],
maxTurns: 10,
},
{
id: 'reviewer',
platform: 'claude',
role: 'reviewer',
prompt: `Review the code and tests for quality, security, and best practices.`,
dependsOn: ['coder', 'tester'],
maxTurns: 8,
},
],
/**
* Security audit swarm
*/
securityAudit: (target) => [
{
id: 'scanner',
platform: 'codex',
role: 'security-scanner',
prompt: `Scan ${target} for security vulnerabilities. Check OWASP Top 10.`,
maxTurns: 10,
},
{
id: 'analyzer',
platform: 'claude',
role: 'security-analyst',
prompt: `Analyze scan results and identify critical vulnerabilities.`,
dependsOn: ['scanner'],
maxTurns: 8,
},
{
id: 'fixer',
platform: 'codex',
role: 'security-fixer',
prompt: `Generate fixes for identified vulnerabilities.`,
dependsOn: ['analyzer'],
maxTurns: 12,
},
],
/**
* Refactoring swarm
*/
refactoring: (target) => [
{
id: 'analyzer',
platform: 'claude',
role: 'code-analyzer',
prompt: `Analyze ${target} for refactoring opportunities. Identify code smells.`,
maxTurns: 8,
},
{
id: 'planner',
platform: 'claude',
role: 'refactor-planner',
prompt: `Create a refactoring plan based on the analysis.`,
dependsOn: ['analyzer'],
maxTurns: 6,
},
{
id: 'refactorer',
platform: 'codex',
role: 'refactorer',
prompt: `Execute the refactoring plan. Maintain all existing functionality.`,
dependsOn: ['planner'],
maxTurns: 15,
},
{
id: 'validator',
platform: 'codex',
role: 'validator',
prompt: `Run tests and validate the refactoring didn't break anything.`,
dependsOn: ['refactorer'],
maxTurns: 5,
},
],
};
//# sourceMappingURL=orchestrator.js.map