@n8n-as-code/n8nac 2026.4.0 → 2026.5.0-next.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @n8n-as-code/n8nac
2
2
 
3
+ ## [2026.4.1](https://github.com/EtienneLescot/n8n-as-code/compare/@n8n-as-code/n8nac@v2026.4.0...@n8n-as-code/n8nac@v2026.4.1) (2026-03-30)
4
+
5
+ ### Bug Fixes
6
+
7
+ * make agent workflow testing and sync state resilient ([5850d07](https://github.com/EtienneLescot/n8n-as-code/commit/5850d07d8136ffb24c5106c7391b2d49d4dd2e5d))
8
+
3
9
  ## [2026.4.0](https://github.com/EtienneLescot/n8n-as-code/compare/@n8n-as-code/n8nac@v2026.3.1...@n8n-as-code/n8nac@v2026.4.0) (2026-03-17)
4
10
 
5
11
  ### Features
package/README.md CHANGED
@@ -23,7 +23,7 @@ Restart the gateway, then run the setup wizard:
23
23
  openclaw n8nac:setup
24
24
  ```
25
25
 
26
- The wizard asks for your n8n host URL and API key once, saves them via
26
+ The wizard asks for your n8n host URL and API key once, saves an instance config via
27
27
  `n8nac init-auth`, selects your project, and generates an AI context file
28
28
  (`AGENTS.md`) in the workspace (`~/.openclaw/n8nac/`).
29
29
 
@@ -63,7 +63,7 @@ All files live in `~/.openclaw/n8nac/`:
63
63
 
64
64
  ```
65
65
  ~/.openclaw/n8nac/
66
- n8nac-config.json ← project binding (written by n8nac init-project)
66
+ n8nac-config.json ← saved instance configs + active selection
67
67
  AGENTS.md ← AI context (written by n8nac update-ai)
68
68
  workflows/ ← .workflow.ts files (your n8n workflows)
69
69
  ```
@@ -119,7 +119,7 @@ openclaw n8nac:setup
119
119
  ```
120
120
 
121
121
  Enter your n8n host and API key when prompted. The wizard writes
122
- `~/.openclaw/n8nac/n8nac-config.json` and generates `AGENTS.md`.
122
+ `~/.openclaw/n8nac/n8nac-config.json` with the saved instance configs and active selection, then generates `AGENTS.md`.
123
123
 
124
124
  ### 4. Iterate on the code
125
125
 
@@ -141,7 +141,7 @@ The plugin prefixes all `api.logger` calls with `[n8nac]`.
141
141
 
142
142
  ```
143
143
  ~/.openclaw/n8nac/
144
- n8nac-config.json ← written by init-project
144
+ n8nac-config.json ← saved instance configs + active selection
145
145
  AGENTS.md ← written by update-ai
146
146
  workflows/ ← .workflow.ts files
147
147
  ```
package/index.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { accessSync, constants, existsSync, mkdirSync, readFileSync } from "node:fs";
1
+ import { accessSync, constants, mkdirSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
4
4
  import { registerN8nAcCli } from "./src/cli.js";
5
5
  import { createN8nAcTool } from "./src/tool.js";
6
- import { getWorkspaceDir, isWorkspaceInitialized } from "./src/workspace.js";
6
+ import { getWorkspaceDir, isWorkspaceInitialized, readWorkspaceBinding } from "./src/workspace.js";
7
7
 
8
8
  // ---------------------------------------------------------------------------
9
9
  // Lightweight prompt context
@@ -21,17 +21,8 @@ Once you have both, call the \`n8nac\` tool with \`action: "init_auth"\`, then
21
21
  \`action: "init_project"\` to finish setup.
22
22
  `;
23
23
 
24
- function readConfig(workspaceDir: string): Record<string, string> {
25
- try {
26
- const raw = readFileSync(join(workspaceDir, "n8nac-config.json"), "utf-8");
27
- return JSON.parse(raw) as Record<string, string>;
28
- } catch {
29
- return {};
30
- }
31
- }
32
-
33
24
  function buildStatusHeader(workspaceDir: string): string {
34
- const cfg = readConfig(workspaceDir);
25
+ const cfg = readWorkspaceBinding(workspaceDir);
35
26
  const host = cfg.host ?? "(unknown)";
36
27
  const project = cfg.projectName ?? cfg.projectId ?? "(unknown)";
37
28
  return [
@@ -40,6 +31,7 @@ function buildStatusHeader(workspaceDir: string): string {
40
31
  "**The workspace is already fully initialized. Do NOT ask the user for credentials.**",
41
32
  "",
42
33
  `- Workspace directory: \`${workspaceDir}\``,
34
+ `- Active instance: \`${cfg.activeInstanceName ?? cfg.activeInstanceId ?? "(unknown)"}\``,
43
35
  `- n8n host: \`${host}\``,
44
36
  `- Active project: \`${project}\``,
45
37
  ].join("\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@n8n-as-code/n8nac",
3
- "version": "2026.4.0",
3
+ "version": "2026.5.0-next.8",
4
4
  "description": "OpenClaw plugin for n8n-as-code — create and manage n8n workflows from OpenClaw",
5
5
  "keywords": [
6
6
  "n8n",
@@ -15,7 +15,7 @@
15
15
  "url": "git+https://github.com/EtienneLescot/n8n-as-code.git",
16
16
  "directory": "plugins/openclaw/n8n-as-code"
17
17
  },
18
- "license": "Apache-2.0",
18
+ "license": "MIT",
19
19
  "type": "module",
20
20
  "scripts": {
21
21
  "build": "npm run typecheck",
@@ -20,6 +20,10 @@ Use this skill only for explicit n8n workflow work.
20
20
  - Use `action: "skills"` whenever you need node search or schema details.
21
21
  - Never guess node parameters. The schema lookup is the source of truth.
22
22
  - Treat `AGENTS.md` as the authoritative workflow-engineering protocol once this skill is active.
23
+ - When a workflow fails due to missing credentials (Class A), identify the missing credentials clearly and use the documented `n8nac` CLI commands from `AGENTS.md` (for example `npx --yes n8nac workflow credential-required <workflowId> --json`, `npx --yes n8nac credential schema <type>`, `npx --yes n8nac credential create --type <type> --name "<name>" --file cred.json --json`, and `npx --yes n8nac workflow activate <workflowId>`). Do not invent unsupported `n8nac` tool actions or CLI flags; use `--help` if you are unsure.
24
+ - When `n8nac test` reports that a webhook is not registered, treat that as a runtime-state issue first, not as a workflow-code bug. For classic Webhook/Form triggers, the test URL usually requires a manual arm step in the n8n editor (`Execute workflow` or `Listen for test event`). There is no documented public API here to arm test webhooks automatically.
25
+ - When a webhook call succeeds but the workflow still seems broken, inspect the resulting execution with the documented CLI commands from `AGENTS.md` (for example `npx --yes n8nac execution list --workflow-id <workflowId> --limit 5 --json` then `npx --yes n8nac execution get <executionId> --include-data --json`).
26
+ - For GET/HEAD webhooks, prefer `n8nac test --query <json>` when the workflow reads from `$json.query`. Do not invent flags like `--query` unless they are documented in the current `--help`.
23
27
 
24
28
  ## Reading workflow files efficiently
25
29
 
package/src/workspace.ts CHANGED
@@ -2,6 +2,15 @@ import { existsSync, readFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
4
 
5
+ export type WorkspaceBinding = {
6
+ host?: string;
7
+ projectId?: string;
8
+ projectName?: string;
9
+ syncFolder?: string;
10
+ activeInstanceId?: string;
11
+ activeInstanceName?: string;
12
+ };
13
+
5
14
  /**
6
15
  * Fixed workspace directory for V1.
7
16
  * All n8nac files (n8nac-config.json, AGENTS.md, workflows/) live here.
@@ -10,22 +19,63 @@ export function getWorkspaceDir(): string {
10
19
  return join(homedir(), ".openclaw", "n8nac");
11
20
  }
12
21
 
13
- /**
14
- * Returns true when n8nac has been initialized in the given directory,
15
- * meaning the config exists and contains a selected project + sync folder.
16
- */
17
- export function isWorkspaceInitialized(workspaceDir: string): boolean {
22
+ function readString(value: unknown): string {
23
+ return typeof value === "string" ? value.trim() : "";
24
+ }
25
+
26
+ function resolveActiveInstance(config: Record<string, unknown>): Record<string, unknown> | undefined {
27
+ if (!Array.isArray(config.instances)) {
28
+ return undefined;
29
+ }
30
+
31
+ const instances = config.instances.filter((value): value is Record<string, unknown> => !!value && typeof value === "object");
32
+ if (!instances.length) {
33
+ return undefined;
34
+ }
35
+
36
+ const activeInstanceId = readString(config.activeInstanceId);
37
+ if (activeInstanceId) {
38
+ return instances.find((instance) => readString(instance.id) === activeInstanceId) || instances[0];
39
+ }
40
+
41
+ return instances[0];
42
+ }
43
+
44
+ export function readWorkspaceBinding(workspaceDir: string): WorkspaceBinding {
18
45
  const configPath = join(workspaceDir, "n8nac-config.json");
19
- if (!existsSync(configPath)) return false;
46
+ if (!existsSync(configPath)) {
47
+ return {};
48
+ }
20
49
 
21
50
  try {
22
51
  const raw = readFileSync(configPath, "utf-8");
23
52
  const config = JSON.parse(raw) as Record<string, unknown>;
24
- const projectId = typeof config.projectId === "string" ? config.projectId.trim() : "";
25
- const projectName = typeof config.projectName === "string" ? config.projectName.trim() : "";
26
- const syncFolder = typeof config.syncFolder === "string" ? config.syncFolder.trim() : "";
27
- return projectId.length > 0 && projectName.length > 0 && syncFolder.length > 0;
53
+ const activeInstance = resolveActiveInstance(config);
54
+ const configActiveInstanceId = readString(config.activeInstanceId);
55
+ const resolvedActiveInstanceId = readString(activeInstance?.id);
56
+ const activeInstanceId =
57
+ resolvedActiveInstanceId && resolvedActiveInstanceId === configActiveInstanceId
58
+ ? configActiveInstanceId
59
+ : resolvedActiveInstanceId || undefined;
60
+
61
+ return {
62
+ host: readString(activeInstance?.host) || readString(config.host) || undefined,
63
+ projectId: readString(activeInstance?.projectId) || readString(config.projectId) || undefined,
64
+ projectName: readString(activeInstance?.projectName) || readString(config.projectName) || undefined,
65
+ syncFolder: readString(activeInstance?.syncFolder) || readString(config.syncFolder) || undefined,
66
+ activeInstanceId,
67
+ activeInstanceName: readString(activeInstance?.name) || undefined,
68
+ };
28
69
  } catch {
29
- return false;
70
+ return {};
30
71
  }
31
72
  }
73
+
74
+ /**
75
+ * Returns true when n8nac has been initialized in the given directory,
76
+ * meaning the config exists and contains a selected project + sync folder.
77
+ */
78
+ export function isWorkspaceInitialized(workspaceDir: string): boolean {
79
+ const binding = readWorkspaceBinding(workspaceDir);
80
+ return Boolean(binding.projectId && binding.projectName && binding.syncFolder);
81
+ }