#!/usr/bin/env bun
/**
* Zesdex CLI — main entry point.
*
* Zesdex is a pure CLI/TUI tool:
* - `zesdex` → boots the interactive OpenTUI (primary interface)
* - `zesdex --headless "
"` → runs a single one-shot agent turn and exits
*
* All server modes (REST API / WebSocket / gRPC / web / daemon) were removed in
* the "full cli tui" rombak. The TUI owns the interactive agent runtime; this
* entry point only routes to it or runs a one-shot headless turn.
*/
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { parseCli } from "./args.ts";
/** Initialise file logging (append to /zesdex/zesdex.log). */
function initLogging(): void {
const dataDir = os.homedir();
const logDir = path.join(dataDir, ".local", "share", "zesdex");
fs.mkdirSync(logDir, { recursive: true });
const logPath = path.join(logDir, "zesdex.log");
const stream = fs.createWriteStream(logPath, { flags: "a" });
const originalLog = console.log;
const originalError = console.error;
const write = (level: string, args: unknown[]) => {
const line = `[${new Date().toISOString()}] ${level} ${args
.map((a) => (typeof a === "string" ? a : JSON.stringify(a)))
.join(" ")}`;
stream.write(line + "\n");
if (level === "ERROR") originalError(...args);
else originalLog(...args);
};
console.log = (...a: unknown[]) => write("LOG", a);
console.info = (...a: unknown[]) => write("INFO", a);
console.warn = (...a: unknown[]) => write("WARN", a);
console.error = (...a: unknown[]) => write("ERROR", a);
}
/** Run a single one-shot agent turn non-interactively, printing the outcome. */
async function runHeadless(prompt: string): Promise {
const { runSingleProcess, buildTurnParams } = await import("./compose.ts");
const runtime = await runSingleProcess();
const events: import("@zesdex/domain").TurnEvent[] = [];
const abort = new AbortController();
const params = buildTurnParams(runtime, prompt, events, abort);
await runtime.turnService.runTurn(params);
const tokens: string[] = [];
for (const ev of events) {
if (ev.kind === "stream_token") tokens.push(ev.content);
}
const full = tokens.join("");
process.stdout.write("\n");
if (full.trim() !== "") console.log(full.trim());
else console.log("(no response content)");
}
/** Main entry. `zesdex` → TUI; `--headless ` → one-shot turn. */
async function main(): Promise {
initLogging();
const args = process.argv.slice(2);
let parsed;
try {
parsed = parseCli(args);
} catch (e) {
console.error((e as Error).message);
process.exit(2);
}
if ("help" in parsed) {
console.log(parsed.text);
process.exit(0);
}
if (parsed.headless !== null) {
await runHeadless(parsed.headless);
process.exit(0);
}
// Default: boot the interactive TUI.
const { runTui } = await import("@zesdex/tui");
await runTui();
process.exit(0);
}
main().catch((e) => {
console.error(e instanceof Error ? e.message : String(e));
process.exit(1);
});