@@ -14,7 +14,11 @@ fn get_platform_binary_name() -> &'static str {
1414}
1515
1616pub fn discover_codex_command ( ) -> Option < PathBuf > {
17- let home = std:: env:: var ( "HOME" ) . unwrap_or_default ( ) ;
17+ let home = if cfg ! ( windows) {
18+ std:: env:: var ( "USERPROFILE" ) . or_else ( |_| std:: env:: var ( "HOME" ) ) . unwrap_or_default ( )
19+ } else {
20+ std:: env:: var ( "HOME" ) . unwrap_or_default ( )
21+ } ;
1822 let binary_name = get_platform_binary_name ( ) ;
1923
2024 // 0) Optional override via environment variable
@@ -31,83 +35,91 @@ pub fn discover_codex_command() -> Option<PathBuf> {
3135 // First priority: Check actual binary locations in node_modules
3236 let binary_locations = [
3337 // Bun global installation
34- format ! (
35- "{}/.bun/install/global/node_modules/@openai/codex/bin/{}" ,
36- home, binary_name
37- ) ,
38+ PathBuf :: from ( & home) . join ( ".bun/install/global/node_modules/@openai/codex/bin" ) . join ( binary_name) ,
3839 // NPM rootless (user) global installation
39- format ! (
40- "{}/.local/share/npm/lib/node_modules/@openai/codex/bin/{}" ,
41- home, binary_name
42- ) ,
40+ PathBuf :: from ( & home) . join ( ".local/share/npm/lib/node_modules/@openai/codex/bin" ) . join ( binary_name) ,
4341 // NPM global installations
44- format ! (
45- "/usr/local/lib/node_modules/@openai/codex/bin/{}" ,
46- binary_name
47- ) ,
48- format ! (
49- "/opt/homebrew/lib/node_modules/@openai/codex/bin/{}" ,
50- binary_name
51- ) ,
42+ PathBuf :: from ( "/usr/local/lib/node_modules/@openai/codex/bin" ) . join ( binary_name) ,
43+ PathBuf :: from ( "/opt/homebrew/lib/node_modules/@openai/codex/bin" ) . join ( binary_name) ,
5244 ] ;
5345
54- for path in & binary_locations {
55- let path_buf = PathBuf :: from ( path) ;
46+ for path_buf in & binary_locations {
5647 if path_buf. exists ( ) {
57- log:: debug!( "Found codex binary at {}" , path) ;
58- return Some ( path_buf) ;
48+ log:: debug!( "Found codex binary at {}" , path_buf. display( ) ) ;
49+ return Some ( path_buf. clone ( ) ) ;
50+ }
51+ }
52+
53+ // Windows npm global installation paths
54+ if cfg ! ( windows) {
55+ if let Ok ( appdata) = std:: env:: var ( "APPDATA" ) {
56+ let npm_paths = [
57+ PathBuf :: from ( & appdata) . join ( "npm" ) . join ( "codex.cmd" ) ,
58+ PathBuf :: from ( & appdata) . join ( "npm" ) . join ( "codex.ps1" ) ,
59+ PathBuf :: from ( & appdata) . join ( "npm" ) . join ( "codex" ) ,
60+ ] ;
61+ for path_buf in & npm_paths {
62+ if path_buf. exists ( ) {
63+ log:: debug!( "Found npm codex at {}" , path_buf. display( ) ) ;
64+ return Some ( path_buf. clone ( ) ) ;
65+ }
66+ }
5967 }
6068 }
6169
6270 // Second priority: Check if there are native rust/cargo installations
6371 let native_paths = [
64- format ! ( "{}/.cargo/bin/codex" , home) ,
65- "/usr/local/bin/codex" . to_string ( ) ,
66- "/opt/homebrew/bin/codex" . to_string ( ) ,
72+ PathBuf :: from ( & home) . join ( ".cargo/bin/codex" ) ,
73+ PathBuf :: from ( & home) . join ( ".cargo/bin/codex.exe" ) ,
74+ PathBuf :: from ( "/usr/local/bin/codex" ) ,
75+ PathBuf :: from ( "/opt/homebrew/bin/codex" ) ,
6776 ] ;
6877
69- for path in & native_paths {
70- let path_buf = PathBuf :: from ( path) ;
78+ for path_buf in & native_paths {
7179 if path_buf. exists ( ) {
7280 // Check if it's a real binary (not a js wrapper)
73- if let Ok ( content) = std:: fs:: read_to_string ( & path_buf) {
81+ if let Ok ( content) = std:: fs:: read_to_string ( path_buf) {
7482 if content. contains ( "codex.js" ) || content. starts_with ( "#!/usr/bin/env node" ) {
7583 // This is a wrapper script, skip it
76- log:: debug!( "Skipping wrapper script at {}" , path ) ;
84+ log:: debug!( "Skipping wrapper script at {}" , path_buf . display ( ) ) ;
7785 continue ;
7886 }
7987 }
80- log:: debug!( "Found native codex binary at {}" , path ) ;
81- return Some ( path_buf) ;
88+ log:: debug!( "Found native codex binary at {}" , path_buf . display ( ) ) ;
89+ return Some ( path_buf. clone ( ) ) ;
8290 }
8391 }
8492
85- // Final fallback: Check PATH for native binaries; prefer non-wrappers, but accept wrappers as last resort
8693 if let Ok ( path_env) = std:: env:: var ( "PATH" ) {
8794 let separator = if cfg ! ( windows) { ';' } else { ':' } ;
8895 let mut wrapper_candidate: Option < PathBuf > = None ;
96+ let candidate_names: & [ & str ] = if cfg ! ( windows) {
97+ & [ "codex.exe" , "codex.cmd" , "codex.ps1" , "codex" ]
98+ } else {
99+ & [ "codex" ]
100+ } ;
89101 for dir in path_env. split ( separator) {
90102 if dir. is_empty ( ) {
91103 continue ;
92104 }
93- let exe_name = if cfg ! ( windows) { "codex.exe" } else { "codex" } ;
94- let candidate = PathBuf :: from ( dir) . join ( exe_name) ;
95- if candidate. exists ( ) {
96- // Try to verify if it's a wrapper
97- if let Ok ( content) = std:: fs:: read_to_string ( & candidate) {
98- let is_wrapper = content. contains ( "codex.js" )
99- || content. starts_with ( "#!/usr/bin/env node" )
100- || content. contains ( "import" ) ;
101- if is_wrapper {
102- // Keep as last-resort fallback
103- wrapper_candidate = Some ( candidate. clone ( ) ) ;
104- log:: debug!( "Found wrapper script candidate at {} (will use only if no native binary is found)" , candidate. display( ) ) ;
105- continue ;
105+ for name in candidate_names {
106+ let candidate = PathBuf :: from ( dir) . join ( name) ;
107+ if candidate. exists ( ) {
108+ if let Ok ( content) = std:: fs:: read_to_string ( & candidate) {
109+ let is_wrapper = content. contains ( "codex.js" )
110+ || content. starts_with ( "#!/usr/bin/env node" )
111+ || content. contains ( "import" ) ;
112+ if is_wrapper {
113+ if wrapper_candidate. is_none ( ) {
114+ wrapper_candidate = Some ( candidate. clone ( ) ) ;
115+ log:: debug!( "Found wrapper script candidate at {} (will use only if no native binary is found)" , candidate. display( ) ) ;
116+ }
117+ continue ;
118+ }
106119 }
120+ log:: debug!( "Found codex in PATH at {}" , candidate. display( ) ) ;
121+ return Some ( candidate) ;
107122 }
108- // Looks like a native binary
109- log:: debug!( "Found native codex in PATH at {}" , candidate. display( ) ) ;
110- return Some ( candidate) ;
111123 }
112124 }
113125 if let Some ( wrapper) = wrapper_candidate {
0 commit comments