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
+144
View File
@@ -0,0 +1,144 @@
"""HTTP API client for CUA cloud services."""
import hashlib
import os
from pathlib import Path
from typing import Any, Optional
from urllib.parse import quote
import aiohttp
from cua_cli.auth.store import require_api_key
from cua_core.http import cua_version_headers
DEFAULT_API_BASE = "https://api.cua.ai"
def get_api_base() -> str:
"""Get the API base URL."""
return os.environ.get("CUA_API_BASE", DEFAULT_API_BASE).rstrip("/")
class CloudAPIClient:
"""HTTP client for CUA cloud API."""
def __init__(self, api_key: Optional[str] = None, api_base: Optional[str] = None):
self.api_key = api_key or require_api_key()
self.api_base = api_base or get_api_base()
def _headers(self) -> dict[str, str]:
return {
"Authorization": f"Bearer {self.api_key}",
"Accept": "application/json",
**cua_version_headers(),
}
async def _request(
self,
method: str,
path: str,
json: Optional[dict] = None,
timeout: int = 30,
) -> tuple[int, Any]:
"""Make an HTTP request to the API.
Returns:
Tuple of (status_code, response_data)
"""
url = f"{self.api_base}{path}"
headers = self._headers()
if json is not None:
headers["Content-Type"] = "application/json"
async with aiohttp.ClientSession() as session:
timeout_obj = aiohttp.ClientTimeout(total=timeout)
async with session.request(
method, url, headers=headers, json=json, timeout=timeout_obj
) as resp:
try:
data = await resp.json(content_type=None)
except Exception:
data = await resp.text()
return resp.status, data
# Image API methods
async def list_images(self) -> list[dict[str, Any]]:
"""List all images in the workspace."""
status, data = await self._request("GET", "/v1/images")
if status == 200 and isinstance(data, list):
return data
return []
async def initiate_upload(
self,
name: str,
tag: str,
image_type: str,
size_bytes: int,
checksum_sha256: str,
) -> tuple[int, dict[str, Any]]:
"""Initiate a multi-part upload session.
Returns:
Tuple of (status_code, session_data)
"""
path = f"/v1/images/{quote(name, safe='')}/upload"
body = {
"tag": tag,
"image_type": image_type,
"size_bytes": size_bytes,
"checksum_sha256": checksum_sha256,
}
return await self._request("POST", path, json=body)
async def get_upload_part_url(
self, name: str, upload_id: str, part_number: int
) -> tuple[int, dict[str, Any]]:
"""Get a signed URL for uploading a part."""
path = f"/v1/images/{quote(name, safe='')}/upload/{upload_id}/part/{part_number}"
return await self._request("GET", path)
async def complete_upload(
self, name: str, upload_id: str, parts: list[dict[str, Any]]
) -> tuple[int, dict[str, Any]]:
"""Complete a multi-part upload."""
path = f"/v1/images/{quote(name, safe='')}/upload/{upload_id}/complete"
return await self._request("POST", path, json={"parts": parts})
async def abort_upload(self, name: str, upload_id: str) -> tuple[int, Any]:
"""Abort an upload session."""
path = f"/v1/images/{quote(name, safe='')}/upload/{upload_id}"
return await self._request("DELETE", path)
async def get_download_url(self, name: str, tag: str) -> tuple[int, dict[str, Any]]:
"""Get a signed URL for downloading an image."""
path = f"/v1/images/{quote(name, safe='')}/download?tag={quote(tag, safe='')}"
return await self._request("GET", path)
async def delete_image(self, name: str, tag: str) -> tuple[int, Any]:
"""Delete an image version."""
path = f"/v1/images/{quote(name, safe='')}?tag={quote(tag, safe='')}"
return await self._request("DELETE", path)
def calculate_file_hash(file_path: Path) -> str:
"""Calculate SHA256 hash of a file."""
sha256 = hashlib.sha256()
with open(file_path, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
sha256.update(chunk)
return sha256.hexdigest()
def format_bytes(size_bytes: int) -> str:
"""Format bytes in human-readable format."""
if size_bytes == 0:
return "0 B"
units = ["B", "KB", "MB", "GB", "TB"]
k = 1024
i = 0
while size_bytes >= k and i < len(units) - 1:
size_bytes /= k
i += 1
return f"{size_bytes:.2f} {units[i]}"