Deprecate legacy login/register methods with warnings

- Add DeprecationWarning to login() and register() static methods
- Update docstrings with .. deprecated:: notices
- Sync types with backend changes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 4.6 committed 2026-03-25 00:48:33 -04:00
1 parent 024ea8ee97
commit 47a8038e8d
4 files changed
+329 -9

No files matched your search

+44
View File
@@ -18,17 +18,32 @@ from .types import (
EAR, EAR,
Agent, Agent,
AgentKey, AgentKey,
AgentAssignment,
AdminEvent,
EpochAnchor,
MemberEntry,
Operation, Operation,
Receipt, Receipt,
Epoch, Epoch,
Export, Export,
Organization, Organization,
User, User,
WebhookEntry,
AdminAction,
AgentStatus,
ExportStatus,
IntegrationType,
KeyStatus,
RbacRole,
RegisterAgentRequest, RegisterAgentRequest,
RegisterAgentResponse, RegisterAgentResponse,
UpdateAgentRequest,
UpdateAgentResponse,
GetAgentResponse, GetAgentResponse,
ListAgentsResponse, ListAgentsResponse,
DeleteAgentResponse, DeleteAgentResponse,
FreezeAgentResponse,
UnfreezeAgentResponse,
SubmitOperationResponse, SubmitOperationResponse,
GetOperationResponse, GetOperationResponse,
VerifyOperationResponse, VerifyOperationResponse,
@@ -38,6 +53,10 @@ from .types import (
CreateExportResponse, CreateExportResponse,
GetExportResponse, GetExportResponse,
ListExportsResponse, ListExportsResponse,
ListWebhooksResponse,
RegisterWebhookResponse,
ListMembersResponse,
ListAdminEventsResponse,
JWKSResponse, JWKSResponse,
AuthRegisterResponse, AuthRegisterResponse,
AuthLoginResponse, AuthLoginResponse,
@@ -47,6 +66,7 @@ from .types import (
IssueTokenResponse, IssueTokenResponse,
RotateTokenResponse, RotateTokenResponse,
HealthResponse, HealthResponse,
DeepHealthResponse,
) )
__version__ = "1.2.0" __version__ = "1.2.0"
@@ -66,21 +86,40 @@ __all__ = [
"base64url_decode", "base64url_decode",
"generate_nonce", "generate_nonce",
"generate_uuidv7", "generate_uuidv7",
# Literal type aliases
"AdminAction",
"AgentStatus",
"ExportStatus",
"IntegrationType",
"KeyStatus",
"RbacRole",
# Entity types
"EOR", "EOR",
"EAR", "EAR",
"Agent", "Agent",
"AgentAssignment",
"AgentKey", "AgentKey",
"AdminEvent",
"EpochAnchor",
"MemberEntry",
"Operation", "Operation",
"Receipt", "Receipt",
"Epoch", "Epoch",
"Export", "Export",
"Organization", "Organization",
"User", "User",
"WebhookEntry",
# Request types
"RegisterAgentRequest", "RegisterAgentRequest",
"UpdateAgentRequest",
# Response types
"RegisterAgentResponse", "RegisterAgentResponse",
"UpdateAgentResponse",
"GetAgentResponse", "GetAgentResponse",
"ListAgentsResponse", "ListAgentsResponse",
"DeleteAgentResponse", "DeleteAgentResponse",
"FreezeAgentResponse",
"UnfreezeAgentResponse",
"SubmitOperationResponse", "SubmitOperationResponse",
"GetOperationResponse", "GetOperationResponse",
"VerifyOperationResponse", "VerifyOperationResponse",
@@ -90,6 +129,10 @@ __all__ = [
"CreateExportResponse", "CreateExportResponse",
"GetExportResponse", "GetExportResponse",
"ListExportsResponse", "ListExportsResponse",
"ListWebhooksResponse",
"RegisterWebhookResponse",
"ListMembersResponse",
"ListAdminEventsResponse",
"JWKSResponse", "JWKSResponse",
"AuthRegisterResponse", "AuthRegisterResponse",
"AuthLoginResponse", "AuthLoginResponse",
@@ -99,4 +142,5 @@ __all__ = [
"IssueTokenResponse", "IssueTokenResponse",
"RotateTokenResponse", "RotateTokenResponse",
"HealthResponse", "HealthResponse",
"DeepHealthResponse",
] ]
+77 -4
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import time import time
import asyncio import asyncio
import warnings
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Optional, Union
import aiohttp import aiohttp
@@ -15,8 +16,10 @@ from .types import (
AuthLoginResponse, AuthLoginResponse,
AuthRegisterResponse, AuthRegisterResponse,
CreateExportResponse, CreateExportResponse,
DeepHealthResponse,
DeleteAgentResponse, DeleteAgentResponse,
EOR, EOR,
FreezeAgentResponse,
GetAgentResponse, GetAgentResponse,
GetEpochResponse, GetEpochResponse,
GetExportResponse, GetExportResponse,
@@ -24,6 +27,9 @@ from .types import (
GetOperationResponse, GetOperationResponse,
HealthResponse, HealthResponse,
IssueApiTokenResponse, IssueApiTokenResponse,
ListAdminEventsResponse,
ListMembersResponse,
ListWebhooksResponse,
RotateApiTokenResponse, RotateApiTokenResponse,
JWKSResponse, JWKSResponse,
ListAgentsResponse, ListAgentsResponse,
@@ -31,7 +37,10 @@ from .types import (
ListExportsResponse, ListExportsResponse,
RegisterAgentRequest, RegisterAgentRequest,
RegisterAgentResponse, RegisterAgentResponse,
RegisterWebhookResponse,
SubmitOperationResponse, SubmitOperationResponse,
UnfreezeAgentResponse,
UpdateAgentResponse,
VerifyOperationResponse, VerifyOperationResponse,
) )
from .utils import generate_nonce, generate_uuidv7 from .utils import generate_nonce, generate_uuidv7
@@ -172,7 +181,16 @@ class AsyncElydoraClient:
display_name: Optional[str] = None, display_name: Optional[str] = None,
org_name: Optional[str] = None, org_name: Optional[str] = None,
) -> AuthRegisterResponse: ) -> AuthRegisterResponse:
"""Register a new user and organization.""" """Register a new user and organization.
.. deprecated::
Use Better Auth endpoints directly. See docs.
"""
warnings.warn(
"AsyncElydoraClient.register() is deprecated. Use Better Auth endpoints directly. See docs.",
DeprecationWarning,
stacklevel=2,
)
url = f"{base_url.rstrip('/')}/v1/auth/register" url = f"{base_url.rstrip('/')}/v1/auth/register"
body: Dict[str, Any] = {"email": email, "password": password} body: Dict[str, Any] = {"email": email, "password": password}
if display_name is not None: if display_name is not None:
@@ -187,7 +205,16 @@ class AsyncElydoraClient:
@staticmethod @staticmethod
async def login(base_url: str, email: str, password: str) -> AuthLoginResponse: async def login(base_url: str, email: str, password: str) -> AuthLoginResponse:
"""Authenticate and receive a session token.""" """Authenticate and receive a session token.
.. deprecated::
Use Better Auth endpoints directly. See docs.
"""
warnings.warn(
"AsyncElydoraClient.login() is deprecated. Use Better Auth endpoints directly. See docs.",
DeprecationWarning,
stacklevel=2,
)
url = f"{base_url.rstrip('/')}/v1/auth/login" url = f"{base_url.rstrip('/')}/v1/auth/login"
body = {"email": email, "password": password} body = {"email": email, "password": password}
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
@@ -235,9 +262,13 @@ class AsyncElydoraClient:
"""List all agents for the organization.""" """List all agents for the organization."""
return await self._request("GET", "/v1/agents") return await self._request("GET", "/v1/agents")
async def unfreeze_agent(self, agent_id: str, reason: str) -> None: async def unfreeze_agent(self, agent_id: str, reason: str) -> UnfreezeAgentResponse:
"""Unfreeze an agent.""" """Unfreeze an agent."""
await self._request("POST", f"/v1/agents/{agent_id}/unfreeze", json_body={"reason": reason}) return await self._request("POST", f"/v1/agents/{agent_id}/unfreeze", json_body={"reason": reason})
async def update_agent(self, agent_id: str, integration_type: str) -> UpdateAgentResponse:
"""Update an agent's integration type."""
return await self._request("PATCH", f"/v1/agents/{agent_id}", json_body={"integration_type": integration_type})
async def delete_agent(self, agent_id: str) -> DeleteAgentResponse: async def delete_agent(self, agent_id: str) -> DeleteAgentResponse:
"""Delete an agent.""" """Delete an agent."""
@@ -399,6 +430,41 @@ class AsyncElydoraClient:
await self._handle_response(resp) await self._handle_response(resp)
return await resp.read() return await resp.read()
# -----------------------------------------------------------------
# Webhooks
# -----------------------------------------------------------------
async def list_webhooks(self) -> ListWebhooksResponse:
"""List all webhooks for the organization."""
return await self._request("GET", "/v1/webhooks")
async def register_webhook(self, endpoint_url: str, events: list, secret: str) -> RegisterWebhookResponse:
"""Register a new webhook."""
return await self._request("POST", "/v1/webhooks", json_body={"endpoint_url": endpoint_url, "events": events, "secret": secret})
async def delete_webhook(self, webhook_id: str) -> None:
"""Delete a webhook."""
await self._request("DELETE", f"/v1/webhooks/{webhook_id}")
# -----------------------------------------------------------------
# Members
# -----------------------------------------------------------------
async def list_members(self) -> ListMembersResponse:
"""List all members in the organization."""
return await self._request("GET", "/v1/members")
# -----------------------------------------------------------------
# Admin events
# -----------------------------------------------------------------
async def list_admin_events(self, limit: Optional[int] = None) -> ListAdminEventsResponse:
"""List recent admin events."""
params: Dict[str, str] = {}
if limit is not None:
params["limit"] = str(limit)
return await self._request("GET", "/v1/admin/events", params=params)
# ----------------------------------------------------------------- # -----------------------------------------------------------------
# JWKS # JWKS
# ----------------------------------------------------------------- # -----------------------------------------------------------------
@@ -420,3 +486,10 @@ class AsyncElydoraClient:
session = await self._get_session() session = await self._get_session()
async with session.get(url, timeout=aiohttp.ClientTimeout(total=30)) as resp: async with session.get(url, timeout=aiohttp.ClientTimeout(total=30)) as resp:
return await self._handle_response(resp) return await self._handle_response(resp)
async def deep_health(self) -> DeepHealthResponse:
"""Check API deep health (public, no auth required)."""
url = f"{self.base_url}/v1/health/deep"
session = await self._get_session()
async with session.get(url, timeout=aiohttp.ClientTimeout(total=30)) as resp:
return await self._handle_response(resp)
+76 -4
View File
@@ -3,6 +3,7 @@
from __future__ import annotations from __future__ import annotations
import time import time
import warnings
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Optional, Union
import requests import requests
@@ -14,8 +15,10 @@ from .types import (
AuthLoginResponse, AuthLoginResponse,
AuthRegisterResponse, AuthRegisterResponse,
CreateExportResponse, CreateExportResponse,
DeepHealthResponse,
DeleteAgentResponse, DeleteAgentResponse,
EOR, EOR,
FreezeAgentResponse,
GetAgentResponse, GetAgentResponse,
GetEpochResponse, GetEpochResponse,
GetExportResponse, GetExportResponse,
@@ -23,6 +26,9 @@ from .types import (
GetOperationResponse, GetOperationResponse,
HealthResponse, HealthResponse,
IssueApiTokenResponse, IssueApiTokenResponse,
ListAdminEventsResponse,
ListMembersResponse,
ListWebhooksResponse,
RotateApiTokenResponse, RotateApiTokenResponse,
JWKSResponse, JWKSResponse,
ListAgentsResponse, ListAgentsResponse,
@@ -30,7 +36,10 @@ from .types import (
ListExportsResponse, ListExportsResponse,
RegisterAgentRequest, RegisterAgentRequest,
RegisterAgentResponse, RegisterAgentResponse,
RegisterWebhookResponse,
SubmitOperationResponse, SubmitOperationResponse,
UnfreezeAgentResponse,
UpdateAgentResponse,
VerifyOperationResponse, VerifyOperationResponse,
) )
from .utils import generate_nonce, generate_uuidv7 from .utils import generate_nonce, generate_uuidv7
@@ -169,7 +178,16 @@ class ElydoraClient:
display_name: Optional[str] = None, display_name: Optional[str] = None,
org_name: Optional[str] = None, org_name: Optional[str] = None,
) -> AuthRegisterResponse: ) -> AuthRegisterResponse:
"""Register a new user and organization.""" """Register a new user and organization.
.. deprecated::
Use Better Auth endpoints directly. See docs.
"""
warnings.warn(
"ElydoraClient.register() is deprecated. Use Better Auth endpoints directly. See docs.",
DeprecationWarning,
stacklevel=2,
)
url = f"{base_url.rstrip('/')}/v1/auth/register" url = f"{base_url.rstrip('/')}/v1/auth/register"
body: Dict[str, Any] = {"email": email, "password": password} body: Dict[str, Any] = {"email": email, "password": password}
if display_name is not None: if display_name is not None:
@@ -181,7 +199,16 @@ class ElydoraClient:
@staticmethod @staticmethod
def login(base_url: str, email: str, password: str) -> AuthLoginResponse: def login(base_url: str, email: str, password: str) -> AuthLoginResponse:
"""Authenticate and receive a session token.""" """Authenticate and receive a session token.
.. deprecated::
Use Better Auth endpoints directly. See docs.
"""
warnings.warn(
"ElydoraClient.login() is deprecated. Use Better Auth endpoints directly. See docs.",
DeprecationWarning,
stacklevel=2,
)
url = f"{base_url.rstrip('/')}/v1/auth/login" url = f"{base_url.rstrip('/')}/v1/auth/login"
body = {"email": email, "password": password} body = {"email": email, "password": password}
resp = requests.post(url, json=body, headers={"Content-Type": "application/json"}, timeout=30) resp = requests.post(url, json=body, headers={"Content-Type": "application/json"}, timeout=30)
@@ -226,9 +253,13 @@ class ElydoraClient:
"""List all agents for the organization.""" """List all agents for the organization."""
return self._request("GET", "/v1/agents") return self._request("GET", "/v1/agents")
def unfreeze_agent(self, agent_id: str, reason: str) -> None: def unfreeze_agent(self, agent_id: str, reason: str) -> UnfreezeAgentResponse:
"""Unfreeze an agent.""" """Unfreeze an agent."""
self._request("POST", f"/v1/agents/{agent_id}/unfreeze", json_body={"reason": reason}) return self._request("POST", f"/v1/agents/{agent_id}/unfreeze", json_body={"reason": reason})
def update_agent(self, agent_id: str, integration_type: str) -> UpdateAgentResponse:
"""Update an agent's integration type."""
return self._request("PATCH", f"/v1/agents/{agent_id}", json_body={"integration_type": integration_type})
def delete_agent(self, agent_id: str) -> DeleteAgentResponse: def delete_agent(self, agent_id: str) -> DeleteAgentResponse:
"""Delete an agent.""" """Delete an agent."""
@@ -389,6 +420,41 @@ class ElydoraClient:
self._handle_response(resp) self._handle_response(resp)
return resp.content return resp.content
# -----------------------------------------------------------------
# Webhooks
# -----------------------------------------------------------------
def list_webhooks(self) -> ListWebhooksResponse:
"""List all webhooks for the organization."""
return self._request("GET", "/v1/webhooks")
def register_webhook(self, endpoint_url: str, events: list, secret: str) -> RegisterWebhookResponse:
"""Register a new webhook."""
return self._request("POST", "/v1/webhooks", json_body={"endpoint_url": endpoint_url, "events": events, "secret": secret})
def delete_webhook(self, webhook_id: str) -> None:
"""Delete a webhook."""
self._request("DELETE", f"/v1/webhooks/{webhook_id}")
# -----------------------------------------------------------------
# Members
# -----------------------------------------------------------------
def list_members(self) -> ListMembersResponse:
"""List all members in the organization."""
return self._request("GET", "/v1/members")
# -----------------------------------------------------------------
# Admin events
# -----------------------------------------------------------------
def list_admin_events(self, limit: Optional[int] = None) -> ListAdminEventsResponse:
"""List recent admin events."""
params: Dict[str, str] = {}
if limit is not None:
params["limit"] = str(limit)
return self._request("GET", "/v1/admin/events", params=params)
# ----------------------------------------------------------------- # -----------------------------------------------------------------
# JWKS # JWKS
# ----------------------------------------------------------------- # -----------------------------------------------------------------
@@ -408,3 +474,9 @@ class ElydoraClient:
url = f"{self.base_url}/v1/health" url = f"{self.base_url}/v1/health"
resp = self._session.get(url, timeout=30) resp = self._session.get(url, timeout=30)
return self._handle_response(resp) return self._handle_response(resp)
def deep_health(self) -> DeepHealthResponse:
"""Check API deep health (public, no auth required)."""
url = f"{self.base_url}/v1/health/deep"
resp = self._session.get(url, timeout=30)
return self._handle_response(resp)
+132 -1
View File
@@ -28,6 +28,28 @@ RbacRole = Literal[
"readonly_investigator", "readonly_investigator",
"integration_engineer", "integration_engineer",
] ]
IntegrationType = Literal[
"openai",
"anthropic",
"gemini",
"langchain",
"llamaindex",
"autogen",
"crewai",
"custom",
]
AdminAction = Literal[
"agent.freeze",
"agent.unfreeze",
"agent.revoke_key",
"agent.delete",
"member.invite",
"member.remove",
"member.role_change",
"webhook.register",
"webhook.delete",
"org.update",
]
ErrorCode = Literal[ ErrorCode = Literal[
"INVALID_SIGNATURE", "INVALID_SIGNATURE",
"UNKNOWN_AGENT", "UNKNOWN_AGENT",
@@ -56,6 +78,7 @@ class Agent(TypedDict):
display_name: str display_name: str
responsible_entity: str responsible_entity: str
status: AgentStatus status: AgentStatus
integration_type: str
created_at: int created_at: int
updated_at: int updated_at: int
@@ -111,6 +134,8 @@ class Epoch(TypedDict):
class Organization(TypedDict): class Organization(TypedDict):
org_id: str org_id: str
name: str name: str
description: Optional[str]
ba_org_id: Optional[str]
created_at: int created_at: int
updated_at: int updated_at: int
@@ -247,9 +272,16 @@ class AuditQueryResponse(TypedDict):
total_count: int total_count: int
class EpochAnchor(TypedDict, total=False):
tsa_url: str
anchored_at: int
tsa_token: str
root_hash: str
class GetEpochResponse(TypedDict, total=False): class GetEpochResponse(TypedDict, total=False):
epoch: Epoch epoch: Epoch
anchor: Dict[str, Any] anchor: EpochAnchor
class ListEpochsResponse(TypedDict): class ListEpochsResponse(TypedDict):
@@ -327,3 +359,102 @@ class HealthResponse(TypedDict):
version: str version: str
protocol_version: str protocol_version: str
timestamp: int timestamp: int
# ---------------------------------------------------------------------------
# Additional entity types
# ---------------------------------------------------------------------------
class AdminEvent(TypedDict):
event_id: str
org_id: str
actor_user_id: str
action: str
target_type: str
target_id: str
metadata: Optional[Dict[str, Any]]
created_at: int
class AgentAssignment(TypedDict):
assignment_id: str
agent_id: str
org_id: str
assigned_to: str
assigned_by: str
created_at: int
class WebhookEntry(TypedDict):
webhook_id: str
org_id: str
endpoint_url: str
events: List[str]
created_at: int
updated_at: int
class MemberEntry(TypedDict):
user_id: str
org_id: str
email: str
display_name: str
role: RbacRole
status: Literal["active", "suspended"]
created_at: int
# ---------------------------------------------------------------------------
# Additional request types
# ---------------------------------------------------------------------------
class UpdateAgentRequest(TypedDict, total=False):
integration_type: str
# ---------------------------------------------------------------------------
# Additional response types
# ---------------------------------------------------------------------------
class UpdateAgentResponse(TypedDict):
agent: Agent
class FreezeAgentResponse(TypedDict):
agent_id: str
previous_status: AgentStatus
status: AgentStatus
class UnfreezeAgentResponse(TypedDict):
agent_id: str
previous_status: AgentStatus
status: AgentStatus
class ListWebhooksResponse(TypedDict):
webhooks: List[WebhookEntry]
class RegisterWebhookResponse(TypedDict):
webhook: WebhookEntry
class ListMembersResponse(TypedDict):
members: List[MemberEntry]
class ListAdminEventsResponse(TypedDict):
events: List[AdminEvent]
cursor: Optional[str]
class DeepHealthResponse(TypedDict, total=False):
status: str
version: str
protocol_version: str
timestamp: int
checks: Dict[str, Any]