@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.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);
|
|
382
|
+
forceUpdate({});
|
|
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)));
|
|
335
394
|
forceUpdate({});
|
|
336
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 ? BAR_THUMB : BAR_TRACK);
|
|
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);
|
|
@@ -1097,24 +1136,19 @@ function ScrollableText({
|
|
|
1097
1136
|
...rowNodes
|
|
1098
1137
|
);
|
|
1099
1138
|
}
|
|
1100
|
-
var import_react5, import_ink5, h5,
|
|
1139
|
+
var import_react5, import_ink5, h5, SCROLL_KEYS;
|
|
1101
1140
|
var init_scrollable_text = __esm({
|
|
1102
1141
|
"src/screen/scrollable-text.js"() {
|
|
1103
1142
|
import_react5 = require("react");
|
|
1104
1143
|
import_ink5 = require("ink");
|
|
1105
1144
|
init_components();
|
|
1145
|
+
init_scrollbar();
|
|
1146
|
+
init_scrollbar();
|
|
1106
1147
|
h5 = import_react5.createElement;
|
|
1107
|
-
BAR_THUMB = "#";
|
|
1108
|
-
BAR_TRACK = "|";
|
|
1109
1148
|
SCROLL_KEYS = [
|
|
1110
1149
|
{ key: "upArrow", caption: "scroll", action: "scrollUp", order: 0 },
|
|
1111
1150
|
{ key: "downArrow", caption: "scroll", action: "scrollDown", order: 0 },
|
|
1112
|
-
|
|
1113
|
-
{ key: "downArrow", meta: true, caption: "page", action: "pageDown", order: 0 },
|
|
1114
|
-
{ key: "upArrow", ctrl: true, caption: "page", action: "pageUp", order: 0 },
|
|
1115
|
-
{ key: "downArrow", ctrl: true, caption: "page", action: "pageDown", order: 0 },
|
|
1116
|
-
{ key: "pageUp", caption: "page", action: "pageUp", order: 0 },
|
|
1117
|
-
{ key: "pageDown", caption: "page", action: "pageDown", order: 0 }
|
|
1151
|
+
...PAGE_SCROLL_KEY_BINDINGS
|
|
1118
1152
|
];
|
|
1119
1153
|
}
|
|
1120
1154
|
});
|
|
@@ -1250,6 +1284,8 @@ var init_footer_builder = __esm({
|
|
|
1250
1284
|
// src/screen/index.js
|
|
1251
1285
|
var screen_exports = {};
|
|
1252
1286
|
__export(screen_exports, {
|
|
1287
|
+
BAR_THUMB: () => BAR_THUMB,
|
|
1288
|
+
BAR_TRACK: () => BAR_TRACK,
|
|
1253
1289
|
Box: () => import_ink6.Box,
|
|
1254
1290
|
Divider: () => Divider,
|
|
1255
1291
|
FooterPresets: () => FooterPresets,
|
|
@@ -1259,6 +1295,7 @@ __export(screen_exports, {
|
|
|
1259
1295
|
ListItem: () => ListItem,
|
|
1260
1296
|
MultiColumnListComponent: () => MultiColumnListComponent,
|
|
1261
1297
|
MultiColumnListWithPreviewComponent: () => MultiColumnListWithPreviewComponent,
|
|
1298
|
+
PAGE_SCROLL_KEY_BINDINGS: () => PAGE_SCROLL_KEY_BINDINGS,
|
|
1262
1299
|
React: () => import_react6.default,
|
|
1263
1300
|
ScreenBody: () => ScreenBody,
|
|
1264
1301
|
ScreenContainer: () => ScreenContainer,
|
|
@@ -1314,6 +1351,7 @@ var init_screen = __esm({
|
|
|
1314
1351
|
init_components();
|
|
1315
1352
|
init_ui_elements();
|
|
1316
1353
|
init_scrollable_text();
|
|
1354
|
+
init_scrollbar();
|
|
1317
1355
|
init_key_bindings();
|
|
1318
1356
|
init_utils();
|
|
1319
1357
|
init_footer_builder();
|
|
@@ -4166,12 +4204,13 @@ function tasksSchemaSpec(queueName = "tasks") {
|
|
|
4166
4204
|
}
|
|
4167
4205
|
};
|
|
4168
4206
|
}
|
|
4169
|
-
function taskHistoryInsertFromQueueRow(row, overrides) {
|
|
4207
|
+
function taskHistoryInsertFromQueueRow(row, overrides = {}) {
|
|
4170
4208
|
const { id, ...snapshot } = row;
|
|
4171
|
-
void id;
|
|
4209
|
+
const opid = overrides.opid !== void 0 ? overrides.opid : snapshot.opid != null && String(snapshot.opid).trim() !== "" ? snapshot.opid : id;
|
|
4172
4210
|
return {
|
|
4173
4211
|
...snapshot,
|
|
4174
|
-
...overrides
|
|
4212
|
+
...overrides,
|
|
4213
|
+
opid
|
|
4175
4214
|
};
|
|
4176
4215
|
}
|
|
4177
4216
|
async function ensureTaskTables(context, options = {}) {
|
|
@@ -6471,69 +6510,6 @@ var TaskStopRunner = class extends AbstractTask {
|
|
|
6471
6510
|
}
|
|
6472
6511
|
};
|
|
6473
6512
|
|
|
6474
|
-
// src/tasks/coreTasks/TaskGetLogs.js
|
|
6475
|
-
var TaskGetLogs = class extends AbstractTask {
|
|
6476
|
-
/**
|
|
6477
|
-
* @param {object} context
|
|
6478
|
-
* @param {Record<string, unknown>} [overrides]
|
|
6479
|
-
* @returns {Promise<{ source: string, resource: string, tail: number, afterTs?: string }>}
|
|
6480
|
-
*/
|
|
6481
|
-
static async resolveCustomParams(context, overrides = {}) {
|
|
6482
|
-
const merged = AbstractTask._mergeTypedParams(context, "task-get-logs", {
|
|
6483
|
-
source: "string",
|
|
6484
|
-
resource: "string",
|
|
6485
|
-
tail: "number default 100",
|
|
6486
|
-
afterTs: "string"
|
|
6487
|
-
}, overrides);
|
|
6488
|
-
const source = typeof merged.source === "string" ? merged.source.trim() : "";
|
|
6489
|
-
const resource = typeof merged.resource === "string" ? merged.resource.trim() : "";
|
|
6490
|
-
if (!source) throw new ParamError('getLogs: param "source" is required');
|
|
6491
|
-
if (!resource) throw new ParamError('getLogs: param "resource" is required');
|
|
6492
|
-
let tail = Number(merged.tail);
|
|
6493
|
-
if (!Number.isFinite(tail) || tail < 1) tail = 100;
|
|
6494
|
-
tail = Math.min(1e4, Math.max(1, Math.floor(tail)));
|
|
6495
|
-
const out = { source, resource, tail };
|
|
6496
|
-
if (typeof merged.afterTs === "string" && merged.afterTs.trim()) {
|
|
6497
|
-
out.afterTs = merged.afterTs.trim();
|
|
6498
|
-
}
|
|
6499
|
-
return out;
|
|
6500
|
-
}
|
|
6501
|
-
/**
|
|
6502
|
-
* @param {unknown} _reportProgress Unused (single-shot read, no progress events).
|
|
6503
|
-
* @returns {Promise<{ success: boolean, results: unknown }>}
|
|
6504
|
-
*/
|
|
6505
|
-
async run(_reportProgress) {
|
|
6506
|
-
const p = this.task.params ?? {};
|
|
6507
|
-
const source = String(p.source ?? "").trim();
|
|
6508
|
-
const resource = String(p.resource ?? "").trim();
|
|
6509
|
-
const tail = Math.max(1, Math.min(1e4, Number(p.tail) > 0 ? Number(p.tail) : 100));
|
|
6510
|
-
const afterTs = p.afterTs != null && String(p.afterTs).trim() ? String(p.afterTs).trim() : null;
|
|
6511
|
-
if (!source || !resource) {
|
|
6512
|
-
return {
|
|
6513
|
-
success: false,
|
|
6514
|
-
results: { error: 'getLogs requires params "source" and "resource"' }
|
|
6515
|
-
};
|
|
6516
|
-
}
|
|
6517
|
-
try {
|
|
6518
|
-
const { records, latestTs } = await readTaskIpcLogsSnapshot(this.context, {
|
|
6519
|
-
source,
|
|
6520
|
-
resource,
|
|
6521
|
-
tail,
|
|
6522
|
-
afterTs
|
|
6523
|
-
});
|
|
6524
|
-
return {
|
|
6525
|
-
success: true,
|
|
6526
|
-
results: { records, latestTs, source, resource }
|
|
6527
|
-
};
|
|
6528
|
-
} catch (e) {
|
|
6529
|
-
return {
|
|
6530
|
-
success: false,
|
|
6531
|
-
results: { error: e?.message ?? String(e) }
|
|
6532
|
-
};
|
|
6533
|
-
}
|
|
6534
|
-
}
|
|
6535
|
-
};
|
|
6536
|
-
|
|
6537
6513
|
// src/tasks/runtimeParams.js
|
|
6538
6514
|
var LOOP_RUNTIME_KEYS = ["maxParallel", "pollMs", "claimJitterMs", "scanLimit"];
|
|
6539
6515
|
var LOGGER_RUNTIME_KEYS = [
|
|
@@ -6547,7 +6523,69 @@ var LOGGER_RUNTIME_KEYS = [
|
|
|
6547
6523
|
"progressWithTimes",
|
|
6548
6524
|
"progressThrottleMs"
|
|
6549
6525
|
];
|
|
6550
|
-
var
|
|
6526
|
+
var DEFAULT_RUNTIME_PARAM_SPECS = [
|
|
6527
|
+
{
|
|
6528
|
+
key: "maxParallel",
|
|
6529
|
+
type: "number",
|
|
6530
|
+
label: "Max parallel",
|
|
6531
|
+
description: "Worker-lane concurrency (how many tasks claim at once)"
|
|
6532
|
+
},
|
|
6533
|
+
{
|
|
6534
|
+
key: "pollMs",
|
|
6535
|
+
type: "number",
|
|
6536
|
+
label: "Poll ms",
|
|
6537
|
+
description: "Idle poll interval between claim attempts"
|
|
6538
|
+
},
|
|
6539
|
+
{
|
|
6540
|
+
key: "claimJitterMs",
|
|
6541
|
+
type: "number",
|
|
6542
|
+
label: "Claim jitter ms",
|
|
6543
|
+
description: "Random delay before worker claims (0 = off)"
|
|
6544
|
+
},
|
|
6545
|
+
{
|
|
6546
|
+
key: "scanLimit",
|
|
6547
|
+
type: "number",
|
|
6548
|
+
label: "Scan limit",
|
|
6549
|
+
description: "Max idle rows scanned per claim attempt"
|
|
6550
|
+
}
|
|
6551
|
+
];
|
|
6552
|
+
function mergeRuntimeParamSpecs(extra) {
|
|
6553
|
+
const byKey = new Map(DEFAULT_RUNTIME_PARAM_SPECS.map((s) => [s.key, { ...s }]));
|
|
6554
|
+
if (Array.isArray(extra)) {
|
|
6555
|
+
for (const raw of extra) {
|
|
6556
|
+
if (!raw || typeof raw !== "object") continue;
|
|
6557
|
+
const key = String(raw.key ?? "").trim();
|
|
6558
|
+
if (!key) continue;
|
|
6559
|
+
const prev = byKey.get(key) ?? {};
|
|
6560
|
+
const type = ["number", "boolean", "string"].includes(raw.type) ? raw.type : prev.type ?? "string";
|
|
6561
|
+
byKey.set(key, {
|
|
6562
|
+
key,
|
|
6563
|
+
type,
|
|
6564
|
+
label: String(raw.label ?? prev.label ?? key),
|
|
6565
|
+
description: raw.description != null ? String(raw.description) : prev.description != null ? String(prev.description) : void 0
|
|
6566
|
+
});
|
|
6567
|
+
}
|
|
6568
|
+
}
|
|
6569
|
+
return Array.from(byKey.values());
|
|
6570
|
+
}
|
|
6571
|
+
function runtimeValuesForSpecs(context, specs = DEFAULT_RUNTIME_PARAM_SPECS) {
|
|
6572
|
+
const rt = ensureTasksRuntime(context);
|
|
6573
|
+
const out = {};
|
|
6574
|
+
for (const s of specs) {
|
|
6575
|
+
if (rt[s.key] !== void 0) out[s.key] = rt[s.key];
|
|
6576
|
+
}
|
|
6577
|
+
return out;
|
|
6578
|
+
}
|
|
6579
|
+
var CONTROL_LANE_TASK_NAMES = [
|
|
6580
|
+
"stopRunner",
|
|
6581
|
+
"stop",
|
|
6582
|
+
"pauseRunner",
|
|
6583
|
+
"pause",
|
|
6584
|
+
"unpauseRunner",
|
|
6585
|
+
"unpause",
|
|
6586
|
+
"setRuntimeParam",
|
|
6587
|
+
"setRunnerParam"
|
|
6588
|
+
];
|
|
6551
6589
|
function controlLaneTaskNames(extra) {
|
|
6552
6590
|
const names = [...CONTROL_LANE_TASK_NAMES];
|
|
6553
6591
|
if (extra == null || extra === "") return names;
|
|
@@ -6610,6 +6648,7 @@ function ensureTasksRuntime(context, seed = {}) {
|
|
|
6610
6648
|
if (rt.pollMs === void 0) rt.pollMs = seed.pollMs ?? 1e3;
|
|
6611
6649
|
if (rt.claimJitterMs === void 0) rt.claimJitterMs = seed.claimJitterMs ?? 0;
|
|
6612
6650
|
if (rt.scanLimit === void 0) rt.scanLimit = seed.scanLimit ?? 100;
|
|
6651
|
+
if (rt.paused === void 0) rt.paused = seed.paused === true;
|
|
6613
6652
|
return rt;
|
|
6614
6653
|
}
|
|
6615
6654
|
async function applyRuntimeParam(context, key, value) {
|
|
@@ -6633,12 +6672,15 @@ async function applyRuntimeParam(context, key, value) {
|
|
|
6633
6672
|
const reg = context.servicesRegistry;
|
|
6634
6673
|
if (reg?.rowId && reg?.registryTable) {
|
|
6635
6674
|
try {
|
|
6636
|
-
const
|
|
6675
|
+
const specs = Array.isArray(context.tasksRuntimeParamSpecs) ? context.tasksRuntimeParamSpecs : DEFAULT_RUNTIME_PARAM_SPECS;
|
|
6676
|
+
const runtimeSnapshot = runtimeValuesForSpecs(context, specs);
|
|
6637
6677
|
for (const lk of LOOP_RUNTIME_KEYS) {
|
|
6638
|
-
if (runtime[lk] !== void 0
|
|
6678
|
+
if (runtime[lk] !== void 0 && runtimeSnapshot[lk] === void 0) {
|
|
6679
|
+
runtimeSnapshot[lk] = runtime[lk];
|
|
6680
|
+
}
|
|
6639
6681
|
}
|
|
6640
6682
|
await updateServicesRegistryMetadata(context, reg, {
|
|
6641
|
-
runtime:
|
|
6683
|
+
runtime: runtimeSnapshot,
|
|
6642
6684
|
runtimeUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6643
6685
|
});
|
|
6644
6686
|
applied.push("servicesRegistry");
|
|
@@ -6674,6 +6716,140 @@ function readLoopRuntime(context) {
|
|
|
6674
6716
|
};
|
|
6675
6717
|
}
|
|
6676
6718
|
|
|
6719
|
+
// src/tasks/coreTasks/TaskPauseRunner.js
|
|
6720
|
+
async function applyRunnerPaused(context, paused) {
|
|
6721
|
+
const runtime = ensureTasksRuntime(context);
|
|
6722
|
+
const was = runtime.paused === true;
|
|
6723
|
+
runtime.paused = paused === true;
|
|
6724
|
+
const registry = context.servicesRegistry;
|
|
6725
|
+
if (registry && typeof registry === "object") {
|
|
6726
|
+
await updateServicesRegistryMetadata(context, registry, {
|
|
6727
|
+
paused: runtime.paused,
|
|
6728
|
+
pausedAt: runtime.paused ? (/* @__PURE__ */ new Date()).toISOString() : null
|
|
6729
|
+
});
|
|
6730
|
+
}
|
|
6731
|
+
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).";
|
|
6732
|
+
context.logger?.warn?.(
|
|
6733
|
+
runtime.paused ? `[TaskPauseRunner] ${message}` : `[TaskUnpauseRunner] ${message}`
|
|
6734
|
+
);
|
|
6735
|
+
return {
|
|
6736
|
+
success: true,
|
|
6737
|
+
results: {
|
|
6738
|
+
paused: runtime.paused,
|
|
6739
|
+
message
|
|
6740
|
+
}
|
|
6741
|
+
};
|
|
6742
|
+
}
|
|
6743
|
+
var TaskPauseRunner = class extends AbstractTask {
|
|
6744
|
+
static taskName = "pauseRunner";
|
|
6745
|
+
static description = "Pause a runner: finish in-flight tasks, claim no new worker tasks until unpaused";
|
|
6746
|
+
static aliases = ["pause"];
|
|
6747
|
+
static defaultWaitForResult = true;
|
|
6748
|
+
/**
|
|
6749
|
+
* @param {object} context
|
|
6750
|
+
* @param {Record<string, unknown>} [overrides]
|
|
6751
|
+
* @returns {Promise<object>}
|
|
6752
|
+
*/
|
|
6753
|
+
static async resolveParams(context, overrides = {}) {
|
|
6754
|
+
const main = await super.resolveParams(context, overrides);
|
|
6755
|
+
if (!main.serviceName) {
|
|
6756
|
+
throw new ParamError(
|
|
6757
|
+
"pause/pauseRunner requires --serviceName (registry instance name; optional --serviceGroup, --instanceNumber, --serverName to narrow targeting)"
|
|
6758
|
+
);
|
|
6759
|
+
}
|
|
6760
|
+
return main;
|
|
6761
|
+
}
|
|
6762
|
+
async run() {
|
|
6763
|
+
return applyRunnerPaused(this.context, true);
|
|
6764
|
+
}
|
|
6765
|
+
};
|
|
6766
|
+
var TaskUnpauseRunner = class extends AbstractTask {
|
|
6767
|
+
static taskName = "unpauseRunner";
|
|
6768
|
+
static description = "Unpause a runner: resume claiming worker tasks";
|
|
6769
|
+
static aliases = ["unpause"];
|
|
6770
|
+
static defaultWaitForResult = true;
|
|
6771
|
+
/**
|
|
6772
|
+
* @param {object} context
|
|
6773
|
+
* @param {Record<string, unknown>} [overrides]
|
|
6774
|
+
* @returns {Promise<object>}
|
|
6775
|
+
*/
|
|
6776
|
+
static async resolveParams(context, overrides = {}) {
|
|
6777
|
+
const main = await super.resolveParams(context, overrides);
|
|
6778
|
+
if (!main.serviceName) {
|
|
6779
|
+
throw new ParamError(
|
|
6780
|
+
"unpause/unpauseRunner requires --serviceName (registry instance name; optional --serviceGroup, --instanceNumber, --serverName to narrow targeting)"
|
|
6781
|
+
);
|
|
6782
|
+
}
|
|
6783
|
+
return main;
|
|
6784
|
+
}
|
|
6785
|
+
async run() {
|
|
6786
|
+
return applyRunnerPaused(this.context, false);
|
|
6787
|
+
}
|
|
6788
|
+
};
|
|
6789
|
+
|
|
6790
|
+
// src/tasks/coreTasks/TaskGetLogs.js
|
|
6791
|
+
var TaskGetLogs = class extends AbstractTask {
|
|
6792
|
+
/**
|
|
6793
|
+
* @param {object} context
|
|
6794
|
+
* @param {Record<string, unknown>} [overrides]
|
|
6795
|
+
* @returns {Promise<{ source: string, resource: string, tail: number, afterTs?: string }>}
|
|
6796
|
+
*/
|
|
6797
|
+
static async resolveCustomParams(context, overrides = {}) {
|
|
6798
|
+
const merged = AbstractTask._mergeTypedParams(context, "task-get-logs", {
|
|
6799
|
+
source: "string",
|
|
6800
|
+
resource: "string",
|
|
6801
|
+
tail: "number default 100",
|
|
6802
|
+
afterTs: "string"
|
|
6803
|
+
}, overrides);
|
|
6804
|
+
const source = typeof merged.source === "string" ? merged.source.trim() : "";
|
|
6805
|
+
const resource = typeof merged.resource === "string" ? merged.resource.trim() : "";
|
|
6806
|
+
if (!source) throw new ParamError('getLogs: param "source" is required');
|
|
6807
|
+
if (!resource) throw new ParamError('getLogs: param "resource" is required');
|
|
6808
|
+
let tail = Number(merged.tail);
|
|
6809
|
+
if (!Number.isFinite(tail) || tail < 1) tail = 100;
|
|
6810
|
+
tail = Math.min(1e4, Math.max(1, Math.floor(tail)));
|
|
6811
|
+
const out = { source, resource, tail };
|
|
6812
|
+
if (typeof merged.afterTs === "string" && merged.afterTs.trim()) {
|
|
6813
|
+
out.afterTs = merged.afterTs.trim();
|
|
6814
|
+
}
|
|
6815
|
+
return out;
|
|
6816
|
+
}
|
|
6817
|
+
/**
|
|
6818
|
+
* @param {unknown} _reportProgress Unused (single-shot read, no progress events).
|
|
6819
|
+
* @returns {Promise<{ success: boolean, results: unknown }>}
|
|
6820
|
+
*/
|
|
6821
|
+
async run(_reportProgress) {
|
|
6822
|
+
const p = this.task.params ?? {};
|
|
6823
|
+
const source = String(p.source ?? "").trim();
|
|
6824
|
+
const resource = String(p.resource ?? "").trim();
|
|
6825
|
+
const tail = Math.max(1, Math.min(1e4, Number(p.tail) > 0 ? Number(p.tail) : 100));
|
|
6826
|
+
const afterTs = p.afterTs != null && String(p.afterTs).trim() ? String(p.afterTs).trim() : null;
|
|
6827
|
+
if (!source || !resource) {
|
|
6828
|
+
return {
|
|
6829
|
+
success: false,
|
|
6830
|
+
results: { error: 'getLogs requires params "source" and "resource"' }
|
|
6831
|
+
};
|
|
6832
|
+
}
|
|
6833
|
+
try {
|
|
6834
|
+
const { records, latestTs } = await readTaskIpcLogsSnapshot(this.context, {
|
|
6835
|
+
source,
|
|
6836
|
+
resource,
|
|
6837
|
+
tail,
|
|
6838
|
+
afterTs
|
|
6839
|
+
});
|
|
6840
|
+
return {
|
|
6841
|
+
success: true,
|
|
6842
|
+
results: { records, latestTs, source, resource }
|
|
6843
|
+
};
|
|
6844
|
+
} catch (e) {
|
|
6845
|
+
return {
|
|
6846
|
+
success: false,
|
|
6847
|
+
results: { error: e?.message ?? String(e) }
|
|
6848
|
+
};
|
|
6849
|
+
}
|
|
6850
|
+
}
|
|
6851
|
+
};
|
|
6852
|
+
|
|
6677
6853
|
// src/tasks/coreTasks/TaskSetRuntimeParam.js
|
|
6678
6854
|
var TaskSetRuntimeParam = class extends AbstractTask {
|
|
6679
6855
|
static defaultWaitForResult = true;
|
|
@@ -6829,7 +7005,7 @@ var TasksRegistry = class _TasksRegistry {
|
|
|
6829
7005
|
* @returns {TasksRegistry}
|
|
6830
7006
|
*/
|
|
6831
7007
|
static withCoreTasks() {
|
|
6832
|
-
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);
|
|
7008
|
+
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);
|
|
6833
7009
|
}
|
|
6834
7010
|
/**
|
|
6835
7011
|
* Register a single task class under a name. Overwrites any previous entry.
|
|
@@ -7187,17 +7363,25 @@ async function runTasksLoop(context, options) {
|
|
|
7187
7363
|
if (hbGroup) {
|
|
7188
7364
|
const hbIntervalMs = options.runnerHeartbeatIntervalMs ?? 1e4;
|
|
7189
7365
|
const staleMs = options.runnerHeartbeatStaleMs ?? 45e3;
|
|
7190
|
-
const
|
|
7366
|
+
const runtimeParamSpecs = mergeRuntimeParamSpecs(options.runnerRuntimeParams);
|
|
7367
|
+
context.tasksRuntimeParamSpecs = runtimeParamSpecs;
|
|
7191
7368
|
const defaultMeta = {
|
|
7192
7369
|
component: "tasks-runner",
|
|
7193
7370
|
allowedTasks: allowedTasks?.length ? allowedTasks.join(",") : "all",
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7371
|
+
paused: false,
|
|
7372
|
+
runtimeParams: runtimeParamSpecs,
|
|
7373
|
+
runtime: runtimeValuesForSpecs(context, runtimeParamSpecs)
|
|
7374
|
+
};
|
|
7375
|
+
const metadata = {
|
|
7376
|
+
...defaultMeta,
|
|
7377
|
+
...options.runnerMetadata && typeof options.runnerMetadata === "object" ? options.runnerMetadata : {}
|
|
7200
7378
|
};
|
|
7379
|
+
if (!Array.isArray(metadata.runtimeParams) || metadata.runtimeParams.length === 0) {
|
|
7380
|
+
metadata.runtimeParams = defaultMeta.runtimeParams;
|
|
7381
|
+
}
|
|
7382
|
+
if (!metadata.runtime || typeof metadata.runtime !== "object") {
|
|
7383
|
+
metadata.runtime = defaultMeta.runtime;
|
|
7384
|
+
}
|
|
7201
7385
|
registryReg = await registerInServicesRegistry(context, {
|
|
7202
7386
|
queueName,
|
|
7203
7387
|
target,
|
|
@@ -7207,7 +7391,7 @@ async function runTasksLoop(context, options) {
|
|
|
7207
7391
|
staleMs,
|
|
7208
7392
|
groupMaxInstances: options.runnerGroupMaxInstances,
|
|
7209
7393
|
enforceMaxInstances: options.runnerEnforceMaxInstances ?? true,
|
|
7210
|
-
metadata
|
|
7394
|
+
metadata
|
|
7211
7395
|
});
|
|
7212
7396
|
context.servicesRegistry = registryReg;
|
|
7213
7397
|
runnerIdentity = {
|
|
@@ -7257,28 +7441,38 @@ async function runTasksLoop(context, options) {
|
|
|
7257
7441
|
if (claimJitterMs > 0) {
|
|
7258
7442
|
await sleepMs(Math.floor(Math.random() * (claimJitterMs + 1)));
|
|
7259
7443
|
}
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
if (
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
|
|
7444
|
+
const paused = context.tasksRuntime?.paused === true;
|
|
7445
|
+
if (!paused) {
|
|
7446
|
+
while (runningPromises.size < maxParallel) {
|
|
7447
|
+
const claimed = await claimNextRunnableTask(
|
|
7448
|
+
context,
|
|
7449
|
+
tasksTable,
|
|
7450
|
+
target,
|
|
7451
|
+
registry,
|
|
7452
|
+
scanLimit,
|
|
7453
|
+
allowedTasks,
|
|
7454
|
+
runnerIdentity
|
|
7455
|
+
);
|
|
7456
|
+
if (!claimed) break;
|
|
7457
|
+
const p = executeClaimedTask(
|
|
7458
|
+
context,
|
|
7459
|
+
tasksTable,
|
|
7460
|
+
historyTable,
|
|
7461
|
+
claimed,
|
|
7462
|
+
registry,
|
|
7463
|
+
runningTaskInstances
|
|
7464
|
+
).then(async (outcome) => {
|
|
7465
|
+
if (outcome.stopRunnerRequested && !stopRequested) {
|
|
7466
|
+
stopRequested = true;
|
|
7467
|
+
stopAllowanceMs = outcome.stopAllowanceMs || stopAllowanceMs;
|
|
7468
|
+
context.tasksRunnerStop = true;
|
|
7469
|
+
await signalRunningTasksStop(context, runningTaskInstances, stopAllowanceMs);
|
|
7470
|
+
}
|
|
7471
|
+
}).finally(() => {
|
|
7472
|
+
runningPromises.delete(p);
|
|
7473
|
+
});
|
|
7474
|
+
runningPromises.add(p);
|
|
7475
|
+
}
|
|
7282
7476
|
}
|
|
7283
7477
|
const wakePromises = [...runningPromises];
|
|
7284
7478
|
if (runningControlPromise) {
|