Skip to content

fix(cli): use a shared cross-platform npm process resolver without shell#2257

Merged
ghostdevv merged 7 commits intonpmx-dev:mainfrom
MathurAditya724:patch-5
Apr 4, 2026
Merged

fix(cli): use a shared cross-platform npm process resolver without shell#2257
ghostdevv merged 7 commits intonpmx-dev:mainfrom
MathurAditya724:patch-5

Conversation

@MathurAditya724
Copy link
Copy Markdown
Contributor

@MathurAditya724 MathurAditya724 commented Mar 24, 2026

Summary

Fixes the Windows connector auth flow while avoiding shell-based npm execution and DEP0190 warnings.

What changed

  • Added a shared npm process resolver in cli/src/npm-process.ts.
  • Routed npm invocations through this resolver in:
    • cli/src/cli.ts (npm login)
    • cli/src/npm-client.ts (non-interactive npm exec + package publish path)
  • Kept interactive PTY execution using npm directly.
  • On Windows, npm now runs via cmd.exe /d /s /c npm ... (no shell: true, no direct npm.cmd spawn).
  • Cleaned up an unused exported type to satisfy unused-code checks.

Why

The previous approach removed DEP0190 warnings but caused Windows runtime failures (spawn EINVAL) in login flow.
Using a centralized, explicit cross-platform command resolver keeps behavior consistent and avoids shell-based execution.

Closes #2216

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs.npmx.dev Ready Ready Preview, Comment Apr 4, 2026 9:57pm
npmx.dev Ready Ready Preview, Comment Apr 4, 2026 9:57pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
npmx-lunaria Ignored Ignored Apr 4, 2026 9:57pm

Request Review

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 24, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@mynameistito
Copy link
Copy Markdown

Dam I got nerd sniped!

