/** * KHAELOR * File: src/daemon/client.ts * Description: Control-socket client — status/goals/approvals/stop requests against a running khaelord (v2 design §7.3). * * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ import { connect } from "node:net"; import { join } from "node:path"; /** One JSON-line request/response round-trip on the daemon's unix socket. */ export function daemonRequest( daemonDir: string, request: Record, timeoutMs = 3_000, ): Promise> { const socketPath = join(daemonDir, "daemon.sock"); return new Promise((resolve, reject) => { const socket = connect(socketPath); const timer = setTimeout(() => { socket.destroy(); reject(new Error("daemon did not respond")); }, timeoutMs); let buffer = ""; socket.on("connect", () => { socket.write(`${JSON.stringify(request)}\n`); }); socket.on("data", (chunk) => { buffer += chunk.toString("utf8"); }); socket.on("end", () => { clearTimeout(timer); try { resolve(JSON.parse(buffer.trim()) as Record); } catch { reject(new Error(`invalid daemon response: ${buffer.slice(0, 200)}`)); } }); socket.on("error", (error) => { clearTimeout(timer); reject(error); }); }); } /** True when the recorded pid is alive. */ export function pidAlive(pid: number): boolean { try { process.kill(pid, 0); return true; } catch { return false; } }