Files
openclaw/src/infra/runtime-guard.ts
Glucksberg 2ca78a8aed fix(runtime): bump minimum Node.js version to 22.12.0 (#5370)
* fix(runtime): bump minimum Node.js version to 22.12.0

Aligns the runtime guard with the declared package.json engines requirement.

The Matrix plugin (and potentially others) requires Node >= 22.12.0,
but the runtime guard previously allowed 22.0.0+. This caused confusing
errors like 'Cannot find module @vector-im/matrix-bot-sdk' when the real
issue was an unsupported Node version.

- Update MIN_NODE from 22.0.0 to 22.12.0
- Update error message to reflect the correct version
- Update tests to use 22.12.0 as the minimum valid version

Fixes #5292

* fix: update test versions to match MIN_NODE=22.12.0

---------

Co-authored-by: Markus Glucksberg <markus@glucksberg.com>
2026-02-05 13:42:52 -08:00

100 lines
2.4 KiB
TypeScript

import process from "node:process";
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
export type RuntimeKind = "node" | "unknown";
type Semver = {
major: number;
minor: number;
patch: number;
};
const MIN_NODE: Semver = { major: 22, minor: 12, patch: 0 };
export type RuntimeDetails = {
kind: RuntimeKind;
version: string | null;
execPath: string | null;
pathEnv: string;
};
const SEMVER_RE = /(\d+)\.(\d+)\.(\d+)/;
export function parseSemver(version: string | null): Semver | null {
if (!version) {
return null;
}
const match = version.match(SEMVER_RE);
if (!match) {
return null;
}
const [, major, minor, patch] = match;
return {
major: Number.parseInt(major, 10),
minor: Number.parseInt(minor, 10),
patch: Number.parseInt(patch, 10),
};
}
export function isAtLeast(version: Semver | null, minimum: Semver): boolean {
if (!version) {
return false;
}
if (version.major !== minimum.major) {
return version.major > minimum.major;
}
if (version.minor !== minimum.minor) {
return version.minor > minimum.minor;
}
return version.patch >= minimum.patch;
}
export function detectRuntime(): RuntimeDetails {
const kind: RuntimeKind = process.versions?.node ? "node" : "unknown";
const version = process.versions?.node ?? null;
return {
kind,
version,
execPath: process.execPath ?? null,
pathEnv: process.env.PATH ?? "(not set)",
};
}
export function runtimeSatisfies(details: RuntimeDetails): boolean {
const parsed = parseSemver(details.version);
if (details.kind === "node") {
return isAtLeast(parsed, MIN_NODE);
}
return false;
}
export function isSupportedNodeVersion(version: string | null): boolean {
return isAtLeast(parseSemver(version), MIN_NODE);
}
export function assertSupportedRuntime(
runtime: RuntimeEnv = defaultRuntime,
details: RuntimeDetails = detectRuntime(),
): void {
if (runtimeSatisfies(details)) {
return;
}
const versionLabel = details.version ?? "unknown";
const runtimeLabel =
details.kind === "unknown" ? "unknown runtime" : `${details.kind} ${versionLabel}`;
const execLabel = details.execPath ?? "unknown";
runtime.error(
[
"openclaw requires Node >=22.12.0.",
`Detected: ${runtimeLabel} (exec: ${execLabel}).`,
`PATH searched: ${details.pathEnv}`,
"Install Node: https://nodejs.org/en/download",
"Upgrade Node and re-run openclaw.",
].join("\n"),
);
runtime.exit(1);
}