#!/bin/bash # Health-check для голосовой диктовки. Запускать когда «не работает». # Проверяет: Hammerspoon, Accessibility, init.lua, скрипт, модели, Groq, ffmpeg, mic, Fn-key поведение set +e GREEN="\033[32m"; RED="\033[31m"; YELLOW="\033[33m"; CYAN="\033[36m"; RST="\033[0m" ok() { echo -e "${GREEN}✅ $1${RST}"; } fail() { echo -e "${RED}❌ $1${RST}"; ((FAILS++)); } warn() { echo -e "${YELLOW}⚠️ $1${RST}"; } hdr() { echo -e "\n${CYAN}── $1 ──${RST}"; } FAILS=0 hdr "1. Hammerspoon" if pgrep -x Hammerspoon >/dev/null; then ok "Hammerspoon запущен (pid $(pgrep -x Hammerspoon))" else fail "Hammerspoon не запущен → open -a Hammerspoon"; fi if [ -f ~/.hammerspoon/init.lua ]; then ok "init.lua на месте" else fail "~/.hammerspoon/init.lua отсутствует"; fi hdr "2. Accessibility (TCC)" TCC=$(echo " " | sudo -S sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" \ "SELECT auth_value FROM access WHERE service='kTCCServiceAccessibility' AND client='org.hammerspoon.Hammerspoon'" 2>/dev/null) case "$TCC" in 2) ok "Hammerspoon Accessibility: ALLOWED" ;; 0) fail "Hammerspoon Accessibility: DENIED → System Settings → Universal Access → включить" ;; "") fail "Hammerspoon в TCC отсутствует → System Settings → Universal Access → добавить и включить" ;; *) warn "Hammerspoon TCC: $TCC (странное значение)" ;; esac hdr "3. Скрипт диктовки" if [ -x ~/bin/groq-dictate.sh ]; then ok "~/bin/groq-dictate.sh executable" else fail "~/bin/groq-dictate.sh отсутствует или не +x"; fi hdr "4. Зависимости" for cmd in ffmpeg jq curl whisper-cli osascript; do if command -v "$cmd" >/dev/null; then ok "$cmd найден ($(command -v $cmd))" else fail "$cmd не установлен → brew install $cmd"; fi done hdr "5. Модели" if [ -f "$HOME/.cache/whisper-cpp/ggml-tiny-q5_1.bin" ]; then SIZE=$(ls -lh "$HOME/.cache/whisper-cpp/ggml-tiny-q5_1.bin" | awk '{print $5}') ok "whisper-cpp tiny: $SIZE" else warn "локальная модель whisper-cpp отсутствует (fallback будет недоступен)" fi hdr "6. Groq API" HTTP=$(curl -sS -o /tmp/_groq_test.json -w "%{http_code}" --max-time 10 \ -H "Authorization: Bearer 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" \ https://api.groq.com/openai/v1/models 2>/dev/null) case "$HTTP" in 200) ok "Groq API отвечает (HTTP 200)" ;; 401) fail "Groq: 401 — ключ невалиден или просрочен" ;; 403) fail "Groq: 403 — доступ запрещён (возможно, RU IP заблокирован)" ;; 429) warn "Groq: 429 — rate limit, подожди" ;; 000) fail "Groq не отвечает (нет сети?)" ;; *) fail "Groq HTTP $HTTP" ;; esac rm -f /tmp/_groq_test.json hdr "7. Микрофон" DEFAULT_MIC=$(system_profiler SPAudioDataType 2>/dev/null | awk '/Default Input Device: Yes/{found=1} found && /Input Source:/{print $3, $4, $5; exit}') [ -n "$DEFAULT_MIC" ] && ok "Default mic: $DEFAULT_MIC" || warn "Не удалось определить default mic" hdr "8. Fn-key behavior" FN_PREF=$(defaults read com.apple.HIToolbox AppleFnUsageType 2>/dev/null) case "$FN_PREF" in 0) ok "Fn = 'действие не требуется' (правильно)" ;; 1) warn "Fn = 'переключает источник ввода' — конфликт с диктовкой" ;; 2) warn "Fn = 'эмодзи панель' — конфликт" ;; 3) warn "Fn = 'начинает Apple Dictation' — КОНФЛИКТ! Поменяй на 'Не требуется'" ;; *) warn "Fn behavior unknown: $FN_PREF" ;; esac hdr "9. Лог последних попыток" [ -f /tmp/groq-dictate.log ] && tail -10 /tmp/groq-dictate.log || warn "Лог пуст" echo if [ $FAILS -eq 0 ]; then echo -e "${GREEN}═══ Всё в порядке ═══${RST}" exit 0 else echo -e "${RED}═══ Найдено проблем: $FAILS ═══${RST}" exit 1 fi