Skip to content

fix(desktop): run Windows servers without console windows - #110

Open
crazy-daddy wants to merge 4 commits into
JayantDevkar:mainfrom
crazy-daddy:feature/windows-silent-launch
Open

crazy-daddy wants to merge 4 commits into
JayantDevkar:mainfrom
crazy-daddy:feature/windows-silent-launch

Conversation

@crazy-daddy

@crazy-daddy crazy-daddy commented Oct 12, 2026 •

Copy link
Copy Markdown

Summary

On Windows, clicking the Karma desktop icon leaves two console windows open (one for the API, one for the frontend). This PR starts the servers without them, on every install path.

Root cause: spawn_detached uses CREATE_NO_WINDOW | DETACHED_PROCESS. Per the CreateProcess flags docs, CREATE_NO_WINDOW is ignored when combined with DETACHED_PROCESS. The detached server has no console, so its own console children (uvicorn.exe → python.exe, npm.cmd → cmd.exe → node.exe) each allocate a new, visible console window.

Fix: spawn with CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP instead. The server gets a hidden console that its children inherit, and it still outlives the launcher. This matches the existing intent ("never flash a window"), so it is the only behavior: no flag, no opt-in.

Changes

  • core.spawn_detached: Windows creation flags are now CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP (one line, plus a comment on why DETACHED_PROCESS can't be used).
  • Tests: test_spawn_detached_hides_console asserts both flags are set and DETACHED_PROCESS is not; test_notify_opens_no_console_window asserts the balloon passes CREATE_NO_WINDOW.
  • launcher.notify: the cold-start "Starting servers" balloon ran powershell -WindowStyle Hidden without CREATE_NO_WINDOW. Under pythonw the launcher has no console, so PowerShell got a new, visible one (a Windows Terminal window on Windows 11) before the hidden style applied. Found while retesting the cold start; it has been there since feat(desktop): one-click launcher + autostart for macOS and Windows #94. Now passes CREATE_NO_WINDOW, with a test.
  • Installer, shortcuts and the dashboard's Settings → Desktop App install are unchanged. The servers all go through spawn_detached, so every install path gets the fix.
  • main (after Make local Karma fast: normal-priority autostart, git cache, subagent parse cache, production frontend #112) merged in; the conflict in spawn_detached is resolved onto the new _popen helper. run_logged and stop_process_tree (taskkill /T) are unaffected.

Component

  • API (FastAPI backend)
  • Frontend (SvelteKit dashboard)
  • Captain Hook (hook models)
  • Hook Scripts
  • Documentation
  • CI / Build
  • Desktop launcher (scripts/karma_desktop, scripts/install_karma_app.py)

Type

  • Feature (new functionality)
  • Bug fix
  • Refactor (no behavior change)
  • Performance improvement
  • Documentation
  • Tests

Testing

  • scripts/tests (83 passed, 17 skipped as other-OS) and api/tests/test_desktop_app_guard.py (12 passed, 2 skipped) pass on Windows
  • ruff check and ruff format --check clean on scripts/ and the endpoint, as in the desktop-launcher workflow
  • Manually tested these flags on Windows 11: no console windows; servers keep running after the launcher exits, and a second click while running only reopens the dashboard
  • Checked on Windows 11 by recording every console window that opened: a cold start from the icon opened one (the balloon's PowerShell), and with this fix it opens none
  • Autostart toggled on in the dashboard's Settings → Desktop App: the Startup entry carries --no-open --quiet and no extra flag. Running it the way Windows does at login, with both servers stopped, brought both back with no console windows and no browser window
  • Not tested on macOS (no change there: the flags are Windows-only)

Related Issues

None filed. Companion PR: #109 (open the dashboard in the Windows default browser). Both touch launcher.py and its tests in different places; they merge together without conflicts in either order.

The launcher spawns the servers with CREATE_NO_WINDOW | DETACHED_PROCESS.
Windows ignores CREATE_NO_WINDOW alongside DETACHED_PROCESS, and the
servers' own console children (uvicorn.exe -> python.exe, npm.cmd ->
node.exe) then each open a new, visible console window.

--silent spawns them with CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP
instead: a hidden console the children inherit, still outliving the
launcher. install_karma_app.py --silent bakes the flag into the Desktop
shortcut and, with --autostart, the Startup entry. Off by default; no-op
note on macOS, which has no per-process console windows.
@crazy-daddy
crazy-daddy marked this pull request as ready for review October 12, 2026 04:26

@JayantDevkar JayantDevkar left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline context for reviewers, one note per change area.

Comment thread scripts/karma_desktop/core.py Outdated
Comment thread scripts/install_karma_app.py Outdated
@JayantDevkar

Copy link
Copy Markdown
Owner

Good find on the CREATE_NO_WINDOW + DETACHED_PROCESS interaction, that explains the two consoles people have hit.

You offered to flip the default or drop the flag, and I'd take the second option. The existing comment says "never flash a window", so windowless was always the intent, and I can't think of a reason someone would opt back into the console windows. Making the new creation flags unconditional turns this into a small change in spawn_detached plus the two tests on the flags, and removes the --silent plumbing through the installer, the shortcut arguments and the launcher. It also avoids a gap in the current version: the dashboard's own Settings → Desktop App install doesn't know about the flag, so toggling autostart there would rewrite the shortcut without --silent.

While you're in there, README line 291 still says no console window flashes on Windows; after this change it'll be true again, so it can stay.

I'll approve the CI run so the Windows job picks up your tests.

Per review: make the hidden-console creation flags the only behavior
instead of an opt-in flag. spawn_detached now always uses
CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP, so the dashboard's
Settings -> Desktop App install gets it too. Reverts the --silent
plumbing through the installer, the shortcut arguments and the launcher.
…t-launch

# Conflicts:
#	scripts/karma_desktop/core.py
#	scripts/tests/test_karma_desktop.py
@crazy-daddy crazy-daddy changed the title feat(desktop): add --silent to run Windows servers without console windows fix(desktop): run Windows servers without console windows Oct 13, 2026
notify() ran powershell -WindowStyle Hidden without CREATE_NO_WINDOW.
Under pythonw the launcher has no console, so powershell got a new,
visible one (a Windows Terminal window on Windows 11) before the hidden
window style applied, flashing on every cold start.
@crazy-daddy

Copy link
Copy Markdown
Author

Thanks! Dropped --silent as suggested, so the hidden-console flags are now the only behavior (bbadef8). I also merged in main after #112, which conflicted with spawn_detached (3743a0e).
While retesting the cold start I found one more flash: the “Starting servers” balloon ran PowerShell without CREATE_NO_WINDOW. It was there before this PR, but it’s the same bug, so the fix is in here too (b30f66e).
Retested on Windows 11, recording every console window that opened: icon cold start, icon while running, and autostart toggled in Settings → Desktop App and then run as Windows does at login. No console windows in any of them.

Title and description are updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants