@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.cjs
CHANGED
|
@@ -4381,6 +4381,9 @@ var DEFAULT_GROUP_MAX_INSTANCES = {
|
|
|
4381
4381
|
harvest: 1,
|
|
4382
4382
|
harvester: 0,
|
|
4383
4383
|
loader: 0,
|
|
4384
|
+
toolkit: 0,
|
|
4385
|
+
photocare: 0,
|
|
4386
|
+
/** @deprecated use photocare */
|
|
4384
4387
|
photos: 0,
|
|
4385
4388
|
photosprocessor: 0,
|
|
4386
4389
|
ingest: 0
|
|
@@ -5868,6 +5871,8 @@ var AbstractTask = class _AbstractTask {
|
|
|
5868
5871
|
constructor(context, task) {
|
|
5869
5872
|
this.context = context;
|
|
5870
5873
|
this.task = task;
|
|
5874
|
+
this._stopRequested = false;
|
|
5875
|
+
this._pauseRequested = false;
|
|
5871
5876
|
}
|
|
5872
5877
|
/**
|
|
5873
5878
|
* Return a short reason string when the task should be deferred (e.g. "locked
|
|
@@ -5885,6 +5890,26 @@ var AbstractTask = class _AbstractTask {
|
|
|
5885
5890
|
* @param {number} [_allowanceMs] Grace period the runner promises before hard exit.
|
|
5886
5891
|
*/
|
|
5887
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;
|
|
5888
5913
|
}
|
|
5889
5914
|
/**
|
|
5890
5915
|
* Perform the task. Must be implemented by subclasses.
|
|
@@ -6583,6 +6608,8 @@ var CONTROL_LANE_TASK_NAMES = [
|
|
|
6583
6608
|
"pause",
|
|
6584
6609
|
"unpauseRunner",
|
|
6585
6610
|
"unpause",
|
|
6611
|
+
"pauseTask",
|
|
6612
|
+
"resumeTask",
|
|
6586
6613
|
"setRuntimeParam",
|
|
6587
6614
|
"setRunnerParam"
|
|
6588
6615
|
];
|
|
@@ -6787,6 +6814,190 @@ var TaskUnpauseRunner = class extends AbstractTask {
|
|
|
6787
6814
|
}
|
|
6788
6815
|
};
|
|
6789
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
|
+
|
|
6790
7001
|
// src/tasks/coreTasks/TaskGetLogs.js
|
|
6791
7002
|
var TaskGetLogs = class extends AbstractTask {
|
|
6792
7003
|
/**
|
|
@@ -7005,7 +7216,7 @@ var TasksRegistry = class _TasksRegistry {
|
|
|
7005
7216
|
* @returns {TasksRegistry}
|
|
7006
7217
|
*/
|
|
7007
7218
|
static withCoreTasks() {
|
|
7008
|
-
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);
|
|
7009
7220
|
}
|
|
7010
7221
|
/**
|
|
7011
7222
|
* Register a single task class under a name. Overwrites any previous entry.
|
|
@@ -7197,11 +7408,12 @@ async function executeClaimedTask(context, tasksTable, historyTable, row, regist
|
|
|
7197
7408
|
} finally {
|
|
7198
7409
|
runningTaskInstances.delete(row.id);
|
|
7199
7410
|
}
|
|
7411
|
+
const taskPaused = success && results && typeof results === "object" && results.taskPaused === true;
|
|
7200
7412
|
await db(historyTable).insert(
|
|
7201
7413
|
taskHistoryInsertFromQueueRow(row, {
|
|
7202
7414
|
completed_at: /* @__PURE__ */ new Date(),
|
|
7203
7415
|
success,
|
|
7204
|
-
status: success ? "completed" : "failed",
|
|
7416
|
+
status: taskPaused ? "paused" : success ? "completed" : "failed",
|
|
7205
7417
|
status_changed_at: db.fn.now(),
|
|
7206
7418
|
params: toJsonColumn(row.params),
|
|
7207
7419
|
results: toJsonColumn(results)
|
|
@@ -7224,7 +7436,24 @@ async function executeClaimedTask(context, tasksTable, historyTable, row, regist
|
|
|
7224
7436
|
details: results
|
|
7225
7437
|
});
|
|
7226
7438
|
}
|
|
7227
|
-
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) {
|
|
7228
7457
|
if (success) {
|
|
7229
7458
|
let nextRunAt = null;
|
|
7230
7459
|
try {
|
|
@@ -7352,6 +7581,7 @@ async function runTasksLoop(context, options) {
|
|
|
7352
7581
|
}
|
|
7353
7582
|
const runningPromises = /* @__PURE__ */ new Set();
|
|
7354
7583
|
const runningTaskInstances = /* @__PURE__ */ new Map();
|
|
7584
|
+
context.runningTaskInstances = runningTaskInstances;
|
|
7355
7585
|
let runningControlPromise = null;
|
|
7356
7586
|
let stopRequested = false;
|
|
7357
7587
|
let stopAllowanceMs = Number(context.stopAllowanceMs) > 0 ? Number(context.stopAllowanceMs) : 6e4;
|