Files
pyMC_Repeater/repeater/local_cli.py

147 lines
4.3 KiB
Python

"""
CLI client for pyMC Repeater.
Connects to an already-running repeater daemon via its HTTP API.
Reads admin password and HTTP port from the local config.yaml automatically.
"""
import sys
CONFIG_PATHS = [
"/etc/pymc_repeater/config.yaml",
"config.yaml",
]
def _load_config(config_path=None):
"""Load repeater config.yaml, trying common paths."""
import yaml
from pathlib import Path
paths = [config_path] if config_path else CONFIG_PATHS
for p in paths:
path = Path(p)
if path.is_file():
with open(path) as f:
return yaml.safe_load(f) or {}
return {}
def run_client_cli(host: str = "127.0.0.1", port: int = 8000, password: str = ""):
"""
Standalone CLI client that connects to a running repeater's HTTP API.
"""
import urllib.request
import urllib.error
import json
base_url = f"http://{host}:{port}"
# Authenticate to get JWT token
token = None
if password:
try:
auth_data = json.dumps({
"username": "admin",
"password": password,
"client_id": "pymc-cli",
}).encode()
req = urllib.request.Request(
f"{base_url}/auth/login",
data=auth_data,
headers={"Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(req, timeout=5) as resp:
result = json.loads(resp.read())
token = result.get("token") or result.get("data", {}).get("token")
except urllib.error.URLError as e:
print(f"Error: Cannot connect to repeater at {base_url}{e.reason}")
sys.exit(1)
except Exception as e:
print(f"Authentication failed: {e}")
sys.exit(1)
if not token:
print("Error: Authentication failed. Check password or repeater status.")
sys.exit(1)
print(f"\npyMC Repeater CLI (connected to {base_url})")
print("Type 'help' for available commands, 'exit' to quit.\n")
while True:
try:
command = input(">> ").strip()
except (EOFError, KeyboardInterrupt):
print()
break
if not command:
continue
if command in ("exit", "quit"):
break
try:
payload = json.dumps({"command": command}).encode()
req = urllib.request.Request(
f"{base_url}/api/cli",
data=payload,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {token}",
},
method="POST",
)
with urllib.request.urlopen(req, timeout=10) as resp:
result = json.loads(resp.read())
if result.get("success"):
print(result["data"]["reply"])
else:
print(f"Error: {result.get('error', 'Unknown error')}")
except urllib.error.URLError as e:
print(f"Connection error: {e.reason}")
except Exception as e:
print(f"Error: {e}")
def main():
"""Entry point for pymc-cli command."""
import argparse
parser = argparse.ArgumentParser(
description="Connect to a running pyMC Repeater and issue CLI commands"
)
parser.add_argument(
"--config", default=None,
help="Path to config.yaml (auto-detected if not set)",
)
parser.add_argument(
"--host", default=None,
help="Repeater HTTP host (default: 127.0.0.1)",
)
parser.add_argument(
"--port", type=int, default=None,
help="Repeater HTTP port (default: from config or 8000)",
)
args = parser.parse_args()
# Load config to get password and port automatically
config = _load_config(args.config)
repeater_cfg = config.get("repeater", {})
security_cfg = repeater_cfg.get("security", {})
password = security_cfg.get("admin_password", "")
if not password:
print("Error: No admin_password found in config.yaml.")
print("Searched: " + ", ".join(CONFIG_PATHS))
sys.exit(1)
host = args.host or "127.0.0.1"
port = args.port or config.get("http", {}).get("port", 8000)
run_client_cli(host=host, port=port, password=password)
if __name__ == "__main__":
main()