2026-09-02 20:17:14 +07:00
|
|
|
#!/usr/bin/env bun
|
|
|
|
|
/**
|
|
|
|
|
* Zesdex CLI — main entry point.
|
|
|
|
|
*
|
2026-09-03 01:19:27 +07:00
|
|
|
* Zesdex is a pure CLI/TUI tool:
|
|
|
|
|
* - `zesdex` → boots the interactive OpenTUI (primary interface)
|
|
|
|
|
* - `zesdex --headless "<p>"` → 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.
|
2026-09-02 20:17:14 +07:00
|
|
|
*/
|
|
|
|
|
import * as fs from "node:fs";
|
|
|
|
|
import * as os from "node:os";
|
|
|
|
|
import * as path from "node:path";
|
2026-09-03 01:19:27 +07:00
|
|
|
import { parseCli } from "./args.ts";
|
2026-09-02 20:17:14 +07:00
|
|
|
|
|
|
|
|
/** Initialise file logging (append to <data-dir>/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);
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-03 01:19:27 +07:00
|
|
|
/** Run a single one-shot agent turn non-interactively, printing the outcome. */
|
|
|
|
|
async function runHeadless(prompt: string): Promise<void> {
|
|
|
|
|
const { runSingleProcess, buildTurnParams } = await import("./compose.ts");
|
2026-09-02 20:17:14 +07:00
|
|
|
const runtime = await runSingleProcess();
|
|
|
|
|
|
2026-09-03 01:19:27 +07:00
|
|
|
const events: import("@zesdex/domain").TurnEvent[] = [];
|
|
|
|
|
const abort = new AbortController();
|
|
|
|
|
const params = buildTurnParams(runtime, prompt, events, abort);
|
2026-09-02 20:17:14 +07:00
|
|
|
|
2026-09-03 01:19:27 +07:00
|
|
|
await runtime.turnService.runTurn(params);
|
2026-09-02 20:17:14 +07:00
|
|
|
|
2026-09-03 01:19:27 +07:00
|
|
|
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)");
|
2026-09-02 20:17:14 +07:00
|
|
|
}
|
|
|
|
|
|
2026-09-03 01:19:27 +07:00
|
|
|
/** Main entry. `zesdex` → TUI; `--headless <prompt>` → one-shot turn. */
|
2026-09-02 20:17:14 +07:00
|
|
|
async function main(): Promise<void> {
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-03 01:19:27 +07:00
|
|
|
if (parsed.headless !== null) {
|
|
|
|
|
await runHeadless(parsed.headless);
|
|
|
|
|
process.exit(0);
|
2026-09-02 20:17:14 +07:00
|
|
|
}
|
|
|
|
|
|
2026-09-03 01:19:27 +07:00
|
|
|
// Default: boot the interactive TUI.
|
|
|
|
|
const { runTui } = await import("@zesdex/tui");
|
|
|
|
|
await runTui();
|
|
|
|
|
process.exit(0);
|
2026-09-02 20:17:14 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
main().catch((e) => {
|
|
|
|
|
console.error(e instanceof Error ? e.message : String(e));
|
|
|
|
|
process.exit(1);
|
|
|
|
|
});
|