import { createInterface, type Interface as ReadlineInterface, } from "node:readline/promises"; import type { Extension } from "../../kernel"; import { Event, ExtensionId, Hook } from "../catalog"; import type { AgentService } from "../shared/agent"; import type { WorkspaceService } from "../shared/workspace"; import { parseInput } from "./input"; export function createCliExtension(): Extension { let terminal: ReadlineInterface | undefined; let loop: Promise | undefined; let currentRequest: AbortController | undefined; return { setup() {}, start(context) { const agent = context.get(Hook.Agent); const workspace = context.get(Hook.Workspace); const roleNames = { user: "用户", assistant: "助手", system: "系统", }; terminal = createInterface({ input: process.stdin, output: process.stdout, }); terminal.on("SIGINT", () => { if (currentRequest) { currentRequest.abort(); } else { terminal?.close(); } }); loop = (async () => { try { process.stdout.write( `llm-to-agent\nWorkspace: ${process.cwd()}\n当前对话: ${workspace.conversationId}\n命令: /new、/switch 、/history、/exit,Ctrl+C 取消当前回复。\n\n`, ); terminal?.setPrompt("> "); terminal?.prompt(); for await (const line of terminal!) { const input = parseInput(line); if (!input) { terminal?.prompt(); continue; } if (input.type === "command") { if (input.name === "exit") break; if (input.name === "new") { const conversationId = await workspace.newConversation(); process.stdout.write(`已新建对话: ${conversationId}\n\n`); } else if (input.name === "switch") { if (!input.argument) { process.stdout.write("用法: /switch \n\n"); } else if (await workspace.switchConversation(input.argument)) { process.stdout.write(`已切换到对话: ${workspace.conversationId}\n\n`); } else { process.stdout.write(`对话不存在: ${input.argument}\n\n`); } } else if (input.name === "history") { const messages = await workspace.messages(); if (messages.length === 0) { process.stdout.write("当前对话暂无消息。\n\n"); } else { process.stdout.write(`对话 ${workspace.conversationId}\n`); for (const message of messages) { process.stdout.write( `${roleNames[message.role]}: ${message.content}\n`, ); } process.stdout.write("\n"); } } else { process.stdout.write(`未知命令: /${input.name}\n\n`); } terminal?.prompt(); continue; } currentRequest = new AbortController(); try { for await (const chunk of agent.chat(input.content, currentRequest.signal)) { process.stdout.write(chunk); } process.stdout.write("\n\n"); } catch (error) { if (currentRequest.signal.aborted) { process.stdout.write("\n[已取消]\n\n"); } else { process.stderr.write(`\n${String(error)}\n\n`); } } finally { currentRequest = undefined; } terminal?.prompt(); } } finally { terminal?.close(); await context.emit(Event.RuntimeStopRequested, { source: "cli" }); } })(); }, async stop() { currentRequest?.abort(); terminal?.close(); await loop; }, }; } createCliExtension.id = ExtensionId.Cli;