@lhi/n8m 0.2.0 → 0.2.2

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.
Files changed (37) hide show
  1. package/README.md +105 -6
  2. package/dist/agentic/graph.d.ts +50 -0
  3. package/dist/agentic/graph.js +0 -2
  4. package/dist/agentic/nodes/architect.d.ts +5 -0
  5. package/dist/agentic/nodes/architect.js +8 -22
  6. package/dist/agentic/nodes/engineer.d.ts +15 -0
  7. package/dist/agentic/nodes/engineer.js +25 -4
  8. package/dist/agentic/nodes/qa.d.ts +1 -0
  9. package/dist/agentic/nodes/qa.js +280 -45
  10. package/dist/agentic/nodes/reviewer.d.ts +4 -0
  11. package/dist/agentic/nodes/reviewer.js +71 -13
  12. package/dist/agentic/nodes/supervisor.js +2 -3
  13. package/dist/agentic/state.d.ts +1 -0
  14. package/dist/agentic/state.js +4 -0
  15. package/dist/commands/create.js +37 -3
  16. package/dist/commands/doc.js +1 -1
  17. package/dist/commands/fixture.d.ts +12 -0
  18. package/dist/commands/fixture.js +258 -0
  19. package/dist/commands/test.d.ts +63 -4
  20. package/dist/commands/test.js +1179 -90
  21. package/dist/fixture-schema.json +162 -0
  22. package/dist/resources/node-definitions-fallback.json +185 -8
  23. package/dist/resources/node-test-hints.json +188 -0
  24. package/dist/resources/workflow-test-fixtures.json +42 -0
  25. package/dist/services/ai.service.d.ts +42 -0
  26. package/dist/services/ai.service.js +271 -21
  27. package/dist/services/node-definitions.service.d.ts +1 -0
  28. package/dist/services/node-definitions.service.js +4 -11
  29. package/dist/utils/config.js +2 -0
  30. package/dist/utils/fixtureManager.d.ts +28 -0
  31. package/dist/utils/fixtureManager.js +41 -0
  32. package/dist/utils/n8nClient.d.ts +27 -0
  33. package/dist/utils/n8nClient.js +169 -5
  34. package/dist/utils/spinner.d.ts +17 -0
  35. package/dist/utils/spinner.js +52 -0
  36. package/oclif.manifest.json +49 -1
  37. package/package.json +2 -2
