#!/usr/bin/env bash # ============================================================ # Claude Code Setup — Anthropic / GPT-5.5 / DeepSeek / Kimi / Gemini # Запуск: bash claude_setup.sh # ============================================================ BASHRC="$HOME/.bashrc" CONFIG_DIR="$HOME/.config/claude-launcher" DEEPSEEK_KEY_FILE="$CONFIG_DIR/deepseek_key" NPM_GLOBAL="$HOME/.npm-global" PROXY_BIN="$HOME/.local/bin/claude-code-proxy" RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' info() { echo -e "${CYAN}[INFO]${NC} $*"; } success() { echo -e "${GREEN}[OK]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } err() { echo -e "${RED}[ERR]${NC} $*"; exit 1; } # Запрет запуска от root if [ "$EUID" -eq 0 ]; then echo -e "${RED}Не запускайте этот скрипт через sudo!${NC}" echo "Запустите просто: bash claude_setup.sh" exit 1 fi # ── 1. npm prefix в домашнюю папку ────────────────────────── info "Настраиваю npm prefix..." mkdir -p "$NPM_GLOBAL" npm config set prefix "$NPM_GLOBAL" success "npm prefix -> $NPM_GLOBAL" # Добавляем npm-global в PATH прямо сейчас (для этого запуска скрипта) export PATH="$NPM_GLOBAL/bin:$HOME/.local/bin:$PATH" # Прописываем в .bashrc если ещё нет if ! grep -q 'NPM_GLOBAL' "$BASHRC" 2>/dev/null; then cat >> "$BASHRC" << 'PATHEOF' # npm global без sudo export NPM_GLOBAL="$HOME/.npm-global" export PATH="$NPM_GLOBAL/bin:$HOME/.local/bin:$PATH" PATHEOF success "npm PATH добавлен в $BASHRC" else success "npm PATH уже есть в $BASHRC" fi # ── 2. Node.js ─────────────────────────────────────────────── info "Проверяю Node.js..." if ! command -v node &>/dev/null; then info "Устанавливаю Node.js (нужен sudo)..." curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt-get install -y nodejs fi success "Node.js $(node --version)" # ── 3. Claude Code ─────────────────────────────────────────── info "Проверяю Claude Code..." if ! command -v claude &>/dev/null; then info "Устанавливаю Claude Code..." # Пробуем официальный инсталлер if curl -fsSL https://claude.ai/install.sh | bash 2>/dev/null; then success "Claude Code установлен (официальный инсталлер)" else # Fallback: npm в наш prefix (без sudo) npm install -g @anthropic-ai/claude-code success "Claude Code установлен (npm)" fi else success "Claude Code уже установлен: $(claude --version 2>/dev/null | head -1)" fi # ── 4. claude-code-proxy (GPT) ─────────────────────────────── mkdir -p "$HOME/.local/bin" install_proxy() { info "Устанавливаю claude-code-proxy..." ARCH=$(uname -m) case "$ARCH" in x86_64) ARCH_TAG="amd64" ;; aarch64) ARCH_TAG="arm64" ;; *) err "Неизвестная архитектура: $ARCH" ;; esac LATEST=$(curl -fsSL "https://api.github.com/repos/raine/claude-code-proxy/releases/latest" \ | grep '"tag_name"' | sed 's/.*"tag_name": *"\(.*\)".*/\1/') [ -z "$LATEST" ] && err "Не удалось получить версию claude-code-proxy с GitHub" TMP=$(mktemp -d) trap 'rm -rf "$TMP"' EXIT URL="https://github.com/raine/claude-code-proxy/releases/download/${LATEST}/claude-code-proxy-linux-${ARCH_TAG}.tar.gz" info "Скачиваю $URL" curl -fsSL "$URL" -o "$TMP/proxy.tar.gz" || err "Не удалось скачать claude-code-proxy" tar -xzf "$TMP/proxy.tar.gz" -C "$TMP" BINARY=$(find "$TMP" -name "claude-code-proxy" -type f | head -1) [ -z "$BINARY" ] && err "Бинарник не найден в архиве" cp "$BINARY" "$PROXY_BIN" chmod +x "$PROXY_BIN" success "claude-code-proxy $LATEST -> $PROXY_BIN" } if [ -f "$PROXY_BIN" ]; then CURRENT_VER=$("$PROXY_BIN" --version 2>/dev/null | head -1 || echo "unknown") success "claude-code-proxy уже установлен ($CURRENT_VER)" else install_proxy fi # ── 5. antigravity-claude-proxy (Gemini) ──────────────────── info "Проверяю antigravity-claude-proxy..." if command -v antigravity-claude-proxy &>/dev/null || command -v acc &>/dev/null; then success "antigravity-claude-proxy уже установлен" else info "Устанавливаю antigravity-claude-proxy (npm, без sudo)..." npm install -g antigravity-claude-proxy@latest success "antigravity-claude-proxy установлен" fi # ── 6. Папка для конфигов ──────────────────────────────────── mkdir -p "$CONFIG_DIR" # ── 7. Функции в .bashrc ───────────────────────────────────── info "Прописываю функции запуска в $BASHRC..." MARKER="# === CLAUDE LAUNCHER ===" if grep -q "$MARKER" "$BASHRC" 2>/dev/null; then warn "Блок уже есть в $BASHRC — обновляю..." python3 - "$BASHRC" <<'PYEOF' import sys path = sys.argv[1] with open(path, 'r') as f: content = f.read() marker = '# === CLAUDE LAUNCHER ===' end_marker = '# === END CLAUDE LAUNCHER ===' start = content.find(marker) end = content.find(end_marker) if start != -1 and end != -1: new_content = content[:start] + content[end + len(end_marker):].lstrip('\n') with open(path, 'w') as f: f.write(new_content) PYEOF fi cat >> "$BASHRC" << 'BASHEOF' # === CLAUDE LAUNCHER === # ── Shared auth validation helpers ────────────────────────── # _claude_test_api: Send 1-token test to an Anthropic-compatible endpoint # Usage: _claude_test_api [model] # auth_header format: "Authorization: Bearer TOKEN" or "x-api-key: KEY" # Side effects: Sets globals _CLAUDE_TEST_CODE, _CLAUDE_TEST_BODY _claude_test_api() { local url="$1" auth_header="$2" model="${3:-claude-sonnet-4-6}" local response response=$(curl -s -w "\n%{http_code}" --max-time 15 "$url" \ -H "$auth_header" \ -H "Content-Type: application/json" \ -H "anthropic-version: 2023-06-01" \ -d "{\"model\":\"$model\",\"max_tokens\":1,\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}]}" \ 2>/dev/null || echo "000") _CLAUDE_TEST_CODE=$(echo "$response" | tail -1) _CLAUDE_TEST_BODY=$(echo "$response" | sed '$d') } # _claude_extract_error: Extract error.message from Anthropic-style error JSON _claude_extract_error() { local body="$1" echo "$body" | python3 -c " import sys, json try: d = json.load(sys.stdin) e = d.get('error', {}) if isinstance(e, str): print(e) else: msg = e.get('message', '') if msg: print(msg) except: pass " 2>/dev/null } # _claude_offer_reauth: Ask user if they want to re-authenticate # Returns 0 for yes, 1 for no _claude_offer_reauth() { local provider="$1" echo "" echo -n "Хотите выполнить повторную авторизацию $provider? [Y/n] " local answer read -r answer case "${answer:-Y}" in [Yy]|[Yy][Ee][Ss]) return 0 ;; *) echo "Отменено."; return 1 ;; esac } # ── claude_anthropic ────────────────────────────────────────── claude_anthropic() { unset ANTHROPIC_BASE_URL ANTHROPIC_AUTH_TOKEN unset CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC local _token="" _method="" # Извлекаем OAuth-токен из credentials _token=$(python3 -c " import json, os try: with open(os.path.expanduser('~/.claude/.credentials.json')) as f: d = json.load(f) print(d.get('claudeAiOauth', {}).get('accessToken', '')) except: pass " 2>/dev/null) if [ -n "$_token" ]; then _method="oauth" elif [ -n "${ANTHROPIC_API_KEY:-}" ]; then _token="$ANTHROPIC_API_KEY" _method="apikey" fi # ── Pre-launch auth validation ── if [ "$_method" = "oauth" ]; then # OAuth-токен предназначен для внутренней авторизации Claude Code, а не для прямых API-вызовов. # Claude Code сам обрабатывает OAuth — прямой тест API с Bearer-токеном некорректен и даёт ложные 429. : # пропускаем проверку, Claude Code обработает авторизацию самостоятельно elif [ -n "$_token" ]; then echo -n "Проверка авторизации Anthropic... " _claude_test_api "https://api.anthropic.com/v1/messages" "x-api-key: $_token" local _emsg case "$_CLAUDE_TEST_CODE" in 200) echo -e "\033[0;32mOK\033[0m" ;; 401) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo -e "\033[0;31mОШИБКА: Недействительная авторизация (HTTP 401)\033[0m" [ -n "$_emsg" ] && echo " $_emsg" echo "" echo "API-ключ недействителен." echo " export ANTHROPIC_API_KEY=sk-ant-..." echo "" read -r -p "Ввести новый ключ сейчас? [y/N] " _ans case "${_ans:-N}" in [Yy]*) read -r -p "Введите ключ: " _token [ -z "$_token" ] && { echo "Ключ не введён."; return 1; } echo -n "Проверяю новый ключ... " _claude_test_api "https://api.anthropic.com/v1/messages" "x-api-key: $_token" case "$_CLAUDE_TEST_CODE" in 200) echo -e "\033[0;32mOK\033[0m"; ANTHROPIC_API_KEY="$_token" ;; *) echo -e "\033[0;31mОШИБКА (HTTP $_CLAUDE_TEST_CODE)\033[0m"; return 1 ;; esac ;; *) return 1 ;; esac ;; 403) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;33m[ПРЕДУПРЕЖДЕНИЕ]\033[0m Доступ запрещён (HTTP 403) — возможно, подписка не позволяет." [ -n "$_emsg" ] && echo " $_emsg" echo "" echo -n "Продолжить всё равно? (запросы могут не работать) [y/N] " local _ans; read -r _ans case "${_ans:-N}" in [Yy]*) ;; *) return 1 ;; esac ;; 429) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;33m[ЛИМИТ]\033[0m Лимит запросов исчерпан или пустой баланс (HTTP 429)." [ -n "$_emsg" ] && echo " $_emsg" echo "" echo "Варианты:" echo " [C] Продолжить всё равно (может не работать)" echo " [Q] Выйти" echo -n "Выберите [C/q]: " local _ans; read -r _ans case "${_ans:-C}" in [Cc]) ;; # продолжаем *) return 1 ;; esac ;; 000) echo "" echo -e "\033[0;33m[СЕТЬ]\033[0m Не удалось проверить авторизацию (нет сети?). Продолжаю..." ;; *) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;31m[ОШИБКА]\033[0m API вернул HTTP $_CLAUDE_TEST_CODE" [ -n "$_emsg" ] && echo " $_emsg" echo "" echo -n "Продолжить всё равно? [y/N] " local _ans; read -r _ans case "${_ans:-N}" in [Yy]*) ;; *) return 1 ;; esac ;; esac else # Нет ни OAuth, ни API-ключа — предлагаем выбор echo "" echo -e "\033[1;33mАнтропная авторизация не найдена.\033[0m" echo "" echo "Варианты:" echo " [L] Залогиниться через браузер (OAuth)" echo " [K] Ввести API-ключ вручную" echo " [Q] Выйти" echo -n "Выберите [L/k/q]: " local _ans; read -r _ans case "${_ans:-L}" in [Ll]) echo "" echo "Открываю браузер для входа в Anthropic аккаунт..." claude auth login || { echo "" echo -e "\033[0;31mНе удалось выполнить вход.\033[0m" echo "Попробуйте вручную: claude auth login" return 1 } ;; [Kk]) echo "" read -r -p "Введите Anthropic API ключ (sk-ant-...): " _token [ -z "$_token" ] && { echo "Ключ не введён."; return 1; } _method="apikey" echo -n "Проверяю ключ... " _claude_test_api "https://api.anthropic.com/v1/messages" "x-api-key: $_token" case "$_CLAUDE_TEST_CODE" in 200) echo -e "\033[0;32mOK\033[0m"; export ANTHROPIC_API_KEY="$_token" ;; *) echo -e "\033[0;31mОШИБКА (HTTP $_CLAUDE_TEST_CODE)\033[0m"; return 1 ;; esac ;; *) echo "Отменено."; return 1 ;; esac fi # Явно задаём модели Anthropic, чтобы не подхватился deepseek-chat из settings.json ANTHROPIC_MODEL=claude-sonnet-4-6 \ ANTHROPIC_DEFAULT_OPUS_MODEL=claude-opus-4-8 \ ANTHROPIC_DEFAULT_SONNET_MODEL=claude-sonnet-4-6 \ ANTHROPIC_DEFAULT_HAIKU_MODEL=claude-haiku-4-5-20251001 \ CLAUDE_CODE_SUBAGENT_MODEL=claude-haiku-4-5-20251001 \ claude "$@" } # ── claude_gpt ──────────────────────────────────────────────── claude_gpt() { local proxy_bin="$HOME/.local/bin/claude-code-proxy" if [ ! -f "$proxy_bin" ]; then echo "Ошибка: claude-code-proxy не найден. Перезапустите claude_setup.sh" return 1 fi # Проверяем авторизацию if ! "$proxy_bin" codex auth status &>/dev/null; then echo "" echo "Авторизация ChatGPT не найдена." echo "Сейчас появится ссылка или откроется браузер..." echo "" if ! "$proxy_bin" codex auth login 2>&1; then echo "" echo "Если браузер не открылся, попробуйте device flow:" echo " claude-code-proxy codex auth device" return 1 fi fi # Запускаем прокси в фоне (если ещё не запущен) local proxy_pid="" if ! pgrep -f "claude-code-proxy serve" &>/dev/null; then "$proxy_bin" serve &>/tmp/claude-code-proxy.log & proxy_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; любой другой = прокси слушает _i=$((_i + 1)) done fi # Убиваем прокси при любом выходе из функции (early return или нормальный) trap '[ -n "$proxy_pid" ] && kill "$proxy_pid" 2>/dev/null' RETURN # ── Pre-launch API validation through proxy ── echo -n "Проверка авторизации ChatGPT... " _claude_test_api "http://localhost:18765/v1/messages" "x-api-key: dummy" "gpt-5.4-mini" local _emsg case "$_CLAUDE_TEST_CODE" in 200) echo -e "\033[0;32mOK\033[0m" ;; 401|403) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;31m[ОШИБКА АВТОРИЗАЦИИ]\033[0m Авторизация ChatGPT недействительна (HTTP $_CLAUDE_TEST_CODE)." [ -n "$_emsg" ] && echo " $_emsg" echo "" echo "Очищаю недействительную авторизацию..." "$proxy_bin" codex auth logout 2>/dev/null if _claude_offer_reauth "ChatGPT"; then echo "Запускаю повторную авторизацию..." if ! "$proxy_bin" codex auth login 2>&1; then echo "" echo "Если браузер не открылся, попробуйте device flow:" echo " claude-code-proxy codex auth device" return 1 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 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 ;; 429) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;33m[ЛИМИТ ИСЧЕРПАН]\033[0m Лимит ChatGPT исчерпан." [ -n "$_emsg" ] && echo " $_emsg" return 1 ;; 000) echo "" echo -e "\033[0;33m[СЕТЬ]\033[0m Не удалось проверить ChatGPT прокси (нет сети?). Продолжаю..." ;; *) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;31m[ОШИБКА]\033[0m Прокси вернул HTTP $_CLAUDE_TEST_CODE" [ -n "$_emsg" ] && echo " $_emsg" return 1 ;; esac ANTHROPIC_BASE_URL=http://localhost:18765 \ ANTHROPIC_AUTH_TOKEN=dummy \ ANTHROPIC_MODEL=gpt-5.5 \ ANTHROPIC_DEFAULT_OPUS_MODEL=gpt-5.5 \ ANTHROPIC_DEFAULT_SONNET_MODEL=gpt-5.5 \ ANTHROPIC_DEFAULT_HAIKU_MODEL=gpt-5.4-mini \ CLAUDE_CODE_SUBAGENT_MODEL=gpt-5.4-mini \ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \ claude "$@" if [ -n "$proxy_pid" ]; then kill "$proxy_pid" 2>/dev/null wait "$proxy_pid" 2>/dev/null fi } # ── claude_deepseek ─────────────────────────────────────────── claude_deepseek() { local key_file="$HOME/.config/claude-launcher/deepseek_key" local api_key="" reauth=0 local _emsg # Read stored key if [ -f "$key_file" ]; then api_key=$(cat "$key_file") fi # Validate stored key if present if [ -n "$api_key" ]; then echo -n "Проверка сохранённого DeepSeek ключа... " _claude_test_api "https://api.deepseek.com/anthropic/v1/messages" "Authorization: Bearer $api_key" "deepseek-v4-flash" case "$_CLAUDE_TEST_CODE" in 200) echo -e "\033[0;32mOK\033[0m" ;; 401|403) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;31m[ОШИБКА АВТОРИЗАЦИИ]\033[0m Сохранённый ключ недействителен (HTTP $_CLAUDE_TEST_CODE)." [ -n "$_emsg" ] && echo " $_emsg" echo "Удаляю недействительный ключ..." rm -f "$key_file" api_key="" reauth=1 ;; 429) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;33m[ЛИМИТ ИСЧЕРПАН]\033[0m Баланс DeepSeek пуст или превышен лимит." [ -n "$_emsg" ] && echo " $_emsg" echo " Пополните баланс: https://platform.deepseek.com/top_up" echo -n "Продолжить всё равно? (запросы могут не проходить) [y/N] " local _ans; read -r _ans case "${_ans:-N}" in [Yy]|[Yy][Ee][Ss]) ;; *) return 1 ;; esac ;; 000) echo "" echo -e "\033[0;33m[СЕТЬ]\033[0m Не удалось проверить ключ (нет сети?). Продолжаю..." ;; *) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;31m[ОШИБКА]\033[0m API вернул HTTP $_CLAUDE_TEST_CODE" [ -n "$_emsg" ] && echo " $_emsg" return 1 ;; esac fi # Re-prompt if key was invalidated if [ -z "$api_key" ] && [ "$reauth" -eq 1 ]; then echo "" echo -n "Хотите ввести новый DeepSeek ключ? [Y/n] " local _ans; read -r _ans case "${_ans:-Y}" in [Yy]|[Yy][Ee][Ss]) ;; *) return 1 ;; esac fi # Prompt for key if missing if [ -z "$api_key" ]; then echo "" echo "DeepSeek API ключ не найден." echo "Получить ключ: https://platform.deepseek.com/api_keys" echo "" read -r -p "Введите ваш DeepSeek API ключ: " api_key echo "" if [ -z "$api_key" ]; then echo "Ключ не введён. Выход." return 1 fi echo "Проверяю ключ и баланс..." _claude_test_api "https://api.deepseek.com/anthropic/v1/messages" "Authorization: Bearer $api_key" "deepseek-v4-flash" case "$_CLAUDE_TEST_CODE" in 200) mkdir -p "$(dirname "$key_file")" echo "$api_key" > "$key_file" chmod 600 "$key_file" echo -e "\033[0;32mКлюч действителен, баланс в порядке. Ключ сохранён.\033[0m" ;; 000) echo "Не удалось проверить ключ (нет сети?). Сохраняю без проверки..." mkdir -p "$(dirname "$key_file")" echo "$api_key" > "$key_file" chmod 600 "$key_file" ;; 429) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;31m[ОШИБКА]\033[0m Ключ действителен, но аккаунт заблокирован." [ -n "$_emsg" ] && echo " Причина: $_emsg" echo " Пополните баланс: https://platform.deepseek.com/top_up" echo " Ключ НЕ сохранён — сначала пополните счёт." return 1 ;; 401|403) echo -e "\033[0;31mКлюч недействителен (HTTP $_CLAUDE_TEST_CODE).\033[0m Ключ не сохранён." return 1 ;; *) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") [ -z "$_emsg" ] && _emsg="HTTP $_CLAUDE_TEST_CODE" echo "Ошибка API: $_emsg" echo "Ключ не сохранён." return 1 ;; esac fi ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic \ ANTHROPIC_AUTH_TOKEN="$api_key" \ ANTHROPIC_MODEL=deepseek-v4-pro \ ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-v4-pro \ ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-v4-pro \ ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-v4-flash \ CLAUDE_CODE_SUBAGENT_MODEL=deepseek-v4-flash \ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \ claude "$@" } # ── claude_kimi ───────────────────────────────────────────── claude_kimi() { local key_file="$HOME/.config/claude-launcher/kimi_key" local api_key="" reauth=0 local _emsg # Read stored key if [ -f "$key_file" ]; then api_key=$(cat "$key_file") fi # Validate stored key if present if [ -n "$api_key" ]; then echo -n "Проверка сохранённого Kimi ключа... " _claude_test_api "https://api.moonshot.ai/anthropic/v1/messages" "Authorization: Bearer $api_key" "kimi-k2.6" case "$_CLAUDE_TEST_CODE" in 200) echo -e "\033[0;32mOK\033[0m" ;; 401|403) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;31m[ОШИБКА АВТОРИЗАЦИИ]\033[0m Сохранённый ключ недействителен (HTTP $_CLAUDE_TEST_CODE)." [ -n "$_emsg" ] && echo " $_emsg" echo "Удаляю недействительный ключ..." rm -f "$key_file" api_key="" reauth=1 ;; 429) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;33m[ЛИМИТ ИСЧЕРПАН]\033[0m Баланс Kimi пуст или превышен лимит." [ -n "$_emsg" ] && echo " $_emsg" echo " Пополните баланс: https://platform.moonshot.ai/console/billing" echo -n "Продолжить всё равно? (запросы могут не проходить) [y/N] " local _ans; read -r _ans case "${_ans:-N}" in [Yy]|[Yy][Ee][Ss]) ;; *) return 1 ;; esac ;; 000) echo "" echo -e "\033[0;33m[СЕТЬ]\033[0m Не удалось проверить ключ (нет сети?). Продолжаю..." ;; *) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;31m[ОШИБКА]\033[0m API вернул HTTP $_CLAUDE_TEST_CODE" [ -n "$_emsg" ] && echo " $_emsg" return 1 ;; esac fi # Re-prompt if key was invalidated if [ -z "$api_key" ] && [ "$reauth" -eq 1 ]; then echo "" echo -n "Хотите ввести новый Kimi ключ? [Y/n] " local _ans; read -r _ans case "${_ans:-Y}" in [Yy]|[Yy][Ee][Ss]) ;; *) return 1 ;; esac fi # Prompt for key if missing if [ -z "$api_key" ]; then echo "" echo "Kimi (Moonshot AI) API ключ не найден." echo "Получить ключ: https://platform.moonshot.ai/console/api-keys" echo "" read -r -p "Введите ваш Kimi API ключ (начинается с 'sk-'): " api_key echo "" if [ -z "$api_key" ]; then echo "Ключ не введён. Выход." return 1 fi echo "Проверяю ключ и баланс..." _claude_test_api "https://api.moonshot.ai/anthropic/v1/messages" "Authorization: Bearer $api_key" "kimi-k2.6" case "$_CLAUDE_TEST_CODE" in 200) mkdir -p "$(dirname "$key_file")" echo "$api_key" > "$key_file" chmod 600 "$key_file" echo -e "\033[0;32mКлюч действителен, баланс в порядке. Ключ сохранён.\033[0m" ;; 000) echo "Не удалось проверить ключ (нет сети?). Сохраняю без проверки..." mkdir -p "$(dirname "$key_file")" echo "$api_key" > "$key_file" chmod 600 "$key_file" ;; 429) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;31m[ОШИБКА]\033[0m Ключ действителен, но аккаунт заблокирован." [ -n "$_emsg" ] && echo " Причина: $_emsg" echo " Пополните баланс: https://platform.moonshot.ai/console/billing" echo " Ключ НЕ сохранён — сначала пополните счёт." return 1 ;; 401|403) echo -e "\033[0;31mКлюч недействителен (HTTP $_CLAUDE_TEST_CODE).\033[0m Ключ не сохранён." return 1 ;; *) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") [ -z "$_emsg" ] && _emsg="HTTP $_CLAUDE_TEST_CODE" echo "Ошибка API: $_emsg" echo "Ключ не сохранён." return 1 ;; esac fi ANTHROPIC_BASE_URL=https://api.moonshot.ai/anthropic \ ANTHROPIC_AUTH_TOKEN="$api_key" \ ANTHROPIC_MODEL=kimi-k2.6 \ ANTHROPIC_DEFAULT_OPUS_MODEL=kimi-k2.6 \ ANTHROPIC_DEFAULT_SONNET_MODEL=kimi-k2.6 \ ANTHROPIC_DEFAULT_HAIKU_MODEL=kimi-k2.6 \ CLAUDE_CODE_SUBAGENT_MODEL=kimi-k2.6 \ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \ claude "$@" } # ── claude_gemini ───────────────────────────────────────────── claude_gemini() { local acc_cmd="" command -v antigravity-claude-proxy &>/dev/null && acc_cmd="antigravity-claude-proxy" command -v acc &>/dev/null && acc_cmd="acc" if [ -z "$acc_cmd" ]; then echo "Ошибка: antigravity-claude-proxy не найден. Перезапустите claude_setup.sh" return 1 fi # Запускаем прокси в фоне если не запущен local proxy_pid="" if ! curl -sf http://localhost:8080/health &>/dev/null; then "$acc_cmd" start &>/tmp/antigravity-proxy.log & proxy_pid=$! echo "Запускаю Gemini прокси..." local i=0 while [ $i -lt 15 ]; do sleep 1 curl -sf http://localhost:8080/health &>/dev/null && break i=$((i+1)) done fi # ── Проверка аккаунтов ── local has_auth total_count invalid_count has_auth=$(curl -sf "http://localhost:8080/account-limits" 2>/dev/null || echo "") if [ -n "$has_auth" ]; then total_count=$(echo "$has_auth" | python3 -c " import sys, json try: d = json.load(sys.stdin) print(len(d.get('accounts', []))) except: print(0) " 2>/dev/null || echo "0") invalid_count=$(echo "$has_auth" | python3 -c " import sys, json try: d = json.load(sys.stdin) accounts = d.get('accounts', []) invalid = [a for a in accounts if a.get('isInvalid')] print(len(invalid)) except: print(0) " 2>/dev/null || echo "0") fi if [ -z "$has_auth" ] || [ "$total_count" = "0" ]; then echo "" echo "Google-аккаунт не найден." echo "" echo -e "\033[1;33m⚠️ ВНИМАНИЕ: Используйте ОТДЕЛЬНЫЙ Google-аккаунт!\033[0m" echo " Google может заблокировать аккаунты, использующие этот прокси." echo "" echo "Открываю http://localhost:8080 в браузере..." echo "Перейдите: Accounts → Add Account → войдите через Google." echo "" xdg-open "http://localhost:8080" 2>/dev/null || \ sensible-browser "http://localhost:8080" 2>/dev/null || \ echo "Откройте вручную: http://localhost:8080" echo "Нажмите Enter после завершения авторизации в браузере..." read -r elif [ "$invalid_count" -gt 0 ]; then echo "" echo -e "\033[0;33m[ПРЕДУПРЕЖДЕНИЕ]\033[0m Обнаружены проблемные аккаунты ($invalid_count из $total_count)." echo "Проверьте статус: http://localhost:8080" fi # ── Pre-launch API test through proxy ── echo -n "Проверка доступа к Gemini API... " _claude_test_api "http://localhost:8080/v1/messages" "x-api-key: dummy" "gemini-3-flash-agent" local _emsg case "$_CLAUDE_TEST_CODE" in 200) echo -e "\033[0;32mOK\033[0m" ;; 401|403) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;31m[ОШИБКА АВТОРИЗАЦИИ]\033[0m Gemini аккаунты не авторизованы (HTTP $_CLAUDE_TEST_CODE)." [ -n "$_emsg" ] && echo " $_emsg" echo "Попробуйте переавторизоваться через 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 ;; 429) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;33m[ЛИМИТ ИСЧЕРПАН]\033[0m Все Gemini аккаунты исчерпали лимит." [ -n "$_emsg" ] && echo " $_emsg" echo "Подождите сброса лимитов или добавьте новый аккаунт." 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 ;; 000) echo "" echo -e "\033[0;33m[СЕТЬ]\033[0m Не удалось проверить Gemini прокси (нет сети?). Продолжаю..." ;; *) _emsg=$(_claude_extract_error "$_CLAUDE_TEST_BODY") echo "" echo -e "\033[0;31m[ОШИБКА]\033[0m Прокси вернул HTTP $_CLAUDE_TEST_CODE" [ -n "$_emsg" ] && echo " $_emsg" return 1 ;; esac echo "Запускаю Claude Code с Gemini..." ANTHROPIC_BASE_URL=http://localhost:8080 \ ANTHROPIC_AUTH_TOKEN=dummy \ ANTHROPIC_MODEL=gemini-pro-agent \ ANTHROPIC_DEFAULT_OPUS_MODEL=gemini-pro-agent \ ANTHROPIC_DEFAULT_SONNET_MODEL=gemini-3-flash-agent \ ANTHROPIC_DEFAULT_HAIKU_MODEL=gemini-3.5-flash-low \ CLAUDE_CODE_SUBAGENT_MODEL=gemini-3.5-flash-low \ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 \ claude "$@" if [ -n "$proxy_pid" ]; then kill "$proxy_pid" 2>/dev/null wait "$proxy_pid" 2>/dev/null fi } # === END CLAUDE LAUNCHER === BASHEOF success "Функции добавлены в $BASHRC" # ── 8. Итог ────────────────────────────────────────────────── echo "" echo -e "${GREEN}════════════════════════════════════════════════════${NC}" echo -e "${GREEN} Установка завершена!${NC}" echo -e "${GREEN}════════════════════════════════════════════════════${NC}" echo "" echo "Доступные команды:" echo -e " ${CYAN}claude_anthropic${NC} — оригинальный Claude (Anthropic API)" echo -e " ${CYAN}claude_gpt${NC} — GPT-5.5 (ChatGPT Plus/Pro, браузерная авторизация)" echo -e " ${CYAN}claude_deepseek${NC} — DeepSeek (API ключ сохраняется)" echo -e " ${CYAN}claude_kimi${NC} — Kimi K2.6 (Moonshot AI, API ключ сохраняется)" echo -e " ${CYAN}claude_gemini${NC} — Gemini (Google OAuth через браузер)" echo "" echo "Все команды принимают стандартные флаги:" echo -e " ${CYAN}claude_gpt --dangerously-skip-permissions${NC}" echo "" echo -e "${YELLOW}⚠️ Для Gemini используйте отдельный Google-аккаунт!${NC}" echo "" echo "Управление:" echo -e " DeepSeek ключ: ${CYAN}rm $DEEPSEEK_KEY_FILE${NC} (сбросить)" echo -e " Kimi ключ: ${CYAN}rm $CONFIG_DIR/kimi_key${NC} (сбросить)" echo -e " GPT статус: ${CYAN}claude-code-proxy codex auth status${NC}" echo -e " GPT выйти: ${CYAN}claude-code-proxy codex auth logout${NC}" echo -e " Gemini WebUI: ${CYAN}http://localhost:8080${NC} (когда прокси запущен)" echo "" if [ "$0" != "${BASH_SOURCE[0]}" ]; then return 0 2>/dev/null || exit 0 fi echo -e "${CYAN}Перезапускаю shell для применения функций...${NC}" exec bash echo ""