← Commit history

0.3.4: /health no longer blocks on tasklist (fixes spawn-backoff lockout)

John Lauer ·d80dd05a8c ·2mo ago ·parent 7789e49
status_payload() called _sessions() inline, which shells out to tasklist /V (~3s on a loaded box). That exceeded Adom Bridge's spawn health-check timeout, so ab aborted the response mid-write (ConnectionAbortedError 10053), marked the spawn failed, and after 17 attempts locked the bridge in spawn-backoff so no rustdesk_* verb could run. Sessions are now refreshed on a background thread; /health returns the cached list. Measured on the affected machine: ~3000ms -> 22-74ms. Also: updateManifestUrl/docs/homepage pointed at pre-rename slugs that now 301, which broke bridge_install and auto-update.
6 files changed +48−20
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.3.2+0.3.4\ No newline at end of file
bridge.json+5−5
@@ -2,11 +2,11 @@   "manifest_version": 1,   "name": "rustdesk",   "displayName": "RustDesk",-  "version": "0.3.0",+  "version": "0.3.4",   "description": "Drive RustDesk remote-desktop connections (client side): connect out to an ID/IP, list and close sessions.",-  "homepage": "https://wiki.adom.inc/adom/adom-desktop",-  "docs": "https://wiki.adom.inc/adom/adom-desktop-rustdesk-bridge",-  "updateManifestUrl": "https://wiki.adom.inc/api/v1/pages/adom-desktop-rustdesk-bridge/files/rustdesk-bridge-manifest.json",+  "homepage": "https://wiki.adom.inc/adom/adom-bridge",+  "docs": "https://wiki.adom.inc/adom/rustdesk-bridge",+  "updateManifestUrl": "https://wiki.adom.inc/api/v1/pages/rustdesk-bridge/files/rustdesk-bridge-manifest.json",   "author": "adom",   "license": "MIT",   "spawn": {@@ -59,4 +59,4 @@   "prewarm": {     "assets": []   }-}\ No newline at end of file+}
package.json+2−2
@@ -2,7 +2,7 @@   "slug": "rustdesk-bridge",   "title": "Adom Desktop - RustDesk Bridge",   "type": "app",-  "version": "0.3.3",+  "version": "0.3.4",   "description": "Drive RustDesk remote-desktop sessions from cloud Claude via Adom Desktop \u2014 connect out to any host by ID or IP (with a password), and list/close sessions. Works where RDP can't (Windows Home).",   "tags": [     "bridge",@@ -82,4 +82,4 @@     "install.sh",     "uninstall.sh"   ]-}\ No newline at end of file+}
page.json+5−5
@@ -2,8 +2,8 @@   "slug": "rustdesk-bridge",   "title": "Adom Desktop - RustDesk Bridge",   "type": "app",-  "version": "0.3.3",-  "description": "Drive RustDesk remote-desktop sessions from cloud Claude via Adom Desktop — connect out to any host by ID or IP (with a password), and list/close sessions. Works where RDP can't (Windows Home).",+  "version": "0.3.4",+  "description": "Drive RustDesk remote-desktop sessions from cloud Claude via Adom Desktop \u2014 connect out to any host by ID or IP (with a password), and list/close sessions. Works where RDP can't (Windows Home).",   "tags": [     "bridge",     "rustdesk",@@ -34,7 +34,7 @@     "access my pc from another location",     "take over a remote pc"   ],-  "discovery_pitch": "Drive a RustDesk session from cloud Claude — connect out to any host by ID or IP with a password and manage the session. Works where RDP can't (Windows Home).",+  "discovery_pitch": "Drive a RustDesk session from cloud Claude \u2014 connect out to any host by ID or IP with a password and manage the session. Works where RDP can't (Windows Home).",   "sample_prompts": [     {       "label": "Connect",@@ -72,7 +72,7 @@   "hero": {     "type": "billboard",     "headline": "RustDesk Bridge",-    "subhead": "Drive a remote desktop from cloud Claude — even on Windows Home.",+    "subhead": "Drive a remote desktop from cloud Claude \u2014 even on Windows Home.",     "screenshot": "docs/hero.png"   },   "visibility": "public",@@ -90,4 +90,4 @@     "uninstall.sh"   ],   "updated_at": "2026-08-08T18:05:42.474Z"-}\ No newline at end of file+}
rustdesk-bridge-manifest.json+5−5
@@ -1,8 +1,8 @@ {   "manifest_version": 1,-  "version": "0.3.2",-  "url": "https://wiki.adom.inc/download/adom/rustdesk-bridge/0.3.2/rustdesk-bridge-v0.3.2.zip",-  "sha256": "5cd038fd1ca3335a6b16ab831d03f1cf677f1d51eaf28c9e52219bb77b5e5c1f",-  "size": 6891,-  "released_at": "2026-06-30T02:22:26Z"+  "version": "0.3.4",+  "url": "https://wiki.adom.inc/download/adom/rustdesk-bridge/0.3.4/rustdesk-bridge-v0.3.4.zip",+  "sha256": "5987686da7ba82abc9bb2b834109b77eacc9a63cd225c4d5a4bd771ff3843f1a",+  "size": 7221,+  "released_at": "2026-08-30T10:15:32Z" }\ No newline at end of file
server.py+30−2
@@ -27,7 +27,7 @@ import ctypes from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer  BRIDGE_NAME = "rustdesk"-BRIDGE_VERSION = "0.3.2"+BRIDGE_VERSION = "0.3.4" DEFAULT_PORT = 8810 RUSTDESK_DL_URL = "https://github.com/rustdesk/rustdesk/releases/download/1.4.8/rustdesk-1.4.8-x86_64.exe" @@ -103,6 +103,33 @@ def _sessions():     return out  +# The status/health path must be CHEAP (ab polls /health every ~15s and its+# spawn health-check has a short timeout). _sessions() shells out to tasklist,+# which can take multiple seconds on a loaded box -- doing that inline in+# do_GET stalls /health past ab's timeout, so the spawn is marked failed and+# the bridge gets stuck in spawn-backoff. Refresh the session list on a+# background thread and let /health return the cached value INSTANTLY.+_sess_cache = {"val": [], "at": 0.0}+_sess_cache_lock = threading.Lock()+++def _sessions_cached():+    with _sess_cache_lock:+        return list(_sess_cache["val"])+++def _sess_refresher():+    while True:+        try:+            v = _sessions()+            with _sess_cache_lock:+                _sess_cache["val"] = v+                _sess_cache["at"] = time.time()+        except Exception:+            pass+        time.sleep(8)++ def _norm(s):     return re.sub(r"\s+", "", s or "") @@ -330,7 +357,7 @@ VERBS = { # ---------------------------------------------------------------- status chip def status_payload():     exe = find_rustdesk()-    sess = _sessions()+    sess = _sessions_cached()     with _dl_lock:         dl = dict(_dl)     if not exe and dl["state"] == "downloading":@@ -417,6 +444,7 @@ def main():                      % (BRIDGE_VERSION, port, IDLE_DISCONNECT_SECS))     sys.stderr.flush()     threading.Thread(target=_idle_monitor, daemon=True).start()+    threading.Thread(target=_sess_refresher, daemon=True).start()     srv.serve_forever()