Skip to content

gui_cmd

gui_cmd

jarvis gui — start and open the local graphical interface.

Functions

gui

gui(frontend_port: int, api_port: int, no_server: bool, no_browser: bool) -> None

Start the browser-based graphical mode in the default browser.

This command is intended for source checkouts. For an installed desktop application, launch OpenJarvis from the operating system menu instead.

Source code in src/openjarvis/cli/gui_cmd.py
@click.command()
@click.option(
    "--frontend-port", default=5173, show_default=True, type=click.IntRange(1, 65535)
)
@click.option(
    "--api-port", default=8000, show_default=True, type=click.IntRange(1, 65535)
)
@click.option("--no-server", is_flag=True, help="Do not start the API server.")
@click.option("--no-browser", is_flag=True, help="Only start the frontend.")
def gui(frontend_port: int, api_port: int, no_server: bool, no_browser: bool) -> None:
    """Start the browser-based graphical mode in the default browser.

    This command is intended for source checkouts. For an installed desktop
    application, launch OpenJarvis from the operating system menu instead.
    """
    console = Console(stderr=True)
    frontend = _frontend_dir()
    if frontend is None:
        raise click.ClickException(
            "The graphical frontend is not available in this installation. "
            "Download the OpenJarvis desktop app or run this command "
            "from a source checkout."
        )
    npm = shutil.which("npm") or shutil.which("npm.cmd")
    if npm is None:
        raise click.ClickException(
            "Node.js/npm is required for graphical mode. Install Node.js 22 or newer."
        )
    _check_frontend_port(frontend_port)
    _ensure_frontend_dependencies(frontend, npm)

    if not no_server:
        uv = shutil.which("uv")
        if uv is None:
            raise click.ClickException(
                "uv is required to start the API with desktop dependencies. "
                "Install uv or use --no-server with an already-running API."
            )
        server = subprocess.run(
            [
                uv,
                "run",
                "--extra",
                "desktop",
                "jarvis",
                "start",
                "--port",
                str(api_port),
            ],
            cwd=frontend.parent,
            check=False,
        )
        if server.returncode != 0:
            raise click.ClickException("Could not start the OpenJarvis API server.")

    env = os.environ.copy()
    # Let browser requests use Vite's same-origin proxy at any frontend port.
    # VITE_API_URL is exposed to browser code, so clear an inherited override.
    env["VITE_API_URL"] = ""
    env["OPENJARVIS_VITE_PROXY_TARGET"] = f"http://127.0.0.1:{api_port}"
    process = subprocess.Popen(
        [
            npm,
            "run",
            "dev",
            "--",
            "--host",
            "127.0.0.1",
            "--port",
            str(frontend_port),
            "--strictPort",
        ],
        cwd=frontend,
        env=env,
    )
    if not _wait_for_port(process, "127.0.0.1", frontend_port):
        process.terminate()
        process.wait()
        raise click.ClickException(
            "The graphical frontend did not start on the requested port."
        )

    url = f"http://127.0.0.1:{frontend_port}"
    console.print(f"[green]OpenJarvis graphical mode is ready:[/green] {url}")
    if not no_browser:
        webbrowser.open(url)
    try:
        process.wait()
    except KeyboardInterrupt:
        process.terminate()
        process.wait()