cite-agent 1.0.5__py3-none-any.whl → 1.2.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cite-agent might be problematic. Click here for more details.

@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ User-Friendly Session Manager for Cite-Agent
4
+ Handles session detection, user choices, and authentication flow
5
+ """
6
+
7
+ import json
8
+ import os
9
+ import sys
10
+ from pathlib import Path
11
+ from typing import Optional, Dict, Any
12
+ from rich.console import Console
13
+ from rich.panel import Panel
14
+ from rich.prompt import Prompt, Confirm
15
+ from rich.table import Table
16
+ from rich.text import Text
17
+
18
+ class SessionManager:
19
+ """User-friendly session management for Cite-Agent"""
20
+
21
+ def __init__(self):
22
+ self.console = Console()
23
+ self.session_file = Path.home() / ".nocturnal_archive" / "session.json"
24
+ self.config_file = Path.home() / ".nocturnal_archive" / "config.env"
25
+ self.session_data: Optional[Dict[str, Any]] = None
26
+
27
+ def detect_existing_session(self) -> bool:
28
+ """Detect if there's an existing session and load it"""
29
+ if not self.session_file.exists():
30
+ return False
31
+
32
+ try:
33
+ with open(self.session_file, 'r') as f:
34
+ self.session_data = json.load(f)
35
+ return True
36
+ except Exception as e:
37
+ self.console.print(f"[red]⚠️ Session file corrupted: {e}[/red]")
38
+ return False
39
+
40
+ def show_session_info(self):
41
+ """Display existing session information in a user-friendly way"""
42
+ if not self.session_data:
43
+ return
44
+
45
+ email = self.session_data.get('email', 'Unknown')
46
+ user_id = self.session_data.get('user_id', 'Unknown')[:8] + "..."
47
+ expires_at = self.session_data.get('expires_at', 'Unknown')
48
+ daily_limit = self.session_data.get('daily_token_limit', 0)
49
+
50
+ # Create a nice table
51
+ table = Table(title="🔑 Existing Session Found", show_header=True, header_style="bold green")
52
+ table.add_column("Property", style="cyan", width=15)
53
+ table.add_column("Value", style="white")
54
+
55
+ table.add_row("Email", email)
56
+ table.add_row("User ID", user_id)
57
+ table.add_row("Daily Limit", f"{daily_limit:,} queries")
58
+ table.add_row("Expires", expires_at)
59
+
60
+ self.console.print()
61
+ self.console.print(table)
62
+ self.console.print()
63
+
64
+ def ask_session_choice(self) -> str:
65
+ """Ask user what they want to do with the existing session"""
66
+ self.console.print("[bold cyan]What would you like to do?[/bold cyan]")
67
+ self.console.print()
68
+
69
+ # Create a nice menu
70
+ menu_table = Table(show_header=False, box=None, padding=(0, 1))
71
+ menu_table.add_column("Choice", style="bold green", width=3)
72
+ menu_table.add_column("Action", style="white", width=20)
73
+ menu_table.add_column("Description", style="dim", width=40)
74
+
75
+ menu_table.add_row("1", "Resume", "Continue with this session")
76
+ menu_table.add_row("2", "Switch", "Login with different account")
77
+ menu_table.add_row("3", "Logout", "Clear session and start fresh")
78
+ menu_table.add_row("4", "Help", "Show session management help")
79
+
80
+ self.console.print(menu_table)
81
+ self.console.print()
82
+
83
+ while True:
84
+ choice = Prompt.ask(
85
+ "Choose an option",
86
+ choices=["1", "2", "3", "4", "resume", "switch", "logout", "help"],
87
+ default="1"
88
+ ).lower()
89
+
90
+ if choice in ["1", "resume"]:
91
+ return "resume"
92
+ elif choice in ["2", "switch"]:
93
+ return "switch"
94
+ elif choice in ["3", "logout"]:
95
+ return "logout"
96
+ elif choice in ["4", "help"]:
97
+ self.show_help()
98
+ continue
99
+ else:
100
+ self.console.print("[red]Invalid choice. Please try again.[/red]")
101
+
102
+ def show_help(self):
103
+ """Show help for session management"""
104
+ help_text = """
105
+ [bold cyan]Session Management Help[/bold cyan]
106
+
107
+ [bold green]Resume:[/bold green] Continue with your existing session
108
+ • Use your current login and settings
109
+ • No need to re-authenticate
110
+ • All your data and preferences are preserved
111
+
112
+ [bold yellow]Switch:[/bold yellow] Login with a different account
113
+ • Logout from current session
114
+ • Start fresh with new account
115
+ • Previous session data will be cleared
116
+
117
+ [bold red]Logout:[/bold red] Clear session and start fresh
118
+ • Remove all saved login information
119
+ • Start completely fresh
120
+ • You'll need to login again
121
+
122
+ [bold blue]Session Files:[/bold blue]
123
+ • Session: ~/.nocturnal_archive/session.json
124
+ • Config: ~/.nocturnal_archive/config.env
125
+
126
+ [bold blue]Manual Session Management:[/bold blue]
127
+ • To clear session manually: rm ~/.nocturnal_archive/session.json
128
+ • To clear config: rm ~/.nocturnal_archive/config.env
129
+ """
130
+
131
+ self.console.print(Panel(help_text, title="Help", border_style="blue"))
132
+ self.console.print()
133
+
134
+ def clear_session(self) -> bool:
135
+ """Clear the existing session"""
136
+ try:
137
+ if self.session_file.exists():
138
+ self.session_file.unlink()
139
+ if self.config_file.exists():
140
+ self.config_file.unlink()
141
+ self.console.print("[green]✅ Session cleared successfully[/green]")
142
+ return True
143
+ except Exception as e:
144
+ self.console.print(f"[red]❌ Error clearing session: {e}[/red]")
145
+ return False
146
+
147
+ def handle_session_affirmation(self) -> str:
148
+ """Main function to handle session affirmation with user-friendly interface"""
149
+ # Check for existing session
150
+ has_session = self.detect_existing_session()
151
+
152
+ if not has_session:
153
+ self.console.print("[yellow]No existing session found. Starting fresh...[/yellow]")
154
+ return "fresh"
155
+
156
+ # Show session information
157
+ self.show_session_info()
158
+
159
+ # Ask user what to do
160
+ choice = self.ask_session_choice()
161
+
162
+ if choice == "resume":
163
+ self.console.print("[green]✅ Resuming existing session...[/green]")
164
+ return "resume"
165
+ elif choice == "switch":
166
+ self.console.print("[yellow]🔄 Switching to different account...[/yellow]")
167
+ if self.clear_session():
168
+ return "fresh"
169
+ else:
170
+ return "error"
171
+ elif choice == "logout":
172
+ self.console.print("[red]🚪 Logging out...[/red]")
173
+ if self.clear_session():
174
+ return "fresh"
175
+ else:
176
+ return "error"
177
+
178
+ return "error"
179
+
180
+ def setup_environment_variables(self):
181
+ """Set up environment variables for backend mode"""
182
+ # PRODUCTION MODE: Force backend, ensure monetization
183
+ # NEVER load user's .env files in production
184
+
185
+ # Set backend URL if not already set
186
+ if "NOCTURNAL_API_URL" not in os.environ:
187
+ os.environ["NOCTURNAL_API_URL"] = "https://cite-agent-api-720dfadd602c.herokuapp.com/api"
188
+
189
+ # SECURITY: Default to backend mode (USE_LOCAL_KEYS=false)
190
+ # This ensures users MUST authenticate and pay
191
+ if "USE_LOCAL_KEYS" not in os.environ:
192
+ os.environ["USE_LOCAL_KEYS"] = "false"
193
+
194
+ def get_session_status(self) -> Dict[str, Any]:
195
+ """Get current session status for debugging"""
196
+ return {
197
+ "session_file_exists": self.session_file.exists(),
198
+ "config_file_exists": self.config_file.exists(),
199
+ "session_data": self.session_data,
200
+ "use_local_keys": os.environ.get("USE_LOCAL_KEYS", "not set"),
201
+ "api_url": os.environ.get("NOCTURNAL_API_URL", "not set")
202
+ }
203
+
204
+ def main():
205
+ """Test the session manager"""
206
+ sm = SessionManager()
207
+ result = sm.handle_session_affirmation()
208
+ print(f"Result: {result}")
209
+
210
+ # Show status
211
+ status = sm.get_session_status()
212
+ print(f"Status: {status}")
213
+
214
+ if __name__ == "__main__":
215
+ main()
cite_agent/updater.py CHANGED
@@ -12,18 +12,29 @@ from pathlib import Path
12
12
  from typing import Optional, Dict, Any
