feat(tasks): manage markdown loops from scheduled tasks

This commit is contained in:
Bohdan Triapitsyn
2026-08-09 21:43:16 +03:00
parent ce6912fe40
commit e13b4526c6
20 changed files with 322 additions and 34 deletions
@@ -1,6 +1,7 @@
import fs from 'node:fs';
import path from 'node:path';
import { OpenChamberControlError } from '../openchamber-control/error.js';
import { setLoopFileEnabled } from './loops.js';
const asNonEmptyString = (value) => {
if (typeof value !== 'string') return null;
@@ -53,6 +54,47 @@ export const createScheduledTaskService = (dependencies) => {
return scheduledTasksRuntime.syncProject(projectID);
};
const findLoopTask = async (projectID, taskID) => {
await findProjectByID(projectID);
const normalizedTaskID = asNonEmptyString(taskID);
if (!normalizedTaskID) throw new OpenChamberControlError('taskId is required', 400);
const tasks = await scheduledTasksRuntime.syncProject(projectID);
const task = tasks.find((entry) => entry?.id === normalizedTaskID) || null;
if (!task) throw new OpenChamberControlError('Task not found', 404);
if (!task.loopFile) throw new OpenChamberControlError('Task is not managed by a loop file', 400);
if (!fs.existsSync(task.loopFile)) throw new OpenChamberControlError('Loop file not found', 404);
return task;
};
const setLoopEnabled = async (projectID, taskID, enabled) => {
if (typeof enabled !== 'boolean') {
throw new OpenChamberControlError('enabled must be a boolean', 400);
}
const task = await findLoopTask(projectID, taskID);
try {
if (!setLoopFileEnabled(task.loopFile, enabled)) {
throw new OpenChamberControlError('Loop file must be valid before changing its enabled state', 400);
}
} catch (error) {
if (error instanceof OpenChamberControlError) throw error;
const message = error instanceof Error ? error.message : 'Failed to update loop file';
throw new OpenChamberControlError(message, 500);
}
const tasks = await scheduledTasksRuntime.syncProject(projectID);
return tasks.find((entry) => entry.id === taskID) || null;
};
const removeLoopFile = async (projectID, taskID) => {
const task = await findLoopTask(projectID, taskID);
try {
fs.unlinkSync(task.loopFile);
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to delete loop file';
throw new OpenChamberControlError(message, 500);
}
return scheduledTasksRuntime.syncProject(projectID);
};
const upsert = async (projectID, taskInput) => {
await findProjectByID(projectID);
if (!taskInput || typeof taskInput !== 'object') {
@@ -154,6 +196,8 @@ export const createScheduledTaskService = (dependencies) => {
remove,
run,
setEnabled,
setLoopEnabled,
removeLoopFile,
status,
};
};