#!/usr/bin/env python3 """Open the NexusOS UI in a native window. Uses pywebview, which renders via the WebView2 runtime on Windows (already present on Win10/11) -- a real app window with no browser chrome and none of the Edge --app profile cold-start. Blocks until the window is closed; the launcher waits on this process and stops the services when it exits. The window opens IMMEDIATELY on a loading page and navigates to the app once the backend answers, rather than waiting up to 40s with nothing on screen and then appearing. One window the whole time: nothing pops up and vanishes, and there is no moment where the user is left wondering whether the command did anything. Falls back to the default browser if pywebview/WebView2 is unavailable, staying alive so the launcher doesn't tear the services down underneath it. """ import sys import time import urllib.request URL = "http://localhost:8000" # Inline so it renders with no server and no asset files - the whole point is # that it shows before anything is listening. Colours match the app's dark UI so # the swap to the real page is not a flash of a different-looking window. LOADING_HTML = """ NexusOS

Loading Nexus core services

Starting the memory service and backend...

""" FAILED_HTML = LOADING_HTML.replace( "
", "" ).replace( "Loading Nexus core services", "Backend did not start" ).replace( "Starting the memory service and backend...", "Nothing answered on :8000 after 40s. Check: ncp logs -b" ) def _wait_for_backend(timeout: float = 40.0) -> bool: """Poll /status until the backend answers, so the window never loads before the server is up (which shows a localhost error the webview won't retry).""" status_url = URL.rstrip("/") + "/status" deadline = time.time() + timeout while time.time() < deadline: try: # 5s, not 2: /status probes Ollama, and a wedged Ollama made it # slower than a 2s ceiling - the backend was up and answering 200 # while this loop timed out on every attempt and declared it dead. # The server side is fixed too; this is the margin. with urllib.request.urlopen(status_url, timeout=5) as r: if r.status == 200: return True except Exception: time.sleep(1) return False def main() -> int: try: import webview except Exception as e: # pywebview not installed return _browser_fallback(f"pywebview unavailable ({e})") try: window = webview.create_window( "NexusOS", html=LOADING_HTML, width=1200, height=800, min_size=(900, 600), ) def _swap_in_app(win): """Runs once the GUI loop is up, so the spinner is already on screen while we wait. load_url replaces the loading page in place - there is never a second window to close.""" if _wait_for_backend(): win.load_url(URL) else: print("[nexus] backend not reachable on :8000 after 40s.", file=sys.stderr) win.load_html(FAILED_HTML) webview.start(_swap_in_app, window) # blocks until the window is closed return 0 except Exception as e: # no WebView2 runtime / backend failure return _browser_fallback(f"native window failed ({e})") def _browser_fallback(reason: str) -> int: import webbrowser print(f"[nexus] {reason}; opening default browser instead.", file=sys.stderr) webbrowser.open(URL) # Stay alive so the launcher keeps the services up. The user stops NexusOS # by closing the launcher (or the browser tab, then the services idle out). try: while True: time.sleep(3600) except KeyboardInterrupt: return 0 if __name__ == "__main__": sys.exit(main())