AO-03: CLI Interactive Mode

This commit is contained in:
larchanka
2026-02-22 22:38:32 +01:00
parent 9eb13e49f9
commit 9c0482147d
4 changed files with 46 additions and 8 deletions

View File

@@ -9,7 +9,8 @@ vi.mock("../../shared/config.js", () => ({
ollama: { baseUrl: "http://localhost:11434" },
modelRouter: { plannerComplexity: "small" },
taskMemory: { dbPath: ":memory:" },
cron: { dbPath: ":memory:" }
cron: { dbPath: ":memory:" },
maxConcurrentTasks: 0
})
}));

View File

@@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { Orchestrator } from '../orchestrator';
import { getConfig } from '../../shared/config';
import { Orchestrator } from '../orchestrator.js';
import { getConfig } from '../../shared/config.js';
// Mock dependencies
vi.mock('../../shared/config', () => ({

View File

@@ -1,7 +1,5 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { BaseProcess } from "../base-process.js";
import { envelopeSchema } from "../../protocol.js";
describe("BaseProcess", () => {
let bp: BaseProcess;
const processName = "test-process";
@@ -53,7 +51,7 @@ describe("BaseProcess", () => {
await new Promise(resolve => setTimeout(resolve, 50));
const line = stdoutSpy.mock.calls[0][0].toString();
const line = stdoutSpy.mock.calls[0]?.[0]?.toString() || "{}";
const env = JSON.parse(line);
expect(env.payload.memory).toBeDefined();

View File

@@ -34,6 +34,7 @@ export class BaseProcess extends EventEmitter {
private heartbeatTimer: NodeJS.Timeout | null = null;
private readonly heartbeatInterval: number;
private readonly startTime: number;
private interactive = false;
constructor(options: BaseProcessOptions) {
super();
@@ -49,9 +50,27 @@ export class BaseProcess extends EventEmitter {
if (this.running) return;
this.running = true;
this.status = "ready";
this.interactive = process.argv.includes("--interactive");
this.rl = createInterface({
input: process.stdin,
output: this.interactive ? process.stderr : undefined,
prompt: this.interactive ? "\x1b[36m[Interactive Mode] Enter JSONL:\x1b[0m\n> " : "",
terminal: this.interactive
});
if (this.interactive) {
this.rl.prompt();
}
this.rl.on("line", (line: string) => {
this.handleLine(line);
if (this.interactive && this.rl) {
this.rl.setPrompt("> ");
this.rl.prompt();
}
});
this.rl = createInterface({ input: process.stdin, terminal: false });
this.rl.on("line", (line: string) => this.handleLine(line));
this.rl.on("close", () => this.handleClose());
this.startHeartbeat();
@@ -136,8 +155,16 @@ export class BaseProcess extends EventEmitter {
try {
const raw = JSON.parse(trimmed) as unknown;
const envelope = envelopeSchema.parse(raw) as Envelope;
if (this.interactive) {
process.stderr.write(`\x1b[32m[RECV]\x1b[0m \x1b[33m[${envelope.type}]\x1b[0m \x1b[34m<- ${envelope.from}\x1b[0m\n`);
}
this.handleEnvelope(envelope);
} catch (error) {
if (this.interactive) {
process.stderr.write(`\x1b[31m[Parse Error]\x1b[0m ${error instanceof Error ? error.message : String(error)}\n`);
}
this.handleParseError(line, error);
}
}
@@ -155,6 +182,18 @@ export class BaseProcess extends EventEmitter {
const parsed = envelopeSchema.parse(envelope) as Envelope;
const line = JSON.stringify(parsed) + "\n";
process.stdout.write(line);
if (this.interactive && parsed.type !== "event.system.heartbeat") {
const typeStr = `\x1b[33m[${parsed.type}]\x1b[0m`;
const toStr = `\x1b[34m-> ${parsed.to}\x1b[0m`;
let payloadStr = "";
try {
payloadStr = JSON.stringify(parsed.payload, null, 2);
} catch {
payloadStr = String(parsed.payload);
}
process.stderr.write(`\n\x1b[36m[SEND]\x1b[0m ${typeStr} ${toStr}\n${payloadStr}\n> `);
}
}
/**