/** * ╔═══════════════════════════════════════════════════════════════════════════╗ * ║ MOTOR CORTEX SERVICE ║ * ║═══════════════════════════════════════════════════════════════════════════║ * ║ The system's "hands" - executes actions in the real world ║ * ║ Git operations, file management, deployments, shell commands ║ * ║ All actions require approval workflow for safety ║ * ╚═══════════════════════════════════════════════════════════════════════════╝ */ import { exec } from 'child_process'; import { promisify } from 'util'; import * as fs from 'fs/promises'; import * as path from 'path'; import { neo4jAdapter } from '../adapters/Neo4jAdapter.js'; import { v4 as uuidv4 } from 'uuid'; const execAsync = promisify(exec); // ═══════════════════════════════════════════════════════════════════════════ // Types // ═══════════════════════════════════════════════════════════════════════════ export type ActionType = | 'GIT_COMMIT' | 'GIT_PUSH' | 'GIT_BRANCH' | 'GIT_MERGE' | 'FILE_CREATE' | 'FILE_MODIFY' | 'FILE_DELETE' | 'SHELL_COMMAND' | 'NPM_INSTALL' | 'NPM_RUN' | 'DEPLOY' | 'RESTART_SERVICE'; export type ActionStatus = 'PENDING' | 'APPROVED' | 'REJECTED' | 'EXECUTING' | 'COMPLETED' | 'FAILED'; export interface ActionRequest { id: string; type: ActionType; description: string; command?: string; targetPath?: string; content?: string; params?: Record; requestedBy: string; requestedAt: string; status: ActionStatus; requiresApproval: boolean; approvedBy?: string; approvedAt?: string; executedAt?: string; result?: ActionResult; riskLevel: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'; } export interface ActionResult { success: boolean; output?: string; error?: string; duration_ms: number; artifacts?: string[]; } // ═══════════════════════════════════════════════════════════════════════════ // Motor Cortex Service // ═══════════════════════════════════════════════════════════════════════════ class MotorCortexService { private static instance: MotorCortexService; private actionQueue: Map = new Map(); private actionHistory: ActionRequest[] = []; // Safety configuration private readonly AUTO_APPROVE_RISK_LEVELS: ActionRequest['riskLevel'][] = ['LOW']; private readonly BLOCKED_COMMANDS = [ /rm\s+-rf\s+\//, // rm -rf / /mkfs/, // format disk /dd\s+if=/, // disk destroyer /:(){ :|:& };:/, // fork bomb />\s*\/dev\/sd/, // write to disk device /shutdown/, // system shutdown /reboot/, // system reboot /init\s+0/, // halt system ]; private readonly PROJECT_ROOT = process.cwd(); private constructor() { this.loadHistoryFromNeo4j(); } public static getInstance(): MotorCortexService { if (!MotorCortexService.instance) { MotorCortexService.instance = new MotorCortexService(); } return MotorCortexService.instance; } // ═══════════════════════════════════════════════════════════════════════ // Action Request & Approval // ═══════════════════════════════════════════════════════════════════════ /** * Request an action to be executed */ public async requestAction(params: { type: ActionType; description: string; command?: string; targetPath?: string; content?: string; params?: Record; requestedBy: string; }): Promise { const riskLevel = this.assessRisk(params); const action: ActionRequest = { id: `action-${uuidv4()}`, type: params.type, description: params.description, command: params.command, targetPath: params.targetPath, content: params.content, params: params.params, requestedBy: params.requestedBy, requestedAt: new Date().toISOString(), status: 'PENDING', requiresApproval: !this.AUTO_APPROVE_RISK_LEVELS.includes(riskLevel), riskLevel }; // Check for blocked commands if (params.command && this.isBlockedCommand(params.command)) { action.status = 'REJECTED'; action.result = { success: false, error: 'Command blocked for safety reasons', duration_ms: 0 }; console.error(`[MotorCortex] 🚫 Blocked dangerous command: ${params.command}`); return action; } this.actionQueue.set(action.id, action); // Auto-approve low-risk actions if (!action.requiresApproval) { return await this.approveAndExecute(action.id, 'SYSTEM_AUTO'); } // Persist pending action await this.persistAction(action); console.error(`[MotorCortex] 📋 Action queued (${action.riskLevel}): ${action.description}`); return action; } /** * Approve a pending action */ public async approveAction(actionId: string, approvedBy: string): Promise { const action = this.actionQueue.get(actionId); if (!action || action.status !== 'PENDING') { return null; } return await this.approveAndExecute(actionId, approvedBy); } /** * Reject a pending action */ public rejectAction(actionId: string, rejectedBy: string): ActionRequest | null { const action = this.actionQueue.get(actionId); if (!action || action.status !== 'PENDING') { return null; } action.status = 'REJECTED'; action.approvedBy = rejectedBy; action.approvedAt = new Date().toISOString(); this.actionHistory.push(action); this.actionQueue.delete(actionId); console.error(`[MotorCortex] ❌ Action rejected: ${action.description}`); return action; } /** * Approve and execute action */ private async approveAndExecute(actionId: string, approvedBy: string): Promise { const action = this.actionQueue.get(actionId)!; action.status = 'APPROVED'; action.approvedBy = approvedBy; action.approvedAt = new Date().toISOString(); console.error(`[MotorCortex] ✅ Action approved: ${action.description}`); return await this.executeAction(action); } // ═══════════════════════════════════════════════════════════════════════ // Action Execution // ═══════════════════════════════════════════════════════════════════════ /** * Execute an approved action */ private async executeAction(action: ActionRequest): Promise { action.status = 'EXECUTING'; action.executedAt = new Date().toISOString(); const startTime = Date.now(); try { let result: ActionResult; switch (action.type) { case 'GIT_COMMIT': result = await this.executeGitCommit(action); break; case 'GIT_PUSH': result = await this.executeGitPush(action); break; case 'GIT_BRANCH': result = await this.executeGitBranch(action); break; case 'FILE_CREATE': result = await this.executeFileCreate(action); break; case 'FILE_MODIFY': result = await this.executeFileModify(action); break; case 'FILE_DELETE': result = await this.executeFileDelete(action); break; case 'SHELL_COMMAND': result = await this.executeShellCommand(action); break; case 'NPM_INSTALL': result = await this.executeNpmInstall(action); break; case 'NPM_RUN': result = await this.executeNpmRun(action); break; default: result = { success: false, error: `Unsupported action type: ${action.type}`, duration_ms: Date.now() - startTime }; } action.result = result; action.status = result.success ? 'COMPLETED' : 'FAILED'; } catch (error: any) { action.status = 'FAILED'; action.result = { success: false, error: error.message, duration_ms: Date.now() - startTime }; } // Move to history this.actionHistory.push(action); this.actionQueue.delete(action.id); // Persist result await this.persistAction(action); const emoji = action.status === 'COMPLETED' ? '✅' : '❌'; console.error(`[MotorCortex] ${emoji} Action ${action.status}: ${action.description}`); return action; } // ═══════════════════════════════════════════════════════════════════════ // Specific Action Handlers // ═══════════════════════════════════════════════════════════════════════ private async executeGitCommit(action: ActionRequest): Promise { const message = action.params?.message as string || action.description; const startTime = Date.now(); try { // Stage all changes await execAsync('git add -A', { cwd: this.PROJECT_ROOT }); // Commit const { stdout, stderr } = await execAsync( `git commit -m "${message.replace(/"/g, '\\"')}"`, { cwd: this.PROJECT_ROOT } ); return { success: true, output: stdout || stderr, duration_ms: Date.now() - startTime }; } catch (error: any) { return { success: false, error: error.message, duration_ms: Date.now() - startTime }; } } private async executeGitPush(action: ActionRequest): Promise { const branch = action.params?.branch as string || 'main'; const startTime = Date.now(); try { const { stdout, stderr } = await execAsync( `git push origin ${branch}`, { cwd: this.PROJECT_ROOT } ); return { success: true, output: stdout || stderr, duration_ms: Date.now() - startTime }; } catch (error: any) { return { success: false, error: error.message, duration_ms: Date.now() - startTime }; } } private async executeGitBranch(action: ActionRequest): Promise { const branchName = action.params?.branchName as string; const checkout = action.params?.checkout as boolean; const startTime = Date.now(); try { const command = checkout ? `git checkout -b ${branchName}` : `git branch ${branchName}`; const { stdout, stderr } = await execAsync(command, { cwd: this.PROJECT_ROOT }); return { success: true, output: stdout || stderr, duration_ms: Date.now() - startTime }; } catch (error: any) { return { success: false, error: error.message, duration_ms: Date.now() - startTime }; } } private async executeFileCreate(action: ActionRequest): Promise { const startTime = Date.now(); const targetPath = action.targetPath!; const content = action.content || ''; try { // Ensure directory exists await fs.mkdir(path.dirname(targetPath), { recursive: true }); // Create file await fs.writeFile(targetPath, content, 'utf-8'); return { success: true, output: `Created: ${targetPath}`, duration_ms: Date.now() - startTime, artifacts: [targetPath] }; } catch (error: any) { return { success: false, error: error.message, duration_ms: Date.now() - startTime }; } } private async executeFileModify(action: ActionRequest): Promise { const startTime = Date.now(); const targetPath = action.targetPath!; const content = action.content!; try { await fs.writeFile(targetPath, content, 'utf-8'); return { success: true, output: `Modified: ${targetPath}`, duration_ms: Date.now() - startTime, artifacts: [targetPath] }; } catch (error: any) { return { success: false, error: error.message, duration_ms: Date.now() - startTime }; } } private async executeFileDelete(action: ActionRequest): Promise { const startTime = Date.now(); const targetPath = action.targetPath!; try { await fs.unlink(targetPath); return { success: true, output: `Deleted: ${targetPath}`, duration_ms: Date.now() - startTime }; } catch (error: any) { return { success: false, error: error.message, duration_ms: Date.now() - startTime }; } } private async executeShellCommand(action: ActionRequest): Promise { const startTime = Date.now(); const command = action.command!; try { const { stdout, stderr } = await execAsync(command, { cwd: this.PROJECT_ROOT, timeout: 60000 // 1 minute timeout }); return { success: true, output: stdout || stderr, duration_ms: Date.now() - startTime }; } catch (error: any) { return { success: false, error: error.message, output: error.stdout || error.stderr, duration_ms: Date.now() - startTime }; } } private async executeNpmInstall(action: ActionRequest): Promise { const startTime = Date.now(); const packageName = action.params?.package as string; try { const command = packageName ? `npm install ${packageName}` : 'npm install'; const { stdout, stderr } = await execAsync(command, { cwd: this.PROJECT_ROOT, timeout: 300000 // 5 minute timeout }); return { success: true, output: stdout || stderr, duration_ms: Date.now() - startTime }; } catch (error: any) { return { success: false, error: error.message, duration_ms: Date.now() - startTime }; } } private async executeNpmRun(action: ActionRequest): Promise { const startTime = Date.now(); const script = action.params?.script as string; try { const { stdout, stderr } = await execAsync(`npm run ${script}`, { cwd: this.PROJECT_ROOT, timeout: 300000 // 5 minute timeout }); return { success: true, output: stdout || stderr, duration_ms: Date.now() - startTime }; } catch (error: any) { return { success: false, error: error.message, duration_ms: Date.now() - startTime }; } } // ═══════════════════════════════════════════════════════════════════════ // Risk Assessment // ═══════════════════════════════════════════════════════════════════════ private assessRisk(params: { type: ActionType; command?: string; targetPath?: string }): ActionRequest['riskLevel'] { // File deletion is high risk if (params.type === 'FILE_DELETE') return 'HIGH'; // Git push is medium risk if (params.type === 'GIT_PUSH' || params.type === 'GIT_MERGE') return 'MEDIUM'; // Deployment is critical if (params.type === 'DEPLOY' || params.type === 'RESTART_SERVICE') return 'CRITICAL'; // Shell commands need careful assessment if (params.type === 'SHELL_COMMAND' && params.command) { if (/sudo|rm|chmod|chown/i.test(params.command)) return 'HIGH'; if (/curl|wget|npm|pip/i.test(params.command)) return 'MEDIUM'; } // File creation/modification outside project if (params.targetPath && !params.targetPath.startsWith(this.PROJECT_ROOT)) { return 'HIGH'; } return 'LOW'; } private isBlockedCommand(command: string): boolean { return this.BLOCKED_COMMANDS.some(pattern => pattern.test(command)); } // ═══════════════════════════════════════════════════════════════════════ // Query Functions // ═══════════════════════════════════════════════════════════════════════ public getPendingActions(): ActionRequest[] { return Array.from(this.actionQueue.values()).filter(a => a.status === 'PENDING'); } public getActionHistory(limit: number = 50): ActionRequest[] { return this.actionHistory.slice(-limit); } public getAction(actionId: string): ActionRequest | undefined { return this.actionQueue.get(actionId) || this.actionHistory.find(a => a.id === actionId); } public getStatus(): { pendingActions: number; executingActions: number; completedToday: number; failedToday: number; } { const today = new Date().toISOString().split('T')[0]; const todayActions = this.actionHistory.filter(a => a.executedAt?.startsWith(today) ); return { pendingActions: this.getPendingActions().length, executingActions: Array.from(this.actionQueue.values()).filter(a => a.status === 'EXECUTING').length, completedToday: todayActions.filter(a => a.status === 'COMPLETED').length, failedToday: todayActions.filter(a => a.status === 'FAILED').length }; } // ═══════════════════════════════════════════════════════════════════════ // Persistence // ═══════════════════════════════════════════════════════════════════════ private async persistAction(action: ActionRequest): Promise { try { await neo4jAdapter.executeQuery(` MERGE (a:Action {id: $id}) SET a.type = $type, a.description = $description, a.status = $status, a.riskLevel = $riskLevel, a.requestedBy = $requestedBy, a.requestedAt = $requestedAt, a.approvedBy = $approvedBy, a.executedAt = $executedAt, a.success = $success `, { id: action.id, type: action.type, description: action.description, status: action.status, riskLevel: action.riskLevel, requestedBy: action.requestedBy, requestedAt: action.requestedAt, approvedBy: action.approvedBy || '', executedAt: action.executedAt || '', success: action.result?.success ?? null }); } catch (error) { console.warn('[MotorCortex] Failed to persist action:', error); } } private async loadHistoryFromNeo4j(): Promise { try { const results = await neo4jAdapter.executeQuery(` MATCH (a:Action) WHERE a.executedAt IS NOT NULL RETURN a ORDER BY a.executedAt DESC LIMIT 100 `); // Load into history (simplified) console.error(`[MotorCortex] 🔄 Loaded ${results.length} actions from history`); } catch (error) { console.warn('[MotorCortex] Failed to load history:', error); } } } export const motorCortex = MotorCortexService.getInstance();