Files
echo-core/cli.py
MoltBot Service 010580b3c3 stage-2: secrets manager with keyring
Credential broker via keyring (zero plaintext on disk), CLI secrets subcommand, 29 new tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 11:40:11 +00:00

91 lines
2.7 KiB
Python
Executable File

#!/usr/bin/env python3
"""Echo Core CLI tool."""
import argparse
import getpass
import sys
from pathlib import Path
# Add project root to path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from src.secrets import set_secret, get_secret, list_secrets, delete_secret, check_secrets
def cmd_secrets(args):
"""Handle secrets subcommand."""
if args.secrets_action == "set":
if args.file:
path = Path(args.file)
if not path.exists():
print(f"Error: file {args.file} not found")
sys.exit(1)
value = path.read_text().strip()
set_secret(args.name, value)
path.unlink() # Delete source file after storing
print(f"Secret '{args.name}' set from file (file deleted)")
else:
value = getpass.getpass(f"Enter value for '{args.name}': ")
set_secret(args.name, value)
print(f"Secret '{args.name}' set")
elif args.secrets_action == "list":
names = list_secrets()
if not names:
print("No secrets stored")
else:
for name in names:
print(f" - {name}")
elif args.secrets_action == "delete":
if delete_secret(args.name):
print(f"Secret '{args.name}' deleted")
else:
print(f"Secret '{args.name}' not found")
elif args.secrets_action == "test":
results = check_secrets()
for name, exists in results.items():
print(f" {name}: {'OK' if exists else 'MISSING'}")
if all(results.values()):
print("\nAll required secrets present.")
else:
print("\nWARNING: Some required secrets are missing!")
sys.exit(1)
def main():
parser = argparse.ArgumentParser(prog="echo", description="Echo Core CLI")
sub = parser.add_subparsers(dest="command")
# secrets
secrets_parser = sub.add_parser("secrets", help="Manage secrets")
secrets_sub = secrets_parser.add_subparsers(dest="secrets_action")
set_p = secrets_sub.add_parser("set", help="Set a secret")
set_p.add_argument("name", help="Secret name")
set_p.add_argument("--file", help="Read value from file (file deleted after)")
secrets_sub.add_parser("list", help="List secret names")
del_p = secrets_sub.add_parser("delete", help="Delete a secret")
del_p.add_argument("name", help="Secret name")
secrets_sub.add_parser("test", help="Check required secrets")
args = parser.parse_args()
if args.command is None:
parser.print_help()
sys.exit(0)
if args.command == "secrets":
if args.secrets_action is None:
secrets_parser.print_help()
sys.exit(0)
cmd_secrets(args)
if __name__ == "__main__":
main()