feat: deliver installable local alpha sessions
Some checks failed
tamq-ci / test (push) Failing after 6s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a03397-4d51-7fd1-8ff2-946eb22ea2bc
This commit is contained in:
tegwick 2026-08-24 16:10:59 +02:00
parent d56bd3c79f
commit 5774fbd548
18 changed files with 890 additions and 94 deletions

View file

@ -67,7 +67,21 @@ def ensure_service() -> bool:
if asyncio.run(ping()):
return True
time.sleep(0.05)
return False
return False
def preflight_runtime_paths() -> None:
"""Ensure local state/runtime parents are available before tmux mutation."""
parents = {db_path().parent, socket_path().parent, pid_path().parent, lock_path().parent}
for parent in parents:
parent.mkdir(parents=True, exist_ok=True)
if not os.access(parent, os.W_OK | os.X_OK):
raise OSError(f"runtime directory is not writable: {parent}")
def attach_session(session: str) -> int:
command = ["tmux", "switch-client" if os.environ.get("TMUX") else "attach-session", "-t", session]
return subprocess.run(command, check=False).returncode
def build_parser() -> argparse.ArgumentParser:
@ -83,9 +97,9 @@ def build_parser() -> argparse.ArgumentParser:
start = subparsers.add_parser("start", help="start or reuse a tamq endpoint")
start.add_argument("repos", nargs="*", help="gita-registered repository slugs")
start.add_argument("--cmd", default="codex", help="agent command for new windows")
start.add_argument("--command", "--cmd", dest="agent_command", default="codex", help="agent command for new windows (default: codex)")
start.add_argument("--detach", action="store_true", help="detach after startup")
start.add_argument("--no-service", action="store_true", help="skip service startup")
start.add_argument("--no-service", action="store_true", help="open tmux windows without service registration or messaging")
attach = subparsers.add_parser("attach", help="attach to the managed tmux session")
attach.add_argument("--session", default="tamq", help="tmux session name")
@ -168,8 +182,7 @@ def main(argv: list[str] | None = None) -> int:
except KeyboardInterrupt: return 0
return 0
if args.command == "attach":
result = subprocess.run(["tmux", "attach-session", "-t", args.session], check=False)
return result.returncode
return attach_session(args.session)
if args.command == "completion":
print(completion_script(args.shell), end="")
return 0
@ -189,30 +202,56 @@ def main(argv: list[str] | None = None) -> int:
finally:
store.close()
if args.command == "start":
manager = TmuxManager()
try:
preflight_runtime_paths()
launch_plan = manager.preflight(args.repos, args.agent_command)
except (TmuxError, OSError) as exc:
print(f"tamq: {exc}", file=sys.stderr)
return 2
if not args.no_service and not ensure_service():
print("tamq service failed to start; use --no-service to open repos without messaging", file=sys.stderr)
return 1
try:
endpoint = TmuxManager().ensure(args.repos, args.cmd)
endpoint = manager.ensure_plan(launch_plan)
except (TmuxError, OSError) as exc:
print(f"tamq: {exc}", file=sys.stderr)
return 2
registration = asyncio.run(request({"op": "register", "endpoint_id": endpoint.endpoint_id, "instance_id": endpoint.instance_key, "pid": endpoint.pid, "session": endpoint.session, "repos": endpoint.repos}))
if not registration.get("ok"):
print(f"tamq: endpoint registration failed: {registration.get('error', 'unknown error')}", file=sys.stderr)
return 1
control = ControlModeClient(endpoint.session)
queue_store = Store(db_path())
try:
control.start()
delivered = InputBroker(queue_store, BrokerIdentity(endpoint.instance_key, endpoint.repos[0])).deliver_pending(
control, window_for_repo=lambda repo: f"{endpoint.session}:{repo}"
)
finally:
control.close()
queue_store.close()
print(json.dumps({"endpoint_id": endpoint.endpoint_id, "instance_id": endpoint.instance_key, "session": endpoint.session, "repos": endpoint.repos, "delivered": delivered}))
return 0
delivered = 0
registered = False
if not args.no_service:
try:
registration = asyncio.run(request({"op": "register", "endpoint_id": endpoint.endpoint_id, "instance_id": endpoint.instance_key, "pid": endpoint.pid, "session": endpoint.session, "repos": endpoint.repos}))
if not registration.get("ok"):
raise RuntimeError(f"endpoint registration failed: {registration.get('error', 'unknown error')}")
registered = True
control = ControlModeClient(endpoint.session)
queue_store = Store(db_path())
try:
control.start()
delivered = InputBroker(queue_store, BrokerIdentity(endpoint.instance_key, endpoint.repos[0])).deliver_pending(
control, window_for_repo=lambda repo: f"{endpoint.session}:{repo}"
)
finally:
control.close()
queue_store.close()
except (OSError, RuntimeError, ValueError, json.JSONDecodeError) as exc:
manager.rollback(endpoint)
print(f"tamq: {exc}", file=sys.stderr)
return 1
summary = {
"endpoint_id": endpoint.endpoint_id,
"instance_id": endpoint.instance_key,
"session": endpoint.session,
"repos": endpoint.repos,
"delivered": delivered,
"service": not args.no_service,
"messaging": registered,
}
print(json.dumps(summary), flush=True)
if args.detach:
return 0
return attach_session(endpoint.session)
store = Store(db_path())
try:
advisory = history_advisory(store)