@nmakarov/cli-toolkit 0.69.0 → 0.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tasks.cjs CHANGED
@@ -34,6 +34,7 @@ __export(tasks_exports, {
34
34
  LOOP_RUNTIME_KEYS: () => LOOP_RUNTIME_KEYS,
35
35
  SERVICE_TASK_NAMES: () => SERVICE_TASK_NAMES,
36
36
  TaskGetLogs: () => TaskGetLogs,
37
+ TaskPauseRunner: () => TaskPauseRunner,
37
38
  TaskPing: () => TaskPing,
38
39
  TaskSampleProcess: () => TaskSampleProcess,
39
40
  TaskSetRuntimeParam: () => TaskSetRuntimeParam,
@@ -41,9 +42,11 @@ __export(tasks_exports, {
41
42
  TaskStopRunner: () => TaskStopRunner,
42
43
  TaskSumAB: () => TaskSumAB,
43
44
  TaskSystemInfo: () => TaskSystemInfo,
45
+ TaskUnpauseRunner: () => TaskUnpauseRunner,
44
46
  TasksManager: () => TasksManager,
45
47
  TasksRegistry: () => TasksRegistry,
46
48
  appendTaskIpcLog: () => appendTaskIpcLog,
49
+ applyRunnerPaused: () => applyRunnerPaused,
47
50
  applyRuntimeParam: () => applyRuntimeParam,
48
51
  applyRuntimePatch: () => applyRuntimePatch,
49
52
  coerceRuntimeValue: () => coerceRuntimeValue,
@@ -428,12 +431,13 @@ function tasksSchemaSpec(queueName = "tasks") {
428
431
  }
429
432
  };
430
433
  }
431
- function taskHistoryInsertFromQueueRow(row, overrides) {
434
+ function taskHistoryInsertFromQueueRow(row, overrides = {}) {
432
435
  const { id, ...snapshot } = row;
433
- void id;
436
+ const opid = overrides.opid !== void 0 ? overrides.opid : snapshot.opid != null && String(snapshot.opid).trim() !== "" ? snapshot.opid : id;
434
437
  return {
435
438
  ...snapshot,
436
- ...overrides
439
+ ...overrides,
440
+ opid
437
441
  };
438
442
  }
439
443
  async function ensureTaskTables(context, options = {}) {
@@ -2772,69 +2776,6 @@ var TaskStopRunner = class extends AbstractTask {
2772
2776
  }
2773
2777
  };
2774
2778
 
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
2779
  // src/tasks/runtimeParams.js
2839
2780
  var LOOP_RUNTIME_KEYS = ["maxParallel", "pollMs", "claimJitterMs", "scanLimit"];
2840
2781
  var LOGGER_RUNTIME_KEYS = [
@@ -2848,7 +2789,16 @@ var LOGGER_RUNTIME_KEYS = [
2848
2789
  "progressWithTimes",
2849
2790
  "progressThrottleMs"
2850
2791
  ];
2851
- var CONTROL_LANE_TASK_NAMES = ["stopRunner", "stop", "setRuntimeParam", "setRunnerParam"];
2792
+ var CONTROL_LANE_TASK_NAMES = [
2793
+ "stopRunner",
2794
+ "stop",
2795
+ "pauseRunner",
2796
+ "pause",
2797
+ "unpauseRunner",
2798
+ "unpause",
2799
+ "setRuntimeParam",
2800
+ "setRunnerParam"
2801
+ ];
2852
2802
  function controlLaneTaskNames(extra) {
2853
2803
  const names = [...CONTROL_LANE_TASK_NAMES];
2854
2804
  if (extra == null || extra === "") return names;
@@ -2911,6 +2861,7 @@ function ensureTasksRuntime(context, seed = {}) {
2911
2861
  if (rt.pollMs === void 0) rt.pollMs = seed.pollMs ?? 1e3;
2912
2862
  if (rt.claimJitterMs === void 0) rt.claimJitterMs = seed.claimJitterMs ?? 0;
2913
2863
  if (rt.scanLimit === void 0) rt.scanLimit = seed.scanLimit ?? 100;
2864
+ if (rt.paused === void 0) rt.paused = seed.paused === true;
2914
2865
  return rt;
2915
2866
  }
2916
2867
  async function applyRuntimeParam(context, key, value) {
@@ -2975,6 +2926,140 @@ function readLoopRuntime(context) {
2975
2926
  };
2976
2927
  }
2977
2928
 
2929
+ // src/tasks/coreTasks/TaskPauseRunner.js
2930
+ async function applyRunnerPaused(context, paused) {
2931
+ const runtime = ensureTasksRuntime(context);
2932
+ const was = runtime.paused === true;
2933
+ runtime.paused = paused === true;
2934
+ const registry = context.servicesRegistry;
2935
+ if (registry && typeof registry === "object") {
2936
+ await updateServicesRegistryMetadata(context, registry, {
2937
+ paused: runtime.paused,
2938
+ pausedAt: runtime.paused ? (/* @__PURE__ */ new Date()).toISOString() : null
2939
+ });
2940
+ }
2941
+ 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).";
2942
+ context.logger?.warn?.(
2943
+ runtime.paused ? `[TaskPauseRunner] ${message}` : `[TaskUnpauseRunner] ${message}`
2944
+ );
2945
+ return {
2946
+ success: true,
2947
+ results: {
2948
+ paused: runtime.paused,
2949
+ message
2950
+ }
2951
+ };
2952
+ }
2953
+ var TaskPauseRunner = class extends AbstractTask {
2954
+ static taskName = "pauseRunner";
2955
+ static description = "Pause a runner: finish in-flight tasks, claim no new worker tasks until unpaused";
2956
+ static aliases = ["pause"];
2957
+ static defaultWaitForResult = true;
2958
+ /**
2959
+ * @param {object} context
2960
+ * @param {Record<string, unknown>} [overrides]
2961
+ * @returns {Promise<object>}
2962
+ */
2963
+ static async resolveParams(context, overrides = {}) {
2964
+ const main = await super.resolveParams(context, overrides);
2965
+ if (!main.serviceName) {
2966
+ throw new ParamError(
2967
+ "pause/pauseRunner requires --serviceName (registry instance name; optional --serviceGroup, --instanceNumber, --serverName to narrow targeting)"
2968
+ );
2969
+ }
2970
+ return main;
2971
+ }
2972
+ async run() {
2973
+ return applyRunnerPaused(this.context, true);
2974
+ }
2975
+ };
2976
+ var TaskUnpauseRunner = class extends AbstractTask {
2977
+ static taskName = "unpauseRunner";
2978
+ static description = "Unpause a runner: resume claiming worker tasks";
2979
+ static aliases = ["unpause"];
2980
+ static defaultWaitForResult = true;
2981
+ /**
2982
+ * @param {object} context
2983
+ * @param {Record<string, unknown>} [overrides]
2984
+ * @returns {Promise<object>}
2985
+ */
2986
+ static async resolveParams(context, overrides = {}) {
2987
+ const main = await super.resolveParams(context, overrides);
2988
+ if (!main.serviceName) {
2989
+ throw new ParamError(
2990
+ "unpause/unpauseRunner requires --serviceName (registry instance name; optional --serviceGroup, --instanceNumber, --serverName to narrow targeting)"
2991
+ );
2992
+ }
2993
+ return main;
2994
+ }
2995
+ async run() {
2996
+ return applyRunnerPaused(this.context, false);
2997
+ }
2998
+ };
2999
+
3000
+ // src/tasks/coreTasks/TaskGetLogs.js
3001
+ var TaskGetLogs = class extends AbstractTask {
3002
+ /**
3003
+ * @param {object} context
3004
+ * @param {Record<string, unknown>} [overrides]
3005
+ * @returns {Promise<{ source: string, resource: string, tail: number, afterTs?: string }>}
3006
+ */
3007
+ static async resolveCustomParams(context, overrides = {}) {
3008
+ const merged = AbstractTask._mergeTypedParams(context, "task-get-logs", {
3009
+ source: "string",
3010
+ resource: "string",
3011
+ tail: "number default 100",
3012
+ afterTs: "string"
3013
+ }, overrides);
3014
+ const source = typeof merged.source === "string" ? merged.source.trim() : "";
3015
+ const resource = typeof merged.resource === "string" ? merged.resource.trim() : "";
3016
+ if (!source) throw new ParamError('getLogs: param "source" is required');
3017
+ if (!resource) throw new ParamError('getLogs: param "resource" is required');
3018
+ let tail = Number(merged.tail);
3019
+ if (!Number.isFinite(tail) || tail < 1) tail = 100;
3020
+ tail = Math.min(1e4, Math.max(1, Math.floor(tail)));
3021
+ const out = { source, resource, tail };
3022
+ if (typeof merged.afterTs === "string" && merged.afterTs.trim()) {
3023
+ out.afterTs = merged.afterTs.trim();
3024
+ }
3025
+ return out;
3026
+ }
3027
+ /**
3028
+ * @param {unknown} _reportProgress Unused (single-shot read, no progress events).
3029
+ * @returns {Promise<{ success: boolean, results: unknown }>}
3030
+ */
3031
+ async run(_reportProgress) {
3032
+ const p = this.task.params ?? {};
3033
+ const source = String(p.source ?? "").trim();
3034
+ const resource = String(p.resource ?? "").trim();
3035
+ const tail = Math.max(1, Math.min(1e4, Number(p.tail) > 0 ? Number(p.tail) : 100));
3036
+ const afterTs = p.afterTs != null && String(p.afterTs).trim() ? String(p.afterTs).trim() : null;
3037
+ if (!source || !resource) {
3038
+ return {
3039
+ success: false,
3040
+ results: { error: 'getLogs requires params "source" and "resource"' }
3041
+ };
3042
+ }
3043
+ try {
3044
+ const { records, latestTs } = await readTaskIpcLogsSnapshot(this.context, {
3045
+ source,
3046
+ resource,
3047
+ tail,
3048
+ afterTs
3049
+ });
3050
+ return {
3051
+ success: true,
3052
+ results: { records, latestTs, source, resource }
3053
+ };
3054
+ } catch (e) {
3055
+ return {
3056
+ success: false,
3057
+ results: { error: e?.message ?? String(e) }
3058
+ };
3059
+ }
3060
+ }
3061
+ };
3062
+
2978
3063
  // src/tasks/coreTasks/TaskSetRuntimeParam.js
2979
3064
  var TaskSetRuntimeParam = class extends AbstractTask {
2980
3065
  static defaultWaitForResult = true;
@@ -3130,7 +3215,7 @@ var TasksRegistry = class _TasksRegistry {
3130
3215
  * @returns {TasksRegistry}
3131
3216
  */
3132
3217
  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);
3218
+ 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
3219
  }
3135
3220
  /**
3136
3221
  * Register a single task class under a name. Overwrites any previous entry.
@@ -3233,6 +3318,10 @@ var SERVICE_TASK_NAMES = [
3233
3318
  "ping",
3234
3319
  "stop",
3235
3320
  "stopRunner",
3321
+ "pause",
3322
+ "pauseRunner",
3323
+ "unpause",
3324
+ "unpauseRunner",
3236
3325
  "shellCommand",
3237
3326
  "systemInfo",
3238
3327
  "info",
@@ -3698,6 +3787,7 @@ async function runTasksLoop(context, options) {
3698
3787
  const defaultMeta = {
3699
3788
  component: "tasks-runner",
3700
3789
  allowedTasks: allowedTasks?.length ? allowedTasks.join(",") : "all",
3790
+ paused: false,
3701
3791
  runtime: {
3702
3792
  maxParallel: loop0.maxParallel,
3703
3793
  pollMs: loop0.pollMs,
@@ -3764,28 +3854,38 @@ async function runTasksLoop(context, options) {
3764
3854
  if (claimJitterMs > 0) {
3765
3855
  await sleepMs(Math.floor(Math.random() * (claimJitterMs + 1)));
3766
3856
  }
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);
3857
+ const paused = context.tasksRuntime?.paused === true;
3858
+ if (!paused) {
3859
+ while (runningPromises.size < maxParallel) {
3860
+ const claimed = await claimNextRunnableTask(
3861
+ context,
3862
+ tasksTable,
3863
+ target,
3864
+ registry,
3865
+ scanLimit,
3866
+ allowedTasks,
3867
+ runnerIdentity
3868
+ );
3869
+ if (!claimed) break;
3870
+ const p = executeClaimedTask(
3871
+ context,
3872
+ tasksTable,
3873
+ historyTable,
3874
+ claimed,
3875
+ registry,
3876
+ runningTaskInstances
3877
+ ).then(async (outcome) => {
3878
+ if (outcome.stopRunnerRequested && !stopRequested) {
3879
+ stopRequested = true;
3880
+ stopAllowanceMs = outcome.stopAllowanceMs || stopAllowanceMs;
3881
+ context.tasksRunnerStop = true;
3882
+ await signalRunningTasksStop(context, runningTaskInstances, stopAllowanceMs);
3883
+ }
3884
+ }).finally(() => {
3885
+ runningPromises.delete(p);
3886
+ });
3887
+ runningPromises.add(p);
3888
+ }
3789
3889
  }
3790
3890
  const wakePromises = [...runningPromises];
3791
3891
  if (runningControlPromise) {
@@ -3839,16 +3939,26 @@ async function waitForTaskResult(context, taskId, options = {}) {
3839
3939
  const pollMs = options.pollMs ?? 500;
3840
3940
  const { tasksTable, historyTable } = queueToTableNames(queueName);
3841
3941
  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);
3942
+ const waitStartedAt = new Date(Date.now() - 5e3);
3943
+ let cachedNameOpid = options.name != null && String(options.name).trim() !== "" ? {
3944
+ name: String(options.name).trim(),
3945
+ opid: options.opid !== void 0 ? options.opid : null
3946
+ } : null;
3947
+ async function findHistory(name, opid) {
3948
+ const base = () => db(historyTable).where({ name }).where("completed_at", ">=", waitStartedAt);
3949
+ if (opid != null && String(opid).trim() !== "") {
3950
+ const byOpid = await base().where({ opid }).orderBy("completed_at", "desc").first();
3951
+ if (byOpid) return byOpid;
3952
+ }
3953
+ const byQueueId = await base().where({ opid: taskId }).orderBy("completed_at", "desc").first();
3954
+ if (byQueueId) return byQueueId;
3955
+ const byQueueIdAny = await db(historyTable).where({ name, opid: taskId }).orderBy("completed_at", "desc").first();
3956
+ if (byQueueIdAny) return byQueueIdAny;
3846
3957
  if (opid == null || opid === "") {
3847
- q = q.whereNull("opid");
3848
- } else {
3849
- q = q.where({ opid });
3958
+ const byNull = await base().whereNull("opid").orderBy("completed_at", "desc").first();
3959
+ if (byNull) return byNull;
3850
3960
  }
3851
- return await q.orderBy("completed_at", "desc").first();
3961
+ return void 0;
3852
3962
  }
3853
3963
  while (Date.now() <= deadline) {
3854
3964
  const legacy = await db(historyTable).where({ id: taskId }).orderBy("created_at", "desc").first();
@@ -3858,18 +3968,17 @@ async function waitForTaskResult(context, taskId, options = {}) {
3858
3968
  const pending = await db(tasksTable).where({ id: taskId }).first();
3859
3969
  if (pending) {
3860
3970
  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);
3971
+ }
3972
+ if (cachedNameOpid) {
3973
+ const done = await findHistory(cachedNameOpid.name, cachedNameOpid.opid);
3867
3974
  if (done) {
3868
3975
  return done;
3869
3976
  }
3870
- return null;
3871
3977
  } else {
3872
- return null;
3978
+ const byQueueId = await db(historyTable).where({ opid: taskId }).orderBy("completed_at", "desc").first();
3979
+ if (byQueueId) {
3980
+ return byQueueId;
3981
+ }
3873
3982
  }
3874
3983
  await sleepMs(pollMs);
3875
3984
  }
@@ -4014,6 +4123,7 @@ var TasksManager = class _TasksManager {
4014
4123
  LOOP_RUNTIME_KEYS,
4015
4124
  SERVICE_TASK_NAMES,
4016
4125
  TaskGetLogs,
4126
+ TaskPauseRunner,
4017
4127
  TaskPing,
4018
4128
  TaskSampleProcess,
4019
4129
  TaskSetRuntimeParam,
@@ -4021,9 +4131,11 @@ var TasksManager = class _TasksManager {
4021
4131
  TaskStopRunner,
4022
4132
  TaskSumAB,
4023
4133
  TaskSystemInfo,
4134
+ TaskUnpauseRunner,
4024
4135
  TasksManager,
4025
4136
  TasksRegistry,
4026
4137
  appendTaskIpcLog,
4138
+ applyRunnerPaused,
4027
4139
  applyRuntimeParam,
4028
4140
  applyRuntimePatch,
4029
4141
  coerceRuntimeValue,