@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
package/dist/src/commands/run.js
CHANGED
|
@@ -66,8 +66,6 @@ import {
|
|
|
66
66
|
deleteRunState,
|
|
67
67
|
listOpenEpics,
|
|
68
68
|
resolveDefaultEpic,
|
|
69
|
-
runResume,
|
|
70
|
-
runRestart,
|
|
71
69
|
runStatus,
|
|
72
70
|
runStop,
|
|
73
71
|
startRun,
|
|
@@ -194,6 +192,15 @@ function isRemoteConnectionSelected(projectRoot) {
|
|
|
194
192
|
|
|
195
193
|
// packages/cli/src/commands/_server-client.ts
|
|
196
194
|
var scopedGitHubBearerTokens = new Map;
|
|
195
|
+
var serverPhaseListener = null;
|
|
196
|
+
function setServerPhaseListener(listener) {
|
|
197
|
+
const previous = serverPhaseListener;
|
|
198
|
+
serverPhaseListener = listener;
|
|
199
|
+
return previous;
|
|
200
|
+
}
|
|
201
|
+
function reportServerPhase(label) {
|
|
202
|
+
serverPhaseListener?.(label);
|
|
203
|
+
}
|
|
197
204
|
function cleanToken(value) {
|
|
198
205
|
const trimmed = value?.trim();
|
|
199
206
|
return trimmed ? trimmed : null;
|
|
@@ -236,6 +243,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
236
243
|
try {
|
|
237
244
|
const selected = resolveSelectedConnection(projectRoot);
|
|
238
245
|
if (selected?.connection.kind === "remote") {
|
|
246
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
239
247
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
240
248
|
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
241
249
|
return {
|
|
@@ -245,6 +253,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
245
253
|
serverProjectRoot
|
|
246
254
|
};
|
|
247
255
|
}
|
|
256
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
248
257
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
249
258
|
return {
|
|
250
259
|
baseUrl: connection.baseUrl,
|
|
@@ -351,6 +360,7 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
351
360
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
352
361
|
if (server.serverProjectRoot)
|
|
353
362
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
363
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
354
364
|
let response;
|
|
355
365
|
try {
|
|
356
366
|
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
@@ -409,6 +419,38 @@ async function getRunTimelineViaServer(context, runId, options = {}) {
|
|
|
409
419
|
const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
|
|
410
420
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { entries: [] };
|
|
411
421
|
}
|
|
422
|
+
var RESUMABLE_RUN_STATUSES = new Set([
|
|
423
|
+
"created",
|
|
424
|
+
"preparing",
|
|
425
|
+
"running",
|
|
426
|
+
"validating",
|
|
427
|
+
"reviewing",
|
|
428
|
+
"stopped",
|
|
429
|
+
"failed",
|
|
430
|
+
"needs-attention",
|
|
431
|
+
"needs_attention"
|
|
432
|
+
]);
|
|
433
|
+
async function resumeRunViaServer(context, runId, options) {
|
|
434
|
+
let targetRunId = runId?.trim() || null;
|
|
435
|
+
if (!targetRunId) {
|
|
436
|
+
const candidates = (await listRunsViaServer(context)).filter((run) => RESUMABLE_RUN_STATUSES.has(String(run.status ?? ""))).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")));
|
|
437
|
+
targetRunId = typeof candidates[0]?.runId === "string" ? candidates[0].runId : null;
|
|
438
|
+
}
|
|
439
|
+
if (!targetRunId) {
|
|
440
|
+
throw new CliError(options.restart ? "No run is available to restart." : "No resumable run is available.", 2, { hint: "List runs with `rig run list`, then pass an explicit id: `rig run resume <run-id>`." });
|
|
441
|
+
}
|
|
442
|
+
const payload = await requestServerJson(context, "/api/runs/resume", {
|
|
443
|
+
method: "POST",
|
|
444
|
+
headers: { "content-type": "application/json" },
|
|
445
|
+
body: JSON.stringify({ runId: targetRunId, createdAt: new Date().toISOString(), restart: options.restart })
|
|
446
|
+
});
|
|
447
|
+
const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
448
|
+
if (record.ok === false) {
|
|
449
|
+
const message = typeof record.error === "string" && record.error.trim() ? record.error : "run resume failed";
|
|
450
|
+
throw new CliError(`${options.restart ? "restart" : "resume"} failed for ${targetRunId}: ${message}`, 1);
|
|
451
|
+
}
|
|
452
|
+
return { ok: true, runId: targetRunId, ...record };
|
|
453
|
+
}
|
|
412
454
|
async function stopRunViaServer(context, runId) {
|
|
413
455
|
const payload = await requestServerJson(context, "/api/runs/stop", {
|
|
414
456
|
method: "POST",
|
|
@@ -811,7 +853,12 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
811
853
|
};
|
|
812
854
|
let detached = false;
|
|
813
855
|
try {
|
|
814
|
-
await runPiMain([
|
|
856
|
+
await runPiMain([
|
|
857
|
+
"--no-extensions",
|
|
858
|
+
"--no-skills",
|
|
859
|
+
"--no-prompt-templates",
|
|
860
|
+
"--no-context-files"
|
|
861
|
+
], {
|
|
815
862
|
extensionFactories: [piRigExtensionFactory]
|
|
816
863
|
});
|
|
817
864
|
detached = true;
|
|
@@ -834,6 +881,127 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
834
881
|
};
|
|
835
882
|
}
|
|
836
883
|
|
|
884
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
885
|
+
import pc from "picocolors";
|
|
886
|
+
|
|
887
|
+
// packages/cli/src/commands/_spinner.ts
|
|
888
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
889
|
+
function createTtySpinner(input) {
|
|
890
|
+
const output = input.output ?? process.stdout;
|
|
891
|
+
const isTty = output.isTTY === true;
|
|
892
|
+
const frames = input.frames && input.frames.length > 0 ? input.frames : SPINNER_FRAMES;
|
|
893
|
+
let label = input.label;
|
|
894
|
+
let frame = 0;
|
|
895
|
+
let paused = false;
|
|
896
|
+
let stopped = false;
|
|
897
|
+
let lastPrintedLabel = "";
|
|
898
|
+
const render = () => {
|
|
899
|
+
if (stopped || paused)
|
|
900
|
+
return;
|
|
901
|
+
if (!isTty) {
|
|
902
|
+
if (label !== lastPrintedLabel) {
|
|
903
|
+
output.write(`${label}
|
|
904
|
+
`);
|
|
905
|
+
lastPrintedLabel = label;
|
|
906
|
+
}
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
frame = (frame + 1) % frames.length;
|
|
910
|
+
const glyph = frames[frame] ?? frames[0] ?? "";
|
|
911
|
+
output.write(`\r\x1B[2K${input.styleFrame ? input.styleFrame(glyph) : glyph} ${label}`);
|
|
912
|
+
};
|
|
913
|
+
const clearLine = () => {
|
|
914
|
+
if (isTty)
|
|
915
|
+
output.write("\r\x1B[2K");
|
|
916
|
+
};
|
|
917
|
+
render();
|
|
918
|
+
const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
|
|
919
|
+
return {
|
|
920
|
+
setLabel(next) {
|
|
921
|
+
label = next;
|
|
922
|
+
render();
|
|
923
|
+
},
|
|
924
|
+
pause() {
|
|
925
|
+
paused = true;
|
|
926
|
+
clearLine();
|
|
927
|
+
},
|
|
928
|
+
resume() {
|
|
929
|
+
if (stopped)
|
|
930
|
+
return;
|
|
931
|
+
paused = false;
|
|
932
|
+
render();
|
|
933
|
+
},
|
|
934
|
+
stop(finalLine) {
|
|
935
|
+
if (stopped)
|
|
936
|
+
return;
|
|
937
|
+
stopped = true;
|
|
938
|
+
if (timer)
|
|
939
|
+
clearInterval(timer);
|
|
940
|
+
clearLine();
|
|
941
|
+
if (finalLine)
|
|
942
|
+
output.write(`${finalLine}
|
|
943
|
+
`);
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// packages/cli/src/commands/_async-ui.ts
|
|
949
|
+
var CLACK_SPINNER_FRAMES = ["\u25D2", "\u25D0", "\u25D3", "\u25D1"];
|
|
950
|
+
var DONE_SYMBOL = pc.green("\u25C7");
|
|
951
|
+
var FAIL_SYMBOL = pc.red("\u25A0");
|
|
952
|
+
var activeUpdate = null;
|
|
953
|
+
async function withSpinner(label, work, options = {}) {
|
|
954
|
+
if (options.outputMode === "json") {
|
|
955
|
+
return work(() => {});
|
|
956
|
+
}
|
|
957
|
+
if (activeUpdate) {
|
|
958
|
+
const outer = activeUpdate;
|
|
959
|
+
outer(label);
|
|
960
|
+
return work(outer);
|
|
961
|
+
}
|
|
962
|
+
const output = options.output ?? process.stderr;
|
|
963
|
+
const isTty = output.isTTY === true;
|
|
964
|
+
let lastLabel = label;
|
|
965
|
+
if (!isTty) {
|
|
966
|
+
output.write(`${label}
|
|
967
|
+
`);
|
|
968
|
+
const update2 = (next) => {
|
|
969
|
+
lastLabel = next;
|
|
970
|
+
};
|
|
971
|
+
activeUpdate = update2;
|
|
972
|
+
const previousListener2 = setServerPhaseListener(update2);
|
|
973
|
+
try {
|
|
974
|
+
return await work(update2);
|
|
975
|
+
} finally {
|
|
976
|
+
activeUpdate = null;
|
|
977
|
+
setServerPhaseListener(previousListener2);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
const spinner = createTtySpinner({
|
|
981
|
+
label,
|
|
982
|
+
output,
|
|
983
|
+
frames: CLACK_SPINNER_FRAMES,
|
|
984
|
+
styleFrame: (frame) => pc.magenta(frame)
|
|
985
|
+
});
|
|
986
|
+
const update = (next) => {
|
|
987
|
+
lastLabel = next;
|
|
988
|
+
spinner.setLabel(next);
|
|
989
|
+
};
|
|
990
|
+
activeUpdate = update;
|
|
991
|
+
const previousListener = setServerPhaseListener(update);
|
|
992
|
+
try {
|
|
993
|
+
const result = await work(update);
|
|
994
|
+
spinner.stop(options.doneLabel ? `${DONE_SYMBOL} ${options.doneLabel}` : undefined);
|
|
995
|
+
return result;
|
|
996
|
+
} catch (error) {
|
|
997
|
+
spinner.stop(`${FAIL_SYMBOL} ${lastLabel}`);
|
|
998
|
+
throw error;
|
|
999
|
+
} finally {
|
|
1000
|
+
activeUpdate = null;
|
|
1001
|
+
setServerPhaseListener(previousListener);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
837
1005
|
// packages/cli/src/commands/_operator-view.ts
|
|
838
1006
|
var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
|
|
839
1007
|
function runStatusFromPayload(payload) {
|
|
@@ -876,8 +1044,9 @@ async function readOperatorSnapshot(context, runId, options = {}) {
|
|
|
876
1044
|
}
|
|
877
1045
|
async function attachRunOperatorView(context, input) {
|
|
878
1046
|
let steered = false;
|
|
879
|
-
|
|
880
|
-
|
|
1047
|
+
const attachMessage = input.message?.trim();
|
|
1048
|
+
if (attachMessage) {
|
|
1049
|
+
await withSpinner("Queueing steering message\u2026", () => sendRunPiPromptViaServer(context, input.runId, attachMessage, "steer").catch(() => steerRunViaServer(context, input.runId, attachMessage)), { outputMode: context.outputMode });
|
|
881
1050
|
steered = true;
|
|
882
1051
|
}
|
|
883
1052
|
if (input.follow && !input.once && input.interactive !== false && context.outputMode === "text" && Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
@@ -887,7 +1056,7 @@ async function attachRunOperatorView(context, input) {
|
|
|
887
1056
|
});
|
|
888
1057
|
}
|
|
889
1058
|
const surface = createOperatorSurface({ interactive: input.interactive !== false });
|
|
890
|
-
let snapshot = await readOperatorSnapshot(context, input.runId);
|
|
1059
|
+
let snapshot = await withSpinner(`Connecting to run ${input.runId}\u2026`, () => readOperatorSnapshot(context, input.runId), { outputMode: context.outputMode });
|
|
891
1060
|
if (context.outputMode === "text") {
|
|
892
1061
|
surface.renderSnapshot(snapshot);
|
|
893
1062
|
surface.renderTimeline(snapshot.timeline);
|
|
@@ -927,7 +1096,7 @@ async function attachRunOperatorView(context, input) {
|
|
|
927
1096
|
|
|
928
1097
|
// packages/cli/src/commands/_cli-format.ts
|
|
929
1098
|
import { log, note } from "@clack/prompts";
|
|
930
|
-
import
|
|
1099
|
+
import pc2 from "picocolors";
|
|
931
1100
|
function stringField(record, key, fallback = "") {
|
|
932
1101
|
const value = record[key];
|
|
933
1102
|
return typeof value === "string" && value.trim() ? value.trim() : fallback;
|
|
@@ -949,14 +1118,14 @@ function pad(value, width) {
|
|
|
949
1118
|
function statusColor(status) {
|
|
950
1119
|
const normalized = status.toLowerCase();
|
|
951
1120
|
if (["completed", "merged", "closed", "done", "accepted", "pass", "selected", "approved"].includes(normalized))
|
|
952
|
-
return
|
|
1121
|
+
return pc2.green;
|
|
953
1122
|
if (["failed", "needs_attention", "needs-attention", "blocked", "error", "rejected"].includes(normalized))
|
|
954
|
-
return
|
|
1123
|
+
return pc2.red;
|
|
955
1124
|
if (["running", "reviewing", "validating", "in_progress", "in-progress", "remote"].includes(normalized))
|
|
956
|
-
return
|
|
1125
|
+
return pc2.cyan;
|
|
957
1126
|
if (["ready", "open", "queued", "created", "preparing", "local", "pending"].includes(normalized))
|
|
958
|
-
return
|
|
959
|
-
return
|
|
1127
|
+
return pc2.yellow;
|
|
1128
|
+
return pc2.dim;
|
|
960
1129
|
}
|
|
961
1130
|
function compactDate(value) {
|
|
962
1131
|
if (!value.trim())
|
|
@@ -1001,23 +1170,23 @@ function formatStatusPill(status) {
|
|
|
1001
1170
|
return statusColor(label)(`\u25CF ${label}`);
|
|
1002
1171
|
}
|
|
1003
1172
|
function formatSection(title, subtitle) {
|
|
1004
|
-
return `${
|
|
1173
|
+
return `${pc2.bold(pc2.cyan("\u25C6"))} ${pc2.bold(title)}${subtitle ? pc2.dim(` \u2014 ${subtitle}`) : ""}`;
|
|
1005
1174
|
}
|
|
1006
1175
|
function formatSuccessCard(title, rows = []) {
|
|
1007
|
-
const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${
|
|
1176
|
+
const body = rows.filter(([, value]) => value !== undefined && value !== null && String(value).length > 0).map(([key, value]) => `${pc2.dim("\u2502")} ${pc2.dim(key.padEnd(12))} ${value}`);
|
|
1008
1177
|
return [formatSection(title), ...body].join(`
|
|
1009
1178
|
`);
|
|
1010
1179
|
}
|
|
1011
1180
|
function formatNextSteps(steps) {
|
|
1012
1181
|
if (steps.length === 0)
|
|
1013
1182
|
return [];
|
|
1014
|
-
return [
|
|
1183
|
+
return [pc2.bold("Next"), ...steps.map((step) => `${pc2.dim("\u203A")} ${step}`)];
|
|
1015
1184
|
}
|
|
1016
1185
|
function formatRunList(runs, options = {}) {
|
|
1017
1186
|
if (runs.length === 0) {
|
|
1018
1187
|
return [
|
|
1019
1188
|
formatSection("Runs", "none recorded"),
|
|
1020
|
-
options.source === "server" ?
|
|
1189
|
+
options.source === "server" ? pc2.dim("No runs recorded on the selected Rig server.") : pc2.dim("No runs recorded in .rig/runs."),
|
|
1021
1190
|
"",
|
|
1022
1191
|
...formatNextSteps(["Start one: `rig task run --next`", "Check server: `rig server status`"])
|
|
1023
1192
|
].join(`
|
|
@@ -1033,11 +1202,11 @@ function formatRunList(runs, options = {}) {
|
|
|
1033
1202
|
});
|
|
1034
1203
|
const idWidth = Math.min(36, Math.max(6, ...rows.map((row) => row.runId.length)));
|
|
1035
1204
|
const statusWidth = Math.min(16, Math.max(6, ...rows.map((row) => row.status.length)));
|
|
1036
|
-
const header = `${
|
|
1205
|
+
const header = `${pc2.bold(pad("RUN", idWidth))} ${pc2.bold(pad("STATUS", statusWidth))} ${pc2.bold("TITLE")}`;
|
|
1037
1206
|
const body = rows.map((row) => [
|
|
1038
|
-
|
|
1207
|
+
pc2.bold(pad(truncate(row.runId, idWidth), idWidth)),
|
|
1039
1208
|
statusColor(row.status)(pad(truncate(row.status, statusWidth), statusWidth)),
|
|
1040
|
-
`${row.title}${row.runtime ?
|
|
1209
|
+
`${row.title}${row.runtime ? pc2.dim(` ${row.runtime}`) : ""}`
|
|
1041
1210
|
].join(" "));
|
|
1042
1211
|
return [formatSection("Runs", options.source === "server" ? "selected server" : "local state"), header, ...body, "", ...formatNextSteps(["Follow live: `rig run attach <run-id> --follow`", "Details: `rig run show <run-id>`"])].join(`
|
|
1043
1212
|
`);
|
|
@@ -1062,7 +1231,7 @@ function formatRunCard(run, options = {}) {
|
|
|
1062
1231
|
const approvals = Array.isArray(merged.approvals) ? merged.approvals.length : null;
|
|
1063
1232
|
const inputs = Array.isArray(merged.userInputs) ? merged.userInputs.length : null;
|
|
1064
1233
|
const rows = [
|
|
1065
|
-
["run",
|
|
1234
|
+
["run", pc2.bold(runId)],
|
|
1066
1235
|
["status", formatStatusPill(status)],
|
|
1067
1236
|
["task", taskId],
|
|
1068
1237
|
["title", title],
|
|
@@ -1088,17 +1257,17 @@ function formatRunStatus(summary, options = {}) {
|
|
|
1088
1257
|
const activeRuns = summary.activeRuns ?? [];
|
|
1089
1258
|
const recentRuns = summary.recentRuns ?? [];
|
|
1090
1259
|
const lines = [formatSection("Run status", options.source === "server" ? "selected server" : "local state")];
|
|
1091
|
-
lines.push("",
|
|
1260
|
+
lines.push("", pc2.bold(`Active runs (${activeRuns.length})`));
|
|
1092
1261
|
if (activeRuns.length === 0) {
|
|
1093
|
-
lines.push(
|
|
1262
|
+
lines.push(pc2.dim("No active runs."));
|
|
1094
1263
|
} else {
|
|
1095
1264
|
for (const run of activeRuns) {
|
|
1096
1265
|
lines.push(formatRunSummaryLine(run));
|
|
1097
1266
|
}
|
|
1098
1267
|
}
|
|
1099
|
-
lines.push("",
|
|
1268
|
+
lines.push("", pc2.bold(`Recent runs (${recentRuns.length})`));
|
|
1100
1269
|
if (recentRuns.length === 0) {
|
|
1101
|
-
lines.push(
|
|
1270
|
+
lines.push(pc2.dim("No recent terminal runs."));
|
|
1102
1271
|
} else {
|
|
1103
1272
|
for (const run of recentRuns.slice(0, 10)) {
|
|
1104
1273
|
lines.push(formatRunSummaryLine(run));
|
|
@@ -1116,7 +1285,7 @@ function formatRunSummaryLine(run) {
|
|
|
1116
1285
|
const title = runTitleOf(record);
|
|
1117
1286
|
const runtime = firstString(record, ["runtimeAdapter", "runtime", "adapter"]);
|
|
1118
1287
|
const descriptor = [taskId, title].filter(Boolean).join(" \xB7 ");
|
|
1119
|
-
return `${
|
|
1288
|
+
return `${pc2.dim("\u2502")} ${pc2.bold(runId)} ${formatStatusPill(status)} ${descriptor}${runtime ? pc2.dim(` ${runtime}`) : ""}`;
|
|
1120
1289
|
}
|
|
1121
1290
|
|
|
1122
1291
|
// packages/cli/src/commands/inbox.ts
|
|
@@ -1250,7 +1419,7 @@ async function executeRun(context, args) {
|
|
|
1250
1419
|
switch (command) {
|
|
1251
1420
|
case "list": {
|
|
1252
1421
|
requireNoExtraArgs(rest, "rig run list");
|
|
1253
|
-
const { runs, source } = await listRunsForSelectedConnection(context, { limit: 100 });
|
|
1422
|
+
const { runs, source } = isRemoteConnectionSelected(context.projectRoot) ? await withSpinner("Reading runs from server\u2026", () => listRunsForSelectedConnection(context, { limit: 100 }), { outputMode: context.outputMode }) : await listRunsForSelectedConnection(context, { limit: 100 });
|
|
1254
1423
|
if (context.outputMode === "text") {
|
|
1255
1424
|
printFormattedOutput(formatRunList(runs, { source }));
|
|
1256
1425
|
await printPendingInboxFooter(context);
|
|
@@ -1323,7 +1492,7 @@ async function executeRun(context, args) {
|
|
|
1323
1492
|
if (!runId) {
|
|
1324
1493
|
throw new CliError("run show requires a run id.", 1, { hint: "Run `rig run list` to find run ids, then `rig run show <run-id>`." });
|
|
1325
1494
|
}
|
|
1326
|
-
const record = readAuthorityRun2(context.projectRoot, runId) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, runId).catch(() => ({})));
|
|
1495
|
+
const record = readAuthorityRun2(context.projectRoot, runId) ?? normalizeRemoteRunDetails(await withSpinner(`Reading run ${runId} from server\u2026`, () => getRunDetailsViaServer(context, runId).catch(() => ({})), { outputMode: context.outputMode }));
|
|
1327
1496
|
if (!record) {
|
|
1328
1497
|
throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see recorded runs." });
|
|
1329
1498
|
}
|
|
@@ -1344,7 +1513,8 @@ async function executeRun(context, args) {
|
|
|
1344
1513
|
}
|
|
1345
1514
|
const renderer = createPiRunStreamRenderer();
|
|
1346
1515
|
let cursor = null;
|
|
1347
|
-
const
|
|
1516
|
+
const timelineRunId = run.value;
|
|
1517
|
+
const page = await withSpinner(`Reading timeline for ${timelineRunId}\u2026`, () => getRunTimelineViaServer(context, timelineRunId, { limit: 500 }), { outputMode: context.outputMode });
|
|
1348
1518
|
const events = Array.isArray(page.entries) ? page.entries.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
|
|
1349
1519
|
cursor = typeof page.nextCursor === "string" ? page.nextCursor : null;
|
|
1350
1520
|
if (context.outputMode === "text") {
|
|
@@ -1421,8 +1591,9 @@ async function executeRun(context, args) {
|
|
|
1421
1591
|
throw new CliError("run attach requires a run id.", 2, { hint: "Run `rig run list` to find run ids, then `rig run attach <run-id> --follow`." });
|
|
1422
1592
|
}
|
|
1423
1593
|
let steered = false;
|
|
1424
|
-
|
|
1425
|
-
|
|
1594
|
+
const steerMessage = messageOption.value?.trim();
|
|
1595
|
+
if (steerMessage) {
|
|
1596
|
+
await withSpinner("Queueing steering message\u2026", () => steerRunViaServer(context, runId, steerMessage), { outputMode: context.outputMode });
|
|
1426
1597
|
steered = true;
|
|
1427
1598
|
}
|
|
1428
1599
|
const attached = await attachRunOperatorView(context, {
|
|
@@ -1442,7 +1613,7 @@ async function executeRun(context, args) {
|
|
|
1442
1613
|
}
|
|
1443
1614
|
return { ok: true, group: "run", command };
|
|
1444
1615
|
}
|
|
1445
|
-
const summary = isRemoteConnectionSelected(context.projectRoot) ? buildServerRunStatus(await listRunsViaServer(context, { limit: 100 })) : runStatus(context.projectRoot, runtimeContext);
|
|
1616
|
+
const summary = isRemoteConnectionSelected(context.projectRoot) ? buildServerRunStatus(await withSpinner("Reading run status from server\u2026", () => listRunsViaServer(context, { limit: 100 }), { outputMode: context.outputMode })) : runStatus(context.projectRoot, runtimeContext);
|
|
1446
1617
|
const activeRuns = Array.isArray(summary.activeRuns) ? summary.activeRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
|
|
1447
1618
|
const recentRuns = Array.isArray(summary.recentRuns) ? summary.recentRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
|
|
1448
1619
|
if (context.outputMode === "text") {
|
|
@@ -1531,7 +1702,7 @@ async function executeRun(context, args) {
|
|
|
1531
1702
|
}
|
|
1532
1703
|
return { ok: true, group: "run", command };
|
|
1533
1704
|
}
|
|
1534
|
-
const resumed = await
|
|
1705
|
+
const resumed = await withSpinner(targetRunId ? `Resuming run ${targetRunId}\u2026` : "Resuming latest resumable run\u2026", () => resumeRunViaServer(context, targetRunId, { restart: false }), { outputMode: context.outputMode });
|
|
1535
1706
|
if (context.outputMode === "text") {
|
|
1536
1707
|
console.log(`Resumed run: ${resumed.runId}`);
|
|
1537
1708
|
}
|
|
@@ -1550,7 +1721,7 @@ async function executeRun(context, args) {
|
|
|
1550
1721
|
}
|
|
1551
1722
|
return { ok: true, group: "run", command };
|
|
1552
1723
|
}
|
|
1553
|
-
const restarted = await
|
|
1724
|
+
const restarted = await withSpinner(targetRunId ? `Restarting run ${targetRunId}\u2026` : "Restarting latest run\u2026", () => resumeRunViaServer(context, targetRunId, { restart: true }), { outputMode: context.outputMode });
|
|
1554
1725
|
if (context.outputMode === "text") {
|
|
1555
1726
|
console.log(`Restarted run: ${restarted.runId}`);
|
|
1556
1727
|
}
|
|
@@ -1577,7 +1748,8 @@ async function executeRun(context, args) {
|
|
|
1577
1748
|
}
|
|
1578
1749
|
return { ok: true, group: "run", command, details: { runId, dryRun: true } };
|
|
1579
1750
|
}
|
|
1580
|
-
|
|
1751
|
+
const trimmedMessage = message.trim();
|
|
1752
|
+
await withSpinner("Queueing steering message\u2026", () => steerRunViaServer(context, runId, trimmedMessage), { outputMode: context.outputMode });
|
|
1581
1753
|
if (context.outputMode === "text") {
|
|
1582
1754
|
console.log(`Steering message queued for ${runId}.`);
|
|
1583
1755
|
}
|
|
@@ -1598,7 +1770,7 @@ async function executeRun(context, args) {
|
|
|
1598
1770
|
};
|
|
1599
1771
|
}
|
|
1600
1772
|
if (runId) {
|
|
1601
|
-
const stopped = await stopRunViaServer(context, runId);
|
|
1773
|
+
const stopped = await withSpinner(`Requesting stop for ${runId}\u2026`, () => stopRunViaServer(context, runId), { outputMode: context.outputMode });
|
|
1602
1774
|
if (context.outputMode === "text")
|
|
1603
1775
|
console.log(`Stop requested: ${runId}`);
|
|
1604
1776
|
return { ok: true, group: "run", command, details: stopped };
|
|
@@ -361,6 +361,10 @@ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
|
361
361
|
import { resolve as resolve2 } from "path";
|
|
362
362
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
363
363
|
var scopedGitHubBearerTokens = new Map;
|
|
364
|
+
var serverPhaseListener = null;
|
|
365
|
+
function reportServerPhase(label) {
|
|
366
|
+
serverPhaseListener?.(label);
|
|
367
|
+
}
|
|
364
368
|
function cleanToken(value) {
|
|
365
369
|
const trimmed = value?.trim();
|
|
366
370
|
return trimmed ? trimmed : null;
|
|
@@ -403,6 +407,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
403
407
|
try {
|
|
404
408
|
const selected = resolveSelectedConnection(projectRoot);
|
|
405
409
|
if (selected?.connection.kind === "remote") {
|
|
410
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
406
411
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
407
412
|
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
408
413
|
return {
|
|
@@ -412,6 +417,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
412
417
|
serverProjectRoot
|
|
413
418
|
};
|
|
414
419
|
}
|
|
420
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
415
421
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
416
422
|
return {
|
|
417
423
|
baseUrl: connection.baseUrl,
|
|
@@ -518,6 +524,7 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
518
524
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
519
525
|
if (server.serverProjectRoot)
|
|
520
526
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
527
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
521
528
|
let response;
|
|
522
529
|
try {
|
|
523
530
|
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
@@ -546,6 +553,17 @@ ${failure.contextLine}`, 1, { hint: failure.hint });
|
|
|
546
553
|
}
|
|
547
554
|
return payload;
|
|
548
555
|
}
|
|
556
|
+
var RESUMABLE_RUN_STATUSES = new Set([
|
|
557
|
+
"created",
|
|
558
|
+
"preparing",
|
|
559
|
+
"running",
|
|
560
|
+
"validating",
|
|
561
|
+
"reviewing",
|
|
562
|
+
"stopped",
|
|
563
|
+
"failed",
|
|
564
|
+
"needs-attention",
|
|
565
|
+
"needs_attention"
|
|
566
|
+
]);
|
|
549
567
|
async function submitTaskRunViaServer(context, input) {
|
|
550
568
|
const isTaskRun = Boolean(input.taskId);
|
|
551
569
|
const endpoint = isTaskRun ? "/api/runs/task" : "/api/runs/adhoc";
|