@h-rig/cli 0.0.6-alpha.6 → 0.0.6-alpha.61
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/rig.js +5033 -1895
- package/dist/src/commands/_authority-runs.js +2 -3
- package/dist/src/commands/_cli-format.js +369 -0
- package/dist/src/commands/_connection-state.js +12 -6
- package/dist/src/commands/_doctor-checks.js +79 -34
- package/dist/src/commands/_help-catalog.js +446 -0
- package/dist/src/commands/_operator-surface.js +220 -0
- package/dist/src/commands/_operator-view.js +1124 -64
- package/dist/src/commands/_parsers.js +0 -2
- package/dist/src/commands/_pi-frontend.js +1080 -0
- package/dist/src/commands/_pi-install.js +4 -3
- package/dist/src/commands/_pi-remote-session.js +771 -0
- package/dist/src/commands/_pi-worker-bridge-extension.js +834 -0
- package/dist/src/commands/_policy.js +0 -2
- package/dist/src/commands/_preflight.js +98 -116
- package/dist/src/commands/_run-driver-helpers.js +46 -19
- package/dist/src/commands/_run-replay.js +142 -0
- package/dist/src/commands/_server-client.js +225 -48
- package/dist/src/commands/_snapshot-upload.js +74 -30
- package/dist/src/commands/_spinner.js +63 -0
- package/dist/src/commands/_task-picker.js +44 -16
- package/dist/src/commands/agent.js +10 -12
- package/dist/src/commands/browser.js +4 -6
- package/dist/src/commands/connect.js +134 -26
- package/dist/src/commands/dist.js +4 -6
- package/dist/src/commands/doctor.js +79 -34
- package/dist/src/commands/github.js +76 -32
- package/dist/src/commands/inbox.js +410 -31
- package/dist/src/commands/init.js +398 -90
- package/dist/src/commands/inspect.js +296 -23
- package/dist/src/commands/inspector.js +2 -4
- package/dist/src/commands/pi.js +168 -0
- package/dist/src/commands/plugin.js +81 -22
- package/dist/src/commands/profile-and-review.js +8 -10
- package/dist/src/commands/queue.js +2 -3
- package/dist/src/commands/remote.js +18 -20
- package/dist/src/commands/repo-git-harness.js +6 -8
- package/dist/src/commands/run.js +1600 -131
- package/dist/src/commands/server.js +281 -42
- package/dist/src/commands/setup.js +84 -45
- package/dist/src/commands/task-report-bug.js +5 -7
- package/dist/src/commands/task-run-driver.js +736 -93
- package/dist/src/commands/task.js +1863 -262
- package/dist/src/commands/test.js +3 -5
- package/dist/src/commands/workspace.js +4 -6
- package/dist/src/commands.js +5675 -2531
- package/dist/src/index.js +5654 -2519
- package/dist/src/launcher.js +5 -3
- package/dist/src/report-bug.js +3 -3
- package/dist/src/runner.js +5 -19
- package/package.json +7 -5
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/commands/_task-picker.ts
|
|
3
|
-
import {
|
|
3
|
+
import { cancel, isCancel, select } from "@clack/prompts";
|
|
4
|
+
|
|
5
|
+
// packages/cli/src/commands/_operator-surface.ts
|
|
6
|
+
import { createInterface as createPromptInterface } from "readline/promises";
|
|
4
7
|
function taskId(task) {
|
|
5
8
|
return typeof task.id === "string" && task.id.trim() ? task.id : "<unknown>";
|
|
6
9
|
}
|
|
@@ -13,6 +16,19 @@ function taskStatus(task) {
|
|
|
13
16
|
function renderTaskPickerRows(tasks) {
|
|
14
17
|
return tasks.map((task, index) => `${index + 1}. ${taskId(task)} \xB7 ${taskStatus(task)} \xB7 ${taskTitle(task)}`);
|
|
15
18
|
}
|
|
19
|
+
async function promptForTaskSelection(question) {
|
|
20
|
+
const rl = createPromptInterface({ input: process.stdin, output: process.stdout });
|
|
21
|
+
try {
|
|
22
|
+
return await rl.question(question);
|
|
23
|
+
} finally {
|
|
24
|
+
rl.close();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// packages/cli/src/commands/_task-picker.ts
|
|
29
|
+
function taskId2(task) {
|
|
30
|
+
return typeof task.id === "string" && task.id.trim() ? task.id : "<unknown>";
|
|
31
|
+
}
|
|
16
32
|
async function selectTaskWithTextPicker(tasks, io = {}) {
|
|
17
33
|
if (tasks.length === 0)
|
|
18
34
|
return null;
|
|
@@ -22,25 +38,37 @@ async function selectTaskWithTextPicker(tasks, io = {}) {
|
|
|
22
38
|
if (!isTty) {
|
|
23
39
|
throw new Error("task run requires an interactive terminal to pick a task; pass --task <id>, --next, or --detach with a task id.");
|
|
24
40
|
}
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
if (io.prompt || io.renderer) {
|
|
42
|
+
const prompt = io.prompt ?? promptForTaskSelection;
|
|
43
|
+
const renderer = io.renderer ?? { writeLine: (line) => process.stdout.write(`${line}
|
|
44
|
+
`) };
|
|
45
|
+
renderer.writeLine("Select Rig task:");
|
|
46
|
+
for (const row of renderTaskPickerRows(tasks))
|
|
47
|
+
renderer.writeLine(` ${row}`);
|
|
48
|
+
const answer2 = (await prompt(`Task [1-${tasks.length}] or id: `)).trim();
|
|
49
|
+
if (!answer2)
|
|
50
|
+
return null;
|
|
51
|
+
if (/^\d+$/.test(answer2)) {
|
|
52
|
+
const index2 = Number.parseInt(answer2, 10) - 1;
|
|
53
|
+
return tasks[index2] ?? null;
|
|
31
54
|
}
|
|
55
|
+
return tasks.find((task) => taskId2(task) === answer2) ?? null;
|
|
56
|
+
}
|
|
57
|
+
const options = tasks.map((task, index2) => ({
|
|
58
|
+
value: `${index2}`,
|
|
59
|
+
label: `${taskId2(task)} \xB7 ${typeof task.title === "string" && task.title.trim() ? task.title.trim() : "Untitled task"}`,
|
|
60
|
+
hint: typeof task.status === "string" && task.status.trim() ? task.status.trim() : undefined
|
|
61
|
+
}));
|
|
62
|
+
const answer = await select({
|
|
63
|
+
message: "Select Rig task",
|
|
64
|
+
options
|
|
32
65
|
});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
console.log(` ${row}`);
|
|
36
|
-
const answer = (await prompt(`Task [1-${tasks.length}] or id: `)).trim();
|
|
37
|
-
if (!answer)
|
|
66
|
+
if (isCancel(answer)) {
|
|
67
|
+
cancel("No task selected.");
|
|
38
68
|
return null;
|
|
39
|
-
if (/^\d+$/.test(answer)) {
|
|
40
|
-
const index = Number.parseInt(answer, 10) - 1;
|
|
41
|
-
return tasks[index] ?? null;
|
|
42
69
|
}
|
|
43
|
-
|
|
70
|
+
const index = Number.parseInt(String(answer), 10);
|
|
71
|
+
return Number.isFinite(index) ? tasks[index] ?? null : null;
|
|
44
72
|
}
|
|
45
73
|
export {
|
|
46
74
|
selectTaskWithTextPicker,
|
|
@@ -6,8 +6,6 @@ import { resolve as resolve2 } from "path";
|
|
|
6
6
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
7
7
|
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
8
8
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
9
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
10
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
11
9
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
12
10
|
import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
|
|
13
11
|
function takeFlag(args, flag) {
|
|
@@ -64,8 +62,7 @@ import { resolve } from "path";
|
|
|
64
62
|
import {
|
|
65
63
|
readAuthorityRun,
|
|
66
64
|
readJsonlFile,
|
|
67
|
-
|
|
68
|
-
writeJsonFile
|
|
65
|
+
writeAuthorityRunRecord
|
|
69
66
|
} from "@rig/runtime/control-plane/authority-files";
|
|
70
67
|
|
|
71
68
|
// packages/cli/src/commands/_paths.ts
|
|
@@ -159,7 +156,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
159
156
|
} else if ("errorText" in next) {
|
|
160
157
|
delete next.errorText;
|
|
161
158
|
}
|
|
162
|
-
|
|
159
|
+
writeAuthorityRunRecord(projectRoot, input.runId, next);
|
|
163
160
|
return next;
|
|
164
161
|
}
|
|
165
162
|
|
|
@@ -221,6 +218,7 @@ import { ensureProjectMainFreshBeforeRun } from "@rig/runtime/control-plane/proj
|
|
|
221
218
|
|
|
222
219
|
// packages/cli/src/commands/_server-client.ts
|
|
223
220
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
221
|
+
var scopedGitHubBearerTokens = new Map;
|
|
224
222
|
|
|
225
223
|
// packages/cli/src/commands/_preflight.ts
|
|
226
224
|
async function runProjectMainSyncPreflight(context, options) {
|
|
@@ -289,7 +287,7 @@ async function executeAgent(context, args) {
|
|
|
289
287
|
const [command = "list", ...rest] = args;
|
|
290
288
|
switch (command) {
|
|
291
289
|
case "list": {
|
|
292
|
-
requireNoExtraArgs(rest, "
|
|
290
|
+
requireNoExtraArgs(rest, "rig agent list");
|
|
293
291
|
const runtimes = await listAgentRuntimes(context.projectRoot);
|
|
294
292
|
if (context.outputMode === "text") {
|
|
295
293
|
if (runtimes.length === 0) {
|
|
@@ -310,12 +308,12 @@ async function executeAgent(context, args) {
|
|
|
310
308
|
pending = modeResult.rest;
|
|
311
309
|
const taskResult = takeOption(pending, "--task");
|
|
312
310
|
pending = taskResult.rest;
|
|
313
|
-
requireNoExtraArgs(pending, "
|
|
311
|
+
requireNoExtraArgs(pending, "rig agent prepare --task <id> [--id <id>] [--mode worktree]");
|
|
314
312
|
const mode = parseIsolationMode(modeResult.value, false);
|
|
315
313
|
const id = idResult.value || agentId("agent");
|
|
316
314
|
const taskId = taskResult.value?.trim();
|
|
317
315
|
if (!taskId) {
|
|
318
|
-
throw new CliError2("Usage:
|
|
316
|
+
throw new CliError2("Usage: rig agent prepare --task <id> [--id <id>] [--mode worktree]");
|
|
319
317
|
}
|
|
320
318
|
const runtime = await withMutedConsole(context.outputMode === "json", () => ensureAgentRuntime({
|
|
321
319
|
projectRoot: context.projectRoot,
|
|
@@ -333,7 +331,7 @@ async function executeAgent(context, args) {
|
|
|
333
331
|
case "run": {
|
|
334
332
|
const { options, commandParts } = splitAtDoubleDash(rest);
|
|
335
333
|
if (commandParts.length === 0) {
|
|
336
|
-
throw new CliError2("Usage:
|
|
334
|
+
throw new CliError2("Usage: rig agent run [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
|
|
337
335
|
}
|
|
338
336
|
let pending = options;
|
|
339
337
|
const idResult = takeOption(pending, "--id");
|
|
@@ -344,12 +342,12 @@ async function executeAgent(context, args) {
|
|
|
344
342
|
pending = taskResult.rest;
|
|
345
343
|
const skipProjectSyncResult = takeFlag(pending, "--skip-project-sync");
|
|
346
344
|
pending = skipProjectSyncResult.rest;
|
|
347
|
-
requireNoExtraArgs(pending, "
|
|
345
|
+
requireNoExtraArgs(pending, "rig agent run --task <id> [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
|
|
348
346
|
const mode = parseIsolationMode(modeResult.value, false);
|
|
349
347
|
const id = idResult.value || agentId("agent-run");
|
|
350
348
|
const taskId = taskResult.value?.trim();
|
|
351
349
|
if (!taskId) {
|
|
352
|
-
throw new CliError2("Usage:
|
|
350
|
+
throw new CliError2("Usage: rig agent run --task <id> [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
|
|
353
351
|
}
|
|
354
352
|
await runProjectMainSyncPreflight(context, { disabled: skipProjectSyncResult.value });
|
|
355
353
|
const createdAt = new Date().toISOString();
|
|
@@ -463,7 +461,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
463
461
|
pending = allResult.rest;
|
|
464
462
|
const idResult = takeOption(pending, "--id");
|
|
465
463
|
pending = idResult.rest;
|
|
466
|
-
requireNoExtraArgs(pending, "
|
|
464
|
+
requireNoExtraArgs(pending, "rig agent cleanup (--id <id> | --all)");
|
|
467
465
|
if (!allResult.value && !idResult.value) {
|
|
468
466
|
throw new CliError2("Provide --id <id> or --all.");
|
|
469
467
|
}
|
|
@@ -12,8 +12,6 @@ import pc2 from "picocolors";
|
|
|
12
12
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
13
13
|
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
14
14
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
15
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
16
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
17
15
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
18
16
|
import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
|
|
19
17
|
function takeFlag(args, flag) {
|
|
@@ -351,7 +349,7 @@ async function executeBrowser(context, args) {
|
|
|
351
349
|
return { ok: true, group: "browser", command: "help" };
|
|
352
350
|
}
|
|
353
351
|
if (command === "explain") {
|
|
354
|
-
requireNoExtraArgs(rest, "
|
|
352
|
+
requireNoExtraArgs(rest, "rig browser explain");
|
|
355
353
|
console.log(browserAgentUsageText());
|
|
356
354
|
return { ok: true, group: "browser", command: "explain" };
|
|
357
355
|
}
|
|
@@ -377,7 +375,7 @@ ${browserHelpText()}`);
|
|
|
377
375
|
|
|
378
376
|
${browserHelpText()}`);
|
|
379
377
|
}
|
|
380
|
-
requireNoExtraArgs(appRest, `
|
|
378
|
+
requireNoExtraArgs(appRest, `rig browser ${command} ${subcommand}`);
|
|
381
379
|
await context.runCommand(["bun", "run", `app:${subcommand}:browser:${appSlug}`]);
|
|
382
380
|
return { ok: true, group: "browser", command: `${command}-${subcommand}` };
|
|
383
381
|
}
|
|
@@ -389,7 +387,7 @@ ${browserHelpText()}`);
|
|
|
389
387
|
};
|
|
390
388
|
const packageScript = packageScripts[command];
|
|
391
389
|
if (packageScript) {
|
|
392
|
-
requireNoExtraArgs(rest, `
|
|
390
|
+
requireNoExtraArgs(rest, `rig browser ${command}`);
|
|
393
391
|
await context.runCommand(["bun", "run", "--filter=@rig/browser", packageScript]);
|
|
394
392
|
return { ok: true, group: "browser", command };
|
|
395
393
|
}
|
|
@@ -416,7 +414,7 @@ async function executeBrowserDemo(context, args) {
|
|
|
416
414
|
pending = keepOpenFlag.rest;
|
|
417
415
|
const noBuildFlag = takeFlag(pending, "--no-build");
|
|
418
416
|
pending = noBuildFlag.rest;
|
|
419
|
-
requireNoExtraArgs(pending, "
|
|
417
|
+
requireNoExtraArgs(pending, "rig browser demo [--port <n>] [--profile <name>] [--state-dir <path>] [--target-url <url>] [--keep-open] [--no-build]");
|
|
420
418
|
if (context.outputMode !== "text" || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
421
419
|
throw new CliError2("rig browser demo requires an interactive TTY in text mode.");
|
|
422
420
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// packages/cli/src/commands/connect.ts
|
|
3
|
+
import { cancel, isCancel, select } from "@clack/prompts";
|
|
4
|
+
|
|
2
5
|
// packages/cli/src/runner.ts
|
|
3
6
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
4
7
|
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
5
8
|
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
9
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
9
10
|
import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
|
|
10
11
|
function requireNoExtraArgs(args, usage) {
|
|
@@ -96,19 +97,90 @@ function readRepoConnection(projectRoot) {
|
|
|
96
97
|
return {
|
|
97
98
|
selected,
|
|
98
99
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
99
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
100
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
101
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
100
102
|
};
|
|
101
103
|
}
|
|
102
104
|
function writeRepoConnection(projectRoot, state) {
|
|
103
105
|
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
104
106
|
}
|
|
105
107
|
|
|
108
|
+
// packages/cli/src/commands/_cli-format.ts
|
|
109
|
+
import { log, note } from "@clack/prompts";
|
|
110
|
+
import pc from "picocolors";
|
|
111
|
+
function truncate(value, width) {
|
|
112
|
+
if (value.length <= width)
|
|
113
|
+
return value;
|
|
114
|
+
if (width <= 1)
|
|
115
|
+
return "\u2026";
|
|
116
|
+
return `${value.slice(0, width - 1)}\u2026`;
|
|
117
|
+
}
|
|
118
|
+
function pad(value, width) {
|
|
119
|
+
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
120
|
+
}
|
|
121
|
+
function statusColor(status) {
|
|
122
|
+
const normalized = status.toLowerCase();
|
|
123
|
+
if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved"].includes(normalized))
|
|
124
|
+
return pc.green;
|
|
125
|
+
if (["failed", "needs_attention", "needs-attention", "blocked", "error", "rejected"].includes(normalized))
|
|
126
|
+
return pc.red;
|
|
127
|
+
if (["running", "reviewing", "validating", "in_progress", "in-progress", "remote"].includes(normalized))
|
|
128
|
+
return pc.cyan;
|
|
129
|
+
if (["ready", "open", "queued", "created", "preparing", "local", "pending"].includes(normalized))
|
|
130
|
+
return pc.yellow;
|
|
131
|
+
return pc.dim;
|
|
132
|
+
}
|
|
133
|
+
function formatStatusPill(status) {
|
|
134
|
+
const label = status || "unknown";
|
|
135
|
+
return statusColor(label)(`\u25CF ${label}`);
|
|
136
|
+
}
|
|
137
|
+
function formatSection(title, subtitle) {
|
|
138
|
+
return `${pc.bold(pc.cyan("\u25C6"))} ${pc.bold(title)}${subtitle ? pc.dim(` \u2014 ${subtitle}`) : ""}`;
|
|
139
|
+
}
|
|
140
|
+
function formatSuccessCard(title, rows = []) {
|
|
141
|
+
const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${pc.dim("\u2502")} ${pc.dim(key.padEnd(12))} ${value}`);
|
|
142
|
+
return [formatSection(title), ...body].join(`
|
|
143
|
+
`);
|
|
144
|
+
}
|
|
145
|
+
function formatNextSteps(steps) {
|
|
146
|
+
if (steps.length === 0)
|
|
147
|
+
return [];
|
|
148
|
+
return [pc.bold("Next"), ...steps.map((step) => `${pc.dim("\u203A")} ${step}`)];
|
|
149
|
+
}
|
|
150
|
+
function formatConnectionList(connections) {
|
|
151
|
+
const rows = [["local", { kind: "local", mode: "auto" }], ...Object.entries(connections)];
|
|
152
|
+
const aliasWidth = Math.min(24, Math.max(5, ...rows.map(([alias]) => alias.length)));
|
|
153
|
+
const lines = rows.map(([alias, connection]) => [
|
|
154
|
+
pc.bold(pad(truncate(alias, aliasWidth), aliasWidth)),
|
|
155
|
+
formatStatusPill(connection.kind),
|
|
156
|
+
connection.kind === "remote" ? connection.baseUrl ?? "" : connection.mode ?? "local"
|
|
157
|
+
].join(" "));
|
|
158
|
+
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(`
|
|
159
|
+
`);
|
|
160
|
+
}
|
|
161
|
+
function formatConnectionStatus(selected, connections) {
|
|
162
|
+
const connection = selected === "local" ? { kind: "local", mode: "auto" } : connections[selected];
|
|
163
|
+
const target = !connection ? "not configured" : connection.kind === "remote" ? connection.baseUrl : "local";
|
|
164
|
+
return [
|
|
165
|
+
formatSection("Rig server", "selected for this repo"),
|
|
166
|
+
`${pc.dim("\u2502")} ${pc.dim("selected ")} ${pc.bold(selected)}`,
|
|
167
|
+
`${pc.dim("\u2502")} ${pc.dim("kind ")} ${formatStatusPill(connection?.kind ?? "unknown")}`,
|
|
168
|
+
`${pc.dim("\u2502")} ${pc.dim("target ")} ${target ?? "not configured"}`,
|
|
169
|
+
"",
|
|
170
|
+
...formatNextSteps(["Change: `rig server use <alias|local>`", "List saved servers: `rig server list`"])
|
|
171
|
+
].join(`
|
|
172
|
+
`);
|
|
173
|
+
}
|
|
174
|
+
|
|
106
175
|
// packages/cli/src/commands/connect.ts
|
|
107
|
-
function
|
|
176
|
+
function usageName(options) {
|
|
177
|
+
return `rig ${options.group}`;
|
|
178
|
+
}
|
|
179
|
+
function parseConnection(alias, value, options) {
|
|
108
180
|
if (alias === "local" && !value)
|
|
109
181
|
return { kind: "local", mode: "auto" };
|
|
110
182
|
if (!value)
|
|
111
|
-
throw new CliError2(
|
|
183
|
+
throw new CliError2(`Missing remote server URL. Usage: ${usageName(options)} add <alias> <url>`, 1);
|
|
112
184
|
let parsed;
|
|
113
185
|
try {
|
|
114
186
|
parsed = new URL(value);
|
|
@@ -127,54 +199,90 @@ function printJsonOrText(context, payload, text) {
|
|
|
127
199
|
console.log(text);
|
|
128
200
|
}
|
|
129
201
|
}
|
|
130
|
-
async function
|
|
202
|
+
async function promptForConnectionAlias(context) {
|
|
203
|
+
const state = readGlobalConnections();
|
|
204
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
205
|
+
const options = [
|
|
206
|
+
{ value: "local", label: "local", hint: "Use/start a local Rig server" },
|
|
207
|
+
...Object.entries(state.connections).map(([alias, connection]) => ({
|
|
208
|
+
value: alias,
|
|
209
|
+
label: alias,
|
|
210
|
+
hint: connection.kind === "remote" ? connection.baseUrl : "local"
|
|
211
|
+
}))
|
|
212
|
+
].filter((option, index, all) => all.findIndex((candidate) => candidate.value === option.value) === index);
|
|
213
|
+
const answer = await select({
|
|
214
|
+
message: "Select Rig server for this repo",
|
|
215
|
+
initialValue: repo?.selected ?? "local",
|
|
216
|
+
options
|
|
217
|
+
});
|
|
218
|
+
if (isCancel(answer)) {
|
|
219
|
+
cancel("No server selected.");
|
|
220
|
+
throw new CliError2("No server selected.", 3);
|
|
221
|
+
}
|
|
222
|
+
return String(answer);
|
|
223
|
+
}
|
|
224
|
+
async function executeConnectionCommand(context, args, options) {
|
|
131
225
|
const [command, ...rest] = args;
|
|
132
226
|
switch (command ?? "status") {
|
|
133
227
|
case "list": {
|
|
134
|
-
requireNoExtraArgs(rest,
|
|
228
|
+
requireNoExtraArgs(rest, `${usageName(options)} list`);
|
|
135
229
|
const state = readGlobalConnections();
|
|
136
|
-
printJsonOrText(context, state,
|
|
137
|
-
|
|
138
|
-
return { ok: true, group: "connect", command: "list", details: state };
|
|
230
|
+
printJsonOrText(context, state, formatConnectionList(state.connections));
|
|
231
|
+
return { ok: true, group: options.group, command: "list", details: state };
|
|
139
232
|
}
|
|
140
233
|
case "add": {
|
|
141
234
|
const [alias, url, ...extra] = rest;
|
|
142
235
|
if (!alias)
|
|
143
|
-
throw new CliError2(
|
|
144
|
-
requireNoExtraArgs(extra,
|
|
145
|
-
const connection = parseConnection(alias, url);
|
|
236
|
+
throw new CliError2(`Missing alias. Usage: ${usageName(options)} add <alias> <url>`, 1);
|
|
237
|
+
requireNoExtraArgs(extra, `${usageName(options)} add <alias> <url>`);
|
|
238
|
+
const connection = parseConnection(alias, url, options);
|
|
146
239
|
const state = upsertGlobalConnection(alias, connection);
|
|
147
|
-
printJsonOrText(context, { alias, connection },
|
|
148
|
-
|
|
240
|
+
printJsonOrText(context, { alias, connection }, formatSuccessCard("Rig server saved", [
|
|
241
|
+
["alias", alias],
|
|
242
|
+
["target", connection.kind === "remote" ? connection.baseUrl : "local"],
|
|
243
|
+
["next", `${usageName(options)} use ${alias}`]
|
|
244
|
+
]));
|
|
245
|
+
return { ok: true, group: options.group, command: "add", details: { alias, connection, count: Object.keys(state.connections).length } };
|
|
149
246
|
}
|
|
150
247
|
case "use": {
|
|
151
|
-
|
|
248
|
+
let [alias, ...extra] = rest;
|
|
249
|
+
requireNoExtraArgs(extra, `${usageName(options)} use <alias|local>`);
|
|
250
|
+
if (!alias && options.interactiveUse && context.outputMode === "text" && process.stdin.isTTY) {
|
|
251
|
+
alias = await promptForConnectionAlias(context);
|
|
252
|
+
}
|
|
152
253
|
if (!alias)
|
|
153
|
-
throw new CliError2(
|
|
154
|
-
requireNoExtraArgs(extra, "rig connect use <alias|local>");
|
|
254
|
+
throw new CliError2(`Missing alias. Usage: ${usageName(options)} use <alias|local>`, 1);
|
|
155
255
|
if (alias !== "local") {
|
|
156
256
|
const state = readGlobalConnections();
|
|
157
257
|
if (!state.connections[alias])
|
|
158
|
-
throw new CliError2(`Unknown Rig
|
|
258
|
+
throw new CliError2(`Unknown Rig server: ${alias}`, 1);
|
|
159
259
|
}
|
|
160
260
|
const repoState = { selected: alias, linkedAt: new Date().toISOString() };
|
|
161
261
|
writeRepoConnection(context.projectRoot, repoState);
|
|
162
|
-
printJsonOrText(context, repoState,
|
|
163
|
-
|
|
262
|
+
printJsonOrText(context, repoState, formatSuccessCard("Rig server selected", [
|
|
263
|
+
["selected", alias],
|
|
264
|
+
["scope", "this repo"],
|
|
265
|
+
["next", "rig task list"]
|
|
266
|
+
]));
|
|
267
|
+
return { ok: true, group: options.group, command: "use", details: repoState };
|
|
164
268
|
}
|
|
165
269
|
case "status": {
|
|
166
|
-
requireNoExtraArgs(rest,
|
|
270
|
+
requireNoExtraArgs(rest, `${usageName(options)} status`);
|
|
167
271
|
const repo = readRepoConnection(context.projectRoot);
|
|
168
272
|
const global = readGlobalConnections();
|
|
169
273
|
const details = { selected: repo?.selected ?? "local", repo, connections: global.connections };
|
|
170
|
-
printJsonOrText(context, details,
|
|
171
|
-
return { ok: true, group:
|
|
274
|
+
printJsonOrText(context, details, formatConnectionStatus(details.selected, global.connections));
|
|
275
|
+
return { ok: true, group: options.group, command: "status", details };
|
|
172
276
|
}
|
|
173
277
|
default:
|
|
174
|
-
throw new CliError2(`Unknown
|
|
175
|
-
Usage:
|
|
278
|
+
throw new CliError2(`Unknown ${options.group} command: ${String(command)}
|
|
279
|
+
Usage: ${usageName(options)} <list|add|use|status>`, 1);
|
|
176
280
|
}
|
|
177
281
|
}
|
|
282
|
+
async function executeConnect(context, args) {
|
|
283
|
+
return executeConnectionCommand(context, args, { group: "connect" });
|
|
284
|
+
}
|
|
178
285
|
export {
|
|
286
|
+
executeConnectionCommand,
|
|
179
287
|
executeConnect
|
|
180
288
|
};
|
|
@@ -19,8 +19,6 @@ import { resolve as resolve3 } from "path";
|
|
|
19
19
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
20
20
|
import { CliError } 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
|
import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
|
|
26
24
|
function takeOption(args, option) {
|
|
@@ -185,7 +183,7 @@ async function executeDist(context, args) {
|
|
|
185
183
|
switch (command) {
|
|
186
184
|
case "build": {
|
|
187
185
|
const { value: outputDir, rest: pending } = takeOption(rest, "--output-dir");
|
|
188
|
-
requireNoExtraArgs(pending, "
|
|
186
|
+
requireNoExtraArgs(pending, "rig dist build [--output-dir <dir>]");
|
|
189
187
|
const commandParts = ["bun", "run", "packages/cli/bin/build-rig-binaries.ts"];
|
|
190
188
|
if (outputDir) {
|
|
191
189
|
commandParts.push("--output-dir", outputDir);
|
|
@@ -199,7 +197,7 @@ async function executeDist(context, args) {
|
|
|
199
197
|
pending = scopeResult.rest;
|
|
200
198
|
const pathResult = takeOption(pending, "--path");
|
|
201
199
|
pending = pathResult.rest;
|
|
202
|
-
requireNoExtraArgs(pending, "
|
|
200
|
+
requireNoExtraArgs(pending, "rig dist install [--scope user|system] [--path <dir>]");
|
|
203
201
|
const scope = parseInstallScope(scopeResult.value);
|
|
204
202
|
const installDir = resolveInstallDir(scope, pathResult.value);
|
|
205
203
|
mkdirSync(installDir, { recursive: true });
|
|
@@ -242,7 +240,7 @@ async function executeDist(context, args) {
|
|
|
242
240
|
};
|
|
243
241
|
}
|
|
244
242
|
case "doctor": {
|
|
245
|
-
requireNoExtraArgs(rest, "
|
|
243
|
+
requireNoExtraArgs(rest, "rig dist doctor");
|
|
246
244
|
const details = await runDistDoctor(context.projectRoot);
|
|
247
245
|
if (context.outputMode === "text") {
|
|
248
246
|
console.log(`bun: ${details.bun.available ? `ok (${details.bun.version})` : "missing"}`);
|
|
@@ -253,7 +251,7 @@ async function executeDist(context, args) {
|
|
|
253
251
|
return { ok: true, group: "dist", command, details };
|
|
254
252
|
}
|
|
255
253
|
case "rebuild-agent": {
|
|
256
|
-
requireNoExtraArgs(rest, "
|
|
254
|
+
requireNoExtraArgs(rest, "rig dist rebuild-agent");
|
|
257
255
|
const fp = await computeRuntimeImageFingerprint(context.projectRoot);
|
|
258
256
|
const currentId = computeRuntimeImageId(fp);
|
|
259
257
|
const imagesDir = resolve3(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
|