@lhi/n8m 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,136 @@
1
+ import { Args, Command, Flags } from '@oclif/core';
2
+ import { theme } from '../utils/theme.js';
3
+ import { N8nClient } from '../utils/n8nClient.js';
4
+ import { ConfigManager } from '../utils/config.js';
5
+ import { DocService } from '../services/doc.service.js';
6
+ import * as path from 'path';
7
+ import * as fs from 'fs/promises';
8
+ import { existsSync } from 'fs';
9
+ import inquirer from 'inquirer';
10
+ export default class Doc extends Command {
11
+ static args = {
12
+ workflow: Args.string({
13
+ description: 'Path or Name of the workflow to document',
14
+ required: false,
15
+ }),
16
+ };
17
+ static description = 'Generate visual and text documentation for n8n workflows';
18
+ static flags = {
19
+ output: Flags.string({
20
+ char: 'o',
21
+ description: 'Output directory for documentation (defaults to ./docs)',
22
+ }),
23
+ };
24
+ async run() {
25
+ const { args, flags } = await this.parse(Doc);
26
+ this.log(theme.brand());
27
+ this.log(theme.header('WORKFLOW DOCUMENTATION'));
28
+ // 1. Load Credentials & Client
29
+ const config = await ConfigManager.load();
30
+ const n8nUrl = config.n8nUrl || process.env.N8N_API_URL;
31
+ const n8nKey = config.n8nKey || process.env.N8N_API_KEY;
32
+ if (!n8nUrl || !n8nKey) {
33
+ this.error('Credentials missing. Configure environment via \'n8m config\'.');
34
+ }
35
+ const client = new N8nClient({ apiUrl: n8nUrl, apiKey: n8nKey });
36
+ const docService = DocService.getInstance();
37
+ // 2. Resolve Workflow
38
+ let workflowData;
39
+ let workflowName = 'Untitled';
40
+ let localPath = null;
41
+ if (args.workflow && existsSync(args.workflow)) {
42
+ const content = await fs.readFile(args.workflow, 'utf-8');
43
+ workflowData = JSON.parse(content);
44
+ workflowName = workflowData.name || path.basename(args.workflow, '.json');
45
+ localPath = path.resolve(args.workflow);
46
+ }
47
+ else {
48
+ this.log(theme.info('Searching for local and remote workflows...'));
49
+ const localChoices = [];
50
+ const workflowsDir = path.join(process.cwd(), 'workflows');
51
+ if (existsSync(workflowsDir)) {
52
+ // Scan for loose files AND directory-based workflows
53
+ const entries = await fs.readdir(workflowsDir, { withFileTypes: true });
54
+ for (const entry of entries) {
55
+ if (entry.isFile() && entry.name.endsWith('.json')) {
56
+ localChoices.push({
57
+ name: `${theme.value('[LOCAL]')} ${entry.name}`,
58
+ value: { type: 'local', path: path.join(workflowsDir, entry.name) }
59
+ });
60
+ }
61
+ else if (entry.isDirectory()) {
62
+ const subPath = path.join(workflowsDir, entry.name, 'workflow.json');
63
+ if (existsSync(subPath)) {
64
+ localChoices.push({
65
+ name: `${theme.value('[LOCAL]')} ${entry.name}/workflow.json`,
66
+ value: { type: 'local', path: subPath }
67
+ });
68
+ }
69
+ }
70
+ }
71
+ }
72
+ const remoteWorkflows = await client.getWorkflows();
73
+ const remoteChoices = remoteWorkflows
74
+ .map(w => ({
75
+ name: `${theme.info('[n8n]')} ${w.name} (${w.id}) ${w.active ? '[Active]' : ''}`,
76
+ value: { type: 'remote', id: w.id, data: w }
77
+ }));
78
+ const choices = [
79
+ ...(localChoices.length > 0 ? [new inquirer.Separator('--- Local Files ---'), ...localChoices] : []),
80
+ ...(remoteChoices.length > 0 ? [new inquirer.Separator('--- n8n Instance ---'), ...remoteChoices] : []),
81
+ ];
82
+ if (choices.length === 0)
83
+ this.error('No workflows found sequence.');
84
+ const { selection } = await inquirer.prompt([{
85
+ type: 'select',
86
+ name: 'selection',
87
+ message: 'Select a workflow to document:',
88
+ choices,
89
+ pageSize: 15
90
+ }]);
91
+ if (selection.type === 'local') {
92
+ const content = await fs.readFile(selection.path, 'utf-8');
93
+ workflowData = JSON.parse(content);
94
+ workflowName = workflowData.name || path.basename(selection.path, '.json');
95
+ localPath = selection.path;
96
+ }
97
+ else {
98
+ workflowData = await client.getWorkflow(selection.id);
99
+ workflowName = workflowData.name || 'Remote Workflow';
100
+ }
101
+ }
102
+ // 3. Prepare Folder Structure
103
+ const slug = docService.generateSlug(workflowName);
104
+ const workflowsDir = path.join(process.cwd(), 'workflows');
105
+ const targetDir = path.join(workflowsDir, slug);
106
+ const targetJsonPath = path.join(targetDir, 'workflow.json');
107
+ const targetReadmePath = path.join(targetDir, 'README.md');
108
+ await fs.mkdir(targetDir, { recursive: true });
109
+ // Move or Save JSON
110
+ if (localPath) {
111
+ if (path.resolve(localPath) !== path.resolve(targetJsonPath)) {
112
+ this.log(theme.info(`Moving workflow to: ${theme.value(targetJsonPath)}`));
113
+ await fs.writeFile(targetJsonPath, JSON.stringify(workflowData, null, 2));
114
+ // Only delete if it's a loose file in workflows/ or provided path
115
+ if (path.resolve(localPath) !== path.resolve(targetJsonPath)) {
116
+ await fs.unlink(localPath);
117
+ }
118
+ }
119
+ }
120
+ else {
121
+ this.log(theme.info(`Saving remote workflow to: ${theme.value(targetJsonPath)}`));
122
+ await fs.writeFile(targetJsonPath, JSON.stringify(workflowData, null, 2));
123
+ }
124
+ // 4. Generate & Save Documentation
125
+ this.log(theme.agent(`Generating diagrams and summary for: "${workflowName}"...`));
126
+ const mermaid = docService.generateMermaid(workflowData);
127
+ const readme = await docService.generateReadme(workflowData);
128
+ const fullDoc = `# ${workflowName}\n\n## Visual Flow\n\n\`\`\`mermaid\n${mermaid}\`\`\`\n\n${readme}`;
129
+ await fs.writeFile(targetReadmePath, fullDoc);
130
+ this.log(theme.success(`✔ Documentation Generated & Organized.`));
131
+ this.log(`${theme.label('Folder')} ${theme.value(targetDir)}`);
132
+ this.log(theme.divider());
133
+ this.log(theme.subHeader('Mermaid Diagram Preview:'));
134
+ this.log(theme.muted(mermaid));
135
+ }
136
+ }
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class MCP extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,25 @@
1
+ import { Command } from '@oclif/core';
2
+ import { theme } from '../utils/theme.js';
3
+ import { MCPService } from '../services/mcp.service.js';
4
+ export default class MCP extends Command {
5
+ static description = 'Launch the n8m MCP (Model Context Protocol) server';
6
+ static examples = [
7
+ '<%= config.bin %> <%= command.id %>',
8
+ ];
9
+ async run() {
10
+ this.log(theme.brand());
11
+ this.log(theme.info('Starting n8m MCP Server...'));
12
+ try {
13
+ const mcpService = new MCPService();
14
+ await mcpService.start();
15
+ // Wait for interrupt (Process will stay alive due to stdio transport)
16
+ process.on('SIGINT', () => {
17
+ this.log(theme.info('\nStopping MCP Server...'));
18
+ process.exit(0);
19
+ });
20
+ }
21
+ catch (error) {
22
+ this.error(`Failed to start MCP Server: ${error.message}`);
23
+ }
24
+ }
25
+ }
@@ -20,7 +20,7 @@ export default class Modify extends Command {
20
20
  required: false,
21
21
  }),
