import process from "node:process"; import { defaultRuntime, type RuntimeEnv } from "../runtime.js"; export type RuntimeKind = "node" | "bun" | "unknown"; type Semver = { major: number; minor: number; patch: number; }; const MIN_NODE: Semver = { major: 22, minor: 0, patch: 0 }; const MIN_BUN: Semver = { major: 1, minor: 3, 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 isBun = Boolean(process.versions?.bun); const kind: RuntimeKind = isBun ? "bun" : process.versions?.node ? "node" : "unknown"; const bunVersion = (globalThis as { Bun?: { version?: string } })?.Bun?.version ?? null; const version = isBun ? (process.versions?.bun ?? bunVersion) : (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 === "bun") return isAtLeast(parsed, MIN_BUN); if (details.kind === "node") return isAtLeast(parsed, MIN_NODE); return false; } 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( [ "clawdis requires Node >=22.0.0 or Bun >=1.3.0.", `Detected: ${runtimeLabel} (exec: ${execLabel}).`, `PATH searched: ${details.pathEnv}`, "Install Node: https://nodejs.org/en/download", "Install Bun: https://bun.sh/docs/installation", details.kind === "bun" ? "Upgrade Bun or re-run with Node by setting CLAWDIS_RUNTIME=node." : "Upgrade Node or re-run with Bun by setting CLAWDIS_RUNTIME=bun.", ].join("\n"), ); runtime.exit(1); }