mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-31 10:47:15 +00:00
* fix(daemon): stop reporting failed runtime probes as unsupported runtimes The daemon runtime probe wrapped its exec in a bare catch that returned `supported: false`, so any failure to *run* the probe was laundered into a verdict that the runtime itself was unsupported. Operators on a perfectly good Node install were told to install a Node version they already had. Observed on Ubuntu 26.04 with Node 26.8.1: `openclaw node install` failed with "No supported Node runtime was selected for the daemon" whenever the process cwd was not readable by the service user (e.g. `runuser -u openclaw` inheriting root's 0700 home over SSH), because every child spawn then fails EACCES. The version logic was never wrong -- resolveSystemNodeInfo returned supported:true and resolvePreferredNodePath returned /usr/bin/node when probed directly on the affected host. Node and Bun probes now share one resolver returning a closed supported | unsupported | probe-failed union. A failed probe retains its cause, executable, and cwd, and selection propagates that instead of falling through to Node-upgrade advice. Also: - Derive the supported-version wording from NODE_RELEASE_FLOORS via a new exported SUPPORTED_NODE_VERSIONS, replacing six hand-copied spellings that omitted the >=25.9.0 line and told operators to downgrade. - Forward OPENCLAW_WRAPPER through the node-host install path; the documented escape hatch was previously gateway-daemon-only. Production LOC net +6 (+156/-150); consolidating the duplicated Node/Bun probes paid for the new failure handling. * docs(cli): drop machine-local path from node probe-failure guidance ClawSweeper P3: docs/AGENTS.md requires generic docs content with no local paths. The probe-failure recovery example prescribed a specific directory; state the readability requirement instead.
84 lines
3.2 KiB
JavaScript
84 lines
3.2 KiB
JavaScript
// Zero-dependency Node release-version contract shared by source and packaged entry points.
|
|
const NODE_RELEASE_VERSION_RE =
|
|
/^v?((?:0|[1-9]\d*))\.((?:0|[1-9]\d*))\.((?:0|[1-9]\d*))(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/u;
|
|
|
|
const NODE_RELEASE_FLOORS = [
|
|
{ major: 22, minor: 22, patch: 3 },
|
|
{ major: 24, minor: 15, patch: 0 },
|
|
{ major: 25, minor: 9, patch: 0 },
|
|
];
|
|
const HIGHEST_RELEASE_FLOOR = NODE_RELEASE_FLOORS[NODE_RELEASE_FLOORS.length - 1];
|
|
|
|
// Render diagnostics from the same release floors used by the runtime guard.
|
|
export const SUPPORTED_NODE_VERSIONS = `${NODE_RELEASE_FLOORS.map(
|
|
({ major, minor, patch }, index) =>
|
|
`>=${major}.${minor}.${patch}${index < NODE_RELEASE_FLOORS.length - 1 ? ` <${major + 1}` : ""}`,
|
|
)
|
|
.join(", ")
|
|
.replace(/, ([^,]+)$/, ", or $1")} (Node 26 recommended)`;
|
|
|
|
/** Parses an anchored release SemVer, allowing a leading v and valid build metadata. */
|
|
export function parseNodeReleaseVersion(value) {
|
|
if (typeof value !== "string") {
|
|
return null;
|
|
}
|
|
const match = NODE_RELEASE_VERSION_RE.exec(value.trim());
|
|
if (!match) {
|
|
return null;
|
|
}
|
|
const version = {
|
|
major: Number(match[1]),
|
|
minor: Number(match[2]),
|
|
patch: Number(match[3]),
|
|
};
|
|
return Object.values(version).every(Number.isSafeInteger) ? version : null;
|
|
}
|
|
|
|
export function isNodeVersionAtLeast(version, minimum) {
|
|
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;
|
|
}
|
|
|
|
/** Checks OpenClaw's supported release lines. Node 23 remains unsupported. */
|
|
export function isSupportedOpenClawNodeVersion(value) {
|
|
const version = parseNodeReleaseVersion(value);
|
|
if (!version) {
|
|
return false;
|
|
}
|
|
const minimum = NODE_RELEASE_FLOORS.find((floor) => floor.major === version.major);
|
|
return minimum
|
|
? isNodeVersionAtLeast(version, minimum)
|
|
: version.major > HIGHEST_RELEASE_FLOOR.major;
|
|
}
|
|
|
|
function renderProcessNodeVersionCheck() {
|
|
const patternSource = JSON.stringify(NODE_RELEASE_VERSION_RE.source);
|
|
const releaseFloors = JSON.stringify(NODE_RELEASE_FLOORS);
|
|
return `((value) => {
|
|
if (typeof value !== "string") return false;
|
|
const match = new RegExp(${patternSource}, "u").exec(value.trim());
|
|
if (!match) return false;
|
|
const version = { major: Number(match[1]), minor: Number(match[2]), patch: Number(match[3]) };
|
|
if (!Object.values(version).every(Number.isSafeInteger)) return false;
|
|
const floors = ${releaseFloors};
|
|
const minimum = floors.find((floor) => floor.major === version.major);
|
|
const atLeast = (floor) =>
|
|
version.major > floor.major ||
|
|
(version.major === floor.major &&
|
|
(version.minor > floor.minor ||
|
|
(version.minor === floor.minor && version.patch >= floor.patch)));
|
|
return minimum ? atLeast(minimum) : version.major > floors[floors.length - 1].major;
|
|
})(process.versions.node)`;
|
|
}
|
|
|
|
// Worker bootstrap runs before OpenClaw is transferred. Carry the canonical
|
|
// release policy as a self-contained expression instead of a second parser.
|
|
export const PROCESS_NODE_VERSION_CHECK = renderProcessNodeVersionCheck();
|