Backend (runtime.uc, agent_api.uc, tachyon CLI):
- Enrich AI Doctor LLM context with Watchdog state (OOM, streaks, active repairs) and last 15 critical log lines
- Support multi-fix parsing (quick_fixes array) and comma-separated FIX tags
- Add persistent history saving to /tmp/ai_doctor_last.json and ai_doctor_last endpoint
- Expand quick fix codes from 9 to 13 (added clear_dns_cache, update_subscriptions, reset_firewall, restart_network)
- Implement apply_quick_fix CLI command and REST API endpoint
Frontend (fe-app-tachyon, LuCI main.js):
- Add AI Doctor action button in Diagnostics tab
- Add shell methods aiDoctor, aiDoctorLast, applyQuickFix
- Render AI Doctor modal with Multi-Fix buttons and 'Fix All' option
- Integrate toast notifications and state updates
Backend (runtime.uc):
- query_llm() accepts model_override (5th param), falls back to provider default
- ai_doctor() reads ai_doctor_model from UCI and passes to query_llm
Frontend (renderAdvancedSettings.ts):
- aiDoctorModel state field (type / init / load / save)
- Model dropdown per provider with sensible defaults:
openai: gpt-4o-mini(default)/gpt-4o/gpt-4.1-mini/gpt-4.1/o4-mini
anthropic: claude-3-5-haiku(default)/claude-3-5-sonnet/claude-3-7-sonnet
deepseek: deepseek-chat(default)/deepseek-reasoner
custom: free-text input
- Switching provider resets model to '' (use new provider default)
Legacy LuCI (settings.js):
- ai_doctor_model Value field with placeholder '(provider default)'
- При получении 'Another component action is already running' или сетевого
RPC-сбоя спиннер на нажатой кнопке больше не сбрасывается сразу.
- Добавлено повторное связывание (fallback lookups) с 800ms задержкой, чтобы
бэкенд успевал прописать запущенный job в state до снятия индикатора.
- При отслеживании запущенного задания теперь учитываются любые фоновые
акшны для того же компонента.
- В waitComponentActionJob добавлен минимальный порог ожидания (10с)
перед срабатыванием fallback проверки версии Tachyon. При same-release
обновлениях (одинаковый номер версии, но новый SHA) совпадение версии
больше не приводит к мгновенному (за 1с) снятию спиннера loading, пока
бэкенд на самом деле только скачивает и ставит пакет.
- Расширен список транзиентных RPC ошибок (isTransientRpcError) для
надежного перешивания перезапуска uhttpd/rpcd/ubus во время переустановки.
- Увеличен COMPONENT_ACTION_TRANSIENT_RPC_GRACE_MS до 60с.
Lock contention ('Another component action is already running') is an
expected transient state — user clicked a button while a previous action
was still in flight. Showing [error] toast confuses users into thinking
something broke. Filter it from the log watcher notification pipeline.
Also suppress DNS retry debug lines (issue #24 retry logs) from showing
as error toasts.
Added tests for both suppressed patterns.
- В query_llm (runtime.uc) добавлена интеграция с Anthropic Messages API (https://api.anthropic.com/v1/messages) с заголовком x-api-key
- В настройки Settings и Dashboard добавлен выпадающий выбор провайдера 'Anthropic (Claude API)'
- Добавлен русский перевод для опций AI Doctor
- Добавлена опция agent_api_token (AI Agent REST API Bearer Token) в CBI-форму настроек settings.js
- Сохраняет токен через стандартную кнопку Save & Apply в LuCI
- kill_matching_command и цикл зачистки fd/1000 прокси в package.uc теперь пропускют процессы ucode (action.uc / updates.uc) и их предков, предотвращая выбивание воркера обновлений по SIGKILL во время prerm
- isTransientRpcError теперь учитывает 'failed to execute' в качестве транзиентной ошибки RPC при перезапуске rpcd / служб во время обновления
- get_proxy_args() теперь поддерживает опцию UCI bot_proxy_section:
если sing-box запущен и секция указана, переключает GLOBAL outbound через REST API
- Исправлен краш 'left-hand side expression is null' при обработке
callback_query без поля from (например channel_post callbacks)
- В LuCI settings.js добавлен ListValue 'bot_proxy_section' с выпадающим
списком активных секций (паттерн аналогичен download_via_proxy_section)
- Локализация: ru/tachyon.po и templates/tachyon.pot синхронизированы
- Пересборка main.js через npm run build
opkg считает пакет уже установленным при равенстве версий, поэтому
пересборка под тем же тегом молча не применялась: задание завершалось
успехом, билд оставался старым. Добавлен --force-reinstall в
pkg_install_files_command (opkg) и в install.sh; apk не тронут, он
всегда пишет переданный файл.
Детект пересборки больше не зависит только от SHA, которого нет в
релизах старше f974e4a0: добавлен отпечаток билда по published_at и
метаданным ipk-ассета, решение вынесено в tachyon_build_differs с
префиксным сравнением SHA разной длины. Отпечаток установленного
билда пишется в state-файл и попадает в extra.
Telegram уведомляет о пересборках: check_notified_updates пропускает
outdated_same_release, дедуп идёт по версии и SHA, текст показывает
короткие SHA вместо пары одинаковых версий.
Кэш проверки обновлений сохраняет current_sha из результата действия
вместо безусловного latest, иначе неудачная установка выглядела как
"актуально".
Фронт пересобран из TS, ручная правка main.js снята. Добавлен
describeSameReleaseBuild с фолбэк-текстом, когда SHA недоступен.
UI (section.js):
- showUrlTestSettingsModal и showPriorityLevelSettingsModal: предзагрузка
кэша outbound-имён до открытия модала — CBINamedSection.render() не
вызывает section.load(), из-за чего DynamicList рендерился без данных
- renderStackedJsonSettingsModal: вызов map.load() перед map.render()
LuCI ACL (luci-app-tachyon.json):
- Добавлены пути /tmp/run/tachyon/... наряду с /var/run/tachyon/...
На OpenWrt /var — симлинк на /tmp; rpcd резолвит симлинки перед
проверкой ACL, поэтому /var/run/... никогда не давал прав чтения
и fs.read() возвращал PermissionError
Postinstall (50_luci-tachyon):
- Добавлен перезапуск rpcd после установки пакета, чтобы новые ACL
вступали в силу без ручного logout/login в LuCI
Telegram-бот (telegram.uc):
- handle_toggle_mac(): блокировка/разблокировка устройств теперь
через правила firewall (fw4), а не через UCI blocked_macs
- /cb команда: разрешение токенизированных коллбэков для oversized
inline-клавиатур (лимит Telegram 64 байта на callback_data)
- /qh_start, /qh_end: интерактивная настройка часов тихого режима
- in_quiet_hours() и daily report: исправлен localtime(time())
вместо clock() — clock() возвращает [сек, мксек], а не tm-структуру
- Уведомления об обновлениях: current_version вместо installed_version
- Описание команды /restart исправлено
Backend (api.uc, state.uc):
- http_probe(): выделена функция с проверкой HTTP-статуса
(curl выходит 0 даже при 4xx/5xx — теперь проверяем код явно)
- run_speedtest(): учёт curl exit status
- Math.random() заменён на clock()[1] (Math недоступен без import)
- current_year(): убрано +1900 — localtime().year уже полный год
Дашборд:
- Добавлены карточки статуса Zapret, Zapret2, ByeDpi на панель мониторинга
- Показывают статус (работает/остановлен), количество перезапусков, предупреждения
- Баг: API возвращает boolean а не число — исправлено сравнение
Секция Hosts:
- Перенесены опции hosts из глобальных настроек в отдельный тип секции (action=hosts)
- Опции: пользовательские DNS записи, удалённые списки хостов, автообновление, интервал
- Миграция: автоматический перенос глобальных настроек hosts в секцию
- Вкладка «Условия» скрыта для секций типа Hosts (hosts записи применяются глобально)
- Возвращена кнопка «Import Hosts File» с модальным окном загрузки из файла
- Убраны неиспользуемые опции из settings.js (~280 строк)
Локализация:
- Переведены 6 новых строк для превью импорта hosts
Replaced custom colored rows/badges with standard CBI table layout:
cbi-section-table, cbi-input-checkbox, cbi-input-text,
cbi-button-action, cbi-button-remove. No custom colors or borders.
Completely replaced DynamicList with a custom renderWidget that shows
each URL as a row with: checkbox toggle, URL label, ON/OFF badge, and
delete button. Disabled URLs are stored in hosts_list_disabled UCI list.
Backend filters them out before downloading. URLs stay in settings
when disabled — they just don't get applied.
- Backend: get_hosts_urls() now reads hosts_list_disabled list and
filters out disabled URLs before downloading.
- Backend: hosts_list_status() now returns total and disabled counts.
- Frontend: Added hidden hosts_list_disabled DynamicList option.
- Frontend: Overrode renderWidget on hosts_list_urls to prepend a
toggle switch (ON/OFF) next to each URL. Toggle syncs with
hosts_list_disabled via uci.set().
- watchdog: add ai_heal_rpcd() - monitors rpcd file descriptor count,
restarts rpcd when FD count exceeds 512 (threshold of 1024 limit).
Prevents LuCI from showing all services as 'stopped' due to FD exhaustion
when rpcd can no longer fork to execute /usr/bin/tachyon commands.
Runs on every watchdog health audit cycle (~15s).
- watchdog: move safe_reload_firewall() definition before ai_heal_nftables()
and ai_heal_qos() to fix 'left-hand side is not a function' error.
ucode does not hoist function declarations.
- updates: show Update button for outdated_same_release status (same version
number but different commit SHA). Previously the button was hidden when
current_version == latest_version even if SHAs differed.
- action.uc: killall logread перед restart_tachyon_after_successful_change,
предотвращает FD-каскад при reinstall/update (orphan logread pipe FD)
- action.uc: current_sha/latest_sha в ответе check_update для Tachyon
- updates.uc: SHA пробрасывается через кэш проверки обновлений
- fe/types.ts: outdated_same_release добавлен в union статусов,
поля current_sha/latest_sha в ComponentActionResult,
AI Watchdog поля в ConfigSection (pre-existing TS2339 fix)
- fe/store.service.ts: outdated_same_release и SHA в состоянии updatesChecks
- fe/initController.ts: рендер SHA hash-badge для outdated_same_release,
setCheckResult принимает SHA, applyCachedCheckResults/handleCheckResult обновлены
- fe/styles.ts: .sha-info — monospace badge для отображения коммит-хешей
Backend:
- constants.uc: add TACHYON_COMMIT_SHA constant (placeholder __COMPILED_COMMIT_SHA__)
- build.sh: capture git rev-parse HEAD and sed-replace commit SHA placeholder
- Makefile: same commit SHA sed replacement for OpenWrt builds
- action.uc: modify check_tachyon() to compare installed commit SHA with
target_commitish from GitHub release, new status 'outdated_same_release'
- action.uc: reuse release_json for both version and commit SHA extraction
- updater.uc: add release_commit_sha helper to extract target_commitish
- updates.uc: extend cacheable status to include outdated_same_release
Frontend:
- main.js: handle outdated_same_release status in renderComponentCard
- main.js: include outdated_same_release in shouldShowInstallAfterCheck
- main.js: include outdated_same_release in applyCachedCheckResults
i18n:
- Add 'Update is available for current release' and 'Update is available
for current release:' to .pot and .ru.po files
78 tests pass.