Lmk if ya need a test on Windows (if you're not also on Windows / have a windows machine)

@MathurAditya724
Copy link
Copy Markdown
Contributor Author

if you have a windows machine, go ahead, more the testers the better. I don't have one, so was planning to ask my friend today evening, will mention how that went.

@mynameistito
Copy link
Copy Markdown

Checking it out now, are you in the discord? @MathurAditya724

there is a few issues with your implementation.

@MathurAditya724
Copy link
Copy Markdown
Contributor Author

yes, I'm .n1ghtmare.

@mynameistito
Copy link
Copy Markdown

mynameistito commented Mar 24, 2026

As stated on Discord, the changes you made seemed to have caused some issues;

my output with your changes
PS F:\GitHub\temp\npmx.dev\cli> npmx-connector
┌   npmx connector
│
▲  This allows npmx.dev to access your npm cli and any authenticated contexts.
│
◇  Do you accept?
│  Yes
│
●  Checking npm authentication...
│
▲  Not logged in to npm. Starting npm login...

Error: spawn EINVAL
    at ChildProcess.spawn (node:internal/child_process:421:11)
    at spawn (node:child_process:796:9)
    at file:///F:/GitHub/temp/npmx.dev/cli/dist/cli.mjs:16:17
    at new Promise (<anonymous>)
    at runNpmLogin (file:///F:/GitHub/temp/npmx.dev/cli/dist/cli.mjs:15:9)
    at Object.run (file:///F:/GitHub/temp/npmx.dev/cli/dist/cli.mjs:54:31)
    at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
    at async runCommand (file:///F:/GitHub/temp/npmx.dev/node_modules/.pnpm/citty@0.2.1/node_modules/
citty/dist/index.mjs:188:47)
    at async runMain (file:///F:/GitHub/temp/npmx.dev/node_modules/.pnpm/citty@0.2.1/node_modules/cit
ty/dist/index.mjs:285:10) {
  errno: -4071,
  code: 'EINVAL',
  syscall: 'spawn'
}

I asked Claude to fix up the issues and this was the Fix that the model proposed and I tested it manually and seems to work as intended;

Fix suggested by Claude Opus 4.6 Lines 15, through 21
// ~/cli.ts
-const NPM_COMMAND = process.platform === 'win32' ? 'npm.cmd' : 'npm'
-
 async function runNpmLogin(): Promise<boolean> {
   return new Promise(resolve => {
-    const child = spawn(NPM_COMMAND, ['login', `--registry=${NPM_REGISTRY_URL}`], {
-      stdio: 'inherit',
-    })
+
+    const loginArgs = ['login', `--registry=${NPM_REGISTRY_URL}`]
+    const child =
+      process.platform === 'win32'
+        ? spawn('cmd.exe', ['/c', 'npm', ...loginArgs], { stdio: 'inherit' })
+        : spawn('npm', loginArgs, { stdio: 'inherit' })

Routing through cmd.exe /c npm on Windows avoids both EINVAL and the DEP0190 warning — no .cmd file is spawned directly, and no shell: true is needed.

Might be good to get some more eyes on it though.

@mynameistito
Copy link
Copy Markdown

mynameistito commented Mar 24, 2026

Seems to work as intended now;

Whilst Logged in to npmjs;

PS F:\GitHub\temp\npmx.dev\cli> bun run build
$ tsdown
ℹ tsdown v0.21.4 powered by rolldown v1.0.0-rc.9
ℹ config file: F:\GitHub\temp\npmx.dev\cli\tsdown.config.ts
ℹ entry: src\index.ts, src\cli.ts
ℹ target: node24.0.0
ℹ tsconfig: tsconfig.json

 WARN  `external` is deprecated. Use `deps.neverBundle` instead.

ℹ Build start
ℹ Granting execute permission to dist\cli.mjs
ℹ dist\cli.mjs                       2.62 kB │ gzip:  1.23 kB
ℹ dist\index.mjs                     0.17 kB │ gzip:  0.14 kB
ℹ dist\server-FP9KQp94.mjs.map      81.93 kB │ gzip: 18.58 kB
ℹ dist\server-FP9KQp94.mjs          40.86 kB │ gzip:  9.61 kB
ℹ dist\cli.mjs.map                   4.53 kB │ gzip:  1.91 kB
ℹ dist\index.d.mts.map               2.16 kB │ gzip:  0.67 kB
ℹ dist\node-pty-PmtOFpde.d.mts.map   1.45 kB │ gzip:  0.65 kB
ℹ dist\index.d.mts                   5.20 kB │ gzip:  1.60 kB
ℹ dist\cli.d.mts                     0.01 kB │ gzip:  0.03 kB
ℹ dist\node-pty-PmtOFpde.d.mts       1.52 kB │ gzip:  0.59 kB
ℹ 10 files, total: 140.45 kB
✔ Build complete in 678ms
PS F:\GitHub\temp\npmx.dev\cli> bun install
bun install v1.3.11 (af24e281)

Checked 57 installs across 83 packages (no changes) [11.00ms]
PS F:\GitHub\temp\npmx.dev\cli> bun run build
$ tsdown
ℹ tsdown v0.21.4 powered by rolldown v1.0.0-rc.9
ℹ config file: F:\GitHub\temp\npmx.dev\cli\tsdown.config.ts
ℹ entry: src\index.ts, src\cli.ts
ℹ target: node24.0.0
ℹ tsconfig: tsconfig.json

 WARN  `external` is deprecated. Use `deps.neverBundle` instead.

ℹ Build start
ℹ Cleaning 11 files
ℹ Granting execute permission to dist\cli.mjs
ℹ dist\cli.mjs                       2.62 kB │ gzip:  1.23 kB
ℹ dist\index.mjs                     0.17 kB │ gzip:  0.14 kB
ℹ dist\server-FP9KQp94.mjs.map      81.93 kB │ gzip: 18.58 kB
ℹ dist\server-FP9KQp94.mjs          40.86 kB │ gzip:  9.61 kB
ℹ dist\cli.mjs.map                   4.53 kB │ gzip:  1.91 kB
ℹ dist\index.d.mts.map               2.16 kB │ gzip:  0.67 kB
ℹ dist\node-pty-PmtOFpde.d.mts.map   1.45 kB │ gzip:  0.65 kB
ℹ dist\index.d.mts                   5.20 kB │ gzip:  1.60 kB
ℹ dist\cli.d.mts                     0.01 kB │ gzip:  0.03 kB
ℹ dist\node-pty-PmtOFpde.d.mts       1.52 kB │ gzip:  0.59 kB
ℹ 10 files, total: 140.45 kB
✔ Build complete in 627ms
PS F:\GitHub\temp\npmx.dev\cli> bun link
bun link v1.3.11 (af24e281)
Success! Registered "npmx-connector"

To use npmx-connector in a project, run:
  bun link npmx-connector

Or add it in dependencies in your package.json file:
  "npmx-connector": "link:npmx-connector"
PS F:\GitHub\temp\npmx.dev\cli> npmx-connector
┌   npmx connector
│
▲  This allows npmx.dev to access your npm cli and any authenticated contexts.
│
◇  Do you accept?
│  Yes
│
●  Checking npm authentication...
│
●  Authenticated as: mynameistito
│
◇  Click to connect ──────────────────────────────────────────────────────────╮
│                                                                             │
│  Open: https://npmx.dev/?token=$TOKEN&port=31415  │
│                                                                             │
│  Or paste token manually: $TOKEN                  │
│                                                                             │
├─────────────────────────────────────────────────────────────────────────────╯
➜ Listening on: http://127.0.0.1:31415/
│
●  Waiting for connection... (Press Ctrl+C to stop)
Server closed successfully.
PS F:\GitHub\temp\npmx.dev\cli>

Without being logged into npmjs;

PS F:\GitHub\temp\npmx.dev\cli> npmx-connector
┌   npmx connector
│
▲  This allows npmx.dev to access your npm cli and any authenticated contexts.
│
◇  Do you accept?
│  Yes
│
●  Checking npm authentication...
│
▲  Not logged in to npm. Starting npm login...

npm notice Log in on https://registry.npmjs.org/
Login at:
https://www.npmjs.com/login?next=/login/cli/$UUID
Press ENTER to open in the browser...
/

However, there was an issue when trying to originally build on src/server.ts L 90 which doesnt seem to be related to your changes;

-export function createConnectorApp(expectedToken: string) {
+export function createConnectorApp(expectedToken: string): H3 {

@RYGRIT
Copy link
Copy Markdown
Contributor

RYGRIT commented Mar 24, 2026

Thanks for tackling this!

Directly spawning npm.cmd on Windows isn’t safe — see Node.js issue #58763 and the docs: .cmd/.bat files can’t be executed directly on Windows, they need a shell. Using args + shell: true triggers DEP0190 because args are just space-joined.

I’d recommend using cmd.exe /c npm ... on Windows.

@MathurAditya724 MathurAditya724 changed the title fix(cli): avoid DEP0190 warning on Windows by using npm.cmd instead of shell execution fix(cli): use a shared cross-platform npm process resolver without shell Mar 24, 2026
@MathurAditya724
Copy link
Copy Markdown
Contributor Author

@RYGRIT just changed the approach, thanks to @mynameistito, and update the PR title and description to better reflect what we have done

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8407dd5e-645b-4df1-a327-e4ede6e9c11d

📥 Commits

Reviewing files that changed from the base of the PR and between d35ff2f and 33fb969.

📒 Files selected for processing (2)
  • cli/src/cli.ts
  • cli/src/npm-client.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • cli/src/npm-client.ts
  • cli/src/cli.ts

📝 Walkthrough

Walkthrough

Adds a new utility cli/src/npm-process.ts exposing resolveNpmProcessCommand(npmArgs, platform, comSpec) and NpmProcessCommand. Calls that function from cli/src/cli.ts and cli/src/npm-client.ts to obtain { command, args } for npm invocations. Replaces prior uses of spawn('npm', ...)/execFileAsync('npm', ...) with executions of the resolved command and args, and removes use of shell: true/platform-specific shell options from those calls. No exported API signatures were changed.

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description clearly outlines the changes made and the rationale for fixing Windows connector auth flow issues.
Linked Issues check ✅ Passed The PR implements all coding requirements from issue #2216: eliminates DEP0190 warnings, avoids shell: true, ensures Windows compatibility without spawn EINVAL errors, and uses cross-platform command resolution.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the shared npm process resolver and routing npm invocations through it, directly addressing the linked issue objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
cli/src/npm-client.ts (1)

336-337: Please route the PTY path through the same resolver as well.

These two call sites now use resolveNpmProcessCommand(), but execNpmInteractive() still hardcodes pty.spawn('npm', npmArgs, ...) at Line 213. On Windows, Node documents that .cmd entrypoints need a terminal/cmd.exe, and node-pty’s own Windows example launches a shell rather than a raw command, so the interactive OTP/browser-auth branch is still on a different launcher. Please wire it through the same resolver, or an equivalent PTY-safe wrapper, so Windows behaviour stays consistent. (nodejs.org)

Also applies to: 611-612


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e08d4647-5925-49aa-87af-4b52453a8238

📥 Commits

Reviewing files that changed from the base of the PR and between 84a7fc9 and d35ff2f.

📒 Files selected for processing (3)
  • cli/src/cli.ts
  • cli/src/npm-client.ts
  • cli/src/npm-process.ts

Comment on lines +13 to +18
if (platform === 'win32') {
return {
command: comSpec || 'cmd.exe',
args: ['/d', '/s', '/c', 'npm', ...npmArgs],
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any examples anyone is aware of, of other projects doing this? Just want to make sure, as not a windows user, this is the safest best way 😅

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unsure if it's the safest way, however am aware that it works as intended to minimise the DEP1090 Issue

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looked into this a bit more.

The two approaches I found were either:

  • keep using a shell, but pass a single command string
  • explicitly use cmd.exe /d /s /c ... on Windows

Node-RED took the first approach for the same deprecation, with the main change in b364f8f. For this case though, I think the second one is the better fit. It is still consistent with Node’s Windows guidance for spawning .bat and .cmd files, while avoiding shell: true with args[], which is what DEP0190 warns about.

So this seems like a solid approach to me.

@ghostdevv ghostdevv added this pull request to the merge queue Apr 4, 2026
Merged via the queue into npmx-dev:main with commit 5fe1486 Apr 4, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG - cli win] DEP0190 deprecation warning

4 participants