@ouro.bot/cli 0.1.0-alpha.444 → 0.1.0-alpha.446
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/README.md +1 -1
- package/changelog.json +16 -0
- package/dist/heart/daemon/agentic-repair.js +2 -0
- package/dist/heart/daemon/cli-exec.js +3 -11
- package/dist/heart/daemon/connect-bay.js +69 -245
- package/dist/heart/daemon/human-command-screens.js +112 -114
- package/dist/heart/daemon/interactive-repair.js +96 -9
- package/dist/heart/daemon/startup-tui.js +2 -10
- package/dist/heart/daemon/terminal-ui.js +311 -27
- package/dist/heart/daemon/up-progress.js +37 -30
- package/package.json +1 -1
|
@@ -4,9 +4,12 @@ exports.stripAnsi = stripAnsi;
|
|
|
4
4
|
exports.visibleLength = visibleLength;
|
|
5
5
|
exports.padAnsi = padAnsi;
|
|
6
6
|
exports.wrapPlain = wrapPlain;
|
|
7
|
+
exports.renderOverwriteFrame = renderOverwriteFrame;
|
|
7
8
|
exports.renderOuroMasthead = renderOuroMasthead;
|
|
8
9
|
exports.formatActionActorLabel = formatActionActorLabel;
|
|
10
|
+
exports.renderTerminalWizard = renderTerminalWizard;
|
|
9
11
|
exports.renderTerminalBoard = renderTerminalBoard;
|
|
12
|
+
exports.renderTerminalGuide = renderTerminalGuide;
|
|
10
13
|
exports.renderTerminalOperation = renderTerminalOperation;
|
|
11
14
|
const runtime_1 = require("../../nerves/runtime");
|
|
12
15
|
const RESET = "\x1b[0m";
|
|
@@ -16,6 +19,7 @@ const SCALE = "\x1b[38;2;45;148;71m";
|
|
|
16
19
|
const GLOW = "\x1b[38;2;74;227;108m";
|
|
17
20
|
const BONE = "\x1b[38;2;237;242;238m";
|
|
18
21
|
const MIST = "\x1b[38;2;154;174;159m";
|
|
22
|
+
const ALERT = "\x1b[38;2;255;106;106m";
|
|
19
23
|
const ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
20
24
|
const MASTHEAD_WORD = "OUROBOROS";
|
|
21
25
|
const CLASSIC_WORDMARK_GLYPHS = {
|
|
@@ -71,6 +75,25 @@ function boardWidth(columns) {
|
|
|
71
75
|
const requested = columns ?? 88;
|
|
72
76
|
return Math.max(58, Math.min(requested, 96));
|
|
73
77
|
}
|
|
78
|
+
function renderOverwriteFrame(lines, prevLineCount, isTTY) {
|
|
79
|
+
if (!isTTY)
|
|
80
|
+
return `${lines.join("\n")}\n`;
|
|
81
|
+
let output = "";
|
|
82
|
+
if (prevLineCount > 0) {
|
|
83
|
+
output += `\x1b[${prevLineCount}A`;
|
|
84
|
+
}
|
|
85
|
+
for (const line of lines) {
|
|
86
|
+
output += `\x1b[2K${line}\n`;
|
|
87
|
+
}
|
|
88
|
+
const extraLineCount = Math.max(0, prevLineCount - lines.length);
|
|
89
|
+
for (let i = 0; i < extraLineCount; i++) {
|
|
90
|
+
output += "\x1b[2K\n";
|
|
91
|
+
}
|
|
92
|
+
if (extraLineCount > 0) {
|
|
93
|
+
output += `\x1b[${extraLineCount}A`;
|
|
94
|
+
}
|
|
95
|
+
return output;
|
|
96
|
+
}
|
|
74
97
|
function renderPanelTTY(title, lines, width) {
|
|
75
98
|
const innerWidth = Math.max(8, width - 4);
|
|
76
99
|
const topPrefix = `╭─ ${title} `;
|
|
@@ -94,15 +117,18 @@ function renderPanelPlain(title, lines) {
|
|
|
94
117
|
];
|
|
95
118
|
}
|
|
96
119
|
function mastheadArt(columns) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
rows[index].push(line);
|
|
103
|
-
}
|
|
120
|
+
const rows = Array.from({ length: 5 }, () => []);
|
|
121
|
+
for (const letter of MASTHEAD_WORD.split("")) {
|
|
122
|
+
const glyph = CLASSIC_WORDMARK_GLYPHS[letter];
|
|
123
|
+
for (const [index, line] of glyph.entries()) {
|
|
124
|
+
rows[index].push(line);
|
|
104
125
|
}
|
|
105
|
-
|
|
126
|
+
}
|
|
127
|
+
const classicWordmark = rows.map((row) => row.join(" "));
|
|
128
|
+
const availableColumns = columns ?? 88;
|
|
129
|
+
const classicWidth = Math.max(...classicWordmark.map((line) => line.length));
|
|
130
|
+
if (availableColumns >= classicWidth) {
|
|
131
|
+
return classicWordmark;
|
|
106
132
|
}
|
|
107
133
|
return [MASTHEAD_WORD];
|
|
108
134
|
}
|
|
@@ -121,6 +147,154 @@ function renderOuroMasthead(options) {
|
|
|
121
147
|
function formatActionActorLabel(actor) {
|
|
122
148
|
return actor.replace(/-/g, " ");
|
|
123
149
|
}
|
|
150
|
+
function formatWizardStatusLabel(status) {
|
|
151
|
+
return status;
|
|
152
|
+
}
|
|
153
|
+
function isQuietWizardStatus(status) {
|
|
154
|
+
return status === "ready" || status === "attached" || status === "not attached";
|
|
155
|
+
}
|
|
156
|
+
function renderWizardStatusBadge(status, isTTY) {
|
|
157
|
+
const symbol = status === "ready" || status === "attached"
|
|
158
|
+
? "●"
|
|
159
|
+
: status === "not attached"
|
|
160
|
+
? "◌"
|
|
161
|
+
: "◆";
|
|
162
|
+
const label = `${symbol} ${formatWizardStatusLabel(status)}`;
|
|
163
|
+
if (!isTTY)
|
|
164
|
+
return label;
|
|
165
|
+
if (status === "ready" || status === "attached")
|
|
166
|
+
return color(label, GLOW, true);
|
|
167
|
+
if (status === "not attached")
|
|
168
|
+
return color(label, MIST);
|
|
169
|
+
return color(label, ALERT, true);
|
|
170
|
+
}
|
|
171
|
+
function renderWizardActorBadge(actor, isTTY) {
|
|
172
|
+
const label = `[${formatActionActorLabel(actor)}]`;
|
|
173
|
+
if (!isTTY)
|
|
174
|
+
return label;
|
|
175
|
+
if (actor === "human-required")
|
|
176
|
+
return color(label, ALERT, true);
|
|
177
|
+
if (actor === "human-choice")
|
|
178
|
+
return color(label, SCALE, true);
|
|
179
|
+
return color(label, MIST);
|
|
180
|
+
}
|
|
181
|
+
function renderWizardRecommendedBadge(isTTY) {
|
|
182
|
+
if (!isTTY)
|
|
183
|
+
return "[recommended]";
|
|
184
|
+
return color("[recommended]", GLOW, true);
|
|
185
|
+
}
|
|
186
|
+
function wrapWizardDetailLines(lines, width, isTTY, tone) {
|
|
187
|
+
const rendered = [];
|
|
188
|
+
for (const line of lines) {
|
|
189
|
+
const wrapped = wrapPlain(line, width);
|
|
190
|
+
for (const segment of wrapped) {
|
|
191
|
+
rendered.push(isTTY ? color(segment, tone) : segment);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return rendered;
|
|
195
|
+
}
|
|
196
|
+
function renderWizardItem(item, width, isTTY) {
|
|
197
|
+
const badges = [
|
|
198
|
+
...(item.status ? [renderWizardStatusBadge(item.status, isTTY)] : []),
|
|
199
|
+
...(item.actor ? [renderWizardActorBadge(item.actor, isTTY)] : []),
|
|
200
|
+
...(item.recommended ? [renderWizardRecommendedBadge(isTTY)] : []),
|
|
201
|
+
];
|
|
202
|
+
const keyPrefix = item.key ? `${item.key}. ` : "";
|
|
203
|
+
const header = `${keyPrefix}${item.label}${badges.length > 0 ? ` ${badges.join(" ")}` : ""}`;
|
|
204
|
+
const detailWidth = Math.max(18, width - 6);
|
|
205
|
+
const detailTone = item.status && !isQuietWizardStatus(item.status) ? BONE : MIST;
|
|
206
|
+
const lines = [isTTY ? color(header, BONE, true) : header];
|
|
207
|
+
if (item.summary) {
|
|
208
|
+
lines.push(...wrapWizardDetailLines([item.summary], detailWidth, isTTY, detailTone));
|
|
209
|
+
}
|
|
210
|
+
if (item.detailLines && item.detailLines.length > 0) {
|
|
211
|
+
lines.push(...wrapWizardDetailLines(item.detailLines, detailWidth, isTTY, detailTone));
|
|
212
|
+
}
|
|
213
|
+
if (item.command) {
|
|
214
|
+
lines.push(...wrapWizardDetailLines([`run: ${item.command}`], detailWidth, isTTY, MIST));
|
|
215
|
+
}
|
|
216
|
+
return lines.flatMap((line, index) => index === 0 ? [line] : [` ${line}`]);
|
|
217
|
+
}
|
|
218
|
+
function renderWizardSectionTTY(section, width) {
|
|
219
|
+
const lines = [];
|
|
220
|
+
if (section.summary) {
|
|
221
|
+
lines.push(...wrapWizardDetailLines([section.summary], Math.max(18, width - 4), true, MIST));
|
|
222
|
+
}
|
|
223
|
+
for (const [index, item] of section.items.entries()) {
|
|
224
|
+
if (index > 0)
|
|
225
|
+
lines.push("");
|
|
226
|
+
lines.push(...renderWizardItem(item, width, true));
|
|
227
|
+
}
|
|
228
|
+
return renderOperationSectionTTY(section.title, lines, width);
|
|
229
|
+
}
|
|
230
|
+
function renderWizardSectionPlain(section, width) {
|
|
231
|
+
const lines = [];
|
|
232
|
+
if (section.summary) {
|
|
233
|
+
lines.push(...wrapWizardDetailLines([section.summary], Math.max(18, width - 4), false, MIST));
|
|
234
|
+
}
|
|
235
|
+
for (const [index, item] of section.items.entries()) {
|
|
236
|
+
if (index > 0)
|
|
237
|
+
lines.push("");
|
|
238
|
+
lines.push(...renderWizardItem(item, width, false));
|
|
239
|
+
}
|
|
240
|
+
return renderOperationSectionPlain(section.title, lines);
|
|
241
|
+
}
|
|
242
|
+
function renderTerminalWizard(options) {
|
|
243
|
+
if (!options.suppressEvent) {
|
|
244
|
+
(0, runtime_1.emitNervesEvent)({
|
|
245
|
+
component: "daemon",
|
|
246
|
+
event: "daemon.terminal_wizard_rendered",
|
|
247
|
+
message: "rendered shared terminal wizard",
|
|
248
|
+
meta: {
|
|
249
|
+
title: options.title,
|
|
250
|
+
sections: options.sections?.length ?? 0,
|
|
251
|
+
items: options.sections?.reduce((count, section) => count + section.items.length, 0) ?? 0,
|
|
252
|
+
hasNextStep: !!options.nextStep,
|
|
253
|
+
tty: options.isTTY,
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
const width = boardWidth(options.columns);
|
|
258
|
+
const blocks = [];
|
|
259
|
+
blocks.push(renderOuroMasthead({
|
|
260
|
+
isTTY: options.isTTY,
|
|
261
|
+
columns: width,
|
|
262
|
+
subtitle: options.masthead?.subtitle,
|
|
263
|
+
}).trimEnd());
|
|
264
|
+
const introLines = [
|
|
265
|
+
options.isTTY ? color(options.title, BONE, true) : options.title,
|
|
266
|
+
...(options.summary
|
|
267
|
+
? wrapPlain(options.summary, Math.max(20, width - 2)).map((line) => options.isTTY ? color(line, MIST) : line)
|
|
268
|
+
: []),
|
|
269
|
+
];
|
|
270
|
+
blocks.push(introLines.join("\n"));
|
|
271
|
+
if (options.nextStep) {
|
|
272
|
+
const nextStepLines = [
|
|
273
|
+
options.isTTY ? color(options.nextStep.label, BONE, true) : options.nextStep.label,
|
|
274
|
+
...(options.nextStep.detail
|
|
275
|
+
? wrapPlain(options.nextStep.detail, Math.max(18, width - 4)).map((line) => options.isTTY ? color(line, MIST) : line)
|
|
276
|
+
: []),
|
|
277
|
+
...(options.nextStep.command
|
|
278
|
+
? wrapPlain(`run: ${options.nextStep.command}`, Math.max(18, width - 4)).map((line) => options.isTTY ? color(line, MIST) : line)
|
|
279
|
+
: []),
|
|
280
|
+
];
|
|
281
|
+
blocks.push((options.isTTY
|
|
282
|
+
? renderOperationSectionTTY("Recommended next step", nextStepLines, width)
|
|
283
|
+
: renderOperationSectionPlain("Recommended next step", nextStepLines)).join("\n"));
|
|
284
|
+
}
|
|
285
|
+
for (const section of options.sections ?? []) {
|
|
286
|
+
blocks.push((options.isTTY
|
|
287
|
+
? renderWizardSectionTTY(section, width)
|
|
288
|
+
: renderWizardSectionPlain(section, width)).join("\n"));
|
|
289
|
+
}
|
|
290
|
+
if (options.footerLines && options.footerLines.length > 0) {
|
|
291
|
+
blocks.push(options.footerLines.map((line) => options.isTTY ? color(line, MIST) : line).join("\n"));
|
|
292
|
+
}
|
|
293
|
+
if (options.prompt) {
|
|
294
|
+
blocks.push(options.isTTY ? color(options.prompt, BONE, true) : options.prompt);
|
|
295
|
+
}
|
|
296
|
+
return `${blocks.join("\n\n")}\n`;
|
|
297
|
+
}
|
|
124
298
|
function renderActionLine(action) {
|
|
125
299
|
const chips = [`[${formatActionActorLabel(action.actor)}]`];
|
|
126
300
|
if (action.recommended)
|
|
@@ -171,6 +345,56 @@ function renderTerminalBoard(options) {
|
|
|
171
345
|
}
|
|
172
346
|
return `${blocks.join("\n\n")}\n`;
|
|
173
347
|
}
|
|
348
|
+
function renderTerminalGuide(options) {
|
|
349
|
+
if (!options.suppressEvent) {
|
|
350
|
+
(0, runtime_1.emitNervesEvent)({
|
|
351
|
+
component: "daemon",
|
|
352
|
+
event: "daemon.terminal_guide_rendered",
|
|
353
|
+
message: "rendered shared terminal guide",
|
|
354
|
+
meta: {
|
|
355
|
+
title: options.title,
|
|
356
|
+
sections: options.sections?.length ?? 0,
|
|
357
|
+
actions: options.actions?.length ?? 0,
|
|
358
|
+
tty: options.isTTY,
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
const width = boardWidth(options.columns);
|
|
363
|
+
const blocks = [];
|
|
364
|
+
blocks.push(renderOuroMasthead({
|
|
365
|
+
isTTY: options.isTTY,
|
|
366
|
+
columns: width,
|
|
367
|
+
subtitle: options.masthead?.subtitle,
|
|
368
|
+
}).trimEnd());
|
|
369
|
+
const introLines = [
|
|
370
|
+
options.isTTY ? color(options.title, BONE, true) : options.title,
|
|
371
|
+
...(options.summary
|
|
372
|
+
? wrapPlain(options.summary, Math.max(20, width - 2)).map((line) => options.isTTY ? color(line, MIST) : line)
|
|
373
|
+
: []),
|
|
374
|
+
];
|
|
375
|
+
blocks.push(introLines.join("\n"));
|
|
376
|
+
for (const section of options.sections ?? []) {
|
|
377
|
+
const lines = section.lines.map((line) => options.isTTY ? color(line, BONE) : line);
|
|
378
|
+
blocks.push((options.isTTY
|
|
379
|
+
? renderOperationSectionTTY(section.title, lines, width)
|
|
380
|
+
: renderOperationSectionPlain(section.title, lines)).join("\n"));
|
|
381
|
+
}
|
|
382
|
+
const actionList = options.actions ?? [];
|
|
383
|
+
if (actionList.length > 0) {
|
|
384
|
+
const lines = [];
|
|
385
|
+
for (const [index, action] of actionList.entries()) {
|
|
386
|
+
lines.push(options.isTTY ? color(`${index + 1}. ${renderActionLine(action)}`, BONE, true) : `${index + 1}. ${renderActionLine(action)}`);
|
|
387
|
+
lines.push(options.isTTY ? color(`run: ${action.command}`, MIST) : `run: ${action.command}`);
|
|
388
|
+
}
|
|
389
|
+
blocks.push((options.isTTY
|
|
390
|
+
? renderOperationSectionTTY("Next moves", lines, width)
|
|
391
|
+
: renderOperationSectionPlain("Next moves", lines)).join("\n"));
|
|
392
|
+
}
|
|
393
|
+
if (options.prompt) {
|
|
394
|
+
blocks.push(options.isTTY ? color(options.prompt, BONE, true) : options.prompt);
|
|
395
|
+
}
|
|
396
|
+
return `${blocks.join("\n\n")}\n`;
|
|
397
|
+
}
|
|
174
398
|
function formatOperationStep(step) {
|
|
175
399
|
const marker = step.status === "done"
|
|
176
400
|
? "✓"
|
|
@@ -182,7 +406,58 @@ function formatOperationStep(step) {
|
|
|
182
406
|
const detail = step.detail ? ` — ${step.detail}` : "";
|
|
183
407
|
return `${marker} ${step.label}${detail}`;
|
|
184
408
|
}
|
|
409
|
+
function operationMarkerTone(status) {
|
|
410
|
+
switch (status) {
|
|
411
|
+
case "done":
|
|
412
|
+
return GLOW;
|
|
413
|
+
case "active":
|
|
414
|
+
return BONE;
|
|
415
|
+
case "failed":
|
|
416
|
+
return ALERT;
|
|
417
|
+
case "pending":
|
|
418
|
+
default:
|
|
419
|
+
return MIST;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
function renderOperationStepTTY(step) {
|
|
423
|
+
const marker = step.status === "done"
|
|
424
|
+
? "✓"
|
|
425
|
+
: step.status === "failed"
|
|
426
|
+
? "✗"
|
|
427
|
+
: step.status === "active"
|
|
428
|
+
? "→"
|
|
429
|
+
: "○";
|
|
430
|
+
const label = color(step.label, step.status === "pending" ? MIST : BONE, step.status !== "pending");
|
|
431
|
+
const detail = step.detail ? ` ${color(`— ${step.detail}`, MIST)}` : "";
|
|
432
|
+
return `${color(marker, operationMarkerTone(step.status), true)} ${label}${detail}`;
|
|
433
|
+
}
|
|
434
|
+
function renderOperationSectionTTY(title, lines, width) {
|
|
435
|
+
const rule = "─".repeat(Math.max(8, width - title.length - 3));
|
|
436
|
+
return [
|
|
437
|
+
`${color("─ ", CANOPY)}${color(title, BONE, true)} ${color(rule, CANOPY)}`,
|
|
438
|
+
...lines.map((line) => ` ${line}`),
|
|
439
|
+
];
|
|
440
|
+
}
|
|
441
|
+
function renderOperationSectionPlain(title, lines) {
|
|
442
|
+
return [
|
|
443
|
+
title,
|
|
444
|
+
...lines.map((line) => ` ${plainLine(line)}`),
|
|
445
|
+
];
|
|
446
|
+
}
|
|
185
447
|
function renderTerminalOperation(options) {
|
|
448
|
+
if (!options.suppressEvent) {
|
|
449
|
+
(0, runtime_1.emitNervesEvent)({
|
|
450
|
+
component: "daemon",
|
|
451
|
+
event: "daemon.terminal_operation_rendered",
|
|
452
|
+
message: "rendered terminal operation surface",
|
|
453
|
+
meta: {
|
|
454
|
+
title: options.title,
|
|
455
|
+
steps: options.steps?.length ?? 0,
|
|
456
|
+
hasCurrentStep: !!options.currentStep,
|
|
457
|
+
tty: options.isTTY,
|
|
458
|
+
},
|
|
459
|
+
});
|
|
460
|
+
}
|
|
186
461
|
const steps = options.steps ?? [];
|
|
187
462
|
const currentLines = options.currentStep
|
|
188
463
|
? [
|
|
@@ -191,25 +466,34 @@ function renderTerminalOperation(options) {
|
|
|
191
466
|
]
|
|
192
467
|
: ["Standing by."];
|
|
193
468
|
const progressLines = steps.length > 0
|
|
194
|
-
?
|
|
469
|
+
? options.isTTY
|
|
470
|
+
? steps.map((step) => renderOperationStepTTY(step))
|
|
471
|
+
: steps.map((step) => formatOperationStep(step))
|
|
195
472
|
: ["No active steps yet."];
|
|
196
|
-
|
|
473
|
+
const width = boardWidth(options.columns);
|
|
474
|
+
const blocks = [];
|
|
475
|
+
blocks.push(renderOuroMasthead({
|
|
197
476
|
isTTY: options.isTTY,
|
|
198
|
-
columns:
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
477
|
+
columns: width,
|
|
478
|
+
subtitle: options.masthead?.subtitle,
|
|
479
|
+
}).trimEnd());
|
|
480
|
+
const introLines = [
|
|
481
|
+
options.isTTY ? color(options.title, BONE, true) : options.title,
|
|
482
|
+
...(options.summary
|
|
483
|
+
? wrapPlain(options.summary, Math.max(20, width - 2)).map((line) => options.isTTY ? color(line, MIST) : line)
|
|
484
|
+
: []),
|
|
485
|
+
];
|
|
486
|
+
blocks.push(introLines.join("\n"));
|
|
487
|
+
const renderedSteps = options.isTTY
|
|
488
|
+
? renderOperationSectionTTY(options.stepsTitle ?? "Checklist", progressLines, width)
|
|
489
|
+
: renderOperationSectionPlain(options.stepsTitle ?? "Checklist", progressLines);
|
|
490
|
+
const renderedCurrent = options.isTTY
|
|
491
|
+
? renderOperationSectionTTY(options.currentTitle ?? "Current work", currentLines.map((line, index) => index === 0 ? color(line, BONE, true) : color(line, MIST)), width)
|
|
492
|
+
: renderOperationSectionPlain(options.currentTitle ?? "Current work", currentLines);
|
|
493
|
+
blocks.push(renderedSteps.join("\n"));
|
|
494
|
+
blocks.push(renderedCurrent.join("\n"));
|
|
495
|
+
if (options.prompt) {
|
|
496
|
+
blocks.push(options.isTTY ? color(options.prompt, BONE, true) : options.prompt);
|
|
497
|
+
}
|
|
498
|
+
return `${blocks.join("\n\n")}\n`;
|
|
215
499
|
}
|
|
@@ -219,19 +219,7 @@ class UpProgress {
|
|
|
219
219
|
return "";
|
|
220
220
|
}
|
|
221
221
|
const lines = this.renderLines(now);
|
|
222
|
-
|
|
223
|
-
if (this.prevLineCount > 0) {
|
|
224
|
-
output += `\x1b[${this.prevLineCount}A`;
|
|
225
|
-
}
|
|
226
|
-
for (const line of lines) {
|
|
227
|
-
output += `\x1b[2K${line}\n`;
|
|
228
|
-
}
|
|
229
|
-
// Clear any leftover lines from previous render that are no longer needed
|
|
230
|
-
if (lines.length < this.prevLineCount) {
|
|
231
|
-
for (let i = 0; i < this.prevLineCount - lines.length; i++) {
|
|
232
|
-
output += `\x1b[2K\n`;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
222
|
+
const output = (0, terminal_ui_1.renderOverwriteFrame)(lines, this.prevLineCount, true);
|
|
235
223
|
this.prevLineCount = lines.length;
|
|
236
224
|
return output;
|
|
237
225
|
}
|
|
@@ -301,14 +289,8 @@ class UpProgress {
|
|
|
301
289
|
}
|
|
302
290
|
renderUpScreen(now) {
|
|
303
291
|
const seenLabels = new Set();
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
return {
|
|
307
|
-
label: this.renderUpStepLabel(phase.label),
|
|
308
|
-
status: phase.status === "failure" ? "failed" : "done",
|
|
309
|
-
detail: phase.detail,
|
|
310
|
-
};
|
|
311
|
-
});
|
|
292
|
+
const completedByLabel = new Map(this.completed.map((phase) => [phase.label, phase]));
|
|
293
|
+
const steps = [];
|
|
312
294
|
let currentStepLabel = this.completed.some((phase) => phase.status === "failure")
|
|
313
295
|
? "Boot paused."
|
|
314
296
|
: this.completed.length > 0
|
|
@@ -322,19 +304,44 @@ class UpProgress {
|
|
|
322
304
|
const spinner = SPINNER_FRAMES[frameIndex];
|
|
323
305
|
currentStepLabel = `${spinner} ${this.renderUpStepLabel(this.currentPhase.label)} (${elapsedSec}s)`;
|
|
324
306
|
currentStepDetails = splitDetailLines(this.currentPhase.detail);
|
|
325
|
-
steps.push({
|
|
326
|
-
label: this.renderUpStepLabel(this.currentPhase.label),
|
|
327
|
-
status: "active",
|
|
328
|
-
});
|
|
329
|
-
seenLabels.add(this.currentPhase.label);
|
|
330
307
|
}
|
|
331
308
|
for (const label of this.upPhasePlan) {
|
|
332
|
-
|
|
309
|
+
seenLabels.add(label);
|
|
310
|
+
const completedPhase = completedByLabel.get(label);
|
|
311
|
+
if (completedPhase) {
|
|
312
|
+
steps.push({
|
|
313
|
+
label: this.renderUpStepLabel(label),
|
|
314
|
+
status: completedPhase.status === "failure" ? "failed" : "done",
|
|
315
|
+
detail: completedPhase.detail,
|
|
316
|
+
});
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
if (this.currentPhase?.label === label) {
|
|
333
320
|
steps.push({
|
|
334
321
|
label: this.renderUpStepLabel(label),
|
|
335
|
-
status: "
|
|
322
|
+
status: "active",
|
|
336
323
|
});
|
|
324
|
+
continue;
|
|
337
325
|
}
|
|
326
|
+
steps.push({
|
|
327
|
+
label: this.renderUpStepLabel(label),
|
|
328
|
+
status: "pending",
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
for (const phase of this.completed) {
|
|
332
|
+
if (!seenLabels.has(phase.label)) {
|
|
333
|
+
steps.push({
|
|
334
|
+
label: this.renderUpStepLabel(phase.label),
|
|
335
|
+
status: phase.status === "failure" ? "failed" : "done",
|
|
336
|
+
detail: phase.detail,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (this.currentPhase && !seenLabels.has(this.currentPhase.label)) {
|
|
341
|
+
steps.push({
|
|
342
|
+
label: this.renderUpStepLabel(this.currentPhase.label),
|
|
343
|
+
status: "active",
|
|
344
|
+
});
|
|
338
345
|
}
|
|
339
346
|
return (0, terminal_ui_1.renderTerminalOperation)({
|
|
340
347
|
isTTY: true,
|
|
@@ -342,8 +349,8 @@ class UpProgress {
|
|
|
342
349
|
masthead: {
|
|
343
350
|
subtitle: "Booting the local agent runtime.",
|
|
344
351
|
},
|
|
345
|
-
title: "Ouro
|
|
346
|
-
summary: "Ouro
|
|
352
|
+
title: "Starting Ouro",
|
|
353
|
+
summary: "Ouro is bringing the local agent runtime online and will stop here if anything needs attention.",
|
|
347
354
|
currentStep: {
|
|
348
355
|
label: currentStepLabel,
|
|
349
356
|
detailLines: currentStepDetails,
|