"""`python -m lib_py.delegation.cli` — the router the shell helpers call. Deliberately thin: every subcommand delegates to the existing `main()` of the module that already owned that behaviour, so exit codes, stdout shape and side effects cannot drift between the shell path and the script path. The one piece of real logic here is `ensure-daemon`, which reproduces the shell helper's stderr wording and exit status for a missing monitor script. """ from __future__ import annotations import argparse import logging import sys from typing import List, Optional from lib_py.delegation import daemon, mqtt_common, publish_event, registry logger = logging.getLogger("delegate_job.cli") def _build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( prog="lib_py.delegation.cli", description="delegation helpers invoked by lib.sh", ) sub = parser.add_subparsers(dest="command", required=True) p_submit = sub.add_parser("submit", help="register a job; prints the job id") p_submit.add_argument("--prompt", required=True) p_submit.add_argument("--agent", default="claude-code") p_submit.add_argument("--agent-session", default="herdr:claude") p_submit.add_argument("--registry-dir", default=None) p_pub = sub.add_parser("publish", help="publish a job lifecycle event") p_pub.add_argument("--job", required=True) p_pub.add_argument("--event", default="progress", choices=publish_event.VALID_EVENTS) p_pub.add_argument("--detail", default="") p_pub.add_argument("--data", default=None) p_pub.add_argument("--retained", action="store_true") p_pub.add_argument("--registry-dir", default=None) p_pub.add_argument("--attempts", type=int, default=3) p_pub.add_argument("--expect-dispatch-token", default=None) p_pub.add_argument("--attempt-id", type=int, default=None) p_pub.add_argument("-v", "--verbose", action="store_true") p_daemon = sub.add_parser("ensure-daemon", help="start the subscriber daemon if absent") p_daemon.add_argument("--job", required=True) p_daemon.add_argument("--workdir", default=None) return parser def main(argv: Optional[List[str]] = None) -> int: mqtt_common.ensure_env_loaded() args = _build_parser().parse_args(argv) if args.command == "submit": forward = ["register", "--prompt", args.prompt, "--agent", args.agent, "--agent-session", args.agent_session] if args.registry_dir: forward = ["--registry-dir", args.registry_dir] + forward return registry.main(forward) if args.command == "publish": forward = ["--job", args.job, "--event", args.event, "--detail", args.detail, "--attempts", str(args.attempts)] if args.data is not None: forward += ["--data", args.data] if args.retained: forward += ["--retained"] if args.registry_dir: forward += ["--registry-dir", args.registry_dir] if args.expect_dispatch_token: forward += ["--expect-dispatch-token", args.expect_dispatch_token] if args.attempt_id is not None: forward += ["--attempt-id", str(args.attempt_id)] if args.verbose: forward += ["--verbose"] return publish_event.main(forward) if args.command == "ensure-daemon": try: pids = daemon.ensure_subscriber_daemon(args.job, args.workdir) except FileNotFoundError as exc: # Byte-identical to the legacy shell helper's failure path. print("ERROR: monitor script not found: {}".format(exc.args[0]), file=sys.stderr) return 1 for pid in pids: print(pid) return 0 return 1 if __name__ == "__main__": sys.exit(main())