chore: import upstream snapshot with attribution
CI: cua-driver distro-compat matrix / debian:12 (glibc 2.36) (push) Has been cancelled
CI: SPDX Headers / Check SPDX headers (warn-only) (push) Has been cancelled
CD: Docs MCP Server / build (linux/amd64) (push) Has been cancelled
CD: Docs MCP Server / build (linux/arm64) (push) Has been cancelled
CD: Docs MCP Server / merge (push) Has been cancelled
CI: cua-driver distro-compat matrix / Resolve release version (push) Has been cancelled
CI: cua-driver distro-compat matrix / fedora:41 (glibc 2.40) (push) Has been cancelled
CI: cua-driver distro-compat matrix / rockylinux:9 (glibc 2.34) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:22.04 (glibc 2.35) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:24.04 (glibc 2.39) (push) Has been cancelled
CI: cua-driver distro-compat matrix / Distro compat summary (push) Has been cancelled
CI: Rust Linux unit / Rust Linux unit and compile (push) Has been cancelled
CI: Rust Windows unit / Rust Windows unit and compile (push) Has been cancelled
CI: Nix Linux Rust source / Nix / compositor build (push) Has been cancelled
CI: Nix Linux Rust source / Nix / driver package (push) Has been cancelled
CI: Nix Linux Rust source / Nix / Rust unit tests (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:03:19 +08:00
commit 91e75e620b
3227 changed files with 1307078 additions and 0 deletions
@@ -0,0 +1,5 @@
"""cua-train — Python client for the Cua Cloud API."""
from .client import TrainClient
__all__ = ["TrainClient"]
@@ -0,0 +1,117 @@
"""TrainClient — authenticated httpx client for the Cua Cloud API."""
from __future__ import annotations
import threading
import time
import httpx
class TrainClient:
"""Authenticated client for the Cua Cloud API.
Obtain via :meth:`from_key` using a per-user key (``ukey-…``) from
your Cua Cloud account. The client transparently refreshes its bearer
token before it expires.
Both the control plane (VM claim lifecycle via ``/api/k8s``) and the
data plane (exec/upload/screenshot via ``/api/svc``) are reached through
the same base URL (default: ``https://run.cua.ai``).
"""
_DEFAULT_TOKEN_URL = "https://auth.cua.ai/realms/cyclops-cs/protocol/openid-connect/token"
_DEFAULT_BASE_URL = "https://run.cua.ai"
def __init__(
self,
token: str,
base_url: str = _DEFAULT_BASE_URL,
*,
token_url: str = _DEFAULT_TOKEN_URL,
client_id: str = "",
client_secret: str = "",
token_deadline: float = 0.0,
) -> None:
self._token = token
self._base_url = base_url.rstrip("/")
self._token_url = token_url
self._client_id = client_id
self._client_secret = client_secret
self._token_deadline = token_deadline
self._refresh_lock = threading.Lock()
self._async_client: httpx.AsyncClient | None = None
@classmethod
def from_key(
cls,
*,
token_url: str = _DEFAULT_TOKEN_URL,
client_id: str,
client_secret: str,
base_url: str = _DEFAULT_BASE_URL,
) -> "TrainClient":
"""Exchange client credentials for a bearer token.
Args:
token_url: Keycloak token endpoint.
client_id: Per-user key client ID (``ukey-…``).
client_secret: Client secret.
base_url: Cua Cloud API base URL (default: ``https://run.cua.ai``).
"""
token, ttl = cls._exchange(token_url, client_id, client_secret)
return cls(
token=token,
base_url=base_url,
token_url=token_url,
client_id=client_id,
client_secret=client_secret,
token_deadline=time.monotonic() + ttl,
)
@staticmethod
def _exchange(token_url: str, client_id: str, client_secret: str) -> tuple[str, float]:
resp = httpx.post(
token_url,
data={
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
},
)
resp.raise_for_status()
body = resp.json()
return body["access_token"], float(body.get("expires_in", 300))
def reset_token(self) -> None:
"""Force re-exchange on the next :meth:`get_async_httpx_client` call."""
self._token_deadline = 0
def _refresh_token(self) -> None:
if not self._client_id:
return
if time.monotonic() < self._token_deadline - 30:
return
with self._refresh_lock:
if time.monotonic() < self._token_deadline - 30:
return
token, ttl = self._exchange(self._token_url, self._client_id, self._client_secret)
self._token = token
self._token_deadline = time.monotonic() + ttl
auth_header = f"Bearer {token}"
if self._async_client is not None:
self._async_client.headers["Authorization"] = auth_header
def get_async_httpx_client(self) -> httpx.AsyncClient:
"""Return an async httpx client with the current bearer token.
Re-mints the token transparently when it is about to expire.
"""
self._refresh_token()
if self._async_client is None:
self._async_client = httpx.AsyncClient(
base_url=self._base_url,
headers={"Authorization": f"Bearer {self._token}"},
follow_redirects=False,
)
return self._async_client