Skip to content

Commit 2bf8b6d

Browse files
authored
Perf improvements (#1196)
1 parent 678abf4 commit 2bf8b6d

File tree

5 files changed

+288
-95
lines changed

5 files changed

+288
-95
lines changed

src/codegen/cli/auth/login.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ def login_routine(token: str | None = None) -> str:
3737

3838
# Validate and store token
3939
try:
40+
rich.print("[blue]Validating token and fetching account info...[/blue]")
4041
token_manager = TokenManager()
4142
token_manager.authenticate_token(token)
42-
rich.print(f"[green]✓ Stored token to:[/green] {token_manager.token_file}")
43+
rich.print(f"[green]✓ Stored token and profile to:[/green] {token_manager.token_file}")
4344
return token
4445
except AuthError as e:
4546
rich.print(f"[red]Error:[/red] {e!s}")

src/codegen/cli/auth/token_manager.py

Lines changed: 170 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
from codegen.cli.auth.constants import AUTH_FILE, CONFIG_DIR
66

7+
# Simple cache to avoid repeated file I/O
8+
_token_cache = None
9+
_cache_mtime = None
10+
711

812
class TokenManager:
913
# Simple token manager to store and retrieve tokens.
@@ -20,17 +24,79 @@ def _ensure_config_dir(self):
2024
Path(self.config_dir).mkdir(parents=True, exist_ok=True)
2125

2226
def authenticate_token(self, token: str) -> None:
23-
"""Store the token locally."""
24-
self.save_token(token)
27+
"""Store the token locally and fetch organization info."""
28+
self.save_token_with_org_info(token)
29+
30+
def save_token_with_org_info(self, token: str) -> None:
31+
"""Save api token to disk along with organization info."""
32+
global _token_cache, _cache_mtime
33+
34+
# First fetch organization info using the token
35+
try:
36+
import requests
37+
38+
from codegen.cli.api.endpoints import API_ENDPOINT
39+
40+
headers = {"Authorization": f"Bearer {token}"}
41+
42+
# Test token by getting user info
43+
user_response = requests.get(f"{API_ENDPOINT.rstrip('/')}/v1/users/me", headers=headers, timeout=10)
44+
user_response.raise_for_status()
45+
user_data = user_response.json()
46+
47+
# Get organizations
48+
org_response = requests.get(f"{API_ENDPOINT.rstrip('/')}/v1/organizations", headers=headers, timeout=10)
49+
org_response.raise_for_status()
50+
org_data = org_response.json()
51+
52+
# Prepare auth data with org info
53+
auth_data = {
54+
"token": token,
55+
"user": {"id": user_data.get("id"), "email": user_data.get("email"), "full_name": user_data.get("full_name"), "github_username": user_data.get("github_username")},
56+
}
57+
58+
# Add organization info if available
59+
orgs = org_data.get("items", [])
60+
if orgs and len(orgs) > 0:
61+
primary_org = orgs[0] # Use first org as primary
62+
auth_data["organization"] = {"id": primary_org.get("id"), "name": primary_org.get("name"), "all_orgs": [{"id": org.get("id"), "name": org.get("name")} for org in orgs]}
63+
64+
except requests.RequestException as e:
65+
# If we can't fetch org info, still save the token but without org data
66+
print(f"Warning: Could not fetch organization info: {e}")
67+
auth_data = {"token": token}
68+
except Exception as e:
69+
print(f"Warning: Error fetching user/org info: {e}")
70+
auth_data = {"token": token}
71+
72+
# Save to file
73+
try:
74+
with open(self.token_file, "w") as f:
75+
json.dump(auth_data, f, indent=2)
76+
77+
# Secure the file permissions (read/write for owner only)
78+
os.chmod(self.token_file, 0o600)
79+
80+
# Invalidate cache
81+
_token_cache = None
82+
_cache_mtime = None
83+
except Exception as e:
84+
print(f"Error saving token: {e!s}")
85+
raise
2586

2687
def save_token(self, token: str) -> None:
27-
"""Save api token to disk."""
88+
"""Save api token to disk (legacy method - just saves token)."""
89+
global _token_cache, _cache_mtime
2890
try:
2991
with open(self.token_file, "w") as f:
3092
json.dump({"token": token}, f)
3193

3294
# Secure the file permissions (read/write for owner only)
3395
os.chmod(self.token_file, 0o600)
96+
97+
# Invalidate cache
98+
_token_cache = None
99+
_cache_mtime = None
34100
except Exception as e:
35101
print(f"Error saving token: {e!s}")
36102
raise
@@ -58,20 +124,120 @@ def get_token(self) -> str | None:
58124

59125
def clear_token(self) -> None:
60126
"""Remove stored token."""
127+
global _token_cache, _cache_mtime
61128
if os.path.exists(self.token_file):
62129
os.remove(self.token_file)
130+
# Invalidate cache
131+
_token_cache = None
132+
_cache_mtime = None
133+
134+
def get_auth_data(self) -> dict | None:
135+
"""Retrieve complete auth data from disk."""
136+
try:
137+
if not os.access(self.config_dir, os.R_OK):
138+
return None
139+
140+
if not os.path.exists(self.token_file):
141+
return None
142+
143+
with open(self.token_file) as f:
144+
return json.load(f)
145+
except Exception:
146+
return None
147+
148+
def get_org_id(self) -> int | None:
149+
"""Get the stored organization ID."""
150+
auth_data = self.get_auth_data()
151+
if auth_data and "organization" in auth_data:
152+
org_id = auth_data["organization"].get("id")
153+
if org_id:
154+
try:
155+
return int(org_id)
156+
except (ValueError, TypeError):
157+
return None
158+
return None
159+
160+
def get_org_name(self) -> str | None:
161+
"""Get the stored organization name."""
162+
auth_data = self.get_auth_data()
163+
if auth_data and "organization" in auth_data:
164+
return auth_data["organization"].get("name")
165+
return None
166+
167+
def get_user_info(self) -> dict | None:
168+
"""Get the stored user info."""
169+
auth_data = self.get_auth_data()
170+
if auth_data and "user" in auth_data:
171+
return auth_data["user"]
172+
return None
63173

64174

65175
def get_current_token() -> str | None:
66176
"""Get the current authentication token if one exists.
67177
68178
This is a helper function that creates a TokenManager instance and retrieves
69179
the stored token. The token is validated before being returned.
180+
Uses a simple cache to avoid repeated file I/O.
70181
71182
Returns:
72183
Optional[str]: The current valid api token if one exists.
73184
Returns None if no token exists.
74185
75186
"""
187+
global _token_cache, _cache_mtime
188+
189+
try:
190+
# Check if token file exists
191+
if not os.path.exists(AUTH_FILE):
192+
return None
193+
194+
# Get file modification time
195+
current_mtime = os.path.getmtime(AUTH_FILE)
196+
197+
# Use cache if file hasn't changed
198+
if _token_cache is not None and _cache_mtime == current_mtime:
199+
return _token_cache
200+
201+
# Read token from file
202+
token_manager = TokenManager()
203+
token = token_manager.get_token()
204+
205+
# Update cache
206+
_token_cache = token
207+
_cache_mtime = current_mtime
208+
209+
return token
210+
except Exception:
211+
# Fall back to uncached version on any error
212+
token_manager = TokenManager()
213+
return token_manager.get_token()
214+
215+
216+
def get_current_org_id() -> int | None:
217+
"""Get the stored organization ID if available.
218+
219+
Returns:
220+
Optional[int]: The organization ID if stored, None otherwise.
221+
"""
222+
token_manager = TokenManager()
223+
return token_manager.get_org_id()
224+
225+
226+
def get_current_org_name() -> str | None:
227+
"""Get the stored organization name if available.
228+
229+
Returns:
230+
Optional[str]: The organization name if stored, None otherwise.
231+
"""
232+
token_manager = TokenManager()
233+
return token_manager.get_org_name()
234+
235+
236+
def get_current_user_info() -> dict | None:
237+
"""Get the stored user info if available.
238+
239+
Returns:
240+
Optional[dict]: The user info if stored, None otherwise.
241+
"""
76242
token_manager = TokenManager()
77-
return token_manager.get_token()
243+
return token_manager.get_user_info()

src/codegen/cli/commands/agents/main.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,19 @@ def list_agents(org_id: int | None = typer.Option(None, help="Organization ID (d
2626
raise typer.Exit(1)
2727

2828
try:
29-
# Resolve org id
29+
# Resolve org id (now fast, uses stored data)
3030
resolved_org_id = resolve_org_id(org_id)
3131
if resolved_org_id is None:
3232
console.print("[red]Error:[/red] Organization ID not provided. Pass --org-id, set CODEGEN_ORG_ID, or REPOSITORY_ORG_ID.")
3333
raise typer.Exit(1)
3434

35-
# Make API request to list agent runs with spinner
35+
# Start spinner for API calls only
3636
spinner = create_spinner("Fetching your recent API agent runs...")
3737
spinner.start()
3838

3939
try:
4040
headers = {"Authorization": f"Bearer {token}"}
41+
4142
# Filter to only API source type and current user's agent runs
4243
params = {
4344
"source_type": "API",

0 commit comments

Comments
 (0)