| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- "use strict";
- /**
- * Binary discovery + child environment for GUI-context launches.
- *
- * macOS (Finder/launchd) and Windows (Explorer/scheduled-task) both start GUI
- * apps with a minimal PATH that cannot resolve the `#!/usr/bin/env node`
- * shebangs of dsh/npm. All spawned children therefore run through childEnv(),
- * which prepends the directories that actually hold dsh, npm, and node.
- */
- const fs = require("node:fs");
- const os = require("node:os");
- const path = require("node:path");
- const IS_WIN = process.platform === "win32";
- function firstExisting(candidates) {
- for (const candidate of candidates) {
- try { if (candidate && fs.existsSync(candidate)) return candidate; } catch { /* next */ }
- }
- return null;
- }
- function pathDirs() {
- const sep = IS_WIN ? ";" : ":";
- return (process.env.PATH || "").split(sep).filter(Boolean);
- }
- /** The executable extension for node/npm shims (.cmd on Windows, else none). */
- function exe(name) { return IS_WIN ? `${name}.cmd` : name; }
- /**
- * The dsh binary: explicit override, common user install spots, then PATH.
- * On Windows, `npm i -g @deepseek-ai/dsh` places dsh.cmd in the npm prefix
- * (%APPDATA%\npm); on macOS, ~/.local/bin/dsh (a symlink to the package).
- */
- function findDsh() {
- const override = process.env.DSH_DESKTOP_DSH_BIN;
- if (override) return override;
- const home = os.homedir();
- const appdata = process.env.APPDATA || path.join(home, "AppData", "Roaming");
- const candidates = IS_WIN ? [
- path.join(appdata, "npm", "dsh.cmd"),
- path.join(home, ".local", "bin", "dsh.cmd"),
- path.join(home, "AppData", "Roaming", "npm", "dsh.cmd"),
- ...pathDirs().map((dir) => path.join(dir, "dsh.cmd")),
- ] : [
- path.join(home, ".local/bin/dsh"),
- "/opt/homebrew/bin/dsh",
- "/usr/local/bin/dsh",
- ...pathDirs().map((dir) => path.join(dir, "dsh")),
- ];
- return firstExisting(candidates) || "dsh";
- }
- /** The npm binary: the same discovery strategy. */
- function findNpm() {
- const home = os.homedir();
- const appdata = process.env.APPDATA || path.join(home, "AppData", "Roaming");
- const candidates = IS_WIN ? [
- path.join(appdata, "npm", "npm.cmd"),
- path.join(home, ".local", "bin", "npm.cmd"),
- path.join(home, "AppData", "Roaming", "npm", "npm.cmd"),
- ...pathDirs().map((dir) => path.join(dir, "npm.cmd")),
- ] : [
- path.join(home, ".local/bin/npm"),
- "/opt/homebrew/bin/npm",
- "/usr/local/bin/npm",
- ...pathDirs().map((dir) => path.join(dir, "npm")),
- ];
- return firstExisting(candidates) || "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/cmd during upgrades; a path captured at shell
- * startup would then hit ENOENT — exactly the upgrade-race failure in the wild.
- */
- function resolveDsh() {
- return findDsh();
- }
- /** Environment (PATH augmented) for every child process we spawn. */
- function childEnv() {
- const sep = IS_WIN ? ";" : ":";
- 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 */ }
- }
- if (IS_WIN) {
- const appdata = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
- dirs.push(path.join(appdata, "npm"));
- dirs.push(path.join(os.homedir(), ".local", "bin"));
- } else {
- dirs.push(
- path.join(os.homedir(), ".local/bin"),
- "/opt/homebrew/bin",
- "/usr/local/bin",
- );
- }
- const unique = [...new Set(dirs.filter(Boolean))];
- const augmented = unique.join(sep);
- const existing = process.env.PATH || "";
- return { ...process.env, PATH: existing ? `${augmented}${sep}${existing}` : augmented };
- }
- module.exports = { DSH_BIN, NPM_BIN, findDsh, findNpm, resolveDsh, childEnv, IS_WIN };
|