mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-15 17:30:13 +08:00
feat(cli): add 'webui' group + register command in CLI
This commit is contained in:
@@ -7,7 +7,7 @@ import click
|
||||
from click.shell_completion import get_completion_class
|
||||
|
||||
from . import __version__
|
||||
from .commands import bk, conf, init, plug, run, uninstall
|
||||
from .commands import bk, conf, init, plug, run, uninstall, webui
|
||||
|
||||
logo_tmpl = r"""
|
||||
___ _______.___________..______ .______ ______ .___________.
|
||||
@@ -77,6 +77,7 @@ def help(command_name: str | None, all: bool) -> None:
|
||||
|
||||
cli.add_command(init)
|
||||
cli.add_command(run)
|
||||
cli.add_command(webui)
|
||||
cli.add_command(help)
|
||||
cli.add_command(plug)
|
||||
cli.add_command(conf)
|
||||
|
||||
@@ -4,5 +4,6 @@ from .cmd_init import init
|
||||
from .cmd_plug import plug
|
||||
from .cmd_run import run
|
||||
from .cmd_uninstall import uninstall
|
||||
from .cmd_webui import webui
|
||||
|
||||
__all__ = ["conf", "init", "plug", "run", "uninstall", "bk"]
|
||||
__all__ = ["bk", "conf", "init", "plug", "run", "uninstall", "webui"]
|
||||
|
||||
168
astrbot/cli/commands/cmd_webui.py
Normal file
168
astrbot/cli/commands/cmd_webui.py
Normal file
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
CLI commands to manage the WebUI (dashboard) lifecycle: install/build/dev/serve.
|
||||
|
||||
Commands:
|
||||
- webui install [--root] : install frontend dependencies (pnpm/npm)
|
||||
- webui build [--root] : build production assets
|
||||
- webui dev [--root] : run frontend dev server (long-running)
|
||||
- webui serve [--root --port] : serve built dist via simple HTTP server
|
||||
|
||||
This file follows the existing AstrBot CLI conventions and raises ClickException
|
||||
for error conditions so callers and pre-commit hooks can observe failures.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import click
|
||||
|
||||
from astrbot.cli.utils.basic import get_astrbot_root
|
||||
from astrbot.core.utils.astrbot_path import get_astrbot_path
|
||||
|
||||
|
||||
def _find_dashboard_source(root: str | None = None) -> Path | None:
|
||||
"""
|
||||
Locate the dashboard source directory.
|
||||
|
||||
Priority:
|
||||
1. <ASTRBOT_ROOT>/dashboard
|
||||
2. repository/dashboard relative to the project path (get_astrbot_path)
|
||||
3. None if not found
|
||||
"""
|
||||
base = Path(root) if root else Path(get_astrbot_root())
|
||||
cand = base / "dashboard"
|
||||
if cand.is_dir():
|
||||
return cand
|
||||
|
||||
pkg_cand = Path(get_astrbot_path()) / "dashboard"
|
||||
if pkg_cand.is_dir():
|
||||
return pkg_cand
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _find_dashboard_dist(root: str | None = None) -> Path | None:
|
||||
"""
|
||||
Locate the built dashboard dist directory.
|
||||
|
||||
Priority:
|
||||
1. <ASTRBOT_ROOT>/data/dist
|
||||
2. packaged astrbot/dashboard/dist
|
||||
3. None
|
||||
"""
|
||||
base = Path(root) if root else Path(get_astrbot_root())
|
||||
cand = base / "data" / "dist"
|
||||
if cand.is_dir():
|
||||
return cand
|
||||
|
||||
pkg_dist = Path(get_astrbot_path()) / "dashboard" / "dist"
|
||||
if pkg_dist.is_dir():
|
||||
return pkg_dist
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _choose_package_manager() -> str:
|
||||
"""
|
||||
Prefer pnpm, fall back to npm. Raise ClickException if none available.
|
||||
"""
|
||||
if shutil.which("pnpm"):
|
||||
return "pnpm"
|
||||
if shutil.which("npm"):
|
||||
return "npm"
|
||||
raise click.ClickException(
|
||||
"Neither 'pnpm' nor 'npm' found on PATH. Please install one of them to build/run the dashboard."
|
||||
)
|
||||
|
||||
|
||||
@click.group(name="webui")
|
||||
def webui_group() -> None:
|
||||
"""Manage the WebUI (dashboard) frontend: install/build/dev/serve"""
|
||||
pass
|
||||
|
||||
|
||||
@webui_group.command(name="install")
|
||||
@click.option("--root", type=str, required=False, help="AstrBot root directory")
|
||||
def webui_install(root: str | None) -> None:
|
||||
"""Install frontend dependencies (pnpm/npm install)."""
|
||||
src = _find_dashboard_source(root)
|
||||
if not src:
|
||||
raise click.ClickException(
|
||||
"Dashboard source directory not found. Cannot install dependencies."
|
||||
)
|
||||
pm = _choose_package_manager()
|
||||
click.echo(f"Installing frontend dependencies using {pm} in {src}")
|
||||
try:
|
||||
subprocess.run([pm, "install"], cwd=str(src), check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise click.ClickException(f"Failed to install frontend dependencies: {e}")
|
||||
|
||||
|
||||
@webui_group.command(name="build")
|
||||
@click.option("--root", type=str, required=False, help="AstrBot root directory")
|
||||
def webui_build(root: str | None) -> None:
|
||||
"""Build production dashboard (runs pnpm/npm run build)."""
|
||||
src = _find_dashboard_source(root)
|
||||
if not src:
|
||||
raise click.ClickException(
|
||||
"Dashboard source directory not found. Cannot build."
|
||||
)
|
||||
pm = _choose_package_manager()
|
||||
click.echo(f"Building dashboard using {pm} in {src}")
|
||||
|
||||
# Ensure dependencies are installed first (best-effort)
|
||||
try:
|
||||
subprocess.run([pm, "install"], cwd=str(src), check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise click.ClickException(
|
||||
f"Failed to install frontend dependencies before build: {e}"
|
||||
)
|
||||
|
||||
try:
|
||||
subprocess.run([pm, "run", "build"], cwd=str(src), check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise click.ClickException(f"Dashboard build failed: {e}")
|
||||
|
||||
|
||||
@webui_group.command(name="dev")
|
||||
@click.option("--root", type=str, required=False, help="AstrBot root directory")
|
||||
def webui_dev(root: str | None) -> None:
|
||||
"""Start the frontend dev server (long-running)."""
|
||||
src = _find_dashboard_source(root)
|
||||
if not src:
|
||||
raise click.ClickException(
|
||||
"Dashboard source directory not found. Cannot start dev server."
|
||||
)
|
||||
pm = _choose_package_manager()
|
||||
click.echo(f"Starting dashboard dev server using {pm} in {src}")
|
||||
|
||||
# Note: This is intentionally long-running; we do not set check=True to allow
|
||||
# the process's exit code to be observed by the caller.
|
||||
try:
|
||||
subprocess.run([pm, "run", "dev"], cwd=str(src))
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise click.ClickException(f"Dev server exited with error: {e}")
|
||||
|
||||
|
||||
@webui_group.command(name="serve")
|
||||
@click.option("--root", type=str, required=False, help="AstrBot root directory")
|
||||
@click.option("--port", type=int, required=False, default=8080, help="Port to serve on")
|
||||
def webui_serve(root: str | None, port: int) -> None:
|
||||
"""Serve built dashboard dist via a simple HTTP server."""
|
||||
dist = _find_dashboard_dist(root)
|
||||
if not dist:
|
||||
raise click.ClickException(
|
||||
"Built dashboard dist not found. Run 'astrbot webui build' first."
|
||||
)
|
||||
click.echo(f"Serving dashboard from {dist} on port {port}")
|
||||
python_bin = shutil.which("python3") or shutil.which("python") or sys.executable
|
||||
try:
|
||||
# Long running; let exit code propagate
|
||||
subprocess.run([python_bin, "-m", "http.server", str(port)], cwd=str(dist))
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise click.ClickException(f"Failed to serve dashboard: {e}")
|
||||
Reference in New Issue
Block a user