fix(ncp web): no stray consoles, and show a loading window while services start

The two black terminals had two causes: uvicorn --reload (a dev flag whose
reloader spawns the server as a child, and Windows gives a console window to a
child of a console-less parent), and DETACHED_PROCESS in launch(), which is
exactly the console-less condition that triggers it. Drops --reload here
(launch_nexus.sh keeps it for the Linux dev loop) and uses CREATE_NO_WINDOW.

The window now opens immediately on an inline 'Loading Nexus core services' page
and navigates to the app once /status answers, instead of waiting 40s offscreen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jon
2026-07-22 15:22:44 -05:00
co-authored by Claude Opus 4.8
parent a62b68a909
commit 4cc0481ffd
2 changed files with 65 additions and 8 deletions
+13 -2
View File
@@ -100,8 +100,15 @@ class Service:
def _uvicorn(app: str, port: int):
# No --reload. It is a dev-loop flag: uvicorn's reloader runs a supervisor
# that spawns the real server as a CHILD, so every service became two
# processes - and on Windows that child, spawned from a parent with no
# console, got handed a brand new console WINDOW. `ncp web` popped two black
# terminals for what should have been a silent start. launch_nexus.sh still
# passes --reload for the Linux dev loop, where a visible console is the
# point; this launcher is the one users run.
return [str(PYTHON), "-m", "uvicorn", app, "--host", "0.0.0.0",
"--port", str(port), "--reload"]
"--port", str(port)]
SERVICES = {
@@ -153,8 +160,12 @@ def launch(svc: Service) -> None:
svc.log_file.write_text("")
with open(svc.log_file, "ab") as log:
# Detach so the service outlives this process, on both platforms.
# CREATE_NO_WINDOW, not DETACHED_PROCESS: detached means the process has
# NO console, and Windows then gives a console window to any console
# program it starts in turn. CREATE_NO_WINDOW gives it a console that is
# never shown, which its children inherit - so nothing flashes up.
kwargs = ({"creationflags": subprocess.CREATE_NEW_PROCESS_GROUP
| getattr(subprocess, "DETACHED_PROCESS", 0)}
| getattr(subprocess, "CREATE_NO_WINDOW", 0)}
if WINDOWS else {"start_new_session": True})
proc = subprocess.Popen(argv, cwd=str(svc.cwd), stdout=log,
stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL,