@h-rig/cli 0.0.6-alpha.3 → 0.0.6-alpha.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/rig.js +3606 -1172
- package/dist/src/commands/_authority-runs.js +1 -0
- package/dist/src/commands/_cli-format.js +369 -0
- package/dist/src/commands/_connection-state.js +1 -3
- package/dist/src/commands/_doctor-checks.js +13 -27
- package/dist/src/commands/_help-catalog.js +388 -0
- package/dist/src/commands/_operator-surface.js +204 -0
- package/dist/src/commands/_operator-view.js +861 -56
- package/dist/src/commands/_parsers.js +0 -2
- package/dist/src/commands/_pi-frontend.js +841 -0
- package/dist/src/commands/_pi-install.js +4 -3
- package/dist/src/commands/_pi-worker-bridge-extension.js +759 -0
- package/dist/src/commands/_policy.js +0 -2
- package/dist/src/commands/_preflight.js +32 -109
- package/dist/src/commands/_run-driver-helpers.js +0 -2
- package/dist/src/commands/_server-client.js +161 -31
- package/dist/src/commands/_snapshot-upload.js +8 -23
- package/dist/src/commands/_task-picker.js +44 -16
- package/dist/src/commands/agent.js +9 -9
- package/dist/src/commands/browser.js +4 -6
- package/dist/src/commands/connect.js +132 -25
- package/dist/src/commands/dist.js +4 -6
- package/dist/src/commands/doctor.js +13 -27
- package/dist/src/commands/github.js +10 -25
- package/dist/src/commands/inbox.js +351 -31
- package/dist/src/commands/init.js +298 -71
- package/dist/src/commands/inspect.js +10 -12
- package/dist/src/commands/inspector.js +2 -4
- package/dist/src/commands/plugin.js +76 -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 +1157 -122
- package/dist/src/commands/server.js +217 -33
- package/dist/src/commands/setup.js +17 -37
- package/dist/src/commands/task-report-bug.js +5 -7
- package/dist/src/commands/task-run-driver.js +660 -73
- package/dist/src/commands/task.js +1542 -252
- package/dist/src/commands/test.js +3 -5
- package/dist/src/commands/workspace.js +4 -6
- package/dist/src/commands.js +3599 -1159
- package/dist/src/index.js +3646 -1215
- 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 +6 -4
|
@@ -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) {
|
|
@@ -126,6 +124,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
126
124
|
const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
|
|
127
125
|
const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
|
|
128
126
|
const next = {
|
|
127
|
+
...existing ?? {},
|
|
129
128
|
runId: input.runId,
|
|
130
129
|
projectRoot,
|
|
131
130
|
workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
|
|
@@ -220,6 +219,7 @@ import { ensureProjectMainFreshBeforeRun } from "@rig/runtime/control-plane/proj
|
|
|
220
219
|
|
|
221
220
|
// packages/cli/src/commands/_server-client.ts
|
|
222
221
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
222
|
+
var scopedGitHubBearerTokens = new Map;
|
|
223
223
|
|
|
224
224
|
// packages/cli/src/commands/_preflight.ts
|
|
225
225
|
async function runProjectMainSyncPreflight(context, options) {
|
|
@@ -288,7 +288,7 @@ async function executeAgent(context, args) {
|
|
|
288
288
|
const [command = "list", ...rest] = args;
|
|
289
289
|
switch (command) {
|
|
290
290
|
case "list": {
|
|
291
|
-
requireNoExtraArgs(rest, "
|
|
291
|
+
requireNoExtraArgs(rest, "rig agent list");
|
|
292
292
|
const runtimes = await listAgentRuntimes(context.projectRoot);
|
|
293
293
|
if (context.outputMode === "text") {
|
|
294
294
|
if (runtimes.length === 0) {
|
|
@@ -309,12 +309,12 @@ async function executeAgent(context, args) {
|
|
|
309
309
|
pending = modeResult.rest;
|
|
310
310
|
const taskResult = takeOption(pending, "--task");
|
|
311
311
|
pending = taskResult.rest;
|
|
312
|
-
requireNoExtraArgs(pending, "
|
|
312
|
+
requireNoExtraArgs(pending, "rig agent prepare --task <id> [--id <id>] [--mode worktree]");
|
|
313
313
|
const mode = parseIsolationMode(modeResult.value, false);
|
|
314
314
|
const id = idResult.value || agentId("agent");
|
|
315
315
|
const taskId = taskResult.value?.trim();
|
|
316
316
|
if (!taskId) {
|
|
317
|
-
throw new CliError2("Usage:
|
|
317
|
+
throw new CliError2("Usage: rig agent prepare --task <id> [--id <id>] [--mode worktree]");
|
|
318
318
|
}
|
|
319
319
|
const runtime = await withMutedConsole(context.outputMode === "json", () => ensureAgentRuntime({
|
|
320
320
|
projectRoot: context.projectRoot,
|
|
@@ -332,7 +332,7 @@ async function executeAgent(context, args) {
|
|
|
332
332
|
case "run": {
|
|
333
333
|
const { options, commandParts } = splitAtDoubleDash(rest);
|
|
334
334
|
if (commandParts.length === 0) {
|
|
335
|
-
throw new CliError2("Usage:
|
|
335
|
+
throw new CliError2("Usage: rig agent run [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
|
|
336
336
|
}
|
|
337
337
|
let pending = options;
|
|
338
338
|
const idResult = takeOption(pending, "--id");
|
|
@@ -343,12 +343,12 @@ async function executeAgent(context, args) {
|
|
|
343
343
|
pending = taskResult.rest;
|
|
344
344
|
const skipProjectSyncResult = takeFlag(pending, "--skip-project-sync");
|
|
345
345
|
pending = skipProjectSyncResult.rest;
|
|
346
|
-
requireNoExtraArgs(pending, "
|
|
346
|
+
requireNoExtraArgs(pending, "rig agent run --task <id> [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
|
|
347
347
|
const mode = parseIsolationMode(modeResult.value, false);
|
|
348
348
|
const id = idResult.value || agentId("agent-run");
|
|
349
349
|
const taskId = taskResult.value?.trim();
|
|
350
350
|
if (!taskId) {
|
|
351
|
-
throw new CliError2("Usage:
|
|
351
|
+
throw new CliError2("Usage: rig agent run --task <id> [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
|
|
352
352
|
}
|
|
353
353
|
await runProjectMainSyncPreflight(context, { disabled: skipProjectSyncResult.value });
|
|
354
354
|
const createdAt = new Date().toISOString();
|
|
@@ -462,7 +462,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
462
462
|
pending = allResult.rest;
|
|
463
463
|
const idResult = takeOption(pending, "--id");
|
|
464
464
|
pending = idResult.rest;
|
|
465
|
-
requireNoExtraArgs(pending, "
|
|
465
|
+
requireNoExtraArgs(pending, "rig agent cleanup (--id <id> | --all)");
|
|
466
466
|
if (!allResult.value && !idResult.value) {
|
|
467
467
|
throw new CliError2("Provide --id <id> or --all.");
|
|
468
468
|
}
|
|
@@ -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) {
|
|
@@ -103,12 +104,82 @@ function writeRepoConnection(projectRoot, state) {
|
|
|
103
104
|
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
// packages/cli/src/commands/_cli-format.ts
|
|
108
|
+
import { log, note } from "@clack/prompts";
|
|
109
|
+
import pc from "picocolors";
|
|
110
|
+
function truncate(value, width) {
|
|
111
|
+
if (value.length <= width)
|
|
112
|
+
return value;
|
|
113
|
+
if (width <= 1)
|
|
114
|
+
return "\u2026";
|
|
115
|
+
return `${value.slice(0, width - 1)}\u2026`;
|
|
116
|
+
}
|
|
117
|
+
function pad(value, width) {
|
|
118
|
+
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
119
|
+
}
|
|
120
|
+
function statusColor(status) {
|
|
121
|
+
const normalized = status.toLowerCase();
|
|
122
|
+
if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved"].includes(normalized))
|
|
123
|
+
return pc.green;
|
|
124
|
+
if (["failed", "needs_attention", "needs-attention", "blocked", "error", "rejected"].includes(normalized))
|
|
125
|
+
return pc.red;
|
|
126
|
+
if (["running", "reviewing", "validating", "in_progress", "in-progress", "remote"].includes(normalized))
|
|
127
|
+
return pc.cyan;
|
|
128
|
+
if (["ready", "open", "queued", "created", "preparing", "local", "pending"].includes(normalized))
|
|
129
|
+
return pc.yellow;
|
|
130
|
+
return pc.dim;
|
|
131
|
+
}
|
|
132
|
+
function formatStatusPill(status) {
|
|
133
|
+
const label = status || "unknown";
|
|
134
|
+
return statusColor(label)(`\u25CF ${label}`);
|
|
135
|
+
}
|
|
136
|
+
function formatSection(title, subtitle) {
|
|
137
|
+
return `${pc.bold(pc.cyan("\u25C6"))} ${pc.bold(title)}${subtitle ? pc.dim(` \u2014 ${subtitle}`) : ""}`;
|
|
138
|
+
}
|
|
139
|
+
function formatSuccessCard(title, rows = []) {
|
|
140
|
+
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}`);
|
|
141
|
+
return [formatSection(title), ...body].join(`
|
|
142
|
+
`);
|
|
143
|
+
}
|
|
144
|
+
function formatNextSteps(steps) {
|
|
145
|
+
if (steps.length === 0)
|
|
146
|
+
return [];
|
|
147
|
+
return [pc.bold("Next"), ...steps.map((step) => `${pc.dim("\u203A")} ${step}`)];
|
|
148
|
+
}
|
|
149
|
+
function formatConnectionList(connections) {
|
|
150
|
+
const rows = [["local", { kind: "local", mode: "auto" }], ...Object.entries(connections)];
|
|
151
|
+
const aliasWidth = Math.min(24, Math.max(5, ...rows.map(([alias]) => alias.length)));
|
|
152
|
+
const lines = rows.map(([alias, connection]) => [
|
|
153
|
+
pc.bold(pad(truncate(alias, aliasWidth), aliasWidth)),
|
|
154
|
+
formatStatusPill(connection.kind),
|
|
155
|
+
connection.kind === "remote" ? connection.baseUrl ?? "" : connection.mode ?? "local"
|
|
156
|
+
].join(" "));
|
|
157
|
+
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(`
|
|
158
|
+
`);
|
|
159
|
+
}
|
|
160
|
+
function formatConnectionStatus(selected, connections) {
|
|
161
|
+
const connection = selected === "local" ? { kind: "local", mode: "auto" } : connections[selected];
|
|
162
|
+
const target = !connection ? "not configured" : connection.kind === "remote" ? connection.baseUrl : "local";
|
|
163
|
+
return [
|
|
164
|
+
formatSection("Rig server", "selected for this repo"),
|
|
165
|
+
`${pc.dim("\u2502")} ${pc.dim("selected ")} ${pc.bold(selected)}`,
|
|
166
|
+
`${pc.dim("\u2502")} ${pc.dim("kind ")} ${formatStatusPill(connection?.kind ?? "unknown")}`,
|
|
167
|
+
`${pc.dim("\u2502")} ${pc.dim("target ")} ${target ?? "not configured"}`,
|
|
168
|
+
"",
|
|
169
|
+
...formatNextSteps(["Change: `rig server use <alias|local>`", "List saved servers: `rig server list`"])
|
|
170
|
+
].join(`
|
|
171
|
+
`);
|
|
172
|
+
}
|
|
173
|
+
|
|
106
174
|
// packages/cli/src/commands/connect.ts
|
|
107
|
-
function
|
|
175
|
+
function usageName(options) {
|
|
176
|
+
return `rig ${options.group}`;
|
|
177
|
+
}
|
|
178
|
+
function parseConnection(alias, value, options) {
|
|
108
179
|
if (alias === "local" && !value)
|
|
109
180
|
return { kind: "local", mode: "auto" };
|
|
110
181
|
if (!value)
|
|
111
|
-
throw new CliError2(
|
|
182
|
+
throw new CliError2(`Missing remote server URL. Usage: ${usageName(options)} add <alias> <url>`, 1);
|
|
112
183
|
let parsed;
|
|
113
184
|
try {
|
|
114
185
|
parsed = new URL(value);
|
|
@@ -127,54 +198,90 @@ function printJsonOrText(context, payload, text) {
|
|
|
127
198
|
console.log(text);
|
|
128
199
|
}
|
|
129
200
|
}
|
|
130
|
-
async function
|
|
201
|
+
async function promptForConnectionAlias(context) {
|
|
202
|
+
const state = readGlobalConnections();
|
|
203
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
204
|
+
const options = [
|
|
205
|
+
{ value: "local", label: "local", hint: "Use/start a local Rig server" },
|
|
206
|
+
...Object.entries(state.connections).map(([alias, connection]) => ({
|
|
207
|
+
value: alias,
|
|
208
|
+
label: alias,
|
|
209
|
+
hint: connection.kind === "remote" ? connection.baseUrl : "local"
|
|
210
|
+
}))
|
|
211
|
+
].filter((option, index, all) => all.findIndex((candidate) => candidate.value === option.value) === index);
|
|
212
|
+
const answer = await select({
|
|
213
|
+
message: "Select Rig server for this repo",
|
|
214
|
+
initialValue: repo?.selected ?? "local",
|
|
215
|
+
options
|
|
216
|
+
});
|
|
217
|
+
if (isCancel(answer)) {
|
|
218
|
+
cancel("No server selected.");
|
|
219
|
+
throw new CliError2("No server selected.", 3);
|
|
220
|
+
}
|
|
221
|
+
return String(answer);
|
|
222
|
+
}
|
|
223
|
+
async function executeConnectionCommand(context, args, options) {
|
|
131
224
|
const [command, ...rest] = args;
|
|
132
225
|
switch (command ?? "status") {
|
|
133
226
|
case "list": {
|
|
134
|
-
requireNoExtraArgs(rest,
|
|
227
|
+
requireNoExtraArgs(rest, `${usageName(options)} list`);
|
|
135
228
|
const state = readGlobalConnections();
|
|
136
|
-
printJsonOrText(context, state,
|
|
137
|
-
|
|
138
|
-
return { ok: true, group: "connect", command: "list", details: state };
|
|
229
|
+
printJsonOrText(context, state, formatConnectionList(state.connections));
|
|
230
|
+
return { ok: true, group: options.group, command: "list", details: state };
|
|
139
231
|
}
|
|
140
232
|
case "add": {
|
|
141
233
|
const [alias, url, ...extra] = rest;
|
|
142
234
|
if (!alias)
|
|
143
|
-
throw new CliError2(
|
|
144
|
-
requireNoExtraArgs(extra,
|
|
145
|
-
const connection = parseConnection(alias, url);
|
|
235
|
+
throw new CliError2(`Missing alias. Usage: ${usageName(options)} add <alias> <url>`, 1);
|
|
236
|
+
requireNoExtraArgs(extra, `${usageName(options)} add <alias> <url>`);
|
|
237
|
+
const connection = parseConnection(alias, url, options);
|
|
146
238
|
const state = upsertGlobalConnection(alias, connection);
|
|
147
|
-
printJsonOrText(context, { alias, connection },
|
|
148
|
-
|
|
239
|
+
printJsonOrText(context, { alias, connection }, formatSuccessCard("Rig server saved", [
|
|
240
|
+
["alias", alias],
|
|
241
|
+
["target", connection.kind === "remote" ? connection.baseUrl : "local"],
|
|
242
|
+
["next", `${usageName(options)} use ${alias}`]
|
|
243
|
+
]));
|
|
244
|
+
return { ok: true, group: options.group, command: "add", details: { alias, connection, count: Object.keys(state.connections).length } };
|
|
149
245
|
}
|
|
150
246
|
case "use": {
|
|
151
|
-
|
|
247
|
+
let [alias, ...extra] = rest;
|
|
248
|
+
requireNoExtraArgs(extra, `${usageName(options)} use <alias|local>`);
|
|
249
|
+
if (!alias && options.interactiveUse && context.outputMode === "text" && process.stdin.isTTY) {
|
|
250
|
+
alias = await promptForConnectionAlias(context);
|
|
251
|
+
}
|
|
152
252
|
if (!alias)
|
|
153
|
-
throw new CliError2(
|
|
154
|
-
requireNoExtraArgs(extra, "rig connect use <alias|local>");
|
|
253
|
+
throw new CliError2(`Missing alias. Usage: ${usageName(options)} use <alias|local>`, 1);
|
|
155
254
|
if (alias !== "local") {
|
|
156
255
|
const state = readGlobalConnections();
|
|
157
256
|
if (!state.connections[alias])
|
|
158
|
-
throw new CliError2(`Unknown Rig
|
|
257
|
+
throw new CliError2(`Unknown Rig server: ${alias}`, 1);
|
|
159
258
|
}
|
|
160
259
|
const repoState = { selected: alias, linkedAt: new Date().toISOString() };
|
|
161
260
|
writeRepoConnection(context.projectRoot, repoState);
|
|
162
|
-
printJsonOrText(context, repoState,
|
|
163
|
-
|
|
261
|
+
printJsonOrText(context, repoState, formatSuccessCard("Rig server selected", [
|
|
262
|
+
["selected", alias],
|
|
263
|
+
["scope", "this repo"],
|
|
264
|
+
["next", "rig task list"]
|
|
265
|
+
]));
|
|
266
|
+
return { ok: true, group: options.group, command: "use", details: repoState };
|
|
164
267
|
}
|
|
165
268
|
case "status": {
|
|
166
|
-
requireNoExtraArgs(rest,
|
|
269
|
+
requireNoExtraArgs(rest, `${usageName(options)} status`);
|
|
167
270
|
const repo = readRepoConnection(context.projectRoot);
|
|
168
271
|
const global = readGlobalConnections();
|
|
169
272
|
const details = { selected: repo?.selected ?? "local", repo, connections: global.connections };
|
|
170
|
-
printJsonOrText(context, details,
|
|
171
|
-
return { ok: true, group:
|
|
273
|
+
printJsonOrText(context, details, formatConnectionStatus(details.selected, global.connections));
|
|
274
|
+
return { ok: true, group: options.group, command: "status", details };
|
|
172
275
|
}
|
|
173
276
|
default:
|
|
174
|
-
throw new CliError2(`Unknown
|
|
175
|
-
Usage:
|
|
277
|
+
throw new CliError2(`Unknown ${options.group} command: ${String(command)}
|
|
278
|
+
Usage: ${usageName(options)} <list|add|use|status>`, 1);
|
|
176
279
|
}
|
|
177
280
|
}
|
|
281
|
+
async function executeConnect(context, args) {
|
|
282
|
+
return executeConnectionCommand(context, args, { group: "connect" });
|
|
283
|
+
}
|
|
178
284
|
export {
|
|
285
|
+
executeConnectionCommand,
|
|
179
286
|
executeConnect
|
|
180
287
|
};
|
|
@@ -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");
|
|
@@ -5,8 +5,6 @@ var __require = import.meta.require;
|
|
|
5
5
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
6
6
|
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
7
7
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
8
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
9
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
10
8
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
11
9
|
import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
|
|
12
10
|
function requireNoExtraArgs(args, usage) {
|
|
@@ -98,17 +96,16 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
98
96
|
const global = readGlobalConnections(options);
|
|
99
97
|
const connection = global.connections[repo.selected];
|
|
100
98
|
if (!connection) {
|
|
101
|
-
throw new CliError2(`Selected Rig
|
|
99
|
+
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
102
100
|
}
|
|
103
101
|
return { alias: repo.selected, connection };
|
|
104
102
|
}
|
|
105
103
|
|
|
106
104
|
// packages/cli/src/commands/_server-client.ts
|
|
107
|
-
import { spawnSync } from "child_process";
|
|
108
105
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
109
106
|
import { resolve as resolve2 } from "path";
|
|
110
107
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
111
|
-
var
|
|
108
|
+
var scopedGitHubBearerTokens = new Map;
|
|
112
109
|
function cleanToken(value) {
|
|
113
110
|
const trimmed = value?.trim();
|
|
114
111
|
return trimmed ? trimmed : null;
|
|
@@ -125,25 +122,13 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
125
122
|
}
|
|
126
123
|
}
|
|
127
124
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
128
|
-
|
|
129
|
-
|
|
125
|
+
const scopedKey = resolve2(projectRoot);
|
|
126
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
127
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
130
128
|
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
131
|
-
if (privateSession)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
|
|
136
|
-
if (envToken) {
|
|
137
|
-
cachedGitHubBearerToken = envToken;
|
|
138
|
-
return cachedGitHubBearerToken;
|
|
139
|
-
}
|
|
140
|
-
const result = spawnSync("gh", ["auth", "token"], {
|
|
141
|
-
encoding: "utf8",
|
|
142
|
-
timeout: 5000,
|
|
143
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
144
|
-
});
|
|
145
|
-
cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
|
|
146
|
-
return cachedGitHubBearerToken;
|
|
129
|
+
if (privateSession)
|
|
130
|
+
return privateSession;
|
|
131
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
147
132
|
}
|
|
148
133
|
async function ensureServerForCli(projectRoot) {
|
|
149
134
|
try {
|
|
@@ -226,7 +211,8 @@ async function loadRigConfigOrNull(projectRoot) {
|
|
|
226
211
|
import { existsSync as existsSync3, readFileSync as readFileSync3, rmSync } from "fs";
|
|
227
212
|
import { homedir as homedir2 } from "os";
|
|
228
213
|
import { resolve as resolve3 } from "path";
|
|
229
|
-
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
214
|
+
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
215
|
+
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
230
216
|
async function defaultCommandRunner(command, options = {}) {
|
|
231
217
|
const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
|
|
232
218
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
@@ -245,7 +231,7 @@ function resolvePiHomeDir(inputHomeDir) {
|
|
|
245
231
|
function piListContainsPiRig(output) {
|
|
246
232
|
return output.split(/\r?\n/).some((line) => {
|
|
247
233
|
const normalized = line.trim();
|
|
248
|
-
return normalized.includes(PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
234
|
+
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
249
235
|
});
|
|
250
236
|
}
|
|
251
237
|
async function safeRun(runner, command, options) {
|
|
@@ -421,7 +407,7 @@ async function runRigDoctorChecks(options) {
|
|
|
421
407
|
const taskSourceKind = config?.taskSource?.kind;
|
|
422
408
|
checks.push(taskSourceKind ? check("task-source", "task source configured", "pass", taskSourceKind) : check("task-source", "task source configured", "fail", "missing taskSource", "Configure taskSource in rig.config.ts."));
|
|
423
409
|
const repo = readRepoConnection(projectRoot);
|
|
424
|
-
checks.push(repo ? check("project-link", "repo selected Rig
|
|
410
|
+
checks.push(repo ? check("project-link", "repo selected Rig server", repo.project ? "pass" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : ""}`, "Run `rig init --yes --repo owner/repo` to link this checkout to a GitHub repo slug.") : check("project-link", "repo selected Rig server", "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig server use <alias|local>`."));
|
|
425
411
|
const selected = (() => {
|
|
426
412
|
try {
|
|
427
413
|
return resolveSelectedConnection(projectRoot);
|
|
@@ -429,7 +415,7 @@ async function runRigDoctorChecks(options) {
|
|
|
429
415
|
return null;
|
|
430
416
|
}
|
|
431
417
|
})();
|
|
432
|
-
checks.push(selected ? check("connection", "selected server connection", "pass", selected.connection.kind === "remote" ? selected.connection.baseUrl : "local auto") : check("connection", "selected server
|
|
418
|
+
checks.push(selected ? check("connection", "selected server connection", "pass", selected.connection.kind === "remote" ? selected.connection.baseUrl : "local auto") : check("connection", "selected server", repo ? "fail" : "warn", repo ? "selected alias is missing" : "will auto-start local server", repo ? "Run `rig server list` and `rig server use <alias|local>`." : undefined));
|
|
433
419
|
let server = null;
|
|
434
420
|
try {
|
|
435
421
|
server = await (options.resolveServer ?? ensureServerForCli)(projectRoot);
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/commands/github.ts
|
|
3
|
-
import { spawnSync
|
|
3
|
+
import { spawnSync } from "child_process";
|
|
4
4
|
|
|
5
5
|
// packages/cli/src/runner.ts
|
|
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 takeOption(args, option) {
|
|
@@ -32,7 +30,6 @@ function takeOption(args, option) {
|
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
// packages/cli/src/commands/_server-client.ts
|
|
35
|
-
import { spawnSync } from "child_process";
|
|
36
33
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
37
34
|
import { resolve as resolve2 } from "path";
|
|
38
35
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
@@ -114,13 +111,13 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
114
111
|
const global = readGlobalConnections(options);
|
|
115
112
|
const connection = global.connections[repo.selected];
|
|
116
113
|
if (!connection) {
|
|
117
|
-
throw new CliError2(`Selected Rig
|
|
114
|
+
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
118
115
|
}
|
|
119
116
|
return { alias: repo.selected, connection };
|
|
120
117
|
}
|
|
121
118
|
|
|
122
119
|
// packages/cli/src/commands/_server-client.ts
|
|
123
|
-
var
|
|
120
|
+
var scopedGitHubBearerTokens = new Map;
|
|
124
121
|
function cleanToken(value) {
|
|
125
122
|
const trimmed = value?.trim();
|
|
126
123
|
return trimmed ? trimmed : null;
|
|
@@ -137,25 +134,13 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
137
134
|
}
|
|
138
135
|
}
|
|
139
136
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
const scopedKey = resolve2(projectRoot);
|
|
138
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
139
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
142
140
|
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
143
|
-
if (privateSession)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
|
|
148
|
-
if (envToken) {
|
|
149
|
-
cachedGitHubBearerToken = envToken;
|
|
150
|
-
return cachedGitHubBearerToken;
|
|
151
|
-
}
|
|
152
|
-
const result = spawnSync("gh", ["auth", "token"], {
|
|
153
|
-
encoding: "utf8",
|
|
154
|
-
timeout: 5000,
|
|
155
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
156
|
-
});
|
|
157
|
-
cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
|
|
158
|
-
return cachedGitHubBearerToken;
|
|
141
|
+
if (privateSession)
|
|
142
|
+
return privateSession;
|
|
143
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
159
144
|
}
|
|
160
145
|
async function ensureServerForCli(projectRoot) {
|
|
161
146
|
try {
|
|
@@ -244,7 +229,7 @@ function printPayload(context, payload, fallback) {
|
|
|
244
229
|
console.log(fallback);
|
|
245
230
|
}
|
|
246
231
|
function readGhToken() {
|
|
247
|
-
const result =
|
|
232
|
+
const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
|
|
248
233
|
if (result.status !== 0) {
|
|
249
234
|
const detail = result.stderr?.trim() || result.stdout?.trim() || "gh auth token failed";
|
|
250
235
|
throw new CliError2(`Could not import GitHub token from gh: ${detail}`, 1);
|