@nmakarov/cli-toolkit 0.69.0 → 0.71.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 +386 -192
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +386 -192
- package/dist/cli-runner.js.map +1 -1
- package/dist/index.cjs +440 -208
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +430 -208
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +129 -91
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +129 -91
- package/dist/init.js.map +1 -1
- package/dist/screen.cjs +124 -90
- package/dist/screen.cjs.map +1 -1
- package/dist/screen.js +121 -90
- package/dist/screen.js.map +1 -1
- package/dist/tasks.cjs +308 -117
- package/dist/tasks.cjs.map +1 -1
- package/dist/tasks.js +301 -117
- package/dist/tasks.js.map +1 -1
- package/package.json +2 -2
package/dist/cli-runner.js
CHANGED
|
@@ -94,9 +94,48 @@ var init_components = __esm({
|
|
|
94
94
|
}
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
+
// src/screen/scrollbar.js
|
|
98
|
+
function scrollbarGlyphs(viewportRows, totalLines, scrollTop) {
|
|
99
|
+
const view = Math.max(1, Math.floor(viewportRows));
|
|
100
|
+
const total = Math.max(0, Math.floor(totalLines));
|
|
101
|
+
if (total <= view) return null;
|
|
102
|
+
const maxScroll = total - view;
|
|
103
|
+
const thumbSize = Math.max(1, Math.round(view / total * view));
|
|
104
|
+
const travel = Math.max(0, view - thumbSize);
|
|
105
|
+
const s = Math.min(Math.max(0, Math.floor(scrollTop)), maxScroll);
|
|
106
|
+
const thumbStart = maxScroll === 0 ? 0 : Math.round(s / maxScroll * travel);
|
|
107
|
+
const glyphs = [];
|
|
108
|
+
for (let i = 0; i < view; i++) {
|
|
109
|
+
glyphs.push(i >= thumbStart && i < thumbStart + thumbSize ? BAR_THUMB : BAR_TRACK);
|
|
110
|
+
}
|
|
111
|
+
return glyphs;
|
|
112
|
+
}
|
|
113
|
+
var BAR_THUMB, BAR_TRACK, PAGE_SCROLL_KEY_BINDINGS;
|
|
114
|
+
var init_scrollbar = __esm({
|
|
115
|
+
"src/screen/scrollbar.js"() {
|
|
116
|
+
BAR_THUMB = "#";
|
|
117
|
+
BAR_TRACK = "|";
|
|
118
|
+
PAGE_SCROLL_KEY_BINDINGS = [
|
|
119
|
+
{ key: "upArrow", meta: true, caption: "page", action: "pageUp", order: 0 },
|
|
120
|
+
{ key: "downArrow", meta: true, caption: "page", action: "pageDown", order: 0 },
|
|
121
|
+
{ key: "upArrow", ctrl: true, caption: "page", action: "pageUp", order: 0 },
|
|
122
|
+
{ key: "downArrow", ctrl: true, caption: "page", action: "pageDown", order: 0 },
|
|
123
|
+
{ key: "pageUp", caption: "page", action: "pageUp", order: 0 },
|
|
124
|
+
{ key: "pageDown", caption: "page", action: "pageDown", order: 0 }
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
97
129
|
// src/screen/list-components.js
|
|
98
130
|
import React, { useState, useEffect, useRef, createElement } from "react";
|
|
99
131
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
132
|
+
function clipNameForScrollbar(name, needsBar) {
|
|
133
|
+
if (!needsBar || name == null) return name;
|
|
134
|
+
const s = String(name);
|
|
135
|
+
if (s.length <= 1) return s;
|
|
136
|
+
if (/\s$/.test(s)) return s.slice(0, -1);
|
|
137
|
+
return s;
|
|
138
|
+
}
|
|
100
139
|
function MultiColumnListComponent({ items, ctx, selectedIndexRef }) {
|
|
101
140
|
const [, forceUpdate] = useState({});
|
|
102
141
|
const termWidth = (process.stdout.columns || 80) - 8;
|
|
@@ -310,17 +349,42 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
|
|
|
310
349
|
});
|
|
311
350
|
ctx.setAction("scrollUp", () => {
|
|
312
351
|
const { scrollOffset: currentScrollOffset } = scrollStateRef.current;
|
|
313
|
-
|
|
314
|
-
setScrollOffset(newScrollOffset);
|
|
352
|
+
setScrollOffset(Math.max(0, currentScrollOffset - 1));
|
|
315
353
|
forceUpdate({});
|
|
316
354
|
});
|
|
317
355
|
ctx.setAction("scrollDown", () => {
|
|
318
356
|
const { scrollOffset: currentScrollOffset, maxHeight: currentMaxHeight, totalItems } = scrollStateRef.current;
|
|
319
357
|
const currentMaxScrollOffset = Math.max(0, totalItems - currentMaxHeight);
|
|
320
|
-
|
|
321
|
-
|
|
358
|
+
setScrollOffset(Math.min(currentMaxScrollOffset, currentScrollOffset + 1));
|
|
359
|
+
forceUpdate({});
|
|
360
|
+
});
|
|
361
|
+
ctx.setAction("pageUp", () => {
|
|
362
|
+
const { maxHeight: vh } = scrollStateRef.current;
|
|
363
|
+
const page = Math.max(1, vh || 1);
|
|
364
|
+
const newIndex = Math.max(0, selectedIndexRef.current - page);
|
|
365
|
+
selectedIndexRef.current = newIndex;
|
|
366
|
+
const list = displayItemsRef.current;
|
|
367
|
+
onSelectionChangeRef.current?.(newIndex, list[newIndex]);
|
|
368
|
+
setScrollOffset(newIndex);
|
|
369
|
+
forceUpdate({});
|
|
370
|
+
});
|
|
371
|
+
ctx.setAction("pageDown", () => {
|
|
372
|
+
const { maxHeight: vh, totalItems } = scrollStateRef.current;
|
|
373
|
+
const page = Math.max(1, vh || 1);
|
|
374
|
+
const maxIndex = Math.max(0, totalItems - 1);
|
|
375
|
+
const newIndex = Math.min(maxIndex, selectedIndexRef.current + page);
|
|
376
|
+
selectedIndexRef.current = newIndex;
|
|
377
|
+
const list = displayItemsRef.current;
|
|
378
|
+
onSelectionChangeRef.current?.(newIndex, list[newIndex]);
|
|
379
|
+
const maxScroll = Math.max(0, totalItems - page);
|
|
380
|
+
setScrollOffset(Math.min(maxScroll, Math.max(0, newIndex - page + 1)));
|
|
322
381
|
forceUpdate({});
|
|
323
382
|
});
|
|
383
|
+
const navBindings = [
|
|
384
|
+
{ key: "upArrow", caption: "navigate", action: "moveUp", order: 0 },
|
|
385
|
+
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 },
|
|
386
|
+
...PAGE_SCROLL_KEY_BINDINGS
|
|
387
|
+
];
|
|
324
388
|
if (sortable) {
|
|
325
389
|
ctx.setAction("toggleSort", () => {
|
|
326
390
|
const nextSort = sortOrder === "none" ? "asc" : sortOrder === "asc" ? "desc" : "none";
|
|
@@ -367,8 +431,7 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
|
|
|
367
431
|
}
|
|
368
432
|
};
|
|
369
433
|
ctx.setKeyBinding([
|
|
370
|
-
|
|
371
|
-
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 },
|
|
434
|
+
...navBindings,
|
|
372
435
|
{
|
|
373
436
|
key: "s",
|
|
374
437
|
caption: sortCaption,
|
|
@@ -378,47 +441,47 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
|
|
|
378
441
|
]);
|
|
379
442
|
ctx.update();
|
|
380
443
|
} else {
|
|
381
|
-
ctx.setKeyBinding(
|
|
382
|
-
{ key: "upArrow", caption: "navigate", action: "moveUp", order: 0 },
|
|
383
|
-
{ key: "downArrow", caption: "navigate", action: "moveDown", order: 0 }
|
|
384
|
-
]);
|
|
444
|
+
ctx.setKeyBinding(navBindings);
|
|
385
445
|
}
|
|
386
446
|
}, [sortOrder, sortable]);
|
|
387
447
|
const selectedIndex = selectedIndexRef.current;
|
|
448
|
+
const needsBar = displayItems.length > effectiveMaxHeight;
|
|
449
|
+
const barGlyphs = needsBar ? scrollbarGlyphs(effectiveMaxHeight, displayItems.length, clampedScrollOffset) : null;
|
|
450
|
+
const appendBar = (rowContent, displayIndex) => {
|
|
451
|
+
if (!barGlyphs) return rowContent;
|
|
452
|
+
const glyph = barGlyphs[displayIndex] ?? BAR_TRACK;
|
|
453
|
+
return h2(
|
|
454
|
+
Box2,
|
|
455
|
+
{ flexDirection: "row" },
|
|
456
|
+
rowContent,
|
|
457
|
+
h2(Text2, { color: glyph === BAR_THUMB ? "cyan" : "gray" }, glyph)
|
|
458
|
+
);
|
|
459
|
+
};
|
|
388
460
|
const defaultRenderItem = (item, isSelected, displayIndex, actualIndex) => {
|
|
389
461
|
const isFirstVisible = displayIndex === 0;
|
|
390
462
|
const isLastVisible = displayIndex === visibleItems.length - 1;
|
|
391
|
-
let arrowPrefix = "";
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
arrowPrefix = "\
|
|
395
|
-
} else if (isLastVisible && canScrollDown) {
|
|
396
|
-
arrowPrefix = "\u2193 ";
|
|
397
|
-
} else {
|
|
398
|
-
arrowPrefix = " ";
|
|
399
|
-
}
|
|
400
|
-
if (isSelected) {
|
|
401
|
-
selectionPrefix = selectionMarker;
|
|
402
|
-
} else {
|
|
403
|
-
selectionPrefix = " ".repeat(selectionMarker.length);
|
|
463
|
+
let arrowPrefix = " ";
|
|
464
|
+
if (!needsBar) {
|
|
465
|
+
if (isFirstVisible && canScrollUp) arrowPrefix = "\u2191 ";
|
|
466
|
+
else if (isLastVisible && canScrollDown) arrowPrefix = "\u2193 ";
|
|
404
467
|
}
|
|
468
|
+
const selectionPrefix = isSelected ? selectionMarker : " ".repeat(selectionMarker.length);
|
|
469
|
+
const label = clipNameForScrollbar(item.name, needsBar);
|
|
405
470
|
return h2(
|
|
406
471
|
Box2,
|
|
407
472
|
{ flexDirection: "row" },
|
|
408
|
-
|
|
409
|
-
h2(Text2, {
|
|
410
|
-
key: `arrow-${actualIndex}`,
|
|
411
|
-
color: "white"
|
|
412
|
-
}, arrowPrefix),
|
|
413
|
-
// Selection marker space (always same width, not highlighted)
|
|
473
|
+
h2(Text2, { key: `arrow-${actualIndex}`, color: "white" }, arrowPrefix),
|
|
414
474
|
h2(Text2, { key: `marker-${actualIndex}`, color: "white" }, selectionPrefix),
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
475
|
+
h2(
|
|
476
|
+
Text2,
|
|
477
|
+
{
|
|
478
|
+
key: `name-${actualIndex}`,
|
|
479
|
+
color: isSelected ? "black" : "white",
|
|
480
|
+
backgroundColor: isSelected ? "cyan" : void 0,
|
|
481
|
+
bold: isSelected
|
|
482
|
+
},
|
|
483
|
+
label
|
|
484
|
+
)
|
|
422
485
|
);
|
|
423
486
|
};
|
|
424
487
|
const itemRenderer = renderItem || defaultRenderItem;
|
|
@@ -431,42 +494,32 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
|
|
|
431
494
|
if (renderItem) {
|
|
432
495
|
const isFirstVisible = displayIndex === 0;
|
|
433
496
|
const isLastVisible = displayIndex === visibleItems.length - 1;
|
|
434
|
-
let arrowPrefix = "";
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
arrowPrefix = "\
|
|
438
|
-
} else if (isLastVisible && canScrollDown) {
|
|
439
|
-
arrowPrefix = "\u2193 ";
|
|
440
|
-
} else {
|
|
441
|
-
arrowPrefix = " ";
|
|
442
|
-
}
|
|
443
|
-
if (isSelected) {
|
|
444
|
-
selectionPrefix = selectionMarker;
|
|
445
|
-
} else {
|
|
446
|
-
selectionPrefix = " ".repeat(selectionMarker.length);
|
|
497
|
+
let arrowPrefix = " ";
|
|
498
|
+
if (!needsBar) {
|
|
499
|
+
if (isFirstVisible && canScrollUp) arrowPrefix = "\u2191 ";
|
|
500
|
+
else if (isLastVisible && canScrollDown) arrowPrefix = "\u2193 ";
|
|
447
501
|
}
|
|
502
|
+
const selectionPrefix = isSelected ? selectionMarker : " ".repeat(selectionMarker.length);
|
|
503
|
+
const clipped = needsBar ? { ...item, name: clipNameForScrollbar(item.name, true) } : item;
|
|
504
|
+
const row = h2(
|
|
505
|
+
Box2,
|
|
506
|
+
{ flexDirection: "row" },
|
|
507
|
+
h2(Text2, { key: `arrow-${actualIndex}`, color: "white" }, arrowPrefix),
|
|
508
|
+
h2(Text2, { key: `marker-${actualIndex}`, color: "white" }, selectionPrefix),
|
|
509
|
+
renderItem(clipped, isSelected, displayIndex)
|
|
510
|
+
);
|
|
448
511
|
return h2(ScreenRow, {
|
|
449
512
|
key: `item-${actualIndex}`,
|
|
450
|
-
children:
|
|
451
|
-
Box2,
|
|
452
|
-
{ flexDirection: "row" },
|
|
453
|
-
// Arrow (clickable if functional, not highlighted)
|
|
454
|
-
h2(Text2, {
|
|
455
|
-
key: `arrow-${actualIndex}`,
|
|
456
|
-
color: "white"
|
|
457
|
-
}, arrowPrefix),
|
|
458
|
-
// Selection marker space (always same width, not highlighted)
|
|
459
|
-
h2(Text2, { key: `marker-${actualIndex}`, color: "white" }, selectionPrefix),
|
|
460
|
-
// Custom rendered content
|
|
461
|
-
renderItem(item, isSelected, displayIndex)
|
|
462
|
-
)
|
|
463
|
-
});
|
|
464
|
-
} else {
|
|
465
|
-
return h2(ScreenRow, {
|
|
466
|
-
key: `item-${actualIndex}`,
|
|
467
|
-
children: itemRenderer(item, isSelected, displayIndex, actualIndex)
|
|
513
|
+
children: appendBar(row, displayIndex)
|
|
468
514
|
});
|
|
469
515
|
}
|
|
516
|
+
return h2(ScreenRow, {
|
|
517
|
+
key: `item-${actualIndex}`,
|
|
518
|
+
children: appendBar(
|
|
519
|
+
itemRenderer(item, isSelected, displayIndex, actualIndex),
|
|
520
|
+
displayIndex
|
|
521
|
+
)
|
|
522
|
+
});
|
|
470
523
|
})
|
|
471
524
|
);
|
|
472
525
|
}
|
|
@@ -474,6 +527,7 @@ var h2;
|
|
|
474
527
|
var init_list_components = __esm({
|
|
475
528
|
"src/screen/list-components.js"() {
|
|
476
529
|
init_components();
|
|
530
|
+
init_scrollbar();
|
|
477
531
|
h2 = createElement;
|
|
478
532
|
}
|
|
479
533
|
});
|
|
@@ -968,21 +1022,6 @@ function wrapTextLines(text, cols) {
|
|
|
968
1022
|
}
|
|
969
1023
|
return out;
|
|
970
1024
|
}
|
|
971
|
-
function scrollbarGlyphs(viewportRows, totalLines, scrollTop) {
|
|
972
|
-
const view = Math.max(1, Math.floor(viewportRows));
|
|
973
|
-
const total = Math.max(0, Math.floor(totalLines));
|
|
974
|
-
if (total <= view) return null;
|
|
975
|
-
const maxScroll = total - view;
|
|
976
|
-
const thumbSize = Math.max(1, Math.round(view / total * view));
|
|
977
|
-
const travel = Math.max(0, view - thumbSize);
|
|
978
|
-
const s = Math.min(Math.max(0, Math.floor(scrollTop)), maxScroll);
|
|
979
|
-
const thumbStart = maxScroll === 0 ? 0 : Math.round(s / maxScroll * travel);
|
|
980
|
-
const glyphs = [];
|
|
981
|
-
for (let i = 0; i < view; i++) {
|
|
982
|
-
glyphs.push(i >= thumbStart && i < thumbStart + thumbSize ? BAR_THUMB : BAR_TRACK);
|
|
983
|
-
}
|
|
984
|
-
return glyphs;
|
|
985
|
-
}
|
|
986
1025
|
function padEndVisible(s, w) {
|
|
987
1026
|
const t = String(s ?? "");
|
|
988
1027
|
if (t.length >= w) return t.slice(0, w);
|
|
@@ -1083,22 +1122,17 @@ function ScrollableText({
|
|
|
1083
1122
|
...rowNodes
|
|
1084
1123
|
);
|
|
1085
1124
|
}
|
|
1086
|
-
var h5,
|
|
1125
|
+
var h5, SCROLL_KEYS;
|
|
1087
1126
|
var init_scrollable_text = __esm({
|
|
1088
1127
|
"src/screen/scrollable-text.js"() {
|
|
1089
1128
|
init_components();
|
|
1129
|
+
init_scrollbar();
|
|
1130
|
+
init_scrollbar();
|
|
1090
1131
|
h5 = createElement2;
|
|
1091
|
-
BAR_THUMB = "#";
|
|
1092
|
-
BAR_TRACK = "|";
|
|
1093
1132
|
SCROLL_KEYS = [
|
|
1094
1133
|
{ key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
|
|
1095
1134
|
{ key: "downArrow", caption: "scroll", action: "scrollDown", order: 0 },
|
|
1096
|
-
|
|
1097
|
-
{ key: "downArrow", meta: true, caption: "page", action: "pageDown", order: 0 },
|
|
1098
|
-
{ key: "upArrow", ctrl: true, caption: "page", action: "pageUp", order: 0 },
|
|
1099
|
-
{ key: "downArrow", ctrl: true, caption: "page", action: "pageDown", order: 0 },
|
|
1100
|
-
{ key: "pageUp", caption: "page", action: "pageUp", order: 0 },
|
|
1101
|
-
{ key: "pageDown", caption: "page", action: "pageDown", order: 0 }
|
|
1135
|
+
...PAGE_SCROLL_KEY_BINDINGS
|
|
1102
1136
|
];
|
|
1103
1137
|
}
|
|
1104
1138
|
});
|
|
@@ -1234,6 +1268,8 @@ var init_footer_builder = __esm({
|
|
|
1234
1268
|
// src/screen/index.js
|
|
1235
1269
|
var screen_exports = {};
|
|
1236
1270
|
__export(screen_exports, {
|
|
1271
|
+
BAR_THUMB: () => BAR_THUMB,
|
|
1272
|
+
BAR_TRACK: () => BAR_TRACK,
|
|
1237
1273
|
Box: () => Box5,
|
|
1238
1274
|
Divider: () => Divider,
|
|
1239
1275
|
FooterPresets: () => FooterPresets,
|
|
@@ -1243,6 +1279,7 @@ __export(screen_exports, {
|
|
|
1243
1279
|
ListItem: () => ListItem,
|
|
1244
1280
|
MultiColumnListComponent: () => MultiColumnListComponent,
|
|
1245
1281
|
MultiColumnListWithPreviewComponent: () => MultiColumnListWithPreviewComponent,
|
|
1282
|
+
PAGE_SCROLL_KEY_BINDINGS: () => PAGE_SCROLL_KEY_BINDINGS,
|
|
1246
1283
|
React: () => React2,
|
|
1247
1284
|
ScreenBody: () => ScreenBody,
|
|
1248
1285
|
ScreenContainer: () => ScreenContainer,
|
|
@@ -1298,6 +1335,7 @@ var init_screen = __esm({
|
|
|
1298
1335
|
init_components();
|
|
1299
1336
|
init_ui_elements();
|
|
1300
1337
|
init_scrollable_text();
|
|
1338
|
+
init_scrollbar();
|
|
1301
1339
|
init_key_bindings();
|
|
1302
1340
|
init_utils();
|
|
1303
1341
|
init_footer_builder();
|
|
@@ -4150,12 +4188,13 @@ function tasksSchemaSpec(queueName = "tasks") {
|
|
|
4150
4188
|
}
|
|
4151
4189
|
};
|
|
4152
4190
|
}
|
|
4153
|
-
function taskHistoryInsertFromQueueRow(row, overrides) {
|
|
4191
|
+
function taskHistoryInsertFromQueueRow(row, overrides = {}) {
|
|
4154
4192
|
const { id, ...snapshot } = row;
|
|
4155
|
-
void id;
|
|
4193
|
+
const opid = overrides.opid !== void 0 ? overrides.opid : snapshot.opid != null && String(snapshot.opid).trim() !== "" ? snapshot.opid : id;
|
|
4156
4194
|
return {
|
|
4157
4195
|
...snapshot,
|
|
4158
|
-
...overrides
|
|
4196
|
+
...overrides,
|
|
4197
|
+
opid
|
|
4159
4198
|
};
|
|
4160
4199
|
}
|
|
4161
4200
|
async function ensureTaskTables(context, options = {}) {
|
|
@@ -6455,69 +6494,6 @@ var TaskStopRunner = class extends AbstractTask {
|
|
|
6455
6494
|
}
|
|
6456
6495
|
};
|
|
6457
6496
|
|
|
6458
|
-
// src/tasks/coreTasks/TaskGetLogs.js
|
|
6459
|
-
var TaskGetLogs = class extends AbstractTask {
|
|
6460
|
-
/**
|
|
6461
|
-
* @param {object} context
|
|
6462
|
-
* @param {Record<string, unknown>} [overrides]
|
|
6463
|
-
* @returns {Promise<{ source: string, resource: string, tail: number, afterTs?: string }>}
|
|
6464
|
-
*/
|
|
6465
|
-
static async resolveCustomParams(context, overrides = {}) {
|
|
6466
|
-
const merged = AbstractTask._mergeTypedParams(context, "task-get-logs", {
|
|
6467
|
-
source: "string",
|
|
6468
|
-
resource: "string",
|
|
6469
|
-
tail: "number default 100",
|
|
6470
|
-
afterTs: "string"
|
|
6471
|
-
}, overrides);
|
|
6472
|
-
const source = typeof merged.source === "string" ? merged.source.trim() : "";
|
|
6473
|
-
const resource = typeof merged.resource === "string" ? merged.resource.trim() : "";
|
|
6474
|
-
if (!source) throw new ParamError('getLogs: param "source" is required');
|
|
6475
|
-
if (!resource) throw new ParamError('getLogs: param "resource" is required');
|
|
6476
|
-
let tail = Number(merged.tail);
|
|
6477
|
-
if (!Number.isFinite(tail) || tail < 1) tail = 100;
|
|
6478
|
-
tail = Math.min(1e4, Math.max(1, Math.floor(tail)));
|
|
6479
|
-
const out = { source, resource, tail };
|
|
6480
|
-
if (typeof merged.afterTs === "string" && merged.afterTs.trim()) {
|
|
6481
|
-
out.afterTs = merged.afterTs.trim();
|
|
6482
|
-
}
|
|
6483
|
-
return out;
|
|
6484
|
-
}
|
|
6485
|
-
/**
|
|
6486
|
-
* @param {unknown} _reportProgress Unused (single-shot read, no progress events).
|
|
6487
|
-
* @returns {Promise<{ success: boolean, results: unknown }>}
|
|
6488
|
-
*/
|
|
6489
|
-
async run(_reportProgress) {
|
|
6490
|
-
const p = this.task.params ?? {};
|
|
6491
|
-
const source = String(p.source ?? "").trim();
|
|
6492
|
-
const resource = String(p.resource ?? "").trim();
|
|
6493
|
-
const tail = Math.max(1, Math.min(1e4, Number(p.tail) > 0 ? Number(p.tail) : 100));
|
|
6494
|
-
const afterTs = p.afterTs != null && String(p.afterTs).trim() ? String(p.afterTs).trim() : null;
|
|
6495
|
-
if (!source || !resource) {
|
|
6496
|
-
return {
|
|
6497
|
-
success: false,
|
|
6498
|
-
results: { error: 'getLogs requires params "source" and "resource"' }
|
|
6499
|
-
};
|
|
6500
|
-
}
|
|
6501
|
-
try {
|
|
6502
|
-
const { records, latestTs } = await readTaskIpcLogsSnapshot(this.context, {
|
|
6503
|
-
source,
|
|
6504
|
-
resource,
|
|
6505
|
-
tail,
|
|
6506
|
-
afterTs
|
|
6507
|
-
});
|
|
6508
|
-
return {
|
|
6509
|
-
success: true,
|
|
6510
|
-
results: { records, latestTs, source, resource }
|
|
6511
|
-
};
|
|
6512
|
-
} catch (e) {
|
|
6513
|
-
return {
|
|
6514
|
-
success: false,
|
|
6515
|
-
results: { error: e?.message ?? String(e) }
|
|
6516
|
-
};
|
|
6517
|
-
}
|
|
6518
|
-
}
|
|
6519
|
-
};
|
|
6520
|
-
|
|
6521
6497
|
// src/tasks/runtimeParams.js
|
|
6522
6498
|
var LOOP_RUNTIME_KEYS = ["maxParallel", "pollMs", "claimJitterMs", "scanLimit"];
|
|
6523
6499
|
var LOGGER_RUNTIME_KEYS = [
|
|
@@ -6531,7 +6507,69 @@ var LOGGER_RUNTIME_KEYS = [
|
|
|
6531
6507
|
"progressWithTimes",
|
|
6532
6508
|
"progressThrottleMs"
|
|
6533
6509
|
];
|
|
6534
|
-
var
|
|
6510
|
+
var DEFAULT_RUNTIME_PARAM_SPECS = [
|
|
6511
|
+
{
|
|
6512
|
+
key: "maxParallel",
|
|
6513
|
+
type: "number",
|
|
6514
|
+
label: "Max parallel",
|
|
6515
|
+
description: "Worker-lane concurrency (how many tasks claim at once)"
|
|
6516
|
+
},
|
|
6517
|
+
{
|
|
6518
|
+
key: "pollMs",
|
|
6519
|
+
type: "number",
|
|
6520
|
+
label: "Poll ms",
|
|
6521
|
+
description: "Idle poll interval between claim attempts"
|
|
6522
|
+
},
|
|
6523
|
+
{
|
|
6524
|
+
key: "claimJitterMs",
|
|
6525
|
+
type: "number",
|
|
6526
|
+
label: "Claim jitter ms",
|
|
6527
|
+
description: "Random delay before worker claims (0 = off)"
|
|
6528
|
+
},
|
|
6529
|
+
{
|
|
6530
|
+
key: "scanLimit",
|
|
6531
|
+
type: "number",
|
|
6532
|
+
label: "Scan limit",
|
|
6533
|
+
description: "Max idle rows scanned per claim attempt"
|
|
6534
|
+
}
|
|
6535
|
+
];
|
|
6536
|
+
function mergeRuntimeParamSpecs(extra) {
|
|
6537
|
+
const byKey = new Map(DEFAULT_RUNTIME_PARAM_SPECS.map((s) => [s.key, { ...s }]));
|
|
6538
|
+
if (Array.isArray(extra)) {
|
|
6539
|
+
for (const raw of extra) {
|
|
6540
|
+
if (!raw || typeof raw !== "object") continue;
|
|
6541
|
+
const key = String(raw.key ?? "").trim();
|
|
6542
|
+
if (!key) continue;
|
|
6543
|
+
const prev = byKey.get(key) ?? {};
|
|
6544
|
+
const type = ["number", "boolean", "string"].includes(raw.type) ? raw.type : prev.type ?? "string";
|
|
6545
|
+
byKey.set(key, {
|
|
6546
|
+
key,
|
|
6547
|
+
type,
|
|
6548
|
+
label: String(raw.label ?? prev.label ?? key),
|
|
6549
|
+
description: raw.description != null ? String(raw.description) : prev.description != null ? String(prev.description) : void 0
|
|
6550
|
+
});
|
|
6551
|
+
}
|
|
6552
|
+
}
|
|
6553
|
+
return Array.from(byKey.values());
|
|
6554
|
+
}
|
|
6555
|
+
function runtimeValuesForSpecs(context, specs = DEFAULT_RUNTIME_PARAM_SPECS) {
|
|
6556
|
+
const rt = ensureTasksRuntime(context);
|
|
6557
|
+
const out = {};
|
|
6558
|
+
for (const s of specs) {
|
|
6559
|
+
if (rt[s.key] !== void 0) out[s.key] = rt[s.key];
|
|
6560
|
+
}
|
|
6561
|
+
return out;
|
|
6562
|
+
}
|
|
6563
|
+
var CONTROL_LANE_TASK_NAMES = [
|
|
6564
|
+
"stopRunner",
|
|
6565
|
+
"stop",
|
|
6566
|
+
"pauseRunner",
|
|
6567
|
+
"pause",
|
|
6568
|
+
"unpauseRunner",
|
|
6569
|
+
"unpause",
|
|
6570
|
+
"setRuntimeParam",
|
|
6571
|
+
"setRunnerParam"
|
|
6572
|
+
];
|
|
6535
6573
|
function controlLaneTaskNames(extra) {
|
|
6536
6574
|
const names = [...CONTROL_LANE_TASK_NAMES];
|
|
6537
6575
|
if (extra == null || extra === "") return names;
|
|
@@ -6594,6 +6632,7 @@ function ensureTasksRuntime(context, seed = {}) {
|
|
|
6594
6632
|
if (rt.pollMs === void 0) rt.pollMs = seed.pollMs ?? 1e3;
|
|
6595
6633
|
if (rt.claimJitterMs === void 0) rt.claimJitterMs = seed.claimJitterMs ?? 0;
|
|
6596
6634
|
if (rt.scanLimit === void 0) rt.scanLimit = seed.scanLimit ?? 100;
|
|
6635
|
+
if (rt.paused === void 0) rt.paused = seed.paused === true;
|
|
6597
6636
|
return rt;
|
|
6598
6637
|
}
|
|
6599
6638
|
async function applyRuntimeParam(context, key, value) {
|
|
@@ -6617,12 +6656,15 @@ async function applyRuntimeParam(context, key, value) {
|
|
|
6617
6656
|
const reg = context.servicesRegistry;
|
|
6618
6657
|
if (reg?.rowId && reg?.registryTable) {
|
|
6619
6658
|
try {
|
|
6620
|
-
const
|
|
6659
|
+
const specs = Array.isArray(context.tasksRuntimeParamSpecs) ? context.tasksRuntimeParamSpecs : DEFAULT_RUNTIME_PARAM_SPECS;
|
|
6660
|
+
const runtimeSnapshot = runtimeValuesForSpecs(context, specs);
|
|
6621
6661
|
for (const lk of LOOP_RUNTIME_KEYS) {
|
|
6622
|
-
if (runtime[lk] !== void 0
|
|
6662
|
+
if (runtime[lk] !== void 0 && runtimeSnapshot[lk] === void 0) {
|
|
6663
|
+
runtimeSnapshot[lk] = runtime[lk];
|
|
6664
|
+
}
|
|
6623
6665
|
}
|
|
6624
6666
|
await updateServicesRegistryMetadata(context, reg, {
|
|
6625
|
-
runtime:
|
|
6667
|
+
runtime: runtimeSnapshot,
|
|
6626
6668
|
runtimeUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6627
6669
|
});
|
|
6628
6670
|
applied.push("servicesRegistry");
|
|
@@ -6658,6 +6700,140 @@ function readLoopRuntime(context) {
|
|
|
6658
6700
|
};
|
|
6659
6701
|
}
|
|
6660
6702
|
|
|
6703
|
+
// src/tasks/coreTasks/TaskPauseRunner.js
|
|
6704
|
+
async function applyRunnerPaused(context, paused) {
|
|
6705
|
+
const runtime = ensureTasksRuntime(context);
|
|
6706
|
+
const was = runtime.paused === true;
|
|
6707
|
+
runtime.paused = paused === true;
|
|
6708
|
+
const registry = context.servicesRegistry;
|
|
6709
|
+
if (registry && typeof registry === "object") {
|
|
6710
|
+
await updateServicesRegistryMetadata(context, registry, {
|
|
6711
|
+
paused: runtime.paused,
|
|
6712
|
+
pausedAt: runtime.paused ? (/* @__PURE__ */ new Date()).toISOString() : null
|
|
6713
|
+
});
|
|
6714
|
+
}
|
|
6715
|
+
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).";
|
|
6716
|
+
context.logger?.warn?.(
|
|
6717
|
+
runtime.paused ? `[TaskPauseRunner] ${message}` : `[TaskUnpauseRunner] ${message}`
|
|
6718
|
+
);
|
|
6719
|
+
return {
|
|
6720
|
+
success: true,
|
|
6721
|
+
results: {
|
|
6722
|
+
paused: runtime.paused,
|
|
6723
|
+
message
|
|
6724
|
+
}
|
|
6725
|
+
};
|
|
6726
|
+
}
|
|
6727
|
+
var TaskPauseRunner = class extends AbstractTask {
|
|
6728
|
+
static taskName = "pauseRunner";
|
|
6729
|
+
static description = "Pause a runner: finish in-flight tasks, claim no new worker tasks until unpaused";
|
|
6730
|
+
static aliases = ["pause"];
|
|
6731
|
+
static defaultWaitForResult = true;
|
|
6732
|
+
/**
|
|
6733
|
+
* @param {object} context
|
|
6734
|
+
* @param {Record<string, unknown>} [overrides]
|
|
6735
|
+
* @returns {Promise<object>}
|
|
6736
|
+
*/
|
|
6737
|
+
static async resolveParams(context, overrides = {}) {
|
|
6738
|
+
const main = await super.resolveParams(context, overrides);
|
|
6739
|
+
if (!main.serviceName) {
|
|
6740
|
+
throw new ParamError(
|
|
6741
|
+
"pause/pauseRunner requires --serviceName (registry instance name; optional --serviceGroup, --instanceNumber, --serverName to narrow targeting)"
|
|
6742
|
+
);
|
|
6743
|
+
}
|
|
6744
|
+
return main;
|
|
6745
|
+
}
|
|
6746
|
+
async run() {
|
|
6747
|
+
return applyRunnerPaused(this.context, true);
|
|
6748
|
+
}
|
|
6749
|
+
};
|
|
6750
|
+
var TaskUnpauseRunner = class extends AbstractTask {
|
|
6751
|
+
static taskName = "unpauseRunner";
|
|
6752
|
+
static description = "Unpause a runner: resume claiming worker tasks";
|
|
6753
|
+
static aliases = ["unpause"];
|
|
6754
|
+
static defaultWaitForResult = true;
|
|
6755
|
+
/**
|
|
6756
|
+
* @param {object} context
|
|
6757
|
+
* @param {Record<string, unknown>} [overrides]
|
|
6758
|
+
* @returns {Promise<object>}
|
|
6759
|
+
*/
|
|
6760
|
+
static async resolveParams(context, overrides = {}) {
|
|
6761
|
+
const main = await super.resolveParams(context, overrides);
|
|
6762
|
+
if (!main.serviceName) {
|
|
6763
|
+
throw new ParamError(
|
|
6764
|
+
"unpause/unpauseRunner requires --serviceName (registry instance name; optional --serviceGroup, --instanceNumber, --serverName to narrow targeting)"
|
|
6765
|
+
);
|
|
6766
|
+
}
|
|
6767
|
+
return main;
|
|
6768
|
+
}
|
|
6769
|
+
async run() {
|
|
6770
|
+
return applyRunnerPaused(this.context, false);
|
|
6771
|
+
}
|
|
6772
|
+
};
|
|
6773
|
+
|
|
6774
|
+
// src/tasks/coreTasks/TaskGetLogs.js
|
|
6775
|
+
var TaskGetLogs = class extends AbstractTask {
|
|
6776
|
+
/**
|
|
6777
|
+
* @param {object} context
|
|
6778
|
+
* @param {Record<string, unknown>} [overrides]
|
|
6779
|
+
* @returns {Promise<{ source: string, resource: string, tail: number, afterTs?: string }>}
|
|
6780
|
+
*/
|
|
6781
|
+
static async resolveCustomParams(context, overrides = {}) {
|
|
6782
|
+
const merged = AbstractTask._mergeTypedParams(context, "task-get-logs", {
|
|
6783
|
+
source: "string",
|
|
6784
|
+
resource: "string",
|
|
6785
|
+
tail: "number default 100",
|
|
6786
|
+
afterTs: "string"
|
|
6787
|
+
}, overrides);
|
|
6788
|
+
const source = typeof merged.source === "string" ? merged.source.trim() : "";
|
|
6789
|
+
const resource = typeof merged.resource === "string" ? merged.resource.trim() : "";
|
|
6790
|
+
if (!source) throw new ParamError('getLogs: param "source" is required');
|
|
6791
|
+
if (!resource) throw new ParamError('getLogs: param "resource" is required');
|
|
6792
|
+
let tail = Number(merged.tail);
|
|
6793
|
+
if (!Number.isFinite(tail) || tail < 1) tail = 100;
|
|
6794
|
+
tail = Math.min(1e4, Math.max(1, Math.floor(tail)));
|
|
6795
|
+
const out = { source, resource, tail };
|
|
6796
|
+
if (typeof merged.afterTs === "string" && merged.afterTs.trim()) {
|
|
6797
|
+
out.afterTs = merged.afterTs.trim();
|
|
6798
|
+
}
|
|
6799
|
+
return out;
|
|
6800
|
+
}
|
|
6801
|
+
/**
|
|
6802
|
+
* @param {unknown} _reportProgress Unused (single-shot read, no progress events).
|
|
6803
|
+
* @returns {Promise<{ success: boolean, results: unknown }>}
|
|
6804
|
+
*/
|
|
6805
|
+
async run(_reportProgress) {
|
|
6806
|
+
const p = this.task.params ?? {};
|
|
6807
|
+
const source = String(p.source ?? "").trim();
|
|
6808
|
+
const resource = String(p.resource ?? "").trim();
|
|
6809
|
+
const tail = Math.max(1, Math.min(1e4, Number(p.tail) > 0 ? Number(p.tail) : 100));
|
|
6810
|
+
const afterTs = p.afterTs != null && String(p.afterTs).trim() ? String(p.afterTs).trim() : null;
|
|
6811
|
+
if (!source || !resource) {
|
|
6812
|
+
return {
|
|
6813
|
+
success: false,
|
|
6814
|
+
results: { error: 'getLogs requires params "source" and "resource"' }
|
|
6815
|
+
};
|
|
6816
|
+
}
|
|
6817
|
+
try {
|
|
6818
|
+
const { records, latestTs } = await readTaskIpcLogsSnapshot(this.context, {
|
|
6819
|
+
source,
|
|
6820
|
+
resource,
|
|
6821
|
+
tail,
|
|
6822
|
+
afterTs
|
|
6823
|
+
});
|
|
6824
|
+
return {
|
|
6825
|
+
success: true,
|
|
6826
|
+
results: { records, latestTs, source, resource }
|
|
6827
|
+
};
|
|
6828
|
+
} catch (e) {
|
|
6829
|
+
return {
|
|
6830
|
+
success: false,
|
|
6831
|
+
results: { error: e?.message ?? String(e) }
|
|
6832
|
+
};
|
|
6833
|
+
}
|
|
6834
|
+
}
|
|
6835
|
+
};
|
|
6836
|
+
|
|
6661
6837
|
// src/tasks/coreTasks/TaskSetRuntimeParam.js
|
|
6662
6838
|
var TaskSetRuntimeParam = class extends AbstractTask {
|
|
6663
6839
|
static defaultWaitForResult = true;
|
|
@@ -6813,7 +6989,7 @@ var TasksRegistry = class _TasksRegistry {
|
|
|
6813
6989
|
* @returns {TasksRegistry}
|
|
6814
6990
|
*/
|
|
6815
6991
|
static withCoreTasks() {
|
|
6816
|
-
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);
|
|
6992
|
+
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);
|
|
6817
6993
|
}
|
|
6818
6994
|
/**
|
|
6819
6995
|
* Register a single task class under a name. Overwrites any previous entry.
|
|
@@ -7171,17 +7347,25 @@ async function runTasksLoop(context, options) {
|
|
|
7171
7347
|
if (hbGroup) {
|
|
7172
7348
|
const hbIntervalMs = options.runnerHeartbeatIntervalMs ?? 1e4;
|
|
7173
7349
|
const staleMs = options.runnerHeartbeatStaleMs ?? 45e3;
|
|
7174
|
-
const
|
|
7350
|
+
const runtimeParamSpecs = mergeRuntimeParamSpecs(options.runnerRuntimeParams);
|
|
7351
|
+
context.tasksRuntimeParamSpecs = runtimeParamSpecs;
|
|
7175
7352
|
const defaultMeta = {
|
|
7176
7353
|
component: "tasks-runner",
|
|
7177
7354
|
allowedTasks: allowedTasks?.length ? allowedTasks.join(",") : "all",
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
7182
|
-
|
|
7183
|
-
|
|
7355
|
+
paused: false,
|
|
7356
|
+
runtimeParams: runtimeParamSpecs,
|
|
7357
|
+
runtime: runtimeValuesForSpecs(context, runtimeParamSpecs)
|
|
7358
|
+
};
|
|
7359
|
+
const metadata = {
|
|
7360
|
+
...defaultMeta,
|
|
7361
|
+
...options.runnerMetadata && typeof options.runnerMetadata === "object" ? options.runnerMetadata : {}
|
|
7184
7362
|
};
|
|
7363
|
+
if (!Array.isArray(metadata.runtimeParams) || metadata.runtimeParams.length === 0) {
|
|
7364
|
+
metadata.runtimeParams = defaultMeta.runtimeParams;
|
|
7365
|
+
}
|
|
7366
|
+
if (!metadata.runtime || typeof metadata.runtime !== "object") {
|
|
7367
|
+
metadata.runtime = defaultMeta.runtime;
|
|
7368
|
+
}
|
|
7185
7369
|
registryReg = await registerInServicesRegistry(context, {
|
|
7186
7370
|
queueName,
|
|
7187
7371
|
target,
|
|
@@ -7191,7 +7375,7 @@ async function runTasksLoop(context, options) {
|
|
|
7191
7375
|
staleMs,
|
|
7192
7376
|
groupMaxInstances: options.runnerGroupMaxInstances,
|
|
7193
7377
|
enforceMaxInstances: options.runnerEnforceMaxInstances ?? true,
|
|
7194
|
-
metadata
|
|
7378
|
+
metadata
|
|
7195
7379
|
});
|
|
7196
7380
|
context.servicesRegistry = registryReg;
|
|
7197
7381
|
runnerIdentity = {
|
|
@@ -7241,28 +7425,38 @@ async function runTasksLoop(context, options) {
|
|
|
7241
7425
|
if (claimJitterMs > 0) {
|
|
7242
7426
|
await sleepMs(Math.floor(Math.random() * (claimJitterMs + 1)));
|
|
7243
7427
|
}
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
if (
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7428
|
+
const paused = context.tasksRuntime?.paused === true;
|
|
7429
|
+
if (!paused) {
|
|
7430
|
+
while (runningPromises.size < maxParallel) {
|
|
7431
|
+
const claimed = await claimNextRunnableTask(
|
|
7432
|
+
context,
|
|
7433
|
+
tasksTable,
|
|
7434
|
+
target,
|
|
7435
|
+
registry,
|
|
7436
|
+
scanLimit,
|
|
7437
|
+
allowedTasks,
|
|
7438
|
+
runnerIdentity
|
|
7439
|
+
);
|
|
7440
|
+
if (!claimed) break;
|
|
7441
|
+
const p = executeClaimedTask(
|
|
7442
|
+
context,
|
|
7443
|
+
tasksTable,
|
|
7444
|
+
historyTable,
|
|
7445
|
+
claimed,
|
|
7446
|
+
registry,
|
|
7447
|
+
runningTaskInstances
|
|
7448
|
+
).then(async (outcome) => {
|
|
7449
|
+
if (outcome.stopRunnerRequested && !stopRequested) {
|
|
7450
|
+
stopRequested = true;
|
|
7451
|
+
stopAllowanceMs = outcome.stopAllowanceMs || stopAllowanceMs;
|
|
7452
|
+
context.tasksRunnerStop = true;
|
|
7453
|
+
await signalRunningTasksStop(context, runningTaskInstances, stopAllowanceMs);
|
|
7454
|
+
}
|
|
7455
|
+
}).finally(() => {
|
|
7456
|
+
runningPromises.delete(p);
|
|
7457
|
+
});
|
|
7458
|
+
runningPromises.add(p);
|
|
7459
|
+
}
|
|
7266
7460
|
}
|
|
7267
7461
|
const wakePromises = [...runningPromises];
|
|
7268
7462
|
if (runningControlPromise) {
|