@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
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
|
|
5
|
+
// packages/cli/src/commands/_spinner.ts
|
|
6
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
7
|
+
function createTtySpinner(input) {
|
|
8
|
+
const output = input.output ?? process.stdout;
|
|
9
|
+
const isTty = output.isTTY === true;
|
|
10
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
11
|
+
let label = input.label;
|
|
12
|
+
let frame = 0;
|
|
13
|
+
let paused = false;
|
|
14
|
+
let stopped = false;
|
|
15
|
+
let lastPrintedLabel = "";
|
|
16
|
+
const render = () => {
|
|
17
|
+
if (stopped || paused)
|
|
18
|
+
return;
|
|
19
|
+
if (!isTty) {
|
|
20
|
+
if (label !== lastPrintedLabel) {
|
|
21
|
+
output.write(`${label}
|
|
22
|
+
`);
|
|
23
|
+
lastPrintedLabel = label;
|
|
24
|
+
}
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
frame = (frame + 1) % frames.length;
|
|
28
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
29
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
30
|
+
};
|
|
31
|
+
const clearLine = () => {
|
|
32
|
+
if (isTty)
|
|
33
|
+
output.write("\r\x1B[2K");
|
|
34
|
+
};
|
|
35
|
+
render();
|
|
36
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
|
|
37
|
+
return {
|
|
38
|
+
setLabel(next) {
|
|
39
|
+
label = next;
|
|
40
|
+
render();
|
|
41
|
+
},
|
|
42
|
+
pause() {
|
|
43
|
+
paused = true;
|
|
44
|
+
clearLine();
|
|
45
|
+
},
|
|
46
|
+
resume() {
|
|
47
|
+
if (stopped)
|
|
48
|
+
return;
|
|
49
|
+
paused = false;
|
|
50
|
+
render();
|
|
51
|
+
},
|
|
52
|
+
stop(finalLine) {
|
|
53
|
+
if (stopped)
|
|
54
|
+
return;
|
|
55
|
+
stopped = true;
|
|
56
|
+
if (timer)
|
|
57
|
+
clearInterval(timer);
|
|
58
|
+
clearLine();
|
|
59
|
+
if (finalLine)
|
|
60
|
+
output.write(`${finalLine}
|
|
61
|
+
`);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// packages/cli/src/runner.ts
|
|
67
|
+
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
68
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
69
|
+
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
70
|
+
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
71
|
+
|
|
72
|
+
// packages/cli/src/commands/_server-client.ts
|
|
73
|
+
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
74
|
+
var scopedGitHubBearerTokens = new Map;
|
|
75
|
+
var serverPhaseListener = null;
|
|
76
|
+
function setServerPhaseListener(listener) {
|
|
77
|
+
const previous = serverPhaseListener;
|
|
78
|
+
serverPhaseListener = listener;
|
|
79
|
+
return previous;
|
|
80
|
+
}
|
|
81
|
+
var serverReachabilityCache = new Map;
|
|
82
|
+
|
|
83
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
84
|
+
var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
85
|
+
var DONE_SYMBOL = pc.green("\u25C7");
|
|
86
|
+
var FAIL_SYMBOL = pc.red("\u25A0");
|
|
87
|
+
var activeUpdate = null;
|
|
88
|
+
async function withSpinner(label, work, options = {}) {
|
|
89
|
+
if (options.outputMode === "json") {
|
|
90
|
+
return work(() => {});
|
|
91
|
+
}
|
|
92
|
+
if (activeUpdate) {
|
|
93
|
+
const outer = activeUpdate;
|
|
94
|
+
outer(label);
|
|
95
|
+
return work(outer);
|
|
96
|
+
}
|
|
97
|
+
const output = options.output ?? process.stderr;
|
|
98
|
+
const isTty = output.isTTY === true;
|
|
99
|
+
let lastLabel = label;
|
|
100
|
+
if (!isTty) {
|
|
101
|
+
output.write(`${label}
|
|
102
|
+
`);
|
|
103
|
+
const update2 = (next) => {
|
|
104
|
+
lastLabel = next;
|
|
105
|
+
};
|
|
106
|
+
activeUpdate = update2;
|
|
107
|
+
const previousListener2 = setServerPhaseListener(update2);
|
|
108
|
+
try {
|
|
109
|
+
return await work(update2);
|
|
110
|
+
} finally {
|
|
111
|
+
activeUpdate = null;
|
|
112
|
+
setServerPhaseListener(previousListener2);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const spinner = createTtySpinner({
|
|
116
|
+
label,
|
|
117
|
+
output,
|
|
118
|
+
frames: CLACK_SPINNER_FRAMES,
|
|
119
|
+
styleFrame: (frame) => pc.magenta(frame)
|
|
120
|
+
});
|
|
121
|
+
const update = (next) => {
|
|
122
|
+
lastLabel = next;
|
|
123
|
+
spinner.setLabel(next);
|
|
124
|
+
};
|
|
125
|
+
activeUpdate = update;
|
|
126
|
+
const previousListener = setServerPhaseListener(update);
|
|
127
|
+
try {
|
|
128
|
+
const result = await work(update);
|
|
129
|
+
spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
|
|
130
|
+
return result;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
|
|
133
|
+
throw error;
|
|
134
|
+
} finally {
|
|
135
|
+
activeUpdate = null;
|
|
136
|
+
setServerPhaseListener(previousListener);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
withSpinner
|
|
141
|
+
};
|
|
@@ -126,6 +126,10 @@ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
|
126
126
|
import { resolve as resolve2 } from "path";
|
|
127
127
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
128
128
|
var scopedGitHubBearerTokens = new Map;
|
|
129
|
+
var serverPhaseListener = null;
|
|
130
|
+
function reportServerPhase(label) {
|
|
131
|
+
serverPhaseListener?.(label);
|
|
132
|
+
}
|
|
129
133
|
function cleanToken(value) {
|
|
130
134
|
const trimmed = value?.trim();
|
|
131
135
|
return trimmed ? trimmed : null;
|
|
@@ -168,6 +172,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
168
172
|
try {
|
|
169
173
|
const selected = resolveSelectedConnection(projectRoot);
|
|
170
174
|
if (selected?.connection.kind === "remote") {
|
|
175
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
171
176
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
172
177
|
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
173
178
|
return {
|
|
@@ -177,6 +182,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
177
182
|
serverProjectRoot
|
|
178
183
|
};
|
|
179
184
|
}
|
|
185
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
180
186
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
181
187
|
return {
|
|
182
188
|
baseUrl: connection.baseUrl,
|
|
@@ -283,6 +289,7 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
283
289
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
284
290
|
if (server.serverProjectRoot)
|
|
285
291
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
292
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
286
293
|
let response;
|
|
287
294
|
try {
|
|
288
295
|
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
@@ -514,7 +521,10 @@ async function runRigDoctorChecks(options) {
|
|
|
514
521
|
const bunVersion = options.bunVersion ?? Bun.version;
|
|
515
522
|
const request = options.requestJson ?? ((pathname, init) => requestServerJson({ projectRoot }, pathname, init));
|
|
516
523
|
const loadConfig = options.loadConfig ?? loadRigConfigOrNull;
|
|
524
|
+
const progress = options.onProgress ?? (() => {});
|
|
525
|
+
progress("Checking local toolchain\u2026");
|
|
517
526
|
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`)."));
|
|
527
|
+
progress("Loading rig.config\u2026");
|
|
518
528
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
519
529
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
520
530
|
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync4(resolve4(projectRoot, name)));
|
|
@@ -533,6 +543,7 @@ async function runRigDoctorChecks(options) {
|
|
|
533
543
|
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));
|
|
534
544
|
let server = null;
|
|
535
545
|
try {
|
|
546
|
+
progress("Connecting to the selected Rig server\u2026");
|
|
536
547
|
server = await (options.resolveServer ?? ensureServerForCli)(projectRoot);
|
|
537
548
|
checks.push(check("server", "Rig server reachable", "pass", `${server.connectionKind} ${server.baseUrl}`));
|
|
538
549
|
} catch (error) {
|
|
@@ -540,18 +551,21 @@ async function runRigDoctorChecks(options) {
|
|
|
540
551
|
}
|
|
541
552
|
if (server || options.requestJson) {
|
|
542
553
|
try {
|
|
554
|
+
progress("Checking server status\u2026");
|
|
543
555
|
const status = await request("/api/server/status");
|
|
544
556
|
checks.push(check("server-status", "server project status", "pass", JSON.stringify(status).slice(0, 180)));
|
|
545
557
|
} catch (error) {
|
|
546
558
|
checks.push(check("server-status", "server project status", "fail", errorMessage(error), "Run `rig doctor` after the selected server is reachable."));
|
|
547
559
|
}
|
|
548
560
|
try {
|
|
561
|
+
progress("Checking GitHub auth\u2026");
|
|
549
562
|
const auth = await request("/api/github/auth/status");
|
|
550
563
|
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>`."));
|
|
551
564
|
} catch (error) {
|
|
552
565
|
checks.push(check("github-auth", "GitHub auth", "fail", errorMessage(error), "Authenticate GitHub through Rig and ensure the server exposes auth status."));
|
|
553
566
|
}
|
|
554
567
|
try {
|
|
568
|
+
progress("Checking GitHub repo permissions\u2026");
|
|
555
569
|
const permissions = await request("/api/github/repo/permissions");
|
|
556
570
|
const allowed = permissionAllowsPr(permissions);
|
|
557
571
|
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."));
|
|
@@ -559,6 +573,7 @@ async function runRigDoctorChecks(options) {
|
|
|
559
573
|
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."));
|
|
560
574
|
}
|
|
561
575
|
try {
|
|
576
|
+
progress("Checking GitHub issue labels\u2026");
|
|
562
577
|
const labels = await request("/api/workspace/task-labels");
|
|
563
578
|
const ready = labelsReady(labels);
|
|
564
579
|
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."));
|
|
@@ -566,6 +581,7 @@ async function runRigDoctorChecks(options) {
|
|
|
566
581
|
checks.push(check("task-labels", "GitHub issue labels", "warn", errorMessage(error), "Run `rig init`/`rig doctor` after label setup is wired on the server."));
|
|
567
582
|
}
|
|
568
583
|
try {
|
|
584
|
+
progress("Checking task projection\u2026");
|
|
569
585
|
const projection = await request("/api/workspace/task-projection");
|
|
570
586
|
checks.push(check("task-projection", "task projection", "pass", JSON.stringify(projection).slice(0, 180)));
|
|
571
587
|
} catch (error) {
|
|
@@ -574,6 +590,7 @@ async function runRigDoctorChecks(options) {
|
|
|
574
590
|
const slug = projectStatusSlug(projectRoot, config);
|
|
575
591
|
if (slug) {
|
|
576
592
|
try {
|
|
593
|
+
progress("Checking server project checkout\u2026");
|
|
577
594
|
const project = await request(`/api/projects/${encodeURIComponent(slug)}`);
|
|
578
595
|
checks.push(check("remote-checkout", "server project checkout", "pass", JSON.stringify(project).slice(0, 180)));
|
|
579
596
|
} catch (error) {
|
|
@@ -588,6 +605,7 @@ async function runRigDoctorChecks(options) {
|
|
|
588
605
|
}
|
|
589
606
|
checks.push(githubProjectsCheck(config));
|
|
590
607
|
checks.push(prMergeCheck(config));
|
|
608
|
+
progress("Checking Pi installation\u2026");
|
|
591
609
|
const piChecks = await (options.piChecks ?? (() => buildPiSetupChecks()))().catch((error) => [{
|
|
592
610
|
ok: false,
|
|
593
611
|
label: "pi/pi-rig checks",
|
|
@@ -121,6 +121,15 @@ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
|
121
121
|
|
|
122
122
|
// packages/cli/src/commands/_server-client.ts
|
|
123
123
|
var scopedGitHubBearerTokens = new Map;
|
|
124
|
+
var serverPhaseListener = null;
|
|
125
|
+
function setServerPhaseListener(listener) {
|
|
126
|
+
const previous = serverPhaseListener;
|
|
127
|
+
serverPhaseListener = listener;
|
|
128
|
+
return previous;
|
|
129
|
+
}
|
|
130
|
+
function reportServerPhase(label) {
|
|
131
|
+
serverPhaseListener?.(label);
|
|
132
|
+
}
|
|
124
133
|
function cleanToken(value) {
|
|
125
134
|
const trimmed = value?.trim();
|
|
126
135
|
return trimmed ? trimmed : null;
|
|
@@ -163,6 +172,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
163
172
|
try {
|
|
164
173
|
const selected = resolveSelectedConnection(projectRoot);
|
|
165
174
|
if (selected?.connection.kind === "remote") {
|
|
175
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
166
176
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
167
177
|
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
168
178
|
return {
|
|
@@ -172,6 +182,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
172
182
|
serverProjectRoot
|
|
173
183
|
};
|
|
174
184
|
}
|
|
185
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
175
186
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
176
187
|
return {
|
|
177
188
|
baseUrl: connection.baseUrl,
|
|
@@ -278,6 +289,7 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
278
289
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
279
290
|
if (server.serverProjectRoot)
|
|
280
291
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
292
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
281
293
|
let response;
|
|
282
294
|
try {
|
|
283
295
|
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
@@ -592,7 +604,12 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
592
604
|
};
|
|
593
605
|
let detached = false;
|
|
594
606
|
try {
|
|
595
|
-
await runPiMain([
|
|
607
|
+
await runPiMain([
|
|
608
|
+
"--no-extensions",
|
|
609
|
+
"--no-skills",
|
|
610
|
+
"--no-prompt-templates",
|
|
611
|
+
"--no-context-files"
|
|
612
|
+
], {
|
|
596
613
|
extensionFactories: [piRigExtensionFactory]
|
|
597
614
|
});
|
|
598
615
|
detached = true;
|
|
@@ -615,6 +632,127 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
615
632
|
};
|
|
616
633
|
}
|
|
617
634
|
|
|
635
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
636
|
+
import pc from "picocolors";
|
|
637
|
+
|
|
638
|
+
// packages/cli/src/commands/_spinner.ts
|
|
639
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
640
|
+
function createTtySpinner(input) {
|
|
641
|
+
const output = input.output ?? process.stdout;
|
|
642
|
+
const isTty = output.isTTY === true;
|
|
643
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
644
|
+
let label = input.label;
|
|
645
|
+
let frame = 0;
|
|
646
|
+
let paused = false;
|
|
647
|
+
let stopped = false;
|
|
648
|
+
let lastPrintedLabel = "";
|
|
649
|
+
const render = () => {
|
|
650
|
+
if (stopped || paused)
|
|
651
|
+
return;
|
|
652
|
+
if (!isTty) {
|
|
653
|
+
if (label !== lastPrintedLabel) {
|
|
654
|
+
output.write(`${label}
|
|
655
|
+
`);
|
|
656
|
+
lastPrintedLabel = label;
|
|
657
|
+
}
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
frame = (frame + 1) % frames.length;
|
|
661
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
662
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
663
|
+
};
|
|
664
|
+
const clearLine = () => {
|
|
665
|
+
if (isTty)
|
|
666
|
+
output.write("\r\x1B[2K");
|
|
667
|
+
};
|
|
668
|
+
render();
|
|
669
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
|
|
670
|
+
return {
|
|
671
|
+
setLabel(next) {
|
|
672
|
+
label = next;
|
|
673
|
+
render();
|
|
674
|
+
},
|
|
675
|
+
pause() {
|
|
676
|
+
paused = true;
|
|
677
|
+
clearLine();
|
|
678
|
+
},
|
|
679
|
+
resume() {
|
|
680
|
+
if (stopped)
|
|
681
|
+
return;
|
|
682
|
+
paused = false;
|
|
683
|
+
render();
|
|
684
|
+
},
|
|
685
|
+
stop(finalLine) {
|
|
686
|
+
if (stopped)
|
|
687
|
+
return;
|
|
688
|
+
stopped = true;
|
|
689
|
+
if (timer)
|
|
690
|
+
clearInterval(timer);
|
|
691
|
+
clearLine();
|
|
692
|
+
if (finalLine)
|
|
693
|
+
output.write(`${finalLine}
|
|
694
|
+
`);
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
700
|
+
var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
701
|
+
var DONE_SYMBOL = pc.green("\u25C7");
|
|
702
|
+
var FAIL_SYMBOL = pc.red("\u25A0");
|
|
703
|
+
var activeUpdate = null;
|
|
704
|
+
async function withSpinner(label, work, options = {}) {
|
|
705
|
+
if (options.outputMode === "json") {
|
|
706
|
+
return work(() => {});
|
|
707
|
+
}
|
|
708
|
+
if (activeUpdate) {
|
|
709
|
+
const outer = activeUpdate;
|
|
710
|
+
outer(label);
|
|
711
|
+
return work(outer);
|
|
712
|
+
}
|
|
713
|
+
const output = options.output ?? process.stderr;
|
|
714
|
+
const isTty = output.isTTY === true;
|
|
715
|
+
let lastLabel = label;
|
|
716
|
+
if (!isTty) {
|
|
717
|
+
output.write(`${label}
|
|
718
|
+
`);
|
|
719
|
+
const update2 = (next) => {
|
|
720
|
+
lastLabel = next;
|
|
721
|
+
};
|
|
722
|
+
activeUpdate = update2;
|
|
723
|
+
const previousListener2 = setServerPhaseListener(update2);
|
|
724
|
+
try {
|
|
725
|
+
return await work(update2);
|
|
726
|
+
} finally {
|
|
727
|
+
activeUpdate = null;
|
|
728
|
+
setServerPhaseListener(previousListener2);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
const spinner = createTtySpinner({
|
|
732
|
+
label,
|
|
733
|
+
output,
|
|
734
|
+
frames: CLACK_SPINNER_FRAMES,
|
|
735
|
+
styleFrame: (frame) => pc.magenta(frame)
|
|
736
|
+
});
|
|
737
|
+
const update = (next) => {
|
|
738
|
+
lastLabel = next;
|
|
739
|
+
spinner.setLabel(next);
|
|
740
|
+
};
|
|
741
|
+
activeUpdate = update;
|
|
742
|
+
const previousListener = setServerPhaseListener(update);
|
|
743
|
+
try {
|
|
744
|
+
const result = await work(update);
|
|
745
|
+
spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
|
|
746
|
+
return result;
|
|
747
|
+
} catch (error) {
|
|
748
|
+
spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
|
|
749
|
+
throw error;
|
|
750
|
+
} finally {
|
|
751
|
+
activeUpdate = null;
|
|
752
|
+
setServerPhaseListener(previousListener);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
618
756
|
// packages/cli/src/commands/_operator-view.ts
|
|
619
757
|
var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
|
|
620
758
|
function runStatusFromPayload(payload) {
|
|
@@ -657,8 +795,9 @@ async function readOperatorSnapshot(context, runId, options = {}) {
|
|
|
657
795
|
}
|
|
658
796
|
async function attachRunOperatorView(context, input) {
|
|
659
797
|
let steered = false;
|
|
660
|
-
|
|
661
|
-
|
|
798
|
+
const attachMessage = input.message?.trim();
|
|
799
|
+
if (attachMessage) {
|
|
800
|
+
await withSpinner("Queueing steering message\u2026", () => sendRunPiPromptViaServer(context, input.runId, attachMessage, "steer").catch(() => steerRunViaServer(context, input.runId, attachMessage)), { outputMode: context.outputMode });
|
|
662
801
|
steered = true;
|
|
663
802
|
}
|
|
664
803
|
if (input.follow && !input.once && input.interactive !== false && context.outputMode === "text" && Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
@@ -668,7 +807,7 @@ async function attachRunOperatorView(context, input) {
|
|
|
668
807
|
});
|
|
669
808
|
}
|
|
670
809
|
const surface = createOperatorSurface({ interactive: input.interactive !== false });
|
|
671
|
-
let snapshot = await readOperatorSnapshot(context, input.runId);
|
|
810
|
+
let snapshot = await withSpinner(`Connecting to run ${input.runId}\u2026`, () => readOperatorSnapshot(context, input.runId), { outputMode: context.outputMode });
|
|
672
811
|
if (context.outputMode === "text") {
|
|
673
812
|
surface.renderSnapshot(snapshot);
|
|
674
813
|
surface.renderTimeline(snapshot.timeline);
|
|
@@ -128,6 +128,10 @@ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
|
128
128
|
|
|
129
129
|
// packages/cli/src/commands/_server-client.ts
|
|
130
130
|
var scopedGitHubBearerTokens = new Map;
|
|
131
|
+
var serverPhaseListener = null;
|
|
132
|
+
function reportServerPhase(label) {
|
|
133
|
+
serverPhaseListener?.(label);
|
|
134
|
+
}
|
|
131
135
|
function cleanToken(value) {
|
|
132
136
|
const trimmed = value?.trim();
|
|
133
137
|
return trimmed ? trimmed : null;
|
|
@@ -170,6 +174,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
170
174
|
try {
|
|
171
175
|
const selected = resolveSelectedConnection(projectRoot);
|
|
172
176
|
if (selected?.connection.kind === "remote") {
|
|
177
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
173
178
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
174
179
|
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
175
180
|
return {
|
|
@@ -179,6 +184,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
179
184
|
serverProjectRoot
|
|
180
185
|
};
|
|
181
186
|
}
|
|
187
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
182
188
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
183
189
|
return {
|
|
184
190
|
baseUrl: connection.baseUrl,
|
|
@@ -285,6 +291,7 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
285
291
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
286
292
|
if (server.serverProjectRoot)
|
|
287
293
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
294
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
288
295
|
let response;
|
|
289
296
|
try {
|
|
290
297
|
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
@@ -360,7 +367,12 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
360
367
|
};
|
|
361
368
|
let detached = false;
|
|
362
369
|
try {
|
|
363
|
-
await runPiMain([
|
|
370
|
+
await runPiMain([
|
|
371
|
+
"--no-extensions",
|
|
372
|
+
"--no-skills",
|
|
373
|
+
"--no-prompt-templates",
|
|
374
|
+
"--no-context-files"
|
|
375
|
+
], {
|
|
364
376
|
extensionFactories: [piRigExtensionFactory]
|
|
365
377
|
});
|
|
366
378
|
detached = true;
|
|
@@ -120,6 +120,10 @@ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
|
120
120
|
import { resolve as resolve2 } from "path";
|
|
121
121
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
122
122
|
var scopedGitHubBearerTokens = new Map;
|
|
123
|
+
var serverPhaseListener = null;
|
|
124
|
+
function reportServerPhase(label) {
|
|
125
|
+
serverPhaseListener?.(label);
|
|
126
|
+
}
|
|
123
127
|
function cleanToken(value) {
|
|
124
128
|
const trimmed = value?.trim();
|
|
125
129
|
return trimmed ? trimmed : null;
|
|
@@ -162,6 +166,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
162
166
|
try {
|
|
163
167
|
const selected = resolveSelectedConnection(projectRoot);
|
|
164
168
|
if (selected?.connection.kind === "remote") {
|
|
169
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
165
170
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
166
171
|
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
167
172
|
return {
|
|
@@ -171,6 +176,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
171
176
|
serverProjectRoot
|
|
172
177
|
};
|
|
173
178
|
}
|
|
179
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
174
180
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
175
181
|
return {
|
|
176
182
|
baseUrl: connection.baseUrl,
|
|
@@ -277,6 +283,7 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
277
283
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
278
284
|
if (server.serverProjectRoot)
|
|
279
285
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
286
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
280
287
|
let response;
|
|
281
288
|
try {
|
|
282
289
|
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
@@ -121,6 +121,15 @@ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
|
121
121
|
|
|
122
122
|
// packages/cli/src/commands/_server-client.ts
|
|
123
123
|
var scopedGitHubBearerTokens = new Map;
|
|
124
|
+
var serverPhaseListener = null;
|
|
125
|
+
function setServerPhaseListener(listener) {
|
|
126
|
+
const previous = serverPhaseListener;
|
|
127
|
+
serverPhaseListener = listener;
|
|
128
|
+
return previous;
|
|
129
|
+
}
|
|
130
|
+
function reportServerPhase(label) {
|
|
131
|
+
serverPhaseListener?.(label);
|
|
132
|
+
}
|
|
124
133
|
function cleanToken(value) {
|
|
125
134
|
const trimmed = value?.trim();
|
|
126
135
|
return trimmed ? trimmed : null;
|
|
@@ -167,6 +176,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
167
176
|
try {
|
|
168
177
|
const selected = resolveSelectedConnection(projectRoot);
|
|
169
178
|
if (selected?.connection.kind === "remote") {
|
|
179
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
170
180
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
171
181
|
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
172
182
|
return {
|
|
@@ -176,6 +186,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
176
186
|
serverProjectRoot
|
|
177
187
|
};
|
|
178
188
|
}
|
|
189
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
179
190
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
180
191
|
return {
|
|
181
192
|
baseUrl: connection.baseUrl,
|
|
@@ -292,6 +303,7 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
292
303
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
293
304
|
if (server.serverProjectRoot)
|
|
294
305
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
306
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
295
307
|
let response;
|
|
296
308
|
try {
|
|
297
309
|
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
@@ -588,6 +600,7 @@ export {
|
|
|
588
600
|
submitTaskRunViaServer,
|
|
589
601
|
stopRunViaServer,
|
|
590
602
|
steerRunViaServer,
|
|
603
|
+
setServerPhaseListener,
|
|
591
604
|
setGitHubBearerTokenForCurrentProcess,
|
|
592
605
|
sendRunPiShellViaServer,
|
|
593
606
|
sendRunPiPromptViaServer,
|
|
@@ -125,6 +125,10 @@ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
|
125
125
|
|
|
126
126
|
// packages/cli/src/commands/_server-client.ts
|
|
127
127
|
var scopedGitHubBearerTokens = new Map;
|
|
128
|
+
var serverPhaseListener = null;
|
|
129
|
+
function reportServerPhase(label) {
|
|
130
|
+
serverPhaseListener?.(label);
|
|
131
|
+
}
|
|
128
132
|
function cleanToken(value) {
|
|
129
133
|
const trimmed = value?.trim();
|
|
130
134
|
return trimmed ? trimmed : null;
|
|
@@ -167,6 +171,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
167
171
|
try {
|
|
168
172
|
const selected = resolveSelectedConnection(projectRoot);
|
|
169
173
|
if (selected?.connection.kind === "remote") {
|
|
174
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
170
175
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
171
176
|
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
172
177
|
return {
|
|
@@ -176,6 +181,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
176
181
|
serverProjectRoot
|
|
177
182
|
};
|
|
178
183
|
}
|
|
184
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
179
185
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
180
186
|
return {
|
|
181
187
|
baseUrl: connection.baseUrl,
|
|
@@ -282,6 +288,7 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
282
288
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
283
289
|
if (server.serverProjectRoot)
|
|
284
290
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
291
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
285
292
|
let response;
|
|
286
293
|
try {
|
|
287
294
|
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
@@ -4,6 +4,7 @@ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834"
|
|
|
4
4
|
function createTtySpinner(input) {
|
|
5
5
|
const output = input.output ?? process.stdout;
|
|
6
6
|
const isTty = output.isTTY === true;
|
|
7
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
7
8
|
let label = input.label;
|
|
8
9
|
let frame = 0;
|
|
9
10
|
let paused = false;
|
|
@@ -20,8 +21,9 @@ function createTtySpinner(input) {
|
|
|
20
21
|
}
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
23
|
-
frame = (frame + 1) %
|
|
24
|
-
|
|
24
|
+
frame = (frame + 1) % frames.length;
|
|
25
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
26
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
25
27
|
};
|
|
26
28
|
const clearLine = () => {
|
|
27
29
|
if (isTty)
|