99 lines
2.9 KiB
Python
99 lines
2.9 KiB
Python
"""Thin HTTP client for the control plane API."""
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
import httpx
|
|
|
|
|
|
class ApiError(RuntimeError):
|
|
def __init__(self, status: int, message: str) -> None:
|
|
self.status = status
|
|
super().__init__(f"API {status}: {message}")
|
|
|
|
|
|
class ControlPlaneClient:
|
|
def __init__(self, api_url: str, token: str | None = None) -> None:
|
|
self.api_url = api_url.rstrip("/")
|
|
self.token = token
|
|
|
|
def _headers(self) -> dict[str, str]:
|
|
h = {"content-type": "application/json"}
|
|
if self.token:
|
|
h["authorization"] = f"bearer {self.token}"
|
|
return h
|
|
|
|
def _request(self, method: str, path: str, **kw: Any) -> Any:
|
|
url = f"{self.api_url}{path}"
|
|
with httpx.Client(timeout=30.0) as c:
|
|
resp = c.request(method, url, headers=self._headers(), **kw)
|
|
if resp.status_code >= 400:
|
|
try:
|
|
detail = resp.json().get("detail", resp.text)
|
|
except Exception: # noqa: BLE001
|
|
detail = resp.text
|
|
raise ApiError(resp.status_code, str(detail))
|
|
if resp.status_code == 204 or not resp.content:
|
|
return None
|
|
return resp.json()
|
|
|
|
def signup(self, email: str, password: str) -> dict[str, Any]:
|
|
return self._request("POST", "/v1/auth/signup", json={"email": email, "password": password})
|
|
|
|
def login(self, email: str, password: str) -> dict[str, Any]:
|
|
return self._request("POST", "/v1/auth/login", json={"email": email, "password": password})
|
|
|
|
def me(self) -> dict[str, Any]:
|
|
return self._request("GET", "/v1/me")
|
|
|
|
def register_agent(
|
|
self,
|
|
*,
|
|
name: str,
|
|
description: str,
|
|
version: str,
|
|
image: str,
|
|
public: bool,
|
|
card: dict[str, Any],
|
|
) -> dict[str, Any]:
|
|
return self._request(
|
|
"POST",
|
|
"/v1/agents",
|
|
json={
|
|
"name": name,
|
|
"description": description,
|
|
"version": version,
|
|
"image": image,
|
|
"public": public,
|
|
"card": card,
|
|
},
|
|
)
|
|
|
|
def from_source(
|
|
self,
|
|
*,
|
|
name: str,
|
|
description: str,
|
|
version: str,
|
|
public: bool,
|
|
) -> dict[str, Any]:
|
|
return self._request(
|
|
"POST",
|
|
"/v1/agents/from-source",
|
|
json={
|
|
"name": name,
|
|
"description": description,
|
|
"version": version,
|
|
"public": public,
|
|
},
|
|
)
|
|
|
|
def list_agents(self) -> list[dict[str, Any]]:
|
|
return self._request("GET", "/v1/agents")
|
|
|
|
def get_agent(self, name: str) -> dict[str, Any]:
|
|
return self._request("GET", f"/v1/agents/{name}")
|
|
|
|
def delete_agent(self, name: str) -> None:
|
|
self._request("DELETE", f"/v1/agents/{name}")
|