84 lines
2.5 KiB
TypeScript
84 lines
2.5 KiB
TypeScript
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`,
|
|
);
|
|
}
|
|
}
|