@h-rig/cli 0.0.6-alpha.66 → 0.0.6-alpha.68
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 +250 -166
- package/dist/src/commands/_async-ui.js +141 -0
- package/dist/src/commands/_connection-state.js +5 -1
- package/dist/src/commands/_doctor-checks.js +18 -0
- package/dist/src/commands/_operator-view.js +143 -4
- package/dist/src/commands/_pi-frontend.js +13 -1
- package/dist/src/commands/_preflight.js +7 -0
- package/dist/src/commands/_server-client.js +13 -0
- package/dist/src/commands/_snapshot-upload.js +7 -0
- package/dist/src/commands/_spinner.js +4 -2
- package/dist/src/commands/doctor.js +145 -1
- package/dist/src/commands/github.js +137 -3
- package/dist/src/commands/inbox.js +139 -6
- package/dist/src/commands/init.js +18 -0
- package/dist/src/commands/inspect.js +147 -8
- package/dist/src/commands/run.js +190 -38
- package/dist/src/commands/server.js +7 -0
- package/dist/src/commands/setup.js +150 -6
- package/dist/src/commands/stats.js +448 -110
- package/dist/src/commands/task-run-driver.js +7 -0
- package/dist/src/commands/task.js +186 -47
- package/dist/src/commands.js +250 -166
- package/dist/src/index.js +250 -166
- package/package.json +8 -8
|
@@ -285,6 +285,15 @@ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
|
285
285
|
import { resolve as resolve4 } from "path";
|
|
286
286
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
287
287
|
var scopedGitHubBearerTokens = new Map;
|
|
288
|
+
var serverPhaseListener = null;
|
|
289
|
+
function setServerPhaseListener(listener) {
|
|
290
|
+
const previous = serverPhaseListener;
|
|
291
|
+
serverPhaseListener = listener;
|
|
292
|
+
return previous;
|
|
293
|
+
}
|
|
294
|
+
function reportServerPhase(label) {
|
|
295
|
+
serverPhaseListener?.(label);
|
|
296
|
+
}
|
|
288
297
|
function cleanToken(value) {
|
|
289
298
|
const trimmed = value?.trim();
|
|
290
299
|
return trimmed ? trimmed : null;
|
|
@@ -327,6 +336,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
327
336
|
try {
|
|
328
337
|
const selected = resolveSelectedConnection(projectRoot);
|
|
329
338
|
if (selected?.connection.kind === "remote") {
|
|
339
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
330
340
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
331
341
|
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
332
342
|
return {
|
|
@@ -336,6 +346,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
336
346
|
serverProjectRoot
|
|
337
347
|
};
|
|
338
348
|
}
|
|
349
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
339
350
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
340
351
|
return {
|
|
341
352
|
baseUrl: connection.baseUrl,
|
|
@@ -442,6 +453,7 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
442
453
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
443
454
|
if (server.serverProjectRoot)
|
|
444
455
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
456
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
445
457
|
let response;
|
|
446
458
|
try {
|
|
447
459
|
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
@@ -590,7 +602,10 @@ async function runRigDoctorChecks(options) {
|
|
|
590
602
|
const bunVersion = options.bunVersion ?? Bun.version;
|
|
591
603
|
const request = options.requestJson ?? ((pathname, init) => requestServerJson({ projectRoot }, pathname, init));
|
|
592
604
|
const loadConfig = options.loadConfig ?? loadRigConfigOrNull;
|
|
605
|
+
const progress = options.onProgress ?? (() => {});
|
|
606
|
+
progress("Checking local toolchain\u2026");
|
|
593
607
|
checks.push(check("bun", `bun >= ${MIN_SUPPORTED_BUN_VERSION}`, isSupportedBunVersion(bunVersion) ? "pass" : "fail", `found ${bunVersion}`, `Install Bun ${MIN_SUPPORTED_BUN_VERSION} or newer.`), check("git", "git", which("git") ? "pass" : "fail", which("git") ?? undefined, "Install git and ensure it is on PATH."), check("jq", "jq", which("jq") ? "pass" : "warn", which("jq") ?? undefined, "Install jq (for example `brew install jq`)."));
|
|
608
|
+
progress("Loading rig.config\u2026");
|
|
594
609
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
595
610
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
596
611
|
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync4(resolve5(projectRoot, name)));
|
|
@@ -609,6 +624,7 @@ async function runRigDoctorChecks(options) {
|
|
|
609
624
|
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));
|
|
610
625
|
let server = null;
|
|
611
626
|
try {
|
|
627
|
+
progress("Connecting to the selected Rig server\u2026");
|
|
612
628
|
server = await (options.resolveServer ?? ensureServerForCli)(projectRoot);
|
|
613
629
|
checks.push(check("server", "Rig server reachable", "pass", `${server.connectionKind} ${server.baseUrl}`));
|
|
614
630
|
} catch (error) {
|
|
@@ -616,18 +632,21 @@ async function runRigDoctorChecks(options) {
|
|
|
616
632
|
}
|
|
617
633
|
if (server || options.requestJson) {
|
|
618
634
|
try {
|
|
635
|
+
progress("Checking server status\u2026");
|
|
619
636
|
const status = await request("/api/server/status");
|
|
620
637
|
checks.push(check("server-status", "server project status", "pass", JSON.stringify(status).slice(0, 180)));
|
|
621
638
|
} catch (error) {
|
|
622
639
|
checks.push(check("server-status", "server project status", "fail", errorMessage(error), "Run `rig doctor` after the selected server is reachable."));
|
|
623
640
|
}
|
|
624
641
|
try {
|
|
642
|
+
progress("Checking GitHub auth\u2026");
|
|
625
643
|
const auth = await request("/api/github/auth/status");
|
|
626
644
|
checks.push(isAuthenticated(auth) ? check("github-auth", "GitHub auth", "pass") : check("github-auth", "GitHub auth", "fail", "not authenticated", "Run `rig github auth import-gh` or `rig github auth token --token <token>`."));
|
|
627
645
|
} catch (error) {
|
|
628
646
|
checks.push(check("github-auth", "GitHub auth", "fail", errorMessage(error), "Authenticate GitHub through Rig and ensure the server exposes auth status."));
|
|
629
647
|
}
|
|
630
648
|
try {
|
|
649
|
+
progress("Checking GitHub repo permissions\u2026");
|
|
631
650
|
const permissions = await request("/api/github/repo/permissions");
|
|
632
651
|
const allowed = permissionAllowsPr(permissions);
|
|
633
652
|
checks.push(allowed === true ? check("github-repo-permissions", "GitHub repo PR permissions", "pass", JSON.stringify(permissions).slice(0, 180)) : allowed === false ? check("github-repo-permissions", "GitHub repo PR permissions", "fail", JSON.stringify(permissions).slice(0, 180), "Grant the selected GitHub token permission to push branches, open PRs, and merge according to repo rules.") : check("github-repo-permissions", "GitHub repo PR permissions", "warn", JSON.stringify(permissions).slice(0, 180), "Confirm the selected token can push branches and open PRs."));
|
|
@@ -635,6 +654,7 @@ async function runRigDoctorChecks(options) {
|
|
|
635
654
|
checks.push(check("github-repo-permissions", "GitHub repo PR permissions", "warn", errorMessage(error), "Ensure the server exposes repo permission checks and the token can open PRs."));
|
|
636
655
|
}
|
|
637
656
|
try {
|
|
657
|
+
progress("Checking GitHub issue labels\u2026");
|
|
638
658
|
const labels = await request("/api/workspace/task-labels");
|
|
639
659
|
const ready = labelsReady(labels);
|
|
640
660
|
checks.push(ready === false ? check("task-labels", "GitHub issue labels", "fail", JSON.stringify(labels).slice(0, 180), "Let Rig create required labels or create the configured lifecycle labels manually.") : check("task-labels", "GitHub issue labels", ready === true ? "pass" : "warn", JSON.stringify(labels).slice(0, 180), "Confirm required Rig lifecycle labels exist."));
|
|
@@ -642,6 +662,7 @@ async function runRigDoctorChecks(options) {
|
|
|
642
662
|
checks.push(check("task-labels", "GitHub issue labels", "warn", errorMessage(error), "Run `rig init`/`rig doctor` after label setup is wired on the server."));
|
|
643
663
|
}
|
|
644
664
|
try {
|
|
665
|
+
progress("Checking task projection\u2026");
|
|
645
666
|
const projection = await request("/api/workspace/task-projection");
|
|
646
667
|
checks.push(check("task-projection", "task projection", "pass", JSON.stringify(projection).slice(0, 180)));
|
|
647
668
|
} catch (error) {
|
|
@@ -650,6 +671,7 @@ async function runRigDoctorChecks(options) {
|
|
|
650
671
|
const slug = projectStatusSlug(projectRoot, config);
|
|
651
672
|
if (slug) {
|
|
652
673
|
try {
|
|
674
|
+
progress("Checking server project checkout\u2026");
|
|
653
675
|
const project = await request(`/api/projects/${encodeURIComponent(slug)}`);
|
|
654
676
|
checks.push(check("remote-checkout", "server project checkout", "pass", JSON.stringify(project).slice(0, 180)));
|
|
655
677
|
} catch (error) {
|
|
@@ -664,6 +686,7 @@ async function runRigDoctorChecks(options) {
|
|
|
664
686
|
}
|
|
665
687
|
checks.push(githubProjectsCheck(config));
|
|
666
688
|
checks.push(prMergeCheck(config));
|
|
689
|
+
progress("Checking Pi installation\u2026");
|
|
667
690
|
const piChecks = await (options.piChecks ?? (() => buildPiSetupChecks()))().catch((error) => [{
|
|
668
691
|
ok: false,
|
|
669
692
|
label: "pi/pi-rig checks",
|
|
@@ -687,6 +710,127 @@ function countDoctorFailures(checks) {
|
|
|
687
710
|
return checks.filter((entry) => entry.status === "fail").length;
|
|
688
711
|
}
|
|
689
712
|
|
|
713
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
714
|
+
import pc from "picocolors";
|
|
715
|
+
|
|
716
|
+
// packages/cli/src/commands/_spinner.ts
|
|
717
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
718
|
+
function createTtySpinner(input) {
|
|
719
|
+
const output = input.output ?? process.stdout;
|
|
720
|
+
const isTty = output.isTTY === true;
|
|
721
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
722
|
+
let label = input.label;
|
|
723
|
+
let frame = 0;
|
|
724
|
+
let paused = false;
|
|
725
|
+
let stopped = false;
|
|
726
|
+
let lastPrintedLabel = "";
|
|
727
|
+
const render = () => {
|
|
728
|
+
if (stopped || paused)
|
|
729
|
+
return;
|
|
730
|
+
if (!isTty) {
|
|
731
|
+
if (label !== lastPrintedLabel) {
|
|
732
|
+
output.write(`${label}
|
|
733
|
+
`);
|
|
734
|
+
lastPrintedLabel = label;
|
|
735
|
+
}
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
frame = (frame + 1) % frames.length;
|
|
739
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
740
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
741
|
+
};
|
|
742
|
+
const clearLine = () => {
|
|
743
|
+
if (isTty)
|
|
744
|
+
output.write("\r\x1B[2K");
|
|
745
|
+
};
|
|
746
|
+
render();
|
|
747
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
|
|
748
|
+
return {
|
|
749
|
+
setLabel(next) {
|
|
750
|
+
label = next;
|
|
751
|
+
render();
|
|
752
|
+
},
|
|
753
|
+
pause() {
|
|
754
|
+
paused = true;
|
|
755
|
+
clearLine();
|
|
756
|
+
},
|
|
757
|
+
resume() {
|
|
758
|
+
if (stopped)
|
|
759
|
+
return;
|
|
760
|
+
paused = false;
|
|
761
|
+
render();
|
|
762
|
+
},
|
|
763
|
+
stop(finalLine) {
|
|
764
|
+
if (stopped)
|
|
765
|
+
return;
|
|
766
|
+
stopped = true;
|
|
767
|
+
if (timer)
|
|
768
|
+
clearInterval(timer);
|
|
769
|
+
clearLine();
|
|
770
|
+
if (finalLine)
|
|
771
|
+
output.write(`${finalLine}
|
|
772
|
+
`);
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
778
|
+
var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
779
|
+
var DONE_SYMBOL = pc.green("\u25C7");
|
|
780
|
+
var FAIL_SYMBOL = pc.red("\u25A0");
|
|
781
|
+
var activeUpdate = null;
|
|
782
|
+
async function withSpinner(label, work, options = {}) {
|
|
783
|
+
if (options.outputMode === "json") {
|
|
784
|
+
return work(() => {});
|
|
785
|
+
}
|
|
786
|
+
if (activeUpdate) {
|
|
787
|
+
const outer = activeUpdate;
|
|
788
|
+
outer(label);
|
|
789
|
+
return work(outer);
|
|
790
|
+
}
|
|
791
|
+
const output = options.output ?? process.stderr;
|
|
792
|
+
const isTty = output.isTTY === true;
|
|
793
|
+
let lastLabel = label;
|
|
794
|
+
if (!isTty) {
|
|
795
|
+
output.write(`${label}
|
|
796
|
+
`);
|
|
797
|
+
const update2 = (next) => {
|
|
798
|
+
lastLabel = next;
|
|
799
|
+
};
|
|
800
|
+
activeUpdate = update2;
|
|
801
|
+
const previousListener2 = setServerPhaseListener(update2);
|
|
802
|
+
try {
|
|
803
|
+
return await work(update2);
|
|
804
|
+
} finally {
|
|
805
|
+
activeUpdate = null;
|
|
806
|
+
setServerPhaseListener(previousListener2);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
const spinner = createTtySpinner({
|
|
810
|
+
label,
|
|
811
|
+
output,
|
|
812
|
+
frames: CLACK_SPINNER_FRAMES,
|
|
813
|
+
styleFrame: (frame) => pc.magenta(frame)
|
|
814
|
+
});
|
|
815
|
+
const update = (next) => {
|
|
816
|
+
lastLabel = next;
|
|
817
|
+
spinner.setLabel(next);
|
|
818
|
+
};
|
|
819
|
+
activeUpdate = update;
|
|
820
|
+
const previousListener = setServerPhaseListener(update);
|
|
821
|
+
try {
|
|
822
|
+
const result = await work(update);
|
|
823
|
+
spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
|
|
824
|
+
return result;
|
|
825
|
+
} catch (error) {
|
|
826
|
+
spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
|
|
827
|
+
throw error;
|
|
828
|
+
} finally {
|
|
829
|
+
activeUpdate = null;
|
|
830
|
+
setServerPhaseListener(previousListener);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
690
834
|
// packages/cli/src/commands/setup.ts
|
|
691
835
|
async function executeSetup(context, args) {
|
|
692
836
|
const [command = "check", ...rest] = args;
|
|
@@ -717,7 +861,7 @@ async function executeSetup(context, args) {
|
|
|
717
861
|
case "check":
|
|
718
862
|
requireNoExtraArgs(rest, `rig setup ${command}`);
|
|
719
863
|
{
|
|
720
|
-
const checks = await withMutedConsole(context.outputMode === "json", () => runSetupCheck(context.projectRoot));
|
|
864
|
+
const checks = await withMutedConsole(context.outputMode === "json", () => runSetupCheck(context.projectRoot, context.outputMode));
|
|
721
865
|
return { ok: true, group: "setup", command, details: { checks, failures: countDoctorFailures(checks) } };
|
|
722
866
|
}
|
|
723
867
|
case "setup":
|
|
@@ -726,7 +870,7 @@ async function executeSetup(context, args) {
|
|
|
726
870
|
return { ok: true, group: "setup", command };
|
|
727
871
|
case "preflight":
|
|
728
872
|
requireNoExtraArgs(rest, "rig setup preflight");
|
|
729
|
-
await withMutedConsole(context.outputMode === "json", () => runSetupPreflight(context.projectRoot));
|
|
873
|
+
await withMutedConsole(context.outputMode === "json", () => runSetupPreflight(context.projectRoot, context.outputMode));
|
|
730
874
|
return { ok: true, group: "setup", command };
|
|
731
875
|
default:
|
|
732
876
|
throw new CliError(`Unknown setup command: ${command}`, 1, { hint: "Run `rig setup --help` \u2014 commands are bootstrap|check|preflight." });
|
|
@@ -763,8 +907,8 @@ function runSetupInit(projectRoot) {
|
|
|
763
907
|
}
|
|
764
908
|
console.log("Harness directories ready.");
|
|
765
909
|
}
|
|
766
|
-
async function runSetupCheck(projectRoot) {
|
|
767
|
-
const doctorChecks = await runRigDoctorChecks({ projectRoot });
|
|
910
|
+
async function runSetupCheck(projectRoot, outputMode = "text") {
|
|
911
|
+
const doctorChecks = await withSpinner("Running setup checks\u2026", (update) => runRigDoctorChecks({ projectRoot, onProgress: update }), { outputMode });
|
|
768
912
|
console.log(formatDoctorChecks(doctorChecks));
|
|
769
913
|
const failures = countDoctorFailures(doctorChecks);
|
|
770
914
|
if (failures > 0) {
|
|
@@ -772,8 +916,8 @@ async function runSetupCheck(projectRoot) {
|
|
|
772
916
|
}
|
|
773
917
|
return doctorChecks;
|
|
774
918
|
}
|
|
775
|
-
async function runSetupPreflight(projectRoot) {
|
|
776
|
-
await runSetupCheck(projectRoot);
|
|
919
|
+
async function runSetupPreflight(projectRoot, outputMode = "text") {
|
|
920
|
+
await runSetupCheck(projectRoot, outputMode);
|
|
777
921
|
const validationRoot = resolve6(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
|
|
778
922
|
if (existsSync5(validationRoot)) {
|
|
779
923
|
const validators = readdirSync(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|