Kraft102's picture
Update backend source
34367da verified
/**
* ╔═══════════════════════════════════════════════════════════════════════════╗
* β•‘ 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<string, unknown>;
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<string, ActionRequest> = 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<string, unknown>;
requestedBy: string;
}): Promise<ActionRequest> {
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<ActionRequest | null> {
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<ActionRequest> {
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<ActionRequest> {
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<ActionResult> {
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<ActionResult> {
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<ActionResult> {
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<ActionResult> {
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<ActionResult> {
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<ActionResult> {
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<ActionResult> {
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<ActionResult> {
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<ActionResult> {
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<void> {
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<void> {
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();