File size: 4,322 Bytes
6c7818b 815c829 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
#!/usr/bin/env node
import readline from "readline";
import { spawn, execSync } from "child_process";
import { initializeDatabase, db } from "./modules/database.js";
import { bootUp, getPrompt, log, C, setReadline } from "./modules/ui.js";
import { commandHandlers } from "./modules/commands.js";
import { route } from "./modules/router.js";
import { GGUF } from "./config.js";
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
setReadline(rl); // Give the UI module access to the readline instance
let multiline = false;
let multiBuf = [];
function refreshPrompt() {
const newPrompt = getPrompt();
rl.setPrompt(newPrompt);
rl.prompt(true);
}
function handleMultiline(line) {
if (line.trim() === "```") {
if (!multiline) {
multiline = true;
multiBuf = [];
log.gray("(multiline ON — type ``` to send)");
rl.setPrompt(''); // Use a blank prompt for multiline input
rl.prompt();
} else {
multiline = false;
return multiBuf.join("\n");
}
return null;
}
if (multiline) {
multiBuf.push(line);
return null;
}
return line.trim();
}
async function processInput(input, source = 'terminal') {
if (!input) return false;
try {
const lowerInput = input.trim().toLowerCase();
if (lowerInput === 'exit' || lowerInput === 'quit' || lowerInput === 'bye') {
await commandHandlers.quit();
// Return a special value to signal that we are quitting and should not refresh the prompt.
return { isQuitting: true };
} else if (input.trim().startsWith('/')) {
const [command] = input.substring(1).split(/\s+/);
const handler = commandHandlers[command];
if (handler) {
await handler(input);
} else {
log.warn(`Unknown command, brah. Sending to router anyway...`);
await route(input);
}
} else {
await route(input);
}
// Return a default object if not quitting.
return { isQuitting: false };
} catch (err) {
log.error(`Magnus error: An unhandled exception occurred.`);
console.error(err);
return { isQuitting: false };
}
}
let serverProcess = null; // Keep the server process in a higher scope
function startServer() { // No longer returns the process
log.info('[SERVER] Firing up the llama-server engine...');
serverProcess = spawn('llama-server', ['-m', GGUF, '--jinja'], {
detached: true,
stdio: 'ignore'
});
serverProcess.unref(); // Let the main process exit independently
log.gray(`[SERVER] Llama-server is shredding on PID: ${serverProcess.pid}`);
}
function shutdown(exitCode = 0) {
log.warn("\n[CLEANUP] Taking down the server... Catch ya later, legend!");
if (serverProcess && serverProcess.pid) {
try {
if (process.platform === "win32") {
// Bitchin' Fix: Use execSync to block until the command finishes. No more zombies!
execSync(`taskkill /pid ${serverProcess.pid} /f /t`);
} else {
// Kill the entire process group. The `-` is the magic here.
process.kill(-serverProcess.pid, 'SIGTERM');
}
log.success("[CLEANUP] Server process terminated.");
} catch (e) {
log.error(`[CLEANUP] Bogus! Couldn't kill server process ${serverProcess.pid}: ${e.message}`);
}
}
process.exit(exitCode);
}
async function main() {
startServer();
log.gray('[SYSTEM] Giving the server 5 seconds to warm up...');
await new Promise(resolve => setTimeout(resolve, 5000)); // Simple 5-second delay
log.success('[SERVER] Aight, server should be ready. Let\'s rock!');
initializeDatabase();
await bootUp();
rl.setPrompt(getPrompt());
rl.prompt();
rl.on("line", async (line) => {
const processedLine = handleMultiline(line);
if (processedLine === null) return; // Multiline is active, just wait.
rl.pause(); // Pause the prompt while processing and streaming
const { isQuitting } = await processInput(processedLine, 'terminal');
if (isQuitting) {
shutdown(0); // Gracefully shutdown if command handler signals a quit
return;
}
if (!multiline) refreshPrompt();
rl.resume();
});
// Catch Ctrl+C and other termination signals for a graceful shutdown
process.on('SIGINT', () => shutdown(0));
process.on('SIGTERM', () => shutdown(0));
}
main().catch(err => { console.error(err); shutdown(1); }); |