22
22
  };
23
- static description = 'Modify existing n8n workflows using Gemini AI Agent';
23
+ static description = 'Modify existing n8n workflows using an AI Agent';
24
24
  static flags = {
25
25
  multiline: Flags.boolean({
26
26
  char: 'm',
@@ -21,8 +21,47 @@ export default class Resume extends Command {
21
21
  return;
22
22
  }
23
23
  this.log(theme.info(`Workflow is paused at: ${next.join(', ')}`));
24
- this.log(theme.agent("Resuming..."));
25
24
  try {
25
+ if (next.includes('engineer')) {
26
+ const state = snapshot.values;
27
+ if (state.strategies && state.strategies.length > 0) {
28
+ this.log(theme.header('\nPAUSED STRATEGIES:'));
29
+ state.strategies.forEach((s, i) => {
30
+ this.log(`${i === 0 ? theme.success(' [Primary]') : theme.info(' [Alternative]')} ${theme.value(s.suggestedName)}`);
31
+ this.log(` Description: ${s.description}`);
32
+ if (s.nodes && s.nodes.length > 0) {
33
+ this.log(` Proposed Nodes: ${s.nodes.map((n) => n.type.split('.').pop()).join(', ')}`);
34
+ }
35
+ this.log('');
36
+ });
37
+ const { action } = await (await import('inquirer')).default.prompt([{
38
+ type: 'list',
39
+ name: 'action',
40
+ message: 'How would you like to proceed?',
41
+ choices: [
42
+ { name: 'Approve and Generate Workflow', value: 'approve' },
43
+ { name: 'Provide Feedback / Refine Strategy', value: 'feedback' },
44
+ { name: 'Exit', value: 'exit' }
45
+ ]
46
+ }]);
47
+ if (action === 'approve') {
48
+ this.log(theme.agent("Approve! Resuming..."));
49
+ await graph.updateState({ configurable: { thread_id: threadId } }, { userFeedback: undefined }, 'engineer');
50
+ }
51
+ else if (action === 'feedback') {
52
+ const { feedback } = await (await import('inquirer')).default.prompt([{
53
+ type: 'input',
54
+ name: 'feedback',
55
+ message: 'Enter your feedback/instructions:',
56
+ }]);
57
+ await graph.updateState({ configurable: { thread_id: threadId } }, { userFeedback: feedback }, 'engineer');
58
+ }
59
+ else {
60
+ return;
61
+ }
62
+ }
63
+ }
64
+ this.log(theme.agent("Resuming..."));
26
65
  const result = await resumeAgenticWorkflow(threadId);
27
66
  if (result.validationStatus === 'passed') {
28
67
  this.log(theme.success("Workflow completed successfully!"));
@@ -9,6 +9,7 @@ export default class Test extends Command {
9
9
  'keep-on-fail': import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
10
  'no-brand': import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
11
  'validate-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ 'ai-scenarios': import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
13
  };
13
14
  run(): Promise<void>;
14
15
  /**
@@ -3,6 +3,8 @@ import { Args, Command, Flags } from '@oclif/core';
3
3
  import { theme } from '../utils/theme.js';
4
4
  import { N8nClient } from '../utils/n8nClient.js';
5
5
  import { ConfigManager } from '../utils/config.js';
6
+ import { AIService } from '../services/ai.service.js';
7
+ import { DocService } from '../services/doc.service.js';
6
8
  import { runAgenticWorkflow, graph, resumeAgenticWorkflow } from '../agentic/graph.js';
7
9
  import * as path from 'path';
8
10
  import * as fs from 'fs/promises';
@@ -35,6 +37,10 @@ export default class Test extends Command {
35
37
  hidden: true,
36
38
  description: 'Execute test but do not prompt for deploy/save actions',
37
39
  }),
40
+ 'ai-scenarios': Flags.boolean({
41
+ default: false,
42
+ description: 'Generate 3 diverse AI test scenarios (happy path, edge case, error)',
43
+ }),
38
44
  };
39
45
  async run() {
40
46
  const { args, flags } = await this.parse(Test);
@@ -50,6 +56,7 @@ export default class Test extends Command {
50
56
  this.error('Credentials missing. Configure environment via \'n8m config\'.');
51
57
  }
52
58
  const client = new N8nClient({ apiUrl: n8nUrl, apiKey: n8nKey });
59
+ const aiService = AIService.getInstance();
53
60
  // 1a. Fetch Valid Node Types (New)
54
61
  let validNodeTypes = [];
55
62
  try {
@@ -354,12 +361,19 @@ export default class Test extends Command {
354
361
  this.log(theme.subHeader('AGENTIC VALIDATION'));
355
362
  this.log(theme.agent("Initializing Agentic Workflow to validate/repair this workflow..."));
356
363
  const goal = `Validate and fix the workflow named "${workflowName}"`;
364
+ let testScenarios = [];
365
+ if (flags['ai-scenarios']) {
366
+ this.log(theme.agent("Generating AI test scenarios..."));
367
+ testScenarios = await aiService.generateTestScenarios(workflowData, goal);
368
+ this.log(theme.muted(`Generated ${testScenarios.length} scenarios.`));
369
+ }
357
370
  const initialState = {
358
371
  userGoal: goal,
359
372
  messages: [],
360
373
  validationErrors: [],
361
374
  workflowJson: workflowData,
362
- availableNodeTypes: validNodeTypes
375
+ availableNodeTypes: validNodeTypes,
376
+ testScenarios: testScenarios
363
377
  };
364
378
  // We need to route the graph logger to our CLI logger if possible, or just let it print to stdout
365
379
  // The graph uses console.log currently, which is fine.
@@ -540,20 +554,38 @@ export default class Test extends Command {
540
554
  }]);
541
555
  if (!save)
542
556
  return;
557
+ const docService = DocService.getInstance();
543
558
  for (const [, def] of deployedDefinitions.entries()) {
544
559
  const cleanData = this.sanitizeWorkflow(this.stripShim(def.data));
545
- cleanData.name = def.name;
546
- const targetPath = originalPath && def.type === 'root' ? originalPath : path.join(process.cwd(), 'workflows', `${def.name.replace(/[^a-z0-9]/gi, '_').toLowerCase()}.json`);
560
+ // Use AI to suggest title if it looks like a temporary name
561
+ let workflowName = def.name;
562
+ if (workflowName.startsWith('[n8m:test]') || workflowName.includes('Agentic_Test')) {
563
+ this.log(theme.agent("Suggesting professional project title..."));
564
+ workflowName = await docService.generateProjectTitle(cleanData);
565
+ }
566
+ cleanData.name = workflowName;
567
+ const slug = docService.generateSlug(workflowName);
568
+ const targetDir = path.join(process.cwd(), 'workflows', slug);
569
+ const targetPath = path.join(targetDir, 'workflow.json');
547
570
  const { confirmPath } = await inquirer.prompt([{
548
571
  type: 'input',
549
572
  name: 'confirmPath',
550
- message: `Save '${def.name}' to:`,
573
+ message: `Save '${workflowName}' to:`,
551
574
  default: targetPath
552
575
  }]);
553
576
  try {
554
577
  await fs.mkdir(path.dirname(confirmPath), { recursive: true });
555
578
  await fs.writeFile(confirmPath, JSON.stringify(cleanData, null, 2));
556
579
  this.log(theme.success(`Saved to ${confirmPath}`));
580
+ // Optionally generate doc if it's a new directory
581
+ const readmePath = path.join(path.dirname(confirmPath), 'README.md');
582
+ if (!existsSync(readmePath)) {
583
+ this.log(theme.agent("Generating initial documentation..."));
584
+ const mermaid = docService.generateMermaid(cleanData);
585
+ const readmeContent = await docService.generateReadme(cleanData);
586
+ const fullDoc = `# ${workflowName}\n\n## Visual Flow\n\n\`\`\`mermaid\n${mermaid}\`\`\`\n\n${readmeContent}`;
587
+ await fs.writeFile(readmePath, fullDoc);
588
+ }
557
589
  }
558
590
  catch (e) {
559
591
  this.log(theme.fail(`Failed to save: ${e.message}`));
@@ -0,0 +1,213 @@
1
+ [
2
+ {
3
+ "name": "n8n-nodes-base.start",
4
+ "displayName": "Manual Trigger",
5
+ "description": "The starting point for manual execution.",
6
+ "properties": []
7
+ },
8
+ {
9
+ "name": "n8n-nodes-base.httpRequest",
10
+ "displayName": "HTTP Request",
11
+ "description": "Send HTTP requests to any API",
12
+ "properties": [
13
+ {
14
+ "name": "method",
15
+ "displayName": "Method",
16
+ "type": "options",
17
+ "default": "GET",
18
+ "options": [
19
+ { "name": "GET", "value": "GET" },
20
+ { "name": "POST", "value": "POST" },
21
+ { "name": "PUT", "value": "PUT" },
22
+ { "name": "DELETE", "value": "DELETE" }
23
+ ]
24
+ },
25
+ {
26
+ "name": "url",
27
+ "displayName": "URL",
28
+ "type": "string",
29
+ "default": "",
30
+ "description": "The URL to send the request to"
31
+ },
32
+ {
33
+ "name": "authentication",
34
+ "displayName": "Authentication",
35
+ "type": "options",
36
+ "default": "none",
37
+ "options": [
38
+ { "name": "None", "value": "none" },
39
+ { "name": "Predefined Credential Type", "value": "predefinedCredentialType" }
40
+ ]
41
+ },
42
+ {
43
+ "name": "sendBody",
44
+ "displayName": "Send Body",
45
+ "type": "boolean",
46
+ "default": false
47
+ },
48
+ {
49
+ "name": "body",
50
+ "displayName": "Body",
51
+ "type": "json",
52
+ "default": ""
53
+ }
54
+ ]
55
+ },
56
+ {
57
+ "name": "n8n-nodes-base.slack",
58
+ "displayName": "Slack",
59
+ "description": "Interact with Slack",
60
+ "properties": [
61
+ {
62
+ "name": "resource",
63
+ "displayName": "Resource",
64
+ "type": "options",
65
+ "default": "message",
66
+ "options": [
67
+ { "name": "Message", "value": "message" },
68
+ { "name": "Channel", "value": "channel" }
69
+ ]
70
+ },
71
+ {
72
+ "name": "operation",
73
+ "displayName": "Operation",
74
+ "type": "options",
75
+ "default": "post",
76
+ "options": [
77
+ { "name": "Post", "value": "post" },
78
+ { "name": "Update", "value": "update" }
79
+ ]
80
+ },
81
+ {
82
+ "name": "channel",
83
+ "displayName": "Channel",
84
+ "type": "string",
85
+ "default": ""
86
+ },
87
+ {
88
+ "name": "text",
89
+ "displayName": "Text",
90
+ "type": "string",
91
+ "default": ""
92
+ }
93
+ ]
94
+ },
95
+ {
96
+ "name": "n8n-nodes-base.googleSheets",
97
+ "displayName": "Google Sheets",
98
+ "description": "Interact with Google Sheets",
99
+ "properties": [
100
+ {
101
+ "name": "resource",
102
+ "displayName": "Resource",
103
+ "type": "options",
104
+ "default": "sheet",
105
+ "options": [
106
+ { "name": "Sheet", "value": "sheet" }
107
+ ]
108
+ },
109
+ {
110
+ "name": "operation",
111
+ "displayName": "Operation",
112
+ "type": "options",
113
+ "default": "append",
114
+ "options": [
115
+ { "name": "Append", "value": "append" },
116
+ { "name": "Read", "value": "read" },
117
+ { "name": "Update", "value": "update" }
118
+ ]
119
+ },
120
+ {
121
+ "name": "sheetId",
122
+ "displayName": "Sheet ID",
123
+ "type": "string",
124
+ "default": ""
125
+ },
126
+ {
127
+ "name": "range",
128
+ "displayName": "Range",
129
+ "type": "string",
130
+ "default": "Sheet1!A:Z"
131
+ }
132
+ ]
133
+ },
134
+ {
135
+ "name": "n8n-nodes-base.if",
136
+ "displayName": "IF",
137
+ "description": "Conditional logic: True/False",
138
+ "properties": [
139
+ {
140
+ "name": "conditions",
141
+ "displayName": "Conditions",
142
+ "type": "json",
143
+ "default": {}
144
+ }
145
+ ]
146
+ },
147
+ {
148
+ "name": "n8n-nodes-base.set",
149
+ "displayName": "Set",
150
+ "description": "Set variables or values",
151
+ "properties": [
152
+ {
153
+ "name": "values",
154
+ "displayName": "Values",
155
+ "type": "fixedCollection",
156
+ "default": {}
157
+ }
158
+ ]
159
+ },
160
+ {
161
+ "name": "n8n-nodes-base.webhook",
162
+ "displayName": "Webhook",
163
+ "description": "Receive HTTP requests",
164
+ "properties": [
165
+ {
166
+ "name": "httpMethod",
167
+ "displayName": "HTTP Method",
168
+ "type": "options",
169
+ "default": "GET",
170
+ "options": [
171
+ { "name": "GET", "value": "GET" },
172
+ { "name": "POST", "value": "POST" }
173
+ ]
174
+ },
175
+ {
176
+ "name": "path",
177
+ "displayName": "Path",
178
+ "type": "string",
179
+ "default": ""
180
+ }
181
+ ]
182
+ },
183
+ {
184
+ "name": "n8n-nodes-base.code",
185
+ "displayName": "Code",
186
+ "description": "Run JavaScript/TypeScript",
187
+ "properties": [
188
+ {
189
+ "name": "jsCode",
190
+ "displayName": "JS Code",
191
+ "type": "string",
192
+ "default": "// Your code here\nreturn items;"
193
+ }
194
+ ]
195
+ },
196
+ {
197
+ "name": "n8n-nodes-base.merge",
198
+ "displayName": "Merge",
199
+ "description": "Merge data from multiple inputs",
200
+ "properties": [
201
+ {
202
+ "name": "mode",
203
+ "displayName": "Mode",
204
+ "type": "options",
205
+ "default": "append",
206
+ "options": [
207
+ { "name": "Append", "value": "append" },
208
+ { "name": "Merge by Index", "value": "mergeByIndex" }
209
+ ]
210
+ }
211
+ ]
212
+ }
213
+ ]
@@ -1,64 +1,55 @@
1
1
  export interface GenerateOptions {
2
2
  model?: string;
3
+ provider?: string;
3
4
  temperature?: number;
4
5
  }
6
+ export interface WorkflowSpec {
7
+ suggestedName: string;
8
+ description: string;
9
+ nodes: {
10
+ type: string;
11
+ purpose: string;
12
+ config?: any;
13
+ }[];
14
+ questions?: string[];
15
+ strategyName?: string;
16
+ aiModel?: string;
17
+ aiProvider?: string;
18
+ }
19
+ export declare const PROVIDER_PRESETS: Record<string, {
20
+ baseURL?: string;
21
+ defaultModel: string;
22
+ models: string[];
23
+ }>;
5
24
  export declare class AIService {
6
25
  private static instance;
7
- private client;
26
+ private clients;
27
+ private defaultProvider;
8
28
  private model;
29
+ private apiKey;
30
+ private baseURL?;
9
31
  private constructor();
32
+ private getClient;
10
33
  static getInstance(): AIService;
11
- /**
12
- * Core generation method — works with any OpenAI-compatible API
13
- */
34
+ private callAnthropicNative;
14
35
  generateContent(prompt: string, options?: GenerateOptions): Promise<string>;
15
- /**
16
- * Generate an n8n workflow from a description
17
- */
18
- generateWorkflow(description: string): Promise<any>;
19
- /**
20
- * Generate a Workflow Specification from a description
21
- */
22
- generateSpec(description: string): Promise<any>;
23
- /**
24
- * Refine a Specification based on user feedback
25
- */
26
- refineSpec(spec: any, feedback: string): Promise<any>;
27
- /**
28
- * Generate workflow JSONs from an approved Specification
29
- */
30
- generateWorkflowFromSpec(spec: any): Promise<any>;
31
- /**
32
- * Generate mock data for a workflow execution
33
- */
34
- generateMockData(context: string, previousFailures?: string[]): Promise<any>;
35
- /**
36
- * Diagnostic Repair: Fix a workflow based on execution error
37
- */
38
- generateWorkflowFix(workflowJson: any, errorContext: string, model?: string, _useSearch?: boolean, validNodeTypes?: string[]): Promise<any>;
39
- /**
40
- * Auto-correct common n8n node type hallucinations
41
- */
42
- private fixHallucinatedNodes;
43
- /**
44
- * Force-fix connection structure to prevent "object is not iterable" errors
45
- */
46
- private fixN8nConnections;
47
- /**
48
- * Generate an alternative workflow specification with a different approach to the same goal.
49
- * Used by the Architect node to produce a second strategy for parallel Engineer execution.
50
- */
51
- generateAlternativeSpec(goal: string, primarySpec: any): Promise<any>;
52
- /**
53
- * Evaluate multiple workflow candidates and select the best one for the given goal.
54
- * Used by the Supervisor node to choose between parallel Engineer outputs.
55
- */
36
+ getAlternativeModel(): string;
37
+ getDefaultModel(): string;
38
+ getDefaultProvider(): string;
39
+ generateSpec(goal: string): Promise<WorkflowSpec>;
40
+ generateWorkflow(goal: string): Promise<any>;
41
+ generateAlternativeSpec(goal: string, primarySpec: WorkflowSpec): Promise<WorkflowSpec>;
42
+ generateWorkflowFix(workflow: any, error: string, model?: string, _stream?: boolean, validNodeTypes?: string[]): Promise<any>;
43
+ validateAndShim(workflow: any, validNodeTypes?: string[], explicitlyInvalid?: string[]): any;
44
+ fixHallucinatedNodes(workflow: any): any;
45
+ fixN8nConnections(workflow: any): any;
46
+ generateMockData(context: string): Promise<any>;
56
47
  evaluateCandidates(goal: string, candidates: any[]): Promise<{
57
48
  selectedIndex: number;
58
49
  reason: string;
59
50
  }>;
60
51
  /**
61
- * Validate against real node types and shim unknown ones
52
+ * Generates 3-5 diverse test scenarios (input payloads) for a workflow.
62
53
  */
63
- validateAndShim(workflow: any, validNodeTypes?: string[], explicitlyInvalid?: string[]): any;
54
+ generateTestScenarios(workflowJson: any, goal: string): Promise<any[]>;
64
55
  }