#!/usr/bin/env python """Manage MODELBEAST users directly against the DB — for bootstrapping or when you're locked out of the UI. Run from the repo root: uv run python scripts/users.py ... """ import argparse import getpass import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from server import auth, db # noqa: E402 def main(): con = db.connect() ap = argparse.ArgumentParser() sub = ap.add_subparsers(dest="cmd", required=True) a = sub.add_parser("add") a.add_argument("username") a.add_argument("--owner", action="store_true") a.add_argument("--max-jobs", type=int, default=4) sub.add_parser("list") p = sub.add_parser("passwd") p.add_argument("username") d = sub.add_parser("delete") d.add_argument("username") t = sub.add_parser("token", help="mint a named API token (printed ONCE — store it in a .env)") t.add_argument("name", help="what this key is for, e.g. m1ultra, deadstock-agent") t.add_argument("--user", default=None, help="username to bind to (default: the owner)") args = ap.parse_args() if args.cmd == "add": pw = getpass.getpass("password: ") u = auth.create_user(con, args.username, pw, role="owner" if args.owner else "guest", max_active_jobs=args.max_jobs) print(f"created {u['username']} ({u['role']})") elif args.cmd == "list": for u in auth.list_users(con): print(f"{u['username']:<20} {u['role']:<7} max_jobs={u['max_active_jobs']}") elif args.cmd == "passwd": u = auth.get_user_by_name(con, args.username) if not u: sys.exit("no such user") auth.set_password(con, u["id"], getpass.getpass("new password: ")) print("updated") elif args.cmd == "delete": u = auth.get_user_by_name(con, args.username) if not u: sys.exit("no such user") auth.delete_user(con, u["id"]) print("deleted") elif args.cmd == "token": if args.user: u = auth.get_user_by_name(con, args.user) else: u = next((x for x in auth.list_users(con) if x["role"] == "owner"), None) if not u: sys.exit("no such user") raw = auth.create_token(con, u["id"], name=args.name) print(f"# token '{args.name}' for {u['username']} — shown once, goes in a .env:") print(f"MB_TOKEN={raw}") if __name__ == "__main__": main()