@h-rig/cli 0.0.6-alpha.81 → 0.0.6-alpha.83
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/dist/bin/build-rig-binaries.js +40 -8
- package/dist/bin/rig.js +2807 -1443
- package/dist/src/app/board.js +62 -13
- package/dist/src/app-opentui/adapters/command.d.ts +2 -0
- package/dist/src/app-opentui/adapters/command.js +329 -0
- package/dist/src/app-opentui/adapters/common.js +2 -2
- package/dist/src/app-opentui/adapters/doctor.js +63 -14
- package/dist/src/app-opentui/adapters/fleet.js +84 -20
- package/dist/src/app-opentui/adapters/inbox.js +83 -19
- package/dist/src/app-opentui/adapters/init.js +87 -23
- package/dist/src/app-opentui/adapters/pi-attach.js +102 -36
- package/dist/src/app-opentui/adapters/run-detail.js +83 -19
- package/dist/src/app-opentui/adapters/server.js +100 -23
- package/dist/src/app-opentui/adapters/tasks.d.ts +11 -0
- package/dist/src/app-opentui/adapters/tasks.js +742 -94
- package/dist/src/app-opentui/bootstrap.d.ts +1 -6
- package/dist/src/app-opentui/bootstrap.js +13776 -12369
- package/dist/src/app-opentui/command-pty-host.d.ts +62 -0
- package/dist/src/app-opentui/command-pty-host.js +248 -0
- package/dist/src/app-opentui/events.js +1 -1
- package/dist/src/app-opentui/focus-manager.d.ts +14 -0
- package/dist/src/app-opentui/focus-manager.js +24 -0
- package/dist/src/app-opentui/index.js +3806 -2878
- package/dist/src/app-opentui/intent.js +154 -48
- package/dist/src/app-opentui/keymap.d.ts +20 -0
- package/dist/src/app-opentui/keymap.js +707 -0
- package/dist/src/app-opentui/launch-routing.d.ts +16 -0
- package/dist/src/app-opentui/launch-routing.js +55 -0
- package/dist/src/app-opentui/layout.js +2 -2
- package/dist/src/app-opentui/pi-host-child.js +66 -16
- package/dist/src/app-opentui/pi-pty-host.d.ts +9 -0
- package/dist/src/app-opentui/pi-pty-host.js +15 -13
- package/dist/src/app-opentui/registry.js +3228 -2396
- package/dist/src/app-opentui/render/ascii-fleet.d.ts +15 -0
- package/dist/src/app-opentui/render/ascii-fleet.js +82 -0
- package/dist/src/app-opentui/render/graphics.d.ts +1 -1
- package/dist/src/app-opentui/render/graphics.js +131 -16
- package/dist/src/app-opentui/render/image-visual-layer-worker.js +413 -958
- package/dist/src/app-opentui/render/image-visual-layer.d.ts +19 -10
- package/dist/src/app-opentui/render/image-visual-layer.js +391 -357
- package/dist/src/app-opentui/render/panel-layout.d.ts +38 -0
- package/dist/src/app-opentui/render/panel-layout.js +48 -0
- package/dist/src/app-opentui/render/panels.d.ts +2 -0
- package/dist/src/app-opentui/render/panels.js +62 -29
- package/dist/src/app-opentui/render/preloader.d.ts +10 -0
- package/dist/src/app-opentui/render/preloader.js +163 -0
- package/dist/src/app-opentui/render/scene.d.ts +3 -0
- package/dist/src/app-opentui/render/scene.js +12 -0
- package/dist/src/app-opentui/render/text.js +5 -1
- package/dist/src/app-opentui/render/type-bar.d.ts +2 -1
- package/dist/src/app-opentui/render/type-bar.js +51 -16
- package/dist/src/app-opentui/runtime-resources.d.ts +16 -0
- package/dist/src/app-opentui/runtime-resources.js +62 -0
- package/dist/src/app-opentui/runtime.js +2329 -1512
- package/dist/src/app-opentui/scenes/command.d.ts +3 -0
- package/dist/src/app-opentui/scenes/command.js +103 -0
- package/dist/src/app-opentui/scenes/doctor.d.ts +2 -1
- package/dist/src/app-opentui/scenes/doctor.js +53 -14
- package/dist/src/app-opentui/scenes/error.js +44 -14
- package/dist/src/app-opentui/scenes/fleet.js +40 -21
- package/dist/src/app-opentui/scenes/handoff.js +142 -27
- package/dist/src/app-opentui/scenes/help.js +63 -48
- package/dist/src/app-opentui/scenes/inbox.d.ts +2 -1
- package/dist/src/app-opentui/scenes/inbox.js +64 -17
- package/dist/src/app-opentui/scenes/init.d.ts +2 -1
- package/dist/src/app-opentui/scenes/init.js +62 -21
- package/dist/src/app-opentui/scenes/main.d.ts +2 -1
- package/dist/src/app-opentui/scenes/main.js +80 -24
- package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -1
- package/dist/src/app-opentui/scenes/run-detail.js +39 -25
- package/dist/src/app-opentui/scenes/server.d.ts +2 -1
- package/dist/src/app-opentui/scenes/server.js +71 -20
- package/dist/src/app-opentui/scenes/tasks.js +44 -22
- package/dist/src/app-opentui/state.js +70 -30
- package/dist/src/app-opentui/terminal-capabilities.d.ts +7 -0
- package/dist/src/app-opentui/terminal-capabilities.js +15 -0
- package/dist/src/app-opentui/types.d.ts +10 -2
- package/dist/src/commands/_doctor-checks.js +62 -13
- package/dist/src/commands/_operator-view.js +63 -13
- package/dist/src/commands/_pi-frontend.js +63 -13
- package/dist/src/commands/_preflight.js +64 -14
- package/dist/src/commands/_server-client.js +82 -18
- package/dist/src/commands/_snapshot-upload.js +62 -13
- package/dist/src/commands/connect.js +7 -1
- package/dist/src/commands/doctor.js +62 -13
- package/dist/src/commands/github.js +144 -23
- package/dist/src/commands/inbox.js +62 -13
- package/dist/src/commands/init.js +82 -18
- package/dist/src/commands/inspect.js +62 -13
- package/dist/src/commands/run.js +66 -13
- package/dist/src/commands/server.js +69 -14
- package/dist/src/commands/setup.js +62 -13
- package/dist/src/commands/stats.js +62 -13
- package/dist/src/commands/task-run-driver.js +62 -13
- package/dist/src/commands/task.js +65 -14
- package/dist/src/commands.js +227 -92
- package/dist/src/index.js +234 -99
- package/package.json +8 -9
- package/dist/src/app-opentui/render/image-visual-layer-native-canvas.d.ts +0 -2
- package/dist/src/app-opentui/render/image-visual-layer-native-canvas.js +0 -1480
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/cli/src/app-opentui/theme.ts
|
|
3
|
+
import {
|
|
4
|
+
bold as otuiBold,
|
|
5
|
+
dim as otuiDim,
|
|
6
|
+
fg as otuiFg,
|
|
7
|
+
t,
|
|
8
|
+
TextAttributes
|
|
9
|
+
} from "@opentui/core";
|
|
10
|
+
var RIG_UI = {
|
|
11
|
+
bg: "#070809",
|
|
12
|
+
bg2: "#0b0c0e",
|
|
13
|
+
panel: "#101115",
|
|
14
|
+
panel2: "#101115",
|
|
15
|
+
glass: "#14161b",
|
|
16
|
+
ink: "#f2f3f6",
|
|
17
|
+
ink2: "#aeb0ba",
|
|
18
|
+
ink3: "#6c6e79",
|
|
19
|
+
ink4: "#44464f",
|
|
20
|
+
lime: "#ccff4d",
|
|
21
|
+
limeDim: "#a9d63f",
|
|
22
|
+
cyan: "#56d8ff",
|
|
23
|
+
red: "#ff5d5d",
|
|
24
|
+
yellow: "#ffd24d",
|
|
25
|
+
magenta: "#ff79b0"
|
|
26
|
+
};
|
|
27
|
+
var styles = {
|
|
28
|
+
ink: otuiFg(RIG_UI.ink),
|
|
29
|
+
ink2: otuiFg(RIG_UI.ink2),
|
|
30
|
+
ink3: otuiFg(RIG_UI.ink3),
|
|
31
|
+
ink4: otuiFg(RIG_UI.ink4),
|
|
32
|
+
lime: otuiFg(RIG_UI.lime),
|
|
33
|
+
limeDim: otuiFg(RIG_UI.limeDim),
|
|
34
|
+
cyan: otuiFg(RIG_UI.cyan),
|
|
35
|
+
red: otuiFg(RIG_UI.red),
|
|
36
|
+
yellow: otuiFg(RIG_UI.yellow),
|
|
37
|
+
magenta: otuiFg(RIG_UI.magenta)
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// packages/cli/src/app-opentui/render/scene.ts
|
|
41
|
+
function line(text, options = {}) {
|
|
42
|
+
return { text, ...options };
|
|
43
|
+
}
|
|
44
|
+
function makeSceneFrame(input) {
|
|
45
|
+
return input;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// packages/cli/src/app-opentui/scenes/command.ts
|
|
49
|
+
function terminalState(state) {
|
|
50
|
+
const value = state.data.commandTerminal;
|
|
51
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
52
|
+
}
|
|
53
|
+
function panelWidth(layout) {
|
|
54
|
+
return layout?.centerWidth ?? 118;
|
|
55
|
+
}
|
|
56
|
+
function panelHeight(layout, top = 0) {
|
|
57
|
+
return Math.max(14, (layout?.centerHeight ?? 36) - top);
|
|
58
|
+
}
|
|
59
|
+
function header(snapshot, state) {
|
|
60
|
+
const label = snapshot?.label ?? (state.argv.length > 0 ? `rig ${state.argv.join(" ")}` : "rig");
|
|
61
|
+
const suffix = snapshot?.message ? ` \xB7 ${snapshot.message}` : "";
|
|
62
|
+
return `${label}${suffix}`;
|
|
63
|
+
}
|
|
64
|
+
function commandLines(snapshot) {
|
|
65
|
+
if (!snapshot) {
|
|
66
|
+
return [line("starting command\u2026", { fg: RIG_UI.ink3 })];
|
|
67
|
+
}
|
|
68
|
+
const rows = snapshot.lines.length > 0 ? snapshot.lines : ["starting command\u2026"];
|
|
69
|
+
return rows.map((text) => line(text, { fg: text.trim() ? RIG_UI.ink2 : RIG_UI.ink4 }));
|
|
70
|
+
}
|
|
71
|
+
function renderCommandScene(state, layout) {
|
|
72
|
+
const terminal = terminalState(state);
|
|
73
|
+
const width = panelWidth(layout);
|
|
74
|
+
const panelTop = 0;
|
|
75
|
+
return makeSceneFrame({
|
|
76
|
+
scene: "command",
|
|
77
|
+
title: "Command",
|
|
78
|
+
lines: [],
|
|
79
|
+
panels: [{
|
|
80
|
+
id: "command-terminal",
|
|
81
|
+
top: panelTop,
|
|
82
|
+
width,
|
|
83
|
+
height: panelHeight(layout, panelTop),
|
|
84
|
+
lines: commandLines(terminal),
|
|
85
|
+
backgroundColor: RIG_UI.panel,
|
|
86
|
+
backgroundAlpha: 184,
|
|
87
|
+
opacity: 0.98,
|
|
88
|
+
border: false,
|
|
89
|
+
chrome: "ad-terminal",
|
|
90
|
+
headerText: header(terminal, state),
|
|
91
|
+
stickyScroll: true,
|
|
92
|
+
stickyStart: "bottom",
|
|
93
|
+
paddingX: 5,
|
|
94
|
+
paddingY: 2
|
|
95
|
+
}],
|
|
96
|
+
footer: { message: terminal?.message ?? "command" },
|
|
97
|
+
live: terminal?.status === "starting" || terminal?.status === "running" || state.status === "action",
|
|
98
|
+
hideTypeBar: true
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
export {
|
|
102
|
+
renderCommandScene
|
|
103
|
+
};
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
+
import type { StageLayout } from "../layout";
|
|
1
2
|
import type { AppSceneFrame, AppState } from "../types";
|
|
2
|
-
export declare function renderDoctorScene(state: AppState): AppSceneFrame;
|
|
3
|
+
export declare function renderDoctorScene(state: AppState, layout?: StageLayout): AppSceneFrame;
|
|
@@ -41,11 +41,15 @@ var styles = {
|
|
|
41
41
|
function line(text, options = {}) {
|
|
42
42
|
return { text, ...options };
|
|
43
43
|
}
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
function deckRow(input) {
|
|
45
|
+
const prefix = input.active ? "\u25B8" : " ";
|
|
46
|
+
const label = input.label.toUpperCase().padEnd(10);
|
|
47
|
+
return line(`${prefix} ${label} ${input.detail}`, {
|
|
48
|
+
fg: input.active ? RIG_UI.lime : RIG_UI.ink2,
|
|
49
|
+
bold: input.active,
|
|
50
|
+
selectableIndex: input.index,
|
|
51
|
+
activateOnClick: input.activateOnClick
|
|
52
|
+
});
|
|
49
53
|
}
|
|
50
54
|
function makeSceneFrame(input) {
|
|
51
55
|
return input;
|
|
@@ -65,24 +69,59 @@ function glyph(status) {
|
|
|
65
69
|
function doctorColor(status) {
|
|
66
70
|
return status === "pass" ? RIG_UI.limeDim : status === "warn" ? RIG_UI.yellow : RIG_UI.red;
|
|
67
71
|
}
|
|
68
|
-
function
|
|
72
|
+
function panelWidth(layout) {
|
|
73
|
+
return layout?.centerWidth ?? 100;
|
|
74
|
+
}
|
|
75
|
+
function panelHeight(layout) {
|
|
76
|
+
return Math.max(10, (layout?.centerHeight ?? 24) - 1);
|
|
77
|
+
}
|
|
78
|
+
function actionRows(selected) {
|
|
79
|
+
return [
|
|
80
|
+
line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
|
|
81
|
+
deckRow({ label: "run", detail: "run diagnostics now", index: 0, active: selected === 0, activateOnClick: true }),
|
|
82
|
+
deckRow({ label: "server", detail: "open server controls", index: 1, active: selected === 1, activateOnClick: true }),
|
|
83
|
+
deckRow({ label: "repair", detail: "repair project/server/auth link", index: 2, active: selected === 2, activateOnClick: true }),
|
|
84
|
+
deckRow({ label: "tasks", detail: "open tasks", index: 3, active: selected === 3, activateOnClick: true })
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
function renderDoctorScene(state, layout) {
|
|
69
88
|
const records = checks(state);
|
|
89
|
+
const selected = Math.max(0, Math.min(3, state.selection.index));
|
|
70
90
|
const failures = records.filter((check) => check.status === "fail").length;
|
|
71
91
|
const warnings = records.filter((check) => check.status === "warn").length;
|
|
72
92
|
const firstRemediation = records.find((check) => check.status !== "pass" && check.remediation)?.remediation;
|
|
93
|
+
const checkLines = records.length > 0 ? records.slice(0, 12).map((check) => line(`${glyph(check.status)} ${check.label}${check.detail ? ` \u2014 ${check.detail}` : ""}`, { fg: doctorColor(check.status) })) : [line("Diagnostics have not run in this session.", { fg: RIG_UI.ink3 })];
|
|
73
94
|
return makeSceneFrame({
|
|
74
95
|
scene: "doctor",
|
|
75
96
|
title: "Doctor",
|
|
76
97
|
lines: [
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
blank(),
|
|
80
|
-
...records.length > 0 ? records.slice(0, 14).map((check) => line(`${glyph(check.status)} ${check.label}${check.detail ? ` \u2014 ${check.detail}` : ""}`, { fg: doctorColor(check.status), align: "center" })) : [line("type doctor to run checks", { fg: RIG_UI.ink3, align: "center" })],
|
|
81
|
-
blank(),
|
|
82
|
-
line(firstRemediation ? `next: ${firstRemediation}` : records.length > 0 ? "ready" : "idle", { fg: firstRemediation ? RIG_UI.yellow : RIG_UI.ink4, align: "center" })
|
|
98
|
+
line("", { fg: RIG_UI.ink4 }),
|
|
99
|
+
line(` doctor \xB7 ${records.length > 0 ? `${failures} fail \xB7 ${warnings} warn` : "ready"}`, { fg: RIG_UI.ink3 })
|
|
83
100
|
],
|
|
84
|
-
|
|
85
|
-
|
|
101
|
+
panels: [{
|
|
102
|
+
id: "doctor-actions",
|
|
103
|
+
top: 0,
|
|
104
|
+
width: panelWidth(layout),
|
|
105
|
+
height: panelHeight(layout),
|
|
106
|
+
lines: [
|
|
107
|
+
...actionRows(selected),
|
|
108
|
+
line("", { fg: RIG_UI.ink3 }),
|
|
109
|
+
line("CHECKS", { fg: RIG_UI.ink3, bold: true }),
|
|
110
|
+
...checkLines,
|
|
111
|
+
...firstRemediation ? [line("", { fg: RIG_UI.ink3 }), line(`next: ${firstRemediation}`, { fg: RIG_UI.yellow })] : []
|
|
112
|
+
],
|
|
113
|
+
backgroundColor: RIG_UI.panel,
|
|
114
|
+
backgroundAlpha: 184,
|
|
115
|
+
opacity: 1,
|
|
116
|
+
border: false,
|
|
117
|
+
chrome: "ad-terminal",
|
|
118
|
+
headerText: `doctor \xB7 ${records.length} checks`,
|
|
119
|
+
headerHeight: 3,
|
|
120
|
+
paddingX: layout?.compact ? 2 : 3,
|
|
121
|
+
paddingY: 1
|
|
122
|
+
}],
|
|
123
|
+
footer: { message: records.length > 0 ? `${records.length} checks` : "enter/click run to diagnose" },
|
|
124
|
+
hideTypeBar: true,
|
|
86
125
|
live: state.status === "action" || records.length === 0
|
|
87
126
|
});
|
|
88
127
|
}
|
|
@@ -156,11 +156,18 @@ function errorDrone(tick) {
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
// packages/cli/src/app-opentui/render/scene.ts
|
|
159
|
-
function
|
|
160
|
-
return { text
|
|
159
|
+
function line(text, options = {}) {
|
|
160
|
+
return { text, ...options };
|
|
161
161
|
}
|
|
162
|
-
function
|
|
163
|
-
|
|
162
|
+
function deckRow(input) {
|
|
163
|
+
const prefix = input.active ? "\u25B8" : " ";
|
|
164
|
+
const label = input.label.toUpperCase().padEnd(10);
|
|
165
|
+
return line(`${prefix} ${label} ${input.detail}`, {
|
|
166
|
+
fg: input.active ? RIG_UI.lime : RIG_UI.ink2,
|
|
167
|
+
bold: input.active,
|
|
168
|
+
selectableIndex: input.index,
|
|
169
|
+
activateOnClick: input.activateOnClick
|
|
170
|
+
});
|
|
164
171
|
}
|
|
165
172
|
function makeSceneFrame(input) {
|
|
166
173
|
return input;
|
|
@@ -168,20 +175,43 @@ function makeSceneFrame(input) {
|
|
|
168
175
|
|
|
169
176
|
// packages/cli/src/app-opentui/scenes/error.ts
|
|
170
177
|
function renderErrorScene(state) {
|
|
171
|
-
const message = state.error?.message ?? "Unknown
|
|
172
|
-
const hint = state.error?.hint ?? "
|
|
178
|
+
const message = state.error?.message ?? "Unknown app error";
|
|
179
|
+
const hint = state.error?.hint ?? "Use the actions below to recover.";
|
|
180
|
+
const selected = Math.max(0, Math.min(3, state.selection.index));
|
|
173
181
|
return makeSceneFrame({
|
|
174
182
|
scene: "error",
|
|
175
|
-
title: "
|
|
183
|
+
title: "Error",
|
|
176
184
|
lines: [
|
|
177
|
-
...errorDrone(state.tick).slice(2,
|
|
178
|
-
blank(),
|
|
179
|
-
center("DRONE SIGNAL DEGRADED", RIG_UI.red, true),
|
|
180
|
-
center(message, RIG_UI.ink2),
|
|
181
|
-
blank(),
|
|
182
|
-
center(hint, RIG_UI.limeDim)
|
|
185
|
+
...errorDrone(state.tick).slice(2, 10)
|
|
183
186
|
],
|
|
184
|
-
|
|
187
|
+
panels: [{
|
|
188
|
+
id: "error-recovery",
|
|
189
|
+
top: 2,
|
|
190
|
+
width: 96,
|
|
191
|
+
height: 18,
|
|
192
|
+
lines: [
|
|
193
|
+
line("DRONE SIGNAL DEGRADED", { fg: RIG_UI.red, bold: true }),
|
|
194
|
+
line(message, { fg: RIG_UI.ink2 }),
|
|
195
|
+
...hint ? [line(hint, { fg: RIG_UI.yellow })] : [],
|
|
196
|
+
line("", { fg: RIG_UI.ink3 }),
|
|
197
|
+
line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
|
|
198
|
+
deckRow({ label: "main", detail: "return to dashboard", index: 0, active: selected === 0, activateOnClick: true }),
|
|
199
|
+
deckRow({ label: "doctor", detail: "run diagnostics", index: 1, active: selected === 1, activateOnClick: true }),
|
|
200
|
+
deckRow({ label: "server", detail: "open server controls", index: 2, active: selected === 2, activateOnClick: true }),
|
|
201
|
+
deckRow({ label: "repair", detail: "repair project/server/auth link", index: 3, active: selected === 3, activateOnClick: true })
|
|
202
|
+
],
|
|
203
|
+
backgroundColor: RIG_UI.panel,
|
|
204
|
+
backgroundAlpha: 184,
|
|
205
|
+
opacity: 1,
|
|
206
|
+
border: false,
|
|
207
|
+
chrome: "ad-terminal",
|
|
208
|
+
headerText: "recovery",
|
|
209
|
+
headerHeight: 3,
|
|
210
|
+
paddingX: 3,
|
|
211
|
+
paddingY: 1
|
|
212
|
+
}],
|
|
213
|
+
footer: { message: "enter/click a recovery action" },
|
|
214
|
+
hideTypeBar: true,
|
|
185
215
|
live: true
|
|
186
216
|
});
|
|
187
217
|
}
|
|
@@ -67,6 +67,16 @@ function statusColor(status) {
|
|
|
67
67
|
function line(text, options = {}) {
|
|
68
68
|
return { text, ...options };
|
|
69
69
|
}
|
|
70
|
+
function deckRow(input) {
|
|
71
|
+
const prefix = input.active ? "\u25B8" : " ";
|
|
72
|
+
const label = input.label.toUpperCase().padEnd(10);
|
|
73
|
+
return line(`${prefix} ${label} ${input.detail}`, {
|
|
74
|
+
fg: input.active ? RIG_UI.lime : RIG_UI.ink2,
|
|
75
|
+
bold: input.active,
|
|
76
|
+
selectableIndex: input.index,
|
|
77
|
+
activateOnClick: input.activateOnClick
|
|
78
|
+
});
|
|
79
|
+
}
|
|
70
80
|
function makeSceneFrame(input) {
|
|
71
81
|
return input;
|
|
72
82
|
}
|
|
@@ -132,31 +142,34 @@ function runRow(width, run, index, active) {
|
|
|
132
142
|
return line(`${prefix}${clip(run.title, titleWidth)}`, {
|
|
133
143
|
fg: active ? RIG_UI.ink : statusColor(run.status),
|
|
134
144
|
bold: active,
|
|
135
|
-
selectableIndex: index
|
|
145
|
+
selectableIndex: index,
|
|
146
|
+
activateOnClick: true
|
|
136
147
|
});
|
|
137
148
|
}
|
|
138
|
-
function
|
|
149
|
+
function recoveryRows(selected, reason) {
|
|
150
|
+
return [
|
|
151
|
+
...reason ? [line(reason, { fg: RIG_UI.yellow }), line("", { fg: RIG_UI.ink3 })] : [],
|
|
152
|
+
line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
|
|
153
|
+
deckRow({ label: "tasks", detail: "open tasks and dispatch work", index: 0, active: selected === 0, activateOnClick: true }),
|
|
154
|
+
deckRow({ label: "repair", detail: "repair project/server/auth link", index: 1, active: selected === 1, activateOnClick: true }),
|
|
155
|
+
deckRow({ label: "server", detail: "open server controls", index: 2, active: selected === 2, activateOnClick: true }),
|
|
156
|
+
deckRow({ label: "doctor", detail: "diagnose project", index: 3, active: selected === 3, activateOnClick: true })
|
|
157
|
+
];
|
|
158
|
+
}
|
|
159
|
+
function emptyRows(query, total, selected) {
|
|
160
|
+
if (!query.trim() && total === 0)
|
|
161
|
+
return recoveryRows(selected, "No runs yet.");
|
|
139
162
|
if (query.trim() && total > 0) {
|
|
140
163
|
return [
|
|
141
|
-
|
|
142
|
-
line(`search ${JSON.stringify(query)} returned 0/${total}`, { fg: RIG_UI.ink3 }),
|
|
143
|
-
line("escape clears search", { fg: RIG_UI.ink4 })
|
|
164
|
+
...recoveryRows(selected, `No matching runs for ${JSON.stringify(query)} (${total} total).`)
|
|
144
165
|
];
|
|
145
166
|
}
|
|
146
|
-
return
|
|
147
|
-
line("No runs yet.", { fg: RIG_UI.ink2, bold: true }),
|
|
148
|
-
line("Open Tasks and dispatch work; live and completed runs appear here.", { fg: RIG_UI.ink3 }),
|
|
149
|
-
line("tab switches screens", { fg: RIG_UI.ink4 })
|
|
150
|
-
];
|
|
167
|
+
return recoveryRows(selected, "No runs are visible with this filter.");
|
|
151
168
|
}
|
|
152
|
-
function degradedRows(state) {
|
|
169
|
+
function degradedRows(state, selected) {
|
|
153
170
|
if (!state.error)
|
|
154
171
|
return [];
|
|
155
|
-
return
|
|
156
|
-
line(`status: ${state.error.message}`, { fg: RIG_UI.red, bold: true }),
|
|
157
|
-
...state.error.hint ? [line(state.error.hint, { fg: RIG_UI.yellow })] : [],
|
|
158
|
-
line("", { fg: RIG_UI.ink3 })
|
|
159
|
-
];
|
|
172
|
+
return recoveryRows(selected, state.error.message);
|
|
160
173
|
}
|
|
161
174
|
function renderFleetScene(state, layout) {
|
|
162
175
|
const width = panelWidth(layout);
|
|
@@ -165,21 +178,27 @@ function renderFleetScene(state, layout) {
|
|
|
165
178
|
const runs = filterRunsForSearch(allRuns, query);
|
|
166
179
|
const selectedRun = typeof state.data.selectedRunId === "string" && runs.some((run) => run.runId === state.data.selectedRunId) ? state.data.selectedRunId : runs[0]?.runId;
|
|
167
180
|
const selectedIndex = Math.max(0, runs.findIndex((run) => run.runId === selectedRun));
|
|
181
|
+
const recoveryIndex = Math.max(0, Math.min(3, state.selection.index));
|
|
168
182
|
const panelTop = 0;
|
|
169
183
|
const contentWidth = Math.max(12, width - (layout?.compact ? 8 : 10));
|
|
170
184
|
const panelLines = [
|
|
171
185
|
line(`RUN STATUS TITLE`, { fg: RIG_UI.ink3, bold: true }),
|
|
172
186
|
line("", { fg: RIG_UI.ink3 }),
|
|
173
|
-
...
|
|
174
|
-
|
|
175
|
-
|
|
187
|
+
...runs.length > 0 ? [
|
|
188
|
+
...degradedRows(state, recoveryIndex),
|
|
189
|
+
...query.trim() ? [line(searchSummary("runs", query, runs.length, allRuns.length), { fg: RIG_UI.ink4 }), line("", { fg: RIG_UI.ink3 })] : [],
|
|
190
|
+
...runs.map((run, index) => runRow(contentWidth, run, index, index === selectedIndex))
|
|
191
|
+
] : state.error ? degradedRows(state, recoveryIndex) : [
|
|
192
|
+
...query.trim() ? [line(searchSummary("runs", query, runs.length, allRuns.length), { fg: RIG_UI.ink4 }), line("", { fg: RIG_UI.ink3 })] : [],
|
|
193
|
+
...emptyRows(query, allRuns.length, recoveryIndex)
|
|
194
|
+
]
|
|
176
195
|
];
|
|
177
196
|
return makeSceneFrame({
|
|
178
197
|
scene: "fleet",
|
|
179
198
|
title: "Runs",
|
|
180
199
|
lines: [
|
|
181
200
|
line("", { fg: RIG_UI.ink4 }),
|
|
182
|
-
line(query.trim() ? `
|
|
201
|
+
line(query.trim() ? ` runs --search ${JSON.stringify(query)}` : " runs", { fg: RIG_UI.ink3 })
|
|
183
202
|
],
|
|
184
203
|
panels: [{
|
|
185
204
|
id: "runs-list",
|
|
@@ -192,7 +211,7 @@ function renderFleetScene(state, layout) {
|
|
|
192
211
|
opacity: 1,
|
|
193
212
|
border: false,
|
|
194
213
|
chrome: "ad-terminal",
|
|
195
|
-
headerText: query.trim() ? `
|
|
214
|
+
headerText: query.trim() ? `runs --search ${JSON.stringify(query)}` : "runs",
|
|
196
215
|
headerHeight: 3,
|
|
197
216
|
paddingX: layout?.compact ? 2 : 3,
|
|
198
217
|
paddingY: 1
|
|
@@ -41,12 +41,6 @@ var styles = {
|
|
|
41
41
|
function line(text, options = {}) {
|
|
42
42
|
return { text, ...options };
|
|
43
43
|
}
|
|
44
|
-
function blank() {
|
|
45
|
-
return { text: "" };
|
|
46
|
-
}
|
|
47
|
-
function center(text, fg = RIG_UI.ink2, bold = false) {
|
|
48
|
-
return { text, fg, bold, align: "center" };
|
|
49
|
-
}
|
|
50
44
|
function deckRow(input) {
|
|
51
45
|
const prefix = input.active ? "\u25B8" : " ";
|
|
52
46
|
const label = input.label.toUpperCase().padEnd(10);
|
|
@@ -61,6 +55,83 @@ function makeSceneFrame(input) {
|
|
|
61
55
|
return input;
|
|
62
56
|
}
|
|
63
57
|
|
|
58
|
+
// packages/cli/src/app-opentui/render/ascii-fleet.ts
|
|
59
|
+
var LEAD_DRONE = [
|
|
60
|
+
" .-=-. .-=-. ",
|
|
61
|
+
" ( !!! ) ( !!! ) ",
|
|
62
|
+
" '-=-'._ _.'-=-' ",
|
|
63
|
+
" '._ _.' ",
|
|
64
|
+
" '=$$$$$$$=.' ",
|
|
65
|
+
" =$$$$$$$$$$$= ",
|
|
66
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
67
|
+
" $$$@@ @@$$$ ",
|
|
68
|
+
" $$@ ? @$$$ ",
|
|
69
|
+
" $$$@ '-' @$$$ ",
|
|
70
|
+
" $$$@@ @@$$$ ",
|
|
71
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
72
|
+
" =$$$$$$$$$$$= ",
|
|
73
|
+
" '=$$$$$$$=.' ",
|
|
74
|
+
" _.' '._ ",
|
|
75
|
+
" .-=-.' '.-=-. ",
|
|
76
|
+
" ( !!! ) ( !!! ) ",
|
|
77
|
+
" '-=-' '-=-' "
|
|
78
|
+
];
|
|
79
|
+
var MINI_DRONE = [
|
|
80
|
+
"(!!!) (!!!)",
|
|
81
|
+
" \\%==%/ ",
|
|
82
|
+
" %%?%% ",
|
|
83
|
+
" /%==%\\ ",
|
|
84
|
+
"(!!!) (!!!)"
|
|
85
|
+
];
|
|
86
|
+
var ROTORS = ["---", "\\\\\\", "|||", "///"];
|
|
87
|
+
var FLEET_GRID_WIDTH = 52;
|
|
88
|
+
var FLEET_GRID_HEIGHT = 27;
|
|
89
|
+
var FLEET = [
|
|
90
|
+
{ art: MINI_DRONE, x: 0, y: 2, amp: 1, speed: 0.05, phase: 0, dx: 1, driftSpeed: 0.02 },
|
|
91
|
+
{ art: MINI_DRONE, x: 39, y: 2, amp: 1, speed: 0.044, phase: 2.1, dx: 1, driftSpeed: 0.017 },
|
|
92
|
+
{ art: MINI_DRONE, x: 1, y: 20, amp: 1, speed: 0.048, phase: 4.2, dx: 1, driftSpeed: 0.023 },
|
|
93
|
+
{ art: MINI_DRONE, x: 38, y: 20, amp: 1, speed: 0.041, phase: 1.3, dx: 1, driftSpeed: 0.015 },
|
|
94
|
+
{ art: LEAD_DRONE, x: 10, y: 5, amp: 2, speed: 0.04, phase: 0, dx: 0, driftSpeed: 0.011 }
|
|
95
|
+
];
|
|
96
|
+
function fleetRows(tick, options = {}) {
|
|
97
|
+
const animate = options.animate ?? true;
|
|
98
|
+
const t2 = animate ? tick : 0;
|
|
99
|
+
const blade = ROTORS[Math.floor(t2 / 4) % ROTORS.length];
|
|
100
|
+
const pulse = Math.sin(t2 * 0.07);
|
|
101
|
+
const eye = pulse > 0.45 ? "@" : pulse > -0.1 ? "o" : ".";
|
|
102
|
+
const grid = Array.from({ length: FLEET_GRID_HEIGHT }, () => Array.from({ length: FLEET_GRID_WIDTH }, () => " "));
|
|
103
|
+
for (const drone of FLEET) {
|
|
104
|
+
const yOffset = drone.y + Math.round(drone.amp * Math.sin(t2 * drone.speed + drone.phase));
|
|
105
|
+
const xOffset = drone.x + Math.round(drone.dx * Math.sin(t2 * drone.driftSpeed + drone.phase));
|
|
106
|
+
for (let row = 0;row < drone.art.length; row += 1) {
|
|
107
|
+
const source = drone.art[row];
|
|
108
|
+
const targetY = yOffset + row;
|
|
109
|
+
if (targetY < 0 || targetY >= FLEET_GRID_HEIGHT)
|
|
110
|
+
continue;
|
|
111
|
+
for (let col = 0;col < source.length; col += 1) {
|
|
112
|
+
let char = source[col];
|
|
113
|
+
if (char === " ")
|
|
114
|
+
continue;
|
|
115
|
+
const targetX = xOffset + col;
|
|
116
|
+
if (targetX < 0 || targetX >= FLEET_GRID_WIDTH)
|
|
117
|
+
continue;
|
|
118
|
+
if (source.slice(col, col + 3) === "!!!") {
|
|
119
|
+
for (let rotorIndex = 0;rotorIndex < 3; rotorIndex += 1) {
|
|
120
|
+
if (targetX + rotorIndex < FLEET_GRID_WIDTH)
|
|
121
|
+
grid[targetY][targetX + rotorIndex] = blade[rotorIndex];
|
|
122
|
+
}
|
|
123
|
+
col += 2;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (char === "?")
|
|
127
|
+
char = eye;
|
|
128
|
+
grid[targetY][targetX] = char;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return grid.map((row) => row.join("").replace(/\s+$/, ""));
|
|
133
|
+
}
|
|
134
|
+
|
|
64
135
|
// packages/cli/src/app-opentui/scenes/handoff.ts
|
|
65
136
|
function handoffState(state) {
|
|
66
137
|
const value = state.data.piAttach ?? state.data.handoff;
|
|
@@ -79,9 +150,32 @@ function terminalRail(snapshot) {
|
|
|
79
150
|
const text = ` run ${snapshot.runId.slice(0, 8)} \xB7 bundled Pi \xB7 mouse wheel scroll \xB7 ctrl-] detach \xB7 ${snapshot.message ?? snapshot.status}`;
|
|
80
151
|
return line(clip(text, snapshot.cols), { fg: RIG_UI.limeDim });
|
|
81
152
|
}
|
|
82
|
-
function
|
|
153
|
+
function shouldShowTerminalPreloader(snapshot) {
|
|
154
|
+
if (snapshot.status !== "starting" && snapshot.status !== "running")
|
|
155
|
+
return false;
|
|
156
|
+
const nonEmptyLines = snapshot.lines.filter((entry) => entry.trim().length > 0).length;
|
|
157
|
+
return nonEmptyLines <= 4;
|
|
158
|
+
}
|
|
159
|
+
function terminalPreloaderLines(snapshot, tick) {
|
|
160
|
+
const bodyRows = Math.max(1, snapshot.rows - 1);
|
|
161
|
+
const existing = snapshot.lines.slice(0, bodyRows).map((text, index) => line(text, { fg: RIG_UI.ink, styledText: snapshot.styledLines?.[index] }));
|
|
162
|
+
const sprite = fleetRows(tick);
|
|
163
|
+
const top = Math.max(0, Math.floor((bodyRows - sprite.length) / 2));
|
|
164
|
+
const left = Math.max(0, Math.floor((snapshot.cols - FLEET_GRID_WIDTH) / 2));
|
|
165
|
+
for (let index = 0;index < sprite.length && top + index < existing.length; index += 1) {
|
|
166
|
+
const targetIndex = top + index;
|
|
167
|
+
const current = snapshot.lines[targetIndex] ?? "";
|
|
168
|
+
if (current.trim())
|
|
169
|
+
continue;
|
|
170
|
+
existing[targetIndex] = line(`${" ".repeat(left)}${sprite[index] ?? ""}`, {
|
|
171
|
+
fg: index >= 7 && index <= 18 ? RIG_UI.lime : RIG_UI.limeDim
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return existing;
|
|
175
|
+
}
|
|
176
|
+
function terminalLines(snapshot, tick) {
|
|
83
177
|
const hasVisibleOutput = snapshot.lines.some((entry) => entry.trim().length > 0);
|
|
84
|
-
const body = !hasVisibleOutput && snapshot.status === "running" ? [line(`starting bundled Pi for ${snapshot.runId.slice(0, 8)}\u2026`, { fg: RIG_UI.ink3 })] : snapshot.lines.map((text, index) => line(text, { fg: RIG_UI.ink, styledText: snapshot.styledLines?.[index] }));
|
|
178
|
+
const body = shouldShowTerminalPreloader(snapshot) ? terminalPreloaderLines(snapshot, tick) : !hasVisibleOutput && snapshot.status === "running" ? [line(`starting bundled Pi for ${snapshot.runId.slice(0, 8)}\u2026`, { fg: RIG_UI.ink3 })] : snapshot.lines.map((text, index) => line(text, { fg: RIG_UI.ink, styledText: snapshot.styledLines?.[index] }));
|
|
85
179
|
return [...body, terminalRail(snapshot)];
|
|
86
180
|
}
|
|
87
181
|
function renderHandoffScene(state) {
|
|
@@ -97,7 +191,7 @@ function renderHandoffScene(state) {
|
|
|
97
191
|
left: 0,
|
|
98
192
|
width: terminal.cols,
|
|
99
193
|
height: terminal.rows,
|
|
100
|
-
lines: terminalLines(terminal),
|
|
194
|
+
lines: terminalLines(terminal, state.tick),
|
|
101
195
|
backgroundColor: "#050607",
|
|
102
196
|
backgroundAlpha: 128,
|
|
103
197
|
opacity: 1,
|
|
@@ -115,30 +209,51 @@ function renderHandoffScene(state) {
|
|
|
115
209
|
const handoff = handoffState(state);
|
|
116
210
|
const runId = handoff?.runId ?? terminal?.runId ?? (typeof state.data.selectedRunId === "string" ? state.data.selectedRunId : undefined);
|
|
117
211
|
const status = handoff?.status ?? (runId ? "preparing" : "select-run");
|
|
118
|
-
const message = handoff?.message ?? (runId ? "ready to start bundled Pi inside
|
|
212
|
+
const message = handoff?.message ?? (runId ? "ready to start bundled Pi inside this app" : "choose a run before entering Pi");
|
|
119
213
|
const hasRun = Boolean(runId);
|
|
214
|
+
const selected = Math.max(0, Math.min(1, state.selection.index));
|
|
120
215
|
return makeSceneFrame({
|
|
121
216
|
scene: "handoff",
|
|
122
217
|
title: "Pi handoff",
|
|
123
218
|
lines: [
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
blank(),
|
|
127
|
-
...hasRun ? [
|
|
128
|
-
line(` run ${runId}`, { fg: RIG_UI.limeDim }),
|
|
129
|
-
line(` status ${status}`, { fg: status === "failed" ? RIG_UI.red : RIG_UI.cyan }),
|
|
130
|
-
line(` message ${message}`, { fg: RIG_UI.ink2 }),
|
|
131
|
-
blank(),
|
|
132
|
-
deckRow({ label: "attach", detail: "open bundled Pi", index: 0, active: true })
|
|
133
|
-
] : [
|
|
134
|
-
line(" run none selected", { fg: RIG_UI.yellow }),
|
|
135
|
-
line(` message ${message}`, { fg: RIG_UI.ink2 }),
|
|
136
|
-
blank(),
|
|
137
|
-
deckRow({ label: "runs", detail: "select a run first", index: 0, active: true })
|
|
138
|
-
]
|
|
219
|
+
line("", { fg: RIG_UI.ink4 }),
|
|
220
|
+
line(` ${hasRun ? "pi" : "attach"} \xB7 ${status}`, { fg: RIG_UI.ink3 })
|
|
139
221
|
],
|
|
140
|
-
|
|
141
|
-
|
|
222
|
+
panels: [{
|
|
223
|
+
id: "pi-handoff",
|
|
224
|
+
top: 0,
|
|
225
|
+
width: 96,
|
|
226
|
+
height: 16,
|
|
227
|
+
lines: [
|
|
228
|
+
line(hasRun ? "Bundled Pi handoff" : "Select a run before entering Pi", { fg: RIG_UI.ink, bold: true }),
|
|
229
|
+
...hasRun ? [
|
|
230
|
+
line(`run ${runId}`, { fg: RIG_UI.limeDim }),
|
|
231
|
+
line(`status ${status}`, { fg: status === "failed" ? RIG_UI.red : RIG_UI.cyan }),
|
|
232
|
+
line(`message ${message}`, { fg: RIG_UI.ink2 }),
|
|
233
|
+
line("", { fg: RIG_UI.ink3 }),
|
|
234
|
+
line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
|
|
235
|
+
deckRow({ label: "attach", detail: "open bundled Pi", index: 0, active: selected === 0, activateOnClick: true }),
|
|
236
|
+
deckRow({ label: "runs", detail: "return to fleet", index: 1, active: selected === 1, activateOnClick: true })
|
|
237
|
+
] : [
|
|
238
|
+
line(`message ${message}`, { fg: RIG_UI.ink2 }),
|
|
239
|
+
line("", { fg: RIG_UI.ink3 }),
|
|
240
|
+
line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
|
|
241
|
+
deckRow({ label: "runs", detail: "select a run first", index: 0, active: selected === 0, activateOnClick: true }),
|
|
242
|
+
deckRow({ label: "tasks", detail: "open tasks to dispatch work", index: 1, active: selected === 1, activateOnClick: true })
|
|
243
|
+
]
|
|
244
|
+
],
|
|
245
|
+
backgroundColor: RIG_UI.panel,
|
|
246
|
+
backgroundAlpha: 184,
|
|
247
|
+
opacity: 1,
|
|
248
|
+
border: false,
|
|
249
|
+
chrome: "ad-terminal",
|
|
250
|
+
headerText: hasRun ? "pi handoff" : "attach",
|
|
251
|
+
headerHeight: 3,
|
|
252
|
+
paddingX: 3,
|
|
253
|
+
paddingY: 1
|
|
254
|
+
}],
|
|
255
|
+
footer: { run: runId ? `${runId.slice(0, 8)} handoff` : "select run", message: "enter/click action" },
|
|
256
|
+
hideTypeBar: true,
|
|
142
257
|
live: true
|
|
143
258
|
});
|
|
144
259
|
}
|