fix(installer): Windows npm launcher EINVAL on modern Node (#289) (#292)

The npm thin-installer shim spawned the per-platform bundle's `.cmd`
launcher directly. Modern Node on Windows refuses to spawn `.cmd`/`.bat`
without `shell: true` (the CVE-2024-27980 hardening), so every `codegraph`
command failed with `spawnSync …\codegraph.cmd EINVAL` (seen on Node 24).

On Windows the shim now invokes the bundled `node.exe` against the app
entry point directly, bypassing the `.cmd` (and avoiding the arg-quoting
pitfalls of `shell: true`). Unix is unchanged.

Validated end-to-end against a real win32-x64 bundle: `npm install` of the
packed tarballs + `codegraph init -i`/`status` run on the bundled Node 24.

Also cuts release 0.9.2, rolling up the pending Drupal, zero-config,
config-removal, Hermes-installer, and symlink-security changes.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-05-21 21:22:50 -05:00
committed by GitHub
parent f6772dac7c
commit c41559a9d0
5 changed files with 46 additions and 9 deletions
+3 -1
View File
@@ -50,7 +50,9 @@ linux/amd64`).
bundles ship as per-platform `optionalDependencies`
(`@colbymchenry/codegraph-<target>` with `os`/`cpu`), so npm installs only the
matching one. The shim — run by the user's Node — execs the bundle, so the
real work runs on the bundled Node 24. Works even on old Node.
real work runs on the bundled Node 24. Works even on old Node. On Windows it
invokes the bundled `node.exe` against the app entry directly (not the `.cmd`
launcher) — modern Node throws `EINVAL` when asked to spawn a `.cmd`/`.bat`.
3. **Windows** ([`install.ps1`](install.ps1)) — `irm … | iex`; same flow as
install.sh (detect arch, pull the `.zip` from Releases, add to PATH).
4. **Homebrew / Scoop** — TODO (tap + cask pointing at the Release archives).
+24 -1
View File
@@ -7,9 +7,13 @@ a [GitHub Release](https://github.com/colbymchenry/codegraph/releases) tagged
This project follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.9.2] - 2026-05-21
### Added
- **Installer target: Hermes Agent (Nous Research).** `codegraph install` now
supports Hermes Agent — it writes the `mcp_servers.codegraph` entry and ensures
`platform_toolsets.cli` includes `mcp-codegraph` in `$HERMES_HOME/config.yaml`,
so Hermes can drive the CodeGraph knowledge graph like the other agents.
- **Framework support: Drupal 8/9/10/11** — CodeGraph now detects Drupal
projects (via a `drupal/*` dependency in `composer.json`) and adds three
levels of intelligence:
@@ -42,6 +46,15 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
those names; now `.gitignore` is the single source of truth. Resolves
[#283](https://github.com/colbymchenry/codegraph/issues/283).
### Fixed
- **Windows: `npm i -g @colbymchenry/codegraph` then any `codegraph` command
failed with `spawnSync …\codegraph.cmd EINVAL`.** The npm launcher spawned the
bundle's `.cmd` file directly, which modern Node refuses to do on Windows
(the CVE-2024-27980 hardening — seen on Node 24). The launcher now invokes the
bundled `node.exe` against the app directly, so `codegraph` works on Windows
regardless of your Node version. Resolves
[#289](https://github.com/colbymchenry/codegraph/issues/289).
### Removed
- **`.codegraph/config.json` and the entire config surface.** Every field was
either inert or now redundant with `.gitignore`:
@@ -60,6 +73,15 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
exports are gone. Existing `.codegraph/config.json` files are simply ignored.
The `.codegraphignore` marker is no longer supported — use `.gitignore`.
### Security
- **MCP session marker no longer follows symlinks** (CWE-59). Every
`codegraph_context` call writes a `codegraph-consulted-*` marker into the
system temp dir; the previous write followed symlinks, so on a multi-user
system another local user could pre-plant that path as a symlink and redirect
the write onto a victim-writable file. The marker is now opened with
`O_NOFOLLOW` and mode `0600`, and a planted symlink is refused rather than
followed. Resolves [#280](https://github.com/colbymchenry/codegraph/issues/280).
## [0.9.1] - 2026-05-21
### Fixed
@@ -71,6 +93,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
find its bundle. The release pipeline now verifies every package reached the
registry (and is idempotent), so a release can't pass green-but-broken again.
[0.9.2]: https://github.com/colbymchenry/codegraph/releases/tag/v0.9.2
[0.9.1]: https://github.com/colbymchenry/codegraph/releases/tag/v0.9.1
## [0.9.0] - 2026-05-21
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@colbymchenry/codegraph",
"version": "0.9.1",
"version": "0.9.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@colbymchenry/codegraph",
"version": "0.9.1",
"version": "0.9.2",
"license": "MIT",
"dependencies": {
"@clack/prompts": "^1.3.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@colbymchenry/codegraph",
"version": "0.9.1",
"version": "0.9.2",
"description": "Supercharge Claude Code with semantic code intelligence. 94% fewer tool calls • 77% faster exploration • 100% local.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
+16 -4
View File
@@ -19,11 +19,23 @@ var childProcess = require('child_process');
var target = process.platform + '-' + process.arch; // e.g. darwin-arm64, linux-x64
var pkg = '@colbymchenry/codegraph-' + target;
var launcher = process.platform === 'win32' ? 'bin/codegraph.cmd' : 'bin/codegraph';
var isWindows = process.platform === 'win32';
var binPath;
// On Windows the bundle's launcher is a .cmd batch file. Modern Node refuses to
// spawn .cmd/.bat directly — spawnSync throws EINVAL (the CVE-2024-27980
// hardening, observed on Node 24). So on Windows we skip the .cmd and invoke the
// bundled node.exe against the app entry point directly. On unix the bin launcher
// is a shell script that spawns cleanly.
var command, args;
try {
binPath = require.resolve(pkg + '/' + launcher);
if (isWindows) {
command = require.resolve(pkg + '/node.exe');
var entry = require.resolve(pkg + '/lib/dist/bin/codegraph.js');
args = [entry].concat(process.argv.slice(2));
} else {
command = require.resolve(pkg + '/bin/codegraph');
args = process.argv.slice(2);
}
} catch (e) {
process.stderr.write(
'codegraph: no prebuilt bundle for ' + target + '.\n' +
@@ -35,7 +47,7 @@ try {
process.exit(1);
}
var res = childProcess.spawnSync(binPath, process.argv.slice(2), { stdio: 'inherit' });
var res = childProcess.spawnSync(command, args, { stdio: 'inherit' });
if (res.error) {
process.stderr.write('codegraph: ' + res.error.message + '\n');
process.exit(1);