from __future__ import annotations import argparse import json from pathlib import Path import sys from .models import ContractError, TaskSpec from .adb import AdbClient from .process import ProcessSupervisor from .terminal import canonical_terminal_bytes, legacy_v003_projection from .ui import HiborUiDriver, UiConfig from .workflow import HiborWorkflow def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(prog="hibor-fast-collection") parser.add_argument("--task", type=Path, required=True, help="UTF-8 TaskSpec JSON") parser.add_argument("--terminal-schema", choices=("V009", "V003"), default="V009") parser.add_argument("--dry-run", action="store_true", help="force no-external-action validation") parser.add_argument("--discover", action="store_true", help="read-only APP candidate discovery; never reserve or trigger") parser.add_argument("--execute", action="store_true", help="perform the explicitly authorized APP collection run") return parser def main(argv: list[str] | None = None) -> int: args = build_parser().parse_args(argv) try: raw = args.task.read_bytes() if raw.startswith(b"\xef\xbb\xbf") or b"\x00" in raw: raise ValueError("BOM/NUL forbidden") value = json.loads(raw.decode("utf-8")) spec = TaskSpec.from_mapping(value) if args.dry_run or spec.mode == "dry-run": if args.execute or args.discover: raise ValueError("--dry-run is mutually exclusive with execution/discovery") terminal = HiborWorkflow(spec).dry_run() elif args.discover or spec.mode == "discover": if args.execute: raise ValueError("--discover is a distinct read-only APP mode") supervisor = ProcessSupervisor() adb = AdbClient(Path(spec.adb_executable), supervisor, serial=spec.device_serial, package_name=spec.package_name, cache_root=spec.cache_root) ui = HiborUiDriver(adb, UiConfig.from_source_scope(spec.source_scope)) discovery = HiborWorkflow(spec).discover(adb=adb, ui=ui, execute=True) data = json.dumps(discovery, ensure_ascii=False, separators=(",", ":"), sort_keys=False).encode("utf-8") sys.stdout.buffer.write(data + b"\n") return 0 else: if not args.execute: raise ValueError("real APP collection requires explicit --execute") supervisor = ProcessSupervisor() adb = AdbClient(Path(spec.adb_executable), supervisor, serial=spec.device_serial, package_name=spec.package_name, cache_root=spec.cache_root) ui = HiborUiDriver(adb, UiConfig.from_source_scope(spec.source_scope)) terminal = HiborWorkflow(spec).collect(adb=adb, ui=ui, execute=True) if args.terminal_schema == "V003": data = json.dumps(legacy_v003_projection(terminal), ensure_ascii=False, separators=(",", ":"), sort_keys=False).encode("utf-8") else: data = canonical_terminal_bytes(terminal) sys.stdout.buffer.write(data + b"\n") return int(terminal["exit_code"]) except (OSError, ValueError, ContractError, json.JSONDecodeError) as exc: sys.stderr.write(f"HIBOR_FAST_COLLECTION_INPUT_ERROR:{type(exc).__name__}\n") return 12 if __name__ == "__main__": raise SystemExit(main())