mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-13 09:01:54 +00:00
macOS Desktop backend processes can still miss Apple Silicon Homebrew paths even after adding Hermes-managed Node and venv bins. That leaves `/codex-runtime on` unable to find a Homebrew-installed `codex` binary at `/opt/homebrew/bin/codex`. Add a small testable backend env helper that builds the dashboard subprocess environment in one place. It prepends Hermes-managed Node and venv bins, appends missing POSIX sane PATH entries individually, preserves caller precedence without duplicates, and keeps Windows PATH casing/delimiters intact. Wire both source-checkout and active-install backend descriptors through the helper, and add Node regression coverage to the desktop platform test suite.
101 lines
2.8 KiB
JavaScript
101 lines
2.8 KiB
JavaScript
const path = require('node:path')
|
|
|
|
// Match the POSIX fallback surface used by the Python terminal environment.
|
|
// macOS apps launched from Finder/Dock often inherit only /usr/bin:/bin:/usr/sbin:/sbin,
|
|
// which misses Apple Silicon Homebrew and user-installed CLI tools such as codex.
|
|
const POSIX_SANE_PATH_ENTRIES = Object.freeze([
|
|
'/opt/homebrew/bin',
|
|
'/opt/homebrew/sbin',
|
|
'/usr/local/sbin',
|
|
'/usr/local/bin',
|
|
'/usr/sbin',
|
|
'/usr/bin',
|
|
'/sbin',
|
|
'/bin'
|
|
])
|
|
|
|
function delimiterForPlatform(platform = process.platform) {
|
|
return platform === 'win32' ? ';' : ':'
|
|
}
|
|
|
|
function pathModuleForPlatform(platform = process.platform) {
|
|
return platform === 'win32' ? path.win32 : path.posix
|
|
}
|
|
|
|
function pathEnvKey(env = process.env, platform = process.platform) {
|
|
if (platform !== 'win32') return 'PATH'
|
|
return Object.keys(env || {}).find(key => key.toUpperCase() === 'PATH') || 'PATH'
|
|
}
|
|
|
|
function currentPathValue(env = process.env, platform = process.platform) {
|
|
const key = pathEnvKey(env, platform)
|
|
return env?.[key] || ''
|
|
}
|
|
|
|
function appendUniquePathEntries(entries, { delimiter = path.delimiter } = {}) {
|
|
const seen = new Set()
|
|
const ordered = []
|
|
|
|
for (const entry of entries) {
|
|
if (!entry) continue
|
|
const parts = Array.isArray(entry) ? entry : String(entry).split(delimiter)
|
|
for (const part of parts) {
|
|
if (!part || seen.has(part)) continue
|
|
seen.add(part)
|
|
ordered.push(part)
|
|
}
|
|
}
|
|
|
|
return ordered.join(delimiter)
|
|
}
|
|
|
|
function buildDesktopBackendPath({
|
|
hermesHome,
|
|
venvRoot,
|
|
currentPath = '',
|
|
platform = process.platform,
|
|
pathModule = pathModuleForPlatform(platform)
|
|
} = {}) {
|
|
const delimiter = delimiterForPlatform(platform)
|
|
const hermesNodeBin = hermesHome ? pathModule.join(hermesHome, 'node', 'bin') : null
|
|
const venvBin = venvRoot ? pathModule.join(venvRoot, platform === 'win32' ? 'Scripts' : 'bin') : null
|
|
const saneEntries = platform === 'win32' ? [] : POSIX_SANE_PATH_ENTRIES
|
|
|
|
return appendUniquePathEntries(
|
|
[hermesNodeBin, venvBin, currentPath, saneEntries],
|
|
{ delimiter }
|
|
)
|
|
}
|
|
|
|
function buildDesktopBackendEnv({
|
|
hermesHome,
|
|
pythonPathEntries = [],
|
|
venvRoot,
|
|
currentEnv = process.env,
|
|
platform = process.platform,
|
|
pathModule = pathModuleForPlatform(platform)
|
|
} = {}) {
|
|
const delimiter = delimiterForPlatform(platform)
|
|
const currentPythonPath = currentEnv?.PYTHONPATH || ''
|
|
const key = pathEnvKey(currentEnv, platform)
|
|
|
|
return {
|
|
PYTHONPATH: appendUniquePathEntries([...pythonPathEntries, currentPythonPath], { delimiter }),
|
|
[key]: buildDesktopBackendPath({
|
|
hermesHome,
|
|
venvRoot,
|
|
currentPath: currentPathValue(currentEnv, platform),
|
|
platform,
|
|
pathModule
|
|
})
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
POSIX_SANE_PATH_ENTRIES,
|
|
appendUniquePathEntries,
|
|
buildDesktopBackendEnv,
|
|
buildDesktopBackendPath,
|
|
delimiterForPlatform,
|
|
pathEnvKey
|
|
}
|