@kal-elsam/kairo-runtime 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -15
- package/package.json +1 -1
- package/scripts/cockpit-smoke.mjs +6 -6
- package/scripts/ux-prototype-tty.mjs +9 -0
- package/src/cli.js +24 -1
- package/src/global/control-plane-proposals.js +2 -1
- package/src/global/control-plane-snapshot.js +1 -1
- package/src/global/global-doctor.js +2 -0
- package/src/global/ink/cockpit/primitives.js +127 -50
- package/src/global/ink/cockpit-alerts.js +36 -0
- package/src/global/ink/cockpit-changes.js +61 -37
- package/src/global/ink/cockpit-control-center.js +79 -53
- package/src/global/ink/cockpit-controller.js +98 -15
- package/src/global/ink/cockpit-enter.js +1 -0
- package/src/global/ink/cockpit-focus.js +4 -2
- package/src/global/ink/cockpit-models.js +100 -51
- package/src/global/ink/cockpit-palette.js +109 -0
- package/src/global/ink/cockpit-path-label.js +19 -0
- package/src/global/ink/cockpit-recovery.js +84 -18
- package/src/global/ink/cockpit-reviews.js +14 -10
- package/src/global/ink/cockpit-runs.js +13 -4
- package/src/global/ink/cockpit-settings.js +194 -0
- package/src/global/ink/cockpit-usage.js +111 -0
- package/src/global/ink/cockpit-views.js +119 -116
- package/src/global/ink/orchestrator-app.js +169 -46
- package/src/global/ink/orchestrator-state.js +24 -14
- package/src/global/ink/setup-app.js +55 -72
- package/src/global/ink/setup-state.js +16 -0
- package/src/global/ink/theme.js +27 -0
- package/src/global/ink/use-orchestrator-data.js +58 -0
- package/src/global/ink/ux/live-activity.js +194 -0
- package/src/global/ink/ux/live-alerts.js +159 -0
- package/src/global/ink/ux/live-governance.js +195 -0
- package/src/global/ink/ux/live-orchestration.js +188 -0
- package/src/global/ink/ux/live-overview.js +125 -0
- package/src/global/ink/ux/live-settings.js +189 -0
- package/src/global/ink/ux/live-setup.js +160 -0
- package/src/global/ink/ux/live-usage.js +51 -0
- package/src/global/ink/ux/semantic.js +84 -0
- package/src/global/ink/ux/task-flow-app.js +85 -0
- package/src/global/ink/ux/task-flow.js +173 -0
- package/src/global/orchestrator.js +37 -20
- package/src/global/paths.js +3 -0
- package/src/global/runtime/alerts/alert-store.js +216 -0
- package/src/global/runtime/alerts/alert-types.js +59 -0
- package/src/global/runtime/alerts/alert-validate.js +117 -0
- package/src/global/runtime/monitor/monitor-cli.js +62 -0
- package/src/global/runtime/monitor/monitor-platform.js +95 -0
- package/src/global/runtime/monitor/monitor.js +249 -0
|
@@ -13,19 +13,26 @@ import {
|
|
|
13
13
|
isContentInteractiveView
|
|
14
14
|
} from "./cockpit-controller.js";
|
|
15
15
|
import {
|
|
16
|
-
COCKPIT_NAV,
|
|
17
16
|
COCKPIT_REGIONS,
|
|
18
17
|
buildFooterModel,
|
|
19
18
|
buildNavModel,
|
|
20
|
-
buildSystemStripModel,
|
|
21
19
|
buildTopBarModel,
|
|
22
20
|
navIndexForView,
|
|
23
21
|
resolveProjectName
|
|
24
22
|
} from "./cockpit-models.js";
|
|
25
23
|
import { buildControlCenterModel } from "./cockpit-control-center.js";
|
|
24
|
+
import {
|
|
25
|
+
buildPaletteActions,
|
|
26
|
+
buildPaletteModel,
|
|
27
|
+
canOpenPalette,
|
|
28
|
+
PALETTE_KINDS,
|
|
29
|
+
resolvePaletteDestination
|
|
30
|
+
} from "./cockpit-palette.js";
|
|
26
31
|
import { resolveEnterNavIntent } from "./cockpit-enter.js";
|
|
27
32
|
import { resolveRunsHubItem, RUNS_HUB_ITEMS } from "./cockpit-runs.js";
|
|
28
33
|
import { selectReviewFromList } from "./cockpit-reviews.js";
|
|
34
|
+
import { selectAlertFromList } from "./cockpit-alerts.js";
|
|
35
|
+
import { ALERT_STATES } from "../runtime/alerts/alert-types.js";
|
|
29
36
|
import { resolveProjectReadiness } from "../dashboard-guidance.js";
|
|
30
37
|
import { CONTROL_PLANE_HEALTH } from "../control-plane-snapshot.js";
|
|
31
38
|
import { CockpitShell } from "./cockpit/primitives.js";
|
|
@@ -38,6 +45,7 @@ import { COCKPIT_COLORS } from "./theme.js";
|
|
|
38
45
|
import { LAYOUT_MODES } from "./layout.js";
|
|
39
46
|
import { CHANGES_PHASE } from "./cockpit-changes.js";
|
|
40
47
|
import { RECOVERY_PHASE, listRecoverySnapshots } from "./cockpit-recovery.js";
|
|
48
|
+
import { SETTINGS_PHASE } from "./cockpit-settings.js";
|
|
41
49
|
|
|
42
50
|
export function OrchestratorApp({
|
|
43
51
|
homeDir,
|
|
@@ -76,7 +84,7 @@ export function OrchestratorApp({
|
|
|
76
84
|
};
|
|
77
85
|
|
|
78
86
|
const openDestination = (destinationKey) => {
|
|
79
|
-
const view =
|
|
87
|
+
const view = resolvePaletteDestination(destinationKey);
|
|
80
88
|
if (!view) return false;
|
|
81
89
|
dispatch({
|
|
82
90
|
type: "set-view",
|
|
@@ -86,6 +94,15 @@ export function OrchestratorApp({
|
|
|
86
94
|
return true;
|
|
87
95
|
};
|
|
88
96
|
|
|
97
|
+
const confirming = data.changesAction?.phase === CHANGES_PHASE.CONFIRMING
|
|
98
|
+
|| data.recoveryAction?.phase === RECOVERY_PHASE.CONFIRMING
|
|
99
|
+
|| data.settingsAction?.phase === SETTINGS_PHASE.CONFIRMING;
|
|
100
|
+
const paletteActions = buildPaletteActions({
|
|
101
|
+
ctaDestination: data.snapshot?.cta?.destination ?? null,
|
|
102
|
+
ctaTitle: data.snapshot?.cta?.title ?? null,
|
|
103
|
+
ctaDetail: data.snapshot?.cta?.detail ?? null
|
|
104
|
+
});
|
|
105
|
+
|
|
89
106
|
useInput((inputKey, key) => {
|
|
90
107
|
if (data.loading) return;
|
|
91
108
|
|
|
@@ -104,6 +121,68 @@ export function OrchestratorApp({
|
|
|
104
121
|
|
|
105
122
|
if (data.busy) return;
|
|
106
123
|
|
|
124
|
+
if (ui.paletteOpen) {
|
|
125
|
+
if (key.escape) {
|
|
126
|
+
dispatch({ type: "close-palette" });
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (inputKey === "/") {
|
|
130
|
+
dispatch({ type: "toggle-palette" });
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (key.upArrow || key.downArrow) {
|
|
134
|
+
dispatch({
|
|
135
|
+
type: "palette-arrow",
|
|
136
|
+
direction: key.upArrow ? "up" : "down",
|
|
137
|
+
listLength: paletteActions.length
|
|
138
|
+
});
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (key.return) {
|
|
142
|
+
const selected = paletteActions[ui.paletteIndex] ?? null;
|
|
143
|
+
if (!selected) return;
|
|
144
|
+
if (selected.kind === PALETTE_KINDS.REFRESH) {
|
|
145
|
+
dispatch({ type: "run-palette", kind: selected.kind });
|
|
146
|
+
data.reload().catch(() => {});
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (selected.kind === PALETTE_KINDS.SETUP) {
|
|
150
|
+
finish({ cancelled: false, action: "setup" });
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
dispatch({
|
|
154
|
+
type: "run-palette",
|
|
155
|
+
kind: selected.kind,
|
|
156
|
+
view: selected.view
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (inputKey === "/"
|
|
163
|
+
&& canOpenPalette({ loading: data.loading, busy: data.busy, confirming })) {
|
|
164
|
+
dispatch({ type: "toggle-palette" });
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (inputKey === " " && ui.view === ORCHESTRATOR_VIEWS.HOME && !ui.paletteOpen) {
|
|
169
|
+
dispatch({ type: "toggle-overview-details" });
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (inputKey === " " && ui.view === ORCHESTRATOR_VIEWS.CHANGES && !ui.paletteOpen) {
|
|
174
|
+
dispatch({ type: "toggle-governance-details" });
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (inputKey === " "
|
|
179
|
+
&& ui.view === ORCHESTRATOR_VIEWS.ACTIVITY
|
|
180
|
+
&& !ui.paletteOpen
|
|
181
|
+
&& data.recoveryAction?.preview) {
|
|
182
|
+
dispatch({ type: "toggle-activity-details" });
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
107
186
|
if (key.escape) {
|
|
108
187
|
if (ui.view === ORCHESTRATOR_VIEWS.CHANGES
|
|
109
188
|
&& data.changesAction?.phase === CHANGES_PHASE.CONFIRMING) {
|
|
@@ -115,6 +194,18 @@ export function OrchestratorApp({
|
|
|
115
194
|
data.cancelRecovery();
|
|
116
195
|
return;
|
|
117
196
|
}
|
|
197
|
+
if (ui.view === ORCHESTRATOR_VIEWS.PROFILE) {
|
|
198
|
+
const settingsPhase = data.settingsAction?.phase;
|
|
199
|
+
if (settingsPhase === SETTINGS_PHASE.CONFIRMING) {
|
|
200
|
+
data.cancelSettings();
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (settingsPhase === SETTINGS_PHASE.PREVIEW
|
|
204
|
+
|| settingsPhase === SETTINGS_PHASE.COMPLETED) {
|
|
205
|
+
data.resetSettings();
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
118
209
|
|
|
119
210
|
if (ui.view === ORCHESTRATOR_VIEWS.LAUNCH && data.launchableAgents.length > 0) {
|
|
120
211
|
const retreated = handleLaunchInput({
|
|
@@ -162,9 +253,15 @@ export function OrchestratorApp({
|
|
|
162
253
|
? (data.dashboard?.recentRuns ?? []).length
|
|
163
254
|
: ui.view === ORCHESTRATOR_VIEWS.REVIEWS
|
|
164
255
|
? (data.reviews ?? []).length
|
|
165
|
-
: ui.view === ORCHESTRATOR_VIEWS.
|
|
166
|
-
?
|
|
167
|
-
|
|
256
|
+
: ui.view === ORCHESTRATOR_VIEWS.ALERTS
|
|
257
|
+
? (Array.isArray(data.alerts) ? data.alerts : [])
|
|
258
|
+
.filter((alert) => alert.state === ALERT_STATES.OPEN).length
|
|
259
|
+
: ui.view === ORCHESTRATOR_VIEWS.ACTIVITY
|
|
260
|
+
? listRecoverySnapshots(data.snapshot).length
|
|
261
|
+
: ui.view === ORCHESTRATOR_VIEWS.PROFILE
|
|
262
|
+
&& data.settingsAction?.phase === SETTINGS_PHASE.BROWSE
|
|
263
|
+
? data.curatedIntegrations.length
|
|
264
|
+
: 0;
|
|
168
265
|
|
|
169
266
|
let routed = null;
|
|
170
267
|
if (key.tab) {
|
|
@@ -187,6 +284,10 @@ export function OrchestratorApp({
|
|
|
187
284
|
navItem: item,
|
|
188
285
|
ctaDestination: data.snapshot?.cta?.destination ?? null
|
|
189
286
|
});
|
|
287
|
+
if (intent.kind === "activate-setup") {
|
|
288
|
+
finish({ cancelled: false, action: "setup" });
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
190
291
|
if (intent.kind === "activate-cta") {
|
|
191
292
|
if (openDestination(intent.destination)) return;
|
|
192
293
|
}
|
|
@@ -261,6 +362,19 @@ export function OrchestratorApp({
|
|
|
261
362
|
return;
|
|
262
363
|
}
|
|
263
364
|
|
|
365
|
+
if (ui.region === COCKPIT_REGIONS.CONTENT
|
|
366
|
+
&& ui.view === ORCHESTRATOR_VIEWS.ALERTS) {
|
|
367
|
+
const selected = selectAlertFromList(data.alerts, ui.listIndex);
|
|
368
|
+
if (key.return) {
|
|
369
|
+
data.handleAlertTransition(selected, "resolve").catch(() => {});
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (inputKey.toLowerCase() === "d") {
|
|
373
|
+
data.handleAlertTransition(selected, "dismiss").catch(() => {});
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
264
378
|
if (ui.view === ORCHESTRATOR_VIEWS.RUN_DETAIL) {
|
|
265
379
|
if (inputKey.toLowerCase() === "c" && isRunCancellable(data.selectedRun)) {
|
|
266
380
|
data.handleCancelRun();
|
|
@@ -317,6 +431,30 @@ export function OrchestratorApp({
|
|
|
317
431
|
}
|
|
318
432
|
}
|
|
319
433
|
|
|
434
|
+
if (ui.view === ORCHESTRATOR_VIEWS.PROFILE) {
|
|
435
|
+
const keyName = inputKey.toLowerCase();
|
|
436
|
+
const phase = data.settingsAction?.phase ?? SETTINGS_PHASE.BROWSE;
|
|
437
|
+
if (key.return && ui.region === COCKPIT_REGIONS.CONTENT) {
|
|
438
|
+
if (phase === SETTINGS_PHASE.BROWSE) {
|
|
439
|
+
const entry = data.curatedIntegrations[ui.listIndex];
|
|
440
|
+
if (entry?.id) data.previewSettings(entry.id);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (phase === SETTINGS_PHASE.PREVIEW) {
|
|
444
|
+
data.promptConfirmSettings();
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (keyName === "y" && phase === SETTINGS_PHASE.CONFIRMING) {
|
|
449
|
+
data.confirmSettings();
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
if (keyName === "n" && phase === SETTINGS_PHASE.CONFIRMING) {
|
|
453
|
+
data.cancelSettings();
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
320
458
|
if (inputKey.toLowerCase() === "r" && ui.view !== ORCHESTRATOR_VIEWS.LAUNCH) {
|
|
321
459
|
if (ui.view === ORCHESTRATOR_VIEWS.REVIEWS || ui.view === ORCHESTRATOR_VIEWS.REVIEW_DETAIL) {
|
|
322
460
|
data.loadReviews().catch(() => {});
|
|
@@ -354,7 +492,9 @@ export function OrchestratorApp({
|
|
|
354
492
|
const controlCenter = buildControlCenterModel({
|
|
355
493
|
projectName,
|
|
356
494
|
snapshot: data.snapshot,
|
|
357
|
-
|
|
495
|
+
dashboard: data.dashboard,
|
|
496
|
+
layoutMode: mode,
|
|
497
|
+
alerts: data.alerts
|
|
358
498
|
});
|
|
359
499
|
const systemOnline = data.snapshot
|
|
360
500
|
? data.snapshot.health !== CONTROL_PLANE_HEALTH.NOT_CONFIGURED
|
|
@@ -376,12 +516,17 @@ export function OrchestratorApp({
|
|
|
376
516
|
region: ui.region,
|
|
377
517
|
navIndex: ui.navIndex,
|
|
378
518
|
helpOpen: ui.helpOpen,
|
|
519
|
+
paletteOpen: ui.paletteOpen,
|
|
379
520
|
canCancel: isRunCancellable(data.selectedRun),
|
|
380
521
|
unicode,
|
|
381
522
|
changesPhase: data.changesAction?.phase ?? null,
|
|
382
|
-
recoveryPhase: data.recoveryAction?.phase ?? null
|
|
523
|
+
recoveryPhase: data.recoveryAction?.phase ?? null,
|
|
524
|
+
recoveryHasPreview: Boolean(data.recoveryAction?.preview),
|
|
525
|
+
settingsPhase: data.settingsAction?.phase ?? null,
|
|
526
|
+
columns
|
|
383
527
|
}),
|
|
384
528
|
layoutMode: mode,
|
|
529
|
+
columns,
|
|
385
530
|
nav: buildNavModel({
|
|
386
531
|
navIndex: ui.navIndex,
|
|
387
532
|
currentView: ui.view,
|
|
@@ -391,24 +536,8 @@ export function OrchestratorApp({
|
|
|
391
536
|
diagnostics: data.diagnostics,
|
|
392
537
|
snapshot: data.snapshot
|
|
393
538
|
}),
|
|
394
|
-
system: buildSystemStripModel({
|
|
395
|
-
dashboard: data.dashboard,
|
|
396
|
-
diagnostics: data.diagnostics,
|
|
397
|
-
readiness: data.snapshot
|
|
398
|
-
? {
|
|
399
|
-
kind: data.snapshot.health.toLowerCase(),
|
|
400
|
-
label: controlCenter.health.label,
|
|
401
|
-
healthKind: data.snapshot.health === CONTROL_PLANE_HEALTH.HEALTHY
|
|
402
|
-
? "ready"
|
|
403
|
-
: data.snapshot.health === CONTROL_PLANE_HEALTH.CHECK_FAILED
|
|
404
|
-
? "error"
|
|
405
|
-
: "warn"
|
|
406
|
-
}
|
|
407
|
-
: readiness
|
|
408
|
-
}),
|
|
409
539
|
navFocused: ui.region === COCKPIT_REGIONS.NAV,
|
|
410
540
|
contentFocused: ui.region === COCKPIT_REGIONS.CONTENT,
|
|
411
|
-
systemFocused: ui.region === COCKPIT_REGIONS.SYSTEM,
|
|
412
541
|
colorEnabled
|
|
413
542
|
},
|
|
414
543
|
renderCockpitView({
|
|
@@ -426,33 +555,27 @@ export function OrchestratorApp({
|
|
|
426
555
|
selectedEvents: data.selectedEvents,
|
|
427
556
|
reviews: data.reviews,
|
|
428
557
|
selectedReview: data.selectedReview,
|
|
558
|
+
alerts: data.alerts,
|
|
429
559
|
changesAction: data.changesAction,
|
|
430
560
|
recoveryAction: data.recoveryAction,
|
|
561
|
+
settingsAction: data.settingsAction,
|
|
431
562
|
controlCenter,
|
|
563
|
+
palette: ui.paletteOpen
|
|
564
|
+
? buildPaletteModel({
|
|
565
|
+
actions: paletteActions,
|
|
566
|
+
index: ui.paletteIndex,
|
|
567
|
+
unicode
|
|
568
|
+
})
|
|
569
|
+
: null,
|
|
432
570
|
layoutMode: mode,
|
|
433
|
-
colorEnabled
|
|
571
|
+
colorEnabled,
|
|
572
|
+
unicode,
|
|
573
|
+
overviewDetailsOpen: ui.overviewDetailsOpen,
|
|
574
|
+
governanceDetailsOpen: ui.governanceDetailsOpen,
|
|
575
|
+
activityDetailsOpen: ui.activityDetailsOpen,
|
|
576
|
+
contentFocused: ui.region === COCKPIT_REGIONS.CONTENT,
|
|
577
|
+
homeDir
|
|
434
578
|
})
|
|
435
579
|
)
|
|
436
580
|
);
|
|
437
581
|
}
|
|
438
|
-
|
|
439
|
-
function resolveCtaDestinationView(destinationKey) {
|
|
440
|
-
switch (destinationKey) {
|
|
441
|
-
case "changes":
|
|
442
|
-
return ORCHESTRATOR_VIEWS.CHANGES;
|
|
443
|
-
case "control-center":
|
|
444
|
-
return ORCHESTRATOR_VIEWS.HOME;
|
|
445
|
-
case "ides":
|
|
446
|
-
return ORCHESTRATOR_VIEWS.IDES;
|
|
447
|
-
case "modules":
|
|
448
|
-
return ORCHESTRATOR_VIEWS.MODULES;
|
|
449
|
-
case "activity":
|
|
450
|
-
return ORCHESTRATOR_VIEWS.ACTIVITY;
|
|
451
|
-
case "profile":
|
|
452
|
-
return ORCHESTRATOR_VIEWS.PROFILE;
|
|
453
|
-
case "runs":
|
|
454
|
-
return ORCHESTRATOR_VIEWS.RUNS;
|
|
455
|
-
default:
|
|
456
|
-
return null;
|
|
457
|
-
}
|
|
458
|
-
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { stdin as input, stdout as output } from "node:process";
|
|
2
2
|
import { canUseSetupInk } from "./terminal.js";
|
|
3
3
|
import { isActiveRunState, formatTaskLabel } from "../runtime/run-types.js";
|
|
4
|
+
import { formatConfirmPath } from "./cockpit-path-label.js";
|
|
4
5
|
|
|
5
6
|
export function canUseOrchestratorShell({
|
|
6
7
|
interactive = Boolean(input.isTTY && output.isTTY),
|
|
@@ -27,7 +28,9 @@ export const ORCHESTRATOR_VIEWS = {
|
|
|
27
28
|
PROVIDERS: "providers",
|
|
28
29
|
LAUNCH: "launch",
|
|
29
30
|
DIAGNOSTICS: "diagnostics",
|
|
30
|
-
HELP: "help"
|
|
31
|
+
HELP: "help",
|
|
32
|
+
USAGE: "usage",
|
|
33
|
+
ALERTS: "alerts"
|
|
31
34
|
};
|
|
32
35
|
|
|
33
36
|
export const ORCHESTRATOR_MENU = [
|
|
@@ -175,8 +178,9 @@ export function formatRunLines(runs, {
|
|
|
175
178
|
const agent = humanizeToken(run.agentId);
|
|
176
179
|
const state = humanizeRunState(run.state);
|
|
177
180
|
const task = formatTaskLabel(run);
|
|
181
|
+
const strategy = run.strategy ? ` · ${run.strategy}` : "";
|
|
178
182
|
if (readable) {
|
|
179
|
-
return `${agent} · ${state} · ${task}`;
|
|
183
|
+
return `${agent} · ${state}${strategy} · ${task}`;
|
|
180
184
|
}
|
|
181
185
|
const statePad = run.state.padEnd(12);
|
|
182
186
|
const agentPad = run.agentId.padEnd(10);
|
|
@@ -270,23 +274,29 @@ export function formatAgentStatusLines(capabilities) {
|
|
|
270
274
|
});
|
|
271
275
|
}
|
|
272
276
|
|
|
273
|
-
export function formatRunDetailLines(run, events = []) {
|
|
277
|
+
export function formatRunDetailLines(run, events = [], { homeDir = null } = {}) {
|
|
274
278
|
if (!run) return ["Run not found."];
|
|
275
279
|
|
|
276
280
|
const lines = [
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
`
|
|
280
|
-
`
|
|
281
|
-
`Cwd: ${run.cwd}`,
|
|
282
|
-
`Started: ${run.startedAt}`,
|
|
283
|
-
`Updated: ${run.updatedAt}`
|
|
281
|
+
"SUMMARY",
|
|
282
|
+
`${humanizeToken(run.agentId)} · ${humanizeRunState(run.state)}${run.strategy ? ` · ${run.strategy}` : ""}`,
|
|
283
|
+
`Model · ${run.model ?? "default"}`,
|
|
284
|
+
`Started · ${run.startedAt ?? "n/a"}`
|
|
284
285
|
];
|
|
286
|
+
if (run.completedAt) lines.push(`Completed · ${run.completedAt}`);
|
|
287
|
+
if (run.tokenUsage && typeof run.tokenUsage === "object") {
|
|
288
|
+
const parts = [];
|
|
289
|
+
if (Number.isFinite(run.tokenUsage.input)) parts.push(`in ${run.tokenUsage.input}`);
|
|
290
|
+
if (Number.isFinite(run.tokenUsage.output)) parts.push(`out ${run.tokenUsage.output}`);
|
|
291
|
+
if (Number.isFinite(run.tokenUsage.total)) parts.push(`total ${run.tokenUsage.total}`);
|
|
292
|
+
if (parts.length > 0) lines.push(`Tokens · ${parts.join(" · ")}`);
|
|
293
|
+
}
|
|
294
|
+
if (run.error) lines.push(`Error · ${run.error}`);
|
|
285
295
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
if (run.
|
|
289
|
-
if (run.tools?.length) lines.push(`Tools
|
|
296
|
+
lines.push("", "DETAILS");
|
|
297
|
+
lines.push(`Run id · ${run.runId}`);
|
|
298
|
+
if (run.cwd) lines.push(`Cwd · ${formatConfirmPath(run.cwd, homeDir)}`);
|
|
299
|
+
if (run.tools?.length) lines.push(`Tools · ${run.tools.join(", ")}`);
|
|
290
300
|
|
|
291
301
|
if (events.length > 0) {
|
|
292
302
|
lines.push("", "Recent events");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useState } from "react";
|
|
2
2
|
import { Box, Text, useApp, useInput } from "ink";
|
|
3
3
|
import { stdout as output } from "node:process";
|
|
4
|
-
import { BRAND
|
|
4
|
+
import { BRAND } from "../brand/index.js";
|
|
5
5
|
import { DEFAULT_COMPONENT_IDS, describeComponentCatalog } from "../component-registry.js";
|
|
6
6
|
import { GLOBAL_AGENT_IDS, detectInstalledAdapters, listAdapters } from "../registry.js";
|
|
7
7
|
import { buildSetupPreview, resolveComponentSelection } from "../clack/setup-preview.js";
|
|
@@ -9,72 +9,57 @@ import {
|
|
|
9
9
|
SETUP_STEPS,
|
|
10
10
|
buildAgentOptions,
|
|
11
11
|
buildComponentOptions,
|
|
12
|
-
formatInkDetectPanel,
|
|
13
12
|
formatInkHeaderLines,
|
|
14
|
-
formatInkPreviewLines,
|
|
15
|
-
formatInkSelectList,
|
|
16
13
|
formatInkSplashLines,
|
|
17
14
|
INITIAL_SETUP_STEP,
|
|
18
15
|
shouldStartPreviewLoad,
|
|
19
16
|
shouldUseCompactSplashLogo,
|
|
17
|
+
setupLineKey,
|
|
20
18
|
toggleComponentSelection,
|
|
21
19
|
toggleSelection,
|
|
22
20
|
transitionFromSplash
|
|
23
21
|
} from "./setup-state.js";
|
|
24
|
-
import { COCKPIT_COLORS } from "./theme.js";
|
|
25
|
-
import {
|
|
22
|
+
import { COCKPIT_COLORS, resolveInkColor } from "./theme.js";
|
|
23
|
+
import { useTerminalSize } from "./use-terminal-size.js";
|
|
24
|
+
import { resolveTerminalCapabilities } from "./terminal-capabilities.js";
|
|
25
|
+
import { SemanticSetupPanel } from "./ux/live-setup.js";
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
accent: COCKPIT_COLORS.primary,
|
|
29
|
-
success: COCKPIT_COLORS.success,
|
|
30
|
-
warning: COCKPIT_COLORS.warning,
|
|
31
|
-
danger: COCKPIT_COLORS.danger,
|
|
32
|
-
muted: COCKPIT_COLORS.muted
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
function Header() {
|
|
27
|
+
export function SetupHeader({ colorEnabled = true }) {
|
|
36
28
|
const lines = formatInkHeaderLines();
|
|
37
29
|
return React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
|
|
38
|
-
React.createElement(Text, {
|
|
39
|
-
|
|
30
|
+
React.createElement(Text, {
|
|
31
|
+
bold: true,
|
|
32
|
+
color: resolveInkColor(colorEnabled, COCKPIT_COLORS.primary)
|
|
33
|
+
}, `╭─ ${lines[0]}`),
|
|
34
|
+
React.createElement(Text, {
|
|
35
|
+
color: resolveInkColor(colorEnabled, COCKPIT_COLORS.secondary)
|
|
36
|
+
}, lines[1]),
|
|
40
37
|
React.createElement(Text, { dimColor: true }, lines[2])
|
|
41
38
|
);
|
|
42
39
|
}
|
|
43
40
|
|
|
44
|
-
function
|
|
45
|
-
return React.createElement(CockpitPanel, {
|
|
46
|
-
title,
|
|
47
|
-
focused: true,
|
|
48
|
-
width: "100%"
|
|
49
|
-
}, children);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function Footer({ children }) {
|
|
53
|
-
return React.createElement(Text, { dimColor: true }, children);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function Splash({ compact, onboarding = false }) {
|
|
41
|
+
export function SetupSplash({ compact, onboarding = false, colorEnabled = true }) {
|
|
57
42
|
const lines = formatInkSplashLines({ compact, onboarding });
|
|
58
43
|
const logoLineCount = compact ? BRAND.compactLogo.length : BRAND.asciiLogo.length;
|
|
44
|
+
const accent = resolveInkColor(colorEnabled, COCKPIT_COLORS.primary);
|
|
45
|
+
const muted = resolveInkColor(colorEnabled, COCKPIT_COLORS.muted);
|
|
59
46
|
|
|
60
47
|
return React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
|
|
61
48
|
lines.map((line, index) => {
|
|
49
|
+
const key = setupLineKey(index, line);
|
|
62
50
|
if (index < logoLineCount) {
|
|
63
|
-
return React.createElement(Text, { key
|
|
51
|
+
return React.createElement(Text, { key, bold: true, color: accent }, line);
|
|
64
52
|
}
|
|
65
53
|
if (line === BRAND.name) {
|
|
66
|
-
return React.createElement(Text, { key
|
|
54
|
+
return React.createElement(Text, { key, bold: true, color: accent }, line);
|
|
67
55
|
}
|
|
68
56
|
if (line === BRAND.tagline) {
|
|
69
|
-
return React.createElement(Text, { key
|
|
57
|
+
return React.createElement(Text, { key, color: muted }, line);
|
|
70
58
|
}
|
|
71
59
|
if (line === BRAND.splashHint || line.includes("Esc to exit") || line.includes("Press Enter")) {
|
|
72
|
-
return React.createElement(Text, { key
|
|
73
|
-
}
|
|
74
|
-
if (line === "") {
|
|
75
|
-
return React.createElement(Text, { key: `line-${index}` }, "");
|
|
60
|
+
return React.createElement(Text, { key, dimColor: true }, line);
|
|
76
61
|
}
|
|
77
|
-
return React.createElement(Text, { key
|
|
62
|
+
return React.createElement(Text, { key }, line);
|
|
78
63
|
})
|
|
79
64
|
);
|
|
80
65
|
}
|
|
@@ -90,6 +75,12 @@ export function SetupApp({
|
|
|
90
75
|
onComplete
|
|
91
76
|
}) {
|
|
92
77
|
const { exit } = useApp();
|
|
78
|
+
const { columns, rows, layoutMode } = useTerminalSize({
|
|
79
|
+
initialColumns: output.columns ?? 80,
|
|
80
|
+
initialRows: output.rows ?? 24
|
|
81
|
+
});
|
|
82
|
+
const caps = resolveTerminalCapabilities({ columns, rows, isTTY: true });
|
|
83
|
+
const colorEnabled = caps.color;
|
|
93
84
|
const adapters = listAdapters();
|
|
94
85
|
const detected = detectInstalledAdapters({ homeDir });
|
|
95
86
|
const componentCatalog = describeComponentCatalog({ workspaceRoot });
|
|
@@ -98,7 +89,7 @@ export function SetupApp({
|
|
|
98
89
|
const defaultAgents = detected.length > 0 ? detected : [...GLOBAL_AGENT_IDS];
|
|
99
90
|
|
|
100
91
|
const [step, setStep] = useState(INITIAL_SETUP_STEP);
|
|
101
|
-
const useCompactSplash = shouldUseCompactSplashLogo(
|
|
92
|
+
const useCompactSplash = shouldUseCompactSplashLogo(columns);
|
|
102
93
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
103
94
|
const [selectedAgents, setSelectedAgents] = useState(defaultAgents);
|
|
104
95
|
const [selectedComponents, setSelectedComponents] = useState([...DEFAULT_COMPONENT_IDS]);
|
|
@@ -238,42 +229,34 @@ export function SetupApp({
|
|
|
238
229
|
}
|
|
239
230
|
});
|
|
240
231
|
|
|
241
|
-
const detectPanel = formatInkDetectPanel({ adapters, detected });
|
|
242
|
-
|
|
243
232
|
return React.createElement(Box, { flexDirection: "column" },
|
|
244
|
-
step === SETUP_STEPS.SPLASH && React.createElement(
|
|
233
|
+
step === SETUP_STEPS.SPLASH && React.createElement(SetupSplash, {
|
|
245
234
|
compact: useCompactSplash,
|
|
246
|
-
onboarding
|
|
235
|
+
onboarding,
|
|
236
|
+
colorEnabled
|
|
247
237
|
}),
|
|
248
|
-
step
|
|
249
|
-
|
|
250
|
-
detectPanel.split("\n")
|
|
251
|
-
.map((line) => React.createElement(Text, { key: line }, line))
|
|
252
|
-
),
|
|
253
|
-
step === SETUP_STEPS.AGENTS && React.createElement(Panel, { title: WIZARD_COPY.agentsPrompt },
|
|
254
|
-
formatInkSelectList({ options: agentOptions, selected: selectedAgents, activeIndex })
|
|
255
|
-
.map((line) => React.createElement(Text, { key: line }, line))
|
|
256
|
-
),
|
|
257
|
-
step === SETUP_STEPS.COMPONENTS && React.createElement(Panel, { title: WIZARD_COPY.componentsPrompt },
|
|
258
|
-
formatInkSelectList({ options: componentOptions, selected: selectedComponents, activeIndex })
|
|
259
|
-
.map((line) => React.createElement(Text, { key: line }, line))
|
|
238
|
+
step === SETUP_STEPS.SPLASH && React.createElement(Text, { dimColor: true },
|
|
239
|
+
`${BRAND.splashHint} · Esc cancel`
|
|
260
240
|
),
|
|
261
|
-
step
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
241
|
+
step !== SETUP_STEPS.SPLASH && React.createElement(SetupHeader, { colorEnabled }),
|
|
242
|
+
step !== SETUP_STEPS.SPLASH && React.createElement(SemanticSetupPanel, {
|
|
243
|
+
step,
|
|
244
|
+
activeIndex,
|
|
245
|
+
agentOptions,
|
|
246
|
+
componentOptions,
|
|
247
|
+
componentCatalog,
|
|
248
|
+
selectedAgents,
|
|
249
|
+
selectedComponents,
|
|
250
|
+
adapters,
|
|
251
|
+
detected,
|
|
252
|
+
preview,
|
|
253
|
+
previewLoading,
|
|
254
|
+
previewError,
|
|
255
|
+
dryRun,
|
|
256
|
+
layoutMode,
|
|
257
|
+
colorEnabled,
|
|
258
|
+
unicode: caps.unicode,
|
|
259
|
+
columns
|
|
260
|
+
})
|
|
278
261
|
);
|
|
279
262
|
}
|
|
@@ -178,6 +178,22 @@ export function formatInkPreviewLines({ preview, componentCatalog }) {
|
|
|
178
178
|
return lines;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
/** Cap preview lines for the active layout; keep remaining-count when truncated. */
|
|
182
|
+
export function setupPreviewLineLimit(layoutMode = "compact") {
|
|
183
|
+
if (layoutMode === "wide") return 12;
|
|
184
|
+
if (layoutMode === "minimal") return 5;
|
|
185
|
+
return 8;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function windowSetupLines(lines = [], limit = 8) {
|
|
189
|
+
if (lines.length <= limit) return lines;
|
|
190
|
+
return [...lines.slice(0, limit), `… ${lines.length - limit} more`];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function setupLineKey(index, line) {
|
|
194
|
+
return `${index}-${line}`;
|
|
195
|
+
}
|
|
196
|
+
|
|
181
197
|
export function formatInkSuccessLines(result, { dryRun = false, cliName = PREFERRED_CLI } = {}) {
|
|
182
198
|
const agentLine = result.agents.map((id) => getAgentLabel(id)).join(", ");
|
|
183
199
|
const componentLine = result.components.length > 0
|
package/src/global/ink/theme.js
CHANGED
|
@@ -73,6 +73,33 @@ export function statusColor(kind, { colorEnabled = true } = {}) {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/** Ink `color` / `borderColor` prop — undefined when color is disabled. */
|
|
77
|
+
export function resolveInkColor(colorEnabled, color) {
|
|
78
|
+
return colorEnabled ? color : undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Detect ANSI color SGR in a string.
|
|
83
|
+
* Bold (1), dim (2), and reset (0) alone are not color.
|
|
84
|
+
*/
|
|
85
|
+
export function hasColorSgr(text) {
|
|
86
|
+
const re = /\u001b\[([0-9;]*)m/g;
|
|
87
|
+
let match;
|
|
88
|
+
while ((match = re.exec(String(text ?? ""))) !== null) {
|
|
89
|
+
const params = match[1].length === 0
|
|
90
|
+
? []
|
|
91
|
+
: match[1].split(";").map((part) => Number(part));
|
|
92
|
+
for (let i = 0; i < params.length; i += 1) {
|
|
93
|
+
const code = params[i];
|
|
94
|
+
if (!Number.isFinite(code)) continue;
|
|
95
|
+
if (code === 38 || code === 48) return true;
|
|
96
|
+
if ((code >= 30 && code <= 37) || (code >= 90 && code <= 97)) return true;
|
|
97
|
+
if ((code >= 40 && code <= 47) || (code >= 100 && code <= 107)) return true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
76
103
|
export function formatStatusBadge(kind, label = STATUS_LABELS[kind] ?? String(kind)) {
|
|
77
104
|
return label;
|
|
78
105
|
}
|