@h-rig/cli 0.0.6-alpha.88 → 0.0.6-alpha.89
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 +1159 -292
- package/dist/src/app/board.js +462 -48
- package/dist/src/app-opentui/adapters/doctor.js +458 -46
- package/dist/src/app-opentui/adapters/family.js +670 -147
- package/dist/src/app-opentui/adapters/fleet.js +477 -46
- package/dist/src/app-opentui/adapters/inbox.js +477 -46
- package/dist/src/app-opentui/adapters/init.js +497 -74
- package/dist/src/app-opentui/adapters/inspect.js +477 -46
- package/dist/src/app-opentui/adapters/pi-attach.js +484 -53
- package/dist/src/app-opentui/adapters/run-detail.js +477 -46
- package/dist/src/app-opentui/adapters/server.d.ts +26 -0
- package/dist/src/app-opentui/adapters/server.js +676 -59
- package/dist/src/app-opentui/adapters/tasks.js +539 -81
- package/dist/src/app-opentui/autocomplete.js +4 -2
- package/dist/src/app-opentui/bootstrap.js +1155 -288
- package/dist/src/app-opentui/command-palette.js +37 -10
- package/dist/src/app-opentui/index.js +566 -89
- package/dist/src/app-opentui/intent.js +33 -8
- package/dist/src/app-opentui/keymap.js +43 -29
- package/dist/src/app-opentui/pi-host-child.js +465 -53
- package/dist/src/app-opentui/react/App.js +104 -44
- package/dist/src/app-opentui/react/ChromeHost.js +16 -4
- package/dist/src/app-opentui/react/launch.js +555 -88
- package/dist/src/app-opentui/react/nav.js +1 -1
- package/dist/src/app-opentui/registry.js +1062 -237
- package/dist/src/app-opentui/remote-link.d.ts +10 -0
- package/dist/src/app-opentui/remote-link.js +47 -0
- package/dist/src/app-opentui/runtime.js +566 -89
- package/dist/src/app-opentui/scenes/doctor.js +1 -1
- package/dist/src/app-opentui/scenes/error.js +50 -4
- package/dist/src/app-opentui/scenes/family.js +60 -6
- package/dist/src/app-opentui/scenes/fleet.js +65 -13
- package/dist/src/app-opentui/scenes/help.js +4 -2
- package/dist/src/app-opentui/scenes/init.js +12 -12
- package/dist/src/app-opentui/scenes/main.js +7 -7
- package/dist/src/app-opentui/scenes/server.js +83 -11
- package/dist/src/app-opentui/scenes/tasks.js +79 -16
- package/dist/src/app-opentui/state.js +25 -5
- package/dist/src/app-opentui/surface-catalog.js +4 -2
- package/dist/src/app-opentui/types.d.ts +1 -1
- package/dist/src/commands/_cli-format.d.ts +10 -1
- package/dist/src/commands/_cli-format.js +5 -2
- package/dist/src/commands/_connection-state.d.ts +11 -1
- package/dist/src/commands/_connection-state.js +50 -5
- package/dist/src/commands/_doctor-checks.js +458 -46
- package/dist/src/commands/_help-catalog.js +4 -2
- package/dist/src/commands/_json-output.js +4 -0
- package/dist/src/commands/_operator-view.js +465 -53
- package/dist/src/commands/_pi-frontend.js +465 -53
- package/dist/src/commands/_preflight.js +509 -72
- package/dist/src/commands/_server-client.d.ts +33 -0
- package/dist/src/commands/_server-client.js +477 -46
- package/dist/src/commands/_server-events.js +446 -41
- package/dist/src/commands/_snapshot-upload.js +460 -48
- package/dist/src/commands/connect.js +620 -15
- package/dist/src/commands/doctor.js +458 -46
- package/dist/src/commands/github.js +462 -50
- package/dist/src/commands/inbox.js +458 -46
- package/dist/src/commands/init.js +497 -74
- package/dist/src/commands/inspect.js +458 -46
- package/dist/src/commands/run.js +465 -53
- package/dist/src/commands/server.js +647 -163
- package/dist/src/commands/setup.js +463 -51
- package/dist/src/commands/stats.js +462 -48
- package/dist/src/commands/task-run-driver.js +464 -52
- package/dist/src/commands/task.js +520 -81
- package/dist/src/commands.js +670 -147
- package/dist/src/index.js +674 -147
- package/dist/src/launcher.js +4 -0
- package/package.json +8 -8
|
@@ -356,14 +356,56 @@ function sortTasks(tasks, spec) {
|
|
|
356
356
|
return applyDir(tasks, compare, dir);
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
+
// packages/cli/src/app-opentui/remote-link.ts
|
|
360
|
+
var REPAIRABLE_REMOTE_LINK_STATUSES = new Set([
|
|
361
|
+
"auth_required",
|
|
362
|
+
"project_not_registered",
|
|
363
|
+
"no_server_checkout",
|
|
364
|
+
"invalid_root",
|
|
365
|
+
"needs_prepare",
|
|
366
|
+
"error"
|
|
367
|
+
]);
|
|
368
|
+
function remoteProjectLinkError(message) {
|
|
369
|
+
return /no server-host project root link|remote project(?:-root)? link|x-rig-project-root|serverProjectRoot/i.test(message ?? "");
|
|
370
|
+
}
|
|
371
|
+
function serverRecordForRemoteLink(state) {
|
|
372
|
+
const server = state.data.server;
|
|
373
|
+
return server && typeof server === "object" && !Array.isArray(server) ? server : null;
|
|
374
|
+
}
|
|
375
|
+
function remoteProjectLinkState(state) {
|
|
376
|
+
const serverRecord = serverRecordForRemoteLink(state);
|
|
377
|
+
if (!serverRecord || serverRecord.kind !== "remote")
|
|
378
|
+
return null;
|
|
379
|
+
const link = serverRecord.remoteProjectLink;
|
|
380
|
+
if (link && typeof link === "object" && !Array.isArray(link))
|
|
381
|
+
return link;
|
|
382
|
+
const direct = state.data.remoteProjectLink;
|
|
383
|
+
if (direct && typeof direct === "object" && !Array.isArray(direct))
|
|
384
|
+
return direct;
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
function shouldOfferRemoteLinkRepair(state, message) {
|
|
388
|
+
const link = remoteProjectLinkState(state);
|
|
389
|
+
if (link) {
|
|
390
|
+
if (link.ok === true)
|
|
391
|
+
return false;
|
|
392
|
+
const status = link.status ?? "";
|
|
393
|
+
return REPAIRABLE_REMOTE_LINK_STATUSES.has(status);
|
|
394
|
+
}
|
|
395
|
+
const serverRecord = serverRecordForRemoteLink(state);
|
|
396
|
+
if (serverRecord && serverRecord.kind !== "remote")
|
|
397
|
+
return false;
|
|
398
|
+
return remoteProjectLinkError(message ?? state.error?.message);
|
|
399
|
+
}
|
|
400
|
+
|
|
359
401
|
// packages/cli/src/app-opentui/scenes/tasks.ts
|
|
360
|
-
var
|
|
402
|
+
var BASE_TASK_RECOVERY_ITEMS = [
|
|
361
403
|
{ id: "refresh", label: "refresh", intent: { scene: "tasks", argv: ["tasks"], action: { kind: "refresh", label: "Refresh tasks" } }, message: "refresh task source" },
|
|
362
|
-
{ id: "next", label: "next", intent: { scene: "
|
|
363
|
-
{ id: "repair", label: "repair", intent: { scene: "command", argv: ["init", "--repair"], action: { kind: "command-run", label: "Repair task source" } }, message: "repair project/task source" },
|
|
404
|
+
{ id: "next", label: "next", intent: { scene: "tasks", argv: ["task", "run", "--next"], action: { kind: "task-run-next", label: "Dispatch next task" } }, message: "dispatch next runnable task" },
|
|
364
405
|
{ id: "server", label: "server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Open server controls" } }, message: "server controls" },
|
|
365
406
|
{ id: "doctor", label: "doctor", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "diagnose task source" }
|
|
366
407
|
];
|
|
408
|
+
var TASK_REPAIR_LINK_ITEM = { id: "repair-link", label: "repair link", intent: { scene: "server", argv: ["server", "repair-link"], action: { kind: "remote-link-repair", label: "Repair remote link", payload: { returnScene: "tasks" } } }, message: "backfill or prepare remote project link" };
|
|
367
409
|
var COL = {
|
|
368
410
|
glyph: 1,
|
|
369
411
|
id: 11,
|
|
@@ -523,34 +565,44 @@ function taskRow(width, task, active, optimistic) {
|
|
|
523
565
|
const item = taskSelectableItem(task);
|
|
524
566
|
return item ? withSelectable(row, item) : row;
|
|
525
567
|
}
|
|
526
|
-
var
|
|
568
|
+
var BASE_RECOVERY_DECKS = [
|
|
527
569
|
{ label: "refresh", detail: "reload task source" },
|
|
528
570
|
{ label: "next", detail: "dispatch next runnable task" },
|
|
529
|
-
{ label: "repair", detail: "repair project/task source link" },
|
|
530
571
|
{ label: "server", detail: "open server controls" },
|
|
531
572
|
{ label: "doctor", detail: "diagnose task source" }
|
|
532
573
|
];
|
|
533
|
-
function
|
|
574
|
+
function taskRecoveryItems(state) {
|
|
575
|
+
if (!shouldOfferRemoteLinkRepair(state))
|
|
576
|
+
return BASE_TASK_RECOVERY_ITEMS;
|
|
577
|
+
return [BASE_TASK_RECOVERY_ITEMS[0], BASE_TASK_RECOVERY_ITEMS[1], TASK_REPAIR_LINK_ITEM, ...BASE_TASK_RECOVERY_ITEMS.slice(2)];
|
|
578
|
+
}
|
|
579
|
+
function taskRecoveryDecks(state) {
|
|
580
|
+
if (!shouldOfferRemoteLinkRepair(state))
|
|
581
|
+
return BASE_RECOVERY_DECKS;
|
|
582
|
+
return [BASE_RECOVERY_DECKS[0], BASE_RECOVERY_DECKS[1], { label: "repair link", detail: "backfill/prepare remote project-root link" }, ...BASE_RECOVERY_DECKS.slice(2)];
|
|
583
|
+
}
|
|
584
|
+
function recoveryRows(state, selected, reason) {
|
|
585
|
+
const items = taskRecoveryItems(state);
|
|
534
586
|
return [
|
|
535
587
|
...reason ? [line(reason, { fg: RIG_UI.yellow }), line("", { fg: RIG_UI.ink3 })] : [],
|
|
536
588
|
line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
|
|
537
|
-
...
|
|
589
|
+
...taskRecoveryDecks(state).map((deck, index) => selectableDeckRow({ ...deck, index, active: selected === index }, items[index]))
|
|
538
590
|
];
|
|
539
591
|
}
|
|
540
|
-
function emptyRows(query, total, selected) {
|
|
592
|
+
function emptyRows(state, query, total, selected) {
|
|
541
593
|
if (!query.trim() && total === 0)
|
|
542
|
-
return recoveryRows(selected, "No tasks loaded.");
|
|
594
|
+
return recoveryRows(state, selected, "No tasks loaded.");
|
|
543
595
|
if (query.trim() && total > 0) {
|
|
544
596
|
return [
|
|
545
|
-
...recoveryRows(selected, `No matching tasks for ${JSON.stringify(query)} (${total} total).`)
|
|
597
|
+
...recoveryRows(state, selected, `No matching tasks for ${JSON.stringify(query)} (${total} total).`)
|
|
546
598
|
];
|
|
547
599
|
}
|
|
548
|
-
return recoveryRows(selected, "No tasks are visible with this filter.");
|
|
600
|
+
return recoveryRows(state, selected, "No tasks are visible with this filter.");
|
|
549
601
|
}
|
|
550
602
|
function degradedRows(state, selected) {
|
|
551
603
|
if (!state.error)
|
|
552
604
|
return [];
|
|
553
|
-
return recoveryRows(selected, state.error.message);
|
|
605
|
+
return recoveryRows(state, selected, state.error.message);
|
|
554
606
|
}
|
|
555
607
|
function rawString(raw, key) {
|
|
556
608
|
const value = raw?.[key];
|
|
@@ -574,7 +626,10 @@ function renderTaskDetail(state, taskId, layout) {
|
|
|
574
626
|
const activeRunId = task?.activeRun?.runId?.trim();
|
|
575
627
|
const width = panelWidth(layout);
|
|
576
628
|
const selected = Math.max(0, state.selection.index);
|
|
629
|
+
const errorText = state.error?.message ?? lastRefreshError(state);
|
|
630
|
+
const offerRepairLink = shouldOfferRemoteLinkRepair(state, errorText);
|
|
577
631
|
const headerLines = [
|
|
632
|
+
...errorText ? errorBanner(errorText, offerRepairLink ? "select repair link below to prepare the remote checkout" : "retry dispatch after resolving the issue") : [],
|
|
578
633
|
line(`${glyph} ${title}`, { fg: color, bold: true }),
|
|
579
634
|
line(`task ${taskId} \xB7 ${status}`, { fg: RIG_UI.ink3 }),
|
|
580
635
|
...task && task.labels.length > 0 ? [line(`labels: ${task.labels.join(", ")}`, { fg: RIG_UI.ink4 })] : [],
|
|
@@ -586,8 +641,16 @@ function renderTaskDetail(state, taskId, layout) {
|
|
|
586
641
|
const actionRows = [];
|
|
587
642
|
actionRows.push(selectableDeckRow({ label: "back", detail: "return to the task list", index, active: selected === index }, { id: "task-detail-back", label: "back", data: { taskDetailId: undefined }, message: "back to task list" }));
|
|
588
643
|
index += 1;
|
|
589
|
-
|
|
590
|
-
|
|
644
|
+
if (offerRepairLink) {
|
|
645
|
+
actionRows.push(selectableDeckRow({ label: "repair link", detail: "prepare/backfill the selected remote project-root link", index, active: selected === index }, { id: `task-detail-repair-link:${taskId}`, label: "repair link", intent: { scene: "server", argv: ["server", "repair-link"], action: { kind: "remote-link-repair", label: "Repair remote link", payload: { returnScene: "tasks", taskDetailId: taskId } } }, message: "prepare remote checkout link" }));
|
|
646
|
+
index += 1;
|
|
647
|
+
}
|
|
648
|
+
if (offerRepairLink) {
|
|
649
|
+
actionRows.push(line(" dispatch blocked until the remote project link is repaired", { fg: RIG_UI.ink4 }));
|
|
650
|
+
} else {
|
|
651
|
+
actionRows.push(selectableDeckRow({ label: "dispatch", detail: "submit a Pi run for this task", index, active: selected === index }, { id: `task-detail-run:${taskId}`, label: `dispatch ${taskId}`, intent: { scene: "tasks", argv: ["run", taskId], action: { kind: "task-run-id", payload: { task: taskId }, label: `Dispatching ${taskId}` } }, message: `dispatch task ${taskId}` }));
|
|
652
|
+
index += 1;
|
|
653
|
+
}
|
|
591
654
|
if (activeRunId) {
|
|
592
655
|
actionRows.push(selectableDeckRow({ label: "attach", detail: `attach the Pi console to run ${activeRunId.slice(0, 8)}`, index, active: selected === index }, { id: `task-detail-attach:${taskId}`, label: `attach ${activeRunId.slice(0, 8)}`, intent: { scene: "handoff", argv: ["attach", activeRunId], action: { kind: "run-attach", payload: { runId: activeRunId }, label: `Attach Pi ${activeRunId.slice(0, 8)}` } }, message: `attach Pi to ${activeRunId.slice(0, 8)}` }));
|
|
593
656
|
index += 1;
|
|
@@ -646,7 +709,7 @@ function renderTasksScene(state, layout) {
|
|
|
646
709
|
const tasks = sortTasks(filterTasksForSearch(allTasks, query), spec);
|
|
647
710
|
const selected = typeof state.data.selectedTaskId === "string" && tasks.some((task) => task.id === state.data.selectedTaskId) ? state.data.selectedTaskId : tasks[0]?.id;
|
|
648
711
|
const selectedIndex = Math.max(0, tasks.findIndex((task) => task.id === selected));
|
|
649
|
-
const recoveryIndex = Math.max(0, Math.min(
|
|
712
|
+
const recoveryIndex = Math.max(0, Math.min(taskRecoveryItems(state).length - 1, state.selection.index));
|
|
650
713
|
const dispatching = dispatchingFor(state);
|
|
651
714
|
const dispatchLine = dispatching ? ` \xB7 ${state.data.dispatchingRun?.runId ?? "new"} ${dispatching.status}` : "";
|
|
652
715
|
const updatedAgo = updatedAgoLabel(state);
|
|
@@ -666,7 +729,7 @@ function renderTasksScene(state, layout) {
|
|
|
666
729
|
...tasks.map((task, index) => taskRow(contentWidth, task, index === selectedIndex, dispatching && dispatching.taskId === task.id ? dispatching.status : undefined))
|
|
667
730
|
] : state.error ? degradedRows(state, recoveryIndex) : loading ? loadingRows("tasks", state.tick) : [
|
|
668
731
|
...query.trim() ? [line(searchSummary("tasks", query, tasks.length, allTasks.length), { fg: RIG_UI.ink4 }), line("", { fg: RIG_UI.ink3 })] : [],
|
|
669
|
-
...emptyRows(query, allTasks.length, recoveryIndex)
|
|
732
|
+
...emptyRows(state, query, allTasks.length, recoveryIndex)
|
|
670
733
|
]
|
|
671
734
|
];
|
|
672
735
|
return makeSceneFrame({
|
|
@@ -152,16 +152,31 @@ function taskViewIntent(argv, command = "", rest = [], raw) {
|
|
|
152
152
|
const { payload, label } = taskViewPayload(command, rest);
|
|
153
153
|
return intent("tasks", argv, "refresh", payload, label, raw);
|
|
154
154
|
}
|
|
155
|
-
function routeServer(argv, command, raw, checkingLabel) {
|
|
156
|
-
if (!command || command === "status") {
|
|
155
|
+
function routeServer(argv, command, rest, raw, checkingLabel) {
|
|
156
|
+
if (!command || command === "status" || command === "list") {
|
|
157
157
|
return intent("server", argv, "refresh", undefined, command ? checkingLabel : "Loading server", raw);
|
|
158
158
|
}
|
|
159
|
+
if (command === "use") {
|
|
160
|
+
const alias = firstNonOption(rest);
|
|
161
|
+
if (alias === "local")
|
|
162
|
+
return intent("server", argv, "server-use-local", undefined, "Use local server", raw);
|
|
163
|
+
if (alias)
|
|
164
|
+
return intent("server", argv, "server-use-remote", { alias }, `Use ${alias}`, raw);
|
|
165
|
+
return intent("server", argv, "refresh", undefined, "Select a remote alias in Server", raw);
|
|
166
|
+
}
|
|
167
|
+
if (command === "add")
|
|
168
|
+
return intent("server", argv, "server-add-remote", { argv: [...argv] }, "Add remote server", raw);
|
|
169
|
+
if (command === "repair-link")
|
|
170
|
+
return intent("server", argv, "remote-link-repair", undefined, "Repair remote link", raw);
|
|
159
171
|
return commandRunIntent(argv, raw);
|
|
160
172
|
}
|
|
161
173
|
function routeGithub(argv, command, rest, raw) {
|
|
162
174
|
if (command === "auth" && (rest[0]?.toLowerCase() ?? "status") === "status" && rest.length <= 1) {
|
|
163
175
|
return intent("server", argv, "refresh", undefined, "Checking GitHub auth", raw);
|
|
164
176
|
}
|
|
177
|
+
if (command === "auth" && rest[0]?.toLowerCase() === "import-gh" && rest.length <= 1) {
|
|
178
|
+
return intent("server", argv, "github-auth-import", undefined, "Import GitHub auth", raw);
|
|
179
|
+
}
|
|
165
180
|
return commandRunIntent(argv, raw);
|
|
166
181
|
}
|
|
167
182
|
function routeDoctor(argv, command, raw) {
|
|
@@ -210,11 +225,16 @@ function intentFromArgv(argv) {
|
|
|
210
225
|
return commandRunIntent(argv);
|
|
211
226
|
}
|
|
212
227
|
if (normalizedGroup === "server")
|
|
213
|
-
return routeServer(argv, normalizedCommand, undefined, "Checking server");
|
|
228
|
+
return routeServer(argv, normalizedCommand, rest, undefined, "Checking server");
|
|
214
229
|
if (normalizedGroup === "github")
|
|
215
230
|
return routeGithub(argv, normalizedCommand, rest);
|
|
216
|
-
if (normalizedGroup === "init")
|
|
217
|
-
|
|
231
|
+
if (normalizedGroup === "init") {
|
|
232
|
+
if (!normalizedCommand)
|
|
233
|
+
return intent("init", argv, "refresh", undefined, "Open init");
|
|
234
|
+
if (rest.includes("--yes") || normalizedCommand === "--yes")
|
|
235
|
+
return intent("init", argv, "init-start", undefined, "Run init");
|
|
236
|
+
return intent("init", argv, "refresh", undefined, "Open init");
|
|
237
|
+
}
|
|
218
238
|
if (normalizedGroup === "doctor")
|
|
219
239
|
return routeDoctor(argv, normalizedCommand);
|
|
220
240
|
if (normalizedGroup === "inbox")
|
|
@@ -67,12 +67,13 @@ var PRIMARY_GROUPS = [
|
|
|
67
67
|
{
|
|
68
68
|
name: "server",
|
|
69
69
|
summary: "Choose, inspect, and start the Rig server that owns tasks and runs.",
|
|
70
|
-
usage: ["rig server <status|list|add|use|start> [options]"],
|
|
70
|
+
usage: ["rig server <status|list|add|use|repair-link|start> [options]"],
|
|
71
71
|
commands: [
|
|
72
|
-
{ command: "status", description: "Show the selected server for this repo.", primary: true },
|
|
72
|
+
{ command: "status", description: "Show the selected server and remote project-root link for this repo.", primary: true },
|
|
73
73
|
{ command: "use local", description: "Switch this repo to the local Rig server.", primary: true },
|
|
74
74
|
{ command: "add <alias> <url>", description: "Save a remote Rig server URL.", primary: true },
|
|
75
75
|
{ command: "use <alias>", description: "Select a saved remote server alias.", primary: true },
|
|
76
|
+
{ command: "repair-link [--prepare|--backfill-only] [--repo owner/repo]", description: "Backfill or prepare the selected remote's server-host checkout link.", primary: true },
|
|
76
77
|
{ command: "list", description: "List saved local/remote server aliases.", primary: true },
|
|
77
78
|
{ command: "start [--host <host>] [--port <n>]", description: "Start a local rig-server process." }
|
|
78
79
|
],
|
|
@@ -80,6 +81,7 @@ var PRIMARY_GROUPS = [
|
|
|
80
81
|
"rig server status",
|
|
81
82
|
"rig server add prod https://where.rig-does.work",
|
|
82
83
|
"rig server use prod",
|
|
84
|
+
"rig server repair-link --repo owner/repo",
|
|
83
85
|
"rig server use local",
|
|
84
86
|
"rig server start --port 3773"
|
|
85
87
|
],
|
|
@@ -2,7 +2,7 @@ import type { StyledText } from "@opentui/core";
|
|
|
2
2
|
import type { RunnerContext } from "../runner";
|
|
3
3
|
import type { StageLayout } from "./layout";
|
|
4
4
|
export type AppSceneId = "main" | "fleet" | "tasks" | "inbox" | "server" | "init" | "doctor" | "command" | "run-detail" | "handoff" | "pi" | "plugin" | "repo" | "workspace" | "inspect" | "family" | "help" | "error";
|
|
5
|
-
export type AppActionKind = "none" | "refresh" | "navigate" | "task-run-next" | "task-run-id" | "run-stop" | "run-attach" | "run-steer" | "inbox-approve" | "inbox-reject" | "inbox-answer" | "init-start" | "command-run" | "doctor-run";
|
|
5
|
+
export type AppActionKind = "none" | "refresh" | "navigate" | "task-run-next" | "task-run-id" | "run-stop" | "run-attach" | "run-steer" | "inbox-approve" | "inbox-reject" | "inbox-answer" | "init-start" | "server-use-local" | "server-use-remote" | "server-add-remote" | "github-auth-import" | "remote-link-repair" | "command-run" | "doctor-run";
|
|
6
6
|
export type AppIntent = {
|
|
7
7
|
readonly scene: AppSceneId;
|
|
8
8
|
readonly argv: readonly string[];
|
|
@@ -46,4 +46,13 @@ export declare function formatConnectionStatus(selected: string, connections: Re
|
|
|
46
46
|
kind: string;
|
|
47
47
|
baseUrl?: string;
|
|
48
48
|
mode?: string;
|
|
49
|
-
}
|
|
49
|
+
}>, repo?: {
|
|
50
|
+
project?: string;
|
|
51
|
+
serverProjectRoot?: string;
|
|
52
|
+
} | null, remoteProjectLink?: {
|
|
53
|
+
ok?: boolean;
|
|
54
|
+
status?: string;
|
|
55
|
+
serverProjectRoot?: string;
|
|
56
|
+
message?: string;
|
|
57
|
+
hint?: string;
|
|
58
|
+
} | null): string;
|
|
@@ -415,16 +415,19 @@ function formatConnectionList(connections) {
|
|
|
415
415
|
return [formatSection("Rig servers", `${rows.length} available`), `${pc.bold(pad("ALIAS", aliasWidth))} ${pc.bold("KIND")} ${pc.bold("TARGET")}`, ...lines, "", ...formatNextSteps(["Select one: `rig server use <alias|local>`"])].join(`
|
|
416
416
|
`);
|
|
417
417
|
}
|
|
418
|
-
function formatConnectionStatus(selected, connections) {
|
|
418
|
+
function formatConnectionStatus(selected, connections, repo, remoteProjectLink) {
|
|
419
419
|
const connection = selected === "local" ? { kind: "local", mode: "auto" } : connections[selected];
|
|
420
420
|
const target = !connection ? "not configured" : connection.kind === "remote" ? connection.baseUrl : "local";
|
|
421
|
+
const rootLabel = remoteProjectLink ? remoteProjectLink.ok ? `ready \xB7 ${remoteProjectLink.serverProjectRoot ?? repo?.serverProjectRoot ?? "linked"}` : `${remoteProjectLink.status ?? "needs repair"} \xB7 ${remoteProjectLink.hint ?? remoteProjectLink.message ?? "run rig server repair-link"}` : repo?.serverProjectRoot ? repo.serverProjectRoot : "not required";
|
|
421
422
|
return [
|
|
422
423
|
formatSection("Rig server", "selected for this repo"),
|
|
423
424
|
`${themeFaint("\u2502")} ${themeDim("selected ")} ${pc.bold(selected)}`,
|
|
424
425
|
`${themeFaint("\u2502")} ${themeDim("kind ")} ${formatStatusPill(connection?.kind ?? "unknown")}`,
|
|
425
426
|
`${themeFaint("\u2502")} ${themeDim("target ")} ${target ?? "not configured"}`,
|
|
427
|
+
...repo?.project ? [`${themeFaint("\u2502")} ${themeDim("project ")} ${repo.project}`] : [],
|
|
428
|
+
...connection?.kind === "remote" ? [`${themeFaint("\u2502")} ${themeDim("root ")} ${rootLabel}`] : [],
|
|
426
429
|
"",
|
|
427
|
-
...formatNextSteps(["Change: `rig server use <alias|local>`", "List saved servers: `rig server list`"])
|
|
430
|
+
...formatNextSteps(remoteProjectLink && !remoteProjectLink.ok ? ["Repair remote link: `rig server repair-link`", "Authenticate first if needed: `rig github auth import-gh`"] : ["Change: `rig server use <alias|local>`", "List saved servers: `rig server list`"])
|
|
428
431
|
].join(`
|
|
429
432
|
`);
|
|
430
433
|
}
|
|
@@ -17,6 +17,10 @@ export type RepoConnectionState = {
|
|
|
17
17
|
* Sent as the x-rig-project-root header on every remote request.
|
|
18
18
|
*/
|
|
19
19
|
serverProjectRoot?: string;
|
|
20
|
+
/** The selected alias that produced serverProjectRoot. Prevents cross-alias reuse. */
|
|
21
|
+
serverProjectRootAlias?: string;
|
|
22
|
+
/** The remote base URL that produced serverProjectRoot. Prevents same-alias retarget leaks. */
|
|
23
|
+
serverProjectRootBaseUrl?: string;
|
|
20
24
|
};
|
|
21
25
|
export declare function resolveGlobalConnectionsPath(env?: NodeJS.ProcessEnv): string;
|
|
22
26
|
export declare function resolveRepoConnectionPath(projectRoot: string): string;
|
|
@@ -39,6 +43,12 @@ export declare function resolveSelectedConnection(projectRoot: string, options?:
|
|
|
39
43
|
serverProjectRoot?: string;
|
|
40
44
|
} | null;
|
|
41
45
|
/** Persist the server-host project root this repo scopes to (multi-root serving). */
|
|
42
|
-
export declare function writeRepoServerProjectRoot(projectRoot: string, serverProjectRoot: string
|
|
46
|
+
export declare function writeRepoServerProjectRoot(projectRoot: string, serverProjectRoot: string, metadata?: {
|
|
47
|
+
alias?: string;
|
|
48
|
+
baseUrl?: string;
|
|
49
|
+
project?: string;
|
|
50
|
+
}): void;
|
|
51
|
+
/** Remove a stale server-host root while preserving selected server/repo state. */
|
|
52
|
+
export declare function clearRepoServerProjectRoot(projectRoot: string): void;
|
|
43
53
|
/** Whether this repo's selected connection targets a remote Rig server. */
|
|
44
54
|
export declare function isRemoteConnectionSelected(projectRoot: string): boolean;
|
|
@@ -100,12 +100,28 @@ function readRepoConnection(projectRoot) {
|
|
|
100
100
|
selected,
|
|
101
101
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
102
102
|
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
103
|
-
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
103
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined,
|
|
104
|
+
serverProjectRootAlias: typeof record.serverProjectRootAlias === "string" && record.serverProjectRootAlias.trim() ? record.serverProjectRootAlias.trim() : undefined,
|
|
105
|
+
serverProjectRootBaseUrl: typeof record.serverProjectRootBaseUrl === "string" && record.serverProjectRootBaseUrl.trim() ? record.serverProjectRootBaseUrl.trim().replace(/\/+$/, "") : undefined
|
|
104
106
|
};
|
|
105
107
|
}
|
|
106
108
|
function writeRepoConnection(projectRoot, state) {
|
|
107
109
|
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
108
110
|
}
|
|
111
|
+
function rootAllowedForSelection(repo, connection) {
|
|
112
|
+
const root = repo.serverProjectRoot?.trim();
|
|
113
|
+
if (!root)
|
|
114
|
+
return;
|
|
115
|
+
if (connection.kind === "remote") {
|
|
116
|
+
if (repo.serverProjectRootAlias !== repo.selected)
|
|
117
|
+
return;
|
|
118
|
+
if (repo.serverProjectRootBaseUrl !== connection.baseUrl)
|
|
119
|
+
return;
|
|
120
|
+
} else if (repo.serverProjectRootAlias && repo.serverProjectRootAlias !== repo.selected) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
return root;
|
|
124
|
+
}
|
|
109
125
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
110
126
|
const repo = readRepoConnection(projectRoot);
|
|
111
127
|
if (!repo)
|
|
@@ -117,13 +133,41 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
117
133
|
if (!connection) {
|
|
118
134
|
throw new CliError(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
119
135
|
}
|
|
120
|
-
return { alias: repo.selected, connection, serverProjectRoot: repo
|
|
136
|
+
return { alias: repo.selected, connection, serverProjectRoot: rootAllowedForSelection(repo, connection) };
|
|
137
|
+
}
|
|
138
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot, metadata = {}) {
|
|
139
|
+
const repo = readRepoConnection(projectRoot);
|
|
140
|
+
if (!repo)
|
|
141
|
+
return;
|
|
142
|
+
let inferred = metadata;
|
|
143
|
+
if (!inferred.alias || !inferred.baseUrl) {
|
|
144
|
+
try {
|
|
145
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
146
|
+
if (selected?.connection.kind === "remote") {
|
|
147
|
+
inferred = {
|
|
148
|
+
alias: inferred.alias ?? selected.alias,
|
|
149
|
+
baseUrl: inferred.baseUrl ?? selected.connection.baseUrl
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
} catch {}
|
|
153
|
+
}
|
|
154
|
+
writeRepoConnection(projectRoot, {
|
|
155
|
+
...repo,
|
|
156
|
+
...metadata.project ? { project: metadata.project } : {},
|
|
157
|
+
serverProjectRoot,
|
|
158
|
+
...inferred.alias ? { serverProjectRootAlias: inferred.alias } : {},
|
|
159
|
+
...inferred.baseUrl ? { serverProjectRootBaseUrl: inferred.baseUrl.replace(/\/+$/, "") } : {}
|
|
160
|
+
});
|
|
121
161
|
}
|
|
122
|
-
function
|
|
162
|
+
function clearRepoServerProjectRoot(projectRoot) {
|
|
123
163
|
const repo = readRepoConnection(projectRoot);
|
|
124
164
|
if (!repo)
|
|
125
165
|
return;
|
|
126
|
-
writeRepoConnection(projectRoot, {
|
|
166
|
+
writeRepoConnection(projectRoot, {
|
|
167
|
+
selected: repo.selected,
|
|
168
|
+
...repo.project ? { project: repo.project } : {},
|
|
169
|
+
...repo.linkedAt ? { linkedAt: repo.linkedAt } : {}
|
|
170
|
+
});
|
|
127
171
|
}
|
|
128
172
|
function isRemoteConnectionSelected(projectRoot) {
|
|
129
173
|
return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
|
|
@@ -138,5 +182,6 @@ export {
|
|
|
138
182
|
resolveGlobalConnectionsPath,
|
|
139
183
|
readRepoConnection,
|
|
140
184
|
readGlobalConnections,
|
|
141
|
-
isRemoteConnectionSelected
|
|
185
|
+
isRemoteConnectionSelected,
|
|
186
|
+
clearRepoServerProjectRoot
|
|
142
187
|
};
|