Jacob Hinkle 8162af63b6 Initial commit: fitness agent project
- opencode agent (fitness-trainer) for personal training conversations
- fitness-workout skill with programming methodology guidelines
- workout.py script (1RM, volume, cycle helpers)
- logs/ directory for workout and check-in markdown files
- inputs/ with user profile (equipment, goals, medical, Juggernaut history)
- google-sheets-automation skill for optional Sheets integration
- AGENTS.md with setup documentation
2026-06-25 19:08:30 -04:00

142 lines
4.4 KiB
Python

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()