@kal-elsam/kairo-runtime 0.9.0 → 0.11.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 +14 -8
- package/package.json +1 -1
- package/scripts/cockpit-smoke.mjs +3 -3
- package/scripts/ux-prototype-tty.mjs +9 -0
- package/src/global/control-plane-proposals.js +2 -1
- package/src/global/control-plane-snapshot.js +1 -1
- package/src/global/ink/brand/wordmark.js +50 -0
- package/src/global/ink/cockpit/primitives.js +72 -50
- package/src/global/ink/cockpit-changes.js +2 -2
- package/src/global/ink/cockpit-control-center.js +12 -64
- package/src/global/ink/cockpit-controller.js +41 -5
- package/src/global/ink/cockpit-enter.js +1 -0
- package/src/global/ink/cockpit-models.js +14 -7
- package/src/global/ink/cockpit-palette.js +18 -7
- package/src/global/ink/cockpit-recovery.js +9 -6
- package/src/global/ink/cockpit-usage.js +111 -0
- package/src/global/ink/cockpit-views.js +70 -97
- package/src/global/ink/orchestrator-app.js +44 -4
- package/src/global/ink/setup-app.js +55 -72
- package/src/global/ink/setup-state.js +16 -0
- package/src/global/ink/theme.js +40 -8
- package/src/global/ink/ux/live-activity.js +191 -0
- package/src/global/ink/ux/live-alerts.js +156 -0
- package/src/global/ink/ux/live-governance.js +191 -0
- package/src/global/ink/ux/live-orchestration.js +185 -0
- package/src/global/ink/ux/live-overview.js +175 -0
- package/src/global/ink/ux/live-settings.js +186 -0
- package/src/global/ink/ux/live-setup.js +160 -0
- package/src/global/ink/ux/live-usage.js +49 -0
- package/src/global/ink/ux/semantic.js +101 -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/README.md
CHANGED
|
@@ -16,8 +16,11 @@ as a technical alias).
|
|
|
16
16
|
Kairo Runtime and prints a migration warning. Prefer `@kal-elsam/kairo-runtime` and the
|
|
17
17
|
`kairo` CLI for new installs. See [`packages/harness-bridge/README.md`](packages/harness-bridge/README.md).
|
|
18
18
|
|
|
19
|
-
Terminal UX
|
|
20
|
-
|
|
19
|
+
Terminal UX is **terminal-first**: bare `kairo` opens the Ink Control Center (or Setup
|
|
20
|
+
when unconfigured). Explicit commands (`setup`, `status`, `sync`, `doctor`, `monitor`,
|
|
21
|
+
`shell`, …) keep their current behavior. Progressive disclosure keeps paths and IDs in
|
|
22
|
+
Details, list caps honest (`… N more`), and Callout reserved for status — never for
|
|
23
|
+
primary values. No web loopback / `kairo ui` in this release.
|
|
21
24
|
|
|
22
25
|
- **npm:** https://www.npmjs.com/package/@kal-elsam/kairo-runtime
|
|
23
26
|
- **repo:** https://github.com/Kal-elSam/harness
|
|
@@ -32,13 +35,11 @@ npx @kal-elsam/kairo-runtime
|
|
|
32
35
|
kairo
|
|
33
36
|
```
|
|
34
37
|
|
|
35
|
-
**First run** (no `~/.harness/state.json`):
|
|
36
|
-
|
|
38
|
+
**First run** (no `~/.harness/state.json`): semantic Setup (Detect → Agents →
|
|
39
|
+
Components → Preview → Confirm) → full-screen Control Center.
|
|
37
40
|
|
|
38
|
-
**Later runs** (state present):
|
|
39
|
-
|
|
40
|
-
governance (scan → evidence → preview → confirm → apply → re-scan → recovery).
|
|
41
|
-
Runs stay secondary. Layout adapts to terminal size:
|
|
41
|
+
**Later runs** (state present): semantic Cockpit — Overview, Governance, Activity,
|
|
42
|
+
Orchestration, Usage, Settings — plus Alerts inbox. Layout adapts to terminal size:
|
|
42
43
|
|
|
43
44
|
| Mode | Size | Layout |
|
|
44
45
|
|------|------|--------|
|
|
@@ -73,6 +74,11 @@ Settings browses a curated catalog (pinned `pi-usage-widget@0.2.1`, MIT).
|
|
|
73
74
|
Preview → confirm shows an in-session confirmation receipt (`wroteFiles: false`);
|
|
74
75
|
it does not persist or install anything.
|
|
75
76
|
|
|
77
|
+
### Usage evidence
|
|
78
|
+
|
|
79
|
+
Usage shows measured budget pairs, profile limits, and finite run `tokenUsage`
|
|
80
|
+
fields only. It never invents totals, costs, or savings.
|
|
81
|
+
|
|
76
82
|
Respects `NO_COLOR`, `HARNESS_ASCII=1`, and `HARNESS_INK=0`. Status is always labeled
|
|
77
83
|
in text, never color alone.
|
|
78
84
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kal-elsam/kairo-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Kairo Runtime — local agent operating system for Codex, Cursor, Claude, Pi, Engram, and Graphify.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://github.com/Kal-elSam/harness#readme",
|
|
@@ -26,7 +26,7 @@ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
|
26
26
|
const require = createRequire(import.meta.url);
|
|
27
27
|
const pkg = require(join(root, "package.json"));
|
|
28
28
|
|
|
29
|
-
assert.equal(pkg.version, "0.
|
|
29
|
+
assert.equal(pkg.version, "0.11.0");
|
|
30
30
|
assert.ok(pkg.dependencies["ansi-escapes"]);
|
|
31
31
|
|
|
32
32
|
assert.equal(resolveLayoutMode({ columns: 120, rows: 40 }), LAYOUT_MODES.WIDE);
|
|
@@ -132,7 +132,7 @@ const proposalLines = formatProposalLines([
|
|
|
132
132
|
id: "setup-local",
|
|
133
133
|
severity: "high",
|
|
134
134
|
title: "Finish local setup",
|
|
135
|
-
destination: "
|
|
135
|
+
destination: "setup",
|
|
136
136
|
evidence: [{ type: "status", source: "status.overall", ref: "missing" }]
|
|
137
137
|
}
|
|
138
138
|
], {
|
|
@@ -143,7 +143,7 @@ const proposalLines = formatProposalLines([
|
|
|
143
143
|
requestBudgetTokens: 10
|
|
144
144
|
}
|
|
145
145
|
});
|
|
146
|
-
assert.match(proposalLines.join("\n"), /\[HIGH\] Finish local setup →
|
|
146
|
+
assert.match(proposalLines.join("\n"), /\[HIGH\] Finish local setup → setup/);
|
|
147
147
|
assert.match(proposalLines.join("\n"), /Budget · stable 1\/10/);
|
|
148
148
|
assert.match(proposalLines.join("\n"), /evidence: status\.overall/);
|
|
149
149
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Manual TTY gate: Home → Setup → Overview. No writes. Esc exits. */
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { render } from "ink";
|
|
5
|
+
import { TaskFlowApp } from "../src/global/ink/ux/task-flow-app.js";
|
|
6
|
+
|
|
7
|
+
const app = render(React.createElement(TaskFlowApp, {
|
|
8
|
+
onExit: () => app.unmount()
|
|
9
|
+
}));
|
|
@@ -12,6 +12,7 @@ export const PROPOSAL_SEVERITY = Object.freeze({
|
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
export const PROPOSAL_DESTINATION = Object.freeze({
|
|
15
|
+
SETUP: "setup",
|
|
15
16
|
CHANGES: "changes",
|
|
16
17
|
CONTROL_CENTER: "control-center",
|
|
17
18
|
IDES: "ides",
|
|
@@ -48,7 +49,7 @@ export function buildControlPlaneProposals({
|
|
|
48
49
|
PROPOSAL_SEVERITY.HIGH,
|
|
49
50
|
"Finish local setup",
|
|
50
51
|
status?.nextAction ?? "Configure the local ecosystem before applying repairs.",
|
|
51
|
-
PROPOSAL_DESTINATION.
|
|
52
|
+
PROPOSAL_DESTINATION.SETUP,
|
|
52
53
|
[
|
|
53
54
|
ev("status", "status.overall", status?.overall ?? "missing"),
|
|
54
55
|
ev("health", "control-plane.health", health ?? "NOT_CONFIGURED")
|
|
@@ -66,7 +66,7 @@ export function resolveControlPlaneCta({ health, status, diff } = {}) {
|
|
|
66
66
|
title: "Finish local setup",
|
|
67
67
|
detail: status?.nextAction
|
|
68
68
|
?? `Run ${formatCliCommand("setup")} to configure the local ecosystem.`,
|
|
69
|
-
destination: "
|
|
69
|
+
destination: "setup"
|
|
70
70
|
};
|
|
71
71
|
case CONTROL_PLANE_HEALTH.ACTION_REQUIRED:
|
|
72
72
|
return {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static ASCII wordmark for Kairo Overview — no figlet dependency.
|
|
3
|
+
* Hero art appears only on Overview (wide / compact); minimal stays textual.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { LAYOUT_MODES } from "../layout.js";
|
|
7
|
+
|
|
8
|
+
const WORDMARK_WIDE = [
|
|
9
|
+
"╦╔═╔═╗╦╦═╗╔═╗",
|
|
10
|
+
"╠╩╗╠═╣║╠╦╝║ ║",
|
|
11
|
+
"╩ ╩╩ ╩╩╩╚═╚═╝"
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const WORDMARK_COMPACT = [
|
|
15
|
+
"╦╔═╔═╗╦╦═╗╔═╗",
|
|
16
|
+
"╩ ╩╩ ╩╩╩╚═╚═╝"
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const WORDMARK_WIDE_ASCII = [
|
|
20
|
+
"K A I R O",
|
|
21
|
+
"========="
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const WORDMARK_COMPACT_ASCII = [
|
|
25
|
+
"KAIRO"
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export function shouldShowWordmark(layoutMode) {
|
|
29
|
+
return layoutMode === LAYOUT_MODES.WIDE || layoutMode === LAYOUT_MODES.COMPACT;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {"wide"|"compact"|"minimal"|string} layoutMode
|
|
34
|
+
* @param {{ unicode?: boolean }} [opts]
|
|
35
|
+
* @returns {string[]}
|
|
36
|
+
*/
|
|
37
|
+
export function wordmarkLines(layoutMode, { unicode = true } = {}) {
|
|
38
|
+
if (layoutMode === LAYOUT_MODES.WIDE) {
|
|
39
|
+
return unicode ? WORDMARK_WIDE.slice() : WORDMARK_WIDE_ASCII.slice();
|
|
40
|
+
}
|
|
41
|
+
if (layoutMode === LAYOUT_MODES.COMPACT) {
|
|
42
|
+
return unicode ? WORDMARK_COMPACT.slice() : WORDMARK_COMPACT_ASCII.slice();
|
|
43
|
+
}
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function overviewBrandTitle(layoutMode) {
|
|
48
|
+
if (shouldShowWordmark(layoutMode)) return null;
|
|
49
|
+
return "KAIRO · Overview";
|
|
50
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
|
-
import { COCKPIT_COLORS, statusColor } from "../theme.js";
|
|
3
|
+
import { COCKPIT_COLORS, resolveInkColor, statusColor } from "../theme.js";
|
|
4
4
|
|
|
5
5
|
export function CockpitBadge({ label, kind = "ready", colorEnabled = true }) {
|
|
6
6
|
return React.createElement(Text, {
|
|
@@ -8,36 +8,44 @@ export function CockpitBadge({ label, kind = "ready", colorEnabled = true }) {
|
|
|
8
8
|
}, label);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function CockpitEmptyState({ title, message, hint }) {
|
|
11
|
+
export function CockpitEmptyState({ title, message, hint, colorEnabled = true }) {
|
|
12
12
|
return React.createElement(Box, { flexDirection: "column", marginY: 1 },
|
|
13
|
-
title && React.createElement(Text, {
|
|
13
|
+
title && React.createElement(Text, {
|
|
14
|
+
bold: true,
|
|
15
|
+
color: resolveInkColor(colorEnabled, COCKPIT_COLORS.brand)
|
|
16
|
+
}, title),
|
|
14
17
|
message && React.createElement(Text, null, message),
|
|
15
|
-
hint && React.createElement(Text, {
|
|
18
|
+
hint && React.createElement(Text, {
|
|
19
|
+
color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
|
|
20
|
+
}, hint)
|
|
16
21
|
);
|
|
17
22
|
}
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
/** Content region — no border; focus is conveyed by interactive children. */
|
|
25
|
+
export function CockpitPanel({ title, focused = false, width, children, colorEnabled = true }) {
|
|
20
26
|
return React.createElement(Box, {
|
|
21
27
|
flexDirection: "column",
|
|
22
28
|
width,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
paddingX: 1,
|
|
29
|
+
paddingX: 0,
|
|
30
|
+
marginY: 1,
|
|
26
31
|
flexGrow: 1
|
|
27
32
|
},
|
|
28
33
|
title && React.createElement(Text, {
|
|
29
34
|
bold: true,
|
|
30
|
-
color:
|
|
35
|
+
color: resolveInkColor(
|
|
36
|
+
colorEnabled,
|
|
37
|
+
focused ? COCKPIT_COLORS.interactive : COCKPIT_COLORS.brand
|
|
38
|
+
)
|
|
31
39
|
}, title),
|
|
32
40
|
children
|
|
33
41
|
);
|
|
34
42
|
}
|
|
35
43
|
|
|
36
|
-
export function CockpitSection({ title, children }) {
|
|
44
|
+
export function CockpitSection({ title, children, colorEnabled = true }) {
|
|
37
45
|
return React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
|
|
38
46
|
title && React.createElement(Text, {
|
|
39
47
|
bold: true,
|
|
40
|
-
color: COCKPIT_COLORS.
|
|
48
|
+
color: resolveInkColor(colorEnabled, COCKPIT_COLORS.brand)
|
|
41
49
|
}, title),
|
|
42
50
|
children
|
|
43
51
|
);
|
|
@@ -45,19 +53,33 @@ export function CockpitSection({ title, children }) {
|
|
|
45
53
|
|
|
46
54
|
export function CockpitKeyHint({ keys, label, colorEnabled = true }) {
|
|
47
55
|
return React.createElement(Text, {
|
|
48
|
-
color: colorEnabled
|
|
56
|
+
color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
|
|
49
57
|
}, `${keys} ${label}`);
|
|
50
58
|
}
|
|
51
59
|
|
|
60
|
+
/** Compact header: brand · status · project — no box-drawing. */
|
|
52
61
|
export function CockpitTopBar({ model, colorEnabled = true }) {
|
|
62
|
+
const statusKind = String(model.status ?? "").toUpperCase() === "ONLINE"
|
|
63
|
+
? "online"
|
|
64
|
+
: "muted";
|
|
53
65
|
return React.createElement(Box, { justifyContent: "space-between", width: "100%" },
|
|
66
|
+
React.createElement(Box, null,
|
|
67
|
+
React.createElement(Text, {
|
|
68
|
+
bold: true,
|
|
69
|
+
color: resolveInkColor(colorEnabled, COCKPIT_COLORS.brand)
|
|
70
|
+
}, model.brand),
|
|
71
|
+
React.createElement(Text, {
|
|
72
|
+
color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
|
|
73
|
+
}, " · "),
|
|
74
|
+
React.createElement(CockpitBadge, {
|
|
75
|
+
label: model.status,
|
|
76
|
+
kind: statusKind,
|
|
77
|
+
colorEnabled
|
|
78
|
+
})
|
|
79
|
+
),
|
|
54
80
|
React.createElement(Text, {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}, `╭─ ${model.brand} ─ ${model.status}`),
|
|
58
|
-
React.createElement(Text, {
|
|
59
|
-
color: colorEnabled ? COCKPIT_COLORS.muted : undefined
|
|
60
|
-
}, `${model.projectLabel} ─╮`)
|
|
81
|
+
color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
|
|
82
|
+
}, model.projectLabel)
|
|
61
83
|
);
|
|
62
84
|
}
|
|
63
85
|
|
|
@@ -67,11 +89,11 @@ export function CockpitNav({ model, colorEnabled = true }) {
|
|
|
67
89
|
model.items.map((item) => {
|
|
68
90
|
const suffix = item.current && !item.selected ? " (open)" : "";
|
|
69
91
|
const color = item.focused
|
|
70
|
-
? (colorEnabled
|
|
92
|
+
? resolveInkColor(colorEnabled, COCKPIT_COLORS.interactive)
|
|
71
93
|
: item.selected
|
|
72
|
-
? (colorEnabled
|
|
94
|
+
? resolveInkColor(colorEnabled, COCKPIT_COLORS.brand)
|
|
73
95
|
: item.current
|
|
74
|
-
? (colorEnabled
|
|
96
|
+
? resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
|
|
75
97
|
: undefined;
|
|
76
98
|
return React.createElement(Text, {
|
|
77
99
|
key: item.id,
|
|
@@ -80,7 +102,7 @@ export function CockpitNav({ model, colorEnabled = true }) {
|
|
|
80
102
|
}, `${item.marker} ${item.label}${suffix}`);
|
|
81
103
|
}),
|
|
82
104
|
model.explanation && React.createElement(Text, {
|
|
83
|
-
color: COCKPIT_COLORS.muted
|
|
105
|
+
color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
|
|
84
106
|
}, model.explanation)
|
|
85
107
|
);
|
|
86
108
|
}
|
|
@@ -90,7 +112,7 @@ function stripLabel(item, { compact }) {
|
|
|
90
112
|
return item.label.split(/[&·]/)[0].trim().split(/\s+/)[0];
|
|
91
113
|
}
|
|
92
114
|
|
|
93
|
-
/**
|
|
115
|
+
/** Segmented horizontal nav — no border; active segment uses interactive. */
|
|
94
116
|
export function CockpitNavStrip({
|
|
95
117
|
model,
|
|
96
118
|
colorEnabled = true,
|
|
@@ -101,18 +123,20 @@ export function CockpitNavStrip({
|
|
|
101
123
|
const parts = model.items.map((item) => {
|
|
102
124
|
const label = stripLabel(item, { compact });
|
|
103
125
|
const suffix = item.current && !item.selected ? "*" : "";
|
|
104
|
-
const
|
|
105
|
-
|
|
126
|
+
const active = item.focused || item.selected;
|
|
127
|
+
const color = item.focused || (focused && item.selected)
|
|
128
|
+
? resolveInkColor(colorEnabled, COCKPIT_COLORS.interactive)
|
|
106
129
|
: item.selected
|
|
107
|
-
? (colorEnabled
|
|
108
|
-
:
|
|
109
|
-
|
|
110
|
-
|
|
130
|
+
? resolveInkColor(colorEnabled, COCKPIT_COLORS.interactive)
|
|
131
|
+
: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted);
|
|
132
|
+
const text = focused && item.selected
|
|
133
|
+
? `[${label}]${suffix}`
|
|
134
|
+
: `${item.marker}${label}${suffix}`;
|
|
111
135
|
return React.createElement(Text, {
|
|
112
136
|
key: item.id,
|
|
113
|
-
bold:
|
|
137
|
+
bold: active,
|
|
114
138
|
color
|
|
115
|
-
},
|
|
139
|
+
}, text);
|
|
116
140
|
});
|
|
117
141
|
|
|
118
142
|
const joined = [];
|
|
@@ -120,7 +144,7 @@ export function CockpitNavStrip({
|
|
|
120
144
|
if (index > 0) {
|
|
121
145
|
joined.push(React.createElement(Text, {
|
|
122
146
|
key: `sep-${index}`,
|
|
123
|
-
color: colorEnabled
|
|
147
|
+
color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
|
|
124
148
|
}, " · "));
|
|
125
149
|
}
|
|
126
150
|
joined.push(node);
|
|
@@ -129,13 +153,12 @@ export function CockpitNavStrip({
|
|
|
129
153
|
return React.createElement(Box, {
|
|
130
154
|
flexDirection: "column",
|
|
131
155
|
width: "100%",
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
paddingX: 1
|
|
156
|
+
marginTop: 0,
|
|
157
|
+
marginBottom: 0
|
|
135
158
|
},
|
|
136
159
|
React.createElement(Box, { flexDirection: "row", flexWrap: "wrap" }, ...joined),
|
|
137
160
|
model.explanation && React.createElement(Text, {
|
|
138
|
-
color: COCKPIT_COLORS.muted
|
|
161
|
+
color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
|
|
139
162
|
}, model.explanation)
|
|
140
163
|
);
|
|
141
164
|
}
|
|
@@ -145,7 +168,9 @@ export function CockpitSystemStrip({ model, colorEnabled = true }) {
|
|
|
145
168
|
return React.createElement(Box, { flexDirection: "column" },
|
|
146
169
|
model.rows.map((row) =>
|
|
147
170
|
React.createElement(Text, { key: row.key },
|
|
148
|
-
React.createElement(Text, {
|
|
171
|
+
React.createElement(Text, {
|
|
172
|
+
color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
|
|
173
|
+
}, `${row.key.padEnd(7)}`),
|
|
149
174
|
React.createElement(CockpitBadge, {
|
|
150
175
|
label: row.value,
|
|
151
176
|
kind: row.kind,
|
|
@@ -156,23 +181,19 @@ export function CockpitSystemStrip({ model, colorEnabled = true }) {
|
|
|
156
181
|
);
|
|
157
182
|
}
|
|
158
183
|
|
|
159
|
-
|
|
184
|
+
/** Single-line shortcut bar — no framed box. */
|
|
185
|
+
export function CockpitFooter({ model, columns = 80, colorEnabled = true }) {
|
|
160
186
|
const width = Math.max(24, Math.min(Number(columns) || 80, 120));
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
),
|
|
166
|
-
React.createElement(Text, { color: COCKPIT_COLORS.muted }, `│ ${model.text}`),
|
|
167
|
-
React.createElement(Text, { color: COCKPIT_COLORS.muted },
|
|
168
|
-
`╰${"─".repeat(bar)}╯`
|
|
169
|
-
)
|
|
187
|
+
return React.createElement(Box, { width: "100%", marginTop: 1 },
|
|
188
|
+
React.createElement(Text, {
|
|
189
|
+
color: resolveInkColor(colorEnabled, COCKPIT_COLORS.muted)
|
|
190
|
+
}, String(model.text ?? "").slice(0, width))
|
|
170
191
|
);
|
|
171
192
|
}
|
|
172
193
|
|
|
173
194
|
/**
|
|
174
195
|
* Responsive single-panel shell: TopBar → NavStrip → Main → Footer.
|
|
175
|
-
*
|
|
196
|
+
* No nested borders — hierarchy via type and spacing.
|
|
176
197
|
*/
|
|
177
198
|
export function CockpitShell({
|
|
178
199
|
topBar,
|
|
@@ -196,8 +217,9 @@ export function CockpitShell({
|
|
|
196
217
|
React.createElement(CockpitPanel, {
|
|
197
218
|
title: undefined,
|
|
198
219
|
focused: contentFocused,
|
|
199
|
-
width: "100%"
|
|
220
|
+
width: "100%",
|
|
221
|
+
colorEnabled
|
|
200
222
|
}, children),
|
|
201
|
-
React.createElement(CockpitFooter, { model: footer, columns })
|
|
223
|
+
React.createElement(CockpitFooter, { model: footer, columns, colorEnabled })
|
|
202
224
|
);
|
|
203
225
|
}
|
|
@@ -143,6 +143,6 @@ export function formatChangesActionLines({
|
|
|
143
143
|
|
|
144
144
|
export function buildChangesFooterParts(phase) {
|
|
145
145
|
if (phase === CHANGES_PHASE.PREVIEWING || phase === CHANGES_PHASE.APPLYING) return ["Working…", "Esc Back"];
|
|
146
|
-
if (phase === CHANGES_PHASE.CONFIRMING) return ["Y Apply", "N/Esc Cancel"];
|
|
147
|
-
return ["A Preview", "R Re-scan", "Esc Back"];
|
|
146
|
+
if (phase === CHANGES_PHASE.CONFIRMING) return ["Y Apply", "N/Esc Cancel", "Space"];
|
|
147
|
+
return ["A Preview", "R Re-scan", "Space", "Esc Back"];
|
|
148
148
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { CONTROL_PLANE_HEALTH } from "../control-plane-snapshot.js";
|
|
2
2
|
import { formatAlertsHeadline } from "./cockpit-alerts.js";
|
|
3
|
+
import {
|
|
4
|
+
adaptUsageModel,
|
|
5
|
+
formatMeasuredBudgets,
|
|
6
|
+
formatUsageLinesFromModel
|
|
7
|
+
} from "./cockpit-usage.js";
|
|
3
8
|
|
|
4
9
|
export function buildControlCenterModel({
|
|
5
10
|
projectName = "project",
|
|
@@ -112,73 +117,16 @@ function formatHealthLabel(kind) {
|
|
|
112
117
|
}
|
|
113
118
|
|
|
114
119
|
function formatTokenHeadline(budgets) {
|
|
115
|
-
|
|
116
|
-
const parts = [];
|
|
117
|
-
if (Number.isFinite(budgets.stableUsedTokens) && Number.isFinite(budgets.stableBudgetTokens)) {
|
|
118
|
-
parts.push(`stable ${budgets.stableUsedTokens}/${budgets.stableBudgetTokens}`);
|
|
119
|
-
}
|
|
120
|
-
if (Number.isFinite(budgets.requestUsedTokens) && Number.isFinite(budgets.requestBudgetTokens)) {
|
|
121
|
-
parts.push(`request ${budgets.requestUsedTokens}/${budgets.requestBudgetTokens}`);
|
|
122
|
-
}
|
|
123
|
-
return parts.length > 0 ? parts.join(" · ") : "Data unavailable";
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Usage surface: measured budgets when present, configured profile limits,
|
|
128
|
-
* and auditable run tokenUsage — never invent totals.
|
|
129
|
-
*/
|
|
130
|
-
function resolveProfileLimits(dashboard) {
|
|
131
|
-
const resolved = dashboard?.profile?.profile ?? {};
|
|
132
|
-
const configured = [];
|
|
133
|
-
if (Number.isFinite(resolved.tokenBudget)) configured.push(`token ${resolved.tokenBudget}`);
|
|
134
|
-
if (Number.isFinite(resolved.stableContextBudget)) configured.push(`stable ${resolved.stableContextBudget}`);
|
|
135
|
-
if (Number.isFinite(resolved.requestContextBudget)) configured.push(`request ${resolved.requestContextBudget}`);
|
|
136
|
-
return configured;
|
|
120
|
+
return formatMeasuredBudgets(budgets) ?? "Data unavailable";
|
|
137
121
|
}
|
|
138
122
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function formatRunUsageLine(run) {
|
|
147
|
-
const usage = run.tokenUsage;
|
|
148
|
-
const parts = [];
|
|
149
|
-
if (Number.isFinite(usage.input)) parts.push(`in ${usage.input}`);
|
|
150
|
-
if (Number.isFinite(usage.output)) parts.push(`out ${usage.output}`);
|
|
151
|
-
if (Number.isFinite(usage.total)) parts.push(`total ${usage.total}`);
|
|
152
|
-
return `${run.agentId ?? "agent"} · ${parts.join(" · ")}`;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export function formatUsageLines({ snapshot = null, dashboard = null } = {}) {
|
|
156
|
-
const lines = ["MEASURED"];
|
|
157
|
-
const measured = formatTokenHeadline(snapshot?.budgets);
|
|
158
|
-
lines.push(measured === "Data unavailable" ? "Data unavailable" : measured);
|
|
159
|
-
|
|
160
|
-
const configured = resolveProfileLimits(dashboard);
|
|
161
|
-
lines.push("", "CONFIGURED LIMITS");
|
|
162
|
-
lines.push(configured.length > 0 ? configured.join(" · ") : "No profile token budgets configured.");
|
|
163
|
-
|
|
164
|
-
const runs = [...(dashboard?.activeRuns ?? []), ...(dashboard?.recentRuns ?? [])]
|
|
165
|
-
.filter((run) => hasFiniteUsage(run?.tokenUsage));
|
|
166
|
-
lines.push("", "RUN USAGE");
|
|
167
|
-
if (runs.length === 0) {
|
|
168
|
-
lines.push("No auditable run tokenUsage yet.");
|
|
169
|
-
} else {
|
|
170
|
-
for (const run of runs.slice(0, 3)) {
|
|
171
|
-
lines.push(formatRunUsageLine(run));
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
lines.push("", "Auditable budgets only — no invented token savings.");
|
|
176
|
-
return lines;
|
|
123
|
+
/** Usage surface via shared auditable model — never invent totals. */
|
|
124
|
+
export function formatUsageLines({
|
|
125
|
+
snapshot = null, dashboard = null, layoutMode = undefined
|
|
126
|
+
} = {}) {
|
|
127
|
+
return formatUsageLinesFromModel(adaptUsageModel({ snapshot, dashboard, layoutMode }));
|
|
177
128
|
}
|
|
178
129
|
|
|
179
130
|
export function hasAuditableUsage({ snapshot = null, dashboard = null } = {}) {
|
|
180
|
-
|
|
181
|
-
if (resolveProfileLimits(dashboard).length > 0) return true;
|
|
182
|
-
return [...(dashboard?.activeRuns ?? []), ...(dashboard?.recentRuns ?? [])]
|
|
183
|
-
.some((run) => hasFiniteUsage(run?.tokenUsage));
|
|
131
|
+
return adaptUsageModel({ snapshot, dashboard }).hasEvidence;
|
|
184
132
|
}
|
|
@@ -35,7 +35,10 @@ export function createCockpitUiState({
|
|
|
35
35
|
helpOpen = false,
|
|
36
36
|
returnView = null,
|
|
37
37
|
paletteOpen = false,
|
|
38
|
-
paletteIndex = 0
|
|
38
|
+
paletteIndex = 0,
|
|
39
|
+
overviewDetailsOpen = false,
|
|
40
|
+
governanceDetailsOpen = false,
|
|
41
|
+
activityDetailsOpen = false
|
|
39
42
|
} = {}) {
|
|
40
43
|
const regions = regionsForLayout(layoutMode);
|
|
41
44
|
const preferred = region ?? defaultRegionForView(view, layoutMode);
|
|
@@ -52,6 +55,9 @@ export function createCockpitUiState({
|
|
|
52
55
|
returnView,
|
|
53
56
|
paletteOpen,
|
|
54
57
|
paletteIndex: Math.max(0, paletteIndex),
|
|
58
|
+
overviewDetailsOpen: Boolean(overviewDetailsOpen),
|
|
59
|
+
governanceDetailsOpen: Boolean(governanceDetailsOpen),
|
|
60
|
+
activityDetailsOpen: Boolean(activityDetailsOpen),
|
|
55
61
|
shouldExit: false
|
|
56
62
|
};
|
|
57
63
|
}
|
|
@@ -145,7 +151,10 @@ export function reduceCockpitUi(state, action) {
|
|
|
145
151
|
listIndex: 0,
|
|
146
152
|
region: defaultRegionForView(item.view, state.layoutMode),
|
|
147
153
|
helpOpen: false,
|
|
148
|
-
returnView: null
|
|
154
|
+
returnView: null,
|
|
155
|
+
overviewDetailsOpen: false,
|
|
156
|
+
governanceDetailsOpen: false,
|
|
157
|
+
activityDetailsOpen: false
|
|
149
158
|
});
|
|
150
159
|
}
|
|
151
160
|
case "set-view": {
|
|
@@ -159,9 +168,21 @@ export function reduceCockpitUi(state, action) {
|
|
|
159
168
|
: clamp(action.navIndex, 0, COCKPIT_NAV.length - 1),
|
|
160
169
|
region: action.region ?? defaultRegionForView(nextView, state.layoutMode),
|
|
161
170
|
returnView: action.returnView ?? state.returnView,
|
|
162
|
-
helpOpen: nextView === ORCHESTRATOR_VIEWS.HELP
|
|
171
|
+
helpOpen: nextView === ORCHESTRATOR_VIEWS.HELP,
|
|
172
|
+
overviewDetailsOpen: false,
|
|
173
|
+
governanceDetailsOpen: false,
|
|
174
|
+
activityDetailsOpen: false
|
|
163
175
|
});
|
|
164
176
|
}
|
|
177
|
+
case "toggle-overview-details":
|
|
178
|
+
if (state.view !== ORCHESTRATOR_VIEWS.HOME) return state;
|
|
179
|
+
return { ...state, overviewDetailsOpen: !state.overviewDetailsOpen };
|
|
180
|
+
case "toggle-governance-details":
|
|
181
|
+
if (state.view !== ORCHESTRATOR_VIEWS.CHANGES) return state;
|
|
182
|
+
return { ...state, governanceDetailsOpen: !state.governanceDetailsOpen };
|
|
183
|
+
case "toggle-activity-details":
|
|
184
|
+
if (state.view !== ORCHESTRATOR_VIEWS.ACTIVITY) return state;
|
|
185
|
+
return { ...state, activityDetailsOpen: !state.activityDetailsOpen };
|
|
165
186
|
case "toggle-help":
|
|
166
187
|
if (state.helpOpen || state.view === ORCHESTRATOR_VIEWS.HELP) {
|
|
167
188
|
return goOverview(state);
|
|
@@ -170,12 +191,24 @@ export function reduceCockpitUi(state, action) {
|
|
|
170
191
|
...state,
|
|
171
192
|
helpOpen: true,
|
|
172
193
|
view: ORCHESTRATOR_VIEWS.HELP,
|
|
173
|
-
region: defaultRegionForView(ORCHESTRATOR_VIEWS.HELP, state.layoutMode)
|
|
194
|
+
region: defaultRegionForView(ORCHESTRATOR_VIEWS.HELP, state.layoutMode),
|
|
195
|
+
overviewDetailsOpen: false,
|
|
196
|
+
governanceDetailsOpen: false,
|
|
197
|
+
activityDetailsOpen: false
|
|
174
198
|
});
|
|
175
199
|
case "escape": {
|
|
176
200
|
if (state.paletteOpen) {
|
|
177
201
|
return closePalette(state);
|
|
178
202
|
}
|
|
203
|
+
if (state.view === ORCHESTRATOR_VIEWS.HOME && state.overviewDetailsOpen) {
|
|
204
|
+
return { ...state, overviewDetailsOpen: false };
|
|
205
|
+
}
|
|
206
|
+
if (state.view === ORCHESTRATOR_VIEWS.CHANGES && state.governanceDetailsOpen) {
|
|
207
|
+
return { ...state, governanceDetailsOpen: false };
|
|
208
|
+
}
|
|
209
|
+
if (state.view === ORCHESTRATOR_VIEWS.ACTIVITY && state.activityDetailsOpen) {
|
|
210
|
+
return { ...state, activityDetailsOpen: false };
|
|
211
|
+
}
|
|
179
212
|
if (state.helpOpen || state.view === ORCHESTRATOR_VIEWS.HELP) {
|
|
180
213
|
return goOverview(state);
|
|
181
214
|
}
|
|
@@ -228,7 +261,10 @@ function goOverview(state) {
|
|
|
228
261
|
navIndex: 0,
|
|
229
262
|
listIndex: 0,
|
|
230
263
|
region: defaultRegionForView(ORCHESTRATOR_VIEWS.HOME, state.layoutMode),
|
|
231
|
-
returnView: null
|
|
264
|
+
returnView: null,
|
|
265
|
+
overviewDetailsOpen: false,
|
|
266
|
+
governanceDetailsOpen: false,
|
|
267
|
+
activityDetailsOpen: false
|
|
232
268
|
});
|
|
233
269
|
}
|
|
234
270
|
|
|
@@ -14,6 +14,7 @@ export function resolveEnterNavIntent({
|
|
|
14
14
|
const isOverview = navItem.id === "overview" || navItem.view === ORCHESTRATOR_VIEWS.HOME;
|
|
15
15
|
if (isOverview) {
|
|
16
16
|
if (currentView === ORCHESTRATOR_VIEWS.HOME && ctaDestination) {
|
|
17
|
+
if (ctaDestination === "setup") return { kind: "activate-setup" };
|
|
17
18
|
return { kind: "activate-cta", destination: ctaDestination };
|
|
18
19
|
}
|
|
19
20
|
return { kind: "open-nav", view: ORCHESTRATOR_VIEWS.HOME };
|