86 lines
2.1 KiB
TypeScript
86 lines
2.1 KiB
TypeScript
import * as fs from "node:fs";
|
|
import * as os from "node:os";
|
|
import * as path from "node:path";
|
|
|
|
type BinaryLookup = {
|
|
env: Record<string, string | undefined>;
|
|
platform: NodeJS.Platform;
|
|
homeDir: string;
|
|
isExecutable: (path: string) => boolean;
|
|
};
|
|
|
|
type SearchSpec = {
|
|
paths: path.PlatformPath;
|
|
names: string[];
|
|
knownDirs: string[];
|
|
};
|
|
|
|
function getWindowsSearchSpec(env: BinaryLookup["env"], homeDir: string): SearchSpec {
|
|
const paths = path.win32;
|
|
const appData = env.APPDATA ?? paths.join(homeDir, "AppData", "Roaming");
|
|
|
|
return {
|
|
paths,
|
|
names: ["claude.exe", "claude.cmd"],
|
|
knownDirs: [
|
|
paths.join(homeDir, ".local", "bin"),
|
|
paths.join(homeDir, ".claude", "local"),
|
|
env.npm_config_prefix ?? paths.join(appData, "npm"),
|
|
],
|
|
};
|
|
}
|
|
|
|
function getPosixSearchSpec(env: BinaryLookup["env"], homeDir: string): SearchSpec {
|
|
const paths = path.posix;
|
|
const npmBin = env.npm_config_prefix
|
|
? paths.join(env.npm_config_prefix, "bin")
|
|
: paths.join(homeDir, ".npm-global", "bin");
|
|
|
|
return {
|
|
paths,
|
|
names: ["claude"],
|
|
knownDirs: [
|
|
paths.join(homeDir, ".local", "bin"),
|
|
paths.join(homeDir, ".claude", "local"),
|
|
npmBin,
|
|
"/opt/homebrew/bin",
|
|
"/usr/local/bin",
|
|
],
|
|
};
|
|
}
|
|
|
|
export function resolveClaudeBinary({ env, platform, homeDir, isExecutable }: BinaryLookup): string | null {
|
|
const { paths, names, knownDirs } = platform === "win32"
|
|
? getWindowsSearchSpec(env, homeDir)
|
|
: getPosixSearchSpec(env, homeDir);
|
|
|
|
const pathDirs = (env.PATH ?? "").split(paths.delimiter).filter(Boolean);
|
|
|
|
for (const name of names) {
|
|
for (const dir of [...pathDirs, ...knownDirs]) {
|
|
const candidate = paths.join(dir, name);
|
|
if (isExecutable(candidate)) return candidate;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function isExecutableFile(filePath: string): boolean {
|
|
try {
|
|
fs.accessSync(filePath, fs.constants.X_OK);
|
|
return fs.statSync(filePath).isFile();
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function getClaudeBinary(): string | null {
|
|
return resolveClaudeBinary({
|
|
env: process.env,
|
|
platform: process.platform,
|
|
homeDir: os.homedir(),
|
|
isExecutable: isExecutableFile,
|
|
});
|
|
}
|