@n8n-as-code/n8nac 2026.3.1 → 2026.3.2-next.3
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/package.json +1 -1
- package/src/child-env.ts +34 -0
- package/src/cli.ts +10 -4
- package/src/tool.ts +9 -3
package/package.json
CHANGED
package/src/child-env.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a minimal environment for child processes.
|
|
3
|
+
* Only passes the vars needed for npx/node to operate, deliberately excluding
|
|
4
|
+
* any sensitive credentials that the parent (agent host) may hold in its env
|
|
5
|
+
* (e.g. LLM API keys), preventing accidental credential forwarding.
|
|
6
|
+
*/
|
|
7
|
+
export function getChildEnv(): NodeJS.ProcessEnv {
|
|
8
|
+
const env: NodeJS.ProcessEnv = {};
|
|
9
|
+
|
|
10
|
+
// Matches var names that look like credentials anywhere in the name — these are never forwarded
|
|
11
|
+
// even if they match another allowlist prefix (e.g. NODE_AUTH_TOKEN, npm_config_*:_authToken,
|
|
12
|
+
// npm_config_authority is intentionally over-blocked since we prefer false-positives to leaks).
|
|
13
|
+
const secretPattern = /(?:auth|token|password|secret|apikey|api_key|_key)/i;
|
|
14
|
+
|
|
15
|
+
for (const key of Object.keys(process.env)) {
|
|
16
|
+
const upperKey = key.toUpperCase();
|
|
17
|
+
if (
|
|
18
|
+
// Basic system vars needed by node/npx (case-insensitive, including Windows-specific ones)
|
|
19
|
+
/^(PATH|HOME|USERPROFILE|HOMEDRIVE|HOMEPATH|TMPDIR|TMP|TEMP|LANG|LC_ALL|SHELL|TERM|TERM_PROGRAM|NODE_PATH|NODE_OPTIONS|SYSTEMROOT|COMSPEC|PATHEXT)$/.test(
|
|
20
|
+
upperKey,
|
|
21
|
+
) ||
|
|
22
|
+
// npm execution/config vars required by npx — but NOT auth/token vars
|
|
23
|
+
// (e.g. excludes npm_config_//registry.npmjs.org/:_authToken)
|
|
24
|
+
(key.startsWith("npm_") && !secretPattern.test(key)) ||
|
|
25
|
+
// Specific safe NODE_* vars (deliberately NOT a prefix match to exclude NODE_AUTH_TOKEN)
|
|
26
|
+
/^NODE_(ENV|NO_WARNINGS|ICU_DATA)$/.test(upperKey) ||
|
|
27
|
+
// n8n-as-code specific vars
|
|
28
|
+
key.startsWith("N8N_AS_CODE_")
|
|
29
|
+
) {
|
|
30
|
+
env[key] = process.env[key];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return env;
|
|
34
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { spawn } from "node:child_process";
|
|
|
3
3
|
import type { ChildProcess, ChildProcessWithoutNullStreams } from "node:child_process";
|
|
4
4
|
import * as p from "@clack/prompts";
|
|
5
5
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
6
|
+
import { getChildEnv } from "./child-env.js";
|
|
6
7
|
import { isWorkspaceInitialized } from "./workspace.js";
|
|
7
8
|
|
|
8
9
|
type CliProgram = Parameters<Parameters<OpenClawPluginApi["registerCli"]>[0]>[0]["program"];
|
|
@@ -24,14 +25,14 @@ function runN8nac(
|
|
|
24
25
|
opts: {
|
|
25
26
|
cwd: string;
|
|
26
27
|
timeout: number;
|
|
27
|
-
|
|
28
|
+
stdinInput?: string;
|
|
28
29
|
stdio?: "pipe" | "inherit";
|
|
29
30
|
},
|
|
30
31
|
): Promise<RunResult> {
|
|
31
32
|
return new Promise((resolve) => {
|
|
32
33
|
const baseOptions = {
|
|
33
34
|
cwd: opts.cwd,
|
|
34
|
-
env:
|
|
35
|
+
env: getChildEnv(),
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
const child: ChildProcess | ChildProcessWithoutNullStreams =
|
|
@@ -65,6 +66,11 @@ function runN8nac(
|
|
|
65
66
|
});
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
if (opts.stdinInput !== undefined && "stdin" in child && child.stdin) {
|
|
70
|
+
child.stdin.write(`${opts.stdinInput}\n`);
|
|
71
|
+
child.stdin.end();
|
|
72
|
+
}
|
|
73
|
+
|
|
68
74
|
const timer = setTimeout(() => {
|
|
69
75
|
timedOut = true;
|
|
70
76
|
child.kill("SIGTERM");
|
|
@@ -154,10 +160,10 @@ export function registerN8nAcCli({ program, workspaceDir }: CliOpts): void {
|
|
|
154
160
|
const authSpinner = p.spinner();
|
|
155
161
|
authSpinner.start("Saving credentials…");
|
|
156
162
|
|
|
157
|
-
const authResult = await runN8nac(["init-auth", "--host", host], {
|
|
163
|
+
const authResult = await runN8nac(["init-auth", "--host", host, "--api-key-stdin"], {
|
|
158
164
|
cwd: workspaceDir,
|
|
159
165
|
timeout: 60_000,
|
|
160
|
-
|
|
166
|
+
stdinInput: apiKey,
|
|
161
167
|
});
|
|
162
168
|
|
|
163
169
|
if (authResult.exitCode !== 0) {
|
package/src/tool.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { Type } from "@sinclair/typebox";
|
|
3
|
+
import { getChildEnv } from "./child-env.js";
|
|
3
4
|
import { isWorkspaceInitialized } from "./workspace.js";
|
|
4
5
|
|
|
5
6
|
// ---------------------------------------------------------------------------
|
|
@@ -100,13 +101,13 @@ type RunResult = {
|
|
|
100
101
|
function runNpx(
|
|
101
102
|
args: string[],
|
|
102
103
|
cwd: string,
|
|
103
|
-
|
|
104
|
+
stdinInput?: string,
|
|
104
105
|
): Promise<RunResult> {
|
|
105
106
|
return new Promise((resolve) => {
|
|
106
107
|
const child = spawn("npx", ["--yes", "n8nac", ...args], {
|
|
107
108
|
cwd,
|
|
108
|
-
env: { ...process.env, ...env },
|
|
109
109
|
stdio: "pipe",
|
|
110
|
+
env: getChildEnv(),
|
|
110
111
|
});
|
|
111
112
|
|
|
112
113
|
let stdout = "";
|
|
@@ -131,6 +132,11 @@ function runNpx(
|
|
|
131
132
|
stderr += chunk.toString();
|
|
132
133
|
});
|
|
133
134
|
|
|
135
|
+
if (stdinInput !== undefined) {
|
|
136
|
+
child.stdin.write(`${stdinInput}\n`);
|
|
137
|
+
child.stdin.end();
|
|
138
|
+
}
|
|
139
|
+
|
|
134
140
|
const timer = setTimeout(() => {
|
|
135
141
|
timedOut = true;
|
|
136
142
|
child.kill("SIGTERM");
|
|
@@ -250,7 +256,7 @@ export function createN8nAcTool(opts: { workspaceDir: string }) {
|
|
|
250
256
|
if (!host || !key) {
|
|
251
257
|
return ok({ error: "n8nHost and n8nApiKey are required for init_auth" });
|
|
252
258
|
}
|
|
253
|
-
const r = await runNpx(["init-auth", "--host", host], workspaceDir,
|
|
259
|
+
const r = await runNpx(["init-auth", "--host", host, "--api-key-stdin"], workspaceDir, key);
|
|
254
260
|
if (r.exitCode !== 0) {
|
|
255
261
|
return ok({ error: r.stderr || r.stdout, exitCode: r.exitCode });
|
|
256
262
|
}
|