crewai-ts
Version:
TypeScript port of crewAI for agent-based workflows
139 lines (138 loc) ⢠6.34 kB
JavaScript
/**
* CLI command for replaying a crew execution from a specific task.
* Optimized for performance and debugging.
* Memory-optimized with proper type safety.
*/
import { Command } from '../Command.js';
import { setInterval, clearInterval } from 'node:timers';
export class ReplayTaskCommand extends Command {
name = 'replay-task';
description = 'Replay the crew execution from a specific task ID';
syntax = '<task-id> [options]';
examples = [
'replay-task task_123456',
'replay-task task_123456 --verbose'
];
async execute(args) {
const parsedArgs = this.parseArgs(args);
if (!parsedArgs.taskId) {
console.error('Error: Task ID is required');
this.showHelp();
// Use explicit process.exit with proper type signatures
process.exit(1);
}
try {
// Dynamically import required modules only when needed (optimization)
const { default: chalk } = await import('chalk');
const { default: ora } = await import('ora');
// Show spinner for better UX
const spinner = ora('Preparing task replay...').start();
// Import memory storage to load the task with proper type checking
const { KickoffTaskOutputsStorage } = await import('../../memory/storage/KickoffTaskOutputsStorage.js');
// Ensure task ID is defined with type safety
const taskId = parsedArgs.taskId || '';
// Load the task outputs with memory-optimized type handling
spinner.text = 'Loading task data...';
const storage = new KickoffTaskOutputsStorage();
const task = await storage.getTaskById(taskId);
if (!task) {
spinner.fail(`Task with ID ${taskId} not found`);
// Use explicit process.exit with proper type signatures
process.exit(1);
}
// Load the crew module for replay with proper type checking
spinner.text = 'Loading crew module...';
// Create a fallback implementation if the module can't be loaded
const createFallbackService = () => {
class FallbackTaskReplayService {
async replayFromTask(taskId, options) {
console.warn('Using fallback replay service - some features may be limited');
return { success: false, message: 'TaskReplayService module could not be loaded' };
}
}
return FallbackTaskReplayService;
};
// Try to load the module with memory-safe implementation
let TaskReplayServiceClass;
try {
// Dynamic import with proper type checking
const replayModule = await import('../../services/TaskReplayService.js');
if (!replayModule || !('TaskReplayService' in replayModule)) {
throw new Error('Invalid TaskReplayService module structure');
}
TaskReplayServiceClass = replayModule.TaskReplayService;
}
catch (error) {
console.warn('Error loading TaskReplayService module:', error);
TaskReplayServiceClass = createFallbackService();
}
// Create service instance with proper type handling
const replayService = new TaskReplayServiceClass();
// Track execution progress with memory-efficient timer management
// Use proper Node.js types for timers with memory optimization
let progressInterval;
if (!parsedArgs.verbose) {
// Initialize timer with memory-optimized implementation
// Store task ID once to avoid repeated property access
const displayTaskId = parsedArgs.taskId || '';
progressInterval = setInterval(() => {
spinner.text = `Replaying from task ${displayTaskId}...`;
}, 1000);
}
else {
spinner.stop();
console.log(chalk.blue(`š Replaying from task ${chalk.bold(taskId)}...`));
}
// Execute the replay with proper null handling
const results = await replayService.replayFromTask(taskId, {
verbose: !!parsedArgs.verbose // Convert to boolean for type safety
});
// Clean up progress tracking
if (!parsedArgs.verbose && progressInterval) {
clearInterval(progressInterval);
}
// Show results
if (!parsedArgs.verbose) {
spinner.succeed(`Replay from task ${taskId} completed`);
}
else {
console.log(chalk.green(`\nā
Replay completed`));
}
console.log('\nResults:', JSON.stringify(results, null, 2));
}
catch (error) {
console.error('Error during task replay:', error);
// Use explicit process.exit with proper type signatures
process.exit(1);
}
}
/**
* Parse command line arguments for the replay-task command
* Optimized for handling various argument formats
*/
/**
* Parse command line arguments with enhanced type safety and memory optimization
* @param args Command line arguments
* @returns Parsed arguments object
*/
parseArgs(args) {
// Early return with memory-optimized empty object for no arguments
if (!Array.isArray(args) || args.length === 0) {
return {};
}
// Create result object with memory efficiency in mind
const result = {
// Only assign if args[0] is a valid string
taskId: typeof args[0] === 'string' && args[0].trim() ? args[0].trim() : undefined
};
// Parse options with type safety
for (let i = 1; i < args.length; i++) {
const arg = args[i];
// Strict equality check for argument names
if (typeof arg === 'string' && (arg === '--verbose' || arg === '-v')) {
result.verbose = true; // Set boolean flag for type safety
}
}
return result;
}
}