#!/usr/bin/env python3 """Register, list, or revoke Ed25519 clients for Firefox Agent Bridge.""" from __future__ import annotations import argparse import json import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) from fab_auth import CLIENTS_PATH, load_clients, register_client, revoke_client # noqa: E402 def main() -> int: parser = argparse.ArgumentParser(description="Manage Firefox Agent Bridge clients") sub = parser.add_subparsers(dest="cmd", required=True) add = sub.add_parser("add", help="generate a keypair and register the public half") add.add_argument("--name", required=True, help="label, e.g. cursor-agent") add.add_argument( "--write-key", required=True, type=Path, help="private key bundle path for tooling (must not be under LocalAppData\\firefox-agent-bridge)", ) sub.add_parser("list", help="show registered public clients") drop = sub.add_parser("revoke", help="drop a client by name or id") drop.add_argument("name_or_id") ns = parser.parse_args() if ns.cmd == "add": info = register_client(ns.name, ns.write_key) print(json.dumps(info, indent=2)) print(f"give {info['key_file']} to tooling via FAB_KEY_FILE or --key", file=sys.stderr) return 0 if ns.cmd == "list": rows = [ {"id": c.get("id"), "name": c.get("name"), "created": c.get("created")} for c in load_clients() ] print(json.dumps({"store": str(CLIENTS_PATH), "clients": rows}, indent=2)) return 0 revoke_client(ns.name_or_id) print(json.dumps({"revoked": ns.name_or_id})) return 0 if __name__ == "__main__": raise SystemExit(main())