@kal-elsam/kairo-runtime 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,40 +1,32 @@
1
- import React, { useEffect, useState } from "react";
1
+ import React, { useEffect, useReducer } from "react";
2
2
  import { Box, Text, useApp, useInput } from "ink";
3
- import { BRAND } from "../brand/index.js";
4
- import { buildReadOnlyDiagnostics } from "../action-planner.js";
5
- import { buildRuntimeDashboardData } from "../runtime/run-cli.js";
6
- import { readRunEvents } from "../runtime/run-store.js";
7
- import { stopRun } from "../runtime/run-manager.js";
8
- import { startRun } from "../runtime/run-manager.js";
9
3
  import {
10
- ORCHESTRATOR_MENU,
11
4
  ORCHESTRATOR_VIEWS,
12
- LAUNCH_WIZARD_STEPS,
13
- LAUNCH_PERMISSION_OPTIONS,
14
- createLaunchDraft,
15
- formatDashboardSnapshot,
16
- formatDiagnosticsLines,
17
- formatLaunchWizardLines,
18
- formatProviderLines,
19
- formatRunDetailLines,
20
- formatRunLines,
21
5
  isRunCancellable,
22
- resolveLaunchPermissions,
23
- resolveLaunchableAgents,
24
- resolveMenuItem,
25
- resolveMenuItemView,
26
- retreatLaunchWizardStep,
27
- selectRunFromList,
28
- shiftMenuIndex
6
+ selectRunFromList
29
7
  } from "./orchestrator-state.js";
30
-
31
- const COLORS = {
32
- accent: "cyan",
33
- success: "green",
34
- warning: "yellow",
35
- danger: "red",
36
- muted: "gray"
37
- };
8
+ import {
9
+ createCockpitUiState,
10
+ reduceCockpitUi,
11
+ resolveNavAction
12
+ } from "./cockpit-controller.js";
13
+ import {
14
+ COCKPIT_REGIONS,
15
+ buildFooterModel,
16
+ buildHomeMissionModel,
17
+ buildNavModel,
18
+ buildSystemStripModel,
19
+ buildTopBarModel,
20
+ resolveProjectName
21
+ } from "./cockpit-models.js";
22
+ import { CockpitShell } from "./cockpit/primitives.js";
23
+ import { renderCockpitView } from "./cockpit-views.js";
24
+ import { handleLaunchInput } from "./launch-input.js";
25
+ import { useTerminalSize } from "./use-terminal-size.js";
26
+ import { useOrchestratorData } from "./use-orchestrator-data.js";
27
+ import { resolveTerminalCapabilities } from "./terminal-capabilities.js";
28
+ import { COCKPIT_COLORS } from "./theme.js";
29
+ import { LAYOUT_MODES } from "./layout.js";
38
30
 
