@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
|
@@ -1,6 +1,131 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// packages/cli/src/
|
|
3
|
-
import {
|
|
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
|
+
var isTty = () => Boolean(process.stdout.isTTY);
|
|
74
|
+
function droneCancel(text) {
|
|
75
|
+
console.log(` ${red("\u2716")} ${ink3(text)}`);
|
|
76
|
+
}
|
|
77
|
+
var SELECT_THEME = {
|
|
78
|
+
selectedPrefix: (text) => accent(text),
|
|
79
|
+
selectedText: (text) => bold(ink(text)),
|
|
80
|
+
description: (text) => ink3(text),
|
|
81
|
+
scrollInfo: (text) => ink4(text),
|
|
82
|
+
noMatch: (text) => ink3(text)
|
|
83
|
+
};
|
|
84
|
+
async function runMiniTui(build) {
|
|
85
|
+
const terminal = new ProcessTerminal;
|
|
86
|
+
const tui = new TUI(terminal);
|
|
87
|
+
let settled = false;
|
|
88
|
+
return await new Promise((resolve) => {
|
|
89
|
+
const finish = (result) => {
|
|
90
|
+
if (settled)
|
|
91
|
+
return;
|
|
92
|
+
settled = true;
|
|
93
|
+
tui.stop();
|
|
94
|
+
resolve(result);
|
|
95
|
+
};
|
|
96
|
+
build(tui, finish);
|
|
97
|
+
tui.start();
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async function droneSelect(input) {
|
|
101
|
+
if (!isTty() || input.options.length === 0) {
|
|
102
|
+
return input.initialValue ?? input.options[0]?.value ?? null;
|
|
103
|
+
}
|
|
104
|
+
return runMiniTui((tui, finish) => {
|
|
105
|
+
tui.addChild(new Text(` ${accent("\u258D")}${bold(ink(input.message))}`));
|
|
106
|
+
const items = input.options.map((option) => ({
|
|
107
|
+
value: option.value,
|
|
108
|
+
label: option.label,
|
|
109
|
+
...option.hint ? { description: option.hint } : {}
|
|
110
|
+
}));
|
|
111
|
+
const list = new SelectList(items, Math.min(items.length, 12), SELECT_THEME);
|
|
112
|
+
const initialIndex = input.initialValue ? items.findIndex((item) => item.value === input.initialValue) : -1;
|
|
113
|
+
if (initialIndex > 0)
|
|
114
|
+
list.setSelectedIndex(initialIndex);
|
|
115
|
+
list.onSelect = (item) => finish(item.value);
|
|
116
|
+
list.onCancel = () => finish(null);
|
|
117
|
+
tui.addChild(list);
|
|
118
|
+
tui.addChild(new Text(ink4(" \u2191\u2193 navigate \xB7 enter select \xB7 esc cancel")));
|
|
119
|
+
tui.setFocus(list);
|
|
120
|
+
tui.addInputListener((data) => {
|
|
121
|
+
if (matchesKey(data, "ctrl+c") || matchesKey(data, "escape")) {
|
|
122
|
+
finish(null);
|
|
123
|
+
return { consume: true };
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
4
129
|
|
|
5
130
|
// packages/cli/src/commands/_operator-surface.ts
|
|
6
131
|
import { createInterface as createPromptInterface } from "readline/promises";
|
|
@@ -34,8 +159,8 @@ async function selectTaskWithTextPicker(tasks, io = {}) {
|
|
|
34
159
|
return null;
|
|
35
160
|
if (tasks.length === 1)
|
|
36
161
|
return tasks[0];
|
|
37
|
-
const
|
|
38
|
-
if (!
|
|
162
|
+
const isTty2 = io.isTty ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
163
|
+
if (!isTty2) {
|
|
39
164
|
throw new Error("task run requires an interactive terminal to pick a task; pass --task <id>, --next, or --detach with a task id.");
|
|
40
165
|
}
|
|
41
166
|
if (io.prompt || io.renderer) {
|
|
@@ -59,12 +184,12 @@ async function selectTaskWithTextPicker(tasks, io = {}) {
|
|
|
59
184
|
label: `${taskId2(task)} \xB7 ${typeof task.title === "string" && task.title.trim() ? task.title.trim() : "Untitled task"}`,
|
|
60
185
|
hint: typeof task.status === "string" && task.status.trim() ? task.status.trim() : undefined
|
|
61
186
|
}));
|
|
62
|
-
const answer = await
|
|
187
|
+
const answer = await droneSelect({
|
|
63
188
|
message: "Select Rig task",
|
|
64
189
|
options
|
|
65
190
|
});
|
|
66
|
-
if (
|
|
67
|
-
|
|
191
|
+
if (answer === null) {
|
|
192
|
+
droneCancel("No task selected.");
|
|
68
193
|
return null;
|
|
69
194
|
}
|
|
70
195
|
const index = Number.parseInt(String(answer), 10);
|
|
@@ -5,7 +5,282 @@ import { resolve } from "path";
|
|
|
5
5
|
import { spawn } from "child_process";
|
|
6
6
|
import { emitKeypressEvents } from "readline";
|
|
7
7
|
import { pathToFileURL } from "url";
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
// packages/cli/src/app/drone-ui.ts
|
|
10
|
+
import { ProcessTerminal, TUI, Text, Input, SelectList, matchesKey } from "@earendil-works/pi-tui";
|
|
11
|
+
|
|
12
|
+
// packages/cli/src/app/theme.ts
|
|
13
|
+
var RIG_PALETTE = {
|
|
14
|
+
ink: "#f2f3f6",
|
|
15
|
+
ink2: "#aeb0ba",
|
|
16
|
+
ink3: "#6c6e79",
|
|
17
|
+
ink4: "#44464f",
|
|
18
|
+
accent: "#ccff4d",
|
|
19
|
+
accentDim: "#a9d63f",
|
|
20
|
+
cyan: "#56d8ff",
|
|
21
|
+
red: "#ff5d5d",
|
|
22
|
+
yellow: "#ffd24d"
|
|
23
|
+
};
|
|
24
|
+
function hexToRgb(hex) {
|
|
25
|
+
const value = hex.replace("#", "");
|
|
26
|
+
return [
|
|
27
|
+
Number.parseInt(value.slice(0, 2), 16),
|
|
28
|
+
Number.parseInt(value.slice(2, 4), 16),
|
|
29
|
+
Number.parseInt(value.slice(4, 6), 16)
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
function fg(hex) {
|
|
33
|
+
const [r, g, b] = hexToRgb(hex);
|
|
34
|
+
return (text) => `\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`;
|
|
35
|
+
}
|
|
36
|
+
var ink = fg(RIG_PALETTE.ink);
|
|
37
|
+
var ink2 = fg(RIG_PALETTE.ink2);
|
|
38
|
+
var ink3 = fg(RIG_PALETTE.ink3);
|
|
39
|
+
var ink4 = fg(RIG_PALETTE.ink4);
|
|
40
|
+
var accent = fg(RIG_PALETTE.accent);
|
|
41
|
+
var accentDim = fg(RIG_PALETTE.accentDim);
|
|
42
|
+
var cyan = fg(RIG_PALETTE.cyan);
|
|
43
|
+
var red = fg(RIG_PALETTE.red);
|
|
44
|
+
var yellow = fg(RIG_PALETTE.yellow);
|
|
45
|
+
function bold(text) {
|
|
46
|
+
return `\x1B[1m${text}\x1B[22m`;
|
|
47
|
+
}
|
|
48
|
+
var DRONE_ART = [
|
|
49
|
+
" .-=-. .-=-. ",
|
|
50
|
+
" ( !!! ) ( !!! ) ",
|
|
51
|
+
" '-=-'._ _.'-=-' ",
|
|
52
|
+
" '._ _.' ",
|
|
53
|
+
" '=$$$$$$$=.' ",
|
|
54
|
+
" =$$$$$$$$$$$= ",
|
|
55
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
56
|
+
" $$$@@ @@$$$ ",
|
|
57
|
+
" $$@ ? @$$$ ",
|
|
58
|
+
" $$$@ '-' @$$$ ",
|
|
59
|
+
" $$$@@ @@$$$ ",
|
|
60
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
61
|
+
" =$$$$$$$$$$$= ",
|
|
62
|
+
" '=$$$$$$$=.' ",
|
|
63
|
+
" _.' '._ ",
|
|
64
|
+
" .-=-.' '.-=-. ",
|
|
65
|
+
" ( !!! ) ( !!! ) ",
|
|
66
|
+
" '-=-' '-=-' "
|
|
67
|
+
];
|
|
68
|
+
var EYE_FRAMES = ["@", "o", "."];
|
|
69
|
+
var DRONE_WIDTH = DRONE_ART[0].length;
|
|
70
|
+
var DRONE_HEIGHT = DRONE_ART.length;
|
|
71
|
+
var MICRO_BLADES = ["---", "\\\\\\", "|||", "///"];
|
|
72
|
+
function microDroneFrame(tick) {
|
|
73
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
74
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
75
|
+
return `(${blade})${eye}(${blade})`;
|
|
76
|
+
}
|
|
77
|
+
var MICRO_DRONE_FRAMES = Array.from({ length: 12 }, (_, index) => microDroneFrame(index));
|
|
78
|
+
function renderMicroDroneFrame(tick) {
|
|
79
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
80
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
81
|
+
return `${ink4("(")}${cyan(blade)}${ink4(")")}${bold(accent(eye))}${ink4("(")}${cyan(blade)}${ink4(")")}`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// packages/cli/src/commands/_spinner.ts
|
|
85
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
86
|
+
function createTtySpinner(input) {
|
|
87
|
+
const output = input.output ?? process.stdout;
|
|
88
|
+
const isTty = output.isTTY === true;
|
|
89
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
90
|
+
let label = input.label;
|
|
91
|
+
let frame = 0;
|
|
92
|
+
let paused = false;
|
|
93
|
+
let stopped = false;
|
|
94
|
+
let lastPrintedLabel = "";
|
|
95
|
+
const render = () => {
|
|
96
|
+
if (stopped || paused)
|
|
97
|
+
return;
|
|
98
|
+
if (!isTty) {
|
|
99
|
+
if (label !== lastPrintedLabel) {
|
|
100
|
+
output.write(`${label}
|
|
101
|
+
`);
|
|
102
|
+
lastPrintedLabel = label;
|
|
103
|
+
}
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
frame = (frame + 1) % frames.length;
|
|
107
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
108
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
109
|
+
};
|
|
110
|
+
const clearLine = () => {
|
|
111
|
+
if (isTty)
|
|
112
|
+
output.write("\r\x1B[2K");
|
|
113
|
+
};
|
|
114
|
+
render();
|
|
115
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
|
|
116
|
+
return {
|
|
117
|
+
setLabel(next) {
|
|
118
|
+
label = next;
|
|
119
|
+
render();
|
|
120
|
+
},
|
|
121
|
+
pause() {
|
|
122
|
+
paused = true;
|
|
123
|
+
clearLine();
|
|
124
|
+
},
|
|
125
|
+
resume() {
|
|
126
|
+
if (stopped)
|
|
127
|
+
return;
|
|
128
|
+
paused = false;
|
|
129
|
+
render();
|
|
130
|
+
},
|
|
131
|
+
stop(finalLine) {
|
|
132
|
+
if (stopped)
|
|
133
|
+
return;
|
|
134
|
+
stopped = true;
|
|
135
|
+
if (timer)
|
|
136
|
+
clearInterval(timer);
|
|
137
|
+
clearLine();
|
|
138
|
+
if (finalLine)
|
|
139
|
+
output.write(`${finalLine}
|
|
140
|
+
`);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// packages/cli/src/app/drone-ui.ts
|
|
146
|
+
var isTty = () => Boolean(process.stdout.isTTY);
|
|
147
|
+
function hairline(width = Math.min(process.stdout.columns ?? 80, 100)) {
|
|
148
|
+
return ink4("\u2500".repeat(Math.max(10, width)));
|
|
149
|
+
}
|
|
150
|
+
function droneIntro(title, subtitle) {
|
|
151
|
+
console.log("");
|
|
152
|
+
console.log(` ${accent("\u258D")}${bold(ink(title))}${subtitle ? ink3(` \u2014 ${subtitle}`) : ""}`);
|
|
153
|
+
console.log(hairline());
|
|
154
|
+
}
|
|
155
|
+
function droneOutro(text) {
|
|
156
|
+
console.log(hairline());
|
|
157
|
+
console.log(` ${accent("\u25C6")} ${ink2(text)}`);
|
|
158
|
+
console.log("");
|
|
159
|
+
}
|
|
160
|
+
function droneNote(message, title) {
|
|
161
|
+
if (title)
|
|
162
|
+
console.log(` ${accentDim("\u25C7")} ${bold(ink2(title))}`);
|
|
163
|
+
for (const line of message.split(`
|
|
164
|
+
`)) {
|
|
165
|
+
console.log(` ${ink4("\u2502")} ${line}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function droneInfo(text) {
|
|
169
|
+
console.log(` ${cyan("\xB7")} ${ink2(text)}`);
|
|
170
|
+
}
|
|
171
|
+
function droneWarn(text) {
|
|
172
|
+
console.log(` ${yellow("\u25B2")} ${ink2(text)}`);
|
|
173
|
+
}
|
|
174
|
+
function droneCancel(text) {
|
|
175
|
+
console.log(` ${red("\u2716")} ${ink3(text)}`);
|
|
176
|
+
}
|
|
177
|
+
function droneSpinner() {
|
|
178
|
+
let active = null;
|
|
179
|
+
return {
|
|
180
|
+
start(message) {
|
|
181
|
+
active = createTtySpinner({
|
|
182
|
+
label: message,
|
|
183
|
+
frames: MICRO_DRONE_FRAMES,
|
|
184
|
+
styleFrame: (frame) => renderMicroDroneFrame(Math.max(0, MICRO_DRONE_FRAMES.indexOf(frame)))
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
stop(message) {
|
|
188
|
+
active?.stop(message ? ` ${accent("\u25C6")} ${ink2(message)}` : undefined);
|
|
189
|
+
active = null;
|
|
190
|
+
},
|
|
191
|
+
error(message) {
|
|
192
|
+
active?.stop(message ? ` ${red("\u2716")} ${ink2(message)}` : undefined);
|
|
193
|
+
active = null;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
var SELECT_THEME = {
|
|
198
|
+
selectedPrefix: (text) => accent(text),
|
|
199
|
+
selectedText: (text) => bold(ink(text)),
|
|
200
|
+
description: (text) => ink3(text),
|
|
201
|
+
scrollInfo: (text) => ink4(text),
|
|
202
|
+
noMatch: (text) => ink3(text)
|
|
203
|
+
};
|
|
204
|
+
async function runMiniTui(build) {
|
|
205
|
+
const terminal = new ProcessTerminal;
|
|
206
|
+
const tui = new TUI(terminal);
|
|
207
|
+
let settled = false;
|
|
208
|
+
return await new Promise((resolve) => {
|
|
209
|
+
const finish = (result) => {
|
|
210
|
+
if (settled)
|
|
211
|
+
return;
|
|
212
|
+
settled = true;
|
|
213
|
+
tui.stop();
|
|
214
|
+
resolve(result);
|
|
215
|
+
};
|
|
216
|
+
build(tui, finish);
|
|
217
|
+
tui.start();
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
async function droneSelect(input) {
|
|
221
|
+
if (!isTty() || input.options.length === 0) {
|
|
222
|
+
return input.initialValue ?? input.options[0]?.value ?? null;
|
|
223
|
+
}
|
|
224
|
+
return runMiniTui((tui, finish) => {
|
|
225
|
+
tui.addChild(new Text(` ${accent("\u258D")}${bold(ink(input.message))}`));
|
|
226
|
+
const items = input.options.map((option) => ({
|
|
227
|
+
value: option.value,
|
|
228
|
+
label: option.label,
|
|
229
|
+
...option.hint ? { description: option.hint } : {}
|
|
230
|
+
}));
|
|
231
|
+
const list = new SelectList(items, Math.min(items.length, 12), SELECT_THEME);
|
|
232
|
+
const initialIndex = input.initialValue ? items.findIndex((item) => item.value === input.initialValue) : -1;
|
|
233
|
+
if (initialIndex > 0)
|
|
234
|
+
list.setSelectedIndex(initialIndex);
|
|
235
|
+
list.onSelect = (item) => finish(item.value);
|
|
236
|
+
list.onCancel = () => finish(null);
|
|
237
|
+
tui.addChild(list);
|
|
238
|
+
tui.addChild(new Text(ink4(" \u2191\u2193 navigate \xB7 enter select \xB7 esc cancel")));
|
|
239
|
+
tui.setFocus(list);
|
|
240
|
+
tui.addInputListener((data) => {
|
|
241
|
+
if (matchesKey(data, "ctrl+c") || matchesKey(data, "escape")) {
|
|
242
|
+
finish(null);
|
|
243
|
+
return { consume: true };
|
|
244
|
+
}
|
|
245
|
+
return;
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
async function droneText(input) {
|
|
250
|
+
if (!isTty())
|
|
251
|
+
return input.initialValue ?? null;
|
|
252
|
+
return runMiniTui((tui, finish) => {
|
|
253
|
+
tui.addChild(new Text(` ${accent("\u258D")}${bold(ink(input.message))}${input.placeholder ? ink4(` (${input.placeholder})`) : ""}`));
|
|
254
|
+
const field = new Input;
|
|
255
|
+
if (input.initialValue)
|
|
256
|
+
field.setValue(input.initialValue);
|
|
257
|
+
field.onSubmit = (value) => finish(value);
|
|
258
|
+
field.onEscape = () => finish(null);
|
|
259
|
+
tui.addChild(field);
|
|
260
|
+
tui.addChild(new Text(ink4(" enter submit \xB7 esc cancel")));
|
|
261
|
+
tui.setFocus(field);
|
|
262
|
+
tui.addInputListener((data) => {
|
|
263
|
+
if (matchesKey(data, "ctrl+c")) {
|
|
264
|
+
finish(null);
|
|
265
|
+
return { consume: true };
|
|
266
|
+
}
|
|
267
|
+
return;
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
async function droneConfirm(input) {
|
|
272
|
+
const answer = await droneSelect({
|
|
273
|
+
message: input.message,
|
|
274
|
+
options: [
|
|
275
|
+
{ value: "yes", label: "Yes" },
|
|
276
|
+
{ value: "no", label: "No" }
|
|
277
|
+
],
|
|
278
|
+
initialValue: input.initialValue === false ? "no" : "yes"
|
|
279
|
+
});
|
|
280
|
+
return answer === null ? null : answer === "yes";
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// packages/cli/src/commands/browser.ts
|
|
9
284
|
import pc2 from "picocolors";
|
|
10
285
|
|
|
11
286
|
// packages/cli/src/runner.ts
|
|
@@ -66,7 +341,6 @@ Usage: ${usage}`);
|
|
|
66
341
|
import { runCapture as runCapture2 } from "@rig/runtime/control-plane/native/utils";
|
|
67
342
|
|
|
68
343
|
// packages/cli/src/commands/task-report-bug.ts
|
|
69
|
-
import * as clack from "@clack/prompts";
|
|
70
344
|
import pc from "picocolors";
|
|
71
345
|
import {
|
|
72
346
|
appendJsonlRecord,
|
|
@@ -81,27 +355,29 @@ import { resolveMonorepoRoot } from "@rig/runtime/control-plane/native/utils";
|
|
|
81
355
|
|
|
82
356
|
// packages/cli/src/commands/task-report-bug.ts
|
|
83
357
|
async function promptBugText(message, defaultValue, options = {}) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
358
|
+
for (;; ) {
|
|
359
|
+
const result = await droneText({
|
|
360
|
+
message,
|
|
361
|
+
...options.placeholder ? { placeholder: options.placeholder } : {},
|
|
362
|
+
...defaultValue?.trim() ? { initialValue: defaultValue } : {}
|
|
363
|
+
});
|
|
364
|
+
if (result === null) {
|
|
365
|
+
droneCancel("Bug report cancelled.");
|
|
366
|
+
throw new CliError("Bug report cancelled by user.");
|
|
367
|
+
}
|
|
368
|
+
const invalid = options.required ? validateRequiredBugPromptValue(result, message) : undefined;
|
|
369
|
+
if (!invalid)
|
|
370
|
+
return result.trim();
|
|
371
|
+
droneInfo(invalid);
|
|
372
|
+
}
|
|
91
373
|
}
|
|
92
374
|
function validateRequiredBugPromptValue(value, label) {
|
|
93
375
|
return value?.trim() ? undefined : `${label} is required.`;
|
|
94
376
|
}
|
|
95
377
|
async function promptBugConfirm(message, initialValue) {
|
|
96
|
-
const result = await
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
});
|
|
100
|
-
return unwrapClackPrompt(result);
|
|
101
|
-
}
|
|
102
|
-
function unwrapClackPrompt(result) {
|
|
103
|
-
if (clack.isCancel(result)) {
|
|
104
|
-
clack.cancel("Bug report cancelled.");
|
|
378
|
+
const result = await droneConfirm({ message, initialValue });
|
|
379
|
+
if (result === null) {
|
|
380
|
+
droneCancel("Bug report cancelled.");
|
|
105
381
|
throw new CliError("Bug report cancelled by user.");
|
|
106
382
|
}
|
|
107
383
|
return result;
|
|
@@ -437,26 +713,26 @@ async function executeBrowserDemo(context, args) {
|
|
|
437
713
|
...stateDirResult.value ? { stateDir: stateDirResult.value } : {},
|
|
438
714
|
...targetUrlResult.value ? { targetUrl: targetUrlResult.value } : {}
|
|
439
715
|
});
|
|
440
|
-
|
|
441
|
-
|
|
716
|
+
droneIntro("rig browser demo");
|
|
717
|
+
droneNote([
|
|
442
718
|
"You are now acting as the agent inside a browser-required task run.",
|
|
443
719
|
"Type each simple command when prompted. The CLI will run the real backing action",
|
|
444
720
|
"and print the same kind of output an agent would inspect before the next step."
|
|
445
721
|
].join(`
|
|
446
722
|
`), "Agent task-run walkthrough");
|
|
447
|
-
|
|
723
|
+
droneNote(formatBrowserDemoRuntime(runtime), "Runtime contract the agent receives");
|
|
448
724
|
const agentCommands = buildBrowserDemoAgentCommands(runtime);
|
|
449
725
|
await promptBrowserDemoAgentCommand("Agent command 1/4: print browser attach info", agentCommands.attachInfo);
|
|
450
726
|
printBrowserDemoCommandOutput(agentCommands.attachInfo.primary, formatBrowserDemoAttachInfoOutput(runtime));
|
|
451
727
|
await promptBrowserDemoAgentCommand("Agent command 2/4: launch Rig Browser", agentCommands.launch);
|
|
452
728
|
if (!noBuildFlag.value) {
|
|
453
|
-
const
|
|
454
|
-
|
|
729
|
+
const spinner = droneSpinner();
|
|
730
|
+
spinner.start("Preparing browser bundle for the launch command");
|
|
455
731
|
try {
|
|
456
732
|
runBrowserDemoBuild(context.projectRoot);
|
|
457
|
-
|
|
733
|
+
spinner.stop("Browser bundle built");
|
|
458
734
|
} catch (error) {
|
|
459
|
-
|
|
735
|
+
spinner.error("Browser bundle build failed");
|
|
460
736
|
throw error;
|
|
461
737
|
}
|
|
462
738
|
}
|
|
@@ -465,20 +741,20 @@ async function executeBrowserDemo(context, args) {
|
|
|
465
741
|
try {
|
|
466
742
|
printBrowserDemoCommandOutput(agentCommands.launch.primary, formatBrowserDemoLaunchOutput(runtime, child.pid));
|
|
467
743
|
await promptBrowserDemoAgentCommand("Agent command 3/4: check live browser endpoint", agentCommands.check);
|
|
468
|
-
const readySpinner =
|
|
744
|
+
const readySpinner = droneSpinner();
|
|
469
745
|
readySpinner.start(`Waiting for live CDP endpoint at ${runtime.attachUrl}`);
|
|
470
746
|
const { version, pageTarget, targets } = await waitForBrowserDemoReady(runtime, child);
|
|
471
747
|
readySpinner.stop("Live CDP endpoint is ready");
|
|
472
748
|
printBrowserDemoCommandOutput(agentCommands.check.primary, formatBrowserDemoCheckOutput(runtime, version, pageTarget, targets));
|
|
473
749
|
await promptBugText("Look at the launched browser page, then type ready", undefined, { required: true, placeholder: "ready" });
|
|
474
750
|
await promptBrowserDemoAgentCommand("Agent command 4/4: attach DevTools MCP", agentCommands.attach);
|
|
475
|
-
|
|
751
|
+
droneNote([
|
|
476
752
|
"The attach step evaluates JavaScript inside the live browser page.",
|
|
477
753
|
"It records DOM state before the change, changes #status, #details, and the page background,",
|
|
478
754
|
"then records DOM state and layout again so the transcript proves what changed."
|
|
479
755
|
].join(`
|
|
480
756
|
`), "What mutationEvaluation means");
|
|
481
|
-
const cdpSpinner =
|
|
757
|
+
const cdpSpinner = droneSpinner();
|
|
482
758
|
cdpSpinner.start("Agent attaching over WebSocket CDP");
|
|
483
759
|
const cdpResult = await runBrowserDemoCdpAction(pageTarget.webSocketDebuggerUrl);
|
|
484
760
|
cdpSpinner.stop("Agent CDP action completed");
|
|
@@ -487,7 +763,7 @@ async function executeBrowserDemo(context, args) {
|
|
|
487
763
|
if (!keepOpen) {
|
|
488
764
|
keepOpen = await promptBugConfirm("Keep the demo browser open after this command exits?", false);
|
|
489
765
|
}
|
|
490
|
-
|
|
766
|
+
droneOutro(keepOpen ? "Demo complete. Browser left open by request." : "Demo complete. Closing browser.");
|
|
491
767
|
return {
|
|
492
768
|
ok: true,
|
|
493
769
|
group: "browser",
|
|
@@ -594,7 +870,7 @@ async function promptBrowserDemoAgentCommand(message, command) {
|
|
|
594
870
|
if (browserDemoCommandMatches(value, command)) {
|
|
595
871
|
return normalizeBrowserDemoCommand(value);
|
|
596
872
|
}
|
|
597
|
-
|
|
873
|
+
droneWarn(`Expected one of:
|
|
598
874
|
${command.accepted.map((accepted) => ` ${accepted}`).join(`
|
|
599
875
|
`)}`);
|
|
600
876
|
}
|