@nmakarov/cli-toolkit 0.71.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 +233 -3
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +233 -3
- package/dist/cli-runner.js.map +1 -1
- package/dist/filedatabase.cjs.map +1 -1
- package/dist/filedatabase.js.map +1 -1
- package/dist/index.cjs +243 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +239 -3
- package/dist/index.js.map +1 -1
- package/dist/tasks.cjs +243 -3
- package/dist/tasks.cjs.map +1 -1
- package/dist/tasks.js +239 -3
- package/dist/tasks.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7230,6 +7230,9 @@ var DEFAULT_GROUP_MAX_INSTANCES = {
|
|
|
7230
7230
|
harvest: 1,
|
|
7231
7231
|
harvester: 0,
|
|
7232
7232
|
loader: 0,
|
|
7233
|
+
toolkit: 0,
|
|
7234
|
+
photocare: 0,
|
|
7235
|
+
/** @deprecated use photocare */
|
|
7233
7236
|
photos: 0,
|
|
7234
7237
|
photosprocessor: 0,
|
|
7235
7238
|
ingest: 0
|
|
@@ -7680,6 +7683,8 @@ var AbstractTask = class _AbstractTask {
|
|
|
7680
7683
|
constructor(context, task) {
|
|
7681
7684
|
this.context = context;
|
|
7682
7685
|
this.task = task;
|
|
7686
|
+
this._stopRequested = false;
|
|
7687
|
+
this._pauseRequested = false;
|
|
7683
7688
|
}
|
|
7684
7689
|
/**
|
|
7685
7690
|
* Return a short reason string when the task should be deferred (e.g. "locked
|
|
@@ -7697,6 +7702,26 @@ var AbstractTask = class _AbstractTask {
|
|
|
7697
7702
|
* @param {number} [_allowanceMs] Grace period the runner promises before hard exit.
|
|
7698
7703
|
*/
|
|
7699
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;
|
|
7700
7725
|
}
|
|
7701
7726
|
/**
|
|
7702
7727
|
* Perform the task. Must be implemented by subclasses.
|
|
@@ -8403,6 +8428,8 @@ var CONTROL_LANE_TASK_NAMES = [
|
|
|
8403
8428
|
"pause",
|
|
8404
8429
|
"unpauseRunner",
|
|
8405
8430
|
"unpause",
|
|
8431
|
+
"pauseTask",
|
|
8432
|
+
"resumeTask",
|
|
8406
8433
|
"setRuntimeParam",
|
|
8407
8434
|
"setRunnerParam"
|
|
8408
8435
|
];
|
|
@@ -8607,6 +8634,190 @@ var TaskUnpauseRunner = class extends AbstractTask {
|
|
|
8607
8634
|
}
|
|
8608
8635
|
};
|
|
8609
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
|
+
|
|
8610
8821
|
// src/tasks/coreTasks/TaskGetLogs.js
|
|
8611
8822
|
var TaskGetLogs = class extends AbstractTask {
|
|
8612
8823
|
/**
|
|
@@ -8825,7 +9036,7 @@ var TasksRegistry = class _TasksRegistry {
|
|
|
8825
9036
|
* @returns {TasksRegistry}
|
|
8826
9037
|
*/
|
|
8827
9038
|
static withCoreTasks() {
|
|
8828
|
-
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);
|
|
8829
9040
|
}
|
|
8830
9041
|
/**
|
|
8831
9042
|
* Register a single task class under a name. Overwrites any previous entry.
|
|
@@ -8932,6 +9143,8 @@ var SERVICE_TASK_NAMES = [
|
|
|
8932
9143
|
"pauseRunner",
|
|
8933
9144
|
"unpause",
|
|
8934
9145
|
"unpauseRunner",
|
|
9146
|
+
"pauseTask",
|
|
9147
|
+
"resumeTask",
|
|
8935
9148
|
"shellCommand",
|
|
8936
9149
|
"systemInfo",
|
|
8937
9150
|
"info",
|
|
@@ -9227,11 +9440,12 @@ async function executeClaimedTask(context, tasksTable, historyTable, row, regist
|
|
|
9227
9440
|
} finally {
|
|
9228
9441
|
runningTaskInstances.delete(row.id);
|
|
9229
9442
|
}
|
|
9443
|
+
const taskPaused = success && results && typeof results === "object" && results.taskPaused === true;
|
|
9230
9444
|
await db(historyTable).insert(
|
|
9231
9445
|
taskHistoryInsertFromQueueRow(row, {
|
|
9232
9446
|
completed_at: /* @__PURE__ */ new Date(),
|
|
9233
9447
|
success,
|
|
9234
|
-
status: success ? "completed" : "failed",
|
|
9448
|
+
status: taskPaused ? "paused" : success ? "completed" : "failed",
|
|
9235
9449
|
status_changed_at: db.fn.now(),
|
|
9236
9450
|
params: toJsonColumn(row.params),
|
|
9237
9451
|
results: toJsonColumn(results)
|
|
@@ -9254,7 +9468,24 @@ async function executeClaimedTask(context, tasksTable, historyTable, row, regist
|
|
|
9254
9468
|
details: results
|
|
9255
9469
|
});
|
|
9256
9470
|
}
|
|
9257
|
-
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) {
|
|
9258
9489
|
if (success) {
|
|
9259
9490
|
let nextRunAt = null;
|
|
9260
9491
|
try {
|
|
@@ -9382,6 +9613,7 @@ async function runTasksLoop(context, options) {
|
|
|
9382
9613
|
}
|
|
9383
9614
|
const runningPromises = /* @__PURE__ */ new Set();
|
|
9384
9615
|
const runningTaskInstances = /* @__PURE__ */ new Map();
|
|
9616
|
+
context.runningTaskInstances = runningTaskInstances;
|
|
9385
9617
|
let runningControlPromise = null;
|
|
9386
9618
|
let stopRequested = false;
|
|
9387
9619
|
let stopAllowanceMs = Number(context.stopAllowanceMs) > 0 ? Number(context.stopAllowanceMs) : 6e4;
|
|
@@ -9769,7 +10001,9 @@ export {
|
|
|
9769
10001
|
ScrollableText,
|
|
9770
10002
|
TaskGetLogs,
|
|
9771
10003
|
TaskPauseRunner,
|
|
10004
|
+
TaskPauseTask,
|
|
9772
10005
|
TaskPing,
|
|
10006
|
+
TaskResumeTask,
|
|
9773
10007
|
TaskSampleProcess,
|
|
9774
10008
|
TaskSetRuntimeParam,
|
|
9775
10009
|
TaskShellCommand,
|
|
@@ -9784,6 +10018,8 @@ export {
|
|
|
9784
10018
|
activateRelease,
|
|
9785
10019
|
appendDeployLog,
|
|
9786
10020
|
appendTaskIpcLog,
|
|
10021
|
+
applyPauseTask,
|
|
10022
|
+
applyResumeTask,
|
|
9787
10023
|
applyRunnerPaused,
|
|
9788
10024
|
applyRuntimeParam,
|
|
9789
10025
|
applyRuntimePatch,
|