@h-rig/cli 0.0.6-alpha.9 → 0.0.6-alpha.90
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 +1 -1
- package/dist/bin/build-rig-binaries.d.ts +2 -0
- package/dist/bin/rig.d.ts +2 -0
- package/dist/bin/rig.js +11052 -6668
- package/dist/src/app/board.d.ts +23 -0
- package/dist/src/app/board.js +1786 -0
- package/dist/src/app/drone-ui.d.ts +37 -0
- package/dist/src/app/drone-ui.js +294 -0
- package/dist/src/app/theme.d.ts +47 -0
- package/dist/src/app/theme.js +150 -0
- package/dist/src/commands/_async-ui.d.ts +13 -0
- package/dist/src/commands/_async-ui.js +223 -0
- package/dist/src/commands/_authority-runs.d.ts +22 -0
- package/dist/src/commands/_authority-runs.js +2 -3
- package/dist/src/commands/_cli-format.d.ts +49 -0
- package/dist/src/commands/_cli-format.js +447 -0
- package/dist/src/commands/_connection-state.d.ts +44 -0
- package/dist/src/commands/_connection-state.js +30 -11
- package/dist/src/commands/_doctor-checks.d.ts +52 -0
- package/dist/src/commands/_doctor-checks.js +180 -44
- package/dist/src/commands/_help-catalog.d.ts +51 -0
- package/dist/src/commands/_help-catalog.js +442 -0
- package/dist/src/commands/_json-output.d.ts +11 -0
- package/dist/src/commands/_json-output.js +56 -0
- package/dist/src/commands/_operator-surface.d.ts +34 -0
- package/dist/src/commands/_operator-surface.js +220 -0
- package/dist/src/commands/_operator-view.d.ts +30 -0
- package/dist/src/commands/_operator-view.js +803 -73
- package/dist/src/commands/_parsers.d.ts +15 -0
- package/dist/src/commands/_parsers.js +18 -11
- package/dist/src/commands/_paths.d.ts +11 -0
- package/dist/src/commands/_pi-frontend.d.ts +27 -0
- package/dist/src/commands/_pi-frontend.js +742 -0
- package/dist/src/commands/_pi-install.d.ts +42 -0
- package/dist/src/commands/_pi-install.js +5 -4
- package/dist/src/commands/_policy.d.ts +8 -0
- package/dist/src/commands/_policy.js +12 -5
- package/dist/src/commands/_preflight.d.ts +22 -0
- package/dist/src/commands/_preflight.js +190 -128
- package/dist/src/commands/_probes.d.ts +1 -0
- package/dist/src/commands/_run-driver-helpers.d.ts +99 -0
- package/dist/src/commands/_run-driver-helpers.js +75 -22
- package/dist/src/commands/_run-replay.d.ts +24 -0
- package/dist/src/commands/_run-replay.js +142 -0
- package/dist/src/commands/_server-client.d.ts +186 -0
- package/dist/src/commands/_server-client.js +360 -61
- package/dist/src/commands/_snapshot-upload.d.ts +39 -0
- package/dist/src/commands/_snapshot-upload.js +163 -39
- package/dist/src/commands/_spinner.d.ts +25 -0
- package/dist/src/commands/_spinner.js +65 -0
- package/dist/src/commands/_task-picker.d.ts +9 -0
- package/dist/src/commands/_task-picker.js +172 -19
- package/dist/src/commands/agent.d.ts +3 -0
- package/dist/src/commands/agent.js +39 -20
- package/dist/src/commands/browser.d.ts +65 -0
- package/dist/src/commands/browser.js +334 -51
- package/dist/src/commands/connect.d.ts +7 -0
- package/dist/src/commands/connect.js +272 -33
- package/dist/src/commands/dist.d.ts +28 -0
- package/dist/src/commands/dist.js +19 -12
- package/dist/src/commands/doctor.d.ts +3 -0
- package/dist/src/commands/doctor.js +378 -45
- package/dist/src/commands/github.d.ts +3 -0
- package/dist/src/commands/github.js +375 -53
- package/dist/src/commands/inbox.d.ts +30 -0
- package/dist/src/commands/inbox.js +760 -70
- package/dist/src/commands/init.d.ts +74 -0
- package/dist/src/commands/init.js +963 -119
- package/dist/src/commands/inspect.d.ts +3 -0
- package/dist/src/commands/inspect.js +588 -32
- package/dist/src/commands/inspector.d.ts +3 -0
- package/dist/src/commands/inspector.js +20 -13
- package/dist/src/commands/pi.d.ts +3 -0
- package/dist/src/commands/pi.js +177 -0
- package/dist/src/commands/plugin.d.ts +16 -0
- package/dist/src/commands/plugin.js +95 -27
- package/dist/src/commands/profile-and-review.d.ts +4 -0
- package/dist/src/commands/profile-and-review.js +26 -19
- package/dist/src/commands/queue.d.ts +3 -0
- package/dist/src/commands/queue.js +32 -12
- package/dist/src/commands/remote.d.ts +3 -0
- package/dist/src/commands/remote.js +43 -36
- package/dist/src/commands/repo-git-harness.d.ts +5 -0
- package/dist/src/commands/repo-git-harness.js +22 -15
- package/dist/src/commands/run.d.ts +3 -0
- package/dist/src/commands/run.js +1379 -159
- package/dist/src/commands/server.d.ts +7 -0
- package/dist/src/commands/server.js +502 -57
- package/dist/src/commands/setup.d.ts +16 -0
- package/dist/src/commands/setup.js +390 -63
- package/dist/src/commands/stats.d.ts +12 -0
- package/dist/src/commands/stats.js +1051 -0
- package/dist/src/commands/task-report-bug.d.ts +19 -0
- package/dist/src/commands/task-report-bug.js +260 -62
- package/dist/src/commands/task-run-driver.d.ts +132 -0
- package/dist/src/commands/task-run-driver.js +886 -130
- package/dist/src/commands/task.d.ts +14 -0
- package/dist/src/commands/task.js +1644 -314
- package/dist/src/commands/test.d.ts +3 -0
- package/dist/src/commands/test.js +15 -8
- package/dist/src/commands/workspace.d.ts +3 -0
- package/dist/src/commands/workspace.js +18 -11
- package/dist/src/commands.d.ts +29 -0
- package/dist/src/commands.js +10967 -6637
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +10168 -5787
- package/dist/src/launcher.d.ts +61 -0
- package/dist/src/launcher.js +77 -13
- package/dist/src/report-bug.d.ts +44 -0
- package/dist/src/report-bug.js +3 -3
- package/dist/src/runner.d.ts +47 -0
- package/dist/src/runner.js +16 -22
- package/dist/src/withMutedConsole.d.ts +2 -0
- package/package.json +13 -6
|
@@ -1,12 +1,147 @@
|
|
|
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
|
+
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
|
+
}
|
|
129
|
+
|
|
2
130
|
// packages/cli/src/runner.ts
|
|
3
131
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
4
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
132
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
5
133
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
6
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
7
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
8
134
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
9
|
-
|
|
135
|
+
|
|
136
|
+
class CliError extends RuntimeCliError {
|
|
137
|
+
hint;
|
|
138
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
139
|
+
super(message, exitCode);
|
|
140
|
+
if (options.hint?.trim()) {
|
|
141
|
+
this.hint = options.hint.trim();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
10
145
|
function requireNoExtraArgs(args, usage) {
|
|
11
146
|
if (args.length > 0) {
|
|
12
147
|
throw new CliError(`Unexpected arguments: ${args.join(" ")}
|
|
@@ -36,7 +171,7 @@ function readJsonFile(path) {
|
|
|
36
171
|
try {
|
|
37
172
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
38
173
|
} catch (error) {
|
|
39
|
-
throw new
|
|
174
|
+
throw new CliError(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1, { hint: "Fix or delete that file, then re-select a server with `rig server use <alias|local>`." });
|
|
40
175
|
}
|
|
41
176
|
}
|
|
42
177
|
function writeJsonFile(path, value) {
|
|
@@ -79,7 +214,7 @@ function writeGlobalConnections(state, options = {}) {
|
|
|
79
214
|
function upsertGlobalConnection(alias, connection, options = {}) {
|
|
80
215
|
const cleanAlias = alias.trim();
|
|
81
216
|
if (!cleanAlias)
|
|
82
|
-
throw new
|
|
217
|
+
throw new CliError("Connection alias is required.", 1, { hint: "Save a server with `rig server add <alias> <url>`." });
|
|
83
218
|
const state = readGlobalConnections(options);
|
|
84
219
|
state.connections[cleanAlias] = connection;
|
|
85
220
|
writeGlobalConnections(state, options);
|
|
@@ -96,27 +231,99 @@ function readRepoConnection(projectRoot) {
|
|
|
96
231
|
return {
|
|
97
232
|
selected,
|
|
98
233
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
99
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
234
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
235
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
100
236
|
};
|
|
101
237
|
}
|
|
102
238
|
function writeRepoConnection(projectRoot, state) {
|
|
103
239
|
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
104
240
|
}
|
|
105
241
|
|
|
242
|
+
// packages/cli/src/commands/_cli-format.ts
|
|
243
|
+
import pc from "picocolors";
|
|
244
|
+
var themeDim = (value) => ink3(value);
|
|
245
|
+
var themeFaint = (value) => ink4(value);
|
|
246
|
+
function truncate(value, width) {
|
|
247
|
+
if (value.length <= width)
|
|
248
|
+
return value;
|
|
249
|
+
if (width <= 1)
|
|
250
|
+
return "\u2026";
|
|
251
|
+
return `${value.slice(0, width - 1)}\u2026`;
|
|
252
|
+
}
|
|
253
|
+
function pad(value, width) {
|
|
254
|
+
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
255
|
+
}
|
|
256
|
+
function statusColor(status) {
|
|
257
|
+
const normalized = status.toLowerCase();
|
|
258
|
+
if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved", "running"].includes(normalized))
|
|
259
|
+
return accent;
|
|
260
|
+
if (["failed", "needs_attention", "needs-attention", "blocked", "error", "rejected"].includes(normalized))
|
|
261
|
+
return red;
|
|
262
|
+
if (["reviewing", "validating", "in_progress", "in-progress", "remote", "preparing", "closing-out"].includes(normalized))
|
|
263
|
+
return cyan;
|
|
264
|
+
if (["ready", "open", "queued", "created", "local", "pending"].includes(normalized))
|
|
265
|
+
return yellow;
|
|
266
|
+
return themeDim;
|
|
267
|
+
}
|
|
268
|
+
function formatStatusPill(status) {
|
|
269
|
+
const label = status || "unknown";
|
|
270
|
+
return statusColor(label)(`\u25CF ${label}`);
|
|
271
|
+
}
|
|
272
|
+
function formatSection(title, subtitle) {
|
|
273
|
+
return `${pc.bold(accent("\u25C6"))} ${pc.bold(title)}${subtitle ? themeDim(` \u2014 ${subtitle}`) : ""}`;
|
|
274
|
+
}
|
|
275
|
+
function formatSuccessCard(title, rows = []) {
|
|
276
|
+
const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${themeFaint("\u2502")} ${themeDim(key.padEnd(12))} ${value}`);
|
|
277
|
+
return [formatSection(title), ...body].join(`
|
|
278
|
+
`);
|
|
279
|
+
}
|
|
280
|
+
function formatNextSteps(steps) {
|
|
281
|
+
if (steps.length === 0)
|
|
282
|
+
return [];
|
|
283
|
+
return [pc.bold("Next"), ...steps.map((step) => `${accent("\u203A")} ${step}`)];
|
|
284
|
+
}
|
|
285
|
+
function formatConnectionList(connections) {
|
|
286
|
+
const rows = [["local", { kind: "local", mode: "auto" }], ...Object.entries(connections)];
|
|
287
|
+
const aliasWidth = Math.min(24, Math.max(5, ...rows.map(([alias]) => alias.length)));
|
|
288
|
+
const lines = rows.map(([alias, connection]) => [
|
|
289
|
+
pc.bold(pad(truncate(alias, aliasWidth), aliasWidth)),
|
|
290
|
+
formatStatusPill(connection.kind),
|
|
291
|
+
connection.kind === "remote" ? connection.baseUrl ?? "" : connection.mode ?? "local"
|
|
292
|
+
].join(" "));
|
|
293
|
+
return [formatSection("Rig servers", `${rows.length} available`), `${pc.bold(pad("ALIAS", aliasWidth))} ${pc.bold("KIND")} ${pc.bold("TARGET")}`, ...lines, "", ...formatNextSteps(["Select one: `rig server use <alias|local>`"])].join(`
|
|
294
|
+
`);
|
|
295
|
+
}
|
|
296
|
+
function formatConnectionStatus(selected, connections) {
|
|
297
|
+
const connection = selected === "local" ? { kind: "local", mode: "auto" } : connections[selected];
|
|
298
|
+
const target = !connection ? "not configured" : connection.kind === "remote" ? connection.baseUrl : "local";
|
|
299
|
+
return [
|
|
300
|
+
formatSection("Rig server", "selected for this repo"),
|
|
301
|
+
`${themeFaint("\u2502")} ${themeDim("selected ")} ${pc.bold(selected)}`,
|
|
302
|
+
`${themeFaint("\u2502")} ${themeDim("kind ")} ${formatStatusPill(connection?.kind ?? "unknown")}`,
|
|
303
|
+
`${themeFaint("\u2502")} ${themeDim("target ")} ${target ?? "not configured"}`,
|
|
304
|
+
"",
|
|
305
|
+
...formatNextSteps(["Change: `rig server use <alias|local>`", "List saved servers: `rig server list`"])
|
|
306
|
+
].join(`
|
|
307
|
+
`);
|
|
308
|
+
}
|
|
309
|
+
|
|
106
310
|
// packages/cli/src/commands/connect.ts
|
|
107
|
-
function
|
|
311
|
+
function usageName(options) {
|
|
312
|
+
return `rig ${options.group}`;
|
|
313
|
+
}
|
|
314
|
+
function parseConnection(alias, value, options) {
|
|
108
315
|
if (alias === "local" && !value)
|
|
109
316
|
return { kind: "local", mode: "auto" };
|
|
110
317
|
if (!value)
|
|
111
|
-
throw new
|
|
318
|
+
throw new CliError(`Missing remote server URL. Usage: ${usageName(options)} add <alias> <url>`, 1);
|
|
112
319
|
let parsed;
|
|
113
320
|
try {
|
|
114
321
|
parsed = new URL(value);
|
|
115
322
|
} catch {
|
|
116
|
-
throw new
|
|
323
|
+
throw new CliError(`Invalid Rig server URL: ${value}`, 1, { hint: "Pass a full http(s) URL, e.g. `rig server add prod https://rig.example.com`." });
|
|
117
324
|
}
|
|
118
325
|
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
119
|
-
throw new
|
|
326
|
+
throw new CliError("Rig remote server URL must be http(s).", 1, { hint: "Use an http:// or https:// URL, e.g. `rig server add prod https://rig.example.com`." });
|
|
120
327
|
}
|
|
121
328
|
return { kind: "remote", baseUrl: parsed.toString().replace(/\/+$/, "") };
|
|
122
329
|
}
|
|
@@ -127,54 +334,86 @@ function printJsonOrText(context, payload, text) {
|
|
|
127
334
|
console.log(text);
|
|
128
335
|
}
|
|
129
336
|
}
|
|
130
|
-
async function
|
|
337
|
+
async function promptForConnectionAlias(context) {
|
|
338
|
+
const state = readGlobalConnections();
|
|
339
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
340
|
+
const options = [
|
|
341
|
+
{ value: "local", label: "local", hint: "Use/start a local Rig server" },
|
|
342
|
+
...Object.entries(state.connections).map(([alias, connection]) => ({
|
|
343
|
+
value: alias,
|
|
344
|
+
label: alias,
|
|
345
|
+
hint: connection.kind === "remote" ? connection.baseUrl : "local"
|
|
346
|
+
}))
|
|
347
|
+
].filter((option, index, all) => all.findIndex((candidate) => candidate.value === option.value) === index);
|
|
348
|
+
const answer = await droneSelect({
|
|
349
|
+
message: "Select Rig server for this repo",
|
|
350
|
+
initialValue: repo?.selected ?? "local",
|
|
351
|
+
options
|
|
352
|
+
});
|
|
353
|
+
if (answer === null) {
|
|
354
|
+
droneCancel("No server selected.");
|
|
355
|
+
throw new CliError("No server selected.", 3);
|
|
356
|
+
}
|
|
357
|
+
return String(answer);
|
|
358
|
+
}
|
|
359
|
+
async function executeConnectionCommand(context, args, options) {
|
|
131
360
|
const [command, ...rest] = args;
|
|
132
361
|
switch (command ?? "status") {
|
|
133
362
|
case "list": {
|
|
134
|
-
requireNoExtraArgs(rest,
|
|
363
|
+
requireNoExtraArgs(rest, `${usageName(options)} list`);
|
|
135
364
|
const state = readGlobalConnections();
|
|
136
|
-
printJsonOrText(context, state,
|
|
137
|
-
|
|
138
|
-
return { ok: true, group: "connect", command: "list", details: state };
|
|
365
|
+
printJsonOrText(context, state, formatConnectionList(state.connections));
|
|
366
|
+
return { ok: true, group: options.group, command: "list", details: state };
|
|
139
367
|
}
|
|
140
368
|
case "add": {
|
|
141
369
|
const [alias, url, ...extra] = rest;
|
|
142
370
|
if (!alias)
|
|
143
|
-
throw new
|
|
144
|
-
requireNoExtraArgs(extra,
|
|
145
|
-
const connection = parseConnection(alias, url);
|
|
371
|
+
throw new CliError(`Missing alias. Usage: ${usageName(options)} add <alias> <url>`, 1);
|
|
372
|
+
requireNoExtraArgs(extra, `${usageName(options)} add <alias> <url>`);
|
|
373
|
+
const connection = parseConnection(alias, url, options);
|
|
146
374
|
const state = upsertGlobalConnection(alias, connection);
|
|
147
|
-
printJsonOrText(context, { alias, connection },
|
|
148
|
-
|
|
375
|
+
printJsonOrText(context, { alias, connection }, formatSuccessCard("Rig server saved", [
|
|
376
|
+
["alias", alias],
|
|
377
|
+
["target", connection.kind === "remote" ? connection.baseUrl : "local"],
|
|
378
|
+
["next", `${usageName(options)} use ${alias}`]
|
|
379
|
+
]));
|
|
380
|
+
return { ok: true, group: options.group, command: "add", details: { alias, connection, count: Object.keys(state.connections).length } };
|
|
149
381
|
}
|
|
150
382
|
case "use": {
|
|
151
|
-
|
|
383
|
+
let [alias, ...extra] = rest;
|
|
384
|
+
requireNoExtraArgs(extra, `${usageName(options)} use <alias|local>`);
|
|
385
|
+
if (!alias && options.interactiveUse && context.outputMode === "text" && process.stdin.isTTY) {
|
|
386
|
+
alias = await promptForConnectionAlias(context);
|
|
387
|
+
}
|
|
152
388
|
if (!alias)
|
|
153
|
-
throw new
|
|
154
|
-
requireNoExtraArgs(extra, "rig connect use <alias|local>");
|
|
389
|
+
throw new CliError(`Missing alias. Usage: ${usageName(options)} use <alias|local>`, 1);
|
|
155
390
|
if (alias !== "local") {
|
|
156
391
|
const state = readGlobalConnections();
|
|
157
392
|
if (!state.connections[alias])
|
|
158
|
-
throw new
|
|
393
|
+
throw new CliError(`Unknown Rig server: ${alias}`, 1, { hint: "Run `rig server list` to see saved aliases, or save one with `rig server add <alias> <url>`." });
|
|
159
394
|
}
|
|
160
395
|
const repoState = { selected: alias, linkedAt: new Date().toISOString() };
|
|
161
396
|
writeRepoConnection(context.projectRoot, repoState);
|
|
162
|
-
printJsonOrText(context, repoState,
|
|
163
|
-
|
|
397
|
+
printJsonOrText(context, repoState, formatSuccessCard("Rig server selected", [
|
|
398
|
+
["selected", alias],
|
|
399
|
+
["scope", "this repo"],
|
|
400
|
+
["next", "rig task list"]
|
|
401
|
+
]));
|
|
402
|
+
return { ok: true, group: options.group, command: "use", details: repoState };
|
|
164
403
|
}
|
|
165
404
|
case "status": {
|
|
166
|
-
requireNoExtraArgs(rest,
|
|
405
|
+
requireNoExtraArgs(rest, `${usageName(options)} status`);
|
|
167
406
|
const repo = readRepoConnection(context.projectRoot);
|
|
168
407
|
const global = readGlobalConnections();
|
|
169
408
|
const details = { selected: repo?.selected ?? "local", repo, connections: global.connections };
|
|
170
|
-
printJsonOrText(context, details,
|
|
171
|
-
return { ok: true, group:
|
|
409
|
+
printJsonOrText(context, details, formatConnectionStatus(details.selected, global.connections));
|
|
410
|
+
return { ok: true, group: options.group, command: "status", details };
|
|
172
411
|
}
|
|
173
412
|
default:
|
|
174
|
-
throw new
|
|
175
|
-
Usage:
|
|
413
|
+
throw new CliError(`Unknown ${options.group} command: ${String(command)}
|
|
414
|
+
Usage: ${usageName(options)} <list|add|use|status>`, 1);
|
|
176
415
|
}
|
|
177
416
|
}
|
|
178
417
|
export {
|
|
179
|
-
|
|
418
|
+
executeConnectionCommand
|
|
180
419
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type RunnerContext } from "../runner";
|
|
2
|
+
import type { CommandOutcome } from "@rig/runtime/control-plane/runtime/types";
|
|
3
|
+
export type DistDoctorDetails = {
|
|
4
|
+
bun: {
|
|
5
|
+
available: boolean;
|
|
6
|
+
path: string | null;
|
|
7
|
+
version: string;
|
|
8
|
+
};
|
|
9
|
+
rig: {
|
|
10
|
+
onPath: boolean;
|
|
11
|
+
path: string | null;
|
|
12
|
+
runnable: boolean;
|
|
13
|
+
};
|
|
14
|
+
userBinDir: string;
|
|
15
|
+
userBinInPath: boolean;
|
|
16
|
+
latestDistBinary: string | null;
|
|
17
|
+
};
|
|
18
|
+
export type BuildTarget = {
|
|
19
|
+
source: string;
|
|
20
|
+
dest: string;
|
|
21
|
+
cwd: string;
|
|
22
|
+
};
|
|
23
|
+
export declare function collectRigValidatorBuildTargets(input: {
|
|
24
|
+
contextProjectRoot: string;
|
|
25
|
+
hostProjectRoot: string;
|
|
26
|
+
imageDir: string;
|
|
27
|
+
}): BuildTarget[];
|
|
28
|
+
export declare function executeDist(context: RunnerContext, args: string[]): Promise<CommandOutcome>;
|
|
@@ -17,12 +17,19 @@ import { resolve as resolve3 } from "path";
|
|
|
17
17
|
|
|
18
18
|
// packages/cli/src/runner.ts
|
|
19
19
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
20
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
20
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
21
21
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
22
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
23
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
24
22
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
25
|
-
|
|
23
|
+
|
|
24
|
+
class CliError extends RuntimeCliError {
|
|
25
|
+
hint;
|
|
26
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
27
|
+
super(message, exitCode);
|
|
28
|
+
if (options.hint?.trim()) {
|
|
29
|
+
this.hint = options.hint.trim();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
26
33
|
function takeOption(args, option) {
|
|
27
34
|
const rest = [];
|
|
28
35
|
let value;
|
|
@@ -31,7 +38,7 @@ function takeOption(args, option) {
|
|
|
31
38
|
if (current === option) {
|
|
32
39
|
const next = args[index + 1];
|
|
33
40
|
if (!next || next.startsWith("-")) {
|
|
34
|
-
throw new CliError(`Missing value for ${option}`);
|
|
41
|
+
throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
|
|
35
42
|
}
|
|
36
43
|
value = next;
|
|
37
44
|
index += 1;
|
|
@@ -67,7 +74,7 @@ function parseInstallScope(value) {
|
|
|
67
74
|
if (value === "system") {
|
|
68
75
|
return "system";
|
|
69
76
|
}
|
|
70
|
-
throw new
|
|
77
|
+
throw new CliError(`Invalid --scope value: ${value}. Use user|system.`);
|
|
71
78
|
}
|
|
72
79
|
function resolveInstallDir(scope, explicitPath) {
|
|
73
80
|
if (explicitPath) {
|
|
@@ -185,7 +192,7 @@ async function executeDist(context, args) {
|
|
|
185
192
|
switch (command) {
|
|
186
193
|
case "build": {
|
|
187
194
|
const { value: outputDir, rest: pending } = takeOption(rest, "--output-dir");
|
|
188
|
-
requireNoExtraArgs(pending, "
|
|
195
|
+
requireNoExtraArgs(pending, "rig dist build [--output-dir <dir>]");
|
|
189
196
|
const commandParts = ["bun", "run", "packages/cli/bin/build-rig-binaries.ts"];
|
|
190
197
|
if (outputDir) {
|
|
191
198
|
commandParts.push("--output-dir", outputDir);
|
|
@@ -199,7 +206,7 @@ async function executeDist(context, args) {
|
|
|
199
206
|
pending = scopeResult.rest;
|
|
200
207
|
const pathResult = takeOption(pending, "--path");
|
|
201
208
|
pending = pathResult.rest;
|
|
202
|
-
requireNoExtraArgs(pending, "
|
|
209
|
+
requireNoExtraArgs(pending, "rig dist install [--scope user|system] [--path <dir>]");
|
|
203
210
|
const scope = parseInstallScope(scopeResult.value);
|
|
204
211
|
const installDir = resolveInstallDir(scope, pathResult.value);
|
|
205
212
|
mkdirSync(installDir, { recursive: true });
|
|
@@ -211,7 +218,7 @@ async function executeDist(context, args) {
|
|
|
211
218
|
source = resolve3(buildDir, "bin", "rig");
|
|
212
219
|
}
|
|
213
220
|
if (!existsSync(source)) {
|
|
214
|
-
throw new
|
|
221
|
+
throw new CliError(`Unable to locate rig binary at ${source}.`, 2, { hint: "Build it first with `rig dist build`, then re-run `rig dist install`." });
|
|
215
222
|
}
|
|
216
223
|
const installedPath = resolve3(installDir, "rig");
|
|
217
224
|
if (existsSync(installedPath)) {
|
|
@@ -242,7 +249,7 @@ async function executeDist(context, args) {
|
|
|
242
249
|
};
|
|
243
250
|
}
|
|
244
251
|
case "doctor": {
|
|
245
|
-
requireNoExtraArgs(rest, "
|
|
252
|
+
requireNoExtraArgs(rest, "rig dist doctor");
|
|
246
253
|
const details = await runDistDoctor(context.projectRoot);
|
|
247
254
|
if (context.outputMode === "text") {
|
|
248
255
|
console.log(`bun: ${details.bun.available ? `ok (${details.bun.version})` : "missing"}`);
|
|
@@ -253,7 +260,7 @@ async function executeDist(context, args) {
|
|
|
253
260
|
return { ok: true, group: "dist", command, details };
|
|
254
261
|
}
|
|
255
262
|
case "rebuild-agent": {
|
|
256
|
-
requireNoExtraArgs(rest, "
|
|
263
|
+
requireNoExtraArgs(rest, "rig dist rebuild-agent");
|
|
257
264
|
const fp = await computeRuntimeImageFingerprint(context.projectRoot);
|
|
258
265
|
const currentId = computeRuntimeImageId(fp);
|
|
259
266
|
const imagesDir = resolve3(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
|
|
@@ -393,7 +400,7 @@ async function executeDist(context, args) {
|
|
|
393
400
|
return { ok: true, group: "dist", command, details: { imageId: currentId, count: targets.length } };
|
|
394
401
|
}
|
|
395
402
|
default:
|
|
396
|
-
throw new
|
|
403
|
+
throw new CliError(`Unknown dist command: ${command}`, 1, { hint: "Run `rig dist --help` \u2014 commands are build|install|doctor." });
|
|
397
404
|
}
|
|
398
405
|
}
|
|
399
406
|
export {
|