#!/usr/bin/env python3 """Present one review artifact through Orca or a reusable Herdr mcat pane.""" from __future__ import annotations import argparse import base64 import fcntl import hashlib import json import os import re import secrets import shutil import subprocess import tempfile import time from contextlib import contextmanager from dataclasses import dataclass from pathlib import Path from typing import Callable, Mapping, Sequence PANE_LABEL = "artifact presenter" VIEWER_PROCESS_NAME = "mcat" TEXT_SAMPLE_BYTES = 8192 ORCA_SESSION_ENVIRONMENT_KEYS = ( "ORCA_WORKSPACE_ID", "ORCA_WORKTREE_ID", "ORCA_PANE_KEY", "ORCA_TERMINAL_HANDLE", ) PANE_READY_ATTEMPTS = 40 PANE_READY_DELAY_SECONDS = 0.05 PANE_SHELL_STABLE_CHECKS = 2 VIEWER_START_ATTEMPTS = 40 SAFE_COMMAND_TOKEN = re.compile(r"^[A-Za-z0-9_./=-]+$") VIEWER_LAUNCH_SCRIPT = """#!/usr/bin/env python3 import base64 import os import sys from pathlib import Path def decode(value: str) -> str: return base64.urlsafe_b64decode(value.encode("ascii")).decode("utf-8") acknowledgment, token, viewer, artifact = map(decode, sys.argv[1:]) Path(acknowledgment).write_text(token, encoding="utf-8") os.execv(viewer, [viewer, artifact]) """ RunCommand = Callable[..., subprocess.CompletedProcess[str]] FindExecutable = Callable[[str], str | None] @dataclass(frozen=True) class PresentationResult: status: str artifact: Path pane_id: str | None = None reason: str | None = None def message(self) -> str: if self.status == "artifact-only": return f"artifact-only: {self.reason}; {self.artifact}" if self.status == "orca-opened": return f"orca-opened: {self.artifact}" return f"{self.status}: pane {self.pane_id}; {self.artifact}" def is_orca_managed_session(env: Mapping[str, str]) -> bool: """Return whether this process belongs to an Orca-managed terminal session.""" if env.get("TERM_PROGRAM", "").casefold() == "orca": return True return any(env.get(key) for key in ORCA_SESSION_ENVIRONMENT_KEYS) def is_editor_friendly_text(artifact: Path) -> bool: """Recognize UTF-8 text without maintaining a source-extension allowlist.""" with artifact.open("rb") as handle: sample = handle.read(TEXT_SAMPLE_BYTES) if b"\0" in sample: return False try: sample.decode("utf-8") except UnicodeDecodeError: return False return True def orca_executable( env: Mapping[str, str], which: FindExecutable, ) -> str | None: configured = env.get("ORCA_CLI_COMMAND") if configured: return which(configured) if env.get("ORCA_DEV_REPO_ROOT"): return which("orca-dev") return which("orca") def open_in_orca( orca: str, artifact: Path, run: RunCommand, ) -> PresentationResult: completed = run( [orca, "file", "open", str(artifact), "--json"], check=True, capture_output=True, text=True, ) response = json.loads(completed.stdout) result = response.get("result") if isinstance(response, dict) else None if ( not isinstance(response, dict) or response.get("ok") is not True or not isinstance(result, dict) or result.get("opened") is not True ): raise ValueError("Orca did not confirm that it opened the file") return PresentationResult("orca-opened", artifact) def run_json( executable: str, args: Sequence[str], run: RunCommand, ) -> dict[str, object]: completed = run( [executable, *args], check=True, capture_output=True, text=True, ) value = json.loads(completed.stdout) if not isinstance(value, dict): raise ValueError("Herdr returned a non-object response") return value def panes_with_label(response: Mapping[str, object]) -> list[str]: result = response.get("result") if not isinstance(result, dict): return [] panes = result.get("panes") if not isinstance(panes, list): return [] pane_ids: list[str] = [] for pane in panes: if not isinstance(pane, dict) or pane.get("label") != PANE_LABEL: continue pane_id = pane.get("pane_id") if isinstance(pane_id, str) and pane_id: pane_ids.append(pane_id) return pane_ids def created_pane_id(response: Mapping[str, object]) -> str: result = response.get("result") pane = result.get("pane") if isinstance(result, dict) else None pane_id = pane.get("pane_id") if isinstance(pane, dict) else None if not isinstance(pane_id, str) or not pane_id: raise ValueError("Herdr split response is missing result.pane.pane_id") return pane_id @dataclass(frozen=True) class PaneProcessState: shell_pid: int foreground_pids: frozenset[int] foreground_names: frozenset[str] @property def shell_is_ready(self) -> bool: return self.shell_pid in self.foreground_pids @property def viewer_is_active(self) -> bool: return VIEWER_PROCESS_NAME in self.foreground_names def pane_process_state(response: Mapping[str, object]) -> PaneProcessState: result = response.get("result") process_info = result.get("process_info") if isinstance(result, dict) else None if not isinstance(process_info, dict): raise ValueError("Herdr process response is missing process info") shell_pid = process_info.get("shell_pid") processes = process_info.get("foreground_processes") if not isinstance(shell_pid, int) or not isinstance(processes, list): raise ValueError("Herdr process response is missing shell state") pids: set[int] = set() names: set[str] = set() for process in processes: if not isinstance(process, dict): continue pid = process.get("pid") name = process.get("name") if isinstance(pid, int): pids.add(pid) if isinstance(name, str) and name: names.add(name) return PaneProcessState(shell_pid, frozenset(pids), frozenset(names)) def prepare_pane_for_command( herdr: str, pane_id: str, run: RunCommand, *, allow_startup_transition: bool, ) -> None: viewer_closed = False stable_shell_checks = 0 for attempt in range(PANE_READY_ATTEMPTS): response = run_json(herdr, ["pane", "process-info", "--pane", pane_id], run) state = pane_process_state(response) if state.shell_is_ready: stable_shell_checks += 1 if stable_shell_checks >= PANE_SHELL_STABLE_CHECKS: return else: stable_shell_checks = 0 if state.viewer_is_active: if not viewer_closed: run( [herdr, "pane", "send-keys", pane_id, "q"], check=True, capture_output=True, text=True, ) viewer_closed = True elif ( state.foreground_pids and not state.shell_is_ready and not viewer_closed and not allow_startup_transition ): processes = ", ".join(sorted(state.foreground_names)) or "unknown" raise ValueError(f"pane is running an unrelated foreground process: {processes}") if attempt + 1 < PANE_READY_ATTEMPTS: time.sleep(PANE_READY_DELAY_SECONDS) raise ValueError("pane shell did not become ready for the artifact command") def encode_argument(value: str) -> str: return base64.urlsafe_b64encode(value.encode("utf-8")).decode("ascii") @contextmanager def lock_pane(pane_id: str): runtime_directory = Path(os.environ.get("XDG_RUNTIME_DIR", tempfile.gettempdir())) lock_directory = runtime_directory / "pi-artifact-presenter-locks" lock_directory.mkdir(mode=0o700, exist_ok=True) lock_name = hashlib.sha256(pane_id.encode("utf-8")).hexdigest() + ".lock" descriptor = os.open( lock_directory / lock_name, os.O_CREAT | os.O_RDWR | os.O_CLOEXEC, 0o600, ) with os.fdopen(descriptor, "w") as handle: fcntl.flock(handle, fcntl.LOCK_EX) try: yield finally: fcntl.flock(handle, fcntl.LOCK_UN) def run_mcat( herdr: str, mcat: str, pane_id: str, artifact: Path, run: RunCommand, *, reused: bool = False, ) -> None: with lock_pane(pane_id): prepare_pane_for_command( herdr, pane_id, run, allow_startup_transition=not reused, ) token = secrets.token_hex(12) with tempfile.TemporaryDirectory( prefix="pi-artifact-presenter-", dir="/tmp", ) as acknowledgment_directory: temporary = Path(acknowledgment_directory) acknowledgment = temporary / "viewer-started" launcher = temporary / "launch-viewer.py" launcher.write_text(VIEWER_LAUNCH_SCRIPT, encoding="utf-8") command_parts = [ "python3", str(launcher), encode_argument(str(acknowledgment)), encode_argument(token), encode_argument(mcat), encode_argument(str(artifact)), ] if not all(SAFE_COMMAND_TOKEN.fullmatch(part) for part in command_parts): raise ValueError("viewer launcher produced an unsafe terminal token") command = " ".join(command_parts) run( [herdr, "pane", "send-text", pane_id, command + "\r"], check=True, capture_output=True, text=True, ) for attempt in range(VIEWER_START_ATTEMPTS): try: if acknowledgment.read_text(encoding="utf-8") == token: return except FileNotFoundError: pass if attempt + 1 < VIEWER_START_ATTEMPTS: time.sleep(PANE_READY_DELAY_SECONDS) raise ValueError("artifact command was delivered but did not start") def present_in_workspace( artifact: Path, herdr: str, mcat: str, workspace_id: str, source_pane_id: str, run: RunCommand, ) -> PresentationResult: try: pane_list = run_json(herdr, ["pane", "list", "--workspace", workspace_id], run) existing_panes = panes_with_label(pane_list) except (json.JSONDecodeError, OSError, subprocess.SubprocessError, ValueError) as error: return PresentationResult("artifact-only", artifact, reason=f"could not list Herdr panes: {error}") for existing_pane in existing_panes: try: run_mcat(herdr, mcat, existing_pane, artifact, run, reused=True) return PresentationResult("reused", artifact, pane_id=existing_pane) except (OSError, subprocess.SubprocessError, ValueError): try: run( [herdr, "pane", "rename", existing_pane, f"{PANE_LABEL} stale"], check=False, capture_output=True, text=True, ) except OSError: pass try: split = run_json( herdr, [ "pane", "split", "--pane", source_pane_id, "--direction", "right", "--cwd", str(artifact.parent), "--no-focus", ], run, ) pane_id = created_pane_id(split) except (json.JSONDecodeError, OSError, subprocess.SubprocessError, ValueError) as error: return PresentationResult("artifact-only", artifact, reason=f"could not create Herdr viewer: {error}") try: run( [herdr, "pane", "rename", pane_id, PANE_LABEL], check=True, capture_output=True, text=True, ) run_mcat(herdr, mcat, pane_id, artifact, run) return PresentationResult("opened", artifact, pane_id=pane_id) except (OSError, subprocess.SubprocessError, ValueError) as error: try: run( [herdr, "pane", "close", pane_id], check=False, capture_output=True, text=True, ) except OSError: pass return PresentationResult("artifact-only", artifact, reason=f"could not initialize Herdr viewer: {error}") def present_document( document: Path, *, env: Mapping[str, str] = os.environ, which: FindExecutable = shutil.which, run: RunCommand = subprocess.run, ) -> PresentationResult: artifact = document.expanduser().resolve() if not artifact.is_file(): raise ValueError(f"artifact is not a file: {artifact}") if is_orca_managed_session(env) and is_editor_friendly_text(artifact): orca = orca_executable(env, which) if orca: try: return open_in_orca(orca, artifact, run) except (json.JSONDecodeError, OSError, subprocess.SubprocessError, ValueError): pass herdr = which("herdr") mcat = which("mcat") workspace_id = env.get("HERDR_WORKSPACE_ID") source_pane_id = env.get("HERDR_PANE_ID") if not herdr: return PresentationResult("artifact-only", artifact, reason="herdr is unavailable") if not mcat: return PresentationResult("artifact-only", artifact, reason="mcat is unavailable") if not workspace_id or not source_pane_id: return PresentationResult("artifact-only", artifact, reason="not running in a Herdr pane") with lock_pane(f"workspace:{workspace_id}"): return present_in_workspace( artifact, herdr, mcat, workspace_id, source_pane_id, run, ) def create_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("artifact", type=Path, help="Local file to present with mcat") return parser def main() -> int: args = create_parser().parse_args() try: result = present_document(args.artifact) except ValueError as error: print(error, file=os.sys.stderr) return 2 print(result.message()) return 0 if __name__ == "__main__": raise SystemExit(main())