From ef6a9e1acd3cec95d001661f796c37c66e96c3e2 Mon Sep 17 00:00:00 2001 From: Hermes Team Date: Mon, 24 Aug 2026 09:59:41 +0700 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=B2=D0=B5=D0=B1-=D1=81=D0=B5=D1=80?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=20=D0=BD=D0=B5=20=D0=B7=D0=B0=D0=BF=D1=83?= =?UTF-8?q?=D1=81=D0=BA=D0=B0=D0=BB=D1=81=D1=8F=20=D0=BD=D0=B0=20Windows,?= =?UTF-8?q?=20=D0=B4=D0=B8=D0=B0=D0=B3=D1=80=D0=B0=D0=BC=D0=BC=D0=B0=20?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=BC=D0=BE=D0=B7=D0=B8=D0=BB=D0=B0=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=20=D0=BF=D0=B5=D1=80=D0=B5=D1=82=D0=B0=D1=81=D0=BA?= =?UTF-8?q?=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Три дефекта из установки владельца на вторую машину. 1. Веб-сервер падал сразу: «web server process terminated unexpectedly». Установщик ставит в venv Hermes только customtkinter, pillow, pyyaml и psutil — fastapi и uvicorn отсутствовали в списке вовсе. Добавлены во все шесть мест: проверка, сообщение, pip, uv, перепроверка. Поэтому на Windows открывался только десктоп: веб физически не мог стартовать. 2. Лаунчер показывал голое «terminated unexpectedly» без причины — та же болезнь, что у кода 12. Теперь перехватывает вывод процесса и выводит последние строки ошибки в окне. 3. Окно тормозило при перетаскивании и продолжало двигаться несколько секунд после отпускания мыши. _RouteDiagram перерисовывал всю канву на КАЖДОЕ событие , а при перетаскивании их сотни; очередь не успевала разгребаться. Гашение в приложении существовало, но _handle_debounced_resize был пустой заглушкой. Перерисовка сведена к одной после затишья. Замерено: 199 событий давали 199 перерисовок, теперь 4. Тесты: 373 passed, ruff чисто. Установщик пересобран. Co-Authored-By: Claude Opus 5 --- installer/HermesHubSetup.cs | 14 ++++----- launcher/HermesHub.exe | Bin 6144 -> 6144 bytes launcher/HermesHubWeb.cs | 16 ++++++++++- launcher/HermesHubWeb.exe | Bin 11776 -> 12288 bytes .../router/ui/views/dashboard_view.py | 27 +++++++++++++++++- 5 files changed, 48 insertions(+), 9 deletions(-) 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 cf546c17cd6660215e712bb06b7dbba771389765..22fae0c99851073d087b2946e7cf5436adb471c6 100644 GIT binary patch delta 35 rcmZoLXfT-2!DPF6W7jDjfkV}az0DJz`)>&-_`2bzis0rH-Z@+V9oi5P delta 35 rcmZoLXfT-2!Q`@GW7jDjfj^pp8uOJnJXY!Eof0kIcYbpU?;I`w_*4z? 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 a864b29e89aeac321eccbb63af1b07869a2e7d44..527ab8b36e22345c67963ba6baa9ac786c548c31 100644 GIT binary patch delta 2290 zcmaJ>Yiu0V6+U)uJFCQbj5?5(wlz0)o>1Km`GU7N`^z-1JWcszO3l6=*A`-@Us5 zr>Z*AoX2;*bM8I&+?mSm%D#sm>>OD-d5CJydD2*zg^9WV1gcGba`(M!!l3_e~%EM{`z zCasSBM(ol0d>Jz%9?=GreqFm>>3?WjlpgVoDE%E@yV6hk29^GUub_04PZ`J`e%vSa z@gW60##7ooT)e{XRBoHTPU-D6UC|%V*7!ugxH(~7n{ctU5J_XLG+^0|xZB7s+^=D? z@mPC2){*}LCYcR3AS1rQVpz}#QX$Y%!De z0D?*&CnoJd$xfO{FQL&~yraR!a6(=AMGVJ!4coWZW4Q7(Iuf_XI?RraudQ%dZTRz* z%a_e|Rd|fNlj5?`+$fvRvIHr#QMEE8vi_EZ&c;~E>{Okqm`C$zxp3M{o1Ly&!AyHh zr#bT)JCBHq)4?#(ao3gxEZdO@DO&D}wUIX|scmj2Fz*@Ic0yX!9&*(lP^kd03#WV? zQFfBN3n11wqg4)lMasys?c##pxmmX1_{~(~3T|_)8=RAwa)%MY*uI=db>6Ba>^ABS zsrXj}x*kVZ{JS_5Sij)sMD=hRp}V0mYWWq=x8X;je*lfiN>JgaYxpn!3;!-ib>KY& zy_$a>j;xO^3HRj7`a_1|FVJQfnN@+4)c`#Ty}Hm7@LRzVQ^$#RVfq!cm6|Sl2WJn=V+Qmd?qC{MAa%<$eI6%j0G67(Wh-_-0elEQ6Nl?J zuu1XL`Zu<{%Kzpw=+FGOOiokEoAgb3gpTU7kSCRVUdh*$JO^peXNtMT7$$>84975N z0#f`sva!7lA0*L_=teB7Q3F0L>FuE=@=;@UsqLB;ws4Lf<}cDYIs^GJy~SUJe3!oo z`3ZlAzM!z~!EVyup(5G(`;Z@N=ct$Z^(NSDzGY(UHvKQ)?9o#|ntXqS{TBZe@*W^T zcE8@mg6uK99rjQ3pF_T^J1ogQ()-z(?QBIauurjpO{_=_#uO{CjDbRIr{S`#Y_Cy< zJY*bWMK%w_!gcSa9*U7*Jrt%B>|Qzs0QEr3;0LvGoIm38y$%7AbN>GT-6F0h delta 1884 zcmaJ>Z){Ul6hHU%-Pi7|D|_#acI;30x^C@bUAJ``UB?(WJH}8%1Q7$9Y}pEej91@^ zfgoFrNB{$2UNB$`82n-kF(g%^3B+h1fDT0x{1c?g7bAX9!DyTxrsutOfM0y=JHK;& z=iGDex#!-O9!U2c+FRXy`sK}#e|`Xt6p})K8YCP!ax6aa_7kp}IKUc{7y$TP1YZh0z9(syt6BRZ>;e!_&N# z?4;zi@If$6+#Sv?*k_fP$wR~gmb(Op%6bF)*AET~$Of6SPQ?3xq z)P!UJvQP&QS^@?U?iTw%E(Y!e)tYIW?QBe_V3!=lE6iHLcF9*VjYlJ2(mY&)-BB9`S&6nuiV6Yb9tDI&JO zY$y28Fyk8#n-OQ?x1(*w??$Y>kDtu*-yq`uZk{X}rG^uT zDHR8af~w*|^%k=b!PsJlq~2=jI_`j&!d=H(X4j+aDbZcuhLnbvP8^M)aDqc`M=UBZ zQs%h%sE5(mYZ>ef(c|tw3ciD^Y{$J-s6@kdyWYVri+yFsG3un`@ED~IODV0Y&;$%O z_ON*2QgW3YD2$c;l=FFv#|!=&G|9flAUB)qT+moA<}%GB$e^tiU@A;sYIq7Q@0=O$ z4tChtOFm>boISzI=;Ji}G>GEZ0+fQ@i4yBISIbL$xDr);A$zDO=+Bojw&;K24i^PU z2Rl-xB zS--1;EMmu8pDjDY-?0gBmOsV2;79&C%D*h>rKTRWWDiOKhUo=7^t&v%-;!_9`5?d~ zJwpY!hLWA0wa{B{o<(>S9>HljQ1Qx{RzJ)J8`RWgoW*5i{1zPL&%-S^&QHQ^ILBW> z{Zrl#T`=2r4fUh^b(CKqGX=MK7nuS(^`frO_fXc+5+ae+w2TyxXQ)c%l5ZhEBIF1) zFvAJjNZQG}6tj_=bPZWbexZFRr|1yrBAl>>s9rq3L1+L4c9I4t#LGMcJCV5wtt3vK z&ipRD!_~~|j&`*~qLFZ{y{9MKy0|SGj>V$UaCdh+*0wkr?TN%%GUrRmx$+f;K9m?8 zg;l9RBLy21sr9{sql>q1N+pKbLC@7f>`Y>CWMyJ-IKh^CJZzx!+02#F2v0^>UuAt) z5)9B`2of-gKNV7_888ByU@Ht-)-bZ$&`Q8|NTM9dvs+*XRsorHmB;MGgOSbs|Ll8i z`Q8mTE{<)u{UYOQzf^z_2L;aMs-QsRZLWHI4V$j5ZIidjBb-U?a#0oJ^h+qE^Z`qs zaHt%S(-U%-R|yWa3v&7eIlWI#A7OogK6zQ(Y-#S_QCwwLBPT*9=rTtC0 ObW28R_?1jF+5ZK7M_q^j 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."""