@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/cli-runner.js
CHANGED
|
@@ -4365,6 +4365,9 @@ var DEFAULT_GROUP_MAX_INSTANCES = {
|
|
|
4365
4365
|
harvest: 1,
|
|
4366
4366
|
harvester: 0,
|
|
4367
4367
|
loader: 0,
|
|
4368
|
+
toolkit: 0,
|
|
4369
|
+
photocare: 0,
|
|
4370
|
+
/** @deprecated use photocare */
|
|
4368
4371
|
photos: 0,
|
|
4369
4372
|
photosprocessor: 0,
|
|
4370
4373
|
ingest: 0
|
|
@@ -5852,6 +5855,8 @@ var AbstractTask = class _AbstractTask {
|
|
|
5852
5855
|
constructor(context, task) {
|
|
5853
5856
|
this.context = context;
|
|
5854
5857
|
this.task = task;
|
|
5858
|
+
this._stopRequested = false;
|
|
5859
|
+
this._pauseRequested = false;
|
|
5855
5860
|
}
|
|
5856
5861
|
/**
|
|
5857
5862
|
* Return a short reason string when the task should be deferred (e.g. "locked
|
|
@@ -5869,6 +5874,26 @@ var AbstractTask = class _AbstractTask {
|
|
|
5869
5874
|
* @param {number} [_allowanceMs] Grace period the runner promises before hard exit.
|
|
5870
5875
|
*/
|
|
5871
5876
|
requestStop(_allowanceMs) {
|
|
5877
|
+
this._stopRequested = true;
|
|
5878
|
+
}
|
|
5879
|
+
/**
|
|
5880
|
+
* Cooperative pause signal (from `pauseTask` control-lane task). Long-running
|
|
5881
|
+
* tasks should finish the current unit of work, persist a checkpoint, and
|
|
5882
|
+
* return `{ success: true, results: { taskPaused: true, … } }` so the runner
|
|
5883
|
+
* keeps the row as `status=paused` instead of deleting it.
|
|
5884
|
+
*
|
|
5885
|
+
* @param {number} [_allowanceMs]
|
|
5886
|
+
*/
|
|
5887
|
+
requestPause(_allowanceMs) {
|
|
5888
|
+
this._pauseRequested = true;
|
|
5889
|
+
}
|
|
5890
|
+
/** @returns {boolean} */
|
|
5891
|
+
isStopRequested() {
|
|
5892
|
+
return this._stopRequested === true;
|
|
5893
|
+
}
|
|
5894
|
+
/** @returns {boolean} */
|
|
5895
|
+
isPauseRequested() {
|
|
5896
|
+
return this._pauseRequested === true;
|
|
5872
5897
|
}
|
|
5873
5898
|
/**
|
|
5874
5899
|
* Perform the task. Must be implemented by subclasses.
|
|
@@ -6567,6 +6592,8 @@ var CONTROL_LANE_TASK_NAMES = [
|
|
|
6567
6592
|
"pause",
|
|
6568
6593
|
"unpauseRunner",
|
|
6569
6594
|
"unpause",
|
|
6595
|
+
"pauseTask",
|
|
6596
|
+
"resumeTask",
|
|
6570
6597
|
"setRuntimeParam",
|
|
6571
6598
|
"setRunnerParam"
|
|
6572
6599
|
];
|
|
@@ -6771,6 +6798,190 @@ var TaskUnpauseRunner = class extends AbstractTask {
|
|
|
6771
6798
|
}
|
|
6772
6799
|
};
|
|
6773
6800
|
|
|
6801
|
+
// src/tasks/coreTasks/TaskPauseTask.js
|
|
6802
|
+
function parseProgressObject(progress) {
|
|
6803
|
+
if (progress == null || progress === "") return {};
|
|
6804
|
+
if (typeof progress === "object" && !Array.isArray(progress)) {
|
|
6805
|
+
return { ...progress };
|
|
6806
|
+
}
|
|
6807
|
+
if (typeof progress === "string") {
|
|
6808
|
+
const t = progress.trim();
|
|
6809
|
+
if (t.startsWith("{") || t.startsWith("[")) {
|
|
6810
|
+
try {
|
|
6811
|
+
const parsed = JSON.parse(t);
|
|
6812
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return { ...parsed };
|
|
6813
|
+
} catch {
|
|
6814
|
+
}
|
|
6815
|
+
}
|
|
6816
|
+
return { message: progress };
|
|
6817
|
+
}
|
|
6818
|
+
return { message: String(progress) };
|
|
6819
|
+
}
|
|
6820
|
+
async function applyPauseTask(context, taskId) {
|
|
6821
|
+
const db = context.db;
|
|
6822
|
+
if (!db) throw new Error("pauseTask: context.db is required");
|
|
6823
|
+
const id = String(taskId ?? "").trim();
|
|
6824
|
+
if (!id) throw new ParamError('pauseTask: param "taskId" is required');
|
|
6825
|
+
const { tasksTable } = queueToTableNames(context.tasksQueueName ?? "tasks");
|
|
6826
|
+
const row = await db(tasksTable).where({ id }).first();
|
|
6827
|
+
if (!row) {
|
|
6828
|
+
return { success: false, results: { error: `Task ${id} not found`, taskId: id } };
|
|
6829
|
+
}
|
|
6830
|
+
if (row.status === "paused") {
|
|
6831
|
+
return {
|
|
6832
|
+
success: true,
|
|
6833
|
+
results: { taskId: id, status: "paused", message: "Task already paused" }
|
|
6834
|
+
};
|
|
6835
|
+
}
|
|
6836
|
+
if (row.status === "idle") {
|
|
6837
|
+
const progress = parseProgressObject(row.progress);
|
|
6838
|
+
progress.pauseRequested = true;
|
|
6839
|
+
progress.pausedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6840
|
+
await db(tasksTable).where({ id }).update({
|
|
6841
|
+
status: "paused",
|
|
6842
|
+
status_changed_at: db.fn.now(),
|
|
6843
|
+
progress: toJsonColumn(progress)
|
|
6844
|
+
});
|
|
6845
|
+
context.logger?.warn?.(`[pauseTask] idle task ${id} (${row.name}) \u2192 paused`);
|
|
6846
|
+
return {
|
|
6847
|
+
success: true,
|
|
6848
|
+
results: { taskId: id, status: "paused", message: "Idle task paused" }
|
|
6849
|
+
};
|
|
6850
|
+
}
|
|
6851
|
+
if (row.status === "running") {
|
|
6852
|
+
const progress = parseProgressObject(row.progress);
|
|
6853
|
+
progress.pauseRequested = true;
|
|
6854
|
+
progress.pauseRequestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6855
|
+
await db(tasksTable).where({ id }).update({
|
|
6856
|
+
progress: toJsonColumn(progress)
|
|
6857
|
+
});
|
|
6858
|
+
const inst = context.runningTaskInstances?.get?.(id);
|
|
6859
|
+
if (inst && typeof inst.requestPause === "function") {
|
|
6860
|
+
await inst.requestPause();
|
|
6861
|
+
context.logger?.warn?.(
|
|
6862
|
+
`[pauseTask] signaled running task ${id} (${row.name}) to pause after current unit`
|
|
6863
|
+
);
|
|
6864
|
+
return {
|
|
6865
|
+
success: true,
|
|
6866
|
+
results: {
|
|
6867
|
+
taskId: id,
|
|
6868
|
+
status: "running",
|
|
6869
|
+
signaled: true,
|
|
6870
|
+
message: "Pause requested; task will finish current unit then park as paused"
|
|
6871
|
+
}
|
|
6872
|
+
};
|
|
6873
|
+
}
|
|
6874
|
+
context.logger?.warn?.(
|
|
6875
|
+
`[pauseTask] stamped pauseRequested on ${id} (${row.name}) \u2014 not running in this process`
|
|
6876
|
+
);
|
|
6877
|
+
return {
|
|
6878
|
+
success: true,
|
|
6879
|
+
results: {
|
|
6880
|
+
taskId: id,
|
|
6881
|
+
status: "running",
|
|
6882
|
+
signaled: false,
|
|
6883
|
+
message: "pauseRequested stamped on task row; target runner must observe it (enqueue pauseTask on that service)"
|
|
6884
|
+
}
|
|
6885
|
+
};
|
|
6886
|
+
}
|
|
6887
|
+
return {
|
|
6888
|
+
success: false,
|
|
6889
|
+
results: {
|
|
6890
|
+
error: `Cannot pause task in status "${row.status}"`,
|
|
6891
|
+
taskId: id,
|
|
6892
|
+
status: row.status
|
|
6893
|
+
}
|
|
6894
|
+
};
|
|
6895
|
+
}
|
|
6896
|
+
async function applyResumeTask(context, taskId) {
|
|
6897
|
+
const db = context.db;
|
|
6898
|
+
if (!db) throw new Error("resumeTask: context.db is required");
|
|
6899
|
+
const id = String(taskId ?? "").trim();
|
|
6900
|
+
if (!id) throw new ParamError('resumeTask: param "taskId" is required');
|
|
6901
|
+
const { tasksTable } = queueToTableNames(context.tasksQueueName ?? "tasks");
|
|
6902
|
+
const row = await db(tasksTable).where({ id }).first();
|
|
6903
|
+
if (!row) {
|
|
6904
|
+
return { success: false, results: { error: `Task ${id} not found`, taskId: id } };
|
|
6905
|
+
}
|
|
6906
|
+
if (row.status === "idle") {
|
|
6907
|
+
return {
|
|
6908
|
+
success: true,
|
|
6909
|
+
results: { taskId: id, status: "idle", message: "Task already idle (runnable)" }
|
|
6910
|
+
};
|
|
6911
|
+
}
|
|
6912
|
+
if (row.status !== "paused") {
|
|
6913
|
+
return {
|
|
6914
|
+
success: false,
|
|
6915
|
+
results: {
|
|
6916
|
+
error: `Cannot resume task in status "${row.status}" (expected paused)`,
|
|
6917
|
+
taskId: id,
|
|
6918
|
+
status: row.status
|
|
6919
|
+
}
|
|
6920
|
+
};
|
|
6921
|
+
}
|
|
6922
|
+
const progress = parseProgressObject(row.progress);
|
|
6923
|
+
delete progress.pauseRequested;
|
|
6924
|
+
delete progress.pauseRequestedAt;
|
|
6925
|
+
delete progress.pausedAt;
|
|
6926
|
+
progress.resumedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6927
|
+
await db(tasksTable).where({ id }).update({
|
|
6928
|
+
status: "idle",
|
|
6929
|
+
status_changed_at: db.fn.now(),
|
|
6930
|
+
started_at: null,
|
|
6931
|
+
completed_at: null,
|
|
6932
|
+
success: null,
|
|
6933
|
+
progress: toJsonColumn(progress),
|
|
6934
|
+
// Clear claim binding so any matching worker can pick it up.
|
|
6935
|
+
service_name: null,
|
|
6936
|
+
server_name: null,
|
|
6937
|
+
instance_number: null,
|
|
6938
|
+
next_run_at: null
|
|
6939
|
+
});
|
|
6940
|
+
context.logger?.warn?.(`[resumeTask] paused task ${id} (${row.name}) \u2192 idle`);
|
|
6941
|
+
return {
|
|
6942
|
+
success: true,
|
|
6943
|
+
results: { taskId: id, status: "idle", message: "Task resumed (idle; will be claimed)" }
|
|
6944
|
+
};
|
|
6945
|
+
}
|
|
6946
|
+
var TaskPauseTask = class extends AbstractTask {
|
|
6947
|
+
static taskName = "pauseTask";
|
|
6948
|
+
static description = "Pause a specific task: finish current unit, persist status=paused";
|
|
6949
|
+
static defaultWaitForResult = true;
|
|
6950
|
+
static async resolveCustomParams(context, overrides = {}) {
|
|
6951
|
+
const merged = AbstractTask._mergeTypedParams(
|
|
6952
|
+
context,
|
|
6953
|
+
"task-pause-task",
|
|
6954
|
+
{ taskId: "string" },
|
|
6955
|
+
overrides
|
|
6956
|
+
);
|
|
6957
|
+
const taskId = typeof merged.taskId === "string" ? merged.taskId.trim() : "";
|
|
6958
|
+
if (!taskId) throw new ParamError('pauseTask: param "taskId" is required');
|
|
6959
|
+
return { taskId };
|
|
6960
|
+
}
|
|
6961
|
+
async run() {
|
|
6962
|
+
return applyPauseTask(this.context, this.task?.params?.taskId);
|
|
6963
|
+
}
|
|
6964
|
+
};
|
|
6965
|
+
var TaskResumeTask = class extends AbstractTask {
|
|
6966
|
+
static taskName = "resumeTask";
|
|
6967
|
+
static description = "Resume a paused task (status paused \u2192 idle)";
|
|
6968
|
+
static defaultWaitForResult = true;
|
|
6969
|
+
static async resolveCustomParams(context, overrides = {}) {
|
|
6970
|
+
const merged = AbstractTask._mergeTypedParams(
|
|
6971
|
+
context,
|
|
6972
|
+
"task-resume-task",
|
|
6973
|
+
{ taskId: "string" },
|
|
6974
|
+
overrides
|
|
6975
|
+
);
|
|
6976
|
+
const taskId = typeof merged.taskId === "string" ? merged.taskId.trim() : "";
|
|
6977
|
+
if (!taskId) throw new ParamError('resumeTask: param "taskId" is required');
|
|
6978
|
+
return { taskId };
|
|
6979
|
+
}
|
|
6980
|
+
async run() {
|
|
6981
|
+
return applyResumeTask(this.context, this.task?.params?.taskId);
|
|
6982
|
+
}
|
|
6983
|
+
};
|
|
6984
|
+
|
|
6774
6985
|
// src/tasks/coreTasks/TaskGetLogs.js
|
|
6775
6986
|
var TaskGetLogs = class extends AbstractTask {
|
|
6776
6987
|
/**
|
|
@@ -6989,7 +7200,7 @@ var TasksRegistry = class _TasksRegistry {
|
|
|
6989
7200
|
* @returns {TasksRegistry}
|
|
6990
7201
|
*/
|
|
6991
7202
|
static withCoreTasks() {
|
|
6992
|
-
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);
|
|
7203
|
+
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);
|
|
6993
7204
|
}
|
|
6994
7205
|
/**
|
|
6995
7206
|
* Register a single task class under a name. Overwrites any previous entry.
|
|
@@ -7181,11 +7392,12 @@ async function executeClaimedTask(context, tasksTable, historyTable, row, regist
|
|
|
7181
7392
|
} finally {
|
|
7182
7393
|
runningTaskInstances.delete(row.id);
|
|
7183
7394
|
}
|
|
7395
|
+
const taskPaused = success && results && typeof results === "object" && results.taskPaused === true;
|
|
7184
7396
|
await db(historyTable).insert(
|
|
7185
7397
|
taskHistoryInsertFromQueueRow(row, {
|
|
7186
7398
|
completed_at: /* @__PURE__ */ new Date(),
|
|
7187
7399
|
success,
|
|
7188
|
-
status: success ? "completed" : "failed",
|
|
7400
|
+
status: taskPaused ? "paused" : success ? "completed" : "failed",
|
|
7189
7401
|
status_changed_at: db.fn.now(),
|
|
7190
7402
|
params: toJsonColumn(row.params),
|
|
7191
7403
|
results: toJsonColumn(results)
|
|
@@ -7208,7 +7420,24 @@ async function executeClaimedTask(context, tasksTable, historyTable, row, regist
|
|
|
7208
7420
|
details: results
|
|
7209
7421
|
});
|
|
7210
7422
|
}
|
|
7211
|
-
if (
|
|
7423
|
+
if (taskPaused) {
|
|
7424
|
+
const checkpointParams = results.checkpointParams && typeof results.checkpointParams === "object" ? results.checkpointParams : row.params;
|
|
7425
|
+
const progressPayload = results.progress != null ? results.progress : { paused: true, pausedAt: (/* @__PURE__ */ new Date()).toISOString(), message: "paused" };
|
|
7426
|
+
await db(tasksTable).where({ id: row.id }).update({
|
|
7427
|
+
started_at: null,
|
|
7428
|
+
completed_at: /* @__PURE__ */ new Date(),
|
|
7429
|
+
success: true,
|
|
7430
|
+
results: toJsonColumn(results),
|
|
7431
|
+
params: toJsonColumn(checkpointParams),
|
|
7432
|
+
progress: toJsonColumn(progressPayload),
|
|
7433
|
+
status: "paused",
|
|
7434
|
+
status_changed_at: db.fn.now(),
|
|
7435
|
+
past_due: null,
|
|
7436
|
+
service_name: null,
|
|
7437
|
+
server_name: null,
|
|
7438
|
+
instance_number: null
|
|
7439
|
+
});
|
|
7440
|
+
} else if (row.schedule) {
|
|
7212
7441
|
if (success) {
|
|
7213
7442
|
let nextRunAt = null;
|
|
7214
7443
|
try {
|
|
@@ -7336,6 +7565,7 @@ async function runTasksLoop(context, options) {
|
|
|
7336
7565
|
}
|
|
7337
7566
|
const runningPromises = /* @__PURE__ */ new Set();
|
|
7338
7567
|
const runningTaskInstances = /* @__PURE__ */ new Map();
|
|
7568
|
+
context.runningTaskInstances = runningTaskInstances;
|
|
7339
7569
|
let runningControlPromise = null;
|
|
7340
7570
|
let stopRequested = false;
|
|
7341
7571
|
let stopAllowanceMs = Number(context.stopAllowanceMs) > 0 ? Number(context.stopAllowanceMs) : 6e4;
|