@@ -0,0 +1,162 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://n8m.dev/fixture-schema.json",
4
+ "title": "WorkflowFixture",
5
+ "description": "n8m fixture file — captures a real n8n execution for offline testing and replay.",
6
+ "type": "object",
7
+ "required": ["version", "workflowId", "workflowName", "workflow", "execution"],
8
+ "additionalProperties": true,
9
+ "properties": {
10
+ "$schema": {
11
+ "type": "string",
12
+ "description": "JSON Schema reference for editor autocomplete."
13
+ },
14
+ "version": {
15
+ "const": "1.0",
16
+ "description": "Fixture format version. Always \"1.0\"."
17
+ },
18
+ "capturedAt": {
19
+ "type": "string",
20
+ "format": "date-time",
21
+ "description": "ISO 8601 timestamp of when the fixture was captured."
22
+ },
23
+ "workflowId": {
24
+ "type": "string",
25
+ "description": "The n8n workflow ID this fixture was captured from."
26
+ },
27
+ "workflowName": {
28
+ "type": "string",
29
+ "description": "Human-readable workflow name."
30
+ },
31
+ "workflow": {
32
+ "type": "object",
33
+ "description": "Full n8n workflow JSON (nodes, connections, settings, etc.).",
34
+ "required": ["nodes", "connections"],
35
+ "additionalProperties": true,
36
+ "properties": {
37
+ "name": { "type": "string" },
38
+ "nodes": {
39
+ "type": "array",
40
+ "items": {
41
+ "type": "object",
42
+ "additionalProperties": true,
43
+ "required": ["name", "type"],
44
+ "properties": {
45
+ "id": { "type": "string" },
46
+ "name": { "type": "string", "description": "Unique node name within this workflow." },
47
+ "type": { "type": "string", "description": "e.g. n8n-nodes-base.code" },
48
+ "typeVersion": { "type": "number" },
49
+ "position": { "type": "array", "items": { "type": "number" }, "minItems": 2, "maxItems": 2 },
50
+ "parameters": { "type": "object", "additionalProperties": true }
51
+ }
52
+ }
53
+ },
54
+ "connections": {
55
+ "type": "object",
56
+ "description": "Maps source node names to their output connections.",
57
+ "additionalProperties": {
58
+ "type": "object",
59
+ "properties": {
60
+ "main": {
61
+ "type": "array",
62
+ "items": {
63
+ "type": "array",
64
+ "items": {
65
+ "type": "object",
66
+ "properties": {
67
+ "node": { "type": "string" },
68
+ "type": { "type": "string" },
69
+ "index": { "type": "number" }
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }
78
+ },
79
+ "execution": {
80
+ "type": "object",
81
+ "description": "The captured n8n execution result.",
82
+ "required": ["status", "data"],
83
+ "additionalProperties": true,
84
+ "properties": {
85
+ "id": { "type": "string" },
86
+ "status": {
87
+ "type": "string",
88
+ "enum": ["success", "error", "crashed", "waiting", "running"],
89
+ "description": "Final execution status."
90
+ },
91
+ "startedAt": { "type": "string", "format": "date-time" },
92
+ "data": {
93
+ "type": "object",
94
+ "required": ["resultData"],
95
+ "properties": {
96
+ "resultData": {
97
+ "type": "object",
98
+ "required": ["runData"],
99
+ "properties": {
100
+ "error": {
101
+ "description": "Top-level execution error, if any. null on success.",
102
+ "oneOf": [
103
+ { "type": "null" },
104
+ {
105
+ "type": "object",
106
+ "additionalProperties": true,
107
+ "properties": {
108
+ "message": { "type": "string" },
109
+ "description": { "type": "string" },
110
+ "node": {
111
+ "description": "Name of the node that failed.",
112
+ "oneOf": [
113
+ { "type": "string" },
114
+ {
115
+ "type": "object",
116
+ "properties": {
117
+ "name": { "type": "string" },
118
+ "type": { "type": "string" }
119
+ }
120
+ }
121
+ ]
122
+ }
123
+ }
124
+ }
125
+ ]
126
+ },
127
+ "runData": {
128
+ "type": "object",
129
+ "description": "Per-node output data. Keys are exact node names.",
130
+ "additionalProperties": {
131
+ "type": "array",
132
+ "items": {
133
+ "type": "object",
134
+ "additionalProperties": true,
135
+ "properties": {
136
+ "json": {
137
+ "type": "object",
138
+ "additionalProperties": true,
139
+ "description": "The node's output JSON data."
140
+ },
141
+ "binary": {
142
+ "type": "object",
143
+ "additionalProperties": true,
144
+ "description": "Binary field metadata, if any."
145
+ },
146
+ "error": {
147
+ "type": "object",
148
+ "additionalProperties": true,
149
+ "description": "Node-level error, if this node failed."
150
+ }
151
+ }
152
+ }
153
+ }
154
+ }
155
+ }
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+ }
@@ -56,7 +56,7 @@
56
56
  {
57
57
  "name": "n8n-nodes-base.slack",
58
58
  "displayName": "Slack",
59
- "description": "Interact with Slack",
59
+ "description": "Send messages, manage channels, users, files, and reactions in Slack",
60
60
  "properties": [
61
61
  {
62
62
  "name": "resource",
@@ -64,8 +64,13 @@
64
64
  "type": "options",
65
65
  "default": "message",
66
66
  "options": [
67
- { "name": "Message", "value": "message" },
68
- { "name": "Channel", "value": "channel" }
67
+ { "name": "Message", "value": "message" },
68
+ { "name": "Channel", "value": "channel" },
69
+ { "name": "File", "value": "file" },
70
+ { "name": "Reaction", "value": "reaction" },
71
+ { "name": "Star", "value": "star" },
72
+ { "name": "User", "value": "user" },
73
+ { "name": "User Group","value": "userGroup" }
69
74
  ]
70
75
  },
71
76
  {
@@ -73,22 +78,194 @@
73
78
  "displayName": "Operation",
74
79
  "type": "options",
75
80
  "default": "post",
81
+ "description": "Resource=message: delete|get|getAll|post|update|search. Resource=channel: archive|create|get|getAll|history|invite|join|kick|leave|open|rename|replies|setPurpose|setTopic|unarchive. Resource=file: get|getAll|upload. Resource=reaction: add|get|remove. Resource=user: get|getAll|getPresence",
76
82
  "options": [
77
- { "name": "Post", "value": "post" },
78
- { "name": "Update", "value": "update" }
83
+ { "name": "Add", "value": "add" },
84
+ { "name": "Archive", "value": "archive" },
85
+ { "name": "Create", "value": "create" },
86
+ { "name": "Delete", "value": "delete" },
87
+ { "name": "Get", "value": "get" },
88
+ { "name": "Get Many", "value": "getAll" },
89
+ { "name": "Get Presence","value": "getPresence" },
90
+ { "name": "History", "value": "history" },
91
+ { "name": "Invite", "value": "invite" },
92
+ { "name": "Join", "value": "join" },
93
+ { "name": "Kick", "value": "kick" },
94
+ { "name": "Leave", "value": "leave" },
95
+ { "name": "Open", "value": "open" },
96
+ { "name": "Post", "value": "post" },
97
+ { "name": "Remove", "value": "remove" },
98
+ { "name": "Rename", "value": "rename" },
99
+ { "name": "Replies", "value": "replies" },
100
+ { "name": "Search", "value": "search" },
101
+ { "name": "Set Purpose","value": "setPurpose" },
102
+ { "name": "Set Topic", "value": "setTopic" },
103
+ { "name": "Unarchive", "value": "unarchive" },
104
+ { "name": "Update", "value": "update" },
105
+ { "name": "Upload", "value": "upload" }
79
106
  ]
80
107
  },
81
108
  {
82
109
  "name": "channel",
83
110
  "displayName": "Channel",
84
111
  "type": "string",
85
- "default": ""
112
+ "default": "",
113
+ "description": "Channel name (e.g. #general) or ID (e.g. C1234567890)"
86
114
  },
87
115
  {
88
116
  "name": "text",
89
- "displayName": "Text",
117
+ "displayName": "Message Text",
90
118
  "type": "string",
91
- "default": ""
119
+ "default": "",
120
+ "description": "Plain text fallback. Required when blocks/attachments are absent. Supports mrkdwn when mrkdwn option is enabled."
121
+ },
122
+ {
123
+ "name": "blocksUi",
124
+ "displayName": "Blocks",
125
+ "type": "json",
126
+ "default": "",
127
+ "description": "Block Kit blocks as a JSON array string. MUST be a valid JSON array. n8n parses this at runtime — if the expression produces invalid JSON the node throws 'Parameter blocksUi could not be parsed'. ALWAYS wrap expressions inside string values, never at the array level. Example: [{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"={{ $json.body.content }}\"}}]. When using expressions, ensure the referenced value exists and is a string. Supported block types: section, header, divider, image, actions, context, input, file, video."
128
+ },
129
+ {
130
+ "name": "attachments",
131
+ "displayName": "Attachments",
132
+ "type": "json",
133
+ "default": "[]",
134
+ "description": "Legacy Slack attachments as a JSON array. Prefer blocks (blocksUi) for new workflows. Example: [{\"fallback\":\"Alert\",\"color\":\"#36a64f\",\"title\":\"Title\",\"text\":\"Body\",\"fields\":[{\"title\":\"Field\",\"value\":\"Value\",\"short\":true}]}]"
135
+ },
136
+ {
137
+ "name": "otherOptions",
138
+ "displayName": "Other Options",
139
+ "type": "collection",
140
+ "default": {},
141
+ "description": "Additional message options",
142
+ "options": [
143
+ {
144
+ "name": "thread_ts",
145
+ "displayName": "Thread Timestamp",
146
+ "type": "string",
147
+ "default": "",
148
+ "description": "Timestamp of parent message to reply in a thread. Get from $json.ts on a previous Slack message."
149
+ },
150
+ {
151
+ "name": "reply_broadcast",
152
+ "displayName": "Reply Broadcast",
153
+ "type": "boolean",
154
+ "default": false,
155
+ "description": "When true, also post the thread reply to the channel (visible outside thread)."
156
+ },
157
+ {
158
+ "name": "mrkdwn",
159
+ "displayName": "Markdown",
160
+ "type": "boolean",
161
+ "default": false,
162
+ "description": "Enable mrkdwn formatting in the text field."
163
+ },
164
+ {
165
+ "name": "unfurl_links",
166
+ "displayName": "Unfurl Links",
167
+ "type": "boolean",
168
+ "default": false
169
+ },
170
+ {
171
+ "name": "unfurl_media",
172
+ "displayName": "Unfurl Media",
173
+ "type": "boolean",
174
+ "default": true
175
+ },
176
+ {
177
+ "name": "username",
178
+ "displayName": "Bot Username",
179
+ "type": "string",
180
+ "default": "",
181
+ "description": "Override the bot's display name for this message."
182
+ },
183
+ {
184
+ "name": "icon_emoji",
185
+ "displayName": "Icon Emoji",
186
+ "type": "string",
187
+ "default": "",
188
+ "description": "Override the bot icon with an emoji, e.g. :robot_face:"
189
+ },
190
+ {
191
+ "name": "icon_url",
192
+ "displayName": "Icon URL",
193
+ "type": "string",
194
+ "default": "",
195
+ "description": "Override the bot icon with a remote image URL."
196
+ },
197
+ {
198
+ "name": "link_names",
199
+ "displayName": "Link Names",
200
+ "type": "boolean",
201
+ "default": false,
202
+ "description": "Find and linkify channel names and usernames."
203
+ }
204
+ ]
205
+ },
206
+ {
207
+ "name": "select",
208
+ "displayName": "User",
209
+ "type": "string",
210
+ "default": "",
211
+ "description": "User ID for user/reaction operations (e.g. U1234567890)"
212
+ },
213
+ {
214
+ "name": "timestamp",
215
+ "displayName": "Message Timestamp",
216
+ "type": "string",
217
+ "default": "",
218
+ "description": "The ts value of the message for get/update/delete/reaction operations."
219
+ },
220
+ {
221
+ "name": "messageId",
222
+ "displayName": "Message ID (ts)",
223
+ "type": "string",
224
+ "default": "",
225
+ "description": "Unique message timestamp used as ID (ts field from previous Slack response)."
226
+ },
227
+ {
228
+ "name": "fileContent",
229
+ "displayName": "File Content",
230
+ "type": "string",
231
+ "default": "",
232
+ "description": "Content of the file to upload (for file.upload operation)."
233
+ },
234
+ {
235
+ "name": "fileName",
236
+ "displayName": "File Name",
237
+ "type": "string",
238
+ "default": "",
239
+ "description": "Name of the file including extension (for file.upload)."
240
+ }
241
+ ]
242
+ },
243
+ {
244
+ "name": "n8n-nodes-base.slackTrigger",
245
+ "displayName": "Slack Trigger",
246
+ "description": "Receive events from Slack via webhooks or Socket Mode",
247
+ "properties": [
248
+ {
249
+ "name": "triggerOn",
250
+ "displayName": "Trigger On",
251
+ "type": "options",
252
+ "default": "message",
253
+ "options": [
254
+ { "name": "New Message Posted to Channel", "value": "message" },
255
+ { "name": "New Public Channel Created", "value": "channelCreated" },
256
+ { "name": "New User Added", "value": "teamJoin" },
257
+ { "name": "Reaction Added", "value": "reactionAdded" },
258
+ { "name": "Reaction Removed", "value": "reactionRemoved" },
259
+ { "name": "File Shared", "value": "fileShared" },
260
+ { "name": "App Mention", "value": "appMention" }
261
+ ]
262
+ },
263
+ {
264
+ "name": "channelId",
265
+ "displayName": "Channel to Watch",
266
+ "type": "string",
267
+ "default": "",
268
+ "description": "Channel ID to listen for events in. Leave empty to listen to all channels."
92
269
  }
93
270
  ]
94
271
  },
@@ -0,0 +1,188 @@
1
+ {
2
+ "_comment": "Maps n8n node types + parameter names to test data hints. Used by testRemoteWorkflowDirectly() to build better mock payloads and self-healing prompts.",
3
+
4
+ "n8n-nodes-base.slack": {
5
+ "_description": "Slack node. blocksUi must be a valid JSON array of Block Kit blocks — never a plain string.",
6
+ "blocksUi": {
7
+ "type": "jsonArray",
8
+ "description": "Must be a JSON array of Block Kit block objects. If a webhook body field is used here, that field must contain valid blocks JSON, not plain text.",
9
+ "sample": "[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"Hello from n8m test\"}}]",
10
+ "errorSignal": "could not be parsed"
11
+ },
12
+ "attachments": {
13
+ "type": "jsonArray",
14
+ "description": "Legacy Slack attachments array.",
15
+ "sample": "[{\"fallback\":\"Test alert\",\"color\":\"#36a64f\",\"text\":\"Test attachment body\"}]"
16
+ },
17
+ "text": {
18
+ "type": "string",
19
+ "description": "Plain mrkdwn message text. Can use any string value.",
20
+ "sample": "Test message from n8m validation suite"
21
+ },
22
+ "channel": {
23
+ "type": "string",
24
+ "description": "Channel name (#general) or ID (C1234567890).",
25
+ "sample": "#general"
26
+ }
27
+ },
28
+
29
+ "n8n-nodes-base.slackTrigger": {
30
+ "_description": "Slack trigger — receives webhook events. No test payload hints needed."
31
+ },
32
+
33
+ "n8n-nodes-base.httpRequest": {
34
+ "_description": "HTTP Request node. body must match the expected content type.",
35
+ "body": {
36
+ "type": "json",
37
+ "description": "Request body as JSON. The structure depends on the target API.",
38
+ "sample": "{\"key\": \"value\"}"
39
+ },
40
+ "url": {
41
+ "type": "string",
42
+ "description": "Full URL including protocol.",
43
+ "sample": "https://httpbin.org/post"
44
+ }
45
+ },
46
+
47
+ "n8n-nodes-base.openAi": {
48
+ "_description": "OpenAI node. prompt/userMessage must be a non-empty string.",
49
+ "prompt": {
50
+ "type": "string",
51
+ "description": "The prompt to send. Must be a non-empty string.",
52
+ "sample": "Summarize this in one sentence: Hello world."
53
+ },
54
+ "userMessage": {
55
+ "type": "string",
56
+ "description": "User message for chat completions. Must be a non-empty string.",
57
+ "sample": "Write a short test post about productivity."
58
+ }
59
+ },
60
+
61
+ "n8n-nodes-base.googleSheets": {
62
+ "_description": "Google Sheets node.",
63
+ "sheetId": {
64
+ "type": "string",
65
+ "description": "Spreadsheet ID from the sheet URL.",
66
+ "sample": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms"
67
+ }
68
+ },
69
+
70
+ "n8n-nodes-base.twitter": {
71
+ "_description": "Twitter/X node. text must be ≤280 chars.",
72
+ "text": {
73
+ "type": "string",
74
+ "description": "Tweet text. Max 280 characters.",
75
+ "sample": "Test post from n8m validation suite. #test"
76
+ }
77
+ },
78
+
79
+ "n8n-nodes-base.linkedIn": {
80
+ "_description": "LinkedIn node. text/commentary must be a non-empty string.",
81
+ "text": {
82
+ "type": "string",
83
+ "description": "Post body text.",
84
+ "sample": "Sharing insights on productivity. This is a test post from n8m."
85
+ }
86
+ },
87
+
88
+ "n8n-nodes-base.telegram": {
89
+ "_description": "Telegram node. text must be a non-empty string.",
90
+ "text": {
91
+ "type": "string",
92
+ "description": "Message text. Supports HTML/Markdown depending on parseMode.",
93
+ "sample": "Test message from n8m validation suite."
94
+ }
95
+ },
96
+
97
+ "n8n-nodes-base.discord": {
98
+ "_description": "Discord node. text must be a non-empty string, max 2000 chars.",
99
+ "text": {
100
+ "type": "string",
101
+ "description": "Message content. Max 2000 characters.",
102
+ "sample": "Test message from n8m validation suite."
103
+ }
104
+ },
105
+
106
+ "n8n-nodes-base.gmail": {
107
+ "_description": "Gmail node.",
108
+ "subject": {
109
+ "type": "string",
110
+ "description": "Email subject line.",
111
+ "sample": "n8m Test Email"
112
+ },
113
+ "message": {
114
+ "type": "string",
115
+ "description": "Email body. HTML or plain text.",
116
+ "sample": "This is a test email sent by the n8m validation suite."
117
+ },
118
+ "toList": {
119
+ "type": "string",
120
+ "description": "Recipient email address(es), comma-separated.",
121
+ "sample": "test@example.com"
122
+ }
123
+ },
124
+
125
+ "n8n-nodes-base.sendEmail": {
126
+ "_description": "Send Email node (SMTP).",
127
+ "subject": {
128
+ "type": "string",
129
+ "sample": "n8m Test Email"
130
+ },
131
+ "text": {
132
+ "type": "string",
133
+ "sample": "Test email body from n8m validation."
134
+ },
135
+ "toEmail": {
136
+ "type": "string",
137
+ "sample": "test@example.com"
138
+ }
139
+ },
140
+
141
+ "n8n-nodes-base.notion": {
142
+ "_description": "Notion node. pageId must be a valid UUID.",
143
+ "pageId": {
144
+ "type": "string",
145
+ "description": "Notion page ID (UUID format).",
146
+ "sample": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
147
+ }
148
+ },
149
+
150
+ "n8n-nodes-base.airtable": {
151
+ "_description": "Airtable node.",
152
+ "table": {
153
+ "type": "string",
154
+ "description": "Table name exactly as it appears in Airtable.",
155
+ "sample": "Table 1"
156
+ }
157
+ },
158
+
159
+ "n8n-nodes-base.html": {
160
+ "_description": "HTML node. Generates HTML from a template expression. The 'html' parameter must be a valid HTML string or expression producing one.",
161
+ "html": {
162
+ "type": "string",
163
+ "description": "HTML template string. Uses $json fields from the previous node. If this node accesses $json.body.content, the webhook payload must include 'content' as a plain text string.",
164
+ "sample": "<h1>Test Heading</h1><p>Test paragraph content.</p>"
165
+ }
166
+ },
167
+
168
+ "n8n-nodes-base.markdown": {
169
+ "_description": "Markdown node. Converts markdown text to HTML. The markdown field must be a string.",
170
+ "markdown": {
171
+ "type": "string",
172
+ "description": "Markdown text to convert.",
173
+ "sample": "# Test Heading\n\nTest paragraph content."
174
+ }
175
+ },
176
+
177
+ "n8n-nodes-base.set": {
178
+ "_description": "Set node. No special payload requirements — reads from upstream data."
179
+ },
180
+
181
+ "n8n-nodes-base.if": {
182
+ "_description": "IF node. No special payload requirements — evaluates conditions on upstream data."
183
+ },
184
+
185
+ "n8n-nodes-base.code": {
186
+ "_description": "Code node. No special payload requirements — runs JS/TS against upstream data."
187
+ }
188
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "_comment": "Pre-defined test payloads for specific n8n workflows. Keys are workflow IDs or name substrings. The 'payload' object is POST-ed to the webhook as-is (n8n wraps it automatically: POST {content:'x'} → $json.body.content = 'x'). Place a copy of this file at ./workflow-test-fixtures.json or ./workflows/test-fixtures.json in your project to override bundled fixtures.",
3
+
4
+ "IAHbfTdmlTZfIDld": {
5
+ "_name": "Social Posting Flow I - Candidates",
6
+ "payload": {
7
+ "content": "Excited to share some thoughts on AI-powered workflow automation and how it's transforming the way teams operate. The future of work is here, and it's more efficient than ever. #AI #Automation #Productivity",
8
+ "title": "AI Workflow Automation",
9
+ "platform": "linkedin",
10
+ "author": "n8m Test Suite",
11
+ "tags": ["AI", "Automation", "n8n"]
12
+ }
13
+ },
14
+
15
+ "Social Posting Flow": {
16
+ "_note": "Name-based fallback — matches any workflow whose name contains 'Social Posting Flow'.",
17
+ "payload": {
18
+ "content": "Excited to share some thoughts on AI-powered workflow automation and how it's transforming the way teams operate. #AI #Automation",
19
+ "title": "AI Workflow Automation",
20
+ "platform": "linkedin",
21
+ "author": "n8m Test Suite"
22
+ }
23
+ },
24
+
25
+ "_example_slack_workflow": {
26
+ "_note": "Rename this key to your workflow ID or name substring to activate.",
27
+ "payload": {
28
+ "message": "Hello from n8m test suite",
29
+ "channel": "#general",
30
+ "username": "n8m Bot"
31
+ }
32
+ },
33
+
34
+ "_example_email_workflow": {
35
+ "_note": "Rename this key to your workflow ID or name substring to activate.",
36
+ "payload": {
37
+ "to": "test@example.com",
38
+ "subject": "n8m Test Email",
39
+ "body": "This is a test email sent by the n8m validation suite."
40
+ }
41
+ }
42
+ }
@@ -3,6 +3,13 @@ export interface GenerateOptions {
3
3
  provider?: string;
4
4
  temperature?: number;
5
5
  }
6
+ export interface TestErrorEvaluation {
7
+ action: 'fix_node' | 'regenerate_payload' | 'structural_pass' | 'escalate';
8
+ nodeFixType?: 'code_node_js' | 'execute_command' | 'binary_field';
9
+ targetNodeName?: string;
10
+ suggestedBinaryField?: string;
11
+ reason: string;
12
+ }
6
13
  export interface WorkflowSpec {
7
14
  suggestedName: string;
8
15
  description: string;
@@ -44,10 +51,45 @@ export declare class AIService {
44
51
  fixHallucinatedNodes(workflow: any): any;
45
52
  fixN8nConnections(workflow: any): any;
46
53
  generateMockData(context: string): Promise<any>;
54
+ fixExecuteCommandScript(command: string, error?: string): Promise<string>;
55
+ fixCodeNodeJavaScript(code: string, error: string): Promise<string>;
56
+ shimCodeNodeWithMockData(code: string): Promise<string>;
47
57
  evaluateCandidates(goal: string, candidates: any[]): Promise<{
48
58
  selectedIndex: number;
49
59
  reason: string;
50
60
  }>;
61
+ /**
62
+ * AI-powered error evaluation for n8n test executions.
63
+ * Replaces brittle regex classifiers — the model reads the error + node list and decides.
64
+ */
65
+ evaluateTestError(errorMessage: string, workflowNodes: any[], failingNodeName?: string, failingNodeCode?: string): Promise<TestErrorEvaluation>;
66
+ /**
67
+ * Offline-only: evaluates whether a fixed Code node or Execute Command script
68
+ * would succeed given the REAL input items from the fixture's runData.
69
+ * Used when no live re-execution is possible.
70
+ */
71
+ evaluateCodeFixOffline(fixedCode: string, inputItems: any[], originalError: string, nodeType: 'code_node_js' | 'execute_command'): Promise<{
72
+ wouldPass: boolean;
73
+ reason: string;
74
+ }>;
75
+ /**
76
+ * Traces binary data flow through an entire workflow graph to find the correct
77
+ * binary field name for a failing upload node.
78
+ *
79
+ * Handles passthrough nodes (Merge, Set, IF, Switch) by tracing further upstream
80
+ * and reads Code node jsCode to extract the actual binary field assignment.
81
+ * Delegates graph traversal + analysis entirely to the AI.
82
+ */
83
+ inferBinaryFieldNameFromWorkflow(failingNodeName: string, workflowNodes: any[], workflowConnections: any): Promise<string | null>;
84
+ /**
85
+ * Returns jsCode for an n8n Code node (runOnceForAllItems) that produces
86
+ * synthetic binary test data in the specified field.
87
+ *
88
+ * Hardcoded — no LLM call — because the n8n Code node binary format is
89
+ * deterministic and LLM-generated variants consistently misuse APIs that
90
+ * aren't available (this.helpers.prepareBinaryData, $input.all() in wrong mode, etc.).
91
+ */
92
+ generateBinaryShimCode(binaryFieldName: string): string;
51
93
  /**
52
94
  * Generates 3-5 diverse test scenarios (input payloads) for a workflow.
53
95
  */