Makefile was missing the install rule for servicecheck_profiles.json — the community service probe profiles were never copied to /usr/share/tachyon/ on the router, so Service Health Check had zero targets to probe and showed an empty modal.
- Agent REST API (agent_api.uc): Added GET /openapi.json returning OpenAPI 3.0.3 spec for ChatGPT Custom GPTs, N8N, Dify, and LLM Agents
- AI Doctor (runtime.uc): Added compress_log_snippet() for deduplicating repeated log entries and ai_doctor_lang support (ru/en prompt selection)
- Telegram Bot (telegram.uc): Added /ai_doctor and /fix commands with Inline Keyboards for instant quick-fix execution
- Settings (renderAdvancedSettings.ts, settings.js): Added AI Doctor Language selector (ru/en)
- Documentation: Updated README.md and README.en.md with 2026 AI features
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)'
- В query_llm (runtime.uc) добавлена интеграция с Anthropic Messages API (https://api.anthropic.com/v1/messages) с заголовком x-api-key
- В настройки Settings и Dashboard добавлен выпадающий выбор провайдера 'Anthropic (Claude API)'
- Добавлен русский перевод для опций AI Doctor
Добавлена проверка 15b в run_doctor_checks():
- Итерирует все включённые секции через uci_core.section_objects()
- Для каждого community_list проверяет существование .srs-файла
в TMP_RULESET_FOLDER (/tmp/sing-box/rulesets/)
- При наличии пропущенных файлов запускает tachyon list_update и
перепроверяет результат (FIXED / остались отсутствующие)
- Правильно использует section_objects() вместо get_all() который
всегда возвращает null при вызове без section_name
The dashboard showed a sing-box version that never changed. build_system_info()
preferred /etc/tachyon/sing-box-version for every "extended" variant, and that
state file is written by the component action alone — a sing-box installed via
install.sh, opkg or by hand leaves it pinned at whatever was recorded last. The
UI then reported the stale version indefinitely, and the update badge compared
the release against it rather than against the binary actually on disk.
The state file only exists for variants whose binary cannot be executed to print
a version: extended-compressed is a self-extracting stub, and lx needs a runtime
that may be absent. Plain extended answers `sing-box version` fine — singbox
/runtime.uc:259 and subscription/cache.uc:1482 both gate on the compressed
variants only. Covering plain extended was a regression from 46ef97fe.
Both call sites now share sing_box_resolved_version(), which also falls through
to the live probe when the state file is missing or empty instead of reporting
"unknown".
Verified on a router: with the state file still holding 1.13.16-extended-2.6.2,
system-info now reports the installed 1.13.18-extended-2.6.3.
На роутере со 128 МБ несколько мест читали в память вывод, размер которого
ничем не ограничен. Распаковка gzip подписки шла через пайп с чтением целиком:
скачивание curl ограничено 15 МБ, но коэффициент сжатия base64-текста высокий,
так что пик доходил до десятков мегабайт. Оба вызывающих места передают файл и
ждут файл, поэтому распакованный поток теперь уходит редиректом сразу в
выходной файл, а успех определяется по коду возврата и ненулевому размеру.
Таблица nftables содержит сеты с десятками тысяч элементов из списков, и
`nft list table` печатает их все. Проба контроллера делала это каждый тик,
хотя ищет только имена цепочек и правил; четыре проверки доктора и вывод
чужих меток из полного рулесета — то же самое. Везде добавлен grep на
источнике, отбирающий только те строки, которые код потом и разбирает.
Два просмотра логов читали кольцевой буфер syslog целиком, чтобы показать
несколько сотен строк; теперь количество строк ограничено на стороне logread.
Пустых catch в кодовой базе около полусотни, и они не одинаковые. Большинство
выражает намерение: fs.unlink бросает на ENOENT, а вызывающий говорит «пусть
этого файла не будет», и отсутствующий файл это условие уже выполняет; разбор
json в цикле пропускает мусорную строку и берёт следующую. Такие места теперь
несут пояснение, почему пустое тело здесь и есть правильный ответ.
Семь мест к этому классу не относились: там сбой означал потерю состояния, а
дальше код шёл так, будто всё получилось.
Watchdog писал через пустой catch файл лока перезапуска прокси, шкалу
GOMEMLIMIT после OOM, бэкап конфига и метку keepalive. Не лёгший лок означает,
что следующий тик перезапустит прокси заново, а счётчик rate-limit об этом не
узнает; не записанная метка keepalive заставляет init.d считать watchdog мёртвым
и перезапускать его. Все четыре теперь идут через write_state_file, который
логирует отказ. Метка пишется на каждом тике uloop, поэтому безусловная строка
залила бы syslog именно при переполненной файловой системе — там, где лог нужнее
всего; вместо этого каждый путь сообщает о первом отказе и молчит до следующего
успеха, что заодно делает видимым восстановление.
Контроллер разбирал /etc/sing-box/config.json ради портов http и tproxy и при
неудаче молча брал 4534 и 4530. Watchdog после этого опрашивает не тот порт,
вечно считает прокси нерабочим и лечит его по кругу — при том, что в логе нет
ничего, что связало бы это с конфигом. Разбор вынесен в общую функцию, которая
сообщает о непарсящемся конфиге один раз на процесс.
Telegram молча терял карту callback-токенов: токен уже вшит в клавиатуру к
моменту записи, поэтому провал отдаёт пользователю кнопку, которая на нажатие
ответит «кнопка устарела». Обновление списков молча теряло маркер отложенного
применения nft — списки обновлены на диске и до nftables не доехали никогда,
а строкой выше в лог написано, что маркер сохраняется. urltest молча выкидывал
битое регулярное выражение из конфига пользователя, отчего группа тихо
подхватывала меньше outbound-ов, чем сказано в конфиге.
tests/heal_escalation.sh проверял порядок строк по литералу fs.writefile для
лока и обновлён под новый вызов.
Критические исправления:
- safe_proxy_restart: удаление lock-файла после фонового перезапуска
с защитой от stale lock (>300 сек)
- write_json_file: атомарная запись через tmp+mv вместо прямой
- write_reload_state: атомарная запись через tmp+mv
- state_add_list: отклонение значений с пробелами
Исправления безопасности:
- killall logread заменён на pkill -f 'logread -f'
- SIGTERM→SIGKILL для Telegram и sing-box при остановке
- Recover OOM scale: инкремент +0.05 каждые 10 минут
- nfqueue и byedpi: trap для корректного завершения дочерних процессов
- nfqueue: валидация стратегий и откат при ошибке запуска
- nfqueue: flush mangle_output перед добавлением правил
- Исправлен парсинг exit code в 14 файлах (status & 127)
Улучшения стабильности:
- init.d: trap для start_service/stop_service
- watchdog: signal traps для legacy fallback loop
- lifecycle: логирование ошибок при остановке компонентов
- singbox/priority: SIGTERM→SIGKILL при остановке
- telegram: exponential backoff (1x→300s) при ошибках API
- telegram: process_updates возвращает boolean для отслеживания успеха
- watchdog: кэш proxy port для уменьшения чтения конфига sing-box
- singbox/generator: упрощён atomic_write_json_file
- packages.uc: переупорядочивание apk_version (лёгкие запросы первыми)
- dns/apply: проверка PID sing-box перед настройкой dnsmasq
- server/service: cleanup_orphaned_server_certs перед вызовом
1. Добавлен встроенный установщик Tor через LuCI-попап с поддержкой opkg и apk.
2. Маршрутизация скрытых сетей (.onion / .i2p) интегрирована на уровень backend (route.uc, generator.uc).
3. Добавлена прозрачная поддержка форка sing-box lx: UI и генераторы теперь корректно включают 'extended' фичи для lx.
4. Проведен полный аудит и восстановление отсутствующих переводов в fe-app-tachyon и luci-app-tachyon.