// windows-user-env.cjs // // Read a User-scoped environment variable straight from the Windows registry // (HKCU\Environment). // // A GUI app launched from Explorer inherits the environment block captured at // login, so a variable set via `setx` AFTER login is invisible in process.env // even though a fresh shell — and the Hermes CLI — sees it immediately. The // desktop's HERMES_HOME resolution relies on process.env, so that stale-snapshot // gap silently sends the backend to the default %LOCALAPPDATA%\hermes. Reading // the live registry value closes the gap. See #45471. const { execFileSync } = require('node:child_process') // Parse the output of `reg query HKCU\Environment /v `, which looks like: // // HKEY_CURRENT_USER\Environment // HERMES_HOME REG_SZ F:\Hermes\data // // Returns the raw value string (spaces inside the value preserved), or null when // the requested value line isn't present. function parseRegQueryValue(stdout, name) { if (!stdout || !name) return null const typePattern = /^(\S+)\s+(?:REG_SZ|REG_EXPAND_SZ|REG_MULTI_SZ|REG_DWORD|REG_QWORD|REG_BINARY|REG_NONE)\s+(.*)$/ for (const rawLine of String(stdout).split(/\r?\n/)) { const line = rawLine.trim() const match = line.match(typePattern) if (match && match[1].toLowerCase() === name.toLowerCase()) { return match[2] } } return null } // Expand %VAR% references against an env map. REG_EXPAND_SZ values store // unexpanded references; plain REG_SZ paths have none, so this is a no-op for // the common F:\... case. Unknown references are left verbatim. function expandWindowsEnvRefs(value, env = process.env) { if (!value) return value return value.replace(/%([^%]+)%/g, (whole, name) => { const key = Object.keys(env).find(k => k.toUpperCase() === String(name).toUpperCase()) return key != null && env[key] != null ? env[key] : whole }) } // Read a User-scoped env var from HKCU\Environment. Windows-only: returns null // off-Windows (without spawning), on any spawn error, when `reg` exits non-zero // (the value doesn't exist), or when the value is empty. function readWindowsUserEnvVar( name, { platform = process.platform, env = process.env, exec = execFileSync } = {} ) { if (platform !== 'win32' || !name) return null let stdout try { stdout = exec('reg', ['query', 'HKCU\\Environment', '/v', name], { encoding: 'utf8', windowsHide: true, timeout: 5000 }) } catch { // `reg` missing, or value absent (reg exits 1) — caller falls back. return null } const raw = parseRegQueryValue(stdout, name) if (raw == null) return null const expanded = expandWindowsEnvRefs(raw, env).trim() return expanded || null } module.exports = { expandWindowsEnvRefs, parseRegQueryValue, readWindowsUserEnvVar }