scai
Version:
> **AI-powered CLI for local code analysis, commit message suggestions, and natural-language queries.** 100% local, private, GDPR-friendly, made in Denmark/EU with ❤️.
135 lines (134 loc) • 4.56 kB
JavaScript
import fs from 'fs';
import path from 'path';
import { spawn } from 'child_process';
import { fileURLToPath } from 'url';
import { LOG_PATH, PID_PATH, CONFIG_LOCK_PATH } from '../constants.js';
import { Config } from '../config.js';
import { getDbPathForRepo } from '../db/client.js';
// --- Helpers ---
function isProcessRunning(pid) {
try {
process.kill(pid, 0);
return true;
}
catch {
return false;
}
}
function lockConfig(repoKey) {
fs.writeFileSync(CONFIG_LOCK_PATH, repoKey, { encoding: 'utf8' });
}
function unlockConfigFile() {
if (fs.existsSync(CONFIG_LOCK_PATH)) {
fs.unlinkSync(CONFIG_LOCK_PATH);
}
}
function getLockedRepo() {
return fs.existsSync(CONFIG_LOCK_PATH)
? fs.readFileSync(CONFIG_LOCK_PATH, 'utf8')
: null;
}
// --- Commands ---
export async function startDaemon() {
const cfg = Config.getRaw();
const activeRepo = cfg.activeRepo;
if (!activeRepo) {
console.log('❌ No active repo configured. Use `askcmd repo set <path>` first.');
return;
}
const dbPath = getDbPathForRepo();
if (!fs.existsSync(dbPath)) {
console.log(`❌ Cannot start daemon. Index/database not initialized for repo '${activeRepo}'.`);
console.log(` Run the index command first: scai index start`);
return;
}
const lockedRepo = getLockedRepo();
if (lockedRepo && lockedRepo !== activeRepo) {
console.log(`🔒 Daemon already locked to repo: ${lockedRepo}`);
console.log(` Current active repo: ${activeRepo}`);
console.log(` Stop the daemon before starting another.`);
return;
}
if (fs.existsSync(PID_PATH)) {
const pid = parseInt(fs.readFileSync(PID_PATH, 'utf8'), 10);
if (isProcessRunning(pid)) {
console.log(`⚠️ Daemon already running with PID ${pid}.`);
return;
}
else {
console.log(`🧹 Removing stale PID file...`);
fs.unlinkSync(PID_PATH);
}
}
console.log(`🚀 Starting daemon for repo: ${activeRepo}`);
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const workerPath = path.join(__dirname, '../daemon/daemonWorker.js');
fs.mkdirSync(path.dirname(LOG_PATH), { recursive: true });
fs.mkdirSync(path.dirname(PID_PATH), { recursive: true });
const out = fs.openSync(LOG_PATH, 'a');
const err = fs.openSync(LOG_PATH, 'a');
const child = spawn(process.execPath, [workerPath], {
detached: true,
stdio: ['ignore', out, err],
env: { ...process.env, BACKGROUND_MODE: 'true' },
});
child.unref();
fs.writeFileSync(PID_PATH, String(child.pid));
lockConfig(activeRepo);
console.log(`✅ Daemon started (PID ${child.pid})`);
}
export async function stopDaemon() {
if (!fs.existsSync(PID_PATH)) {
console.log(`ℹ️ No PID file found.`);
return;
}
const pid = parseInt(fs.readFileSync(PID_PATH, 'utf8'), 10);
if (isProcessRunning(pid)) {
console.log(`🛑 Stopping daemon (PID ${pid})...`);
process.kill(pid);
}
else {
console.log(`⚠️ Stale PID ${pid}.`);
}
fs.unlinkSync(PID_PATH);
unlockConfigFile();
console.log(`✅ Daemon stopped and unlocked.`);
}
export async function statusDaemon() {
const lockedRepo = getLockedRepo();
if (!fs.existsSync(PID_PATH)) {
console.log(`🔴 Daemon not running.`);
if (lockedRepo)
console.log(` (Locked to repo ${lockedRepo})`);
return;
}
const pid = parseInt(fs.readFileSync(PID_PATH, 'utf8'), 10);
if (isProcessRunning(pid)) {
console.log(`🟢 Daemon running (PID ${pid})`);
if (lockedRepo)
console.log(` ↳ Repo: ${lockedRepo}`);
}
else {
console.log(`🟡 Stale PID file (${pid}) found.`);
}
}
export async function restartDaemon() {
console.log(`♻️ Restarting daemon...`);
await stopDaemon();
await startDaemon();
}
export async function unlockConfig() {
unlockConfigFile();
console.log(`🔓 Configuration unlocked.`);
}
export async function showLogs(lines = 20) {
if (!fs.existsSync(LOG_PATH)) {
console.log(`ℹ️ No logs yet.`);
return;
}
const content = fs.readFileSync(LOG_PATH, 'utf8');
const tail = content.split('\n').slice(-lines).join('\n');
console.log(`\n--- Daemon Logs (last ${lines} lines) ---\n`);
console.log(tail);
}