feat: add shell-native message routing
Some checks failed
tamq-ci / test (push) Failing after 5s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a03397-4d51-7fd1-8ff2-946eb22ea2bc
This commit is contained in:
tegwick 2026-08-24 20:42:35 +02:00
parent 398fe316b4
commit e995cab1b8
12 changed files with 391 additions and 42 deletions

View file

@ -1,6 +1,7 @@
from __future__ import annotations
import os
import re
import shlex
import shutil
from uuid import uuid4
@ -9,6 +10,7 @@ from collections.abc import Sequence
from dataclasses import dataclass
from pathlib import Path
from .config import state_dir
from .registry import repository_paths, validate_targets
@ -48,11 +50,65 @@ class TmuxManager:
*,
tmux_command: Sequence[str] | None = None,
tamq_command: Sequence[str] | None = None,
command_dir: Path | None = None,
):
self.session = session
socket_name = os.environ.get("TAMQ_TMUX_SOCKET")
self.tmux_command = tuple(tmux_command or (["tmux", "-L", socket_name] if socket_name else ["tmux"]))
self.tamq_command = tuple(tamq_command or ["tamq"])
if tamq_command is None:
self.tamq_command = (shutil.which("tamq") or "tamq",)
else:
self.tamq_command = tuple(tamq_command)
configured_command_dir = os.environ.get("TAMQ_COMMAND_DIR")
if command_dir is not None:
self.command_dir = command_dir
elif configured_command_dir:
self.command_dir = Path(configured_command_dir)
elif (
len(self.tamq_command) == 1
and Path(self.tamq_command[0]).is_absolute()
and os.access(Path(self.tamq_command[0]).parent, os.W_OK | os.X_OK)
):
self.command_dir = Path(self.tamq_command[0]).parent
else:
self.command_dir = state_dir() / "commands" / session
def _install_address_commands(self, repos: Sequence[str]) -> None:
"""Install tamq-owned commands without modifying a user's shell files."""
self.command_dir.mkdir(parents=True, exist_ok=True, mode=0o700)
if not os.access(self.command_dir, os.W_OK | os.X_OK):
raise TmuxError(f"address command directory is not writable: {self.command_dir}")
for repo in repos:
if re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", repo) is None:
raise TmuxError(
f"repository name cannot be exposed as a shell command: {repo!r}"
)
target = f"@{repo}:"
script = (
"#!/bin/sh\n"
"# tamq-address-command v1\n"
f"exec {shell_join(list(self.tamq_command))} send -- {shlex.quote(target)} \"$@\"\n"
)
for name in (f"@{repo}", target):
destination = self.command_dir / name
if destination.exists() or destination.is_symlink():
if destination.is_symlink() or not destination.is_file():
raise TmuxError(f"refusing to replace existing command: {destination}")
try:
existing = destination.read_text(encoding="utf-8")
except OSError as exc:
raise TmuxError(f"cannot inspect existing command: {destination}") from exc
if "# tamq-address-command v1" not in existing:
raise TmuxError(f"refusing to replace existing command: {destination}")
temporary = self.command_dir / f".{name}.{uuid4().hex}.tmp"
temporary.write_text(script, encoding="utf-8")
temporary.chmod(0o700)
temporary.replace(destination)
def _window_environment(self, repo: str) -> tuple[str, ...]:
path = os.environ.get("PATH", "")
command_path = f"{self.command_dir}{os.pathsep}{path}" if path else str(self.command_dir)
return "-e", f"TAMQ_REPO={repo}", "-e", f"PATH={command_path}"
def _run(self, *args: str, check: bool = True) -> str:
result = subprocess.run([*self.tmux_command, *args], text=True, capture_output=True, check=False)
@ -133,7 +189,7 @@ class TmuxManager:
first_repo = plan.repos[0]
self._run(
"new-session", "-d", "-s", self.session, "-n", "__tamq_boot",
"-e", f"TAMQ_REPO={first_repo}",
*self._window_environment(first_repo),
"-c", plan.paths[first_repo],
)
created_session = True
@ -151,6 +207,12 @@ class TmuxManager:
self._run("set-option", "-t", self.session, "@tamq_instance_id", instance_id)
self._run("set-option", "-t", self.session, "@tamq_managed", "1")
windows = self._run("list-windows", "-t", self.session, "-F", "#{window_name}").splitlines()
addressable_repos = [repo for repo in windows if repo != "__tamq_boot"]
addressable_repos.extend(repo for repo in plan.repos if repo not in addressable_repos)
self._install_address_commands(addressable_repos)
command_path = self._window_environment(plan.repos[0])[-1].removeprefix("PATH=")
self._run("set-environment", "-t", self.session, "PATH", command_path)
self._run("set-option", "-t", self.session, "@tamq_command_dir", str(self.command_dir))
for index, repo in enumerate(plan.repos):
if repo in windows:
continue
@ -162,7 +224,7 @@ class TmuxManager:
else:
self._run(
"new-window", "-d", "-t", self.session, "-n", repo,
"-e", f"TAMQ_REPO={repo}", "-c", plan.paths[repo],
*self._window_environment(repo), "-c", plan.paths[repo],
)
windows.append(repo)
created_windows.append(repo)