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