feat(codex-discovery): let callers scope install candidate paths - #32
Merged
Conversation
Harold Hunt (huntharo)
force-pushed
the
claude/gifted-faraday-e44e19
branch
2 times, most recently
from
August 18, 2026 03:41
b7322fb to
3258a9d
Compare
Discovery probed a hardcoded list of well-known install locations on top of PATH, with no way to override it. `/usr/local/bin/codex` appears on both the macOS and Linux lists, so on any dev machine with a real Codex CLI installed there, that binary was discovered and — since auto candidates sort newest-first — outranked the temp-dir shims the tests had just written. Four tests failed as a result, including the two "nothing is installed" cases, which resolved instead of throwing. Add an optional `installCandidatePaths` / `homeDir` seam to `discoverCodexCommands` and `resolveCodexCommand`, defaulting to `getCodexInstallCandidatePaths(platform, homeDir)` so existing callers are unaffected. Export `getCodexInstallCandidatePaths` and the new `CodexInstallCandidateOptions` type from the package entry point so hosts can extend the default list rather than replace it. Tests now scope auto candidates to their own fixtures, plus three new tests covering the seam. Also makes "returns auto candidates newest-first" meaningful: it wrote two application shims but never passed them to discovery, so its only assertion ran against an undefined candidate and could not fail.
Review follow-ups on the seam added in the previous commit. Correctness: - `getCodexInstallCandidatePaths` resolved `homeDir` through a default parameter, which fires only on `undefined`. Now that the value is caller-supplied public API, a blank string (a host writing `process.env.HOME ?? ""`, or `os.homedir()` itself when HOME is set but empty) produced relative entries like `.local/bin/codex` that discovery resolved — and executed — against `process.cwd()`. - The same helper joined user-local paths with the host's `path`, so the `platform` argument was only honoured when it matched `process.platform`. It now joins with the target platform's rules. - Its doc comment claimed list order decides which install wins; selection is by version, with order only breaking ties. Corrected, and the `CodexCliNotInstalledError` doc and default message no longer claim a search that a narrowed `installCandidatePaths` scoped out. API: - `resolveCodexCommand` forwards options by spread instead of field-by-field, so a future `CodexInstallCandidateOptions` field cannot be silently dropped, and its `platform` now accepts an explicit `undefined` like its siblings under `exactOptionalPropertyTypes`. - Option docs state that an explicit list replaces rather than extends the defaults, that entries must be absolute, and why `homeDir` has nothing to act on once a list is supplied. Test-suite rationale no longer ships in the published `.d.ts`. Tests: - "defaults the auto candidates to the platform install list" only asserted that a fresh mkdtemp path was absent, so it passed even with the default list replaced by `[]`. Replaced with a positive test that plants a shim under a temp `homeDir`, and that also covers `resolveCodexCommand`'s `homeDir` forward — previously deletable with the suite still green. - Added coverage that an explicit list replaces rather than appends to the defaults, which was indistinguishable on a runner with no Codex installed. - The ordering assertion now covers the whole snapshot instead of a source-filtered subset, and the win32 branch and blank-homeDir fallback are pinned directly. Verified by mutation: dropping the default list, dropping either forward, making `[]` fall through to the defaults, removing the blank-homeDir guard, and reverting the platform-aware join are each now caught.
Harold Hunt (huntharo)
force-pushed
the
claude/gifted-faraday-e44e19
branch
from
August 18, 2026 03:42
3258a9d to
989198f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Four tests in
packages/codex-discovery/test/codex-discovery.test.tsfail on any dev machine with a real Codex CLI installed at a well-known path:parses and exposes a selected 0.139.0 version with trailing punctuationsurfaces a too-old binary as a candidate with failureReason codex_too_old (not dropped)throws CodexCliNotInstalledError (not a raw ENOENT) when nothing is foundthrows a too-old error (not CodexCliNotInstalledError) when only an old binary existsThose tests call discovery with
platform: "linux", andgetCodexInstallCandidatePaths("linux")hardcodes/usr/local/bin/codexamong the auto-candidates — a path shared with the macOS list. On a host with a real install there (reproduced againstcodex-cli 0.146.1on macOS), the real binary is discovered and, since auto candidates sort newest-first, outranks the temp-dir shim each test just wrote. The two "nothing is installed" cases resolve instead of throwing.This is not a regression — the tests fail identically on a pristine checkout of
main. There was simply no way for a caller to control the auto-candidate set.Change
Adds an optional
CodexInstallCandidateOptionsseam, accepted by bothdiscoverCodexCommandsandresolveCodexCommand:installCandidatePaths— replaces the platform list (spreadgetCodexInstallCandidatePathsinto your own list to extend instead); defaults togetCodexInstallCandidatePaths(platform, homeDir)homeDir— expands the user-local entries of the default list; defaults toos.homedir()Both are optional and default to today's behavior, so existing callers (
codex-oneshot-client.ts,codex-thread-client.ts) are unchanged.getCodexInstallCandidatePathsand the new type are now exported so hosts can extend the default list.While making the helper public, three latent problems in it were fixed:
homeDir(a host writingprocess.env.HOME ?? "", oros.homedir()itself whenHOMEis set but empty) produced relative entries like.local/bin/codex, which discovery resolved — and executed — againstprocess.cwd(). Reproduced selecting a repo-local script over the real install. Now falls back to the real home.path, so itsplatformargument was only honoured when it matchedprocess.platform. Now joins with the target platform's rules.CodexCliNotInstalledError's doc and default message, which claimed a search that a narrowedinstallCandidatePathsscoped out.resolveCodexCommandnow forwards options by spread rather than field-by-field, soenv,platform, the probe budget/signal from #31 and the install-candidate options all reach discovery without being re-listed — and anything added later does too.Note for reviewers
One test change goes beyond the fix.
returns auto candidates newest-first and honors env > config > auto prioritywrote two application shims but never passed them to discovery, so its only assertion ran against anundefinedcandidate and could not fail. It now feeds both shims in oldest-first and asserts the whole snapshot's order.Verification
pnpm typecheck,pnpm build,pnpm test, and all three lint targets pass. Verified by mutation — each of these is now caught, where three previously slipped through green:?? [])homeDirforward dropped[]falls through to the defaultshomeDirguard removedRebased onto #31. Its probe-budget rework (
DEFAULT_COMMAND_VERSION_TIMEOUT_MS2s → 10s) also resolves the slow-spawn flakiness an earlier revision of this description flagged: the full suite now passes under parallel load on the machine where it previously failed.🤖 Generated with Claude Code