Merge pull request #651 from kaoru0822-kitauji/fix/node-runtime-engine-alignment

fix(setup): align Node runtime checks with package engines
This commit is contained in:
Kaoru0822 2026-10-05 22:45:21 -07:00 committed by GitHub
commit 5802ef8746
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 121 additions and 40 deletions

View File

@ -144,7 +144,7 @@ Update either installation with `rea update`.
- macOS 12 or newer
- Ubuntu 24.04+, Fedora 41+, or 64-bit Arch Linux
- Node.js 22.19+ or 24.11+ (including newer releases)
- Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+
- npm; REA does not require or install a particular npm version
Native binary analysis requires [Hopper](https://www.hopperapp.com/) or [Ghidra](#ghidra-read-only-analysis-provider). Hopper is separate software with its own license; its demo supports analysis with vendor-defined limits. REA can use Ghidra that you have already installed.

View File

@ -126,7 +126,7 @@ rea setup
- macOS 12 أو أحدث
- Ubuntu 24.04+ أو Fedora 41+ أو Arch Linux بنواة 64 بت
- Node.js 22.19+ أو 24.11+ وnpm
- Node.js 22.x (>=22.19) أو 24.x (>=24.11) أو 26+ وnpm
يتطلب تحليل الملفات التنفيذية الأصلية Hopper أو Ghidra. Hopper برنامج منفصل، وللوضع التجريبي قيود يحددها المورّد، لكن الترخيص المدفوع ليس إلزاميًا.

View File

@ -126,7 +126,7 @@ rea setup
- macOS 12 以降
- Ubuntu 24.04+、Fedora 41+、または 64 ビット Arch Linux
- Node.js 22.19+ または 24.11+ と npm
- Node.js 22.x (>=22.19)、24.x (>=24.11)、または 26+ と npm
ネイティブバイナリ解析には Hopper または Ghidra が必要です。Hopper は別製品です。デモにはベンダー所定の制限がありますが、有料ライセンスは必須ではありません。

View File

@ -127,7 +127,7 @@ rea setup
- macOS 12 이상
- Ubuntu 24.04+, Fedora 41+ 또는 64비트 Arch Linux
- Node.js 22.19+ 또는 24.11+와 npm
- Node.js 22.x (>=22.19), 24.x (>=24.11) 또는 26+와 npm
네이티브 바이너리 분석에는 Hopper 또는 Ghidra가 필요합니다. Hopper는 별도 소프트웨어입니다. 데모에는 공급업체의 제한이 있지만 유료 라이선스가 필수는 아닙니다.

View File

@ -126,7 +126,7 @@ rea setup
- macOS 12 或更高版本
- Ubuntu 24.04+、Fedora 41+ 或 64 位 Arch Linux
- Node.js 22.19+ 或 24.11+,以及 npm
- Node.js 22.x (>=22.19)、24.x (>=24.11) 或 26+,以及 npm
原生二进制分析需要 Hopper 或 Ghidra。Hopper 是独立软件;演示模式有厂商规定的限制,不要求购买许可证。

View File

@ -36,7 +36,7 @@ Setup continues to pin persistent MCP registrations to the exact version that
performed setup. Running current setup later migrates unversioned or older
managed registrations through the normal reviewed setup transaction.
REA supports Node.js 22.19+ and 24.11+ (including newer releases). It uses the npm already paired with that runtime and never upgrades Node.js, npm, or Homebrew.
REA supports Node.js 22.x (>=22.19), 24.x (>=24.11), and 26+. Node.js 23, 25, and prereleases are unsupported. It uses the npm already paired with that runtime and never upgrades Node.js, npm, or Homebrew.
Running `npm install rea-agents` without `--global` installs the executable only
in the current project's `node_modules/.bin`; it does not make `rea` available

View File

@ -55,7 +55,7 @@ harm_rank,location,trigger,current_copy,user_risk,proposed_replacement,implement
45.5,src/application/Uninstall.ts:removeClient restore,Configuration update fails and restoring the original also fails,"Configuration could not be updated or restored. Restore its `.rea.backup` manually, then rerun uninstall.","High risk; persistent configuration may be damaged, but retained backup is authoritative",Keep new recovery copy,implemented,verified by injected restoration failure test
46,src/application/Uninstall.ts:removeManagedPath symlink,Managed cache/state/skill path is a symbolic link,Refused to follow symlink {path}.,Exposes local path and does not explain manual safety check,"REA did not remove this item because its managed path is a symbolic link. Verify the link target before removing it manually.",implemented,focused assertion passed
47,src/application/Uninstall.ts:removeManagedPath failure,Managed item removal fails,{path} could not be removed.,Exposes local path and gives no recovery,"This item could not be removed. Check file permissions, then rerun uninstall.",implemented,verified by injected managed-path removal failure test
48,src/application/Doctor.ts:node,Runtime version is outside supported ranges,Install Node.js 22.19+ or 24.11+.,Low risk; exact dependency recovery,Keep current copy,unchanged,verified by doctor unit test
48,src/application/DoctorDiagnostics.ts:node,Runtime version is outside supported ranges,"Install Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+.",Low risk; exact dependency recovery,Keep current copy,unchanged,verified by doctor unit test
49,src/application/Doctor.ts:host,Host OS/distribution is unsupported,"REA supports macOS 12+, Ubuntu 24.04+, Fedora 41+, and 64-bit Arch Linux.",Low risk; clearly states supported choices,Keep current copy,unchanged,verified by doctor unit tests
50,src/application/Doctor.ts:analysis engine,Optional deep-analysis executable cannot be found,"Run rea setup to install Hopper, or set HOPPER_LAUNCHER_PATH.",Low risk in setup-specific diagnostic; exact recovery paths,Keep current copy,unchanged,verified by doctor unit tests
51,src/application/Doctor.ts:target,Optional target path fails shared validation,Supply a readable local app or program path.,Low risk; tells caller valid target class,Keep current copy,unchanged,verified by doctor unit test
@ -65,7 +65,7 @@ harm_rank,location,trigger,current_copy,user_risk,proposed_replacement,implement
55,src/application/Setup.ts:optional engine,Setup completes without optional deep-analysis engine,Hopper is optional for non-Hopper providers. Rerun with --yes --install-hopper for deep native analysis.,Exposes provider terminology but this is an explanatory setup status rather than an error; recovery command is exact,Keep current copy,unchanged,verified by setup unit test
56,src/application/Setup.ts:doctor fallback,Post-setup diagnostics remain unhealthy,Run rea doctor and apply each reported remediation.,Low risk; delegates to structured per-check recovery,Keep current copy,unchanged,verified by exact setup unit test
57,src/application/Setup.ts:unsupported platform,Platform is neither macOS nor Linux,REA supports Hopper on macOS and selected 64-bit Linux distributions.,Low risk; states support boundary,Keep current copy,unchanged,verified by setup unit test
58,src/application/Setup.ts:unsupported Node,Runtime version cannot run setup,Install Node.js 22.19+ or 24.11+ and rerun setup.,Low risk; exact dependency and retry,Keep current copy,unchanged,verified by setup host tests
58,src/application/SetupHost.ts:unsupported Node,Runtime version cannot run setup,"Install Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+ and rerun setup.",Low risk; exact dependency and retry,Keep current copy,unchanged,verified by setup host tests
59,src/application/Setup.ts:unsupported macOS,macOS version is missing or older than 12,Upgrade to macOS 12 or newer.,Low risk; exact support floor,Keep current copy,unchanged,verified by setup host tests
60,src/application/Setup.ts:unsupported Linux,Linux distribution is outside supported set,"REA supports Hopper on Ubuntu 24.04+, Fedora 41+, and 64-bit Arch Linux.",Low risk; exact supported alternatives,Keep current copy,unchanged,verified by setup unit test
61,src/application/ProcessCli.ts:readJson read,Process scenario or evidence file cannot be read,Raw filesystem exception through CLI framework,Can expose local paths/codes and no recovery,"Process input file could not be read. Check that the path exists and is readable.",implemented,verified by focused CLI test
@ -83,11 +83,11 @@ harm_rank,location,trigger,current_copy,user_risk,proposed_replacement,implement
74,install.sh:unknown option,Installer receives an unsupported flag,unknown option with caller-provided flag,Low risk; identifies offending input,Keep current copy,unchanged,verified by shell branch inspection
75,install.sh:platform,Installer runs outside macOS or supported Linux,REA supports macOS and selected 64-bit Linux distributions.,Low risk; states support boundary,Keep current copy,unchanged,verified by shell branch inspection
76,install.sh:curl missing,curl is absent,curl is required.,No recovery instruction,"curl is required. Install curl, then rerun this installer.",implemented,verified by exact missing-curl installer test
77,install.sh:node missing,node is absent,Node.js 22.19+ or 24.11+ is required; install it from nodejs.org and retry.,Low risk; exact versions and recovery,Keep current copy,unchanged,verified by shell branch inspection
77,install.sh:node missing,node is absent,"Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+ is required; install it from nodejs.org and retry.",Low risk; exact versions and recovery,Keep current copy,unchanged,verified by shell branch inspection
78,install.sh:npm missing,npm is absent,npm is required; install it with Node.js and retry.,Low risk; exact recovery,Keep current copy,unchanged,verified by shell branch inspection
79,install.sh:node unreadable,node exists but does not report a parseable version,could not determine the active Node.js version.,No cause-local recovery,"The active Node.js version could not be read. Check that node works and is on PATH, then retry.",implemented,verified by exact unreadable-Node-version installer test
80,install.sh:Node 22 floor,Node 22 minor version is below 19,Node.js VERSION is unsupported; use Node.js 22.19+ or 24.11+.,Low risk; exact detected version and supported alternatives,Keep current copy,unchanged,verified by exact installer test using Node 20 and branch inspection
81,install.sh:Node 24 floor,Node 24 minor version is below 11,Node.js VERSION is unsupported; use Node.js 24.11 or newer.,Low risk; exact detected version and floor,Keep current copy,unchanged,verified by shell branch inspection
80,install.sh:Node 22 floor,Node 22 minor version is below 19,"Node.js VERSION is unsupported; use Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+.",Low risk; exact detected version and supported alternatives,Keep current copy,unchanged,verified by exact installer test using Node 20 and branch inspection
81,install.sh:Node 24 floor,Node 24 minor version is below 11,"Node.js VERSION is unsupported; use Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+.",Low risk; exact detected version and floor,Keep current copy,unchanged,verified by shell branch inspection
82,install.sh:version syntax,Explicit version is not exact semantic version,version must be an exact semantic version.,Low risk; exact correction,Keep current copy,unchanged,verified by shell branch inspection
83,install.sh:release lookup,Latest GitHub release request fails,could not resolve the latest GitHub release.,No fallback despite supporting pinned versions,"The latest REA release could not be resolved. Check network access or pass --version VERSION, then retry.",implemented,verified by exact release-lookup installer test
84,install.sh:release response,Latest release response is not valid expected JSON,Node JSON parser stack followed by the safe release-response message,Leaks a parser stack and buries recovery guidance,The release response was invalid. Retry later or pass --version VERSION.,implemented,verified by exact malformed-release test with parser stderr suppressed

Can't render this file because it has a wrong number of fields in line 35.

View File

@ -15,7 +15,7 @@ mutable targets require separate acceptance.
Once the required native controls are implemented, the intended P0 is limited to:
- a Windows x64 host;
- Node.js 22.19+ or 24.11+;
- Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+;
- an operator-installed official Ghidra 12.1.4 distribution;
- a 64-bit full JDK 21;
- an explicit native, non-managed, non-DLL x86-64 PE application; and

View File

@ -55,17 +55,15 @@ fi
platform="$(uname -s)"
[[ "$platform" == "Darwin" || "$platform" == "Linux" ]] || fail "REA supports macOS and selected 64-bit Linux distributions."
command -v curl >/dev/null 2>&1 || fail "curl is required. Install curl, then rerun this installer."
command -v node >/dev/null 2>&1 || fail "Node.js 22.19+ or 24.11+ is required; install it from https://nodejs.org and retry."
command -v node >/dev/null 2>&1 || fail "Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+ is required; install it from https://nodejs.org and retry."
command -v npm >/dev/null 2>&1 || fail "npm is required; install it with Node.js and retry."
node_version="$(node -p 'process.versions.node' 2>/dev/null || true)"
IFS=. read -r node_major node_minor _node_patch <<<"$node_version"
[[ "$node_major" =~ ^[0-9]+$ && "$node_minor" =~ ^[0-9]+$ ]] || fail "the active Node.js version could not be read. Check that node works and is on PATH, then retry."
if ! ((node_major == 22 && node_minor >= 19 || node_major >= 24)); then
fail "Node.js $node_version is unsupported; use Node.js 22.19+ or 24.11+."
fi
if ((node_major == 24 && node_minor < 11)); then
fail "Node.js $node_version is unsupported; use Node.js 24.11 or newer."
if ! [[ "$_node_patch" =~ ^[0-9]+(\+[0-9A-Za-z.-]+)?$ ]] ||
! ((node_major == 22 && node_minor >= 19 || node_major == 24 && node_minor >= 11 || node_major >= 26)); then
fail "Node.js $node_version is unsupported; use Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+."
fi
if [[ -n "$version" ]]; then

View File

@ -19,13 +19,27 @@ describe("doctor", () => {
result.checks.map(({ name, remediation }) => [name, remediation]),
),
).toEqual({
node: "Install Node.js 22.19+ or 24.11+.",
node: "Install Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+.",
host: "REA supports macOS 12+, Ubuntu 24.04+, Fedora 41+, 64-bit Arch Linux, and the experimental Windows x64 Ghidra P0 boundary.",
hopper: "Run rea setup to install Hopper, or set HOPPER_LAUNCHER_PATH.",
target: "Supply a readable local app or program path.",
});
});
it("reports Node 25 as unsupported", async () => {
const result = await runDoctor(undefined, host({ nodeVersion: "25.1.0" }));
expect(result.healthy).toBe(false);
expect(result.checks).toContainEqual(
expect.objectContaining({
name: "node",
ok: false,
detail: "25.1.0",
classification: "missing_dependency",
}),
);
});
it.each(["11.7", undefined])(
"rejects unsupported macOS version %s",
async (version) => {

View File

@ -92,7 +92,7 @@ export const doctorHealthy = (
const nodeCheck = (host: DoctorHost): DoctorCheck =>
check("node", supportsNodeVersion(host.nodeVersion), host.nodeVersion, {
remediation: "Install Node.js 22.19+ or 24.11+.",
remediation: "Install Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+.",
classification: "missing_dependency",
});

View File

@ -16,7 +16,7 @@ import type { ClientRegistrationStatus } from "./ClientRegistrationStatus.js";
/** Recording setup host for service-level planning and recovery tests. */
export class FakeSetupHost implements SetupHost {
readonly platform: NodeJS.Platform;
nodeVersion = "25.1.0";
nodeVersion = "24.18.0";
version: string | undefined = "14.5";
distribution: LinuxDistribution | undefined;
hopper: string | undefined;

View File

@ -4,6 +4,23 @@ import { FakeSetupHost, options } from "./Setup.fixture.js";
import { runSetup } from "./Setup.js";
describe("setup workflow", () => {
it("rejects Node 25 before applying setup actions", async () => {
const host = new FakeSetupHost();
host.nodeVersion = "25.1.0";
host.clients = [{ name: "codex", configPath: "/codex.toml" }];
const result = await runSetup(
{ ...options(true, true), installSkill: true, clientIds: ["codex"] },
host,
);
expect(result.status).toBe("needs_human");
expect(result.appliedActions).toEqual([]);
expect(host.hopperInstalls).toBe(0);
expect(host.configurations).toBe(0);
expect(host.skillInstalls).toBe(0);
});
it("records every detected client outcome after an earlier failure", async () => {
const host = new FakeSetupHost();
host.hopper = "/Applications/Hopper";
@ -105,7 +122,7 @@ describe("setup scoped readiness", () => {
const nodeHost = new FakeSetupHost();
nodeHost.nodeVersion = "20.0.0";
expect((await runSetup(options(true), nodeHost)).remediation).toBe(
"Install Node.js 22.19+ or 24.11+ and rerun setup.",
"Install Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+ and rerun setup.",
);
const macHost = new FakeSetupHost();

View File

@ -63,7 +63,7 @@ export const hostRemediation = async (
installHopper: boolean,
): Promise<string | undefined> => {
if (!supportsNodeVersion(host.nodeVersion))
return "Install Node.js 22.19+ or 24.11+ and rerun setup.";
return "Install Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+ and rerun setup.";
if (!installHopper) return undefined;
if (host.platform !== "darwin" && host.platform !== "linux")
return "REA supports Hopper on macOS and selected 64-bit Linux distributions.";

View File

@ -0,0 +1,26 @@
import { describe, expect, it } from "vitest";
import { supportsNodeVersion } from "./runtimeVersion.js";
describe("supported Node.js versions", () => {
it.each([
["20.0.0", false],
["22.18.9", false],
["22.19.0", true],
["22.99.0", true],
["23.0.0", false],
["24.10.9", false],
["24.11.0", true],
["24.99.0", true],
["25.1.0", false],
["26.0.0", true],
["27.0.0", true],
["24.11.0-rc.1", false],
["26.0.0-nightly.20261001", false],
["24.11", false],
["24.11.invalid", false],
["invalid", false],
])("classifies %s as supported=%s", (version, expected) => {
expect(supportsNodeVersion(version)).toBe(expected);
});
});

View File

@ -1,10 +1,8 @@
import { satisfies } from "semver";
/** Supported runtime range, kept in sync with the published package engines. */
export const SUPPORTED_NODE_VERSION_RANGE = "^22.19.0 || ^24.11.0 || >=26.0.0";
/** Return whether a Node.js version satisfies REA's tested runtime families. */
export const supportsNodeVersion = (version: string): boolean => {
const [majorText = "", minorText = ""] = version.split(".");
const major = Number.parseInt(majorText, 10);
const minor = Number.parseInt(minorText, 10);
return (
(major === 22 && minor >= 19) ||
(major >= 24 && (major !== 24 || minor >= 11))
);
};
export const supportsNodeVersion = (version: string): boolean =>
satisfies(version, SUPPORTED_NODE_VERSION_RANGE);

View File

@ -30,13 +30,33 @@ describe("curl installer scenarios", { timeout: 20_000 }, () => {
expect(await readdir(fixture.temporary)).toEqual([]);
});
it("accepts Node 25 without installing or replacing it", async () => {
const fixture = await createFixture();
const result = await runInstaller(fixture, ["--version", "0.3.0"], {
FAKE_NODE_VERSION: "25.1.0",
});
expect(result.stdout).toContain("Runtime: Node.js 25.1.0");
});
it.each(["22.19.0", "24.11.0", "26.0.0", "27.0.0"])(
"accepts supported Node %s without installing or replacing it",
async (version) => {
const fixture = await createFixture();
const result = await runInstaller(fixture, ["--version", "0.3.0"], {
FAKE_NODE_VERSION: version,
});
expect(result.stdout).toContain(`Runtime: Node.js ${version}`);
},
);
it.each(["22.18.9", "23.0.0", "24.10.9", "25.1.0", "26.0.0-rc.1"])(
"rejects unsupported Node %s before invoking npm",
async (version) => {
const fixture = await createFixture();
await expect(
runInstaller(fixture, ["--version", "0.3.0"], {
FAKE_NODE_VERSION: version,
}),
).rejects.toMatchObject({
stderr: expect.stringContaining(`Node.js ${version} is unsupported`),
});
await expect(readFile(fixture.npmLog, "utf8")).rejects.toMatchObject({
code: "ENOENT",
});
},
);
it("resolves and validates the latest REA release tag", async () => {
const fixture = await createFixture();
@ -71,7 +91,7 @@ describe("curl installer scenarios", { timeout: 20_000 }, () => {
"unsupported Node",
["--version", "0.3.0"],
{ FAKE_NODE_VERSION: "20.0.0" },
"REA installation failed: Node.js 20.0.0 is unsupported; use Node.js 22.19+ or 24.11+.\n",
"REA installation failed: Node.js 20.0.0 is unsupported; use Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+.\n",
],
[
"unreadable Node version",

View File

@ -2,6 +2,8 @@ import { readFile } from "node:fs/promises";
import { describe, expect, it } from "vitest";
import { SUPPORTED_NODE_VERSION_RANGE } from "../../src/domain/runtimeVersion.js";
const readJson = async (path: string): Promise<unknown> =>
JSON.parse(await readFile(path, "utf8")) as unknown;
@ -14,6 +16,12 @@ const readmes = [
] as const;
describe("release configuration", () => {
it("keeps doctor and setup runtime support aligned with package engines", async () => {
expect(await readJson("package.json")).toMatchObject({
engines: { node: SUPPORTED_NODE_VERSION_RANGE },
});
});
it("updates every package-version artifact in release PRs", async () => {
const configuration = await readJson("release-please-config.json");