import argparse import json import os import sys from pathlib import Path sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from google.auth.transport.requests import Request from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow SCOPES = ["https://www.googleapis.com/auth/spreadsheets"] SERVICE_NAME = "google-sheets-skill-oauth" try: import keyring HAS_KEYRING = True except ImportError: HAS_KEYRING = False def get_token_dir(): path = os.environ.get("GOOGLE_SHEETS_TOKEN_DIR") if path: return Path(path) default = Path.home() / ".config" / "google-sheets-skill" default.mkdir(parents=True, exist_ok=True) return default def _load_creds(): token_file = get_token_dir() / "token.json" if token_file.exists(): return Credentials.from_authorized_user_file(str(token_file), SCOPES) if HAS_KEYRING: stored = keyring.get_password(SERVICE_NAME, "token") if stored: return Credentials.from_authorized_user_info(json.loads(stored), SCOPES) return None def _save_creds(creds): get_token_dir().mkdir(parents=True, exist_ok=True) token_file = get_token_dir() / "token.json" token_file.write_text(creds.to_json()) token_file.chmod(0o600) if HAS_KEYRING: keyring.set_password(SERVICE_NAME, "token", creds.to_json()) def _find_creds_file(): env_path = os.environ.get("GOOGLE_SHEETS_CREDS") if env_path: return Path(env_path) alongside = Path(__file__).parent / "credentials.json" if alongside.exists(): return alongside return Path.home() / ".config" / "google-sheets-skill" / "credentials.json" def cmd_login(): creds_file = _find_creds_file() if not creds_file.exists(): print( f"credentials.json not found at {creds_file}\n" "Download it from Google Cloud Console:\n" "1. Go to https://console.cloud.google.com/\n" "2. Create a project → Enable Sheets API\n" "3. Create OAuth 2.0 Client ID → Desktop App\n" "4. Download as credentials.json\n" "5. Save to: " + str(creds_file) + "\n\n" "Or set GOOGLE_SHEETS_CREDS env var to its location." ) sys.exit(1) flow = InstalledAppFlow.from_client_secrets_file(str(creds_file), SCOPES) creds = flow.run_local_server(port=0) _save_creds(creds) print(f"Authenticated successfully. Token saved to {get_token_dir() / 'token.json'}") def cmd_status(): creds = _load_creds() if not creds: print("Not authenticated. Run: python .agents/skills/google-sheets-automation/scripts/auth.py login") sys.exit(1) if not creds.valid: if creds.expired and creds.refresh_token: creds.refresh(Request()) _save_creds(creds) print("Token was expired; refreshed successfully.") else: print("Token is invalid. Re-authenticate: python .agents/skills/google-sheets-automation/scripts/auth.py login") sys.exit(1) print(f"Authenticated as: {creds.token[:20]}...") print(f"Expires at: {creds.expiry}") def cmd_logout(): token_file = get_token_dir() / "token.json" if token_file.exists(): token_file.unlink() if HAS_KEYRING: try: keyring.delete_password(SERVICE_NAME, "token") except keyring.errors.PasswordDeleteError: pass print("Logged out. Token deleted.") def get_credentials(): creds = _load_creds() if not creds: print("Not authenticated. Run: python .agents/skills/google-sheets-automation/scripts/auth.py login", file=sys.stderr) sys.exit(1) if not creds.valid: if creds.expired and creds.refresh_token: creds.refresh(Request()) _save_creds(creds) else: print("Token is invalid. Re-authenticate: python .agents/skills/google-sheets-automation/scripts/auth.py login", file=sys.stderr) sys.exit(1) return creds def main(): parser = argparse.ArgumentParser(description="Google Sheets Auth") parser.add_argument("command", choices=["login", "status", "logout"]) args = parser.parse_args() if args.command == "login": cmd_login() elif args.command == "status": cmd_status() elif args.command == "logout": cmd_logout() if __name__ == "__main__": main()