@nmakarov/cli-toolkit 0.21.0 → 0.25.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.
Files changed (68) hide show
  1. package/dist/args.cjs +1 -4
  2. package/dist/args.cjs.map +1 -1
  3. package/dist/args.js +1 -1
  4. package/dist/args.js.map +1 -1
  5. package/dist/cli-runner.cjs +1370 -623
  6. package/dist/cli-runner.cjs.map +1 -1
  7. package/dist/cli-runner.js +1415 -667
  8. package/dist/cli-runner.js.map +1 -1
  9. package/dist/db.cjs +173 -158
  10. package/dist/db.cjs.map +1 -1
  11. package/dist/db.js +172 -151
  12. package/dist/db.js.map +1 -1
  13. package/dist/errors.cjs +2 -2
  14. package/dist/errors.cjs.map +1 -1
  15. package/dist/errors.js +2 -1
  16. package/dist/errors.js.map +1 -1
  17. package/dist/filedatabase.cjs +19 -19
  18. package/dist/filedatabase.cjs.map +1 -1
  19. package/dist/filedatabase.js +19 -16
  20. package/dist/filedatabase.js.map +1 -1
  21. package/dist/http-client.cjs +9 -11
  22. package/dist/http-client.cjs.map +1 -1
  23. package/dist/http-client.js +10 -9
  24. package/dist/http-client.js.map +1 -1
  25. package/dist/http-client2.cjs +34 -37
  26. package/dist/http-client2.cjs.map +1 -1
  27. package/dist/http-client2.js +34 -34
  28. package/dist/http-client2.js.map +1 -1
  29. package/dist/index.cjs +1831 -713
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.js +1837 -713
  32. package/dist/index.js.map +1 -1
  33. package/dist/init.cjs +93 -68
  34. package/dist/init.cjs.map +1 -1
  35. package/dist/init.js +108 -82
  36. package/dist/init.js.map +1 -1
  37. package/dist/logger.cjs +5 -5
  38. package/dist/logger.cjs.map +1 -1
  39. package/dist/logger.js +5 -4
  40. package/dist/logger.js.map +1 -1
  41. package/dist/mock-server.cjs +21 -33
  42. package/dist/mock-server.cjs.map +1 -1
  43. package/dist/mock-server.js +21 -28
  44. package/dist/mock-server.js.map +1 -1
  45. package/dist/params.cjs +18 -9
  46. package/dist/params.cjs.map +1 -1
  47. package/dist/params.js +18 -6
  48. package/dist/params.js.map +1 -1
  49. package/dist/s3.cjs +286 -0
  50. package/dist/s3.cjs.map +1 -0
  51. package/dist/s3.js +273 -0
  52. package/dist/s3.js.map +1 -0
  53. package/dist/screen.cjs +34 -39
  54. package/dist/screen.cjs.map +1 -1
  55. package/dist/screen.js +48 -46
  56. package/dist/screen.js.map +1 -1
  57. package/dist/tasks.cjs +1354 -501
  58. package/dist/tasks.cjs.map +1 -1
  59. package/dist/tasks.js +1369 -527
  60. package/dist/tasks.js.map +1 -1
  61. package/dist/utils.cjs +7 -8
  62. package/dist/utils.cjs.map +1 -1
  63. package/dist/utils.js +6 -6
  64. package/dist/utils.js.map +1 -1
  65. package/package.json +32 -47
  66. package/scripts/ssm/{parse-cli.ts → parse-cli.js} +4 -4
  67. package/scripts/ssm/{ssm-admin.ts → ssm-admin.js} +12 -12
  68. package/scripts/ssm/{ssm-pull.ts → ssm-pull.js} +10 -13
package/dist/init.cjs CHANGED
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -30,7 +29,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
29
  ));
31
30
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
31
 
