Bootstrap tamq local tmux agent message queue
Some checks failed
tamq-ci / test (push) Failing after 36s
Some checks failed
tamq-ci / test (push) Failing after 36s
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02f34-ead4-7a60-85cd-d7f08e22fa0e
This commit is contained in:
parent
521bbe5afc
commit
4d915a2617
58 changed files with 1882 additions and 1 deletions
77
src/tamq/tmux.py
Normal file
77
src/tamq/tmux.py
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shlex
|
||||
from uuid import uuid4
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .registry import repository_paths, validate_targets
|
||||
|
||||
|
||||
class TmuxError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Endpoint:
|
||||
session: str
|
||||
pid: int
|
||||
repos: list[str]
|
||||
instance_id: str = ""
|
||||
|
||||
@property
|
||||
def endpoint_id(self) -> str:
|
||||
return f"tmux-amq-{self.pid}"
|
||||
|
||||
@property
|
||||
def instance_key(self) -> str:
|
||||
return self.instance_id or self.endpoint_id
|
||||
|
||||
|
||||
class TmuxManager:
|
||||
def __init__(self, session: str = "tamq"):
|
||||
self.session = session
|
||||
|
||||
def _run(self, *args: str, check: bool = True) -> str:
|
||||
result = subprocess.run(["tmux", *args], text=True, capture_output=True, check=False)
|
||||
if check and result.returncode:
|
||||
raise TmuxError(result.stderr.strip() or "tmux command failed")
|
||||
return result.stdout.strip()
|
||||
|
||||
def ensure(self, repos: list[str], command: str = "codex") -> Endpoint:
|
||||
if not repos:
|
||||
raise TmuxError("at least one repository is required")
|
||||
try:
|
||||
validate_targets(repos)
|
||||
except Exception as exc:
|
||||
raise TmuxError(str(exc)) from exc
|
||||
result = subprocess.run(["tmux", "has-session", "-t", self.session], capture_output=True)
|
||||
if result.returncode != 0:
|
||||
self._run("new-session", "-d", "-s", self.session, "-n", "__tamq_boot", "-c", os.getcwd(), "sleep", "infinity")
|
||||
pid_text = self._run("display-message", "-p", "-t", self.session, "#{pid}")
|
||||
try:
|
||||
pid = int(pid_text)
|
||||
except ValueError as exc:
|
||||
raise TmuxError(f"unable to determine tmux PID: {pid_text!r}") from exc
|
||||
endpoint = Endpoint(self.session, pid, list(dict.fromkeys(repos)), f"tmux-amq-{pid}-{uuid4().hex[:12]}")
|
||||
paths = repository_paths()
|
||||
windows = self._run("list-windows", "-t", self.session, "-F", "#{window_name}").splitlines()
|
||||
for index, repo in enumerate(endpoint.repos):
|
||||
if repo not in windows:
|
||||
if index == 0 and "__tamq_boot" in windows:
|
||||
self._run("rename-window", "-t", f"{self.session}:__tamq_boot", repo)
|
||||
windows[windows.index("__tamq_boot")] = repo
|
||||
else:
|
||||
try:
|
||||
repo_path = paths[repo]
|
||||
except KeyError as exc:
|
||||
raise TmuxError(f"gita path missing for registered repository: {repo}") from exc
|
||||
self._run("new-window", "-d", "-t", self.session, "-n", repo, "-c", repo_path, "sleep", "infinity")
|
||||
tap = ["tamq", "tap", "--repo", repo, "--endpoint", endpoint.endpoint_id, "--", command]
|
||||
self._run("send-keys", "-t", f"{self.session}:{repo}", shell_join(tap), "C-m")
|
||||
return endpoint
|
||||
|
||||
|
||||
def shell_join(parts: list[str]) -> str:
|
||||
return " ".join(shlex.quote(part) for part in parts)
|
||||
Loading…
Add table
Add a link
Reference in a new issue