@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/tasks.cjs CHANGED
@@ -30,10 +30,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  var tasks_exports = {};
31
31
  __export(tasks_exports, {
32
32
  AbstractTask: () => AbstractTask,
33
+ DEFAULT_RUNTIME_PARAM_SPECS: () => DEFAULT_RUNTIME_PARAM_SPECS,
33
34
  LOGGER_RUNTIME_KEYS: () => LOGGER_RUNTIME_KEYS,
34
35
  LOOP_RUNTIME_KEYS: () => LOOP_RUNTIME_KEYS,
35
36
  SERVICE_TASK_NAMES: () => SERVICE_TASK_NAMES,
36
37
  TaskGetLogs: () => TaskGetLogs,
38
+ TaskPauseRunner: () => TaskPauseRunner,
37
39
  TaskPing: () => TaskPing,
38
40
  TaskSampleProcess: () => TaskSampleProcess,
39
41
  TaskSetRuntimeParam: () => TaskSetRuntimeParam,
@@ -41,9 +43,11 @@ __export(tasks_exports, {
41
43
  TaskStopRunner: () => TaskStopRunner,
42
44
  TaskSumAB: () => TaskSumAB,
43
45
  TaskSystemInfo: () => TaskSystemInfo,
46
+ TaskUnpauseRunner: () => TaskUnpauseRunner,
44
47
  TasksManager: () => TasksManager,
45
48
  TasksRegistry: () => TasksRegistry,
46
49
  appendTaskIpcLog: () => appendTaskIpcLog,
50
+ applyRunnerPaused: () => applyRunnerPaused,
47
51
  applyRuntimeParam: () => applyRuntimeParam,
48
52
  applyRuntimePatch: () => applyRuntimePatch,
49
53
  coerceRuntimeValue: () => coerceRuntimeValue,
@@ -61,6 +65,7 @@ __export(tasks_exports, {
61
65
  listServicesRegistry: () => listServicesRegistry,
62
66
  matchesParsedPattern: () => matchesParsedPattern,
63
67
  mergeAllowedTasksWithServiceTasks: () => mergeAllowedTasksWithServiceTasks,
68
+ mergeRuntimeParamSpecs: () => mergeRuntimeParamSpecs,
64
69
  nextTimeMatch: () => nextTimeMatch,
65
70
  normalizeAllowedTasks: () => normalizeAllowedTasks,
66
71
  queueToTableNames: () => queueToTableNames,
@@ -74,6 +79,8 @@ __export(tasks_exports, {
74
79
  resolveSteps: () => resolveSteps,
75
80
  runNodeTaskScript: () => runNodeTaskScript,
76
81
  runTasksLoop: () => runTasksLoop,
82
+ runtimeParamSpecsFromMetadata: () => runtimeParamSpecsFromMetadata,
83
+ runtimeValuesForSpecs: () => runtimeValuesForSpecs,
77
84
  taskHistoryInsertFromQueueRow: () => taskHistoryInsertFromQueueRow,
78
85
  timeMatcher: () => timeMatcher,
79
86
  touchRunnerHeartbeat: () => touchServicesRegistry,
@@ -428,12 +435,13 @@ function tasksSchemaSpec(queueName = "tasks") {
428
435
  }
429
436
  };
430
437
  }
431
- function taskHistoryInsertFromQueueRow(row, overrides) {
438
+ function taskHistoryInsertFromQueueRow(row, overrides = {}) {
432
439
  const { id, ...snapshot } = row;
433
- void id;
440
+ const opid = overrides.opid !== void 0 ? overrides.opid : snapshot.opid != null && String(snapshot.opid).trim() !== "" ? snapshot.opid : id;
434
441
  return {
435
442
  ...snapshot,
436
- ...overrides
443
+ ...overrides,
444
+ opid
437
445
  };
438
446
  }
439
447
  async function ensureTaskTables(context, options = {}) {
@@ -2772,69 +2780,6 @@ var TaskStopRunner = class extends AbstractTask {
2772
2780
  }
2773
2781
  };
2774
2782
 
2775
- // src/tasks/coreTasks/TaskGetLogs.js
2776
- var TaskGetLogs = class extends AbstractTask {
2777
- /**
2778
- * @param {object} context
2779
- * @param {Record<string, unknown>} [overrides]
2780
- * @returns {Promise<{ source: string, resource: string, tail: number, afterTs?: string }>}
2781
- */
2782
- static async resolveCustomParams(context, overrides = {}) {
2783
- const merged = AbstractTask._mergeTypedParams(context, "task-get-logs", {
2784
- source: "string",
2785
- resource: "string",
2786
- tail: "number default 100",
2787
- afterTs: "string"
2788
- }, overrides);
2789
- const source = typeof merged.source === "string" ? merged.source.trim() : "";
2790
- const resource = typeof merged.resource === "string" ? merged.resource.trim() : "";
2791
- if (!source) throw new ParamError('getLogs: param "source" is required');
2792
- if (!resource) throw new ParamError('getLogs: param "resource" is required');
2793
- let tail = Number(merged.tail);
2794
- if (!Number.isFinite(tail) || tail < 1) tail = 100;
2795
- tail = Math.min(1e4, Math.max(1, Math.floor(tail)));
2796
- const out = { source, resource, tail };
2797
- if (typeof merged.afterTs === "string" && merged.afterTs.trim()) {
2798
- out.afterTs = merged.afterTs.trim();
2799
- }
2800
- return out;
2801
- }
2802
- /**
2803
- * @param {unknown} _reportProgress Unused (single-shot read, no progress events).
2804
- * @returns {Promise<{ success: boolean, results: unknown }>}
2805
- */
2806
- async run(_reportProgress) {
2807
- const p = this.task.params ?? {};
2808
- const source = String(p.source ?? "").trim();
2809
- const resource = String(p.resource ?? "").trim();
2810
- const tail = Math.max(1, Math.min(1e4, Number(p.tail) > 0 ? Number(p.tail) : 100));
2811
- const afterTs = p.afterTs != null && String(p.afterTs).trim() ? String(p.afterTs).trim() : null;
2812
- if (!source || !resource) {
2813
- return {
2814
- success: false,
2815
- results: { error: 'getLogs requires params "source" and "resource"' }
2816
- };
2817
- }
2818
- try {
2819
- const { records, latestTs } = await readTaskIpcLogsSnapshot(this.context, {
2820
- source,
2821
- resource,
2822
- tail,
2823
- afterTs
2824
- });
2825
- return {
2826
- success: true,
2827
- results: { records, latestTs, source, resource }
2828
- };
2829
- } catch (e) {
2830
- return {
2831
- success: false,
2832
- results: { error: e?.message ?? String(e) }
2833
- };
2834
- }
2835
- }
2836
- };
2837
-
2838
2783
  // src/tasks/runtimeParams.js
2839
2784
  var LOOP_RUNTIME_KEYS = ["maxParallel", "pollMs", "claimJitterMs", "scanLimit"];
2840
2785
  var LOGGER_RUNTIME_KEYS = [
@@ -2848,7 +2793,77 @@ var LOGGER_RUNTIME_KEYS = [
2848
2793
  "progressWithTimes",
2849
2794
  "progressThrottleMs"
2850
2795
  ];
2851
- var CONTROL_LANE_TASK_NAMES = ["stopRunner", "stop", "setRuntimeParam", "setRunnerParam"];
2796
+ var DEFAULT_RUNTIME_PARAM_SPECS = [
2797
+ {
2798
+ key: "maxParallel",
2799
+ type: "number",
2800
+ label: "Max parallel",
2801
+ description: "Worker-lane concurrency (how many tasks claim at once)"
2802
+ },
2803
+ {
2804
+ key: "pollMs",
2805
+ type: "number",
2806
+ label: "Poll ms",
2807
+ description: "Idle poll interval between claim attempts"
2808
+ },
2809
+ {
2810
+ key: "claimJitterMs",
2811
+ type: "number",
2812
+ label: "Claim jitter ms",
2813
+ description: "Random delay before worker claims (0 = off)"
2814
+ },
2815
+ {
2816
+ key: "scanLimit",
2817
+ type: "number",
2818
+ label: "Scan limit",
2819
+ description: "Max idle rows scanned per claim attempt"
2820
+ }
2821
+ ];
2822
+ function mergeRuntimeParamSpecs(extra) {
2823
+ const byKey = new Map(DEFAULT_RUNTIME_PARAM_SPECS.map((s) => [s.key, { ...s }]));
2824
+ if (Array.isArray(extra)) {
2825
+ for (const raw of extra) {
2826
+ if (!raw || typeof raw !== "object") continue;
2827
+ const key = String(raw.key ?? "").trim();
2828
+ if (!key) continue;
2829
+ const prev = byKey.get(key) ?? {};
2830
+ const type = ["number", "boolean", "string"].includes(raw.type) ? raw.type : prev.type ?? "string";
2831
+ byKey.set(key, {
2832
+ key,
2833
+ type,
2834
+ label: String(raw.label ?? prev.label ?? key),
2835
+ description: raw.description != null ? String(raw.description) : prev.description != null ? String(prev.description) : void 0
2836
+ });
2837
+ }
2838
+ }
2839
+ return Array.from(byKey.values());
2840
+ }
2841
+ function runtimeValuesForSpecs(context, specs = DEFAULT_RUNTIME_PARAM_SPECS) {
2842
+ const rt = ensureTasksRuntime(context);
2843
+ const out = {};
2844
+ for (const s of specs) {
2845
+ if (rt[s.key] !== void 0) out[s.key] = rt[s.key];
2846
+ }
2847
+ return out;
2848
+ }
2849
+ function runtimeParamSpecsFromMetadata(metadata) {
2850
+ const meta = metadata && typeof metadata === "object" ? metadata : null;
2851
+ const raw = meta?.runtimeParams;
2852
+ if (!Array.isArray(raw) || raw.length === 0) {
2853
+ return mergeRuntimeParamSpecs();
2854
+ }
2855
+ return mergeRuntimeParamSpecs(raw);
2856
+ }
2857
+ var CONTROL_LANE_TASK_NAMES = [
2858
+ "stopRunner",
2859
+ "stop",
2860
+ "pauseRunner",
2861
+ "pause",
2862
+ "unpauseRunner",
2863
+ "unpause",
2864
+ "setRuntimeParam",
2865
+ "setRunnerParam"
2866
+ ];
2852
2867
  function controlLaneTaskNames(extra) {
2853
2868
  const names = [...CONTROL_LANE_TASK_NAMES];
2854
2869
  if (extra == null || extra === "") return names;
@@ -2911,6 +2926,7 @@ function ensureTasksRuntime(context, seed = {}) {
2911
2926
  if (rt.pollMs === void 0) rt.pollMs = seed.pollMs ?? 1e3;
2912
2927
  if (rt.claimJitterMs === void 0) rt.claimJitterMs = seed.claimJitterMs ?? 0;
2913
2928
  if (rt.scanLimit === void 0) rt.scanLimit = seed.scanLimit ?? 100;
2929
+ if (rt.paused === void 0) rt.paused = seed.paused === true;
2914
2930
  return rt;
2915
2931
  }
2916
2932
  async function applyRuntimeParam(context, key, value) {
@@ -2934,12 +2950,15 @@ async function applyRuntimeParam(context, key, value) {
2934
2950
  const reg = context.servicesRegistry;
2935
2951
  if (reg?.rowId && reg?.registryTable) {
2936
2952
  try {
2937
- const loopSnapshot = {};
2953
+ const specs = Array.isArray(context.tasksRuntimeParamSpecs) ? context.tasksRuntimeParamSpecs : DEFAULT_RUNTIME_PARAM_SPECS;
2954
+ const runtimeSnapshot = runtimeValuesForSpecs(context, specs);
2938
2955
  for (const lk of LOOP_RUNTIME_KEYS) {
2939
- if (runtime[lk] !== void 0) loopSnapshot[lk] = runtime[lk];
2956
+ if (runtime[lk] !== void 0 && runtimeSnapshot[lk] === void 0) {
2957
+ runtimeSnapshot[lk] = runtime[lk];
2958
+ }
2940
2959
  }
2941
2960
  await updateServicesRegistryMetadata(context, reg, {
2942
- runtime: loopSnapshot,
2961
+ runtime: runtimeSnapshot,
2943
2962
  runtimeUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
2944
2963
  });
2945
2964
  applied.push("servicesRegistry");
@@ -2975,6 +2994,140 @@ function readLoopRuntime(context) {
2975
2994
  };
2976
2995
  }
2977
2996
 
2997
+ // src/tasks/coreTasks/TaskPauseRunner.js
2998
+ async function applyRunnerPaused(context, paused) {
2999
+ const runtime = ensureTasksRuntime(context);
3000
+ const was = runtime.paused === true;
3001
+ runtime.paused = paused === true;
3002
+ const registry = context.servicesRegistry;
3003
+ if (registry && typeof registry === "object") {
3004
+ await updateServicesRegistryMetadata(context, registry, {
3005
+ paused: runtime.paused,
3006
+ pausedAt: runtime.paused ? (/* @__PURE__ */ new Date()).toISOString() : null
3007
+ });
3008
+ }
3009
+ 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).";
3010
+ context.logger?.warn?.(
3011
+ runtime.paused ? `[TaskPauseRunner] ${message}` : `[TaskUnpauseRunner] ${message}`
3012
+ );
3013
+ return {
3014
+ success: true,
3015
+ results: {
3016
+ paused: runtime.paused,
3017
+ message
3018
+ }
3019
+ };
3020
+ }
3021
+ var TaskPauseRunner = class extends AbstractTask {
3022
+ static taskName = "pauseRunner";
3023
+ static description = "Pause a runner: finish in-flight tasks, claim no new worker tasks until unpaused";
3024
+ static aliases = ["pause"];
3025
+ static defaultWaitForResult = true;
3026
+ /**
3027
+ * @param {object} context
3028
+ * @param {Record<string, unknown>} [overrides]
3029
+ * @returns {Promise<object>}
3030
+ */
3031
+ static async resolveParams(context, overrides = {}) {
3032
+ const main = await super.resolveParams(context, overrides);
3033
+ if (!main.serviceName) {
3034
+ throw new ParamError(
3035
+ "pause/pauseRunner requires --serviceName (registry instance name; optional --serviceGroup, --instanceNumber, --serverName to narrow targeting)"
3036
+ );
3037
+ }
3038
+ return main;
3039
+ }
3040
+ async run() {
3041
+ return applyRunnerPaused(this.context, true);
3042
+ }
3043
+ };
3044
+ var TaskUnpauseRunner = class extends AbstractTask {
3045
+ static taskName = "unpauseRunner";
3046
+ static description = "Unpause a runner: resume claiming worker tasks";
3047
+ static aliases = ["unpause"];
3048
+ static defaultWaitForResult = true;
3049
+ /**
3050
+ * @param {object} context
3051
+ * @param {Record<string, unknown>} [overrides]
3052
+ * @returns {Promise<object>}
3053
+ */
3054
+ static async resolveParams(context, overrides = {}) {
3055
+ const main = await super.resolveParams(context, overrides);
3056
+ if (!main.serviceName) {
3057
+ throw new ParamError(
3058
+ "unpause/unpauseRunner requires --serviceName (registry instance name; optional --serviceGroup, --instanceNumber, --serverName to narrow targeting)"
3059
+ );
3060
+ }
3061
+ return main;
3062
+ }
3063
+ async run() {
3064
+ return applyRunnerPaused(this.context, false);
3065
+ }
3066
+ };
3067
+
3068
+ // src/tasks/coreTasks/TaskGetLogs.js
3069
+ var TaskGetLogs = class extends AbstractTask {
3070
+ /**
3071
+ * @param {object} context
3072
+ * @param {Record<string, unknown>} [overrides]
3073
+ * @returns {Promise<{ source: string, resource: string, tail: number, afterTs?: string }>}
3074
+ */
3075
+ static async resolveCustomParams(context, overrides = {}) {
3076
+ const merged = AbstractTask._mergeTypedParams(context, "task-get-logs", {
3077
+ source: "string",
3078
+ resource: "string",
3079
+ tail: "number default 100",
3080
+ afterTs: "string"
3081
+ }, overrides);
3082
+ const source = typeof merged.source === "string" ? merged.source.trim() : "";
3083
+ const resource = typeof merged.resource === "string" ? merged.resource.trim() : "";
3084
+ if (!source) throw new ParamError('getLogs: param "source" is required');
3085
+ if (!resource) throw new ParamError('getLogs: param "resource" is required');
3086
+ let tail = Number(merged.tail);
3087
+ if (!Number.isFinite(tail) || tail < 1) tail = 100;
3088
+ tail = Math.min(1e4, Math.max(1, Math.floor(tail)));
3089
+ const out = { source, resource, tail };
3090
+ if (typeof merged.afterTs === "string" && merged.afterTs.trim()) {
3091
+ out.afterTs = merged.afterTs.trim();
3092
+ }
3093
+ return out;
3094
+ }
3095
+ /**
3096
+ * @param {unknown} _reportProgress Unused (single-shot read, no progress events).
3097
+ * @returns {Promise<{ success: boolean, results: unknown }>}
3098
+ */
3099
+ async run(_reportProgress) {
3100
+ const p = this.task.params ?? {};
3101
+ const source = String(p.source ?? "").trim();
3102
+ const resource = String(p.resource ?? "").trim();
3103
+ const tail = Math.max(1, Math.min(1e4, Number(p.tail) > 0 ? Number(p.tail) : 100));
3104
+ const afterTs = p.afterTs != null && String(p.afterTs).trim() ? String(p.afterTs).trim() : null;
3105
+ if (!source || !resource) {
3106
+ return {
3107
+ success: false,
3108
+ results: { error: 'getLogs requires params "source" and "resource"' }
3109
+ };
3110
+ }
3111
+ try {
3112
+ const { records, latestTs } = await readTaskIpcLogsSnapshot(this.context, {
3113
+ source,
3114
+ resource,
3115
+ tail,
3116
+ afterTs
3117
+ });
3118
+ return {
3119
+ success: true,
3120
+ results: { records, latestTs, source, resource }
3121
+ };
3122
+ } catch (e) {
3123
+ return {
3124
+ success: false,
3125
+ results: { error: e?.message ?? String(e) }
3126
+ };
3127
+ }
3128
+ }
3129
+ };
3130
+
2978
3131
  // src/tasks/coreTasks/TaskSetRuntimeParam.js
2979
3132
  var TaskSetRuntimeParam = class extends AbstractTask {
2980
3133
  static defaultWaitForResult = true;
@@ -3130,7 +3283,7 @@ var TasksRegistry = class _TasksRegistry {
3130
3283
  * @returns {TasksRegistry}
3131
3284
  */
3132
3285
  static withCoreTasks() {
3133
- 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);
3286
+ 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);
3134
3287
  }
3135
3288
  /**
3136
3289
  * Register a single task class under a name. Overwrites any previous entry.
@@ -3233,6 +3386,10 @@ var SERVICE_TASK_NAMES = [
3233
3386
  "ping",
3234
3387
  "stop",
3235
3388
  "stopRunner",
3389
+ "pause",
3390
+ "pauseRunner",
3391
+ "unpause",
3392
+ "unpauseRunner",
3236
3393
  "shellCommand",
3237
3394
  "systemInfo",
3238
3395
  "info",
@@ -3694,17 +3851,25 @@ async function runTasksLoop(context, options) {
3694
3851
  if (hbGroup) {
3695
3852
  const hbIntervalMs = options.runnerHeartbeatIntervalMs ?? 1e4;
3696
3853
  const staleMs = options.runnerHeartbeatStaleMs ?? 45e3;
3697
- const loop0 = readLoopRuntime(context);
3854
+ const runtimeParamSpecs = mergeRuntimeParamSpecs(options.runnerRuntimeParams);
3855
+ context.tasksRuntimeParamSpecs = runtimeParamSpecs;
3698
3856
  const defaultMeta = {
3699
3857
  component: "tasks-runner",
3700
3858
  allowedTasks: allowedTasks?.length ? allowedTasks.join(",") : "all",
3701
- runtime: {
3702
- maxParallel: loop0.maxParallel,
3703
- pollMs: loop0.pollMs,
3704
- claimJitterMs: loop0.claimJitterMs,
3705
- scanLimit: loop0.scanLimit
3706
- }
3859
+ paused: false,
3860
+ runtimeParams: runtimeParamSpecs,
3861
+ runtime: runtimeValuesForSpecs(context, runtimeParamSpecs)
3707
3862
  };
3863
+ const metadata = {
3864
+ ...defaultMeta,
3865
+ ...options.runnerMetadata && typeof options.runnerMetadata === "object" ? options.runnerMetadata : {}
3866
+ };
3867
+ if (!Array.isArray(metadata.runtimeParams) || metadata.runtimeParams.length === 0) {
3868
+ metadata.runtimeParams = defaultMeta.runtimeParams;
3869
+ }
3870
+ if (!metadata.runtime || typeof metadata.runtime !== "object") {
3871
+ metadata.runtime = defaultMeta.runtime;
3872
+ }
3708
3873
  registryReg = await registerInServicesRegistry(context, {
3709
3874
  queueName,
3710
3875
  target,
@@ -3714,7 +3879,7 @@ async function runTasksLoop(context, options) {
3714
3879
  staleMs,
3715
3880
  groupMaxInstances: options.runnerGroupMaxInstances,
3716
3881
  enforceMaxInstances: options.runnerEnforceMaxInstances ?? true,
3717
- metadata: options.runnerMetadata ?? defaultMeta
3882
+ metadata
3718
3883
  });
3719
3884
  context.servicesRegistry = registryReg;
3720
3885
  runnerIdentity = {
@@ -3764,28 +3929,38 @@ async function runTasksLoop(context, options) {
3764
3929
  if (claimJitterMs > 0) {
3765
3930
  await sleepMs(Math.floor(Math.random() * (claimJitterMs + 1)));
3766
3931
  }
3767
- while (runningPromises.size < maxParallel) {
3768
- const claimed = await claimNextRunnableTask(
3769
- context,
3770
- tasksTable,
3771
- target,
3772
- registry,
3773
- scanLimit,
3774
- allowedTasks,
3775
- runnerIdentity
3776
- );
3777
- if (!claimed) break;
3778
- const p = executeClaimedTask(context, tasksTable, historyTable, claimed, registry, runningTaskInstances).then(async (outcome) => {
3779
- if (outcome.stopRunnerRequested && !stopRequested) {
3780
- stopRequested = true;
3781
- stopAllowanceMs = outcome.stopAllowanceMs || stopAllowanceMs;
3782
- context.tasksRunnerStop = true;
3783
- await signalRunningTasksStop(context, runningTaskInstances, stopAllowanceMs);
3784
- }
3785
- }).finally(() => {
3786
- runningPromises.delete(p);
3787
- });
3788
- runningPromises.add(p);
3932
+ const paused = context.tasksRuntime?.paused === true;
3933
+ if (!paused) {
3934
+ while (runningPromises.size < maxParallel) {
3935
+ const claimed = await claimNextRunnableTask(
3936
+ context,
3937
+ tasksTable,
3938
+ target,
3939
+ registry,
3940
+ scanLimit,
3941
+ allowedTasks,
3942
+ runnerIdentity
3943
+ );
3944
+ if (!claimed) break;
3945
+ const p = executeClaimedTask(
3946
+ context,
3947
+ tasksTable,
3948
+ historyTable,
3949
+ claimed,
3950
+ registry,
3951
+ runningTaskInstances
3952
+ ).then(async (outcome) => {
3953
+ if (outcome.stopRunnerRequested && !stopRequested) {
3954
+ stopRequested = true;
3955
+ stopAllowanceMs = outcome.stopAllowanceMs || stopAllowanceMs;
3956
+ context.tasksRunnerStop = true;
3957
+ await signalRunningTasksStop(context, runningTaskInstances, stopAllowanceMs);
3958
+ }
3959
+ }).finally(() => {
3960
+ runningPromises.delete(p);
3961
+ });
3962
+ runningPromises.add(p);
3963
+ }
3789
3964
  }
3790
3965
  const wakePromises = [...runningPromises];
3791
3966
  if (runningControlPromise) {
@@ -3839,16 +4014,26 @@ async function waitForTaskResult(context, taskId, options = {}) {
3839
4014
  const pollMs = options.pollMs ?? 500;
3840
4015
  const { tasksTable, historyTable } = queueToTableNames(queueName);
3841
4016
  const deadline = Date.now() + timeoutMs;
3842
- const waitStartedAt = /* @__PURE__ */ new Date();
3843
- let cachedNameOpid = null;
3844
- async function historySinceWait(name, opid) {
3845
- let q = db(historyTable).where({ name }).where("completed_at", ">=", waitStartedAt);
4017
+ const waitStartedAt = new Date(Date.now() - 5e3);
4018
+ let cachedNameOpid = options.name != null && String(options.name).trim() !== "" ? {
4019
+ name: String(options.name).trim(),
4020
+ opid: options.opid !== void 0 ? options.opid : null
4021
+ } : null;
4022
+ async function findHistory(name, opid) {
4023
+ const base = () => db(historyTable).where({ name }).where("completed_at", ">=", waitStartedAt);
4024
+ if (opid != null && String(opid).trim() !== "") {
4025
+ const byOpid = await base().where({ opid }).orderBy("completed_at", "desc").first();
4026
+ if (byOpid) return byOpid;
4027
+ }
4028
+ const byQueueId = await base().where({ opid: taskId }).orderBy("completed_at", "desc").first();
4029
+ if (byQueueId) return byQueueId;
4030
+ const byQueueIdAny = await db(historyTable).where({ name, opid: taskId }).orderBy("completed_at", "desc").first();
4031
+ if (byQueueIdAny) return byQueueIdAny;
3846
4032
  if (opid == null || opid === "") {
3847
- q = q.whereNull("opid");
3848
- } else {
3849
- q = q.where({ opid });
4033
+ const byNull = await base().whereNull("opid").orderBy("completed_at", "desc").first();
4034
+ if (byNull) return byNull;
3850
4035
  }
3851
- return await q.orderBy("completed_at", "desc").first();
4036
+ return void 0;
3852
4037
  }
3853
4038
  while (Date.now() <= deadline) {
3854
4039
  const legacy = await db(historyTable).where({ id: taskId }).orderBy("created_at", "desc").first();
@@ -3858,18 +4043,17 @@ async function waitForTaskResult(context, taskId, options = {}) {
3858
4043
  const pending = await db(tasksTable).where({ id: taskId }).first();
3859
4044
  if (pending) {
3860
4045
  cachedNameOpid = { name: pending.name, opid: pending.opid };
3861
- const done = await historySinceWait(pending.name, pending.opid);
3862
- if (done) {
3863
- return done;
3864
- }
3865
- } else if (cachedNameOpid) {
3866
- const done = await historySinceWait(cachedNameOpid.name, cachedNameOpid.opid);
4046
+ }
4047
+ if (cachedNameOpid) {
4048
+ const done = await findHistory(cachedNameOpid.name, cachedNameOpid.opid);
3867
4049
  if (done) {
3868
4050
  return done;
3869
4051
  }
3870
- return null;
3871
4052
  } else {
3872
- return null;
4053
+ const byQueueId = await db(historyTable).where({ opid: taskId }).orderBy("completed_at", "desc").first();
4054
+ if (byQueueId) {
4055
+ return byQueueId;
4056
+ }
3873
4057
  }
3874
4058
  await sleepMs(pollMs);
3875
4059
  }
@@ -4010,10 +4194,12 @@ var TasksManager = class _TasksManager {
4010
4194
  // Annotate the CommonJS export names for ESM import in node:
4011
4195
  0 && (module.exports = {
4012
4196
  AbstractTask,
4197
+ DEFAULT_RUNTIME_PARAM_SPECS,
4013
4198
  LOGGER_RUNTIME_KEYS,
4014
4199
  LOOP_RUNTIME_KEYS,
4015
4200
  SERVICE_TASK_NAMES,
4016
4201
  TaskGetLogs,
4202
+ TaskPauseRunner,
4017
4203
  TaskPing,
4018
4204
  TaskSampleProcess,
4019
4205
  TaskSetRuntimeParam,
@@ -4021,9 +4207,11 @@ var TasksManager = class _TasksManager {
4021
4207
  TaskStopRunner,
4022
4208
  TaskSumAB,
4023
4209
  TaskSystemInfo,
4210
+ TaskUnpauseRunner,
4024
4211
  TasksManager,
4025
4212
  TasksRegistry,
4026
4213
  appendTaskIpcLog,
4214
+ applyRunnerPaused,
4027
4215
  applyRuntimeParam,
4028
4216
  applyRuntimePatch,
4029
4217
  coerceRuntimeValue,
@@ -4041,6 +4229,7 @@ var TasksManager = class _TasksManager {
4041
4229
  listServicesRegistry,
4042
4230
  matchesParsedPattern,
4043
4231
  mergeAllowedTasksWithServiceTasks,
4232
+ mergeRuntimeParamSpecs,
4044
4233
  nextTimeMatch,
4045
4234
  normalizeAllowedTasks,
4046
4235
  queueToTableNames,
@@ -4054,6 +4243,8 @@ var TasksManager = class _TasksManager {
4054
4243
  resolveSteps,
4055
4244
  runNodeTaskScript,
4056
4245
  runTasksLoop,
4246
+ runtimeParamSpecsFromMetadata,
4247
+ runtimeValuesForSpecs,
4057
4248
  taskHistoryInsertFromQueueRow,
4058
4249
  timeMatcher,
4059
4250
  touchRunnerHeartbeat,