This repository was archived by the owner on Jun 7, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwiggum.sh
More file actions
213 lines (190 loc) · 8.08 KB
/
Copy pathwiggum.sh
File metadata and controls
213 lines (190 loc) · 8.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/bin/bash
# TrackXY: Optical Point Tracking and Analysis Tool
# Copyright (C) 2012, 2026 Liam D. Gray
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# wiggum.sh: The Ralph Wiggum Loop Driver
# Philosophy: "Iteration beats perfection."
#
# Auth tiers (in order of preference):
# oauth — Gemini CLI cached OAuth / Code Assist quota (free, generous)
# aistudio — Google AI Studio API key quota (free tier, separate bucket)
# deepseek — DeepSeek via LiteLLM proxy (paid, requires proxy on :4000)
#
# Prerequisites:
# - Run with GEMINI_API_KEY and DEEPSEEK_API_KEY already in the environment,
# e.g.: set -a; source <(sops --input-type dotenv -d secrets.enc.env); set +a
# - LiteLLM proxy optionally running: litellm --config config.yaml --port 4000
echo "Starting the Wiggum Loop for TrackXY..."
chmod +x .gemini/hooks/after_agent.sh
# ── Tier state ─────────────────────────────────────────────────────────────────
AUTH_MODE="oauth"
OAUTH_RESET_EPOCH=0
AISTUDIO_RESET_EPOCH=0
# ── Detect available fallback tiers ───────────────────────────────────────────
HAS_AISTUDIO=false
HAS_DEEPSEEK=false
if [[ -n "${GEMINI_API_KEY:-}" ]]; then
HAS_AISTUDIO=true
echo "AI Studio tier available (GEMINI_API_KEY set)."
else
echo "Warning: GEMINI_API_KEY not set — AI Studio tier unavailable."
fi
if curl -sf http://localhost:4000/health > /dev/null 2>&1; then
HAS_DEEPSEEK=true
echo "DeepSeek tier available (LiteLLM proxy on :4000)."
else
echo "LiteLLM proxy not running — DeepSeek tier unavailable."
fi
# ── Parse retryDelayMs from quota error output ─────────────────────────────────
parse_retry_delay_sec() {
local output="$1"
local pattern='retryDelayMs:[[:space:]]*([0-9.]+)'
if [[ "$output" =~ $pattern ]]; then
python3 -c "import math; print(math.ceil(${BASH_REMATCH[1]} / 1000.0))"
else
echo "3600" # fallback: 1 hour
fi
}
# ── Advance to next available tier on quota exhaustion ────────────────────────
advance_tier() {
case "$AUTH_MODE" in
oauth)
if $HAS_AISTUDIO; then
AUTH_MODE="aistudio"
echo "Advancing to AI Studio tier."
elif $HAS_DEEPSEEK; then
AUTH_MODE="deepseek"
echo "Advancing to DeepSeek tier."
fi
;;
aistudio)
if $HAS_DEEPSEEK; then
AUTH_MODE="deepseek"
echo "Advancing to DeepSeek tier."
fi
;;
deepseek)
# All tiers exhausted — handled in main loop
;;
esac
}
# ── Step back up to better tiers if their quotas have reset ───────────────────
maybe_restore_tier() {
local now
now=$(date +%s)
# Always try to restore to the best available tier
if [[ "$AUTH_MODE" != "oauth" ]] \
&& (( OAUTH_RESET_EPOCH > 0 && now >= OAUTH_RESET_EPOCH )); then
AUTH_MODE="oauth"
echo "OAuth quota reset window passed — restoring to OAuth tier."
return
fi
if [[ "$AUTH_MODE" == "deepseek" ]] \
&& $HAS_AISTUDIO \
&& (( AISTUDIO_RESET_EPOCH > 0 && now >= AISTUDIO_RESET_EPOCH )); then
AUTH_MODE="aistudio"
echo "AI Studio quota reset window passed — restoring to AI Studio tier."
fi
}
# ── Invoke gemini with auth appropriate to current tier ───────────────────────
# NOTE: oauth mode unsets GEMINI_API_KEY for the subprocess so cached OAuth
# tokens take effect. aistudio mode uses the real key directly against
# Google. deepseek mode uses a dummy key with GOOGLE_GEMINI_BASE_URL to
# route through LiteLLM — this relies on Gemini CLI honouring
# GOOGLE_GEMINI_BASE_URL in API key mode, which was unconfirmed in
# testing; if it doesn't work the loop will sleep on all-tier exhaustion.
gemini_run() {
local prompt
prompt="$(cat TASKS.md)"
case "$AUTH_MODE" in
oauth)
# Normal: let Gemini CLI find its cached OAuth tokens
env -u GEMINI_API_KEY \
gemini --approval-mode=yolo --skip-trust -p "$prompt" 2>&1
;;
aistudio|deepseek)
# Hide ~/.gemini/tokens.json by overriding HOME with a temp dir.
# Without cached OAuth, Gemini CLI falls back to GEMINI_API_KEY.
# Project-level .gemini/ (CWD) is still found normally.
local tmpHome
tmpHome=$(mktemp -d)
mkdir -p "$tmpHome/.gemini"
# Copy settings but NOT tokens.json
cp ~/.gemini/settings.json "$tmpHome/.gemini/" 2>/dev/null || true
if [[ "$AUTH_MODE" == "aistudio" ]]; then
HOME="$tmpHome" \
GEMINI_API_KEY="${GEMINI_API_KEY}" \
timeout 30 gemini --approval-mode=yolo --skip-trust -p "$prompt" 2>&1
else
# deepseek: dummy key routes to LiteLLM
HOME="$tmpHome" \
GEMINI_API_KEY="sk-1234" \
GOOGLE_GEMINI_BASE_URL="http://localhost:4000" \
timeout 30 gemini --approval-mode=yolo --skip-trust -p "$prompt" 2>&1
fi
rm -rf "$tmpHome"
;;
esac
}
# ── Main loop ──────────────────────────────────────────────────────────────────
while true; do
echo "--- New Iteration [mode: $AUTH_MODE] ---"
# Exit if all tasks are checked off
if ! grep -qP '^\s*-\s*\[ \]' TASKS.md; then
echo "All tasks complete. Wiggum is done."
break
fi
if [[ -f .wiggum_stop ]]; then
echo "Stop signal detected. Exiting."
rm .wiggum_stop
break
fi
maybe_restore_tier
OUTPUT=$(gemini_run)
echo "$OUTPUT"
if [[ "$OUTPUT" == *"QUOTA_EXHAUSTED"* ]] \
|| [[ "$OUTPUT" == *"TerminalQuotaError"* ]] \
|| [[ "$OUTPUT" == *"quota will reset"* ]]; then
DELAY_SEC=$(parse_retry_delay_sec "$OUTPUT")
RESET_EPOCH=$(( $(date +%s) + DELAY_SEC ))
echo "Quota exhausted on '$AUTH_MODE' tier (resets in ${DELAY_SEC}s)."
case "$AUTH_MODE" in
oauth)
OAUTH_RESET_EPOCH=$RESET_EPOCH
advance_tier
;;
aistudio)
AISTUDIO_RESET_EPOCH=$RESET_EPOCH
advance_tier
;;
deepseek)
# All tiers exhausted — sleep until the soonest reset
NOW=$(date +%s)
SOONEST=$RESET_EPOCH
(( OAUTH_RESET_EPOCH > NOW && OAUTH_RESET_EPOCH < SOONEST )) \
&& SOONEST=$OAUTH_RESET_EPOCH
(( AISTUDIO_RESET_EPOCH > NOW && AISTUDIO_RESET_EPOCH < SOONEST )) \
&& SOONEST=$AISTUDIO_RESET_EPOCH
SLEEP_SEC=$(( SOONEST - NOW ))
echo "All tiers exhausted. Sleeping ${SLEEP_SEC}s..."
sleep "$SLEEP_SEC"
AUTH_MODE="oauth" # maybe_restore_tier will correct if still exhausted
;;
esac
fi
sleep 2
done
echo "Wiggum Loop finished."