| 1 | import { lstatSync, readFileSync } from "node:fs"; |
| 2 | import { basename, dirname, join } from "node:path"; |
| 3 | |
| 4 | // Resolve only the stable entry in this shell's own versioned installation. |
| 5 | // Never relaunch a path supplied by a status client or keep an old service path. |
| 6 | export function supersededLauncher(shellPath: string, version: string): string | undefined { |
| 7 | const versions = dirname(dirname(dirname(shellPath))); |
| 8 | if (basename(versions).toLowerCase() !== "versions") return undefined; |
| 9 | const root = dirname(versions); |
| 10 | try { |
| 11 | const current = JSON.parse(readFileSync(join(root, "current.json"), "utf8")); |
| 12 | if (current.schemaVersion !== 1 || typeof current.activeVersion !== "string" || current.activeVersion === version) return undefined; |
| 13 | if (!/^v[0-9A-Za-z][0-9A-Za-z._+-]*$/.test(current.activeVersion) || current.activeVersion.includes("..") || current.activeDir !== `versions/${current.activeVersion}`) return undefined; |
| 14 | for (const name of ["Reasonix.exe", "reasonix-launcher.exe"]) { |
| 15 | const launcher = join(root, name); |
| 16 | try { if (lstatSync(launcher).isFile()) return launcher; } catch { /* Try the compatible entry. */ } |
| 17 | } |
| 18 | return undefined; |
| 19 | } catch { return undefined; } |
| 20 | } |
| 21 |