diff --git a/tito/commands/login.py b/tito/commands/login.py index 303ec660..1ffbdc2a 100644 --- a/tito/commands/login.py +++ b/tito/commands/login.py @@ -1,6 +1,9 @@ # tito/commands/login.py import webbrowser import time +import json +import urllib.parse +from pathlib import Path from argparse import ArgumentParser, Namespace from rich.prompt import Confirm from tito.commands.base import BaseCommand @@ -37,7 +40,39 @@ class LoginCommand(BaseCommand): receiver = AuthReceiver() try: port = receiver.start() - target_url = f"{ENDPOINTS['cli_login']}?redirect_port={port}" + + # Construct URL with optional profile pre-fill + params = {"redirect_port": str(port)} + + # Try to read local profile for auto-fill + try: + # Check project root first, then global home + project_profile = Path("profile.json").resolve() + global_profile = Path.home() / ".tinytorch" / "profile.json" + + profile_path = None + if project_profile.exists(): + profile_path = project_profile + elif global_profile.exists(): + profile_path = global_profile + + if profile_path: + self.console.print(f"[dim]Reading profile from: {profile_path}[/dim]") + with open(profile_path, 'r') as f: + profile = json.load(f) + self.console.print(f"[dim]Found profile data: {profile.get('email', 'No email')}[/dim]") + if "email" in profile: params["email"] = profile["email"] + if "name" in profile: params["name"] = profile["name"] + if "affiliation" in profile: params["affiliation"] = profile["affiliation"] + else: + self.console.print(f"[yellow]No profile found (checked ./profile.json and {global_profile})[/yellow]") + except Exception as e: + self.console.print(f"[red]Failed to read profile: {e}[/red]") + + + query_string = urllib.parse.urlencode(params) + target_url = f"{ENDPOINTS['cli_login']}?{query_string}" + self.console.print(f"Opening browser to: [cyan]{target_url}[/cyan]") self.console.print("Waiting for authentication...") webbrowser.open(target_url) diff --git a/tito/commands/setup.py b/tito/commands/setup.py index 4846e6ab..8566d7a4 100644 --- a/tito/commands/setup.py +++ b/tito/commands/setup.py @@ -390,6 +390,19 @@ class SetupCommand(BaseCommand): if login_result == 0: self.console.print("[green]✅ Successfully connected to the TinyTorch community![/green]") + + # Post-login profile update prompt + self.console.print() + self.console.print(Panel( + "[bold magenta]✨ Update Community Profile ✨[/bold magenta]\n\n" + "Your CLI is now connected. Would you like to update your profile on the TinyTorch community website?", + title="Community Profile Update", + border_style="magenta", + box=box.ROUNDED + )) + if Confirm.ask("[bold]Update your community profile?[/bold]", default=True): + self.console.print("[dim]Opening profile editor...[/dim]") + webbrowser.open("https://tinytorch.ai/community/?action=profile") else: self.console.print("[yellow]⚠️ Community connection failed or was cancelled. You can try again later with 'tito login'.[/yellow]") except Exception as e: diff --git a/tito/core/auth.py b/tito/core/auth.py index be6a862c..557cb744 100644 --- a/tito/core/auth.py +++ b/tito/core/auth.py @@ -66,10 +66,10 @@ def save_credentials(data: Dict[str, str]) -> None: except OSError: pass - # Print file update notification - console = get_console() - relative_path = p.relative_to(Path.home()) - console.print(f"[dim]📝 Updated: ~/{relative_path}[/dim]") + # File update notification removed for cleaner UX + # console = get_console() + # relative_path = p.relative_to(Path.home()) + # console.print(f"[dim]📝 Updated: ~/{relative_path}[/dim]") def load_credentials() -> Optional[Dict[str, str]]: p = _credentials_path() @@ -213,10 +213,69 @@ class CallbackHandler(http.server.BaseHTTPRequestHandler): } self.send_response(200) - self.send_header('Content-type', 'text/html') + self.send_header('Content-type', 'text/html; charset=utf-8') self.end_headers() - html_content = "
You can close this window and return to the CLI.
" + html_content = """ + + + + + +You have successfully authenticated with the CLI. You can now close this window and return to your terminal.
+ Go to Dashboard +