Skip to content

fix(npm): stop the xurl binary when the wrapper is terminated - #109

Open
EmbryoSpace wants to merge 1 commit into
xdevplatform:mainfrom
EmbryoSpace:fix/npm-wrapper-signals
Open

EmbryoSpace wants to merge 1 commit into
xdevplatform:mainfrom
EmbryoSpace:fix/npm-wrapper-signals

Conversation

@EmbryoSpace

Copy link
Copy Markdown

Fixes #93.

Problem

npm/cli.js runs the binary with execFileSync. That call never forwards termination signals to the child, so when a supervisor stops the Node wrapper, the binary keeps running, gets reparented, and still holds the host's stdin and stdout. For xurl mcp this means an MCP host that restarts the command collects a stray bridge process each time.

Fix

  • Start the binary with spawn (stdio: "inherit", same as before).
  • Forward SIGINT, SIGTERM, SIGHUP and SIGQUIT to it. Signals the platform can't listen for are skipped.
  • When the binary exits, exit with its code. If a signal killed it, the wrapper re-raises that signal, so callers see the same exit status as before.
  • Print a clear message if the binary can't be started. The "binary not found" check is unchanged.
  • Add a CHANGELOG entry under Unreleased.

Testing

On Linux (Docker, node:18-alpine and node:20-bookworm-slim), with a stand-in binary that records its pid and waits, I sent each signal to the wrapper and checked the binary 0.5 s later:

Signal to wrapper Before After Wrapper exit status
SIGTERM binary left running binary stopped 143, unchanged
SIGINT binary left running binary stopped 130, unchanged
SIGHUP binary left running binary stopped 129, unchanged

Also checked, before and after:

  • Arguments and exit codes still pass through (exit3 a "b c" exits 3 with the same arguments).
  • Piped stdin still reaches the binary, which is how MCP hosts talk to xurl mcp.
  • The missing-binary message and exit code 1 are unchanged.
  • On Windows (Node 24), arguments and exit codes pass through.

One limit: SIGKILL can't be caught, so a wrapper killed with SIGKILL still can't stop its child. Hosts that want a hard guarantee can invoke the binary directly. That's the other option in the issue, and I left it alone because it changes how the package is installed.

The npm wrapper ran the binary with execFileSync, which never forwards
termination signals. A supervisor or MCP host that stopped the wrapper
left the binary running, reparented, with the host's stdio still open.

Spawn the binary instead, forward SIGINT, SIGTERM, SIGHUP and SIGQUIT,
and exit with the binary's exit code or signal.

Fixes xdevplatform#93
@CLAassistant

CLAassistant commented Oct 26, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

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.

Node CLI wrapper leaves the binary running when the wrapper is terminated

2 participants