SNAPKITTYWEST's picture
push from SNAPKITTYWEST/j-matrix-twin
efc74bf verified
Raw History Blame Contribute Delete
6.38 kB
#!/usr/bin/env node
/**
* J Matrix Twin WebSocket Server
* Executes J code via ijconsole and streams results to browser
*/
const WebSocket = require('ws');
const { spawn } = require('child_process');
const http = require('http');
const fs = require('fs');
const path = require('path');
const PORT = process.env.PORT || 8080;
const PUBLIC_DIR = path.join(__dirname, '../public');
// Create HTTP server for static files
const server = http.createServer((req, res) => {
let filePath = path.join(PUBLIC_DIR, req.url === '/' ? 'index.html' : req.url);
const extname = path.extname(filePath);
const contentTypes = {
'.html': 'text/html',
'.js': 'text/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpg',
'.svg': 'image/svg+xml'
};
const contentType = contentTypes[extname] || 'text/plain';
fs.readFile(filePath, (error, content) => {
if (error) {
if (error.code === 'ENOENT') {
res.writeHead(404);
res.end('404 Not Found');
} else {
res.writeHead(500);
res.end('500 Internal Server Error');
}
} else {
res.writeHead(200, { 'Content-Type': contentType });
res.end(content, 'utf-8');
}
});
});
// Create WebSocket server
const wss = new WebSocket.Server({ server });
// Check if ijconsole is available
function checkJInstallation() {
return new Promise((resolve) => {
const proc = spawn('ijconsole', ['--version'], { shell: true });
proc.on('error', () => resolve(false));
proc.on('close', (code) => resolve(code === 0));
});
}
// Execute J code
async function executeJCode(code) {
return new Promise((resolve, reject) => {
const jProc = spawn('ijconsole', [], { shell: true });
let stdout = '';
let stderr = '';
jProc.stdout.on('data', (data) => {
stdout += data.toString();
});
jProc.stderr.on('data', (data) => {
stderr += data.toString();
});
jProc.on('error', (error) => {
reject(new Error(`Failed to spawn ijconsole: ${error.message}`));
});
jProc.on('close', (code) => {
if (code !== 0 && stderr) {
reject(new Error(stderr));
} else {
// Clean up J output (remove prompts, etc.)
const cleaned = stdout
.split('\n')
.filter(line => !line.trim().startsWith(' '))
.join('\n')
.trim();
resolve(cleaned);
}
});
// Send code to J
jProc.stdin.write(code + '\n');
jProc.stdin.write('exit 0\n');
jProc.stdin.end();
// Timeout after 30 seconds
setTimeout(() => {
jProc.kill();
reject(new Error('Execution timeout (30s)'));
}, 30000);
});
}
// WebSocket connection handler
wss.on('connection', async (ws) => {
console.log('Client connected');
// Check J installation
const jAvailable = await checkJInstallation();
if (jAvailable) {
ws.send(JSON.stringify({
type: 'status',
status: 'connected',
message: 'J interpreter available'
}));
} else {
ws.send(JSON.stringify({
type: 'status',
status: 'error',
message: 'J interpreter not found. Install from jsoftware.com'
}));
}
ws.on('message', async (message) => {
try {
const data = JSON.parse(message);
if (data.type === 'execute') {
console.log('Executing J code...');
if (!jAvailable) {
ws.send(JSON.stringify({
error: 'J interpreter not available'
}));
return;
}
try {
const output = await executeJCode(data.code);
ws.send(JSON.stringify({
type: 'result',
output: output
}));
console.log('Execution successful');
} catch (error) {
ws.send(JSON.stringify({
type: 'result',
error: error.message
}));
console.error('Execution error:', error.message);
}
}
} catch (error) {
console.error('Message handling error:', error);
ws.send(JSON.stringify({
error: 'Invalid message format'
}));
}
});
ws.on('close', () => {
console.log('Client disconnected');
});
ws.on('error', (error) => {
console.error('WebSocket error:', error);
});
});
// Start server
server.listen(PORT, () => {
console.log('='.repeat(60));
console.log('J Matrix Twin Playground Server');
console.log('='.repeat(60));
console.log(`HTTP Server: http://localhost:${PORT}`);
console.log(`WebSocket: ws://localhost:${PORT}`);
console.log('');
checkJInstallation().then(available => {
if (available) {
console.log('✓ J interpreter found');
} else {
console.log('✗ J interpreter not found');
console.log(' Install from: https://www.jsoftware.com');
console.log(' Server will run in demo mode');
}
console.log('');
console.log('Press Ctrl+C to stop');
console.log('='.repeat(60));
});
});
// Graceful shutdown
process.on('SIGINT', () => {
console.log('\nShutting down server...');
wss.clients.forEach(client => {
client.close();
});
server.close(() => {
console.log('Server stopped');
process.exit(0);
});
});
// Made with Bob