@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/cli-runner.cjs
CHANGED
|
@@ -5871,6 +5871,8 @@ var AbstractTask = class _AbstractTask {
|
|
|
5871
5871
|
constructor(context, task) {
|
|
5872
5872
|
this.context = context;
|
|
5873
5873
|
this.task = task;
|
|
5874
|
+
this._stopRequested = false;
|
|
5875
|
+
this._pauseRequested = false;
|
|
5874
5876
|
}
|
|
5875
5877
|
/**
|
|
5876
5878
|
* Return a short reason string when the task should be deferred (e.g. "locked
|
|
@@ -5888,6 +5890,26 @@ var AbstractTask = class _AbstractTask {
|
|
|
5888
5890
|
* @param {number} [_allowanceMs] Grace period the runner promises before hard exit.
|
|
5889
5891
|
*/
|
|
5890
5892
|
requestStop(_allowanceMs) {
|
|
5893
|
+
this._stopRequested = true;
|
|
5894
|
+
}
|
|
5895
|
+
/**
|
|
5896
|
+
* Cooperative pause signal (from `pauseTask` control-lane task). Long-running
|
|
5897
|
+
* tasks should finish the current unit of work, persist a checkpoint, and
|
|
5898
|
+
* return `{ success: true, results: { taskPaused: true, … } }` so the runner
|
|
5899
|
+
* keeps the row as `status=paused` instead of deleting it.
|
|
5900
|
+
*
|
|
5901
|
+
* @param {number} [_allowanceMs]
|
|
5902
|
+
*/
|
|
5903
|
+
requestPause(_allowanceMs) {
|
|
5904
|
+
this._pauseRequested = true;
|
|
5905
|
+
}
|
|
5906
|
+
/** @returns {boolean} */
|
|
5907
|
+
isStopRequested() {
|
|
5908
|
+
return this._stopRequested === true;
|
|
5909
|
+
}
|
|
5910
|
+
/** @returns {boolean} */
|
|
5911
|
+
isPauseRequested() {
|
|
5912
|
+
return this._pauseRequested === true;
|
|
5891
5913
|
}
|
|
5892
5914
|
/**
|
|
5893
5915
|
* Perform the task. Must be implemented by subclasses.
|
|
@@ -6586,6 +6608,8 @@ var CONTROL_LANE_TASK_NAMES = [
|
|
|
6586
6608
|
"pause",
|
|
6587
6609
|
"unpauseRunner",
|
|
6588
6610
|
"unpause",
|
|
6611
|
+
"pauseTask",
|
|
6612
|
+
"resumeTask",
|
|
6589
6613
|
"setRuntimeParam",
|
|
6590
6614
|
"setRunnerParam"
|
|
6591
6615
|
];
|
|
@@ -6790,6 +6814,190 @@ var TaskUnpauseRunner = class extends AbstractTask {
|
|
|
6790
6814
|
}
|
|
6791
6815
|
};
|
|
6792
6816
|
|
|
6817
|
+
// src/tasks/coreTasks/TaskPauseTask.js
|
|
6818
|
+
function parseProgressObject(progress) {
|
|
6819
|
+
if (progress == null || progress === "") return {};
|
|
6820
|
+
if (typeof progress === "object" && !Array.isArray(progress)) {
|
|
6821
|
+
return { ...progress };
|
|
6822
|
+
}
|
|
6823
|
+
if (typeof progress === "string") {
|
|
6824
|
+
const t = progress.trim();
|
|
6825
|
+
if (t.startsWith("{") || t.startsWith("[")) {
|
|
6826
|
+
try {
|
|
6827
|
+
const parsed = JSON.parse(t);
|
|
6828
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return { ...parsed };
|
|
6829
|
+
} catch {
|
|
6830
|
+
}
|
|
6831
|
+
}
|
|
6832
|
+
return { message: progress };
|
|
6833
|
+
}
|
|
6834
|
+
return { message: String(progress) };
|
|
6835
|
+
}
|
|
6836
|
+
async function applyPauseTask(context, taskId) {
|
|
6837
|
+
const db = context.db;
|
|
6838
|
+
if (!db) throw new Error("pauseTask: context.db is required");
|
|
6839
|
+
const id = String(taskId ?? "").trim();
|
|
6840
|
+
if (!id) throw new ParamError('pauseTask: param "taskId" is required');
|
|
6841
|
+
const { tasksTable } = queueToTableNames(context.tasksQueueName ?? "tasks");
|
|
6842
|
+
const row = await db(tasksTable).where({ id }).first();
|
|
6843
|
+
if (!row) {
|
|
6844
|
+
return { success: false, results: { error: `Task ${id} not found`, taskId: id } };
|
|
6845
|
+
}
|
|
6846
|
+
if (row.status === "paused") {
|
|
6847
|
+
return {
|
|
6848
|
+
success: true,
|
|
6849
|
+
results: { taskId: id, status: "paused", message: "Task already paused" }
|
|
6850
|
+
};
|
|
6851
|
+
}
|
|
6852
|
+
if (row.status === "idle") {
|
|
6853
|
+
const progress = parseProgressObject(row.progress);
|
|
6854
|
+
progress.pauseRequested = true;
|
|
6855
|
+
progress.pausedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6856
|
+
await db(tasksTable).where({ id }).update({
|
|
6857
|
+
status: "paused",
|
|
6858
|
+
status_changed_at: db.fn.now(),
|
|
6859
|
+
progress: toJsonColumn(progress)
|
|
6860
|
+
});
|
|
6861
|
+
context.logger?.warn?.(`[pauseTask] idle task ${id} (${row.name}) \u2192 paused`);
|
|
6862
|
+
return {
|
|
6863
|
+
success: true,
|
|
6864
|
+
results: { taskId: id, status: "paused", message: "Idle task paused" }
|
|
6865
|
+
};
|
|
6866
|
+
}
|
|
6867
|
+
if (row.status === "running") {
|
|
6868
|
+
const progress = parseProgressObject(row.progress);
|
|
6869
|
+
progress.pauseRequested = true;
|
|
6870
|
+
progress.pauseRequestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6871
|
+
await db(tasksTable).where({ id }).update({
|
|
6872
|
+
progress: toJsonColumn(progress)
|
|
6873
|
+
});
|
|
6874
|
+
const inst = context.runningTaskInstances?.get?.(id);
|
|
6875
|
+
if (inst && typeof inst.requestPause === "function") {
|
|
6876
|
+
await inst.requestPause();
|
|
6877
|
+
context.logger?.warn?.(
|
|
6878
|
+
`[pauseTask] signaled running task ${id} (${row.name}) to pause after current unit`
|
|
6879
|
+
);
|
|
6880
|
+
return {
|
|
6881
|
+
success: true,
|
|
6882
|
+
results: {
|
|
6883
|
+
taskId: id,
|
|
6884
|
+
status: "running",
|
|
6885
|
+
signaled: true,
|
|
6886
|
+
message: "Pause requested; task will finish current unit then park as paused"
|
|
6887
|
+
}
|
|
6888
|
+
};
|
|
6889
|
+
}
|
|
6890
|
+
context.logger?.warn?.(
|
|
6891
|
+
`[pauseTask] stamped pauseRequested on ${id} (${row.name}) \u2014 not running in this process`
|
|
6892
|
+
);
|
|
6893
|
+
return {
|
|
6894
|
+
success: true,
|
|
6895
|
+
results: {
|
|
6896
|
+
taskId: id,
|
|
6897
|
+
status: "running",
|
|
6898
|
+
signaled: false,
|
|
6899
|
+
message: "pauseRequested stamped on task row; target runner must observe it (enqueue pauseTask on that service)"
|
|
6900
|
+
}
|
|
6901
|
+
};
|
|
6902
|
+
}
|
|
6903
|
+
return {
|
|
6904
|
+
success: false,
|
|
6905
|
+
results: {
|
|
6906
|
+
error: `Cannot pause task in status "${row.status}"`,
|
|
6907
|
+
taskId: id,
|
|
6908
|
+
status: row.status
|
|
6909
|
+
}
|
|
6910
|
+
};
|
|
6911
|
+
}
|
|
6912
|
+
async function applyResumeTask(context, taskId) {
|
|
6913
|
+
const db = context.db;
|
|
6914
|
+
if (!db) throw new Error("resumeTask: context.db is required");
|
|
6915
|
+
const id = String(taskId ?? "").trim();
|
|
6916
|
+
if (!id) throw new ParamError('resumeTask: param "taskId" is required');
|
|
6917
|
+
const { tasksTable } = queueToTableNames(context.tasksQueueName ?? "tasks");
|
|
6918
|
+
const row = await db(tasksTable).where({ id }).first();
|
|
6919
|
+
if (!row) {
|
|
6920
|
+
return { success: false, results: { error: `Task ${id} not found`, taskId: id } };
|
|
6921
|
+
}
|
|
6922
|
+
if (row.status === "idle") {
|
|
6923
|
+
return {
|
|
6924
|
+
success: true,
|
|
6925
|
+
results: { taskId: id, status: "idle", message: "Task already idle (runnable)" }
|
|
6926
|
+
};
|
|
6927
|
+
}
|
|
6928
|
+
if (row.status !== "paused") {
|
|
6929
|
+
return {
|
|
6930
|
+
success: false,
|
|
6931
|
+
results: {
|
|
6932
|
+
error: `Cannot resume task in status "${row.status}" (expected paused)`,
|
|
6933
|
+
taskId: id,
|
|
6934
|
+
status: row.status
|
|
6935
|
+
}
|
|
6936
|
+
};
|
|
6937
|
+
}
|
|
6938
|
+
const progress = parseProgressObject(row.progress);
|
|
6939
|
+
delete progress.pauseRequested;
|
|
6940
|
+
delete progress.pauseRequestedAt;
|
|
6941
|
+
delete progress.pausedAt;
|
|
6942
|
+
progress.resumedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6943
|
+
await db(tasksTable).where({ id }).update({
|
|
6944
|
+
status: "idle",
|
|
6945
|
+
status_changed_at: db.fn.now(),
|
|
6946
|
+
started_at: null,
|
|
6947
|
+
completed_at: null,
|
|
6948
|
+
success: null,
|
|
6949
|
+
progress: toJsonColumn(progress),
|
|
6950
|
+
// Clear claim binding so any matching worker can pick it up.
|
|
6951
|
+
service_name: null,
|
|
6952
|
+
server_name: null,
|
|
6953
|
+
instance_number: null,
|
|
6954
|
+
next_run_at: null
|
|
6955
|
+
});
|
|
6956
|
+
context.logger?.warn?.(`[resumeTask] paused task ${id} (${row.name}) \u2192 idle`);
|
|
6957
|
+
return {
|
|
6958
|
+
success: true,
|
|
6959
|
+
results: { taskId: id, status: "idle", message: "Task resumed (idle; will be claimed)" }
|
|
6960
|
+
};
|
|
6961
|
+
}
|
|
6962
|
+
var TaskPauseTask = class extends AbstractTask {
|
|
6963
|
+
static taskName = "pauseTask";
|
|
6964
|
+
static description = "Pause a specific task: finish current unit, persist status=paused";
|
|
6965
|
+
static defaultWaitForResult = true;
|
|
6966
|
+
static async resolveCustomParams(context, overrides = {}) {
|
|
6967
|
+
const merged = AbstractTask._mergeTypedParams(
|
|
6968
|
+
context,
|
|
6969
|
+
"task-pause-task",
|
|
6970
|
+
{ taskId: "string" },
|
|
6971
|
+
overrides
|
|
6972
|
+
);
|
|
6973
|
+
const taskId = typeof merged.taskId === "string" ? merged.taskId.trim() : "";
|
|
6974
|
+
if (!taskId) throw new ParamError('pauseTask: param "taskId" is required');
|
|
6975
|
+
return { taskId };
|
|
6976
|
+
}
|
|
6977
|
+
async run() {
|
|
6978
|
+
return applyPauseTask(this.context, this.task?.params?.taskId);
|
|
6979
|
+
}
|
|
6980
|
+
};
|
|
6981
|
+
var TaskResumeTask = class extends AbstractTask {
|
|
6982
|
+
static taskName = "resumeTask";
|
|
6983
|
+
static description = "Resume a paused task (status paused \u2192 idle)";
|
|
6984
|
+
static defaultWaitForResult = true;
|
|
6985
|
+
static async resolveCustomParams(context, overrides = {}) {
|
|
6986
|
+
const merged = AbstractTask._mergeTypedParams(
|
|
6987
|
+
context,
|
|
6988
|
+
"task-resume-task",
|
|
6989
|
+
{ taskId: "string" },
|
|
6990
|
+
overrides
|
|
6991
|
+
);
|
|
6992
|
+
const taskId = typeof merged.taskId === "string" ? merged.taskId.trim() : "";
|
|
6993
|
+
if (!taskId) throw new ParamError('resumeTask: param "taskId" is required');
|
|
6994
|
+
return { taskId };
|
|
6995
|
+
}
|
|
6996
|
+
async run() {
|
|
6997
|
+
return applyResumeTask(this.context, this.task?.params?.taskId);
|
|
6998
|
+
}
|
|
6999
|
+
};
|
|
7000
|
+
|
|
6793
7001
|
// src/tasks/coreTasks/TaskGetLogs.js
|
|
6794
7002
|
var TaskGetLogs = class extends AbstractTask {
|
|
6795
7003
|
/**
|
|
@@ -7008,7 +7216,7 @@ var TasksRegistry = class _TasksRegistry {
|
|
|
7008
7216
|
* @returns {TasksRegistry}
|
|
7009
7217
|
*/
|
|
7010
7218
|
static withCoreTasks() {
|
|
7011
|
-
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);
|
|
7219
|
+
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);
|
|
7012
7220
|
}
|
|
7013
7221
|
/**
|
|
7014
7222
|
* Register a single task class under a name. Overwrites any previous entry.
|
|
@@ -7200,11 +7408,12 @@ async function executeClaimedTask(context, tasksTable, historyTable, row, regist
|
|
|
7200
7408
|
} finally {
|
|
7201
7409
|
runningTaskInstances.delete(row.id);
|
|
7202
7410
|
}
|
|
7411
|
+
const taskPaused = success && results && typeof results === "object" && results.taskPaused === true;
|
|
7203
7412
|
await db(historyTable).insert(
|
|
7204
7413
|
taskHistoryInsertFromQueueRow(row, {
|
|
7205
7414
|
completed_at: /* @__PURE__ */ new Date(),
|
|
7206
7415
|
success,
|
|
7207
|
-
status: success ? "completed" : "failed",
|
|
7416
|
+
status: taskPaused ? "paused" : success ? "completed" : "failed",
|
|
7208
7417
|
status_changed_at: db.fn.now(),
|
|
7209
7418
|
params: toJsonColumn(row.params),
|
|
7210
7419
|
results: toJsonColumn(results)
|
|
@@ -7227,7 +7436,24 @@ async function executeClaimedTask(context, tasksTable, historyTable, row, regist
|
|
|
7227
7436
|
details: results
|
|
7228
7437
|
});
|
|
7229
7438
|
}
|
|
7230
|
-
if (
|
|
7439
|
+
if (taskPaused) {
|
|
7440
|
+
const checkpointParams = results.checkpointParams && typeof results.checkpointParams === "object" ? results.checkpointParams : row.params;
|
|
7441
|
+
const progressPayload = results.progress != null ? results.progress : { paused: true, pausedAt: (/* @__PURE__ */ new Date()).toISOString(), message: "paused" };
|
|
7442
|
+
await db(tasksTable).where({ id: row.id }).update({
|
|
7443
|
+
started_at: null,
|
|
7444
|
+
completed_at: /* @__PURE__ */ new Date(),
|
|
7445
|
+
success: true,
|
|
7446
|
+
results: toJsonColumn(results),
|
|
7447
|
+
params: toJsonColumn(checkpointParams),
|
|
7448
|
+
progress: toJsonColumn(progressPayload),
|
|
7449
|
+
status: "paused",
|
|
7450
|
+
status_changed_at: db.fn.now(),
|
|
7451
|
+
past_due: null,
|
|
7452
|
+
service_name: null,
|
|
7453
|
+
server_name: null,
|
|
7454
|
+
instance_number: null
|
|
7455
|
+
});
|
|
7456
|
+
} else if (row.schedule) {
|
|
7231
7457
|
if (success) {
|
|
7232
7458
|
let nextRunAt = null;
|
|
7233
7459
|
try {
|
|
@@ -7355,6 +7581,7 @@ async function runTasksLoop(context, options) {
|
|
|
7355
7581
|
}
|
|
7356
7582
|
const runningPromises = /* @__PURE__ */ new Set();
|
|
7357
7583
|
const runningTaskInstances = /* @__PURE__ */ new Map();
|
|
7584
|
+
context.runningTaskInstances = runningTaskInstances;
|
|
7358
7585
|
let runningControlPromise = null;
|
|
7359
7586
|
let stopRequested = false;
|
|
7360
7587
|
let stopAllowanceMs = Number(context.stopAllowanceMs) > 0 ? Number(context.stopAllowanceMs) : 6e4;
|