@aui.io/apollo 0.1.6 → 0.1.9
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/node_modules/@aui.io/apollo-cli/dist/src/commands/bundles.js +27 -4
- package/node_modules/@aui.io/apollo-cli/dist/src/commands/bundles.js.map +1 -1
- package/node_modules/@aui.io/apollo-cli/dist/src/commands/env.js +1 -1
- package/node_modules/@aui.io/apollo-cli/dist/src/commands/env.js.map +1 -1
- package/node_modules/@aui.io/apollo-cli/dist/src/commands/hierarchy.js +116 -36
- package/node_modules/@aui.io/apollo-cli/dist/src/commands/hierarchy.js.map +1 -1
- package/node_modules/@aui.io/apollo-cli/dist/src/commands/picker.d.ts +27 -0
- package/node_modules/@aui.io/apollo-cli/dist/src/commands/picker.js +68 -0
- package/node_modules/@aui.io/apollo-cli/dist/src/commands/picker.js.map +1 -0
- package/node_modules/@aui.io/apollo-cli/dist/src/commands/shell.d.ts +9 -3
- package/node_modules/@aui.io/apollo-cli/dist/src/commands/shell.js +26 -4
- package/node_modules/@aui.io/apollo-cli/dist/src/commands/shell.js.map +1 -1
- package/node_modules/@aui.io/apollo-cli/dist/src/commands/workspace.js +38 -53
- package/node_modules/@aui.io/apollo-cli/dist/src/commands/workspace.js.map +1 -1
- package/node_modules/@aui.io/apollo-cli/package.json +3 -3
- package/node_modules/@aui.io/apollo-core/dist/src/services/hierarchy.js +7 -1
- package/node_modules/@aui.io/apollo-core/dist/src/services/hierarchy.js.map +1 -1
- package/node_modules/@aui.io/apollo-core/package.json +1 -1
- package/node_modules/@aui.io/apollo-tui/dist/src/index.d.ts +2 -1
- package/node_modules/@aui.io/apollo-tui/dist/src/index.js +2 -1
- package/node_modules/@aui.io/apollo-tui/dist/src/index.js.map +1 -1
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/actions.d.ts +41 -0
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/actions.js +927 -0
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/actions.js.map +1 -0
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/commands.d.ts +10 -4
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/commands.js +402 -23
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/commands.js.map +1 -1
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/confirm.d.ts +10 -0
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/confirm.js +12 -0
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/confirm.js.map +1 -0
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/picker.d.ts +28 -0
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/picker.js +72 -0
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/picker.js.map +1 -0
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/place.d.ts +19 -0
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/place.js +16 -0
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/place.js.map +1 -0
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/scope.d.ts +4 -0
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/scope.js +9 -1
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/scope.js.map +1 -1
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/sheet.d.ts +2 -9
- package/node_modules/@aui.io/apollo-tui/dist/src/lib/sheet.js.map +1 -1
- package/node_modules/@aui.io/apollo-tui/dist/src/opentui-app.js +478 -182
- package/node_modules/@aui.io/apollo-tui/dist/src/opentui-app.js.map +1 -1
- package/node_modules/@aui.io/apollo-tui/package.json +2 -2
- package/package.json +4 -4
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
|
-
* Apollo TUI — OpenTUI engine
|
|
3
|
+
* Apollo TUI — OpenTUI engine.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* logic stays in @aui.io/apollo-core; presentation-only state lives here.
|
|
5
|
+
* Global slash command desk; tabs are views. Business logic in apollo-core
|
|
6
|
+
* via lib/actions.ts.
|
|
8
7
|
*/
|
|
9
8
|
import { useEffect, useRef, useState } from "react";
|
|
10
9
|
import { createCliRenderer } from "@opentui/core";
|
|
11
10
|
import { createRoot, useKeyboard } from "@opentui/react";
|
|
12
11
|
import { resolveContext } from "@aui.io/apollo-core/config/index.js";
|
|
13
|
-
import { localDiff, pullBundle, pushBundle, remoteDiff, validateLocalBundle, } from "@aui.io/apollo-core/services/bundles.js";
|
|
14
12
|
import { createClients } from "@aui.io/apollo-core/services/clients.js";
|
|
15
|
-
import { provisionMockDb, wireMockDbConnection, } from "@aui.io/apollo-core/services/mockdb.js";
|
|
16
|
-
import { getStatus } from "@aui.io/apollo-core/services/status.service.js";
|
|
17
13
|
import { sendMessage } from "@aui.io/apollo-core/services/messaging.js";
|
|
18
14
|
import { GlyphController } from "./components/glyph.js";
|
|
19
|
-
import {
|
|
15
|
+
import { applyPick, chatOverlay, destructiveSpec, loadPickerCatalog, refreshContext, runDestructive, runSheetAction, runSimpleCommand, } from "./lib/actions.js";
|
|
16
|
+
import { filterCommands, helpLines, PLACE_ONBOARD_COMMAND, resolveCommand, } from "./lib/commands.js";
|
|
17
|
+
import { checkConfirm, createConfirm } from "./lib/confirm.js";
|
|
18
|
+
import { clampPickerCursor, effectivePickerFilter, filterPickerItems, pickerColumnWidths, pickerPageSize, resolvePickerAnswer, truncatePickerText, } from "./lib/picker.js";
|
|
19
|
+
import { hasPlace } from "./lib/place.js";
|
|
20
20
|
import { createSheet, moveFocus, setFocusedValue, validateSheet, visibleFields, } from "./lib/sheet.js";
|
|
21
21
|
import { scopeFromContext } from "./lib/scope.js";
|
|
22
22
|
import { TABS, nextTab, prevTab, tabByIndex } from "./lib/tabs.js";
|
|
@@ -27,30 +27,38 @@ const RED = "#D46A6A";
|
|
|
27
27
|
const DIM = "#8A8A8A";
|
|
28
28
|
const FAINT = "#5C5C5C";
|
|
29
29
|
const FG = "#D8D8D8";
|
|
30
|
-
const VERSION = process.env.npm_package_version ?? "0.1.
|
|
31
|
-
function
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
return "no changes";
|
|
35
|
-
return `+${diff.added.length} ~${diff.changed.length} -${diff.removed.length}`;
|
|
36
|
-
}
|
|
37
|
-
function App({ context, initialScope, glyph, onQuit }) {
|
|
30
|
+
const VERSION = process.env.npm_package_version ?? "0.1.7";
|
|
31
|
+
function App({ context: initialContext, initialScope, glyph, onQuit }) {
|
|
32
|
+
const [context, setContext] = useState(initialContext);
|
|
33
|
+
const [scope, setScope] = useState(initialScope);
|
|
38
34
|
const [tab, setTab] = useState("overview");
|
|
39
35
|
const [input, setInput] = useState("");
|
|
40
36
|
const [chat, setChat] = useState([]);
|
|
41
|
-
const [
|
|
42
|
-
const [
|
|
43
|
-
const [
|
|
37
|
+
const [threadId, setThreadId] = useState();
|
|
38
|
+
const [toast, setToast] = useState();
|
|
39
|
+
const [sheet, setSheet] = useState();
|
|
40
|
+
const [sheetCommand, setSheetCommand] = useState();
|
|
41
|
+
const [picker, setPicker] = useState();
|
|
42
|
+
const [confirm, setConfirm] = useState();
|
|
43
|
+
const [paneTitle, setPaneTitle] = useState();
|
|
44
|
+
const [paneLines, setPaneLines] = useState([]);
|
|
44
45
|
const [working, setWorking] = useState(false);
|
|
45
46
|
const [trace, setTrace] = useState(false);
|
|
46
47
|
const [localMode, setLocalMode] = useState(false);
|
|
47
48
|
const [vars, setVars] = useState([]);
|
|
48
49
|
const [frame, setFrame] = useState(glyph.frame());
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const stateRef = useRef({
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
/** Soft place gate offered once per session (pick / skip / already had place). */
|
|
51
|
+
const placeGateOffered = useRef(false);
|
|
52
|
+
const stateRef = useRef({
|
|
53
|
+
tab,
|
|
54
|
+
sheet,
|
|
55
|
+
sheetCommand,
|
|
56
|
+
picker,
|
|
57
|
+
confirm,
|
|
58
|
+
context,
|
|
59
|
+
input,
|
|
60
|
+
});
|
|
61
|
+
stateRef.current = { tab, sheet, sheetCommand, picker, confirm, context, input };
|
|
54
62
|
useEffect(() => {
|
|
55
63
|
if (tab !== "overview")
|
|
56
64
|
return;
|
|
@@ -60,24 +68,62 @@ function App({ context, initialScope, glyph, onQuit }) {
|
|
|
60
68
|
useEffect(() => {
|
|
61
69
|
glyph.setWorking(working);
|
|
62
70
|
}, [working, glyph]);
|
|
63
|
-
const paletteOpen = !sheet && input.startsWith("/");
|
|
64
|
-
const paletteMatches = paletteOpen
|
|
71
|
+
const paletteOpen = !sheet && !picker && !confirm && input.startsWith("/");
|
|
72
|
+
const paletteMatches = paletteOpen
|
|
73
|
+
? filterCommands(input, tab === "overview" ? undefined : tab).slice(0, 10)
|
|
74
|
+
: [];
|
|
65
75
|
useKeyboard((key) => {
|
|
66
76
|
if (key.eventType === "release")
|
|
67
77
|
return;
|
|
68
78
|
const current = stateRef.current;
|
|
79
|
+
if (current.picker && (key.name === "up" || key.name === "down")) {
|
|
80
|
+
const visible = filterPickerItems(current.picker.items, effectivePickerFilter(current.input));
|
|
81
|
+
if (visible.length === 0)
|
|
82
|
+
return;
|
|
83
|
+
const delta = key.name === "down" ? 1 : -1;
|
|
84
|
+
setPicker({
|
|
85
|
+
...current.picker,
|
|
86
|
+
cursor: clampPickerCursor(current.picker.cursor + delta, visible.length),
|
|
87
|
+
});
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
69
90
|
if (key.name === "tab") {
|
|
91
|
+
if (current.picker) {
|
|
92
|
+
const visible = filterPickerItems(current.picker.items, effectivePickerFilter(current.input));
|
|
93
|
+
if (visible.length === 0)
|
|
94
|
+
return;
|
|
95
|
+
setPicker({
|
|
96
|
+
...current.picker,
|
|
97
|
+
cursor: clampPickerCursor(current.picker.cursor + (key.shift ? -1 : 1), visible.length),
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
70
101
|
if (current.sheet) {
|
|
71
102
|
setSheet(moveFocus(current.sheet, key.shift ? -1 : 1));
|
|
72
103
|
}
|
|
73
|
-
else {
|
|
104
|
+
else if (!current.confirm) {
|
|
74
105
|
setTab((t) => (key.shift ? prevTab(t) : nextTab(t)));
|
|
75
106
|
setInput("");
|
|
76
107
|
}
|
|
77
108
|
return;
|
|
78
109
|
}
|
|
79
110
|
if (key.name === "escape") {
|
|
80
|
-
if (current.
|
|
111
|
+
if (current.confirm) {
|
|
112
|
+
setConfirm(undefined);
|
|
113
|
+
setToast("Cancelled");
|
|
114
|
+
}
|
|
115
|
+
else if (current.picker) {
|
|
116
|
+
const wasPlaceOnboard = current.picker.command.name === "place-onboard";
|
|
117
|
+
setPicker(undefined);
|
|
118
|
+
if (current.picker.sheet && current.picker.command) {
|
|
119
|
+
setSheet(current.picker.sheet);
|
|
120
|
+
setSheetCommand(current.picker.command);
|
|
121
|
+
}
|
|
122
|
+
setToast(wasPlaceOnboard
|
|
123
|
+
? "Continuing without a workspace — /workspace when ready"
|
|
124
|
+
: "Cancelled");
|
|
125
|
+
}
|
|
126
|
+
else if (current.sheet) {
|
|
81
127
|
setSheet(undefined);
|
|
82
128
|
setSheetCommand(undefined);
|
|
83
129
|
setToast("Cancelled");
|
|
@@ -95,6 +141,72 @@ function App({ context, initialScope, glyph, onQuit }) {
|
|
|
95
141
|
}
|
|
96
142
|
}
|
|
97
143
|
});
|
|
144
|
+
async function applyResult(result) {
|
|
145
|
+
if (result.error) {
|
|
146
|
+
setToast(result.error);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (result.openWorkspaceCreate) {
|
|
150
|
+
const createCmd = resolveCommand("/workspace-create").command;
|
|
151
|
+
if (createCmd?.fields) {
|
|
152
|
+
const model = createSheet(createCmd.name, createCmd.fields);
|
|
153
|
+
setSheet(model);
|
|
154
|
+
setSheetCommand(createCmd);
|
|
155
|
+
setToast("Create a workspace — enter a path");
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (result.toast)
|
|
160
|
+
setToast(result.toast);
|
|
161
|
+
if (result.jumpTab)
|
|
162
|
+
setTab(result.jumpTab);
|
|
163
|
+
if (result.chat)
|
|
164
|
+
setChat(result.chat);
|
|
165
|
+
if (result.threadId)
|
|
166
|
+
setThreadId(result.threadId);
|
|
167
|
+
if (result.paneLines) {
|
|
168
|
+
setPaneLines(result.paneLines);
|
|
169
|
+
setPaneTitle(result.paneTitle ?? "result");
|
|
170
|
+
}
|
|
171
|
+
if (result.needConfirm) {
|
|
172
|
+
setConfirm(createConfirm(result.needConfirm.title, result.needConfirm.phrase, result.needConfirm.action, result.needConfirm.args));
|
|
173
|
+
}
|
|
174
|
+
if (result.refreshScope) {
|
|
175
|
+
const next = await refreshContext();
|
|
176
|
+
setContext(next);
|
|
177
|
+
setScope(scopeFromContext(next));
|
|
178
|
+
await maybeOfferPlace(next);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async function openPlaceOnboarding() {
|
|
182
|
+
await openPick(PLACE_ONBOARD_COMMAND, "place-onboard");
|
|
183
|
+
}
|
|
184
|
+
async function maybeOfferPlace(ctx) {
|
|
185
|
+
if (placeGateOffered.current)
|
|
186
|
+
return;
|
|
187
|
+
if (hasPlace(ctx)) {
|
|
188
|
+
placeGateOffered.current = true;
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
// Logged-out: wait for /login (mount toast covers the hint).
|
|
192
|
+
if (!ctx.token)
|
|
193
|
+
return;
|
|
194
|
+
placeGateOffered.current = true;
|
|
195
|
+
await openPlaceOnboarding();
|
|
196
|
+
}
|
|
197
|
+
useEffect(() => {
|
|
198
|
+
if (hasPlace(initialContext)) {
|
|
199
|
+
placeGateOffered.current = true;
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (!initialContext.token) {
|
|
203
|
+
setToast("Not in a workspace · /login, then pick a place — Esc skips");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
placeGateOffered.current = true;
|
|
207
|
+
void openPlaceOnboarding();
|
|
208
|
+
// Once on mount — placeGateOffered guards re-entry.
|
|
209
|
+
}, []);
|
|
98
210
|
function ensureAuth(command) {
|
|
99
211
|
if (!command.requiresAuth || scope.loggedIn)
|
|
100
212
|
return true;
|
|
@@ -102,83 +214,132 @@ function App({ context, initialScope, glyph, onQuit }) {
|
|
|
102
214
|
setInput("/login");
|
|
103
215
|
return false;
|
|
104
216
|
}
|
|
217
|
+
async function openPick(command, kind, sheetModel, sheetField) {
|
|
218
|
+
setWorking(true);
|
|
219
|
+
try {
|
|
220
|
+
const items = await loadPickerCatalog(kind, stateRef.current.context);
|
|
221
|
+
if (items.length === 0) {
|
|
222
|
+
setToast(`No ${kind} options`);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (items.length === 1 && !sheetField && command.name !== "place-onboard") {
|
|
226
|
+
await finishPick(command, items[0].id, sheetModel, sheetField);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
setSheet(undefined);
|
|
230
|
+
setPicker({
|
|
231
|
+
command,
|
|
232
|
+
items,
|
|
233
|
+
cursor: 0,
|
|
234
|
+
...(sheetField ? { sheetField } : {}),
|
|
235
|
+
...(sheetModel ? { sheet: sheetModel } : {}),
|
|
236
|
+
});
|
|
237
|
+
setInput("");
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
setToast(error instanceof Error ? error.message : String(error));
|
|
241
|
+
}
|
|
242
|
+
finally {
|
|
243
|
+
setWorking(false);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async function finishPick(command, id, sheetModel, sheetField) {
|
|
247
|
+
setPicker(undefined);
|
|
248
|
+
if (sheetModel && sheetField) {
|
|
249
|
+
const next = {
|
|
250
|
+
...sheetModel,
|
|
251
|
+
values: { ...sheetModel.values, [sheetField]: id },
|
|
252
|
+
};
|
|
253
|
+
setSheet(next);
|
|
254
|
+
setSheetCommand(command);
|
|
255
|
+
setToast(`Selected ${id}`);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (command.destructive && (command.name === "version-publish" || command.name === "version-archive")) {
|
|
259
|
+
setConfirm(createConfirm(command.name, "yes", command.name, { id }));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
setWorking(true);
|
|
263
|
+
try {
|
|
264
|
+
await applyResult(await applyPick(command.name, id, stateRef.current.context));
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
setToast(error instanceof Error ? error.message : String(error));
|
|
268
|
+
}
|
|
269
|
+
finally {
|
|
270
|
+
setWorking(false);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
105
273
|
async function runCommand(command, args) {
|
|
106
274
|
if (command.jumpTab) {
|
|
107
275
|
setTab(command.jumpTab);
|
|
108
276
|
setToast(`Opened ${command.jumpTab}`);
|
|
109
277
|
return;
|
|
110
278
|
}
|
|
111
|
-
if (!ensureAuth(command))
|
|
112
|
-
return;
|
|
113
279
|
if (command.name === "help") {
|
|
114
|
-
|
|
280
|
+
setPaneTitle("help");
|
|
281
|
+
setPaneLines(helpLines());
|
|
282
|
+
setTab("overview");
|
|
283
|
+
setToast("Help — see pane");
|
|
115
284
|
return;
|
|
116
285
|
}
|
|
117
286
|
if (command.name === "quit") {
|
|
118
287
|
onQuit(0);
|
|
119
288
|
return;
|
|
120
289
|
}
|
|
290
|
+
if (!ensureAuth(command))
|
|
291
|
+
return;
|
|
292
|
+
if (command.name === "trace") {
|
|
293
|
+
setTrace((v) => {
|
|
294
|
+
setToast(`trace ${v ? "off" : "on"}`);
|
|
295
|
+
return !v;
|
|
296
|
+
});
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (command.name === "local") {
|
|
300
|
+
setLocalMode((v) => {
|
|
301
|
+
setToast(`local mode ${v ? "off" : "on"}`);
|
|
302
|
+
return !v;
|
|
303
|
+
});
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const confirmSpec = destructiveSpec(command.name, stateRef.current.context);
|
|
307
|
+
if (confirmSpec && !command.pick) {
|
|
308
|
+
setConfirm(createConfirm(confirmSpec.title, confirmSpec.phrase, confirmSpec.action, confirmSpec.args));
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (command.pick) {
|
|
312
|
+
if (args.trim()) {
|
|
313
|
+
setWorking(true);
|
|
314
|
+
try {
|
|
315
|
+
await applyResult(await applyPick(command.name, args.trim(), stateRef.current.context));
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
setToast(error instanceof Error ? error.message : String(error));
|
|
319
|
+
}
|
|
320
|
+
finally {
|
|
321
|
+
setWorking(false);
|
|
322
|
+
}
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
await openPick(command, command.pick);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
121
328
|
if (command.fields?.length) {
|
|
122
329
|
let model = createSheet(command.name, command.fields);
|
|
123
330
|
if (args)
|
|
124
331
|
model = setFocusedValue(model, args);
|
|
125
332
|
setSheet(model);
|
|
126
333
|
setSheetCommand(command);
|
|
334
|
+
const focused = visibleFields(model)[model.focused];
|
|
335
|
+
if (focused?.control === "picker" && focused.picker) {
|
|
336
|
+
await openPick(command, focused.picker, model, focused.name);
|
|
337
|
+
}
|
|
127
338
|
return;
|
|
128
339
|
}
|
|
129
340
|
setWorking(true);
|
|
130
341
|
try {
|
|
131
|
-
|
|
132
|
-
const status = await getStatus(context, { check: true });
|
|
133
|
-
setToast(`status · env=${status.environment} · agent=${status.agentId ?? "—"}`);
|
|
134
|
-
}
|
|
135
|
-
else if (command.name === "validate") {
|
|
136
|
-
setTab("bundle");
|
|
137
|
-
const result = await validateLocalBundle(context, createClients(context).runtime);
|
|
138
|
-
if (result.valid) {
|
|
139
|
-
setToast(result.program?.id
|
|
140
|
-
? `Valid — ${result.program.id}@${result.program.version ?? "?"}`
|
|
141
|
-
: "Program is valid");
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
const errors = result.findings.filter((f) => f.level !== "warning");
|
|
145
|
-
const first = errors[0]?.message ?? "validation failed";
|
|
146
|
-
setToast(`Invalid: ${first}${errors.length > 1 ? ` (+${errors.length - 1} more)` : ""}`);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
else if (command.name === "diff") {
|
|
150
|
-
setTab("bundle");
|
|
151
|
-
const remote = args.trim().toLowerCase() === "remote";
|
|
152
|
-
const diff = remote
|
|
153
|
-
? await remoteDiff(context, createClients(context).management)
|
|
154
|
-
: await localDiff(context);
|
|
155
|
-
setToast(`diff vs ${remote ? "remote" : "baseline"} · ${diffSummary(diff)}`);
|
|
156
|
-
}
|
|
157
|
-
else if (command.name === "trace") {
|
|
158
|
-
setTrace((v) => {
|
|
159
|
-
setToast(`trace ${v ? "off" : "on"}`);
|
|
160
|
-
return !v;
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
else if (command.name === "local") {
|
|
164
|
-
setLocalMode((v) => {
|
|
165
|
-
setToast(`local mode ${v ? "off" : "on"}`);
|
|
166
|
-
return !v;
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
else if (command.name === "new") {
|
|
170
|
-
setChat([]);
|
|
171
|
-
setToast("New thread");
|
|
172
|
-
}
|
|
173
|
-
else if (command.name === "logout") {
|
|
174
|
-
setToast("Use apollo logout in the CLI (confirm modal coming soon)");
|
|
175
|
-
}
|
|
176
|
-
else if (command.name === "upgrade") {
|
|
177
|
-
setToast("Run apollo upgrade from the CLI");
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
setToast(`/${command.name} queued`);
|
|
181
|
-
}
|
|
342
|
+
await applyResult(await runSimpleCommand(command.name, stateRef.current.context, args));
|
|
182
343
|
}
|
|
183
344
|
catch (error) {
|
|
184
345
|
setToast(error instanceof Error ? error.message : String(error));
|
|
@@ -203,86 +364,59 @@ function App({ context, initialScope, glyph, onQuit }) {
|
|
|
203
364
|
}
|
|
204
365
|
return;
|
|
205
366
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
void executeSheet(command, model.values);
|
|
212
|
-
}
|
|
213
|
-
/** Run the service call behind a submitted command sheet. */
|
|
214
|
-
async function executeSheet(command, values) {
|
|
215
|
-
const text = (name) => {
|
|
216
|
-
const value = values[name];
|
|
217
|
-
return typeof value === "string" ? value.trim() : "";
|
|
218
|
-
};
|
|
219
|
-
setWorking(true);
|
|
220
|
-
try {
|
|
221
|
-
const clients = createClients(context);
|
|
222
|
-
if (command.name === "pull") {
|
|
223
|
-
setTab("bundle");
|
|
224
|
-
const tag = text("tag");
|
|
225
|
-
const dir = text("dir");
|
|
226
|
-
const result = await pullBundle(context, clients.management, {
|
|
227
|
-
// "latest" is the sheet default; omitting the tag pulls the
|
|
228
|
-
// selected/live version, matching the CLI's bare `apollo pull`.
|
|
229
|
-
...(tag && tag !== "latest" ? { tag } : {}),
|
|
230
|
-
...(dir ? { destination: dir } : {}),
|
|
231
|
-
force: false,
|
|
232
|
-
});
|
|
233
|
-
const removed = result.removed.length ? ` · removed ${result.removed.length}` : "";
|
|
234
|
-
setToast(`Pulled ${Object.keys(result.files).length} file(s)${removed}`);
|
|
235
|
-
}
|
|
236
|
-
else if (command.name === "push") {
|
|
237
|
-
setTab("bundle");
|
|
238
|
-
const message = text("message");
|
|
239
|
-
const dryRun = values.dryRun === true;
|
|
240
|
-
const result = await pushBundle(context, clients.management, clients.runtime, {
|
|
241
|
-
...(message ? { commitMessage: message } : {}),
|
|
242
|
-
dryRun,
|
|
243
|
-
});
|
|
244
|
-
setToast(result.push
|
|
245
|
-
? `Pushed ${result.push.new_version_tag} · ${diffSummary(result.diff)}`
|
|
246
|
-
: `Dry run · ${diffSummary(result.diff)}`);
|
|
247
|
-
}
|
|
248
|
-
else if (command.name === "provision") {
|
|
249
|
-
setTab("mockdb");
|
|
250
|
-
const provisioned = await provisionMockDb(context);
|
|
251
|
-
let note = `Provisioned mock DB for ${provisioned.agentId ?? "agent"}`;
|
|
252
|
-
if (values.wire === true) {
|
|
253
|
-
const wired = await wireMockDbConnection(context);
|
|
254
|
-
note += ` · wired ${wired.connectionId}`;
|
|
255
|
-
}
|
|
256
|
-
setToast(note);
|
|
367
|
+
void (async () => {
|
|
368
|
+
setWorking(true);
|
|
369
|
+
try {
|
|
370
|
+
await applyResult(await runSheetAction(command.name, model.values, stateRef.current.context));
|
|
257
371
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
setToast(`Thread ${text("thread") || "new"} (per-thread history coming soon)`);
|
|
372
|
+
catch (err) {
|
|
373
|
+
setToast(err instanceof Error ? err.message : String(err));
|
|
261
374
|
}
|
|
262
|
-
|
|
263
|
-
|
|
375
|
+
finally {
|
|
376
|
+
setWorking(false);
|
|
264
377
|
}
|
|
265
|
-
}
|
|
266
|
-
catch (error) {
|
|
267
|
-
setToast(error instanceof Error ? error.message : String(error));
|
|
268
|
-
}
|
|
269
|
-
finally {
|
|
270
|
-
setWorking(false);
|
|
271
|
-
}
|
|
378
|
+
})();
|
|
272
379
|
}
|
|
273
|
-
function handleSheetLine(model, command, line) {
|
|
380
|
+
async function handleSheetLine(model, command, line) {
|
|
381
|
+
if (line === "+" || line.toLowerCase() === "advanced") {
|
|
382
|
+
setSheet({ ...model, advancedOpen: !model.advancedOpen });
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
274
385
|
const fields = visibleFields(model);
|
|
275
386
|
const focused = fields[model.focused];
|
|
276
387
|
if (line === "") {
|
|
277
|
-
// Empty entry advances; on the last field it submits.
|
|
278
388
|
if (model.focused >= fields.length - 1) {
|
|
279
389
|
submitSheet(model, command);
|
|
280
390
|
}
|
|
281
391
|
else {
|
|
282
|
-
|
|
392
|
+
const next = moveFocus(model, 1);
|
|
393
|
+
setSheet(next);
|
|
394
|
+
const nextField = visibleFields(next)[next.focused];
|
|
395
|
+
if (nextField?.control === "picker" && nextField.picker) {
|
|
396
|
+
await openPick(command, nextField.picker, next, nextField.name);
|
|
397
|
+
}
|
|
283
398
|
}
|
|
284
399
|
return;
|
|
285
400
|
}
|
|
401
|
+
if (focused?.control === "picker" && focused.picker) {
|
|
402
|
+
await openPick(command, focused.picker, model, focused.name);
|
|
403
|
+
// Also try resolving against a fresh catalog if user typed a filter as the line
|
|
404
|
+
setWorking(true);
|
|
405
|
+
try {
|
|
406
|
+
const items = await loadPickerCatalog(focused.picker, stateRef.current.context);
|
|
407
|
+
const hit = resolvePickerAnswer(filterPickerItems(items, line), line);
|
|
408
|
+
if (hit) {
|
|
409
|
+
await finishPick(command, hit.id, model, focused.name);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
catch {
|
|
414
|
+
/* fall through */
|
|
415
|
+
}
|
|
416
|
+
finally {
|
|
417
|
+
setWorking(false);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
286
420
|
let next = model;
|
|
287
421
|
if (focused?.control === "toggle") {
|
|
288
422
|
const truthy = ["y", "yes", "true", "on", "x"].includes(line.toLowerCase());
|
|
@@ -295,7 +429,12 @@ function App({ context, initialScope, glyph, onQuit }) {
|
|
|
295
429
|
submitSheet(next, command);
|
|
296
430
|
}
|
|
297
431
|
else {
|
|
298
|
-
|
|
432
|
+
const moved = moveFocus(next, 1);
|
|
433
|
+
setSheet(moved);
|
|
434
|
+
const nextField = visibleFields(moved)[moved.focused];
|
|
435
|
+
if (nextField?.control === "picker" && nextField.picker) {
|
|
436
|
+
await openPick(command, nextField.picker, moved, nextField.name);
|
|
437
|
+
}
|
|
299
438
|
}
|
|
300
439
|
}
|
|
301
440
|
async function sendChat(text) {
|
|
@@ -306,20 +445,39 @@ function App({ context, initialScope, glyph, onQuit }) {
|
|
|
306
445
|
}
|
|
307
446
|
setWorking(true);
|
|
308
447
|
try {
|
|
309
|
-
const clients = createClients(context);
|
|
310
|
-
const result = await sendMessage(context, clients.runtime, {
|
|
448
|
+
const clients = createClients(stateRef.current.context);
|
|
449
|
+
const result = await sendMessage(stateRef.current.context, clients.runtime, {
|
|
311
450
|
text,
|
|
312
|
-
...(
|
|
313
|
-
|
|
451
|
+
...(threadId ? { threadId } : {}),
|
|
452
|
+
includeTrace: true,
|
|
453
|
+
...(localMode && stateRef.current.context.agentRoot
|
|
454
|
+
? { localDir: stateRef.current.context.agentRoot }
|
|
455
|
+
: {}),
|
|
314
456
|
variables: vars,
|
|
315
457
|
});
|
|
458
|
+
if (result.thread_id)
|
|
459
|
+
setThreadId(result.thread_id);
|
|
316
460
|
const reply = result.message.text ?? JSON.stringify(result);
|
|
317
|
-
setChat((c) => [
|
|
461
|
+
setChat((c) => [
|
|
462
|
+
...c,
|
|
463
|
+
{
|
|
464
|
+
role: "agent",
|
|
465
|
+
text: reply,
|
|
466
|
+
messageId: result.message.id,
|
|
467
|
+
interactionId: result.message.id,
|
|
468
|
+
...(result.message.trace_info
|
|
469
|
+
? { trace: result.message.trace_info }
|
|
470
|
+
: {}),
|
|
471
|
+
},
|
|
472
|
+
]);
|
|
318
473
|
}
|
|
319
474
|
catch (error) {
|
|
320
475
|
setChat((c) => [
|
|
321
476
|
...c,
|
|
322
|
-
{
|
|
477
|
+
{
|
|
478
|
+
role: "system",
|
|
479
|
+
text: error instanceof Error ? error.message : String(error),
|
|
480
|
+
},
|
|
323
481
|
]);
|
|
324
482
|
}
|
|
325
483
|
finally {
|
|
@@ -331,8 +489,63 @@ function App({ context, initialScope, glyph, onQuit }) {
|
|
|
331
489
|
setInput("");
|
|
332
490
|
setToast(undefined);
|
|
333
491
|
const current = stateRef.current;
|
|
492
|
+
if (current.confirm) {
|
|
493
|
+
const checked = checkConfirm(current.confirm, line);
|
|
494
|
+
if (checked === "ok") {
|
|
495
|
+
const spec = current.confirm;
|
|
496
|
+
setConfirm(undefined);
|
|
497
|
+
void (async () => {
|
|
498
|
+
setWorking(true);
|
|
499
|
+
try {
|
|
500
|
+
await applyResult(await runDestructive(spec.action, current.context, spec.args ?? {}));
|
|
501
|
+
}
|
|
502
|
+
catch (error) {
|
|
503
|
+
setToast(error instanceof Error ? error.message : String(error));
|
|
504
|
+
}
|
|
505
|
+
finally {
|
|
506
|
+
setWorking(false);
|
|
507
|
+
}
|
|
508
|
+
})();
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
setConfirm(checked);
|
|
512
|
+
}
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
if (current.picker) {
|
|
516
|
+
const visible = filterPickerItems(current.picker.items, effectivePickerFilter(line));
|
|
517
|
+
if (visible.length === 0) {
|
|
518
|
+
setToast(line ? `No match for "${line}"` : "No options");
|
|
519
|
+
setInput(line);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
// Empty Enter → highlighted row. Digits → index. Text → unique match only.
|
|
523
|
+
const trimmed = line.trim();
|
|
524
|
+
let hit;
|
|
525
|
+
if (!trimmed) {
|
|
526
|
+
hit = visible[clampPickerCursor(current.picker.cursor, visible.length)];
|
|
527
|
+
}
|
|
528
|
+
else if (/^\d+$/.test(trimmed)) {
|
|
529
|
+
hit = resolvePickerAnswer(visible, trimmed);
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
hit = resolvePickerAnswer(visible, trimmed);
|
|
533
|
+
if (!hit && visible.length > 1) {
|
|
534
|
+
setToast("↑↓ to choose, Enter to select (or narrow the filter)");
|
|
535
|
+
setInput(line);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (!hit) {
|
|
540
|
+
setToast(line ? `No match for "${line}"` : "↑↓ to choose, Enter to select");
|
|
541
|
+
setInput(line);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
void finishPick(current.picker.command, hit.id, current.picker.sheet, current.picker.sheetField);
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
334
547
|
if (current.sheet && current.sheetCommand) {
|
|
335
|
-
handleSheetLine(current.sheet, current.sheetCommand, line);
|
|
548
|
+
void handleSheetLine(current.sheet, current.sheetCommand, line);
|
|
336
549
|
return;
|
|
337
550
|
}
|
|
338
551
|
if (line.startsWith("/")) {
|
|
@@ -348,17 +561,54 @@ function App({ context, initialScope, glyph, onQuit }) {
|
|
|
348
561
|
if (line === "")
|
|
349
562
|
return;
|
|
350
563
|
if (current.tab === "chat") {
|
|
564
|
+
const overlay = /^[td](?:\s+(\d+))?$/i.exec(line);
|
|
565
|
+
if (overlay) {
|
|
566
|
+
const kind = line[0].toLowerCase() === "d" ? "debug" : "trace";
|
|
567
|
+
void (async () => {
|
|
568
|
+
setWorking(true);
|
|
569
|
+
try {
|
|
570
|
+
await applyResult(await chatOverlay(kind, chat, threadId, current.context, overlay[1]));
|
|
571
|
+
}
|
|
572
|
+
catch (error) {
|
|
573
|
+
setToast(error instanceof Error ? error.message : String(error));
|
|
574
|
+
}
|
|
575
|
+
finally {
|
|
576
|
+
setWorking(false);
|
|
577
|
+
}
|
|
578
|
+
})();
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
351
581
|
void sendChat(line);
|
|
352
582
|
return;
|
|
353
583
|
}
|
|
354
584
|
setToast("Type / for commands, or use the Chat tab to message the agent");
|
|
355
585
|
}
|
|
356
|
-
const placeholder =
|
|
357
|
-
? "
|
|
358
|
-
:
|
|
359
|
-
? "
|
|
360
|
-
:
|
|
361
|
-
|
|
586
|
+
const placeholder = confirm
|
|
587
|
+
? `type "${confirm.phrase}" to confirm · Esc cancel`
|
|
588
|
+
: picker
|
|
589
|
+
? "filter · ↑↓ move · Enter select · Esc cancel"
|
|
590
|
+
: tab === "chat"
|
|
591
|
+
? "message · t/d trace/debug last reply · / commands"
|
|
592
|
+
: sheet
|
|
593
|
+
? "value · Enter next · + advanced · Esc cancel"
|
|
594
|
+
: "/ for commands · Tab switches tabs";
|
|
595
|
+
const pickerFilter = picker ? effectivePickerFilter(input) : undefined;
|
|
596
|
+
const pickerVisible = picker
|
|
597
|
+
? filterPickerItems(picker.items, pickerFilter)
|
|
598
|
+
: [];
|
|
599
|
+
const pickerCursor = picker
|
|
600
|
+
? clampPickerCursor(picker.cursor, pickerVisible.length)
|
|
601
|
+
: 0;
|
|
602
|
+
// Keep the highlighted row on screen when the list is long.
|
|
603
|
+
const PICKER_PAGE = pickerPageSize();
|
|
604
|
+
const pickerWindowStart = pickerVisible.length <= PICKER_PAGE
|
|
605
|
+
? 0
|
|
606
|
+
: Math.max(0, Math.min(pickerCursor - Math.floor(PICKER_PAGE / 3), pickerVisible.length - PICKER_PAGE));
|
|
607
|
+
const pickerWindow = pickerVisible.slice(pickerWindowStart, pickerWindowStart + PICKER_PAGE);
|
|
608
|
+
const modalOpen = Boolean(picker || confirm || sheet);
|
|
609
|
+
return (_jsxs("box", { style: { flexDirection: "column", flexGrow: 1, paddingLeft: 1, paddingRight: 1 }, children: [_jsx(TabBar, { active: tab }), _jsx("box", { style: { flexDirection: "column", flexGrow: 1, paddingTop: 1 }, children: picker ? (_jsx(PickerPane, { items: pickerWindow, cursor: pickerCursor, windowStart: pickerWindowStart, total: pickerVisible.length, title: picker.command.name, filter: pickerFilter, dismissHint: picker.command.name === "place-onboard"
|
|
610
|
+
? "Esc continue without place"
|
|
611
|
+
: undefined })) : confirm ? (_jsx(ConfirmPane, { confirm: confirm })) : sheet && sheetCommand ? (_jsx(SheetPane, { sheet: sheet })) : (_jsxs(_Fragment, { children: [tab === "overview" && (_jsx(Overview, { frame: frame, working: working, agentPath: scope.agentPath, paneTitle: paneTitle, paneLines: paneLines })), tab === "chat" && (_jsx(ChatPane, { chat: chat, trace: trace, localMode: localMode, vars: vars, threadId: threadId, paneTitle: paneTitle, paneLines: paneLines })), tab === "bundle" && (_jsx(BundlePane, { paneTitle: paneTitle, paneLines: paneLines })), tab === "mockdb" && (_jsx(MockDbPane, { paneTitle: paneTitle, paneLines: paneLines })), tab === "scope" && _jsx(ScopePane, { scope: scope })] })) }), paletteOpen && !modalOpen && paletteMatches.length > 0 && (_jsx(Palette, { matches: paletteMatches })), toast && (_jsxs("text", { fg: DIM, style: { paddingLeft: 1 }, children: ["\u21B3 ", toast] })), !scope.loggedIn && (_jsx("box", { style: { flexDirection: "row", justifyContent: "flex-end" }, children: _jsx("text", { fg: AMBER, children: "Not logged in \u00B7 Run /login " }) })), _jsx("box", { style: {
|
|
362
612
|
border: true,
|
|
363
613
|
borderStyle: "rounded",
|
|
364
614
|
borderColor: working ? AMBER : TEAL_DIM,
|
|
@@ -366,16 +616,22 @@ function App({ context, initialScope, glyph, onQuit }) {
|
|
|
366
616
|
height: 3,
|
|
367
617
|
paddingLeft: 1,
|
|
368
618
|
paddingRight: 1,
|
|
369
|
-
}, children: _jsx("input", { focused: true, value: input, placeholder: placeholder, placeholderColor: FAINT, textColor: FG, focusedTextColor: FG, cursorColor: TEAL, backgroundColor: "transparent", focusedBackgroundColor: "transparent", onInput: (value) =>
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
619
|
+
}, children: _jsx("input", { focused: true, value: input, placeholder: placeholder, placeholderColor: FAINT, textColor: FG, focusedTextColor: FG, cursorColor: TEAL, backgroundColor: "transparent", focusedBackgroundColor: "transparent", onInput: (value) => {
|
|
620
|
+
setInput(value);
|
|
621
|
+
if (picker) {
|
|
622
|
+
const nextFilter = effectivePickerFilter(value);
|
|
623
|
+
const prevFilter = effectivePickerFilter(input);
|
|
624
|
+
if (nextFilter !== prevFilter) {
|
|
625
|
+
setPicker({ ...picker, cursor: 0 });
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}, onSubmit: (value) => handleSubmit(typeof value === "string" ? value : input) }) }), _jsxs("box", { style: { flexDirection: "row", justifyContent: "space-between" }, children: [_jsx("text", { fg: FAINT, children: " Tab tabs \u00B7 / commands \u00B7 Enter send \u00B7 Ctrl+C quit" }), _jsxs("text", { fg: FAINT, children: [scope.profile, "/", scope.env, " \u00B7 ", scope.workspace, " \u00B7 ", scope.agent, " "] })] })] }));
|
|
373
629
|
}
|
|
374
630
|
function TabBar({ active }) {
|
|
375
631
|
return (_jsx("box", { style: { flexDirection: "row", paddingTop: 1 }, children: TABS.map((t) => t.id === active ? (_jsx("text", { fg: TEAL, children: _jsx("b", { children: ` ${t.label} ` }) }, t.id)) : (_jsx("text", { fg: DIM, children: ` ${t.label} ` }, t.id))) }));
|
|
376
632
|
}
|
|
377
|
-
function Overview({ frame, working, agentPath, }) {
|
|
378
|
-
return (_jsxs("box", { style: { flexDirection: "column", alignItems: "center" }, children: [_jsxs("box", { style: {
|
|
633
|
+
function Overview({ frame, working, agentPath, paneTitle, paneLines, }) {
|
|
634
|
+
return (_jsxs("box", { style: { flexDirection: "column", alignItems: "center", flexGrow: 1 }, children: [_jsxs("box", { style: {
|
|
379
635
|
border: true,
|
|
380
636
|
borderStyle: "rounded",
|
|
381
637
|
borderColor: TEAL_DIM,
|
|
@@ -384,19 +640,25 @@ function Overview({ frame, working, agentPath, }) {
|
|
|
384
640
|
width: 44,
|
|
385
641
|
paddingTop: 1,
|
|
386
642
|
paddingBottom: 1,
|
|
387
|
-
}, children: [frame.map((line, i) => (_jsx("text", { fg: working ? AMBER : TEAL, children: line }, i))), _jsxs("text", { fg: FG, children: [_jsx("b", { children: "apollo" }), " ", _jsxs("span", { fg: DIM, children: ["v", VERSION] })] }), _jsx("text", { fg: DIM, children: "Manage and operate Apollo agents" }), agentPath && _jsx("text", { fg: FAINT, children: agentPath })] }), _jsx("text", { children: " " }), _jsx("text", { fg: DIM, children: "/status /
|
|
643
|
+
}, children: [frame.map((line, i) => (_jsx("text", { fg: working ? AMBER : TEAL, children: line }, i))), _jsxs("text", { fg: FG, children: [_jsx("b", { children: "apollo" }), " ", _jsxs("span", { fg: DIM, children: ["v", VERSION] })] }), _jsx("text", { fg: DIM, children: "Manage and operate Apollo agents" }), agentPath && _jsx("text", { fg: FAINT, children: agentPath })] }), _jsx("text", { children: " " }), _jsx("text", { fg: DIM, children: "/status /login /env /agent /push /help" }), paneTitle === "help" && paneLines.length > 0 && (_jsx("scrollbox", { style: { flexGrow: 1, width: "100%", marginTop: 1 }, children: paneLines.map((line, i) => (_jsx("text", { fg: FAINT, children: line }, i))) }))] }));
|
|
388
644
|
}
|
|
389
|
-
function ChatPane({ chat, trace, localMode, vars, }) {
|
|
390
|
-
|
|
645
|
+
function ChatPane({ chat, trace, localMode, vars, threadId, paneTitle, paneLines, }) {
|
|
646
|
+
const agentCount = chat.filter((t) => t.role === "agent").length;
|
|
647
|
+
return (_jsxs("box", { style: { flexDirection: "column", flexGrow: 1 }, children: [_jsxs("text", { fg: FAINT, children: ["thread=", threadId?.slice(0, 12) ?? "new", " \u00B7 local=", localMode ? "on" : "off", " \u00B7 trace=", trace ? "on" : "off", " \u00B7 replies=", agentCount, vars.length > 0 ? ` · vars: ${vars.join(", ")}` : ""] }), _jsx("text", { fg: FAINT, children: " t / d = trace / debug last agent reply \u00B7 t 2 = 2nd reply" }), _jsx("text", { children: " " }), _jsxs("scrollbox", { style: { flexGrow: 1, flexDirection: "column" }, stickyScroll: true, children: [chat.length === 0 ? (_jsx("text", { fg: FAINT, children: " (empty \u2014 type a message, /thread, or /new)" })) : (chat.map((turn, i) => (_jsx("text", { fg: turn.role === "you" ? FG : turn.role === "agent" ? TEAL : AMBER, children: ` ${turn.role.padEnd(6)} ${turn.text}` }, i)))), paneLines.length > 0 &&
|
|
648
|
+
paneTitle &&
|
|
649
|
+
/^(trace|debug)/i.test(paneTitle) ? (_jsxs(_Fragment, { children: [_jsx("text", { children: " " }), _jsxs("text", { fg: AMBER, children: ["\u2014 ", paneTitle, " \u2014"] }), paneLines.map((line, i) => (_jsx("text", { fg: FAINT, children: line }, `p-${i}`)))] })) : null] })] }));
|
|
391
650
|
}
|
|
392
|
-
function
|
|
393
|
-
return (_jsxs("box", { style: { flexDirection: "column" }, children: [_jsx("text", { fg: FG, children:
|
|
651
|
+
function ResultPane({ title, hints, paneTitle, paneLines, }) {
|
|
652
|
+
return (_jsxs("box", { style: { flexDirection: "column", flexGrow: 1 }, children: [_jsx("text", { fg: FG, children: _jsxs("b", { children: [" ", title] }) }), hints.map((h, i) => (_jsxs("text", { fg: DIM, children: [" ", h] }, i))), _jsx("text", { children: " " }), paneLines.length > 0 ? (_jsxs("scrollbox", { style: { flexGrow: 1 }, children: [paneTitle && (_jsxs("text", { fg: TEAL, children: ["\u2014 ", paneTitle, " \u2014"] })), paneLines.map((line, i) => (_jsx("text", { fg: FAINT, children: line }, i)))] })) : (_jsx("text", { fg: FAINT, children: " Use / commands from any tab" }))] }));
|
|
394
653
|
}
|
|
395
|
-
function
|
|
396
|
-
return (
|
|
654
|
+
function BundlePane({ paneTitle, paneLines, }) {
|
|
655
|
+
return (_jsx(ResultPane, { title: "Bundle", hints: ["/validate /diff /pull /push /versions /version-publish"], paneTitle: paneTitle, paneLines: paneLines }));
|
|
656
|
+
}
|
|
657
|
+
function MockDbPane({ paneTitle, paneLines, }) {
|
|
658
|
+
return (_jsx(ResultPane, { title: "Mock DB", hints: ["/provision /wire /seed /mdb-collection-* /mdb-endpoint-* /mdb-status"], paneTitle: paneTitle, paneLines: paneLines }));
|
|
397
659
|
}
|
|
398
660
|
function ScopePane({ scope }) {
|
|
399
|
-
return (_jsxs("box", { style: { flexDirection: "column" }, children: [_jsx("text", { fg: FG, children: _jsx("b", { children: " Scope" }) }), _jsxs("text", { fg: DIM, children: [" env: ", scope.env] }), _jsxs("text", { fg: DIM, children: [" agent: ", scope.agent] }), _jsxs("text", { fg: DIM, children: [" version: ", scope.version] }), _jsxs("text", { fg: scope.loggedIn ? DIM : AMBER, children: [" ", "auth: ", scope.loggedIn ? "logged in" : "not logged in"] }), _jsx("text", { children: " " }),
|
|
661
|
+
return (_jsxs("box", { style: { flexDirection: "column" }, children: [_jsx("text", { fg: FG, children: _jsx("b", { children: " Scope" }) }), _jsxs("text", { fg: DIM, children: [" profile: ", scope.profile] }), _jsxs("text", { fg: DIM, children: [" env: ", scope.env, " (account \u2014 no cd)"] }), _jsxs("text", { fg: DIM, children: [" workspace: ", scope.workspace] }), _jsxs("text", { fg: DIM, children: [" org: ", scope.org] }), _jsxs("text", { fg: DIM, children: [" project: ", scope.project] }), _jsxs("text", { fg: DIM, children: [" agent: ", scope.agent] }), _jsxs("text", { fg: DIM, children: [" version: ", scope.version] }), _jsxs("text", { fg: scope.loggedIn ? DIM : AMBER, children: [" ", "auth: ", scope.loggedIn ? "logged in" : "not logged in"] }), _jsx("text", { children: " " }), _jsxs("text", { fg: FAINT, children: [" ", "/env /profile /workspace /org /project /agent /agent-import /login"] })] }));
|
|
400
662
|
}
|
|
401
663
|
function SheetPane({ sheet }) {
|
|
402
664
|
const fields = visibleFields(sheet);
|
|
@@ -405,14 +667,50 @@ function SheetPane({ sheet }) {
|
|
|
405
667
|
borderStyle: "rounded",
|
|
406
668
|
borderColor: TEAL_DIM,
|
|
407
669
|
flexDirection: "column",
|
|
670
|
+
flexGrow: 1,
|
|
408
671
|
paddingLeft: 1,
|
|
409
672
|
paddingRight: 1,
|
|
410
673
|
}, children: [fields.map((field, i) => {
|
|
411
674
|
const raw = sheet.values[field.name];
|
|
412
675
|
const display = field.control === "toggle" ? (raw ? "[x]" : "[ ]") : String(raw ?? "");
|
|
413
676
|
const active = i === sheet.focused;
|
|
414
|
-
return (_jsxs("text", { fg: active ? FG : DIM, children: [active ? "› " : " ", field.label.padEnd(18), " ", display, field.required && !display ? _jsx("span", { fg: FAINT, children: " (required)" }) : null] }, field.name));
|
|
415
|
-
}), sheet.error && _jsxs("text", { fg: RED, children: ["! ", sheet.error] }), _jsx("text", { fg: FAINT, children: "type value + Enter \u00B7 Enter skip \u00B7 Tab move \u00B7 Esc cancel" })] }));
|
|
677
|
+
return (_jsxs("text", { fg: active ? FG : DIM, children: [active ? "› " : " ", field.label.padEnd(18), " ", display, field.control === "picker" ? _jsx("span", { fg: FAINT, children: " (picker)" }) : null, field.required && !display ? _jsx("span", { fg: FAINT, children: " (required)" }) : null] }, field.name));
|
|
678
|
+
}), sheet.fields.some((f) => f.advanced) && (_jsxs("text", { fg: FAINT, children: [" ", sheet.advancedOpen ? "▾" : "▸", " advanced (+ to toggle)"] })), sheet.error && _jsxs("text", { fg: RED, children: ["! ", sheet.error] }), _jsx("text", { fg: FAINT, children: "type value + Enter \u00B7 Enter skip \u00B7 Tab move \u00B7 Esc cancel" })] }));
|
|
679
|
+
}
|
|
680
|
+
function PickerPane({ items, cursor, windowStart, total, title, filter, dismissHint, }) {
|
|
681
|
+
const { nameWidth, idWidth } = pickerColumnWidths();
|
|
682
|
+
return (_jsxs("box", { title: ` pick ${title}${filter ? ` · “${filter}”` : ""} `, style: {
|
|
683
|
+
border: true,
|
|
684
|
+
borderStyle: "rounded",
|
|
685
|
+
borderColor: TEAL_DIM,
|
|
686
|
+
flexDirection: "column",
|
|
687
|
+
flexGrow: 1,
|
|
688
|
+
paddingLeft: 1,
|
|
689
|
+
paddingRight: 1,
|
|
690
|
+
}, children: [items.length === 0 ? (_jsx("text", { fg: AMBER, children: " No matches" })) : (items.map((item, offset) => {
|
|
691
|
+
const index = windowStart + offset;
|
|
692
|
+
const active = index === cursor;
|
|
693
|
+
const marker = active ? "›" : " ";
|
|
694
|
+
const number = String(index + 1).padStart(2);
|
|
695
|
+
const name = truncatePickerText(item.label, nameWidth).padEnd(nameWidth);
|
|
696
|
+
const secondary = item.detail && item.detail !== item.label
|
|
697
|
+
? truncatePickerText(item.detail, idWidth)
|
|
698
|
+
: "";
|
|
699
|
+
return (_jsxs("text", { fg: active ? TEAL : FG, children: [`${marker} ${number} ${name}`, secondary ? (_jsx("span", { fg: FAINT, children: ` ${secondary}` })) : null] }, `${item.id}-${index}`));
|
|
700
|
+
})), _jsxs("text", { fg: FAINT, children: [total > items.length
|
|
701
|
+
? ` ${cursor + 1}/${total} · ↑↓ move · type to filter · Enter`
|
|
702
|
+
: ` ↑↓ move · type to filter · Enter select`, dismissHint ? ` · ${dismissHint}` : ""] })] }));
|
|
703
|
+
}
|
|
704
|
+
function ConfirmPane({ confirm }) {
|
|
705
|
+
return (_jsxs("box", { title: ` confirm ${confirm.title} `, style: {
|
|
706
|
+
border: true,
|
|
707
|
+
borderStyle: "rounded",
|
|
708
|
+
borderColor: AMBER,
|
|
709
|
+
flexDirection: "column",
|
|
710
|
+
flexGrow: 1,
|
|
711
|
+
paddingLeft: 1,
|
|
712
|
+
paddingRight: 1,
|
|
713
|
+
}, children: [_jsxs("text", { fg: AMBER, children: ["Type ", _jsx("b", { children: confirm.phrase }), " to confirm"] }), confirm.error && _jsxs("text", { fg: RED, children: ["! ", confirm.error] })] }));
|
|
416
714
|
}
|
|
417
715
|
function Palette({ matches }) {
|
|
418
716
|
return (_jsx("box", { title: " commands ", style: {
|
|
@@ -422,7 +720,7 @@ function Palette({ matches }) {
|
|
|
422
720
|
flexDirection: "column",
|
|
423
721
|
paddingLeft: 1,
|
|
424
722
|
paddingRight: 1,
|
|
425
|
-
}, children: matches.map((cmd) => (_jsxs("text", { children: [_jsxs("span", { fg: TEAL, children: ["/", cmd.name.padEnd(
|
|
723
|
+
}, children: matches.map((cmd) => (_jsxs("text", { children: [_jsxs("span", { fg: TEAL, children: ["/", cmd.name.padEnd(16)] }), _jsx("span", { fg: DIM, children: cmd.summary })] }, cmd.name))) }));
|
|
426
724
|
}
|
|
427
725
|
export async function runOpenTui() {
|
|
428
726
|
const context = await resolveContext({});
|
|
@@ -442,8 +740,6 @@ export async function runOpenTui() {
|
|
|
442
740
|
});
|
|
443
741
|
root.unmount();
|
|
444
742
|
renderer.destroy();
|
|
445
|
-
// The raw-mode stdin handle keeps the event loop alive after destroy;
|
|
446
|
-
// exit explicitly so /quit actually returns to the shell.
|
|
447
743
|
process.exit(code);
|
|
448
744
|
}
|
|
449
745
|
//# sourceMappingURL=opentui-app.js.map
|