@lhi/n8m 0.1.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +247 -0
  3. package/bin/dev.js +5 -0
  4. package/bin/run.js +6 -0
  5. package/dist/agentic/checkpointer.d.ts +2 -0
  6. package/dist/agentic/checkpointer.js +14 -0
  7. package/dist/agentic/graph.d.ts +483 -0
  8. package/dist/agentic/graph.js +100 -0
  9. package/dist/agentic/nodes/architect.d.ts +6 -0
  10. package/dist/agentic/nodes/architect.js +51 -0
  11. package/dist/agentic/nodes/engineer.d.ts +11 -0
  12. package/dist/agentic/nodes/engineer.js +182 -0
  13. package/dist/agentic/nodes/qa.d.ts +5 -0
  14. package/dist/agentic/nodes/qa.js +151 -0
  15. package/dist/agentic/nodes/reviewer.d.ts +5 -0
  16. package/dist/agentic/nodes/reviewer.js +111 -0
  17. package/dist/agentic/nodes/supervisor.d.ts +6 -0
  18. package/dist/agentic/nodes/supervisor.js +18 -0
  19. package/dist/agentic/state.d.ts +51 -0
  20. package/dist/agentic/state.js +26 -0
  21. package/dist/commands/config.d.ts +13 -0
  22. package/dist/commands/config.js +47 -0
  23. package/dist/commands/create.d.ts +14 -0
  24. package/dist/commands/create.js +182 -0
  25. package/dist/commands/deploy.d.ts +13 -0
  26. package/dist/commands/deploy.js +68 -0
  27. package/dist/commands/modify.d.ts +13 -0
  28. package/dist/commands/modify.js +276 -0
  29. package/dist/commands/prune.d.ts +9 -0
  30. package/dist/commands/prune.js +98 -0
  31. package/dist/commands/resume.d.ts +8 -0
  32. package/dist/commands/resume.js +39 -0
  33. package/dist/commands/test.d.ts +27 -0
  34. package/dist/commands/test.js +619 -0
  35. package/dist/index.d.ts +1 -0
  36. package/dist/index.js +1 -0
  37. package/dist/services/ai.service.d.ts +51 -0
  38. package/dist/services/ai.service.js +421 -0
  39. package/dist/services/n8n.service.d.ts +17 -0
  40. package/dist/services/n8n.service.js +81 -0
  41. package/dist/services/node-definitions.service.d.ts +36 -0
  42. package/dist/services/node-definitions.service.js +102 -0
  43. package/dist/utils/config.d.ts +15 -0
  44. package/dist/utils/config.js +25 -0
  45. package/dist/utils/multilinePrompt.d.ts +1 -0
  46. package/dist/utils/multilinePrompt.js +52 -0
  47. package/dist/utils/n8nClient.d.ts +97 -0
  48. package/dist/utils/n8nClient.js +440 -0
  49. package/dist/utils/sandbox.d.ts +13 -0
  50. package/dist/utils/sandbox.js +34 -0
  51. package/dist/utils/theme.d.ts +23 -0
  52. package/dist/utils/theme.js +92 -0
  53. package/oclif.manifest.json +331 -0
  54. package/package.json +95 -0
