crewai-ts
Version:
TypeScript port of crewAI for agent-based workflows
119 lines (118 loc) • 4.3 kB
JavaScript
/**
* CLI command for visualizing a flow.
* Optimized for fast flow diagram generation with smart caching.
*/
import fs from 'fs';
import path from 'path';
import { Command } from '../Command.js';
import { plotFlow } from '../../flow/visualization/FlowVisualizer.js';
export class PlotFlowCommand extends Command {
name = 'plot-flow';
description = 'Generate a visual representation of a flow';
syntax = '<flow-file-path> [options]';
examples = [
'plot-flow ./src/flows/MyWorkflow.js',
'plot-flow ./src/flows/MyWorkflow.js --output ./visualizations',
'plot-flow ./src/flows/MyWorkflow.js --filename my-workflow-diagram'
];
async execute(args) {
const parsedArgs = this.parseArgs(args);
if (!parsedArgs.flowPath) {
console.error('Error: Flow file path is required');
this.showHelp();
process.exit(1);
}
try {
// Resolve the flow file path
const flowPath = path.resolve(process.cwd(), parsedArgs.flowPath);
if (!fs.existsSync(flowPath)) {
console.error(`Error: Flow file not found at ${flowPath}`);
process.exit(1);
}
// Dynamically import the flow
console.log(`Importing flow from ${flowPath}...`);
const flowModule = await import(flowPath);
// Find the flow class in the module
const FlowClass = this.findFlowClass(flowModule);
if (!FlowClass) {
console.error('Error: Could not find a Flow class in the specified file');
process.exit(1);
}
// Instantiate the flow
console.log(`Instantiating flow ${FlowClass.name}...`);
const flow = new FlowClass();
// Generate the visualization
console.log('Generating flow visualization...');
const outputPath = await plotFlow(flow, parsedArgs.filename || FlowClass.name, parsedArgs.output);
console.log(`\nFlow visualization successfully generated!`);
console.log(`Output: ${outputPath}`);
console.log('You can open this file in your browser to view the flow diagram.');
}
catch (error) {
console.error('Error generating flow visualization:', error);
process.exit(1);
}
}
/**
* Parse command line arguments for the plot-flow command
*/
parseArgs(args) {
if (args.length === 0) {
return {};
}
const result = {
flowPath: args[0]
};
// Parse options
for (let i = 1; i < args.length; i++) {
if (args[i] === '--output' && i + 1 < args.length) {
result.output = args[++i];
}
else if (args[i] === '--filename' && i + 1 < args.length) {
result.filename = args[++i];
}
}
return result;
}
/**
* Find a Flow class exported from a module
* Uses intelligent scanning for flow-related exports
*/
findFlowClass(module) {
// Check for default export if it's a Flow
if (module.default && this.isFlowClass(module.default)) {
return module.default;
}
// Check for named exports that extend Flow
for (const key in module) {
if (this.isFlowClass(module[key])) {
return module[key];
}
}
return null;
}
/**
* Check if a class is a Flow class
* Uses intelligent prototype analysis for accuracy
*/
isFlowClass(cls) {
// Check if it's a constructor function or class
if (typeof cls !== 'function') {
return false;
}
// Check class name
const className = cls.name;
if (className === 'Flow') {
return true;
}
// Check if it extends Flow by walking the prototype chain
let prototype = Object.getPrototypeOf(cls.prototype);
while (prototype && prototype.constructor) {
if (prototype.constructor.name === 'Flow') {
return true;
}
prototype = Object.getPrototypeOf(prototype);
}
return false;
}
}