cite-agent 1.0.3__tar.gz → 1.0.4__tar.gz
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.
- {cite_agent-1.0.3/cite_agent.egg-info → cite_agent-1.0.4}/PKG-INFO +1 -1
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/__init__.py +1 -1
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/account_client.py +46 -19
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/cli.py +1 -1
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/setup_config.py +21 -5
- {cite_agent-1.0.3 → cite_agent-1.0.4/cite_agent.egg-info}/PKG-INFO +1 -1
- {cite_agent-1.0.3 → cite_agent-1.0.4}/setup.py +1 -1
- {cite_agent-1.0.3 → cite_agent-1.0.4}/LICENSE +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/MANIFEST.in +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/README.md +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/__distribution__.py +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/agent_backend_only.py +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/ascii_plotting.py +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/auth.py +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/backend_only_client.py +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/cli_enhanced.py +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/dashboard.py +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/enhanced_ai_agent.py +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/rate_limiter.py +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/telemetry.py +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/ui.py +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/updater.py +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent/web_search.py +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent.egg-info/SOURCES.txt +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent.egg-info/dependency_links.txt +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent.egg-info/entry_points.txt +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent.egg-info/requires.txt +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/cite_agent.egg-info/top_level.txt +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/requirements.txt +0 -0
- {cite_agent-1.0.3 → cite_agent-1.0.4}/setup.cfg +0 -0
|
@@ -7,7 +7,7 @@ prior stacks preserved only in Git history, kept out of the runtime footprint.
|
|
|
7
7
|
|
|
8
8
|
from .enhanced_ai_agent import EnhancedNocturnalAgent, ChatRequest, ChatResponse
|
|
9
9
|
|
|
10
|
-
__version__ = "1.0.
|
|
10
|
+
__version__ = "1.0.4"
|
|
11
11
|
__author__ = "Nocturnal Archive Team"
|
|
12
12
|
__email__ = "contact@nocturnal.dev"
|
|
13
13
|
|
|
@@ -63,14 +63,28 @@ class AccountClient:
|
|
|
63
63
|
)
|
|
64
64
|
self.timeout = timeout
|
|
65
65
|
|
|
66
|
-
def provision(self, email: str, password: str) -> AccountCredentials:
|
|
66
|
+
def provision(self, email: str, password: str, is_new_user: bool = False) -> AccountCredentials:
|
|
67
|
+
"""
|
|
68
|
+
Provision account credentials (login or register).
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
email: User email address
|
|
72
|
+
password: User password
|
|
73
|
+
is_new_user: True for registration, False for login
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
AccountCredentials object
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
AccountProvisioningError: If authentication fails
|
|
80
|
+
"""
|
|
67
81
|
if self.base_url:
|
|
68
|
-
payload = self._request_credentials(email, password)
|
|
82
|
+
payload = self._request_credentials(email, password, is_new_user=is_new_user)
|
|
69
83
|
return AccountCredentials.from_payload(email=email, payload=payload)
|
|
70
84
|
return self._generate_offline_credentials(email, password)
|
|
71
85
|
|
|
72
86
|
# -- internal helpers -------------------------------------------------
|
|
73
|
-
def _request_credentials(self, email: str, password: str) -> Dict[str, Any]:
|
|
87
|
+
def _request_credentials(self, email: str, password: str, is_new_user: bool = False) -> Dict[str, Any]:
|
|
74
88
|
try: # pragma: no cover - requires network
|
|
75
89
|
import requests # type: ignore
|
|
76
90
|
except Exception as exc: # pragma: no cover - executed when requests missing
|
|
@@ -78,29 +92,42 @@ class AccountClient:
|
|
|
78
92
|
"The 'requests' package is required for control-plane authentication"
|
|
79
93
|
) from exc
|
|
80
94
|
|
|
81
|
-
#
|
|
82
|
-
|
|
95
|
+
# Choose endpoint based on is_new_user flag
|
|
96
|
+
if is_new_user:
|
|
97
|
+
endpoint = self.base_url.rstrip("/") + "/api/auth/register"
|
|
98
|
+
operation = "registration"
|
|
99
|
+
else:
|
|
100
|
+
endpoint = self.base_url.rstrip("/") + "/api/auth/login"
|
|
101
|
+
operation = "login"
|
|
102
|
+
|
|
83
103
|
body = {"email": email, "password": password}
|
|
84
104
|
|
|
85
105
|
try:
|
|
86
|
-
response = requests.post(
|
|
106
|
+
response = requests.post(endpoint, json=body, timeout=self.timeout)
|
|
87
107
|
except Exception as exc: # pragma: no cover - network failure
|
|
88
|
-
raise AccountProvisioningError("Failed to reach control plane") from exc
|
|
108
|
+
raise AccountProvisioningError(f"Failed to reach control plane for {operation}") from exc
|
|
89
109
|
|
|
90
|
-
#
|
|
91
|
-
if response.status_code == 401:
|
|
92
|
-
register_endpoint = self.base_url.rstrip("/") + "/api/auth/register"
|
|
93
|
-
try:
|
|
94
|
-
response = requests.post(register_endpoint, json=body, timeout=self.timeout)
|
|
95
|
-
except Exception as exc:
|
|
96
|
-
raise AccountProvisioningError("Failed to register account") from exc
|
|
97
|
-
|
|
98
|
-
# If still failing, raise error
|
|
110
|
+
# Handle errors
|
|
99
111
|
if response.status_code >= 400:
|
|
100
112
|
detail = self._extract_error_detail(response)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
113
|
+
|
|
114
|
+
# Provide helpful error messages
|
|
115
|
+
if response.status_code == 401 and not is_new_user:
|
|
116
|
+
raise AccountProvisioningError(
|
|
117
|
+
f"Login failed: Invalid email or password. If you're a new user, please register first."
|
|
118
|
+
)
|
|
119
|
+
elif response.status_code == 409:
|
|
120
|
+
raise AccountProvisioningError(
|
|
121
|
+
f"Registration failed: This email is already registered. Please log in instead."
|
|
122
|
+
)
|
|
123
|
+
elif response.status_code == 400 and "academic" in detail.lower():
|
|
124
|
+
raise AccountProvisioningError(
|
|
125
|
+
f"Registration requires an academic email address (.edu, .ac.uk, etc.)"
|
|
126
|
+
)
|
|
127
|
+
else:
|
|
128
|
+
raise AccountProvisioningError(
|
|
129
|
+
f"{operation.capitalize()} failed (status {response.status_code}): {detail}"
|
|
130
|
+
)
|
|
104
131
|
|
|
105
132
|
try:
|
|
106
133
|
payload = response.json()
|
|
@@ -63,6 +63,18 @@ class NocturnalConfig:
|
|
|
63
63
|
print("You'll use your institution-issued account to sign in. No invite codes or manual API keys required.")
|
|
64
64
|
print()
|
|
65
65
|
|
|
66
|
+
# Ask if new user or returning user
|
|
67
|
+
print("Are you a new user or returning user?")
|
|
68
|
+
print(" 1. New user (register)")
|
|
69
|
+
print(" 2. Returning user (login)")
|
|
70
|
+
choice = input("Enter choice (1 or 2): ").strip()
|
|
71
|
+
|
|
72
|
+
is_new_user = choice == "1"
|
|
73
|
+
action = "Registration" if is_new_user else "Login"
|
|
74
|
+
|
|
75
|
+
print(f"\n{action}")
|
|
76
|
+
print("-" * 40)
|
|
77
|
+
|
|
66
78
|
email = self._prompt_academic_email()
|
|
67
79
|
if not email:
|
|
68
80
|
return False
|
|
@@ -71,14 +83,18 @@ class NocturnalConfig:
|
|
|
71
83
|
if not password:
|
|
72
84
|
return False
|
|
73
85
|
|
|
74
|
-
if not self._confirm_beta_terms():
|
|
86
|
+
if is_new_user and not self._confirm_beta_terms():
|
|
75
87
|
print("❌ Terms must be accepted to continue")
|
|
76
88
|
return False
|
|
77
89
|
|
|
78
90
|
try:
|
|
79
|
-
credentials = self._provision_account(email, password)
|
|
91
|
+
credentials = self._provision_account(email, password, is_new_user=is_new_user)
|
|
92
|
+
if is_new_user:
|
|
93
|
+
print(f"\n✅ Account created successfully for {email}")
|
|
94
|
+
else:
|
|
95
|
+
print(f"\n✅ Logged in successfully as {email}")
|
|
80
96
|
except AccountProvisioningError as exc:
|
|
81
|
-
print(f"❌
|
|
97
|
+
print(f"❌ {action} failed: {exc}")
|
|
82
98
|
return False
|
|
83
99
|
|
|
84
100
|
print("\n🛡️ Recap of beta limitations:")
|
|
@@ -163,9 +179,9 @@ class NocturnalConfig:
|
|
|
163
179
|
print("❌ Could not confirm password after multiple attempts")
|
|
164
180
|
return None
|
|
165
181
|
|
|
166
|
-
def _provision_account(self, email: str, password: str) -> AccountCredentials:
|
|
182
|
+
def _provision_account(self, email: str, password: str, is_new_user: bool = False) -> AccountCredentials:
|
|
167
183
|
client = AccountClient()
|
|
168
|
-
return client.provision(email=email, password=password)
|
|
184
|
+
return client.provision(email=email, password=password, is_new_user=is_new_user)
|
|
169
185
|
|
|
170
186
|
def _is_academic_email(self, email: str) -> bool:
|
|
171
187
|
if "@" not in email:
|
|
@@ -7,7 +7,7 @@ long_description = readme_path.read_text() if readme_path.exists() else "AI Rese
|
|
|
7
7
|
|
|
8
8
|
setup(
|
|
9
9
|
name="cite-agent",
|
|
10
|
-
version="1.0.
|
|
10
|
+
version="1.0.4",
|
|
11
11
|
author="Cite-Agent Team",
|
|
12
12
|
author_email="contact@citeagent.dev",
|
|
13
13
|
description="AI Research Assistant - Backend-Only Distribution",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|