@tbrandenburg/node-red-cli 0.2.15 → 0.2.16

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
@@ -290,6 +290,15 @@ deterministic **named Docker volume** (derived from the `--user-dir` value)
290
290
  mounted inside the container, never a host bind mount — so "no stray host
291
291
  files" holds even for persistent installs.
292
292
 
293
+ For images/derived images that pre-install Node-RED node packages into
294
+ their own conventional userDir, setting the `NODE_RED_CLI_DEFAULT_USERDIR`
295
+ environment variable (inside the image, e.g. via `ENV`) to that path lets
296
+ `--docker` discover it automatically whenever `--user-dir` isn't given —
297
+ that directory is used as `userDir` and, like an explicit `--user-dir`,
298
+ never deleted afterward. If the path doesn't exist or isn't a directory,
299
+ `--docker` logs a warning and falls back to the normal ephemeral `userDir`
300
+ rather than failing the invocation.
301
+
293
302
  Fails fast with a clear `node-red-cli: docker unavailable: ...` error if
294
303
  the Docker CLI/daemon isn't reachable, or `node-red-cli: docker build
295
304
  failed: ...` if the image build fails (e.g. the local version isn't yet
@@ -320,10 +329,9 @@ already ships `opencode` + `node-red-agents`, e.g.
320
329
  [`ghcr.io/tbrandenburg/agentic-workflow-dev-env`](https://github.com/tbrandenburg/agentic-workflow-dev-env)
321
330
  (`--network` is required for network access, since the agent calls out to
322
331
  its own API; `--node-modules`/`--user-dir` are still required too, since
323
- Node-RED only discovers node types from a userDir it actually loaded
324
- see [#24](https://github.com/tbrandenburg/node-red-cli/issues/24) for a
325
- currently-tracked compatibility gap when the image's own default userDir
326
- already ships the package):
332
+ Node-RED only discovers node types from a userDir it actually loaded, and
333
+ `--docker` doesn't yet reuse an image's own pre-populated default userDir —
334
+ see [#31](https://github.com/tbrandenburg/node-red-cli/issues/31)):
327
335
 
328
336
  ```bash
329
337
  echo '{"payload":"Summarize this repo in one sentence.","cwd":"/repo"}' \
@@ -7,6 +7,12 @@
7
7
  * envelope as JSON from stdin, runs it against a real Node-RED runtime via
8
8
  * the shared `runFlowInvocation` (the exact same logic the host CLI uses
9
9
  * for its non-Docker path), and writes the formatted result to stdout.
