# Agent CLI Simplification — Handoff to ria-toolkit-oss **Repo:** `ria-toolkit-oss`, branch `screens-connection` **Goal:** Reduce agent setup from 3+ commands to 2 simple ones. --- ## Current UX (painful) ```bash # Step 1: Register via curl against FastAPI directly curl -X POST http://hub:8005/screens/agents/register \ -H 'X-API-Key: supersecretapikey' \ -d '{"name": "my-agent"}' # → {"agent_id": "agent-55cf3c5b8137f6f3", "token": "45Hbt..."} # Step 2: Manually save credentials ria-agent register \ --url http://hub:8005 \ --token 45HbtlpVDX7_XTF47biDcLcyiVmM51icEZVJ7J_UrEE \ --agent-id agent-55cf3c5b8137f6f3 # Step 3: Stream (with manual URL construction) ria-agent stream \ --url "ws://hub:8005/screens/agent/ws?agent_id=agent-55cf3c5b8137f6f3" \ --token 45HbtlpVDX7_XTF47biDcLcyiVmM51icEZVJ7J_UrEE ``` Problems: - User must know the FastAPI port (8005), not just the hub URL (3005) - `register` subcommand only saves locally — doesn't call the server - `_derive_ws_url` builds `/api/agent/ws/{agent_id}` but server endpoint is `/screens/agent/ws?agent_id=...` - User must copy-paste agent_id and token between commands --- ## Target UX ```bash # One-time setup: register with the hub (hits server, saves config) ria-agent register --hub http://whitehorse:3005 --api-key supersecretapikey --name lab-pluto # Stream (reads config, connects automatically) ria-agent stream ``` That's it. Two commands, no copy-pasting. --- ## Changes needed in ria-toolkit-oss ### 1. `cli.py` — Make `register` call the server Current `_cmd_register` just saves to `~/.ria/agent.json`. It should: 1. POST to `{hub_url}/screens/agents/register` with `X-API-Key` header 2. Receive `{agent_id, token}` from the server 3. Save everything to `~/.ria/agent.json` ```python def _cmd_register(args: argparse.Namespace) -> int: import urllib.request import json as _json hub_url = args.hub.rstrip("/") api_key = args.api_key # Call the server to register url = f"{hub_url}/screens/agents/register" body = _json.dumps({"name": args.name or ""}).encode() req = urllib.request.Request( url, data=body, headers={ "Content-Type": "application/json", "X-API-Key": api_key, }, ) try: with urllib.request.urlopen(req) as resp: data = _json.loads(resp.read()) except Exception as e: print(f"error: registration failed: {e}", file=sys.stderr) return 1 agent_id = data["agent_id"] token = data["token"] # Save to config cfg = _config.load() cfg.hub_url = hub_url cfg.agent_id = agent_id cfg.token = token if args.name: cfg.name = args.name cfg.insecure = bool(args.insecure) path = _config.save(cfg) print(f"Registered agent: {agent_id}") print(f"Credentials saved to {path}") return 0 ``` Update the argparse for `register`: ```python p_reg = sub.add_parser("register", help="Register agent with RIA Hub and save credentials") p_reg.add_argument("--hub", required=True, help="RIA Hub URL (e.g. http://whitehorse:3005)") p_reg.add_argument("--api-key", required=True, help="Hub API key for authentication") p_reg.add_argument("--name", default=None, help="Human-friendly agent name") p_reg.add_argument("--insecure", action="store_true", help="Skip TLS verification") ``` Remove `--url`, `--token`, `--agent-id` from register — those are now server-generated. ### 2. `cli.py` — Fix `_derive_ws_url` Current (wrong): ```python suffix = f"/api/agent/ws/{agent_id}" if agent_id else "/api/agent/ws" ``` Should be: ```python suffix = f"/screens/agent/ws?agent_id={agent_id}" if agent_id else "/screens/agent/ws" ``` ### 3. `cli.py` — Make `stream` zero-arg by default Current `_cmd_stream` already loads config and derives the URL — it just needs the URL fix above. After that, bare `ria-agent stream` works if `register` was run first. ### 4. `config.py` — Add `api_key` field (optional) Add `api_key: str = ""` to `AgentConfig` so the hub API key can be persisted for re-registration or other API calls. Not strictly required but useful. --- ## Changes already done in ria-hub (Part B) The server side is ready: - `POST /screens/agents/register` — accepts `{"name": "..."}` with `X-API-Key` header, returns `{"agent_id": "...", "token": "..."}` - `GET /screens/agent/ws?agent_id=...` — WebSocket endpoint, authenticates via `Authorization: Bearer {token}` header - Agent token is hashed (SHA-256) and stored in MongoDB; lookup happens on WS connect The Go proxy for `/screens/agents/register` through port 3005 still needs to be added (currently agents must hit FastAPI port 8005 directly). That's a ria-hub task, not ria-toolkit-oss. --- ## Summary of file changes | File | Change | |------|--------| | `src/ria_toolkit_oss/agent/cli.py` | `register` calls server API, new flags `--hub`/`--api-key`; fix `_derive_ws_url` path | | `src/ria_toolkit_oss/agent/config.py` | Optional: add `api_key` field to `AgentConfig` | | `tests/agent/test_cli.py` | Update register tests for new server-calling behavior | --- ## Validated E2E flow (what works today) We tested the full pipeline on whitehorse with a real Pluto SDR: 1. Agent connects via WebSocket with bearer token auth ✅ 2. Server sends `start` with `radio_config` via Redis pub/sub → agent ✅ 3. Agent opens Pluto, streams interleaved float32 IQ via binary WS frames ✅ 4. FastAPI pushes frames to Redis list, Celery worker's `AgentDataSource.next_chunk()` BLPOP reads them ✅ 5. Inference loop runs on live agent data identically to direct SDR mode ✅ The only manual friction is the multi-step registration and URL construction — which these CLI changes eliminate.