@@ -0,0 +1,92 @@
1
+ import chalk from 'chalk';
2
+ import { readFileSync, existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ const __dirname = fileURLToPath(new URL('.', import.meta.url));
6
+ let tokens;
7
+ const rootPath = join(__dirname, '../../');
8
+ const tokensPath = join(rootPath, 'design-tokens.json');
9
+ try {
10
+ if (existsSync(tokensPath)) {
11
+ tokens = JSON.parse(readFileSync(tokensPath, 'utf-8'));
12
+ }
13
+ else {
14
+ throw new Error('Tokens file not found');
15
+ }
16
+ }
17
+ catch {
18
+ // Fallback tokens matching the user's file
19
+ tokens = {
20
+ colors: {
21
+ primary: { value: '#10B981' },
22
+ secondary: { value: '#6366F1' },
23
+ background: { value: '#0F172A' },
24
+ foreground: { value: '#F1F5F9' },
25
+ mutedForeground: { value: '#94A3B8' },
26
+ card: { value: '#1E293B' },
27
+ semantic: {
28
+ success: { value: '#10B981' },
29
+ warning: { value: '#F59E0B' },
30
+ error: { value: '#EF4444' },
31
+ aiProcessing: { value: '#A855F7' },
32
+ },
33
+ },
34
+ };
35
+ }
36
+ const c = {
37
+ primary: chalk.hex(tokens.colors.primary.value),
38
+ secondary: chalk.hex(tokens.colors.secondary.value),
39
+ muted: chalk.hex(tokens.colors.mutedForeground.value),
40
+ foreground: chalk.hex(tokens.colors.foreground.value),
41
+ success: chalk.hex(tokens.colors.semantic.success.value),
42
+ warning: chalk.hex(tokens.colors.semantic.warning.value),
43
+ error: chalk.hex(tokens.colors.semantic.error.value),
44
+ ai: chalk.hex(tokens.colors.semantic.aiProcessing.value),
45
+ card: chalk.hex(tokens.colors.card.value),
46
+ };
47
+ export const theme = {
48
+ ...c,
49
+ // Layout helpers
50
+ divider: (len = 60) => c.muted('━'.repeat(len)),
51
+ header: (text) => {
52
+ return `\n${c.primary.bold('◆ ' + text)}\n${c.muted('━'.repeat(text.length + 4))}`;
53
+ },
54
+ subHeader: (text) => {
55
+ return `\n${c.secondary.bold(text)}`;
56
+ },
57
+ // Field styling
58
+ label: (text) => c.secondary(text.padEnd(15)),
59
+ value: (text) => c.foreground(text.toString()),
60
+ // Semantic status
61
+ info: (text) => c.primary('ℹ ') + c.foreground(text),
62
+ done: (text) => c.success('✔ ') + c.foreground(text),
63
+ warn: (text) => c.warning('⚠ ') + c.foreground(text),
64
+ fail: (text) => c.error('✘ ') + c.foreground(text),
65
+ // AI/Agentic
66
+ agent: (text) => c.ai('✧ ') + c.ai.italic(text),
67
+ // Brand/Banner
68
+ brand: () => {
69
+ const bannerPath = join(rootPath, 'banner.txt');
70
+ if (existsSync(bannerPath)) {
71
+ const banner = readFileSync(bannerPath, 'utf-8');
72
+ const lines = banner.split('\n');
73
+ // Calculate a smoother line-by-line gradient between secondary and primary
74
+ // Secondary: Indigo, Primary: Emerald
75
+ return lines.map((line, i) => {
76
+ if (!line.trim())
77
+ return '';
78
+ const ratio = i / Math.max(lines.length - 1, 1);
79
+ // Simple interpolation between #6366F1 and #10B981
80
+ // We'll use 3 steps for simplicity given it's line based
81
+ if (ratio < 0.33)
82
+ return c.secondary(line);
83
+ if (ratio < 0.66)
84
+ return chalk.hex('#3A90B9')(line); // Midpoint between Indigo and Emerald
85
+ return c.primary(line);
86
+ }).join('\n');
87
+ }
88
+ return c.primary.bold('N8M CLI');
89
+ },
90
+ // Badge/Tag
91
+ tag: (text) => chalk.bgHex(tokens.colors.primary.value).hex(tokens.colors.background.value).bold(` ${text} `)
92
+ };
@@ -0,0 +1,331 @@
1
+ {
2
+ "commands": {
3
+ "config": {
4
+ "aliases": [],
5
+ "args": {},
6
+ "description": "Manage n8m configuration",
7
+ "flags": {
8
+ "n8n-url": {
9
+ "description": "Set n8n Instance URL",
10
+ "name": "n8n-url",
11
+ "hasDynamicHelp": false,
12
+ "multiple": false,
13
+ "type": "option"
14
+ },
15
+ "n8n-key": {
16
+ "description": "Set n8n API Key",
17
+ "name": "n8n-key",
18
+ "hasDynamicHelp": false,
19
+ "multiple": false,
20
+ "type": "option"
21
+ },
22
+ "ai-key": {
23
+ "description": "Set AI API Key (used for all AI features)",
24
+ "name": "ai-key",
25
+ "hasDynamicHelp": false,
26
+ "multiple": false,
27
+ "type": "option"
28
+ },
29
+ "ai-provider": {
30
+ "description": "Set AI provider (openai, anthropic, gemini)",
31
+ "name": "ai-provider",
32
+ "hasDynamicHelp": false,
33
+ "multiple": false,
34
+ "type": "option"
35
+ },
36
+ "ai-model": {
37
+ "description": "Set AI model name (e.g. gpt-4o, claude-sonnet-4-6)",
38
+ "name": "ai-model",
39
+ "hasDynamicHelp": false,
40
+ "multiple": false,
41
+ "type": "option"
42
+ },
43
+ "ai-base-url": {
44
+ "description": "Set custom AI base URL (for OpenAI-compatible endpoints)",
45
+ "name": "ai-base-url",
46
+ "hasDynamicHelp": false,
47
+ "multiple": false,
48
+ "type": "option"
49
+ }
50
+ },
51
+ "hasDynamicHelp": false,
52
+ "hiddenAliases": [],
53
+ "id": "config",
54
+ "pluginAlias": "@lhi/n8m",
55
+ "pluginName": "@lhi/n8m",
56
+ "pluginType": "core",
57
+ "strict": true,
58
+ "enableJsonFlag": false,
59
+ "isESM": true,
60
+ "relativePath": [
61
+ "dist",
62
+ "commands",
63
+ "config.js"
64
+ ]
65
+ },
66
+ "create": {
67
+ "aliases": [],
68
+ "args": {
69
+ "description": {
70
+ "description": "Natural language description of the workflow",
71
+ "name": "description",
72
+ "required": false
73
+ }
74
+ },
75
+ "description": "Generate n8n workflows from natural language using Gemini AI Agent",
76
+ "examples": [
77
+ "<%= config.bin %> <%= command.id %> \"Send a telegram alert when I receive an email\"",
78
+ "echo \"Slack to Discord sync\" | <%= config.bin %> <%= command.id %>",
79
+ "<%= config.bin %> <%= command.id %> --output ./my-workflow.json"
80
+ ],
81
+ "flags": {
82
+ "deploy": {
83
+ "char": "d",
84
+ "description": "Deploy the generated workflow to n8n instance (Not yet fully integrated with agent)",
85
+ "hidden": true,
86
+ "name": "deploy",
87
+ "allowNo": false,
88
+ "type": "boolean"
89
+ },
90
+ "output": {
91
+ "char": "o",
92
+ "description": "Path to save the generated workflow JSON",
93
+ "name": "output",
94
+ "hasDynamicHelp": false,
95
+ "multiple": false,
96
+ "type": "option"
97
+ },
98
+ "multiline": {
99
+ "char": "m",
100
+ "description": "Open editor for multiline workflow description",
101
+ "name": "multiline",
102
+ "allowNo": false,
103
+ "type": "boolean"
104
+ }
105
+ },
106
+ "hasDynamicHelp": false,
107
+ "hiddenAliases": [],
108
+ "id": "create",
109
+ "pluginAlias": "@lhi/n8m",
110
+ "pluginName": "@lhi/n8m",
111
+ "pluginType": "core",
112
+ "strict": true,
113
+ "enableJsonFlag": false,
114
+ "isESM": true,
115
+ "relativePath": [
116
+ "dist",
117
+ "commands",
118
+ "create.js"
119
+ ]
120
+ },
121
+ "deploy": {
122
+ "aliases": [],
123
+ "args": {
124
+ "workflow": {
125
+ "description": "Path to the workflow file or workflow ID",
126
+ "name": "workflow",
127
+ "required": true
128
+ }
129
+ },
130
+ "description": "Push workflows to n8n instance via API",
131
+ "examples": [
132
+ "<%= config.bin %> <%= command.id %> ./workflows/slack-notifier.json"
133
+ ],
134
+ "flags": {
135
+ "instance": {
136
+ "char": "i",
137
+ "description": "n8n instance name (from config)",
138
+ "name": "instance",
139
+ "default": "production",
140
+ "hasDynamicHelp": false,
141
+ "multiple": false,
142
+ "type": "option"
143
+ },
144
+ "activate": {
145
+ "char": "a",
146
+ "description": "Activate workflow after deployment",
147
+ "name": "activate",
148
+ "allowNo": false,
149
+ "type": "boolean"
150
+ }
151
+ },
152
+ "hasDynamicHelp": false,
153
+ "hiddenAliases": [],
154
+ "id": "deploy",
155
+ "pluginAlias": "@lhi/n8m",
156
+ "pluginName": "@lhi/n8m",
157
+ "pluginType": "core",
158
+ "strict": true,
159
+ "enableJsonFlag": false,
160
+ "isESM": true,
161
+ "relativePath": [
162
+ "dist",
163
+ "commands",
164
+ "deploy.js"
165
+ ]
166
+ },
167
+ "modify": {
168
+ "aliases": [],
169
+ "args": {
170
+ "workflow": {
171
+ "description": "Path or Name of the workflow to modify",
172
+ "name": "workflow",
173
+ "required": false
174
+ },
175
+ "instruction": {
176
+ "description": "Modification instructions",
177
+ "name": "instruction",
178
+ "required": false
179
+ }
180
+ },
181
+ "description": "Modify existing n8n workflows using Gemini AI Agent",
182
+ "flags": {
183
+ "multiline": {
184
+ "char": "m",
185
+ "description": "Open editor for multiline modification instructions",
186
+ "name": "multiline",
187
+ "allowNo": false,
188
+ "type": "boolean"
189
+ },
190
+ "output": {
191
+ "char": "o",
192
+ "description": "Path to save the modified workflow JSON (defaults to overwriting if local file)",
193
+ "name": "output",
194
+ "hasDynamicHelp": false,
195
+ "multiple": false,
196
+ "type": "option"
197
+ }
198
+ },
199
+ "hasDynamicHelp": false,
200
+ "hiddenAliases": [],
201
+ "id": "modify",
202
+ "pluginAlias": "@lhi/n8m",
203
+ "pluginName": "@lhi/n8m",
204
+ "pluginType": "core",
205
+ "strict": true,
206
+ "enableJsonFlag": false,
207
+ "isESM": true,
208
+ "relativePath": [
209
+ "dist",
210
+ "commands",
211
+ "modify.js"
212
+ ]
213
+ },
214
+ "prune": {
215
+ "aliases": [],
216
+ "args": {},
217
+ "description": "Deduplicate workflows on the instance",
218
+ "flags": {
219
+ "force": {
220
+ "char": "f",
221
+ "description": "Force delete without confirmation",
222
+ "name": "force",
223
+ "allowNo": false,
224
+ "type": "boolean"
225
+ },
226
+ "dry-run": {
227
+ "char": "d",
228
+ "description": "Show what would be deleted",
229
+ "name": "dry-run",
230
+ "allowNo": false,
231
+ "type": "boolean"
232
+ }
233
+ },
234
+ "hasDynamicHelp": false,
235
+ "hiddenAliases": [],
236
+ "id": "prune",
237
+ "pluginAlias": "@lhi/n8m",
238
+ "pluginName": "@lhi/n8m",
239
+ "pluginType": "core",
240
+ "strict": true,
241
+ "enableJsonFlag": false,
242
+ "isESM": true,
243
+ "relativePath": [
244
+ "dist",
245
+ "commands",
246
+ "prune.js"
247
+ ]
248
+ },
249
+ "resume": {
250
+ "aliases": [],
251
+ "args": {
252
+ "threadId": {
253
+ "description": "The Thread ID of the session to resume",
254
+ "name": "threadId",
255
+ "required": true
256
+ }
257
+ },
258
+ "description": "Resume a paused/interrupted agentic workflow",
259
+ "flags": {},
260
+ "hasDynamicHelp": false,
261
+ "hiddenAliases": [],
262
+ "id": "resume",
263
+ "pluginAlias": "@lhi/n8m",
264
+ "pluginName": "@lhi/n8m",
265
+ "pluginType": "core",
266
+ "strict": true,
267
+ "enableJsonFlag": false,
268
+ "isESM": true,
269
+ "relativePath": [
270
+ "dist",
271
+ "commands",
272
+ "resume.js"
273
+ ]
274
+ },
275
+ "test": {
276
+ "aliases": [],
277
+ "args": {
278
+ "workflow": {
279
+ "description": "Path to workflow JSON file",
280
+ "name": "workflow",
281
+ "required": false
282
+ }
283
+ },
284
+ "description": "Run ephemeral end-to-end tests for n8n workflows",
285
+ "flags": {
286
+ "headless": {
287
+ "char": "h",
288
+ "description": "Run tests in headless mode",
289
+ "name": "headless",
290
+ "allowNo": false,
291
+ "type": "boolean"
292
+ },
293
+ "keep-on-fail": {
294
+ "description": "Do not delete workflow if test fails",
295
+ "name": "keep-on-fail",
296
+ "allowNo": false,
297
+ "type": "boolean"
298
+ },
299
+ "no-brand": {
300
+ "description": "Suppress branding header",
301
+ "hidden": true,
302
+ "name": "no-brand",
303
+ "allowNo": false,
304
+ "type": "boolean"
305
+ },
306
+ "validate-only": {
307
+ "description": "Execute test but do not prompt for deploy/save actions",
308
+ "hidden": true,
309
+ "name": "validate-only",
310
+ "allowNo": false,
311
+ "type": "boolean"
312
+ }
313
+ },
314
+ "hasDynamicHelp": false,
315
+ "hiddenAliases": [],
316
+ "id": "test",
317
+ "pluginAlias": "@lhi/n8m",
318
+ "pluginName": "@lhi/n8m",
319
+ "pluginType": "core",
320
+ "strict": true,
321
+ "enableJsonFlag": false,
322
+ "isESM": true,
323
+ "relativePath": [
324
+ "dist",
325
+ "commands",
326
+ "test.js"
327
+ ]
328
+ }
329
+ },
330
+ "version": "0.1.0"
331
+ }
package/package.json ADDED
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "@lhi/n8m",
3
+ "version": "0.1.0",
4
+ "description": "Agentic n8n CLI wrapper - A Skill Bridge for n8n workflow automation",
5
+ "author": "n8m",
6
+ "bin": {
7
+ "n8m": "./bin/run.js"
8
+ },
9
+ "homepage": "https://github.com/lcanady/n8m",
10
+ "main": "dist/index.js",
11
+ "type": "module",
12
+ "exports": "./dist/index.js",
13
+ "types": "dist/index.d.ts",
14
+ "files": [
15
+ "bin",
16
+ "dist",
17
+ "oclif.manifest.json"
18
+ ],
19
+ "dependencies": {
20
+ "openai": "^4.0.0",
21
+ "@langchain/core": "^1.1.18",
22
+ "@langchain/langgraph": "^1.1.2",
23
+ "@langchain/langgraph-checkpoint-sqlite": "^1.0.0",
24
+ "@oclif/core": "^4",
25
+ "@oclif/plugin-help": "^6",
26
+ "@oclif/plugin-plugins": "^5",
27
+ "@types/inquirer": "^9.0.9",
28
+ "chalk": "^5.3.0",
29
+ "cli-progress": "^3.12.0",
30
+ "dotenv": "^17.2.3",
31
+ "ink": "^6.6.0",
32
+ "ink-gradient": "^3.0.0",
33
+ "ink-link": "^5.0.0",
34
+ "ink-markdown": "^1.0.4",
35
+ "ink-multiline-input": "^0.1.0",
36
+ "ink-spinner": "^5.0.0",
37
+ "ink-text-input": "^6.0.0",
38
+ "inquirer": "^13.2.2",
39
+ "react": "^19.2.4"
40
+ },
41
+ "devDependencies": {
42
+ "@oclif/prettier-config": "^0.2.1",
43
+ "@oclif/test": "^4",
44
+ "@types/chai": "^5",
45
+ "@types/cli-progress": "^3.11.6",
46
+ "@types/mocha": "^10",
47
+ "@types/node": "^22.19.7",
48
+ "@types/react": "^19.2.10",
49
+ "chai": "^5",
50
+ "eslint": "^9",
51
+ "eslint-config-oclif": "^6",
52
+ "eslint-config-oclif-typescript": "^3",
53
+ "mocha": "^10",
54
+ "oclif": "^4",
55
+ "shx": "^0.3.4",
56
+ "ts-node": "^10",
57
+ "tsx": "^4.21.0",
58
+ "typescript": "^5"
59
+ },
60
+ "engines": {
61
+ "node": ">=18.0.0"
62
+ },
63
+ "oclif": {
64
+ "bin": "n8m",
65
+ "dirname": "n8m",
66
+ "commands": "./dist/commands",
67
+ "plugins": [
68
+ "@oclif/plugin-help",
69
+ "@oclif/plugin-plugins"
70
+ ],
71
+ "topicSeparator": " ",
72
+ "topics": {
73
+ "create": {
74
+ "description": "Generate or modify n8n workflows"
75
+ },
76
+ "deploy": {
77
+ "description": "Push workflows to n8n instance"
78
+ },
79
+ "test": {
80
+ "description": "Run end-to-end workflow tests"
81
+ }
82
+ }
83
+ },
84
+ "scripts": {
85
+ "build": "shx rm -rf dist && tsc -b",
86
+ "lint": "eslint .",
87
+ "postpack": "shx rm -f oclif.manifest.json",
88
+ "posttest": "npm run lint",
89
+ "prepack": "oclif manifest && oclif readme",
90
+ "test": "mocha --forbid-only \"test/**/*.test.ts\"",
91
+ "n8m": "./bin/run.js",
92
+ "dev": "tsc -b -w",
93
+ "version": "oclif readme && git add README.md"
94
+ }
95
+ }