@h-rig/cli-surface-plugin 0.0.6-alpha.146
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 -0
- package/dist/src/app/drone-ui.d.ts +34 -0
- package/dist/src/app/drone-ui.js +278 -0
- package/dist/src/commands/_async-ui.d.ts +10 -0
- package/dist/src/commands/_async-ui.js +121 -0
- package/dist/src/commands/_cli-format.d.ts +56 -0
- package/dist/src/commands/_cli-format.js +332 -0
- package/dist/src/commands/_connection-state.d.ts +54 -0
- package/dist/src/commands/_connection-state.js +187 -0
- package/dist/src/commands/_doctor-checks.d.ts +9 -0
- package/dist/src/commands/_doctor-checks.js +24 -0
- package/dist/src/commands/_help-catalog.d.ts +29 -0
- package/dist/src/commands/_help-catalog.js +157 -0
- package/dist/src/commands/_inprocess-services.d.ts +33 -0
- package/dist/src/commands/_inprocess-services.js +102 -0
- package/dist/src/commands/_json-output.d.ts +11 -0
- package/dist/src/commands/_json-output.js +54 -0
- package/dist/src/commands/_parsers.d.ts +15 -0
- package/dist/src/commands/_parsers.js +114 -0
- package/dist/src/commands/_paths.d.ts +11 -0
- package/dist/src/commands/_paths.js +50 -0
- package/dist/src/commands/_pi-frontend.d.ts +35 -0
- package/dist/src/commands/_pi-frontend.js +64 -0
- package/dist/src/commands/_pi-install.d.ts +42 -0
- package/dist/src/commands/_pi-install.js +167 -0
- package/dist/src/commands/_policy.d.ts +8 -0
- package/dist/src/commands/_policy.js +138 -0
- package/dist/src/commands/_probes.d.ts +1 -0
- package/dist/src/commands/_probes.js +13 -0
- package/dist/src/commands/_run-driver-helpers.d.ts +26 -0
- package/dist/src/commands/_run-driver-helpers.js +132 -0
- package/dist/src/commands/_run-subcommands.d.ts +3 -0
- package/dist/src/commands/_run-subcommands.js +31 -0
- package/dist/src/commands/_spinner.d.ts +25 -0
- package/dist/src/commands/_spinner.js +65 -0
- package/dist/src/commands/agent.d.ts +3 -0
- package/dist/src/commands/agent.js +322 -0
- package/dist/src/commands/config.d.ts +3 -0
- package/dist/src/commands/config.js +193 -0
- package/dist/src/commands/dist.d.ts +28 -0
- package/dist/src/commands/dist.js +435 -0
- package/dist/src/commands/doctor.d.ts +3 -0
- package/dist/src/commands/doctor.js +171 -0
- package/dist/src/commands/github.d.ts +3 -0
- package/dist/src/commands/github.js +342 -0
- package/dist/src/commands/inbox.d.ts +19 -0
- package/dist/src/commands/inbox.js +241 -0
- package/dist/src/commands/init.d.ts +64 -0
- package/dist/src/commands/init.js +1449 -0
- package/dist/src/commands/inspect.d.ts +20 -0
- package/dist/src/commands/inspect.js +337 -0
- package/dist/src/commands/pi.d.ts +3 -0
- package/dist/src/commands/pi.js +177 -0
- package/dist/src/commands/plugin.d.ts +20 -0
- package/dist/src/commands/plugin.js +238 -0
- package/dist/src/commands/profile-and-review.d.ts +4 -0
- package/dist/src/commands/profile-and-review.js +223 -0
- package/dist/src/commands/queue.d.ts +3 -0
- package/dist/src/commands/queue.js +197 -0
- package/dist/src/commands/remote.d.ts +3 -0
- package/dist/src/commands/remote.js +516 -0
- package/dist/src/commands/repo-git-harness.d.ts +5 -0
- package/dist/src/commands/repo-git-harness.js +282 -0
- package/dist/src/commands/run.d.ts +22 -0
- package/dist/src/commands/run.js +645 -0
- package/dist/src/commands/server.d.ts +3 -0
- package/dist/src/commands/server.js +155 -0
- package/dist/src/commands/setup.d.ts +16 -0
- package/dist/src/commands/setup.js +356 -0
- package/dist/src/commands/stats.d.ts +11 -0
- package/dist/src/commands/stats.js +219 -0
- package/dist/src/commands/task-run-driver.d.ts +93 -0
- package/dist/src/commands/task-run-driver.js +136 -0
- package/dist/src/commands/task.d.ts +46 -0
- package/dist/src/commands/task.js +555 -0
- package/dist/src/commands/test.d.ts +3 -0
- package/dist/src/commands/test.js +46 -0
- package/dist/src/commands/triage.d.ts +11 -0
- package/dist/src/commands/triage.js +224 -0
- package/dist/src/commands/workspace.d.ts +3 -0
- package/dist/src/commands/workspace.js +130 -0
- package/dist/src/kernel-dispatch.d.ts +15 -0
- package/dist/src/kernel-dispatch.js +16 -0
- package/dist/src/plugin.d.ts +3 -0
- package/dist/src/plugin.js +5440 -0
- package/dist/src/rig-config-package-deps.d.ts +10 -0
- package/dist/src/rig-config-package-deps.js +272 -0
- package/dist/src/runner.d.ts +47 -0
- package/dist/src/runner.js +267 -0
- package/dist/src/version.d.ts +8 -0
- package/dist/src/version.js +47 -0
- package/dist/src/withMutedConsole.d.ts +2 -0
- package/dist/src/withMutedConsole.js +42 -0
- package/package.json +34 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/cli-surface-plugin/src/commands/_cli-format.ts
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
import { runStatusColorRole, runStatusText, statusColorRole } from "@rig/client";
|
|
5
|
+
var dim = pc.dim;
|
|
6
|
+
var faintBar = pc.dim("\u2502");
|
|
7
|
+
var accent = pc.cyan;
|
|
8
|
+
function numberField(record, key) {
|
|
9
|
+
const value = record[key];
|
|
10
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
11
|
+
}
|
|
12
|
+
function arrayField(record, key) {
|
|
13
|
+
const value = record[key];
|
|
14
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
|
|
15
|
+
}
|
|
16
|
+
function rawObject(record) {
|
|
17
|
+
const value = record.raw;
|
|
18
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : record;
|
|
19
|
+
}
|
|
20
|
+
function truncate(value, width) {
|
|
21
|
+
return value.length <= width ? value : `${value.slice(0, Math.max(0, width - 1))}\u2026`;
|
|
22
|
+
}
|
|
23
|
+
function pad(value, width) {
|
|
24
|
+
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
25
|
+
}
|
|
26
|
+
function colorForRole(role) {
|
|
27
|
+
switch (role) {
|
|
28
|
+
case "success":
|
|
29
|
+
return pc.green;
|
|
30
|
+
case "action-yellow":
|
|
31
|
+
return pc.yellow;
|
|
32
|
+
case "active-cyan":
|
|
33
|
+
return pc.cyan;
|
|
34
|
+
case "failure":
|
|
35
|
+
return pc.red;
|
|
36
|
+
case "muted":
|
|
37
|
+
case "neutral":
|
|
38
|
+
return pc.dim;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function statusColor(status) {
|
|
42
|
+
return colorForRole(statusColorRole(status));
|
|
43
|
+
}
|
|
44
|
+
function compactDate(value) {
|
|
45
|
+
const parsed = Date.parse(value);
|
|
46
|
+
if (!Number.isFinite(parsed))
|
|
47
|
+
return value;
|
|
48
|
+
return new Date(parsed).toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "Z");
|
|
49
|
+
}
|
|
50
|
+
function compactValue(value) {
|
|
51
|
+
if (value === null || value === undefined)
|
|
52
|
+
return "\u2014";
|
|
53
|
+
if (typeof value === "string")
|
|
54
|
+
return value;
|
|
55
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
56
|
+
return String(value);
|
|
57
|
+
return JSON.stringify(value);
|
|
58
|
+
}
|
|
59
|
+
function firstString(record, keys, fallback = "") {
|
|
60
|
+
for (const key of keys) {
|
|
61
|
+
const value = record[key];
|
|
62
|
+
if (typeof value === "string" && value.trim())
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
return fallback;
|
|
66
|
+
}
|
|
67
|
+
function runIdOf(run) {
|
|
68
|
+
return firstString(run, ["runId", "id"], "(unknown-run)");
|
|
69
|
+
}
|
|
70
|
+
function taskIdOf(run) {
|
|
71
|
+
return firstString(run, ["taskId", "task", "task_id"]);
|
|
72
|
+
}
|
|
73
|
+
function runTitleOf(run) {
|
|
74
|
+
return firstString(run, ["title", "summary", "name"], taskIdOf(run) || "(untitled)");
|
|
75
|
+
}
|
|
76
|
+
function requestIdOf(entry) {
|
|
77
|
+
return firstString(entry, ["requestId", "id", "approvalId", "inputId"], "(unknown-request)");
|
|
78
|
+
}
|
|
79
|
+
function runLikeStatusText(run) {
|
|
80
|
+
return runStatusText(run);
|
|
81
|
+
}
|
|
82
|
+
function runLikeStatusColor(run) {
|
|
83
|
+
return colorForRole(runStatusColorRole(run));
|
|
84
|
+
}
|
|
85
|
+
function printFormattedOutput(message) {
|
|
86
|
+
console.log(message);
|
|
87
|
+
}
|
|
88
|
+
function formatStatusPill(status) {
|
|
89
|
+
const label = status || "unknown";
|
|
90
|
+
return statusColor(label)(`\u25CF ${label}`);
|
|
91
|
+
}
|
|
92
|
+
function formatSection(title, subtitle) {
|
|
93
|
+
return `${pc.bold(accent("\u25C6"))} ${pc.bold(title)}${subtitle ? dim(` \u2014 ${subtitle}`) : ""}`;
|
|
94
|
+
}
|
|
95
|
+
function formatSuccessCard(title, rows = []) {
|
|
96
|
+
const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${faintBar} ${dim(key.padEnd(12))} ${value}`);
|
|
97
|
+
return [formatSection(title), ...body].join(`
|
|
98
|
+
`);
|
|
99
|
+
}
|
|
100
|
+
function formatNextSteps(steps) {
|
|
101
|
+
if (steps.length === 0)
|
|
102
|
+
return [];
|
|
103
|
+
return [pc.bold("Next"), ...steps.map((step) => `${accent("\u203A")} ${step}`)];
|
|
104
|
+
}
|
|
105
|
+
function formatLegacyAutomationSurface() {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
function formatTaskList(tasks, options = {}) {
|
|
109
|
+
if (tasks.length === 0) {
|
|
110
|
+
return [formatSection("Tasks", "empty"), dim("No tasks available."), "", ...formatNextSteps(["Refresh: `rig task list`", "Pick next: `rig task next`"])].join(`
|
|
111
|
+
`);
|
|
112
|
+
}
|
|
113
|
+
if (options.raw) {
|
|
114
|
+
return JSON.stringify(tasks, null, 2);
|
|
115
|
+
}
|
|
116
|
+
const rows = tasks.map((task) => summarizeTaskRow(task));
|
|
117
|
+
const idWidth = Math.max(2, ...rows.map((row) => row.id.length));
|
|
118
|
+
const statusWidth = Math.max(6, ...rows.map((row) => row.status.length));
|
|
119
|
+
const body = rows.map((row) => {
|
|
120
|
+
const labels = row.labels.length > 0 ? dim(` ${row.labels.slice(0, 4).map((label) => `#${label}`).join(" ")}`) : "";
|
|
121
|
+
const source = row.source ? dim(` ${row.source}`) : "";
|
|
122
|
+
return [
|
|
123
|
+
pc.bold(pad(truncate(row.id, idWidth), idWidth)),
|
|
124
|
+
statusColor(row.status)(pad(truncate(row.status, statusWidth), statusWidth)),
|
|
125
|
+
`${row.title}${labels}${source}`
|
|
126
|
+
].join(" ");
|
|
127
|
+
});
|
|
128
|
+
return [formatSection("Tasks", `${tasks.length}`), ...body].join(`
|
|
129
|
+
`);
|
|
130
|
+
}
|
|
131
|
+
function summarizeTaskRow(task) {
|
|
132
|
+
const raw = rawObject(task);
|
|
133
|
+
return {
|
|
134
|
+
id: firstString(task, ["id", "taskId"], "(unknown-task)"),
|
|
135
|
+
title: firstString(task, ["title", "summary", "name"], "(untitled)"),
|
|
136
|
+
status: firstString(task, ["status"], "unknown"),
|
|
137
|
+
labels: arrayField(task, "labels").length > 0 ? arrayField(task, "labels") : arrayField(raw, "labels"),
|
|
138
|
+
source: firstString(task, ["source"], "")
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function formatTaskCard(task, options = {}) {
|
|
142
|
+
const raw = rawObject(task);
|
|
143
|
+
const lines = [formatSection(options.title ?? (options.selected ? "Selected task" : "Task"))];
|
|
144
|
+
for (const [label, value] of [
|
|
145
|
+
["id", firstString(task, ["id", "taskId"], "(unknown-task)")],
|
|
146
|
+
["title", firstString(task, ["title", "summary", "name"], "(untitled)")],
|
|
147
|
+
["status", firstString(task, ["status"], "unknown")],
|
|
148
|
+
["source", firstString(task, ["source"], "")],
|
|
149
|
+
["url", firstString(raw, ["url"], "")]
|
|
150
|
+
]) {
|
|
151
|
+
if (value)
|
|
152
|
+
lines.push(`${faintBar} ${dim(label.padEnd(12))} ${value}`);
|
|
153
|
+
}
|
|
154
|
+
const labels = arrayField(task, "labels");
|
|
155
|
+
if (labels.length > 0)
|
|
156
|
+
lines.push(`${faintBar} ${dim("labels".padEnd(12))} ${labels.map((label) => `#${label}`).join(" ")}`);
|
|
157
|
+
return lines.join(`
|
|
158
|
+
`);
|
|
159
|
+
}
|
|
160
|
+
function formatTaskDetails(task) {
|
|
161
|
+
return formatTaskCard(task, { title: "Task details" });
|
|
162
|
+
}
|
|
163
|
+
function formatRunList(runs, options = {}) {
|
|
164
|
+
if (runs.length === 0) {
|
|
165
|
+
return [
|
|
166
|
+
formatSection("Runs", "none recorded"),
|
|
167
|
+
options.source === "server" ? dim("No runs recorded on the selected server.") : dim("No runs recorded in local state."),
|
|
168
|
+
"",
|
|
169
|
+
...formatLegacyAutomationSurface()
|
|
170
|
+
].join(`
|
|
171
|
+
`);
|
|
172
|
+
}
|
|
173
|
+
const body = runs.map((run) => {
|
|
174
|
+
const row = run;
|
|
175
|
+
const runId = runIdOf(row);
|
|
176
|
+
const status = runLikeStatusText(run);
|
|
177
|
+
const runtime = firstString(row, ["runtime", "runtimeAdapter"], "");
|
|
178
|
+
return [
|
|
179
|
+
pc.bold(runId),
|
|
180
|
+
runLikeStatusColor(run)(pad(truncate(status, 12), 12)),
|
|
181
|
+
`${runTitleOf(row)}${runtime ? dim(` ${runtime}`) : ""}`
|
|
182
|
+
].join(" ");
|
|
183
|
+
});
|
|
184
|
+
return [formatSection("Runs", options.source === "server" ? "selected server" : "local state"), ...body, "", ...formatLegacyAutomationSurface()].join(`
|
|
185
|
+
`);
|
|
186
|
+
}
|
|
187
|
+
function formatSubmittedRun(input) {
|
|
188
|
+
return formatSuccessCard("Run submitted", [
|
|
189
|
+
["run", input.runId],
|
|
190
|
+
["task", input.taskId ?? undefined],
|
|
191
|
+
["title", input.title ?? undefined],
|
|
192
|
+
["queue", input.queuePosition ?? undefined]
|
|
193
|
+
]);
|
|
194
|
+
}
|
|
195
|
+
function formatRunCard(run, options = {}) {
|
|
196
|
+
const record = run;
|
|
197
|
+
const lines = [formatSection(options.title ?? "Run")];
|
|
198
|
+
for (const [label, value] of [
|
|
199
|
+
["run", runIdOf(record)],
|
|
200
|
+
["task", taskIdOf(record)],
|
|
201
|
+
["title", runTitleOf(record)],
|
|
202
|
+
["status", runLikeStatusText(run)],
|
|
203
|
+
["runtime", firstString(record, ["runtime", "runtimeAdapter"], "")],
|
|
204
|
+
["created", firstString(record, ["createdAt"], "")],
|
|
205
|
+
["updated", firstString(record, ["updatedAt"], "")]
|
|
206
|
+
]) {
|
|
207
|
+
if (value)
|
|
208
|
+
lines.push(`${faintBar} ${dim(label.padEnd(12))} ${label === "status" ? runLikeStatusColor(run)(value) : label === "created" || label === "updated" ? compactDate(value) : value}`);
|
|
209
|
+
}
|
|
210
|
+
const errorSummary = firstString(record, ["errorSummary"]);
|
|
211
|
+
if (errorSummary)
|
|
212
|
+
lines.push(`${faintBar} Error: ${errorSummary}`);
|
|
213
|
+
return lines.join(`
|
|
214
|
+
`);
|
|
215
|
+
}
|
|
216
|
+
function formatRunStatus(summary, options = {}) {
|
|
217
|
+
const activeRuns = summary.activeRuns ?? [];
|
|
218
|
+
const recentRuns = summary.recentRuns ?? [];
|
|
219
|
+
const lines = [formatSection("Run status", options.source === "server" ? "selected server" : "local state")];
|
|
220
|
+
lines.push(`Active runs (${activeRuns.length})`);
|
|
221
|
+
if (activeRuns.length === 0)
|
|
222
|
+
lines.push(dim("No active runs."));
|
|
223
|
+
else
|
|
224
|
+
for (const run of activeRuns)
|
|
225
|
+
lines.push(formatRunSummaryLine(run));
|
|
226
|
+
lines.push("", `Recent runs (${recentRuns.length})`);
|
|
227
|
+
if (recentRuns.length === 0)
|
|
228
|
+
lines.push(dim("No recent terminal runs."));
|
|
229
|
+
else
|
|
230
|
+
for (const run of recentRuns.slice(0, 10))
|
|
231
|
+
lines.push(formatRunSummaryLine(run));
|
|
232
|
+
lines.push("", ...formatLegacyAutomationSurface());
|
|
233
|
+
return lines.join(`
|
|
234
|
+
`);
|
|
235
|
+
}
|
|
236
|
+
function formatRunSummaryLine(run) {
|
|
237
|
+
const record = run;
|
|
238
|
+
const runId = runIdOf(record);
|
|
239
|
+
const taskId = taskIdOf(record);
|
|
240
|
+
const title = runTitleOf(record);
|
|
241
|
+
const status = runLikeStatusText(run);
|
|
242
|
+
const runtime = firstString(record, ["runtime", "runtimeAdapter"], "");
|
|
243
|
+
const descriptor = [taskId, title].filter(Boolean).join(" \xB7 ");
|
|
244
|
+
return `${faintBar} ${pc.bold(runId)} ${runLikeStatusColor(run)(`\u25CF ${status}`)} ${descriptor}${runtime ? dim(` ${runtime}`) : ""}`;
|
|
245
|
+
}
|
|
246
|
+
function formatInboxList(kind, entries) {
|
|
247
|
+
const title = kind === "approvals" ? "Pending approvals" : "Pending user input";
|
|
248
|
+
if (entries.length === 0) {
|
|
249
|
+
return [
|
|
250
|
+
formatSection(title, "empty"),
|
|
251
|
+
dim(kind === "approvals" ? "No pending approvals." : "No pending user-input requests."),
|
|
252
|
+
"",
|
|
253
|
+
...formatLegacyAutomationSurface()
|
|
254
|
+
].join(`
|
|
255
|
+
`);
|
|
256
|
+
}
|
|
257
|
+
const lines = [formatSection(title, `${entries.length}`)];
|
|
258
|
+
for (const record of entries) {
|
|
259
|
+
const runId = firstString(record, ["runId"], "(unknown-run)");
|
|
260
|
+
const taskId = firstString(record, ["taskId"], "");
|
|
261
|
+
const status = firstString(record, ["status"], "pending");
|
|
262
|
+
const prompt = firstString(record, ["prompt", "message", "reason", "title", "summary"], kind === "approvals" ? "Approval requested" : "Input requested");
|
|
263
|
+
lines.push(`${faintBar} ${pc.bold(requestIdOf(record))} ${formatStatusPill(status)} ${prompt}`);
|
|
264
|
+
lines.push(`${faintBar} ${dim("run".padEnd(12))} ${runId}${taskId ? dim(` task ${taskId}`) : ""}`);
|
|
265
|
+
}
|
|
266
|
+
lines.push("", ...formatLegacyAutomationSurface(), ...formatNextSteps(kind === "approvals" ? ["Resolve: `rig inbox approve --run <run-id> --request <request-id> --decision approve|reject`"] : ["Reply: `rig inbox answer --run <run-id> --request <request-id> --text ...`"]));
|
|
267
|
+
return lines.join(`
|
|
268
|
+
`);
|
|
269
|
+
}
|
|
270
|
+
function formatConnectionList(connections) {
|
|
271
|
+
const names = Object.keys(connections).sort();
|
|
272
|
+
if (names.length === 0) {
|
|
273
|
+
return [formatSection("Rig servers", "empty"), dim("No saved server connections.")].join(`
|
|
274
|
+
`);
|
|
275
|
+
}
|
|
276
|
+
return [formatSection("Rig servers", `${names.length}`), ...names.map((name) => {
|
|
277
|
+
const connection = connections[name];
|
|
278
|
+
const target = connection.kind === "remote" ? connection.baseUrl ?? "(missing url)" : connection.mode ?? "local";
|
|
279
|
+
return `${faintBar} ${pc.bold(name)} ${dim(connection.kind)} ${target}`;
|
|
280
|
+
})].join(`
|
|
281
|
+
`);
|
|
282
|
+
}
|
|
283
|
+
function formatConnectionStatus(selected, connections, repo, remoteProjectLink) {
|
|
284
|
+
const selectedConnection = connections[selected];
|
|
285
|
+
const lines = [formatSection("Rig server", "selected for this repo")];
|
|
286
|
+
lines.push(`${faintBar} ${dim("selected".padEnd(12))} ${pc.bold(selected)}`);
|
|
287
|
+
if (selectedConnection) {
|
|
288
|
+
lines.push(`${faintBar} ${dim("kind".padEnd(12))} ${selectedConnection.kind}`);
|
|
289
|
+
if (selectedConnection.baseUrl)
|
|
290
|
+
lines.push(`${faintBar} ${dim("base url".padEnd(12))} ${selectedConnection.baseUrl}`);
|
|
291
|
+
if (selectedConnection.mode)
|
|
292
|
+
lines.push(`${faintBar} ${dim("mode".padEnd(12))} ${selectedConnection.mode}`);
|
|
293
|
+
}
|
|
294
|
+
if (repo?.project)
|
|
295
|
+
lines.push(`${faintBar} ${dim("project".padEnd(12))} ${repo.project}`);
|
|
296
|
+
if (repo?.serverProjectRoot)
|
|
297
|
+
lines.push(`${faintBar} ${dim("server root".padEnd(12))} ${repo.serverProjectRoot}`);
|
|
298
|
+
if (remoteProjectLink?.status)
|
|
299
|
+
lines.push(`${faintBar} ${dim("link".padEnd(12))} ${remoteProjectLink.status}`);
|
|
300
|
+
if (remoteProjectLink?.message)
|
|
301
|
+
lines.push(`${faintBar} ${dim("message".padEnd(12))} ${remoteProjectLink.message}`);
|
|
302
|
+
if (remoteProjectLink?.hint)
|
|
303
|
+
lines.push(`${faintBar} ${dim("hint".padEnd(12))} ${remoteProjectLink.hint}`);
|
|
304
|
+
return lines.join(`
|
|
305
|
+
`);
|
|
306
|
+
}
|
|
307
|
+
function formatStatsTable(rows) {
|
|
308
|
+
return rows.map(([label, value]) => `${faintBar} ${dim(label.padEnd(20))} ${compactValue(value)}`).join(`
|
|
309
|
+
`);
|
|
310
|
+
}
|
|
311
|
+
function readOptionalCount(record, key) {
|
|
312
|
+
return numberField(record, key);
|
|
313
|
+
}
|
|
314
|
+
export {
|
|
315
|
+
readOptionalCount,
|
|
316
|
+
printFormattedOutput,
|
|
317
|
+
formatTaskList,
|
|
318
|
+
formatTaskDetails,
|
|
319
|
+
formatTaskCard,
|
|
320
|
+
formatSuccessCard,
|
|
321
|
+
formatSubmittedRun,
|
|
322
|
+
formatStatusPill,
|
|
323
|
+
formatStatsTable,
|
|
324
|
+
formatSection,
|
|
325
|
+
formatRunStatus,
|
|
326
|
+
formatRunList,
|
|
327
|
+
formatRunCard,
|
|
328
|
+
formatNextSteps,
|
|
329
|
+
formatInboxList,
|
|
330
|
+
formatConnectionStatus,
|
|
331
|
+
formatConnectionList
|
|
332
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export type RigConnection = {
|
|
2
|
+
kind: "local";
|
|
3
|
+
mode: "auto";
|
|
4
|
+
} | {
|
|
5
|
+
kind: "remote";
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
};
|
|
8
|
+
export type GlobalConnectionState = {
|
|
9
|
+
connections: Record<string, RigConnection>;
|
|
10
|
+
};
|
|
11
|
+
export type RepoConnectionState = {
|
|
12
|
+
selected: string;
|
|
13
|
+
project?: string;
|
|
14
|
+
linkedAt?: string;
|
|
15
|
+
/**
|
|
16
|
+
* The server-host project root this repo scopes to (multi-root serving).
|
|
17
|
+
* Sent as the x-rig-project-root header on every remote request.
|
|
18
|
+
*/
|
|
19
|
+
serverProjectRoot?: string;
|
|
20
|
+
/** The selected alias that produced serverProjectRoot. Prevents cross-alias reuse. */
|
|
21
|
+
serverProjectRootAlias?: string;
|
|
22
|
+
/** The remote base URL that produced serverProjectRoot. Prevents same-alias retarget leaks. */
|
|
23
|
+
serverProjectRootBaseUrl?: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function resolveGlobalConnectionsPath(env?: NodeJS.ProcessEnv): string;
|
|
26
|
+
export declare function resolveRepoConnectionPath(projectRoot: string): string;
|
|
27
|
+
export declare function readGlobalConnections(options?: {
|
|
28
|
+
env?: NodeJS.ProcessEnv;
|
|
29
|
+
}): GlobalConnectionState;
|
|
30
|
+
export declare function writeGlobalConnections(state: GlobalConnectionState, options?: {
|
|
31
|
+
env?: NodeJS.ProcessEnv;
|
|
32
|
+
}): void;
|
|
33
|
+
export declare function upsertGlobalConnection(alias: string, connection: RigConnection, options?: {
|
|
34
|
+
env?: NodeJS.ProcessEnv;
|
|
35
|
+
}): GlobalConnectionState;
|
|
36
|
+
export declare function readRepoConnection(projectRoot: string): RepoConnectionState | null;
|
|
37
|
+
export declare function writeRepoConnection(projectRoot: string, state: RepoConnectionState): void;
|
|
38
|
+
export declare function resolveSelectedConnection(projectRoot: string, options?: {
|
|
39
|
+
env?: NodeJS.ProcessEnv;
|
|
40
|
+
}): {
|
|
41
|
+
alias: string;
|
|
42
|
+
connection: RigConnection;
|
|
43
|
+
serverProjectRoot?: string;
|
|
44
|
+
} | null;
|
|
45
|
+
/** Persist the server-host project root this repo scopes to (multi-root serving). */
|
|
46
|
+
export declare function writeRepoServerProjectRoot(projectRoot: string, serverProjectRoot: string, metadata?: {
|
|
47
|
+
alias?: string;
|
|
48
|
+
baseUrl?: string;
|
|
49
|
+
project?: string;
|
|
50
|
+
}): void;
|
|
51
|
+
/** Remove a stale server-host root while preserving selected server/repo state. */
|
|
52
|
+
export declare function clearRepoServerProjectRoot(projectRoot: string): void;
|
|
53
|
+
/** Whether this repo's selected connection targets a remote Rig server. */
|
|
54
|
+
export declare function isRemoteConnectionSelected(projectRoot: string): boolean;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/cli-surface-plugin/src/commands/_connection-state.ts
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import { dirname, resolve } from "path";
|
|
6
|
+
|
|
7
|
+
// packages/cli-surface-plugin/src/runner.ts
|
|
8
|
+
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
9
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
10
|
+
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
11
|
+
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
12
|
+
|
|
13
|
+
class CliError extends RuntimeCliError {
|
|
14
|
+
hint;
|
|
15
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
16
|
+
super(message, exitCode);
|
|
17
|
+
if (options.hint?.trim()) {
|
|
18
|
+
this.hint = options.hint.trim();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// packages/cli-surface-plugin/src/commands/_connection-state.ts
|
|
24
|
+
function resolveGlobalConnectionsPath(env = process.env) {
|
|
25
|
+
const explicit = env.RIG_CONNECTIONS_FILE?.trim();
|
|
26
|
+
if (explicit)
|
|
27
|
+
return resolve(explicit);
|
|
28
|
+
const stateDir = env.RIG_GLOBAL_STATE_DIR?.trim();
|
|
29
|
+
if (stateDir)
|
|
30
|
+
return resolve(stateDir, "connections.json");
|
|
31
|
+
return resolve(homedir(), ".rig", "connections.json");
|
|
32
|
+
}
|
|
33
|
+
function resolveRepoConnectionPath(projectRoot) {
|
|
34
|
+
return resolve(projectRoot, ".rig", "state", "connection.json");
|
|
35
|
+
}
|
|
36
|
+
function readJsonFile(path) {
|
|
37
|
+
if (!existsSync(path))
|
|
38
|
+
return null;
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
41
|
+
} catch (error) {
|
|
42
|
+
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>`." });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function writeJsonFile(path, value) {
|
|
46
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
47
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
48
|
+
`, "utf8");
|
|
49
|
+
}
|
|
50
|
+
function normalizeConnection(value) {
|
|
51
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
52
|
+
return null;
|
|
53
|
+
const record = value;
|
|
54
|
+
if (record.kind === "local")
|
|
55
|
+
return { kind: "local", mode: "auto" };
|
|
56
|
+
if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
|
|
57
|
+
const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
|
|
58
|
+
return { kind: "remote", baseUrl };
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
function readGlobalConnections(options = {}) {
|
|
63
|
+
const path = resolveGlobalConnectionsPath(options.env ?? process.env);
|
|
64
|
+
const payload = readJsonFile(path);
|
|
65
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
66
|
+
return { connections: {} };
|
|
67
|
+
}
|
|
68
|
+
const rawConnections = payload.connections;
|
|
69
|
+
const connections = {};
|
|
70
|
+
if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
|
|
71
|
+
for (const [alias, raw] of Object.entries(rawConnections)) {
|
|
72
|
+
const connection = normalizeConnection(raw);
|
|
73
|
+
if (connection)
|
|
74
|
+
connections[alias] = connection;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { connections };
|
|
78
|
+
}
|
|
79
|
+
function writeGlobalConnections(state, options = {}) {
|
|
80
|
+
writeJsonFile(resolveGlobalConnectionsPath(options.env ?? process.env), state);
|
|
81
|
+
}
|
|
82
|
+
function upsertGlobalConnection(alias, connection, options = {}) {
|
|
83
|
+
const cleanAlias = alias.trim();
|
|
84
|
+
if (!cleanAlias)
|
|
85
|
+
throw new CliError("Connection alias is required.", 1, { hint: "Save a server with `rig server add <alias> <url>`." });
|
|
86
|
+
const state = readGlobalConnections(options);
|
|
87
|
+
state.connections[cleanAlias] = connection;
|
|
88
|
+
writeGlobalConnections(state, options);
|
|
89
|
+
return state;
|
|
90
|
+
}
|
|
91
|
+
function readRepoConnection(projectRoot) {
|
|
92
|
+
const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
|
|
93
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
94
|
+
return null;
|
|
95
|
+
const record = payload;
|
|
96
|
+
const selected = typeof record.selected === "string" ? record.selected.trim() : "";
|
|
97
|
+
if (!selected)
|
|
98
|
+
return null;
|
|
99
|
+
return {
|
|
100
|
+
selected,
|
|
101
|
+
project: typeof record.project === "string" ? record.project : undefined,
|
|
102
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
103
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined,
|
|
104
|
+
serverProjectRootAlias: typeof record.serverProjectRootAlias === "string" && record.serverProjectRootAlias.trim() ? record.serverProjectRootAlias.trim() : undefined,
|
|
105
|
+
serverProjectRootBaseUrl: typeof record.serverProjectRootBaseUrl === "string" && record.serverProjectRootBaseUrl.trim() ? record.serverProjectRootBaseUrl.trim().replace(/\/+$/, "") : undefined
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function writeRepoConnection(projectRoot, state) {
|
|
109
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
110
|
+
}
|
|
111
|
+
function rootAllowedForSelection(repo, connection) {
|
|
112
|
+
const root = repo.serverProjectRoot?.trim();
|
|
113
|
+
if (!root)
|
|
114
|
+
return;
|
|
115
|
+
if (connection.kind === "remote") {
|
|
116
|
+
if (repo.serverProjectRootAlias !== repo.selected)
|
|
117
|
+
return;
|
|
118
|
+
if (repo.serverProjectRootBaseUrl !== connection.baseUrl)
|
|
119
|
+
return;
|
|
120
|
+
} else if (repo.serverProjectRootAlias && repo.serverProjectRootAlias !== repo.selected) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
return root;
|
|
124
|
+
}
|
|
125
|
+
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
126
|
+
const repo = readRepoConnection(projectRoot);
|
|
127
|
+
if (!repo)
|
|
128
|
+
return null;
|
|
129
|
+
if (repo.selected === "local")
|
|
130
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
131
|
+
const global = readGlobalConnections(options);
|
|
132
|
+
const connection = global.connections[repo.selected];
|
|
133
|
+
if (!connection) {
|
|
134
|
+
throw new CliError(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
135
|
+
}
|
|
136
|
+
return { alias: repo.selected, connection, serverProjectRoot: rootAllowedForSelection(repo, connection) };
|
|
137
|
+
}
|
|
138
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot, metadata = {}) {
|
|
139
|
+
const repo = readRepoConnection(projectRoot);
|
|
140
|
+
if (!repo)
|
|
141
|
+
return;
|
|
142
|
+
let inferred = metadata;
|
|
143
|
+
if (!inferred.alias || !inferred.baseUrl) {
|
|
144
|
+
try {
|
|
145
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
146
|
+
if (selected?.connection.kind === "remote") {
|
|
147
|
+
inferred = {
|
|
148
|
+
alias: inferred.alias ?? selected.alias,
|
|
149
|
+
baseUrl: inferred.baseUrl ?? selected.connection.baseUrl
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
} catch {}
|
|
153
|
+
}
|
|
154
|
+
writeRepoConnection(projectRoot, {
|
|
155
|
+
...repo,
|
|
156
|
+
...metadata.project ? { project: metadata.project } : {},
|
|
157
|
+
serverProjectRoot,
|
|
158
|
+
...inferred.alias ? { serverProjectRootAlias: inferred.alias } : {},
|
|
159
|
+
...inferred.baseUrl ? { serverProjectRootBaseUrl: inferred.baseUrl.replace(/\/+$/, "") } : {}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function clearRepoServerProjectRoot(projectRoot) {
|
|
163
|
+
const repo = readRepoConnection(projectRoot);
|
|
164
|
+
if (!repo)
|
|
165
|
+
return;
|
|
166
|
+
writeRepoConnection(projectRoot, {
|
|
167
|
+
selected: repo.selected,
|
|
168
|
+
...repo.project ? { project: repo.project } : {},
|
|
169
|
+
...repo.linkedAt ? { linkedAt: repo.linkedAt } : {}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
function isRemoteConnectionSelected(projectRoot) {
|
|
173
|
+
return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
|
|
174
|
+
}
|
|
175
|
+
export {
|
|
176
|
+
writeRepoServerProjectRoot,
|
|
177
|
+
writeRepoConnection,
|
|
178
|
+
writeGlobalConnections,
|
|
179
|
+
upsertGlobalConnection,
|
|
180
|
+
resolveSelectedConnection,
|
|
181
|
+
resolveRepoConnectionPath,
|
|
182
|
+
resolveGlobalConnectionsPath,
|
|
183
|
+
readRepoConnection,
|
|
184
|
+
readGlobalConnections,
|
|
185
|
+
isRemoteConnectionSelected,
|
|
186
|
+
clearRepoServerProjectRoot
|
|
187
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { countDoctorFailures, runDoctorChecks } from "@rig/client";
|
|
2
|
+
import type { DoctorCheck, DoctorStatus, RunDoctorChecksOptions } from "@rig/client";
|
|
3
|
+
export type RigDoctorCheck = DoctorCheck;
|
|
4
|
+
export type { DoctorStatus };
|
|
5
|
+
export type RunRigDoctorChecksOptions = RunDoctorChecksOptions;
|
|
6
|
+
export declare const runRigDoctorChecks: typeof runDoctorChecks;
|
|
7
|
+
export { countDoctorFailures };
|
|
8
|
+
export declare function formatDoctorChecks(checks: readonly RigDoctorCheck[]): string;
|
|
9
|
+
export declare function throwIfDoctorFailed(checks: readonly RigDoctorCheck[]): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/cli-surface-plugin/src/commands/_doctor-checks.ts
|
|
3
|
+
import { countDoctorFailures, runDoctorChecks } from "@rig/client";
|
|
4
|
+
var runRigDoctorChecks = runDoctorChecks;
|
|
5
|
+
function formatDoctorChecks(checks) {
|
|
6
|
+
return checks.map((entry) => {
|
|
7
|
+
const status = String(entry.status ?? entry.level ?? "warn");
|
|
8
|
+
const marker = status === "pass" || status === "ok" ? "OK" : status === "warn" ? "WARN" : "FAIL";
|
|
9
|
+
return `${marker}: ${entry.label}${entry.detail ? ` \u2014 ${entry.detail}` : ""}${entry.remediation ? `
|
|
10
|
+
fix: ${entry.remediation}` : ""}`;
|
|
11
|
+
}).join(`
|
|
12
|
+
`);
|
|
13
|
+
}
|
|
14
|
+
function throwIfDoctorFailed(checks) {
|
|
15
|
+
const failures = countDoctorFailures(checks);
|
|
16
|
+
if (failures > 0)
|
|
17
|
+
throw new Error(`Doctor checks failed (${failures}).`);
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
throwIfDoctorFailed,
|
|
21
|
+
runRigDoctorChecks,
|
|
22
|
+
formatDoctorChecks,
|
|
23
|
+
countDoctorFailures
|
|
24
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { helpCatalog, type HelpCommand, type HelpGroup, type TopLevelSection } from "@rig/contracts";
|
|
2
|
+
export { helpCatalog };
|
|
3
|
+
export type { HelpCommand, HelpGroup, TopLevelSection };
|
|
4
|
+
/**
|
|
5
|
+
* Night City boot screen. Rendered only on interactive TTY launches — plain
|
|
6
|
+
* mode and --json never see it.
|
|
7
|
+
*/
|
|
8
|
+
export declare function renderRigBanner(version?: string): string;
|
|
9
|
+
export declare function renderTopLevelHelp(): string;
|
|
10
|
+
export declare function renderAdvancedHelp(): string;
|
|
11
|
+
export declare function renderGroupHelp(groupName: string): string | null;
|
|
12
|
+
export declare function listHelpGroups(): string[];
|
|
13
|
+
/**
|
|
14
|
+
* Fuzzy-match a bare word against known group subcommand names so the
|
|
15
|
+
* unknown-command error can suggest the canonical `rig <group> <cmd>`
|
|
16
|
+
* spelling. Primary groups win ties (ALL_GROUPS is primary-first).
|
|
17
|
+
*/
|
|
18
|
+
export declare function suggestGroupCommandForWord(word: string, liveGroups?: ReadonlySet<string>): string | null;
|
|
19
|
+
export type TopLevelLaunchState = {
|
|
20
|
+
/** True when the cwd resolves to an initialized Rig project. */
|
|
21
|
+
projectInitialized?: boolean;
|
|
22
|
+
/** Short description of the selected server connection, if known. */
|
|
23
|
+
selectedServer?: string | null;
|
|
24
|
+
/** CLI version for the banner footer. */
|
|
25
|
+
version?: string;
|
|
26
|
+
};
|
|
27
|
+
export declare function printTopLevelHelp(state?: TopLevelLaunchState): void;
|
|
28
|
+
export declare function printAdvancedHelp(): void;
|
|
29
|
+
export declare function printGroupHelpDocument(groupName: string): void;
|