@n8n-as-code/n8nac 2026.4.1 → 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/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.1",
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",
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
+ }