10
+ *
11
+ * The `NODE_RED_CLI_DEFAULT_USERDIR` env-var convention (see #31), which
12
+ * lets an image's own pre-populated default userDir be discovered when
13
+ * `--user-dir` isn't given, is resolved entirely inside the shared
14
+ * `runFlowInvocation` (`src/run-envelope.js`) -- nothing to do here beyond
15
+ * the existing pass-through of `envelope.options`.
10
16
  */
11
17
 
12
18
  const { runFlowInvocation } = require("../src/run-envelope");
@@ -74,7 +74,9 @@ const HELP_TEXT = [
74
74
  "network access independent of installing any package), --read-only",
75
75
  "rootfs with a /tmp tmpfs, --cap-drop=ALL, --security-opt=no-new-privileges.",
76
76
  "When combined with --user-dir, persistence uses a named Docker volume,",
77
- "never a host bind mount.",
77
+ "never a host bind mount. Image authors can set",
78
+ "NODE_RED_CLI_DEFAULT_USERDIR=<path> so a pre-installed userDir is",
79
+ "discovered automatically when --user-dir isn't given.",
78
80
  "",
79
81
  "Example:",
80
82
  ' echo \'{"payload":{"x":4,"y":5}}\' | node-red-cli flows.json calculate',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tbrandenburg/node-red-cli",
3
- "version": "0.2.15",
3
+ "version": "0.2.16",
4
4
  "description": "Call existing Node-RED flows from Node.js and the command line",
5
5
  "main": "src/link-call.js",
6
6
  "bin": {
@@ -132,6 +132,34 @@ function waitForFlowsSettled(RED) {
132
132
  });
133
133
  }
134
134
 
135
+ /**
136
+ * Resolves the image/host-provided default `userDir` from the
137
+ * `NODE_RED_CLI_DEFAULT_USERDIR` environment variable (see #31): community
138
+ * Docker images that pre-install Node-RED node packages into their own
139
+ * conventional userDir can set this variable so `--docker` (without an
140
+ * explicit `--user-dir`) discovers it automatically. Returns `undefined` if
141
+ * unset. Fails open, not closed: if the path doesn't exist, isn't a
142
+ * directory, or isn't accessible, logs a one-line stderr warning and returns
143
+ * `undefined` so the caller falls back to its normal ephemeral tmpdir,
144
+ * rather than aborting the invocation.
145
+ */
146
+ function resolveDefaultUserDir() {
147
+ const configuredPath = process.env.NODE_RED_CLI_DEFAULT_USERDIR;
148
+ if (!configuredPath) return undefined;
149
+
150
+ try {
151
+ if (fs.statSync(configuredPath).isDirectory()) return configuredPath;
152
+ console.error(
153
+ `node-red-cli: NODE_RED_CLI_DEFAULT_USERDIR='${configuredPath}' is not usable (not a directory), falling back to an ephemeral userDir`
154
+ );
155
+ } catch (error) {
156
+ console.error(
157
+ `node-red-cli: NODE_RED_CLI_DEFAULT_USERDIR='${configuredPath}' is not usable (${error.message}), falling back to an ephemeral userDir`
158
+ );
159
+ }
160
+ return undefined;
161
+ }
162
+
135
163
  /**
136
164
  * Runs a single link-call invocation against a real, freshly booted
137
165
  * Node-RED runtime: installs any missing `--node-modules`, boots RED with
@@ -146,8 +174,13 @@ function waitForFlowsSettled(RED) {
146
174
  *
147
175
  * `options.userDir`, when set, is treated as a persistent directory and is
148
176
  * never removed afterward (host: an explicit `--user-dir`; container: the
149
- * fixed mount path of a named Docker volume). When omitted, an ephemeral
150
- * tmpdir is created and removed again after the call.
177
+ * fixed mount path of a named Docker volume). When omitted, and the
178
+ * `NODE_RED_CLI_DEFAULT_USERDIR` environment variable points at an existing
179
+ * directory (see `resolveDefaultUserDir`), that directory is used instead —
180
+ * also treated as persistent and never removed afterward, letting a Docker
181
+ * image's own pre-populated default userDir be discovered automatically
182
+ * (see #31). Otherwise an ephemeral tmpdir is created and removed again
183
+ * after the call.
151
184
  */
152
185
  async function runFlowInvocation({ flow, flowFile, msg, options }) {
153
186
  const {
@@ -160,7 +193,10 @@ async function runFlowInvocation({ flow, flowFile, msg, options }) {
160
193
  } = options;
161
194
 
162
195
  const persistentUserDir = Boolean(fixedUserDir);
163
- const userDir = fixedUserDir || fs.mkdtempSync(path.join(os.tmpdir(), "node-red-cli-"));
196
+ const imageDefaultUserDir = !persistentUserDir ? resolveDefaultUserDir() : undefined;
197
+ const userDir =
198
+ fixedUserDir || imageDefaultUserDir || fs.mkdtempSync(path.join(os.tmpdir(), "node-red-cli-"));
199
+ const managedUserDir = !persistentUserDir && !imageDefaultUserDir;
164
200
 
165
201
  try {
166
202
  if (nodeModules.length > 0) {
@@ -194,8 +230,8 @@ async function runFlowInvocation({ flow, flowFile, msg, options }) {
194
230
  await RED.stop();
195
231
  }
196
232
  } finally {
197
- if (!persistentUserDir) fs.rmSync(userDir, { recursive: true, force: true });
233
+ if (managedUserDir) fs.rmSync(userDir, { recursive: true, force: true });
198
234
  }
199
235
  }
200
236
 
201
- module.exports = { runFlowInvocation, stderrLogHandler };
237
+ module.exports = { runFlowInvocation, stderrLogHandler, resolveDefaultUserDir };