import { execFileSync } from 'child_process';
import { existsSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';
const CODEX_CLI_ENV_VAR = 'CODEX_CLI_PATH';
const CODEX_VERSION_ARGS = ['--version'];
const CODEX_VERSION_TIMEOUT_MS = 5000;
const SYSTEM_CODEX_APP_PATH = '/Applications/Codex.app/Contents/Resources/codex';
function dedupe(values: string[]): string[] {
return Array.from(new Set(values));
}
function isFilesystemPath(command: string): boolean {
return command.includes('/') || command.includes('\\');
}
function canExecute(command: string): boolean {
try {
execFileSync(command, CODEX_VERSION_ARGS, { stdio: 'pipe', timeout: CODEX_VERSION_TIMEOUT_MS });
return true;
} catch {
return false;
}
}
export function resolveCodexCliPath(env: NodeJS.ProcessEnv = process.env): string | null {
const envOverride = env[CODEX_CLI_ENV_VAR]?.trim();
const userCodePath = join(homedir(), 'Applications', 'Codex.app', 'Contents', 'Resources', 'codex');
const candidates = dedupe([
...(envOverride ? [envOverride] : []),
'codex',
SYSTEM_CODEX_APP_PATH,
userCodePath,
]);
for (const candidate of candidates) {
if (isFilesystemPath(candidate) && !existsSync(candidate)) {
continue;
}
if (canExecute(candidate)) {
return candidate;
}
}
return null;
}
export function formatCodexCommandForDisplay(command: string): string {
if (!command.includes(' ')) {
return command;
}
return `"${command}"`;
}