"""Interactive helper to store Asana PAT in Windows Credential Manager."""
import re
import sys
import keyring
# Asana PATs contain only ASCII printable chars: digits, letters, '/', ':'
_VALID_PAT_RE = re.compile(r"^[0-9]+/[0-9]+(/[0-9]+)?:[A-Fa-f0-9]+$")
def _sanitize(raw: str) -> str:
"""Strip whitespace and remove any non-ASCII chars that crept in via paste."""
return "".join(c for c in raw.strip() if 32 <= ord(c) < 127)
def main() -> None:
print("=== Asana PAT Setup ===")
print("This script stores your Asana Personal Access Token")
print("in Windows Credential Manager (DPAPI encrypted).\n")
print("PAT format: 2/<user_gid>:<hex_token>")
print()
existing = keyring.get_password("asana-mcp", "pat")
if existing:
clean = _sanitize(existing)
bad_chars = len(existing) - len(clean)
if bad_chars:
print(f"WARNING: Existing PAT contains {bad_chars} non-ASCII char(s).")
print("A PAT is already stored. Enter a new one to replace it,")
print("or press Enter to keep the existing one.\n")
print("Paste your Asana PAT below and press Enter:")
try:
raw = input("> ")
except EOFError:
print("No input received. Aborting.")
sys.exit(1)
token = _sanitize(raw)
if not token:
if existing:
print("Keeping existing PAT.")
else:
print("No token provided. Aborting.")
return
if not _VALID_PAT_RE.match(token):
print(f"\nWARNING: Token does not match expected format (N/N:hex).")
print(f" Length: {len(token)}")
print(f" First 10 chars: {token[:10]}...")
print("Storing anyway -- if connection fails, re-run this script.\n")
keyring.set_password("asana-mcp", "pat", token)
print(f"\nPAT stored ({len(token)} chars, ASCII-clean).")
print("Verify with: .venv\\Scripts\\python.exe scripts\\verify_connection.py")
if __name__ == "__main__":
main()