33
- // src/screen/components.ts
32
+ // src/screen/components.js
34
33
  function getScreenWidth(maxWidth = null) {
35
34
  const terminalWidth = process.stdout.columns || 80;
36
35
  const availableWidth = Math.max(20, terminalWidth - 4);
@@ -104,14 +103,13 @@ function ScreenFooter({ lines, textStyle }) {
104
103
  }
105
104
  var import_react, import_ink;
106
105
  var init_components = __esm({
107
- "src/screen/components.ts"() {
108
- "use strict";
106
+ "src/screen/components.js"() {
109
107
  import_react = require("react");
110
108
  import_ink = require("ink");
111
109
  }
112
110
  });
113
111
 
114
- // src/screen/list-components.ts
112
+ // src/screen/list-components.js
115
113
  function MultiColumnListComponent({ items, ctx, selectedIndexRef }) {
116
114
  const [, forceUpdate] = (0, import_react2.useState)({});
117
115
  const termWidth = (process.stdout.columns || 80) - 8;
@@ -263,11 +261,13 @@ function MultiColumnListWithPreviewComponent({
263
261
  ...previewRows
264
262
  );
265
263
  }
266
- function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sortable = false, maxHeight, sortHighlightStyle, selectionMarker = " " }) {
264
+ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sortable = false, maxHeight, sortHighlightStyle, selectionMarker = " ", onSelectionChange }) {
267
265
  const [, forceUpdate] = (0, import_react2.useState)({});
268
266
  const [sortOrder, setSortOrder] = (0, import_react2.useState)("none");
269
267
  const [scrollOffset, setScrollOffset] = (0, import_react2.useState)(0);
270
268
  const scrollStateRef = (0, import_react2.useRef)({ scrollOffset: 0, maxHeight: 0, totalItems: 0 });
269
+ const itemsRef = (0, import_react2.useRef)(items);
270
+ itemsRef.current = items;
271
271
  const defaultGetTitle = (item) => {
272
272
  return getTitle ? getTitle(item) : typeof item.value === "string" ? item.value : item.value?.title || item.name;
273
273
  };
@@ -281,18 +281,24 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
281
281
  return titleA > titleB ? -1 : titleA < titleB ? 1 : 0;
282
282
  }
283
283
  }) : items;
284
+ const displayItemsRef = (0, import_react2.useRef)(displayItems);
285
+ displayItemsRef.current = displayItems;
284
286
  const effectiveMaxHeight = maxHeight || displayItems.length;
285
- const canScroll = displayItems.length > effectiveMaxHeight;
287
+ const _canScroll = displayItems.length > effectiveMaxHeight;
286
288
  const maxScrollOffset = Math.max(0, displayItems.length - effectiveMaxHeight);
287
289
  const clampedScrollOffset = Math.min(Math.max(0, scrollOffset), maxScrollOffset);
288
290
  const visibleItems = displayItems.slice(clampedScrollOffset, clampedScrollOffset + effectiveMaxHeight);
289
291
  const canScrollUp = clampedScrollOffset > 0;
290
292
  const canScrollDown = clampedScrollOffset < maxScrollOffset;
291
293
  scrollStateRef.current = { scrollOffset, maxHeight: effectiveMaxHeight, totalItems: displayItems.length };
