Сегодня (mmfb / LionART 1C): - projects/mmfb/lionart-1c.md — новый файл: VM 100 на pve LionART (WIN-70M2VEJIKEF, 10.253.1.240, Win Server 2022, 1С+SQL+Effector Saver), SSH-доступ claude/Kl@udeD1ag!2026 заведён, RDP под Администратор + 2FA. - projects/mmfb/proxmox-inventory.md — hostname WIN-70M2VEJIKEF в VM 100. - decisions/2026-05-28-mmfb-effector-saver-locked-admin.md — диагноз цикла 7038 (SCM-пароль разъехался с .\Администратор) + lockout учётки, и пошаговое решение (disable службы → ADSI unlock → LogonUser-проверка → sc.exe config password= → start auto). Накопившийся backlog (без отдельной правки в эту сессию): - decisions/: buzharovo (recon, migration-plan, 1c-licensing), sergey (instagram iPhone fakeip), amneziavpn macOS v1/v2 incompat, benelux compromise 2026-05-20, glavtorg autologon off, omni domain+update. - projects/: benilux README, buzharovo README+server1c, dttb (nextcloud-talk-bot, npm-proxy-hosts, proxmox-inventory, vpn-clients), glavtorg, sergey README, projects/_index. - claude-memory/: benelux, omniroute. - snippets/mac-dictation/groq-dictate.sh. - notes/claude/: ~80 авто-сохранённых транскриптов сессий за май. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
74 lines
3.1 KiB
Bash
Executable File
74 lines
3.1 KiB
Bash
Executable File
#!/bin/bash
|
||
# Toggle dictation: ⌘⇧D / Fn → запись с микрофона; повторное нажатие → Groq Whisper → текст в /tmp/groq-dictate.last
|
||
# Hammerspoon Lua callback вставляет через hs.eventtap.keyStroke({"cmd"},"v") в активное окно.
|
||
# Fallback: если Groq недоступен (нет сети / 429 / 5xx) → локальный whisper-cli (whisper-cpp tiny ru).
|
||
|
||
# Hammerspoon при автозапуске даёт пустой PATH — добавляем явно (Intel + Apple Silicon brew пути)
|
||
export PATH="/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin"
|
||
|
||
PID_FILE="/tmp/groq-dictate.pid"
|
||
WAV_FILE="/tmp/groq-dictate.wav"
|
||
WAV_16K="/tmp/groq-dictate-16k.wav"
|
||
OUT_FILE="/tmp/groq-dictate.last"
|
||
LOG="/tmp/groq-dictate.log"
|
||
GROQ_KEY="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"
|
||
WHISPER_MODEL="$HOME/.cache/whisper-cpp/ggml-tiny-q5_1.bin"
|
||
|
||
log() { echo "[$(date +%H:%M:%S)] $*" >> "$LOG"; }
|
||
notify() { osascript -e "display notification \"$1\" with title \"Groq Dictate\""; }
|
||
|
||
groq_transcribe() {
|
||
local response
|
||
response=$(curl -sS -X POST "https://api.groq.com/openai/v1/audio/transcriptions" \
|
||
-H "Authorization: Bearer $GROQ_KEY" \
|
||
-F "file=@$WAV_FILE" \
|
||
-F "model=whisper-large-v3-turbo" \
|
||
-F "language=ru" \
|
||
-F "response_format=json" \
|
||
--max-time 15 2>/dev/null) || return 1
|
||
local code
|
||
code=$(echo "$response" | jq -r '.error.code // empty' 2>/dev/null)
|
||
[ -n "$code" ] && { log "Groq error: $response"; return 1; }
|
||
echo "$response" | jq -r '.text // empty' 2>/dev/null | sed 's/^ *//;s/ *$//'
|
||
}
|
||
|
||
local_transcribe() {
|
||
[ ! -f "$WHISPER_MODEL" ] && { log "no local model: $WHISPER_MODEL"; return 1; }
|
||
ffmpeg -hide_banner -loglevel error -i "$WAV_FILE" -ar 16000 -ac 1 -y "$WAV_16K" 2>>"$LOG" || return 1
|
||
whisper-cli -m "$WHISPER_MODEL" -l ru -nt -np "$WAV_16K" 2>>"$LOG" | sed 's/^ *//;s/ *$//' | tr -d '\n'
|
||
}
|
||
|
||
if [ -f "$PID_FILE" ]; then
|
||
# === STOP & TRANSCRIBE ===
|
||
PID=$(cat "$PID_FILE")
|
||
log "STOP pid=$PID"
|
||
kill -INT "$PID" 2>/dev/null
|
||
for i in 1 2 3 4 5; do kill -0 "$PID" 2>/dev/null || break; sleep 0.1; done
|
||
rm -f "$PID_FILE"
|
||
|
||
if [ ! -s "$WAV_FILE" ]; then
|
||
log "ERR empty wav"; notify "Запись пустая"; exit 1
|
||
fi
|
||
|
||
log "POST size=$(stat -f%z "$WAV_FILE")B"
|
||
TEXT=$(groq_transcribe)
|
||
if [ -z "$TEXT" ]; then
|
||
log "Groq fail → trying local whisper-cli"
|
||
notify "☁️→💻 Groq не отвечает, локальная модель"
|
||
TEXT=$(local_transcribe)
|
||
[ -z "$TEXT" ] && { log "Local also empty"; notify "Не распознано"; exit 1; }
|
||
log "LOCAL DONE: $TEXT"
|
||
else
|
||
log "GROQ DONE: $TEXT"
|
||
fi
|
||
|
||
printf '%s' "$TEXT" > "$OUT_FILE"
|
||
else
|
||
# === START RECORDING ===
|
||
rm -f "$WAV_FILE" "$WAV_16K" "$OUT_FILE"
|
||
log "START → $WAV_FILE"
|
||
ffmpeg -hide_banner -loglevel error -f avfoundation -i ":0" -ar 16000 -ac 1 -y "$WAV_FILE" >/dev/null 2>>"$LOG" &
|
||
echo $! > "$PID_FILE"
|
||
notify "🎙️ Говори"
|
||
fi
|