ForcePilot/backend/package/yuxi/agents/backends/sandbox/provisioner_client.py
Wenjie Zhang acc7a7342f refactor: Sandbox Provisioner 优化 + 数据库模型更新 + 初始化脚本完善
- 优化 Sandbox Provider/Provisioner 配置和错误处理
- 新增 postgres 数据库日志与项目成员模型
- 完善初始化脚本(init.sh/ps1)和 Makefile
- 新增 seed_initial_users.py 初始用户种子脚本
- 更新 uv.lock 依赖锁定
2026-05-26 17:40:17 +08:00

73 lines
2.6 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
import httpx
@dataclass(slots=True)
class SandboxRecord:
sandbox_id: str
sandbox_url: str
status: str | None = None
class ProvisionerClient:
def __init__(self, base_url: str, *, timeout_seconds: int = 20):
self._base_url = base_url.rstrip("/")
self._timeout = httpx.Timeout(timeout_seconds)
def _request(self, method: str, path: str, **kwargs) -> httpx.Response:
return httpx.request(
method=method,
url=f"{self._base_url}{path}",
timeout=self._timeout,
**kwargs,
)
def health(self) -> bool:
response = self._request("GET", "/health")
return response.status_code == 200
def create(self, sandbox_id: str, thread_id: str, uid: str, env: dict[str, str] | None = None) -> SandboxRecord:
response = self._request(
"POST",
"/api/sandboxes",
json={"sandbox_id": sandbox_id, "thread_id": thread_id, "uid": uid, "env": env or {}},
)
if response.status_code >= 400:
raise RuntimeError(f"failed to create sandbox {sandbox_id}: {response.status_code} {response.text}")
payload = response.json()
return SandboxRecord(
sandbox_id=payload["sandbox_id"],
sandbox_url=payload["sandbox_url"],
status=payload.get("status"),
)
def discover(self, sandbox_id: str) -> SandboxRecord | None:
response = self._request("GET", f"/api/sandboxes/{sandbox_id}")
if response.status_code == 404:
return None
if response.status_code >= 400:
raise RuntimeError(f"failed to discover sandbox {sandbox_id}: {response.status_code} {response.text}")
payload = response.json()
return SandboxRecord(
sandbox_id=payload["sandbox_id"],
sandbox_url=payload["sandbox_url"],
status=payload.get("status"),
)
def touch(self, sandbox_id: str) -> bool:
response = self._request("POST", f"/api/sandboxes/{sandbox_id}/touch")
if response.status_code == 404:
return False
if response.status_code >= 400:
raise RuntimeError(f"failed to touch sandbox {sandbox_id}: {response.status_code} {response.text}")
return True
def delete(self, sandbox_id: str) -> None:
response = self._request("DELETE", f"/api/sandboxes/{sandbox_id}")
if response.status_code in {200, 404}:
return
raise RuntimeError(f"failed to delete sandbox {sandbox_id}: {response.status_code} {response.text}")