13
13
 
14
14
  try:
15
- import pkg_resources
15
+ # Use modern importlib.metadata instead of deprecated pkg_resources
16
+ from importlib.metadata import version as get_version
17
+ pkg_resources = None # Not needed anymore
16
18
  except ImportError:
17
- pkg_resources = None
19
+ # Fallback for Python < 3.8
20
+ try:
21
+ import warnings
22
+ with warnings.catch_warnings():
23
+ warnings.filterwarnings("ignore", category=DeprecationWarning)
24
+ warnings.filterwarnings("ignore", category=UserWarning)
25
+ import pkg_resources
26
+ except ImportError:
27
+ pkg_resources = None
28
+ get_version = None
18
29
 
19
30
  class NocturnalUpdater:
20
31
  """Handles automatic updates for Nocturnal Archive"""
21
32
 
22
33
  def __init__(self):
23
34
  self.current_version = self.get_current_version()
24
- self.package_name = "nocturnal-archive"
35
+ self.package_name = "cite-agent" # Fixed: was "nocturnal-archive"
25
36
  self.pypi_url = f"https://pypi.org/pypi/{self.package_name}/json"
26
- self.kill_switch_url = "https://api.nocturnal.dev/api/admin/status"
37
+ self.kill_switch_url = "https://cite-agent-api-720dfadd602c.herokuapp.com/api/health"
27
38
 
