Spaces:
Sleeping
Sleeping
| """Platform-neutral lifecycle for the FCC desktop shell.""" | |
| from __future__ import annotations | |
| import threading | |
| from collections.abc import Callable | |
| from typing import Protocol | |
| from free_claude_code.cli.commands import ( | |
| ServerStatus, | |
| ServerSupervisor, | |
| load_server_settings, | |
| open_admin_when_ready, | |
| schedule_open_admin_browser, | |
| ) | |
| from free_claude_code.cli.launchers.common import preflight_proxy | |
| from free_claude_code.config.paths import config_dir_path | |
| from free_claude_code.config.server_urls import local_proxy_root_url | |
| from free_claude_code.config.settings import get_settings | |
| from free_claude_code.core.interprocess_lock import InterprocessFileLock | |
| class DesktopTray(Protocol): | |
| """UI loop owned by the platform tray adapter.""" | |
| def run(self) -> None: ... | |
| def stop(self) -> None: ... | |
| class DesktopTrayFactory(Protocol): | |
| """Construct a tray adapter around a desktop controller.""" | |
| def __call__(self, controller: DesktopController) -> DesktopTray: ... | |
| class ServerOwner(Protocol): | |
| """Server lifecycle used by the desktop controller.""" | |
| def status(self) -> ServerStatus: ... | |
| def schedule_run(self) -> bool: ... | |
| def run(self, *, open_admin_browser: bool | None = None) -> None: ... | |
| def request_restart(self) -> bool: ... | |
| def request_stop(self) -> None: ... | |
| class DesktopController: | |
| """Coordinate one tray loop with one in-process FCC server owner.""" | |
| def __init__( | |
| self, | |
| supervisor: ServerOwner, | |
| tray_factory: DesktopTrayFactory, | |
| open_admin: Callable[[], None], | |
| ) -> None: | |
| self._supervisor = supervisor | |
| self._open_admin = open_admin | |
| self._thread_lock = threading.Lock() | |
| self._server_thread: threading.Thread | None = None | |
| self._tray = tray_factory(self) | |
| def status(self) -> ServerStatus: | |
| return self._supervisor.status | |
| def run(self) -> None: | |
| """Run the tray on this thread and the FCC server on its owned worker.""" | |
| self._start_server() | |
| try: | |
| self._tray.run() | |
| finally: | |
| self._supervisor.request_stop() | |
| self._tray.stop() | |
| with self._thread_lock: | |
| thread = self._server_thread | |
| if thread is not None: | |
| thread.join() | |
| def open_admin(self) -> None: | |
| self._open_admin() | |
| def restart_server(self) -> None: | |
| """Restart an active server or relaunch one that exited unexpectedly.""" | |
| with self._thread_lock: | |
| thread = self._server_thread | |
| if thread is not None and thread.is_alive(): | |
| self._supervisor.request_restart() | |
| return | |
| self._start_server() | |
| def quit(self) -> None: | |
| """Close the server gracefully and end the platform tray loop.""" | |
| self._supervisor.request_stop() | |
| self._tray.stop() | |
| def _start_server(self) -> None: | |
| with self._thread_lock: | |
| if self._server_thread is not None and self._server_thread.is_alive(): | |
| return | |
| if not self._supervisor.schedule_run(): | |
| return | |
| self._server_thread = threading.Thread( | |
| target=self._run_server, | |
| name="fcc-desktop-server", | |
| ) | |
| self._server_thread.start() | |
| def _run_server(self) -> None: | |
| self._supervisor.run(open_admin_browser=False) | |
| def launch_desktop(tray_factory: DesktopTrayFactory) -> None: | |
| """Start the singleton desktop host or focus the already running FCC UI.""" | |
| settings = load_server_settings() | |
| instance_lock = InterprocessFileLock(config_dir_path() / "desktop.lock") | |
| if not instance_lock.acquire(): | |
| open_admin_when_ready(settings) | |
| return | |
| try: | |
| if preflight_proxy(local_proxy_root_url(settings)) is None: | |
| open_admin_when_ready(settings) | |
| return | |
| supervisor = ServerSupervisor(console_logging=False) | |
| def open_current_admin() -> None: | |
| schedule_open_admin_browser(get_settings()) | |
| DesktopController(supervisor, tray_factory, open_current_admin).run() | |
| finally: | |
| instance_lock.release() | |