@kal-elsam/kairo-runtime 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -7
- package/package.json +1 -1
- package/scripts/check-published-release.mjs +4 -1
- package/scripts/cockpit-smoke-test.sh +3 -1
- package/scripts/cockpit-smoke.mjs +85 -7
- package/src/cli.js +2 -1
- package/src/global/dashboard-guidance.js +146 -17
- package/src/global/ink/cockpit/primitives.js +16 -9
- package/src/global/ink/cockpit-controller.js +84 -44
- package/src/global/ink/cockpit-focus.js +92 -0
- package/src/global/ink/cockpit-home.js +115 -0
- package/src/global/ink/cockpit-models.js +162 -60
- package/src/global/ink/cockpit-views.js +92 -40
- package/src/global/ink/launch-input.js +13 -3
- package/src/global/ink/orchestrator-app.js +132 -50
- package/src/global/ink/orchestrator-state.js +71 -14
- package/src/global/ink/theme.js +7 -1
- package/src/global/ink/use-orchestrator-data.js +6 -2
|
@@ -3,38 +3,60 @@ import { Box, Text } from "ink";
|
|
|
3
3
|
import { COCKPIT_COLORS } from "./theme.js";
|
|
4
4
|
import { CockpitEmptyState } from "./cockpit/primitives.js";
|
|
5
5
|
import {
|
|
6
|
-
formatDiagnosticsLines,
|
|
7
|
-
formatLaunchWizardLines,
|
|
8
6
|
formatProviderLines,
|
|
9
7
|
formatRunDetailLines,
|
|
10
8
|
formatRunLines,
|
|
9
|
+
formatSystemHealthLines,
|
|
10
|
+
formatLaunchWizardLines,
|
|
11
11
|
ORCHESTRATOR_VIEWS,
|
|
12
12
|
LAUNCH_WIZARD_STEPS
|
|
13
13
|
} from "./orchestrator-state.js";
|
|
14
|
+
import { windowLinesForLayout } from "./cockpit-models.js";
|
|
15
|
+
import { LAYOUT_MODES } from "./layout.js";
|
|
14
16
|
|
|
15
17
|
export function HomeMissionPanel({ model, colorEnabled = true }) {
|
|
18
|
+
const readiness = model.readiness;
|
|
16
19
|
return React.createElement(Box, { flexDirection: "column" },
|
|
17
20
|
React.createElement(Text, {
|
|
18
21
|
bold: true,
|
|
19
22
|
color: colorEnabled ? COCKPIT_COLORS.secondary : undefined
|
|
20
23
|
}, model.title),
|
|
21
|
-
React.createElement(Text,
|
|
24
|
+
React.createElement(Text, null, model.purpose),
|
|
25
|
+
React.createElement(Text, null, ""),
|
|
26
|
+
React.createElement(Box, { flexDirection: "column" },
|
|
27
|
+
React.createElement(Text, {
|
|
28
|
+
bold: true,
|
|
29
|
+
color: colorEnabled ? COCKPIT_COLORS.primary : undefined
|
|
30
|
+
}, readiness.headline),
|
|
31
|
+
React.createElement(Text, null, readiness.summaryLine),
|
|
32
|
+
...(readiness.capabilityLines ?? []).map((line) =>
|
|
33
|
+
React.createElement(Text, { key: line, color: COCKPIT_COLORS.muted }, line)
|
|
34
|
+
),
|
|
35
|
+
React.createElement(Text, null, "")
|
|
36
|
+
),
|
|
37
|
+
React.createElement(Text, { bold: true }, model.next.title),
|
|
22
38
|
React.createElement(Text, {
|
|
39
|
+
bold: true,
|
|
23
40
|
color: colorEnabled ? COCKPIT_COLORS.primary : undefined
|
|
24
|
-
}, model.
|
|
41
|
+
}, model.next.actionTitle),
|
|
42
|
+
React.createElement(Text, null, model.next.actionDetail),
|
|
43
|
+
React.createElement(Text, { color: COCKPIT_COLORS.muted }, model.next.enterHint),
|
|
25
44
|
React.createElement(Text, null, ""),
|
|
26
|
-
React.createElement(Text, { bold: true }, model.
|
|
27
|
-
model.emptyHint
|
|
45
|
+
React.createElement(Text, { bold: true }, model.recent.title),
|
|
46
|
+
model.recent.emptyHint
|
|
28
47
|
? React.createElement(CockpitEmptyState, {
|
|
29
|
-
message: model.emptyHint,
|
|
30
|
-
hint: "Enter
|
|
48
|
+
message: model.recent.emptyHint,
|
|
49
|
+
hint: "Enter opens the recommended next step."
|
|
31
50
|
})
|
|
32
|
-
:
|
|
33
|
-
React.createElement(Text,
|
|
51
|
+
: React.createElement(Box, { flexDirection: "column" },
|
|
52
|
+
React.createElement(Text, null, model.recent.headline),
|
|
53
|
+
React.createElement(Text, { color: COCKPIT_COLORS.muted }, model.recent.hint)
|
|
34
54
|
),
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
55
|
+
React.createElement(Text, null, ""),
|
|
56
|
+
React.createElement(Text, { bold: true }, model.explore.title),
|
|
57
|
+
model.explore.lines.map((line) =>
|
|
58
|
+
React.createElement(Text, { key: line, color: COCKPIT_COLORS.muted }, line)
|
|
59
|
+
)
|
|
38
60
|
);
|
|
39
61
|
}
|
|
40
62
|
|
|
@@ -51,6 +73,7 @@ export function renderCockpitView({
|
|
|
51
73
|
selectedRun,
|
|
52
74
|
selectedEvents,
|
|
53
75
|
homeMission,
|
|
76
|
+
layoutMode = LAYOUT_MODES.COMPACT,
|
|
54
77
|
colorEnabled = true
|
|
55
78
|
}) {
|
|
56
79
|
switch (view) {
|
|
@@ -58,34 +81,52 @@ export function renderCockpitView({
|
|
|
58
81
|
return React.createElement(HomeMissionPanel, { model: homeMission, colorEnabled });
|
|
59
82
|
case ORCHESTRATOR_VIEWS.ACTIVE_RUNS:
|
|
60
83
|
return listBlock(
|
|
61
|
-
"
|
|
62
|
-
formatRunLines(dashboard?.activeRuns ?? [], {
|
|
84
|
+
"Running now",
|
|
85
|
+
formatRunLines(dashboard?.activeRuns ?? [], {
|
|
86
|
+
emptyMessage: "No runs are executing. Open New run to start one.",
|
|
87
|
+
readable: true
|
|
88
|
+
}),
|
|
63
89
|
listIndex,
|
|
64
|
-
colorEnabled
|
|
90
|
+
colorEnabled,
|
|
91
|
+
"Nothing is running yet — that means Kairo is idle, not broken."
|
|
65
92
|
);
|
|
66
93
|
case ORCHESTRATOR_VIEWS.RECENT_RUNS:
|
|
67
94
|
return listBlock(
|
|
68
|
-
"
|
|
69
|
-
formatRunLines(dashboard?.recentRuns ?? [], {
|
|
95
|
+
"History",
|
|
96
|
+
formatRunLines(dashboard?.recentRuns ?? [], {
|
|
97
|
+
emptyMessage: "No completed runs yet. Create a new run to build history.",
|
|
98
|
+
readable: true
|
|
99
|
+
}),
|
|
70
100
|
listIndex,
|
|
71
|
-
colorEnabled
|
|
101
|
+
colorEnabled,
|
|
102
|
+
"History fills after supervised runs finish."
|
|
72
103
|
);
|
|
73
|
-
case ORCHESTRATOR_VIEWS.PROVIDERS:
|
|
104
|
+
case ORCHESTRATOR_VIEWS.PROVIDERS: {
|
|
105
|
+
const lines = formatProviderLines(dashboard?.providers ?? []);
|
|
106
|
+
const windowed = windowLinesForLayout(lines, layoutMode);
|
|
74
107
|
return React.createElement(Box, { flexDirection: "column" },
|
|
75
|
-
React.createElement(Text, { bold: true }, "
|
|
76
|
-
|
|
77
|
-
|
|
108
|
+
React.createElement(Text, { bold: true }, "Agents"),
|
|
109
|
+
lines.length === 0
|
|
110
|
+
? React.createElement(CockpitEmptyState, {
|
|
111
|
+
message: "No agents detected yet.",
|
|
112
|
+
hint: "Open System health or finish setup, then return here."
|
|
113
|
+
})
|
|
114
|
+
: windowed.items.map((line) => React.createElement(Text, { key: line }, line)),
|
|
115
|
+
windowed.moreLine && React.createElement(Text, {
|
|
116
|
+
color: COCKPIT_COLORS.muted
|
|
117
|
+
}, windowed.moreLine)
|
|
78
118
|
);
|
|
119
|
+
}
|
|
79
120
|
case ORCHESTRATOR_VIEWS.LAUNCH:
|
|
80
121
|
if (launchableAgents.length === 0) {
|
|
81
122
|
return React.createElement(CockpitEmptyState, {
|
|
82
|
-
title: "
|
|
83
|
-
message: "No
|
|
84
|
-
hint: "Esc to return ·
|
|
123
|
+
title: "New run",
|
|
124
|
+
message: "No executable agents are ready. Kairo cannot start a run yet.",
|
|
125
|
+
hint: "Esc to return · open System health or Agents"
|
|
85
126
|
});
|
|
86
127
|
}
|
|
87
128
|
return React.createElement(Box, { flexDirection: "column" },
|
|
88
|
-
React.createElement(Text, { bold: true }, "
|
|
129
|
+
React.createElement(Text, { bold: true }, "New run"),
|
|
89
130
|
formatLaunchWizardLines({
|
|
90
131
|
step: launchStep,
|
|
91
132
|
draft: launchDraft,
|
|
@@ -105,18 +146,23 @@ export function renderCockpitView({
|
|
|
105
146
|
formatRunDetailLines(selectedRun, selectedEvents)
|
|
106
147
|
.map((line) => React.createElement(Text, { key: line }, line))
|
|
107
148
|
);
|
|
108
|
-
case ORCHESTRATOR_VIEWS.DIAGNOSTICS:
|
|
149
|
+
case ORCHESTRATOR_VIEWS.DIAGNOSTICS: {
|
|
150
|
+
const lines = formatSystemHealthLines(diagnostics);
|
|
151
|
+
const windowed = windowLinesForLayout(lines, layoutMode);
|
|
109
152
|
return React.createElement(Box, { flexDirection: "column" },
|
|
110
|
-
React.createElement(Text, { bold: true }, "
|
|
111
|
-
|
|
112
|
-
|
|
153
|
+
React.createElement(Text, { bold: true }, "System health"),
|
|
154
|
+
windowed.items.map((line) => React.createElement(Text, { key: line }, line)),
|
|
155
|
+
windowed.moreLine && React.createElement(Text, {
|
|
156
|
+
color: COCKPIT_COLORS.muted
|
|
157
|
+
}, windowed.moreLine)
|
|
113
158
|
);
|
|
159
|
+
}
|
|
114
160
|
case ORCHESTRATOR_VIEWS.HELP:
|
|
115
161
|
return React.createElement(Box, { flexDirection: "column" },
|
|
116
162
|
React.createElement(Text, { bold: true }, "Help"),
|
|
117
|
-
React.createElement(Text, null, "Kairo
|
|
118
|
-
React.createElement(Text, null, "↑↓ navigate ·
|
|
119
|
-
React.createElement(Text, null, "R refresh · C cancel
|
|
163
|
+
React.createElement(Text, null, "Kairo coordinates installed AI agents with controlled, auditable runs."),
|
|
164
|
+
React.createElement(Text, null, "↑↓ navigate · Enter open recommended or selected section · Esc back/exit"),
|
|
165
|
+
React.createElement(Text, null, "Tab region (lists / New run) · R refresh/retry · C cancel · ? help"),
|
|
120
166
|
React.createElement(Text, null, "CLI: kairo run --agent <id> --task \"...\""),
|
|
121
167
|
React.createElement(Text, null, "Audit trail: ~/.harness/runs/<runId>/")
|
|
122
168
|
);
|
|
@@ -127,14 +173,20 @@ export function renderCockpitView({
|
|
|
127
173
|
}
|
|
128
174
|
}
|
|
129
175
|
|
|
130
|
-
function listBlock(title, lines, listIndex, colorEnabled) {
|
|
176
|
+
function listBlock(title, lines, listIndex, colorEnabled, emptyHint) {
|
|
177
|
+
const isEmpty = lines.length === 1 && /no |nothing |empty/i.test(lines[0]);
|
|
131
178
|
return React.createElement(Box, { flexDirection: "column" },
|
|
132
179
|
React.createElement(Text, { bold: true }, title),
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
180
|
+
isEmpty
|
|
181
|
+
? React.createElement(CockpitEmptyState, {
|
|
182
|
+
message: lines[0],
|
|
183
|
+
hint: emptyHint
|
|
184
|
+
})
|
|
185
|
+
: lines.map((line, index) => React.createElement(Text, {
|
|
186
|
+
key: `${index}-${line}`,
|
|
187
|
+
bold: index === listIndex,
|
|
188
|
+
color: index === listIndex && colorEnabled ? COCKPIT_COLORS.primary : undefined
|
|
189
|
+
}, `${index === listIndex ? "› " : " "}${line}`))
|
|
138
190
|
);
|
|
139
191
|
}
|
|
140
192
|
|
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
} from "./orchestrator-state.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Launch-wizard key handler.
|
|
8
|
+
* Launch-wizard key handler.
|
|
9
|
+
* Returns true when consumed, "retreated" when Esc stepped back, false otherwise.
|
|
9
10
|
*/
|
|
10
11
|
export function handleLaunchInput(ctx) {
|
|
11
12
|
const {
|
|
@@ -22,9 +23,18 @@ export function handleLaunchInput(ctx) {
|
|
|
22
23
|
setLaunchPermissionIndex,
|
|
23
24
|
setError,
|
|
24
25
|
handleLaunch,
|
|
25
|
-
reload
|
|
26
|
+
reload,
|
|
27
|
+
allowEscapeRetreat = false
|
|
26
28
|
} = ctx;
|
|
27
29
|
|
|
30
|
+
if (allowEscapeRetreat && key.escape) {
|
|
31
|
+
if (launchStep === LAUNCH_WIZARD_STEPS.AGENT) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
setLaunchStep(retreatLaunchWizardStep(launchStep));
|
|
35
|
+
return "retreated";
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
if (launchStep === LAUNCH_WIZARD_STEPS.AGENT) {
|
|
29
39
|
if (key.upArrow) {
|
|
30
40
|
setLaunchAgentIndex((index) => Math.max(0, index - 1));
|
|
@@ -87,7 +97,7 @@ export function handleLaunchInput(ctx) {
|
|
|
87
97
|
}
|
|
88
98
|
if (key.escape) {
|
|
89
99
|
setLaunchStep(retreatLaunchWizardStep(launchStep));
|
|
90
|
-
return true;
|
|
100
|
+
return allowEscapeRetreat ? "retreated" : true;
|
|
91
101
|
}
|
|
92
102
|
}
|
|
93
103
|
|
|
@@ -8,9 +8,12 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
createCockpitUiState,
|
|
10
10
|
reduceCockpitUi,
|
|
11
|
-
resolveNavAction
|
|
11
|
+
resolveNavAction,
|
|
12
|
+
routeCockpitKey,
|
|
13
|
+
isContentInteractiveView
|
|
12
14
|
} from "./cockpit-controller.js";
|
|
13
15
|
import {
|
|
16
|
+
COCKPIT_NAV,
|
|
14
17
|
COCKPIT_REGIONS,
|
|
15
18
|
buildFooterModel,
|
|
16
19
|
buildHomeMissionModel,
|
|
@@ -19,6 +22,8 @@ import {
|
|
|
19
22
|
buildTopBarModel,
|
|
20
23
|
resolveProjectName
|
|
21
24
|
} from "./cockpit-models.js";
|
|
25
|
+
import { openRecommendedDestination } from "./cockpit-home.js";
|
|
26
|
+
import { resolveProjectReadiness } from "../dashboard-guidance.js";
|
|
22
27
|
import { CockpitShell } from "./cockpit/primitives.js";
|
|
23
28
|
import { renderCockpitView } from "./cockpit-views.js";
|
|
24
29
|
import { handleLaunchInput } from "./launch-input.js";
|
|
@@ -64,8 +69,61 @@ export function OrchestratorApp({
|
|
|
64
69
|
exit();
|
|
65
70
|
};
|
|
66
71
|
|
|
72
|
+
const openDestination = (recommendation) => {
|
|
73
|
+
const destination = openRecommendedDestination(recommendation, { navItems: COCKPIT_NAV });
|
|
74
|
+
if (!destination) return false;
|
|
75
|
+
if (destination.action === "launch") {
|
|
76
|
+
data.resetLaunchWizard();
|
|
77
|
+
}
|
|
78
|
+
dispatch({
|
|
79
|
+
type: "set-view",
|
|
80
|
+
view: destination.view,
|
|
81
|
+
navIndex: destination.navIndex ?? undefined
|
|
82
|
+
});
|
|
83
|
+
return true;
|
|
84
|
+
};
|
|
85
|
+
|
|
67
86
|
useInput((inputKey, key) => {
|
|
87
|
+
if (data.loading) return;
|
|
88
|
+
|
|
89
|
+
if (data.error) {
|
|
90
|
+
if (key.escape) {
|
|
91
|
+
finish({ cancelled: true });
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (inputKey.toLowerCase() === "r") {
|
|
95
|
+
data.setError(null);
|
|
96
|
+
data.reload().catch((reloadError) => {
|
|
97
|
+
data.setError(reloadError instanceof Error ? reloadError.message : String(reloadError));
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (data.busy) return;
|
|
104
|
+
|
|
68
105
|
if (key.escape) {
|
|
106
|
+
if (ui.view === ORCHESTRATOR_VIEWS.LAUNCH && data.launchableAgents.length > 0) {
|
|
107
|
+
const retreated = handleLaunchInput({
|
|
108
|
+
key,
|
|
109
|
+
inputKey,
|
|
110
|
+
launchStep: data.launchStep,
|
|
111
|
+
launchDraft: data.launchDraft,
|
|
112
|
+
launchableAgents: data.launchableAgents,
|
|
113
|
+
launchAgentIndex: data.launchAgentIndex,
|
|
114
|
+
launchPermissionIndex: data.launchPermissionIndex,
|
|
115
|
+
setLaunchAgentIndex: data.setLaunchAgentIndex,
|
|
116
|
+
setLaunchDraft: data.setLaunchDraft,
|
|
117
|
+
setLaunchStep: data.setLaunchStep,
|
|
118
|
+
setLaunchPermissionIndex: data.setLaunchPermissionIndex,
|
|
119
|
+
setError: data.setError,
|
|
120
|
+
handleLaunch: (draft) => data.handleLaunch(draft, data.dashboard?.profile, dispatch),
|
|
121
|
+
reload: data.reload,
|
|
122
|
+
allowEscapeRetreat: true
|
|
123
|
+
});
|
|
124
|
+
if (retreated === "retreated") return;
|
|
125
|
+
}
|
|
126
|
+
|
|
69
127
|
const next = reduceCockpitUi(ui, { type: "escape" });
|
|
70
128
|
if (next.shouldExit) {
|
|
71
129
|
finish({ cancelled: true });
|
|
@@ -77,14 +135,54 @@ export function OrchestratorApp({
|
|
|
77
135
|
return;
|
|
78
136
|
}
|
|
79
137
|
|
|
80
|
-
if (data.loading || data.error || data.busy) return;
|
|
81
|
-
|
|
82
138
|
if (inputKey === "?") {
|
|
83
139
|
dispatch({ type: "toggle-help" });
|
|
84
140
|
return;
|
|
85
141
|
}
|
|
142
|
+
|
|
143
|
+
const listLength = ui.view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
|
|
144
|
+
? (data.dashboard?.activeRuns ?? []).length
|
|
145
|
+
: ui.view === ORCHESTRATOR_VIEWS.RECENT_RUNS
|
|
146
|
+
? (data.dashboard?.recentRuns ?? []).length
|
|
147
|
+
: 0;
|
|
148
|
+
|
|
149
|
+
let routed = null;
|
|
86
150
|
if (key.tab) {
|
|
87
|
-
|
|
151
|
+
routed = routeCockpitKey(ui, { type: "tab" });
|
|
152
|
+
} else if (key.upArrow || key.downArrow) {
|
|
153
|
+
routed = routeCockpitKey(ui, {
|
|
154
|
+
type: "arrow",
|
|
155
|
+
direction: key.upArrow ? "up" : "down",
|
|
156
|
+
listLength
|
|
157
|
+
});
|
|
158
|
+
} else if (key.return) {
|
|
159
|
+
routed = routeCockpitKey(ui, { type: "enter" });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (routed) {
|
|
163
|
+
if (routed.type === "enter-nav") {
|
|
164
|
+
const item = resolveNavAction(ui.navIndex);
|
|
165
|
+
if (item?.id === "overview" || item?.view === ORCHESTRATOR_VIEWS.HOME) {
|
|
166
|
+
const mission = buildHomeMissionModel({
|
|
167
|
+
projectName: resolveProjectName(workspaceRoot),
|
|
168
|
+
hasGlobalState,
|
|
169
|
+
diagnostics: data.diagnostics,
|
|
170
|
+
dashboard: data.dashboard,
|
|
171
|
+
layoutMode: ui.layoutMode ?? LAYOUT_MODES.COMPACT
|
|
172
|
+
});
|
|
173
|
+
if (openDestination(mission.next)) return;
|
|
174
|
+
}
|
|
175
|
+
if (item?.action === "launch") {
|
|
176
|
+
data.resetLaunchWizard();
|
|
177
|
+
dispatch({
|
|
178
|
+
type: "set-view",
|
|
179
|
+
view: ORCHESTRATOR_VIEWS.LAUNCH,
|
|
180
|
+
navIndex: ui.navIndex
|
|
181
|
+
});
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
dispatch(routed);
|
|
88
186
|
return;
|
|
89
187
|
}
|
|
90
188
|
|
|
@@ -109,22 +207,14 @@ export function OrchestratorApp({
|
|
|
109
207
|
}
|
|
110
208
|
}
|
|
111
209
|
|
|
112
|
-
if (ui.
|
|
210
|
+
if (ui.region === COCKPIT_REGIONS.CONTENT
|
|
211
|
+
&& (ui.view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS || ui.view === ORCHESTRATOR_VIEWS.RECENT_RUNS)
|
|
212
|
+
&& key.return) {
|
|
113
213
|
const runs = ui.view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
|
|
114
214
|
? data.dashboard?.activeRuns ?? []
|
|
115
215
|
: data.dashboard?.recentRuns ?? [];
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
type: "arrow",
|
|
119
|
-
direction: key.upArrow ? "up" : "down",
|
|
120
|
-
listLength: runs.length
|
|
121
|
-
});
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
if (key.return) {
|
|
125
|
-
data.openRunDetail(selectRunFromList(runs, ui.listIndex), dispatch);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
216
|
+
data.openRunDetail(selectRunFromList(runs, ui.listIndex), dispatch, ui.view);
|
|
217
|
+
return;
|
|
128
218
|
}
|
|
129
219
|
|
|
130
220
|
if (ui.view === ORCHESTRATOR_VIEWS.RUN_DETAIL) {
|
|
@@ -133,7 +223,7 @@ export function OrchestratorApp({
|
|
|
133
223
|
return;
|
|
134
224
|
}
|
|
135
225
|
if (inputKey.toLowerCase() === "r") {
|
|
136
|
-
data.openRunDetail(data.selectedRun, dispatch);
|
|
226
|
+
data.openRunDetail(data.selectedRun, dispatch, ui.returnView);
|
|
137
227
|
return;
|
|
138
228
|
}
|
|
139
229
|
}
|
|
@@ -142,23 +232,6 @@ export function OrchestratorApp({
|
|
|
142
232
|
data.reload().catch((reloadError) => {
|
|
143
233
|
data.setError(reloadError instanceof Error ? reloadError.message : String(reloadError));
|
|
144
234
|
});
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
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" });
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
if (key.return) {
|
|
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" });
|
|
161
|
-
}
|
|
162
235
|
}
|
|
163
236
|
});
|
|
164
237
|
|
|
@@ -173,13 +246,26 @@ export function OrchestratorApp({
|
|
|
173
246
|
return React.createElement(Box, { flexDirection: "column" },
|
|
174
247
|
React.createElement(Text, { bold: true, color: COCKPIT_COLORS.danger }, "Runtime error"),
|
|
175
248
|
React.createElement(Text, null, data.error),
|
|
176
|
-
React.createElement(Text, { dimColor: true }, "Esc to exit")
|
|
249
|
+
React.createElement(Text, { dimColor: true }, "R Retry · Esc to exit")
|
|
177
250
|
);
|
|
178
251
|
}
|
|
179
252
|
|
|
180
253
|
const mode = ui.layoutMode ?? LAYOUT_MODES.COMPACT;
|
|
181
254
|
const colorEnabled = caps.color;
|
|
182
255
|
const unicode = caps.unicode;
|
|
256
|
+
const projectName = resolveProjectName(workspaceRoot);
|
|
257
|
+
const readiness = resolveProjectReadiness({
|
|
258
|
+
hasGlobalState,
|
|
259
|
+
diagnostics: data.diagnostics,
|
|
260
|
+
dashboard: data.dashboard
|
|
261
|
+
});
|
|
262
|
+
const homeMission = buildHomeMissionModel({
|
|
263
|
+
projectName,
|
|
264
|
+
hasGlobalState,
|
|
265
|
+
diagnostics: data.diagnostics,
|
|
266
|
+
dashboard: data.dashboard,
|
|
267
|
+
layoutMode: mode
|
|
268
|
+
});
|
|
183
269
|
|
|
184
270
|
return React.createElement(Box, { flexDirection: "column" },
|
|
185
271
|
data.statusMessage && React.createElement(Text, {
|
|
@@ -187,8 +273,8 @@ export function OrchestratorApp({
|
|
|
187
273
|
}, data.statusMessage),
|
|
188
274
|
React.createElement(CockpitShell, {
|
|
189
275
|
topBar: buildTopBarModel({
|
|
190
|
-
projectName
|
|
191
|
-
systemOnline:
|
|
276
|
+
projectName,
|
|
277
|
+
systemOnline: readiness.kind !== "needs_setup",
|
|
192
278
|
unicode
|
|
193
279
|
}),
|
|
194
280
|
footer: buildFooterModel({
|
|
@@ -201,13 +287,16 @@ export function OrchestratorApp({
|
|
|
201
287
|
layoutMode: mode,
|
|
202
288
|
nav: buildNavModel({
|
|
203
289
|
navIndex: ui.navIndex,
|
|
204
|
-
|
|
205
|
-
|
|
290
|
+
currentView: ui.view,
|
|
291
|
+
focused: ui.region === COCKPIT_REGIONS.NAV || !isContentInteractiveView(ui.view),
|
|
292
|
+
unicode,
|
|
293
|
+
dashboard: data.dashboard,
|
|
294
|
+
diagnostics: data.diagnostics
|
|
206
295
|
}),
|
|
207
296
|
system: buildSystemStripModel({
|
|
208
297
|
dashboard: data.dashboard,
|
|
209
298
|
diagnostics: data.diagnostics,
|
|
210
|
-
|
|
299
|
+
readiness
|
|
211
300
|
}),
|
|
212
301
|
navFocused: ui.region === COCKPIT_REGIONS.NAV,
|
|
213
302
|
contentFocused: ui.region === COCKPIT_REGIONS.CONTENT,
|
|
@@ -226,15 +315,8 @@ export function OrchestratorApp({
|
|
|
226
315
|
launchableAgents: data.launchableAgents,
|
|
227
316
|
selectedRun: data.selectedRun,
|
|
228
317
|
selectedEvents: data.selectedEvents,
|
|
229
|
-
homeMission
|
|
230
|
-
|
|
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
|
-
}),
|
|
318
|
+
homeMission,
|
|
319
|
+
layoutMode: mode,
|
|
238
320
|
colorEnabled
|
|
239
321
|
})
|
|
240
322
|
)
|
|
@@ -23,11 +23,11 @@ export const ORCHESTRATOR_VIEWS = {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
export const ORCHESTRATOR_MENU = [
|
|
26
|
-
{ id: "active", label: "
|
|
27
|
-
{ id: "recent", label: "
|
|
28
|
-
{ id: "providers", label: "
|
|
29
|
-
{ id: "launch", label: "
|
|
30
|
-
{ id: "diagnostics", label: "
|
|
26
|
+
{ id: "active", label: "Running now", view: ORCHESTRATOR_VIEWS.ACTIVE_RUNS },
|
|
27
|
+
{ id: "recent", label: "History", view: ORCHESTRATOR_VIEWS.RECENT_RUNS },
|
|
28
|
+
{ id: "providers", label: "Agents", view: ORCHESTRATOR_VIEWS.PROVIDERS },
|
|
29
|
+
{ id: "launch", label: "New run", view: ORCHESTRATOR_VIEWS.LAUNCH, action: "launch" },
|
|
30
|
+
{ id: "diagnostics", label: "System health", view: ORCHESTRATOR_VIEWS.DIAGNOSTICS },
|
|
31
31
|
{ id: "help", label: "Help", view: ORCHESTRATOR_VIEWS.HELP }
|
|
32
32
|
];
|
|
33
33
|
|
|
@@ -157,13 +157,22 @@ export function shiftMenuIndex(currentIndex, direction, menuLength = ORCHESTRATO
|
|
|
157
157
|
return Math.min(menuLength - 1, Math.max(0, currentIndex + delta));
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
export function formatRunLines(runs, {
|
|
160
|
+
export function formatRunLines(runs, {
|
|
161
|
+
emptyMessage = "No runs.",
|
|
162
|
+
readable = false
|
|
163
|
+
} = {}) {
|
|
161
164
|
if (!runs || runs.length === 0) return [emptyMessage];
|
|
162
165
|
|
|
163
166
|
return runs.map((run) => {
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
+
const agent = humanizeToken(run.agentId);
|
|
168
|
+
const state = humanizeRunState(run.state);
|
|
169
|
+
const task = formatTaskLabel(run);
|
|
170
|
+
if (readable) {
|
|
171
|
+
return `${agent} · ${state} · ${task}`;
|
|
172
|
+
}
|
|
173
|
+
const statePad = run.state.padEnd(12);
|
|
174
|
+
const agentPad = run.agentId.padEnd(10);
|
|
175
|
+
return `${run.runId} ${statePad} ${agentPad} ${task}`;
|
|
167
176
|
});
|
|
168
177
|
}
|
|
169
178
|
|
|
@@ -181,17 +190,42 @@ export function formatProviderLines(providers) {
|
|
|
181
190
|
}
|
|
182
191
|
|
|
183
192
|
export function formatDiagnosticsLines(diagnostics) {
|
|
193
|
+
return formatSystemHealthLines(diagnostics);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function formatSystemHealthLines(diagnostics) {
|
|
184
197
|
const summary = diagnostics?.diagnostics;
|
|
198
|
+
const intelligence = diagnostics?.intelligence?.summary;
|
|
199
|
+
const profile = diagnostics?.profile;
|
|
200
|
+
const capabilities = diagnostics?.capabilities ?? [];
|
|
201
|
+
const authKnown = capabilities.filter((entry) => entry.authenticated != null);
|
|
202
|
+
const authReady = authKnown.filter((entry) => entry.authenticated).length;
|
|
203
|
+
|
|
185
204
|
const lines = [
|
|
186
|
-
"
|
|
187
|
-
`
|
|
188
|
-
`Agents detected: ${summary?.detected ?? 0}/${diagnostics?.capabilities?.length ?? 0}`,
|
|
205
|
+
"Agents",
|
|
206
|
+
`Detected: ${summary?.detected ?? 0}/${capabilities.length}`,
|
|
189
207
|
`Available: ${summary?.available ?? 0}`,
|
|
190
208
|
`Unknown: ${summary?.unknown ?? 0}`,
|
|
191
209
|
`Errors: ${summary?.errors ?? 0}`,
|
|
210
|
+
...formatAgentStatusLines(capabilities),
|
|
211
|
+
"",
|
|
212
|
+
"Intelligence",
|
|
213
|
+
intelligence?.localAvailable
|
|
214
|
+
? "Local backend available"
|
|
215
|
+
: "Local backend unavailable",
|
|
216
|
+
intelligence?.cloudAuthenticated
|
|
217
|
+
? "Cloud backend authenticated"
|
|
218
|
+
: "Cloud backend not configured",
|
|
219
|
+
`Routing: ${diagnostics?.intelligence?.routingPreview?.reason ?? "n/a"}`,
|
|
220
|
+
"",
|
|
221
|
+
"Authentication",
|
|
222
|
+
authKnown.length === 0
|
|
223
|
+
? "No agent authentication signals yet"
|
|
224
|
+
: `${authReady}/${authKnown.length} agents report ready auth`,
|
|
192
225
|
"",
|
|
193
|
-
"
|
|
194
|
-
|
|
226
|
+
"Configuration",
|
|
227
|
+
`CLI version: ${diagnostics?.cliVersion ?? "unknown"}`,
|
|
228
|
+
`Profile sources: ${(profile?.sources ?? []).join(", ") || "none"}`
|
|
195
229
|
];
|
|
196
230
|
|
|
197
231
|
const recommendations = diagnostics?.recommendations ?? [];
|
|
@@ -265,6 +299,29 @@ export function isRunCancellable(run) {
|
|
|
265
299
|
return run && isActiveRunState(run.state);
|
|
266
300
|
}
|
|
267
301
|
|
|
302
|
+
function humanizeToken(value) {
|
|
303
|
+
const raw = String(value ?? "agent");
|
|
304
|
+
return raw.charAt(0).toUpperCase() + raw.slice(1);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function humanizeRunState(state) {
|
|
308
|
+
switch (state) {
|
|
309
|
+
case "succeeded":
|
|
310
|
+
case "completed":
|
|
311
|
+
return "Succeeded";
|
|
312
|
+
case "failed":
|
|
313
|
+
return "Failed";
|
|
314
|
+
case "cancelled":
|
|
315
|
+
case "canceled":
|
|
316
|
+
return "Cancelled";
|
|
317
|
+
case "running":
|
|
318
|
+
case "starting":
|
|
319
|
+
return "Running";
|
|
320
|
+
default:
|
|
321
|
+
return humanizeToken(state);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
268
325
|
function summarizeEvent(event) {
|
|
269
326
|
if (event.type === "agent.tool_call") {
|
|
270
327
|
return event.data?.tool_name ?? event.data?.name ?? "tool";
|
package/src/global/ink/theme.js
CHANGED
|
@@ -19,7 +19,10 @@ export const STATUS_LABELS = {
|
|
|
19
19
|
offline: "Offline",
|
|
20
20
|
local: "Local",
|
|
21
21
|
online: "ONLINE",
|
|
22
|
-
loading: "Loading"
|
|
22
|
+
loading: "Loading",
|
|
23
|
+
needs_setup: "Needs setup",
|
|
24
|
+
needs_attention: "Needs attention",
|
|
25
|
+
limited: "Limited"
|
|
23
26
|
};
|
|
24
27
|
|
|
25
28
|
export const COCKPIT_GLYPHS = {
|
|
@@ -55,9 +58,12 @@ export function statusColor(kind, { colorEnabled = true } = {}) {
|
|
|
55
58
|
return COCKPIT_COLORS.success;
|
|
56
59
|
case "warn":
|
|
57
60
|
case "warning":
|
|
61
|
+
case "needs_setup":
|
|
62
|
+
case "limited":
|
|
58
63
|
return COCKPIT_COLORS.warning;
|
|
59
64
|
case "error":
|
|
60
65
|
case "danger":
|
|
66
|
+
case "needs_attention":
|
|
61
67
|
return COCKPIT_COLORS.danger;
|
|
62
68
|
case "offline":
|
|
63
69
|
case "muted":
|