28
39
  def check_kill_switch(self) -> Dict[str, Any]:
29
40
  """Check if kill switch is activated"""
@@ -37,16 +48,23 @@ class NocturnalUpdater:
37
48
 
38
49
  def get_current_version(self) -> str:
39
50
  """Get current installed version"""
51
+ # Try modern importlib.metadata first
52
+ try:
53
+ return get_version(self.package_name)
54
+ except Exception:
55
+ pass
56
+
57
+ # Fallback to pkg_resources (deprecated)
40
58
  if pkg_resources:
41
59
  try:
42
60
  return pkg_resources.get_distribution(self.package_name).version
43
- except (pkg_resources.DistributionNotFound, Exception):
61
+ except Exception:
44
62
  pass
45
63
 
46
- # Fallback: try to get version from installed package
64
+ # Last resort: try to get version from installed package
47
65
  try:
48
- import nocturnal_archive
49
- return getattr(nocturnal_archive, '__version__', '1.0.0')
66
+ import cite_agent
67
+ return getattr(cite_agent, '__version__', '1.0.0')
50
68
  except ImportError:
51
69
  return "1.0.0"
52
70
 
@@ -103,32 +121,47 @@ class NocturnalUpdater:
103
121
  except:
104
122
  return False
105
123
 
106
- def update_package(self, force: bool = False) -> bool:
124
+ def update_package(self, force: bool = False, silent: bool = False) -> bool:
107
125
  """Update the package to latest version"""
108
126
  try:
109
- print("🔄 Updating Nocturnal Archive...")
127
+ if not silent:
128
+ print("🔄 Updating cite-agent...")
110
129
 
111
130
  # Check if update is needed
112
131
  if not force:
113
132
  update_info = self.check_for_updates()
114
133
  if not update_info or not update_info["available"]:
115
- print("✅ No updates available")
134
+ if not silent:
135
+ print("✅ No updates available")
116
136
  return True
117
137
 
118
- # Perform update
119
- cmd = [sys.executable, "-m", "pip", "install", "--upgrade", self.package_name]
138
+ # Perform update with user flag to avoid system package conflicts
139
+ cmd = [sys.executable, "-m", "pip", "install", "--upgrade", "--user", self.package_name]
120
140
  result = subprocess.run(cmd, capture_output=True, text=True)
121
141
 
122
142
  if result.returncode == 0:
123
- new_version = self.get_current_version()
124
- print(f"✅ Updated to version {new_version}")
143
+ # Create flag file to notify next launch
144
+ try:
145
+ from pathlib import Path
146
+ update_flag = Path.home() / ".nocturnal_archive" / ".updated"
147
+ update_flag.parent.mkdir(exist_ok=True)
148
+ update_flag.write_text(self.get_current_version())
149
+ except:
150
+ pass
151
+
152
+ if not silent:
153
+ new_version = self.get_current_version()
154
+ print(f"✅ Updated to version {new_version}")
155
+ print("🔄 Restart cite-agent to use the new version")
125
156
  return True
126
157
  else:
127
- print(f"❌ Update failed: {result.stderr}")
158
+ if not silent:
159
+ print(f"❌ Update failed: {result.stderr}")
128
160
  return False
129
161
 
130
162
  except Exception as e:
131
- print(f"❌ Update error: {e}")
163
+ if not silent:
164
+ print(f"❌ Update error: {e}")
132
165
  return False
133
166
 
134
167
  def show_update_status(self):