feat: move dashboard to apps/ so we can share ws proto
This commit is contained in:
+19
-13
@@ -5227,12 +5227,14 @@ def _web_ui_build_needed(web_dir: Path) -> bool:
|
||||
"""Return True if the web UI dist is missing or stale.
|
||||
|
||||
Mirrors the staleness logic used by ``_tui_build_needed()`` for the TUI.
|
||||
The Vite build outputs to ``hermes_cli/web_dist/`` (per vite.config.ts
|
||||
outDir: "../hermes_cli/web_dist"), NOT to ``web/dist/``. Uses the Vite
|
||||
manifest as the sentinel because it is written last and therefore has the
|
||||
newest mtime of any build output.
|
||||
The dashboard source lives under ``apps/dashboard/``, but the Vite build
|
||||
still outputs to ``hermes_cli/web_dist/`` so Python packaging can continue
|
||||
serving the same static asset directory. Uses the Vite manifest as the
|
||||
sentinel because it is written last and therefore has the newest mtime of
|
||||
any build output.
|
||||
"""
|
||||
dist_dir = web_dir.parent / "hermes_cli" / "web_dist"
|
||||
project_root = web_dir.parent.parent if web_dir.parent.name == "apps" else web_dir.parent
|
||||
dist_dir = project_root / "hermes_cli" / "web_dist"
|
||||
sentinel = dist_dir / ".vite" / "manifest.json"
|
||||
if not sentinel.exists():
|
||||
sentinel = dist_dir / "index.html"
|
||||
@@ -5304,7 +5306,7 @@ def _build_web_ui(web_dir: Path, *, fatal: bool = False) -> bool:
|
||||
"""Build the web UI frontend if npm is available.
|
||||
|
||||
Args:
|
||||
web_dir: Path to the ``web/`` source directory.
|
||||
web_dir: Path to the dashboard frontend source directory.
|
||||
fatal: If True, print error guidance and return False on failure
|
||||
instead of a soft warning (used by ``hermes web``).
|
||||
|
||||
@@ -5320,7 +5322,7 @@ def _build_web_ui(web_dir: Path, *, fatal: bool = False) -> bool:
|
||||
if not npm:
|
||||
if fatal:
|
||||
print("Web UI frontend not built and npm is not available.")
|
||||
print("Install Node.js, then run: cd web && npm install && npm run build")
|
||||
print("Install Node.js, then run: cd apps/dashboard && npm install && npm run build")
|
||||
return not fatal
|
||||
print("→ Building web UI...")
|
||||
r1 = _run_npm_install_deterministic(npm, web_dir, extra_args=("--silent",))
|
||||
@@ -5330,7 +5332,7 @@ def _build_web_ui(web_dir: Path, *, fatal: bool = False) -> bool:
|
||||
+ ("" if fatal else " (hermes web will not be available)")
|
||||
)
|
||||
if fatal:
|
||||
print(" Run manually: cd web && npm install && npm run build")
|
||||
print(" Run manually: cd apps/dashboard && npm install && npm run build")
|
||||
return False
|
||||
r2 = subprocess.run([npm, "run", "build"], cwd=web_dir, capture_output=True)
|
||||
if r2.returncode != 0:
|
||||
@@ -5339,7 +5341,7 @@ def _build_web_ui(web_dir: Path, *, fatal: bool = False) -> bool:
|
||||
+ ("" if fatal else " (hermes web will not be available)")
|
||||
)
|
||||
if fatal:
|
||||
print(" Run manually: cd web && npm install && npm run build")
|
||||
print(" Run manually: cd apps/dashboard && npm install && npm run build")
|
||||
return False
|
||||
print(" ✓ Web UI built")
|
||||
return True
|
||||
@@ -5684,7 +5686,7 @@ def _update_via_zip(args):
|
||||
_install_python_dependencies_with_optional_fallback(pip_cmd)
|
||||
|
||||
_update_node_dependencies()
|
||||
_build_web_ui(PROJECT_ROOT / "web")
|
||||
_build_web_ui(PROJECT_ROOT / "apps" / "dashboard")
|
||||
|
||||
# Sync skills
|
||||
try:
|
||||
@@ -6269,10 +6271,14 @@ def _update_node_dependencies() -> None:
|
||||
if not (path / "package.json").exists():
|
||||
continue
|
||||
|
||||
extra_args = ["--silent", "--no-fund", "--no-audit", "--progress=false"]
|
||||
if path == PROJECT_ROOT:
|
||||
extra_args.append("--workspaces=false")
|
||||
|
||||
result = _run_npm_install_deterministic(
|
||||
npm,
|
||||
path,
|
||||
extra_args=("--silent", "--no-fund", "--no-audit", "--progress=false"),
|
||||
extra_args=tuple(extra_args),
|
||||
)
|
||||
if result.returncode == 0:
|
||||
print(f" ✓ {label}")
|
||||
@@ -6990,7 +6996,7 @@ def _cmd_update_impl(args, gateway_mode: bool):
|
||||
_install_python_dependencies_with_optional_fallback(pip_cmd)
|
||||
|
||||
_update_node_dependencies()
|
||||
_build_web_ui(PROJECT_ROOT / "web")
|
||||
_build_web_ui(PROJECT_ROOT / "apps" / "dashboard")
|
||||
|
||||
print()
|
||||
print("✓ Code updated!")
|
||||
@@ -8080,7 +8086,7 @@ def cmd_dashboard(args):
|
||||
sys.exit(1)
|
||||
|
||||
if "HERMES_WEB_DIST" not in os.environ:
|
||||
if not _build_web_ui(PROJECT_ROOT / "web", fatal=True):
|
||||
if not _build_web_ui(PROJECT_ROOT / "apps" / "dashboard", fatal=True):
|
||||
sys.exit(1)
|
||||
|
||||
from hermes_cli.web_server import start_server
|
||||
|
||||
@@ -3094,7 +3094,7 @@ async def get_models_analytics(days: int = 30):
|
||||
# The endpoint spawns the same ``hermes --tui`` binary the CLI uses, behind
|
||||
# a POSIX pseudo-terminal, and forwards bytes + resize escapes across a
|
||||
# WebSocket. The browser renders the ANSI through xterm.js (see
|
||||
# web/src/pages/ChatPage.tsx).
|
||||
# apps/dashboard/src/pages/ChatPage.tsx).
|
||||
#
|
||||
# Auth: ``?token=<session_token>`` query param (browsers can't set
|
||||
# Authorization on the WS upgrade). Same ephemeral ``_SESSION_TOKEN`` as
|
||||
@@ -3435,7 +3435,7 @@ def mount_spa(application: FastAPI):
|
||||
@application.get("/{full_path:path}")
|
||||
async def no_frontend(full_path: str):
|
||||
return JSONResponse(
|
||||
{"error": "Frontend not built. Run: cd web && npm run build"},
|
||||
{"error": "Frontend not built. Run: cd apps/dashboard && npm run build"},
|
||||
status_code=404,
|
||||
)
|
||||
return
|
||||
@@ -3477,7 +3477,7 @@ def mount_spa(application: FastAPI):
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Built-in dashboard themes — label + description only. The actual color
|
||||
# definitions live in the frontend (web/src/themes/presets.ts).
|
||||
# definitions live in the frontend (apps/dashboard/src/themes/presets.ts).
|
||||
_BUILTIN_DASHBOARD_THEMES = [
|
||||
{"name": "default", "label": "Hermes Teal", "description": "Classic dark teal — the canonical Hermes look"},
|
||||
{"name": "midnight", "label": "Midnight", "description": "Deep blue-violet with cool accents"},
|
||||
@@ -3729,7 +3729,7 @@ async def get_dashboard_themes():
|
||||
"""Return available themes and the currently active one.
|
||||
|
||||
Built-in entries ship name/label/description only (the frontend owns
|
||||
their full definitions in `web/src/themes/presets.ts`). User themes
|
||||
their full definitions in `apps/dashboard/src/themes/presets.ts`). User themes
|
||||
from `~/.hermes/dashboard-themes/*.yaml` ship with their full
|
||||
normalised definition under `definition`, so the client can apply
|
||||
them without a stub.
|
||||
|
||||
Reference in New Issue
Block a user