thoth-agents 0.1.6 → 0.1.8
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/README.md +28 -1
- package/dist/chunk-DYGVRAMS.js +182 -0
- package/dist/chunk-OES76C67.js +1898 -0
- package/dist/chunk-OJCEGZSA.js +3735 -0
- package/dist/cli/codex-install.d.ts +15 -0
- package/dist/cli/commands.d.ts +9 -0
- package/dist/cli/index.d.ts +3 -2
- package/dist/cli/index.js +543 -4154
- package/dist/cli/operations/codex.d.ts +22 -0
- package/dist/cli/operations/index.d.ts +8 -0
- package/dist/cli/operations/opencode.d.ts +14 -0
- package/dist/cli/operations/types.d.ts +126 -0
- package/dist/cli/parser.d.ts +6 -0
- package/dist/cli/runtime.d.ts +21 -0
- package/dist/cli/skills.d.ts +17 -0
- package/dist/cli/tui/App.d.ts +7 -0
- package/dist/cli/tui/components/Header.d.ts +6 -0
- package/dist/cli/tui/components/Menu.d.ts +12 -0
- package/dist/cli/tui/components/ModelChoiceScreen.d.ts +9 -0
- package/dist/cli/tui/components/ModelScreen.d.ts +14 -0
- package/dist/cli/tui/components/PathLine.d.ts +8 -0
- package/dist/cli/tui/components/PlanPreview.d.ts +8 -0
- package/dist/cli/tui/components/StatusView.d.ts +7 -0
- package/dist/cli/tui/index.d.ts +3 -0
- package/dist/cli/tui/index.js +1285 -0
- package/dist/cli/tui/model-catalog.d.ts +20 -0
- package/dist/cli/tui/operations.d.ts +18 -0
- package/dist/cli/tui/theme.d.ts +9 -0
- package/dist/cli/types.d.ts +19 -0
- package/dist/index.js +157 -1694
- package/package.json +5 -1
|
@@ -0,0 +1,1285 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CODEX_ROLE_NAMES,
|
|
3
|
+
applyCodexPlan,
|
|
4
|
+
applyOpenCodePlan,
|
|
5
|
+
buildCodexInstallPlan,
|
|
6
|
+
buildCodexModelPlan,
|
|
7
|
+
buildCodexSetupPlan,
|
|
8
|
+
buildCodexSyncPlan,
|
|
9
|
+
buildCodexUpdatePlan,
|
|
10
|
+
buildOpenCodeInstallPlan,
|
|
11
|
+
buildOpenCodeModelPlan,
|
|
12
|
+
buildOpenCodeSyncPlan,
|
|
13
|
+
buildOpenCodeUpdatePlan,
|
|
14
|
+
getCodexStatus,
|
|
15
|
+
getOpenCodeStatus,
|
|
16
|
+
listOperationHarnesses,
|
|
17
|
+
parseRoleTomlModel
|
|
18
|
+
} from "../../chunk-OJCEGZSA.js";
|
|
19
|
+
import {
|
|
20
|
+
ALL_AGENT_NAMES,
|
|
21
|
+
DEFAULT_MODELS,
|
|
22
|
+
getExistingLiteConfigPath,
|
|
23
|
+
parseConfig
|
|
24
|
+
} from "../../chunk-OES76C67.js";
|
|
25
|
+
|
|
26
|
+
// src/cli/tui/index.tsx
|
|
27
|
+
import { render } from "ink";
|
|
28
|
+
|
|
29
|
+
// src/cli/tui/App.tsx
|
|
30
|
+
import { Box as Box8, Text as Text8, useApp, useInput } from "ink";
|
|
31
|
+
import { useMemo, useState } from "react";
|
|
32
|
+
|
|
33
|
+
// src/cli/tui/components/Header.tsx
|
|
34
|
+
import { Box, Text } from "ink";
|
|
35
|
+
|
|
36
|
+
// src/cli/tui/theme.ts
|
|
37
|
+
var theme = {
|
|
38
|
+
accent: "cyan",
|
|
39
|
+
danger: "red",
|
|
40
|
+
dim: "gray",
|
|
41
|
+
ok: "green",
|
|
42
|
+
title: "white",
|
|
43
|
+
warning: "yellow"
|
|
44
|
+
};
|
|
45
|
+
function stateColor(state) {
|
|
46
|
+
if (state === "installed") return "green";
|
|
47
|
+
if (state === "missing" || state === "outdated") return "yellow";
|
|
48
|
+
if (state === "drift" || state === "unknown") return "red";
|
|
49
|
+
return "gray";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/cli/tui/components/Header.tsx
|
|
53
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
54
|
+
function Header({ title, subtitle }) {
|
|
55
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
56
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: theme.title, children: "thoth-agents" }),
|
|
57
|
+
/* @__PURE__ */ jsx(Text, { color: theme.accent, children: title }),
|
|
58
|
+
subtitle ? /* @__PURE__ */ jsx(Text, { color: theme.dim, children: subtitle }) : null
|
|
59
|
+
] });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/cli/tui/components/Menu.tsx
|
|
63
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
64
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
65
|
+
function Menu({ items, selected }) {
|
|
66
|
+
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: items.map((item, index) => /* @__PURE__ */ jsxs2(
|
|
67
|
+
Text2,
|
|
68
|
+
{
|
|
69
|
+
color: item.disabled ? theme.dim : index === selected ? theme.accent : void 0,
|
|
70
|
+
children: [
|
|
71
|
+
index === selected ? ">" : " ",
|
|
72
|
+
" ",
|
|
73
|
+
item.label,
|
|
74
|
+
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
75
|
+
" - ",
|
|
76
|
+
item.detail
|
|
77
|
+
] })
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
item.id
|
|
81
|
+
)) });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/cli/tui/components/ModelChoiceScreen.tsx
|
|
85
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
86
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
87
|
+
var VISIBLE_MODEL_OPTIONS = 8;
|
|
88
|
+
function modelWindowStart(optionsLength, selected) {
|
|
89
|
+
const maxStart = Math.max(0, optionsLength - VISIBLE_MODEL_OPTIONS);
|
|
90
|
+
if (selected >= optionsLength) return maxStart;
|
|
91
|
+
return Math.min(Math.max(0, selected - VISIBLE_MODEL_OPTIONS + 1), maxStart);
|
|
92
|
+
}
|
|
93
|
+
function ModelChoiceScreen({
|
|
94
|
+
currentModel,
|
|
95
|
+
draftModel,
|
|
96
|
+
options,
|
|
97
|
+
selected
|
|
98
|
+
}) {
|
|
99
|
+
const windowStart = modelWindowStart(options.length, selected);
|
|
100
|
+
const visibleOptions = options.slice(
|
|
101
|
+
windowStart,
|
|
102
|
+
windowStart + VISIBLE_MODEL_OPTIONS
|
|
103
|
+
);
|
|
104
|
+
const windowEnd = windowStart + visibleOptions.length;
|
|
105
|
+
const hasHiddenBefore = windowStart > 0;
|
|
106
|
+
const hasHiddenAfter = windowEnd < options.length;
|
|
107
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
108
|
+
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
109
|
+
"Current: ",
|
|
110
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.accent, children: currentModel })
|
|
111
|
+
] }),
|
|
112
|
+
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
113
|
+
"New: ",
|
|
114
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.warning, children: draftModel })
|
|
115
|
+
] }),
|
|
116
|
+
hasHiddenBefore ? /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
|
|
117
|
+
" ... ",
|
|
118
|
+
windowStart,
|
|
119
|
+
" earlier option(s)"
|
|
120
|
+
] }) : null,
|
|
121
|
+
visibleOptions.map((option, offset) => {
|
|
122
|
+
const index = windowStart + offset;
|
|
123
|
+
return /* @__PURE__ */ jsxs3(
|
|
124
|
+
Text3,
|
|
125
|
+
{
|
|
126
|
+
color: index === selected ? theme.accent : void 0,
|
|
127
|
+
children: [
|
|
128
|
+
index === selected ? ">" : " ",
|
|
129
|
+
" ",
|
|
130
|
+
option.id,
|
|
131
|
+
/* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
|
|
132
|
+
" - ",
|
|
133
|
+
option.provider
|
|
134
|
+
] })
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
option.id
|
|
138
|
+
);
|
|
139
|
+
}),
|
|
140
|
+
hasHiddenAfter ? /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
|
|
141
|
+
" ",
|
|
142
|
+
"... ",
|
|
143
|
+
options.length - windowEnd,
|
|
144
|
+
" later option(s)"
|
|
145
|
+
] }) : null,
|
|
146
|
+
/* @__PURE__ */ jsxs3(Text3, { color: options.length === selected ? theme.accent : void 0, children: [
|
|
147
|
+
options.length === selected ? ">" : " ",
|
|
148
|
+
" Manual entry"
|
|
149
|
+
] }),
|
|
150
|
+
options.length > VISIBLE_MODEL_OPTIONS ? /* @__PURE__ */ jsxs3(Text3, { color: theme.dim, children: [
|
|
151
|
+
"Showing ",
|
|
152
|
+
windowStart + 1,
|
|
153
|
+
"-",
|
|
154
|
+
windowEnd,
|
|
155
|
+
" of ",
|
|
156
|
+
options.length,
|
|
157
|
+
". Use j/k to move through all options; Manual entry follows the catalog."
|
|
158
|
+
] }) : null
|
|
159
|
+
] });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/cli/tui/components/ModelScreen.tsx
|
|
163
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
164
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
165
|
+
var CODEX_MODEL_CATALOG_NOTE = [
|
|
166
|
+
"This list may not include every available model.",
|
|
167
|
+
"Official Codex model list: https://developers.openai.com/codex/models"
|
|
168
|
+
];
|
|
169
|
+
function ModelScreen({
|
|
170
|
+
harness,
|
|
171
|
+
roles,
|
|
172
|
+
selected,
|
|
173
|
+
actions
|
|
174
|
+
}) {
|
|
175
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
176
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: harness === "codex" ? "Codex writes generated subagent model lines only." : "OpenCode writes role model overrides in thoth-agents config." }),
|
|
177
|
+
harness === "codex" ? CODEX_MODEL_CATALOG_NOTE.map((note) => /* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: note }, note)) : null,
|
|
178
|
+
roles.map((role, index) => /* @__PURE__ */ jsxs4(
|
|
179
|
+
Text4,
|
|
180
|
+
{
|
|
181
|
+
color: index === selected ? theme.accent : void 0,
|
|
182
|
+
children: [
|
|
183
|
+
index === selected ? ">" : " ",
|
|
184
|
+
" ",
|
|
185
|
+
role.dirty ? "*" : " ",
|
|
186
|
+
role.role,
|
|
187
|
+
": ",
|
|
188
|
+
role.model,
|
|
189
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: role.dirty ? ` (was ${role.currentModel})` : "" })
|
|
190
|
+
]
|
|
191
|
+
},
|
|
192
|
+
role.role
|
|
193
|
+
)),
|
|
194
|
+
actions.map((action, offset) => {
|
|
195
|
+
const index = roles.length + offset;
|
|
196
|
+
return /* @__PURE__ */ jsxs4(
|
|
197
|
+
Text4,
|
|
198
|
+
{
|
|
199
|
+
color: index === selected ? theme.accent : void 0,
|
|
200
|
+
children: [
|
|
201
|
+
index === selected ? ">" : " ",
|
|
202
|
+
" ",
|
|
203
|
+
action
|
|
204
|
+
]
|
|
205
|
+
},
|
|
206
|
+
action
|
|
207
|
+
);
|
|
208
|
+
})
|
|
209
|
+
] });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/cli/tui/components/PlanPreview.tsx
|
|
213
|
+
import { Box as Box6, Text as Text6 } from "ink";
|
|
214
|
+
|
|
215
|
+
// src/cli/tui/components/PathLine.tsx
|
|
216
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
217
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
218
|
+
function wrapTerminalText(text, width = 74) {
|
|
219
|
+
if (text.length <= width) return [text];
|
|
220
|
+
const chunks = [];
|
|
221
|
+
let cursor = 0;
|
|
222
|
+
while (cursor < text.length) {
|
|
223
|
+
chunks.push(text.slice(cursor, cursor + width));
|
|
224
|
+
cursor += width;
|
|
225
|
+
}
|
|
226
|
+
return chunks;
|
|
227
|
+
}
|
|
228
|
+
function PathLine({ label, value, width }) {
|
|
229
|
+
const lines = wrapTerminalText(value, width);
|
|
230
|
+
return /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", children: lines.map((line, index) => /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: index === 0 ? `${label ? `${label}: ` : ""}${line}` : ` ${line}` }, line)) });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/cli/tui/components/PlanPreview.tsx
|
|
234
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
235
|
+
function PlanPreview({
|
|
236
|
+
plan,
|
|
237
|
+
selectedAction,
|
|
238
|
+
result
|
|
239
|
+
}) {
|
|
240
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
241
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, children: plan.title }),
|
|
242
|
+
/* @__PURE__ */ jsx6(Text6, { children: plan.summary }),
|
|
243
|
+
/* @__PURE__ */ jsxs5(Text6, { children: [
|
|
244
|
+
"Target harness: ",
|
|
245
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.accent, children: plan.harness })
|
|
246
|
+
] }),
|
|
247
|
+
/* @__PURE__ */ jsxs5(Text6, { children: [
|
|
248
|
+
"Action: ",
|
|
249
|
+
plan.action
|
|
250
|
+
] }),
|
|
251
|
+
/* @__PURE__ */ jsxs5(Text6, { children: [
|
|
252
|
+
"Can apply: ",
|
|
253
|
+
plan.canApply ? "yes" : "no"
|
|
254
|
+
] }),
|
|
255
|
+
plan.surfaces.length > 0 ? /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "Managed surfaces" }) : null,
|
|
256
|
+
plan.surfaces.map((surface) => /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
257
|
+
/* @__PURE__ */ jsxs5(Text6, { children: [
|
|
258
|
+
"- ",
|
|
259
|
+
surface.label
|
|
260
|
+
] }),
|
|
261
|
+
surface.path ? /* @__PURE__ */ jsx6(PathLine, { value: surface.path }) : null
|
|
262
|
+
] }, surface.id)),
|
|
263
|
+
/* @__PURE__ */ jsxs5(Text6, { color: theme.dim, children: [
|
|
264
|
+
"Backup: ",
|
|
265
|
+
plan.backup.required ? "required" : "not required",
|
|
266
|
+
" (",
|
|
267
|
+
plan.backup.strategy,
|
|
268
|
+
")"
|
|
269
|
+
] }),
|
|
270
|
+
plan.backup.destinations?.map((path) => /* @__PURE__ */ jsx6(PathLine, { label: path.label, value: path.path }, path.path)),
|
|
271
|
+
plan.items.length > 0 ? /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "Preview" }) : null,
|
|
272
|
+
plan.items.slice(0, 5).map((item) => /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
273
|
+
/* @__PURE__ */ jsxs5(Text6, { children: [
|
|
274
|
+
"- ",
|
|
275
|
+
item.title
|
|
276
|
+
] }),
|
|
277
|
+
item.target.path ? /* @__PURE__ */ jsx6(PathLine, { value: item.target.path }) : null,
|
|
278
|
+
item.preview ? /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: item.preview.length > 120 ? `${item.preview.slice(0, 120)}...` : item.preview }) : null
|
|
279
|
+
] }, item.title)),
|
|
280
|
+
plan.items.length > 5 ? /* @__PURE__ */ jsxs5(Text6, { color: theme.dim, children: [
|
|
281
|
+
"...",
|
|
282
|
+
plan.items.length - 5,
|
|
283
|
+
" more items"
|
|
284
|
+
] }) : null,
|
|
285
|
+
plan.warnings.map((warning) => /* @__PURE__ */ jsxs5(Text6, { color: theme.warning, children: [
|
|
286
|
+
"- [",
|
|
287
|
+
warning.severity,
|
|
288
|
+
"] ",
|
|
289
|
+
warning.message
|
|
290
|
+
] }, warning.code ?? warning.message)),
|
|
291
|
+
plan.disclaimers.map((disclaimer) => /* @__PURE__ */ jsxs5(Text6, { color: theme.dim, children: [
|
|
292
|
+
"- ",
|
|
293
|
+
disclaimer.message
|
|
294
|
+
] }, disclaimer.code ?? disclaimer.message)),
|
|
295
|
+
/* @__PURE__ */ jsxs5(Box6, { marginTop: 1, children: [
|
|
296
|
+
/* @__PURE__ */ jsx6(Text6, { color: selectedAction === "apply" ? theme.accent : void 0, children: "[Apply]" }),
|
|
297
|
+
/* @__PURE__ */ jsx6(Text6, { children: " " }),
|
|
298
|
+
/* @__PURE__ */ jsx6(Text6, { color: selectedAction === "cancel" ? theme.accent : void 0, children: "[Cancel]" })
|
|
299
|
+
] }),
|
|
300
|
+
/* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "Enter selects. a applies. c cancels." }),
|
|
301
|
+
result ? /* @__PURE__ */ jsx6(Text6, { color: result.applied ? theme.ok : theme.warning, children: result.summary }) : null
|
|
302
|
+
] });
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/cli/tui/components/StatusView.tsx
|
|
306
|
+
import { Box as Box7, Text as Text7 } from "ink";
|
|
307
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
308
|
+
function countByState(report) {
|
|
309
|
+
return report.targets.reduce(
|
|
310
|
+
(counts, target) => {
|
|
311
|
+
const state = target.state ?? "unknown";
|
|
312
|
+
counts[state] = (counts[state] ?? 0) + 1;
|
|
313
|
+
return counts;
|
|
314
|
+
},
|
|
315
|
+
{}
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
var categoryOrder = [
|
|
319
|
+
"Agents",
|
|
320
|
+
"Skills",
|
|
321
|
+
"Plugin/MCP",
|
|
322
|
+
"Marketplace",
|
|
323
|
+
"Config",
|
|
324
|
+
"Root instructions",
|
|
325
|
+
"Model state",
|
|
326
|
+
"Other"
|
|
327
|
+
];
|
|
328
|
+
function titleCase(value, separator = " ") {
|
|
329
|
+
return value.split(/[-_\s]+/).filter(Boolean).map(
|
|
330
|
+
(part) => part.toLowerCase() === "sdd" ? "SDD" : part.toLowerCase() === "cli" ? "CLI" : part.toLowerCase() === "mcp" ? "MCP" : part.toLowerCase() === "opencode" ? "OpenCode" : `${part.charAt(0).toUpperCase()}${part.slice(1).toLowerCase()}`
|
|
331
|
+
).join(separator);
|
|
332
|
+
}
|
|
333
|
+
function compactPath(path) {
|
|
334
|
+
return path.replaceAll("\\", "/");
|
|
335
|
+
}
|
|
336
|
+
function categorizeTarget(target) {
|
|
337
|
+
const path = target.path ? compactPath(target.path) : "";
|
|
338
|
+
const label = target.label ?? target.kind;
|
|
339
|
+
const agent = path.match(/thoth-agents-([a-z0-9_-]+)\.toml$/i);
|
|
340
|
+
const skill = path.match(/skills\/([^/]+)\/SKILL\.md$/i);
|
|
341
|
+
if (agent?.[1]) {
|
|
342
|
+
return {
|
|
343
|
+
category: "Agents",
|
|
344
|
+
label: titleCase(agent[1]),
|
|
345
|
+
state: target.state
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
if (skill?.[1] || target.kind === "skill") {
|
|
349
|
+
return {
|
|
350
|
+
category: "Skills",
|
|
351
|
+
label: titleCase(skill?.[1] ?? label, "-"),
|
|
352
|
+
state: target.state
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
if (/marketplace\.json$/i.test(path)) {
|
|
356
|
+
return {
|
|
357
|
+
category: "Marketplace",
|
|
358
|
+
label: "Marketplace",
|
|
359
|
+
state: target.state
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
if (/\.mcp\.json$/i.test(path) || /plugin\.json$/i.test(path) || /plugin-assets/i.test(path) || /manifest/i.test(path)) {
|
|
363
|
+
return {
|
|
364
|
+
category: "Plugin/MCP",
|
|
365
|
+
label: titleCase(label.replace(/^codex\s+/i, "")),
|
|
366
|
+
state: target.state
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
if (/AGENTS\.md$/i.test(path) || /root instructions/i.test(label)) {
|
|
370
|
+
return {
|
|
371
|
+
category: "Root instructions",
|
|
372
|
+
label: "AGENTS.md",
|
|
373
|
+
state: target.state
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
if (/config\.toml$/i.test(path) || target.kind === "config") {
|
|
377
|
+
return { category: "Config", label: titleCase(label), state: target.state };
|
|
378
|
+
}
|
|
379
|
+
if (target.kind === "memory-state" || /model/i.test(label)) {
|
|
380
|
+
return {
|
|
381
|
+
category: "Model state",
|
|
382
|
+
label: titleCase(label),
|
|
383
|
+
state: target.state,
|
|
384
|
+
detail: target.observed
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
category: "Other",
|
|
389
|
+
label: titleCase(label),
|
|
390
|
+
state: target.state,
|
|
391
|
+
detail: target.observed
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function groupedTargets(report) {
|
|
395
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
396
|
+
for (const target of report.targets.map(categorizeTarget)) {
|
|
397
|
+
grouped.set(target.category, [
|
|
398
|
+
...grouped.get(target.category) ?? [],
|
|
399
|
+
target
|
|
400
|
+
]);
|
|
401
|
+
}
|
|
402
|
+
return grouped;
|
|
403
|
+
}
|
|
404
|
+
function uniqueTargets(targets) {
|
|
405
|
+
const seen = /* @__PURE__ */ new Set();
|
|
406
|
+
return targets.filter((target) => {
|
|
407
|
+
const key = `${target.label}:${target.state ?? "unknown"}:${target.detail ?? ""}`;
|
|
408
|
+
if (seen.has(key)) return false;
|
|
409
|
+
seen.add(key);
|
|
410
|
+
return true;
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
function StatusView({ report }) {
|
|
414
|
+
const counts = countByState(report);
|
|
415
|
+
const countText = Object.entries(counts).map(([state, count]) => `${state}: ${count}`).join(" ");
|
|
416
|
+
const grouped = groupedTargets(report);
|
|
417
|
+
const warnings = report.diagnostics.slice(0, 4);
|
|
418
|
+
const hiddenWarnings = report.diagnostics.length - warnings.length;
|
|
419
|
+
const notes = (report.disclaimers ?? []).slice(0, 2);
|
|
420
|
+
const hiddenNotes = (report.disclaimers?.length ?? 0) - notes.length;
|
|
421
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
422
|
+
/* @__PURE__ */ jsxs6(Text7, { children: [
|
|
423
|
+
"State: ",
|
|
424
|
+
/* @__PURE__ */ jsx7(Text7, { color: stateColor(report.state), children: report.state })
|
|
425
|
+
] }),
|
|
426
|
+
/* @__PURE__ */ jsx7(Text7, { children: report.summary }),
|
|
427
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: countText || "No managed targets." }),
|
|
428
|
+
/* @__PURE__ */ jsxs6(Text7, { color: theme.dim, children: [
|
|
429
|
+
"Warnings: ",
|
|
430
|
+
report.diagnostics.length,
|
|
431
|
+
" Notes:",
|
|
432
|
+
" ",
|
|
433
|
+
report.disclaimers?.length ?? 0
|
|
434
|
+
] }),
|
|
435
|
+
categoryOrder.map((category) => {
|
|
436
|
+
const targets = uniqueTargets(grouped.get(category) ?? []);
|
|
437
|
+
if (targets.length === 0) return null;
|
|
438
|
+
const visible = targets.slice(0, 6);
|
|
439
|
+
const hidden = targets.length - visible.length;
|
|
440
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
441
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.accent, children: category }),
|
|
442
|
+
visible.map((target) => /* @__PURE__ */ jsxs6(Text7, { children: [
|
|
443
|
+
"- ",
|
|
444
|
+
target.label,
|
|
445
|
+
target.state ? /* @__PURE__ */ jsxs6(Text7, { color: stateColor(target.state), children: [
|
|
446
|
+
": [",
|
|
447
|
+
target.state,
|
|
448
|
+
"]"
|
|
449
|
+
] }) : null,
|
|
450
|
+
target.detail ? /* @__PURE__ */ jsxs6(Text7, { color: theme.dim, children: [
|
|
451
|
+
" - ",
|
|
452
|
+
target.detail
|
|
453
|
+
] }) : null
|
|
454
|
+
] }, `${category}-${target.label}-${target.state}`)),
|
|
455
|
+
hidden > 0 ? /* @__PURE__ */ jsxs6(Text7, { color: theme.dim, children: [
|
|
456
|
+
" +",
|
|
457
|
+
hidden,
|
|
458
|
+
" more"
|
|
459
|
+
] }) : null
|
|
460
|
+
] }, category);
|
|
461
|
+
}),
|
|
462
|
+
report.diagnostics.length > 0 ? /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
463
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.warning, children: "Warnings" }),
|
|
464
|
+
warnings.map((warning) => /* @__PURE__ */ jsxs6(Text7, { color: theme.warning, children: [
|
|
465
|
+
"- [",
|
|
466
|
+
warning.severity,
|
|
467
|
+
"] ",
|
|
468
|
+
warning.message
|
|
469
|
+
] }, warning.code ?? warning.message)),
|
|
470
|
+
hiddenWarnings > 0 ? /* @__PURE__ */ jsxs6(Text7, { color: theme.dim, children: [
|
|
471
|
+
" +",
|
|
472
|
+
hiddenWarnings,
|
|
473
|
+
" more warnings"
|
|
474
|
+
] }) : null
|
|
475
|
+
] }) : null,
|
|
476
|
+
notes.length > 0 ? /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
477
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.dim, children: "Notes" }),
|
|
478
|
+
notes.map((note) => /* @__PURE__ */ jsxs6(Text7, { color: theme.dim, children: [
|
|
479
|
+
"- ",
|
|
480
|
+
note.message
|
|
481
|
+
] }, note.code ?? note.message)),
|
|
482
|
+
hiddenNotes > 0 ? /* @__PURE__ */ jsxs6(Text7, { color: theme.dim, children: [
|
|
483
|
+
" +",
|
|
484
|
+
hiddenNotes,
|
|
485
|
+
" more notes"
|
|
486
|
+
] }) : null
|
|
487
|
+
] }) : null
|
|
488
|
+
] });
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/cli/tui/model-catalog.ts
|
|
492
|
+
import { execFileSync } from "child_process";
|
|
493
|
+
var MODEL_CATALOG_TIMEOUT_MS = 5e3;
|
|
494
|
+
var MODELS_DEV_MAX_BUFFER = 8 * 1024 * 1024;
|
|
495
|
+
function parseOpenCodeModels(output) {
|
|
496
|
+
const seen = /* @__PURE__ */ new Set();
|
|
497
|
+
const options = [];
|
|
498
|
+
for (const line of output.split(/\r?\n/)) {
|
|
499
|
+
const match = line.trim().match(/^([a-z0-9_-]+)\/([^\s{]+)/i);
|
|
500
|
+
if (!match) continue;
|
|
501
|
+
const id = `${match[1]}/${match[2]}`;
|
|
502
|
+
if (seen.has(id)) continue;
|
|
503
|
+
seen.add(id);
|
|
504
|
+
options.push({ id, label: id, provider: match[1] ?? "unknown" });
|
|
505
|
+
}
|
|
506
|
+
return options;
|
|
507
|
+
}
|
|
508
|
+
function parseModelsDevOpenAi(output) {
|
|
509
|
+
const catalog = JSON.parse(output);
|
|
510
|
+
return Object.entries(catalog.openai?.models ?? {}).filter(([id]) => isCodexOpenAiModelId(id)).map(([id, model]) => ({
|
|
511
|
+
id,
|
|
512
|
+
label: model.name ?? id,
|
|
513
|
+
provider: "openai"
|
|
514
|
+
}));
|
|
515
|
+
}
|
|
516
|
+
function isCodexOpenAiModelId(id) {
|
|
517
|
+
const match = id.match(/^gpt-(\d+)(?:[.-]|$)/);
|
|
518
|
+
return match?.[1] !== void 0 && Number(match[1]) >= 5;
|
|
519
|
+
}
|
|
520
|
+
function getOpenCodeModelsInvocation(platform = process.platform) {
|
|
521
|
+
const options = {
|
|
522
|
+
encoding: "utf8",
|
|
523
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
524
|
+
timeout: MODEL_CATALOG_TIMEOUT_MS
|
|
525
|
+
};
|
|
526
|
+
if (platform === "win32") {
|
|
527
|
+
return {
|
|
528
|
+
command: "opencode models",
|
|
529
|
+
args: [],
|
|
530
|
+
options: { ...options, shell: true }
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
return {
|
|
534
|
+
command: "opencode",
|
|
535
|
+
args: ["models"],
|
|
536
|
+
options
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
function getModelsDevCatalog() {
|
|
540
|
+
try {
|
|
541
|
+
const output = execFileSync(
|
|
542
|
+
process.execPath,
|
|
543
|
+
[
|
|
544
|
+
"-e",
|
|
545
|
+
[
|
|
546
|
+
"const controller = new AbortController();",
|
|
547
|
+
`const timeout = setTimeout(() => controller.abort(), ${MODEL_CATALOG_TIMEOUT_MS});`,
|
|
548
|
+
"if (typeof fetch !== 'function') {",
|
|
549
|
+
" console.error('fetch unavailable');",
|
|
550
|
+
" process.exit(1);",
|
|
551
|
+
"}",
|
|
552
|
+
"fetch('https://models.dev/api.json', { signal: controller.signal })",
|
|
553
|
+
" .then(async (response) => {",
|
|
554
|
+
" if (!response.ok) throw new Error(String(response.status));",
|
|
555
|
+
" const catalog = await response.json();",
|
|
556
|
+
" const models = catalog?.openai?.models ?? {};",
|
|
557
|
+
" return JSON.stringify({ openai: { models } });",
|
|
558
|
+
" })",
|
|
559
|
+
" .then((body) => {",
|
|
560
|
+
" clearTimeout(timeout);",
|
|
561
|
+
" process.stdout.write(body);",
|
|
562
|
+
" })",
|
|
563
|
+
" .catch((error) => { clearTimeout(timeout); console.error(error.message); process.exit(1); });"
|
|
564
|
+
].join("\n")
|
|
565
|
+
],
|
|
566
|
+
{
|
|
567
|
+
encoding: "utf8",
|
|
568
|
+
maxBuffer: MODELS_DEV_MAX_BUFFER,
|
|
569
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
570
|
+
timeout: MODEL_CATALOG_TIMEOUT_MS
|
|
571
|
+
}
|
|
572
|
+
);
|
|
573
|
+
return parseModelsDevOpenAi(output);
|
|
574
|
+
} catch {
|
|
575
|
+
return [];
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
function getModelOptions(harness) {
|
|
579
|
+
if (harness === "codex") return getModelsDevCatalog();
|
|
580
|
+
try {
|
|
581
|
+
const invocation = getOpenCodeModelsInvocation();
|
|
582
|
+
const output = execFileSync(
|
|
583
|
+
invocation.command,
|
|
584
|
+
invocation.args,
|
|
585
|
+
invocation.options
|
|
586
|
+
);
|
|
587
|
+
return parseOpenCodeModels(output);
|
|
588
|
+
} catch {
|
|
589
|
+
return [];
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// src/cli/tui/operations.ts
|
|
594
|
+
var context = { cwd: process.cwd() };
|
|
595
|
+
var codexContext = { cwd: process.cwd() };
|
|
596
|
+
var opencodeModelRoles = ALL_AGENT_NAMES.map(
|
|
597
|
+
(role) => ({
|
|
598
|
+
role,
|
|
599
|
+
model: DEFAULT_MODELS[role] ?? "openai/gpt-5.4"
|
|
600
|
+
})
|
|
601
|
+
);
|
|
602
|
+
var codexModelRoles = CODEX_ROLE_NAMES.map(
|
|
603
|
+
(role) => ({
|
|
604
|
+
role,
|
|
605
|
+
model: "gpt-5.4-mini"
|
|
606
|
+
})
|
|
607
|
+
);
|
|
608
|
+
var codexDefaultModels = new Map(
|
|
609
|
+
codexModelRoles.map((role) => [role.role, role.model])
|
|
610
|
+
);
|
|
611
|
+
function codexInstallConfig(source, dryRun) {
|
|
612
|
+
return {
|
|
613
|
+
dryRun,
|
|
614
|
+
reset: false,
|
|
615
|
+
scope: source.scope ?? "user",
|
|
616
|
+
projectRoot: source.cwd,
|
|
617
|
+
homeDir: source.homeDir,
|
|
618
|
+
codexHome: source.codexHome,
|
|
619
|
+
packageRoot: source.packageRoot,
|
|
620
|
+
pluginId: source.pluginId
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
function getCodexModelRoles(source = codexContext) {
|
|
624
|
+
try {
|
|
625
|
+
const plan = buildCodexSetupPlan(codexInstallConfig(source, true));
|
|
626
|
+
return CODEX_ROLE_NAMES.map((role) => {
|
|
627
|
+
const item = plan.items.find(
|
|
628
|
+
(candidate) => candidate.action === "write-role-toml" && candidate.role === role
|
|
629
|
+
);
|
|
630
|
+
return {
|
|
631
|
+
role,
|
|
632
|
+
model: (item?.content ? parseRoleTomlModel(item.content) : void 0) ?? codexDefaultModels.get(role) ?? "gpt-5.4-mini"
|
|
633
|
+
};
|
|
634
|
+
});
|
|
635
|
+
} catch {
|
|
636
|
+
return codexModelRoles.map((role) => ({ ...role }));
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
function readRoleModel(config, role) {
|
|
640
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
641
|
+
return void 0;
|
|
642
|
+
}
|
|
643
|
+
const record = config;
|
|
644
|
+
const value = record[role];
|
|
645
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
646
|
+
return void 0;
|
|
647
|
+
}
|
|
648
|
+
const model = value.model;
|
|
649
|
+
return typeof model === "string" && model.length > 0 ? model : void 0;
|
|
650
|
+
}
|
|
651
|
+
function getOpenCodeModelRoles() {
|
|
652
|
+
const parsed = parseConfig(getExistingLiteConfigPath());
|
|
653
|
+
const agents = parsed.config?.agents && typeof parsed.config.agents === "object" ? parsed.config.agents : void 0;
|
|
654
|
+
const presets = parsed.config?.presets && typeof parsed.config.presets === "object" ? parsed.config.presets : {};
|
|
655
|
+
const openaiPreset = presets.openai && typeof presets.openai === "object" ? presets.openai : void 0;
|
|
656
|
+
return ALL_AGENT_NAMES.map((role) => ({
|
|
657
|
+
role,
|
|
658
|
+
model: readRoleModel(agents, role) ?? readRoleModel(openaiPreset, role) ?? DEFAULT_MODELS[role] ?? "openai/gpt-5.4"
|
|
659
|
+
}));
|
|
660
|
+
}
|
|
661
|
+
function buildTuiModelPlan(harness, roles) {
|
|
662
|
+
return harness === "opencode" ? buildOpenCodeModelPlan({ harness, dryRun: true, roles }, context) : buildCodexModelPlan({ harness, dryRun: true, roles }, codexContext);
|
|
663
|
+
}
|
|
664
|
+
var defaultTuiOperations = {
|
|
665
|
+
status(harness) {
|
|
666
|
+
return harness === "opencode" ? getOpenCodeStatus(context) : getCodexStatus(codexContext);
|
|
667
|
+
},
|
|
668
|
+
modelRoles(harness) {
|
|
669
|
+
return harness === "opencode" ? getOpenCodeModelRoles() : getCodexModelRoles(codexContext);
|
|
670
|
+
},
|
|
671
|
+
modelOptions(harness) {
|
|
672
|
+
return getModelOptions(harness);
|
|
673
|
+
},
|
|
674
|
+
plan(harness, action) {
|
|
675
|
+
if (harness === "opencode") {
|
|
676
|
+
if (action === "install") return buildOpenCodeInstallPlan(context);
|
|
677
|
+
if (action === "update") return buildOpenCodeUpdatePlan(context);
|
|
678
|
+
if (action === "sync") return buildOpenCodeSyncPlan(context);
|
|
679
|
+
return buildTuiModelPlan(harness, getOpenCodeModelRoles());
|
|
680
|
+
}
|
|
681
|
+
if (action === "install") return buildCodexInstallPlan(codexContext);
|
|
682
|
+
if (action === "update") return buildCodexUpdatePlan(codexContext);
|
|
683
|
+
if (action === "sync") return buildCodexSyncPlan(codexContext);
|
|
684
|
+
return buildTuiModelPlan(harness, getCodexModelRoles(codexContext));
|
|
685
|
+
},
|
|
686
|
+
modelPlan(harness, roles) {
|
|
687
|
+
return buildTuiModelPlan(harness, roles);
|
|
688
|
+
},
|
|
689
|
+
apply(plan) {
|
|
690
|
+
return plan.harness === "opencode" ? applyOpenCodePlan(plan) : applyCodexPlan(plan);
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
// src/cli/tui/App.tsx
|
|
695
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
696
|
+
var rootItems = [
|
|
697
|
+
{
|
|
698
|
+
id: "status",
|
|
699
|
+
action: "status",
|
|
700
|
+
label: "Status",
|
|
701
|
+
detail: "Inspect a harness"
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
id: "manage",
|
|
705
|
+
action: "manage",
|
|
706
|
+
label: "Manage Harnesses",
|
|
707
|
+
detail: "List managed surfaces and actions"
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
id: "sync",
|
|
711
|
+
action: "sync",
|
|
712
|
+
label: "Sync / Update",
|
|
713
|
+
detail: "Preview managed setup changes"
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
id: "exit",
|
|
717
|
+
action: "exit",
|
|
718
|
+
label: "Exit",
|
|
719
|
+
detail: "Close the interactive setup"
|
|
720
|
+
}
|
|
721
|
+
];
|
|
722
|
+
var actionItems = [
|
|
723
|
+
{
|
|
724
|
+
id: "update",
|
|
725
|
+
action: "update",
|
|
726
|
+
label: "Update",
|
|
727
|
+
detail: "Refresh managed plugin/setup entries"
|
|
728
|
+
},
|
|
729
|
+
{
|
|
730
|
+
id: "sync",
|
|
731
|
+
action: "sync",
|
|
732
|
+
label: "Sync",
|
|
733
|
+
detail: "Reconcile managed configuration"
|
|
734
|
+
},
|
|
735
|
+
{ id: "back", action: "back", label: "Back", detail: "Return to root" }
|
|
736
|
+
];
|
|
737
|
+
var installManageItems = [
|
|
738
|
+
{
|
|
739
|
+
id: "install",
|
|
740
|
+
action: "install",
|
|
741
|
+
label: "Install",
|
|
742
|
+
detail: "Preview managed setup install"
|
|
743
|
+
},
|
|
744
|
+
{ id: "back", action: "back", label: "Back", detail: "Choose harness" }
|
|
745
|
+
];
|
|
746
|
+
var manageItems = [
|
|
747
|
+
{
|
|
748
|
+
id: "status",
|
|
749
|
+
action: "status",
|
|
750
|
+
label: "View status",
|
|
751
|
+
detail: "Open compact categorized health"
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
id: "update",
|
|
755
|
+
action: "update",
|
|
756
|
+
label: "Update preview",
|
|
757
|
+
detail: "Preview managed plugin/setup refresh"
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
id: "sync",
|
|
761
|
+
action: "sync",
|
|
762
|
+
label: "Sync preview",
|
|
763
|
+
detail: "Preview configuration reconciliation"
|
|
764
|
+
},
|
|
765
|
+
{
|
|
766
|
+
id: "models",
|
|
767
|
+
action: "models",
|
|
768
|
+
label: "Configure models",
|
|
769
|
+
detail: "Edit role model assignments"
|
|
770
|
+
},
|
|
771
|
+
{ id: "back", action: "back", label: "Back", detail: "Choose harness" }
|
|
772
|
+
];
|
|
773
|
+
function buildHarnessItems() {
|
|
774
|
+
return [
|
|
775
|
+
...listOperationHarnesses().map((harness) => ({
|
|
776
|
+
id: harness.id,
|
|
777
|
+
harness: harness.id,
|
|
778
|
+
label: harness.displayName,
|
|
779
|
+
detail: harness.description,
|
|
780
|
+
disabled: !harness.available
|
|
781
|
+
})),
|
|
782
|
+
{ id: "back", action: "back", label: "Back", detail: "Return" }
|
|
783
|
+
];
|
|
784
|
+
}
|
|
785
|
+
function moveSelection(current, direction, items) {
|
|
786
|
+
let next = current;
|
|
787
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
788
|
+
next = (next + direction + items.length) % items.length;
|
|
789
|
+
if (!items[next]?.disabled) return next;
|
|
790
|
+
}
|
|
791
|
+
return current;
|
|
792
|
+
}
|
|
793
|
+
function normalizeSelection(current, items) {
|
|
794
|
+
if (items.length === 0) return 0;
|
|
795
|
+
return Math.min(current, items.length - 1);
|
|
796
|
+
}
|
|
797
|
+
function changedRoles(rows) {
|
|
798
|
+
return rows.filter((role) => role.dirty).map((role) => ({ role: role.role, model: role.model }));
|
|
799
|
+
}
|
|
800
|
+
function App({
|
|
801
|
+
operations = defaultTuiOperations,
|
|
802
|
+
exitOnQuit = true
|
|
803
|
+
}) {
|
|
804
|
+
const { exit } = useApp();
|
|
805
|
+
const harnessItems = useMemo(buildHarnessItems, []);
|
|
806
|
+
const [view, setView] = useState("root");
|
|
807
|
+
const [rootSelected, setRootSelected] = useState(0);
|
|
808
|
+
const [harnessSelected, setHarnessSelected] = useState(0);
|
|
809
|
+
const [actionSelected, setActionSelected] = useState(0);
|
|
810
|
+
const [manageSelected, setManageSelected] = useState(0);
|
|
811
|
+
const [harnessPurpose, setHarnessPurpose] = useState("status");
|
|
812
|
+
const [activeHarness, setActiveHarness] = useState("opencode");
|
|
813
|
+
const [activeAction, setActiveAction] = useState("update");
|
|
814
|
+
const [reportVersion, setReportVersion] = useState(0);
|
|
815
|
+
const [plan, setPlan] = useState();
|
|
816
|
+
const [result, setResult] = useState();
|
|
817
|
+
const [previewAction, setPreviewAction] = useState(
|
|
818
|
+
"cancel"
|
|
819
|
+
);
|
|
820
|
+
const [previewBackView, setPreviewBackView] = useState("harness");
|
|
821
|
+
const [modelHarness, setModelHarness] = useState("codex");
|
|
822
|
+
const [modelRoles, setModelRoles] = useState([]);
|
|
823
|
+
const [modelOptions, setModelOptions] = useState([]);
|
|
824
|
+
const [choiceSelected, setChoiceSelected] = useState(0);
|
|
825
|
+
const [editedModels, setEditedModels] = useState({});
|
|
826
|
+
const [modelSelected, setModelSelected] = useState(0);
|
|
827
|
+
const [editingRole, setEditingRole] = useState();
|
|
828
|
+
const [editDraft, setEditDraft] = useState("");
|
|
829
|
+
const [modelResult, setModelResult] = useState();
|
|
830
|
+
const report = view === "status" || view === "manageHarness" ? operations.status(activeHarness) : void 0;
|
|
831
|
+
const currentManageItems = report?.state === "missing" ? installManageItems : manageItems;
|
|
832
|
+
const modelRows = modelRoles.map((role) => {
|
|
833
|
+
const model = editedModels[role.role] ?? role.model;
|
|
834
|
+
return {
|
|
835
|
+
...role,
|
|
836
|
+
model,
|
|
837
|
+
currentModel: role.model,
|
|
838
|
+
dirty: model !== role.model
|
|
839
|
+
};
|
|
840
|
+
});
|
|
841
|
+
const dirtyRoles = changedRoles(modelRows);
|
|
842
|
+
const modelActions = dirtyRoles.length > 0 ? ["Preview changes", "Apply changes", "Back"] : ["Back"];
|
|
843
|
+
const modelMenuItems = [
|
|
844
|
+
...modelRows.map((role) => ({
|
|
845
|
+
id: role.role,
|
|
846
|
+
label: `${role.dirty ? "* " : ""}${role.role}`,
|
|
847
|
+
detail: role.model
|
|
848
|
+
})),
|
|
849
|
+
...modelActions.map((action) => ({
|
|
850
|
+
id: action,
|
|
851
|
+
label: action,
|
|
852
|
+
detail: action === "Back" ? "Return to harness selection" : `${dirtyRoles.length} changed role(s)`
|
|
853
|
+
}))
|
|
854
|
+
];
|
|
855
|
+
function goBack() {
|
|
856
|
+
setResult(void 0);
|
|
857
|
+
if (view === "status") {
|
|
858
|
+
setView("harness");
|
|
859
|
+
} else if (view === "harness") {
|
|
860
|
+
setView(harnessPurpose === "action" ? "action" : "root");
|
|
861
|
+
} else if (view === "manageHarness") {
|
|
862
|
+
setView("harness");
|
|
863
|
+
} else if (view === "action") {
|
|
864
|
+
setView("root");
|
|
865
|
+
} else if (view === "preview") {
|
|
866
|
+
setView(previewBackView);
|
|
867
|
+
} else if (view === "modelRoles") {
|
|
868
|
+
setView("manageHarness");
|
|
869
|
+
} else if (view === "modelChoice") {
|
|
870
|
+
setView("modelRoles");
|
|
871
|
+
} else if (view === "modelEdit") {
|
|
872
|
+
setView("modelChoice");
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
function openHarnessPicker(purpose) {
|
|
876
|
+
setHarnessPurpose(purpose);
|
|
877
|
+
setHarnessSelected(0);
|
|
878
|
+
setView("harness");
|
|
879
|
+
}
|
|
880
|
+
function openPlan(harness, action, backView = "harness") {
|
|
881
|
+
setActiveHarness(harness);
|
|
882
|
+
setPlan(operations.plan(harness, action));
|
|
883
|
+
setResult(void 0);
|
|
884
|
+
setPreviewAction("cancel");
|
|
885
|
+
setPreviewBackView(backView);
|
|
886
|
+
setView("preview");
|
|
887
|
+
}
|
|
888
|
+
function openModelRoles(harness) {
|
|
889
|
+
setModelHarness(harness);
|
|
890
|
+
setModelRoles(operations.modelRoles(harness));
|
|
891
|
+
setModelOptions(operations.modelOptions(harness));
|
|
892
|
+
setEditedModels({});
|
|
893
|
+
setModelResult(void 0);
|
|
894
|
+
setModelSelected(0);
|
|
895
|
+
setView("modelRoles");
|
|
896
|
+
}
|
|
897
|
+
function previewModelChanges(applyImmediately) {
|
|
898
|
+
const nextPlan = operations.modelPlan(modelHarness, dirtyRoles);
|
|
899
|
+
setPlan(nextPlan);
|
|
900
|
+
setPreviewAction(applyImmediately ? "apply" : "cancel");
|
|
901
|
+
setPreviewBackView("modelRoles");
|
|
902
|
+
if (applyImmediately) {
|
|
903
|
+
const applyResult = operations.apply(nextPlan);
|
|
904
|
+
setModelResult(applyResult);
|
|
905
|
+
if (applyResult.applied) {
|
|
906
|
+
setModelRoles(
|
|
907
|
+
(roles) => roles.map((role) => ({
|
|
908
|
+
...role,
|
|
909
|
+
model: editedModels[role.role] ?? role.model
|
|
910
|
+
}))
|
|
911
|
+
);
|
|
912
|
+
setEditedModels({});
|
|
913
|
+
}
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
setResult(void 0);
|
|
917
|
+
setView("preview");
|
|
918
|
+
}
|
|
919
|
+
useInput((input, key) => {
|
|
920
|
+
if (view === "modelEdit") {
|
|
921
|
+
if (key.escape) {
|
|
922
|
+
goBack();
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
if (key.return && editingRole) {
|
|
926
|
+
setEditedModels((models) => ({
|
|
927
|
+
...models,
|
|
928
|
+
[editingRole.role]: editDraft
|
|
929
|
+
}));
|
|
930
|
+
setView("modelRoles");
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
if (key.tab) {
|
|
934
|
+
const firstOption = modelOptions[0]?.id;
|
|
935
|
+
if (firstOption) setEditDraft(firstOption);
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
if (key.backspace || key.delete) {
|
|
939
|
+
setEditDraft((current) => current.slice(0, -1));
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
if (input && !key.ctrl && !key.meta) {
|
|
943
|
+
setEditDraft((current) => `${current}${input}`);
|
|
944
|
+
}
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
if (view === "modelChoice") {
|
|
948
|
+
if (key.escape) {
|
|
949
|
+
goBack();
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
const choiceItems = [
|
|
953
|
+
...modelOptions.map((option) => ({
|
|
954
|
+
id: option.id,
|
|
955
|
+
label: option.id,
|
|
956
|
+
detail: option.provider
|
|
957
|
+
})),
|
|
958
|
+
{ id: "manual", label: "Manual entry", detail: "Type a model ID" }
|
|
959
|
+
];
|
|
960
|
+
if (key.upArrow || input === "k") {
|
|
961
|
+
setChoiceSelected((current) => moveSelection(current, -1, choiceItems));
|
|
962
|
+
}
|
|
963
|
+
if (key.downArrow || input === "j") {
|
|
964
|
+
setChoiceSelected((current) => moveSelection(current, 1, choiceItems));
|
|
965
|
+
}
|
|
966
|
+
if (key.return && editingRole) {
|
|
967
|
+
const option = modelOptions[choiceSelected];
|
|
968
|
+
if (option) {
|
|
969
|
+
setEditedModels((models) => ({
|
|
970
|
+
...models,
|
|
971
|
+
[editingRole.role]: option.id
|
|
972
|
+
}));
|
|
973
|
+
setView("modelRoles");
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
setEditDraft(editingRole.model);
|
|
977
|
+
setView("modelEdit");
|
|
978
|
+
}
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
if (input === "q" || key.escape) {
|
|
982
|
+
if (view === "root" && exitOnQuit) exit();
|
|
983
|
+
if (view !== "root") goBack();
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
if (view === "root") {
|
|
987
|
+
if (key.upArrow || input === "k") {
|
|
988
|
+
setRootSelected((current) => moveSelection(current, -1, rootItems));
|
|
989
|
+
}
|
|
990
|
+
if (key.downArrow || input === "j") {
|
|
991
|
+
setRootSelected((current) => moveSelection(current, 1, rootItems));
|
|
992
|
+
}
|
|
993
|
+
if (key.return) {
|
|
994
|
+
const item = rootItems[rootSelected];
|
|
995
|
+
if (item?.action === "status") openHarnessPicker("status");
|
|
996
|
+
if (item?.action === "manage") openHarnessPicker("list");
|
|
997
|
+
if (item?.action === "sync") setView("action");
|
|
998
|
+
if (item?.action === "exit" && exitOnQuit) exit();
|
|
999
|
+
}
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
if (view === "action") {
|
|
1003
|
+
if (key.upArrow || input === "k") {
|
|
1004
|
+
setActionSelected((current) => moveSelection(current, -1, actionItems));
|
|
1005
|
+
}
|
|
1006
|
+
if (key.downArrow || input === "j") {
|
|
1007
|
+
setActionSelected((current) => moveSelection(current, 1, actionItems));
|
|
1008
|
+
}
|
|
1009
|
+
if (key.return) {
|
|
1010
|
+
const item = actionItems[actionSelected];
|
|
1011
|
+
if (item?.action === "back") {
|
|
1012
|
+
setView("root");
|
|
1013
|
+
} else if (item) {
|
|
1014
|
+
setActiveAction(item.action);
|
|
1015
|
+
openHarnessPicker("action");
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
if (view === "harness") {
|
|
1021
|
+
const selected = normalizeSelection(harnessSelected, harnessItems);
|
|
1022
|
+
if (key.upArrow || input === "k") {
|
|
1023
|
+
setHarnessSelected(
|
|
1024
|
+
(current) => moveSelection(current, -1, harnessItems)
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
if (key.downArrow || input === "j") {
|
|
1028
|
+
setHarnessSelected(
|
|
1029
|
+
(current) => moveSelection(current, 1, harnessItems)
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
if (key.return) {
|
|
1033
|
+
const item = harnessItems[selected];
|
|
1034
|
+
if (item?.action === "back") {
|
|
1035
|
+
goBack();
|
|
1036
|
+
} else if (item?.harness) {
|
|
1037
|
+
setActiveHarness(item.harness);
|
|
1038
|
+
setReportVersion((current) => current + 1);
|
|
1039
|
+
if (harnessPurpose === "status") setView("status");
|
|
1040
|
+
if (harnessPurpose === "list") {
|
|
1041
|
+
setManageSelected(0);
|
|
1042
|
+
setView("manageHarness");
|
|
1043
|
+
}
|
|
1044
|
+
if (harnessPurpose === "action") {
|
|
1045
|
+
openPlan(item.harness, activeAction);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
if (view === "manageHarness") {
|
|
1052
|
+
if (key.upArrow || input === "k") {
|
|
1053
|
+
setManageSelected(
|
|
1054
|
+
(current) => moveSelection(current, -1, currentManageItems)
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
if (key.downArrow || input === "j") {
|
|
1058
|
+
setManageSelected(
|
|
1059
|
+
(current) => moveSelection(current, 1, currentManageItems)
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
if (key.return) {
|
|
1063
|
+
const item = currentManageItems[manageSelected];
|
|
1064
|
+
if (item?.action === "back") setView("harness");
|
|
1065
|
+
if (item?.action === "install") {
|
|
1066
|
+
openPlan(activeHarness, "install", "manageHarness");
|
|
1067
|
+
}
|
|
1068
|
+
if (item?.action === "status") {
|
|
1069
|
+
setView("status");
|
|
1070
|
+
}
|
|
1071
|
+
if (item?.action === "update") {
|
|
1072
|
+
openPlan(activeHarness, "update", "manageHarness");
|
|
1073
|
+
}
|
|
1074
|
+
if (item?.action === "sync") {
|
|
1075
|
+
openPlan(activeHarness, "sync", "manageHarness");
|
|
1076
|
+
}
|
|
1077
|
+
if (item?.action === "models") openModelRoles(activeHarness);
|
|
1078
|
+
}
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
if (view === "modelRoles") {
|
|
1082
|
+
if (key.upArrow || input === "k") {
|
|
1083
|
+
setModelSelected(
|
|
1084
|
+
(current) => moveSelection(current, -1, modelMenuItems)
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
if (key.downArrow || input === "j") {
|
|
1088
|
+
setModelSelected(
|
|
1089
|
+
(current) => moveSelection(current, 1, modelMenuItems)
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
if (key.return) {
|
|
1093
|
+
if (modelSelected < modelRows.length) {
|
|
1094
|
+
const role = modelRows[modelSelected];
|
|
1095
|
+
setEditingRole(role);
|
|
1096
|
+
setEditDraft(role?.model ?? "");
|
|
1097
|
+
setChoiceSelected(0);
|
|
1098
|
+
setView("modelChoice");
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
const action = modelActions[modelSelected - modelRows.length];
|
|
1102
|
+
if (action === "Back") setView("manageHarness");
|
|
1103
|
+
if (action === "Preview changes") previewModelChanges(false);
|
|
1104
|
+
if (action === "Apply changes") previewModelChanges(true);
|
|
1105
|
+
}
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
if (view === "status") {
|
|
1109
|
+
if (input === "r") setReportVersion((current) => current + 1);
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
if (view === "preview") {
|
|
1113
|
+
if (key.leftArrow || key.rightArrow || key.tab) {
|
|
1114
|
+
setPreviewAction(
|
|
1115
|
+
(current) => current === "apply" ? "cancel" : "apply"
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
if (input === "a" && plan?.canApply) {
|
|
1119
|
+
setPreviewAction("apply");
|
|
1120
|
+
setResult(operations.apply(plan));
|
|
1121
|
+
}
|
|
1122
|
+
if (input === "c") goBack();
|
|
1123
|
+
if (key.return && plan) {
|
|
1124
|
+
if (previewAction === "cancel" || !plan.canApply) {
|
|
1125
|
+
goBack();
|
|
1126
|
+
} else {
|
|
1127
|
+
setResult(operations.apply(plan));
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
});
|
|
1132
|
+
if (view === "root") {
|
|
1133
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
1134
|
+
/* @__PURE__ */ jsx8(
|
|
1135
|
+
Header,
|
|
1136
|
+
{
|
|
1137
|
+
title: "Interactive setup",
|
|
1138
|
+
subtitle: "Use arrows and Enter. Escape goes back. q exits."
|
|
1139
|
+
}
|
|
1140
|
+
),
|
|
1141
|
+
/* @__PURE__ */ jsx8(Menu, { items: rootItems, selected: rootSelected })
|
|
1142
|
+
] });
|
|
1143
|
+
}
|
|
1144
|
+
if (view === "action") {
|
|
1145
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
1146
|
+
/* @__PURE__ */ jsx8(
|
|
1147
|
+
Header,
|
|
1148
|
+
{
|
|
1149
|
+
title: "Sync / Update",
|
|
1150
|
+
subtitle: "Choose an operation before selecting a harness."
|
|
1151
|
+
}
|
|
1152
|
+
),
|
|
1153
|
+
/* @__PURE__ */ jsx8(Menu, { items: actionItems, selected: actionSelected })
|
|
1154
|
+
] });
|
|
1155
|
+
}
|
|
1156
|
+
if (view === "harness") {
|
|
1157
|
+
const title = harnessPurpose === "status" ? "Status" : harnessPurpose === "list" ? "Manage Harnesses" : activeAction === "update" ? "Update" : "Sync";
|
|
1158
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
1159
|
+
/* @__PURE__ */ jsx8(Header, { title, subtitle: "Choose a harness." }),
|
|
1160
|
+
/* @__PURE__ */ jsx8(Menu, { items: harnessItems, selected: harnessSelected })
|
|
1161
|
+
] });
|
|
1162
|
+
}
|
|
1163
|
+
if (view === "manageHarness" && report) {
|
|
1164
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
1165
|
+
/* @__PURE__ */ jsx8(
|
|
1166
|
+
Header,
|
|
1167
|
+
{
|
|
1168
|
+
title: `Manage ${report.displayName ?? activeHarness}`,
|
|
1169
|
+
subtitle: "Choose an action. Escape returns to harness selection."
|
|
1170
|
+
}
|
|
1171
|
+
),
|
|
1172
|
+
/* @__PURE__ */ jsxs7(Text8, { children: [
|
|
1173
|
+
"Health: ",
|
|
1174
|
+
/* @__PURE__ */ jsx8(Text8, { color: stateColor(report.state), children: report.state }),
|
|
1175
|
+
/* @__PURE__ */ jsxs7(Text8, { color: theme.dim, children: [
|
|
1176
|
+
" - ",
|
|
1177
|
+
report.summary
|
|
1178
|
+
] })
|
|
1179
|
+
] }),
|
|
1180
|
+
/* @__PURE__ */ jsx8(Menu, { items: currentManageItems, selected: manageSelected })
|
|
1181
|
+
] });
|
|
1182
|
+
}
|
|
1183
|
+
if (view === "modelRoles") {
|
|
1184
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
1185
|
+
/* @__PURE__ */ jsx8(
|
|
1186
|
+
Header,
|
|
1187
|
+
{
|
|
1188
|
+
title: `${modelHarness === "codex" ? "Codex" : "OpenCode"} Models`,
|
|
1189
|
+
subtitle: "Enter edits a role. Dirty rows are marked with *."
|
|
1190
|
+
}
|
|
1191
|
+
),
|
|
1192
|
+
/* @__PURE__ */ jsx8(
|
|
1193
|
+
ModelScreen,
|
|
1194
|
+
{
|
|
1195
|
+
harness: modelHarness,
|
|
1196
|
+
roles: modelRows,
|
|
1197
|
+
selected: normalizeSelection(modelSelected, modelMenuItems),
|
|
1198
|
+
actions: modelActions
|
|
1199
|
+
}
|
|
1200
|
+
),
|
|
1201
|
+
modelResult ? /* @__PURE__ */ jsx8(Text8, { color: modelResult.applied ? theme.ok : theme.warning, children: modelResult.summary }) : null
|
|
1202
|
+
] });
|
|
1203
|
+
}
|
|
1204
|
+
if (view === "modelEdit" && editingRole) {
|
|
1205
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
1206
|
+
/* @__PURE__ */ jsx8(
|
|
1207
|
+
Header,
|
|
1208
|
+
{
|
|
1209
|
+
title: `Edit ${editingRole.role}`,
|
|
1210
|
+
subtitle: "Type a model ID. Tab inserts the first catalog option. Enter saves."
|
|
1211
|
+
}
|
|
1212
|
+
),
|
|
1213
|
+
/* @__PURE__ */ jsxs7(Text8, { children: [
|
|
1214
|
+
"Current: ",
|
|
1215
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.accent, children: editingRole.model })
|
|
1216
|
+
] }),
|
|
1217
|
+
/* @__PURE__ */ jsxs7(Text8, { children: [
|
|
1218
|
+
"New: ",
|
|
1219
|
+
/* @__PURE__ */ jsx8(Text8, { color: theme.warning, children: editDraft })
|
|
1220
|
+
] })
|
|
1221
|
+
] });
|
|
1222
|
+
}
|
|
1223
|
+
if (view === "modelChoice" && editingRole) {
|
|
1224
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
1225
|
+
/* @__PURE__ */ jsx8(
|
|
1226
|
+
Header,
|
|
1227
|
+
{
|
|
1228
|
+
title: `Choose ${editingRole.role} model`,
|
|
1229
|
+
subtitle: "Select a catalog option or Manual entry."
|
|
1230
|
+
}
|
|
1231
|
+
),
|
|
1232
|
+
/* @__PURE__ */ jsx8(
|
|
1233
|
+
ModelChoiceScreen,
|
|
1234
|
+
{
|
|
1235
|
+
currentModel: editingRole.currentModel,
|
|
1236
|
+
draftModel: editedModels[editingRole.role] ?? editingRole.model,
|
|
1237
|
+
options: modelOptions,
|
|
1238
|
+
selected: choiceSelected
|
|
1239
|
+
}
|
|
1240
|
+
)
|
|
1241
|
+
] });
|
|
1242
|
+
}
|
|
1243
|
+
if (view === "preview" && plan) {
|
|
1244
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
1245
|
+
/* @__PURE__ */ jsx8(
|
|
1246
|
+
Header,
|
|
1247
|
+
{
|
|
1248
|
+
title: plan.title,
|
|
1249
|
+
subtitle: "Enter selects. Escape or c returns one level."
|
|
1250
|
+
}
|
|
1251
|
+
),
|
|
1252
|
+
/* @__PURE__ */ jsx8(
|
|
1253
|
+
PlanPreview,
|
|
1254
|
+
{
|
|
1255
|
+
plan,
|
|
1256
|
+
selectedAction: previewAction,
|
|
1257
|
+
result
|
|
1258
|
+
}
|
|
1259
|
+
)
|
|
1260
|
+
] });
|
|
1261
|
+
}
|
|
1262
|
+
if (!report) return null;
|
|
1263
|
+
return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
|
|
1264
|
+
/* @__PURE__ */ jsx8(
|
|
1265
|
+
Header,
|
|
1266
|
+
{
|
|
1267
|
+
title: `${report.displayName ?? activeHarness} Status`,
|
|
1268
|
+
subtitle: "Categorized summary. Escape returns. r refreshes."
|
|
1269
|
+
}
|
|
1270
|
+
),
|
|
1271
|
+
/* @__PURE__ */ jsx8(StatusView, { report })
|
|
1272
|
+
] }, reportVersion);
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
// src/cli/tui/index.tsx
|
|
1276
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1277
|
+
async function runInteractiveTui() {
|
|
1278
|
+
const instance = render(/* @__PURE__ */ jsx9(App, {}));
|
|
1279
|
+
await instance.waitUntilExit();
|
|
1280
|
+
return 0;
|
|
1281
|
+
}
|
|
1282
|
+
export {
|
|
1283
|
+
App,
|
|
1284
|
+
runInteractiveTui
|
|
1285
|
+
};
|