@nmakarov/cli-toolkit 0.72.0 → 0.75.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 +243 -4
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +243 -4
- package/dist/cli-runner.js.map +1 -1
- package/dist/index.cjs +242 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +238 -3
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +13 -1
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +13 -1
- package/dist/init.js.map +1 -1
- package/dist/tasks.cjs +240 -3
- package/dist/tasks.cjs.map +1 -1
- package/dist/tasks.js +236 -3
- package/dist/tasks.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5449,6 +5449,7 @@ function setup(opts = {}) {
|
|
|
5449
5449
|
const partialContext = {
|
|
5450
5450
|
emitter: new EventEmitter(),
|
|
5451
5451
|
isStop: () => false,
|
|
5452
|
+
isKill: () => false,
|
|
5452
5453
|
cleanupFunctions: [],
|
|
5453
5454
|
registerCleanup: (fn) => {
|
|
5454
5455
|
partialContext.cleanupFunctions.push(fn);
|
|
@@ -5470,6 +5471,7 @@ function setup(opts = {}) {
|
|
|
5470
5471
|
logger,
|
|
5471
5472
|
emitter: partialContext.emitter,
|
|
5472
5473
|
isStop: partialContext.isStop,
|
|
5474
|
+
isKill: partialContext.isKill,
|
|
5473
5475
|
cleanupFunctions: partialContext.cleanupFunctions,
|
|
5474
5476
|
registerCleanup: partialContext.registerCleanup,
|
|
5475
5477
|
// For long-running scripts (servers): with --showUsedParams=top, print
|
|
@@ -7683,6 +7685,8 @@ var AbstractTask = class _AbstractTask {
|
|
|
7683
7685
|
constructor(context, task) {
|
|
7684
7686
|
this.context = context;
|
|
7685
7687
|
this.task = task;
|
|
7688
|
+
this._stopRequested = false;
|
|
7689
|
+
this._pauseRequested = false;
|
|
7686
7690
|
}
|
|
7687
7691
|
/**
|
|
7688
7692
|
* Return a short reason string when the task should be deferred (e.g. "locked
|
|
@@ -7700,6 +7704,26 @@ var AbstractTask = class _AbstractTask {
|
|
|
7700
7704
|
* @param {number} [_allowanceMs] Grace period the runner promises before hard exit.
|
|
7701
7705
|
*/
|
|
7702
7706
|
requestStop(_allowanceMs) {
|
|
7707
|
+
this._stopRequested = true;
|
|
7708
|
+
}
|
|
7709
|
+
/**
|
|
7710
|
+
* Cooperative pause signal (from `pauseTask` control-lane task). Long-running
|
|
7711
|
+
* tasks should finish the current unit of work, persist a checkpoint, and
|
|
7712
|
+
* return `{ success: true, results: { taskPaused: true, … } }` so the runner
|
|
7713
|
+
* keeps the row as `status=paused` instead of deleting it.
|
|
7714
|
+
*
|
|
7715
|
+
* @param {number} [_allowanceMs]
|
|
7716
|
+
*/
|
|
7717
|
+
requestPause(_allowanceMs) {
|
|
7718
|
+
this._pauseRequested = true;
|
|
7719
|
+
}
|
|
7720
|
+
/** @returns {boolean} */
|
|
7721
|
+
isStopRequested() {
|
|
7722
|
+
return this._stopRequested === true;
|
|
7723
|
+
}
|
|
7724
|
+
/** @returns {boolean} */
|
|
7725
|
+
isPauseRequested() {
|
|
7726
|
+
return this._pauseRequested === true;
|
|
7703
7727
|
}
|
|
7704
7728
|
/**
|
|
7705
7729
|
* Perform the task. Must be implemented by subclasses.
|
|
@@ -8406,6 +8430,8 @@ var CONTROL_LANE_TASK_NAMES = [
|
|
|
8406
8430
|
"pause",
|
|
8407
8431
|
"unpauseRunner",
|
|
8408
8432
|
"unpause",
|
|
8433
|
+
"pauseTask",
|
|
8434
|
+
"resumeTask",
|
|
8409
8435
|
"setRuntimeParam",
|
|
8410
8436
|
"setRunnerParam"
|
|
8411
8437
|
];
|
|
@@ -8610,6 +8636,190 @@ var TaskUnpauseRunner = class extends AbstractTask {
|
|
|
8610
8636
|
}
|
|
8611
8637
|
};
|
|
8612
8638
|
|
|
8639
|
+
// src/tasks/coreTasks/TaskPauseTask.js
|
|
8640
|
+
function parseProgressObject(progress) {
|
|
8641
|
+
if (progress == null || progress === "") return {};
|
|
8642
|
+
if (typeof progress === "object" && !Array.isArray(progress)) {
|
|
8643
|
+
return { ...progress };
|
|
8644
|
+
}
|
|
8645
|
+
if (typeof progress === "string") {
|
|
8646
|
+
const t = progress.trim();
|
|
8647
|
+
if (t.startsWith("{") || t.startsWith("[")) {
|
|
8648
|
+
try {
|
|
8649
|
+
const parsed = JSON.parse(t);
|
|
8650
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return { ...parsed };
|
|
8651
|
+
} catch {
|
|
8652
|
+
}
|
|
8653
|
+
}
|
|
8654
|
+
return { message: progress };
|
|
8655
|
+
}
|
|
8656
|
+
return { message: String(progress) };
|
|
8657
|
+
}
|
|
8658
|
+
async function applyPauseTask(context, taskId) {
|
|
8659
|
+
const db = context.db;
|
|
8660
|
+
if (!db) throw new Error("pauseTask: context.db is required");
|
|
8661
|
+
const id = String(taskId ?? "").trim();
|
|
8662
|
+
if (!id) throw new ParamError('pauseTask: param "taskId" is required');
|
|
8663
|
+
const { tasksTable } = queueToTableNames(context.tasksQueueName ?? "tasks");
|
|
8664
|
+
const row = await db(tasksTable).where({ id }).first();
|
|
8665
|
+
if (!row) {
|
|
8666
|
+
return { success: false, results: { error: `Task ${id} not found`, taskId: id } };
|
|
8667
|
+
}
|
|
8668
|
+
if (row.status === "paused") {
|
|
8669
|
+
return {
|
|
8670
|
+
success: true,
|
|
8671
|
+
results: { taskId: id, status: "paused", message: "Task already paused" }
|
|
8672
|
+
};
|
|
8673
|
+
}
|
|
8674
|
+
if (row.status === "idle") {
|
|
8675
|
+
const progress = parseProgressObject(row.progress);
|
|
8676
|
+
progress.pauseRequested = true;
|
|
8677
|
+
progress.pausedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8678
|
+
await db(tasksTable).where({ id }).update({
|
|
8679
|
+
status: "paused",
|
|
8680
|
+
status_changed_at: db.fn.now(),
|
|
8681
|
+
progress: toJsonColumn(progress)
|
|
8682
|
+
});
|
|
8683
|
+
context.logger?.warn?.(`[pauseTask] idle task ${id} (${row.name}) \u2192 paused`);
|
|
8684
|
+
return {
|
|
8685
|
+
success: true,
|
|
8686
|
+
results: { taskId: id, status: "paused", message: "Idle task paused" }
|
|
8687
|
+
};
|
|
8688
|
+
}
|
|
8689
|
+
if (row.status === "running") {
|
|
8690
|
+
const progress = parseProgressObject(row.progress);
|
|
8691
|
+
progress.pauseRequested = true;
|
|
8692
|
+
progress.pauseRequestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8693
|
+
await db(tasksTable).where({ id }).update({
|
|
8694
|
+
progress: toJsonColumn(progress)
|
|
8695
|
+
});
|
|
8696
|
+
const inst = context.runningTaskInstances?.get?.(id);
|
|
8697
|
+
if (inst && typeof inst.requestPause === "function") {
|
|
8698
|
+
await inst.requestPause();
|
|
8699
|
+
context.logger?.warn?.(
|
|
8700
|
+
`[pauseTask] signaled running task ${id} (${row.name}) to pause after current unit`
|
|
8701
|
+
);
|
|
8702
|
+
return {
|
|
8703
|
+
success: true,
|
|
8704
|
+
results: {
|
|
8705
|
+
taskId: id,
|
|
8706
|
+
status: "running",
|
|
8707
|
+
signaled: true,
|
|
8708
|
+
message: "Pause requested; task will finish current unit then park as paused"
|
|
8709
|
+
}
|
|
8710
|
+
};
|
|
8711
|
+
}
|
|
8712
|
+
context.logger?.warn?.(
|
|
8713
|
+
`[pauseTask] stamped pauseRequested on ${id} (${row.name}) \u2014 not running in this process`
|
|
8714
|
+
);
|
|
8715
|
+
return {
|
|
8716
|
+
success: true,
|
|
8717
|
+
results: {
|
|
8718
|
+
taskId: id,
|
|
8719
|
+
status: "running",
|
|
8720
|
+
signaled: false,
|
|
8721
|
+
message: "pauseRequested stamped on task row; target runner must observe it (enqueue pauseTask on that service)"
|
|
8722
|
+
}
|
|
8723
|
+
};
|
|
8724
|
+
}
|
|
8725
|
+
return {
|
|
8726
|
+
success: false,
|
|
8727
|
+
results: {
|
|
8728
|
+
error: `Cannot pause task in status "${row.status}"`,
|
|
8729
|
+
taskId: id,
|
|
8730
|
+
status: row.status
|
|
8731
|
+
}
|
|
8732
|
+
};
|
|
8733
|
+
}
|
|
8734
|
+
async function applyResumeTask(context, taskId) {
|
|
8735
|
+
const db = context.db;
|
|
8736
|
+
if (!db) throw new Error("resumeTask: context.db is required");
|
|
8737
|
+
const id = String(taskId ?? "").trim();
|
|
8738
|
+
if (!id) throw new ParamError('resumeTask: param "taskId" is required');
|
|
8739
|
+
const { tasksTable } = queueToTableNames(context.tasksQueueName ?? "tasks");
|
|
8740
|
+
const row = await db(tasksTable).where({ id }).first();
|
|
8741
|
+
if (!row) {
|
|
8742
|
+
return { success: false, results: { error: `Task ${id} not found`, taskId: id } };
|
|
8743
|
+
}
|
|
8744
|
+
if (row.status === "idle") {
|
|
8745
|
+
return {
|
|
8746
|
+
success: true,
|
|
8747
|
+
results: { taskId: id, status: "idle", message: "Task already idle (runnable)" }
|
|
8748
|
+
};
|
|
8749
|
+
}
|
|
8750
|
+
if (row.status !== "paused") {
|
|
8751
|
+
return {
|
|
8752
|
+
success: false,
|
|
8753
|
+
results: {
|
|
8754
|
+
error: `Cannot resume task in status "${row.status}" (expected paused)`,
|
|
8755
|
+
taskId: id,
|
|
8756
|
+
status: row.status
|
|
8757
|
+
}
|
|
8758
|
+
};
|
|
8759
|
+
}
|
|
8760
|
+
const progress = parseProgressObject(row.progress);
|
|
8761
|
+
delete progress.pauseRequested;
|
|
8762
|
+
delete progress.pauseRequestedAt;
|
|
8763
|
+
delete progress.pausedAt;
|
|
8764
|
+
progress.resumedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8765
|
+
await db(tasksTable).where({ id }).update({
|
|
8766
|
+
status: "idle",
|
|
8767
|
+
status_changed_at: db.fn.now(),
|
|
8768
|
+
started_at: null,
|
|
8769
|
+
completed_at: null,
|
|
8770
|
+
success: null,
|
|
8771
|
+
progress: toJsonColumn(progress),
|
|
8772
|
+
// Clear claim binding so any matching worker can pick it up.
|
|
8773
|
+
service_name: null,
|
|
8774
|
+
server_name: null,
|
|
8775
|
+
instance_number: null,
|
|
8776
|
+
next_run_at: null
|
|
8777
|
+
});
|
|
8778
|
+
context.logger?.warn?.(`[resumeTask] paused task ${id} (${row.name}) \u2192 idle`);
|
|
8779
|
+
return {
|
|
8780
|
+
success: true,
|
|
8781
|
+
results: { taskId: id, status: "idle", message: "Task resumed (idle; will be claimed)" }
|
|
8782
|
+
};
|
|
8783
|
+
}
|
|
8784
|
+
var TaskPauseTask = class extends AbstractTask {
|
|
8785
|
+
static taskName = "pauseTask";
|
|
8786
|
+
static description = "Pause a specific task: finish current unit, persist status=paused";
|
|
8787
|
+
static defaultWaitForResult = true;
|
|
8788
|
+
static async resolveCustomParams(context, overrides = {}) {
|
|
8789
|
+
const merged = AbstractTask._mergeTypedParams(
|
|
8790
|
+
context,
|
|
8791
|
+
"task-pause-task",
|
|
8792
|
+
{ taskId: "string" },
|
|
8793
|
+
overrides
|
|
8794
|
+
);
|
|
8795
|
+
const taskId = typeof merged.taskId === "string" ? merged.taskId.trim() : "";
|
|
8796
|
+
if (!taskId) throw new ParamError('pauseTask: param "taskId" is required');
|
|
8797
|
+
return { taskId };
|
|
8798
|
+
}
|
|
8799
|
+
async run() {
|
|
8800
|
+
return applyPauseTask(this.context, this.task?.params?.taskId);
|
|
8801
|
+
}
|
|
8802
|
+
};
|
|
8803
|
+
var TaskResumeTask = class extends AbstractTask {
|
|
8804
|
+
static taskName = "resumeTask";
|
|
8805
|
+
static description = "Resume a paused task (status paused \u2192 idle)";
|
|
8806
|
+
static defaultWaitForResult = true;
|
|
8807
|
+
static async resolveCustomParams(context, overrides = {}) {
|
|
8808
|
+
const merged = AbstractTask._mergeTypedParams(
|
|
8809
|
+
context,
|
|
8810
|
+
"task-resume-task",
|
|
8811
|
+
{ taskId: "string" },
|
|
8812
|
+
overrides
|
|
8813
|
+
);
|
|
8814
|
+
const taskId = typeof merged.taskId === "string" ? merged.taskId.trim() : "";
|
|
8815
|
+
if (!taskId) throw new ParamError('resumeTask: param "taskId" is required');
|
|
8816
|
+
return { taskId };
|
|
8817
|
+
}
|
|
8818
|
+
async run() {
|
|
8819
|
+
return applyResumeTask(this.context, this.task?.params?.taskId);
|
|
8820
|
+
}
|
|
8821
|
+
};
|
|
8822
|
+
|
|
8613
8823
|
// src/tasks/coreTasks/TaskGetLogs.js
|
|
8614
8824
|
var TaskGetLogs = class extends AbstractTask {
|
|
8615
8825
|
/**
|
|
@@ -8828,7 +9038,7 @@ var TasksRegistry = class _TasksRegistry {
|
|
|
8828
9038
|
* @returns {TasksRegistry}
|
|
8829
9039
|
*/
|
|
8830
9040
|
static withCoreTasks() {
|
|
8831
|
-
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);
|
|
9041
|
+
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("pauseTask", TaskPauseTask).add("resumeTask", TaskResumeTask).add("getLogs", TaskGetLogs).add("setRuntimeParam", TaskSetRuntimeParam).add("setRunnerParam", TaskSetRuntimeParam);
|
|
8832
9042
|
}
|
|
8833
9043
|
/**
|
|
8834
9044
|
* Register a single task class under a name. Overwrites any previous entry.
|
|
@@ -8935,6 +9145,8 @@ var SERVICE_TASK_NAMES = [
|
|
|
8935
9145
|
"pauseRunner",
|
|
8936
9146
|
"unpause",
|
|
8937
9147
|
"unpauseRunner",
|
|
9148
|
+
"pauseTask",
|
|
9149
|
+
"resumeTask",
|
|
8938
9150
|
"shellCommand",
|
|
8939
9151
|
"systemInfo",
|
|
8940
9152
|
"info",
|
|
@@ -9230,11 +9442,12 @@ async function executeClaimedTask(context, tasksTable, historyTable, row, regist
|
|
|
9230
9442
|
} finally {
|
|
9231
9443
|
runningTaskInstances.delete(row.id);
|
|
9232
9444
|
}
|
|
9445
|
+
const taskPaused = success && results && typeof results === "object" && results.taskPaused === true;
|
|
9233
9446
|
await db(historyTable).insert(
|
|
9234
9447
|
taskHistoryInsertFromQueueRow(row, {
|
|
9235
9448
|
completed_at: /* @__PURE__ */ new Date(),
|
|
9236
9449
|
success,
|
|
9237
|
-
status: success ? "completed" : "failed",
|
|
9450
|
+
status: taskPaused ? "paused" : success ? "completed" : "failed",
|
|
9238
9451
|
status_changed_at: db.fn.now(),
|
|
9239
9452
|
params: toJsonColumn(row.params),
|
|
9240
9453
|
results: toJsonColumn(results)
|
|
@@ -9257,7 +9470,24 @@ async function executeClaimedTask(context, tasksTable, historyTable, row, regist
|
|
|
9257
9470
|
details: results
|
|
9258
9471
|
});
|
|
9259
9472
|
}
|
|
9260
|
-
if (
|
|
9473
|
+
if (taskPaused) {
|
|
9474
|
+
const checkpointParams = results.checkpointParams && typeof results.checkpointParams === "object" ? results.checkpointParams : row.params;
|
|
9475
|
+
const progressPayload = results.progress != null ? results.progress : { paused: true, pausedAt: (/* @__PURE__ */ new Date()).toISOString(), message: "paused" };
|
|
9476
|
+
await db(tasksTable).where({ id: row.id }).update({
|
|
9477
|
+
started_at: null,
|
|
9478
|
+
completed_at: /* @__PURE__ */ new Date(),
|
|
9479
|
+
success: true,
|
|
9480
|
+
results: toJsonColumn(results),
|
|
9481
|
+
params: toJsonColumn(checkpointParams),
|
|
9482
|
+
progress: toJsonColumn(progressPayload),
|
|
9483
|
+
status: "paused",
|
|
9484
|
+
status_changed_at: db.fn.now(),
|
|
9485
|
+
past_due: null,
|
|
9486
|
+
service_name: null,
|
|
9487
|
+
server_name: null,
|
|
9488
|
+
instance_number: null
|
|
9489
|
+
});
|
|
9490
|
+
} else if (row.schedule) {
|
|
9261
9491
|
if (success) {
|
|
9262
9492
|
let nextRunAt = null;
|
|
9263
9493
|
try {
|
|
@@ -9385,6 +9615,7 @@ async function runTasksLoop(context, options) {
|
|
|
9385
9615
|
}
|
|
9386
9616
|
const runningPromises = /* @__PURE__ */ new Set();
|
|
9387
9617
|
const runningTaskInstances = /* @__PURE__ */ new Map();
|
|
9618
|
+
context.runningTaskInstances = runningTaskInstances;
|
|
9388
9619
|
let runningControlPromise = null;
|
|
9389
9620
|
let stopRequested = false;
|
|
9390
9621
|
let stopAllowanceMs = Number(context.stopAllowanceMs) > 0 ? Number(context.stopAllowanceMs) : 6e4;
|
|
@@ -9772,7 +10003,9 @@ export {
|
|
|
9772
10003
|
ScrollableText,
|
|
9773
10004
|
TaskGetLogs,
|
|
9774
10005
|
TaskPauseRunner,
|
|
10006
|
+
TaskPauseTask,
|
|
9775
10007
|
TaskPing,
|
|
10008
|
+
TaskResumeTask,
|
|
9776
10009
|
TaskSampleProcess,
|
|
9777
10010
|
TaskSetRuntimeParam,
|
|
9778
10011
|
TaskShellCommand,
|
|
@@ -9787,6 +10020,8 @@ export {
|
|
|
9787
10020
|
activateRelease,
|
|
9788
10021
|
appendDeployLog,
|
|
9789
10022
|
appendTaskIpcLog,
|
|
10023
|
+
applyPauseTask,
|
|
10024
|
+
applyResumeTask,
|
|
9790
10025
|
applyRunnerPaused,
|
|
9791
10026
|
applyRuntimeParam,
|
|
9792
10027
|
applyRuntimePatch,
|