@nmakarov/cli-toolkit 0.68.0 → 0.70.0
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/cli-runner.cjs +328 -193
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +328 -193
- package/dist/cli-runner.js.map +1 -1
- package/dist/index.cjs +366 -209
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +360 -209
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +145 -103
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +145 -103
- package/dist/init.js.map +1 -1
- package/dist/screen.cjs +140 -103
- package/dist/screen.cjs.map +1 -1
- package/dist/screen.js +137 -103
- package/dist/screen.js.map +1 -1
- package/dist/tasks.cjs +218 -106
- package/dist/tasks.cjs.map +1 -1
- package/dist/tasks.js +215 -106
- package/dist/tasks.js.map +1 -1
- package/package.json +2 -2
package/dist/cli-runner.cjs
CHANGED
|
@@ -109,7 +109,46 @@ var init_components = __esm({
|
|
|
109
109
|
}
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
+
// src/screen/scrollbar.js
|
|
113
|
+
function scrollbarGlyphs(viewportRows, totalLines, scrollTop) {
|
|
114
|
+
const view = Math.max(1, Math.floor(viewportRows));
|
|
115
|
+
const total = Math.max(0, Math.floor(totalLines));
|
|
116
|
+
if (total <= view) return null;
|
|
117
|
+
const maxScroll = total - view;
|
|
118
|
+
const thumbSize = Math.max(1, Math.round(view / total * view));
|
|
119
|
+
const travel = Math.max(0, view - thumbSize);
|
|
120
|
+
const s = Math.min(Math.max(0, Math.floor(scrollTop)), maxScroll);
|
|
121
|
+
const thumbStart = maxScroll === 0 ? 0 : Math.round(s / maxScroll * travel);
|
|
122
|
+
const glyphs = [];
|
|
123
|
+
for (let i = 0; i < view; i++) {
|
|
124
|
+
glyphs.push(i >= thumbStart && i < thumbStart + thumbSize ? BAR_THUMB : BAR_TRACK);
|
|
125
|
+
}
|
|
126
|
+
return glyphs;
|
|
127
|
+
}
|
|
128
|
+
var BAR_THUMB, BAR_TRACK, PAGE_SCROLL_KEY_BINDINGS;
|
|
129
|
+
var init_scrollbar = __esm({
|
|
130
|
+
"src/screen/scrollbar.js"() {
|
|
131
|
+
BAR_THUMB = "#";
|
|
132
|
+
BAR_TRACK = "|";
|
|
133
|
+
PAGE_SCROLL_KEY_BINDINGS = [
|
|
134
|
+
{ key: "upArrow", meta: true, caption: "page", action: "pageUp", order: 0 },
|
|
135
|
+
{ key: "downArrow", meta: true, caption: "page", action: "pageDown", order: 0 },
|
|
136
|
+
{ key: "upArrow", ctrl: true, caption: "page", action: "pageUp", order: 0 },
|
|
137
|
+
{ key: "downArrow", ctrl: true, caption: "page", action: "pageDown", order: 0 },
|
|
138
|
+
{ key: "pageUp", caption: "page", action: "pageUp", order: 0 },
|
|
139
|
+
{ key: "pageDown", caption: "page", action: "pageDown", order: 0 }
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
112
144
|
// src/screen/list-components.js
|
|
145
|
+
function clipNameForScrollbar(name, needsBar) {
|
|
146
|
+
if (!needsBar || name == null) return name;
|
|
147
|
+
const s = String(name);
|
|
148
|
+
if (s.length <= 1) return s;
|
|
149
|
+
if (/\s$/.test(s)) return s.slice(0, -1);
|
|
150
|
+
return s;
|
|
151
|
+
}
|
|
113
152
|
function MultiColumnListComponent({ items, ctx, selectedIndexRef }) {
|
|
114
153
|
const [, forceUpdate] = (0, import_react2.useState)({});
|
|
115
154
|
const termWidth = (process.stdout.columns || 80) - 8;
|
|
@@ -323,17 +362,42 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
|
|
|
323
362
|
});
|
|
324
363
|
ctx.setAction("scrollUp", () => {
|
|
325
364
|
const { scrollOffset: currentScrollOffset } = scrollStateRef.current;
|
|
326
|
-
|
|
327
|
-
setScrollOffset(newScrollOffset);
|
|
365
|
+
setScrollOffset(Math.max(0, currentScrollOffset - 1));
|
|
328
366
|
forceUpdate({});
|
|
329
367
|
});
|
|
330
368
|
ctx.setAction("scrollDown", () => {
|
|
331
369
|
const { scrollOffset: currentScrollOffset, maxHeight: currentMaxHeight, totalItems } = scrollStateRef.current;
|
|
332
370
|
const currentMaxScrollOffset = Math.max(0, totalItems - currentMaxHeight);
|
|
333
|
-
|
|
334
|
-
|
|
371
|
+
setScrollOffset(Math.min(currentMaxScrollOffset, currentScrollOffset + 1));
|
|
372
|
+
forceUpdate({});
|
|
373
|
+
});
|
|
374
|
+
ctx.setAction("pageUp", () => {
|
|
375
|
+
const { maxHeight: vh } = scrollStateRef.current;
|
|
376
|
+
const page = Math.max(1, vh || 1);
|
|
377
|
+
const newIndex = Math.max(0, selectedIndexRef.current - page);
|
|
378
|
+
selectedIndexRef.current = newIndex;
|
|
379
|
+
const list = displayItemsRef.current;
|
|
380
|
+
onSelectionChangeRef.current?.(newIndex, list[newIndex]);
|
|
381
|
+
setScrollOffset(newIndex);
|
|
335
382
|
forceUpdate({});
|
|
336
383
|
});
|
|
384
|
+
ctx.setAction("pageDown", () => {
|
|
385
|
+
const { maxHeight: vh, totalItems } = scrollStateRef.current;
|
|
386
|
+
const page = Math.max(1, vh || 1);
|
|
387
|
+
const maxIndex = Math.max(0, totalItems - 1);
|
|
388
|
+
const newIndex = Math.min(maxIndex, selectedIndexRef.current + page);
|
|
389
|
+
selectedIndexRef.current = newIndex;
|
|
390
|
+
const list = displayItemsRef.current;
|
|
391
|
+
onSelectionChangeRef.current?.(newIndex, list[newIndex]);
|
|
392
|
+
const maxScroll = Math.max(0, totalItems - page);
|
|
393
|
+
setScrollOffset(Math.min(maxScroll, Math.max(0, newIndex - page + 1)));
|
|
394
|
+
forceUpdate({});
|
|
395
|
+
});
|
|
396
|
+
const navBindings = [
|
|
397
|
+
{ key: "upArrow", caption: "navigate", action: "moveUp", order: 0 },
|
|
398
|
+
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 },
|
|
399
|
+
...PAGE_SCROLL_KEY_BINDINGS
|
|
400
|
+
];
|
|
337
401
|
if (sortable) {
|
|
338
402
|
ctx.setAction("toggleSort", () => {
|
|
339
403
|
const nextSort = sortOrder === "none" ? "asc" : sortOrder === "asc" ? "desc" : "none";
|
|
@@ -380,8 +444,7 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
|
|
|
380
444
|
}
|
|
381
445
|
};
|
|
382
446
|
ctx.setKeyBinding([
|
|
383
|
-
|
|
384
|
-
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 },
|
|
447
|
+
...navBindings,
|
|
385
448
|
{
|
|
386
449
|
key: "s",
|
|
387
450
|
caption: sortCaption,
|
|
@@ -391,47 +454,47 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
|
|
|
391
454
|
]);
|
|
392
455
|
ctx.update();
|
|
393
456
|
} else {
|
|
394
|
-
ctx.setKeyBinding(
|
|
395
|
-
{ key: "upArrow", caption: "navigate", action: "moveUp", order: 0 },
|
|
396
|
-
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 }
|
|
397
|
-
]);
|
|
457
|
+
ctx.setKeyBinding(navBindings);
|
|
398
458
|
}
|
|
399
459
|
}, [sortOrder, sortable]);
|
|
400
460
|
const selectedIndex = selectedIndexRef.current;
|
|
461
|
+
const needsBar = displayItems.length > effectiveMaxHeight;
|
|
462
|
+
const barGlyphs = needsBar ? scrollbarGlyphs(effectiveMaxHeight, displayItems.length, clampedScrollOffset) : null;
|
|
463
|
+
const appendBar = (rowContent, displayIndex) => {
|
|
464
|
+
if (!barGlyphs) return rowContent;
|
|
465
|
+
const glyph = barGlyphs[displayIndex] ?? BAR_TRACK;
|
|
466
|
+
return h2(
|
|
467
|
+
import_ink2.Box,
|
|
468
|
+
{ flexDirection: "row" },
|
|
469
|
+
rowContent,
|
|
470
|
+
h2(import_ink2.Text, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
|
|
471
|
+
);
|
|
472
|
+
};
|
|
401
473
|
const defaultRenderItem = (item, isSelected, displayIndex, actualIndex) => {
|
|
402
474
|
const isFirstVisible = displayIndex === 0;
|
|
403
475
|
const isLastVisible = displayIndex === visibleItems.length - 1;
|
|
404
|
-
let arrowPrefix = "";
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
arrowPrefix = "\
|
|
408
|
-
} else if (isLastVisible && canScrollDown) {
|
|
409
|
-
arrowPrefix = "\u2193 ";
|
|
410
|
-
} else {
|
|
411
|
-
arrowPrefix = " ";
|
|
412
|
-
}
|
|
413
|
-
if (isSelected) {
|
|
414
|
-
selectionPrefix = selectionMarker;
|
|
415
|
-
} else {
|
|
416
|
-
selectionPrefix = " ".repeat(selectionMarker.length);
|
|
476
|
+
let arrowPrefix = " ";
|
|
477
|
+
if (!needsBar) {
|
|
478
|
+
if (isFirstVisible && canScrollUp) arrowPrefix = "\u2191 ";
|
|
479
|
+
else if (isLastVisible && canScrollDown) arrowPrefix = "\u2193 ";
|
|
417
480
|
}
|
|
481
|
+
const selectionPrefix = isSelected ? selectionMarker : " ".repeat(selectionMarker.length);
|
|
482
|
+
const label = clipNameForScrollbar(item.name, needsBar);
|
|
418
483
|
return h2(
|
|
419
484
|
import_ink2.Box,
|
|
420
485
|
{ flexDirection: "row" },
|
|
421
|
-
|
|
422
|
-
h2(import_ink2.Text, {
|
|
423
|
-
key: `arrow-${actualIndex}`,
|
|
424
|
-
color: "white"
|
|
425
|
-
}, arrowPrefix),
|
|
426
|
-
// Selection marker space (always same width, not highlighted)
|
|
486
|
+
h2(import_ink2.Text, { key: `arrow-${actualIndex}`, color: "white" }, arrowPrefix),
|
|
427
487
|
h2(import_ink2.Text, { key: `marker-${actualIndex}`, color: "white" }, selectionPrefix),
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
488
|
+
h2(
|
|
489
|
+
import_ink2.Text,
|
|
490
|
+
{
|
|
491
|
+
key: `name-${actualIndex}`,
|
|
492
|
+
color: isSelected ? "black" : "white",
|
|
493
|
+
backgroundColor: isSelected ? "cyan" : void 0,
|
|
494
|
+
bold: isSelected
|
|
495
|
+
},
|
|
496
|
+
label
|
|
497
|
+
)
|
|
435
498
|
);
|
|
436
499
|
};
|
|
437
500
|
const itemRenderer = renderItem || defaultRenderItem;
|
|
@@ -444,42 +507,32 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
|
|
|
444
507
|
if (renderItem) {
|
|
445
508
|
const isFirstVisible = displayIndex === 0;
|
|
446
509
|
const isLastVisible = displayIndex === visibleItems.length - 1;
|
|
447
|
-
let arrowPrefix = "";
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
arrowPrefix = "\
|
|
451
|
-
} else if (isLastVisible && canScrollDown) {
|
|
452
|
-
arrowPrefix = "\u2193 ";
|
|
453
|
-
} else {
|
|
454
|
-
arrowPrefix = " ";
|
|
455
|
-
}
|
|
456
|
-
if (isSelected) {
|
|
457
|
-
selectionPrefix = selectionMarker;
|
|
458
|
-
} else {
|
|
459
|
-
selectionPrefix = " ".repeat(selectionMarker.length);
|
|
510
|
+
let arrowPrefix = " ";
|
|
511
|
+
if (!needsBar) {
|
|
512
|
+
if (isFirstVisible && canScrollUp) arrowPrefix = "\u2191 ";
|
|
513
|
+
else if (isLastVisible && canScrollDown) arrowPrefix = "\u2193 ";
|
|
460
514
|
}
|
|
515
|
+
const selectionPrefix = isSelected ? selectionMarker : " ".repeat(selectionMarker.length);
|
|
516
|
+
const clipped = needsBar ? { ...item, name: clipNameForScrollbar(item.name, true) } : item;
|
|
517
|
+
const row = h2(
|
|
518
|
+
import_ink2.Box,
|
|
519
|
+
{ flexDirection: "row" },
|
|
520
|
+
h2(import_ink2.Text, { key: `arrow-${actualIndex}`, color: "white" }, arrowPrefix),
|
|
521
|
+
h2(import_ink2.Text, { key: `marker-${actualIndex}`, color: "white" }, selectionPrefix),
|
|
522
|
+
renderItem(clipped, isSelected, displayIndex)
|
|
523
|
+
);
|
|
461
524
|
return h2(ScreenRow, {
|
|
462
525
|
key: `item-${actualIndex}`,
|
|
463
|
-
children:
|
|
464
|
-
import_ink2.Box,
|
|
465
|
-
{ flexDirection: "row" },
|
|
466
|
-
// Arrow (clickable if functional, not highlighted)
|
|
467
|
-
h2(import_ink2.Text, {
|
|
468
|
-
key: `arrow-${actualIndex}`,
|
|
469
|
-
color: "white"
|
|
470
|
-
}, arrowPrefix),
|
|
471
|
-
// Selection marker space (always same width, not highlighted)
|
|
472
|
-
h2(import_ink2.Text, { key: `marker-${actualIndex}`, color: "white" }, selectionPrefix),
|
|
473
|
-
// Custom rendered content
|
|
474
|
-
renderItem(item, isSelected, displayIndex)
|
|
475
|
-
)
|
|
476
|
-
});
|
|
477
|
-
} else {
|
|
478
|
-
return h2(ScreenRow, {
|
|
479
|
-
key: `item-${actualIndex}`,
|
|
480
|
-
children: itemRenderer(item, isSelected, displayIndex, actualIndex)
|
|
526
|
+
children: appendBar(row, displayIndex)
|
|
481
527
|
});
|
|
482
528
|
}
|
|
529
|
+
return h2(ScreenRow, {
|
|
530
|
+
key: `item-${actualIndex}`,
|
|
531
|
+
children: appendBar(
|
|
532
|
+
itemRenderer(item, isSelected, displayIndex, actualIndex),
|
|
533
|
+
displayIndex
|
|
534
|
+
)
|
|
535
|
+
});
|
|
483
536
|
})
|
|
484
537
|
);
|
|
485
538
|
}
|
|
@@ -489,6 +542,7 @@ var init_list_components = __esm({
|
|
|
489
542
|
import_react2 = __toESM(require("react"), 1);
|
|
490
543
|
import_ink2 = require("ink");
|
|
491
544
|
init_components();
|
|
545
|
+
init_scrollbar();
|
|
492
546
|
h2 = import_react2.createElement;
|
|
493
547
|
}
|
|
494
548
|
});
|
|
@@ -982,21 +1036,6 @@ function wrapTextLines(text, cols) {
|
|
|
982
1036
|
}
|
|
983
1037
|
return out;
|
|
984
1038
|
}
|
|
985
|
-
function scrollbarGlyphs(viewportRows, totalLines, scrollTop) {
|
|
986
|
-
const view = Math.max(1, Math.floor(viewportRows));
|
|
987
|
-
const total = Math.max(0, Math.floor(totalLines));
|
|
988
|
-
if (total <= view) return null;
|
|
989
|
-
const maxScroll = total - view;
|
|
990
|
-
const thumbSize = Math.max(1, Math.round(view / total * view));
|
|
991
|
-
const travel = Math.max(0, view - thumbSize);
|
|
992
|
-
const s = Math.min(Math.max(0, Math.floor(scrollTop)), maxScroll);
|
|
993
|
-
const thumbStart = maxScroll === 0 ? 0 : Math.round(s / maxScroll * travel);
|
|
994
|
-
const glyphs = [];
|
|
995
|
-
for (let i = 0; i < view; i++) {
|
|
996
|
-
glyphs.push(i >= thumbStart && i < thumbStart + thumbSize ? "\u2588" : "\u2502");
|
|
997
|
-
}
|
|
998
|
-
return glyphs;
|
|
999
|
-
}
|
|
1000
1039
|
function padEndVisible(s, w) {
|
|
1001
1040
|
const t = String(s ?? "");
|
|
1002
1041
|
if (t.length >= w) return t.slice(0, w);
|
|
@@ -1015,20 +1054,19 @@ function ScrollableText({
|
|
|
1015
1054
|
}) {
|
|
1016
1055
|
const [scrollTop, setScrollTop] = (0, import_react5.useState)(0);
|
|
1017
1056
|
const [, bump] = (0, import_react5.useState)(0);
|
|
1018
|
-
const termCols = process.stdout.columns || 80;
|
|
1019
1057
|
const termRows = process.stdout.rows || 24;
|
|
1020
1058
|
const viewportRows = Math.max(
|
|
1021
1059
|
4,
|
|
1022
1060
|
maxHeight != null ? Math.floor(maxHeight) : Math.max(8, termRows - 8)
|
|
1023
1061
|
);
|
|
1024
|
-
const
|
|
1025
|
-
const
|
|
1062
|
+
const contentCols = Math.max(20, getScreenWidth() - 4);
|
|
1063
|
+
const barCols = showScrollbar ? 1 : 0;
|
|
1064
|
+
const textWidth = Math.max(8, contentCols - barCols);
|
|
1026
1065
|
const allLines = (0, import_react5.useMemo)(() => {
|
|
1027
1066
|
if (Array.isArray(linesProp)) return linesProp.map((l) => String(l ?? ""));
|
|
1028
|
-
return wrap ? wrapTextLines(text,
|
|
1029
|
-
}, [linesProp, text, wrap,
|
|
1067
|
+
return wrap ? wrapTextLines(text, textWidth) : String(text ?? "").split("\n");
|
|
1068
|
+
}, [linesProp, text, wrap, textWidth]);
|
|
1030
1069
|
const needsBar = showScrollbar && allLines.length > viewportRows;
|
|
1031
|
-
const textWidth = Math.max(8, termCols - (needsBar ? 1 : 0));
|
|
1032
1070
|
const maxScroll = Math.max(0, allLines.length - viewportRows);
|
|
1033
1071
|
const clamped = Math.min(Math.max(0, scrollTop), maxScroll);
|
|
1034
1072
|
const visible = allLines.slice(clamped, clamped + viewportRows);
|
|
@@ -1068,22 +1106,24 @@ function ScrollableText({
|
|
|
1068
1106
|
const status = allLines.length === 0 ? "empty" : `lines ${clamped + 1}-${Math.min(clamped + visible.length, allLines.length)} of ${allLines.length}` + (needsBar ? " \xB7 \u2325\u2191/\u2193 or PgUp/Dn page" : "");
|
|
1069
1107
|
const rowNodes = visible.map((line, i) => {
|
|
1070
1108
|
const body = padEndVisible(line, textWidth);
|
|
1071
|
-
const glyph = bar ? bar[i] ?? "
|
|
1109
|
+
const glyph = bar ? bar[i] ?? BAR_TRACK : showScrollbar ? " " : "";
|
|
1110
|
+
const isThumb = glyph === BAR_THUMB;
|
|
1072
1111
|
return h5(
|
|
1073
|
-
import_ink5.
|
|
1074
|
-
{ key: `L${clamped + i}
|
|
1075
|
-
|
|
1076
|
-
glyph ? h5(import_ink5.Text, { color:
|
|
1112
|
+
import_ink5.Text,
|
|
1113
|
+
{ key: `L${clamped + i}` },
|
|
1114
|
+
body,
|
|
1115
|
+
glyph ? h5(import_ink5.Text, { color: isThumb ? "cyan" : "gray" }, glyph) : null
|
|
1077
1116
|
);
|
|
1078
1117
|
});
|
|
1079
1118
|
if (bar && visible.length < viewportRows) {
|
|
1080
1119
|
for (let i = visible.length; i < viewportRows; i++) {
|
|
1120
|
+
const glyph = bar[i] ?? BAR_TRACK;
|
|
1081
1121
|
rowNodes.push(
|
|
1082
1122
|
h5(
|
|
1083
|
-
import_ink5.
|
|
1084
|
-
{ key: `pad${i}
|
|
1085
|
-
|
|
1086
|
-
h5(import_ink5.Text, { color:
|
|
1123
|
+
import_ink5.Text,
|
|
1124
|
+
{ key: `pad${i}` },
|
|
1125
|
+
padEndVisible("", textWidth),
|
|
1126
|
+
h5(import_ink5.Text, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
|
|
1087
1127
|
)
|
|
1088
1128
|
);
|
|
1089
1129
|
}
|
|
@@ -1101,16 +1141,14 @@ var init_scrollable_text = __esm({
|
|
|
1101
1141
|
"src/screen/scrollable-text.js"() {
|
|
1102
1142
|
import_react5 = require("react");
|
|
1103
1143
|
import_ink5 = require("ink");
|
|
1144
|
+
init_components();
|
|
1145
|
+
init_scrollbar();
|
|
1146
|
+
init_scrollbar();
|
|
1104
1147
|
h5 = import_react5.createElement;
|
|
1105
1148
|
SCROLL_KEYS = [
|
|
1106
1149
|
{ key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
|
|
1107
1150
|
{ key: "downArrow", caption: "scroll", action: "scrollDown", order: 0 },
|
|
1108
|
-
|
|
1109
|
-
{ key: "downArrow", meta: true, caption: "page", action: "pageDown", order: 0 },
|
|
1110
|
-
{ key: "upArrow", ctrl: true, caption: "page", action: "pageUp", order: 0 },
|
|
1111
|
-
{ key: "downArrow", ctrl: true, caption: "page", action: "pageDown", order: 0 },
|
|
1112
|
-
{ key: "pageUp", caption: "page", action: "pageUp", order: 0 },
|
|
1113
|
-
{ key: "pageDown", caption: "page", action: "pageDown", order: 0 }
|
|
1151
|
+
...PAGE_SCROLL_KEY_BINDINGS
|
|
1114
1152
|
];
|
|
1115
1153
|
}
|
|
1116
1154
|
});
|
|
@@ -1246,6 +1284,8 @@ var init_footer_builder = __esm({
|
|
|
1246
1284
|
// src/screen/index.js
|
|
1247
1285
|
var screen_exports = {};
|
|
1248
1286
|
__export(screen_exports, {
|
|
1287
|
+
BAR_THUMB: () => BAR_THUMB,
|
|
1288
|
+
BAR_TRACK: () => BAR_TRACK,
|
|
1249
1289
|
Box: () => import_ink6.Box,
|
|
1250
1290
|
Divider: () => Divider,
|
|
1251
1291
|
FooterPresets: () => FooterPresets,
|
|
@@ -1255,6 +1295,7 @@ __export(screen_exports, {
|
|
|
1255
1295
|
ListItem: () => ListItem,
|
|
1256
1296
|
MultiColumnListComponent: () => MultiColumnListComponent,
|
|
1257
1297
|
MultiColumnListWithPreviewComponent: () => MultiColumnListWithPreviewComponent,
|
|
1298
|
+
PAGE_SCROLL_KEY_BINDINGS: () => PAGE_SCROLL_KEY_BINDINGS,
|
|
1258
1299
|
React: () => import_react6.default,
|
|
1259
1300
|
ScreenBody: () => ScreenBody,
|
|
1260
1301
|
ScreenContainer: () => ScreenContainer,
|
|
@@ -1310,6 +1351,7 @@ var init_screen = __esm({
|
|
|
1310
1351
|
init_components();
|
|
1311
1352
|
init_ui_elements();
|
|
1312
1353
|
init_scrollable_text();
|
|
1354
|
+
init_scrollbar();
|
|
1313
1355
|
init_key_bindings();
|
|
1314
1356
|
init_utils();
|
|
1315
1357
|
init_footer_builder();
|
|
@@ -4162,12 +4204,13 @@ function tasksSchemaSpec(queueName = "tasks") {
|
|
|
4162
4204
|
}
|
|
4163
4205
|
};
|
|
4164
4206
|
}
|
|
4165
|
-
function taskHistoryInsertFromQueueRow(row, overrides) {
|
|
4207
|
+
function taskHistoryInsertFromQueueRow(row, overrides = {}) {
|
|
4166
4208
|
const { id, ...snapshot } = row;
|
|
4167
|
-
void id;
|
|
4209
|
+
const opid = overrides.opid !== void 0 ? overrides.opid : snapshot.opid != null && String(snapshot.opid).trim() !== "" ? snapshot.opid : id;
|
|
4168
4210
|
return {
|
|
4169
4211
|
...snapshot,
|
|
4170
|
-
...overrides
|
|
4212
|
+
...overrides,
|
|
4213
|
+
opid
|
|
4171
4214
|
};
|
|
4172
4215
|
}
|
|
4173
4216
|
async function ensureTaskTables(context, options = {}) {
|
|
@@ -6467,69 +6510,6 @@ var TaskStopRunner = class extends AbstractTask {
|
|
|
6467
6510
|
}
|
|
6468
6511
|
};
|
|
6469
6512
|
|
|
6470
|
-
// src/tasks/coreTasks/TaskGetLogs.js
|
|
6471
|
-
var TaskGetLogs = class extends AbstractTask {
|
|
6472
|
-
/**
|
|
6473
|
-
* @param {object} context
|
|
6474
|
-
* @param {Record<string, unknown>} [overrides]
|
|
6475
|
-
* @returns {Promise<{ source: string, resource: string, tail: number, afterTs?: string }>}
|
|
6476
|
-
*/
|
|
6477
|
-
static async resolveCustomParams(context, overrides = {}) {
|
|
6478
|
-
const merged = AbstractTask._mergeTypedParams(context, "task-get-logs", {
|
|
6479
|
-
source: "string",
|
|
6480
|
-
resource: "string",
|
|
6481
|
-
tail: "number default 100",
|
|
6482
|
-
afterTs: "string"
|
|
6483
|
-
}, overrides);
|
|
6484
|
-
const source = typeof merged.source === "string" ? merged.source.trim() : "";
|
|
6485
|
-
const resource = typeof merged.resource === "string" ? merged.resource.trim() : "";
|
|
6486
|
-
if (!source) throw new ParamError('getLogs: param "source" is required');
|
|
6487
|
-
if (!resource) throw new ParamError('getLogs: param "resource" is required');
|
|
6488
|
-
let tail = Number(merged.tail);
|
|
6489
|
-
if (!Number.isFinite(tail) || tail < 1) tail = 100;
|
|
6490
|
-
tail = Math.min(1e4, Math.max(1, Math.floor(tail)));
|
|
6491
|
-
const out = { source, resource, tail };
|
|
6492
|
-
if (typeof merged.afterTs === "string" && merged.afterTs.trim()) {
|
|
6493
|
-
out.afterTs = merged.afterTs.trim();
|
|
6494
|
-
}
|
|
6495
|
-
return out;
|
|
6496
|
-
}
|
|
6497
|
-
/**
|
|
6498
|
-
* @param {unknown} _reportProgress Unused (single-shot read, no progress events).
|
|
6499
|
-
* @returns {Promise<{ success: boolean, results: unknown }>}
|
|
6500
|
-
*/
|
|
6501
|
-
async run(_reportProgress) {
|
|
6502
|
-
const p = this.task.params ?? {};
|
|
6503
|
-
const source = String(p.source ?? "").trim();
|
|
6504
|
-
const resource = String(p.resource ?? "").trim();
|
|
6505
|
-
const tail = Math.max(1, Math.min(1e4, Number(p.tail) > 0 ? Number(p.tail) : 100));
|
|
6506
|
-
const afterTs = p.afterTs != null && String(p.afterTs).trim() ? String(p.afterTs).trim() : null;
|
|
6507
|
-
if (!source || !resource) {
|
|
6508
|
-
return {
|
|
6509
|
-
success: false,
|
|
6510
|
-
results: { error: 'getLogs requires params "source" and "resource"' }
|
|
6511
|
-
};
|
|
6512
|
-
}
|
|
6513
|
-
try {
|
|
6514
|
-
const { records, latestTs } = await readTaskIpcLogsSnapshot(this.context, {
|
|
6515
|
-
source,
|
|
6516
|
-
resource,
|
|
6517
|
-
tail,
|
|
6518
|
-
afterTs
|
|
6519
|
-
});
|
|
6520
|
-
return {
|
|
6521
|
-
success: true,
|
|
6522
|
-
results: { records, latestTs, source, resource }
|
|
6523
|
-
};
|
|
6524
|
-
} catch (e) {
|
|
6525
|
-
return {
|
|
6526
|
-
success: false,
|
|
6527
|
-
results: { error: e?.message ?? String(e) }
|
|
6528
|
-
};
|
|
6529
|
-
}
|
|
6530
|
-
}
|
|
6531
|
-
};
|
|
6532
|
-
|
|
6533
6513
|
// src/tasks/runtimeParams.js
|
|
6534
6514
|
var LOOP_RUNTIME_KEYS = ["maxParallel", "pollMs", "claimJitterMs", "scanLimit"];
|
|
6535
6515
|
var LOGGER_RUNTIME_KEYS = [
|
|
@@ -6543,7 +6523,16 @@ var LOGGER_RUNTIME_KEYS = [
|
|
|
6543
6523
|
"progressWithTimes",
|
|
6544
6524
|
"progressThrottleMs"
|
|
6545
6525
|
];
|
|
6546
|
-
var CONTROL_LANE_TASK_NAMES = [
|
|
6526
|
+
var CONTROL_LANE_TASK_NAMES = [
|
|
6527
|
+
"stopRunner",
|
|
6528
|
+
"stop",
|
|
6529
|
+
"pauseRunner",
|
|
6530
|
+
"pause",
|
|
6531
|
+
"unpauseRunner",
|
|
6532
|
+
"unpause",
|
|
6533
|
+
"setRuntimeParam",
|
|
6534
|
+
"setRunnerParam"
|
|
6535
|
+
];
|
|
6547
6536
|
function controlLaneTaskNames(extra) {
|
|
6548
6537
|
const names = [...CONTROL_LANE_TASK_NAMES];
|
|
6549
6538
|
if (extra == null || extra === "") return names;
|
|
@@ -6606,6 +6595,7 @@ function ensureTasksRuntime(context, seed = {}) {
|
|
|
6606
6595
|
if (rt.pollMs === void 0) rt.pollMs = seed.pollMs ?? 1e3;
|
|
6607
6596
|
if (rt.claimJitterMs === void 0) rt.claimJitterMs = seed.claimJitterMs ?? 0;
|
|
6608
6597
|
if (rt.scanLimit === void 0) rt.scanLimit = seed.scanLimit ?? 100;
|
|
6598
|
+
if (rt.paused === void 0) rt.paused = seed.paused === true;
|
|
6609
6599
|
return rt;
|
|
6610
6600
|
}
|
|
6611
6601
|
async function applyRuntimeParam(context, key, value) {
|
|
@@ -6670,6 +6660,140 @@ function readLoopRuntime(context) {
|
|
|
6670
6660
|
};
|
|
6671
6661
|
}
|
|
6672
6662
|
|
|
6663
|
+
// src/tasks/coreTasks/TaskPauseRunner.js
|
|
6664
|
+
async function applyRunnerPaused(context, paused) {
|
|
6665
|
+
const runtime = ensureTasksRuntime(context);
|
|
6666
|
+
const was = runtime.paused === true;
|
|
6667
|
+
runtime.paused = paused === true;
|
|
6668
|
+
const registry = context.servicesRegistry;
|
|
6669
|
+
if (registry && typeof registry === "object") {
|
|
6670
|
+
await updateServicesRegistryMetadata(context, registry, {
|
|
6671
|
+
paused: runtime.paused,
|
|
6672
|
+
pausedAt: runtime.paused ? (/* @__PURE__ */ new Date()).toISOString() : null
|
|
6673
|
+
});
|
|
6674
|
+
}
|
|
6675
|
+
const message = runtime.paused ? was ? "Runner already paused (no new worker tasks)." : "Runner paused: finishing in-flight work; no new worker tasks until unpause." : was ? "Runner unpaused: claiming worker tasks again." : "Runner already running (not paused).";
|
|
6676
|
+
context.logger?.warn?.(
|
|
6677
|
+
runtime.paused ? `[TaskPauseRunner] ${message}` : `[TaskUnpauseRunner] ${message}`
|
|
6678
|
+
);
|
|
6679
|
+
return {
|
|
6680
|
+
success: true,
|
|
6681
|
+
results: {
|
|
6682
|
+
paused: runtime.paused,
|
|
6683
|
+
message
|
|
6684
|
+
}
|
|
6685
|
+
};
|
|
6686
|
+
}
|
|
6687
|
+
var TaskPauseRunner = class extends AbstractTask {
|
|
6688
|
+
static taskName = "pauseRunner";
|
|
6689
|
+
static description = "Pause a runner: finish in-flight tasks, claim no new worker tasks until unpaused";
|
|
6690
|
+
static aliases = ["pause"];
|
|
6691
|
+
static defaultWaitForResult = true;
|
|
6692
|
+
/**
|
|
6693
|
+
* @param {object} context
|
|
6694
|
+
* @param {Record<string, unknown>} [overrides]
|
|
6695
|
+
* @returns {Promise<object>}
|
|
6696
|
+
*/
|
|
6697
|
+
static async resolveParams(context, overrides = {}) {
|
|
6698
|
+
const main = await super.resolveParams(context, overrides);
|
|
6699
|
+
if (!main.serviceName) {
|
|
6700
|
+
throw new ParamError(
|
|
6701
|
+
"pause/pauseRunner requires --serviceName (registry instance name; optional --serviceGroup, --instanceNumber, --serverName to narrow targeting)"
|
|
6702
|
+
);
|
|
6703
|
+
}
|
|
6704
|
+
return main;
|
|
6705
|
+
}
|
|
6706
|
+
async run() {
|
|
6707
|
+
return applyRunnerPaused(this.context, true);
|
|
6708
|
+
}
|
|
6709
|
+
};
|
|
6710
|
+
var TaskUnpauseRunner = class extends AbstractTask {
|
|
6711
|
+
static taskName = "unpauseRunner";
|
|
6712
|
+
static description = "Unpause a runner: resume claiming worker tasks";
|
|
6713
|
+
static aliases = ["unpause"];
|
|
6714
|
+
static defaultWaitForResult = true;
|
|
6715
|
+
/**
|
|
6716
|
+
* @param {object} context
|
|
6717
|
+
* @param {Record<string, unknown>} [overrides]
|
|
6718
|
+
* @returns {Promise<object>}
|
|
6719
|
+
*/
|
|
6720
|
+
static async resolveParams(context, overrides = {}) {
|
|
6721
|
+
const main = await super.resolveParams(context, overrides);
|
|
6722
|
+
if (!main.serviceName) {
|
|
6723
|
+
throw new ParamError(
|
|
6724
|
+
"unpause/unpauseRunner requires --serviceName (registry instance name; optional --serviceGroup, --instanceNumber, --serverName to narrow targeting)"
|
|
6725
|
+
);
|
|
6726
|
+
}
|
|
6727
|
+
return main;
|
|
6728
|
+
}
|
|
6729
|
+
async run() {
|
|
6730
|
+
return applyRunnerPaused(this.context, false);
|
|
6731
|
+
}
|
|
6732
|
+
};
|
|
6733
|
+
|
|
6734
|
+
// src/tasks/coreTasks/TaskGetLogs.js
|
|
6735
|
+
var TaskGetLogs = class extends AbstractTask {
|
|
6736
|
+
/**
|
|
6737
|
+
* @param {object} context
|
|
6738
|
+
* @param {Record<string, unknown>} [overrides]
|
|
6739
|
+
* @returns {Promise<{ source: string, resource: string, tail: number, afterTs?: string }>}
|
|
6740
|
+
*/
|
|
6741
|
+
static async resolveCustomParams(context, overrides = {}) {
|
|
6742
|
+
const merged = AbstractTask._mergeTypedParams(context, "task-get-logs", {
|
|
6743
|
+
source: "string",
|
|
6744
|
+
resource: "string",
|
|
6745
|
+
tail: "number default 100",
|
|
6746
|
+
afterTs: "string"
|
|
6747
|
+
}, overrides);
|
|
6748
|
+
const source = typeof merged.source === "string" ? merged.source.trim() : "";
|
|
6749
|
+
const resource = typeof merged.resource === "string" ? merged.resource.trim() : "";
|
|
6750
|
+
if (!source) throw new ParamError('getLogs: param "source" is required');
|
|
6751
|
+
if (!resource) throw new ParamError('getLogs: param "resource" is required');
|
|
6752
|
+
let tail = Number(merged.tail);
|
|
6753
|
+
if (!Number.isFinite(tail) || tail < 1) tail = 100;
|
|
6754
|
+
tail = Math.min(1e4, Math.max(1, Math.floor(tail)));
|
|
6755
|
+
const out = { source, resource, tail };
|
|
6756
|
+
if (typeof merged.afterTs === "string" && merged.afterTs.trim()) {
|
|
6757
|
+
out.afterTs = merged.afterTs.trim();
|
|
6758
|
+
}
|
|
6759
|
+
return out;
|
|
6760
|
+
}
|
|
6761
|
+
/**
|
|
6762
|
+
* @param {unknown} _reportProgress Unused (single-shot read, no progress events).
|
|
6763
|
+
* @returns {Promise<{ success: boolean, results: unknown }>}
|
|
6764
|
+
*/
|
|
6765
|
+
async run(_reportProgress) {
|
|
6766
|
+
const p = this.task.params ?? {};
|
|
6767
|
+
const source = String(p.source ?? "").trim();
|
|
6768
|
+
const resource = String(p.resource ?? "").trim();
|
|
6769
|
+
const tail = Math.max(1, Math.min(1e4, Number(p.tail) > 0 ? Number(p.tail) : 100));
|
|
6770
|
+
const afterTs = p.afterTs != null && String(p.afterTs).trim() ? String(p.afterTs).trim() : null;
|
|
6771
|
+
if (!source || !resource) {
|
|
6772
|
+
return {
|
|
6773
|
+
success: false,
|
|
6774
|
+
results: { error: 'getLogs requires params "source" and "resource"' }
|
|
6775
|
+
};
|
|
6776
|
+
}
|
|
6777
|
+
try {
|
|
6778
|
+
const { records, latestTs } = await readTaskIpcLogsSnapshot(this.context, {
|
|
6779
|
+
source,
|
|
6780
|
+
resource,
|
|
6781
|
+
tail,
|
|
6782
|
+
afterTs
|
|
6783
|
+
});
|
|
6784
|
+
return {
|
|
6785
|
+
success: true,
|
|
6786
|
+
results: { records, latestTs, source, resource }
|
|
6787
|
+
};
|
|
6788
|
+
} catch (e) {
|
|
6789
|
+
return {
|
|
6790
|
+
success: false,
|
|
6791
|
+
results: { error: e?.message ?? String(e) }
|
|
6792
|
+
};
|
|
6793
|
+
}
|
|
6794
|
+
}
|
|
6795
|
+
};
|
|
6796
|
+
|
|
6673
6797
|
// src/tasks/coreTasks/TaskSetRuntimeParam.js
|
|
6674
6798
|
var TaskSetRuntimeParam = class extends AbstractTask {
|
|
6675
6799
|
static defaultWaitForResult = true;
|
|
@@ -6825,7 +6949,7 @@ var TasksRegistry = class _TasksRegistry {
|
|
|
6825
6949
|
* @returns {TasksRegistry}
|
|
6826
6950
|
*/
|
|
6827
6951
|
static withCoreTasks() {
|
|
6828
|
-
return new _TasksRegistry().add("ping", TaskPing).add("sampleProcess", TaskSampleProcess).add("shellCommand", TaskShellCommand).add("systemInfo", TaskSystemInfo).add("info", TaskSystemInfo).add("taskSumAB", TaskSumAB).add("stopRunner", TaskStopRunner).add("stop", TaskStopRunner).add("getLogs", TaskGetLogs).add("setRuntimeParam", TaskSetRuntimeParam).add("setRunnerParam", TaskSetRuntimeParam);
|
|
6952
|
+
return new _TasksRegistry().add("ping", TaskPing).add("sampleProcess", TaskSampleProcess).add("shellCommand", TaskShellCommand).add("systemInfo", TaskSystemInfo).add("info", TaskSystemInfo).add("taskSumAB", TaskSumAB).add("stopRunner", TaskStopRunner).add("stop", TaskStopRunner).add("pauseRunner", TaskPauseRunner).add("pause", TaskPauseRunner).add("unpauseRunner", TaskUnpauseRunner).add("unpause", TaskUnpauseRunner).add("getLogs", TaskGetLogs).add("setRuntimeParam", TaskSetRuntimeParam).add("setRunnerParam", TaskSetRuntimeParam);
|
|
6829
6953
|
}
|
|
6830
6954
|
/**
|
|
6831
6955
|
* Register a single task class under a name. Overwrites any previous entry.
|
|
@@ -7187,6 +7311,7 @@ async function runTasksLoop(context, options) {
|
|
|
7187
7311
|
const defaultMeta = {
|
|
7188
7312
|
component: "tasks-runner",
|
|
7189
7313
|
allowedTasks: allowedTasks?.length ? allowedTasks.join(",") : "all",
|
|
7314
|
+
paused: false,
|
|
7190
7315
|
runtime: {
|
|
7191
7316
|
maxParallel: loop0.maxParallel,
|
|
7192
7317
|
pollMs: loop0.pollMs,
|
|
@@ -7253,28 +7378,38 @@ async function runTasksLoop(context, options) {
|
|
|
7253
7378
|
if (claimJitterMs > 0) {
|
|
7254
7379
|
await sleepMs(Math.floor(Math.random() * (claimJitterMs + 1)));
|
|
7255
7380
|
}
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
if (
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7381
|
+
const paused = context.tasksRuntime?.paused === true;
|
|
7382
|
+
if (!paused) {
|
|
7383
|
+
while (runningPromises.size < maxParallel) {
|
|
7384
|
+
const claimed = await claimNextRunnableTask(
|
|
7385
|
+
context,
|
|
7386
|
+
tasksTable,
|
|
7387
|
+
target,
|
|
7388
|
+
registry,
|
|
7389
|
+
scanLimit,
|
|
7390
|
+
allowedTasks,
|
|
7391
|
+
runnerIdentity
|
|
7392
|
+
);
|
|
7393
|
+
if (!claimed) break;
|
|
7394
|
+
const p = executeClaimedTask(
|
|
7395
|
+
context,
|
|
7396
|
+
tasksTable,
|
|
7397
|
+
historyTable,
|
|
7398
|
+
claimed,
|
|
7399
|
+
registry,
|
|
7400
|
+
runningTaskInstances
|
|
7401
|
+
).then(async (outcome) => {
|
|
7402
|
+
if (outcome.stopRunnerRequested && !stopRequested) {
|
|
7403
|
+
stopRequested = true;
|
|
7404
|
+
stopAllowanceMs = outcome.stopAllowanceMs || stopAllowanceMs;
|
|
7405
|
+
context.tasksRunnerStop = true;
|
|
7406
|
+
await signalRunningTasksStop(context, runningTaskInstances, stopAllowanceMs);
|
|
7407
|
+
}
|
|
7408
|
+
}).finally(() => {
|
|
7409
|
+
runningPromises.delete(p);
|
|
7410
|
+
});
|
|
7411
|
+
runningPromises.add(p);
|
|
7412
|
+
}
|
|
7278
7413
|
}
|
|
7279
7414
|
const wakePromises = [...runningPromises];
|
|
7280
7415
|
if (runningControlPromise) {
|