chore: changeset + update rules.
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
|
||||
import { addDependency } from '../../../../scripts/modules/dependency-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for addDependency with error handling.
|
||||
@@ -51,9 +52,15 @@ export async function addDependencyDirect(args, log) {
|
||||
|
||||
log.info(`Adding dependency: task ${taskId} will depend on ${dependencyId}`);
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core function
|
||||
await addDependency(tasksPath, taskId, dependencyId);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
@@ -63,6 +70,9 @@ export async function addDependencyDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in addDependencyDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { addSubtask } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Add a subtask to an existing task
|
||||
@@ -67,10 +68,17 @@ export async function addSubtaskDirect(args, log) {
|
||||
// Determine if we should generate files
|
||||
const generateFiles = !args.skipGenerate;
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Case 1: Convert existing task to subtask
|
||||
if (existingTaskId) {
|
||||
log.info(`Converting task ${existingTaskId} to a subtask of ${parentId}`);
|
||||
const result = await addSubtask(tasksPath, parentId, existingTaskId, null, generateFiles);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
@@ -92,6 +100,10 @@ export async function addSubtaskDirect(args, log) {
|
||||
};
|
||||
|
||||
const result = await addSubtask(tasksPath, parentId, null, newSubtaskData, generateFiles);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
@@ -101,6 +113,9 @@ export async function addSubtaskDirect(args, log) {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in addSubtaskDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { addTask } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for adding a new task with error handling.
|
||||
@@ -20,6 +21,9 @@ import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
*/
|
||||
export async function addTaskDirect(args, log) {
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Find the tasks.json path
|
||||
const tasksPath = findTasksJsonPath(args, log);
|
||||
|
||||
@@ -44,8 +48,18 @@ export async function addTaskDirect(args, log) {
|
||||
|
||||
log.info(`Adding new task with prompt: "${prompt}", dependencies: [${dependencies.join(', ')}], priority: ${priority}`);
|
||||
|
||||
// Call the addTask function
|
||||
const newTaskId = await addTask(tasksPath, prompt, dependencies, priority);
|
||||
// Call the addTask function with 'json' outputFormat to prevent console output when called via MCP
|
||||
const newTaskId = await addTask(
|
||||
tasksPath,
|
||||
prompt,
|
||||
dependencies,
|
||||
priority,
|
||||
{ mcpLog: log },
|
||||
'json'
|
||||
);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
@@ -55,6 +69,9 @@ export async function addTaskDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in addTaskDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { analyzeTaskComplexity } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
@@ -48,9 +49,15 @@ export async function analyzeTaskComplexityDirect(args, log) {
|
||||
log.info('Using Perplexity AI for research-backed complexity analysis');
|
||||
}
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core function
|
||||
await analyzeTaskComplexity(options);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Verify the report file was created
|
||||
if (!fs.existsSync(outputPath)) {
|
||||
return {
|
||||
@@ -79,6 +86,9 @@ export async function analyzeTaskComplexityDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in analyzeTaskComplexityDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { clearSubtasks } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import fs from 'fs';
|
||||
|
||||
/**
|
||||
@@ -68,9 +69,15 @@ export async function clearSubtasksDirect(args, log) {
|
||||
|
||||
log.info(`Clearing subtasks from tasks: ${taskIds}`);
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core function
|
||||
clearSubtasks(tasksPath, taskIds);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Read the updated data to provide a summary
|
||||
const updatedData = JSON.parse(fs.readFileSync(tasksPath, 'utf8'));
|
||||
const taskIdArray = taskIds.split(',').map(id => parseInt(id.trim(), 10));
|
||||
@@ -90,6 +97,9 @@ export async function clearSubtasksDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in clearSubtasksDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Direct function implementation for displaying complexity analysis report
|
||||
*/
|
||||
|
||||
import { readComplexityReport } from '../../../../scripts/modules/utils.js';
|
||||
import { readComplexityReport, enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { getCachedOrExecute } from '../../tools/utils.js';
|
||||
import path from 'path';
|
||||
@@ -39,8 +39,14 @@ export async function complexityReportDirect(args, log) {
|
||||
// Define the core action function to read the report
|
||||
const coreActionFn = async () => {
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
const report = readComplexityReport(reportPath);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
if (!report) {
|
||||
log.warn(`No complexity report found at ${reportPath}`);
|
||||
return {
|
||||
@@ -60,6 +66,9 @@ export async function complexityReportDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error reading complexity report: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
@@ -82,6 +91,9 @@ export async function complexityReportDirect(args, log) {
|
||||
return result; // Returns { success, data/error, fromCache }
|
||||
} catch (error) {
|
||||
// Catch unexpected errors from getCachedOrExecute itself
|
||||
// Ensure silent mode is disabled
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Unexpected error during getCachedOrExecute for complexityReport: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
@@ -93,6 +105,9 @@ export async function complexityReportDirect(args, log) {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
// Ensure silent mode is disabled if an outer error occurs
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in complexityReportDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import { expandAllTasks } from '../../../../scripts/modules/task-manager.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
|
||||
/**
|
||||
@@ -41,23 +42,38 @@ export async function expandAllTasksDirect(args, log) {
|
||||
log.info('Force regeneration of subtasks is enabled');
|
||||
}
|
||||
|
||||
// Call the core function
|
||||
await expandAllTasks(numSubtasks, useResearch, additionalContext, forceFlag);
|
||||
|
||||
// The expandAllTasks function doesn't have a return value, so we'll create our own success response
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: "Successfully expanded all pending tasks with subtasks",
|
||||
details: {
|
||||
numSubtasks: numSubtasks,
|
||||
research: useResearch,
|
||||
prompt: additionalContext,
|
||||
force: forceFlag
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core function
|
||||
await expandAllTasks(numSubtasks, useResearch, additionalContext, forceFlag);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// The expandAllTasks function doesn't have a return value, so we'll create our own success response
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: "Successfully expanded all pending tasks with subtasks",
|
||||
details: {
|
||||
numSubtasks: numSubtasks,
|
||||
research: useResearch,
|
||||
prompt: additionalContext,
|
||||
force: forceFlag
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
throw error; // Rethrow to be caught by outer catch block
|
||||
}
|
||||
} catch (error) {
|
||||
// Ensure silent mode is disabled
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in expandAllTasksDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { expandTask } from '../../../../scripts/modules/task-manager.js';
|
||||
import { readJSON, writeJSON } from '../../../../scripts/modules/utils.js';
|
||||
import { readJSON, writeJSON, enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
@@ -116,28 +116,50 @@ export async function expandTaskDirect(args, log) {
|
||||
// Save tasks.json with potentially empty subtasks array
|
||||
writeJSON(tasksPath, data);
|
||||
|
||||
// Call the core expandTask function
|
||||
await expandTask(taskId, numSubtasks, useResearch, additionalContext);
|
||||
|
||||
// Read the updated data
|
||||
const updatedData = readJSON(tasksPath);
|
||||
const updatedTask = updatedData.tasks.find(t => t.id === taskId);
|
||||
|
||||
// Calculate how many subtasks were added
|
||||
const subtasksAdded = updatedTask.subtasks ?
|
||||
updatedTask.subtasks.length - subtasksCountBefore : 0;
|
||||
|
||||
// Return the result
|
||||
log.info(`Successfully expanded task ${taskId} with ${subtasksAdded} new subtasks`);
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
task: updatedTask,
|
||||
subtasksAdded,
|
||||
hasExistingSubtasks
|
||||
},
|
||||
fromCache: false
|
||||
};
|
||||
// Process the request
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call expandTask
|
||||
const result = await expandTask(taskId, numSubtasks, useResearch, additionalContext);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Read the updated data
|
||||
const updatedData = readJSON(tasksPath);
|
||||
const updatedTask = updatedData.tasks.find(t => t.id === taskId);
|
||||
|
||||
// Calculate how many subtasks were added
|
||||
const subtasksAdded = updatedTask.subtasks ?
|
||||
updatedTask.subtasks.length - subtasksCountBefore : 0;
|
||||
|
||||
// Return the result
|
||||
log.info(`Successfully expanded task ${taskId} with ${subtasksAdded} new subtasks`);
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
task: updatedTask,
|
||||
subtasksAdded,
|
||||
hasExistingSubtasks
|
||||
},
|
||||
fromCache: false
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error expanding task: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: 'CORE_FUNCTION_ERROR',
|
||||
message: error.message || 'Failed to expand task'
|
||||
},
|
||||
fromCache: false
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(`Error expanding task: ${error.message}`);
|
||||
return {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { fixDependenciesCommand } from '../../../../scripts/modules/dependency-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import fs from 'fs';
|
||||
|
||||
/**
|
||||
@@ -32,9 +33,15 @@ export async function fixDependenciesDirect(args, log) {
|
||||
};
|
||||
}
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the original command function
|
||||
await fixDependenciesCommand(tasksPath);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
@@ -43,6 +50,9 @@ export async function fixDependenciesDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error fixing dependencies: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { generateTaskFiles } from '../../../../scripts/modules/task-manager.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import path from 'path';
|
||||
|
||||
@@ -39,8 +40,27 @@ export async function generateTaskFilesDirect(args, log) {
|
||||
|
||||
log.info(`Generating task files from ${tasksPath} to ${outputDir}`);
|
||||
|
||||
// Execute core generateTaskFiles function
|
||||
generateTaskFiles(tasksPath, outputDir);
|
||||
// Execute core generateTaskFiles function in a separate try/catch
|
||||
try {
|
||||
// Enable silent mode to prevent logs from being written to stdout
|
||||
enableSilentMode();
|
||||
|
||||
// The function is synchronous despite being awaited elsewhere
|
||||
generateTaskFiles(tasksPath, outputDir);
|
||||
|
||||
// Restore normal logging after task generation
|
||||
disableSilentMode();
|
||||
} catch (genError) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in generateTaskFiles: ${genError.message}`);
|
||||
return {
|
||||
success: false,
|
||||
error: { code: 'GENERATE_FILES_ERROR', message: genError.message },
|
||||
fromCache: false
|
||||
};
|
||||
}
|
||||
|
||||
// Return success with file paths
|
||||
return {
|
||||
@@ -54,6 +74,9 @@ export async function generateTaskFilesDirect(args, log) {
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging if an outer error occurs
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error generating task files: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { listTasks } from '../../../../scripts/modules/task-manager.js';
|
||||
import { getCachedOrExecute } from '../../tools/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for listTasks with error handling and caching.
|
||||
@@ -38,6 +39,9 @@ export async function listTasksDirect(args, log) {
|
||||
// Define the action function to be executed on cache miss
|
||||
const coreListTasksAction = async () => {
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
log.info(`Executing core listTasks function for path: ${tasksPath}, filter: ${statusFilter}, subtasks: ${withSubtasks}`);
|
||||
const resultData = listTasks(tasksPath, statusFilter, withSubtasks, 'json');
|
||||
|
||||
@@ -46,9 +50,16 @@ export async function listTasksDirect(args, log) {
|
||||
return { success: false, error: { code: 'INVALID_CORE_RESPONSE', message: 'Invalid or empty response from listTasks core function' } };
|
||||
}
|
||||
log.info(`Core listTasks function retrieved ${resultData.tasks.length} tasks`);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return { success: true, data: resultData };
|
||||
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Core listTasks function failed: ${error.message}`);
|
||||
return { success: false, error: { code: 'LIST_TASKS_CORE_ERROR', message: error.message || 'Failed to list tasks' } };
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { findNextTask } from '../../../../scripts/modules/task-manager.js';
|
||||
import { readJSON } from '../../../../scripts/modules/utils.js';
|
||||
import { getCachedOrExecute } from '../../tools/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for finding the next task to work on with error handling and caching.
|
||||
@@ -38,6 +39,9 @@ export async function nextTaskDirect(args, log) {
|
||||
// Define the action function to be executed on cache miss
|
||||
const coreNextTaskAction = async () => {
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
log.info(`Finding next task from ${tasksPath}`);
|
||||
|
||||
// Read tasks data
|
||||
@@ -67,6 +71,9 @@ export async function nextTaskDirect(args, log) {
|
||||
};
|
||||
}
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Return the next task data with the full tasks array for reference
|
||||
log.info(`Successfully found next task ${nextTask.id}: ${nextTask.title}`);
|
||||
return {
|
||||
@@ -77,6 +84,9 @@ export async function nextTaskDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error finding next task: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -7,6 +7,7 @@ import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { parsePRD } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for parsing PRD documents and generating tasks.
|
||||
@@ -66,9 +67,15 @@ export async function parsePRDDirect(args, log) {
|
||||
|
||||
log.info(`Preparing to parse PRD from ${inputPath} and output to ${outputPath} with ${numTasks} tasks`);
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Execute core parsePRD function (which is not async but we'll await it to maintain consistency)
|
||||
await parsePRD(inputPath, outputPath, numTasks);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Since parsePRD doesn't return a value but writes to a file, we'll read the result
|
||||
// to return it to the caller
|
||||
if (fs.existsSync(outputPath)) {
|
||||
@@ -94,6 +101,9 @@ export async function parsePRDDirect(args, log) {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error parsing PRD: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { removeDependency } from '../../../../scripts/modules/dependency-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Remove a dependency from a task
|
||||
@@ -49,9 +50,15 @@ export async function removeDependencyDirect(args, log) {
|
||||
|
||||
log.info(`Removing dependency: task ${taskId} no longer depends on ${dependencyId}`);
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core function
|
||||
await removeDependency(tasksPath, taskId, dependencyId);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
@@ -61,6 +68,9 @@ export async function removeDependencyDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in removeDependencyDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { removeSubtask } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Remove a subtask from its parent task
|
||||
@@ -18,6 +19,9 @@ import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
*/
|
||||
export async function removeSubtaskDirect(args, log) {
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
log.info(`Removing subtask with args: ${JSON.stringify(args)}`);
|
||||
|
||||
if (!args.id) {
|
||||
@@ -54,6 +58,9 @@ export async function removeSubtaskDirect(args, log) {
|
||||
|
||||
const result = await removeSubtask(tasksPath, args.id, convertToTask, generateFiles);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
if (convertToTask && result) {
|
||||
// Return info about the converted task
|
||||
return {
|
||||
@@ -73,6 +80,9 @@ export async function removeSubtaskDirect(args, log) {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
// Ensure silent mode is disabled even if an outer error occurs
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error in removeSubtaskDirect: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { removeTask } from '../../../../scripts/modules/task-manager.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
|
||||
/**
|
||||
@@ -49,9 +50,15 @@ export async function removeTaskDirect(args, log) {
|
||||
log.info(`Removing task with ID: ${taskId} from ${tasksPath}`);
|
||||
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core removeTask function
|
||||
const result = await removeTask(tasksPath, taskId);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
log.info(`Successfully removed task: ${taskId}`);
|
||||
|
||||
// Return the result
|
||||
@@ -66,6 +73,9 @@ export async function removeTaskDirect(args, log) {
|
||||
fromCache: false
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error removing task: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
@@ -77,6 +87,9 @@ export async function removeTaskDirect(args, log) {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
// Ensure silent mode is disabled even if an outer error occurs
|
||||
disableSilentMode();
|
||||
|
||||
// Catch any unexpected errors
|
||||
log.error(`Unexpected error in removeTaskDirect: ${error.message}`);
|
||||
return {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { setTaskStatus } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for setTaskStatus with error handling.
|
||||
@@ -63,20 +64,40 @@ export async function setTaskStatusDirect(args, log) {
|
||||
|
||||
log.info(`Setting task ${taskId} status to "${newStatus}"`);
|
||||
|
||||
// Execute the setTaskStatus function with source=mcp to avoid console output
|
||||
await setTaskStatus(tasksPath, taskId, newStatus);
|
||||
|
||||
// Return success data
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Successfully updated task ${taskId} status to "${newStatus}"`,
|
||||
taskId,
|
||||
status: newStatus,
|
||||
tasksPath
|
||||
},
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
// Call the core function
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
await setTaskStatus(tasksPath, taskId, newStatus);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
log.info(`Successfully set task ${taskId} status to ${newStatus}`);
|
||||
|
||||
// Return success data
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Successfully updated task ${taskId} status to "${newStatus}"`,
|
||||
taskId,
|
||||
status: newStatus,
|
||||
tasksPath
|
||||
},
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error setting task status: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
error: { code: 'SET_STATUS_ERROR', message: error.message || 'Unknown error setting task status' },
|
||||
fromCache: false
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(`Error setting task status: ${error.message}`);
|
||||
return {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { findTaskById } from '../../../../scripts/modules/utils.js';
|
||||
import { readJSON } from '../../../../scripts/modules/utils.js';
|
||||
import { getCachedOrExecute } from '../../tools/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for showing task details with error handling and caching.
|
||||
@@ -52,6 +53,9 @@ export async function showTaskDirect(args, log) {
|
||||
// Define the action function to be executed on cache miss
|
||||
const coreShowTaskAction = async () => {
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
log.info(`Retrieving task details for ID: ${taskId} from ${tasksPath}`);
|
||||
|
||||
// Read tasks data
|
||||
@@ -79,6 +83,9 @@ export async function showTaskDirect(args, log) {
|
||||
};
|
||||
}
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Return the task data with the full tasks array for reference
|
||||
// (needed for formatDependenciesWithStatus function in UI)
|
||||
log.info(`Successfully found task ${taskId}`);
|
||||
@@ -90,6 +97,9 @@ export async function showTaskDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error showing task: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
@@ -112,6 +122,7 @@ export async function showTaskDirect(args, log) {
|
||||
return result; // Returns { success, data/error, fromCache }
|
||||
} catch (error) {
|
||||
// Catch unexpected errors from getCachedOrExecute itself
|
||||
disableSilentMode();
|
||||
log.error(`Unexpected error during getCachedOrExecute for showTask: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { updateSubtaskById } from '../../../../scripts/modules/task-manager.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
|
||||
/**
|
||||
@@ -68,35 +69,50 @@ export async function updateSubtaskByIdDirect(args, log) {
|
||||
|
||||
log.info(`Updating subtask with ID ${subtaskId} with prompt "${args.prompt}" and research: ${useResearch}`);
|
||||
|
||||
// Execute core updateSubtaskById function
|
||||
const updatedSubtask = await updateSubtaskById(tasksPath, subtaskId, args.prompt, useResearch);
|
||||
|
||||
// Handle the case where the subtask couldn't be updated (e.g., already marked as done)
|
||||
if (!updatedSubtask) {
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Execute core updateSubtaskById function
|
||||
const updatedSubtask = await updateSubtaskById(tasksPath, subtaskId, args.prompt, useResearch);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Handle the case where the subtask couldn't be updated (e.g., already marked as done)
|
||||
if (!updatedSubtask) {
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: 'SUBTASK_UPDATE_FAILED',
|
||||
message: 'Failed to update subtask. It may be marked as completed, or another error occurred.'
|
||||
},
|
||||
fromCache: false
|
||||
};
|
||||
}
|
||||
|
||||
// Return the updated subtask information
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: 'SUBTASK_UPDATE_FAILED',
|
||||
message: 'Failed to update subtask. It may be marked as completed, or another error occurred.'
|
||||
success: true,
|
||||
data: {
|
||||
message: `Successfully updated subtask with ID ${subtaskId}`,
|
||||
subtaskId,
|
||||
parentId: subtaskId.split('.')[0],
|
||||
subtask: updatedSubtask,
|
||||
tasksPath,
|
||||
useResearch
|
||||
},
|
||||
fromCache: false
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
throw error; // Rethrow to be caught by outer catch block
|
||||
}
|
||||
|
||||
// Return the updated subtask information
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Successfully updated subtask with ID ${subtaskId}`,
|
||||
subtaskId,
|
||||
parentId: subtaskId.split('.')[0],
|
||||
subtask: updatedSubtask,
|
||||
tasksPath,
|
||||
useResearch
|
||||
},
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
} catch (error) {
|
||||
// Ensure silent mode is disabled
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error updating subtask by ID: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { updateTaskById } from '../../../../scripts/modules/task-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for updateTaskById with error handling.
|
||||
@@ -79,9 +80,15 @@ export async function updateTaskByIdDirect(args, log) {
|
||||
|
||||
log.info(`Updating task with ID ${taskId} with prompt "${args.prompt}" and research: ${useResearch}`);
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Execute core updateTaskById function
|
||||
await updateTaskById(tasksPath, taskId, args.prompt, useResearch);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Since updateTaskById doesn't return a value but modifies the tasks file,
|
||||
// we'll return a success message
|
||||
return {
|
||||
@@ -95,6 +102,9 @@ export async function updateTaskByIdDirect(args, log) {
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error updating task by ID: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { updateTasks } from '../../../../scripts/modules/task-manager.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
|
||||
/**
|
||||
@@ -73,22 +74,37 @@ export async function updateTasksDirect(args, log) {
|
||||
|
||||
log.info(`Updating tasks from ID ${fromId} with prompt "${args.prompt}" and research: ${useResearch}`);
|
||||
|
||||
// Execute core updateTasks function
|
||||
await updateTasks(tasksPath, fromId, args.prompt, useResearch);
|
||||
|
||||
// Since updateTasks doesn't return a value but modifies the tasks file,
|
||||
// we'll return a success message
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Successfully updated tasks from ID ${fromId} based on the prompt`,
|
||||
fromId,
|
||||
tasksPath,
|
||||
useResearch
|
||||
},
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Execute core updateTasks function
|
||||
await updateTasks(tasksPath, fromId, args.prompt, useResearch);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
// Since updateTasks doesn't return a value but modifies the tasks file,
|
||||
// we'll return a success message
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Successfully updated tasks from ID ${fromId} based on the prompt`,
|
||||
fromId,
|
||||
tasksPath,
|
||||
useResearch
|
||||
},
|
||||
fromCache: false // This operation always modifies state and should never be cached
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
throw error; // Rethrow to be caught by outer catch block
|
||||
}
|
||||
} catch (error) {
|
||||
// Ensure silent mode is disabled
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error updating tasks: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import { validateDependenciesCommand } from '../../../../scripts/modules/dependency-manager.js';
|
||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
import fs from 'fs';
|
||||
|
||||
/**
|
||||
@@ -32,9 +33,15 @@ export async function validateDependenciesDirect(args, log) {
|
||||
};
|
||||
}
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the original command function
|
||||
await validateDependenciesCommand(tasksPath);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
@@ -43,6 +50,9 @@ export async function validateDependenciesDirect(args, log) {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
|
||||
log.error(`Error validating dependencies: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
217
mcp-server/src/core/utils/async-manager.js
Normal file
217
mcp-server/src/core/utils/async-manager.js
Normal file
@@ -0,0 +1,217 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
class AsyncOperationManager {
|
||||
constructor() {
|
||||
this.operations = new Map(); // Stores active operation state
|
||||
this.completedOperations = new Map(); // Stores completed operations
|
||||
this.maxCompletedOperations = 100; // Maximum number of completed operations to store
|
||||
this.listeners = new Map(); // For potential future notifications
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an operation to be executed asynchronously.
|
||||
* @param {Function} operationFn - The async function to execute (e.g., a Direct function).
|
||||
* @param {Object} args - Arguments to pass to the operationFn.
|
||||
* @param {Object} context - The MCP tool context { log, reportProgress, session }.
|
||||
* @returns {string} The unique ID assigned to this operation.
|
||||
*/
|
||||
addOperation(operationFn, args, context) {
|
||||
const operationId = `op-${uuidv4()}`;
|
||||
const operation = {
|
||||
id: operationId,
|
||||
status: 'pending',
|
||||
startTime: Date.now(),
|
||||
endTime: null,
|
||||
result: null,
|
||||
error: null,
|
||||
// Store necessary parts of context, especially log for background execution
|
||||
log: context.log,
|
||||
reportProgress: context.reportProgress, // Pass reportProgress through
|
||||
session: context.session // Pass session through if needed by the operationFn
|
||||
};
|
||||
this.operations.set(operationId, operation);
|
||||
this.log(operationId, 'info', `Operation added.`);
|
||||
|
||||
// Start execution in the background (don't await here)
|
||||
this._runOperation(operationId, operationFn, args, context).catch(err => {
|
||||
// Catch unexpected errors during the async execution setup itself
|
||||
this.log(operationId, 'error', `Critical error starting operation: ${err.message}`, { stack: err.stack });
|
||||
operation.status = 'failed';
|
||||
operation.error = { code: 'MANAGER_EXECUTION_ERROR', message: err.message };
|
||||
operation.endTime = Date.now();
|
||||
|
||||
// Move to completed operations
|
||||
this._moveToCompleted(operationId);
|
||||
});
|
||||
|
||||
return operationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to execute the operation.
|
||||
* @param {string} operationId - The ID of the operation.
|
||||
* @param {Function} operationFn - The async function to execute.
|
||||
* @param {Object} args - Arguments for the function.
|
||||
* @param {Object} context - The original MCP tool context.
|
||||
*/
|
||||
async _runOperation(operationId, operationFn, args, context) {
|
||||
const operation = this.operations.get(operationId);
|
||||
if (!operation) return; // Should not happen
|
||||
|
||||
operation.status = 'running';
|
||||
this.log(operationId, 'info', `Operation running.`);
|
||||
this.emit('statusChanged', { operationId, status: 'running' });
|
||||
|
||||
try {
|
||||
// Pass the necessary context parts to the direct function
|
||||
// The direct function needs to be adapted if it needs reportProgress
|
||||
// We pass the original context's log, plus our wrapped reportProgress
|
||||
const result = await operationFn(args, operation.log, {
|
||||
reportProgress: (progress) => this._handleProgress(operationId, progress),
|
||||
mcpLog: operation.log, // Pass log as mcpLog if direct fn expects it
|
||||
session: operation.session
|
||||
});
|
||||
|
||||
operation.status = result.success ? 'completed' : 'failed';
|
||||
operation.result = result.success ? result.data : null;
|
||||
operation.error = result.success ? null : result.error;
|
||||
this.log(operationId, 'info', `Operation finished with status: ${operation.status}`);
|
||||
|
||||
} catch (error) {
|
||||
this.log(operationId, 'error', `Operation failed with error: ${error.message}`, { stack: error.stack });
|
||||
operation.status = 'failed';
|
||||
operation.error = { code: 'OPERATION_EXECUTION_ERROR', message: error.message };
|
||||
} finally {
|
||||
operation.endTime = Date.now();
|
||||
this.emit('statusChanged', { operationId, status: operation.status, result: operation.result, error: operation.error });
|
||||
|
||||
// Move to completed operations if done or failed
|
||||
if (operation.status === 'completed' || operation.status === 'failed') {
|
||||
this._moveToCompleted(operationId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move an operation from active operations to completed operations history.
|
||||
* @param {string} operationId - The ID of the operation to move.
|
||||
* @private
|
||||
*/
|
||||
_moveToCompleted(operationId) {
|
||||
const operation = this.operations.get(operationId);
|
||||
if (!operation) return;
|
||||
|
||||
// Store only the necessary data in completed operations
|
||||
const completedData = {
|
||||
id: operation.id,
|
||||
status: operation.status,
|
||||
startTime: operation.startTime,
|
||||
endTime: operation.endTime,
|
||||
result: operation.result,
|
||||
error: operation.error,
|
||||
};
|
||||
|
||||
this.completedOperations.set(operationId, completedData);
|
||||
this.operations.delete(operationId);
|
||||
|
||||
// Trim completed operations if exceeding maximum
|
||||
if (this.completedOperations.size > this.maxCompletedOperations) {
|
||||
// Get the oldest operation (sorted by endTime)
|
||||
const oldest = [...this.completedOperations.entries()]
|
||||
.sort((a, b) => a[1].endTime - b[1].endTime)[0];
|
||||
|
||||
if (oldest) {
|
||||
this.completedOperations.delete(oldest[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles progress updates from the running operation and forwards them.
|
||||
* @param {string} operationId - The ID of the operation reporting progress.
|
||||
* @param {Object} progress - The progress object { progress, total? }.
|
||||
*/
|
||||
_handleProgress(operationId, progress) {
|
||||
const operation = this.operations.get(operationId);
|
||||
if (operation && operation.reportProgress) {
|
||||
try {
|
||||
// Use the reportProgress function captured from the original context
|
||||
operation.reportProgress(progress);
|
||||
this.log(operationId, 'debug', `Reported progress: ${JSON.stringify(progress)}`);
|
||||
} catch(err) {
|
||||
this.log(operationId, 'warn', `Failed to report progress: ${err.message}`);
|
||||
// Don't stop the operation, just log the reporting failure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the status and result/error of an operation.
|
||||
* @param {string} operationId - The ID of the operation.
|
||||
* @returns {Object | null} The operation details or null if not found.
|
||||
*/
|
||||
getStatus(operationId) {
|
||||
// First check active operations
|
||||
const operation = this.operations.get(operationId);
|
||||
if (operation) {
|
||||
return {
|
||||
id: operation.id,
|
||||
status: operation.status,
|
||||
startTime: operation.startTime,
|
||||
endTime: operation.endTime,
|
||||
result: operation.result,
|
||||
error: operation.error,
|
||||
};
|
||||
}
|
||||
|
||||
// Then check completed operations
|
||||
const completedOperation = this.completedOperations.get(operationId);
|
||||
if (completedOperation) {
|
||||
return completedOperation;
|
||||
}
|
||||
|
||||
// Operation not found in either active or completed
|
||||
return {
|
||||
error: {
|
||||
code: 'OPERATION_NOT_FOUND',
|
||||
message: `Operation ID ${operationId} not found. It may have been completed and removed from history, or the ID may be invalid.`
|
||||
},
|
||||
status: 'not_found'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal logging helper to prefix logs with the operation ID.
|
||||
* @param {string} operationId - The ID of the operation.
|
||||
* @param {'info'|'warn'|'error'|'debug'} level - Log level.
|
||||
* @param {string} message - Log message.
|
||||
* @param {Object} [meta] - Additional metadata.
|
||||
*/
|
||||
log(operationId, level, message, meta = {}) {
|
||||
const operation = this.operations.get(operationId);
|
||||
// Use the logger instance associated with the operation if available, otherwise console
|
||||
const logger = operation?.log || console;
|
||||
const logFn = logger[level] || logger.log || console.log; // Fallback
|
||||
logFn(`[AsyncOp ${operationId}] ${message}`, meta);
|
||||
}
|
||||
|
||||
// --- Basic Event Emitter ---
|
||||
on(eventName, listener) {
|
||||
if (!this.listeners.has(eventName)) {
|
||||
this.listeners.set(eventName, []);
|
||||
}
|
||||
this.listeners.get(eventName).push(listener);
|
||||
}
|
||||
|
||||
emit(eventName, data) {
|
||||
if (this.listeners.has(eventName)) {
|
||||
this.listeners.get(eventName).forEach(listener => listener(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export a singleton instance
|
||||
const asyncOperationManager = new AsyncOperationManager();
|
||||
|
||||
// Export the manager and potentially the class if needed elsewhere
|
||||
export { asyncOperationManager, AsyncOperationManager };
|
||||
Reference in New Issue
Block a user