- Добавлен _openrouter_balance() в ai-api-helpers.sh
- Statusline теперь показывает баланс для openrouter лаунчера
- Обновлены дефолтные модели в ai-openrouter (Grok 4.20 как opus и т.д.)
- Улучшена изоляция конфигов для deepseek/kimi/openrouter
Co-Authored-By: Claude <noreply@anthropic.com>
Лимиты привязаны к аккаунту, но кэш rate_limits ключевался только по
model_id. При /switch-account (та же модель) проценты смешивались между
аккаунтами. Добавил account в ключ кэша — у каждого аккаунта свои проценты.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Корень багов с потерей токенов: claude auth status читает
oauthAccount.emailAddress из ~/.claude.json, который рассинхронизирован
с реальным токеном в .credentials.json. Из-за этого хуки определяли
текущий аккаунт неверно и сохраняли активный токен под чужим именем,
затирая credentials другого аккаунта.
- account-email.sh (новый): определяет email по OAuth-токену —
локальный матчинг с accounts/, затем API /api/oauth/profile
- switch-account-hook.sh: current выводится из токена, а не из
auth status/хрупкого файла current — порча файлов исключена.
Перезапуск не нужен: на Linux Claude Code перечитывает
.credentials.json на лету
- add-account-hook.sh: email нового аккаунта тоже через хелпер
- skill add-account: убрано упоминание перезапуска
- ai-setup.sh: деплой account-email.sh (секция 6.7.05)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- новый хук add-account-hook.sh: сохраняет текущий аккаунт по реальному
email (claude auth status), запускает OAuth-логин в фоне и после успеха
сам сохраняет новый аккаунт в ~/.claude/accounts + делает его current
- switch-account-hook.sh: активный аккаунт определяется через
claude auth status, а не через хрупкий файл current - защита от порчи
сохранённых credentials при рассинхроне токена
- скилл add-account: краткая инструкция после срабатывания хука
- ai-setup.sh: деплой add-account-hook + регистрация в UserPromptSubmit
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Раньше все ai-* лаунчеры делили один ~/.claude и общий settings.json, из-за
чего кастомная модель (openai/gpt-5.5) из ai-openrouter протекала в пикер
ai-claude. Теперь каждый сторонний провайдер изолирован в своём
CLAUDE_CONFIG_DIR (~/.config/ai-setup/cfg/<launcher>) - свои settings.json и
.claude.json, ноль протечек. ai-claude остаётся на ~/.claude (нативный логин).
Пикеры /model приведены к требуемому виду:
- ai-deepseek: только DeepSeek V4 Pro (opus) и DeepSeek V4 Flash (haiku),
дефолт Pro; через availableModels + ANTHROPIC_DEFAULT_*_MODEL_NAME
- ai-kimi: только Kimi K2.6 (opus)
- ai-claude: только нативные модели Claude
Общие skills и CLAUDE.md шарятся симлинком из ~/.claude.
Persistence effort - гибрид:
- low/medium/high/xhigh живут нативно в settings.json лаунчера, /effort
внутри сессии работает свободно и уровень сохраняется
- max нельзя сохранить в settings.json (session-only), поэтому он
восстанавливается через CLAUDE_CODE_EFFORT_LEVEL; в такой max-сессии
/effort залочен (ограничение Claude Code), выход - AI_EFFORT=<lvl> ai-*
Текущий уровень ловит статусбар в ~/.cache/ai-setup/effort_<launcher>.
Удалён устаревший effort-save-hook (заменён нативным persistence + гибридом),
почищен из ~/.claude/settings.json и осиротевший кэш model_*.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- _brand_color: цвет имени модели по AI_LAUNCHER
deepseek=синий(69), claude=оранжевый(173), kimi=голубой(81),
openrouter=фиолетовый(135), остальные=кремовый(223)
- _restore_model / _restore_model_str: сохранение и восстановление
model_id в кэше лаунчера (~/.cache/ai-setup/model_<launcher>)
- effort-save-hook также сохраняет model_id при завершении сессии
- ai-claude/ai-openrouter используют восстановленную модель при старте
Co-Authored-By: Claude <noreply@anthropic.com>
- _restore_effort: каждый лаунчер читает свой effort из
~/.cache/ai-setup/effort_<launcher> и записывает в settings.json
- effort-save-hook.sh: сохраняет effortLevel из settings.json в кэш
при завершении сессии (через Claude Code hooks)
- Все лаунчеры (claude/deepseek/kimi/openrouter) экспортируют
AI_LAUNCHER для идентификации в statusline и хуках
- _deepseek_balance: мультивалютный вывод (USD + CNY с символами $ и ¥)
- Дефолтные effort: claude=xhigh, deepseek/kimi/openrouter=high
Co-Authored-By: Claude <noreply@anthropic.com>
- switch-account-hook.sh: сохранять обновлённые OAuth-токены обратно в файл
аккаунта перед переключением — предотвращает 401 после обратного свитча
- statusline-command.sh:
- effort.level из stdin (сессия), а не из общего settings.json —
ai-claude и ai-deepseek больше не пересекаются
- автоопределение аккаунта по access-токену в .credentials.json
- фолбек: если аккаунт не найден — запрос к haiku напрямую через
Anthropic (в обход DeepSeek) для определения email
- показ аккаунта только для claude-* моделей
- statusline: [account] выводится между [branch] и model, тот же оранжевый цвет
- switch-account-hook: exit 0 вместо exit 2, чтобы Claude ответил и перерисовал статусбар
- switch-account skill: инструкция отвечать одним символом ✓
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude Code не предоставляет API для обновления статусной строки в idle.
SIGWINCH, TIOCSWINSZ, запись в TTY, write в stdin - ничего не работает.
Статусная строка обновится при следующем LLM запросе автоматически.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SIGWINCH и TIOCSWINSZ не заставляют Claude Code обновить статусную строку.
Запускаем statusline-command.sh с кешем и пишем результат напрямую
в TTY claude через \0337 (save cursor) / \033[999B / \033[2K / \0338.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SIGWINCH напрямую игнорируется. TIOCSWINSZ на TTY claude посылает
SIGWINCH через kernel к foreground process group.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SIGWINCH до exit 2 игнорируется - claude ещё рисует блокировку.
Запускаем sleep+kill в фоне, они живут после завершения хука.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Посылаем SIGWINCH родительскому процессу claude после переключения -
это заставляет TUI перерисовать UI включая статусную строку.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Документация врала: реальный UserPromptSubmit шлёт поле prompt, а не user_prompt.
Хук получал пустую строку и выходил с exit 0, пропуская блокировку.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Без скилла Claude Code выдаёт "Unknown command" до запуска хука.
Скилл нужен как регистрация команды, но тело пустое - хук перехватывает
через exit 2 (stderr) до вызова LLM. Откат изменения в ai-setup.sh
которое скрывало скилл от деплоя.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Скилл switch-account загружался в LLM раньше чем срабатывал UserPromptSubmit
хук - из-за этого каждый /switch-account съедал токены. Теперь ai-setup.sh
пропускает "hook-backed skills" при деплое в ~/.claude/skills/, хук перехватывает
команду до LLM и возвращает decision:block.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- UserPromptSubmit хук перехватывает /switch-account до LLM, переключает
credentials по кругу и возвращает decision:block - нулевой расход токенов
- Статусная строка: effort и имя аккаунта в квадратных скобках [high·work]
- ai-setup.sh деплоит хук switch-account-hook.sh и прописывает его в settings.json
- Скилл switch-account оставлен как fallback-документация для setup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- sed удаляет всё от маркера до конца файла, а не только строку с маркером
- заодно чистит старые дубли без маркера (eltex.loc, eltex-co.ru)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- BIN_DIR теперь определяется по наличию ~/bin в PATH (для Mint, где нет .bashrc)
- source путей в генерируемых скриптах заменены на self-referential (через BASH_SOURCE)
- agy_bin подменяется post-generation sed при нестандартном BIN_DIR
- add_path_to_rc() формирует PATH динамически с учётом BIN_DIR
- при миграции на ~/bin старые скрипты из ~/.local/bin удаляются
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
IPv6-трафик обходит UFW (который работает только с IPv4) — kill switch
не защищает от утечек по IPv6.
Изменения:
- ru-bypass.sh: молча отключает IPv6 (sysctl + /etc/sysctl.d) при
каждом запуске, если есть глобальные IPv6-адреса
- ks-on.sh: тоже отключает IPv6, но без интерактивного вопроса
(раньше спрашивал «Отключить IPv6? [Y/n]»)
- ai-setup.sh (direct mode): не восстанавливает IPv6, если
UFW kill switch активен (раньше безусловно включал обратно,
из-за чего после каждого запуска setup.sh IPv6 снова утекал)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Три корневые проблемы и их исправления:
1. MANAGE_BUILTINS=no в /etc/default/ufw — цепочка ufw-before-output
не вызывалась из OUTPUT, правила before.rules не применялись.
→ автофикс no→yes + прямые правила iptables (не зависят от UFW).
2. UFW-правила создавались однократно по маркеру — при смене DEV
(wlp1s0→enp4s0) продолжали ссылаться на старый интерфейс.
→ теперь при каждом запуске удаляются и пересоздаются с актуальным DEV.
3. DNS через VPN для локальных доменов возвращал внешние IP вместо
внутренних (RFC1918) — трафик уходил в VPN и не достигал серверов.
→ /etc/hosts с фиксированными IP для *.eltex.loc, mattermost, elph.
→ замена dig +short на getent hosts (уважает /etc/hosts).
Добавлены built-in KILL_SWITCH_EXCEPTIONS:
mattermost.eltex-co.ru elph.eltex-co.ru 10.80.0.15
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ru-bypass.sh теперь сохраняет параметры (GATEWAY, DEV, LOCAL_DNS, AMNEZIA_SERVER,
KILL_SWITCH_EXCEPTIONS) в /etc/ru-bypass.conf при каждом запуске, и читает их
оттуда при старте из systemd/NM dispatcher (без env). ENV-переменные имеют приоритет.
setup.sh: read -e -i для KS_EXCEPTIONS - редактирование значения инлайн вместо
показа текущего значения в скобках.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ключ при записи KILL_SWITCH_EXCEPTIONS, а при чтении искался KS_EXCEPTIONS —
исключения kill switch не загружались при повторном запуске setup.sh.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Все лаунчеры (ai-claude, ai-deepseek, ai-kimi, ai-openrouter): промпт
пишется во временный файл через --system-prompt-file вместо аргумента
командной строки. Решает E2BIG при промптах > 128KB (MAX_ARG_STRLEN)
из проектов с большими .md файлами.
- statusline: кешируем rate_limits по model_id (раздельные файлы для
claude/kimi/openrouter). При старте сессии показываем данные из кеша
+ ctx:0%. Убирает пустую статусную строку до первого запроса.
- settings.json: добавляем SessionStart хук при setup, триггерит
вызов statusLine при открытии сессии.
- ai-claude: --model sonnet зафиксирован, убрали exec для корректной
работы trap (cleanup временного файла).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DeepSeek: накопленная стоимость сессии по DeepSeek-ценам (V4: $0.55/$2.19, V3: $0.27/$1.10)
Anthropic/Kimi/прочие: рейт-лимиты (5h, 7d) без долларов
Все: заполнение контекста (ctx%)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- AMNEZIA_SERVER — только IP/домены серверов Amnezia (для поднятия VPN)
- KILL_SWITCH_EXCEPTIONS — дополнительные исключения (git, etc.)
- Обе переменные поддерживают IP и домены (DNS-резолвинг)
- setup.sh: раздельные промпты в меню
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- ru-bypass.sh: добавлен ufw default deny outgoing (раньше нигде не выполнялся)
- ru-bypass.sh: добавлен ufw allow out on amn0 (разрешён трафик через VPN)
- ru-bypass.sh: поддержка AMNEZIA_SERVER — IP добавляется в ipset и маршруты
- ks-on.sh: default deny + allow amn0 при восстановлении kill switch
- setup.sh: меню запрашивает/сохраняет/передаёт AMNEZIA_SERVER
- test_network.sh: DEV читается из конфига вместо жёсткого wl[pi]
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Прямой curl до ipinfo.io блокируется UFW (не-.ru трафик via wlp1s0
запрещён kill switch-ем — это штатное поведение). Теперь при недоступности
прямого IP тест проверяет маршрут ya.ru: ожидает DEV_DIRECT, а не amn0.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
setup.sh: если whiptail доступен — показывает интерактивное меню
со стрелками (стандартный пакет newt, обычно уже установлен).
Если whiptail не найден — прежнее plain-text меню без изменений.
case-блок единый для обоих путей.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- setup.sh: функция _log(), вызов при каждом пункте меню
- scripts/ru-bypass.sh, ks-on.sh, ks-off.sh: пишут события в лог через USER_HOME
- setup.sh item 5: показывает tail -10 лога в «Последние события»
- USER_HOME передаётся через sudo env в ks-on/ks-off/ru-bypass
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
setup.sh item 2: показывает существующие профили, предлагает ввести имя.
Профиль сохраняется как network_<name>.conf (вместо фиксированного hostname).
Позволяет держать home/office конфиги на одной машине и переключаться явно.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Добавлен блок «API доступность» с curl time_connect до api.anthropic.com,
api.deepseek.com, api.openai.com, api.kimi.com, openrouter.ai.
Показывает время соединения или «недоступен» при timeout.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
setup.sh: добавлен пункт 7 в меню и case-ветка:
git pull --ff-only + bash scripts/ai-setup.sh для обновления
всех лаунчеров в ~/.local/bin без ручного вмешательства.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
_handle_api_response и _handle_openai_api_response теперь имеют явный case
для 5xx: предупреждают что сервер временно недоступен и продолжают (_API_RET=0).
Раньше 5xx попадал в wildcard → _API_RET=$code (non-zero) → exit 1.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Добавлен блок «7. Geo: внешние IP» — curl через прямой интерфейс
и через дефолт (VPN). Показывает оба IP и проверяет что они разные.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- scripts/ks-on.sh: перед включением UFW проверяет глобальные IPv6 адреса,
предлагает отключить IPv6 через sysctl если они есть
- setup.sh item 5: показывает статус IPv6 (отключён / активен с предупреждением)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ru-bypass.sh теперь при первом запуске устанавливает ru-bypass.timer
(OnCalendar=daily, Persistent=true). Timer запускает ru-bypass.service
раз в сутки и обновляет ipset + маршруты без ручного вмешательства.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- setup.sh item 2: добавлен вопрос LOCAL_DNS (офисный DNS-сервер для *.loc)
сохраняется в network_<hostname>.conf, передаётся в ru-bypass.sh через env
- scripts/ru-bypass.sh: добавлена переменная LOCAL_DNS, после RFC1918 маршрутов
настраивает resolvectl dns/domain ~loc на интерфейсе DEV
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
При повторном запуске пункта 2 параметры загружаются из файла — не нужно
вводить снова. Сохраняется отдельно для каждой машины по hostname.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
При выборе пункта 2 скрипт сам парсит маршрут по умолчанию и предлагает
найденные значения — пользователь просто жмёт Enter для подтверждения.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>