fix(cli): print the real package version for --version

printVersion() logged `process.env.npm_package_version ?? '0.2.0'`. npm only
populates npm_package_version inside its own run-script env, so an end user
running `npx pxpipe-proxy --version` or a globally-installed `pxpipe` got the
stale hardcoded fallback (0.2.0) instead of the real version — the code comment
even claimed an esbuild.define that was never wired up.

- scripts/build.mjs reads package.json and inlines the version via esbuild
  `define: { __PXPIPE_VERSION__ }`, then smoke-checks the built binary
  (`node dist/node.js --version` must equal package.json) and fails the build
  on mismatch, so a broken injection can never ship.
- src/node.ts reads the injected constant behind a `typeof` guard (safe under
  tsx, where it is undefined), falling back to npm_package_version then
  'unknown' — never a stale release number.

Verified: node dist/node.js --version now prints 0.8.0.
This commit is contained in:
Danil Silantyev
2026-07-04 12:33:51 +07:00
committed by Steven
parent 3f61348a19
commit 69c9f2e83c
2 changed files with 35 additions and 3 deletions
+25 -1
View File
@@ -3,10 +3,16 @@
// wrangler directly from src/worker.ts, but dist/worker.js is also emitted for
// package consumers via tsc.
import { build } from 'esbuild';
import { mkdir, rm } from 'node:fs/promises';
import { mkdir, rm, readFile } from 'node:fs/promises';
import { existsSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
// Single source of truth for the CLI version: read it here, inline it into the
// bundle via esbuild `define`. Reading npm_package_version at CLI *runtime* is
// unreliable (unset for global bins / npx, or the consumer's version), so the
// value is fixed at build time instead.
const pkg = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
const OUT = 'dist';
if (existsSync(OUT)) await rm(OUT, { recursive: true, force: true });
await mkdir(OUT, { recursive: true });
@@ -26,9 +32,27 @@ await build({
target: 'node18',
format: 'esm',
sourcemap: true,
// Inline the package version so `pxpipe --version` is correct for global/npx
// installs (see the note where `pkg` is read). esbuild replaces the bare
// identifier with the string literal at every reference.
define: { __PXPIPE_VERSION__: JSON.stringify(pkg.version) },
// Atlas is inlined as a base64 string in src/core/atlas.ts, so no external assets.
external: [],
banner: { js: '#!/usr/bin/env node' },
});
console.log('✓ built dist/node.js');
// Smoke check: the bundled CLI must report the real package version, not a
// stale fallback. Runs the shipped artifact end-to-end and fails the build on
// mismatch, so a broken version injection can never reach a release.
const smoke = spawnSync(process.execPath, ['dist/node.js', '--version'], { encoding: 'utf8' });
const printedVersion = (smoke.stdout ?? '').trim();
if (smoke.status !== 0 || printedVersion !== pkg.version) {
console.error(
`✗ version smoke check failed: 'node dist/node.js --version' printed ` +
`${JSON.stringify(printedVersion)} (exit ${smoke.status}), expected ${JSON.stringify(pkg.version)}`,
);
process.exit(1);
}
console.log(`✓ version smoke check: --version prints ${pkg.version}`);
+10 -2
View File
@@ -176,9 +176,17 @@ Use with OpenAI-compatible GPT clients:
`);
}
// Package version, inlined at bundle time by scripts/build.mjs via esbuild
// `define`. Under a non-bundled dev runner (tsx) the identifier is not defined;
// `typeof` returns "undefined" instead of throwing (ECMA-262 §13.5.3), so the
// guard is safe. `npm_package_version` is only a dev fallback: npm sets it just
// inside its own run-script env, so for `npx pxpipe-proxy` or a global bin it is
// undefined (or reflects the *consumer's* package), never this tool's version.
declare const __PXPIPE_VERSION__: string | undefined;
function printVersion(): void {
// Filled in at bundle time by esbuild.define; falls back here.
console.log(process.env.npm_package_version ?? '0.2.0');
const injected = typeof __PXPIPE_VERSION__ === 'string' ? __PXPIPE_VERSION__ : undefined;
console.log(injected ?? process.env.npm_package_version ?? 'unknown');
}
// ---- node:http <-> Web Request/Response bridge ---------------------------