"""Shared subprocess runners for CLI backends.""" import re import subprocess from tai_pkg.errors import BackendError from tai_pkg.text import Text __all__ = ["Cli"] class Cli: """Cli.""" @staticmethod def _cli_unknown_flag(stderr): return bool(re.search(r"unknown (option|flag|command)|unrecognised|unrecognized|invalid flag|not found|no such option", stderr or "", re.IGNORECASE)) @staticmethod def _cli_auth_failure(text): return bool(re.search(r"not (logged in|authenticated)|auth|api key|unauthorized|401|login required|please (login|log in|authenticate)", text or "", re.IGNORECASE)) @staticmethod def _cli_run(argv, prompt_input, timeout, label): try: done = subprocess.run(argv, input=prompt_input, stdin=subprocess.DEVNULL if prompt_input is None else None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, timeout=timeout) except subprocess.TimeoutExpired: raise BackendError("%s run timed out after %ds" % (label, timeout)) except OSError as exc: raise BackendError("%s run failed: %s" % (label, Text.short_error(exc))) return done @staticmethod def _cli_version_detail(done, fallback): return (done.stdout.strip() or done.stderr.strip()).splitlines()[0][:60] if (done.stdout.strip() or done.stderr.strip()) else fallback @staticmethod def _cli_version_only_health(binary, fallback_name): """Shared by the CLIs with no cheap, read-only way to check real login state (muse, grok): `--version` only proves the binary runs, never that it's actually authenticated, so this deliberately never claims "ok" or "subscription" -- a claim this check cannot back up (see codex_health/ claude_health for the two CLIs that DO expose a real status command, used instead of this). Caught a real bug: this used to print "ok, subscription" unconditionally on a successful --version, which is false whenever the CLI is installed but never logged in.""" try: done = subprocess.run([binary, "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, timeout=15) except (OSError, subprocess.SubprocessError) as exc: return "fail: %s" % Text.short_error(exc)[:60] if done.returncode != 0: return "fail: exit %d" % done.returncode return "cli found (%s), login unverified: no status command" % Cli._cli_version_detail(done, fallback_name)