@@ -29,22 +29,27 @@ gemini_preflight() {
2929 log_error " jq is not installed. Install it to use gemini-delegate (e.g., 'sudo apt install jq')."
3030 exit " ${EXIT_PREFLIGHT_FAIL} "
3131 fi
32+ if ! command -v gemini & > /dev/null; then
33+ log_error " gemini CLI is not installed or not on PATH. Install it, then run: gemini (Google OAuth)"
34+ exit " ${EXIT_PREFLIGHT_FAIL} "
35+ fi
3236 if [[ ! -f " $GEMINI_SETTINGS " ]]; then
3337 log_error " Gemini not authenticated. Run: gemini (Google OAuth interactive login)"
3438 exit " ${EXIT_PREFLIGHT_FAIL} "
3539 fi
36- log_info " Auth : OK (settings.json present )"
40+ log_info " Preflight : OK (jq present, gemini CLI available, settings.json found )"
3741}
3842
3943# --- Pre-flight: path safelist ---
4044# Usage: gemini_check_path_safe "path/to/file"
45+ # Uses substring matching (*$pattern*) to block secrets in nested paths (e.g. config/.env)
4146gemini_check_path_safe () {
4247 local file=" $1 "
43- local -a denied_patterns=(" .env" " .env.* " " *.pem" " *.key" " *.p12" " *.pfx" " *.secret" " *password*" " *credential*" " *_secret*" " .git" " .git/* " )
48+ local -a denied_patterns=(" .env" " .env." " *.pem" " *.key" " *.p12" " *.pfx" " *.secret" " *password*" " *credential*" " *_secret*" " .git" " .git/" )
4449 for pattern in " ${denied_patterns[@]} " ; do
4550 # shellcheck disable=SC2254
4651 case " $file " in
47- $pattern )
52+ * $pattern * )
4853 log_error " DENIED: '$file ' matches safelist pattern '$pattern '. Never delegate secrets."
4954 return " ${EXIT_PREFLIGHT_FAIL} "
5055 ;;
@@ -79,9 +84,10 @@ gemini_invoke_with_retry() {
7984 local exit_code=0
8085 local raw_json=" "
8186
82- local _stderr_file
87+ local _stderr_file _rc
8388 _stderr_file=$( mktemp /tmp/gemini_stderr_XXXXXX.txt)
84- trap ' rm -f "$_stderr_file"' RETURN
89+ # No trap RETURN — bash traps are global and would fire on every nested function return.
90+ # Explicit rm -f before every exit/return path ensures cleanup without side effects.
8591
8692 while (( attempt < max_retries )) ; do
8793 attempt=$(( attempt + 1 ))
@@ -97,6 +103,7 @@ gemini_invoke_with_retry() {
97103 # Auth error — exit immediately, no retry
98104 if (( exit_code == EXIT_AUTH_FAIL )) ; then
99105 log_error " Gemini auth error (exit 41). Run: gemini (Google OAuth)"
106+ rm -f " $_stderr_file "
100107 exit " ${EXIT_AUTH_FAIL} "
101108 fi
102109
@@ -106,14 +113,17 @@ gemini_invoke_with_retry() {
106113 log_warn " Retrying..."
107114 continue
108115 fi
109- return " $exit_code "
116+ _rc=" $exit_code "
117+ rm -f " $_stderr_file "
118+ return " $_rc "
110119 fi
111120
112121 # Check for error in JSON body (Gemini may exit 0 with error payload)
113122 local err_code
114123 err_code=$( echo " $raw_json " | jq -r ' .error.code // empty' 2> /dev/null || true)
115124 if [[ " $err_code " == " 41" ]]; then
116125 log_error " Gemini auth error in JSON payload. Run: gemini (Google OAuth)"
126+ rm -f " $_stderr_file "
117127 exit " ${EXIT_AUTH_FAIL} "
118128 fi
119129
@@ -123,13 +133,16 @@ gemini_invoke_with_retry() {
123133 if (( attempt < max_retries )) ; then
124134 continue
125135 fi
136+ rm -f " $_stderr_file "
126137 return " ${EXIT_VALIDATOR_FAIL} "
127138 fi
128139
140+ rm -f " $_stderr_file "
129141 return 0
130142 done
131143
132144 log_error " All $max_retries attempts exhausted."
145+ rm -f " $_stderr_file "
133146 return " ${EXIT_VALIDATOR_FAIL} "
134147}
135148
0 commit comments