jay-code
Version:
Streamlined AI CLI orchestration engine with mathematical rigor and enterprise-grade reliability
909 lines (810 loc) ⢠31.8 kB
text/typescript
/**
* Simple orchestrator implementation for Node.js compatibility
*/
import { EventEmitter } from 'events';
import express from 'express';
import { WebSocketServer } from 'ws';
import { createServer } from 'http';
import { spawn } from 'child_process';
import path from 'path';
import { fileURLToPath } from 'url';
import cors from 'cors';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Simple in-memory stores
const agents = new Map();
const tasks = new Map();
const memory = new Map();
// Event bus
const eventBus = new EventEmitter();
// Component status
const componentStatus = {
eventBus: false,
orchestrator: false,
memoryManager: false,
terminalPool: false,
mcpServer: false,
coordinationManager: false,
webUI: false,
};
// Simple MCP server
function startMCPServer(port: number) {
console.log(`š Starting MCP server on port ${port}...`);
// In a real implementation, this would start the actual MCP server
componentStatus.mcpServer = true;
return true;
}
// Enhanced web UI with console interface
function startWebUI(host: string, port: number) {
const app = express();
const server = createServer(app);
const wss = new WebSocketServer({ server });
// Add CORS middleware for cross-origin support
app.use(
cors({
origin: '*',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
}),
);
// Global error handler middleware
app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
console.error('Global error handler:', err);
res.status(err.status || 500).json({
error: err.message || 'Internal server error',
timestamp: new Date().toISOString(),
});
});
// Request logging middleware
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} ${req.method} ${req.path}`);
next();
});
// Store CLI output history and active connections
const outputHistory: string[] = [];
const activeConnections: Set<any> = new Set();
// CLI output capture system
let cliProcess: any = null;
const consoleHTML = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jay-Code Console</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
background: #0d1117;
color: #c9d1d9;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
.header {
background: #161b22;
border-bottom: 1px solid #21262d;
padding: 10px 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.title {
font-weight: bold;
color: #58a6ff;
}
.connection-status {
font-size: 12px;
color: #7c3aed;
}
.console-container {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.console-output {
flex: 1;
overflow-y: auto;
padding: 10px;
background: #0d1117;
font-size: 13px;
line-height: 1.4;
white-space: pre-wrap;
word-wrap: break-word;
}
.console-input {
background: #161b22;
border: none;
border-top: 1px solid #21262d;
padding: 10px;
color: #c9d1d9;
font-family: inherit;
font-size: 13px;
outline: none;
}
.console-input:focus {
background: #21262d;
}
.prompt {
color: #58a6ff;
font-weight: bold;
}
.error {
color: #ff7b72;
}
.success {
color: #3fb950;
}
.warning {
color: #ffa657;
}
.info {
color: #79c0ff;
}
.dim {
color: #8b949e;
}
.scrollbar {
scrollbar-width: thin;
scrollbar-color: #21262d #0d1117;
}
.scrollbar::-webkit-scrollbar {
width: 8px;
}
.scrollbar::-webkit-scrollbar-track {
background: #0d1117;
}
.scrollbar::-webkit-scrollbar-thumb {
background: #21262d;
border-radius: 4px;
}
.scrollbar::-webkit-scrollbar-thumb:hover {
background: #30363d;
}
.system-status {
display: flex;
gap: 15px;
font-size: 11px;
}
.status-item {
display: flex;
align-items: center;
gap: 5px;
}
.status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: #3fb950;
}
.status-dot.inactive {
background: #f85149;
}
</style>
</head>
<body>
<div class="header">
<div class="title">š§ Jay-Code Console</div>
<div class="system-status">
<div class="status-item">
<div class="status-dot" id="ws-status"></div>
<span id="ws-text">Connecting...</span>
</div>
<div class="status-item">
<div class="status-dot" id="cli-status"></div>
<span id="cli-text">CLI Ready</span>
</div>
</div>
</div>
<div class="console-container">
<div class="console-output scrollbar" id="output"></div>
<input type="text" class="console-input" id="input" placeholder="Enter jay-code command..." autocomplete="off">
</div>
<script>
const output = document.getElementById('output');
const input = document.getElementById('input');
const wsStatus = document.getElementById('ws-status');
const wsText = document.getElementById('ws-text');
const cliStatus = document.getElementById('cli-status');
const cliText = document.getElementById('cli-text');
let ws = null;
let commandHistory = [];
let historyIndex = -1;
let reconnectAttempts = 0;
let reconnectTimer = null;
let isReconnecting = false;
const MAX_RECONNECT_ATTEMPTS = 10;
const BASE_RECONNECT_DELAY = 1000;
function getReconnectDelay() {
// Exponential backoff with jitter
const exponentialDelay = Math.min(BASE_RECONNECT_DELAY * Math.pow(2, reconnectAttempts), 30000);
const jitter = Math.random() * 0.3 * exponentialDelay;
return exponentialDelay + jitter;
}
function connect() {
if (isReconnecting || (ws && ws.readyState === WebSocket.CONNECTING)) {
console.log('Already connecting, skipping duplicate attempt');
return;
}
isReconnecting = true;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = \`\${protocol}//\${window.location.host}\`;
try {
console.log(\`Attempting WebSocket connection to \${wsUrl}\`);
ws = new WebSocket(wsUrl);
ws.onopen = () => {
console.log('WebSocket connected successfully');
wsStatus.classList.remove('inactive');
wsText.textContent = 'Connected';
reconnectAttempts = 0;
isReconnecting = false;
if (reconnectTimer) {
clearTimeout(reconnectTimer);
reconnectTimer = null;
}
appendOutput('\n<span class="success">š Connected to Jay-Code Console</span>\n');
appendOutput('<span class="info">Type "help" for available commands or use any jay-code command</span>\n\n');
};
ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
handleMessage(data);
} catch (error) {
console.error('Failed to parse WebSocket message:', error);
appendOutput(\`\n<span class="error">ā Invalid message received: \${(error instanceof Error ? error.message : String(error))}</span>\n\`);
}
};
ws.onclose = (event) => {
console.log(\`WebSocket closed: code=\${event.code}, reason=\${event.reason}\`);
wsStatus.classList.add('inactive');
wsText.textContent = 'Disconnected';
isReconnecting = false;
if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
reconnectAttempts++;
const delay = getReconnectDelay();
appendOutput(\`\n<span class="error">š Connection lost. Reconnecting in \${Math.round(delay/1000)}s... (attempt \${reconnectAttempts}/\${MAX_RECONNECT_ATTEMPTS})</span>\n\`);
reconnectTimer = setTimeout(() => {
reconnectTimer = null;
connect();
}, delay);
} else {
appendOutput(\`\n<span class="error">ā Failed to reconnect after \${MAX_RECONNECT_ATTEMPTS} attempts. Please refresh the page.</span>\n\`);
wsText.textContent = 'Failed to connect';
}
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
appendOutput(\`\n<span class="error">ā WebSocket error: \${(error instanceof Error ? error.message : String(error)) || 'Connection failed'}</span>\n\`);
isReconnecting = false;
};
} catch (error) {
console.error('Failed to create WebSocket:', error);
appendOutput(\`\n<span class="error">ā Failed to create WebSocket connection: \${(error instanceof Error ? error.message : String(error))}</span>\n\`);
isReconnecting = false;
// Try reconnect if not exceeded max attempts
if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
reconnectAttempts++;
const delay = getReconnectDelay();
reconnectTimer = setTimeout(() => {
reconnectTimer = null;
connect();
}, delay);
}
}
}
function handleMessage(data) {
switch (data.type) {
case 'output':
appendOutput(data.data);
break;
case 'error':
appendOutput('<span class="error">' + data.data + '</span>');
break;
case 'command_complete':
appendOutput('\n<span class="prompt">jay-code> </span>');
break;
case 'status':
updateStatus(data.data);
break;
}
}
function appendOutput(text) {
output.innerHTML += text;
output.scrollTop = output.scrollHeight;
}
function updateStatus(status) {
// Update CLI status based on server response
if (status.cliActive) {
cliStatus.classList.remove('inactive');
cliText.textContent = 'CLI Active';
} else {
cliStatus.classList.add('inactive');
cliText.textContent = 'CLI Inactive';
}
}
function sendCommand(command) {
if (ws && ws.readyState === WebSocket.OPEN) {
appendOutput('<span class="prompt">jay-code> </span>' + command + '\n');
ws.send(JSON.stringify({
type: 'command',
data: command
}));
// Add to history
if (command.trim() && commandHistory[commandHistory.length - 1] !== command) {
commandHistory.push(command);
if (commandHistory.length > 100) {
commandHistory.shift();
}
}
historyIndex = commandHistory.length;
}
}
// Input handling
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
const command = input.value.trim();
if (command) {
sendCommand(command);
input.value = '';
}
} else if (e.key === 'ArrowUp') {
e.preventDefault();
if (historyIndex > 0) {
historyIndex--;
input.value = commandHistory[historyIndex] || '';
}
} else if (e.key === 'ArrowDown') {
e.preventDefault();
if (historyIndex < commandHistory.length - 1) {
historyIndex++;
input.value = commandHistory[historyIndex] || '';
} else {
historyIndex = commandHistory.length;
input.value = '';
}
} else if (e.key === 'Tab') {
e.preventDefault();
// Basic tab completion for common commands
const value = input.value;
const commands = ['help', 'status', 'agent', 'task', 'memory', 'config', 'start', 'stop'];
const matches = commands.filter(cmd => cmd.startsWith(value));
if (matches.length === 1) {
input.value = matches[0] + ' ';
}
}
});
// Focus input on page load
window.addEventListener('load', () => {
input.focus();
connect();
});
// Implement heartbeat to detect stale connections
setInterval(() => {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping', timestamp: Date.now() }));
}
}, 30000); // Ping every 30 seconds
// Handle page visibility changes
document.addEventListener('visibilitychange', () => {
if (!document.hidden && ws && ws.readyState !== WebSocket.OPEN) {
console.log('Page became visible, checking connection...');
reconnectAttempts = 0; // Reset attempts when page becomes visible
connect();
}
});
// Keep input focused
document.addEventListener('click', () => {
input.focus();
});
</script>
</body>
</html>
`;
app.get('/', (req, res) => {
res.send(consoleHTML);
});
// API endpoints
app.get('/api/status', (req, res) => {
res.json({
components: componentStatus,
metrics: {
agents: agents.size,
tasks: tasks.size,
memory: memory.size,
connectedClients: activeConnections.size,
},
});
});
app.get('/api/history', (req, res) => {
const limit = parseInt(req.query.limit as string) || 100;
res.json({
history: outputHistory.slice(-limit),
total: outputHistory.length,
});
});
app.post('/api/command', express.json(), (req, res) => {
const { command } = req.body;
if (!command) {
res.status(400).json({ error: 'Command is required' });
return;
}
// Execute command and return immediately
// Output will be sent via WebSocket
try {
broadcastToClients({
type: 'output',
data: `<span class="prompt">API> </span>${command}\\n`,
});
executeCliCommand(command, null);
res.json({ success: true, message: 'Command executed' });
} catch (error) {
res.status(500).json({ error: error instanceof Error ? error.message : String(error) });
}
});
app.get('/api/agents', (req, res) => {
const agentList = Array.from(agents.entries()).map(([id, agent]) => ({
id,
...agent,
}));
res.json(agentList);
});
app.get('/api/tasks', (req, res) => {
const taskList = Array.from(tasks.entries()).map(([id, task]) => ({
id,
...task,
}));
res.json(taskList);
});
app.get('/api/memory', (req, res) => {
const memoryList = Array.from(memory.entries()).map(([key, value]) => ({
key,
value,
type: typeof value,
size: JSON.stringify(value).length,
}));
res.json(memoryList);
});
// Health check endpoint
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
components: componentStatus,
});
});
// WebSocket for real-time CLI interaction
wss.on('connection', (ws, req) => {
const clientIp = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
console.log(`š WebSocket client connected from ${clientIp}`);
activeConnections.add(ws);
// Send initial status and history
ws.send(
JSON.stringify({
type: 'status',
data: { ...componentStatus, cliActive: true },
}),
);
// Send recent output history
outputHistory.slice(-50).forEach((line) => {
ws.send(
JSON.stringify({
type: 'output',
data: line,
}),
);
});
// Handle incoming commands
ws.on('message', (message) => {
try {
const data = JSON.parse(message.toString());
console.log(`Received command from client: ${data.type}`);
if (data.type === 'command') {
handleCliCommand(data.data, ws);
} else if (data.type === 'ping') {
// Handle ping/pong for connection keepalive
ws.send(JSON.stringify({ type: 'pong', timestamp: Date.now() }));
}
} catch (error) {
console.error('Failed to handle WebSocket message:', error);
ws.send(
JSON.stringify({
type: 'error',
data: `Invalid message format: ${error instanceof Error ? error.message : String(error)}`,
timestamp: new Date().toISOString(),
}),
);
}
});
ws.on('close', () => {
console.log('š WebSocket client disconnected');
activeConnections.delete(ws);
});
ws.on('error', (error) => {
console.error('WebSocket client error:', error);
// Send detailed error information to client before closing
try {
ws.send(
JSON.stringify({
type: 'error',
data: `Server WebSocket error: ${(error instanceof Error ? error.message : String(error)) || 'Unknown error'}`,
timestamp: new Date().toISOString(),
}),
);
} catch (sendError) {
console.error('Failed to send error to client:', sendError);
}
activeConnections.delete(ws);
});
});
// Helper function to send response to specific client or broadcast
function sendResponse(ws: any, data: any) {
if (ws) {
ws.send(JSON.stringify(data));
} else {
broadcastToClients(data);
}
}
// CLI command execution handler
function handleCliCommand(command: string, ws: any) {
try {
// Add timestamp and format output
const timestamp = new Date().toLocaleTimeString();
const logEntry = `[${timestamp}] Executing: ${command}`;
outputHistory.push(logEntry);
// Broadcast to all connected clients
broadcastToClients({
type: 'output',
data: `<span class="dim">[${timestamp}]</span> <span class="info">Executing:</span> ${command}\\n`,
});
// Execute the command
executeCliCommand(command, ws);
} catch (error) {
const errorMsg = `Error executing command: ${error instanceof Error ? error.message : String(error)}`;
outputHistory.push(errorMsg);
sendResponse(ws, {
type: 'error',
data: errorMsg,
});
}
}
// Execute CLI commands and capture output
function executeCliCommand(command: string, ws: any) {
// Handle built-in commands first
if (command === 'help') {
const helpText = `<span class="success">Available Commands:</span>
⢠<span class="info">help</span> - Show this help message
⢠<span class="info">status</span> - Show system status
⢠<span class="info">agent list</span> - List all agents
⢠<span class="info">agent spawn [type]</span> - Spawn a new agent
⢠<span class="info">task list</span> - List all tasks
⢠<span class="info">memory list</span> - List memory entries
⢠<span class="info">config show</span> - Show configuration
⢠<span class="info">clear</span> - Clear console
⢠<span class="info">version</span> - Show version information
<span class="warning">Note:</span> This is a web console interface for jay-code CLI commands.
`;
sendResponse(ws, {
type: 'output',
data: helpText,
});
sendResponse(ws, { type: 'command_complete' });
return;
}
if (command === 'clear') {
sendResponse(ws, {
type: 'output',
data: '\\x1b[2J\\x1b[H', // ANSI clear screen
});
sendResponse(ws, { type: 'command_complete' });
return;
}
if (command === 'status') {
const statusText = `<span class="success">System Status:</span>
⢠Event Bus: <span class="${componentStatus.eventBus ? 'success' : 'error'}">${componentStatus.eventBus ? 'Active' : 'Inactive'}</span>
⢠Orchestrator: <span class="${componentStatus.orchestrator ? 'success' : 'error'}">${componentStatus.orchestrator ? 'Active' : 'Inactive'}</span>
⢠Memory Manager: <span class="${componentStatus.memoryManager ? 'success' : 'error'}">${componentStatus.memoryManager ? 'Active' : 'Inactive'}</span>
⢠Terminal Pool: <span class="${componentStatus.terminalPool ? 'success' : 'error'}">${componentStatus.terminalPool ? 'Active' : 'Inactive'}</span>
⢠MCP Server: <span class="${componentStatus.mcpServer ? 'success' : 'error'}">${componentStatus.mcpServer ? 'Active' : 'Inactive'}</span>
⢠Coordination Manager: <span class="${componentStatus.coordinationManager ? 'success' : 'error'}">${componentStatus.coordinationManager ? 'Active' : 'Inactive'}</span>
⢠Web UI: <span class="${componentStatus.webUI ? 'success' : 'error'}">${componentStatus.webUI ? 'Active' : 'Inactive'}</span>
<span class="info">Metrics:</span>
⢠Active Agents: ${agents.size}
⢠Pending Tasks: ${tasks.size}
⢠Memory Entries: ${memory.size}
`;
sendResponse(ws, {
type: 'output',
data: statusText,
});
sendResponse(ws, { type: 'command_complete' });
return;
}
// For other commands, spawn a subprocess
const args = command.split(' ');
const cmd = args[0];
const cmdArgs = args.slice(1);
// Determine the correct jay-code executable path
const rootDir = path.resolve(__dirname, '../..');
const cliPath = path.join(rootDir, 'bin', 'jay-code');
// Spawn the command
const child = spawn('node', [path.join(rootDir, 'src/cli/simple-cli.js'), ...cmdArgs], {
stdio: ['pipe', 'pipe', 'pipe'],
env: { ...process.env, JAY_CODE_WEB_MODE: 'true' },
});
// Handle stdout
child.stdout.on('data', (data) => {
const output = data.toString();
outputHistory.push(output);
// Convert ANSI colors to HTML spans
const htmlOutput = convertAnsiToHtml(output);
broadcastToClients({
type: 'output',
data: htmlOutput,
});
});
// Handle stderr
child.stderr.on('data', (data) => {
const error = data.toString();
outputHistory.push(error);
broadcastToClients({
type: 'error',
data: convertAnsiToHtml(error),
});
});
// Handle process exit
child.on('close', (code) => {
const exitMsg =
code === 0
? `<span class="success">Command completed successfully</span>`
: `<span class="error">Command failed with exit code ${code}</span>`;
broadcastToClients({
type: 'output',
data: `\\n${exitMsg}\\n`,
});
sendResponse(ws, { type: 'command_complete' });
});
child.on('error', (error) => {
const errorMsg = `<span class="error">Failed to execute command: ${error instanceof Error ? error.message : String(error)}</span>`;
outputHistory.push(errorMsg);
sendResponse(ws, {
type: 'error',
data: errorMsg,
});
sendResponse(ws, { type: 'command_complete' });
});
}
// Broadcast message to all connected clients
function broadcastToClients(message: any) {
const messageStr = JSON.stringify(message);
activeConnections.forEach((client) => {
if (client.readyState === 1) {
// WebSocket.OPEN
client.send(messageStr);
}
});
}
// Convert ANSI escape codes to HTML
function convertAnsiToHtml(text: string): string {
return text
.replace(/\x1b\[0m/g, '</span>')
.replace(/\x1b\[1m/g, '<span style="font-weight: bold;">')
.replace(/\x1b\[31m/g, '<span class="error">')
.replace(/\x1b\[32m/g, '<span class="success">')
.replace(/\x1b\[33m/g, '<span class="warning">')
.replace(/\x1b\[34m/g, '<span class="info">')
.replace(/\x1b\[35m/g, '<span style="color: #d946ef;">')
.replace(/\x1b\[36m/g, '<span style="color: #06b6d4;">')
.replace(/\x1b\[37m/g, '<span class="dim">')
.replace(/\x1b\[90m/g, '<span class="dim">')
.replace(/\x1b\[[0-9;]*m/g, '') // Remove any remaining ANSI codes
.replace(/\n/g, '\\n')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/<span/g, '<span')
.replace(/span>/g, 'span>');
}
return new Promise((resolve, reject) => {
server.on('error', (err: any) => {
if (err.code === 'EADDRINUSE') {
console.error(`\nā Port ${port} is already in use`);
console.log(`š” Try a different port: jay-code start --ui --port ${port + 1}`);
console.log(`š” Or stop the process using port ${port}: lsof -ti:${port} | xargs kill -9`);
componentStatus.webUI = false;
reject(err);
} else {
console.error('ā Web UI server error:', err.message, err.stack);
reject(err);
}
});
server.listen(port, host, () => {
console.log(`š Web UI available at http://${host}:${port}`);
componentStatus.webUI = true;
resolve(server);
});
});
}
// Start all components
export async function startOrchestrator(options: any) {
console.log('\nš Starting orchestration components...\n');
// Start Event Bus
console.log('ā” Starting Event Bus...');
componentStatus.eventBus = true;
eventBus.emit('system:start');
console.log('ā
Event Bus started');
// Start Orchestrator Engine
console.log('š§ Starting Orchestrator Engine...');
componentStatus.orchestrator = true;
console.log('ā
Orchestrator Engine started');
// Start Memory Manager
console.log('š¾ Starting Memory Manager...');
componentStatus.memoryManager = true;
console.log('ā
Memory Manager started');
// Start Terminal Pool
console.log('š„ļø Starting Terminal Pool...');
componentStatus.terminalPool = true;
console.log('ā
Terminal Pool started');
// Start MCP Server
const mcpPort = options.mcpPort || 3001;
startMCPServer(mcpPort);
console.log('ā
MCP Server started');
// Start Coordination Manager
console.log('š Starting Coordination Manager...');
componentStatus.coordinationManager = true;
console.log('ā
Coordination Manager started');
// Start Web UI if requested
if (options.ui && !options.noUi) {
const host = options.host || 'localhost';
const port = options.port || 3000;
try {
await startWebUI(host, port);
} catch (err: any) {
if (err.code === 'EADDRINUSE') {
console.log('\nā ļø Web UI could not start due to port conflict');
console.log(' Orchestrator is running without Web UI');
} else {
console.error('\nā ļø Web UI failed to start:', err.message);
}
}
}
console.log('\nā
All components started successfully!');
console.log('\nš System Status:');
console.log(' ⢠Event Bus: Active');
console.log(' ⢠Orchestrator: Active');
console.log(' ⢠Memory Manager: Active');
console.log(' ⢠Terminal Pool: Active');
console.log(' ⢠MCP Server: Active');
console.log(' ⢠Coordination Manager: Active');
if (options.ui && !options.noUi) {
console.log(
` ⢠Web UI: Active at http://${options.host || 'localhost'}:${options.port || 3000}`,
);
}
console.log('\nš” Use "jay-code status" to check system status');
console.log('š” Use "jay-code stop" to stop the orchestrator');
// Keep the process running
if (!options.daemon) {
console.log('\nš Press Ctrl+C to stop the orchestrator...\n');
// Handle graceful shutdown
process.on('SIGINT', () => {
console.log('\n\nš Shutting down orchestrator...');
process.exit(0);
});
}
}
// Export component status for other commands
export function getComponentStatus() {
return componentStatus;
}
// Export stores for other commands
export function getStores() {
return { agents, tasks, memory };
}