From be5f41a52e0c9a7e85e0250fc663c125bb3537c2 Mon Sep 17 00:00:00 2001 From: Dushnilin Date: Tue, 11 Aug 2026 16:10:31 +0300 Subject: [PATCH] feat(ai-stack-2026): add OpenAPI 3.0.3 schema, Telegram /ai_doctor & /fix integration, AI language selector, and log deduplication - 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 --- README.en.md | 14 +- README.md | 14 +- .../partials/renderAdvancedSettings.ts | 45 +++++ .../resources/view/tachyon/settings.js | 12 ++ tachyon/files/usr/lib/diagnostics/runtime.uc | 108 +++++++++--- tachyon/files/usr/lib/service/agent_api.uc | 161 ++++++++++++++++++ 6 files changed, 330 insertions(+), 24 deletions(-) diff --git a/README.en.md b/README.en.md index 8fb51bcd..04c67d55 100644 --- a/README.en.md +++ b/README.en.md @@ -43,8 +43,20 @@ A feature-rich control center right inside your messenger: * **Device Access Control**: View active DHCP clients and block/unblock internet access by MAC address. * **Network Diagnostics**: Trigger connection tests directly from Telegram. +### πŸ€– AI Doctor & HTTP REST Agent API (2026 AI Stack) +* **Tachyon AI Doctor (v2)**: Advanced AI diagnostic engine supporting modern LLMs (**OpenAI**, **Anthropic Claude**, **DeepSeek**, and custom OpenAI-compatible servers/OpenRouter/Ollama). + * **Contextual Intelligence**: Feeds the LLM real-time service status, Watchdog failure history (OOM events, error streaks), and compressed system logs. + * **Multi-Fix Execution Chains**: Generates multi-action repair sequences with dedicated UI buttons per fix or a single "Fix All" action. + * **13 Built-in Quick Fix Codes**: Automated repairs for sing-box, nftables, dnsmasq, resolv.conf, DNS cache clearing, subscription refreshing, and network restarts. + * **Language Modes**: Configurable diagnosis output language (`ru` / `en`). +* **πŸ€– Interactive Telegram Commands (`/ai_doctor` & `/fix`)**: + * Trigger AI diagnostics directly from Telegram using `/ai_doctor` with interactive Inline Keyboards for instant quick-fix execution. +* **🌐 HTTP REST Agent API & OpenAPI 3.0 (Swagger)**: + * Full-featured API at `/cgi-bin/tachyon-agent/` with granular READ/WRITE permission controls secured by a Bearer token (`agent_api_token`). + * Native **OpenAPI 3.0.3** spec (`GET /cgi-bin/tachyon-agent/openapi.json`) and OpenAI Function Calling schema (`GET /tools`) for seamless integration with **ChatGPT Custom GPTs**, N8N, Dify, Flowise, and autonomous LLM agents (Cursor, AutoGPT, Claude Code). + ### 🩺 System Self-Healing β€” Tachyon Doctor -An intelligent diagnostic and repair engine available via CLI (`tachyon doctor`) or WebUI: +An intelligent local diagnostic and repair engine available via CLI (`tachyon doctor`) or WebUI: * Scans all core services (`sing-box`, `zapret`, `zapret2`, `byedpi`, `dnsmasq`, `watchdog`). * Validates `nftables` chains and local DNS resolution health. * **Auto-Repair**: Automatically rebuilds broken firewall rules and restarts crashed services without requiring a full router reboot. diff --git a/README.md b/README.md index f540e287..60145d51 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,20 @@ * **Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ устройствами LAN**: ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€ Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… DHCP-ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΎΠ² ΠΈ Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠ°/Ρ€Π°Π·Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠ° доступа Π² ΠΈΠ½Ρ‚Π΅Ρ€Π½Π΅Ρ‚ ΠΏΠΎ MAC-адрСсам. * **Диагностика сСти**: Запуск тСстов доступности прямо ΠΈΠ· Ρ‡Π°Ρ‚Π°. +### πŸ€– ИИ-Π”ΠΎΠΊΡ‚ΠΎΡ€ ΠΈ HTTP REST Agent API (2026 AI Stack) +* **Tachyon AI Doctor (v2)**: Π˜Π½Ρ‚Π΅Π»Π»Π΅ΠΊΡ‚ΡƒΠ°Π»ΡŒΠ½Ρ‹ΠΉ диагностичСский ΠΌΠΎΠ΄ΡƒΠ»ΡŒ с ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΎΠΉ соврСмСнных LLM (**OpenAI**, **Anthropic Claude**, **DeepSeek** ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΡ… OpenAI-совмСстимых сСрвСров/OpenRouter/Ollama). + * **ΠšΠΎΠ½Ρ‚Π΅ΠΊΡΡ‚Π½Ρ‹ΠΉ Π°Π½Π°Π»ΠΈΠ·**: ΠŸΠ΅Ρ€Π΅Π΄Π°Ρ‘Ρ‚ Π² модСль Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ снимки слуТб, Π½ΠΎ ΠΈ события Watchdog (OOM, сСрии ошибок) ΠΈ послСдниС сТатыС Π»ΠΎΠ³ΠΈ Ρ€ΠΎΡƒΡ‚Π΅Ρ€Π°. + * **ΠœΠ½ΠΎΠ³ΠΎΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π½Ρ‹Π΅ Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠΈ фиксов (Multi-Fix)**: Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ Ρ†Π΅ΠΏΠΎΡ‡ΠΊΡƒ автоматичСских исправлСний с Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒΡŽ Π²Ρ‹Π±ΠΎΡ€Π° ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎΠ³ΠΎ фикса ΠΈΠ»ΠΈ ΠΎΠ΄Π½ΠΎΠΉ ΠΊΠ½ΠΎΠΏΠΊΠΎΠΉ Β«Π˜ΡΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ всё». + * **13 встроСнных Quick Fix ΠΊΠΎΠ΄ΠΎΠ²**: АвтоматичСский Ρ€Π΅ΠΌΠΎΠ½Ρ‚ sing-box, nftables, dnsmasq, resolv.conf, очистка кэша DNS, ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ подписок ΠΈ пСрСзапуск сСтСвых слуТб. + * **Π―Π·Ρ‹ΠΊΠΎΠ²Ρ‹Π΅ Ρ€Π΅ΠΆΠΈΠΌΡ‹**: ДинамичСский Π²Ρ‹Π±ΠΎΡ€ языка Π²Π΅Ρ€Π΄ΠΈΠΊΡ‚Π° (`ru` / `en`). +* **πŸ€– Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ Ρ‡Π΅Ρ€Π΅Π· Telegram-Π±ΠΎΡ‚ (`/ai_doctor` & `/fix`)**: + * Запуск AI-диагностики прямо ΠΈΠ· мСссСндТСра ΠΎΠ΄Π½ΠΎΠΉ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ `/ai_doctor` с Π²Ρ‹Π²Π΅Π΄Π΅Π½ΠΈΠ΅ΠΌ ΠΈΠ½Ρ‚Π΅Ρ€Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… Inline-ΠΊΠ½ΠΎΠΏΠΎΠΊ для ΠΌΠΎΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ примСнСния Π°Π²Ρ‚ΠΎ-исправлСний. +* **🌐 HTTP REST Agent API & OpenAPI 3.0 (Swagger)**: + * ΠŸΠΎΠ»Π½ΠΎΡ†Π΅Π½Π½Ρ‹ΠΉ API ΠΏΠΎ адрСсу `/cgi-bin/tachyon-agent/` с Ρ€Π°Π·Π΄Π΅Π»Π΅Π½ΠΈΠ΅ΠΌ READ/WRITE эндпоинтов ΠΈ Π·Π°Ρ‰ΠΈΡ‚Ρ‹ Bearer-Ρ‚ΠΎΠΊΠ΅Π½ΠΎΠΌ (`agent_api_token`). + * БпСцификация **OpenAPI 3.0.3** (`GET /cgi-bin/tachyon-agent/openapi.json`) ΠΈ схСма инструмСнтов (`GET /tools`) для подсоСдинСния ΠΊ **ChatGPT Custom GPTs**, N8N, Dify, Flowise ΠΈ Π°Π²Ρ‚ΠΎΠ½ΠΎΠΌΠ½Ρ‹ΠΌ ИИ-Π°Π³Π΅Π½Ρ‚Π°ΠΌ (Cursor, AutoGPT, Claude Code). + ### 🩺 БистСмный Π°Π²Ρ‚ΠΎΡ€Π΅ΠΌΠΎΠ½Ρ‚ β€” Tachyon Doctor -ВстроСнный ΠΌΠΎΠ΄ΡƒΠ»ΡŒ ΠΈΠ½Ρ‚Π΅Π»Π»Π΅ΠΊΡ‚ΡƒΠ°Π»ΡŒΠ½ΠΎΠΉ диагностики ΠΈ самовосстановлСния (`tachyon doctor` ΠΈΠ»ΠΈ ΠΈΠ· WebUI): +ВстроСнный ΠΌΠΎΠ΄ΡƒΠ»ΡŒ локальной диагностики ΠΈ самовосстановлСния (`tachyon doctor` ΠΈΠ»ΠΈ ΠΈΠ· WebUI): * Π‘ΠΊΠ°Π½ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ состояния всСх ΠΊΠ»ΡŽΡ‡Π΅Π²Ρ‹Ρ… слуТб (`sing-box`, `zapret`, `zapret2`, `byedpi`, `dnsmasq`, `watchdog`). * Валидация ΠΏΡ€Π°Π²ΠΈΠ» Ρ„Π°ΠΉΡ€Π²ΠΎΠ»Π° `nftables` ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Ρ€Π΅Π·ΠΎΠ»Π²ΠΈΠ½Π³Π° DNS Ρ‡Π΅Ρ€Π΅Π· `localhost`. * **АвтоматичСскоС исправлСниС**: Π‘Π°ΠΌΠΎΡΡ‚ΠΎΡΡ‚Π΅Π»ΡŒΠ½ΠΎ устраняСт сбои (пСрСсоздаСт Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠΈ `nftables`, пСрСзапускаСт ΡƒΠΏΠ°Π²ΡˆΠΈΠ΅ сСрвисы) Π±Π΅Π· ΠΏΠΎΠ»Π½ΠΎΠΉ ΠΏΠ΅Ρ€Π΅Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ устройства. diff --git a/fe-app-tachyon/src/tachyon/tabs/dashboard/partials/renderAdvancedSettings.ts b/fe-app-tachyon/src/tachyon/tabs/dashboard/partials/renderAdvancedSettings.ts index aac2eb42..57827525 100644 --- a/fe-app-tachyon/src/tachyon/tabs/dashboard/partials/renderAdvancedSettings.ts +++ b/fe-app-tachyon/src/tachyon/tabs/dashboard/partials/renderAdvancedSettings.ts @@ -18,6 +18,7 @@ interface AdvancedSettingsState { enableAiDoctor: boolean; aiDoctorProvider: string; aiDoctorModel: string; + aiDoctorLang: string; aiDoctorApiKey: string; aiDoctorCustomUrl: string; aiWatchdog: { @@ -54,6 +55,7 @@ let _state: AdvancedSettingsState = { enableAiDoctor: false, aiDoctorProvider: 'openai', aiDoctorModel: '', + aiDoctorLang: 'ru', aiDoctorApiKey: '', aiDoctorCustomUrl: '', aiWatchdog: { @@ -153,6 +155,9 @@ export async function loadAdvancedSettingsState() { aiDoctorModel: ((settingsSec as unknown as Record) ?.ai_doctor_model as string) || '', + aiDoctorLang: + ((settingsSec as unknown as Record) + ?.ai_doctor_lang as string) || 'ru', aiDoctorApiKey: ((settingsSec as unknown as Record) ?.ai_doctor_api_key as string) || '', @@ -311,6 +316,10 @@ async function saveAiWatchdogSettings() { 'set', `${TACHYON_UCI_PACKAGE}.settings.ai_doctor_model=${_state.aiDoctorModel}`, ], + [ + 'set', + `${TACHYON_UCI_PACKAGE}.settings.ai_doctor_lang=${_state.aiDoctorLang}`, + ], [ 'set', `${TACHYON_UCI_PACKAGE}.settings.ai_doctor_api_key=${_state.aiDoctorApiKey}`, @@ -826,6 +835,42 @@ function renderAiWatchdogSection(state: AdvancedSettingsState) { }, }), ]), + E('div', { class: 'tachyon_adv__row' }, [ + E( + 'label', + { class: 'tachyon_adv__label' }, + _('AI Doctor Language'), + ), + E( + 'select', + { + class: 'cbi-input-select', + onchange: (e: Event) => { + const val = (e.target as HTMLSelectElement).value; + _state = { ..._state, aiDoctorLang: val }; + rerender(); + }, + }, + [ + E( + 'option', + { + value: 'ru', + selected: state.aiDoctorLang === 'ru', + }, + 'Русский (Russian)', + ), + E( + 'option', + { + value: 'en', + selected: state.aiDoctorLang === 'en', + }, + 'English', + ), + ], + ), + ]), E('div', { class: 'tachyon_adv__row' }, [ E('label', { class: 'tachyon_adv__label' }, _('AI API Key')), E('input', { diff --git a/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/settings.js b/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/settings.js index 6be7b302..40452e45 100644 --- a/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/settings.js +++ b/luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/settings.js @@ -1282,6 +1282,18 @@ function createSettingsContent(section, capabilities) { o.rmempty = true; o.placeholder = _("(provider default)"); + o = section.taboption( + "ai", + form.ListValue, + "ai_doctor_lang", + _("AI Doctor Language"), + _("Language for AI Doctor diagnosis responses"), + ); + o.depends("enable_ai_doctor", "1"); + o.value("ru", "Русский (Russian)"); + o.value("en", "English"); + o.default = "ru"; + o = section.taboption( "ai", diff --git a/tachyon/files/usr/lib/diagnostics/runtime.uc b/tachyon/files/usr/lib/diagnostics/runtime.uc index b245990c..58228cfe 100644 --- a/tachyon/files/usr/lib/diagnostics/runtime.uc +++ b/tachyon/files/usr/lib/diagnostics/runtime.uc @@ -2941,6 +2941,43 @@ function diagnose_json() { return 0; } +function compress_log_snippet(raw_snippet) { + if (!raw_snippet || raw_snippet == "") return "No recent system errors logged."; + let lines = split(raw_snippet, "\n"); + let compressed = []; + let prev_line = ""; + let repeat_cnt = 1; + + for (let i = 0; i < length(lines); i++) { + let line = trim(as_string(lines[i])); + if (line == "") continue; + let normalized = replace(line, /^[A-Z][a-z]{2}\s+\d+\s+\d+:\d+:\d+\s+[^\s]+\s+/, ""); + normalized = replace(normalized, /^\d{4}-\d{2}-\d{2}\s+\d+:\d+:\d+\s+/, ""); + + if (normalized == prev_line) { + repeat_cnt++; + } else { + if (prev_line != "") { + if (repeat_cnt > 1) { + push(compressed, sprintf("%s [repeated %dx]", lines[i-1], repeat_cnt)); + } else { + push(compressed, lines[i-1]); + } + } + prev_line = normalized; + repeat_cnt = 1; + } + } + if (prev_line != "" && length(lines) > 0) { + if (repeat_cnt > 1) { + push(compressed, sprintf("%s [repeated %dx]", lines[length(lines)-1], repeat_cnt)); + } else { + push(compressed, lines[length(lines)-1]); + } + } + return join("\n", compressed); +} + function apply_quick_fix(codes_str) { if (!codes_str || codes_str == "") { print(sprintf("%J\n", { success: false, error: "No fix code provided" })); @@ -3083,7 +3120,8 @@ function ai_doctor() { } } - let log_snippet = trim(command_output("logread | grep -iE 'tachyon|sing-box|dnsmasq|oom|error|fatal' | tail -n 15 2>/dev/null")) || "No recent system errors logged."; + let raw_log_snippet = trim(command_output("logread | grep -iE 'tachyon|sing-box|dnsmasq|oom|error|fatal' | tail -n 25 2>/dev/null")) || "No recent system errors logged."; + let log_snippet = compress_log_snippet(raw_log_snippet); let sys_context = sprintf( "Tachyon Doctor Report:\n%s\n\n" + @@ -3098,27 +3136,53 @@ function ai_doctor() { watchdog_info, log_snippet ); - let prompt = sprintf( - "Π’Ρ‹ β€” ИИ-ассистСнт \"Tachyon AI Doctor\" для сСрвиса ΠΎΠ±Ρ…ΠΎΠ΄Π° Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΎΠΊ Π½Π° OpenWrt.\n" + - "ΠŸΡ€ΠΎΠ°Π½Π°Π»ΠΈΠ·ΠΈΡ€ΡƒΠΉΡ‚Π΅ диагностичСский ΠΎΡ‚Ρ‡Π΅Ρ‚ ΠΈ Π»ΠΎΠ³ΠΈ Π½ΠΈΠΆΠ΅.\n\n%s\n\n" + - "Π‘Ρ„ΠΎΡ€ΠΌΡƒΠ»ΠΈΡ€ΡƒΠΉΡ‚Π΅ ΠΊΡ€Π°Ρ‚ΠΊΠΈΠΉ Π΄ΠΈΠ°Π³Π½ΠΎΠ· (Π½Π° русском, максимум 3-4 ΠΏΡƒΠ½ΠΊΡ‚Π°).\n" + - "Если Π°Π²Ρ‚ΠΎ-исправлСниС Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, ΡƒΠΊΠ°ΠΆΠΈΡ‚Π΅ Π² самом ΠΊΠΎΠ½Ρ†Π΅ ΠΎΡ‚Π²Π΅Ρ‚Π° строчку:\n" + - "FIX: ΠΊΠΎΠ΄1, ΠΊΠΎΠ΄2\n\n" + - "ДоступныС ΠΊΠΎΠ΄Ρ‹ быстрого исправлСния:\n" + - "- start_singbox (sing-box ΡƒΠΏΠ°Π» ΠΈΠ»ΠΈ остановлСн)\n" + - "- rebuild_rules (nftables ΠΈΠ»ΠΈ ip rule ΠΏΡ€Π°Π²ΠΈΠ»Π° Π½Π°Ρ€ΡƒΡˆΠ΅Π½Ρ‹)\n" + - "- fix_dnsmasq (ΠΊΠΎΠ½Ρ„ΠΈΠ³ ΠΈΠ»ΠΈ сСрвис dnsmasq Π½Π΅ ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅Ρ‚)\n" + - "- fix_resolv_symlink (resolv.conf ΠΏΠΎΠ²Ρ€Π΅ΠΆΠ΄Ρ‘Π½)\n" + - "- start_watchdog (watchdog остановлСн)\n" + - "- restart_singbox_dns (sing-box DNS Π½Π΅ ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅Ρ‚)\n" + - "- fix_uci_config (ΠΊΠΎΠ½Ρ„ΠΈΠ³ Tachyon ΠΏΠΎΠ²Ρ€Π΅ΠΆΠ΄Ρ‘Π½)\n" + - "- fix_wan_interface (WAN интСрфСйс Π½Π΅ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚)\n" + - "- fix_gateway (шлюз отсутствуСт)\n" + - "- clear_dns_cache (ΠΎΡ‡ΠΈΡΡ‚ΠΈΡ‚ΡŒ кэш DNS ΠΈ ΠΏΠ΅Ρ€Π΅Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ dnsmasq)\n" + - "- update_subscriptions (ΠΏΡ€ΠΈΠ½ΡƒΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΠΎΠ±Π½ΠΎΠ²ΠΈΡ‚ΡŒ прокси подписки)\n" + - "- reset_firewall (ΠΏΠ΅Ρ€Π΅Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ Ρ„Π°ΠΉΡ€Π²ΠΎΠ» Ρ€ΠΎΡƒΡ‚Π΅Ρ€Π°)\n" + - "- restart_network (ΠΏΠ΅Ρ€Π΅Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ сСтСвой стСк)\n\n" + - "Если Π°Π²Ρ‚ΠΎ-исправлСниС Π½Π΅ ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΠΌΠΎ, Π½Π΅ ΠΏΠΈΡˆΠΈΡ‚Π΅ Ρ‚Π΅Π³ FIX.", sys_context); + let lang = lc(trim(cfg.ai_doctor_lang || "ru")); + let prompt = ""; + if (lang == "en") { + prompt = sprintf( + "You are \"Tachyon AI Doctor\", an AI assistant for anti-censorship and proxy services on OpenWrt.\n" + + "Analyze the diagnostic report and system logs below.\n\n%s\n\n" + + "Formulate a concise diagnosis (in English, max 3-4 bullet points).\n" + + "If automatic quick fix is possible, include at the very end of your response:\n" + + "FIX: code1, code2\n\n" + + "Available quick fix codes:\n" + + "- start_singbox (sing-box process is stopped or missing)\n" + + "- rebuild_rules (nftables or ip rules damaged)\n" + + "- fix_dnsmasq (dnsmasq service not responding)\n" + + "- fix_resolv_symlink (resolv.conf symlink broken)\n" + + "- start_watchdog (watchdog daemon stopped)\n" + + "- restart_singbox_dns (sing-box DNS failed)\n" + + "- fix_uci_config (Tachyon UCI config corrupted)\n" + + "- fix_wan_interface (WAN interface down)\n" + + "- fix_gateway (default gateway missing)\n" + + "- clear_dns_cache (clear DNS cache & restart dnsmasq)\n" + + "- update_subscriptions (force update proxy subscriptions)\n" + + "- reset_firewall (restart router firewall)\n" + + "- restart_network (restart network service)\n\n" + + "If no quick fix applies, do NOT output any FIX tag.", sys_context); + } else { + prompt = sprintf( + "Π’Ρ‹ β€” ИИ-ассистСнт \"Tachyon AI Doctor\" для сСрвиса ΠΎΠ±Ρ…ΠΎΠ΄Π° Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΎΠΊ Π½Π° OpenWrt.\n" + + "ΠŸΡ€ΠΎΠ°Π½Π°Π»ΠΈΠ·ΠΈΡ€ΡƒΠΉΡ‚Π΅ диагностичСский ΠΎΡ‚Ρ‡Π΅Ρ‚ ΠΈ Π»ΠΎΠ³ΠΈ Π½ΠΈΠΆΠ΅.\n\n%s\n\n" + + "Π‘Ρ„ΠΎΡ€ΠΌΡƒΠ»ΠΈΡ€ΡƒΠΉΡ‚Π΅ ΠΊΡ€Π°Ρ‚ΠΊΠΈΠΉ Π΄ΠΈΠ°Π³Π½ΠΎΠ· (Π½Π° русском, максимум 3-4 ΠΏΡƒΠ½ΠΊΡ‚Π°).\n" + + "Если Π°Π²Ρ‚ΠΎ-исправлСниС Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, ΡƒΠΊΠ°ΠΆΠΈΡ‚Π΅ Π² самом ΠΊΠΎΠ½Ρ†Π΅ ΠΎΡ‚Π²Π΅Ρ‚Π° строчку:\n" + + "FIX: ΠΊΠΎΠ΄1, ΠΊΠΎΠ΄2\n\n" + + "ДоступныС ΠΊΠΎΠ΄Ρ‹ быстрого исправлСния:\n" + + "- start_singbox (sing-box ΡƒΠΏΠ°Π» ΠΈΠ»ΠΈ остановлСн)\n" + + "- rebuild_rules (nftables ΠΈΠ»ΠΈ ip rule ΠΏΡ€Π°Π²ΠΈΠ»Π° Π½Π°Ρ€ΡƒΡˆΠ΅Π½Ρ‹)\n" + + "- fix_dnsmasq (ΠΊΠΎΠ½Ρ„ΠΈΠ³ ΠΈΠ»ΠΈ сСрвис dnsmasq Π½Π΅ ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅Ρ‚)\n" + + "- fix_resolv_symlink (resolv.conf ΠΏΠΎΠ²Ρ€Π΅ΠΆΠ΄Ρ‘Π½)\n" + + "- start_watchdog (watchdog остановлСн)\n" + + "- restart_singbox_dns (sing-box DNS Π½Π΅ ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅Ρ‚)\n" + + "- fix_uci_config (ΠΊΠΎΠ½Ρ„ΠΈΠ³ Tachyon ΠΏΠΎΠ²Ρ€Π΅ΠΆΠ΄Ρ‘Π½)\n" + + "- fix_wan_interface (WAN интСрфСйс Π½Π΅ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚)\n" + + "- fix_gateway (шлюз отсутствуСт)\n" + + "- clear_dns_cache (ΠΎΡ‡ΠΈΡΡ‚ΠΈΡ‚ΡŒ кэш DNS ΠΈ ΠΏΠ΅Ρ€Π΅Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ dnsmasq)\n" + + "- update_subscriptions (ΠΏΡ€ΠΈΠ½ΡƒΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΠΎΠ±Π½ΠΎΠ²ΠΈΡ‚ΡŒ прокси подписки)\n" + + "- reset_firewall (ΠΏΠ΅Ρ€Π΅Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ Ρ„Π°ΠΉΡ€Π²ΠΎΠ» Ρ€ΠΎΡƒΡ‚Π΅Ρ€Π°)\n" + + "- restart_network (ΠΏΠ΅Ρ€Π΅Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ сСтСвой стСк)\n\n" + + "Если Π°Π²Ρ‚ΠΎ-исправлСниС Π½Π΅ ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΠΌΠΎ, Π½Π΅ ΠΏΠΈΡˆΠΈΡ‚Π΅ Ρ‚Π΅Π³ FIX.", sys_context); + } let provider = cfg.ai_doctor_provider || "openai"; let api_key = cfg.ai_doctor_api_key || ""; diff --git a/tachyon/files/usr/lib/service/agent_api.uc b/tachyon/files/usr/lib/service/agent_api.uc index e18e5a5f..81e428c3 100644 --- a/tachyon/files/usr/lib/service/agent_api.uc +++ b/tachyon/files/usr/lib/service/agent_api.uc @@ -446,6 +446,165 @@ function handle_domain_add(body) { ok({ message: "Domain '" + domain + "' added to section '" + section + "'" }); } +function handle_openapi() { + let spec = { + openapi: "3.0.3", + info: { + title: "Tachyon Router AI Agent API", + description: "OpenAPI 3.0 REST API for managing Tachyon anti-censorship orchestration & AI Doctor on OpenWrt routers. Supports ChatGPT Custom GPTs, N8N, Dify, and LLM Agents.", + version: "1.0.0" + }, + servers: [ + { + url: "/cgi-bin/tachyon-agent", + description: "Tachyon Router Agent API Gateway" + } + ], + paths: { + "/health": { + get: { + summary: "System health check", + operationId: "getHealth", + responses: { "200": { description: "Health status" } } + } + }, + "/snapshot": { + get: { + summary: "Full system state snapshot for LLM context", + operationId: "getSnapshot", + responses: { "200": { description: "Complete system snapshot" } } + } + }, + "/diagnose": { + get: { + summary: "Run system diagnostics and auto-repair", + operationId: "getDiagnose", + responses: { "200": { description: "Diagnostic report" } } + } + }, + "/logs": { + get: { + summary: "Get recent system log entries", + operationId: "getLogs", + responses: { "200": { description: "System log lines" } } + } + }, + "/config": { + get: { + summary: "Get UCI configuration (secrets masked)", + operationId: "getConfig", + responses: { "200": { description: "UCI settings" } } + } + }, + "/tools": { + get: { + summary: "Get tool definitions in OpenAI Function Calling format", + operationId: "getTools", + responses: { "200": { description: "Tool schemas" } } + } + }, + "/ai-doctor/last": { + get: { + summary: "Get last AI Doctor diagnosis report", + operationId: "getAiDoctorLast", + responses: { "200": { description: "AI Doctor history report" } } + } + }, + "/heal": { + post: { + summary: "Trigger autonomous repair cycle", + operationId: "postHeal", + security: [{ BearerAuth: [] }], + responses: { "200": { description: "Repair initiated" } } + } + }, + "/ai-doctor/fix": { + post: { + summary: "Apply AI Doctor quick fix codes", + operationId: "postAiDoctorFix", + security: [{ BearerAuth: [] }], + requestBody: { + required: true, + content: { + "application/json": { + schema: { + type: "object", + properties: { + fix: { type: "string", example: "clear_dns_cache,start_singbox" } + } + } + } + } + }, + responses: { "200": { description: "Quick fix results" } } + } + }, + "/restart": { + post: { + summary: "Restart system service", + operationId: "postRestart", + security: [{ BearerAuth: [] }], + requestBody: { + required: true, + content: { + "application/json": { + schema: { + type: "object", + properties: { + service: { type: "string", enum: ["singbox", "tachyon", "dnsmasq", "watchdog"] } + } + } + } + } + }, + responses: { "200": { description: "Restart outcome" } } + } + }, + "/reload": { + post: { + summary: "Reload firewall rules & configs", + operationId: "postReload", + security: [{ BearerAuth: [] }], + responses: { "200": { description: "Reload status" } } + } + }, + "/config/set": { + post: { + summary: "Set UCI configuration option", + operationId: "postConfigSet", + security: [{ BearerAuth: [] }], + requestBody: { + required: true, + content: { + "application/json": { + schema: { + type: "object", + properties: { + section: { type: "string" }, + option: { type: "string" }, + value: { type: "string" } + } + } + } + } + }, + responses: { "200": { description: "UCI update result" } } + } + } + }, + components: { + securitySchemes: { + BearerAuth: { + type: "http", + scheme: "bearer", + bearerFormat: "Secret Token" + } + } + } + }; + write_json(spec); +} + function handle_ai_doctor_last() { let raw = fs.readfile("/tmp/ai_doctor_last.json"); if (!raw) { @@ -504,6 +663,8 @@ if (method == "GET") { handle_tools(); else if (route == "/ai-doctor/last" || route == "/ai-doctor/last/") handle_ai_doctor_last(); + else if (route == "/openapi.json" || route == "/openapi.json/") + handle_openapi(); else err("Unknown endpoint: GET " + route, 404); } else if (method == "POST") {