fix(tui): use a named SSH profile's working directory in the desktop app
The app showed the profile's terminal.cwd, but SSH sessions still ran in the launch profile's directory because a remote path that does not exist on the desktop host was discarded. (cherry picked from commit e4f4a43ebc4408adb6ac37e8de1ee7ff414a9158)
This commit is contained in:
@@ -965,7 +965,11 @@ def _(rid, params: dict) -> dict:
|
||||
_resume_follow_tip(ctx)
|
||||
if (resp := _resume_guard(ctx)) is not None:
|
||||
return resp
|
||||
ctx.profile_resume_cwd = _str_param(ctx.found, "cwd") or _profile_configured_cwd(ctx.profile_home)
|
||||
ctx.profile_resume_cwd = (
|
||||
_str_param(ctx.found, "cwd")
|
||||
or _profile_configured_cwd(ctx.profile_home)
|
||||
or _declared_remote_profile_cwd(ctx.profile_home)
|
||||
)
|
||||
# Fast path: reuse a session live IN THIS PROFILE (never another profile's runtime).
|
||||
with _session_resume_lock:
|
||||
live = _find_live_session_by_key(ctx.target, ctx.profile_home)
|
||||
|
||||
@@ -30,9 +30,15 @@ def _completion_cwd(params: dict | None = None) -> str:
|
||||
# cannot tell the two apart, so the desktop ships the flag alongside the path.
|
||||
client_cwd = params.get("cwd")
|
||||
if not params.get("cwd_explicit") and client_cwd:
|
||||
profile_cwd = _profile_configured_cwd(_profile_home(params.get("profile")))
|
||||
profile_home = _profile_home(params.get("profile"))
|
||||
profile_cwd = _profile_configured_cwd(profile_home)
|
||||
if profile_cwd:
|
||||
return profile_cwd
|
||||
# SSH cwd usually does not exist on the desktop host, so the isdir
|
||||
# check above drops it and the launch profile's workspace wins.
|
||||
remote_cwd = _declared_remote_profile_cwd(profile_home)
|
||||
if remote_cwd:
|
||||
return remote_cwd
|
||||
# A session bound to another profile resolves its workspace from THAT profile's config before the launch profile's
|
||||
# env var; the dashboard's in-memory gateway does NOT inherit the PTY child's bridged TERMINAL_CWD, so a configured
|
||||
# terminal.cwd is read directly.
|
||||
@@ -52,6 +58,10 @@ def _completion_cwd(params: dict | None = None) -> str:
|
||||
return resolved
|
||||
if os.path.isdir(resolved):
|
||||
return resolved
|
||||
if not params.get("cwd_explicit"):
|
||||
remote_cwd = _declared_remote_profile_cwd(_profile_home(params.get("profile")))
|
||||
if remote_cwd:
|
||||
return remote_cwd
|
||||
return os.getcwd()
|
||||
|
||||
|
||||
@@ -64,6 +74,48 @@ def _workdir_terminal_cfg(key: str) -> str:
|
||||
return ""
|
||||
|
||||
|
||||
_REMOTE_CWD_PLACEHOLDERS = {".", "./", "auto", "cwd"}
|
||||
|
||||
|
||||
def _profile_terminal_section(profile_home) -> dict:
|
||||
"""``terminal:`` from a profile's own config.yaml, or {}."""
|
||||
if not profile_home:
|
||||
return {}
|
||||
with contextlib.suppress(Exception):
|
||||
from pathlib import Path
|
||||
|
||||
from hermes_cli.config_effective import load_user_config_effective
|
||||
|
||||
path = Path(profile_home) / "config.yaml"
|
||||
if not path.is_file():
|
||||
return {}
|
||||
cfg = load_user_config_effective(path)
|
||||
terminal = cfg.get("terminal") if isinstance(cfg, dict) else None
|
||||
return terminal if isinstance(terminal, dict) else {}
|
||||
return {}
|
||||
|
||||
|
||||
def _declared_remote_profile_cwd(profile_home) -> str | None:
|
||||
"""A non-local profile's ``terminal.cwd``, kept when the path is not on this host.
|
||||
|
||||
``_profile_configured_cwd`` requires ``os.path.isdir``. An SSH working
|
||||
directory lives on the remote, so that check drops it and the desktop
|
||||
keeps using the launch profile's ``TERMINAL_CWD``.
|
||||
"""
|
||||
terminal = _profile_terminal_section(profile_home)
|
||||
if not terminal:
|
||||
return None
|
||||
backend = str(terminal.get("backend") or "").strip().lower() or _effective_terminal_backend()
|
||||
if not backend or backend == "local":
|
||||
return None
|
||||
raw = str(terminal.get("cwd") or "").strip()
|
||||
if not raw or raw in _REMOTE_CWD_PLACEHOLDERS:
|
||||
return None
|
||||
if raw == "~" or raw.startswith("~/") or os.path.isabs(raw):
|
||||
return raw
|
||||
return None
|
||||
|
||||
|
||||
def _terminal_task_cwd(session: dict | None) -> str:
|
||||
"""The cwd terminal_tool should use for this TUI session (NOT host-validated: a non-local backend's cwd lives
|
||||
inside the target environment)."""
|
||||
@@ -79,6 +131,12 @@ def _terminal_task_cwd_with_source(session: dict | None) -> tuple[str, str]:
|
||||
# THIS session's explicit workspace beats the LAST session's env var.
|
||||
if session and session.get("explicit_cwd") and session.get("cwd"):
|
||||
return str(session["cwd"]), "session"
|
||||
# Process TERMINAL_CWD is the launch profile. A named SSH profile's
|
||||
# terminal.cwd is on the remote and must not lose to that env var.
|
||||
if backend == "ssh":
|
||||
remote_cwd = _declared_remote_profile_cwd((session or {}).get("profile_home"))
|
||||
if remote_cwd:
|
||||
return remote_cwd, "session"
|
||||
raw = os.environ.get("TERMINAL_CWD", "").strip() or _workdir_terminal_cfg("cwd")
|
||||
if raw and raw not in {".", "auto", "cwd"}:
|
||||
return raw, "process"
|
||||
|
||||
Reference in New Issue
Block a user