mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-20 13:52:28 +03:00
* fix(cli): find npm-global Claude .cmd shims under Program Files\nodejs (#12563) Stock Node MSI installs drop claude.cmd there, but detection never listed that directory and Electron's PATH often omits it, so the dashboard reported settings_found_binary_unresolved while a normal shell could run the CLI. * fix(cli): soft-fail npm prefix cache and enrich Windows lookup PATH (#12563) Stop permanently caching a failed npm config get prefix as empty, and prepend npm-prefix / APPDATA\npm / nvm / Program Files dirs on Windows lookup PATH so custom installs survive Electron PATH gaps.
76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
import path from "path";
|
|
import { execFileSync } from "child_process";
|
|
|
|
/**
|
|
* npm global-prefix detection for cliRuntime.ts (#12563).
|
|
*
|
|
* Extracted so the soft-fail / retry logic does not grow the frozen
|
|
* `cliRuntime.ts` file-size budget.
|
|
*/
|
|
|
|
const DANGEROUS_PATH_CHARS = ["&", "|", ";", "<", ">", "(", ")", "`", "$", "^", "%", "!"];
|
|
const NPM_PREFIX_RETRY_MS = 30_000;
|
|
|
|
let _npmGlobalPrefix: string | undefined;
|
|
let _npmGlobalPrefixFailAt = 0;
|
|
|
|
const isWindows = (): boolean => process.platform === "win32";
|
|
|
|
/** Test-only: clear prefix success/failure state between cases. */
|
|
export const __resetNpmGlobalPrefixCacheForTests = (): void => {
|
|
_npmGlobalPrefix = undefined;
|
|
_npmGlobalPrefixFailAt = 0;
|
|
};
|
|
|
|
/**
|
|
* Detect the npm global bin directory.
|
|
*
|
|
* Success is cached for the process lifetime (`execFileSync` is expensive).
|
|
* Failure is NOT cached as permanent `""` (#12563): a cold-shell timeout or
|
|
* missing `npm` on Electron's PATH must not delete every npm-derived candidate
|
|
* for the rest of the run. Env `npm_config_prefix` is re-read every call so it
|
|
* can recover without waiting for the spawn retry window.
|
|
*/
|
|
export const getNpmGlobalPrefix = (): string => {
|
|
const envPrefix = String(process.env.npm_config_prefix || "").trim();
|
|
if (
|
|
envPrefix &&
|
|
path.isAbsolute(envPrefix) &&
|
|
!DANGEROUS_PATH_CHARS.some((c) => envPrefix.includes(c))
|
|
) {
|
|
_npmGlobalPrefix = envPrefix;
|
|
_npmGlobalPrefixFailAt = 0;
|
|
return _npmGlobalPrefix;
|
|
}
|
|
|
|
if (_npmGlobalPrefix !== undefined) return _npmGlobalPrefix;
|
|
|
|
if (_npmGlobalPrefixFailAt && Date.now() - _npmGlobalPrefixFailAt < NPM_PREFIX_RETRY_MS) {
|
|
return "";
|
|
}
|
|
|
|
try {
|
|
const result = execFileSync("npm", ["config", "get", "prefix"], {
|
|
windowsHide: true,
|
|
timeout: 5000,
|
|
encoding: "utf8",
|
|
stdio: ["ignore", "pipe", "ignore"],
|
|
...(isWindows() ? { shell: true } : {}),
|
|
});
|
|
const prefix = result.trim();
|
|
if (
|
|
prefix &&
|
|
path.isAbsolute(prefix) &&
|
|
!DANGEROUS_PATH_CHARS.some((c) => prefix.includes(c))
|
|
) {
|
|
_npmGlobalPrefix = prefix;
|
|
_npmGlobalPrefixFailAt = 0;
|
|
return _npmGlobalPrefix;
|
|
}
|
|
} catch {}
|
|
|
|
// Soft-fail only — do not poison `_npmGlobalPrefix` with permanent "".
|
|
_npmGlobalPrefixFailAt = Date.now();
|
|
return "";
|
|
};
|