diff --git a/installer/HermesHubSetup.cs b/installer/HermesHubSetup.cs index bd2bcac..bafc630 100644 --- a/installer/HermesHubSetup.cs +++ b/installer/HermesHubSetup.cs @@ -18,7 +18,7 @@ namespace HermesHubSetup // Подставляется сборщиком из фактического git-коммита. Раньше здесь // жил зашитый "8cddc9f", то есть манифест сообщал неправду о том, из // какого кода собран установщик. - public const string BuildCommit = "36b449b"; + public const string BuildCommit = "57e7ca2"; public const string MIN_HERMES_VERSION = "0.20.0"; public const string MAX_TESTED_HERMES = "0.20.4"; @@ -137,7 +137,7 @@ namespace HermesHubSetup { ProcessStartInfo checkPsi = new ProcessStartInfo(); checkPsi.FileName = pythonExe; - checkPsi.Arguments = "-c \"import customtkinter, PIL, yaml, psutil; print('DEPS_OK')\""; + checkPsi.Arguments = "-c \"import customtkinter, PIL, yaml, psutil, fastapi, uvicorn; print('DEPS_OK')\""; checkPsi.UseShellExecute = false; checkPsi.RedirectStandardOutput = true; checkPsi.RedirectStandardError = true; @@ -159,7 +159,7 @@ namespace HermesHubSetup if (needsInstall) { - if (progressCallback != null) progressCallback("Installing dependencies into Hermes venv (customtkinter, Pillow, PyYAML, psutil)...", 40); + if (progressCallback != null) progressCallback("Installing dependencies into Hermes venv (customtkinter, Pillow, PyYAML, psutil, FastAPI, uvicorn)...", 40); // 1. Ensure pip is installed/bootstrapped if needed try @@ -185,7 +185,7 @@ namespace HermesHubSetup { ProcessStartInfo pipPsi = new ProcessStartInfo(); pipPsi.FileName = pythonExe; - pipPsi.Arguments = "-m pip install --no-warn-script-location customtkinter pillow pyyaml psutil"; + pipPsi.Arguments = "-m pip install --no-warn-script-location customtkinter pillow pyyaml psutil fastapi uvicorn"; pipPsi.UseShellExecute = false; pipPsi.RedirectStandardOutput = true; pipPsi.RedirectStandardError = true; @@ -209,7 +209,7 @@ namespace HermesHubSetup { ProcessStartInfo uvPsi = new ProcessStartInfo(); uvPsi.FileName = "uv"; - uvPsi.Arguments = string.Format("pip install --python \"{0}\" customtkinter pillow pyyaml psutil", pythonExe); + uvPsi.Arguments = string.Format("pip install --python \"{0}\" customtkinter pillow pyyaml psutil fastapi uvicorn", pythonExe); uvPsi.UseShellExecute = false; uvPsi.RedirectStandardOutput = true; uvPsi.RedirectStandardError = true; @@ -237,7 +237,7 @@ namespace HermesHubSetup { ProcessStartInfo recheckPsi = new ProcessStartInfo(); recheckPsi.FileName = pythonExe; - recheckPsi.Arguments = "-c \"import customtkinter, PIL, yaml, psutil; print('DEPS_VERIFIED')\""; + recheckPsi.Arguments = "-c \"import customtkinter, PIL, yaml, psutil, fastapi, uvicorn; print('DEPS_VERIFIED')\""; recheckPsi.UseShellExecute = false; recheckPsi.RedirectStandardOutput = true; recheckPsi.RedirectStandardError = true; @@ -309,7 +309,7 @@ namespace HermesHubSetup } // 2. Install UI & System Dependencies into Hermes Python Environment - if (progressCallback != null) progressCallback("Checking Python UI dependencies (customtkinter, Pillow)...", 35); + if (progressCallback != null) progressCallback("Checking Python dependencies (customtkinter, Pillow, FastAPI, uvicorn)...", 35); if (!EnsurePythonDependencies(HermesPython, progressCallback)) { return 13; // Dependency install failed diff --git a/launcher/HermesHub.exe b/launcher/HermesHub.exe index cf546c1..22fae0c 100644 Binary files a/launcher/HermesHub.exe and b/launcher/HermesHub.exe differ diff --git a/launcher/HermesHubWeb.cs b/launcher/HermesHubWeb.cs index 8c80783..d633e42 100644 --- a/launcher/HermesHubWeb.cs +++ b/launcher/HermesHubWeb.cs @@ -122,6 +122,11 @@ namespace HermesHub serverPsi.UseShellExecute = false; serverPsi.CreateNoWindow = true; serverPsi.WindowStyle = ProcessWindowStyle.Hidden; + // Вывод перехватываем, чтобы при падении показать причину, а не + // голое «terminated unexpectedly». Без этого владелец видит факт + // отказа и ни слова о том, чего не хватает. + serverPsi.RedirectStandardError = true; + serverPsi.RedirectStandardOutput = true; try { @@ -144,7 +149,16 @@ namespace HermesHub } if (serverProcess.HasExited) { - MessageBox.Show("Hermes Hub web server process terminated unexpectedly.", "Hermes Hub", MessageBoxButtons.OK, MessageBoxIcon.Error); + string why = ""; + try { why = serverProcess.StandardError.ReadToEnd(); } catch { } + if (string.IsNullOrEmpty(why)) + { + try { why = serverProcess.StandardOutput.ReadToEnd(); } catch { } + } + if (why.Length > 1500) why = why.Substring(why.Length - 1500); + string msg = "Веб-сервер Hermes Hub завершился с ошибкой."; + if (!string.IsNullOrEmpty(why)) msg += Environment.NewLine + Environment.NewLine + why.Trim(); + MessageBox.Show(msg, "Hermes Hub", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } Thread.Sleep(200); diff --git a/launcher/HermesHubWeb.exe b/launcher/HermesHubWeb.exe index a864b29..527ab8b 100644 Binary files a/launcher/HermesHubWeb.exe and b/launcher/HermesHubWeb.exe differ diff --git a/src/antigravity_provider/router/ui/views/dashboard_view.py b/src/antigravity_provider/router/ui/views/dashboard_view.py index 0b2b608..e857618 100644 --- a/src/antigravity_provider/router/ui/views/dashboard_view.py +++ b/src/antigravity_provider/router/ui/views/dashboard_view.py @@ -220,7 +220,32 @@ class _RouteDiagram(ctk.CTkFrame): self.context_status.pack() self._left_labels: list[str] = [] self._right_labels: list[str] = [] - self.bind("", self._redraw) + self._redraw_timer_id: Any = None + self.bind("", self._schedule_redraw) + + def _schedule_redraw(self, event: Any = None) -> None: + """Свести поток к одной перерисовке. + + При перетаскивании окна Tk шлёт непрерывно, и раньше каждое + событие вызывало полную перерисовку канвы: удаление и построение всех + связей, подписей и узлов. Очередь не успевала разгребаться, и окно + продолжало ползти ещё несколько секунд после того, как мышь отпущена. + + Перерисовываем один раз, когда поток событий утих. + """ + if self._redraw_timer_id is not None: + try: + self.after_cancel(self._redraw_timer_id) + except Exception: + pass + self._redraw_timer_id = self.after(80, self._redraw_now) + + def _redraw_now(self) -> None: + self._redraw_timer_id = None + try: + self._redraw() + except Exception: + pass def sync_slots(self, provider_count: int, agent_count: int) -> None: """Grow/shrink endpoint slots to match the snapshot without hard caps."""