diff --git a/cli/src/configuration.ts b/cli/src/configuration.ts index 9832852a..dc3813cb 100644 --- a/cli/src/configuration.ts +++ b/cli/src/configuration.ts @@ -9,6 +9,7 @@ import { existsSync, mkdirSync } from 'node:fs' import { homedir } from 'node:os' import { join } from 'node:path' import packageJson from '../package.json' +import { getCliArgs } from '@/utils/cliArgs' class Configuration { public readonly serverUrl: string @@ -32,7 +33,7 @@ class Configuration { this._cliApiToken = process.env.CLI_API_TOKEN || '' // Check if we're running as daemon based on process args - const args = process.argv.slice(2) + const args = getCliArgs() this.isDaemonProcess = args.length >= 2 && args[0] === 'daemon' && (args[1] === 'start-sync') // Directory configuration - Priority: HAPI_HOME env > default home dir diff --git a/cli/src/index.ts b/cli/src/index.ts index b113f620..3400cc16 100755 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -30,24 +30,11 @@ import { initializeToken } from './ui/tokenInit' import { ensureRuntimeAssets } from './runtime/assets' import { runHappyMcpStdioBridge } from './codex/happyMcpStdioBridge' import { withBunRuntimeEnv } from './utils/bunRuntime' +import { getCliArgs } from './utils/cliArgs' (async () => { - const args = (() => { - if (!isBunCompiled()) { - return process.argv.slice(2) - } - - const arg1 = process.argv[1] || '' - if (arg1 === process.execPath) { - return process.argv.slice(2) - } - if (arg1.endsWith('.js') || arg1.endsWith('.mjs') || arg1.endsWith('.ts')) { - return process.argv.slice(2) - } - - return process.argv.slice(1) - })() + const args = getCliArgs() if (args.includes('-v') || args.includes('--version')) { console.log(`hapi version: ${packageJson.version}`) diff --git a/cli/src/utils/cliArgs.ts b/cli/src/utils/cliArgs.ts new file mode 100644 index 00000000..5470e90d --- /dev/null +++ b/cli/src/utils/cliArgs.ts @@ -0,0 +1,78 @@ +import { basename } from 'node:path'; + +function resolveRawArgv(): string[] { + const bunArgv = globalThis.Bun?.argv; + if (Array.isArray(bunArgv) && bunArgv.length > 0) { + return bunArgv; + } + return process.argv; +} + +function isEntrypointPath(value: string, bunMain: string): boolean { + if (!value) { + return false; + } + if (value === bunMain) { + return true; + } + return /\.(c|m)?(ts|js)$/.test(value); +} + +function hasRuntimeWrapper(preArgs: string[], execPath: string, execBase: string, bunMain: string): boolean { + if (preArgs.length === 0) { + return false; + } + if (preArgs[0] === 'bun') { + return true; + } + if (preArgs.length < 2) { + return false; + } + if (preArgs[0] !== execPath && preArgs[0] !== execBase) { + return false; + } + return isEntrypointPath(preArgs[1], bunMain); +} + +export function normalizeCliArgs(rawArgv: string[]): string[] { + if (!Array.isArray(rawArgv) || rawArgv.length === 0) { + return []; + } + + const execPath = process.execPath; + const execBase = basename(execPath); + const bunMain = globalThis.Bun?.main ?? ''; + const dashIndex = rawArgv.indexOf('--'); + let argv = rawArgv.slice(); + if (dashIndex >= 0) { + const preArgs = rawArgv.slice(0, dashIndex); + const postArgs = rawArgv.slice(dashIndex + 1); + argv = hasRuntimeWrapper(preArgs, execPath, execBase, bunMain) + ? postArgs + : [...preArgs, ...postArgs]; + } + + let startIndex = 0; + while (startIndex < argv.length) { + const value = argv[startIndex] || ''; + const nextValue = argv[startIndex + 1] || ''; + if ( + value === 'bun' && + (nextValue === bunMain || nextValue === execPath || nextValue === execBase || isEntrypointPath(nextValue, bunMain)) + ) { + startIndex += 2; + continue; + } + if (value === execPath || value === execBase || isEntrypointPath(value, bunMain)) { + startIndex += 1; + continue; + } + break; + } + + return argv.slice(startIndex); +} + +export function getCliArgs(): string[] { + return normalizeCliArgs(resolveRawArgv()); +}