Merge remote-tracking branch 'origin/main' into hermes/hermes-6b48295e

This commit is contained in:
Teknium
2026-06-11 07:38:25 -07:00
239 changed files with 18356 additions and 2494 deletions
+83 -2
View File
@@ -1623,7 +1623,11 @@ def _make_tui_argv(tui_dir: Path, tui_dev: bool) -> tuple[list[str], Path]:
npm_cwd = _workspace_root(tui_dir)
# --workspace ui-tui avoids resolving apps/desktop (Electron + node-pty).
# See #38772.
npm_workspace_args: tuple[str, ...] = ("--workspace", "ui-tui")
# When ui-tui/ has its own package-lock.json (e.g. curl install),
# _workspace_root() returns tui_dir itself. Passing --workspace in
# that case fails because npm cannot find a workspace named "ui-tui"
# inside ui-tui/. See #42973.
npm_workspace_args: tuple[str, ...] = () if npm_cwd == tui_dir else ("--workspace", "ui-tui")
if termux_startup:
npm_cwd, npm_workspace_args = _termux_workspace_install_context(
tui_dir,
@@ -4661,7 +4665,9 @@ def _build_web_ui(web_dir: Path, *, fatal: bool = False) -> bool:
# graph (including apps/desktop with its Electron + node-pty deps) is never
# resolved here. Without --workspace the root package.json's apps/* glob
# would pull in desktop on every web build. See #38772.
npm_workspace_args: tuple[str, ...] = ("--workspace", "web")
# When web/ has its own package-lock.json, _workspace_root() returns
# web_dir itself and --workspace would fail. See #42973.
npm_workspace_args: tuple[str, ...] = () if npm_cwd == web_dir else ("--workspace", "web")
if _is_termux_startup_environment():
npm_cwd, npm_workspace_args = _termux_workspace_install_context(web_dir)
r1 = _run_npm_install_deterministic(
@@ -10234,6 +10240,21 @@ def _report_dashboard_status() -> int:
return len(pids)
def _dashboard_listening(host: str, port: int) -> bool:
"""True when something is accepting TCP connections at host:port.
Any listener counts even a 401 response proves a dashboard is up.
Used by the unified profile-launch routing to decide attach-vs-start.
"""
import socket
try:
with socket.create_connection((host or "127.0.0.1", port), timeout=1.5):
return True
except OSError:
return False
def cmd_dashboard(args):
"""Start the web UI server, or (with --stop/--status) manage running ones."""
# --status: report running dashboards and exit, no deps needed.
@@ -10254,6 +10275,65 @@ def cmd_dashboard(args):
remaining = _find_stale_dashboard_pids()
sys.exit(1 if remaining else 0)
# ── Unified profile launch routing ────────────────────────────────
# The dashboard is a MACHINE management surface: it can read/write any
# profile via the per-request ?profile= scoping. Running one dashboard
# per profile just fragments that (port collisions, N processes, and a
# "which dashboard am I on?" guessing game). So when a NAMED profile
# launches the dashboard (`worker dashboard` → HERMES_HOME points into
# profiles/), default to the machine dashboard:
# - already running → open the browser at ?profile=<name> and exit
# - not running → re-exec as the machine dashboard (pinned to the
# default profile so _apply_profile_override can't re-route through
# the sticky active_profile file) with the launching profile
# preselected in the UI's switcher.
# `--isolated` opts out and preserves the old per-profile behavior.
try:
from hermes_cli.profiles import get_active_profile_name
_launch_profile = get_active_profile_name()
except Exception:
_launch_profile = "default"
if (
_launch_profile not in ("default", "custom")
and not getattr(args, "isolated", False)
and not getattr(args, "open_profile", "")
):
url = f"http://{args.host or '127.0.0.1'}:{args.port}/?profile={_launch_profile}"
if _dashboard_listening(args.host, args.port):
print(f"Machine dashboard already running on port {args.port}.")
print(f" Managing profile '{_launch_profile}': {url}")
if not args.no_open:
try:
import webbrowser
webbrowser.open(url)
except Exception:
pass
sys.exit(0)
print(
f"Routing to the machine dashboard (profile '{_launch_profile}' "
f"preselected). Use --isolated for a dedicated per-profile server."
)
reexec_argv = [
sys.executable, "-m", "hermes_cli.main",
"-p", "default",
"dashboard",
"--port", str(args.port),
"--host", args.host,
"--open-profile", _launch_profile,
]
if args.no_open:
reexec_argv.append("--no-open")
if getattr(args, "insecure", False):
reexec_argv.append("--insecure")
if getattr(args, "skip_build", False):
reexec_argv.append("--skip-build")
env = os.environ.copy()
# Drop the profile HERMES_HOME so the child binds the machine root.
env.pop("HERMES_HOME", None)
os.execvpe(sys.executable, reexec_argv, env)
# Attach gui.log early so dashboard startup/build failures are captured in
# the same logs directory as every other Hermes surface.
try:
@@ -10327,6 +10407,7 @@ def cmd_dashboard(args):
port=args.port,
open_browser=not args.no_open,
allow_public=getattr(args, "insecure", False),
initial_profile=getattr(args, "open_profile", "") or "",
)