diff --git a/claude_setup.sh b/claude_setup.sh index 1955dbc..73d95f6 100755 --- a/claude_setup.sh +++ b/claude_setup.sh @@ -106,6 +106,60 @@ else install_proxy fi +# ── 4b. effort-proxy wrapper (патч xhigh→max для claude-code-proxy) ───────── +EFFORT_PROXY_BIN="$HOME/.local/bin/claude-gpt-effort-proxy.py" +cat > "$EFFORT_PROXY_BIN" << 'PYEOF' +#!/usr/bin/env python3 +"""Reverse proxy: rewrites "xhigh" effort → "max" for claude-code-proxy (bug in ≤0.0.13).""" +import http.client, http.server, sys + +UPSTREAM_PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 18766 +LISTEN_PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 18765 + +class _Proxy(http.server.BaseHTTPRequestHandler): + def proxy_request(self): + body = b"" + if cl := self.headers.get("Content-Length"): + body = self.rfile.read(int(cl)) + body = body.replace(b'"xhigh"', b'"max"') + try: + conn = http.client.HTTPConnection("localhost", UPSTREAM_PORT, timeout=300) + hdrs = dict(self.headers) + hdrs.pop("Host", None) + hdrs["Content-Length"] = str(len(body)) + conn.request(self.command, self.path, body=body or None, headers=hdrs) + resp = conn.getresponse() + self.send_response(resp.status, resp.reason) + chunked = False + for k, v in resp.getheaders(): + if k.lower() == "transfer-encoding" and "chunked" in v.lower(): + chunked = True + self.send_header(k, v) + self.end_headers() + while chunk := resp.read(4096): + try: + if chunked: + self.wfile.write(f"{len(chunk):X}\r\n".encode() + chunk + b"\r\n") + else: + self.wfile.write(chunk) + self.wfile.flush() + except (BrokenPipeError, ConnectionResetError): + break + if chunked: + try: self.wfile.write(b"0\r\n\r\n") + except: pass + conn.close() + except Exception as e: + try: self.send_error(502, str(e)) + except: pass + do_GET = do_POST = do_PUT = do_DELETE = do_HEAD = proxy_request + def log_message(self, *args): pass + +http.server.HTTPServer(("localhost", LISTEN_PORT), _Proxy).serve_forever() +PYEOF +chmod +x "$EFFORT_PROXY_BIN" +success "claude-gpt-effort-proxy -> $EFFORT_PROXY_BIN" + # ── 5. antigravity-claude-proxy (Gemini) ──────────────────── info "Проверяю antigravity-claude-proxy..." if command -v antigravity-claude-proxy &>/dev/null || command -v acc &>/dev/null; then @@ -375,20 +429,44 @@ claude_gpt() { fi # Запускаем прокси в фоне (если ещё не запущен) - local proxy_pid="" + local proxy_pid="" wrapper_pid="" + local effort_proxy="$HOME/.local/bin/claude-gpt-effort-proxy.py" + + # Если прокси занял порт 18765 (старый формат без wrapper) — мигрируем + if pgrep -f "claude-code-proxy serve" &>/dev/null && \ + ! pgrep -f "claude-gpt-effort-proxy" &>/dev/null; then + pkill -f "claude-code-proxy serve" 2>/dev/null + sleep 0.5 + fi + + # Реальный прокси на 18766 if ! pgrep -f "claude-code-proxy serve" &>/dev/null; then - "$proxy_bin" serve &>/tmp/claude-code-proxy.log & + PORT=18766 "$proxy_bin" serve &>/tmp/claude-code-proxy.log & proxy_pid=$! + local _j=0 + while [ $_j -lt 10 ]; do + sleep 1 + curl -sf --max-time 1 http://localhost:18766/ &>/dev/null + [ "$?" -ne 7 ] && break + _j=$((_j + 1)) + done + fi + + # Wrapper (xhigh→max патч) на 18765 + if ! pgrep -f "claude-gpt-effort-proxy" &>/dev/null; then + python3 "$effort_proxy" 18766 18765 &>/tmp/claude-gpt-effort-proxy.log & + wrapper_pid=$! local _i=0 while [ $_i -lt 10 ]; do sleep 1 curl -sf --max-time 1 http://localhost:18765/ &>/dev/null; local _ce=$? - [ "$_ce" -ne 7 ] && break # exit 7 = connection refused; любой другой = прокси слушает + [ "$_ce" -ne 7 ] && break # exit 7 = connection refused; любой другой = wrapper слушает _i=$((_i + 1)) done fi - # Убиваем прокси при любом выходе из функции (early return или нормальный) - trap '[ -n "$proxy_pid" ] && kill "$proxy_pid" 2>/dev/null' RETURN + + # Убиваем прокси и wrapper при любом выходе из функции + trap '[ -n "$proxy_pid" ] && kill "$proxy_pid" 2>/dev/null; [ -n "$wrapper_pid" ] && kill "$wrapper_pid" 2>/dev/null' RETURN # ── Pre-launch API validation through proxy ── echo -n "Проверка авторизации ChatGPT... " @@ -417,7 +495,7 @@ claude_gpt() { fi echo -n "Проверяю авторизацию после входа... " _claude_test_api "http://localhost:18765/v1/messages" "x-api-key: dummy" "gpt-5.4-mini" - if [ "$_CLAUDE_TEST_CODE" != "200" ]; then + if [ "$_CLAUDE_TEST_CODE" != "200" ] && [ "$_CLAUDE_TEST_CODE" != "400" ]; then echo -e "\033[0;31mОШИБКА (HTTP $_CLAUDE_TEST_CODE)\033[0m" return 1 fi @@ -433,6 +511,11 @@ claude_gpt() { [ -n "$_emsg" ] && echo " $_emsg" return 1 ;; + 400) + # Прокси работает — 400 означает только, что тест-запрос не содержит обязательное поле «instructions» + # Авторизация уже проверена через codex auth status; запускаем Claude + echo -e "\033[0;32mOK\033[0m" + ;; 000) echo "" echo -e "\033[0;33m[СЕТЬ]\033[0m Не удалось проверить ChatGPT прокси (нет сети?). Продолжаю..." @@ -440,12 +523,26 @@ claude_gpt() { *) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" - echo -e "\033[0;31m[ОШИБКА]\033[0m Прокси вернул HTTP $_CLAUDE_TEST_CODE" + echo -e "\033[0;31m[ОШИБКА]\033[0m Прокси вернул неожиданный HTTP $_CLAUDE_TEST_CODE." [ -n "$_emsg" ] && echo " $_emsg" + echo "" + echo "Попробуйте:" + echo " • Перезапустить claude_gpt" + echo " • Переавторизоваться: claude-code-proxy codex auth logout && claude-code-proxy codex auth login" return 1 ;; esac + # Сохраняем Anthropic credentials перед запуском — команда /logout внутри Claude Code + # удаляет их, хотя в режиме GPT они не нужны, но потом сломают claude_anthropic + local _creds_file="$HOME/.claude/.credentials.json" + local _creds_backup="" + [ -f "$_creds_file" ] && _creds_backup=$(cat "$_creds_file") + + echo -e "\033[0;33m[ИНФО]\033[0m Режим ChatGPT. Для выхода: Ctrl+C или /exit" + echo -e " Команда \033[1m/logout\033[0m выйдет из Anthropic, а не из ChatGPT." + echo "" + ANTHROPIC_BASE_URL=http://localhost:18765 \ ANTHROPIC_AUTH_TOKEN=dummy \ ANTHROPIC_MODEL=gpt-5.5 \ @@ -456,6 +553,14 @@ claude_gpt() { CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \ claude "$@" + # Восстанавливаем credentials если /logout их удалил + if [ -n "$_creds_backup" ] && [ ! -f "$_creds_file" ]; then + mkdir -p "$(dirname "$_creds_file")" + echo "$_creds_backup" > "$_creds_file" + echo "" + echo -e "\033[0;33m[ИНФО]\033[0m Anthropic credentials восстановлены после /logout внутри сессии." + fi + if [ -n "$proxy_pid" ]; then kill "$proxy_pid" 2>/dev/null wait "$proxy_pid" 2>/dev/null @@ -841,6 +946,44 @@ except: return 1 fi ;; + 400) + _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") + echo "" + if echo "$_emsg" | grep -q "RESOURCE_EXHAUSTED"; then + local _reset + _reset=$(echo "$_emsg" | sed -n 's/.*reset after \([^.]*\).*/\1/p' 2>/dev/null || true) + echo -e "\033[0;33m[КВОТА ИСЧЕРПАНА]\033[0m Все Gemini аккаунты исчерпали лимит запросов." + [ -n "$_reset" ] && echo " Квота обновится через: $_reset" + echo "" + echo "Что делать:" + echo " • Подождите сброса квоты и повторите попытку" + echo " • Или добавьте новый аккаунт Google через http://localhost:8080" + if _claude_offer_reauth "Gemini (добавить аккаунт)"; then + xdg-open "http://localhost:8080" 2>/dev/null || \ + sensible-browser "http://localhost:8080" 2>/dev/null || \ + echo "Откройте http://localhost:8080" + echo "Нажмите Enter после добавления..." + read -r + echo -n "Проверяю авторизацию Gemini... " + _claude_test_api "http://localhost:8080/v1/messages" "x-api-key: dummy" "gemini-3-flash-agent" + if [ "$_CLAUDE_TEST_CODE" != "200" ]; then + echo -e "\033[0;31mОШИБКА (HTTP $_CLAUDE_TEST_CODE)\033[0m" + return 1 + fi + echo -e "\033[0;32mOK\033[0m" + else + return 1 + fi + else + echo -e "\033[0;31m[ОШИБКА]\033[0m Прокси Gemini вернул HTTP 400." + [ -n "$_emsg" ] && echo " $_emsg" + echo "" + echo "Попробуйте:" + echo " • Перезапустить прокси: перезапустите claude_gemini" + echo " • Проверить статус аккаунтов: http://localhost:8080" + return 1 + fi + ;; 000) echo "" echo -e "\033[0;33m[СЕТЬ]\033[0m Не удалось проверить Gemini прокси (нет сети?). Продолжаю..." @@ -848,8 +991,12 @@ except: *) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" - echo -e "\033[0;31m[ОШИБКА]\033[0m Прокси вернул HTTP $_CLAUDE_TEST_CODE" + echo -e "\033[0;31m[ОШИБКА]\033[0m Прокси вернул неожиданный HTTP $_CLAUDE_TEST_CODE." [ -n "$_emsg" ] && echo " $_emsg" + echo "" + echo "Попробуйте:" + echo " • Перезапустить claude_gemini" + echo " • Проверить статус аккаунтов: http://localhost:8080" return 1 ;; esac