@lhi/n8m 0.3.3 → 1.0.1
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.
- package/README.md +27 -9
- package/banner.txt +9 -0
- package/dist/agentic/graph.d.ts +11 -1
- package/dist/agentic/graph.js +2 -1
- package/dist/agentic/nodes/architect.js +3 -2
- package/dist/agentic/nodes/engineer.js +4 -2
- package/dist/agentic/state.d.ts +2 -0
- package/dist/agentic/state.js +4 -0
- package/dist/commands/create.js +15 -2
- package/dist/commands/deploy.d.ts +2 -0
- package/dist/commands/deploy.js +25 -8
- package/dist/commands/fixture.js +1 -0
- package/dist/commands/learn.js +1 -1
- package/dist/commands/modify.js +13 -1
- package/dist/commands/rollback.d.ts +31 -0
- package/dist/commands/rollback.js +201 -0
- package/dist/fixture-schema.json +162 -0
- package/dist/help.d.ts +6 -0
- package/dist/help.js +12 -0
- package/dist/resources/node-definitions-fallback.json +390 -0
- package/dist/resources/node-test-hints.json +188 -0
- package/dist/resources/workflow-test-fixtures.json +42 -0
- package/dist/services/ai.service.d.ts +9 -2
- package/dist/services/ai.service.js +27 -6
- package/dist/services/git.service.d.ts +52 -0
- package/dist/services/git.service.js +110 -0
- package/dist/services/mcp.service.d.ts +1 -0
- package/dist/services/mcp.service.js +201 -0
- package/dist/services/node-definitions.service.js +1 -1
- package/dist/utils/config.js +3 -1
- package/dist/utils/n8nClient.d.ts +11 -0
- package/dist/utils/n8nClient.js +39 -0
- package/docs/.nojekyll +0 -0
- package/docs/CNAME +1 -0
- package/docs/DEVELOPER_GUIDE.md +12 -2
- package/docs/apple-touch-icon.png +0 -0
- package/docs/favicon-16x16.png +0 -0
- package/docs/favicon-192x192.png +0 -0
- package/docs/favicon-32x32.png +0 -0
- package/docs/favicon.svg +4 -0
- package/docs/index.html +1580 -0
- package/docs/social-card.html +237 -0
- package/docs/social-card.png +0 -0
- package/n8m-cover.png +0 -0
- package/n8m-logo-light.png +0 -0
- package/n8m-logo-mono.png +0 -0
- package/n8m-logo-v2.png +0 -0
- package/oclif.manifest.json +68 -1
- package/package.json +12 -1
package/README.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://n8m.run/social-card.png" alt="n8m — agentic n8n workflow generator" width="100%" />
|
|
3
|
+
</div>
|
|
2
4
|
|
|
3
5
|
> Generate, modify, test, and deploy n8n workflows from the command line using
|
|
4
6
|
> AI.
|
|
@@ -10,6 +12,8 @@
|
|
|
10
12
|
[](https://oclif.io/)
|
|
11
13
|
[](https://n8n.io)
|
|
12
14
|
[](https://github.com/Lee-Holdings-International/n8m#sponsors)
|
|
15
|
+
[](https://github.com/Lee-Holdings-International/n8m/discussions)
|
|
16
|
+
[](CODE_OF_CONDUCT.md)
|
|
13
17
|
|
|
14
18
|
**Stop clicking. Start shipping.** `n8m` is an open-source CLI that wraps your
|
|
15
19
|
n8n instance with an agentic AI layer. Describe what you want in plain English —
|
|
@@ -50,8 +54,8 @@ npx n8m config --ai-provider gemini --ai-key AIza...
|
|
|
50
54
|
npx n8m config --ai-base-url http://localhost:11434/v1 --ai-key ollama --ai-model llama3
|
|
51
55
|
```
|
|
52
56
|
|
|
53
|
-
You can also use environment variables or a `.env` file —
|
|
54
|
-
|
|
57
|
+
You can also use environment variables or a `.env` file — stored config takes
|
|
58
|
+
priority; env vars are used as fallback when no config file value is set:
|
|
55
59
|
|
|
56
60
|
| Variable | Description |
|
|
57
61
|
| ------------- | -------------------------------------------------------- |
|
|
@@ -285,6 +289,12 @@ n8m deploy ./workflows/my-flow.json
|
|
|
285
289
|
|
|
286
290
|
# Activate the workflow immediately after deployment
|
|
287
291
|
n8m deploy ./workflows/my-flow.json --activate
|
|
292
|
+
|
|
293
|
+
# Non-interactive: always update the existing workflow (useful in CI)
|
|
294
|
+
n8m deploy ./workflows/my-flow.json --update
|
|
295
|
+
|
|
296
|
+
# Non-interactive: always create a new workflow, ignoring any existing ID
|
|
297
|
+
n8m deploy ./workflows/my-flow.json --force-create
|
|
288
298
|
```
|
|
289
299
|
|
|
290
300
|
---
|
|
@@ -378,12 +388,18 @@ n8m mcp
|
|
|
378
388
|
# → Starting n8m MCP Server...
|
|
379
389
|
```
|
|
380
390
|
|
|
381
|
-
The server runs over stdio and registers
|
|
391
|
+
The server runs over stdio and registers the following tools:
|
|
382
392
|
|
|
383
393
|
| Tool | Description |
|
|
384
394
|
|---|---|
|
|
385
395
|
| `create_workflow` | Generate an n8n workflow from a natural-language goal |
|
|
396
|
+
| `modify_workflow` | Modify an existing workflow JSON using natural language instructions |
|
|
386
397
|
| `test_workflow` | Deploy a workflow ephemerally to n8n and validate it |
|
|
398
|
+
| `deploy_workflow` | Create or update a workflow on the configured n8n instance |
|
|
399
|
+
| `get_workflow` | Fetch a workflow from n8n by ID |
|
|
400
|
+
| `list_workflows` | List all workflows on the n8n instance |
|
|
401
|
+
| `delete_workflow` | Delete a workflow from n8n by ID |
|
|
402
|
+
| `generate_docs` | Generate a Mermaid diagram and README for a workflow JSON |
|
|
387
403
|
|
|
388
404
|
Add it to your MCP client config (e.g. Claude Desktop's `claude_desktop_config.json`):
|
|
389
405
|
|
|
@@ -507,7 +523,7 @@ Developer → n8m learn <workflow.json>
|
|
|
507
523
|
## Local Development
|
|
508
524
|
|
|
509
525
|
```bash
|
|
510
|
-
git clone https://github.com/
|
|
526
|
+
git clone https://github.com/Lee-Holdings-International/n8m.git
|
|
511
527
|
cd n8m
|
|
512
528
|
npm install
|
|
513
529
|
|
|
@@ -550,7 +566,7 @@ evolving node ecosystem.
|
|
|
550
566
|
|
|
551
567
|
### Shipped
|
|
552
568
|
|
|
553
|
-
- [x] Agentic graph (Architect → Engineer → QA)
|
|
569
|
+
- [x] Agentic graph (Architect → Engineer → Supervisor → Reviewer → QA)
|
|
554
570
|
- [x] SQLite session persistence
|
|
555
571
|
- [x] HITL interrupts and resume
|
|
556
572
|
- [x] Sub-workflow dependency resolution in tests
|
|
@@ -566,13 +582,15 @@ evolving node ecosystem.
|
|
|
566
582
|
- [x] Pattern library — extract & reuse knowledge from validated workflows (`n8m learn`)
|
|
567
583
|
- [x] GitHub pattern archive import (`n8m learn --github owner/repo`)
|
|
568
584
|
- [x] MCP server — expose n8m as tools for Claude Desktop and other MCP clients
|
|
585
|
+
- [x] **`n8m rollback`** — restore a workflow to a previous git-tracked version with a single command
|
|
586
|
+
- [x] **Credential awareness** — AI consults available credential types on the target instance so it stops generating nodes it can't authenticate
|
|
587
|
+
- [x] **MCP server expanded to 8 tools** — full workflow lifecycle from any MCP client: create, modify, test, deploy, get, list, delete, generate docs
|
|
588
|
+
- [x] **Non-interactive deploy flags** — `--update` and `--force-create` for CI/scripted use without interactive prompts
|
|
569
589
|
|
|
570
590
|
### Near-term
|
|
571
591
|
|
|
572
592
|
- [ ] **`n8m watch`** — file-system watcher that auto-deploys on save with live reload in the n8n editor
|
|
573
|
-
- [ ] **`n8m diff`** — human-readable structural diff between two versions
|
|
574
|
-
- [ ] **`n8m rollback`** — restore a workflow to a previous git-tracked version with a single command
|
|
575
|
-
- [ ] **Credential awareness** — AI consults available credential types on the target instance so it stops generating nodes it can't authenticate
|
|
593
|
+
- [ ] **`n8m diff`** — human-readable structural diff between two workflow versions (nodes added/removed/changed)
|
|
576
594
|
- [ ] **Parallel test runs** — fire multiple fixture payloads concurrently and report aggregate pass/fail
|
|
577
595
|
|
|
578
596
|
### Medium-term
|
package/banner.txt
ADDED
package/dist/agentic/graph.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
|
|
|
9
9
|
validationStatus: "passed" | "failed";
|
|
10
10
|
availableNodeTypes: string[];
|
|
11
11
|
revisionCount: number;
|
|
12
|
+
availableCredentials: import("../utils/n8nClient.js").N8nCredential[];
|
|
12
13
|
strategies: any[];
|
|
13
14
|
candidates: any[];
|
|
14
15
|
customTools: Record<string, string>;
|
|
@@ -26,6 +27,7 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
|
|
|
26
27
|
validationStatus?: "passed" | "failed" | undefined;
|
|
27
28
|
availableNodeTypes?: string[] | undefined;
|
|
28
29
|
revisionCount?: number | undefined;
|
|
30
|
+
availableCredentials?: import("../utils/n8nClient.js").N8nCredential[] | undefined;
|
|
29
31
|
strategies?: any[] | undefined;
|
|
30
32
|
candidates?: any[] | undefined;
|
|
31
33
|
customTools?: Record<string, string> | undefined;
|
|
@@ -75,6 +77,7 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
|
|
|
75
77
|
(annotation: import("@langchain/langgraph").SingleReducer<number, number>): import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
76
78
|
Root: <S extends import("@langchain/langgraph").StateDefinition>(sd: S) => import("@langchain/langgraph").AnnotationRoot<S>;
|
|
77
79
|
};
|
|
80
|
+
availableCredentials: import("@langchain/langgraph").BinaryOperatorAggregate<import("../utils/n8nClient.js").N8nCredential[], import("../utils/n8nClient.js").N8nCredential[]>;
|
|
78
81
|
strategies: {
|
|
79
82
|
(): import("@langchain/langgraph").LastValue<any[]>;
|
|
80
83
|
(annotation: import("@langchain/langgraph").SingleReducer<any[], any[]>): import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
|
@@ -136,6 +139,7 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
|
|
|
136
139
|
(annotation: import("@langchain/langgraph").SingleReducer<number, number>): import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
137
140
|
Root: <S extends import("@langchain/langgraph").StateDefinition>(sd: S) => import("@langchain/langgraph").AnnotationRoot<S>;
|
|
138
141
|
};
|
|
142
|
+
availableCredentials: import("@langchain/langgraph").BinaryOperatorAggregate<import("../utils/n8nClient.js").N8nCredential[], import("../utils/n8nClient.js").N8nCredential[]>;
|
|
139
143
|
strategies: {
|
|
140
144
|
(): import("@langchain/langgraph").LastValue<any[]>;
|
|
141
145
|
(annotation: import("@langchain/langgraph").SingleReducer<any[], any[]>): import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
|
@@ -252,6 +256,7 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
|
|
|
252
256
|
(annotation: import("@langchain/langgraph").SingleReducer<number, number>): import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
253
257
|
Root: <S extends import("@langchain/langgraph").StateDefinition>(sd: S) => import("@langchain/langgraph").AnnotationRoot<S>;
|
|
254
258
|
};
|
|
259
|
+
availableCredentials: import("@langchain/langgraph").BinaryOperatorAggregate<import("../utils/n8nClient.js").N8nCredential[], import("../utils/n8nClient.js").N8nCredential[]>;
|
|
255
260
|
strategies: {
|
|
256
261
|
(): import("@langchain/langgraph").LastValue<any[]>;
|
|
257
262
|
(annotation: import("@langchain/langgraph").SingleReducer<any[], any[]>): import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
|
@@ -321,6 +326,7 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
|
|
|
321
326
|
(annotation: import("@langchain/langgraph").SingleReducer<number, number>): import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
322
327
|
Root: <S extends import("@langchain/langgraph").StateDefinition>(sd: S) => import("@langchain/langgraph").AnnotationRoot<S>;
|
|
323
328
|
};
|
|
329
|
+
availableCredentials: import("@langchain/langgraph").BinaryOperatorAggregate<import("../utils/n8nClient.js").N8nCredential[], import("../utils/n8nClient.js").N8nCredential[]>;
|
|
324
330
|
strategies: {
|
|
325
331
|
(): import("@langchain/langgraph").LastValue<any[]>;
|
|
326
332
|
(annotation: import("@langchain/langgraph").SingleReducer<any[], any[]>): import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
|
@@ -390,6 +396,7 @@ export declare const runAgenticWorkflow: (goal: string, initialState?: Partial<t
|
|
|
390
396
|
(annotation: import("@langchain/langgraph").SingleReducer<number, number>): import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
391
397
|
Root: <S extends import("@langchain/langgraph").StateDefinition>(sd: S) => import("@langchain/langgraph").AnnotationRoot<S>;
|
|
392
398
|
};
|
|
399
|
+
availableCredentials: import("@langchain/langgraph").BinaryOperatorAggregate<import("../utils/n8nClient.js").N8nCredential[], import("../utils/n8nClient.js").N8nCredential[]>;
|
|
393
400
|
strategies: {
|
|
394
401
|
(): import("@langchain/langgraph").LastValue<any[]>;
|
|
395
402
|
(annotation: import("@langchain/langgraph").SingleReducer<any[], any[]>): import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
|
@@ -415,7 +422,7 @@ export declare const runAgenticWorkflow: (goal: string, initialState?: Partial<t
|
|
|
415
422
|
* @param goal The user's goal string
|
|
416
423
|
* @returns AsyncIterable for events
|
|
417
424
|
*/
|
|
418
|
-
export declare const runAgenticWorkflowStream: (goal: string, threadId?: string) => Promise<import("@langchain/core/utils/stream").IterableReadableStream<{
|
|
425
|
+
export declare const runAgenticWorkflowStream: (goal: string, threadId?: string, initialState?: Partial<typeof TeamState.State>) => Promise<import("@langchain/core/utils/stream").IterableReadableStream<{
|
|
419
426
|
architect?: {
|
|
420
427
|
spec: any;
|
|
421
428
|
collaborationLog: string[];
|
|
@@ -512,6 +519,7 @@ export declare const runAgenticWorkflowStream: (goal: string, threadId?: string)
|
|
|
512
519
|
(annotation: import("@langchain/langgraph").SingleReducer<number, number>): import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
513
520
|
Root: <S extends import("@langchain/langgraph").StateDefinition>(sd: S) => import("@langchain/langgraph").AnnotationRoot<S>;
|
|
514
521
|
};
|
|
522
|
+
availableCredentials: import("@langchain/langgraph").BinaryOperatorAggregate<import("../utils/n8nClient.js").N8nCredential[], import("../utils/n8nClient.js").N8nCredential[]>;
|
|
515
523
|
strategies: {
|
|
516
524
|
(): import("@langchain/langgraph").LastValue<any[]>;
|
|
517
525
|
(annotation: import("@langchain/langgraph").SingleReducer<any[], any[]>): import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
|
@@ -581,6 +589,7 @@ export declare const runAgenticWorkflowStream: (goal: string, threadId?: string)
|
|
|
581
589
|
(annotation: import("@langchain/langgraph").SingleReducer<number, number>): import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
582
590
|
Root: <S extends import("@langchain/langgraph").StateDefinition>(sd: S) => import("@langchain/langgraph").AnnotationRoot<S>;
|
|
583
591
|
};
|
|
592
|
+
availableCredentials: import("@langchain/langgraph").BinaryOperatorAggregate<import("../utils/n8nClient.js").N8nCredential[], import("../utils/n8nClient.js").N8nCredential[]>;
|
|
584
593
|
strategies: {
|
|
585
594
|
(): import("@langchain/langgraph").LastValue<any[]>;
|
|
586
595
|
(annotation: import("@langchain/langgraph").SingleReducer<any[], any[]>): import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
|
@@ -647,6 +656,7 @@ export declare const resumeAgenticWorkflow: (threadId: string, input?: any) => P
|
|
|
647
656
|
(annotation: import("@langchain/langgraph").SingleReducer<number, number>): import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
648
657
|
Root: <S extends import("@langchain/langgraph").StateDefinition>(sd: S) => import("@langchain/langgraph").AnnotationRoot<S>;
|
|
649
658
|
};
|
|
659
|
+
availableCredentials: import("@langchain/langgraph").BinaryOperatorAggregate<import("../utils/n8nClient.js").N8nCredential[], import("../utils/n8nClient.js").N8nCredential[]>;
|
|
650
660
|
strategies: {
|
|
651
661
|
(): import("@langchain/langgraph").LastValue<any[]>;
|
|
652
662
|
(annotation: import("@langchain/langgraph").SingleReducer<any[], any[]>): import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
package/dist/agentic/graph.js
CHANGED
|
@@ -72,12 +72,13 @@ export const runAgenticWorkflow = async (goal, initialState = {}, threadId = "de
|
|
|
72
72
|
* @param goal The user's goal string
|
|
73
73
|
* @returns AsyncIterable for events
|
|
74
74
|
*/
|
|
75
|
-
export const runAgenticWorkflowStream = async (goal, threadId = "default_session") => {
|
|
75
|
+
export const runAgenticWorkflowStream = async (goal, threadId = "default_session", initialState = {}) => {
|
|
76
76
|
return await graph.stream({
|
|
77
77
|
userGoal: goal,
|
|
78
78
|
messages: [],
|
|
79
79
|
validationErrors: [],
|
|
80
80
|
revisionCount: 0,
|
|
81
|
+
...initialState,
|
|
81
82
|
}, {
|
|
82
83
|
configurable: { thread_id: threadId }
|
|
83
84
|
});
|
|
@@ -17,13 +17,14 @@ export const architectNode = async (state) => {
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
try {
|
|
20
|
-
const
|
|
20
|
+
const credentials = state.availableCredentials ?? [];
|
|
21
|
+
const spec = await aiService.generateSpec(state.userGoal, credentials);
|
|
21
22
|
// Check if the spec requires clarification
|
|
22
23
|
const questions = spec.questions;
|
|
23
24
|
const needsClarification = questions && questions.length > 0;
|
|
24
25
|
// Multi-agent collaboration: generate an alternative strategy in parallel with the primary.
|
|
25
26
|
// Both are handed off to separate Engineer agents that run concurrently.
|
|
26
|
-
const alternativeSpec = await aiService.generateAlternativeSpec(state.userGoal, spec);
|
|
27
|
+
const alternativeSpec = await aiService.generateAlternativeSpec(state.userGoal, spec, credentials);
|
|
27
28
|
const alternativeModel = aiService.getAlternativeModel();
|
|
28
29
|
const strategies = [
|
|
29
30
|
{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AIService } from "../../services/ai.service.js";
|
|
1
|
+
import { AIService, buildCredentialContext } from "../../services/ai.service.js";
|
|
2
2
|
import { NodeDefinitionsService } from "../../services/node-definitions.service.js";
|
|
3
3
|
import { jsonrepair } from "jsonrepair";
|
|
4
4
|
import { theme } from "../../utils/theme.js";
|
|
@@ -75,13 +75,15 @@ export const engineerNode = async (state) => {
|
|
|
75
75
|
if (!state.spec) {
|
|
76
76
|
throw new Error("Workflow specification is missing.");
|
|
77
77
|
}
|
|
78
|
+
const credentialContext = buildCredentialContext(state.availableCredentials ?? []);
|
|
78
79
|
try {
|
|
79
80
|
const prompt = `You are an n8n Workflow Engineer.
|
|
80
81
|
Generate the valid n8n workflow JSON(s) based on the following approved Specification.
|
|
81
|
-
|
|
82
|
+
|
|
82
83
|
Specification:
|
|
83
84
|
${JSON.stringify(state.spec, null, 2)}
|
|
84
85
|
${ragContext}
|
|
86
|
+
${credentialContext}
|
|
85
87
|
${state.userFeedback ? `\n\nUSER FEEDBACK / REFINEMENTS:\n${state.userFeedback}\n(Incorporate this feedback into the generation process)` : ""}
|
|
86
88
|
|
|
87
89
|
IMPORTANT:
|
package/dist/agentic/state.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BaseMessage } from "@langchain/core/messages";
|
|
2
|
+
import type { N8nCredential } from '../utils/n8nClient.js';
|
|
2
3
|
export declare const TeamState: import("@langchain/langgraph").AnnotationRoot<{
|
|
3
4
|
userGoal: {
|
|
4
5
|
(): import("@langchain/langgraph").LastValue<string>;
|
|
@@ -41,6 +42,7 @@ export declare const TeamState: import("@langchain/langgraph").AnnotationRoot<{
|
|
|
41
42
|
(annotation: import("@langchain/langgraph").SingleReducer<number, number>): import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
42
43
|
Root: <S extends import("@langchain/langgraph").StateDefinition>(sd: S) => import("@langchain/langgraph").AnnotationRoot<S>;
|
|
43
44
|
};
|
|
45
|
+
availableCredentials: import("@langchain/langgraph").BinaryOperatorAggregate<N8nCredential[], N8nCredential[]>;
|
|
44
46
|
strategies: {
|
|
45
47
|
(): import("@langchain/langgraph").LastValue<any[]>;
|
|
46
48
|
(annotation: import("@langchain/langgraph").SingleReducer<any[], any[]>): import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
package/dist/agentic/state.js
CHANGED
|
@@ -12,6 +12,10 @@ export const TeamState = Annotation.Root({
|
|
|
12
12
|
validationStatus: (Annotation),
|
|
13
13
|
availableNodeTypes: (Annotation),
|
|
14
14
|
revisionCount: (Annotation),
|
|
15
|
+
availableCredentials: Annotation({
|
|
16
|
+
value: (_x, y) => y ?? [],
|
|
17
|
+
default: () => [],
|
|
18
|
+
}),
|
|
15
19
|
// Parallel Execution Support
|
|
16
20
|
strategies: (Annotation),
|
|
17
21
|
candidates: Annotation({
|
package/dist/commands/create.js
CHANGED
|
@@ -78,9 +78,23 @@ export default class Create extends Command {
|
|
|
78
78
|
// 2. AGENTIC EXECUTION
|
|
79
79
|
const threadId = randomUUID();
|
|
80
80
|
this.log(theme.info(`\nInitializing Agentic Workflow for: "${description}" (Session: ${threadId})`));
|
|
81
|
+
// Fetch available credentials for AI guidance (gracefully skipped if n8n not configured)
|
|
82
|
+
let availableCredentials = [];
|
|
83
|
+
{
|
|
84
|
+
const preConfig = await ConfigManager.load();
|
|
85
|
+
const preUrl = process.env.N8N_API_URL || preConfig.n8nUrl;
|
|
86
|
+
const preKey = process.env.N8N_API_KEY || preConfig.n8nKey;
|
|
87
|
+
if (preUrl && preKey) {
|
|
88
|
+
const preClient = new N8nClient({ apiUrl: preUrl, apiKey: preKey });
|
|
89
|
+
availableCredentials = await preClient.getCredentials();
|
|
90
|
+
if (availableCredentials.length > 0) {
|
|
91
|
+
this.log(theme.muted(` Found ${availableCredentials.length} credential(s) on n8n instance — AI will use these for node selection.`));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
81
95
|
let lastWorkflowJson = null;
|
|
82
96
|
try {
|
|
83
|
-
const stream = await runAgenticWorkflowStream(description, threadId);
|
|
97
|
+
const stream = await runAgenticWorkflowStream(description, threadId, { availableCredentials });
|
|
84
98
|
for await (const event of stream) {
|
|
85
99
|
// event keys correspond to node names that just finished
|
|
86
100
|
const nodeName = Object.keys(event)[0];
|
|
@@ -184,7 +198,6 @@ export default class Create extends Command {
|
|
|
184
198
|
this.log(theme.header('\nCHATTING WITH THE ENGINEER'));
|
|
185
199
|
this.log(theme.muted(` Plan: ${currentSpec?.suggestedName}`));
|
|
186
200
|
this.log(theme.muted(` Type your question or request. Enter "done" when ready to build.\n`));
|
|
187
|
-
// eslint-disable-next-line no-constant-condition
|
|
188
201
|
while (true) {
|
|
189
202
|
const { message } = await inquirer.prompt([{
|
|
190
203
|
type: 'input',
|
|
@@ -9,6 +9,8 @@ export default class Deploy extends Command {
|
|
|
9
9
|
instance: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
10
|
activate: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
11
|
dir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
update: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
'force-create': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
14
|
};
|
|
13
15
|
run(): Promise<void>;
|
|
14
16
|
}
|
package/dist/commands/deploy.js
CHANGED
|
@@ -64,6 +64,14 @@ export default class Deploy extends Command {
|
|
|
64
64
|
char: 'd',
|
|
65
65
|
description: 'Directory to scan for workflows (default: ./workflows)',
|
|
66
66
|
}),
|
|
67
|
+
update: Flags.boolean({
|
|
68
|
+
description: 'Update existing workflow without prompting (non-interactive)',
|
|
69
|
+
default: false,
|
|
70
|
+
}),
|
|
71
|
+
'force-create': Flags.boolean({
|
|
72
|
+
description: 'Always create as a new workflow, ignoring any existing ID (non-interactive)',
|
|
73
|
+
default: false,
|
|
74
|
+
}),
|
|
67
75
|
};
|
|
68
76
|
async run() {
|
|
69
77
|
this.log(theme.brand());
|
|
@@ -126,14 +134,23 @@ export default class Deploy extends Command {
|
|
|
126
134
|
// not found — will create
|
|
127
135
|
}
|
|
128
136
|
if (existsRemotely) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
+
let action;
|
|
138
|
+
if (flags.update) {
|
|
139
|
+
action = 'update';
|
|
140
|
+
}
|
|
141
|
+
else if (flags['force-create']) {
|
|
142
|
+
action = 'create';
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
const { default: select } = await import('@inquirer/select');
|
|
146
|
+
action = await select({
|
|
147
|
+
message: `Workflow "${workflowData.name}" already exists in n8n (ID: ${workflowData.id}). What would you like to do?`,
|
|
148
|
+
choices: [
|
|
149
|
+
{ name: 'Update existing workflow', value: 'update' },
|
|
150
|
+
{ name: 'Create as new workflow', value: 'create' },
|
|
151
|
+
],
|
|
152
|
+
});
|
|
153
|
+
}
|
|
137
154
|
if (action === 'update') {
|
|
138
155
|
await client.updateWorkflow(workflowData.id, workflowData);
|
|
139
156
|
deployedId = workflowData.id;
|
package/dist/commands/fixture.js
CHANGED
|
@@ -26,6 +26,7 @@ export default class Fixture extends Command {
|
|
|
26
26
|
'<%= config.bin %> fixture capture abc123 # pull latest real execution for a specific workflow ID',
|
|
27
27
|
];
|
|
28
28
|
async run() {
|
|
29
|
+
this.log(theme.brand());
|
|
29
30
|
const { args } = await this.parse(Fixture);
|
|
30
31
|
if (args.action === 'init') {
|
|
31
32
|
await this.initFixture(args.workflowId);
|
package/dist/commands/learn.js
CHANGED
|
@@ -162,7 +162,7 @@ export default class Learn extends Command {
|
|
|
162
162
|
return;
|
|
163
163
|
}
|
|
164
164
|
this.log(theme.info(`Found ${mdFiles.length} pattern(s).`));
|
|
165
|
-
const { checkbox } = await import('inquirer');
|
|
165
|
+
const { default: checkbox } = await import('@inquirer/checkbox');
|
|
166
166
|
const selected = await checkbox({
|
|
167
167
|
message: 'Select patterns to import:',
|
|
168
168
|
choices: mdFiles.map(f => ({ name: f.path, value: f.path, checked: true })),
|
package/dist/commands/modify.js
CHANGED
|
@@ -55,6 +55,17 @@ export default class Modify extends Command {
|
|
|
55
55
|
catch (e) {
|
|
56
56
|
this.log(theme.warn(`⚠ Failed to fetch node types: ${e.message}`));
|
|
57
57
|
}
|
|
58
|
+
// 1b. Fetch available credentials for AI guidance
|
|
59
|
+
let availableCredentials = [];
|
|
60
|
+
try {
|
|
61
|
+
availableCredentials = await client.getCredentials();
|
|
62
|
+
if (availableCredentials.length > 0) {
|
|
63
|
+
this.log(theme.muted(` Found ${availableCredentials.length} credential(s) — AI will use these for node selection.`));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Non-fatal — proceed without credential context
|
|
68
|
+
}
|
|
58
69
|
// 2. Resolve Workflow
|
|
59
70
|
let workflowData;
|
|
60
71
|
let workflowName = 'Untitled';
|
|
@@ -152,7 +163,8 @@ export default class Modify extends Command {
|
|
|
152
163
|
messages: [],
|
|
153
164
|
validationErrors: [],
|
|
154
165
|
workflowJson: workflowData,
|
|
155
|
-
availableNodeTypes: validNodeTypes
|
|
166
|
+
availableNodeTypes: validNodeTypes,
|
|
167
|
+
availableCredentials,
|
|
156
168
|
};
|
|
157
169
|
try {
|
|
158
170
|
const stream = await graph.stream({
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import type { GitCommit } from '../services/git.service.js';
|
|
3
|
+
export interface RollbackChoice {
|
|
4
|
+
name: string;
|
|
5
|
+
value: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Build the interactive select choices from a list of git commits.
|
|
9
|
+
* The most-recent commit is labelled "(HEAD)" to make it easy to identify.
|
|
10
|
+
* Pure function — safe to unit test.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildRollbackChoices(commits: GitCommit[]): RollbackChoice[];
|
|
13
|
+
/**
|
|
14
|
+
* Build a human-readable diff preview comparing the current on-disk version
|
|
15
|
+
* of a workflow against the version about to be restored.
|
|
16
|
+
* Pure function — safe to unit test.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildRollbackPreview(currentJson: any, targetJson: any): string;
|
|
19
|
+
export default class Rollback extends Command {
|
|
20
|
+
static args: {
|
|
21
|
+
workflow: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
22
|
+
};
|
|
23
|
+
static description: string;
|
|
24
|
+
static examples: string[];
|
|
25
|
+
static flags: {
|
|
26
|
+
instance: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
27
|
+
deploy: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
28
|
+
dir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
29
|
+
};
|
|
30
|
+
run(): Promise<void>;
|
|
31
|
+
}
|