91e75e620b
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
106 lines
2.9 KiB
Python
106 lines
2.9 KiB
Python
"""Persistent state tracking for local sandboxes.
|
|
|
|
Each running local sandbox writes a JSON file at ~/.cua/sandboxes/{name}.json.
|
|
This allows Sandbox.connect(name, local=True), Sandbox.list(local=True), etc.
|
|
to work across Python process restarts.
|
|
|
|
State files are written by Sandbox.create() for local runtimes and deleted by
|
|
Sandbox.delete(). Ephemeral sandboxes never write state files.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
from typing import Any, Optional
|
|
|
|
SANDBOX_STATE_DIR = Path.home() / ".cua" / "sandboxes"
|
|
|
|
|
|
def _state_path(name: str) -> Path:
|
|
return SANDBOX_STATE_DIR / f"{name}.json"
|
|
|
|
|
|
def save(
|
|
name: str,
|
|
*,
|
|
runtime_type: str,
|
|
image: dict,
|
|
host: str,
|
|
api_port: int,
|
|
vnc_port: Optional[int] = None,
|
|
qmp_port: Optional[int] = None,
|
|
grpc_port: Optional[int] = None,
|
|
adb_serial: Optional[str] = None,
|
|
sdk_root: Optional[str] = None,
|
|
disk_path: Optional[str] = None,
|
|
os_type: Optional[str] = None,
|
|
vnc_display: Optional[int] = None,
|
|
memory_mb: Optional[int] = None,
|
|
cpu_count: Optional[int] = None,
|
|
arch: Optional[str] = None,
|
|
status: str = "running",
|
|
) -> None:
|
|
"""Write or overwrite the state file for a local sandbox."""
|
|
SANDBOX_STATE_DIR.mkdir(parents=True, exist_ok=True)
|
|
data: dict[str, Any] = {
|
|
"name": name,
|
|
"runtime_type": runtime_type,
|
|
"image": image,
|
|
"host": host,
|
|
"api_port": api_port,
|
|
"vnc_port": vnc_port,
|
|
"qmp_port": qmp_port,
|
|
"grpc_port": grpc_port,
|
|
"adb_serial": adb_serial,
|
|
"sdk_root": sdk_root,
|
|
"disk_path": disk_path,
|
|
"os_type": os_type,
|
|
"vnc_display": vnc_display,
|
|
"memory_mb": memory_mb,
|
|
"cpu_count": cpu_count,
|
|
"arch": arch,
|
|
"status": status,
|
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
|
}
|
|
_state_path(name).write_text(json.dumps(data, indent=2))
|
|
|
|
|
|
def load(name: str) -> Optional[dict]:
|
|
"""Load state for a named sandbox, or None if not found."""
|
|
p = _state_path(name)
|
|
if not p.exists():
|
|
return None
|
|
try:
|
|
return json.loads(p.read_text())
|
|
except (json.JSONDecodeError, OSError):
|
|
return None
|
|
|
|
|
|
def update(name: str, **fields: Any) -> None:
|
|
"""Update specific fields in an existing state file."""
|
|
data = load(name)
|
|
if data is None:
|
|
return
|
|
data.update(fields)
|
|
_state_path(name).write_text(json.dumps(data, indent=2))
|
|
|
|
|
|
def delete(name: str) -> None:
|
|
"""Remove the state file for a sandbox."""
|
|
_state_path(name).unlink(missing_ok=True)
|
|
|
|
|
|
def list_all() -> list[dict]:
|
|
"""Return all state file contents."""
|
|
if not SANDBOX_STATE_DIR.exists():
|
|
return []
|
|
result = []
|
|
for p in SANDBOX_STATE_DIR.glob("*.json"):
|
|
try:
|
|
result.append(json.loads(p.read_text()))
|
|
except (json.JSONDecodeError, OSError):
|
|
pass
|
|
return result
|