294
+ const onSelectionChangeRef = (0, import_react2.useRef)(onSelectionChange);
295
+ onSelectionChangeRef.current = onSelectionChange;
292
296
  (0, import_react2.useEffect)(() => {
293
297
  ctx.setAction("moveUp", () => {
294
298
  const newIndex = Math.max(0, selectedIndexRef.current - 1);
295
299
  selectedIndexRef.current = newIndex;
300
+ const list = displayItemsRef.current;
301
+ onSelectionChangeRef.current?.(newIndex, list[newIndex]);
296
302
  const { scrollOffset: currentScrollOffset, maxHeight: currentMaxHeight, totalItems } = scrollStateRef.current;
297
303
  const currentMaxScrollOffset = Math.max(0, totalItems - currentMaxHeight);
298
304
  const currentClampedScrollOffset = Math.min(Math.max(0, currentScrollOffset), currentMaxScrollOffset);
@@ -302,18 +308,11 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
302
308
  forceUpdate({});
303
309
  });
304
310
  ctx.setAction("moveDown", () => {
305
- const currentItems = sortable && sortOrder !== "none" ? [...items].sort((a, b) => {
306
- const titleA = titleGetter(a).toLowerCase();
307
- const titleB = titleGetter(b).toLowerCase();
308
- if (sortOrder === "asc") {
309
- return titleA < titleB ? -1 : titleA > titleB ? 1 : 0;
310
- } else {
311
- return titleA > titleB ? -1 : titleA < titleB ? 1 : 0;
312
- }
313
- }) : items;
314
- const maxIndex = currentItems.length - 1;
311
+ const currentItems = displayItemsRef.current;
312
+ const maxIndex = Math.max(0, currentItems.length - 1);
315
313
  const newIndex = Math.min(maxIndex, selectedIndexRef.current + 1);
316
314
  selectedIndexRef.current = newIndex;
315
+ onSelectionChangeRef.current?.(newIndex, currentItems[newIndex]);
317
316
  const { scrollOffset: currentScrollOffset, maxHeight: currentMaxHeight, totalItems } = scrollStateRef.current;
318
317
  const currentMaxScrollOffset = Math.max(0, totalItems - currentMaxHeight);
319
318
  const currentClampedScrollOffset = Math.min(Math.max(0, currentScrollOffset), currentMaxScrollOffset);
@@ -323,8 +322,7 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
323
322
  forceUpdate({});
324
323
  });
325
324
  ctx.setAction("scrollUp", () => {
326
- const { scrollOffset: currentScrollOffset, maxHeight: currentMaxHeight, totalItems } = scrollStateRef.current;
327
- const currentMaxScrollOffset = Math.max(0, totalItems - currentMaxHeight);
325
+ const { scrollOffset: currentScrollOffset } = scrollStateRef.current;
328
326
  const newScrollOffset = Math.max(0, currentScrollOffset - 1);
329
327
  setScrollOffset(newScrollOffset);
330
328
  forceUpdate({});
@@ -339,9 +337,9 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
339
337
  if (sortable) {
340
338
  ctx.setAction("toggleSort", () => {
341
339
  const nextSort = sortOrder === "none" ? "asc" : sortOrder === "asc" ? "desc" : "none";
342
- const currentSelectedItem = displayItems[selectedIndexRef.current];
340
+ const currentSelectedItem = displayItemsRef.current[selectedIndexRef.current];
343
341
  setSortOrder(nextSort);
344
- const newSortedItems = nextSort !== "none" ? [...items].sort((a, b) => {
342
+ const newSortedItems = nextSort !== "none" ? [...itemsRef.current].sort((a, b) => {
345
343
  const titleA = titleGetter(a).toLowerCase();
346
344
  const titleB = titleGetter(b).toLowerCase();
347
345
  if (nextSort === "asc") {
@@ -349,7 +347,7 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
349
347
  } else {
350
348
  return titleA > titleB ? -1 : titleA < titleB ? 1 : 0;
351
349
  }
352
- }) : items;
350
+ }) : itemsRef.current;
353
351
  const newIndex = newSortedItems.findIndex((item) => item === currentSelectedItem);
354
352
  if (newIndex !== -1) {
355
353
  selectedIndexRef.current = newIndex;
@@ -487,8 +485,7 @@ function ListComponent({ items, ctx, selectedIndexRef, renderItem, getTitle, sor
487
485
  }
488
486
  var import_react2, import_ink2, h2;
489
487
  var init_list_components = __esm({
490
- "src/screen/list-components.ts"() {
491
- "use strict";
488
+ "src/screen/list-components.js"() {
492
489
  import_react2 = __toESM(require("react"), 1);
493
490
  import_ink2 = require("ink");
494
491
  init_components();
@@ -496,7 +493,7 @@ var init_list_components = __esm({
496
493
  }
497
494
  });
498
495
 
499
- // src/screen/screens.ts
496
+ // src/screen/screens.js
500
497
  function groupKeyBindings(bindings) {
501
498
  const groups = {};
502
499
  const enabledBindings = bindings.filter((b) => b.enabled !== false);
@@ -572,7 +569,7 @@ async function showScreen(config2) {
572
569
  let renderResult = null;
573
570
  let initialized = false;
574
571
  const Screen = () => {
575
- const [updateCounter, setUpdateCounter] = (0, import_react3.useState)(0);
572
+ const [, setUpdateCounter] = (0, import_react3.useState)(0);
576
573
  if (!initialized) {
577
574
  const defaultBindings = [
578
575
  { key: "escape", caption: "go back", action: "back", protected: true, order: 1 },
@@ -691,7 +688,7 @@ async function showScreen(config2) {
691
688
  }
692
689
  }
693
690
  if (matchedBinding && actions[matchedBinding.action]) {
694
- const actionResult = actions[matchedBinding.action]({
691
+ actions[matchedBinding.action]({
695
692
  input,
696
693
  key,
697
694
  binding: matchedBinding
@@ -821,8 +818,7 @@ async function showMultiColumnListWithPreviewScreen(config2) {
821
818
  }
822
819
  var import_react3, import_ink3, showMenuScreen, showWordGridScreen;
823
820
  var init_screens = __esm({
824
- "src/screen/screens.ts"() {
825
- "use strict";
821
+ "src/screen/screens.js"() {
826
822
  import_react3 = require("react");
827
823
  import_ink3 = require("ink");
828
824
  init_components();
@@ -832,7 +828,7 @@ var init_screens = __esm({
832
828
  }
833
829
  });
834
830
 
835
- // src/screen/ui-elements.ts
831
+ // src/screen/ui-elements.js
836
832
  function ListItem({
837
833
  children,
838
834
  isSelected = false,
@@ -857,7 +853,7 @@ function TextBlock({
857
853
  color = "white",
858
854
  dimmed = false,
859
855
  bold = false,
860
- maxWidth
856
+ maxWidth: _maxWidth
861
857
  }) {
862
858
  return (0, import_react4.createElement)(
863
859
  import_ink4.Box,
@@ -897,7 +893,7 @@ function GridCell({
897
893
  }, children)
898
894
  );
899
895
  }
900
- function InputField({ prompt, value, onChange, onSubmit }) {
896
+ function InputField({ prompt, value, onChange: _onChange, onSubmit: _onSubmit }) {
901
897
  return (0, import_react4.createElement)(
902
898
  import_ink4.Box,
903
899
  { flexDirection: "column" },
@@ -911,14 +907,13 @@ function InputField({ prompt, value, onChange, onSubmit }) {
911
907
  }
912
908
  var import_react4, import_ink4;
913
909
  var init_ui_elements = __esm({
914
- "src/screen/ui-elements.ts"() {
915
- "use strict";
910
+ "src/screen/ui-elements.js"() {
916
911
  import_react4 = require("react");
917
912
  import_ink4 = require("ink");
918
913
  }
919
914
  });
920
915
 
921
- // src/screen/utils.ts
916
+ // src/screen/utils.js
922
917
  function buildBreadcrumb(parts) {
923
918
  if (parts.length === 0) return "";
924
919
  if (parts.length === 1) return parts[0];
@@ -932,12 +927,11 @@ function buildDetailBreadcrumb(path, suffix = "") {
932
927
  return suffix ? `${breadcrumb} ${suffix}` : breadcrumb;
933
928
  }
934
929
  var init_utils = __esm({
935
- "src/screen/utils.ts"() {
936
- "use strict";
930
+ "src/screen/utils.js"() {
937
931
  }
938
932
  });
939
933
 
940
- // src/screen/footer-builder.ts
934
+ // src/screen/footer-builder.js
941
935
  function buildFooter(config2 = {}) {
942
936
  const {
943
937
  navigation = null,
@@ -988,8 +982,7 @@ function organizeFooterMessages(messages) {
988
982
  }
989
983
  var FooterPresets;
990
984
  var init_footer_builder = __esm({
991
- "src/screen/footer-builder.ts"() {
992
- "use strict";
985
+ "src/screen/footer-builder.js"() {
993
986
  FooterPresets = {
994
987
  /**
995
988
  * Menu screen footer
@@ -1048,7 +1041,7 @@ var init_footer_builder = __esm({
1048
1041
  }
1049
1042
  });
1050
1043
 
1051
- // src/screen/index.ts
1044
+ // src/screen/index.js
1052
1045
  var screen_exports = {};
1053
1046
  __export(screen_exports, {
1054
1047
  Box: () => import_ink5.Box,
@@ -1074,6 +1067,7 @@ __export(screen_exports, {
1074
1067
  buildFooter: () => buildFooter,
1075
1068
  h: () => import_react5.createElement,
1076
1069
  load: () => load,
1070
+ memo: () => import_react5.memo,
1077
1071
  organizeFooterMessages: () => organizeFooterMessages,
1078
1072
  showListScreen: () => showListScreen,
1079
1073
  showMenuScreen: () => showMenuScreen,
@@ -1084,6 +1078,7 @@ __export(screen_exports, {
1084
1078
  useCallback: () => import_react5.useCallback,
1085
1079
  useEffect: () => import_react5.useEffect,
1086
1080
  useInput: () => import_ink5.useInput,
1081
+ useLayoutEffect: () => import_react5.useLayoutEffect,
1087
1082
  useMemo: () => import_react5.useMemo,
1088
1083
  useRef: () => import_react5.useRef,
1089
1084
  useState: () => import_react5.useState
@@ -1099,8 +1094,7 @@ async function load() {
1099
1094
  }
1100
1095
  var import_react5, import_ink5, loadPromise;
1101
1096
  var init_screen = __esm({
1102
- "src/screen/index.ts"() {
1103
- "use strict";
1097
+ "src/screen/index.js"() {
1104
1098
  import_react5 = __toESM(require("react"), 1);
1105
1099
  import_ink5 = require("ink");
1106
1100
  init_screens();
@@ -1117,7 +1111,7 @@ var init_screen = __esm({
1117
1111
  }
1118
1112
  });
1119
1113
 
1120
- // src/init.ts
1114
+ // src/init/index.js
1121
1115
  var init_exports = {};
1122
1116
  __export(init_exports, {
1123
1117
  init: () => init,
@@ -1125,7 +1119,7 @@ __export(init_exports, {
1125
1119
  });
1126
1120
  module.exports = __toCommonJS(init_exports);
1127
1121
 
1128
- // src/args/index.ts
1122
+ // src/args/index.js
1129
1123
  var import_fs = require("fs");
1130
1124
  var import_path = require("path");
1131
1125
  var import_dotenv = require("dotenv");
@@ -1608,10 +1602,10 @@ var Args = class _Args {
1608
1602
  }
1609
1603
  };
1610
1604
 
1611
- // src/params/index.ts
1605
+ // src/params/index.js
1612
1606
  var import_joi = __toESM(require("joi"), 1);
1613
1607
 
1614
- // src/errors.ts
1608
+ // src/errors.js
1615
1609
  var FrameworkError = class extends Error {
1616
1610
  constructor(message) {
1617
1611
  super(message);
@@ -1631,7 +1625,7 @@ var InitError = class extends FrameworkError {
1631
1625
  }
1632
1626
  };
1633
1627
 
1634
- // src/params/custom-types.ts
1628
+ // src/params/custom-types.js
1635
1629
  var joiEdateType = (value, helpers) => {
1636
1630
  if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/.test(value)) {
1637
1631
  const testDate = new Date(value);
@@ -1724,7 +1718,7 @@ function calculateTimeOffset(amount, unit, sign) {
1724
1718
  }
1725
1719
  return sign === "+" ? amount * multiplier : -amount * multiplier;
1726
1720
  }
1727
- var joiStringArrayType = (type) => (value, helpers) => {
1721
+ var joiStringArrayType = (type) => (value, _helpers) => {
1728
1722
  if (value === void 0 || typeof value === "function") {
1729
1723
  return [];
1730
1724
  }
@@ -1750,7 +1744,7 @@ var joiStringArrayType = (type) => (value, helpers) => {
1750
1744
  return arr;
1751
1745
  };
1752
1746
 
1753
- // src/params/index.ts
1747
+ // src/params/index.js
1754
1748
  var Params = class _Params {
1755
1749
  context;
1756
1750
  // Partial context during initialization
@@ -2010,7 +2004,7 @@ var Params = class _Params {
2010
2004
  definition = val;
2011
2005
  val = val.value;
2012
2006
  }
2013
- const def = this.assignDefinition(key, definition);
2007
+ this.assignDefinition(key, definition);
2014
2008
  if (!this.runAllRegisteredSetters(key, val)) {
2015
2009
  this.params[key] = val;
2016
2010
  }
@@ -2069,6 +2063,18 @@ var Params = class _Params {
2069
2063
  this._currentModule = prev;
2070
2064
  }
2071
2065
  }
2066
+ /**
2067
+ * Async variant of {@link runWithModule} for modules that await params.get().
2068
+ */
2069
+ async runWithModuleAsync(moduleName, fn) {
2070
+ const prev = this._currentModule;
2071
+ this._currentModule = moduleName;
2072
+ try {
2073
+ return await fn();
2074
+ } finally {
2075
+ this._currentModule = prev;
2076
+ }
2077
+ }
2072
2078
  /**
2073
2079
  * Infer module name from call stack: first caller outside params/index gives path like .../src/<moduleName>/...
2074
2080
  */
@@ -2129,11 +2135,11 @@ var Params = class _Params {
2129
2135
  }
2130
2136
  };
2131
2137
 
2132
- // src/logger/index.ts
2138
+ // src/logger/index.js
2133
2139
  var import_chalk = __toESM(require("chalk"), 1);
2134
2140
  var import_util = __toESM(require("util"), 1);
2135
2141
 
2136
- // src/logger/transports.ts
2142
+ // src/logger/transports.js
2137
2143
  var ConsoleTransport = class {
2138
2144
  write(payload) {
2139
2145
  console.info(payload);
@@ -2153,7 +2159,7 @@ var ParentProcessTransport = class {
2153
2159
  }
2154
2160
  };
2155
2161
 
2156
- // src/logger/index.ts
2162
+ // src/logger/index.js
2157
2163
  var ALL_LEVELS = [
2158
2164
  "silly",
2159
2165
  "debug",
@@ -2226,6 +2232,7 @@ var Logger = class _Logger {
2226
2232
  }
2227
2233
  /**
2228
2234
  * Initialize logger from context and CLI parameters. Whatever is in options goes (after discovered params).
2235
+ * Params are tracked under the `logger` module for --showUsedParams.
2229
2236
  */
2230
2237
  static init(context, options) {
2231
2238
  const paramDefs = {
@@ -2239,7 +2246,7 @@ var Logger = class _Logger {
2239
2246
  progressWithTimes: "boolean default false",
2240
2247
  progressThrottleMs: "number"
2241
2248
  };
2242
- const discovered = context.params.getAllForModule(paramDefs);
2249
+ const discovered = context.params.getAllForModule("logger", paramDefs);
2243
2250
  const config2 = { ...discovered, ...options };
2244
2251
  const logger = new _Logger(context, config2);
2245
2252
  context.logger = logger;
@@ -2435,9 +2442,9 @@ var Logger = class _Logger {
2435
2442
  }
2436
2443
  };
2437
2444
 
2438
- // src/init/index.ts
2445
+ // src/init/index.js
2439
2446
  var import_events = require("events");
2440
- function extractComponentOptions(opts, componentName) {
2447
+ function extractComponentOptions(opts, _componentName) {
2441
2448
  const reservedKeys = ["overrides", "defaults", "modules"];
2442
2449
  const componentOptions = {};
2443
2450
  for (const [key, value] of Object.entries(opts)) {
@@ -2504,6 +2511,19 @@ function printAllParameters(context) {
2504
2511
  async function init(flow, opts = {}) {
2505
2512
  let stop = false;
2506
2513
  let context = null;
2514
+ let cleanupRan = false;
2515
+ const runRegisteredCleanups = async (ctx) => {
2516
+ if (cleanupRan) return;
2517
+ cleanupRan = true;
2518
+ const fns = [...ctx.cleanupFunctions].reverse();
2519
+ for (const fn of fns) {
2520
+ try {
2521
+ await fn(ctx);
2522
+ } catch (error) {
2523
+ ctx.logger.warn("[cleanup] error in cleanup function:", error);
2524
+ }
2525
+ }
2526
+ };
2507
2527
  try {
2508
2528
  try {
2509
2529
  const screenModule = await Promise.resolve().then(() => (init_screen(), screen_exports));
@@ -2526,13 +2546,24 @@ async function init(flow, opts = {}) {
2526
2546
  printAllParameters(context);
2527
2547
  process.exit(0);
2528
2548
  }
2549
+ let sigintCount = 0;
2529
2550
  process.on("SIGINT", async () => {
2530
- if (stop) {
2531
- context.logger.warn("[process] killed");
2532
- process.exit(2);
2551
+ if (!context) return;
2552
+ sigintCount += 1;
2553
+ if (sigintCount === 1) {
2554
+ stop = true;
2555
+ context.logger.info(`>> emitting stop with allowance ${stopAllowance}`);
2556
+ context.emitter.emit("stop", stopAllowance);
2557
+ return;
2533
2558
  }
2559
+ context.logger.warn("[process] second SIGINT: running cleanup then exit");
2560
+ await runRegisteredCleanups(context);
2561
+ process.exit(2);
2562
+ });
2563
+ process.on("SIGTERM", () => {
2564
+ if (!context || stop) return;
2534
2565
  stop = true;
2535
- context.logger.info(`>> emitting stop with allowance ${stopAllowance}`);
2566
+ context.logger.info(`>> SIGTERM: emitting stop with allowance ${stopAllowance}`);
2536
2567
  context.emitter.emit("stop", stopAllowance);
2537
2568
  });
2538
2569
  await flow(context);
@@ -2557,13 +2588,7 @@ async function init(flow, opts = {}) {
2557
2588
  }
2558
2589
  } finally {
2559
2590
  if (context) {
2560
- for (const fn of context.cleanupFunctions.reverse()) {
2561
- try {
2562
- await fn(context);
2563
- } catch (error) {
2564
- context.logger.warn("[cleanup] error in cleanup function:", error);
2565
- }
2566
- }
2591
+ await runRegisteredCleanups(context);
2567
2592
  }
2568
2593
  }
2569
2594
  }