feat: deliver installable local alpha sessions
Some checks failed
tamq-ci / test (push) Failing after 6s
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:
parent
d56bd3c79f
commit
5774fbd548
18 changed files with 890 additions and 94 deletions
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue