( cmd: string, args: string[], options?: Omit<ExecFileOptionsWithStringEncoding, "encoding">, )
| 195 | * shell environment only when needed, then persists the fix to process.env.PATH. |
| 196 | */ |
| 197 | export async function execWithShellEnv( |
| 198 | cmd: string, |
| 199 | args: string[], |
| 200 | options?: Omit<ExecFileOptionsWithStringEncoding, "encoding">, |
| 201 | ): Promise<{ stdout: string; stderr: string }> { |
| 202 | try { |
| 203 | return await execFileAsync(cmd, args, { ...options, encoding: "utf8" }); |
| 204 | } catch (error) { |
| 205 | // Only retry on ENOENT (command not found), only on macOS |
| 206 | // Skip if we've already successfully fixed PATH, or if a fix attempt is in progress |
| 207 | if ( |
| 208 | process.platform !== "darwin" || |
| 209 | pathFixSucceeded || |
| 210 | pathFixAttempted || |
| 211 | !(error instanceof Error) || |
| 212 | !("code" in error) || |
| 213 | error.code !== "ENOENT" |
| 214 | ) { |
| 215 | throw error; |
| 216 | } |
| 217 | |
| 218 | pathFixAttempted = true; |
| 219 | console.log("[shell-env] Command not found, deriving shell environment"); |
| 220 | |
| 221 | try { |
| 222 | const shellEnv = await getShellEnvironment(); |
| 223 | |
| 224 | // Persist the fix to process.env so all subsequent calls benefit |
| 225 | if (shellEnv.PATH) { |
| 226 | process.env.PATH = shellEnv.PATH; |
| 227 | pathFixSucceeded = true; |
| 228 | console.log("[shell-env] Fixed process.env.PATH for GUI app"); |
| 229 | } |
| 230 | |
| 231 | // Retry with fixed env (respect caller's other env vars, force PATH if present) |
| 232 | const retryEnv = shellEnv.PATH |
| 233 | ? { ...shellEnv, ...options?.env, PATH: shellEnv.PATH } |
| 234 | : { ...shellEnv, ...options?.env }; |
| 235 | |
| 236 | return await execFileAsync(cmd, args, { |
| 237 | ...options, |
| 238 | encoding: "utf8", |
| 239 | env: retryEnv, |
| 240 | }); |
| 241 | } catch (retryError) { |
| 242 | // Shell env derivation or retry failed - allow future retries |
| 243 | pathFixAttempted = false; |
| 244 | console.error("[shell-env] Retry failed:", retryError); |
| 245 | throw retryError; |
| 246 | } |
| 247 | } |
| 248 | } |
no test coverage detected