| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980 |
- "use strict";
- /**
- * Binary discovery + child environment for GUI-context launches.
- *
- * From Finder/launchd an app inherits PATH=/usr/bin:/bin:/usr/sbin:/sbin — the
- * `#!/usr/bin/env node` shebangs of dsh and npm cannot resolve there. All
- * spawned children therefore run through childEnv(), which prepends the
- * directories that actually hold dsh, npm, and node: both their symlink spots
- * (~/.local/bin) and their realpath homes (e.g. a version manager under
- * ~/.hermes/node/bin).
- */
- const fs = require("node:fs");
- const os = require("node:os");
- const path = require("node:path");
- function firstExisting(candidates) {
- for (const candidate of candidates) {
- try { if (candidate && fs.existsSync(candidate)) return candidate; } catch { /* next */ }
- }
- return null;
- }
- function pathDirs() {
- return (process.env.PATH || "").split(":").filter(Boolean);
- }
- /** The dsh binary: explicit override, common user install spots, then PATH. */
- function findDsh() {
- const override = process.env.DSH_DESKTOP_DSH_BIN;
- if (override) return override;
- const home = os.homedir();
- return firstExisting([
- path.join(home, ".local/bin/dsh"),
- "/opt/homebrew/bin/dsh",
- "/usr/local/bin/dsh",
- ...pathDirs().map((dir) => path.join(dir, "dsh")),
- ]) || "dsh";
- }
- /** The npm binary: the same discovery strategy. */
- function findNpm() {
- const home = os.homedir();
- return firstExisting([
- path.join(home, ".local/bin/npm"),
- "/opt/homebrew/bin/npm",
- "/usr/local/bin/npm",
- ...pathDirs().map((dir) => path.join(dir, "npm")),
- ]) || "npm";
- }
- const DSH_BIN = findDsh();
- const NPM_BIN = findNpm();
- /**
- * Re-resolve the dsh binary right before every spawn. `npm install -g`
- * replaces the bin symlink during upgrades; a path captured at shell startup
- * (or a bare "dsh" fallback resolved while the link was missing) would then
- * hit ENOENT — exactly the upgrade-race failure seen in the wild.
- */
- function resolveDsh() {
- return findDsh();
- }
- /** Environment (PATH augmented) for every child process we spawn. */
- function childEnv() {
- const dirs = [];
- for (const bin of [DSH_BIN, NPM_BIN]) {
- if (path.isAbsolute(bin)) dirs.push(path.dirname(bin));
- try { dirs.push(path.dirname(fs.realpathSync(bin))); } catch { /* not a file */ }
- }
- dirs.push(
- path.join(os.homedir(), ".local/bin"),
- "/opt/homebrew/bin",
- "/usr/local/bin",
- );
- const unique = [...new Set(dirs.filter(Boolean))];
- return { ...process.env, PATH: `${unique.join(":")}:${process.env.PATH || ""}` };
- }
- module.exports = { DSH_BIN, NPM_BIN, findDsh, findNpm, resolveDsh, childEnv };
|