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>
This commit is contained in:
90
cli.py
Executable file
90
cli.py
Executable file
@@ -0,0 +1,90 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user