@h-rig/cli 0.0.6-alpha.87 → 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 +1380 -865
- package/dist/src/app/board.js +462 -48
- package/dist/src/app-opentui/adapters/common.d.ts +3 -0
- package/dist/src/app-opentui/adapters/common.js +4 -0
- package/dist/src/app-opentui/adapters/doctor.js +458 -46
- package/dist/src/app-opentui/adapters/family.js +701 -151
- 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.d.ts +7 -0
- package/dist/src/app-opentui/adapters/pi-attach.js +1004 -519
- 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 +621 -549
- package/dist/src/app-opentui/autocomplete.js +4 -2
- package/dist/src/app-opentui/bootstrap.js +1376 -861
- package/dist/src/app-opentui/command-palette.js +37 -10
- package/dist/src/app-opentui/index.js +632 -528
- package/dist/src/app-opentui/intent.js +33 -8
- package/dist/src/app-opentui/keymap.js +43 -414
- package/dist/src/app-opentui/pi-host-child.js +496 -57
- package/dist/src/app-opentui/pi-pty-host.d.ts +14 -64
- package/dist/src/app-opentui/pi-pty-host.js +3 -397
- package/dist/src/app-opentui/react/App.js +144 -469
- package/dist/src/app-opentui/react/ChromeHost.js +44 -415
- package/dist/src/app-opentui/react/launch.js +659 -552
- package/dist/src/app-opentui/react/nav.js +1 -1
- package/dist/src/app-opentui/registry.js +1181 -742
- 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/render/terminal-handoff.d.ts +16 -0
- package/dist/src/app-opentui/render/terminal-handoff.js +14 -0
- package/dist/src/app-opentui/runtime.js +632 -528
- 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 +496 -57
- package/dist/src/commands/_pi-frontend.d.ts +25 -0
- package/dist/src/commands/_pi-frontend.js +497 -57
- 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 +496 -57
- 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 +551 -85
- package/dist/src/commands.js +701 -151
- package/dist/src/index.js +705 -151
- package/dist/src/launcher.js +4 -0
- package/package.json +8 -8
|
@@ -219,7 +219,7 @@ function selectableDeckRow(deck, item) {
|
|
|
219
219
|
var DOCTOR_ACTIONS = [
|
|
220
220
|
{ detail: "run diagnostics now", item: { id: "run", label: "run", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "run diagnostics" } },
|
|
221
221
|
{ detail: "open server controls", item: { id: "server", label: "server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Open server controls" } }, message: "open server controls" } },
|
|
222
|
-
{ detail: "repair project/server/auth link", item: { id: "repair", label: "repair", intent: { scene: "
|
|
222
|
+
{ detail: "repair project/server/auth link", item: { id: "repair", label: "repair", intent: { scene: "init", argv: ["init", "--repair", "--yes"], action: { kind: "init-start", label: "Repair project link" } }, message: "repair project link natively" } },
|
|
223
223
|
{ detail: "open tasks", item: { id: "tasks", label: "tasks", intent: { scene: "tasks", argv: ["tasks"], action: { kind: "refresh", label: "Open tasks" } }, message: "open tasks" } }
|
|
224
224
|
];
|
|
225
225
|
function checks(state) {
|
|
@@ -158,6 +158,48 @@ function errorDrone(tick) {
|
|
|
158
158
|
}));
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
// packages/cli/src/app-opentui/remote-link.ts
|
|
162
|
+
var REPAIRABLE_REMOTE_LINK_STATUSES = new Set([
|
|
163
|
+
"auth_required",
|
|
164
|
+
"project_not_registered",
|
|
165
|
+
"no_server_checkout",
|
|
166
|
+
"invalid_root",
|
|
167
|
+
"needs_prepare",
|
|
168
|
+
"error"
|
|
169
|
+
]);
|
|
170
|
+
function remoteProjectLinkError(message) {
|
|
171
|
+
return /no server-host project root link|remote project(?:-root)? link|x-rig-project-root|serverProjectRoot/i.test(message ?? "");
|
|
172
|
+
}
|
|
173
|
+
function serverRecordForRemoteLink(state) {
|
|
174
|
+
const server = state.data.server;
|
|
175
|
+
return server && typeof server === "object" && !Array.isArray(server) ? server : null;
|
|
176
|
+
}
|
|
177
|
+
function remoteProjectLinkState(state) {
|
|
178
|
+
const serverRecord = serverRecordForRemoteLink(state);
|
|
179
|
+
if (!serverRecord || serverRecord.kind !== "remote")
|
|
180
|
+
return null;
|
|
181
|
+
const link = serverRecord.remoteProjectLink;
|
|
182
|
+
if (link && typeof link === "object" && !Array.isArray(link))
|
|
183
|
+
return link;
|
|
184
|
+
const direct = state.data.remoteProjectLink;
|
|
185
|
+
if (direct && typeof direct === "object" && !Array.isArray(direct))
|
|
186
|
+
return direct;
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
function shouldOfferRemoteLinkRepair(state, message) {
|
|
190
|
+
const link = remoteProjectLinkState(state);
|
|
191
|
+
if (link) {
|
|
192
|
+
if (link.ok === true)
|
|
193
|
+
return false;
|
|
194
|
+
const status = link.status ?? "";
|
|
195
|
+
return REPAIRABLE_REMOTE_LINK_STATUSES.has(status);
|
|
196
|
+
}
|
|
197
|
+
const serverRecord = serverRecordForRemoteLink(state);
|
|
198
|
+
if (serverRecord && serverRecord.kind !== "remote")
|
|
199
|
+
return false;
|
|
200
|
+
return remoteProjectLinkError(message ?? state.error?.message);
|
|
201
|
+
}
|
|
202
|
+
|
|
161
203
|
// packages/cli/src/app-opentui/render/scene.ts
|
|
162
204
|
function line(text, options = {}) {
|
|
163
205
|
return { text, ...options };
|
|
@@ -185,13 +227,17 @@ function selectableDeckRow(deck, item) {
|
|
|
185
227
|
var ERROR_ACTIONS = [
|
|
186
228
|
{ detail: "return to dashboard", item: { id: "main", label: "main", intent: { scene: "main", argv: [], action: { kind: "refresh", label: "Back to dashboard" } }, message: "back to dashboard" } },
|
|
187
229
|
{ detail: "run diagnostics", item: { id: "doctor", label: "doctor", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "run diagnostics" } },
|
|
188
|
-
{ detail: "open server controls", item: { id: "server", label: "server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Open server controls" } }, message: "open server controls" } }
|
|
189
|
-
{ detail: "repair project/server/auth link", item: { id: "repair", label: "repair", intent: { scene: "command", argv: ["init", "--repair"], action: { kind: "command-run", label: "Repair project link" } }, message: "repair project link" } }
|
|
230
|
+
{ detail: "open server controls", item: { id: "server", label: "server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Open server controls" } }, message: "open server controls" } }
|
|
190
231
|
];
|
|
232
|
+
var ERROR_REPAIR_LINK_ACTION = { detail: "backfill or prepare selected remote project-root 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: "server" } } }, message: "repair remote project link" } };
|
|
233
|
+
function errorActions(state) {
|
|
234
|
+
return shouldOfferRemoteLinkRepair(state, state.error?.message) ? [...ERROR_ACTIONS, ERROR_REPAIR_LINK_ACTION] : ERROR_ACTIONS;
|
|
235
|
+
}
|
|
191
236
|
function renderErrorScene(state) {
|
|
192
237
|
const message = state.error?.message ?? "Unknown app error";
|
|
193
238
|
const hint = state.error?.hint ?? "Use the actions below to recover.";
|
|
194
|
-
const
|
|
239
|
+
const actions = errorActions(state);
|
|
240
|
+
const selected = Math.max(0, Math.min(actions.length - 1, state.selection.index));
|
|
195
241
|
return makeSceneFrame({
|
|
196
242
|
scene: "error",
|
|
197
243
|
title: "Error",
|
|
@@ -209,7 +255,7 @@ function renderErrorScene(state) {
|
|
|
209
255
|
...hint ? [line(hint, { fg: RIG_UI.yellow })] : [],
|
|
210
256
|
line("", { fg: RIG_UI.ink3 }),
|
|
211
257
|
line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
|
|
212
|
-
...
|
|
258
|
+
...actions.map(({ detail, item }, index) => selectableDeckRow({ label: item.label, detail, index, active: selected === index }, item))
|
|
213
259
|
],
|
|
214
260
|
backgroundColor: RIG_UI.panel,
|
|
215
261
|
backgroundAlpha: 184,
|
|
@@ -284,6 +284,48 @@ function withSelectable(line2, item) {
|
|
|
284
284
|
return { ...line2, selectable: item, activateOnClick: true };
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
+
// packages/cli/src/app-opentui/remote-link.ts
|
|
288
|
+
var REPAIRABLE_REMOTE_LINK_STATUSES = new Set([
|
|
289
|
+
"auth_required",
|
|
290
|
+
"project_not_registered",
|
|
291
|
+
"no_server_checkout",
|
|
292
|
+
"invalid_root",
|
|
293
|
+
"needs_prepare",
|
|
294
|
+
"error"
|
|
295
|
+
]);
|
|
296
|
+
function remoteProjectLinkError(message) {
|
|
297
|
+
return /no server-host project root link|remote project(?:-root)? link|x-rig-project-root|serverProjectRoot/i.test(message ?? "");
|
|
298
|
+
}
|
|
299
|
+
function serverRecordForRemoteLink(state) {
|
|
300
|
+
const server = state.data.server;
|
|
301
|
+
return server && typeof server === "object" && !Array.isArray(server) ? server : null;
|
|
302
|
+
}
|
|
303
|
+
function remoteProjectLinkState(state) {
|
|
304
|
+
const serverRecord = serverRecordForRemoteLink(state);
|
|
305
|
+
if (!serverRecord || serverRecord.kind !== "remote")
|
|
306
|
+
return null;
|
|
307
|
+
const link = serverRecord.remoteProjectLink;
|
|
308
|
+
if (link && typeof link === "object" && !Array.isArray(link))
|
|
309
|
+
return link;
|
|
310
|
+
const direct = state.data.remoteProjectLink;
|
|
311
|
+
if (direct && typeof direct === "object" && !Array.isArray(direct))
|
|
312
|
+
return direct;
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
function shouldOfferRemoteLinkRepair(state, message) {
|
|
316
|
+
const link = remoteProjectLinkState(state);
|
|
317
|
+
if (link) {
|
|
318
|
+
if (link.ok === true)
|
|
319
|
+
return false;
|
|
320
|
+
const status = link.status ?? "";
|
|
321
|
+
return REPAIRABLE_REMOTE_LINK_STATUSES.has(status);
|
|
322
|
+
}
|
|
323
|
+
const serverRecord = serverRecordForRemoteLink(state);
|
|
324
|
+
if (serverRecord && serverRecord.kind !== "remote")
|
|
325
|
+
return false;
|
|
326
|
+
return remoteProjectLinkError(message ?? state.error?.message);
|
|
327
|
+
}
|
|
328
|
+
|
|
287
329
|
// packages/cli/src/app-opentui/fleet-stats.ts
|
|
288
330
|
var ACTIVE_STATUSES = new Set([
|
|
289
331
|
"running",
|
|
@@ -2092,13 +2134,18 @@ function overviewBody(state, family, snap, layout) {
|
|
|
2092
2134
|
function familyRecoveryRows(state, family, errorMessage) {
|
|
2093
2135
|
const selected = Math.max(0, state.selection.index);
|
|
2094
2136
|
const intro = errorMessage ? line(`\u2717 couldn't load ${family || "this family"} \u2014 ${errorMessage}`, { fg: RIG_UI.red, bold: true }) : line(`No data for ${family || "this family"} yet.`, { fg: RIG_UI.ink2 });
|
|
2095
|
-
|
|
2137
|
+
const repairRemote = shouldOfferRemoteLinkRepair(state, errorMessage);
|
|
2138
|
+
const rows = [
|
|
2096
2139
|
intro,
|
|
2097
|
-
blank()
|
|
2098
|
-
selectableDeckRow({ label: "retry", detail: `reload ${family || "the family"}`, index: 0, active: selected === 0 }, { id: "family-retry", label: "retry", intent: { scene: "family", argv: [family], action: { kind: "refresh", label: `Reload ${family}`, payload: { family } } }, message: "reload this family" }),
|
|
2099
|
-
selectableDeckRow({ label: "cockpit", detail: "back to the project cockpit", index: 1, active: selected === 1 }, { id: "family-home", label: "cockpit", intent: { scene: "main", argv: [], action: { kind: "refresh", label: "Home" } }, message: "back to the cockpit" }),
|
|
2100
|
-
selectableDeckRow({ label: "doctor", detail: "diagnose project/setup", index: 2, active: selected === 2 }, { id: "family-doctor", label: "doctor", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "diagnose setup" })
|
|
2140
|
+
blank()
|
|
2101
2141
|
];
|
|
2142
|
+
let index = 0;
|
|
2143
|
+
if (repairRemote) {
|
|
2144
|
+
rows.push(selectableDeckRow({ label: "repair link", detail: "backfill/prepare selected remote checkout link", index, active: selected === index }, { id: "family-repair-link", label: "repair link", intent: { scene: "server", argv: ["server", "repair-link"], action: { kind: "remote-link-repair", label: "Repair remote link", payload: { returnScene: "family", family } } }, message: "repair remote project link" }));
|
|
2145
|
+
index += 1;
|
|
2146
|
+
}
|
|
2147
|
+
rows.push(selectableDeckRow({ label: "retry", detail: `reload ${family || "the family"}`, index, active: selected === index }, { id: "family-retry", label: "retry", intent: { scene: "family", argv: [family], action: { kind: "refresh", label: `Reload ${family}`, payload: { family } } }, message: "reload this family" }), selectableDeckRow({ label: "cockpit", detail: "back to the project cockpit", index: index + 1, active: selected === index + 1 }, { id: "family-home", label: "cockpit", intent: { scene: "main", argv: [], action: { kind: "refresh", label: "Home" } }, message: "back to the cockpit" }), selectableDeckRow({ label: "doctor", detail: "diagnose project/setup", index: index + 2, active: selected === index + 2 }, { id: "family-doctor", label: "doctor", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "diagnose setup" }));
|
|
2148
|
+
return rows;
|
|
2102
2149
|
}
|
|
2103
2150
|
function renderFamilyScene(state, layout) {
|
|
2104
2151
|
const family = typeof state.data.familyName === "string" ? state.data.familyName : "";
|
|
@@ -2107,9 +2154,16 @@ function renderFamilyScene(state, layout) {
|
|
|
2107
2154
|
const loading = state.status === "loading" && !snap;
|
|
2108
2155
|
const errorMessage = state.error?.message ?? lastRefreshError(state) ?? undefined;
|
|
2109
2156
|
const probeFailed = snap ? snap.state.ran === false && Boolean(state.error) : Boolean(state.error);
|
|
2157
|
+
const selected = Math.max(0, state.selection.index);
|
|
2110
2158
|
const body = [
|
|
2111
2159
|
...loading ? loadingRows(family || "family", state.tick) : snap ? [
|
|
2112
|
-
...probeFailed && errorMessage ? [
|
|
2160
|
+
...probeFailed && errorMessage ? [
|
|
2161
|
+
...errorBanner(errorMessage),
|
|
2162
|
+
...shouldOfferRemoteLinkRepair(state, errorMessage) ? [
|
|
2163
|
+
selectableDeckRow({ label: "repair link", detail: "backfill/prepare selected remote checkout link", index: 0, active: selected === 0 }, { id: "family-repair-link", label: "repair link", intent: { scene: "server", argv: ["server", "repair-link"], action: { kind: "remote-link-repair", label: "Repair remote link", payload: { returnScene: "family", family } } }, message: "repair remote project link" })
|
|
2164
|
+
] : [],
|
|
2165
|
+
blank()
|
|
2166
|
+
] : [],
|
|
2113
2167
|
...current.subId ? formView(state, family, snap, current).lines : overviewBody(state, family, snap, layout)
|
|
2114
2168
|
] : familyRecoveryRows(state, family, errorMessage)
|
|
2115
2169
|
];
|
|
@@ -435,13 +435,55 @@ function statusSegments(stats, width) {
|
|
|
435
435
|
};
|
|
436
436
|
}
|
|
437
437
|
|
|
438
|
+
// packages/cli/src/app-opentui/remote-link.ts
|
|
439
|
+
var REPAIRABLE_REMOTE_LINK_STATUSES = new Set([
|
|
440
|
+
"auth_required",
|
|
441
|
+
"project_not_registered",
|
|
442
|
+
"no_server_checkout",
|
|
443
|
+
"invalid_root",
|
|
444
|
+
"needs_prepare",
|
|
445
|
+
"error"
|
|
446
|
+
]);
|
|
447
|
+
function remoteProjectLinkError(message) {
|
|
448
|
+
return /no server-host project root link|remote project(?:-root)? link|x-rig-project-root|serverProjectRoot/i.test(message ?? "");
|
|
449
|
+
}
|
|
450
|
+
function serverRecordForRemoteLink(state) {
|
|
451
|
+
const server = state.data.server;
|
|
452
|
+
return server && typeof server === "object" && !Array.isArray(server) ? server : null;
|
|
453
|
+
}
|
|
454
|
+
function remoteProjectLinkState(state) {
|
|
455
|
+
const serverRecord = serverRecordForRemoteLink(state);
|
|
456
|
+
if (!serverRecord || serverRecord.kind !== "remote")
|
|
457
|
+
return null;
|
|
458
|
+
const link = serverRecord.remoteProjectLink;
|
|
459
|
+
if (link && typeof link === "object" && !Array.isArray(link))
|
|
460
|
+
return link;
|
|
461
|
+
const direct = state.data.remoteProjectLink;
|
|
462
|
+
if (direct && typeof direct === "object" && !Array.isArray(direct))
|
|
463
|
+
return direct;
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
function shouldOfferRemoteLinkRepair(state, message) {
|
|
467
|
+
const link = remoteProjectLinkState(state);
|
|
468
|
+
if (link) {
|
|
469
|
+
if (link.ok === true)
|
|
470
|
+
return false;
|
|
471
|
+
const status = link.status ?? "";
|
|
472
|
+
return REPAIRABLE_REMOTE_LINK_STATUSES.has(status);
|
|
473
|
+
}
|
|
474
|
+
const serverRecord = serverRecordForRemoteLink(state);
|
|
475
|
+
if (serverRecord && serverRecord.kind !== "remote")
|
|
476
|
+
return false;
|
|
477
|
+
return remoteProjectLinkError(message ?? state.error?.message);
|
|
478
|
+
}
|
|
479
|
+
|
|
438
480
|
// packages/cli/src/app-opentui/scenes/fleet.ts
|
|
439
|
-
var
|
|
481
|
+
var BASE_FLEET_RECOVERY_ITEMS = [
|
|
440
482
|
{ id: "tasks", label: "tasks", intent: { scene: "tasks", argv: ["tasks"], action: { kind: "refresh", label: "Open tasks" } }, message: "open tasks and dispatch work" },
|
|
441
|
-
{ id: "repair", label: "repair", intent: { scene: "command", argv: ["init", "--repair"], action: { kind: "command-run", label: "Repair project link" } }, message: "repair project/server/auth link" },
|
|
442
483
|
{ id: "server", label: "server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Open server controls" } }, message: "server controls" },
|
|
443
484
|
{ id: "doctor", label: "doctor", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "diagnose project" }
|
|
444
485
|
];
|
|
486
|
+
var FLEET_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: "fleet" } } }, message: "backfill or prepare remote project link" };
|
|
445
487
|
var COL = {
|
|
446
488
|
glyph: 1,
|
|
447
489
|
runId: 8,
|
|
@@ -606,33 +648,43 @@ function runRow(width, run, active, optimistic) {
|
|
|
606
648
|
const item = runSelectableItem(run);
|
|
607
649
|
return item ? withSelectable(row, item) : row;
|
|
608
650
|
}
|
|
609
|
-
var
|
|
651
|
+
var BASE_RECOVERY_DECKS = [
|
|
610
652
|
{ label: "tasks", detail: "open tasks and dispatch work" },
|
|
611
|
-
{ label: "repair", detail: "repair project/server/auth link" },
|
|
612
653
|
{ label: "server", detail: "open server controls" },
|
|
613
654
|
{ label: "doctor", detail: "diagnose project" }
|
|
614
655
|
];
|
|
615
|
-
function
|
|
656
|
+
function fleetRecoveryItems(state) {
|
|
657
|
+
if (!shouldOfferRemoteLinkRepair(state))
|
|
658
|
+
return BASE_FLEET_RECOVERY_ITEMS;
|
|
659
|
+
return [BASE_FLEET_RECOVERY_ITEMS[0], FLEET_REPAIR_LINK_ITEM, ...BASE_FLEET_RECOVERY_ITEMS.slice(1)];
|
|
660
|
+
}
|
|
661
|
+
function fleetRecoveryDecks(state) {
|
|
662
|
+
if (!shouldOfferRemoteLinkRepair(state))
|
|
663
|
+
return BASE_RECOVERY_DECKS;
|
|
664
|
+
return [BASE_RECOVERY_DECKS[0], { label: "repair link", detail: "backfill/prepare remote project-root link" }, ...BASE_RECOVERY_DECKS.slice(1)];
|
|
665
|
+
}
|
|
666
|
+
function recoveryRows(state, selected, reason) {
|
|
667
|
+
const items = fleetRecoveryItems(state);
|
|
616
668
|
return [
|
|
617
669
|
...reason ? [line(reason, { fg: RIG_UI.yellow }), line("", { fg: RIG_UI.ink3 })] : [],
|
|
618
670
|
line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
|
|
619
|
-
...
|
|
671
|
+
...fleetRecoveryDecks(state).map((deck, index) => selectableDeckRow({ ...deck, index, active: selected === index }, items[index]))
|
|
620
672
|
];
|
|
621
673
|
}
|
|
622
|
-
function emptyRows(query, total, selected) {
|
|
674
|
+
function emptyRows(state, query, total, selected) {
|
|
623
675
|
if (!query.trim() && total === 0)
|
|
624
|
-
return recoveryRows(selected, "No runs yet.");
|
|
676
|
+
return recoveryRows(state, selected, "No runs yet.");
|
|
625
677
|
if (query.trim() && total > 0) {
|
|
626
678
|
return [
|
|
627
|
-
...recoveryRows(selected, `No matching runs for ${JSON.stringify(query)} (${total} total).`)
|
|
679
|
+
...recoveryRows(state, selected, `No matching runs for ${JSON.stringify(query)} (${total} total).`)
|
|
628
680
|
];
|
|
629
681
|
}
|
|
630
|
-
return recoveryRows(selected, "No runs are visible with this filter.");
|
|
682
|
+
return recoveryRows(state, selected, "No runs are visible with this filter.");
|
|
631
683
|
}
|
|
632
684
|
function degradedRows(state, selected) {
|
|
633
685
|
if (!state.error)
|
|
634
686
|
return [];
|
|
635
|
-
return recoveryRows(selected, state.error.message);
|
|
687
|
+
return recoveryRows(state, selected, state.error.message);
|
|
636
688
|
}
|
|
637
689
|
function renderFleetScene(state, layout) {
|
|
638
690
|
const width = panelWidth(layout);
|
|
@@ -642,7 +694,7 @@ function renderFleetScene(state, layout) {
|
|
|
642
694
|
const runs = sortRuns(filterRunsForSearch(allRuns, query), spec);
|
|
643
695
|
const selectedRun = typeof state.data.selectedRunId === "string" && runs.some((run) => run.runId === state.data.selectedRunId) ? state.data.selectedRunId : runs[0]?.runId;
|
|
644
696
|
const selectedIndex = Math.max(0, runs.findIndex((run) => run.runId === selectedRun));
|
|
645
|
-
const recoveryIndex = Math.max(0, Math.min(
|
|
697
|
+
const recoveryIndex = Math.max(0, Math.min(fleetRecoveryItems(state).length - 1, state.selection.index));
|
|
646
698
|
const optimistic = optimisticStatuses(state);
|
|
647
699
|
const updatedAgo = updatedAgoLabel(state);
|
|
648
700
|
const panelTop = 0;
|
|
@@ -674,7 +726,7 @@ function renderFleetScene(state, layout) {
|
|
|
674
726
|
...runs.map((run, index) => runRow(contentWidth, run, index === selectedIndex, optimistic.get(run.runId)))
|
|
675
727
|
] : state.error ? degradedRows(state, recoveryIndex) : loading ? loadingRows("runs", state.tick) : [
|
|
676
728
|
...query.trim() ? [line(searchSummary("runs", query, runs.length, allRuns.length), { fg: RIG_UI.ink4 }), line("", { fg: RIG_UI.ink3 })] : [],
|
|
677
|
-
...emptyRows(query, allRuns.length, recoveryIndex)
|
|
729
|
+
...emptyRows(state, query, allRuns.length, recoveryIndex)
|
|
678
730
|
]
|
|
679
731
|
];
|
|
680
732
|
return makeSceneFrame({
|
|
@@ -143,12 +143,13 @@ var PRIMARY_GROUPS = [
|
|
|
143
143
|
{
|
|
144
144
|
name: "server",
|
|
145
145
|
summary: "Choose, inspect, and start the Rig server that owns tasks and runs.",
|
|
146
|
-
usage: ["rig server <status|list|add|use|start> [options]"],
|
|
146
|
+
usage: ["rig server <status|list|add|use|repair-link|start> [options]"],
|
|
147
147
|
commands: [
|
|
148
|
-
{ command: "status", description: "Show the selected server for this repo.", primary: true },
|
|
148
|
+
{ command: "status", description: "Show the selected server and remote project-root link for this repo.", primary: true },
|
|
149
149
|
{ command: "use local", description: "Switch this repo to the local Rig server.", primary: true },
|
|
150
150
|
{ command: "add <alias> <url>", description: "Save a remote Rig server URL.", primary: true },
|
|
151
151
|
{ command: "use <alias>", description: "Select a saved remote server alias.", primary: true },
|
|
152
|
+
{ command: "repair-link [--prepare|--backfill-only] [--repo owner/repo]", description: "Backfill or prepare the selected remote's server-host checkout link.", primary: true },
|
|
152
153
|
{ command: "list", description: "List saved local/remote server aliases.", primary: true },
|
|
153
154
|
{ command: "start [--host <host>] [--port <n>]", description: "Start a local rig-server process." }
|
|
154
155
|
],
|
|
@@ -156,6 +157,7 @@ var PRIMARY_GROUPS = [
|
|
|
156
157
|
"rig server status",
|
|
157
158
|
"rig server add prod https://where.rig-does.work",
|
|
158
159
|
"rig server use prod",
|
|
160
|
+
"rig server repair-link --repo owner/repo",
|
|
159
161
|
"rig server use local",
|
|
160
162
|
"rig server start --port 3773"
|
|
161
163
|
],
|
|
@@ -79,19 +79,19 @@ function selectableDeckRow(deck, item) {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
// packages/cli/src/app-opentui/scenes/init.ts
|
|
82
|
-
var STEP_CONFIG_RUN = { id: "step-config", label: "run
|
|
82
|
+
var STEP_CONFIG_RUN = { id: "step-config", label: "run init", intent: { scene: "init", argv: ["init", "--yes"], action: { kind: "init-start", label: "Run init" } }, message: "initialize without embedded CLI prompts" };
|
|
83
83
|
var STEP_SERVER = { id: "step-server", label: "select server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Choose server" } }, message: "select local or remote server" };
|
|
84
|
-
var STEP_AUTH = { id: "step-auth", label: "connect GitHub", intent: { scene: "
|
|
85
|
-
var STEP_TASKS = { id: "step-tasks", label: "
|
|
84
|
+
var STEP_AUTH = { id: "step-auth", label: "connect GitHub", intent: { scene: "server", argv: ["github", "auth", "status"], action: { kind: "refresh", label: "Open GitHub auth" } }, message: "open native server/auth controls" };
|
|
85
|
+
var STEP_TASKS = { id: "step-tasks", label: "repair task source", intent: { scene: "init", argv: ["init", "--repair", "--yes"], action: { kind: "init-start", label: "Repair task source" } }, message: "repair generated task-source config without embedded CLI prompts" };
|
|
86
86
|
var SETUP_STEPS = [
|
|
87
|
-
{ id: "config", label: "Project config", detail: "rig.config + private state", done: (s) => s.configured, cta: "Run
|
|
87
|
+
{ id: "config", label: "Project config", detail: "rig.config + private state", done: (s) => s.configured, cta: "Run init", item: STEP_CONFIG_RUN },
|
|
88
88
|
{ id: "server", label: "Server target", detail: "local or remote, reachable", done: (s) => s.serverReachable, cta: "Choose a server", item: STEP_SERVER },
|
|
89
89
|
{ id: "auth", label: "GitHub auth", detail: "signed in on the selected server", done: (s) => s.authSignedIn, cta: "Connect GitHub", item: STEP_AUTH },
|
|
90
90
|
{ id: "tasks", label: "Task source", detail: "where work comes from", done: (s) => s.taskSourceReady, cta: "Pick a task source", item: STEP_TASKS }
|
|
91
91
|
];
|
|
92
92
|
var SECONDARY_ACTIONS = [
|
|
93
|
-
{ label: "repair", detail: "reconfigure generated config and private state", item: { id: "repair", label: "repair", intent: { scene: "
|
|
94
|
-
{ label: "demo", detail: "seed an offline demo task source to explore", item: { id: "demo", label: "demo", intent: { scene: "
|
|
93
|
+
{ label: "repair", detail: "reconfigure generated config and private state", item: { id: "repair", label: "repair", intent: { scene: "init", argv: ["init", "--repair", "--yes"], action: { kind: "init-start", label: "Reconfigure project" } }, message: "verify or rewrite generated config natively" } },
|
|
94
|
+
{ label: "demo", detail: "seed an offline demo task source to explore", item: { id: "demo", label: "demo", intent: { scene: "init", argv: ["init", "--demo", "--repair", "--yes"], action: { kind: "init-start", label: "Seed demo project" } }, message: "offline demo project without embedded CLI prompts" } },
|
|
95
95
|
{ label: "doctor", detail: "diagnose what is still missing", item: { id: "doctor", label: "doctor", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "verify setup" } }
|
|
96
96
|
];
|
|
97
97
|
function record(value) {
|
|
@@ -135,11 +135,11 @@ function renderInitScene(state, layout) {
|
|
|
135
135
|
const allDone = completed === SETUP_STEPS.length;
|
|
136
136
|
const nextStep = SETUP_STEPS.find((step) => !step.done(signals));
|
|
137
137
|
const selected = Math.max(0, state.selection.index);
|
|
138
|
-
const primary = nextStep ? selectableDeckRow({ label: "next", detail: `${nextStep.cta} \u2014 recommended`, active: selected === 0 }, { ...nextStep.item, id: "init-primary" }) : selectableDeckRow({ label: "verify", detail: "
|
|
138
|
+
const primary = nextStep ? selectableDeckRow({ label: "next", detail: `${nextStep.cta} \u2014 recommended`, active: selected === 0 }, { ...nextStep.item, id: "init-primary" }) : selectableDeckRow({ label: "verify", detail: "Init complete \u2014 run doctor to confirm", active: selected === 0 }, { id: "init-primary", label: "verify", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "verify setup end-to-end" });
|
|
139
139
|
const secondaryRows = SECONDARY_ACTIONS.map(({ label, detail, item }, index) => selectableDeckRow({ label, detail, active: selected === index + 1 }, item));
|
|
140
140
|
const progress = allDone ? "all set" : `${completed} of ${SETUP_STEPS.length} complete`;
|
|
141
141
|
const panelLines = [
|
|
142
|
-
line(allDone ? "
|
|
142
|
+
line(allDone ? "INIT \xB7 ready to go" : "INIT \xB7 let's get you running", { fg: allDone ? RIG_UI.limeDim : RIG_UI.ink, bold: true }),
|
|
143
143
|
line(`progress ${progress}`, { fg: allDone ? RIG_UI.limeDim : RIG_UI.ink2 }),
|
|
144
144
|
blank(),
|
|
145
145
|
line("CHECKLIST", { fg: RIG_UI.ink3, bold: true }),
|
|
@@ -151,11 +151,11 @@ function renderInitScene(state, layout) {
|
|
|
151
151
|
line("OTHER PATHS", { fg: RIG_UI.ink3, bold: true }),
|
|
152
152
|
...secondaryRows,
|
|
153
153
|
blank(),
|
|
154
|
-
line("enter/click runs the highlighted action \xB7 esc goes back \xB7 tab switches sections", { fg: RIG_UI.ink4 })
|
|
154
|
+
line("enter/click runs the highlighted native action \xB7 esc goes back \xB7 tab switches sections", { fg: RIG_UI.ink4 })
|
|
155
155
|
];
|
|
156
156
|
return makeSceneFrame({
|
|
157
157
|
scene: "init",
|
|
158
|
-
title: "
|
|
158
|
+
title: "Init",
|
|
159
159
|
lines: [],
|
|
160
160
|
panels: [{
|
|
161
161
|
id: "init-setup",
|
|
@@ -168,11 +168,11 @@ function renderInitScene(state, layout) {
|
|
|
168
168
|
opacity: 0.98,
|
|
169
169
|
border: false,
|
|
170
170
|
chrome: "ad-terminal",
|
|
171
|
-
headerText: "rig
|
|
171
|
+
headerText: "rig init \xB7 guided onboarding",
|
|
172
172
|
paddingX: 5,
|
|
173
173
|
paddingY: 2
|
|
174
174
|
}],
|
|
175
|
-
footer: { message: allDone ? "
|
|
175
|
+
footer: { message: allDone ? "init complete" : "init in progress" },
|
|
176
176
|
live: !allDone || state.status === "action"
|
|
177
177
|
});
|
|
178
178
|
}
|
|
@@ -144,7 +144,7 @@ function sparklineString(values, width) {
|
|
|
144
144
|
|
|
145
145
|
// packages/cli/src/app-opentui/scenes/main.ts
|
|
146
146
|
var CONFIGURED_MENU = [
|
|
147
|
-
["init", "setup, reconfigure, repair project state", { id: "init", label: "init", intent: { scene: "
|
|
147
|
+
["init", "guided setup, reconfigure, repair project state", { id: "init", label: "init", intent: { scene: "init", argv: ["init"], action: { kind: "refresh", label: "Open init" } }, message: "open native init flow" }],
|
|
148
148
|
["server", "local/remote target, auth, linkage", { id: "server", label: "server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Checking server" } }, message: "check selected server" }],
|
|
149
149
|
["github", "auth, selected repo, stored token", { id: "github", label: "github", intent: { scene: "server", argv: ["github", "auth", "status"], action: { kind: "refresh", label: "Checking GitHub auth" } }, message: "check GitHub auth" }],
|
|
150
150
|
["tasks", "browse, filter, dispatch, attach active work", { id: "tasks", label: "tasks", intent: { scene: "tasks", argv: ["tasks"], action: { kind: "refresh", label: "Opening tasks" } }, message: "selected tasks" }],
|
|
@@ -163,10 +163,10 @@ var CONFIGURED_MENU = [
|
|
|
163
163
|
["help", "all actions, shortcuts, mouse controls", { id: "help", label: "help", intent: { scene: "help", argv: ["help"], action: { kind: "none", label: "Opening help" } }, message: "selected help" }]
|
|
164
164
|
];
|
|
165
165
|
var ONBOARD_PRIMARY = {
|
|
166
|
-
id: "onboard-
|
|
167
|
-
label: "Start guided
|
|
168
|
-
intent: { scene: "init", argv: ["init"], action: { kind: "refresh", label: "
|
|
169
|
-
message: "open the
|
|
166
|
+
id: "onboard-init",
|
|
167
|
+
label: "Start guided init",
|
|
168
|
+
intent: { scene: "init", argv: ["init"], action: { kind: "refresh", label: "Init" } },
|
|
169
|
+
message: "open the native init checklist"
|
|
170
170
|
};
|
|
171
171
|
var ONBOARD_SECONDARY = [
|
|
172
172
|
["server", "select a local or remote target", { id: "server", label: "server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Checking server" } }, message: "check selected server" }],
|
|
@@ -229,7 +229,7 @@ function configuredMenuRows(selectedIndex) {
|
|
|
229
229
|
}));
|
|
230
230
|
}
|
|
231
231
|
function onboardingRows(selectedIndex) {
|
|
232
|
-
const primary = selectableDeckRow({ label: "
|
|
232
|
+
const primary = selectableDeckRow({ label: "init", detail: "Start guided init \u2014 recommended", active: selectedIndex === 0 }, ONBOARD_PRIMARY);
|
|
233
233
|
const secondary = ONBOARD_SECONDARY.map(([label, detail, item], index) => ({
|
|
234
234
|
...deckRow({ label, detail, index: index + 1, active: selectedIndex === index + 1, activateOnClick: true }),
|
|
235
235
|
selectable: item
|
|
@@ -293,7 +293,7 @@ function renderMainScene(state, layout) {
|
|
|
293
293
|
statusLine(state)
|
|
294
294
|
] : [
|
|
295
295
|
line("WELCOME", { fg: RIG_UI.ink, bold: true }),
|
|
296
|
-
line("This project isn't
|
|
296
|
+
line("This project isn't initialized yet. One native init flow gets you running.", { fg: RIG_UI.ink2 }),
|
|
297
297
|
blank(),
|
|
298
298
|
line("GET STARTED", { fg: RIG_UI.ink, bold: true }),
|
|
299
299
|
...onboardingRows(actionSelected),
|
|
@@ -215,19 +215,85 @@ function selectableDeckRow(deck, item) {
|
|
|
215
215
|
return { ...deckRow({ ...deck, activateOnClick: true }), selectable: item };
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
// packages/cli/src/app-opentui/remote-link.ts
|
|
219
|
+
var REPAIRABLE_REMOTE_LINK_STATUSES = new Set([
|
|
220
|
+
"auth_required",
|
|
221
|
+
"project_not_registered",
|
|
222
|
+
"no_server_checkout",
|
|
223
|
+
"invalid_root",
|
|
224
|
+
"needs_prepare",
|
|
225
|
+
"error"
|
|
226
|
+
]);
|
|
227
|
+
function remoteProjectLinkError(message) {
|
|
228
|
+
return /no server-host project root link|remote project(?:-root)? link|x-rig-project-root|serverProjectRoot/i.test(message ?? "");
|
|
229
|
+
}
|
|
230
|
+
function serverRecordForRemoteLink(state) {
|
|
231
|
+
const server = state.data.server;
|
|
232
|
+
return server && typeof server === "object" && !Array.isArray(server) ? server : null;
|
|
233
|
+
}
|
|
234
|
+
function remoteProjectLinkState(state) {
|
|
235
|
+
const serverRecord = serverRecordForRemoteLink(state);
|
|
236
|
+
if (!serverRecord || serverRecord.kind !== "remote")
|
|
237
|
+
return null;
|
|
238
|
+
const link = serverRecord.remoteProjectLink;
|
|
239
|
+
if (link && typeof link === "object" && !Array.isArray(link))
|
|
240
|
+
return link;
|
|
241
|
+
const direct = state.data.remoteProjectLink;
|
|
242
|
+
if (direct && typeof direct === "object" && !Array.isArray(direct))
|
|
243
|
+
return direct;
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
function shouldOfferRemoteLinkRepair(state, message) {
|
|
247
|
+
const link = remoteProjectLinkState(state);
|
|
248
|
+
if (link) {
|
|
249
|
+
if (link.ok === true)
|
|
250
|
+
return false;
|
|
251
|
+
const status = link.status ?? "";
|
|
252
|
+
return REPAIRABLE_REMOTE_LINK_STATUSES.has(status);
|
|
253
|
+
}
|
|
254
|
+
const serverRecord = serverRecordForRemoteLink(state);
|
|
255
|
+
if (serverRecord && serverRecord.kind !== "remote")
|
|
256
|
+
return false;
|
|
257
|
+
return remoteProjectLinkError(message ?? state.error?.message);
|
|
258
|
+
}
|
|
259
|
+
|
|
218
260
|
// packages/cli/src/app-opentui/scenes/server.ts
|
|
219
261
|
var SERVER_ACTIONS = [
|
|
220
262
|
{ detail: "probe selected server, project root, and GitHub auth", item: { id: "refresh", label: "refresh", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Refresh server" } }, message: "refresh selected server" } },
|
|
221
|
-
{ detail: "select local server for this repo", item: { id: "local", label: "use local", intent: { scene: "
|
|
222
|
-
{ detail: "
|
|
223
|
-
{ detail: "show saved local/remote aliases", item: { id: "list", label: "list servers", intent: { scene: "
|
|
224
|
-
{ detail: "
|
|
263
|
+
{ detail: "select local server for this repo", item: { id: "local", label: "use local", intent: { scene: "server", argv: ["server", "use", "local"], action: { kind: "server-use-local", label: "Use local server" } }, message: "switch this repo to local" } },
|
|
264
|
+
{ detail: "type a saved remote alias to select", item: { id: "remote", label: "use remote", prompt: { label: "Use remote alias", scene: "server", argv: ["server", "use"], intentKind: "server-use-remote", payloadBase: {}, payloadKey: "alias" }, message: "choose a saved remote alias" } },
|
|
265
|
+
{ detail: "show saved local/remote aliases in the status panel", item: { id: "list", label: "list servers", intent: { scene: "server", argv: ["server", "list"], action: { kind: "refresh", label: "List servers" } }, message: "list saved server aliases" } },
|
|
266
|
+
{ detail: "add and select a remote: enter <alias> <https-url>", item: { id: "add", label: "add remote", prompt: { label: "Add remote", scene: "server", argv: ["server", "add"], intentKind: "server-add-remote", payloadBase: {}, payloadKey: "remote", appendValueToArgv: true }, message: "add a remote server alias" } },
|
|
225
267
|
{ detail: "spawn the local rig-server detached and return here", item: { id: "start", label: "start local", intent: { scene: "server", argv: ["server", "start"], action: { kind: "refresh", payload: { startLocal: true }, label: "Start local server" } }, message: "start local server detached" } },
|
|
226
|
-
{ detail: "import gh token into the selected server namespace", item: { id: "auth", label: "import auth", intent: { scene: "
|
|
268
|
+
{ detail: "import gh token into the selected server namespace", item: { id: "auth", label: "import auth", intent: { scene: "server", argv: ["github", "auth", "import-gh"], action: { kind: "github-auth-import", label: "Import GitHub auth" } }, message: "import gh auth to selected server" } },
|
|
227
269
|
{ detail: "diagnose server/auth/project linkage", item: { id: "doctor", label: "doctor", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "verify server/auth" } },
|
|
228
270
|
{ detail: "verify selected server can list task source", item: { id: "tasks", label: "tasks", intent: { scene: "tasks", argv: ["task", "list"], action: { kind: "refresh", label: "Open tasks" } }, message: "verify task source" } },
|
|
229
271
|
{ detail: "verify selected server can list recent runs", item: { id: "runs", label: "runs", intent: { scene: "fleet", argv: ["run", "status"], action: { kind: "refresh", label: "Open runs" } }, message: "open recent runs" } }
|
|
230
272
|
];
|
|
273
|
+
function remoteAliasActions(server) {
|
|
274
|
+
const aliases = (server?.aliases ?? []).filter((alias) => alias.kind === "remote");
|
|
275
|
+
return aliases.map((alias) => ({
|
|
276
|
+
detail: `${alias.selected ? "selected" : "select"} ${alias.baseUrl ?? "remote endpoint"}`,
|
|
277
|
+
item: {
|
|
278
|
+
id: `remote-alias:${alias.alias}`,
|
|
279
|
+
label: alias.selected ? `\u2713 ${alias.alias}` : `use ${alias.alias}`,
|
|
280
|
+
intent: { scene: "server", argv: ["server", "use", alias.alias], action: { kind: "server-use-remote", label: `Use ${alias.alias}`, payload: { alias: alias.alias } } },
|
|
281
|
+
message: `select ${alias.alias}`
|
|
282
|
+
}
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
function serverActions(state) {
|
|
286
|
+
const server = serverState(state);
|
|
287
|
+
const remoteRows = remoteAliasActions(server);
|
|
288
|
+
let actions = remoteRows.length > 0 ? [...SERVER_ACTIONS.slice(0, 3), ...remoteRows, ...SERVER_ACTIONS.slice(3)] : [...SERVER_ACTIONS];
|
|
289
|
+
if (!shouldOfferRemoteLinkRepair(state))
|
|
290
|
+
return actions;
|
|
291
|
+
const repair = { detail: "backfill or prepare the selected remote checkout/root link", item: { id: "repair-link", label: "repair link", intent: { scene: "server", argv: ["server", "repair-link"], action: { kind: "remote-link-repair", label: "Repair remote link" } }, message: "prepare remote checkout link" } };
|
|
292
|
+
const doctorIndex = actions.findIndex((entry) => entry.item.id === "doctor");
|
|
293
|
+
if (doctorIndex < 0)
|
|
294
|
+
return [...actions, repair];
|
|
295
|
+
return [...actions.slice(0, doctorIndex), repair, ...actions.slice(doctorIndex)];
|
|
296
|
+
}
|
|
231
297
|
function cleanVisible(value, fallback = "unknown") {
|
|
232
298
|
return value?.trim() || fallback;
|
|
233
299
|
}
|
|
@@ -252,31 +318,37 @@ function selectedIndex(state, count) {
|
|
|
252
318
|
function statusRows(server) {
|
|
253
319
|
const auth = server?.auth;
|
|
254
320
|
const signedIn = auth && (auth.signedIn === true || auth.authenticated === true || auth.status === "authenticated") ? "authenticated" : "auth unknown";
|
|
321
|
+
const link = server?.remoteProjectLink;
|
|
322
|
+
const linkText = link ? link.ok ? `ready \xB7 ${link.serverProjectRoot ?? "linked"}` : `${link.status ?? "needs repair"} \xB7 ${link.hint ?? link.message ?? "run repair link"}` : server?.kind === "remote" ? "not checked" : "not required";
|
|
323
|
+
const selectedLinkSummary = link?.ok ? " \xB7 root link ready" : server?.kind === "remote" && link ? ` \xB7 root link ${link.status ?? "needs repair"}` : "";
|
|
255
324
|
return [
|
|
256
|
-
line(`selected ${cleanVisible(server?.label, "not checked")}`, { fg: RIG_UI.ink2 }),
|
|
325
|
+
line(`selected ${cleanVisible(server?.label, "not checked")}${selectedLinkSummary}`, { fg: RIG_UI.ink2 }),
|
|
257
326
|
line(`target ${targetLabel(server)}`, { fg: RIG_UI.ink3 }),
|
|
258
327
|
line(`mode ${server?.kind ?? "local or remote"}`, { fg: RIG_UI.ink3 }),
|
|
259
328
|
line(`health ${server ? server.reachable ? "reachable" : "unreachable" : "check pending"}`, { fg: server?.reachable ? RIG_UI.limeDim : server ? RIG_UI.red : RIG_UI.yellow }),
|
|
260
329
|
line(`github ${signedIn}`, { fg: signedIn === "authenticated" ? RIG_UI.limeDim : RIG_UI.ink4 }),
|
|
330
|
+
line(`root link ${linkText}`, { fg: link?.ok ? RIG_UI.limeDim : server?.kind === "remote" ? RIG_UI.yellow : RIG_UI.ink4 }),
|
|
331
|
+
...server?.aliases && server.aliases.length > 0 ? [line(`aliases ${server.aliases.map((alias) => `${alias.selected ? "*" : ""}${alias.alias}${alias.kind === "remote" && alias.baseUrl ? `=${alias.baseUrl}` : ""}`).join(", ")}`, { fg: RIG_UI.ink4 })] : [],
|
|
261
332
|
...server?.error ? [line(`attention ${server.error}`, { fg: RIG_UI.yellow })] : []
|
|
262
333
|
];
|
|
263
334
|
}
|
|
264
335
|
function renderServerScene(state, layout) {
|
|
265
336
|
const server = serverState(state);
|
|
266
|
-
const
|
|
337
|
+
const actions = serverActions(state);
|
|
338
|
+
const selected = selectedIndex(state, actions.length);
|
|
267
339
|
const loading = state.status === "loading" && !server;
|
|
268
340
|
const errorText = state.error?.message ?? lastRefreshError(state);
|
|
269
341
|
const panelLines = [
|
|
270
342
|
...errorText ? errorBanner(errorText, "select refresh or doctor below to retry") : [],
|
|
271
343
|
line("server controls", { fg: RIG_UI.ink3 }),
|
|
272
344
|
blank(),
|
|
273
|
-
line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
|
|
274
|
-
...SERVER_ACTIONS.map(({ detail, item }, index) => selectableDeckRow({ label: item.label, detail, index, active: selected === index }, item)),
|
|
275
|
-
blank(),
|
|
276
345
|
line("STATUS", { fg: RIG_UI.ink3, bold: true }),
|
|
277
346
|
...loading ? loadingRows("server status", state.tick) : statusRows(server),
|
|
278
347
|
blank(),
|
|
279
|
-
line("
|
|
348
|
+
line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
|
|
349
|
+
...actions.map(({ detail, item }, index) => selectableDeckRow({ label: item.label, detail, index, active: selected === index }, item)),
|
|
350
|
+
blank(),
|
|
351
|
+
line("enter/click runs native actions \xB7 add/use remote prompts stay in OpenTUI", { fg: RIG_UI.ink4 })
|
|
280
352
|
];
|
|
281
353
|
return makeSceneFrame({
|
|
282
354
|
scene: "server",
|