39
31
  export function OrchestratorApp({
40
32
  homeDir,
@@ -42,467 +34,209 @@ export function OrchestratorApp({
42
34
  packageName,
43
35
  packageRoot,
44
36
  cliVersion,
37
+ hasGlobalState = false,
45
38
  onComplete
46
39
  }) {
47
40
  const { exit } = useApp();
48
- const [view, setView] = useState(ORCHESTRATOR_VIEWS.HOME);
49
- const [menuIndex, setMenuIndex] = useState(0);
50
- const [listIndex, setListIndex] = useState(0);
51
- const [launchAgentIndex, setLaunchAgentIndex] = useState(0);
52
- const [launchStep, setLaunchStep] = useState(LAUNCH_WIZARD_STEPS.AGENT);
53
- const [launchDraft, setLaunchDraft] = useState(createLaunchDraft);
54
- const [launchPermissionIndex, setLaunchPermissionIndex] = useState(0);
55
- const [loading, setLoading] = useState(true);
56
- const [busy, setBusy] = useState(false);
57
- const [error, setError] = useState(null);
58
- const [dashboard, setDashboard] = useState(null);
59
- const [diagnostics, setDiagnostics] = useState(null);
60
- const [selectedRun, setSelectedRun] = useState(null);
61
- const [selectedEvents, setSelectedEvents] = useState([]);
62
- const [statusMessage, setStatusMessage] = useState(null);
63
-
64
- const reload = async () => {
65
- const [dash, diag] = await Promise.all([
66
- buildRuntimeDashboardData({ homeDir, workspaceRoot, cliVersion }),
67
- buildReadOnlyDiagnostics({ homeDir, workspaceRoot, packageName, packageRoot, cliVersion })
68
- ]);
69
- setDashboard(dash);
70
- setDiagnostics(diag);
71
- };
41
+ const { columns, rows, layoutMode } = useTerminalSize();
42
+ const caps = resolveTerminalCapabilities({ columns, rows, isTTY: true });
43
+ const [ui, dispatch] = useReducer(
44
+ reduceCockpitUi,
45
+ createCockpitUiState({
46
+ layoutMode: layoutMode ?? LAYOUT_MODES.COMPACT,
47
+ region: COCKPIT_REGIONS.NAV
48
+ })
49
+ );
50
+ const data = useOrchestratorData({
51
+ homeDir,
52
+ workspaceRoot,
53
+ packageName,
54
+ packageRoot,
55
+ cliVersion
56
+ });
72
57
 
73
58
  useEffect(() => {
74
- let cancelled = false;
75
-
76
- async function load() {
77
- try {
78
- await reload();
79
- if (cancelled) return;
80
- setLoading(false);
81
- } catch (loadError) {
82
- if (cancelled) return;
83
- setError(loadError instanceof Error ? loadError.message : String(loadError));
84
- setLoading(false);
85
- }
86
- }
87
-
88
- load();
89
- return () => {
90
- cancelled = true;
91
- };
92
- }, [homeDir, workspaceRoot, packageName, packageRoot, cliVersion]);
59
+ if (layoutMode) dispatch({ type: "resize", layoutMode });
60
+ }, [layoutMode]);
93
61
 
94
62
  const finish = (outcome) => {
95
63
  onComplete(outcome);
96
64
  exit();
97
65
  };
98
66
 
99
- const openRunDetail = async (run) => {
100
- if (!run) return;
101
- const events = await readRunEvents(homeDir, run.runId, { limit: 20 });
102
- setSelectedRun(run);
103
- setSelectedEvents(events);
104
- setView(ORCHESTRATOR_VIEWS.RUN_DETAIL);
105
- };
106
-
107
- const launchableAgents = resolveLaunchableAgents(dashboard?.providers ?? []);
108
-
109
- const resetLaunchWizard = () => {
110
- setLaunchStep(LAUNCH_WIZARD_STEPS.AGENT);
111
- setLaunchDraft(createLaunchDraft());
112
- setLaunchAgentIndex(0);
113
- setLaunchPermissionIndex(0);
114
- };
115
-
116
- const handleLaunch = async (draft) => {
117
- if (!draft.agentId || !draft.task.trim()) {
118
- setError("Agent and task are required.");
119
- return;
120
- }
121
-
122
- setBusy(true);
123
- setStatusMessage(`Launching ${draft.agentId}…`);
124
- try {
125
- const permissions = resolveLaunchPermissions({
126
- ...draft,
127
- permissionIndex: launchPermissionIndex
128
- });
129
- const { runId } = await startRun({
130
- homeDir,
131
- agentId: draft.agentId,
132
- task: draft.task.trim(),
133
- cwd: workspaceRoot,
134
- model: draft.model.trim() || null,
135
- permissions,
136
- cliVersion,
137
- profile: dashboard?.profile ?? null,
138
- follow: false,
139
- wait: false
140
- });
141
- await reload();
142
- const run = (await buildRuntimeDashboardData({ homeDir, workspaceRoot, cliVersion }))
143
- .runs.find((entry) => entry.runId === runId);
144
- setStatusMessage(`Run started: ${runId}`);
145
- resetLaunchWizard();
146
- setView(ORCHESTRATOR_VIEWS.HOME);
147
- if (run) await openRunDetail(run);
148
- } catch (launchError) {
149
- setError(launchError instanceof Error ? launchError.message : String(launchError));
150
- } finally {
151
- setBusy(false);
152
- }
153
- };
154
-
155
- const handleCancelRun = async () => {
156
- if (!selectedRun || !isRunCancellable(selectedRun)) return;
157
- setBusy(true);
158
- try {
159
- await stopRun(homeDir, selectedRun.runId);
160
- await reload();
161
- const refreshed = await buildRuntimeDashboardData({ homeDir, workspaceRoot, cliVersion });
162
- const run = refreshed.runs.find((entry) => entry.runId === selectedRun.runId);
163
- const events = await readRunEvents(homeDir, selectedRun.runId, { limit: 20 });
164
- setSelectedRun(run ?? selectedRun);
165
- setSelectedEvents(events);
166
- setStatusMessage(`Cancelled ${selectedRun.runId}`);
167
- } catch (cancelError) {
168
- setError(cancelError instanceof Error ? cancelError.message : String(cancelError));
169
- } finally {
170
- setBusy(false);
171
- }
172
- };
173
-
174
67
  useInput((inputKey, key) => {
175
68
  if (key.escape) {
176
- if (view === ORCHESTRATOR_VIEWS.HOME) {
69
+ const next = reduceCockpitUi(ui, { type: "escape" });
70
+ if (next.shouldExit) {
177
71
  finish({ cancelled: true });
178
72
  return;
179
73
  }
180
- setView(ORCHESTRATOR_VIEWS.HOME);
181
- setSelectedRun(null);
182
- setListIndex(0);
74
+ data.setSelectedRun(null);
75
+ data.resetLaunchWizard();
76
+ dispatch({ type: "escape" });
183
77
  return;
184
78
  }
185
79
 
186
- if (loading || error || busy) return;
80
+ if (data.loading || data.error || data.busy) return;
187
81
 
188
- if (view === ORCHESTRATOR_VIEWS.LAUNCH) {
189
- if (launchableAgents.length === 0) {
190
- if (key.escape) {
191
- resetLaunchWizard();
192
- setView(ORCHESTRATOR_VIEWS.HOME);
193
- }
194
- return;
195
- }
82
+ if (inputKey === "?") {
83
+ dispatch({ type: "toggle-help" });
84
+ return;
85
+ }
86
+ if (key.tab) {
87
+ dispatch({ type: "tab" });
88
+ return;
89
+ }
196
90
 
197
- if (launchStep === LAUNCH_WIZARD_STEPS.AGENT) {
198
- if (key.upArrow) {
199
- setLaunchAgentIndex((index) => Math.max(0, index - 1));
200
- return;
201
- }
202
- if (key.downArrow) {
203
- setLaunchAgentIndex((index) => Math.min(launchableAgents.length - 1, index + 1));
204
- return;
205
- }
206
- if (key.return) {
207
- const agentId = launchableAgents[launchAgentIndex];
208
- setLaunchDraft((draft) => ({ ...draft, agentId }));
209
- setLaunchStep(LAUNCH_WIZARD_STEPS.TASK);
210
- }
91
+ if (ui.view === ORCHESTRATOR_VIEWS.LAUNCH && data.launchableAgents.length > 0) {
92
+ if (handleLaunchInput({
93
+ key,
94
+ inputKey,
95
+ launchStep: data.launchStep,
96
+ launchDraft: data.launchDraft,
97
+ launchableAgents: data.launchableAgents,
98
+ launchAgentIndex: data.launchAgentIndex,
99
+ launchPermissionIndex: data.launchPermissionIndex,
100
+ setLaunchAgentIndex: data.setLaunchAgentIndex,
101
+ setLaunchDraft: data.setLaunchDraft,
102
+ setLaunchStep: data.setLaunchStep,
103
+ setLaunchPermissionIndex: data.setLaunchPermissionIndex,
104
+ setError: data.setError,
105
+ handleLaunch: (draft) => data.handleLaunch(draft, data.dashboard?.profile, dispatch),
106
+ reload: data.reload
107
+ })) {
211
108
  return;
212
109
  }
110
+ }
213
111
 
214
- if (launchStep === LAUNCH_WIZARD_STEPS.TASK) {
215
- if (key.return) {
216
- if (!launchDraft.task.trim()) {
217
- setError("Task cannot be empty.");
218
- return;
219
- }
220
- setLaunchStep(LAUNCH_WIZARD_STEPS.MODEL);
221
- return;
222
- }
223
- if (key.backspace || key.delete) {
224
- setLaunchDraft((draft) => ({ ...draft, task: draft.task.slice(0, -1) }));
225
- return;
226
- }
227
- if (inputKey && inputKey.length === 1 && !key.ctrl && !key.meta) {
228
- setLaunchDraft((draft) => ({ ...draft, task: `${draft.task}${inputKey}` }));
229
- }
112
+ if (ui.view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS || ui.view === ORCHESTRATOR_VIEWS.RECENT_RUNS) {
113
+ const runs = ui.view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
114
+ ? data.dashboard?.activeRuns ?? []
115
+ : data.dashboard?.recentRuns ?? [];
116
+ if (key.upArrow || key.downArrow) {
117
+ dispatch({
118
+ type: "arrow",
119
+ direction: key.upArrow ? "up" : "down",
120
+ listLength: runs.length
121
+ });
230
122
  return;
231
123
  }
232
-
233
- if (launchStep === LAUNCH_WIZARD_STEPS.MODEL) {
234
- if (key.return) {
235
- setLaunchStep(LAUNCH_WIZARD_STEPS.PERMISSIONS);
236
- return;
237
- }
238
- if (key.backspace || key.delete) {
239
- setLaunchDraft((draft) => ({ ...draft, model: draft.model.slice(0, -1) }));
240
- return;
241
- }
242
- if (inputKey && inputKey.length === 1 && !key.ctrl && !key.meta) {
243
- setLaunchDraft((draft) => ({ ...draft, model: `${draft.model}${inputKey}` }));
244
- }
124
+ if (key.return) {
125
+ data.openRunDetail(selectRunFromList(runs, ui.listIndex), dispatch);
245
126
  return;
246
127
  }
128
+ }
247
129
 
248
- if (launchStep === LAUNCH_WIZARD_STEPS.PERMISSIONS) {
249
- if (key.upArrow) {
250
- setLaunchPermissionIndex((index) => Math.max(0, index - 1));
251
- return;
252
- }
253
- if (key.downArrow) {
254
- setLaunchPermissionIndex((index) => Math.min(LAUNCH_PERMISSION_OPTIONS.length - 1, index + 1));
255
- return;
256
- }
257
- if (key.return) {
258
- setLaunchStep(LAUNCH_WIZARD_STEPS.CONFIRM);
259
- }
130
+ if (ui.view === ORCHESTRATOR_VIEWS.RUN_DETAIL) {
131
+ if (inputKey.toLowerCase() === "c" && isRunCancellable(data.selectedRun)) {
132
+ data.handleCancelRun();
260
133
  return;
261
134
  }
262
-
263
- if (launchStep === LAUNCH_WIZARD_STEPS.CONFIRM) {
264
- if (key.return) {
265
- handleLaunch({ ...launchDraft, permissionIndex: launchPermissionIndex });
266
- }
267
- if (key.escape) {
268
- setLaunchStep(retreatLaunchWizardStep(launchStep));
269
- }
135
+ if (inputKey.toLowerCase() === "r") {
136
+ data.openRunDetail(data.selectedRun, dispatch);
270
137
  return;
271
138
  }
139
+ }
272
140
 
273
- if (inputKey.toLowerCase() === "r") {
274
- reload().catch((reloadError) => {
275
- setError(reloadError instanceof Error ? reloadError.message : String(reloadError));
276
- });
277
- }
141
+ if (inputKey.toLowerCase() === "r" && ui.view !== ORCHESTRATOR_VIEWS.LAUNCH) {
142
+ data.reload().catch((reloadError) => {
143
+ data.setError(reloadError instanceof Error ? reloadError.message : String(reloadError));
144
+ });
278
145
  return;
279
146
  }
280
147
 
281
- if (view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS || view === ORCHESTRATOR_VIEWS.RECENT_RUNS) {
282
- const runs = view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
283
- ? dashboard?.activeRuns ?? []
284
- : dashboard?.recentRuns ?? [];
285
-
286
- if (key.upArrow) {
287
- setListIndex((index) => Math.max(0, index - 1));
288
- return;
289
- }
290
- if (key.downArrow) {
291
- setListIndex((index) => Math.min(Math.max(runs.length - 1, 0), index + 1));
148
+ if (ui.view === ORCHESTRATOR_VIEWS.HOME || ui.region === COCKPIT_REGIONS.NAV) {
149
+ if (key.upArrow || key.downArrow) {
150
+ dispatch({ type: "arrow", direction: key.upArrow ? "up" : "down" });
292
151
  return;
293
152
  }
294
153
  if (key.return) {
295
- openRunDetail(selectRunFromList(runs, listIndex));
296
- }
297
- if (inputKey.toLowerCase() === "r") {
298
- reload().catch((reloadError) => {
299
- setError(reloadError instanceof Error ? reloadError.message : String(reloadError));
300
- });
301
- }
302
- return;
303
- }
304
-
305
- if (view === ORCHESTRATOR_VIEWS.RUN_DETAIL) {
306
- if (inputKey.toLowerCase() === "c" && isRunCancellable(selectedRun)) {
307
- handleCancelRun();
308
- }
309
- if (inputKey.toLowerCase() === "r") {
310
- openRunDetail(selectedRun);
311
- }
312
- return;
313
- }
314
-
315
- if (view !== ORCHESTRATOR_VIEWS.HOME) {
316
- if (inputKey.toLowerCase() === "r") {
317
- reload().catch((reloadError) => {
318
- setError(reloadError instanceof Error ? reloadError.message : String(reloadError));
319
- });
154
+ const item = resolveNavAction(ui.navIndex);
155
+ if (item?.action === "launch") {
156
+ data.resetLaunchWizard();
157
+ dispatch({ type: "set-view", view: ORCHESTRATOR_VIEWS.LAUNCH });
158
+ return;
159
+ }
160
+ dispatch({ type: "enter-nav" });
320
161
  }
321
- return;
322
- }
323
-
324
- if (key.upArrow) {
325
- setMenuIndex((index) => shiftMenuIndex(index, "up"));
326
- return;
327
- }
328
-
329
- if (key.downArrow) {
330
- setMenuIndex((index) => shiftMenuIndex(index, "down"));
331
- return;
332
162
  }
333
-
334
- if (!key.return) return;
335
-
336
- const item = resolveMenuItem(menuIndex);
337
- if (item?.action === "launch") {
338
- resetLaunchWizard();
339
- setView(ORCHESTRATOR_VIEWS.LAUNCH);
340
- return;
341
- }
342
-
343
- setView(resolveMenuItemView(menuIndex));
344
- setListIndex(0);
345
163
  });
346
164
 
347
- if (loading) {
165
+ if (data.loading) {
348
166
  return React.createElement(Box, { flexDirection: "column" },
349
- React.createElement(Text, { bold: true, color: COLORS.accent }, BRAND.displayName),
350
- React.createElement(Text, { color: COLORS.muted }, "Loading runtime dashboard…")
167
+ React.createElement(Text, { bold: true, color: COCKPIT_COLORS.primary }, "KAIRO"),
168
+ React.createElement(Text, { color: COCKPIT_COLORS.muted }, "Loading cockpit…")
351
169
  );
352
170
  }
353
171
 
354
- if (error) {
172
+ if (data.error) {
355
173
  return React.createElement(Box, { flexDirection: "column" },
356
- React.createElement(Text, { bold: true, color: COLORS.danger }, "Runtime error"),
357
- React.createElement(Text, null, error),
174
+ React.createElement(Text, { bold: true, color: COCKPIT_COLORS.danger }, "Runtime error"),
175
+ React.createElement(Text, null, data.error),
358
176
  React.createElement(Text, { dimColor: true }, "Esc to exit")
359
177
  );
360
178
  }
361
179
 
180
+ const mode = ui.layoutMode ?? LAYOUT_MODES.COMPACT;
181
+ const colorEnabled = caps.color;
182
+ const unicode = caps.unicode;
183
+
362
184
  return React.createElement(Box, { flexDirection: "column" },
363
- React.createElement(Text, { bold: true, color: COLORS.accent }, `${BRAND.displayName} runtime`),
364
- React.createElement(Text, { color: COLORS.muted }, "Launch · supervise · audit agent runs"),
365
- statusMessage && React.createElement(Text, { color: COLORS.success }, statusMessage),
366
- React.createElement(Text, null, ""),
367
- renderView({
368
- view,
369
- dashboard,
370
- diagnostics,
371
- menuIndex,
372
- listIndex,
373
- launchStep,
374
- launchDraft,
375
- launchAgentIndex,
376
- launchPermissionIndex,
377
- launchableAgents,
378
- selectedRun,
379
- selectedEvents
380
- }),
381
- React.createElement(Text, null, ""),
382
- React.createElement(Text, { dimColor: true }, footerHint(view, selectedRun, launchStep))
185
+ data.statusMessage && React.createElement(Text, {
186
+ color: COCKPIT_COLORS.success
187
+ }, data.statusMessage),
188
+ React.createElement(CockpitShell, {
189
+ topBar: buildTopBarModel({
190
+ projectName: resolveProjectName(workspaceRoot),
191
+ systemOnline: true,
192
+ unicode
193
+ }),
194
+ footer: buildFooterModel({
195
+ view: ui.view,
196
+ region: ui.region,
197
+ helpOpen: ui.helpOpen,
198
+ canCancel: isRunCancellable(data.selectedRun),
199
+ unicode
200
+ }),
201
+ layoutMode: mode,
202
+ nav: buildNavModel({
203
+ navIndex: ui.navIndex,
204
+ focused: ui.region === COCKPIT_REGIONS.NAV,
205
+ unicode
206
+ }),
207
+ system: buildSystemStripModel({
208
+ dashboard: data.dashboard,
209
+ diagnostics: data.diagnostics,
210
+ healthKind: "ready"
211
+ }),
212
+ navFocused: ui.region === COCKPIT_REGIONS.NAV,
213
+ contentFocused: ui.region === COCKPIT_REGIONS.CONTENT,
214
+ systemFocused: ui.region === COCKPIT_REGIONS.SYSTEM,
215
+ colorEnabled
216
+ },
217
+ renderCockpitView({
218
+ view: ui.view,
219
+ dashboard: data.dashboard,
220
+ diagnostics: data.diagnostics,
221
+ listIndex: ui.listIndex,
222
+ launchStep: data.launchStep,
223
+ launchDraft: data.launchDraft,
224
+ launchAgentIndex: data.launchAgentIndex,
225
+ launchPermissionIndex: data.launchPermissionIndex,
226
+ launchableAgents: data.launchableAgents,
227
+ selectedRun: data.selectedRun,
228
+ selectedEvents: data.selectedEvents,
229
+ homeMission: buildHomeMissionModel({
230
+ hasGlobalState,
231
+ diagnostics: data.diagnostics,
232
+ dashboard: data.dashboard,
233
+ layoutMode: mode,
234
+ activityLines: (data.dashboard?.recentRuns ?? []).map((run) =>
235
+ `${run.runId} ${run.state} ${run.agentId}`
236
+ )
237
+ }),
238
+ colorEnabled
239
+ })
240
+ )
383
241
  );
384
242
  }
385
-
386
- function renderView({
387
- view,
388
- dashboard,
389
- diagnostics,
390
- menuIndex,
391
- listIndex,
392
- launchStep,
393
- launchDraft,
394
- launchAgentIndex,
395
- launchPermissionIndex,
396
- launchableAgents,
397
- selectedRun,
398
- selectedEvents
399
- }) {
400
- switch (view) {
401
- case ORCHESTRATOR_VIEWS.HOME:
402
- return React.createElement(Box, { flexDirection: "column" },
403
- React.createElement(Text, { bold: true }, "Operations"),
404
- ORCHESTRATOR_MENU.map((item, index) =>
405
- React.createElement(Text, {
406
- key: item.id,
407
- color: index === menuIndex ? COLORS.accent : undefined,
408
- bold: index === menuIndex
409
- }, `${index === menuIndex ? "› " : " "}${item.label}`)
410
- ),
411
- React.createElement(Text, null, ""),
412
- React.createElement(Text, { bold: true }, "Snapshot"),
413
- formatDashboardSnapshot(dashboard)
414
- .map((line) => React.createElement(Text, { key: line }, line))
415
- );
416
- case ORCHESTRATOR_VIEWS.ACTIVE_RUNS:
417
- return React.createElement(Box, { flexDirection: "column" },
418
- React.createElement(Text, { bold: true }, "Active runs"),
419
- formatRunLines(dashboard?.activeRuns ?? [], { emptyMessage: "No active runs." })
420
- .map((line, index) => React.createElement(Text, {
421
- key: line,
422
- color: index === listIndex ? COLORS.accent : undefined,
423
- bold: index === listIndex
424
- }, `${index === listIndex ? "› " : " "}${line}`))
425
- );
426
- case ORCHESTRATOR_VIEWS.RECENT_RUNS:
427
- return React.createElement(Box, { flexDirection: "column" },
428
- React.createElement(Text, { bold: true }, "Recent runs"),
429
- formatRunLines(dashboard?.recentRuns ?? [], { emptyMessage: "No completed runs yet." })
430
- .map((line, index) => React.createElement(Text, {
431
- key: line,
432
- color: index === listIndex ? COLORS.accent : undefined,
433
- bold: index === listIndex
434
- }, `${index === listIndex ? "› " : " "}${line}`))
435
- );
436
- case ORCHESTRATOR_VIEWS.PROVIDERS:
437
- return React.createElement(Box, { flexDirection: "column" },
438
- React.createElement(Text, { bold: true }, "Providers"),
439
- formatProviderLines(dashboard?.providers ?? [])
440
- .map((line) => React.createElement(Text, { key: line }, line))
441
- );
442
- case ORCHESTRATOR_VIEWS.LAUNCH:
443
- if (launchableAgents.length === 0) {
444
- return React.createElement(Box, { flexDirection: "column" },
445
- React.createElement(Text, { bold: true }, "Launch run"),
446
- React.createElement(Text, { color: COLORS.warning }, "No launchable agents detected."),
447
- React.createElement(Text, { dimColor: true }, "Esc to return")
448
- );
449
- }
450
- return React.createElement(Box, { flexDirection: "column" },
451
- React.createElement(Text, { bold: true }, "Launch run"),
452
- formatLaunchWizardLines({
453
- step: launchStep,
454
- draft: launchDraft,
455
- launchableAgents,
456
- agentIndex: launchAgentIndex,
457
- permissionIndex: launchPermissionIndex
458
- }).map((line) => React.createElement(Text, { key: line, color: line.startsWith("›") ? COLORS.accent : undefined }, line))
459
- );
460
- case ORCHESTRATOR_VIEWS.RUN_DETAIL:
461
- return React.createElement(Box, { flexDirection: "column" },
462
- React.createElement(Text, { bold: true }, "Run detail"),
463
- formatRunDetailLines(selectedRun, selectedEvents)
464
- .map((line) => React.createElement(Text, { key: line }, line))
465
- );
466
- case ORCHESTRATOR_VIEWS.DIAGNOSTICS:
467
- return React.createElement(Box, { flexDirection: "column" },
468
- React.createElement(Text, { bold: true }, "Diagnostics"),
469
- formatDiagnosticsLines(diagnostics)
470
- .map((line) => React.createElement(Text, { key: line }, line))
471
- );
472
- case ORCHESTRATOR_VIEWS.HELP:
473
- return React.createElement(Box, { flexDirection: "column" },
474
- React.createElement(Text, { bold: true }, "Help"),
475
- React.createElement(Text, null, "Kairo runtime launches and audits agent CLIs you manage."),
476
- React.createElement(Text, null, "CLI: kairo run --agent <id> --task \"...\""),
477
- React.createElement(Text, null, "CLI: kairo runs list|show|stop"),
478
- React.createElement(Text, null, "Audit trail: ~/.harness/runs/<runId>/"),
479
- React.createElement(Text, null, "Transcripts are opt-in via --capture-transcript."),
480
- React.createElement(Text, null, "Credentials stay in environment variables — never in profiles.")
481
- );
482
- default: {
483
- const _exhaustive = view;
484
- return React.createElement(Text, null, `Unknown view: ${_exhaustive}`);
485
- }
486
- }
487
- }
488
-
489
- function footerHint(view, selectedRun, launchStep) {
490
- if (view === ORCHESTRATOR_VIEWS.HOME) return "↑↓ navigate · Enter select · Esc quit";
491
- if (view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS || view === ORCHESTRATOR_VIEWS.RECENT_RUNS) {
492
- return "↑↓ select run · Enter inspect · R refresh · Esc menu";
493
- }
494
- if (view === ORCHESTRATOR_VIEWS.LAUNCH) {
495
- if (launchStep === LAUNCH_WIZARD_STEPS.TASK || launchStep === LAUNCH_WIZARD_STEPS.MODEL) {
496
- return "Type · Enter next · Esc menu";
497
- }
498
- if (launchStep === LAUNCH_WIZARD_STEPS.CONFIRM) {
499
- return "Enter launch · Esc back · R refresh";
500
- }
501
- return "↑↓ navigate · Enter select · Esc menu";
502
- }
503
- if (view === ORCHESTRATOR_VIEWS.RUN_DETAIL) {
504
- if (isRunCancellable(selectedRun)) return "C cancel · R refresh · Esc menu";
505
- return "R refresh · Esc menu";
506
- }
507
- return "R refresh · Esc menu";
508
- }