@h-rig/cli 0.0.6-alpha.67 → 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 +232 -71
- package/dist/src/commands/_async-ui.js +141 -0
- 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 +173 -31
- package/dist/src/commands/server.js +7 -0
- package/dist/src/commands/setup.js +150 -6
- package/dist/src/commands/stats.js +156 -22
- package/dist/src/commands/task-run-driver.js +7 -0
- package/dist/src/commands/task.js +186 -47
- package/dist/src/commands.js +232 -71
- package/dist/src/index.js +232 -71
- 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());
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/commands/stats.ts
|
|
3
|
-
import
|
|
3
|
+
import pc4 from "picocolors";
|
|
4
4
|
|
|
5
5
|
// packages/cli/src/runner.ts
|
|
6
6
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
@@ -152,6 +152,15 @@ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
|
152
152
|
import { resolve as resolve2 } from "path";
|
|
153
153
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
154
154
|
var scopedGitHubBearerTokens = new Map;
|
|
155
|
+
var serverPhaseListener = null;
|
|
156
|
+
function setServerPhaseListener(listener) {
|
|
157
|
+
const previous = serverPhaseListener;
|
|
158
|
+
serverPhaseListener = listener;
|
|
159
|
+
return previous;
|
|
160
|
+
}
|
|
161
|
+
function reportServerPhase(label) {
|
|
162
|
+
serverPhaseListener?.(label);
|
|
163
|
+
}
|
|
155
164
|
function cleanToken(value) {
|
|
156
165
|
const trimmed = value?.trim();
|
|
157
166
|
return trimmed ? trimmed : null;
|
|
@@ -194,6 +203,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
194
203
|
try {
|
|
195
204
|
const selected = resolveSelectedConnection(projectRoot);
|
|
196
205
|
if (selected?.connection.kind === "remote") {
|
|
206
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
197
207
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
198
208
|
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
199
209
|
return {
|
|
@@ -203,6 +213,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
203
213
|
serverProjectRoot
|
|
204
214
|
};
|
|
205
215
|
}
|
|
216
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
206
217
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
207
218
|
return {
|
|
208
219
|
baseUrl: connection.baseUrl,
|
|
@@ -309,6 +320,7 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
309
320
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
310
321
|
if (server.serverProjectRoot)
|
|
311
322
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
323
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
312
324
|
let response;
|
|
313
325
|
try {
|
|
314
326
|
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
@@ -363,9 +375,130 @@ function formatNextSteps(steps) {
|
|
|
363
375
|
return [pc.bold("Next"), ...steps.map((step) => `${pc.dim("\u203A")} ${step}`)];
|
|
364
376
|
}
|
|
365
377
|
|
|
378
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
379
|
+
import pc2 from "picocolors";
|
|
380
|
+
|
|
381
|
+
// packages/cli/src/commands/_spinner.ts
|
|
382
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
383
|
+
function createTtySpinner(input) {
|
|
384
|
+
const output = input.output ?? process.stdout;
|
|
385
|
+
const isTty = output.isTTY === true;
|
|
386
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
387
|
+
let label = input.label;
|
|
388
|
+
let frame = 0;
|
|
389
|
+
let paused = false;
|
|
390
|
+
let stopped = false;
|
|
391
|
+
let lastPrintedLabel = "";
|
|
392
|
+
const render = () => {
|
|
393
|
+
if (stopped || paused)
|
|
394
|
+
return;
|
|
395
|
+
if (!isTty) {
|
|
396
|
+
if (label !== lastPrintedLabel) {
|
|
397
|
+
output.write(`${label}
|
|
398
|
+
`);
|
|
399
|
+
lastPrintedLabel = label;
|
|
400
|
+
}
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
frame = (frame + 1) % frames.length;
|
|
404
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
405
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
406
|
+
};
|
|
407
|
+
const clearLine = () => {
|
|
408
|
+
if (isTty)
|
|
409
|
+
output.write("\r\x1B[2K");
|
|
410
|
+
};
|
|
411
|
+
render();
|
|
412
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
|
|
413
|
+
return {
|
|
414
|
+
setLabel(next) {
|
|
415
|
+
label = next;
|
|
416
|
+
render();
|
|
417
|
+
},
|
|
418
|
+
pause() {
|
|
419
|
+
paused = true;
|
|
420
|
+
clearLine();
|
|
421
|
+
},
|
|
422
|
+
resume() {
|
|
423
|
+
if (stopped)
|
|
424
|
+
return;
|
|
425
|
+
paused = false;
|
|
426
|
+
render();
|
|
427
|
+
},
|
|
428
|
+
stop(finalLine) {
|
|
429
|
+
if (stopped)
|
|
430
|
+
return;
|
|
431
|
+
stopped = true;
|
|
432
|
+
if (timer)
|
|
433
|
+
clearInterval(timer);
|
|
434
|
+
clearLine();
|
|
435
|
+
if (finalLine)
|
|
436
|
+
output.write(`${finalLine}
|
|
437
|
+
`);
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
443
|
+
var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
444
|
+
var DONE_SYMBOL = pc2.green("\u25C7");
|
|
445
|
+
var FAIL_SYMBOL = pc2.red("\u25A0");
|
|
446
|
+
var activeUpdate = null;
|
|
447
|
+
async function withSpinner(label, work, options = {}) {
|
|
448
|
+
if (options.outputMode === "json") {
|
|
449
|
+
return work(() => {});
|
|
450
|
+
}
|
|
451
|
+
if (activeUpdate) {
|
|
452
|
+
const outer = activeUpdate;
|
|
453
|
+
outer(label);
|
|
454
|
+
return work(outer);
|
|
455
|
+
}
|
|
456
|
+
const output = options.output ?? process.stderr;
|
|
457
|
+
const isTty = output.isTTY === true;
|
|
458
|
+
let lastLabel = label;
|
|
459
|
+
if (!isTty) {
|
|
460
|
+
output.write(`${label}
|
|
461
|
+
`);
|
|
462
|
+
const update2 = (next) => {
|
|
463
|
+
lastLabel = next;
|
|
464
|
+
};
|
|
465
|
+
activeUpdate = update2;
|
|
466
|
+
const previousListener2 = setServerPhaseListener(update2);
|
|
467
|
+
try {
|
|
468
|
+
return await work(update2);
|
|
469
|
+
} finally {
|
|
470
|
+
activeUpdate = null;
|
|
471
|
+
setServerPhaseListener(previousListener2);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
const spinner = createTtySpinner({
|
|
475
|
+
label,
|
|
476
|
+
output,
|
|
477
|
+
frames: CLACK_SPINNER_FRAMES,
|
|
478
|
+
styleFrame: (frame) => pc2.magenta(frame)
|
|
479
|
+
});
|
|
480
|
+
const update = (next) => {
|
|
481
|
+
lastLabel = next;
|
|
482
|
+
spinner.setLabel(next);
|
|
483
|
+
};
|
|
484
|
+
activeUpdate = update;
|
|
485
|
+
const previousListener = setServerPhaseListener(update);
|
|
486
|
+
try {
|
|
487
|
+
const result = await work(update);
|
|
488
|
+
spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
|
|
489
|
+
return result;
|
|
490
|
+
} catch (error) {
|
|
491
|
+
spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
|
|
492
|
+
throw error;
|
|
493
|
+
} finally {
|
|
494
|
+
activeUpdate = null;
|
|
495
|
+
setServerPhaseListener(previousListener);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
366
499
|
// packages/cli/src/commands/_help-catalog.ts
|
|
367
500
|
import { intro, log as log2, note as note2, outro } from "@clack/prompts";
|
|
368
|
-
import
|
|
501
|
+
import pc3 from "picocolors";
|
|
369
502
|
var TOP_LEVEL_SECTIONS = [
|
|
370
503
|
{
|
|
371
504
|
title: "Start here",
|
|
@@ -647,13 +780,13 @@ var ADVANCED_GROUPS = [
|
|
|
647
780
|
];
|
|
648
781
|
var ALL_GROUPS = [...PRIMARY_GROUPS, ...ADVANCED_GROUPS];
|
|
649
782
|
function heading(title) {
|
|
650
|
-
return
|
|
783
|
+
return pc3.bold(pc3.cyan(title));
|
|
651
784
|
}
|
|
652
785
|
function renderRigBanner(version) {
|
|
653
|
-
const m = (s) =>
|
|
654
|
-
const c = (s) =>
|
|
655
|
-
const y = (s) =>
|
|
656
|
-
const d = (s) =>
|
|
786
|
+
const m = (s) => pc3.bold(pc3.magenta(s));
|
|
787
|
+
const c = (s) => pc3.bold(pc3.cyan(s));
|
|
788
|
+
const y = (s) => pc3.yellow(s);
|
|
789
|
+
const d = (s) => pc3.dim(s);
|
|
657
790
|
const lines = [
|
|
658
791
|
m(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ") + d(" \u2591\u2592\u2593\u2588 "),
|
|
659
792
|
m(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ") + d("\u2593\u2592\u2591"),
|
|
@@ -662,7 +795,7 @@ function renderRigBanner(version) {
|
|
|
662
795
|
y(" \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + d(" \u2592\u2591"),
|
|
663
796
|
y(" \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D "),
|
|
664
797
|
"",
|
|
665
|
-
` ${c("\u25E2\u25E4")} ${
|
|
798
|
+
` ${c("\u25E2\u25E4")} ${pc3.bold("the control rig for autonomous coding agents")} ${m("//")} ${d("you are the operator")}`,
|
|
666
799
|
version ? ` ${d(`v${version} \xB7 jack in: rig task run --next`)}` : ` ${d("jack in: rig task run --next")}`
|
|
667
800
|
];
|
|
668
801
|
return lines.join(`
|
|
@@ -670,7 +803,7 @@ function renderRigBanner(version) {
|
|
|
670
803
|
}
|
|
671
804
|
function commandLine(command, description) {
|
|
672
805
|
const commandColumn = command.length >= 38 ? `${command} ` : command.padEnd(38);
|
|
673
|
-
return `${
|
|
806
|
+
return `${pc3.dim("\u2502")} ${pc3.bold(commandColumn)} ${description}`;
|
|
674
807
|
}
|
|
675
808
|
function renderCommandBlock(commands) {
|
|
676
809
|
return commands.map((entry) => commandLine(entry.command, entry.description)).join(`
|
|
@@ -680,37 +813,37 @@ function renderGroup(group) {
|
|
|
680
813
|
const lines = [
|
|
681
814
|
`${heading(`rig ${group.name}`)} \u2014 ${group.summary}`,
|
|
682
815
|
"",
|
|
683
|
-
|
|
816
|
+
pc3.bold("Usage"),
|
|
684
817
|
...group.usage.map((line) => ` ${line}`),
|
|
685
818
|
"",
|
|
686
|
-
|
|
819
|
+
pc3.bold("Commands"),
|
|
687
820
|
...group.commands.map((entry) => commandLine(entry.command, entry.description))
|
|
688
821
|
];
|
|
689
822
|
if (group.examples?.length) {
|
|
690
|
-
lines.push("",
|
|
823
|
+
lines.push("", pc3.bold("Examples"), ...group.examples.map((line) => ` ${pc3.dim("$")} ${line}`));
|
|
691
824
|
}
|
|
692
825
|
if (group.next?.length) {
|
|
693
|
-
lines.push("",
|
|
826
|
+
lines.push("", pc3.bold("Next steps"), ...group.next.map((line) => ` ${pc3.dim("\u203A")} ${line}`));
|
|
694
827
|
}
|
|
695
828
|
if (group.advanced?.length) {
|
|
696
|
-
lines.push("",
|
|
829
|
+
lines.push("", pc3.bold("Compatibility / advanced"), ...group.advanced.map((line) => ` ${pc3.dim("\u203A")} ${line}`));
|
|
697
830
|
}
|
|
698
831
|
return lines.join(`
|
|
699
832
|
`);
|
|
700
833
|
}
|
|
701
834
|
function renderTopLevelHelp() {
|
|
702
835
|
return [
|
|
703
|
-
`${heading("rig")} ${
|
|
704
|
-
|
|
836
|
+
`${heading("rig")} ${pc3.dim("\u2014 server-owned task/run control plane for Pi-backed engineering work")}`,
|
|
837
|
+
pc3.dim("Current path: select a server, choose a task, submit a run, attach with native Pi, clear inbox gates."),
|
|
705
838
|
"",
|
|
706
839
|
...TOP_LEVEL_SECTIONS.flatMap((section) => [
|
|
707
|
-
`${
|
|
840
|
+
`${pc3.bold(pc3.magenta(`\u25C7 ${section.title}`))} \u2014 ${pc3.dim(section.subtitle)}`,
|
|
708
841
|
renderCommandBlock(section.commands),
|
|
709
842
|
""
|
|
710
843
|
]),
|
|
711
|
-
|
|
844
|
+
pc3.dim("More: `rig help --advanced` for dev/compatibility commands; `rig <group> --help` for rich per-group help; `rig --version` for the installed version."),
|
|
712
845
|
"",
|
|
713
|
-
|
|
846
|
+
pc3.bold("Global options"),
|
|
714
847
|
commandLine("--project <path>", "Use a project root instead of auto-discovery."),
|
|
715
848
|
commandLine("--json", "Emit structured output for scripts/agents."),
|
|
716
849
|
commandLine("--dry-run", "Print the command plan without mutating state.")
|
|
@@ -834,11 +967,11 @@ function formatStatsReport(stats) {
|
|
|
834
967
|
const keyWidth = Math.max(...rows.map(([key]) => key.length));
|
|
835
968
|
const lines = [
|
|
836
969
|
formatSection("Fleet stats", window),
|
|
837
|
-
...rows.map(([key, value]) => `${
|
|
970
|
+
...rows.map(([key, value]) => `${pc4.dim("\u2502")} ${pc4.dim(key.padEnd(keyWidth + 2))} ${value}`)
|
|
838
971
|
];
|
|
839
972
|
const otherStatuses = Object.entries(stats.statusCounts).filter(([status]) => !["completed", "failed", "needs-attention"].includes(status)).sort(([, left], [, right]) => right - left);
|
|
840
973
|
if (otherStatuses.length > 0) {
|
|
841
|
-
lines.push(`${
|
|
974
|
+
lines.push(`${pc4.dim("\u2502")} ${pc4.dim("other".padEnd(keyWidth + 2))} ${otherStatuses.map(([status, count]) => `${status}:${count}`).join(" ")}`);
|
|
842
975
|
}
|
|
843
976
|
lines.push("", ...formatNextSteps([
|
|
844
977
|
"Inspect a run: `rig run list` then `rig run show <run-id>`",
|
|
@@ -857,7 +990,8 @@ async function executeStats(context, args) {
|
|
|
857
990
|
const sinceResult = takeOption(pending, "--since");
|
|
858
991
|
requireNoExtraArgs(sinceResult.rest, "rig stats [show] [--since <7d|30d|ISO date>]");
|
|
859
992
|
const since = parseSinceOption(sinceResult.value);
|
|
860
|
-
const
|
|
993
|
+
const remoteStats = isRemoteConnectionSelected(context.projectRoot);
|
|
994
|
+
const stats = await withSpinner(remoteStats ? "Computing fleet stats on the server\u2026" : "Scanning local run state\u2026", async () => remoteStats ? await requestServerJson(context, `/api/stats${since ? `?since=${encodeURIComponent(since.toISOString())}` : ""}`) : computeRigStats(context.projectRoot, { since }), { outputMode: context.outputMode });
|
|
861
995
|
if (context.outputMode === "text") {
|
|
862
996
|
printFormattedOutput(formatStatsReport(stats));
|
|
863
997
|
}
|
|
@@ -543,6 +543,10 @@ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
|
543
543
|
|
|
544
544
|
// packages/cli/src/commands/_server-client.ts
|
|
545
545
|
var scopedGitHubBearerTokens = new Map;
|
|
546
|
+
var serverPhaseListener = null;
|
|
547
|
+
function reportServerPhase(label) {
|
|
548
|
+
serverPhaseListener?.(label);
|
|
549
|
+
}
|
|
546
550
|
function cleanToken(value) {
|
|
547
551
|
const trimmed = value?.trim();
|
|
548
552
|
return trimmed ? trimmed : null;
|
|
@@ -585,6 +589,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
585
589
|
try {
|
|
586
590
|
const selected = resolveSelectedConnection(projectRoot);
|
|
587
591
|
if (selected?.connection.kind === "remote") {
|
|
592
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
588
593
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
589
594
|
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
590
595
|
return {
|
|
@@ -594,6 +599,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
594
599
|
serverProjectRoot
|
|
595
600
|
};
|
|
596
601
|
}
|
|
602
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
597
603
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
598
604
|
return {
|
|
599
605
|
baseUrl: connection.baseUrl,
|
|
@@ -700,6 +706,7 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
700
706
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
701
707
|
if (server.serverProjectRoot)
|
|
702
708
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
709
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
703
710
|
let response;
|
|
704
711
|
try {
|
|
705
712
|
response = await fetch(`${server.baseUrl}${pathname}`, {
|