@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/tasks.cjs
CHANGED
|
@@ -36,7 +36,9 @@ __export(tasks_exports, {
|
|
|
36
36
|
SERVICE_TASK_NAMES: () => SERVICE_TASK_NAMES,
|
|
37
37
|
TaskGetLogs: () => TaskGetLogs,
|
|
38
38
|
TaskPauseRunner: () => TaskPauseRunner,
|
|
39
|
+
TaskPauseTask: () => TaskPauseTask,
|
|
39
40
|
TaskPing: () => TaskPing,
|
|
41
|
+
TaskResumeTask: () => TaskResumeTask,
|
|
40
42
|
TaskSampleProcess: () => TaskSampleProcess,
|
|
41
43
|
TaskSetRuntimeParam: () => TaskSetRuntimeParam,
|
|
42
44
|
TaskShellCommand: () => TaskShellCommand,
|
|
@@ -47,6 +49,8 @@ __export(tasks_exports, {
|
|
|
47
49
|
TasksManager: () => TasksManager,
|
|
48
50
|
TasksRegistry: () => TasksRegistry,
|
|
49
51
|
appendTaskIpcLog: () => appendTaskIpcLog,
|
|
52
|
+
applyPauseTask: () => applyPauseTask,
|
|
53
|
+
applyResumeTask: () => applyResumeTask,
|
|
50
54
|
applyRunnerPaused: () => applyRunnerPaused,
|
|
51
55
|
applyRuntimeParam: () => applyRuntimeParam,
|
|
52
56
|
applyRuntimePatch: () => applyRuntimePatch,
|
|
@@ -612,6 +616,9 @@ var DEFAULT_GROUP_MAX_INSTANCES = {
|
|
|
612
616
|
harvest: 1,
|
|
613
617
|
harvester: 0,
|
|
614
618
|
loader: 0,
|
|
619
|
+
toolkit: 0,
|
|
620
|
+
photocare: 0,
|
|
621
|
+
/** @deprecated use photocare */
|
|
615
622
|
photos: 0,
|
|
616
623
|
photosprocessor: 0,
|
|
617
624
|
ingest: 0
|
|
@@ -2138,6 +2145,8 @@ var AbstractTask = class _AbstractTask {
|
|
|
2138
2145
|
constructor(context, task) {
|
|
2139
2146
|
this.context = context;
|
|
2140
2147
|
this.task = task;
|
|
2148
|
+
this._stopRequested = false;
|
|
2149
|
+
this._pauseRequested = false;
|
|
2141
2150
|
}
|
|
2142
2151
|
/**
|
|
2143
2152
|
* Return a short reason string when the task should be deferred (e.g. "locked
|
|
@@ -2155,6 +2164,26 @@ var AbstractTask = class _AbstractTask {
|
|
|
2155
2164
|
* @param {number} [_allowanceMs] Grace period the runner promises before hard exit.
|
|
2156
2165
|
*/
|
|
2157
2166
|
requestStop(_allowanceMs) {
|
|
2167
|
+
this._stopRequested = true;
|
|
2168
|
+
}
|
|
2169
|
+
/**
|
|
2170
|
+
* Cooperative pause signal (from `pauseTask` control-lane task). Long-running
|
|
2171
|
+
* tasks should finish the current unit of work, persist a checkpoint, and
|
|
2172
|
+
* return `{ success: true, results: { taskPaused: true, … } }` so the runner
|
|
2173
|
+
* keeps the row as `status=paused` instead of deleting it.
|
|
2174
|
+
*
|
|
2175
|
+
* @param {number} [_allowanceMs]
|
|
2176
|
+
*/
|
|
2177
|
+
requestPause(_allowanceMs) {
|
|
2178
|
+
this._pauseRequested = true;
|
|
2179
|
+
}
|
|
2180
|
+
/** @returns {boolean} */
|
|
2181
|
+
isStopRequested() {
|
|
2182
|
+
return this._stopRequested === true;
|
|
2183
|
+
}
|
|
2184
|
+
/** @returns {boolean} */
|
|
2185
|
+
isPauseRequested() {
|
|
2186
|
+
return this._pauseRequested === true;
|
|
2158
2187
|
}
|
|
2159
2188
|
/**
|
|
2160
2189
|
* Perform the task. Must be implemented by subclasses.
|
|
@@ -2861,6 +2890,8 @@ var CONTROL_LANE_TASK_NAMES = [
|
|
|
2861
2890
|
"pause",
|
|
2862
2891
|
"unpauseRunner",
|
|
2863
2892
|
"unpause",
|
|
2893
|
+
"pauseTask",
|
|
2894
|
+
"resumeTask",
|
|
2864
2895
|
"setRuntimeParam",
|
|
2865
2896
|
"setRunnerParam"
|
|
2866
2897
|
];
|
|
@@ -3065,6 +3096,190 @@ var TaskUnpauseRunner = class extends AbstractTask {
|
|
|
3065
3096
|
}
|
|
3066
3097
|
};
|
|
3067
3098
|
|
|
3099
|
+
// src/tasks/coreTasks/TaskPauseTask.js
|
|
3100
|
+
function parseProgressObject(progress) {
|
|
3101
|
+
if (progress == null || progress === "") return {};
|
|
3102
|
+
if (typeof progress === "object" && !Array.isArray(progress)) {
|
|
3103
|
+
return { ...progress };
|
|
3104
|
+
}
|
|
3105
|
+
if (typeof progress === "string") {
|
|
3106
|
+
const t = progress.trim();
|
|
3107
|
+
if (t.startsWith("{") || t.startsWith("[")) {
|
|
3108
|
+
try {
|
|
3109
|
+
const parsed = JSON.parse(t);
|
|
3110
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return { ...parsed };
|
|
3111
|
+
} catch {
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
return { message: progress };
|
|
3115
|
+
}
|
|
3116
|
+
return { message: String(progress) };
|
|
3117
|
+
}
|
|
3118
|
+
async function applyPauseTask(context, taskId) {
|
|
3119
|
+
const db = context.db;
|
|
3120
|
+
if (!db) throw new Error("pauseTask: context.db is required");
|
|
3121
|
+
const id = String(taskId ?? "").trim();
|
|
3122
|
+
if (!id) throw new ParamError('pauseTask: param "taskId" is required');
|
|
3123
|
+
const { tasksTable } = queueToTableNames(context.tasksQueueName ?? "tasks");
|
|
3124
|
+
const row = await db(tasksTable).where({ id }).first();
|
|
3125
|
+
if (!row) {
|
|
3126
|
+
return { success: false, results: { error: `Task ${id} not found`, taskId: id } };
|
|
3127
|
+
}
|
|
3128
|
+
if (row.status === "paused") {
|
|
3129
|
+
return {
|
|
3130
|
+
success: true,
|
|
3131
|
+
results: { taskId: id, status: "paused", message: "Task already paused" }
|
|
3132
|
+
};
|
|
3133
|
+
}
|
|
3134
|
+
if (row.status === "idle") {
|
|
3135
|
+
const progress = parseProgressObject(row.progress);
|
|
3136
|
+
progress.pauseRequested = true;
|
|
3137
|
+
progress.pausedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3138
|
+
await db(tasksTable).where({ id }).update({
|
|
3139
|
+
status: "paused",
|
|
3140
|
+
status_changed_at: db.fn.now(),
|
|
3141
|
+
progress: toJsonColumn(progress)
|
|
3142
|
+
});
|
|
3143
|
+
context.logger?.warn?.(`[pauseTask] idle task ${id} (${row.name}) \u2192 paused`);
|
|
3144
|
+
return {
|
|
3145
|
+
success: true,
|
|
3146
|
+
results: { taskId: id, status: "paused", message: "Idle task paused" }
|
|
3147
|
+
};
|
|
3148
|
+
}
|
|
3149
|
+
if (row.status === "running") {
|
|
3150
|
+
const progress = parseProgressObject(row.progress);
|
|
3151
|
+
progress.pauseRequested = true;
|
|
3152
|
+
progress.pauseRequestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3153
|
+
await db(tasksTable).where({ id }).update({
|
|
3154
|
+
progress: toJsonColumn(progress)
|
|
3155
|
+
});
|
|
3156
|
+
const inst = context.runningTaskInstances?.get?.(id);
|
|
3157
|
+
if (inst && typeof inst.requestPause === "function") {
|
|
3158
|
+
await inst.requestPause();
|
|
3159
|
+
context.logger?.warn?.(
|
|
3160
|
+
`[pauseTask] signaled running task ${id} (${row.name}) to pause after current unit`
|
|
3161
|
+
);
|
|
3162
|
+
return {
|
|
3163
|
+
success: true,
|
|
3164
|
+
results: {
|
|
3165
|
+
taskId: id,
|
|
3166
|
+
status: "running",
|
|
3167
|
+
signaled: true,
|
|
3168
|
+
message: "Pause requested; task will finish current unit then park as paused"
|
|
3169
|
+
}
|
|
3170
|
+
};
|
|
3171
|
+
}
|
|
3172
|
+
context.logger?.warn?.(
|
|
3173
|
+
`[pauseTask] stamped pauseRequested on ${id} (${row.name}) \u2014 not running in this process`
|
|
3174
|
+
);
|
|
3175
|
+
return {
|
|
3176
|
+
success: true,
|
|
3177
|
+
results: {
|
|
3178
|
+
taskId: id,
|
|
3179
|
+
status: "running",
|
|
3180
|
+
signaled: false,
|
|
3181
|
+
message: "pauseRequested stamped on task row; target runner must observe it (enqueue pauseTask on that service)"
|
|
3182
|
+
}
|
|
3183
|
+
};
|
|
3184
|
+
}
|
|
3185
|
+
return {
|
|
3186
|
+
success: false,
|
|
3187
|
+
results: {
|
|
3188
|
+
error: `Cannot pause task in status "${row.status}"`,
|
|
3189
|
+
taskId: id,
|
|
3190
|
+
status: row.status
|
|
3191
|
+
}
|
|
3192
|
+
};
|
|
3193
|
+
}
|
|
3194
|
+
async function applyResumeTask(context, taskId) {
|
|
3195
|
+
const db = context.db;
|
|
3196
|
+
if (!db) throw new Error("resumeTask: context.db is required");
|
|
3197
|
+
const id = String(taskId ?? "").trim();
|
|
3198
|
+
if (!id) throw new ParamError('resumeTask: param "taskId" is required');
|
|
3199
|
+
const { tasksTable } = queueToTableNames(context.tasksQueueName ?? "tasks");
|
|
3200
|
+
const row = await db(tasksTable).where({ id }).first();
|
|
3201
|
+
if (!row) {
|
|
3202
|
+
return { success: false, results: { error: `Task ${id} not found`, taskId: id } };
|
|
3203
|
+
}
|
|
3204
|
+
if (row.status === "idle") {
|
|
3205
|
+
return {
|
|
3206
|
+
success: true,
|
|
3207
|
+
results: { taskId: id, status: "idle", message: "Task already idle (runnable)" }
|
|
3208
|
+
};
|
|
3209
|
+
}
|
|
3210
|
+
if (row.status !== "paused") {
|
|
3211
|
+
return {
|
|
3212
|
+
success: false,
|
|
3213
|
+
results: {
|
|
3214
|
+
error: `Cannot resume task in status "${row.status}" (expected paused)`,
|
|
3215
|
+
taskId: id,
|
|
3216
|
+
status: row.status
|
|
3217
|
+
}
|
|
3218
|
+
};
|
|
3219
|
+
}
|
|
3220
|
+
const progress = parseProgressObject(row.progress);
|
|
3221
|
+
delete progress.pauseRequested;
|
|
3222
|
+
delete progress.pauseRequestedAt;
|
|
3223
|
+
delete progress.pausedAt;
|
|
3224
|
+
progress.resumedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3225
|
+
await db(tasksTable).where({ id }).update({
|
|
3226
|
+
status: "idle",
|
|
3227
|
+
status_changed_at: db.fn.now(),
|
|
3228
|
+
started_at: null,
|
|
3229
|
+
completed_at: null,
|
|
3230
|
+
success: null,
|
|
3231
|
+
progress: toJsonColumn(progress),
|
|
3232
|
+
// Clear claim binding so any matching worker can pick it up.
|
|
3233
|
+
service_name: null,
|
|
3234
|
+
server_name: null,
|
|
3235
|
+
instance_number: null,
|
|
3236
|
+
next_run_at: null
|
|
3237
|
+
});
|
|
3238
|
+
context.logger?.warn?.(`[resumeTask] paused task ${id} (${row.name}) \u2192 idle`);
|
|
3239
|
+
return {
|
|
3240
|
+
success: true,
|
|
3241
|
+
results: { taskId: id, status: "idle", message: "Task resumed (idle; will be claimed)" }
|
|
3242
|
+
};
|
|
3243
|
+
}
|
|
3244
|
+
var TaskPauseTask = class extends AbstractTask {
|
|
3245
|
+
static taskName = "pauseTask";
|
|
3246
|
+
static description = "Pause a specific task: finish current unit, persist status=paused";
|
|
3247
|
+
static defaultWaitForResult = true;
|
|
3248
|
+
static async resolveCustomParams(context, overrides = {}) {
|
|
3249
|
+
const merged = AbstractTask._mergeTypedParams(
|
|
3250
|
+
context,
|
|
3251
|
+
"task-pause-task",
|
|
3252
|
+
{ taskId: "string" },
|
|
3253
|
+
overrides
|
|
3254
|
+
);
|
|
3255
|
+
const taskId = typeof merged.taskId === "string" ? merged.taskId.trim() : "";
|
|
3256
|
+
if (!taskId) throw new ParamError('pauseTask: param "taskId" is required');
|
|
3257
|
+
return { taskId };
|
|
3258
|
+
}
|
|
3259
|
+
async run() {
|
|
3260
|
+
return applyPauseTask(this.context, this.task?.params?.taskId);
|
|
3261
|
+
}
|
|
3262
|
+
};
|
|
3263
|
+
var TaskResumeTask = class extends AbstractTask {
|
|
3264
|
+
static taskName = "resumeTask";
|
|
3265
|
+
static description = "Resume a paused task (status paused \u2192 idle)";
|
|
3266
|
+
static defaultWaitForResult = true;
|
|
3267
|
+
static async resolveCustomParams(context, overrides = {}) {
|
|
3268
|
+
const merged = AbstractTask._mergeTypedParams(
|
|
3269
|
+
context,
|
|
3270
|
+
"task-resume-task",
|
|
3271
|
+
{ taskId: "string" },
|
|
3272
|
+
overrides
|
|
3273
|
+
);
|
|
3274
|
+
const taskId = typeof merged.taskId === "string" ? merged.taskId.trim() : "";
|
|
3275
|
+
if (!taskId) throw new ParamError('resumeTask: param "taskId" is required');
|
|
3276
|
+
return { taskId };
|
|
3277
|
+
}
|
|
3278
|
+
async run() {
|
|
3279
|
+
return applyResumeTask(this.context, this.task?.params?.taskId);
|
|
3280
|
+
}
|
|
3281
|
+
};
|
|
3282
|
+
|
|
3068
3283
|
// src/tasks/coreTasks/TaskGetLogs.js
|
|
3069
3284
|
var TaskGetLogs = class extends AbstractTask {
|
|
3070
3285
|
/**
|
|
@@ -3283,7 +3498,7 @@ var TasksRegistry = class _TasksRegistry {
|
|
|
3283
3498
|
* @returns {TasksRegistry}
|
|
3284
3499
|
*/
|
|
3285
3500
|
static withCoreTasks() {
|
|
3286
|
-
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);
|
|
3501
|
+
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);
|
|
3287
3502
|
}
|
|
3288
3503
|
/**
|
|
3289
3504
|
* Register a single task class under a name. Overwrites any previous entry.
|
|
@@ -3390,6 +3605,8 @@ var SERVICE_TASK_NAMES = [
|
|
|
3390
3605
|
"pauseRunner",
|
|
3391
3606
|
"unpause",
|
|
3392
3607
|
"unpauseRunner",
|
|
3608
|
+
"pauseTask",
|
|
3609
|
+
"resumeTask",
|
|
3393
3610
|
"shellCommand",
|
|
3394
3611
|
"systemInfo",
|
|
3395
3612
|
"info",
|
|
@@ -3685,11 +3902,12 @@ async function executeClaimedTask(context, tasksTable, historyTable, row, regist
|
|
|
3685
3902
|
} finally {
|
|
3686
3903
|
runningTaskInstances.delete(row.id);
|
|
3687
3904
|
}
|
|
3905
|
+
const taskPaused = success && results && typeof results === "object" && results.taskPaused === true;
|
|
3688
3906
|
await db(historyTable).insert(
|
|
3689
3907
|
taskHistoryInsertFromQueueRow(row, {
|
|
3690
3908
|
completed_at: /* @__PURE__ */ new Date(),
|
|
3691
3909
|
success,
|
|
3692
|
-
status: success ? "completed" : "failed",
|
|
3910
|
+
status: taskPaused ? "paused" : success ? "completed" : "failed",
|
|
3693
3911
|
status_changed_at: db.fn.now(),
|
|
3694
3912
|
params: toJsonColumn(row.params),
|
|
3695
3913
|
results: toJsonColumn(results)
|
|
@@ -3712,7 +3930,24 @@ async function executeClaimedTask(context, tasksTable, historyTable, row, regist
|
|
|
3712
3930
|
details: results
|
|
3713
3931
|
});
|
|
3714
3932
|
}
|
|
3715
|
-
if (
|
|
3933
|
+
if (taskPaused) {
|
|
3934
|
+
const checkpointParams = results.checkpointParams && typeof results.checkpointParams === "object" ? results.checkpointParams : row.params;
|
|
3935
|
+
const progressPayload = results.progress != null ? results.progress : { paused: true, pausedAt: (/* @__PURE__ */ new Date()).toISOString(), message: "paused" };
|
|
3936
|
+
await db(tasksTable).where({ id: row.id }).update({
|
|
3937
|
+
started_at: null,
|
|
3938
|
+
completed_at: /* @__PURE__ */ new Date(),
|
|
3939
|
+
success: true,
|
|
3940
|
+
results: toJsonColumn(results),
|
|
3941
|
+
params: toJsonColumn(checkpointParams),
|
|
3942
|
+
progress: toJsonColumn(progressPayload),
|
|
3943
|
+
status: "paused",
|
|
3944
|
+
status_changed_at: db.fn.now(),
|
|
3945
|
+
past_due: null,
|
|
3946
|
+
service_name: null,
|
|
3947
|
+
server_name: null,
|
|
3948
|
+
instance_number: null
|
|
3949
|
+
});
|
|
3950
|
+
} else if (row.schedule) {
|
|
3716
3951
|
if (success) {
|
|
3717
3952
|
let nextRunAt = null;
|
|
3718
3953
|
try {
|
|
@@ -3840,6 +4075,7 @@ async function runTasksLoop(context, options) {
|
|
|
3840
4075
|
}
|
|
3841
4076
|
const runningPromises = /* @__PURE__ */ new Set();
|
|
3842
4077
|
const runningTaskInstances = /* @__PURE__ */ new Map();
|
|
4078
|
+
context.runningTaskInstances = runningTaskInstances;
|
|
3843
4079
|
let runningControlPromise = null;
|
|
3844
4080
|
let stopRequested = false;
|
|
3845
4081
|
let stopAllowanceMs = Number(context.stopAllowanceMs) > 0 ? Number(context.stopAllowanceMs) : 6e4;
|
|
@@ -4200,7 +4436,9 @@ var TasksManager = class _TasksManager {
|
|
|
4200
4436
|
SERVICE_TASK_NAMES,
|
|
4201
4437
|
TaskGetLogs,
|
|
4202
4438
|
TaskPauseRunner,
|
|
4439
|
+
TaskPauseTask,
|
|
4203
4440
|
TaskPing,
|
|
4441
|
+
TaskResumeTask,
|
|
4204
4442
|
TaskSampleProcess,
|
|
4205
4443
|
TaskSetRuntimeParam,
|
|
4206
4444
|
TaskShellCommand,
|
|
@@ -4211,6 +4449,8 @@ var TasksManager = class _TasksManager {
|
|
|
4211
4449
|
TasksManager,
|
|
4212
4450
|
TasksRegistry,
|
|
4213
4451
|
appendTaskIpcLog,
|
|
4452
|
+
applyPauseTask,
|
|
4453
|
+
applyResumeTask,
|
|
4214
4454
|
applyRunnerPaused,
|
|
4215
4455
|
applyRuntimeParam,
|
|
4216
4456
|
applyRuntimePatch,
|