diff --git a/src/antigravity_provider/router/profile_manager.py b/src/antigravity_provider/router/profile_manager.py index 174a1c8..912a268 100644 --- a/src/antigravity_provider/router/profile_manager.py +++ b/src/antigravity_provider/router/profile_manager.py @@ -123,6 +123,14 @@ class ProfileAuthManager: access_token = token_info.get("access_token") or auth_data.get("access_token") or "" refresh_token = token_info.get("refresh_token") or auth_data.get("refresh_token") or "" + # Файл без токена доступа бесполезен: agy на нём отвечает «Please sign in + # to view available models», а хаб считает аккаунт подключённым. Пустой + # вход не должен выдаваться за успешный. + if not access_token and not refresh_token: + raise ValueError( + "Вход не завершён: провайдер не вернул токен доступа. " + "Учётные данные agy не записаны." + ) scope = token_info.get("scope") or auth_data.get("scope") or "" token_type = token_info.get("token_type") or auth_data.get("token_type") or "Bearer" id_token = token_info.get("id_token") or auth_data.get("id_token") or "" @@ -314,7 +322,16 @@ class ProfileAuthManager: try: cls.write_agy_oauth_creds(pdir, auth_data) except Exception as e: - logger.warning("Failed to write .gemini/oauth_creds.json for profile=%s: %s", profile_id, e) + # Прежде сбой оставался только в журнале, и владелец видел + # «подключено» при неработающем аккаунте. agy читает именно + # этот файл, поэтому без него подключения нет. + logger.error( + "Не записаны учётные данные agy для профиля %s: %s", profile_id, e + ) + raise RuntimeError( + f"Учётные данные для agy не записаны ({e}). " + f"Аккаунт {profile_id} подключённым не считается." + ) from e # For Local and OpenAI-compatible providers, synchronize custom_base_url in router_profiles.yaml if provider in ("local", "local-llm", "llama.cpp", "ollama", "vllm", "openrouter", "nvidia", "nvidia-nim"): diff --git a/src/antigravity_provider/router/web/server.py b/src/antigravity_provider/router/web/server.py index 3e2d5ca..53012d3 100644 --- a/src/antigravity_provider/router/web/server.py +++ b/src/antigravity_provider/router/web/server.py @@ -23,7 +23,7 @@ from antigravity_provider.router.state_store import HubStateStore from antigravity_provider.router.action_handler import ActionExecutor from antigravity_provider.router.router_config import load_router_config -from antigravity_provider.updater.update_manager import get_installed_commit, UpdateManager +from antigravity_provider.updater.update_manager import get_installed_commit, get_installed_build_time, UpdateManager logger = logging.getLogger("hermes.router.web") @@ -410,6 +410,10 @@ def get_settings(authorized: bool = Depends(get_auth_token)): "config_dir": system_paths["config_dir"], "log_file": system_paths["log_file"], "installed_commit": get_installed_commit(), + # Версия между сборками не меняется намеренно, а коммит — строка из + # шестнадцатеричных цифр, по которой трудно на глаз отличить старую + # сборку от новой. Время установки отвечает на этот вопрос сразу. + "installed_at": get_installed_build_time(), "version": __version__, "last_update_check": last_check.to_dict() if last_check else None, "network_security": { diff --git a/src/antigravity_provider/router/web/static/app.js b/src/antigravity_provider/router/web/static/app.js index 15ac56a..86199a9 100644 --- a/src/antigravity_provider/router/web/static/app.js +++ b/src/antigravity_provider/router/web/static/app.js @@ -570,6 +570,21 @@ async function executeAction(actionName, actionData = {}) { } // ── GLOBAL HEADER ── +// Версия между сборками не меняется намеренно, а коммит — строка из +// шестнадцатеричных цифр. Дата установки отвечает на вопрос «старая сборка +// загрузилась или новая» сразу и без сверки коммитов. +function versionTagText(curVer) { + const installedAt = currentSettings && currentSettings.installed_at; + let stamp = ''; + if (installedAt) { + const d = new Date(installedAt); + if (!isNaN(d.getTime())) { + stamp = ' · ' + d.toLocaleString('ru-RU', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }); + } + } + return curVer ? `Hermes Hub Web v${curVer}${stamp}` : 'Hermes Hub Web — Н/Д: версия не передана сервером'; +} + function updateGlobalHeader() { if (!currentSnapshot) return; @@ -631,7 +646,7 @@ function updateGlobalHeader() { const curVer = (currentSnapshot && (currentSnapshot.version || (currentSnapshot.metrics || {}).version)) || (currentSettings && currentSettings.version) || ''; const versionTag = document.getElementById('version-tag'); if (versionTag) { - versionTag.textContent = curVer ? `Hermes Hub Web v${curVer}` : 'Hermes Hub Web — Н/Д: версия не передана сервером'; + versionTag.textContent = versionTagText(curVer); } } @@ -2412,7 +2427,7 @@ function renderUpdateUI() { } const versionTag = document.getElementById('version-tag'); if (versionTag) { - versionTag.textContent = curVer ? `Hermes Hub Web v${curVer}` : 'Hermes Hub Web — Н/Д: версия не передана сервером'; + versionTag.textContent = versionTagText(curVer); } if (statusBadge) {