Files
ai-setup/setup.sh
Виталий Никитенко e6fe58eb37 feat(mimo): add ai-mimo launcher for Xiaomi MiMo V2.6 Pro
ai-mimo runs Claude Code through Xiaomi's Anthropic-compatible API
(Token Plan or pay-as-you-go). The key's endpoint is found by probing
token-plan-sgp, token-plan-ams and api.xiaomimimo.com (a key only works
in its own region) and is saved to mimo_base_url; MIMO_BASE_URL
overrides. Unreachable endpoints are skipped, token-plan-cn is left out
of the probe since it times out from our network, and a 402 on the
pay-as-you-go endpoint explains that a tp- Token Plan key is needed.

Always the smartest model: mimo-v2.6-pro in every slot and for
subagents, --model opus and --effort max on each start, 128000 output
tokens (Claude Code's ceiling), real 1M context window. Measured on
13.10: MiMo ignores output_config.effort (even "bogus" is accepted, no
length trend from low to max) and treats thinking adaptive like
enabled, so reasoning is always on; EFFORT_MAPPING.md records this.

/logout-mimo is registered as a command and handled by the logout hook
(removes mimo_key and mimo_base_url). setup.sh status, statusline brand
color, README, QUICK_START and AGENTS.md list the new launcher.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:43:25 +07:00

292 lines
13 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# Copyright (c) 2026 Виталий Никитенко. MIT License.
# Мастер-скрипт. Запускай от обычного пользователя (sudo попросит сам где нужно).
cd "$(dirname "$0")"
# Платформа: на macOS сетевые функции (ru-bypass/kill switch) неприменимы
# (они построены на Linux ipset/ufw/systemd; Amnezia на маке — клиент с utun).
IS_MAC=0
[ "$(uname -s)" = "Darwin" ] && IS_MAC=1
BLD='\033[1m'
GRN='\033[0;32m'
YEL='\033[0;33m'
GRY='\033[0;37m'
CLR='\033[0m'
mkdir -p "$HOME/.config/ai-setup"
LOG="$HOME/.config/ai-setup/setup.log"
_log() { printf '%s [%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" "$2" >> "$LOG"; }
# Обновление: git pull + перегенерация всех ai-* лаунчеров (пункт меню 7)
do_update() {
local repo_dir
repo_dir="$(pwd)"
echo -e "${BLD}Обновляем репозиторий...${CLR}"
git -C "$repo_dir" pull --ff-only
echo ""
echo -e "${BLD}Перегенерация скриптов...${CLR}"
bash "$repo_dir/scripts/ai-setup.sh"
}
# CLI-режим: ./setup.sh update (или --update) — обновление без меню
case "${1:-}" in
update|--update)
_log "setup" "update (CLI) на $(hostname)"
do_update
exit $?
;;
"")
# без аргументов — интерактивное меню ниже
;;
*)
echo "Неизвестный аргумент: $1"
echo "Использование: ./setup.sh [update] — без аргументов открывается меню"
exit 1
;;
esac
if command -v whiptail >/dev/null 2>&1; then
MENU_ARGS=()
MENU_ARGS+=("1" "AI-инструменты (установить лаунчеры + ключи)")
if [ "$IS_MAC" -eq 0 ]; then
MENU_ARGS+=("2" "Сеть: ru-bypass + kill switch")
fi
MENU_ARGS+=("" "─── Дополнительно ───────────────────────────")
if [ "$IS_MAC" -eq 0 ]; then
MENU_ARGS+=("3" "Отключить kill switch (прямой доступ без VPN)")
MENU_ARGS+=("4" "Включить kill switch (восстановить защиту)")
fi
MENU_ARGS+=("5" "Статус (Amnezia, UFW, AI инструменты, ключи)")
if [ "$IS_MAC" -eq 0 ]; then
MENU_ARGS+=("6" "Проверить сеть (маршрутизация, geo)")
fi
MENU_ARGS+=("7" "Обновить (git pull + перегенерация скриптов)")
MENU_ARGS+=("8" "Android CLI (установить Google Android CLI)")
choice=$(whiptail --title "AI Setup" \
--menu "Выбери действие (стрелки + Enter):" 24 70 10 \
"${MENU_ARGS[@]}" 3>&1 1>&2 2>&3) || exit 0
echo ""
else
echo ""
echo -e "${BLD}=== AI Setup ===${CLR}"
echo ""
echo -e "${YEL}Шаги для новой машины (выполнить по порядку):${CLR}"
echo ""
echo -e " ${BLD}1) AI-инструменты${CLR}"
echo -e " ${GRY}Устанавливает ai-claude, ai-gpt, ai-deepseek, ai-kimi, ai-glm, ai-gemini и др.${CLR}"
echo -e " ${GRY}Запрашивает API-ключи. Запускать один раз.${CLR}"
echo ""
if [ "$IS_MAC" -eq 0 ]; then
echo -e " ${BLD}2) Сеть: ru-bypass + kill switch${CLR}"
echo -e " ${GRY}.ru сайты (ozon, госуслуги) — напрямую с российским IP.${CLR}"
echo -e " ${GRY}*.loc офисные адреса — тоже напрямую.${CLR}"
echo -e " ${GRY}Всё остальное — только через Amnezia (kill switch).${CLR}"
echo -e " ${GRY}Запускать один раз на каждой машине.${CLR}"
echo ""
fi
echo -e "${YEL}Дополнительно (по необходимости):${CLR}"
echo ""
if [ "$IS_MAC" -eq 0 ]; then
echo -e " ${BLD}3) Отключить kill switch${CLR}"
echo -e " ${GRY}Временно — когда нужен прямой доступ без VPN (российский IP).${CLR}"
echo ""
echo -e " ${BLD}4) Включить kill switch${CLR}"
echo -e " ${GRY}Вернуть защиту обратно после отключения.${CLR}"
echo ""
fi
echo -e " ${BLD}5) Статус${CLR}"
echo -e " ${GRY}Amnezia, UFW, сервисы, установленные AI инструменты и ключи.${CLR}"
echo ""
if [ "$IS_MAC" -eq 0 ]; then
echo -e " ${BLD}6) Проверить сеть${CLR}"
echo -e " ${GRY}Тесты маршрутизации: .ru напрямую, остальное через Amnezia.${CLR}"
echo ""
fi
echo -e " ${BLD}7) Обновить${CLR}"
echo -e " ${GRY}git pull + перегенерация всех скриптов в ~/bin (или ~/.local/bin).${CLR}"
echo ""
echo -e " ${BLD}8) Android CLI${CLR}"
echo -e " ${GRY}Установить Google Android CLI для Android-разработки.${CLR}"
echo ""
echo -n "Выбери [1-8] или Enter для выхода: "
read -r choice
echo ""
fi
[ -n "$choice" ] && _log "setup" "Пункт $choice на $(hostname)"
case "$choice" in
1)
bash scripts/ai-setup.sh
;;
2)
if [ "$IS_MAC" -eq 1 ]; then echo "Неприменимо на macOS"; exit 0; fi
echo -e "${GRY}Нужно указать параметры твоей локальной сети:${CLR}"
echo -e "${GRY} GATEWAY — IP домашнего/офисного роутера (через него пойдёт .ru трафик напрямую)${CLR}"
echo -e "${GRY} DEV — сетевой интерфейс (wifi или провод), через который ты подключён к роутеру${CLR}"
echo -e "${GRY} LOCAL_DNS — IP офисного DNS-сервера для разрешения *.loc доменов (необязательно)${CLR}"
echo ""
mkdir -p "$HOME/.config/ai-setup"
cfg_dir="$HOME/.config/ai-setup"
# Показываем существующие профили
existing=$(ls "$cfg_dir"/network_*.conf 2>/dev/null | sed 's|.*/network_||;s|\.conf||' | tr '\n' ' ')
if [ -n "$existing" ]; then
echo -e "Существующие профили: ${BLD}${existing}${CLR}"
echo -e "${GRY}Введи имя профиля (home/office/$(hostname) и т.д.) или Enter для текущего${CLR}"
read -rp "Профиль [$(hostname)]: " chosen_profile
chosen_profile="${chosen_profile:-$(hostname)}"
else
chosen_profile="$(hostname)"
fi
net_conf="$cfg_dir/network_${chosen_profile}.conf"
auto_gw=$(ip route show default 2>/dev/null | awk '/default/ {print $3; exit}')
auto_dev=$(ip route show default 2>/dev/null | awk '/default/ {print $5; exit}')
auto_gw="${auto_gw:-192.168.1.1}"
auto_dev="${auto_dev:-wlp1s0}"
saved_local_dns=""
saved_amn_srv=""
saved_ks_exc=""
if [ -f "$net_conf" ]; then
saved_gw=$(grep '^GATEWAY=' "$net_conf" | cut -d= -f2)
saved_dev=$(grep '^DEV=' "$net_conf" | cut -d= -f2)
saved_local_dns=$(grep '^LOCAL_DNS=' "$net_conf" | cut -d= -f2)
saved_amn_srv=$(grep '^AMNEZIA_SERVER=' "$net_conf" | cut -d= -f2)
saved_ks_exc=$(grep '^KILL_SWITCH_EXCEPTIONS=' "$net_conf" | cut -d= -f2)
auto_gw="${saved_gw:-$auto_gw}"
auto_dev="${saved_dev:-$auto_dev}"
echo -e "Загружены параметры профиля ${BLD}${chosen_profile}${CLR}: GATEWAY=${BLD}${auto_gw}${CLR} DEV=${BLD}${auto_dev}${CLR}"
else
echo -e "Новый профиль ${BLD}${chosen_profile}${CLR}. Определено автоматически: GATEWAY=${BLD}${auto_gw}${CLR} DEV=${BLD}${auto_dev}${CLR}"
fi
echo -e "${GRY}(просто Enter чтобы принять, или введи другое значение)${CLR}"
echo ""
read -rp "GATEWAY (IP роутера) [${auto_gw}]: " gw
read -rp "DEV (интерфейс) [${auto_dev}]: " dev
read -rp "LOCAL_DNS (DNS для *.loc) [${saved_local_dns:-пусто}]: " local_dns
read -rp "AMNEZIA_SERVER (IP/домен сервера Amnezia) [${saved_amn_srv:-пусто}]: " amn_srv
read -e -rp "KILL_SWITCH_EXCEPTIONS (исключения kill switch: IP/домены через пробел): " -i "${saved_ks_exc}" ks_exc
gw="${gw:-$auto_gw}"
dev="${dev:-$auto_dev}"
[ "$local_dns" = "пусто" ] && local_dns=""
local_dns="${local_dns:-$saved_local_dns}"
[ "$amn_srv" = "пусто" ] && amn_srv=""
amn_srv="${amn_srv:-$saved_amn_srv}"
[ "$ks_exc" = "пусто" ] && ks_exc=""
printf 'GATEWAY=%s\nDEV=%s\nLOCAL_DNS=%s\nAMNEZIA_SERVER=%s\nKILL_SWITCH_EXCEPTIONS=%s\n' "$gw" "$dev" "$local_dns" "$amn_srv" "$ks_exc" > "$net_conf"
echo ""
sudo GATEWAY="$gw" DEV="$dev" LOCAL_DNS="$local_dns" AMNEZIA_SERVER="$amn_srv" KILL_SWITCH_EXCEPTIONS="$ks_exc" USER_HOME="$HOME" bash scripts/ru-bypass.sh
;;
3)
if [ "$IS_MAC" -eq 1 ]; then echo "Неприменимо на macOS"; exit 0; fi
echo -e "${YEL}Перед этим выйди из Claude Code — сессия сменит IP.${CLR}"
echo -n "Продолжить? [y/N] "
read -r confirm
[ "$confirm" = "y" ] || [ "$confirm" = "Y" ] || exit 0
sudo USER_HOME="$HOME" bash scripts/ks-off.sh
;;
4)
if [ "$IS_MAC" -eq 1 ]; then echo "Неприменимо на macOS"; exit 0; fi
sudo USER_HOME="$HOME" bash scripts/ks-on.sh
;;
5)
echo -e "${BLD}=== Статус ===${CLR}"
echo ""
if [ "$IS_MAC" -eq 0 ]; then
echo -e "${BLD}Сеть:${CLR}"
if ip link show amn0 &>/dev/null; then
echo -e " ${GRN}✓${CLR} Amnezia (amn0) подключена"
else
echo -e " ${YEL}✗${CLR} Amnezia (amn0) не найдена"
fi
if sudo ufw status 2>/dev/null | grep -qE "активен|active"; then
echo -e " ${GRN}✓${CLR} UFW kill switch активен"
else
echo -e " ${YEL}✗${CLR} UFW выключен"
fi
if systemctl is-active --quiet ru-bypass.service 2>/dev/null || systemctl is-enabled --quiet ru-bypass.service 2>/dev/null; then
echo -e " ${GRN}✓${CLR} ru-bypass.service установлен"
else
echo -e " ${YEL}✗${CLR} ru-bypass.service не установлен (запусти пункт 2)"
fi
if systemctl is-enabled --quiet ru-ipset-restore.service 2>/dev/null; then
echo -e " ${GRN}✓${CLR} ru-ipset-restore.service установлен"
else
echo -e " ${YEL}✗${CLR} ru-ipset-restore.service не установлен (запусти пункт 2)"
fi
ipv6_cnt=$(ip -6 addr show scope global 2>/dev/null | grep -c 'inet6' || true)
if [ "$ipv6_cnt" -eq 0 ]; then
echo -e " ${GRN}✓${CLR} IPv6 отключён (нет утечки)"
else
echo -e " ${YEL}!${CLR} IPv6 активен ($ipv6_cnt адресов) — возможна утечка, запусти пункт 4"
fi
echo ""
else
echo -e "${BLD}Сеть:${CLR}"
echo -e " ${GRY}(ru-bypass/kill switch неприменимы на macOS)${CLR}"
echo ""
fi
echo -e "${BLD}AI инструменты:${CLR}"
for cmd in ai-claude ai-gpt ai-deepseek ai-kimi ai-glm ai-eltex ai-eltex-qwen ai-mimo ai-openrouter ai-opencode ai-gemini android; do
if command -v "$cmd" &>/dev/null; then
echo -e " ${GRN}✓${CLR} $cmd"
else
echo -e " ${YEL}✗${CLR} $cmd"
fi
done
echo ""
echo -e "${BLD}API ключи:${CLR}"
cfg="$HOME/.config/ai-setup"
for f in deepseek_key kimi_key glm_key eltex_key eltex_qwen_key mimo_key openrouter_key opencode_key; do
name="${f/_key/}"
if [ -s "$cfg/$f" ]; then
echo -e " ${GRN}✓${CLR} $name"
else
echo -e " ${YEL}✗${CLR} $name (не задан)"
fi
done
echo ""
echo -e "${BLD}API доступность:${CLR}"
for entry in "Anthropic:api.anthropic.com" "DeepSeek:api.deepseek.com" "OpenAI:api.openai.com" "Kimi:api.kimi.com" "GLM:api.z.ai" "MiMo:api.xiaomimimo.com" "OpenRouter:openrouter.ai" "OpenCode:opencode.ai"; do
_name="${entry%%:*}"
_host="${entry##*:}"
_ms=$(curl -s -o /dev/null -w "%{time_connect}" --max-time 5 "https://$_host" 2>/dev/null)
if [ -n "$_ms" ] && [ "$_ms" != "0.000000" ]; then
echo -e " ${GRN}✓${CLR} $_name: ${_ms}s"
else
echo -e " ${YEL}✗${CLR} $_name: недоступен"
fi
done
echo ""
echo -e "${BLD}Последние события:${CLR}"
if [ -f "$LOG" ]; then
tail -10 "$LOG" | sed 's/^/ /'
else
echo " (лог пуст)"
fi
;;
6)
if [ "$IS_MAC" -eq 1 ]; then echo "Неприменимо на macOS"; exit 0; fi
bash tests/test_network.sh
;;
7)
do_update
;;
8)
bash scripts/ai-setup.sh
;;
"")
exit 0
;;
*)
echo "Неверный выбор."
exit 1
;;
esac