Skip to content

Commit 5da2e54

Browse files
committed
fix: fail with a clear message when launched on Node.js older than 26.4
npm installs the CLI on any Node version (engines is a warning for consumers), but the TUI requires node:ffi via --experimental-ffi, which Node 24 does not have — the re-exec died with 'bad option'. Check the runtime version before re-exec and print the requirement plus the native-installer alternative.
1 parent a661a10 commit 5da2e54

2 files changed

Lines changed: 32 additions & 0 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@pythoughts/pythinker-code': patch
3+
---
4+
5+
Show a clear requirement message with the native-installer alternative when the CLI is launched on Node.js older than 26.4, instead of failing with a cryptic flag error.

apps/pythinker-code/src/launcher.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,24 @@ const FFI_FLAG = '--experimental-ffi';
44
const FFI_WARNING_FLAG = '--disable-warning=ExperimentalWarning';
55
const FFI_CHILD_ENV = 'PYTHINKER_CODE_FFI_CHILD';
66
const REQUIRED_RUNTIME = 'Node.js 26.4.0 or newer with experimental FFI support';
7+
const MINIMUM_NODE = [26, 4, 0] as const;
8+
const NATIVE_INSTALL_HINT =
9+
'Alternatively, use the native installer (no Node.js required): https://code.pythinker.com';
10+
11+
/**
12+
* Older Node (e.g. 24 LTS) has no `--experimental-ffi`, so the re-exec below
13+
* would die with a cryptic `bad option` error. npm installs the package on any
14+
* Node version (engines is only a warning for consumers), so guard here with
15+
* an actionable message instead.
16+
*/
17+
function isRuntimeTooOld(): boolean {
18+
const parts = process.versions.node.split('.').map(Number);
19+
const [major = 0, minor = 0, patch = 0] = parts;
20+
const [reqMajor, reqMinor, reqPatch] = MINIMUM_NODE;
21+
if (major !== reqMajor) return major < reqMajor;
22+
if (minor !== reqMinor) return minor < reqMinor;
23+
return patch < reqPatch;
24+
}
725

826
function isFfiProcess(): boolean {
927
// Only execArgv decides: a stale env marker must never bypass the FFI re-exec.
@@ -66,6 +84,15 @@ function launchWindowsFallback(
6684
}
6785

6886
async function launch(): Promise<void> {
87+
if (isRuntimeTooOld()) {
88+
process.stderr.write(
89+
`Pythinker Code requires ${REQUIRED_RUNTIME}; you are running Node.js ${process.versions.node}.\n` +
90+
`${NATIVE_INSTALL_HINT}\n`,
91+
);
92+
process.exitCode = 1;
93+
return;
94+
}
95+
6996
if (isFfiProcess()) {
7097
await import(new URL('./main.mjs', import.meta.url).href);
7198
return;

0 commit comments

Comments
 (0)