Add initial VM Provider

This commit is contained in:
f-trycua
2025-05-08 15:29:33 -07:00
parent 7a687af98f
commit 9fdcf2f7df
14 changed files with 511 additions and 6122 deletions
+1 -1
View File
@@ -105,7 +105,7 @@
"${workspaceFolder:cua-root}/libs/som",
"${workspaceFolder:cua-root}/libs/pylume"
],
"python.languageServer": "Pylance",
"python.languageServer": "None",
"[python]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "ms-python.black-formatter",
+2 -1
View File
@@ -20,7 +20,7 @@ for path in pythonpath.split(":"):
sys.path.append(path)
print(f"Added to sys.path: {path}")
from computer.computer import Computer
from computer import Computer, VMProviderType
from computer.logger import LogLevel
from computer.utils import get_image_size
@@ -37,6 +37,7 @@ async def main():
os_type="macos",
verbosity=LogLevel.NORMAL, # Use QUIET to suppress most logs
use_host_computer_server=False,
provider_type=VMProviderType.LUME, # Explicitly use the Lume provider
)
try:
await computer.run()
+5 -1
View File
@@ -42,6 +42,10 @@ except Exception as e:
# Other issues with telemetry
logger.warning(f"Error initializing telemetry: {e}")
# Core components
from .computer import Computer
__all__ = ["Computer"]
# Provider components
from .providers.base import VMProviderType
__all__ = ["Computer", "VMProviderType"]
+129 -92
View File
@@ -1,6 +1,4 @@
from typing import Optional, List, Literal, Dict, Any, Union, TYPE_CHECKING, cast
from pylume import PyLume
from pylume.models import VMRunOpts, VMUpdateOpts, ImageRef, SharedDirectory, VMStatus
import asyncio
from .models import Computer as ComputerConfig, Display
from .interface.factory import InterfaceFactory
@@ -14,13 +12,12 @@ import logging
from .telemetry import record_computer_initialization
import os
# Import provider related modules
from .providers.base import VMProviderType
from .providers.factory import VMProviderFactory
OSType = Literal["macos", "linux"]
# Import BaseComputerInterface for type annotations
if TYPE_CHECKING:
from .interface.base import BaseComputerInterface
class Computer:
"""Computer is the main class for interacting with the computer."""
@@ -36,8 +33,10 @@ class Computer:
use_host_computer_server: bool = False,
verbosity: Union[int, LogLevel] = logging.INFO,
telemetry_enabled: bool = True,
provider_type: Union[str, VMProviderType] = VMProviderType.LUME,
port: Optional[int] = 3000,
host: str = os.environ.get("PYLUME_HOST", "localhost"),
storage_path: Optional[str] = None,
):
"""Initialize a new Computer instance.
@@ -57,8 +56,11 @@ class Computer:
verbosity: Logging level (standard Python logging levels: logging.DEBUG, logging.INFO, etc.)
LogLevel enum values are still accepted for backward compatibility
telemetry_enabled: Whether to enable telemetry tracking. Defaults to True.
port: Optional port to use for the PyLume server
host: Host to use for PyLume connections (e.g. "localhost", "host.docker.internal")
provider_type: The VM provider type to use (lume, qemu, cloud)
port: Optional port to use for the VM provider server
host: Host to use for VM provider connections (e.g. "localhost", "host.docker.internal")
bin_path: Optional path to the VM provider binary
storage_path: Optional path to store VM data
"""
self.logger = Logger("cua.computer", verbosity)
@@ -69,6 +71,8 @@ class Computer:
self.port = port
self.host = host
self.os_type = os_type
self.provider_type = provider_type
self.storage_path = storage_path
# Store telemetry preference
self._telemetry_enabled = telemetry_enabled
@@ -116,25 +120,17 @@ class Computer:
memory=memory,
cpu=cpu,
)
# Initialize PyLume but don't start the server yet - we'll do that in run()
self.config.pylume = PyLume(
debug=(self.verbosity == LogLevel.DEBUG),
port=3000,
use_existing_server=False,
server_start_timeout=120, # Increase timeout to 2 minutes
)
# Initialize VM provider but don't start it yet - we'll do that in run()
self.config.vm_provider = None # Will be initialized in run()
# Store shared directories config
self.shared_directories = shared_directories or []
# Placeholder for VM provider context manager
self._provider_context = None
# Initialize with proper typing - None at first, will be set in run()
self._interface = None
self.os = os
self.shared_paths = []
if shared_directories:
for path in shared_directories:
abs_path = os.path.abspath(os.path.expanduser(path))
if not os.path.exists(abs_path):
raise ValueError(f"Shared directory does not exist: {path}")
self.shared_paths.append(abs_path)
self._pylume_context = None
self.use_host_computer_server = use_host_computer_server
# Record initialization in telemetry (if enabled)
@@ -164,7 +160,7 @@ class Computer:
# We could add cleanup here if needed in the future
pass
async def run(self) -> None:
async def run(self) -> Optional[str]:
"""Initialize the VM and computer interface."""
if TYPE_CHECKING:
from .interface.base import BaseComputerInterface
@@ -199,74 +195,99 @@ class Computer:
else:
# Start or connect to VM
self.logger.info(f"Starting VM: {self.image}")
if not self._pylume_context:
if not self._provider_context:
try:
self.logger.verbose("Initializing PyLume context...")
provider_type_name = self.provider_type.name if isinstance(self.provider_type, VMProviderType) else self.provider_type
self.logger.verbose(f"Initializing {provider_type_name} provider context...")
# Configure PyLume based on initialization parameters
pylume_kwargs = {
"debug": self.verbosity <= LogLevel.DEBUG,
"server_start_timeout": 120, # Increase timeout to 2 minutes
# Configure provider based on initialization parameters
provider_kwargs = {
"storage_path": self.storage_path,
"verbose": self.verbosity >= LogLevel.DEBUG,
}
# Add port if specified
if hasattr(self, "port") and self.port is not None:
pylume_kwargs["port"] = self.port
self.logger.verbose(f"Using specified port for PyLume: {self.port}")
# Set port if specified
if self.port is not None:
provider_kwargs["port"] = self.port
self.logger.verbose(f"Using specified port for provider: {self.port}")
# Add host if specified
if hasattr(self, "host") and self.host != "localhost":
pylume_kwargs["host"] = self.host
self.logger.verbose(f"Using specified host for PyLume: {self.host}")
# Set host if specified
if self.host:
provider_kwargs["host"] = self.host
self.logger.verbose(f"Using specified host for provider: {self.host}")
# Create PyLume instance with configured parameters
self.config.pylume = PyLume(**pylume_kwargs)
self._pylume_context = await self.config.pylume.__aenter__() # type: ignore[attr-defined]
self.logger.verbose("PyLume context initialized successfully")
# Create VM provider instance with configured parameters
try:
self.config.vm_provider = VMProviderFactory.create_provider(
self.provider_type, **provider_kwargs
)
self._provider_context = await self.config.vm_provider.__aenter__()
self.logger.verbose("VM provider context initialized successfully")
except ImportError as ie:
self.logger.error(f"Failed to import provider dependencies: {ie}")
if str(ie).find("lume") >= 0:
self.logger.error("Please install with: pip install cua-computer[lume]")
elif str(ie).find("qemu") >= 0:
self.logger.error("Please install with: pip install cua-computer[qemu]")
elif str(ie).find("cloud") >= 0:
self.logger.error("Please install with: pip install cua-computer[cloud]")
raise
except Exception as e:
self.logger.error(f"Failed to initialize PyLume context: {e}")
raise RuntimeError(f"Failed to initialize PyLume: {e}")
self.logger.error(f"Failed to initialize provider context: {e}")
raise RuntimeError(f"Failed to initialize VM provider: {e}")
# Try to get the VM, if it doesn't exist, return an error
# Check if VM exists or create it
try:
vm = await self.config.pylume.get_vm(self.config.name) # type: ignore[attr-defined]
if self.config.vm_provider is None:
raise RuntimeError(f"VM provider not initialized for {self.config.name}")
vm = await self.config.vm_provider.get_vm(self.config.name)
self.logger.verbose(f"Found existing VM: {self.config.name}")
except Exception as e:
self.logger.error(f"VM not found: {self.config.name}")
self.logger.error(
f"Please pull the VM first with lume pull macos-sequoia-cua-sparse:latest: {e}"
)
self.logger.error(f"Error: {e}")
raise RuntimeError(
f"VM not found: {self.config.name}. Please pull the VM first."
f"VM {self.config.name} could not be found or created."
)
# Convert paths to SharedDirectory objects
shared_directories = []
for path in self.shared_paths:
# Convert paths to dictionary format for shared directories
shared_dirs = []
for path in self.shared_directories:
self.logger.verbose(f"Adding shared directory: {path}")
shared_directories.append(
SharedDirectory(host_path=path) # type: ignore[arg-type]
)
path = os.path.abspath(os.path.expanduser(path))
if not os.path.exists(path):
self.logger.warning(f"Shared directory does not exist: {path}")
continue
shared_dirs.append({"host_path": path, "vm_path": path})
# Run with shared directories
self.logger.info(f"Starting VM {self.config.name}...")
run_opts = VMRunOpts(
no_display=False, # type: ignore[arg-type]
shared_directories=shared_directories, # type: ignore[arg-type]
)
# Create VM run options with specs from config
# Account for optional shared directories
run_opts = {
"cpu": int(self.config.cpu),
"memory": self.config.memory,
"display": {
"width": self.config.display.width,
"height": self.config.display.height
}
}
if shared_dirs:
run_opts["shared_directories"] = shared_dirs
# Log the run options for debugging
self.logger.info(f"VM run options: {vars(run_opts)}")
self.logger.info(f"VM run options: {run_opts}")
# Log the equivalent curl command for debugging
payload = json.dumps({"noDisplay": False, "sharedDirectories": []})
curl_cmd = f"curl -X POST 'http://localhost:3000/lume/vms/{self.config.name}/run' -H 'Content-Type: application/json' -d '{payload}'"
self.logger.info(f"Equivalent curl command:")
self.logger.info(f"{curl_cmd}")
# self.logger.info(f"Equivalent curl command:")
# self.logger.info(f"{curl_cmd}")
try:
response = await self.config.pylume.run_vm(self.config.name, run_opts) # type: ignore[attr-defined]
if self.config.vm_provider is None:
raise RuntimeError(f"VM provider not initialized for {self.config.name}")
response = await self.config.vm_provider.run_vm(self.config.name, run_opts)
self.logger.info(f"VM run response: {response if response else 'None'}")
except Exception as run_error:
self.logger.error(f"Failed to run VM: {run_error}")
@@ -275,11 +296,14 @@ class Computer:
# Wait for VM to be ready with required properties
self.logger.info("Waiting for VM to be ready...")
try:
vm = await self.wait_vm_ready()
if not vm or not vm.ip_address: # type: ignore[attr-defined]
ip = await self.get_ip()
if ip:
self.logger.info(f"VM is ready with IP: {ip}")
# Store the IP address for later use instead of returning early
ip_address = ip
else:
# If no IP was found, try to raise a helpful error
raise RuntimeError(f"VM {self.config.name} failed to get IP address")
ip_address = vm.ip_address # type: ignore[attr-defined]
self.logger.info(f"VM is ready with IP: {ip_address}")
except Exception as wait_error:
self.logger.error(f"Error waiting for VM: {wait_error}")
raise RuntimeError(f"VM failed to become ready: {wait_error}")
@@ -356,15 +380,18 @@ class Computer:
else:
self._interface.close()
if not self.use_host_computer_server and self._pylume_context:
if not self.use_host_computer_server and self._provider_context:
try:
self.logger.info(f"Stopping VM {self.config.name}...")
await self.config.pylume.stop_vm(self.config.name) # type: ignore[attr-defined]
if self.config.vm_provider is not None:
await self.config.vm_provider.stop_vm(self.config.name)
except Exception as e:
self.logger.verbose(f"Error stopping VM: {e}") # VM might already be stopped
self.logger.verbose("Closing PyLume context...")
await self.config.pylume.__aexit__(None, None, None) # type: ignore[attr-defined]
self._pylume_context = None
self.logger.error(f"Error stopping VM: {e}")
self.logger.verbose("Closing VM provider context...")
if self.config.vm_provider is not None:
await self.config.vm_provider.__aexit__(None, None, None)
self._provider_context = None
self.logger.info("Computer stopped")
except Exception as e:
self.logger.debug(
@@ -384,7 +411,7 @@ class Computer:
ip = await self.config.get_ip()
return ip or "unknown" # Return "unknown" if ip is None
async def wait_vm_ready(self) -> Optional[Union[Dict[str, Any], "VMStatus"]]:
async def wait_vm_ready(self) -> Optional[Dict[str, Any]]:
"""Wait for VM to be ready with an IP address.
Returns:
@@ -407,7 +434,7 @@ class Computer:
try:
# Keep polling for VM info
vm = await self.config.pylume.get_vm(self.config.name) # type: ignore[attr-defined]
vm = await self.config.vm_provider.get_vm(self.config.name)
# Log full VM properties for debugging (every 30 attempts)
if attempts % 30 == 0:
@@ -447,10 +474,11 @@ class Computer:
self.logger.error(f"Persistent error getting VM status: {str(e)}")
self.logger.info("Trying to get VM list for debugging...")
try:
vms = await self.config.pylume.list_vms() # type: ignore[attr-defined]
self.logger.info(
f"Available VMs: {[vm.name for vm in vms if hasattr(vm, 'name')]}"
)
if self.config.vm_provider is not None:
vms = await self.config.vm_provider.list_vms()
self.logger.info(
f"Available VMs: {[getattr(vm, 'name', None) for vm in vms if hasattr(vm, 'name')]}"
)
except Exception as list_error:
self.logger.error(f"Failed to list VMs: {str(list_error)}")
@@ -462,9 +490,14 @@ class Computer:
# Try to get final VM status for debugging
try:
vm = await self.config.pylume.get_vm(self.config.name) # type: ignore[attr-defined]
status = getattr(vm, "status", "unknown") if vm else "unknown"
ip = getattr(vm, "ip_address", None) if vm else None
if self.config.vm_provider is not None:
vm = await self.config.vm_provider.get_vm(self.config.name)
# VMStatus is a Pydantic model with attributes, not a dictionary
status = vm.status if vm else "unknown"
ip = vm.ip_address if vm else None
else:
status = "unknown"
ip = None
self.logger.error(f"Final VM status: {status}, IP: {ip}")
except Exception as e:
self.logger.error(f"Failed to get final VM status: {str(e)}")
@@ -478,10 +511,14 @@ class Computer:
self.logger.info(
f"Updating VM settings: CPU={cpu or self.config.cpu}, Memory={memory or self.config.memory}"
)
update_opts = VMUpdateOpts(
cpu=cpu or int(self.config.cpu), memory=memory or self.config.memory
)
await self.config.pylume.update_vm(self.config.image, update_opts) # type: ignore[attr-defined]
update_opts = {
"cpu": cpu or int(self.config.cpu),
"memory": memory or self.config.memory
}
if self.config.vm_provider is not None:
await self.config.vm_provider.update_vm(self.config.name, update_opts)
else:
raise RuntimeError("VM provider not initialized")
def get_screenshot_size(self, screenshot: bytes) -> Dict[str, int]:
"""Get the dimensions of a screenshot.
+12 -5
View File
@@ -1,8 +1,10 @@
"""Models for computer configuration."""
from dataclasses import dataclass
from typing import Optional
from pylume import PyLume
from typing import Optional, Any, Dict
# Import base provider interface
from .providers.base import BaseVMProvider
@dataclass
class Display:
@@ -26,10 +28,15 @@ class Computer:
display: Display
memory: str
cpu: str
pylume: Optional[PyLume] = None
vm_provider: Optional[BaseVMProvider] = None
# @property # Remove the property decorator
async def get_ip(self) -> Optional[str]:
"""Get the IP address of the VM."""
vm = await self.pylume.get_vm(self.name) # type: ignore[attr-defined]
return vm.ip_address if vm else None
if not self.vm_provider:
return None
vm = await self.vm_provider.get_vm(self.name)
# PyLume returns a VMStatus object, not a dictionary
# Access ip_address as an attribute, not with .get()
return vm.ip_address if vm else None
@@ -0,0 +1,4 @@
"""Provider implementations for different VM backends."""
# Import specific providers only when needed to avoid circular imports
__all__ = [] # Let each provider module handle its own exports
+51
View File
@@ -0,0 +1,51 @@
"""Base provider interface for VM backends."""
import abc
from enum import Enum
from typing import Dict, List, Optional, Any, AsyncContextManager
class VMProviderType(str, Enum):
"""Enum of supported VM provider types."""
LUME = "lume"
QEMU = "qemu"
CLOUD = "cloud"
UNKNOWN = "unknown"
class BaseVMProvider(AsyncContextManager):
"""Base interface for VM providers.
All VM provider implementations must implement this interface.
"""
@property
@abc.abstractmethod
def provider_type(self) -> VMProviderType:
"""Get the provider type."""
pass
@abc.abstractmethod
async def get_vm(self, name: str) -> Dict[str, Any]:
"""Get VM information by name."""
pass
@abc.abstractmethod
async def list_vms(self) -> List[Dict[str, Any]]:
"""List all available VMs."""
pass
@abc.abstractmethod
async def run_vm(self, name: str, run_opts: Dict[str, Any]) -> Dict[str, Any]:
"""Run a VM with the given options."""
pass
@abc.abstractmethod
async def stop_vm(self, name: str) -> Dict[str, Any]:
"""Stop a running VM."""
pass
@abc.abstractmethod
async def update_vm(self, name: str, update_opts: Dict[str, Any]) -> Dict[str, Any]:
"""Update VM configuration."""
pass
@@ -0,0 +1,75 @@
"""Factory for creating VM providers."""
import logging
from typing import Dict, Optional, Any, Type, Union
from .base import BaseVMProvider, VMProviderType
logger = logging.getLogger(__name__)
class VMProviderFactory:
"""Factory for creating VM providers based on provider type."""
@staticmethod
def create_provider(
provider_type: Union[str, VMProviderType],
**kwargs
) -> BaseVMProvider:
"""Create a VM provider of the specified type.
Args:
provider_type: Type of VM provider to create
**kwargs: Additional arguments to pass to the provider constructor
Returns:
An instance of the requested VM provider
Raises:
ImportError: If the required dependencies for the provider are not installed
ValueError: If the provider type is not supported
"""
# Convert string to enum if needed
if isinstance(provider_type, str):
try:
provider_type = VMProviderType(provider_type.lower())
except ValueError:
provider_type = VMProviderType.UNKNOWN
if provider_type == VMProviderType.LUME:
try:
from .lume import LumeProvider, HAS_LUME
if not HAS_LUME:
raise ImportError(
"The pylume package is required for LumeProvider. "
"Please install it with 'pip install cua-computer[lume]'"
)
return LumeProvider(**kwargs)
except ImportError as e:
logger.error(f"Failed to import LumeProvider: {e}")
raise ImportError(
"The pylume package is required for LumeProvider. "
"Please install it with 'pip install cua-computer[lume]'"
) from e
elif provider_type == VMProviderType.QEMU:
try:
from .qemu import QEMUProvider
return QEMUProvider(**kwargs)
except ImportError as e:
logger.error(f"Failed to import QEMUProvider: {e}")
raise ImportError(
"The qemu package is required for QEMUProvider. "
"Please install it with 'pip install cua-computer[qemu]'"
) from e
elif provider_type == VMProviderType.CLOUD:
try:
from .cloud import CloudProvider
return CloudProvider(**kwargs)
except ImportError as e:
logger.error(f"Failed to import CloudProvider: {e}")
raise ImportError(
"Cloud provider dependencies are required for CloudProvider. "
"Please install them with 'pip install cua-computer[cloud]'"
) from e
else:
raise ValueError(f"Unsupported provider type: {provider_type}")
@@ -0,0 +1,9 @@
"""Lume VM provider implementation."""
try:
from .provider import LumeProvider
HAS_LUME = True
__all__ = ["LumeProvider"]
except ImportError:
HAS_LUME = False
__all__ = []
@@ -0,0 +1,129 @@
"""Lume VM provider implementation."""
import logging
from typing import Dict, List, Optional, Any, Tuple, TypeVar, Type
# Only import pylume when this module is actually used
try:
from pylume import PyLume
from pylume.models import VMRunOpts, VMUpdateOpts, ImageRef, SharedDirectory, VMStatus
HAS_PYLUME = True
except ImportError:
HAS_PYLUME = False
# Create dummy classes for type checking
class PyLume:
pass
class VMRunOpts:
pass
class VMUpdateOpts:
pass
class ImageRef:
pass
class SharedDirectory:
pass
class VMStatus:
pass
from ..base import BaseVMProvider, VMProviderType
logger = logging.getLogger(__name__)
class LumeProvider(BaseVMProvider):
"""Lume VM provider implementation using pylume."""
def __init__(
self,
port: Optional[int] = None,
host: str = "localhost",
bin_path: Optional[str] = None,
storage_path: Optional[str] = None,
verbose: bool = False,
**kwargs
):
"""Initialize the Lume provider.
Args:
port: Optional port to use for the PyLume server
host: Host to use for PyLume connections
bin_path: Optional path to the Lume binary
storage_path: Optional path to store VM data
verbose: Enable verbose logging
"""
if not HAS_PYLUME:
raise ImportError(
"The pylume package is required for LumeProvider. "
"Please install it with 'pip install cua-computer[lume]'"
)
# PyLume doesn't accept bin_path or storage_path parameters
# Convert verbose to debug parameter for PyLume
self._pylume = PyLume(
port=port,
host=host,
debug=verbose,
**kwargs
)
# Store these for reference, even though PyLume doesn't use them directly
self._bin_path = bin_path
self._storage_path = storage_path
self._context = None
@property
def provider_type(self) -> VMProviderType:
"""Get the provider type."""
return VMProviderType.LUME
async def __aenter__(self):
"""Enter async context manager."""
self._context = await self._pylume.__aenter__()
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
"""Exit async context manager."""
if self._context:
await self._pylume.__aexit__(exc_type, exc_val, exc_tb)
self._context = None
async def get_vm(self, name: str) -> VMStatus:
"""Get VM information by name."""
# PyLume get_vm returns a VMStatus object, not a dictionary
return await self._pylume.get_vm(name)
async def list_vms(self) -> List[Dict[str, Any]]:
"""List all available VMs."""
return await self._pylume.list_vms()
async def run_vm(self, name: str, run_opts: Dict[str, Any]) -> Dict[str, Any]:
"""Run a VM with the given options."""
# Convert dict to VMRunOpts if needed
if isinstance(run_opts, dict):
run_opts = VMRunOpts(**run_opts)
return await self._pylume.run_vm(name, run_opts)
async def stop_vm(self, name: str) -> Dict[str, Any]:
"""Stop a running VM."""
return await self._pylume.stop_vm(name)
async def update_vm(self, name: str, update_opts: Dict[str, Any]) -> Dict[str, Any]:
"""Update VM configuration."""
# Convert dict to VMUpdateOpts if needed
if isinstance(update_opts, dict):
update_opts = VMUpdateOpts(**update_opts)
return await self._pylume.update_vm(name, update_opts)
# Pylume-specific helper methods
def get_pylume_instance(self) -> PyLume:
"""Get the underlying PyLume instance."""
return self._pylume
# Helper methods for converting between PyLume and generic types
@staticmethod
def create_vm_run_opts(**kwargs) -> VMRunOpts:
"""Create VMRunOpts from kwargs."""
return VMRunOpts(**kwargs)
@staticmethod
def create_vm_update_opts(**kwargs) -> VMUpdateOpts:
"""Create VMUpdateOpts from kwargs."""
return VMUpdateOpts(**kwargs)
@@ -0,0 +1,9 @@
"""QEMU VM provider implementation."""
try:
from .provider import QEMUProvider
HAS_QEMU = True
__all__ = ["QEMUProvider"]
except ImportError:
HAS_QEMU = False
__all__ = []
@@ -0,0 +1,77 @@
"""QEMU VM provider implementation."""
import logging
from typing import Dict, List, Optional, Any, AsyncContextManager
from ..base import BaseVMProvider, VMProviderType
logger = logging.getLogger(__name__)
class QEMUProvider(BaseVMProvider):
"""QEMU VM provider implementation.
This is a placeholder implementation. The actual implementation would
use QEMU's API to manage virtual machines.
"""
def __init__(
self,
bin_path: Optional[str] = None,
storage_path: Optional[str] = None,
port: Optional[int] = None,
host: str = "localhost",
verbose: bool = False,
**kwargs
):
"""Initialize the QEMU provider.
Args:
bin_path: Optional path to the QEMU binary
storage_path: Optional path to store VM data
port: Optional port for management
host: Host to use for connections
verbose: Enable verbose logging
"""
self._context = None
self._verbose = verbose
self._bin_path = bin_path
self._storage_path = storage_path
self._port = port
self._host = host
@property
def provider_type(self) -> VMProviderType:
"""Get the provider type."""
return VMProviderType.QEMU
async def __aenter__(self):
"""Enter async context manager."""
# In a real implementation, this would initialize the QEMU management API
self._context = True
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
"""Exit async context manager."""
# In a real implementation, this would clean up QEMU resources
self._context = None
async def get_vm(self, name: str) -> Dict[str, Any]:
"""Get VM information by name."""
raise NotImplementedError("QEMU provider is not implemented yet")
async def list_vms(self) -> List[Dict[str, Any]]:
"""List all available VMs."""
raise NotImplementedError("QEMU provider is not implemented yet")
async def run_vm(self, name: str, run_opts: Dict[str, Any]) -> Dict[str, Any]:
"""Run a VM with the given options."""
raise NotImplementedError("QEMU provider is not implemented yet")
async def stop_vm(self, name: str) -> Dict[str, Any]:
"""Stop a running VM."""
raise NotImplementedError("QEMU provider is not implemented yet")
async def update_vm(self, name: str, update_opts: Dict[str, Any]) -> Dict[str, Any]:
"""Update VM configuration."""
raise NotImplementedError("QEMU provider is not implemented yet")
+8 -1
View File
@@ -11,7 +11,6 @@ authors = [
{ name = "TryCua", email = "gh@trycua.com" }
]
dependencies = [
"pylume>=0.1.8",
"pillow>=10.0.0",
"websocket-client>=1.8.0",
"websockets>=12.0",
@@ -22,10 +21,18 @@ dependencies = [
requires-python = ">=3.10"
[project.optional-dependencies]
lume = [
"pylume>=0.1.8"
]
ui = [
"gradio>=5.23.3,<6.0.0",
"python-dotenv>=1.0.1,<2.0.0",
]
all = [
"pylume>=0.1.8",
"gradio>=5.23.3,<6.0.0",
"python-dotenv>=1.0.1,<2.0.0",
]
[tool.pdm]
distribution = true
Generated
-6021
View File
File diff suppressed because it is too large Load Diff