@h-rig/cli 0.0.6-alpha.64 → 0.0.6-alpha.65
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 +1349 -1599
- package/dist/src/commands/_connection-state.js +14 -5
- package/dist/src/commands/_doctor-checks.js +71 -11
- package/dist/src/commands/_help-catalog.js +99 -60
- package/dist/src/commands/_json-output.js +56 -0
- package/dist/src/commands/_operator-view.js +97 -784
- package/dist/src/commands/_parsers.js +18 -9
- package/dist/src/commands/_pi-frontend.js +96 -788
- package/dist/src/commands/_policy.js +12 -3
- package/dist/src/commands/_preflight.js +73 -13
- package/dist/src/commands/_run-driver-helpers.js +31 -5
- package/dist/src/commands/_run-replay.js +2 -2
- package/dist/src/commands/_server-client.js +76 -16
- package/dist/src/commands/_snapshot-upload.js +70 -10
- package/dist/src/commands/agent.js +21 -11
- package/dist/src/commands/browser.js +24 -15
- package/dist/src/commands/connect.js +22 -17
- package/dist/src/commands/dist.js +15 -6
- package/dist/src/commands/doctor.js +70 -10
- package/dist/src/commands/github.js +79 -19
- package/dist/src/commands/inbox.js +215 -131
- package/dist/src/commands/init.js +202 -35
- package/dist/src/commands/inspect.js +77 -17
- package/dist/src/commands/inspector.js +18 -9
- package/dist/src/commands/pi.js +18 -9
- package/dist/src/commands/plugin.js +19 -10
- package/dist/src/commands/profile-and-review.js +22 -13
- package/dist/src/commands/queue.js +19 -9
- package/dist/src/commands/remote.js +25 -16
- package/dist/src/commands/repo-git-harness.js +18 -9
- package/dist/src/commands/run.js +158 -805
- package/dist/src/commands/server.js +85 -25
- package/dist/src/commands/setup.js +73 -13
- package/dist/src/commands/stats.js +681 -0
- package/dist/src/commands/task-report-bug.js +24 -15
- package/dist/src/commands/task-run-driver.js +121 -49
- package/dist/src/commands/task.js +254 -893
- package/dist/src/commands/test.js +12 -3
- package/dist/src/commands/workspace.js +14 -5
- package/dist/src/commands.js +1339 -1650
- package/dist/src/index.js +1349 -1599
- package/dist/src/launcher.js +72 -10
- package/dist/src/runner.js +14 -5
- package/package.json +10 -7
- package/dist/src/commands/_pi-remote-session.js +0 -771
- package/dist/src/commands/_pi-worker-bridge-extension.js +0 -834
|
@@ -4,10 +4,19 @@ import { resolve as resolve2 } from "path";
|
|
|
4
4
|
|
|
5
5
|
// packages/cli/src/runner.ts
|
|
6
6
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
7
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
7
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
8
8
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
9
9
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
class CliError extends RuntimeCliError {
|
|
12
|
+
hint;
|
|
13
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
14
|
+
super(message, exitCode);
|
|
15
|
+
if (options.hint?.trim()) {
|
|
16
|
+
this.hint = options.hint.trim();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
11
20
|
function takeFlag(args, flag) {
|
|
12
21
|
const rest = [];
|
|
13
22
|
let value = false;
|
|
@@ -28,7 +37,7 @@ function takeOption(args, option) {
|
|
|
28
37
|
if (current === option) {
|
|
29
38
|
const next = args[index + 1];
|
|
30
39
|
if (!next || next.startsWith("-")) {
|
|
31
|
-
throw new CliError(`Missing value for ${option}`);
|
|
40
|
+
throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
|
|
32
41
|
}
|
|
33
42
|
value = next;
|
|
34
43
|
index += 1;
|
|
@@ -210,7 +219,7 @@ function parseIsolationMode(value, allowOff) {
|
|
|
210
219
|
if (allowOff && value === "off") {
|
|
211
220
|
return value;
|
|
212
221
|
}
|
|
213
|
-
throw new
|
|
222
|
+
throw new CliError(`Invalid isolation mode: ${value}. Use ${allowOff ? "off|" : ""}worktree.`);
|
|
214
223
|
}
|
|
215
224
|
|
|
216
225
|
// packages/cli/src/commands/_preflight.ts
|
|
@@ -219,6 +228,7 @@ import { ensureProjectMainFreshBeforeRun } from "@rig/runtime/control-plane/proj
|
|
|
219
228
|
// packages/cli/src/commands/_server-client.ts
|
|
220
229
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
221
230
|
var scopedGitHubBearerTokens = new Map;
|
|
231
|
+
var serverReachabilityCache = new Map;
|
|
222
232
|
|
|
223
233
|
// packages/cli/src/commands/_preflight.ts
|
|
224
234
|
async function runProjectMainSyncPreflight(context, options) {
|
|
@@ -234,7 +244,7 @@ async function runProjectMainSyncPreflight(context, options) {
|
|
|
234
244
|
runBootstrap: async () => {
|
|
235
245
|
const bootstrap = await context.runCommand(["bun", "run", "bootstrap"]);
|
|
236
246
|
if (bootstrap.exitCode !== 0) {
|
|
237
|
-
throw new
|
|
247
|
+
throw new CliError(bootstrap.stderr || bootstrap.stdout || "bun run bootstrap failed during project pre-run sync", bootstrap.exitCode || 1);
|
|
238
248
|
}
|
|
239
249
|
}
|
|
240
250
|
});
|
|
@@ -313,7 +323,7 @@ async function executeAgent(context, args) {
|
|
|
313
323
|
const id = idResult.value || agentId("agent");
|
|
314
324
|
const taskId = taskResult.value?.trim();
|
|
315
325
|
if (!taskId) {
|
|
316
|
-
throw new
|
|
326
|
+
throw new CliError("Usage: rig agent prepare --task <id> [--id <id>] [--mode worktree]");
|
|
317
327
|
}
|
|
318
328
|
const runtime = await withMutedConsole(context.outputMode === "json", () => ensureAgentRuntime({
|
|
319
329
|
projectRoot: context.projectRoot,
|
|
@@ -331,7 +341,7 @@ async function executeAgent(context, args) {
|
|
|
331
341
|
case "run": {
|
|
332
342
|
const { options, commandParts } = splitAtDoubleDash(rest);
|
|
333
343
|
if (commandParts.length === 0) {
|
|
334
|
-
throw new
|
|
344
|
+
throw new CliError("Usage: rig agent run [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
|
|
335
345
|
}
|
|
336
346
|
let pending = options;
|
|
337
347
|
const idResult = takeOption(pending, "--id");
|
|
@@ -347,7 +357,7 @@ async function executeAgent(context, args) {
|
|
|
347
357
|
const id = idResult.value || agentId("agent-run");
|
|
348
358
|
const taskId = taskResult.value?.trim();
|
|
349
359
|
if (!taskId) {
|
|
350
|
-
throw new
|
|
360
|
+
throw new CliError("Usage: rig agent run --task <id> [--id <id>] [--mode worktree] [--skip-project-sync] -- <command...>");
|
|
351
361
|
}
|
|
352
362
|
await runProjectMainSyncPreflight(context, { disabled: skipProjectSyncResult.value });
|
|
353
363
|
const createdAt = new Date().toISOString();
|
|
@@ -406,7 +416,7 @@ async function executeAgent(context, args) {
|
|
|
406
416
|
pid: process.pid,
|
|
407
417
|
errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
|
|
408
418
|
});
|
|
409
|
-
throw new
|
|
419
|
+
throw new CliError(`Agent runtime command failed (${result.exitCode}) in ${runtime.id}${result.stderr ? `
|
|
410
420
|
${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
411
421
|
}
|
|
412
422
|
const completedAt = new Date().toISOString();
|
|
@@ -463,7 +473,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
463
473
|
pending = idResult.rest;
|
|
464
474
|
requireNoExtraArgs(pending, "rig agent cleanup (--id <id> | --all)");
|
|
465
475
|
if (!allResult.value && !idResult.value) {
|
|
466
|
-
throw new
|
|
476
|
+
throw new CliError("Provide --id <id> or --all.", 1, { hint: "Run `rig agent list` to find agent ids." });
|
|
467
477
|
}
|
|
468
478
|
const runtimes = await listAgentRuntimes(context.projectRoot);
|
|
469
479
|
const targets = allResult.value ? runtimes.map((runtime) => runtime.id) : [idResult.value];
|
|
@@ -488,7 +498,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
488
498
|
};
|
|
489
499
|
}
|
|
490
500
|
default:
|
|
491
|
-
throw new
|
|
501
|
+
throw new CliError(`Unknown agent command: ${command}`, 1, { hint: "Run `rig agent --help` to list agent commands." });
|
|
492
502
|
}
|
|
493
503
|
}
|
|
494
504
|
export {
|
|
@@ -10,10 +10,19 @@ import pc2 from "picocolors";
|
|
|
10
10
|
|
|
11
11
|
// packages/cli/src/runner.ts
|
|
12
12
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
13
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
13
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
14
14
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
15
15
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
16
|
-
|
|
16
|
+
|
|
17
|
+
class CliError extends RuntimeCliError {
|
|
18
|
+
hint;
|
|
19
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
20
|
+
super(message, exitCode);
|
|
21
|
+
if (options.hint?.trim()) {
|
|
22
|
+
this.hint = options.hint.trim();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
17
26
|
function takeFlag(args, flag) {
|
|
18
27
|
const rest = [];
|
|
19
28
|
let value = false;
|
|
@@ -34,7 +43,7 @@ function takeOption(args, option) {
|
|
|
34
43
|
if (current === option) {
|
|
35
44
|
const next = args[index + 1];
|
|
36
45
|
if (!next || next.startsWith("-")) {
|
|
37
|
-
throw new CliError(`Missing value for ${option}`);
|
|
46
|
+
throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
|
|
38
47
|
}
|
|
39
48
|
value = next;
|
|
40
49
|
index += 1;
|
|
@@ -93,7 +102,7 @@ async function promptBugConfirm(message, initialValue) {
|
|
|
93
102
|
function unwrapClackPrompt(result) {
|
|
94
103
|
if (clack.isCancel(result)) {
|
|
95
104
|
clack.cancel("Bug report cancelled.");
|
|
96
|
-
throw new
|
|
105
|
+
throw new CliError("Bug report cancelled by user.");
|
|
97
106
|
}
|
|
98
107
|
return result;
|
|
99
108
|
}
|
|
@@ -359,7 +368,7 @@ async function executeBrowser(context, args) {
|
|
|
359
368
|
if (command === "app" || command === "hp-next") {
|
|
360
369
|
const appSlug = command === "hp-next" ? "hp-next" : process.env.RIG_BROWSER_APP?.trim() || "";
|
|
361
370
|
if (!appSlug) {
|
|
362
|
-
throw new
|
|
371
|
+
throw new CliError(`rig browser app: set RIG_BROWSER_APP=<app-slug> to select which project app scripts to invoke.
|
|
363
372
|
` + `Scripts are invoked as 'bun run app:<mode>:browser:<app-slug>'.
|
|
364
373
|
|
|
365
374
|
${browserHelpText()}`);
|
|
@@ -371,7 +380,7 @@ ${browserHelpText()}`);
|
|
|
371
380
|
}
|
|
372
381
|
const modes = ["dev", "start", "check", "e2e", "reset"];
|
|
373
382
|
if (!modes.includes(subcommand)) {
|
|
374
|
-
throw new
|
|
383
|
+
throw new CliError(`Unknown browser ${command} command: ${subcommand}. Valid modes: ${modes.join(", ")}.
|
|
375
384
|
|
|
376
385
|
${browserHelpText()}`);
|
|
377
386
|
}
|
|
@@ -391,9 +400,9 @@ ${browserHelpText()}`);
|
|
|
391
400
|
await context.runCommand(["bun", "run", "--filter=@rig/browser", packageScript]);
|
|
392
401
|
return { ok: true, group: "browser", command };
|
|
393
402
|
}
|
|
394
|
-
throw new
|
|
403
|
+
throw new CliError(`Unknown browser command: ${command}
|
|
395
404
|
|
|
396
|
-
${browserHelpText()}`);
|
|
405
|
+
${browserHelpText()}`, 1, { hint: "Run `rig browser help` for the full browser command reference." });
|
|
397
406
|
}
|
|
398
407
|
async function executeBrowserDemo(context, args) {
|
|
399
408
|
const [maybeHelp] = args;
|
|
@@ -416,11 +425,11 @@ async function executeBrowserDemo(context, args) {
|
|
|
416
425
|
pending = noBuildFlag.rest;
|
|
417
426
|
requireNoExtraArgs(pending, "rig browser demo [--port <n>] [--profile <name>] [--state-dir <path>] [--target-url <url>] [--keep-open] [--no-build]");
|
|
418
427
|
if (context.outputMode !== "text" || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
419
|
-
throw new
|
|
428
|
+
throw new CliError("rig browser demo requires an interactive TTY in text mode.", 1, { hint: "Run `rig browser demo` from an interactive terminal (no --json, no piped stdin)." });
|
|
420
429
|
}
|
|
421
430
|
const port = portResult.value ? Number.parseInt(portResult.value, 10) : undefined;
|
|
422
431
|
if (port !== undefined && (!Number.isFinite(port) || port <= 0)) {
|
|
423
|
-
throw new
|
|
432
|
+
throw new CliError(`Invalid --port value: ${portResult.value}`, 1, { hint: "Pass a positive port number, e.g. `--port 9222`." });
|
|
424
433
|
}
|
|
425
434
|
const runtime = buildBrowserDemoRuntime(context.projectRoot, {
|
|
426
435
|
...port !== undefined ? { port } : {},
|
|
@@ -575,7 +584,7 @@ function runBrowserDemoBuild(projectRoot) {
|
|
|
575
584
|
if (result.exitCode !== 0) {
|
|
576
585
|
const details = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join(`
|
|
577
586
|
`);
|
|
578
|
-
throw new
|
|
587
|
+
throw new CliError(`Browser demo build failed.${details ? `
|
|
579
588
|
${details}` : ""}`, result.exitCode);
|
|
580
589
|
}
|
|
581
590
|
}
|
|
@@ -630,7 +639,7 @@ function readBrowserDemoCommandLine(command) {
|
|
|
630
639
|
cleanup();
|
|
631
640
|
stdout.write(`
|
|
632
641
|
`);
|
|
633
|
-
rejectInput(new
|
|
642
|
+
rejectInput(new CliError("Browser demo cancelled."));
|
|
634
643
|
return;
|
|
635
644
|
}
|
|
636
645
|
if (key.name === "return" || key.name === "enter") {
|
|
@@ -696,7 +705,7 @@ async function waitForBrowserDemoReady(runtime, child) {
|
|
|
696
705
|
return { targets: nextTargets, pageTarget: nextPageTarget };
|
|
697
706
|
}, child, "CDP demo page target");
|
|
698
707
|
if (!version.Browser) {
|
|
699
|
-
throw new
|
|
708
|
+
throw new CliError("Browser demo reached CDP but /json/version did not include Browser metadata.");
|
|
700
709
|
}
|
|
701
710
|
return { version, targets, pageTarget };
|
|
702
711
|
}
|
|
@@ -709,14 +718,14 @@ async function waitForBrowserDemo(check, child, label, timeoutMs = 18000) {
|
|
|
709
718
|
let lastError = null;
|
|
710
719
|
for (;; ) {
|
|
711
720
|
if (child.exitCode !== null || child.signalCode !== null) {
|
|
712
|
-
throw new
|
|
721
|
+
throw new CliError(`Rig Browser exited before ${label} became ready.`);
|
|
713
722
|
}
|
|
714
723
|
try {
|
|
715
724
|
return await check();
|
|
716
725
|
} catch (error) {
|
|
717
726
|
lastError = error;
|
|
718
727
|
if (Date.now() - startedAt > timeoutMs) {
|
|
719
|
-
throw new
|
|
728
|
+
throw new CliError(`${label} did not become ready within ${timeoutMs}ms: ${String(lastError)}`);
|
|
720
729
|
}
|
|
721
730
|
await new Promise((resolveDelay) => setTimeout(resolveDelay, 150));
|
|
722
731
|
}
|
|
@@ -4,10 +4,19 @@ import { cancel, isCancel, select } from "@clack/prompts";
|
|
|
4
4
|
|
|
5
5
|
// packages/cli/src/runner.ts
|
|
6
6
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
7
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
7
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
8
8
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
9
9
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
class CliError extends RuntimeCliError {
|
|
12
|
+
hint;
|
|
13
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
14
|
+
super(message, exitCode);
|
|
15
|
+
if (options.hint?.trim()) {
|
|
16
|
+
this.hint = options.hint.trim();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
11
20
|
function requireNoExtraArgs(args, usage) {
|
|
12
21
|
if (args.length > 0) {
|
|
13
22
|
throw new CliError(`Unexpected arguments: ${args.join(" ")}
|
|
@@ -37,7 +46,7 @@ function readJsonFile(path) {
|
|
|
37
46
|
try {
|
|
38
47
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
39
48
|
} catch (error) {
|
|
40
|
-
throw new
|
|
49
|
+
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>`." });
|
|
41
50
|
}
|
|
42
51
|
}
|
|
43
52
|
function writeJsonFile(path, value) {
|
|
@@ -80,7 +89,7 @@ function writeGlobalConnections(state, options = {}) {
|
|
|
80
89
|
function upsertGlobalConnection(alias, connection, options = {}) {
|
|
81
90
|
const cleanAlias = alias.trim();
|
|
82
91
|
if (!cleanAlias)
|
|
83
|
-
throw new
|
|
92
|
+
throw new CliError("Connection alias is required.", 1, { hint: "Save a server with `rig server add <alias> <url>`." });
|
|
84
93
|
const state = readGlobalConnections(options);
|
|
85
94
|
state.connections[cleanAlias] = connection;
|
|
86
95
|
writeGlobalConnections(state, options);
|
|
@@ -180,15 +189,15 @@ function parseConnection(alias, value, options) {
|
|
|
180
189
|
if (alias === "local" && !value)
|
|
181
190
|
return { kind: "local", mode: "auto" };
|
|
182
191
|
if (!value)
|
|
183
|
-
throw new
|
|
192
|
+
throw new CliError(`Missing remote server URL. Usage: ${usageName(options)} add <alias> <url>`, 1);
|
|
184
193
|
let parsed;
|
|
185
194
|
try {
|
|
186
195
|
parsed = new URL(value);
|
|
187
196
|
} catch {
|
|
188
|
-
throw new
|
|
197
|
+
throw new CliError(`Invalid Rig server URL: ${value}`, 1, { hint: "Pass a full http(s) URL, e.g. `rig server add prod https://rig.example.com`." });
|
|
189
198
|
}
|
|
190
199
|
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
191
|
-
throw new
|
|
200
|
+
throw new CliError("Rig remote server URL must be http(s).", 1, { hint: "Use an http:// or https:// URL, e.g. `rig server add prod https://rig.example.com`." });
|
|
192
201
|
}
|
|
193
202
|
return { kind: "remote", baseUrl: parsed.toString().replace(/\/+$/, "") };
|
|
194
203
|
}
|
|
@@ -217,7 +226,7 @@ async function promptForConnectionAlias(context) {
|
|
|
217
226
|
});
|
|
218
227
|
if (isCancel(answer)) {
|
|
219
228
|
cancel("No server selected.");
|
|
220
|
-
throw new
|
|
229
|
+
throw new CliError("No server selected.", 3);
|
|
221
230
|
}
|
|
222
231
|
return String(answer);
|
|
223
232
|
}
|
|
@@ -233,7 +242,7 @@ async function executeConnectionCommand(context, args, options) {
|
|
|
233
242
|
case "add": {
|
|
234
243
|
const [alias, url, ...extra] = rest;
|
|
235
244
|
if (!alias)
|
|
236
|
-
throw new
|
|
245
|
+
throw new CliError(`Missing alias. Usage: ${usageName(options)} add <alias> <url>`, 1);
|
|
237
246
|
requireNoExtraArgs(extra, `${usageName(options)} add <alias> <url>`);
|
|
238
247
|
const connection = parseConnection(alias, url, options);
|
|
239
248
|
const state = upsertGlobalConnection(alias, connection);
|
|
@@ -251,11 +260,11 @@ async function executeConnectionCommand(context, args, options) {
|
|
|
251
260
|
alias = await promptForConnectionAlias(context);
|
|
252
261
|
}
|
|
253
262
|
if (!alias)
|
|
254
|
-
throw new
|
|
263
|
+
throw new CliError(`Missing alias. Usage: ${usageName(options)} use <alias|local>`, 1);
|
|
255
264
|
if (alias !== "local") {
|
|
256
265
|
const state = readGlobalConnections();
|
|
257
266
|
if (!state.connections[alias])
|
|
258
|
-
throw new
|
|
267
|
+
throw new CliError(`Unknown Rig server: ${alias}`, 1, { hint: "Run `rig server list` to see saved aliases, or save one with `rig server add <alias> <url>`." });
|
|
259
268
|
}
|
|
260
269
|
const repoState = { selected: alias, linkedAt: new Date().toISOString() };
|
|
261
270
|
writeRepoConnection(context.projectRoot, repoState);
|
|
@@ -275,14 +284,10 @@ async function executeConnectionCommand(context, args, options) {
|
|
|
275
284
|
return { ok: true, group: options.group, command: "status", details };
|
|
276
285
|
}
|
|
277
286
|
default:
|
|
278
|
-
throw new
|
|
287
|
+
throw new CliError(`Unknown ${options.group} command: ${String(command)}
|
|
279
288
|
Usage: ${usageName(options)} <list|add|use|status>`, 1);
|
|
280
289
|
}
|
|
281
290
|
}
|
|
282
|
-
async function executeConnect(context, args) {
|
|
283
|
-
return executeConnectionCommand(context, args, { group: "connect" });
|
|
284
|
-
}
|
|
285
291
|
export {
|
|
286
|
-
executeConnectionCommand
|
|
287
|
-
executeConnect
|
|
292
|
+
executeConnectionCommand
|
|
288
293
|
};
|
|
@@ -17,10 +17,19 @@ import { resolve as resolve3 } from "path";
|
|
|
17
17
|
|
|
18
18
|
// packages/cli/src/runner.ts
|
|
19
19
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
20
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
20
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
21
21
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
22
22
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
class CliError extends RuntimeCliError {
|
|
25
|
+
hint;
|
|
26
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
27
|
+
super(message, exitCode);
|
|
28
|
+
if (options.hint?.trim()) {
|
|
29
|
+
this.hint = options.hint.trim();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
24
33
|
function takeOption(args, option) {
|
|
25
34
|
const rest = [];
|
|
26
35
|
let value;
|
|
@@ -29,7 +38,7 @@ function takeOption(args, option) {
|
|
|
29
38
|
if (current === option) {
|
|
30
39
|
const next = args[index + 1];
|
|
31
40
|
if (!next || next.startsWith("-")) {
|
|
32
|
-
throw new CliError(`Missing value for ${option}`);
|
|
41
|
+
throw new CliError(`Missing value for ${option}`, 1, { hint: `Provide a value after ${option}, e.g. \`${option} <value>\`.` });
|
|
33
42
|
}
|
|
34
43
|
value = next;
|
|
35
44
|
index += 1;
|
|
@@ -65,7 +74,7 @@ function parseInstallScope(value) {
|
|
|
65
74
|
if (value === "system") {
|
|
66
75
|
return "system";
|
|
67
76
|
}
|
|
68
|
-
throw new
|
|
77
|
+
throw new CliError(`Invalid --scope value: ${value}. Use user|system.`);
|
|
69
78
|
}
|
|
70
79
|
function resolveInstallDir(scope, explicitPath) {
|
|
71
80
|
if (explicitPath) {
|
|
@@ -209,7 +218,7 @@ async function executeDist(context, args) {
|
|
|
209
218
|
source = resolve3(buildDir, "bin", "rig");
|
|
210
219
|
}
|
|
211
220
|
if (!existsSync(source)) {
|
|
212
|
-
throw new
|
|
221
|
+
throw new CliError(`Unable to locate rig binary at ${source}.`, 2, { hint: "Build it first with `rig dist build`, then re-run `rig dist install`." });
|
|
213
222
|
}
|
|
214
223
|
const installedPath = resolve3(installDir, "rig");
|
|
215
224
|
if (existsSync(installedPath)) {
|
|
@@ -391,7 +400,7 @@ async function executeDist(context, args) {
|
|
|
391
400
|
return { ok: true, group: "dist", command, details: { imageId: currentId, count: targets.length } };
|
|
392
401
|
}
|
|
393
402
|
default:
|
|
394
|
-
throw new
|
|
403
|
+
throw new CliError(`Unknown dist command: ${command}`, 1, { hint: "Run `rig dist --help` \u2014 commands are build|install|doctor." });
|
|
395
404
|
}
|
|
396
405
|
}
|
|
397
406
|
export {
|
|
@@ -3,10 +3,19 @@ var __require = import.meta.require;
|
|
|
3
3
|
|
|
4
4
|
// packages/cli/src/runner.ts
|
|
5
5
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
6
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
6
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
7
7
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
8
8
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
class CliError extends RuntimeCliError {
|
|
11
|
+
hint;
|
|
12
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
13
|
+
super(message, exitCode);
|
|
14
|
+
if (options.hint?.trim()) {
|
|
15
|
+
this.hint = options.hint.trim();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
10
19
|
function requireNoExtraArgs(args, usage) {
|
|
11
20
|
if (args.length > 0) {
|
|
12
21
|
throw new CliError(`Unexpected arguments: ${args.join(" ")}
|
|
@@ -41,7 +50,7 @@ function readJsonFile(path) {
|
|
|
41
50
|
try {
|
|
42
51
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
43
52
|
} catch (error) {
|
|
44
|
-
throw new
|
|
53
|
+
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>`." });
|
|
45
54
|
}
|
|
46
55
|
}
|
|
47
56
|
function writeJsonFile(path, value) {
|
|
@@ -105,7 +114,7 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
105
114
|
const global = readGlobalConnections(options);
|
|
106
115
|
const connection = global.connections[repo.selected];
|
|
107
116
|
if (!connection) {
|
|
108
|
-
throw new
|
|
117
|
+
throw new CliError(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
109
118
|
}
|
|
110
119
|
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
111
120
|
}
|
|
@@ -181,7 +190,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
181
190
|
};
|
|
182
191
|
} catch (error) {
|
|
183
192
|
if (error instanceof Error) {
|
|
184
|
-
throw new
|
|
193
|
+
throw new CliError(error.message, 1);
|
|
185
194
|
}
|
|
186
195
|
throw error;
|
|
187
196
|
}
|
|
@@ -231,15 +240,64 @@ function diagnosticMessage(payload) {
|
|
|
231
240
|
});
|
|
232
241
|
return messages.length > 0 ? messages.join("; ") : null;
|
|
233
242
|
}
|
|
243
|
+
var serverReachabilityCache = new Map;
|
|
244
|
+
async function probeServerReachability(baseUrl, authToken) {
|
|
245
|
+
try {
|
|
246
|
+
const response = await fetch(`${baseUrl.replace(/\/+$/, "")}/api/server/status`, {
|
|
247
|
+
headers: mergeHeaders(undefined, authToken),
|
|
248
|
+
signal: AbortSignal.timeout(1500)
|
|
249
|
+
});
|
|
250
|
+
return response.ok;
|
|
251
|
+
} catch {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function cachedServerReachability(projectRoot, baseUrl, authToken) {
|
|
256
|
+
const key = resolve2(projectRoot);
|
|
257
|
+
const cached = serverReachabilityCache.get(key);
|
|
258
|
+
if (cached)
|
|
259
|
+
return cached;
|
|
260
|
+
const probe = probeServerReachability(baseUrl, authToken);
|
|
261
|
+
serverReachabilityCache.set(key, probe);
|
|
262
|
+
return probe;
|
|
263
|
+
}
|
|
264
|
+
function describeSelectedServer(projectRoot, server) {
|
|
265
|
+
try {
|
|
266
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
267
|
+
if (selected) {
|
|
268
|
+
return {
|
|
269
|
+
alias: selected.alias,
|
|
270
|
+
target: selected.connection.kind === "remote" ? selected.connection.baseUrl : server.baseUrl
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
} catch {}
|
|
274
|
+
return { alias: server.connectionKind === "remote" ? "remote" : "local", target: server.baseUrl };
|
|
275
|
+
}
|
|
276
|
+
async function buildServerFailureContext(projectRoot, server) {
|
|
277
|
+
const { alias, target } = describeSelectedServer(projectRoot, server);
|
|
278
|
+
const reachable = await cachedServerReachability(projectRoot, server.baseUrl, server.authToken);
|
|
279
|
+
const reachability = reachable ? "server is reachable" : "server is unreachable";
|
|
280
|
+
return {
|
|
281
|
+
contextLine: `Currently connected to: ${alias} at ${target} (${reachability}).`,
|
|
282
|
+
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
283
|
+
};
|
|
284
|
+
}
|
|
234
285
|
async function requestServerJson(context, pathname, init = {}) {
|
|
235
286
|
const server = await ensureServerForCli(context.projectRoot);
|
|
236
287
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
237
288
|
if (server.serverProjectRoot)
|
|
238
289
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
290
|
+
let response;
|
|
291
|
+
try {
|
|
292
|
+
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
293
|
+
...init,
|
|
294
|
+
headers
|
|
295
|
+
});
|
|
296
|
+
} catch (error) {
|
|
297
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
298
|
+
throw new CliError(`Rig server request failed: ${error instanceof Error ? error.message : String(error)}
|
|
299
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
300
|
+
}
|
|
243
301
|
const text = await response.text();
|
|
244
302
|
const payload = text.trim().length > 0 ? (() => {
|
|
245
303
|
try {
|
|
@@ -251,7 +309,9 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
251
309
|
if (!response.ok) {
|
|
252
310
|
const diagnostics = diagnosticMessage(payload);
|
|
253
311
|
const detail = diagnostics ?? (text || response.statusText);
|
|
254
|
-
|
|
312
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
313
|
+
throw new CliError(`Rig server request failed (${response.status}): ${detail}
|
|
314
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
255
315
|
}
|
|
256
316
|
return payload;
|
|
257
317
|
}
|