@goah/cli 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +251 -18
- package/dist/cli.js.map +1 -1
- package/dist/control.d.ts +10 -3
- package/dist/control.d.ts.map +1 -1
- package/dist/control.js +24 -9
- package/dist/control.js.map +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +63 -62
- package/dist/index.js.map +1 -1
- package/dist/live-config.d.ts +4 -6
- package/dist/live-config.d.ts.map +1 -1
- package/dist/live-config.js +22 -11
- package/dist/live-config.js.map +1 -1
- package/dist/runner-registry.d.ts +8 -0
- package/dist/runner-registry.d.ts.map +1 -0
- package/dist/runner-registry.js +12 -0
- package/dist/runner-registry.js.map +1 -0
- package/dist/setup-wizard.d.ts +4 -7
- package/dist/setup-wizard.d.ts.map +1 -1
- package/dist/setup-wizard.js +82 -127
- package/dist/setup-wizard.js.map +1 -1
- package/dist/tui.d.ts +8 -0
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +121 -21
- package/dist/tui.js.map +1 -1
- package/dist/welcome.d.ts +9 -3
- package/dist/welcome.d.ts.map +1 -1
- package/dist/welcome.js +40 -5
- package/dist/welcome.js.map +1 -1
- package/node_modules/goah-ledger-contract/dist/execution.d.ts +53 -0
- package/node_modules/goah-ledger-contract/dist/execution.d.ts.map +1 -1
- package/node_modules/goah-ledger-contract/dist/execution.js.map +1 -1
- package/node_modules/goah-runner-pi/dist/configurator.d.ts +19 -0
- package/node_modules/goah-runner-pi/dist/configurator.d.ts.map +1 -0
- package/node_modules/goah-runner-pi/dist/configurator.js +219 -0
- package/node_modules/goah-runner-pi/dist/configurator.js.map +1 -0
- package/node_modules/goah-runner-pi/dist/credential-store.d.ts +12 -0
- package/node_modules/goah-runner-pi/dist/credential-store.d.ts.map +1 -0
- package/node_modules/goah-runner-pi/dist/credential-store.js +69 -0
- package/node_modules/goah-runner-pi/dist/credential-store.js.map +1 -0
- package/node_modules/goah-runner-pi/dist/env-spec.d.ts +1 -0
- package/node_modules/goah-runner-pi/dist/env-spec.d.ts.map +1 -1
- package/node_modules/goah-runner-pi/dist/env-spec.js +8 -0
- package/node_modules/goah-runner-pi/dist/env-spec.js.map +1 -1
- package/node_modules/goah-runner-pi/dist/index.d.ts +8 -2
- package/node_modules/goah-runner-pi/dist/index.d.ts.map +1 -1
- package/node_modules/goah-runner-pi/dist/index.js +16 -8
- package/node_modules/goah-runner-pi/dist/index.js.map +1 -1
- package/node_modules/goah-runner-pi/dist/model-provider.d.ts +26 -9
- package/node_modules/goah-runner-pi/dist/model-provider.d.ts.map +1 -1
- package/node_modules/goah-runner-pi/dist/model-provider.js +84 -91
- package/node_modules/goah-runner-pi/dist/model-provider.js.map +1 -1
- package/node_modules/goah-runner-pi/dist/pi-worker.d.ts.map +1 -1
- package/node_modules/goah-runner-pi/dist/pi-worker.js +16 -7
- package/node_modules/goah-runner-pi/dist/pi-worker.js.map +1 -1
- package/node_modules/goah-runner-pi/dist/verification-worker.js +2 -2
- package/node_modules/goah-runner-pi/dist/verification-worker.js.map +1 -1
- package/node_modules/goah-supervisor/dist/index.d.ts +14 -2
- package/node_modules/goah-supervisor/dist/index.d.ts.map +1 -1
- package/node_modules/goah-supervisor/dist/index.js +60 -2
- package/node_modules/goah-supervisor/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/setup-wizard.js
CHANGED
|
@@ -1,162 +1,117 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* a swap slot that alternates between SelectList (arrow-key selection,
|
|
6
|
-
* omp/pi-style) and Input (free text). Non-TTY invocations fall back to the
|
|
7
|
-
* piped line-queue prompt so onboarding also works over pipes and in CI.
|
|
8
|
-
*/
|
|
9
|
-
import { TUI, Text, Input, SelectList, ProcessTerminal } from "@mariozechner/pi-tui";
|
|
10
|
-
import { writeDefaultProfile, writeDefaultConfig } from "./index.js";
|
|
1
|
+
/** Generic Runner setup. Provider/model semantics remain inside each Runner plugin. */
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { Input, ProcessTerminal, SelectList, Text, TUI } from "@mariozechner/pi-tui";
|
|
4
|
+
import { readDefaultRunnerProfile, updateWorkspaceRunnerProfile, writeDefaultRunnerProfile } from "./index.js";
|
|
11
5
|
import { stdioQueue } from "./prompt-queue.js";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
{ value: "openai", label: "openai", description: "OpenAI models via OPENAI_API_KEY" },
|
|
15
|
-
{ value: "ark-coding", label: "ark-coding", description: "Volcano Ark coding plans (explicit context/output limits)" },
|
|
16
|
-
{ value: "faux", label: "faux", description: "Offline simulated runner for smoke tests" },
|
|
17
|
-
];
|
|
18
|
-
const KNOWN_MODELS = {
|
|
19
|
-
anthropic: ["claude-sonnet-4-6", "claude-opus-4-6", "claude-haiku-4-5"],
|
|
20
|
-
};
|
|
21
|
-
const CUSTOM = "__custom__";
|
|
22
|
-
const listTheme = {
|
|
6
|
+
import { runnerManifests, runnerPlugin } from "./runner-registry.js";
|
|
7
|
+
const theme = {
|
|
23
8
|
selectedPrefix: (text) => `\x1b[36m❯\x1b[0m ${text}`,
|
|
24
9
|
selectedText: (text) => `\x1b[1m${text}\x1b[22m`,
|
|
25
10
|
description: (text) => `\x1b[2m${text}\x1b[22m`,
|
|
26
11
|
scrollInfo: (text) => `\x1b[2m${text}\x1b[22m`,
|
|
27
12
|
noMatch: (text) => `\x1b[2m${text}\x1b[22m`,
|
|
28
13
|
};
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
14
|
+
export async function runSetupWizard(current = readDefaultRunnerProfile()) {
|
|
15
|
+
return process.stdin.isTTY && process.stdout.isTTY ? runTui(current) : runPiped(current);
|
|
16
|
+
}
|
|
17
|
+
async function runTui(current) {
|
|
33
18
|
const tui = new TUI(new ProcessTerminal());
|
|
34
19
|
const header = new Text("");
|
|
35
20
|
let slot = null;
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
/** Free-text entry on one Input line. */
|
|
59
|
-
const askText = (title, subtitle, prompt) => {
|
|
60
|
-
setHeader(title, subtitle);
|
|
61
|
-
const input = new Input();
|
|
62
|
-
const { promise, resolve } = Promise.withResolvers();
|
|
63
|
-
let answerLine = resolve;
|
|
64
|
-
input.onSubmit = (line) => { input.setValue(""); const deliver = answerLine; answerLine = null; deliver?.(line.trim()); };
|
|
65
|
-
setSlot(input);
|
|
66
|
-
process.stdout.write(`${prompt} `);
|
|
67
|
-
return promise;
|
|
21
|
+
const replace = (component) => { if (slot)
|
|
22
|
+
tui.removeChild(slot); slot = component; tui.addChild(component); tui.setFocus(component); tui.requestRender(); };
|
|
23
|
+
const setHeader = (title, description = "") => { header.setText(["", `\x1b[1m Goah setup — ${title}\x1b[22m`, description ? `\x1b[2m ${description}\x1b[22m` : "", ""].join("\n")); };
|
|
24
|
+
const interaction = {
|
|
25
|
+
select: ({ title, description, choices }) => new Promise((resolve) => {
|
|
26
|
+
setHeader(title, description);
|
|
27
|
+
const list = new SelectList(choices, 10, theme);
|
|
28
|
+
list.onSelect = (item) => resolve(item.value);
|
|
29
|
+
list.onCancel = () => resolve(null);
|
|
30
|
+
replace(list);
|
|
31
|
+
}),
|
|
32
|
+
input: ({ title, description, prompt, initial }) => new Promise((resolve) => {
|
|
33
|
+
setHeader(title, `${description ?? ""}\n ${prompt}`.trim());
|
|
34
|
+
const input = new Input();
|
|
35
|
+
if (initial)
|
|
36
|
+
input.setValue(initial);
|
|
37
|
+
input.onSubmit = (line) => resolve(line.trim());
|
|
38
|
+
input.onEscape = () => resolve(null);
|
|
39
|
+
replace(input);
|
|
40
|
+
}),
|
|
41
|
+
notify: (message) => { setHeader("Authentication", message); tui.requestRender(); },
|
|
42
|
+
openUrl,
|
|
68
43
|
};
|
|
69
44
|
tui.addChild(header);
|
|
45
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
70
46
|
void (async () => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
const provider = providerValue;
|
|
79
|
-
options = { provider };
|
|
80
|
-
const known = KNOWN_MODELS[provider] ?? [];
|
|
81
|
-
let model = "";
|
|
82
|
-
if (known.length > 0) {
|
|
83
|
-
const modelValue = await pick("Model", `Provider: ${provider}`, [...known.map((id) => ({ value: id, label: id })), { value: CUSTOM, label: "custom…", description: "type the model id yourself" }]);
|
|
84
|
-
if (!modelValue) {
|
|
85
|
-
resolveDone({ options });
|
|
47
|
+
while (true) {
|
|
48
|
+
try {
|
|
49
|
+
const manifests = runnerManifests();
|
|
50
|
+
const runner = await interaction.select({ title: "Runner", description: "The Runner owns its model, provider, authentication, and execution semantics.", choices: manifests.map((item) => ({ value: item.id, label: item.name, description: item.description })) });
|
|
51
|
+
if (!runner) {
|
|
52
|
+
resolve({ profile: null });
|
|
86
53
|
return;
|
|
87
54
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
55
|
+
const config = await runnerPlugin(runner).configurator.setup(current?.runner === runner ? current.config : null, interaction);
|
|
56
|
+
if (config === null) {
|
|
57
|
+
resolve({ profile: null });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const confirmation = await interaction.select({ title: "Confirm", description: `${runner} runner\n ${summarize(config)}`, choices: [{ value: "save", label: "Save runner profile" }, { value: "cancel", label: "Cancel" }] });
|
|
61
|
+
resolve({ profile: confirmation === "save" ? { id: current?.id ?? "default", runner, config } : null });
|
|
95
62
|
return;
|
|
96
63
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (provider === "ark-coding") {
|
|
104
|
-
const contextWindowTokens = Number(await askText("Context window", "Ark's model-list API does not publish limits; enter the published value", "context window tokens (e.g. 256000):"));
|
|
105
|
-
const maxOutputTokensPerTurn = Number(await askText("Max output", "per-turn output cap", "max output tokens per turn (e.g. 32000):"));
|
|
106
|
-
if (!Number.isInteger(contextWindowTokens) || contextWindowTokens <= 0 || !Number.isInteger(maxOutputTokensPerTurn) || maxOutputTokensPerTurn <= 0)
|
|
107
|
-
throw new Error("ark-coding requires integer context-window and max-output tokens");
|
|
108
|
-
options = { ...options, contextWindowTokens, maxOutputTokensPerTurn };
|
|
64
|
+
catch (error) {
|
|
65
|
+
const choice = await interaction.select({ title: "Setup failed", description: error instanceof Error ? error.message : String(error), choices: [{ value: "retry", label: "Try again" }, { value: "cancel", label: "Cancel without saving" }] });
|
|
66
|
+
if (choice !== "retry") {
|
|
67
|
+
resolve({ profile: null });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
109
70
|
}
|
|
110
|
-
const summary = ["", ` provider ${options.provider}`, ` model ${options.model}`, ...(options.apiKeyEnv ? [` key env ${options.apiKeyEnv}`] : []), ...(options.contextWindowTokens ? [` context ${options.contextWindowTokens}`, ` output ${options.maxOutputTokensPerTurn}`] : [])].join("\n");
|
|
111
|
-
const confirmation = await pick("Confirm", `${summary}`, [{ value: "save", label: "Save profile" }, { value: "cancel", label: "Cancel" }]);
|
|
112
|
-
if (confirmation !== "save")
|
|
113
|
-
options = {};
|
|
114
71
|
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
setHeader("Error", error instanceof Error ? error.message : String(error));
|
|
117
|
-
}
|
|
118
|
-
resolveDone({ options });
|
|
119
72
|
})();
|
|
120
73
|
tui.start();
|
|
121
|
-
const result = await
|
|
74
|
+
const result = await promise;
|
|
122
75
|
tui.stop();
|
|
123
76
|
return result;
|
|
124
77
|
}
|
|
125
|
-
|
|
126
|
-
async function runPipedWizard() {
|
|
78
|
+
async function runPiped(current) {
|
|
127
79
|
const ask = stdioQueue();
|
|
80
|
+
const interaction = {
|
|
81
|
+
select: async ({ title, choices }) => {
|
|
82
|
+
const answer = Number(await ask(`${title}: ${choices.map((choice, index) => `${index + 1}) ${choice.label}`).join(" ")} — select: `));
|
|
83
|
+
return choices[answer - 1]?.value ?? null;
|
|
84
|
+
},
|
|
85
|
+
input: async ({ prompt, initial }) => (await ask(`${prompt}${initial ? ` (${initial})` : ""}: `)) || initial || null,
|
|
86
|
+
notify: (message) => process.stdout.write(`${message}\n`),
|
|
87
|
+
};
|
|
128
88
|
try {
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (provider !== "faux") {
|
|
136
|
-
const destination = provider === "anthropic" ? "ANTHROPIC_API_KEY" : provider === "openai" ? "OPENAI_API_KEY" : "ARK_API_KEY";
|
|
137
|
-
const apiKeyEnv = await ask(`API key environment variable (default ${destination}): `);
|
|
138
|
-
options.apiKeyEnv = apiKeyEnv && /^[A-Z_][A-Z0-9_]*$/.test(apiKeyEnv) ? apiKeyEnv : destination;
|
|
139
|
-
}
|
|
140
|
-
if (provider === "ark-coding") {
|
|
141
|
-
const contextWindowTokens = Number(await ask("context window tokens: "));
|
|
142
|
-
const maxOutputTokensPerTurn = Number(await ask("max output tokens per turn: "));
|
|
143
|
-
if (Number.isInteger(contextWindowTokens) && contextWindowTokens > 0 && Number.isInteger(maxOutputTokensPerTurn) && maxOutputTokensPerTurn > 0) {
|
|
144
|
-
options.contextWindowTokens = contextWindowTokens;
|
|
145
|
-
options.maxOutputTokensPerTurn = maxOutputTokensPerTurn;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return { options };
|
|
89
|
+
const manifests = runnerManifests();
|
|
90
|
+
const runner = await interaction.select({ title: "Runner", choices: manifests.map((item) => ({ value: item.id, label: item.name })) });
|
|
91
|
+
if (!runner)
|
|
92
|
+
return { profile: null };
|
|
93
|
+
const config = await runnerPlugin(runner).configurator.setup(current?.runner === runner ? current.config : null, interaction);
|
|
94
|
+
return { profile: config === null ? null : { id: current?.id ?? "default", runner, config } };
|
|
149
95
|
}
|
|
150
96
|
finally {
|
|
151
97
|
ask.close();
|
|
152
98
|
}
|
|
153
99
|
}
|
|
154
|
-
/** Persist the wizard result and (optionally) materialize this directory's workspace config. */
|
|
155
100
|
export function applyWizardResult(result, configPath) {
|
|
156
|
-
if (!result.
|
|
157
|
-
throw new Error("
|
|
158
|
-
|
|
101
|
+
if (!result.profile)
|
|
102
|
+
throw new Error("Setup was cancelled; your existing configuration was not changed.");
|
|
103
|
+
writeDefaultRunnerProfile(result.profile);
|
|
159
104
|
if (configPath)
|
|
160
|
-
|
|
105
|
+
updateWorkspaceRunnerProfile(configPath, result.profile);
|
|
106
|
+
}
|
|
107
|
+
function summarize(config) {
|
|
108
|
+
if (!config || typeof config !== "object" || Array.isArray(config))
|
|
109
|
+
return String(config);
|
|
110
|
+
return Object.entries(config).filter(([key]) => !/key|token|secret|password/i.test(key)).map(([key, value]) => `${key}: ${String(value)}`).join("\n ");
|
|
111
|
+
}
|
|
112
|
+
function openUrl(url) {
|
|
113
|
+
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
114
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
115
|
+
spawn(command, args, { detached: true, stdio: "ignore" }).unref();
|
|
161
116
|
}
|
|
162
117
|
//# sourceMappingURL=setup-wizard.js.map
|
package/dist/setup-wizard.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup-wizard.js","sourceRoot":"","sources":["../src/setup-wizard.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"setup-wizard.js","sourceRoot":"","sources":["../src/setup-wizard.ts"],"names":[],"mappings":"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"}
|
package/dist/tui.d.ts
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
import { type ControlFrame } from "./control.js";
|
|
2
|
+
export type TuiInputAction = "quit" | "help" | "status" | "stop" | "model" | "setup" | "goal" | "approval" | "empty" | "queue" | "send";
|
|
3
|
+
export declare function classifyTuiInput(value: string, busy: boolean): {
|
|
4
|
+
action: TuiInputAction;
|
|
5
|
+
text: string;
|
|
6
|
+
};
|
|
1
7
|
export declare function runGoahTui(configPath: string, stateDir: string, initialMessage: string | null): Promise<void>;
|
|
8
|
+
/** Render one control-protocol frame into a transcript line. */
|
|
9
|
+
export declare function renderFrame(frame: ControlFrame, push: (line: string) => void, appendLive?: (text: string) => void, commitLive?: (text: string) => void): void;
|
|
2
10
|
//# sourceMappingURL=tui.d.ts.map
|
package/dist/tui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmD,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAUlG,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;AACxI,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAYvG;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAiFnH;AAiCD,gEAAgE;AAChE,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAAE,UAAU,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAW,EAAE,UAAU,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAW,GAAG,IAAI,CA2BzK"}
|
package/dist/tui.js
CHANGED
|
@@ -2,74 +2,150 @@
|
|
|
2
2
|
import { TUI, Text, Input, ProcessTerminal } from "@mariozechner/pi-tui";
|
|
3
3
|
import { controlAvailable, requestControl, streamControl } from "./control.js";
|
|
4
4
|
import { readConsoleMetadata } from "./index.js";
|
|
5
|
-
import { switchModel, reloadDaemon,
|
|
5
|
+
import { switchModel, reloadDaemon, readRunnerDisplay } from "./live-config.js";
|
|
6
6
|
import { welcomeSnapshot, renderWelcome } from "./welcome.js";
|
|
7
7
|
import { runSetupWizard, applyWizardResult } from "./setup-wizard.js";
|
|
8
8
|
import { spawn } from "node:child_process";
|
|
9
|
-
import {
|
|
9
|
+
import { closeSync, mkdirSync, openSync } from "node:fs";
|
|
10
|
+
import { join, resolve } from "node:path";
|
|
11
|
+
export function classifyTuiInput(value, busy) {
|
|
12
|
+
const text = value.trim();
|
|
13
|
+
if (!text)
|
|
14
|
+
return { action: "empty", text };
|
|
15
|
+
if (text === "/quit" || text === "/exit")
|
|
16
|
+
return { action: "quit", text };
|
|
17
|
+
if (text === "/help")
|
|
18
|
+
return { action: "help", text };
|
|
19
|
+
if (text === "/status")
|
|
20
|
+
return { action: "status", text };
|
|
21
|
+
if (text === "/stop")
|
|
22
|
+
return { action: "stop", text };
|
|
23
|
+
if (text.startsWith("/model "))
|
|
24
|
+
return { action: "model", text };
|
|
25
|
+
if (text === "/setup")
|
|
26
|
+
return { action: "setup", text };
|
|
27
|
+
if (text.startsWith("/goal ") || text.startsWith("/observe "))
|
|
28
|
+
return { action: "goal", text };
|
|
29
|
+
if (text.startsWith("/approve ") || text.startsWith("/reject "))
|
|
30
|
+
return { action: "approval", text };
|
|
31
|
+
return { action: busy ? "queue" : "send", text };
|
|
32
|
+
}
|
|
10
33
|
export async function runGoahTui(configPath, stateDir, initialMessage) {
|
|
11
34
|
if (!process.stdout.isTTY || !process.stdin.isTTY)
|
|
12
35
|
return runNonInteractive(configPath, stateDir, initialMessage);
|
|
13
|
-
const
|
|
14
|
-
const snapshot = welcomeSnapshot(stateDir,
|
|
36
|
+
const runner = readRunnerDisplay(configPath);
|
|
37
|
+
const snapshot = welcomeSnapshot(stateDir, runner);
|
|
15
38
|
const transcript = renderWelcome(snapshot, snapshot.handoffs.length > 0);
|
|
16
39
|
await ensureDaemon(configPath, stateDir);
|
|
17
40
|
const terminal = new ProcessTerminal();
|
|
18
41
|
const tui = new TUI(terminal);
|
|
19
42
|
const transcriptView = new Text(transcript.join("\n"));
|
|
20
43
|
const input = new Input();
|
|
44
|
+
const statusView = new Text("Ready · Enter sends · Ctrl+C detaches · /help lists commands");
|
|
21
45
|
const busy = { active: false };
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
46
|
+
const queued = [];
|
|
47
|
+
let liveText = "";
|
|
48
|
+
let activeStream = null;
|
|
49
|
+
let exiting = false;
|
|
50
|
+
const render = () => {
|
|
25
51
|
const kept = transcript.slice(-500);
|
|
26
52
|
transcript.length = 0;
|
|
27
53
|
transcript.push(...kept);
|
|
28
|
-
transcriptView.setText(transcript.join("\n"));
|
|
54
|
+
transcriptView.setText([...transcript, ...(liveText ? [liveText] : [])].join("\n"));
|
|
29
55
|
tui.requestRender();
|
|
30
56
|
};
|
|
57
|
+
const push = (line) => {
|
|
58
|
+
transcript.push(line);
|
|
59
|
+
render();
|
|
60
|
+
};
|
|
61
|
+
const appendLive = (text) => { liveText += text; render(); };
|
|
62
|
+
const commitLive = (text) => { liveText = ""; if (text)
|
|
63
|
+
transcript.push(text); render(); };
|
|
31
64
|
const send = async (message) => {
|
|
32
65
|
busy.active = true;
|
|
66
|
+
const controller = new AbortController();
|
|
67
|
+
activeStream = controller;
|
|
68
|
+
statusView.setText(`Working · ${queued.length} queued · new messages become follow-ups`);
|
|
33
69
|
push(`> ${message}`);
|
|
34
70
|
try {
|
|
35
|
-
await streamControl(stateDir, { op: "interact", message }, (frame) => renderFrame(frame, push));
|
|
71
|
+
await streamControl(stateDir, { op: "interact", message }, (frame) => renderFrame(frame, push, appendLive, commitLive), controller.signal);
|
|
36
72
|
}
|
|
37
73
|
catch (error) {
|
|
38
|
-
|
|
74
|
+
if (!controller.signal.aborted)
|
|
75
|
+
push(`! ${error instanceof Error ? error.message : String(error)}`);
|
|
39
76
|
}
|
|
40
77
|
finally {
|
|
78
|
+
if (activeStream === controller)
|
|
79
|
+
activeStream = null;
|
|
41
80
|
busy.active = false;
|
|
81
|
+
statusView.setText(queued.length ? `${queued.length} follow-up queued` : "Ready · Enter sends · Ctrl+C detaches · /help lists commands");
|
|
82
|
+
const next = exiting ? undefined : queued.shift();
|
|
83
|
+
if (next)
|
|
84
|
+
void send(next);
|
|
42
85
|
}
|
|
43
86
|
};
|
|
44
87
|
input.onSubmit = (line) => {
|
|
45
|
-
const text = line.
|
|
88
|
+
const { action, text } = classifyTuiInput(line, busy.active);
|
|
46
89
|
input.setValue("");
|
|
47
|
-
if (
|
|
90
|
+
if (action === "quit") {
|
|
91
|
+
exiting = true;
|
|
92
|
+
activeStream?.abort();
|
|
48
93
|
tui.stop();
|
|
49
94
|
resolveExit();
|
|
50
95
|
return;
|
|
51
96
|
}
|
|
52
|
-
if (
|
|
97
|
+
if (action === "help") {
|
|
98
|
+
push("Commands: /status · /stop · /model TARGET · /setup · /goal TEXT · /observe TEXT · /approve ID --reason TEXT --evidence SEQ · /reject … · /quit");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (action === "status") {
|
|
53
102
|
void printStatus(stateDir, push);
|
|
54
103
|
return;
|
|
55
104
|
}
|
|
56
|
-
if (
|
|
105
|
+
if (action === "stop") {
|
|
106
|
+
void stopCeoWake(stateDir, push);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (action === "model") {
|
|
57
110
|
void switchModelCommand(text, configPath, stateDir, push);
|
|
58
111
|
return;
|
|
59
112
|
}
|
|
60
|
-
if (
|
|
113
|
+
if (action === "setup") {
|
|
61
114
|
void runSetupReload(configPath, stateDir, push);
|
|
62
115
|
return;
|
|
63
116
|
}
|
|
64
|
-
if (
|
|
117
|
+
if (action === "goal") {
|
|
65
118
|
void slashGoal(text, stateDir, push);
|
|
66
119
|
return;
|
|
67
120
|
}
|
|
121
|
+
if (action === "approval") {
|
|
122
|
+
void slashApprove(text, stateDir, push);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (action === "queue") {
|
|
126
|
+
queued.push(text);
|
|
127
|
+
push(`↳ queued follow-up: ${text}`);
|
|
128
|
+
statusView.setText(`Working · ${queued.length} queued`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (action === "send")
|
|
132
|
+
void send(text);
|
|
68
133
|
};
|
|
69
134
|
tui.addChild(transcriptView);
|
|
135
|
+
tui.addChild(statusView);
|
|
70
136
|
tui.addChild(input);
|
|
71
137
|
tui.setFocus(input);
|
|
72
138
|
const { promise: exited, resolve: resolveExit } = Promise.withResolvers();
|
|
139
|
+
tui.addInputListener((data) => {
|
|
140
|
+
if (data !== "\x03")
|
|
141
|
+
return undefined;
|
|
142
|
+
exiting = true;
|
|
143
|
+
push(busy.active ? "Detached — the current wake continues in the daemon. Use /stop before detaching when you intend to cancel it." : "Detached.");
|
|
144
|
+
activeStream?.abort();
|
|
145
|
+
tui.stop();
|
|
146
|
+
resolveExit();
|
|
147
|
+
return { consume: true };
|
|
148
|
+
});
|
|
73
149
|
tui.start();
|
|
74
150
|
if (initialMessage)
|
|
75
151
|
void send(initialMessage);
|
|
@@ -113,7 +189,7 @@ function frameToLine(frame) {
|
|
|
113
189
|
return lines.length ? lines.join("\n") : null;
|
|
114
190
|
}
|
|
115
191
|
/** Render one control-protocol frame into a transcript line. */
|
|
116
|
-
function renderFrame(frame, push) {
|
|
192
|
+
export function renderFrame(frame, push, appendLive = push, commitLive = push) {
|
|
117
193
|
if (frame.type === "error") {
|
|
118
194
|
push(`! ${frame.error}`);
|
|
119
195
|
return;
|
|
@@ -132,11 +208,19 @@ function renderFrame(frame, push) {
|
|
|
132
208
|
if (record.type === "tool.called") {
|
|
133
209
|
push(`→ ${String(data.name)} ${compact(data.arguments ?? {})}`);
|
|
134
210
|
}
|
|
211
|
+
else if (record.type === "tool.completed") {
|
|
212
|
+
push(`${data.isError ? "✗" : "✓"} ${String(data.name ?? "tool")} ${data.isError ? "failed" : "completed"}`);
|
|
213
|
+
}
|
|
214
|
+
else if (record.type === "message.assistant.delta") {
|
|
215
|
+
const delta = data.delta && typeof data.delta === "object" && !Array.isArray(data.delta) ? data.delta : {};
|
|
216
|
+
if (typeof delta.delta === "string")
|
|
217
|
+
appendLive(delta.delta);
|
|
218
|
+
}
|
|
135
219
|
else if (record.type === "message.assistant.completed") {
|
|
136
220
|
const message = data.message && typeof data.message === "object" ? data.message : {};
|
|
137
221
|
const text = messageText(message.content);
|
|
138
222
|
if (text)
|
|
139
|
-
|
|
223
|
+
commitLive(text);
|
|
140
224
|
}
|
|
141
225
|
else if (record.type === "handoff.recorded") {
|
|
142
226
|
const results = Array.isArray(data.results) ? data.results : [];
|
|
@@ -154,7 +238,20 @@ function renderFrame(frame, push) {
|
|
|
154
238
|
async function printStatus(stateDir, push) {
|
|
155
239
|
try {
|
|
156
240
|
const status = await requestControl(stateDir, { op: "ceo.status" });
|
|
157
|
-
|
|
241
|
+
const value = status && typeof status === "object" && !Array.isArray(status) ? status : {};
|
|
242
|
+
const roots = Array.isArray(value.roots) ? value.roots : [];
|
|
243
|
+
const team = Array.isArray(value.team) ? value.team : [];
|
|
244
|
+
const pending = Array.isArray(value.pendingHuman) ? value.pendingHuman.length : 0;
|
|
245
|
+
push([roots[0] ? `Goal: ${String(roots[0].objective)} [${String(roots[0].phase)}]` : "Goal: none", `Team: ${team.map((member) => `${String(member.agent)}=${String(member.status)}`).join(" · ") || "none"}`, `Needs you: ${pending}`].join("\n"));
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
push(`! ${error instanceof Error ? error.message : String(error)}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
async function stopCeoWake(stateDir, push) {
|
|
252
|
+
try {
|
|
253
|
+
const wake = await requestControl(stateDir, { op: "wake.stop", agent: "ceo" });
|
|
254
|
+
push(wake ? "Stopping the current CEO wake; its durable trace will close as interrupted." : "No active CEO wake.");
|
|
158
255
|
}
|
|
159
256
|
catch (error) {
|
|
160
257
|
push(`! ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -199,7 +296,10 @@ async function slashApprove(text, stateDir, push) {
|
|
|
199
296
|
async function ensureDaemon(configPath, stateDir) {
|
|
200
297
|
if (await controlAvailable(stateDir))
|
|
201
298
|
return;
|
|
202
|
-
|
|
299
|
+
mkdirSync(stateDir, { recursive: true });
|
|
300
|
+
const log = openSync(join(stateDir, "daemon.log"), "a");
|
|
301
|
+
spawn(process.execPath, [process.argv[1], "start", "--config", resolve(configPath)], { cwd: process.cwd(), detached: true, stdio: ["ignore", log, log], env: process.env }).unref();
|
|
302
|
+
closeSync(log);
|
|
203
303
|
const deadline = Date.now() + 10_000;
|
|
204
304
|
while (Date.now() < deadline) {
|
|
205
305
|
if (await controlAvailable(stateDir))
|
|
@@ -242,7 +342,7 @@ async function switchModelCommand(text, configPath, stateDir, push) {
|
|
|
242
342
|
async function runSetupReload(configPath, stateDir, push) {
|
|
243
343
|
push("Leaving the TUI for setup — it returns after the wizard finishes.");
|
|
244
344
|
const result = await runSetupWizard();
|
|
245
|
-
if (!result.
|
|
345
|
+
if (!result.profile) {
|
|
246
346
|
push("setup cancelled; nothing changed");
|
|
247
347
|
return;
|
|
248
348
|
}
|