Skip to content

LauncherΒΆ

DashboardLauncher - manages npm lifecycle and browser launch for the dashboard.

This module handles: - npm dependency installation (first run) - npm dev server (DASHBOARD_DEV=1) or production build - Automatic browser launch - Process cleanup on shutdown

ClassesΒΆ

DashboardLauncher ΒΆ

DashboardLauncher(port: int = 8344, frontend_dir: Path | None = None, static_dir: Path | None = None)

Manages dashboard frontend lifecycle.

Responsibilities: - Ensure npm dependencies installed - Start npm dev server (dev mode) or build for production - Launch browser automatically - Clean up npm processes on shutdown

Usage

launcher = DashboardLauncher(port=8344) launcher.start() # Starts npm and opens browser

... orchestrator runs ...ΒΆ

launcher.stop() # Cleanup

Or as context manager

with DashboardLauncher(port=8344): # orchestrator.serve() runs pass # Automatically cleaned up

Initialize dashboard launcher.

Parameters:

Name Type Description Default
port int

HTTP port where dashboard will be served (default: 8344)

8344
frontend_dir Path | None

Optional frontend directory path (defaults to FRONTEND_DIR)

None
Source code in src/flock/dashboard/launcher.py
def __init__(
    self,
    port: int = 8344,
    frontend_dir: Path | None = None,
    static_dir: Path | None = None,
):
    """Initialize dashboard launcher.

    Args:
        port: HTTP port where dashboard will be served (default: 8344)
        frontend_dir: Optional frontend directory path (defaults to FRONTEND_DIR)
    """
    self.port = port
    self.frontend_dir = frontend_dir or FRONTEND_DIR
    self.static_dir = static_dir or Path(__file__).parent / "static"
    self.dev_mode = os.getenv("DASHBOARD_DEV", "0") == "1"
    self._npm_process: subprocess.Popen | None = None

FunctionsΒΆ

start ΒΆ
start() -> None

Start dashboard: install deps, start npm, launch browser.

This method: 1. Ensures npm dependencies are installed 2. Starts npm dev server (dev mode) or builds production 3. Launches browser to dashboard URL

Source code in src/flock/dashboard/launcher.py
def start(self) -> None:
    """Start dashboard: install deps, start npm, launch browser.

    This method:
    1. Ensures npm dependencies are installed
    2. Starts npm dev server (dev mode) or builds production
    3. Launches browser to dashboard URL
    """
    print(f"[Dashboard] Starting dashboard on port {self.port}")
    print(f"[Dashboard] Mode: {'DEVELOPMENT' if self.dev_mode else 'PRODUCTION'}")
    print(f"[Dashboard] Frontend directory: {self.frontend_dir}")

    # Step 1: Ensure dependencies installed
    self._ensure_npm_dependencies()

    # Step 2: Start npm process
    self._start_npm_process()

    # Step 3: Launch browser
    self._launch_browser()
stop ΒΆ
stop() -> None

Stop dashboard and cleanup npm processes.

Attempts graceful termination, falls back to kill if needed.

Source code in src/flock/dashboard/launcher.py
def stop(self) -> None:
    """Stop dashboard and cleanup npm processes.

    Attempts graceful termination, falls back to kill if needed.
    """
    if self._npm_process is None:
        return

    print("[Dashboard] Stopping npm process...")

    try:
        # Try graceful termination first
        self._npm_process.terminate()

        # Wait up to 5 seconds for process to exit
        for _ in range(5):
            if self._npm_process.poll() is not None:
                print("[Dashboard] npm process stopped gracefully")
                return
            time.sleep(1)

        # Force kill if still running
        print("[Dashboard] npm process did not stop gracefully, forcing kill...")
        self._npm_process.kill()
        self._npm_process.wait(timeout=2)
        print("[Dashboard] npm process killed")

    except Exception as e:
        print(f"[Dashboard] Error stopping npm process: {e}")

    finally:
        self._npm_process = None
__enter__ ΒΆ
__enter__()

Context manager entry: start dashboard.

Source code in src/flock/dashboard/launcher.py
def __enter__(self):
    """Context manager entry: start dashboard."""
    self.start()
    return self
__exit__ ΒΆ
__exit__(exc_type, exc_val, exc_tb)

Context manager exit: stop dashboard.

Source code in src/flock/dashboard/launcher.py
def __exit__(self, exc_type, exc_val, exc_tb):
    """Context manager exit: stop dashboard."""
    self.stop()
    return False  # Don't suppress exceptions