mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 22:03:45 +00:00
Add --mode flag for CLI output control (text/json/rpc)
- text mode: only outputs final assistant message text (default) - json mode: streams all events as JSON (same as session manager writes) - rpc mode: JSON output + listens for JSON input on stdin for headless operation - Suppress informational messages in json/rpc modes
This commit is contained in:
parent
c75f53f6f2
commit
68092ccf01
8 changed files with 800 additions and 286 deletions
|
|
@ -27,6 +27,8 @@ const envApiKeyMap: Record<KnownProvider, string[]> = {
|
|||
zai: ["ZAI_API_KEY"],
|
||||
};
|
||||
|
||||
type Mode = "text" | "json" | "rpc";
|
||||
|
||||
interface Args {
|
||||
provider?: string;
|
||||
model?: string;
|
||||
|
|
@ -35,6 +37,7 @@ interface Args {
|
|||
continue?: boolean;
|
||||
resume?: boolean;
|
||||
help?: boolean;
|
||||
mode?: Mode;
|
||||
messages: string[];
|
||||
}
|
||||
|
||||
|
|
@ -48,6 +51,11 @@ function parseArgs(args: string[]): Args {
|
|||
|
||||
if (arg === "--help" || arg === "-h") {
|
||||
result.help = true;
|
||||
} else if (arg === "--mode" && i + 1 < args.length) {
|
||||
const mode = args[++i];
|
||||
if (mode === "text" || mode === "json" || mode === "rpc") {
|
||||
result.mode = mode;
|
||||
}
|
||||
} else if (arg === "--continue" || arg === "-c") {
|
||||
result.continue = true;
|
||||
} else if (arg === "--resume" || arg === "-r") {
|
||||
|
|
@ -79,6 +87,7 @@ ${chalk.bold("Options:")}
|
|||
--model <id> Model ID (default: gemini-2.5-flash)
|
||||
--api-key <key> API key (defaults to env vars)
|
||||
--system-prompt <text> System prompt (default: coding assistant prompt)
|
||||
--mode <mode> Output mode: text (default), json, or rpc
|
||||
--continue, -c Continue previous session
|
||||
--resume, -r Select a session to resume
|
||||
--help, -h Show this help
|
||||
|
|
@ -194,12 +203,26 @@ async function runInteractiveMode(agent: Agent, sessionManager: SessionManager,
|
|||
}
|
||||
}
|
||||
|
||||
async function runSingleShotMode(agent: Agent, sessionManager: SessionManager, messages: string[]): Promise<void> {
|
||||
for (const message of messages) {
|
||||
console.log(chalk.blue(`\n> ${message}\n`));
|
||||
await agent.prompt(message);
|
||||
async function runSingleShotMode(
|
||||
agent: Agent,
|
||||
_sessionManager: SessionManager,
|
||||
messages: string[],
|
||||
mode: "text" | "json",
|
||||
): Promise<void> {
|
||||
if (mode === "json") {
|
||||
// Subscribe to all events and output as JSON
|
||||
agent.subscribe((event) => {
|
||||
// Output event as JSON (same format as session manager)
|
||||
console.log(JSON.stringify(event));
|
||||
});
|
||||
}
|
||||
|
||||
// Print response
|
||||
for (const message of messages) {
|
||||
await agent.prompt(message);
|
||||
}
|
||||
|
||||
// In text mode, only output the final assistant message
|
||||
if (mode === "text") {
|
||||
const lastMessage = agent.state.messages[agent.state.messages.length - 1];
|
||||
if (lastMessage.role === "assistant") {
|
||||
for (const content of lastMessage.content) {
|
||||
|
|
@ -209,8 +232,40 @@ async function runSingleShotMode(agent: Agent, sessionManager: SessionManager, m
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(chalk.dim(`\nSession saved to: ${sessionManager.getSessionFile()}`));
|
||||
async function runRpcMode(agent: Agent, _sessionManager: SessionManager): Promise<void> {
|
||||
// Subscribe to all events and output as JSON
|
||||
agent.subscribe((event) => {
|
||||
console.log(JSON.stringify(event));
|
||||
});
|
||||
|
||||
// Listen for JSON input on stdin
|
||||
const readline = require("readline");
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
terminal: false,
|
||||
});
|
||||
|
||||
rl.on("line", async (line: string) => {
|
||||
try {
|
||||
const input = JSON.parse(line);
|
||||
|
||||
// Handle different RPC commands
|
||||
if (input.type === "prompt" && input.message) {
|
||||
await agent.prompt(input.message);
|
||||
} else if (input.type === "abort") {
|
||||
agent.abort();
|
||||
}
|
||||
} catch (error: any) {
|
||||
// Output error as JSON
|
||||
console.log(JSON.stringify({ type: "error", error: error.message }));
|
||||
}
|
||||
});
|
||||
|
||||
// Keep process alive
|
||||
return new Promise(() => {});
|
||||
}
|
||||
|
||||
export async function main(args: string[]) {
|
||||
|
|
@ -295,11 +350,18 @@ export async function main(args: string[]) {
|
|||
}),
|
||||
});
|
||||
|
||||
// Determine mode early to know if we should print messages
|
||||
const isInteractive = parsed.messages.length === 0;
|
||||
const mode = parsed.mode || "text";
|
||||
const shouldPrintMessages = isInteractive || mode === "text";
|
||||
|
||||
// Load previous messages if continuing or resuming
|
||||
if (parsed.continue || parsed.resume) {
|
||||
const messages = sessionManager.loadMessages();
|
||||
if (messages.length > 0) {
|
||||
console.log(chalk.dim(`Loaded ${messages.length} messages from previous session`));
|
||||
if (shouldPrintMessages) {
|
||||
console.log(chalk.dim(`Loaded ${messages.length} messages from previous session`));
|
||||
}
|
||||
agent.replaceMessages(messages);
|
||||
}
|
||||
|
||||
|
|
@ -312,9 +374,13 @@ export async function main(args: string[]) {
|
|||
try {
|
||||
const restoredModel = getModel(savedProvider as any, savedModelId);
|
||||
agent.setModel(restoredModel);
|
||||
console.log(chalk.dim(`Restored model: ${savedModel}`));
|
||||
if (shouldPrintMessages) {
|
||||
console.log(chalk.dim(`Restored model: ${savedModel}`));
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(chalk.yellow(`Warning: Could not restore model ${savedModel}: ${error.message}`));
|
||||
if (shouldPrintMessages) {
|
||||
console.error(chalk.yellow(`Warning: Could not restore model ${savedModel}: ${error.message}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -323,7 +389,9 @@ export async function main(args: string[]) {
|
|||
const thinkingLevel = sessionManager.loadThinkingLevel() as ThinkingLevel;
|
||||
if (thinkingLevel) {
|
||||
agent.setThinkingLevel(thinkingLevel);
|
||||
console.log(chalk.dim(`Restored thinking level: ${thinkingLevel}`));
|
||||
if (shouldPrintMessages) {
|
||||
console.log(chalk.dim(`Restored thinking level: ${thinkingLevel}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -343,12 +411,16 @@ export async function main(args: string[]) {
|
|||
}
|
||||
});
|
||||
|
||||
// Determine mode: interactive if no messages provided
|
||||
const isInteractive = parsed.messages.length === 0;
|
||||
|
||||
// Route to appropriate mode
|
||||
if (isInteractive) {
|
||||
// No mode flag in interactive - always use TUI
|
||||
await runInteractiveMode(agent, sessionManager, VERSION);
|
||||
} else {
|
||||
await runSingleShotMode(agent, sessionManager, parsed.messages);
|
||||
// CLI mode with messages
|
||||
if (mode === "rpc") {
|
||||
await runRpcMode(agent, sessionManager);
|
||||
} else {
|
||||
await runSingleShotMode(agent, sessionManager, parsed.messages, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue