fix(web_server): resolve dashboard PTY profile from sticky active_profile on reload - #33056
fix(web_server): resolve dashboard PTY profile from sticky active_profile on reload#33056RobinAngele wants to merge 2 commits into
Conversation
|
The profile resolution fix in I found two issues worth addressing before merge: 1. Bundled CR→LF change in The diff modifies # xterm.js sends bare CR for Enter, Ink TUI needs LF to submit.
if data == b"\r":
data = b"\n"This is a separate behavioral change from the profile resolution fix. The PR body says "2 additions in 2. Profile path traversal — no validation on _active = _sticky.read_text().strip()
...
_pd = _Path.home() / ".hermes" / "profiles" / _active
if _pd.is_dir():
env["HERMES_HOME"] = str(_pd)If the sticky file contains _pd = (_Path.home() / ".hermes" / "profiles" / _active).resolve()
if _pd.is_dir() and str(_pd).startswith(str(_Path.home() / ".hermes" / "profiles")): |
|
Thanks for the thorough review! Both issues fixed in f025f9f:
Updated code: elif _active:
_pd = (_Path.home() / ".hermes" / "profiles" / _active).resolve()
_profiles_root = (_Path.home() / ".hermes" / "profiles").resolve()
if _pd.is_dir() and str(_pd).startswith(str(_profiles_root) + _pd._flavour.sep):
env["HERMES_HOME"] = str(_pd) |
f025f9f to
5cdce5f
Compare
d037f15 to
069555f
Compare
…oint + public active-profile endpoint
1. _resolve_chat_argv() reads ~/.hermes/active_profile sticky file and sets
HERMES_HOME before spawning the PTY. Handles named profiles and default
(missing file = default profile). Includes path traversal guard via
.resolve() + boundary check.
2. POST /api/profiles/{name}/activate - activates a profile persistently
(sticky across restarts). Delegates validation to set_active_profile()
from hermes_cli.profiles (no redundant pre-validation).
3. GET /api/active-profile - public endpoint (no auth) for frontend to
read the current active profile. Added to _PUBLIC_API_PATHS.
Fixes: PTY ignoring switched profile after page reload.
Related: NousResearch#30815
…tion - Add _require_token(request) so only authenticated sessions can switch profiles - Add profile_exists check with 404 for unknown profile names - Return active_profile + profile_dir in response (matches tested server behaviour) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
069555f to
9c5b6ed
Compare
This PR fixes the core backend bug that blocked the profile-switcher feature (frontend in #33739). Without this fix, switching profiles via the dashboard and reloading silently reverts to the wrong profile.
Problem
When a dashboard profile switcher calls
POST /api/profiles/{name}/activateand the page reloads, the PTY (chat terminal) spawns with the dashboard startup profile instead of the newly activated one. Switching to "default" is silently broken:set_active_profile("default")deletes the sticky file, so the PTY falls back to the startup profile.Root Cause
_resolve_chat_argv()spawns the PTY usingos.environ.copy()— inheriting the dashboard process'sHERMES_HOME. It never reads~/.hermes/active_profile.Fix — 40 additions in
hermes_cli/web_server.py, 0 deletions1.
POST /api/profiles/{name}/activateendpoint (auth-gated)Adds the activate endpoint. Uses
profiles_mod.set_active_profile()to write the sticky file, normalizes the profile name, and returns 404 for unknown profiles.2. Read sticky file in
_resolve_chat_argv()(inserted beforeif sidecar_url:)Reads
~/.hermes/active_profileand setsHERMES_HOMEto the correct profile directory..resolve()and verifies it stays within~/.hermes/profiles/."default"(root~/.hermes) when no sticky file exists.elif _active:guards empty-string edge case.3. Public
GET /api/active-profileendpoint — added to_PUBLIC_API_PATHSSimple public endpoint — no auth needed, reads world-readable text file. Returns
{"name": "..."}or{"name": "default"}.Testing (verified on live v0.14.0, Debian 12)
Relationship to other PRs and issues
Also related (no merge dependency):