fix: expose the rotation grace deadline and encode agent ids in paths

Add previous_token_grace_until to RotateApiTokenResponse and
percent-encode the agent id path segment in both clients.

Claude-Session: https://claude.ai/code/session_01Agoc9bRVE9KL7mmWtey9Vq
This commit is contained in:
雷电芽衣 committed 2026-09-03 20:12:50 -04:00
1 parent 7b3ea2922c
commit 11a152bab8
3 files changed
+15 -12

No files matched your search

+7 -6
View File
@@ -3,6 +3,7 @@
from __future__ import annotations from __future__ import annotations
import warnings import warnings
from urllib.parse import quote
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Optional, Union
import aiohttp import aiohttp
@@ -212,19 +213,19 @@ class AsyncElydoraClient:
return await self._request("POST", "/v1/agents/register", json_body=request) return await self._request("POST", "/v1/agents/register", json_body=request)
async def get_agent(self, agent_id: str) -> GetAgentResponse: async def get_agent(self, agent_id: str) -> GetAgentResponse:
return await self._request("GET", f"/v1/agents/{agent_id}") return await self._request("GET", f"/v1/agents/{quote(agent_id, safe='')}")
async def list_agents(self) -> ListAgentsResponse: async def list_agents(self) -> ListAgentsResponse:
return await self._request("GET", "/v1/agents") return await self._request("GET", "/v1/agents")
async def freeze_agent(self, agent_id: str, reason: str) -> FreezeAgentResponse: async def freeze_agent(self, agent_id: str, reason: str) -> FreezeAgentResponse:
return await self._request( return await self._request(
"POST", f"/v1/agents/{agent_id}/freeze", json_body={"reason": reason} "POST", f"/v1/agents/{quote(agent_id, safe='')}/freeze", json_body={"reason": reason}
) )
async def unfreeze_agent(self, agent_id: str, reason: str) -> UnfreezeAgentResponse: async def unfreeze_agent(self, agent_id: str, reason: str) -> UnfreezeAgentResponse:
return await self._request( return await self._request(
"POST", f"/v1/agents/{agent_id}/unfreeze", json_body={"reason": reason} "POST", f"/v1/agents/{quote(agent_id, safe='')}/unfreeze", json_body={"reason": reason}
) )
async def update_agent( async def update_agent(
@@ -232,15 +233,15 @@ class AsyncElydoraClient:
) -> UpdateAgentResponse: ) -> UpdateAgentResponse:
require_integration_type(integration_type) require_integration_type(integration_type)
return await self._request( return await self._request(
"PATCH", f"/v1/agents/{agent_id}", json_body={"integration_type": integration_type} "PATCH", f"/v1/agents/{quote(agent_id, safe='')}", json_body={"integration_type": integration_type}
) )
async def delete_agent(self, agent_id: str) -> DeleteAgentResponse: async def delete_agent(self, agent_id: str) -> DeleteAgentResponse:
return await self._request("DELETE", f"/v1/agents/{agent_id}") return await self._request("DELETE", f"/v1/agents/{quote(agent_id, safe='')}")
async def revoke_key(self, agent_id: str, kid: str, reason: str) -> None: async def revoke_key(self, agent_id: str, kid: str, reason: str) -> None:
await self._request( await self._request(
"POST", f"/v1/agents/{agent_id}/revoke", json_body={"kid": kid, "reason": reason} "POST", f"/v1/agents/{quote(agent_id, safe='')}/revoke", json_body={"kid": kid, "reason": reason}
) )
def create_operation( def create_operation(
+7 -6
View File
@@ -3,6 +3,7 @@
from __future__ import annotations from __future__ import annotations
import warnings import warnings
from urllib.parse import quote
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Optional, Union
import requests import requests
@@ -198,29 +199,29 @@ class ElydoraClient:
return self._request("POST", "/v1/agents/register", json_body=request) return self._request("POST", "/v1/agents/register", json_body=request)
def get_agent(self, agent_id: str) -> GetAgentResponse: def get_agent(self, agent_id: str) -> GetAgentResponse:
return self._request("GET", f"/v1/agents/{agent_id}") return self._request("GET", f"/v1/agents/{quote(agent_id, safe='')}")
def list_agents(self) -> ListAgentsResponse: def list_agents(self) -> ListAgentsResponse:
return self._request("GET", "/v1/agents") return self._request("GET", "/v1/agents")
def freeze_agent(self, agent_id: str, reason: str) -> FreezeAgentResponse: def freeze_agent(self, agent_id: str, reason: str) -> FreezeAgentResponse:
return self._request("POST", f"/v1/agents/{agent_id}/freeze", json_body={"reason": reason}) return self._request("POST", f"/v1/agents/{quote(agent_id, safe='')}/freeze", json_body={"reason": reason})
def unfreeze_agent(self, agent_id: str, reason: str) -> UnfreezeAgentResponse: def unfreeze_agent(self, agent_id: str, reason: str) -> UnfreezeAgentResponse:
return self._request("POST", f"/v1/agents/{agent_id}/unfreeze", json_body={"reason": reason}) return self._request("POST", f"/v1/agents/{quote(agent_id, safe='')}/unfreeze", json_body={"reason": reason})
def update_agent(self, agent_id: str, integration_type: IntegrationType) -> UpdateAgentResponse: def update_agent(self, agent_id: str, integration_type: IntegrationType) -> UpdateAgentResponse:
require_integration_type(integration_type) require_integration_type(integration_type)
return self._request( return self._request(
"PATCH", f"/v1/agents/{agent_id}", json_body={"integration_type": integration_type} "PATCH", f"/v1/agents/{quote(agent_id, safe='')}", json_body={"integration_type": integration_type}
) )
def delete_agent(self, agent_id: str) -> DeleteAgentResponse: def delete_agent(self, agent_id: str) -> DeleteAgentResponse:
return self._request("DELETE", f"/v1/agents/{agent_id}") return self._request("DELETE", f"/v1/agents/{quote(agent_id, safe='')}")
def revoke_key(self, agent_id: str, kid: str, reason: str) -> None: def revoke_key(self, agent_id: str, kid: str, reason: str) -> None:
self._request( self._request(
"POST", f"/v1/agents/{agent_id}/revoke", json_body={"kid": kid, "reason": reason} "POST", f"/v1/agents/{quote(agent_id, safe='')}/revoke", json_body={"kid": kid, "reason": reason}
) )
def create_operation( def create_operation(
+1
View File
@@ -359,6 +359,7 @@ class RotateApiTokenResponse(TypedDict):
token: str token: str
expires_at: Optional[int] expires_at: Optional[int]
token_id: str token_id: str
previous_token_grace_until: int
class HealthResponse(TypedDict): class HealthResponse(TypedDict):