personal memory agent
1# SPDX-License-Identifier: AGPL-3.0-only
2# Copyright (c) 2026 sol pbc
3
4"""CLI for setting the convey web UI password.
5
6Usage:
7 sol password set Set the convey password
8 sol password reset Reset the convey password (alias for set)
9"""
10
11from __future__ import annotations
12
13import argparse
14import getpass
15import json
16import os
17import sys
18from pathlib import Path
19
20from werkzeug.security import generate_password_hash
21
22from think.utils import get_config, get_journal, require_solstone, setup_cli
23
24
25def _set_password() -> None:
26 """Prompt for a password, hash it, and write to journal config."""
27 password = getpass.getpass("Password: ")
28 confirm = getpass.getpass("Confirm password: ")
29
30 if password != confirm:
31 print("Passwords do not match.", file=sys.stderr)
32 sys.exit(1)
33
34 password_hash = generate_password_hash(password)
35
36 config = get_config()
37 config.setdefault("convey", {})["password_hash"] = password_hash
38 config.get("convey", {}).pop("password", None)
39
40 config_path = Path(get_journal()) / "config" / "journal.json"
41 config_path.parent.mkdir(parents=True, exist_ok=True)
42 with open(config_path, "w", encoding="utf-8") as f:
43 json.dump(config, f, indent=2, ensure_ascii=False)
44 f.write("\n")
45 os.chmod(config_path, 0o600)
46
47 print("Password set successfully.")
48
49
50def main() -> None:
51 parser = argparse.ArgumentParser(description="Manage convey web UI password")
52 subparsers = parser.add_subparsers(dest="subcommand")
53 subparsers.add_parser("set", help="Set the convey password")
54 subparsers.add_parser("reset", help="Reset the convey password")
55
56 args = setup_cli(parser)
57 require_solstone()
58
59 if args.subcommand in ("set", "reset"):
60 _set_password()
61 else:
62 parser.print_help()
63 sys.exit(1)
64
65
66if __name__ == "__main__":
67 main()