@h-rig/cli 0.0.6-alpha.67 → 0.0.6-alpha.69
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 +266 -75
- package/dist/src/commands/_async-ui.js +152 -0
- package/dist/src/commands/_doctor-checks.js +29 -0
- package/dist/src/commands/_operator-view.js +154 -4
- package/dist/src/commands/_pi-frontend.js +24 -1
- package/dist/src/commands/_preflight.js +18 -0
- package/dist/src/commands/_server-client.js +46 -0
- package/dist/src/commands/_snapshot-upload.js +18 -0
- package/dist/src/commands/_spinner.js +4 -2
- package/dist/src/commands/agent.js +11 -0
- package/dist/src/commands/doctor.js +156 -1
- package/dist/src/commands/github.js +148 -3
- package/dist/src/commands/inbox.js +150 -6
- package/dist/src/commands/init.js +29 -0
- package/dist/src/commands/inspect.js +158 -8
- package/dist/src/commands/queue.js +11 -0
- package/dist/src/commands/run.js +207 -35
- package/dist/src/commands/server.js +18 -0
- package/dist/src/commands/setup.js +161 -6
- package/dist/src/commands/stats.js +167 -22
- package/dist/src/commands/task-run-driver.js +18 -0
- package/dist/src/commands/task.js +197 -47
- package/dist/src/commands.js +266 -75
- package/dist/src/index.js +266 -75
- package/package.json +8 -8
|
@@ -0,0 +1,152 @@
|
|
|
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
|
+
var RESUMABLE_RUN_STATUSES = new Set([
|
|
83
|
+
"created",
|
|
84
|
+
"preparing",
|
|
85
|
+
"running",
|
|
86
|
+
"validating",
|
|
87
|
+
"reviewing",
|
|
88
|
+
"stopped",
|
|
89
|
+
"failed",
|
|
90
|
+
"needs-attention",
|
|
91
|
+
"needs_attention"
|
|
92
|
+
]);
|
|
93
|
+
|
|
94
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
95
|
+
var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
96
|
+
var DONE_SYMBOL = pc.green("\u25C7");
|
|
97
|
+
var FAIL_SYMBOL = pc.red("\u25A0");
|
|
98
|
+
var activeUpdate = null;
|
|
99
|
+
async function withSpinner(label, work, options = {}) {
|
|
100
|
+
if (options.outputMode === "json") {
|
|
101
|
+
return work(() => {});
|
|
102
|
+
}
|
|
103
|
+
if (activeUpdate) {
|
|
104
|
+
const outer = activeUpdate;
|
|
105
|
+
outer(label);
|
|
106
|
+
return work(outer);
|
|
107
|
+
}
|
|
108
|
+
const output = options.output ?? process.stderr;
|
|
109
|
+
const isTty = output.isTTY === true;
|
|
110
|
+
let lastLabel = label;
|
|
111
|
+
if (!isTty) {
|
|
112
|
+
output.write(`${label}
|
|
113
|
+
`);
|
|
114
|
+
const update2 = (next) => {
|
|
115
|
+
lastLabel = next;
|
|
116
|
+
};
|
|
117
|
+
activeUpdate = update2;
|
|
118
|
+
const previousListener2 = setServerPhaseListener(update2);
|
|
119
|
+
try {
|
|
120
|
+
return await work(update2);
|
|
121
|
+
} finally {
|
|
122
|
+
activeUpdate = null;
|
|
123
|
+
setServerPhaseListener(previousListener2);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const spinner = createTtySpinner({
|
|
127
|
+
label,
|
|
128
|
+
output,
|
|
129
|
+
frames: CLACK_SPINNER_FRAMES,
|
|
130
|
+
styleFrame: (frame) => pc.magenta(frame)
|
|
131
|
+
});
|
|
132
|
+
const update = (next) => {
|
|
133
|
+
lastLabel = next;
|
|
134
|
+
spinner.setLabel(next);
|
|
135
|
+
};
|
|
136
|
+
activeUpdate = update;
|
|
137
|
+
const previousListener = setServerPhaseListener(update);
|
|
138
|
+
try {
|
|
139
|
+
const result = await work(update);
|
|
140
|
+
spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
|
|
141
|
+
return result;
|
|
142
|
+
} catch (error) {
|
|
143
|
+
spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
|
|
144
|
+
throw error;
|
|
145
|
+
} finally {
|
|
146
|
+
activeUpdate = null;
|
|
147
|
+
setServerPhaseListener(previousListener);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export {
|
|
151
|
+
withSpinner
|
|
152
|
+
};
|
|
@@ -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}`, {
|
|
@@ -311,6 +318,17 @@ ${failure.contextLine}`, 1, { hint: failure.hint });
|
|
|
311
318
|
}
|
|
312
319
|
return payload;
|
|
313
320
|
}
|
|
321
|
+
var RESUMABLE_RUN_STATUSES = new Set([
|
|
322
|
+
"created",
|
|
323
|
+
"preparing",
|
|
324
|
+
"running",
|
|
325
|
+
"validating",
|
|
326
|
+
"reviewing",
|
|
327
|
+
"stopped",
|
|
328
|
+
"failed",
|
|
329
|
+
"needs-attention",
|
|
330
|
+
"needs_attention"
|
|
331
|
+
]);
|
|
314
332
|
|
|
315
333
|
// packages/cli/src/commands/_parsers.ts
|
|
316
334
|
async function loadRigConfigOrNull(projectRoot) {
|
|
@@ -514,7 +532,10 @@ async function runRigDoctorChecks(options) {
|
|
|
514
532
|
const bunVersion = options.bunVersion ?? Bun.version;
|
|
515
533
|
const request = options.requestJson ?? ((pathname, init) => requestServerJson({ projectRoot }, pathname, init));
|
|
516
534
|
const loadConfig = options.loadConfig ?? loadRigConfigOrNull;
|
|
535
|
+
const progress = options.onProgress ?? (() => {});
|
|
536
|
+
progress("Checking local toolchain\u2026");
|
|
517
537
|
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`)."));
|
|
538
|
+
progress("Loading rig.config\u2026");
|
|
518
539
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
519
540
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
520
541
|
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync4(resolve4(projectRoot, name)));
|
|
@@ -533,6 +554,7 @@ async function runRigDoctorChecks(options) {
|
|
|
533
554
|
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
555
|
let server = null;
|
|
535
556
|
try {
|
|
557
|
+
progress("Connecting to the selected Rig server\u2026");
|
|
536
558
|
server = await (options.resolveServer ?? ensureServerForCli)(projectRoot);
|
|
537
559
|
checks.push(check("server", "Rig server reachable", "pass", `${server.connectionKind} ${server.baseUrl}`));
|
|
538
560
|
} catch (error) {
|
|
@@ -540,18 +562,21 @@ async function runRigDoctorChecks(options) {
|
|
|
540
562
|
}
|
|
541
563
|
if (server || options.requestJson) {
|
|
542
564
|
try {
|
|
565
|
+
progress("Checking server status\u2026");
|
|
543
566
|
const status = await request("/api/server/status");
|
|
544
567
|
checks.push(check("server-status", "server project status", "pass", JSON.stringify(status).slice(0, 180)));
|
|
545
568
|
} catch (error) {
|
|
546
569
|
checks.push(check("server-status", "server project status", "fail", errorMessage(error), "Run `rig doctor` after the selected server is reachable."));
|
|
547
570
|
}
|
|
548
571
|
try {
|
|
572
|
+
progress("Checking GitHub auth\u2026");
|
|
549
573
|
const auth = await request("/api/github/auth/status");
|
|
550
574
|
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
575
|
} catch (error) {
|
|
552
576
|
checks.push(check("github-auth", "GitHub auth", "fail", errorMessage(error), "Authenticate GitHub through Rig and ensure the server exposes auth status."));
|
|
553
577
|
}
|
|
554
578
|
try {
|
|
579
|
+
progress("Checking GitHub repo permissions\u2026");
|
|
555
580
|
const permissions = await request("/api/github/repo/permissions");
|
|
556
581
|
const allowed = permissionAllowsPr(permissions);
|
|
557
582
|
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 +584,7 @@ async function runRigDoctorChecks(options) {
|
|
|
559
584
|
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
585
|
}
|
|
561
586
|
try {
|
|
587
|
+
progress("Checking GitHub issue labels\u2026");
|
|
562
588
|
const labels = await request("/api/workspace/task-labels");
|
|
563
589
|
const ready = labelsReady(labels);
|
|
564
590
|
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 +592,7 @@ async function runRigDoctorChecks(options) {
|
|
|
566
592
|
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
593
|
}
|
|
568
594
|
try {
|
|
595
|
+
progress("Checking task projection\u2026");
|
|
569
596
|
const projection = await request("/api/workspace/task-projection");
|
|
570
597
|
checks.push(check("task-projection", "task projection", "pass", JSON.stringify(projection).slice(0, 180)));
|
|
571
598
|
} catch (error) {
|
|
@@ -574,6 +601,7 @@ async function runRigDoctorChecks(options) {
|
|
|
574
601
|
const slug = projectStatusSlug(projectRoot, config);
|
|
575
602
|
if (slug) {
|
|
576
603
|
try {
|
|
604
|
+
progress("Checking server project checkout\u2026");
|
|
577
605
|
const project = await request(`/api/projects/${encodeURIComponent(slug)}`);
|
|
578
606
|
checks.push(check("remote-checkout", "server project checkout", "pass", JSON.stringify(project).slice(0, 180)));
|
|
579
607
|
} catch (error) {
|
|
@@ -588,6 +616,7 @@ async function runRigDoctorChecks(options) {
|
|
|
588
616
|
}
|
|
589
617
|
checks.push(githubProjectsCheck(config));
|
|
590
618
|
checks.push(prMergeCheck(config));
|
|
619
|
+
progress("Checking Pi installation\u2026");
|
|
591
620
|
const piChecks = await (options.piChecks ?? (() => buildPiSetupChecks()))().catch((error) => [{
|
|
592
621
|
ok: false,
|
|
593
622
|
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}`, {
|
|
@@ -328,6 +340,17 @@ async function getRunTimelineViaServer(context, runId, options = {}) {
|
|
|
328
340
|
const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
|
|
329
341
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { entries: [] };
|
|
330
342
|
}
|
|
343
|
+
var RESUMABLE_RUN_STATUSES = new Set([
|
|
344
|
+
"created",
|
|
345
|
+
"preparing",
|
|
346
|
+
"running",
|
|
347
|
+
"validating",
|
|
348
|
+
"reviewing",
|
|
349
|
+
"stopped",
|
|
350
|
+
"failed",
|
|
351
|
+
"needs-attention",
|
|
352
|
+
"needs_attention"
|
|
353
|
+
]);
|
|
331
354
|
async function stopRunViaServer(context, runId) {
|
|
332
355
|
const payload = await requestServerJson(context, "/api/runs/stop", {
|
|
333
356
|
method: "POST",
|
|
@@ -592,7 +615,12 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
592
615
|
};
|
|
593
616
|
let detached = false;
|
|
594
617
|
try {
|
|
595
|
-
await runPiMain([
|
|
618
|
+
await runPiMain([
|
|
619
|
+
"--no-extensions",
|
|
620
|
+
"--no-skills",
|
|
621
|
+
"--no-prompt-templates",
|
|
622
|
+
"--no-context-files"
|
|
623
|
+
], {
|
|
596
624
|
extensionFactories: [piRigExtensionFactory]
|
|
597
625
|
});
|
|
598
626
|
detached = true;
|
|
@@ -615,6 +643,127 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
615
643
|
};
|
|
616
644
|
}
|
|
617
645
|
|
|
646
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
647
|
+
import pc from "picocolors";
|
|
648
|
+
|
|
649
|
+
// packages/cli/src/commands/_spinner.ts
|
|
650
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
651
|
+
function createTtySpinner(input) {
|
|
652
|
+
const output = input.output ?? process.stdout;
|
|
653
|
+
const isTty = output.isTTY === true;
|
|
654
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
655
|
+
let label = input.label;
|
|
656
|
+
let frame = 0;
|
|
657
|
+
let paused = false;
|
|
658
|
+
let stopped = false;
|
|
659
|
+
let lastPrintedLabel = "";
|
|
660
|
+
const render = () => {
|
|
661
|
+
if (stopped || paused)
|
|
662
|
+
return;
|
|
663
|
+
if (!isTty) {
|
|
664
|
+
if (label !== lastPrintedLabel) {
|
|
665
|
+
output.write(`${label}
|
|
666
|
+
`);
|
|
667
|
+
lastPrintedLabel = label;
|
|
668
|
+
}
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
frame = (frame + 1) % frames.length;
|
|
672
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
673
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
674
|
+
};
|
|
675
|
+
const clearLine = () => {
|
|
676
|
+
if (isTty)
|
|
677
|
+
output.write("\r\x1B[2K");
|
|
678
|
+
};
|
|
679
|
+
render();
|
|
680
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
|
|
681
|
+
return {
|
|
682
|
+
setLabel(next) {
|
|
683
|
+
label = next;
|
|
684
|
+
render();
|
|
685
|
+
},
|
|
686
|
+
pause() {
|
|
687
|
+
paused = true;
|
|
688
|
+
clearLine();
|
|
689
|
+
},
|
|
690
|
+
resume() {
|
|
691
|
+
if (stopped)
|
|
692
|
+
return;
|
|
693
|
+
paused = false;
|
|
694
|
+
render();
|
|
695
|
+
},
|
|
696
|
+
stop(finalLine) {
|
|
697
|
+
if (stopped)
|
|
698
|
+
return;
|
|
699
|
+
stopped = true;
|
|
700
|
+
if (timer)
|
|
701
|
+
clearInterval(timer);
|
|
702
|
+
clearLine();
|
|
703
|
+
if (finalLine)
|
|
704
|
+
output.write(`${finalLine}
|
|
705
|
+
`);
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
711
|
+
var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
712
|
+
var DONE_SYMBOL = pc.green("\u25C7");
|
|
713
|
+
var FAIL_SYMBOL = pc.red("\u25A0");
|
|
714
|
+
var activeUpdate = null;
|
|
715
|
+
async function withSpinner(label, work, options = {}) {
|
|
716
|
+
if (options.outputMode === "json") {
|
|
717
|
+
return work(() => {});
|
|
718
|
+
}
|
|
719
|
+
if (activeUpdate) {
|
|
720
|
+
const outer = activeUpdate;
|
|
721
|
+
outer(label);
|
|
722
|
+
return work(outer);
|
|
723
|
+
}
|
|
724
|
+
const output = options.output ?? process.stderr;
|
|
725
|
+
const isTty = output.isTTY === true;
|
|
726
|
+
let lastLabel = label;
|
|
727
|
+
if (!isTty) {
|
|
728
|
+
output.write(`${label}
|
|
729
|
+
`);
|
|
730
|
+
const update2 = (next) => {
|
|
731
|
+
lastLabel = next;
|
|
732
|
+
};
|
|
733
|
+
activeUpdate = update2;
|
|
734
|
+
const previousListener2 = setServerPhaseListener(update2);
|
|
735
|
+
try {
|
|
736
|
+
return await work(update2);
|
|
737
|
+
} finally {
|
|
738
|
+
activeUpdate = null;
|
|
739
|
+
setServerPhaseListener(previousListener2);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
const spinner = createTtySpinner({
|
|
743
|
+
label,
|
|
744
|
+
output,
|
|
745
|
+
frames: CLACK_SPINNER_FRAMES,
|
|
746
|
+
styleFrame: (frame) => pc.magenta(frame)
|
|
747
|
+
});
|
|
748
|
+
const update = (next) => {
|
|
749
|
+
lastLabel = next;
|
|
750
|
+
spinner.setLabel(next);
|
|
751
|
+
};
|
|
752
|
+
activeUpdate = update;
|
|
753
|
+
const previousListener = setServerPhaseListener(update);
|
|
754
|
+
try {
|
|
755
|
+
const result = await work(update);
|
|
756
|
+
spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
|
|
757
|
+
return result;
|
|
758
|
+
} catch (error) {
|
|
759
|
+
spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
|
|
760
|
+
throw error;
|
|
761
|
+
} finally {
|
|
762
|
+
activeUpdate = null;
|
|
763
|
+
setServerPhaseListener(previousListener);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
618
767
|
// packages/cli/src/commands/_operator-view.ts
|
|
619
768
|
var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
|
|
620
769
|
function runStatusFromPayload(payload) {
|
|
@@ -657,8 +806,9 @@ async function readOperatorSnapshot(context, runId, options = {}) {
|
|
|
657
806
|
}
|
|
658
807
|
async function attachRunOperatorView(context, input) {
|
|
659
808
|
let steered = false;
|
|
660
|
-
|
|
661
|
-
|
|
809
|
+
const attachMessage = input.message?.trim();
|
|
810
|
+
if (attachMessage) {
|
|
811
|
+
await withSpinner("Queueing steering message\u2026", () => sendRunPiPromptViaServer(context, input.runId, attachMessage, "steer").catch(() => steerRunViaServer(context, input.runId, attachMessage)), { outputMode: context.outputMode });
|
|
662
812
|
steered = true;
|
|
663
813
|
}
|
|
664
814
|
if (input.follow && !input.once && input.interactive !== false && context.outputMode === "text" && Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
@@ -668,7 +818,7 @@ async function attachRunOperatorView(context, input) {
|
|
|
668
818
|
});
|
|
669
819
|
}
|
|
670
820
|
const surface = createOperatorSurface({ interactive: input.interactive !== false });
|
|
671
|
-
let snapshot = await readOperatorSnapshot(context, input.runId);
|
|
821
|
+
let snapshot = await withSpinner(`Connecting to run ${input.runId}\u2026`, () => readOperatorSnapshot(context, input.runId), { outputMode: context.outputMode });
|
|
672
822
|
if (context.outputMode === "text") {
|
|
673
823
|
surface.renderSnapshot(snapshot);
|
|
674
824
|
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}`, {
|
|
@@ -317,6 +324,17 @@ async function getRunDetailsViaServer(context, runId) {
|
|
|
317
324
|
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}`);
|
|
318
325
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
319
326
|
}
|
|
327
|
+
var RESUMABLE_RUN_STATUSES = new Set([
|
|
328
|
+
"created",
|
|
329
|
+
"preparing",
|
|
330
|
+
"running",
|
|
331
|
+
"validating",
|
|
332
|
+
"reviewing",
|
|
333
|
+
"stopped",
|
|
334
|
+
"failed",
|
|
335
|
+
"needs-attention",
|
|
336
|
+
"needs_attention"
|
|
337
|
+
]);
|
|
320
338
|
|
|
321
339
|
// packages/cli/src/commands/_pi-frontend.ts
|
|
322
340
|
function setTemporaryEnv(updates) {
|
|
@@ -360,7 +378,12 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
360
378
|
};
|
|
361
379
|
let detached = false;
|
|
362
380
|
try {
|
|
363
|
-
await runPiMain([
|
|
381
|
+
await runPiMain([
|
|
382
|
+
"--no-extensions",
|
|
383
|
+
"--no-skills",
|
|
384
|
+
"--no-prompt-templates",
|
|
385
|
+
"--no-context-files"
|
|
386
|
+
], {
|
|
364
387
|
extensionFactories: [piRigExtensionFactory]
|
|
365
388
|
});
|
|
366
389
|
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}`, {
|
|
@@ -305,6 +312,17 @@ ${failure.contextLine}`, 1, { hint: failure.hint });
|
|
|
305
312
|
}
|
|
306
313
|
return payload;
|
|
307
314
|
}
|
|
315
|
+
var RESUMABLE_RUN_STATUSES = new Set([
|
|
316
|
+
"created",
|
|
317
|
+
"preparing",
|
|
318
|
+
"running",
|
|
319
|
+
"validating",
|
|
320
|
+
"reviewing",
|
|
321
|
+
"stopped",
|
|
322
|
+
"failed",
|
|
323
|
+
"needs-attention",
|
|
324
|
+
"needs_attention"
|
|
325
|
+
]);
|
|
308
326
|
|
|
309
327
|
// packages/cli/src/commands/_preflight.ts
|
|
310
328
|
function preflightCheck(id, label, status, detail, remediation) {
|