artifact presenter & global Agents updates & plan for nono profiles

This commit is contained in:
jb
2026-08-26 21:57:56 -04:00
parent 645c38d43c
commit 51eace1fa8
7 changed files with 1723 additions and 91 deletions
@@ -4,15 +4,44 @@
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"
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]
@@ -74,43 +103,174 @@ def created_pane_id(response: Mapping[str, object]) -> str:
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,
) -> None:
run(
[herdr, "pane", "run", pane_id, mcat, str(artifact)],
check=True,
capture_output=True,
text=True,
)
def present_document(
document: Path,
*,
env: Mapping[str, str] = os.environ,
which: FindExecutable = shutil.which,
run: RunCommand = subprocess.run,
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:
artifact = document.expanduser().resolve()
if not artifact.is_file():
raise ValueError(f"artifact is not a file: {artifact}")
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")
try:
pane_list = run_json(herdr, ["pane", "list", "--workspace", workspace_id], run)
existing_panes = panes_with_label(pane_list)
@@ -119,9 +279,9 @@ def present_document(
for existing_pane in existing_panes:
try:
run_mcat(herdr, mcat, existing_pane, artifact, run)
run_mcat(herdr, mcat, existing_pane, artifact, run, reused=True)
return PresentationResult("reused", artifact, pane_id=existing_pane)
except (OSError, subprocess.SubprocessError):
except (OSError, subprocess.SubprocessError, ValueError):
try:
run(
[herdr, "pane", "rename", existing_pane, f"{PANE_LABEL} stale"],
@@ -161,7 +321,7 @@ def present_document(
)
run_mcat(herdr, mcat, pane_id, artifact, run)
return PresentationResult("opened", artifact, pane_id=pane_id)
except (OSError, subprocess.SubprocessError) as error:
except (OSError, subprocess.SubprocessError, ValueError) as error:
try:
run(
[herdr, "pane", "close", pane_id],
@@ -174,6 +334,39 @@ def present_document(
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}")
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")