n8n
Version:
n8n Workflow Automation Tool
183 lines • 8.6 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TTWFGenerateCommand = void 0;
const ai_workflow_builder_1 = require("@n8n/ai-workflow-builder");
const decorators_1 = require("@n8n/decorators");
const di_1 = require("@n8n/di");
const fs_1 = __importDefault(require("fs"));
const n8n_workflow_1 = require("n8n-workflow");
const zod_1 = require("zod");
const node_types_1 = require("../../node-types");
const worker_pool_1 = require("./worker-pool");
const base_command_1 = require("../base-command");
async function waitForWorkflowGenerated(aiResponse) {
let workflowJson;
for await (const chunk of aiResponse) {
const wfGeneratedMessage = chunk.messages.find((m) => 'type' in m && m.type === 'workflow-generated');
if (wfGeneratedMessage?.codeSnippet) {
workflowJson = wfGeneratedMessage.codeSnippet;
}
}
if (!workflowJson) {
throw new n8n_workflow_1.UserError('No workflow generated message found in AI response');
}
return workflowJson;
}
const flagsSchema = zod_1.z.object({
prompt: zod_1.z
.string()
.alias('p')
.describe('Prompt to generate a workflow from. Mutually exclusive with --input.')
.optional(),
input: zod_1.z
.string()
.alias('i')
.describe('Input dataset file name. Mutually exclusive with --prompt.')
.optional(),
output: zod_1.z
.string()
.alias('o')
.describe('Output file name to save the results. Default is ttwf-results.jsonl')
.default('ttwf-results.jsonl'),
limit: zod_1.z
.number()
.int()
.alias('l')
.describe('Number of items from the dataset to process. Only valid with --input.')
.default(-1),
concurrency: zod_1.z
.number()
.int()
.alias('c')
.describe('Number of items to process in parallel. Only valid with --input.')
.default(1),
});
let TTWFGenerateCommand = class TTWFGenerateCommand extends base_command_1.BaseCommand {
async readDataset(filePath) {
try {
const data = await fs_1.default.promises.readFile(filePath, { encoding: 'utf-8' });
const lines = data.split('\n').filter((line) => line.trim() !== '');
if (lines.length === 0) {
throw new n8n_workflow_1.UserError('Dataset file is empty or contains no valid lines');
}
return lines.map((line, index) => {
try {
return (0, n8n_workflow_1.jsonParse)(line);
}
catch (error) {
throw new n8n_workflow_1.UserError(`Invalid JSON line on index: ${index}`);
}
});
}
catch (error) {
throw new n8n_workflow_1.UserError(`Failed to read dataset file: ${error}`);
}
}
async run() {
const { flags } = this;
if (!flags.input && !flags.prompt) {
throw new n8n_workflow_1.UserError('Either --input or --prompt must be provided.');
}
if (flags.input && flags.prompt) {
throw new n8n_workflow_1.UserError('You cannot use --input and --prompt together. Use one or the other.');
}
const nodeTypes = di_1.Container.get(node_types_1.NodeTypes);
const wfBuilder = new ai_workflow_builder_1.AiWorkflowBuilderService(nodeTypes);
if (flags.prompt) {
if (flags.output && fs_1.default.existsSync(flags.output)) {
if (fs_1.default.lstatSync(flags.output).isDirectory()) {
this.logger.info('The parameter --output must be a writeable file');
return;
}
this.logger.warn('The output file already exists. It will be overwritten.');
fs_1.default.unlinkSync(flags.output);
}
try {
this.logger.info(`Processing prompt: ${flags.prompt}`);
const aiResponse = wfBuilder.chat({ question: flags.prompt });
const generatedWorkflow = await waitForWorkflowGenerated(aiResponse);
this.logger.info(`Generated workflow for prompt: ${flags.prompt}`);
if (flags.output) {
fs_1.default.writeFileSync(flags.output, generatedWorkflow);
this.logger.info(`Workflow saved to ${flags.output}`);
}
else {
this.logger.info('Generated Workflow:');
this.logger.info(JSON.stringify(JSON.parse(generatedWorkflow), null, 2));
}
}
catch (e) {
const errorMessage = e instanceof Error ? e.message : 'An error occurred';
this.logger.error(`Error processing prompt "${flags.prompt}": ${errorMessage}`);
}
}
else if (flags.input) {
const output = flags.output ?? 'ttwf-results.jsonl';
if (fs_1.default.existsSync(output)) {
if (fs_1.default.lstatSync(output).isDirectory()) {
this.logger.info('The parameter --output must be a writeable file');
return;
}
this.logger.warn('The output file already exists. It will be overwritten.');
fs_1.default.unlinkSync(output);
}
const pool = new worker_pool_1.WorkerPool(flags.concurrency ?? 1);
const dataset = await this.readDataset(flags.input);
const outputStream = fs_1.default.createWriteStream(output, { flags: 'a' });
const datasetWithLimit = (flags.limit ?? -1) > 0 ? dataset.slice(0, flags.limit) : dataset;
await Promise.allSettled(datasetWithLimit.map(async (item) => {
try {
const generatedWorkflow = await pool.execute(async () => {
this.logger.info(`Processing prompt: ${item.prompt}`);
const aiResponse = wfBuilder.chat({ question: item.prompt });
return await waitForWorkflowGenerated(aiResponse);
});
this.logger.info(`Generated workflow for prompt: ${item.prompt}`);
outputStream.write(JSON.stringify({
prompt: item.prompt,
generatedWorkflow,
referenceWorkflow: item.referenceWorkflow,
}) + '\n');
}
catch (e) {
const errorMessage = e instanceof Error ? e.message : 'An error occurred';
this.logger.error(`Error processing prompt "${item.prompt}": ${errorMessage}`);
outputStream.write(JSON.stringify({
prompt: item.prompt,
referenceWorkflow: item.referenceWorkflow,
errorMessage,
}) + '\n');
}
}));
outputStream.end();
}
}
async catch(error) {
this.logger.error('\nGOT ERROR');
this.logger.error('====================================');
this.logger.error(error.message);
this.logger.error(error.stack);
}
};
exports.TTWFGenerateCommand = TTWFGenerateCommand;
exports.TTWFGenerateCommand = TTWFGenerateCommand = __decorate([
(0, decorators_1.Command)({
name: 'ttwf:generate',
description: 'Create a workflow(s) using AI Text-to-Workflow builder',
examples: [
'$ n8n ttwf:generate --prompt "Create a telegram chatbot that can tell current weather in Berlin" --output result.json',
'$ n8n ttwf:generate --input dataset.jsonl --output results.jsonl',
],
flagsSchema,
})
], TTWFGenerateCommand);
//# sourceMappingURL=generate.js.map
;