import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { homedir } from "node:os"; import { dirname, join } from "node:path"; import { spawnSync } from "node:child_process"; const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; interface UpdateCache { checkedAt: number; latest: string; } export function currentCliVersion(): string { try { return JSON.parse(readFileSync(join(import.meta.dir, "..", "package.json"), "utf8")).version; } catch { return "0.0.0"; } } export function compareVersions(a: string, b: string): number { const left = a.split("-")[0]!.split(".").map(Number); const right = b.split("-")[0]!.split(".").map(Number); for (let i = 0; i < 3; i++) { const difference = (left[i] || 0) - (right[i] || 0); if (difference) return difference > 0 ? 1 : -1; } return 0; } function cachePath(): string { return process.env.WRNEXUS_UPDATE_CACHE ?? join(homedir(), ".wrnexus", "update-check.json"); } function cachedLatest(now: number): string | null { try { const cached = JSON.parse(readFileSync(cachePath(), "utf8")) as UpdateCache; return now - cached.checkedAt < CHECK_INTERVAL_MS ? cached.latest : null; } catch { return null; } } function registryLatest(): string | null { const npm = process.platform === "win32" ? "npm.cmd" : "npm"; const result = spawnSync(npm, ["view", "@wrnexus/cli", "version"], { encoding: "utf8", timeout: 5000, windowsHide: true, }); const version = (result.stdout ?? "").trim(); return result.status === 0 && /^\d+\.\d+\.\d+/.test(version) ? version : null; } /** Check at most once per day and print a non-blocking upgrade suggestion. */ export function notifyIfUpdateAvailable(current: string, command?: string): void { if ( process.env.WRNEXUS_NO_UPDATE_CHECK === "1" || command === "update" || command === "upgrade" ) { return; } const now = Date.now(); let latest = cachedLatest(now); if (!latest) { latest = registryLatest(); if (!latest) return; try { const file = cachePath(); if (!existsSync(dirname(file))) mkdirSync(dirname(file), { recursive: true }); writeFileSync(file, JSON.stringify({ checkedAt: now, latest }) + "\n", "utf8"); } catch { // A read-only home directory must never break the CLI. } } if (compareVersions(latest, current) > 0) { console.log( `\n Update available: WRNexusJS ${current} → ${latest}\n` + ` Run: wrnexus update --latest\n`, ); } }