























@@ -1,24 +1,14 @@
11import { stdin as defaultStdin, stdout as defaultStdout } from "node:process";
2-import readline from "node:readline/promises";
32import { defaultRuntime, writeRuntimeJson, type RuntimeEnv } from "../runtime.js";
4-import {
5-planCrestodianCommand,
6-type CrestodianAssistantPlan,
7-type CrestodianAssistantPlanner,
8-} from "./assistant.js";
3+import type { CrestodianAssistantPlanner } from "./assistant.js";
4+import { resolveCrestodianOperation } from "./dialogue.js";
95import {
106executeCrestodianOperation,
11-describeCrestodianPersistentOperation,
127isPersistentCrestodianOperation,
13-parseCrestodianOperation,
148type CrestodianCommandDeps,
15-type CrestodianOperation,
169} from "./operations.js";
17-import {
18-formatCrestodianOverview,
19-loadCrestodianOverview,
20-type CrestodianOverview,
21-} from "./overview.js";
10+import { formatCrestodianOverview, loadCrestodianOverview } from "./overview.js";
11+import { runCrestodianTui } from "./tui-backend.js";
22122313export type RunCrestodianOptions = {
2414message?: string;
@@ -29,16 +19,9 @@ export type RunCrestodianOptions = {
2919planWithAssistant?: CrestodianAssistantPlanner;
3020input?: NodeJS.ReadableStream;
3121output?: NodeJS.WritableStream;
22+runInteractiveTui?: typeof runCrestodianTui;
3223};
332434-function approvalQuestion(operation: CrestodianOperation): string {
35-return `Apply this operation: ${describeCrestodianPersistentOperation(operation)}?`;
36-}
37-38-function isYes(input: string): boolean {
39-return /^(y|yes|apply|do it|approved?)$/i.test(input.trim());
40-}
41-4225async function runOneShot(
4326input: string,
4427runtime: RuntimeEnv,
@@ -51,84 +34,20 @@ async function runOneShot(
5134});
5235}
533654-async function runResolvedOperation(
55-operation: CrestodianOperation,
56-runtime: RuntimeEnv,
57-opts: RunCrestodianOptions,
58-): Promise<{ exitsInteractive: boolean; nextInput?: string }> {
59-const result = await executeCrestodianOperation(operation, runtime, {
60-approved: opts.yes === true || !isPersistentCrestodianOperation(operation),
61-deps: opts.deps,
62-});
63-return {
64-exitsInteractive: result.exitsInteractive === true,
65-nextInput: result.nextInput,
66-};
67-}
68-69-async function resolveCrestodianOperation(
70-input: string,
71-runtime: RuntimeEnv,
72-opts: RunCrestodianOptions,
73-): Promise<CrestodianOperation> {
74-const operation = parseCrestodianOperation(input);
75-if (!shouldAskAssistant(input, operation)) {
76-return operation;
77-}
78-const overview = await loadCrestodianOverview();
79-const planner = opts.planWithAssistant ?? planCrestodianCommand;
80-const plan = await planner({ input, overview });
81-if (!plan) {
82-return operation;
83-}
84-const planned = parseCrestodianOperation(plan.command);
85-if (planned.kind === "none") {
86-return operation;
87-}
88-logAssistantPlan(runtime, plan, overview);
89-return planned;
90-}
91-92-function shouldAskAssistant(input: string, operation: CrestodianOperation): boolean {
93-if (operation.kind !== "none") {
94-return false;
95-}
96-const trimmed = input.trim().toLowerCase();
97-if (!trimmed || trimmed === "quit" || trimmed === "exit") {
98-return false;
99-}
100-return true;
101-}
102-103-function logAssistantPlan(
104-runtime: RuntimeEnv,
105-plan: CrestodianAssistantPlan,
106-overview: CrestodianOverview,
107-): void {
108-const modelLabel = plan.modelLabel ?? overview.defaultModel ?? "configured model";
109-runtime.log(`[crestodian] planner: ${modelLabel}`);
110-if (plan.reply) {
111-runtime.log(plan.reply);
112-}
113-runtime.log(`[crestodian] interpreted: ${plan.command}`);
114-}
115-11637export async function runCrestodian(
11738opts: RunCrestodianOptions = {},
11839runtime: RuntimeEnv = defaultRuntime,
11940): Promise<void> {
120-const overview = await loadCrestodianOverview();
12141if (opts.json) {
42+const overview = await loadCrestodianOverview();
12243writeRuntimeJson(runtime, overview);
12344return;
12445}
125-runtime.log(formatCrestodianOverview(overview));
126-runtime.log("");
127-runtime.log(
128-"Say: status, doctor, health, gateway status, restart gateway, agents, models, set default model <provider/model>, talk to agent, audit, or quit.",
129-);
1304613147if (opts.message?.trim()) {
48+const overview = await loadCrestodianOverview();
49+runtime.log(formatCrestodianOverview(overview));
50+runtime.log("");
13251await runOneShot(opts.message, runtime, opts);
13352return;
13453}
@@ -143,51 +62,6 @@ export async function runCrestodian(
14362return;
14463}
14564146-const rl = readline.createInterface({ input, output });
147-let pending: CrestodianOperation | null = null;
148-try {
149-for (;;) {
150-const answer = await rl.question("crestodian> ");
151-if (pending) {
152-if (isYes(answer)) {
153-const result = await executeCrestodianOperation(pending, runtime, {
154-approved: true,
155-deps: opts.deps,
156-});
157-pending = null;
158-if (result.exitsInteractive) {
159-break;
160-}
161-continue;
162-}
163-runtime.log("Skipped. No barnacles on config today.");
164-pending = null;
165-continue;
166-}
167-const operation = await resolveCrestodianOperation(answer, runtime, opts);
168-if (isPersistentCrestodianOperation(operation) && !opts.yes) {
169-runtime.log(approvalQuestion(operation));
170-pending = operation;
171-continue;
172-}
173-const result = await runResolvedOperation(operation, runtime, opts);
174-if (result.exitsInteractive) {
175-break;
176-}
177-if (result.nextInput?.trim()) {
178-const followUp = await resolveCrestodianOperation(result.nextInput, runtime, opts);
179-if (isPersistentCrestodianOperation(followUp) && !opts.yes) {
180-runtime.log(approvalQuestion(followUp));
181-pending = followUp;
182-continue;
183-}
184-const followUpResult = await runResolvedOperation(followUp, runtime, opts);
185-if (followUpResult.exitsInteractive) {
186-break;
187-}
188-}
189-}
190-} finally {
191-rl.close();
192-}
65+const runInteractiveTui = opts.runInteractiveTui ?? runCrestodianTui;
66+await runInteractiveTui(opts, runtime);
19367}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。