Squashed integration of the user-facing message audit for this surface set. Full per-finding receipts: /tmp/ux-audit/lanes/*-receipt.md (campaign artifacts).
49 lines
2.0 KiB
Python
49 lines
2.0 KiB
Python
"""User-facing copy for dashboard chat (``/api/pty``) start failures.
|
|
|
|
``pty_ws`` writes one red line into the terminal and closes with 1011 when the
|
|
``hermes --tui`` child cannot be spawned. The exception text alone is either
|
|
raw errno noise (``[Errno 2] No such file or directory: 'node'``), a bare exit
|
|
code (``SystemExit(1)`` after ``_make_tui_argv`` already printed to the server
|
|
log), or empty (``RegistryFull``). Turn each into what happened + what to do.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from fastapi import HTTPException
|
|
|
|
from hermes_cli.pty_session import RegistryFull
|
|
|
|
CHAT_NEEDS_NODE = (
|
|
"Chat could not start: Hermes needs Node.js to run the terminal chat. "
|
|
"Install Node 18+ (for example from nodejs.org) and reopen this tab."
|
|
)
|
|
CHAT_TOO_MANY_TERMINALS = (
|
|
"Chat could not start: too many chat terminals are open in other tabs. "
|
|
"Close one and click Start new session."
|
|
)
|
|
CHAT_PROFILE_UNKNOWN = "Chat could not start: {detail} Pick another profile from the switcher and reopen this tab."
|
|
CHAT_START_FAILED = "Chat could not start: {detail} Check the server log (`hermes dashboard` terminal) and click Start new session."
|
|
|
|
|
|
def _node_missing(exc: BaseException) -> bool:
|
|
text = str(exc)
|
|
return isinstance(exc, SystemExit) or "'node'" in text or "'npm'" in text or "Node.js" in text
|
|
|
|
|
|
def chat_start_failure_message(exc: BaseException) -> str:
|
|
"""One plain sentence for the terminal pane; never the bare exception."""
|
|
if isinstance(exc, RegistryFull):
|
|
return CHAT_TOO_MANY_TERMINALS
|
|
if isinstance(exc, HTTPException):
|
|
return CHAT_PROFILE_UNKNOWN.format(detail=_sentence(str(exc.detail)))
|
|
if _node_missing(exc):
|
|
return CHAT_NEEDS_NODE
|
|
return CHAT_START_FAILED.format(detail=_sentence(str(exc) or type(exc).__name__))
|
|
|
|
|
|
def _sentence(text: str) -> str:
|
|
text = text.strip()
|
|
if not text:
|
|
return "the terminal process could not be launched."
|
|
return text if text.endswith((".", "!", "?")) else f"{text}."
|