@h-rig/cli 0.0.6-alpha.76 → 0.0.6-alpha.78
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/rig.js +10130 -9246
- package/dist/src/app/board.js +1783 -0
- package/dist/src/app/drone-ui.js +294 -0
- package/dist/src/{commands/_tui-theme.js → app/theme.js} +16 -1
- package/dist/src/commands/_async-ui.js +74 -3
- package/dist/src/commands/_cli-format.js +107 -29
- package/dist/src/commands/_doctor-checks.js +3 -1
- package/dist/src/commands/_help-catalog.js +8 -70
- package/dist/src/commands/_operator-view.js +184 -7
- package/dist/src/commands/_pi-frontend.js +184 -7
- package/dist/src/commands/_preflight.js +3 -1
- package/dist/src/commands/_server-client.js +3 -1
- package/dist/src/commands/_snapshot-upload.js +3 -1
- package/dist/src/commands/_task-picker.js +132 -7
- package/dist/src/commands/browser.js +306 -30
- package/dist/src/commands/connect.js +146 -20
- package/dist/src/commands/doctor.js +77 -4
- package/dist/src/commands/github.js +77 -4
- package/dist/src/commands/inbox.js +171 -88
- package/dist/src/commands/init.js +349 -9
- package/dist/src/commands/inspect.js +77 -4
- package/dist/src/commands/run.js +214 -28
- package/dist/src/commands/server.js +149 -21
- package/dist/src/commands/setup.js +77 -4
- package/dist/src/commands/stats.js +109 -107
- package/dist/src/commands/task-report-bug.js +231 -40
- package/dist/src/commands/task-run-driver.js +3 -1
- package/dist/src/commands/task.js +355 -184
- package/dist/src/commands.js +10126 -9242
- package/dist/src/index.js +10131 -9247
- package/package.json +8 -9
- package/dist/src/commands/_operator-board.js +0 -730
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/cli/src/app/drone-ui.ts
|
|
3
|
+
import { ProcessTerminal, TUI, Text, Input, SelectList, matchesKey } from "@earendil-works/pi-tui";
|
|
4
|
+
|
|
5
|
+
// packages/cli/src/app/theme.ts
|
|
6
|
+
var RIG_PALETTE = {
|
|
7
|
+
ink: "#f2f3f6",
|
|
8
|
+
ink2: "#aeb0ba",
|
|
9
|
+
ink3: "#6c6e79",
|
|
10
|
+
ink4: "#44464f",
|
|
11
|
+
accent: "#ccff4d",
|
|
12
|
+
accentDim: "#a9d63f",
|
|
13
|
+
cyan: "#56d8ff",
|
|
14
|
+
red: "#ff5d5d",
|
|
15
|
+
yellow: "#ffd24d"
|
|
16
|
+
};
|
|
17
|
+
function hexToRgb(hex) {
|
|
18
|
+
const value = hex.replace("#", "");
|
|
19
|
+
return [
|
|
20
|
+
Number.parseInt(value.slice(0, 2), 16),
|
|
21
|
+
Number.parseInt(value.slice(2, 4), 16),
|
|
22
|
+
Number.parseInt(value.slice(4, 6), 16)
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
function fg(hex) {
|
|
26
|
+
const [r, g, b] = hexToRgb(hex);
|
|
27
|
+
return (text) => `\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`;
|
|
28
|
+
}
|
|
29
|
+
var ink = fg(RIG_PALETTE.ink);
|
|
30
|
+
var ink2 = fg(RIG_PALETTE.ink2);
|
|
31
|
+
var ink3 = fg(RIG_PALETTE.ink3);
|
|
32
|
+
var ink4 = fg(RIG_PALETTE.ink4);
|
|
33
|
+
var accent = fg(RIG_PALETTE.accent);
|
|
34
|
+
var accentDim = fg(RIG_PALETTE.accentDim);
|
|
35
|
+
var cyan = fg(RIG_PALETTE.cyan);
|
|
36
|
+
var red = fg(RIG_PALETTE.red);
|
|
37
|
+
var yellow = fg(RIG_PALETTE.yellow);
|
|
38
|
+
function bold(text) {
|
|
39
|
+
return `\x1B[1m${text}\x1B[22m`;
|
|
40
|
+
}
|
|
41
|
+
var DRONE_ART = [
|
|
42
|
+
" .-=-. .-=-. ",
|
|
43
|
+
" ( !!! ) ( !!! ) ",
|
|
44
|
+
" '-=-'._ _.'-=-' ",
|
|
45
|
+
" '._ _.' ",
|
|
46
|
+
" '=$$$$$$$=.' ",
|
|
47
|
+
" =$$$$$$$$$$$= ",
|
|
48
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
49
|
+
" $$$@@ @@$$$ ",
|
|
50
|
+
" $$@ ? @$$$ ",
|
|
51
|
+
" $$$@ '-' @$$$ ",
|
|
52
|
+
" $$$@@ @@$$$ ",
|
|
53
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
54
|
+
" =$$$$$$$$$$$= ",
|
|
55
|
+
" '=$$$$$$$=.' ",
|
|
56
|
+
" _.' '._ ",
|
|
57
|
+
" .-=-.' '.-=-. ",
|
|
58
|
+
" ( !!! ) ( !!! ) ",
|
|
59
|
+
" '-=-' '-=-' "
|
|
60
|
+
];
|
|
61
|
+
var EYE_FRAMES = ["@", "o", "."];
|
|
62
|
+
var DRONE_WIDTH = DRONE_ART[0].length;
|
|
63
|
+
var DRONE_HEIGHT = DRONE_ART.length;
|
|
64
|
+
var MICRO_BLADES = ["---", "\\\\\\", "|||", "///"];
|
|
65
|
+
function microDroneFrame(tick) {
|
|
66
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
67
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
68
|
+
return `(${blade})${eye}(${blade})`;
|
|
69
|
+
}
|
|
70
|
+
var MICRO_DRONE_FRAMES = Array.from({ length: 12 }, (_, index) => microDroneFrame(index));
|
|
71
|
+
function renderMicroDroneFrame(tick) {
|
|
72
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
73
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
74
|
+
return `${ink4("(")}${cyan(blade)}${ink4(")")}${bold(accent(eye))}${ink4("(")}${cyan(blade)}${ink4(")")}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// packages/cli/src/commands/_spinner.ts
|
|
78
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
79
|
+
function createTtySpinner(input) {
|
|
80
|
+
const output = input.output ?? process.stdout;
|
|
81
|
+
const isTty = output.isTTY === true;
|
|
82
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
83
|
+
let label = input.label;
|
|
84
|
+
let frame = 0;
|
|
85
|
+
let paused = false;
|
|
86
|
+
let stopped = false;
|
|
87
|
+
let lastPrintedLabel = "";
|
|
88
|
+
const render = () => {
|
|
89
|
+
if (stopped || paused)
|
|
90
|
+
return;
|
|
91
|
+
if (!isTty) {
|
|
92
|
+
if (label !== lastPrintedLabel) {
|
|
93
|
+
output.write(`${label}
|
|
94
|
+
`);
|
|
95
|
+
lastPrintedLabel = label;
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
frame = (frame + 1) % frames.length;
|
|
100
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
101
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
102
|
+
};
|
|
103
|
+
const clearLine = () => {
|
|
104
|
+
if (isTty)
|
|
105
|
+
output.write("\r\x1B[2K");
|
|
106
|
+
};
|
|
107
|
+
render();
|
|
108
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
|
|
109
|
+
return {
|
|
110
|
+
setLabel(next) {
|
|
111
|
+
label = next;
|
|
112
|
+
render();
|
|
113
|
+
},
|
|
114
|
+
pause() {
|
|
115
|
+
paused = true;
|
|
116
|
+
clearLine();
|
|
117
|
+
},
|
|
118
|
+
resume() {
|
|
119
|
+
if (stopped)
|
|
120
|
+
return;
|
|
121
|
+
paused = false;
|
|
122
|
+
render();
|
|
123
|
+
},
|
|
124
|
+
stop(finalLine) {
|
|
125
|
+
if (stopped)
|
|
126
|
+
return;
|
|
127
|
+
stopped = true;
|
|
128
|
+
if (timer)
|
|
129
|
+
clearInterval(timer);
|
|
130
|
+
clearLine();
|
|
131
|
+
if (finalLine)
|
|
132
|
+
output.write(`${finalLine}
|
|
133
|
+
`);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// packages/cli/src/app/drone-ui.ts
|
|
139
|
+
var isTty = () => Boolean(process.stdout.isTTY);
|
|
140
|
+
function hairline(width = Math.min(process.stdout.columns ?? 80, 100)) {
|
|
141
|
+
return ink4("\u2500".repeat(Math.max(10, width)));
|
|
142
|
+
}
|
|
143
|
+
function droneIntro(title, subtitle) {
|
|
144
|
+
console.log("");
|
|
145
|
+
console.log(` ${accent("\u258D")}${bold(ink(title))}${subtitle ? ink3(` \u2014 ${subtitle}`) : ""}`);
|
|
146
|
+
console.log(hairline());
|
|
147
|
+
}
|
|
148
|
+
function droneOutro(text) {
|
|
149
|
+
console.log(hairline());
|
|
150
|
+
console.log(` ${accent("\u25C6")} ${ink2(text)}`);
|
|
151
|
+
console.log("");
|
|
152
|
+
}
|
|
153
|
+
function droneNote(message, title) {
|
|
154
|
+
if (title)
|
|
155
|
+
console.log(` ${accentDim("\u25C7")} ${bold(ink2(title))}`);
|
|
156
|
+
for (const line of message.split(`
|
|
157
|
+
`)) {
|
|
158
|
+
console.log(` ${ink4("\u2502")} ${line}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function droneStep(text) {
|
|
162
|
+
console.log(` ${accent("\u203A")} ${ink(text)}`);
|
|
163
|
+
}
|
|
164
|
+
function droneInfo(text) {
|
|
165
|
+
console.log(` ${cyan("\xB7")} ${ink2(text)}`);
|
|
166
|
+
}
|
|
167
|
+
function droneWarn(text) {
|
|
168
|
+
console.log(` ${yellow("\u25B2")} ${ink2(text)}`);
|
|
169
|
+
}
|
|
170
|
+
function droneError(text) {
|
|
171
|
+
console.log(` ${red("\u2716")} ${ink2(text)}`);
|
|
172
|
+
}
|
|
173
|
+
function droneCancel(text) {
|
|
174
|
+
console.log(` ${red("\u2716")} ${ink3(text)}`);
|
|
175
|
+
}
|
|
176
|
+
function droneSpinner() {
|
|
177
|
+
let active = null;
|
|
178
|
+
return {
|
|
179
|
+
start(message) {
|
|
180
|
+
active = createTtySpinner({
|
|
181
|
+
label: message,
|
|
182
|
+
frames: MICRO_DRONE_FRAMES,
|
|
183
|
+
styleFrame: (frame) => renderMicroDroneFrame(Math.max(0, MICRO_DRONE_FRAMES.indexOf(frame)))
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
stop(message) {
|
|
187
|
+
active?.stop(message ? ` ${accent("\u25C6")} ${ink2(message)}` : undefined);
|
|
188
|
+
active = null;
|
|
189
|
+
},
|
|
190
|
+
error(message) {
|
|
191
|
+
active?.stop(message ? ` ${red("\u2716")} ${ink2(message)}` : undefined);
|
|
192
|
+
active = null;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
var SELECT_THEME = {
|
|
197
|
+
selectedPrefix: (text) => accent(text),
|
|
198
|
+
selectedText: (text) => bold(ink(text)),
|
|
199
|
+
description: (text) => ink3(text),
|
|
200
|
+
scrollInfo: (text) => ink4(text),
|
|
201
|
+
noMatch: (text) => ink3(text)
|
|
202
|
+
};
|
|
203
|
+
async function runMiniTui(build) {
|
|
204
|
+
const terminal = new ProcessTerminal;
|
|
205
|
+
const tui = new TUI(terminal);
|
|
206
|
+
let settled = false;
|
|
207
|
+
return await new Promise((resolve) => {
|
|
208
|
+
const finish = (result) => {
|
|
209
|
+
if (settled)
|
|
210
|
+
return;
|
|
211
|
+
settled = true;
|
|
212
|
+
tui.stop();
|
|
213
|
+
resolve(result);
|
|
214
|
+
};
|
|
215
|
+
build(tui, finish);
|
|
216
|
+
tui.start();
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
async function droneSelect(input) {
|
|
220
|
+
if (!isTty() || input.options.length === 0) {
|
|
221
|
+
return input.initialValue ?? input.options[0]?.value ?? null;
|
|
222
|
+
}
|
|
223
|
+
return runMiniTui((tui, finish) => {
|
|
224
|
+
tui.addChild(new Text(` ${accent("\u258D")}${bold(ink(input.message))}`));
|
|
225
|
+
const items = input.options.map((option) => ({
|
|
226
|
+
value: option.value,
|
|
227
|
+
label: option.label,
|
|
228
|
+
...option.hint ? { description: option.hint } : {}
|
|
229
|
+
}));
|
|
230
|
+
const list = new SelectList(items, Math.min(items.length, 12), SELECT_THEME);
|
|
231
|
+
const initialIndex = input.initialValue ? items.findIndex((item) => item.value === input.initialValue) : -1;
|
|
232
|
+
if (initialIndex > 0)
|
|
233
|
+
list.setSelectedIndex(initialIndex);
|
|
234
|
+
list.onSelect = (item) => finish(item.value);
|
|
235
|
+
list.onCancel = () => finish(null);
|
|
236
|
+
tui.addChild(list);
|
|
237
|
+
tui.addChild(new Text(ink4(" \u2191\u2193 navigate \xB7 enter select \xB7 esc cancel")));
|
|
238
|
+
tui.setFocus(list);
|
|
239
|
+
tui.addInputListener((data) => {
|
|
240
|
+
if (matchesKey(data, "ctrl+c") || matchesKey(data, "escape")) {
|
|
241
|
+
finish(null);
|
|
242
|
+
return { consume: true };
|
|
243
|
+
}
|
|
244
|
+
return;
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
async function droneText(input) {
|
|
249
|
+
if (!isTty())
|
|
250
|
+
return input.initialValue ?? null;
|
|
251
|
+
return runMiniTui((tui, finish) => {
|
|
252
|
+
tui.addChild(new Text(` ${accent("\u258D")}${bold(ink(input.message))}${input.placeholder ? ink4(` (${input.placeholder})`) : ""}`));
|
|
253
|
+
const field = new Input;
|
|
254
|
+
if (input.initialValue)
|
|
255
|
+
field.setValue(input.initialValue);
|
|
256
|
+
field.onSubmit = (value) => finish(value);
|
|
257
|
+
field.onEscape = () => finish(null);
|
|
258
|
+
tui.addChild(field);
|
|
259
|
+
tui.addChild(new Text(ink4(" enter submit \xB7 esc cancel")));
|
|
260
|
+
tui.setFocus(field);
|
|
261
|
+
tui.addInputListener((data) => {
|
|
262
|
+
if (matchesKey(data, "ctrl+c")) {
|
|
263
|
+
finish(null);
|
|
264
|
+
return { consume: true };
|
|
265
|
+
}
|
|
266
|
+
return;
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
async function droneConfirm(input) {
|
|
271
|
+
const answer = await droneSelect({
|
|
272
|
+
message: input.message,
|
|
273
|
+
options: [
|
|
274
|
+
{ value: "yes", label: "Yes" },
|
|
275
|
+
{ value: "no", label: "No" }
|
|
276
|
+
],
|
|
277
|
+
initialValue: input.initialValue === false ? "no" : "yes"
|
|
278
|
+
});
|
|
279
|
+
return answer === null ? null : answer === "yes";
|
|
280
|
+
}
|
|
281
|
+
export {
|
|
282
|
+
droneWarn,
|
|
283
|
+
droneText,
|
|
284
|
+
droneStep,
|
|
285
|
+
droneSpinner,
|
|
286
|
+
droneSelect,
|
|
287
|
+
droneOutro,
|
|
288
|
+
droneNote,
|
|
289
|
+
droneIntro,
|
|
290
|
+
droneInfo,
|
|
291
|
+
droneError,
|
|
292
|
+
droneConfirm,
|
|
293
|
+
droneCancel
|
|
294
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// packages/cli/src/
|
|
2
|
+
// packages/cli/src/app/theme.ts
|
|
3
3
|
var RIG_PALETTE = {
|
|
4
4
|
ink: "#f2f3f6",
|
|
5
5
|
ink2: "#aeb0ba",
|
|
@@ -114,11 +114,25 @@ function renderDroneFrame(tick) {
|
|
|
114
114
|
}
|
|
115
115
|
var DRONE_WIDTH = DRONE_ART[0].length;
|
|
116
116
|
var DRONE_HEIGHT = DRONE_ART.length;
|
|
117
|
+
var MICRO_BLADES = ["---", "\\\\\\", "|||", "///"];
|
|
118
|
+
function microDroneFrame(tick) {
|
|
119
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
120
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
121
|
+
return `(${blade})${eye}(${blade})`;
|
|
122
|
+
}
|
|
123
|
+
var MICRO_DRONE_FRAMES = Array.from({ length: 12 }, (_, index) => microDroneFrame(index));
|
|
124
|
+
function renderMicroDroneFrame(tick) {
|
|
125
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
126
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
127
|
+
return `${ink4("(")}${cyan(blade)}${ink4(")")}${bold(accent(eye))}${ink4("(")}${cyan(blade)}${ink4(")")}`;
|
|
128
|
+
}
|
|
117
129
|
export {
|
|
118
130
|
yellow,
|
|
119
131
|
statusColor,
|
|
132
|
+
renderMicroDroneFrame,
|
|
120
133
|
renderDroneFrame,
|
|
121
134
|
red,
|
|
135
|
+
microDroneFrame,
|
|
122
136
|
ink4,
|
|
123
137
|
ink3,
|
|
124
138
|
ink2,
|
|
@@ -130,6 +144,7 @@ export {
|
|
|
130
144
|
accent,
|
|
131
145
|
RIG_SPINNER_FRAMES,
|
|
132
146
|
RIG_PALETTE,
|
|
147
|
+
MICRO_DRONE_FRAMES,
|
|
133
148
|
DRONE_WIDTH,
|
|
134
149
|
DRONE_HEIGHT
|
|
135
150
|
};
|
|
@@ -91,8 +91,79 @@ var RESUMABLE_RUN_STATUSES = new Set([
|
|
|
91
91
|
"needs_attention"
|
|
92
92
|
]);
|
|
93
93
|
|
|
94
|
+
// packages/cli/src/app/theme.ts
|
|
95
|
+
var RIG_PALETTE = {
|
|
96
|
+
ink: "#f2f3f6",
|
|
97
|
+
ink2: "#aeb0ba",
|
|
98
|
+
ink3: "#6c6e79",
|
|
99
|
+
ink4: "#44464f",
|
|
100
|
+
accent: "#ccff4d",
|
|
101
|
+
accentDim: "#a9d63f",
|
|
102
|
+
cyan: "#56d8ff",
|
|
103
|
+
red: "#ff5d5d",
|
|
104
|
+
yellow: "#ffd24d"
|
|
105
|
+
};
|
|
106
|
+
function hexToRgb(hex) {
|
|
107
|
+
const value = hex.replace("#", "");
|
|
108
|
+
return [
|
|
109
|
+
Number.parseInt(value.slice(0, 2), 16),
|
|
110
|
+
Number.parseInt(value.slice(2, 4), 16),
|
|
111
|
+
Number.parseInt(value.slice(4, 6), 16)
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
function fg(hex) {
|
|
115
|
+
const [r, g, b] = hexToRgb(hex);
|
|
116
|
+
return (text) => `\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`;
|
|
117
|
+
}
|
|
118
|
+
var ink = fg(RIG_PALETTE.ink);
|
|
119
|
+
var ink2 = fg(RIG_PALETTE.ink2);
|
|
120
|
+
var ink3 = fg(RIG_PALETTE.ink3);
|
|
121
|
+
var ink4 = fg(RIG_PALETTE.ink4);
|
|
122
|
+
var accent = fg(RIG_PALETTE.accent);
|
|
123
|
+
var accentDim = fg(RIG_PALETTE.accentDim);
|
|
124
|
+
var cyan = fg(RIG_PALETTE.cyan);
|
|
125
|
+
var red = fg(RIG_PALETTE.red);
|
|
126
|
+
var yellow = fg(RIG_PALETTE.yellow);
|
|
127
|
+
function bold(text) {
|
|
128
|
+
return `\x1B[1m${text}\x1B[22m`;
|
|
129
|
+
}
|
|
130
|
+
var DRONE_ART = [
|
|
131
|
+
" .-=-. .-=-. ",
|
|
132
|
+
" ( !!! ) ( !!! ) ",
|
|
133
|
+
" '-=-'._ _.'-=-' ",
|
|
134
|
+
" '._ _.' ",
|
|
135
|
+
" '=$$$$$$$=.' ",
|
|
136
|
+
" =$$$$$$$$$$$= ",
|
|
137
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
138
|
+
" $$$@@ @@$$$ ",
|
|
139
|
+
" $$@ ? @$$$ ",
|
|
140
|
+
" $$$@ '-' @$$$ ",
|
|
141
|
+
" $$$@@ @@$$$ ",
|
|
142
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
143
|
+
" =$$$$$$$$$$$= ",
|
|
144
|
+
" '=$$$$$$$=.' ",
|
|
145
|
+
" _.' '._ ",
|
|
146
|
+
" .-=-.' '.-=-. ",
|
|
147
|
+
" ( !!! ) ( !!! ) ",
|
|
148
|
+
" '-=-' '-=-' "
|
|
149
|
+
];
|
|
150
|
+
var EYE_FRAMES = ["@", "o", "."];
|
|
151
|
+
var DRONE_WIDTH = DRONE_ART[0].length;
|
|
152
|
+
var DRONE_HEIGHT = DRONE_ART.length;
|
|
153
|
+
var MICRO_BLADES = ["---", "\\\\\\", "|||", "///"];
|
|
154
|
+
function microDroneFrame(tick) {
|
|
155
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
156
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
157
|
+
return `(${blade})${eye}(${blade})`;
|
|
158
|
+
}
|
|
159
|
+
var MICRO_DRONE_FRAMES = Array.from({ length: 12 }, (_, index) => microDroneFrame(index));
|
|
160
|
+
function renderMicroDroneFrame(tick) {
|
|
161
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
162
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
163
|
+
return `${ink4("(")}${cyan(blade)}${ink4(")")}${bold(accent(eye))}${ink4("(")}${cyan(blade)}${ink4(")")}`;
|
|
164
|
+
}
|
|
165
|
+
|
|
94
166
|
// packages/cli/src/commands/_async-ui.ts
|
|
95
|
-
var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
96
167
|
var DONE_SYMBOL = pc.green("\u25C7");
|
|
97
168
|
var FAIL_SYMBOL = pc.red("\u25A0");
|
|
98
169
|
var activeUpdate = null;
|
|
@@ -126,8 +197,8 @@ async function withSpinner(label, work, options = {}) {
|
|
|
126
197
|
const spinner = createTtySpinner({
|
|
127
198
|
label,
|
|
128
199
|
output,
|
|
129
|
-
frames:
|
|
130
|
-
styleFrame: (frame) =>
|
|
200
|
+
frames: MICRO_DRONE_FRAMES,
|
|
201
|
+
styleFrame: (frame) => renderMicroDroneFrame(Math.max(0, MICRO_DRONE_FRAMES.indexOf(frame)))
|
|
131
202
|
});
|
|
132
203
|
const update = (next) => {
|
|
133
204
|
lastLabel = next;
|
|
@@ -1,7 +1,88 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// packages/cli/src/app/drone-ui.ts
|
|
3
|
+
import { ProcessTerminal, TUI, Text, Input, SelectList, matchesKey } from "@earendil-works/pi-tui";
|
|
4
|
+
|
|
5
|
+
// packages/cli/src/app/theme.ts
|
|
6
|
+
var RIG_PALETTE = {
|
|
7
|
+
ink: "#f2f3f6",
|
|
8
|
+
ink2: "#aeb0ba",
|
|
9
|
+
ink3: "#6c6e79",
|
|
10
|
+
ink4: "#44464f",
|
|
11
|
+
accent: "#ccff4d",
|
|
12
|
+
accentDim: "#a9d63f",
|
|
13
|
+
cyan: "#56d8ff",
|
|
14
|
+
red: "#ff5d5d",
|
|
15
|
+
yellow: "#ffd24d"
|
|
16
|
+
};
|
|
17
|
+
function hexToRgb(hex) {
|
|
18
|
+
const value = hex.replace("#", "");
|
|
19
|
+
return [
|
|
20
|
+
Number.parseInt(value.slice(0, 2), 16),
|
|
21
|
+
Number.parseInt(value.slice(2, 4), 16),
|
|
22
|
+
Number.parseInt(value.slice(4, 6), 16)
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
function fg(hex) {
|
|
26
|
+
const [r, g, b] = hexToRgb(hex);
|
|
27
|
+
return (text) => `\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`;
|
|
28
|
+
}
|
|
29
|
+
var ink = fg(RIG_PALETTE.ink);
|
|
30
|
+
var ink2 = fg(RIG_PALETTE.ink2);
|
|
31
|
+
var ink3 = fg(RIG_PALETTE.ink3);
|
|
32
|
+
var ink4 = fg(RIG_PALETTE.ink4);
|
|
33
|
+
var accent = fg(RIG_PALETTE.accent);
|
|
34
|
+
var accentDim = fg(RIG_PALETTE.accentDim);
|
|
35
|
+
var cyan = fg(RIG_PALETTE.cyan);
|
|
36
|
+
var red = fg(RIG_PALETTE.red);
|
|
37
|
+
var yellow = fg(RIG_PALETTE.yellow);
|
|
38
|
+
function bold(text) {
|
|
39
|
+
return `\x1B[1m${text}\x1B[22m`;
|
|
40
|
+
}
|
|
41
|
+
var DRONE_ART = [
|
|
42
|
+
" .-=-. .-=-. ",
|
|
43
|
+
" ( !!! ) ( !!! ) ",
|
|
44
|
+
" '-=-'._ _.'-=-' ",
|
|
45
|
+
" '._ _.' ",
|
|
46
|
+
" '=$$$$$$$=.' ",
|
|
47
|
+
" =$$$$$$$$$$$= ",
|
|
48
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
49
|
+
" $$$@@ @@$$$ ",
|
|
50
|
+
" $$@ ? @$$$ ",
|
|
51
|
+
" $$$@ '-' @$$$ ",
|
|
52
|
+
" $$$@@ @@$$$ ",
|
|
53
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
54
|
+
" =$$$$$$$$$$$= ",
|
|
55
|
+
" '=$$$$$$$=.' ",
|
|
56
|
+
" _.' '._ ",
|
|
57
|
+
" .-=-.' '.-=-. ",
|
|
58
|
+
" ( !!! ) ( !!! ) ",
|
|
59
|
+
" '-=-' '-=-' "
|
|
60
|
+
];
|
|
61
|
+
var EYE_FRAMES = ["@", "o", "."];
|
|
62
|
+
var DRONE_WIDTH = DRONE_ART[0].length;
|
|
63
|
+
var DRONE_HEIGHT = DRONE_ART.length;
|
|
64
|
+
var MICRO_BLADES = ["---", "\\\\\\", "|||", "///"];
|
|
65
|
+
function microDroneFrame(tick) {
|
|
66
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
67
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
68
|
+
return `(${blade})${eye}(${blade})`;
|
|
69
|
+
}
|
|
70
|
+
var MICRO_DRONE_FRAMES = Array.from({ length: 12 }, (_, index) => microDroneFrame(index));
|
|
71
|
+
|
|
72
|
+
// packages/cli/src/app/drone-ui.ts
|
|
73
|
+
function droneNote(message, title) {
|
|
74
|
+
if (title)
|
|
75
|
+
console.log(` ${accentDim("\u25C7")} ${bold(ink2(title))}`);
|
|
76
|
+
for (const line of message.split(`
|
|
77
|
+
`)) {
|
|
78
|
+
console.log(` ${ink4("\u2502")} ${line}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
2
82
|
// packages/cli/src/commands/_cli-format.ts
|
|
3
|
-
import { log, note } from "@clack/prompts";
|
|
4
83
|
import pc from "picocolors";
|
|
84
|
+
var themeDim = (value) => ink3(value);
|
|
85
|
+
var themeFaint = (value) => ink4(value);
|
|
5
86
|
function stringField(record, key, fallback = "") {
|
|
6
87
|
const value = record[key];
|
|
7
88
|
return typeof value === "string" && value.trim() ? value.trim() : fallback;
|
|
@@ -30,15 +111,15 @@ function pad(value, width) {
|
|
|
30
111
|
}
|
|
31
112
|
function statusColor(status) {
|
|
32
113
|
const normalized = status.toLowerCase();
|
|
33
|
-
if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved"].includes(normalized))
|
|
34
|
-
return
|
|
114
|
+
if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved", "running"].includes(normalized))
|
|
115
|
+
return accent;
|
|
35
116
|
if (["failed", "needs_attention", "needs-attention", "blocked", "error", "rejected"].includes(normalized))
|
|
36
|
-
return
|
|
37
|
-
if (["
|
|
38
|
-
return
|
|
39
|
-
if (["ready", "open", "queued", "created", "
|
|
40
|
-
return
|
|
41
|
-
return
|
|
117
|
+
return red;
|
|
118
|
+
if (["reviewing", "validating", "in_progress", "in-progress", "remote", "preparing", "closing-out"].includes(normalized))
|
|
119
|
+
return cyan;
|
|
120
|
+
if (["ready", "open", "queued", "created", "local", "pending"].includes(normalized))
|
|
121
|
+
return yellow;
|
|
122
|
+
return themeDim;
|
|
42
123
|
}
|
|
43
124
|
function compactDate(value) {
|
|
44
125
|
if (!value.trim())
|
|
@@ -87,27 +168,24 @@ function printFormattedOutput(message, options = {}) {
|
|
|
87
168
|
console.log(message);
|
|
88
169
|
return;
|
|
89
170
|
}
|
|
90
|
-
|
|
91
|
-
note(message, options.title);
|
|
92
|
-
else
|
|
93
|
-
log.message(message);
|
|
171
|
+
droneNote(message, options.title);
|
|
94
172
|
}
|
|
95
173
|
function formatStatusPill(status) {
|
|
96
174
|
const label = status || "unknown";
|
|
97
175
|
return statusColor(label)(`\u25CF ${label}`);
|
|
98
176
|
}
|
|
99
177
|
function formatSection(title, subtitle) {
|
|
100
|
-
return `${pc.bold(
|
|
178
|
+
return `${pc.bold(accent("\u25C6"))} ${pc.bold(title)}${subtitle ? themeDim(` \u2014 ${subtitle}`) : ""}`;
|
|
101
179
|
}
|
|
102
180
|
function formatSuccessCard(title, rows = []) {
|
|
103
|
-
const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${
|
|
181
|
+
const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${themeFaint("\u2502")} ${themeDim(key.padEnd(12))} ${value}`);
|
|
104
182
|
return [formatSection(title), ...body].join(`
|
|
105
183
|
`);
|
|
106
184
|
}
|
|
107
185
|
function formatNextSteps(steps) {
|
|
108
186
|
if (steps.length === 0)
|
|
109
187
|
return [];
|
|
110
|
-
return [pc.bold("Next"), ...steps.map((step) => `${
|
|
188
|
+
return [pc.bold("Next"), ...steps.map((step) => `${accent("\u203A")} ${step}`)];
|
|
111
189
|
}
|
|
112
190
|
function formatTaskList(tasks, options = {}) {
|
|
113
191
|
if (options.raw)
|
|
@@ -129,8 +207,8 @@ function formatTaskList(tasks, options = {}) {
|
|
|
129
207
|
const statusWidth = Math.min(16, Math.max(6, ...rows.map((row) => row.status.length)));
|
|
130
208
|
const header = `${pc.bold(pad("TASK", idWidth))} ${pc.bold(pad("STATUS", statusWidth))} ${pc.bold("TITLE")}`;
|
|
131
209
|
const body = rows.map((row) => {
|
|
132
|
-
const labels = row.labels.length > 0 ?
|
|
133
|
-
const source = row.source ?
|
|
210
|
+
const labels = row.labels.length > 0 ? themeDim(` ${row.labels.slice(0, 4).map((label) => `#${label}`).join(" ")}`) : "";
|
|
211
|
+
const source = row.source ? themeDim(` ${row.source}`) : "";
|
|
134
212
|
return [
|
|
135
213
|
pc.bold(pad(truncate(row.id, idWidth), idWidth)),
|
|
136
214
|
statusColor(row.status)(pad(truncate(row.status, statusWidth), statusWidth)),
|
|
@@ -178,7 +256,7 @@ function formatRunList(runs, options = {}) {
|
|
|
178
256
|
if (runs.length === 0) {
|
|
179
257
|
return [
|
|
180
258
|
formatSection("Runs", "none recorded"),
|
|
181
|
-
options.source === "server" ?
|
|
259
|
+
options.source === "server" ? themeDim("No runs recorded on the selected Rig server.") : themeDim("No runs recorded in .rig/runs."),
|
|
182
260
|
"",
|
|
183
261
|
...formatNextSteps(["Start one: `rig task run --next`", "Check server: `rig server status`"])
|
|
184
262
|
].join(`
|
|
@@ -198,7 +276,7 @@ function formatRunList(runs, options = {}) {
|
|
|
198
276
|
const body = rows.map((row) => [
|
|
199
277
|
pc.bold(pad(truncate(row.runId, idWidth), idWidth)),
|
|
200
278
|
statusColor(row.status)(pad(truncate(row.status, statusWidth), statusWidth)),
|
|
201
|
-
`${row.title}${row.runtime ?
|
|
279
|
+
`${row.title}${row.runtime ? themeDim(` ${row.runtime}`) : ""}`
|
|
202
280
|
].join(" "));
|
|
203
281
|
return [formatSection("Runs", options.source === "server" ? "selected server" : "local state"), header, ...body, "", ...formatNextSteps(["Follow live: `rig run attach <run-id> --follow`", "Details: `rig run show <run-id>`"])].join(`
|
|
204
282
|
`);
|
|
@@ -272,7 +350,7 @@ function formatRunStatus(summary, options = {}) {
|
|
|
272
350
|
const lines = [formatSection("Run status", options.source === "server" ? "selected server" : "local state")];
|
|
273
351
|
lines.push("", pc.bold(`Active runs (${activeRuns.length})`));
|
|
274
352
|
if (activeRuns.length === 0) {
|
|
275
|
-
lines.push(
|
|
353
|
+
lines.push(themeDim("No active runs."));
|
|
276
354
|
} else {
|
|
277
355
|
for (const run of activeRuns) {
|
|
278
356
|
lines.push(formatRunSummaryLine(run));
|
|
@@ -280,7 +358,7 @@ function formatRunStatus(summary, options = {}) {
|
|
|
280
358
|
}
|
|
281
359
|
lines.push("", pc.bold(`Recent runs (${recentRuns.length})`));
|
|
282
360
|
if (recentRuns.length === 0) {
|
|
283
|
-
lines.push(
|
|
361
|
+
lines.push(themeDim("No recent terminal runs."));
|
|
284
362
|
} else {
|
|
285
363
|
for (const run of recentRuns.slice(0, 10)) {
|
|
286
364
|
lines.push(formatRunSummaryLine(run));
|
|
@@ -298,14 +376,14 @@ function formatRunSummaryLine(run) {
|
|
|
298
376
|
const title = runTitleOf(record);
|
|
299
377
|
const runtime = firstString(record, ["runtimeAdapter", "runtime", "adapter"]);
|
|
300
378
|
const descriptor = [taskId, title].filter(Boolean).join(" \xB7 ");
|
|
301
|
-
return `${
|
|
379
|
+
return `${themeFaint("\u2502")} ${pc.bold(runId)} ${formatStatusPill(status)} ${descriptor}${runtime ? themeDim(` ${runtime}`) : ""}`;
|
|
302
380
|
}
|
|
303
381
|
function formatInboxList(kind, entries) {
|
|
304
382
|
const title = kind === "approvals" ? "Approval inbox" : "Input inbox";
|
|
305
383
|
if (entries.length === 0) {
|
|
306
384
|
return [
|
|
307
385
|
formatSection(title, "empty"),
|
|
308
|
-
|
|
386
|
+
themeDim(kind === "approvals" ? "No pending approvals." : "No pending user-input requests."),
|
|
309
387
|
"",
|
|
310
388
|
...formatNextSteps(["Check runs: `rig run status`", "Start work: `rig task run --next`"])
|
|
311
389
|
].join(`
|
|
@@ -319,8 +397,8 @@ function formatInboxList(kind, entries) {
|
|
|
319
397
|
const requestId = requestIdOf(record);
|
|
320
398
|
const status = firstString(record, ["status", "state"], "pending");
|
|
321
399
|
const prompt = firstString(record, ["prompt", "message", "reason", "title", "summary"], kind === "approvals" ? "Approval requested" : "Input requested");
|
|
322
|
-
lines.push(`${
|
|
323
|
-
lines.push(`${
|
|
400
|
+
lines.push(`${themeFaint("\u2502")} ${pc.bold(requestId)} ${formatStatusPill(status)} ${prompt}`);
|
|
401
|
+
lines.push(`${themeFaint("\u2502")} ${themeDim("run ")} ${runId || "(unknown-run)"}${taskId ? themeDim(` task ${taskId}`) : ""}`);
|
|
324
402
|
}
|
|
325
403
|
lines.push("", ...formatNextSteps(kind === "approvals" ? ["Resolve: `rig inbox approve --run <run-id> --request <request-id> --decision approve|reject`", "Rejoin: `rig run attach <run-id> --follow`"] : ["Respond: `rig inbox respond --run <run-id> --request <request-id> --answer key=value`", "Rejoin: `rig run attach <run-id> --follow`"]));
|
|
326
404
|
return lines.join(`
|
|
@@ -342,9 +420,9 @@ function formatConnectionStatus(selected, connections) {
|
|
|
342
420
|
const target = !connection ? "not configured" : connection.kind === "remote" ? connection.baseUrl : "local";
|
|
343
421
|
return [
|
|
344
422
|
formatSection("Rig server", "selected for this repo"),
|
|
345
|
-
`${
|
|
346
|
-
`${
|
|
347
|
-
`${
|
|
423
|
+
`${themeFaint("\u2502")} ${themeDim("selected ")} ${pc.bold(selected)}`,
|
|
424
|
+
`${themeFaint("\u2502")} ${themeDim("kind ")} ${formatStatusPill(connection?.kind ?? "unknown")}`,
|
|
425
|
+
`${themeFaint("\u2502")} ${themeDim("target ")} ${target ?? "not configured"}`,
|
|
348
426
|
"",
|
|
349
427
|
...formatNextSteps(["Change: `rig server use <alias|local>`", "List saved servers: `rig server list`"])
|
|
350
428
|
].join(`
|
|
@@ -311,7 +311,9 @@ ${failure.contextLine}`, 1, { hint: failure.hint });
|
|
|
311
311
|
})() : null;
|
|
312
312
|
if (!response.ok) {
|
|
313
313
|
const diagnostics = diagnosticMessage(payload);
|
|
314
|
-
const
|
|
314
|
+
const rawDetail = diagnostics ?? (text || response.statusText);
|
|
315
|
+
const detail = diagnostics ? rawDetail : rawDetail.split(`
|
|
316
|
+
`).map((line) => line.trim()).find((line) => line.length > 0)?.slice(0, 200) ?? response.statusText;
|
|
315
317
|
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
316
318
|
throw new CliError(`Rig server request failed (${response.status}): ${detail}
|
|
317
319
|
${failure.contextLine}`, 1, { hint: failure.hint });
|