|
|
#!/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);
|
|
|
|
|
|
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('');
|
|
|
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 { 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 { isQuitting: false };
|
|
|
} catch (err) {
|
|
|
log.error(`Magnus error: An unhandled exception occurred.`);
|
|
|
console.error(err);
|
|
|
return { isQuitting: false };
|
|
|
}
|
|
|
}
|
|
|
|
|
|
let serverProcess = null;
|
|
|
|
|
|
function startServer() {
|
|
|
log.info('[SERVER] Firing up the llama-server engine...');
|
|
|
serverProcess = spawn('llama-server', ['-m', GGUF, '--jinja'], {
|
|
|
detached: true,
|
|
|
stdio: 'ignore'
|
|
|
});
|
|
|
serverProcess.unref();
|
|
|
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") {
|
|
|
|
|
|
execSync(`taskkill /pid ${serverProcess.pid} /f /t`);
|
|
|
} else {
|
|
|
|
|
|
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));
|
|
|
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;
|
|
|
|
|
|
rl.pause();
|
|
|
const { isQuitting } = await processInput(processedLine, 'terminal');
|
|
|
if (isQuitting) {
|
|
|
shutdown(0);
|
|
|
return;
|
|
|
}
|
|
|
if (!multiline) refreshPrompt();
|
|
|
rl.resume();
|
|
|
});
|
|
|
|
|
|
|
|
|
process.on('SIGINT', () => shutdown(0));
|
|
|
process.on('SIGTERM', () => shutdown(0));
|
|
|
}
|
|
|
|
|
|
main().catch(err => { console.error(err); shutdown(1); }); |