cua-computer 0.2.10__py3-none-any.whl → 0.2.12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- computer/__init__.py +2 -2
- computer/computer.py +23 -7
- computer/helpers.py +4 -1
- computer/interface/factory.py +6 -3
- computer/interface/linux.py +1 -1
- computer/interface/macos.py +1 -1
- computer/interface/windows.py +687 -0
- computer/providers/base.py +1 -0
- computer/providers/factory.py +22 -0
- computer/providers/lume_api.py +0 -13
- computer/providers/lumier/provider.py +57 -64
- computer/providers/winsandbox/__init__.py +11 -0
- computer/providers/winsandbox/provider.py +468 -0
- computer/providers/winsandbox/setup_script.ps1 +124 -0
- computer/telemetry.py +5 -5
- computer/ui/__main__.py +15 -0
- computer/ui/gradio/app.py +70 -10
- {cua_computer-0.2.10.dist-info → cua_computer-0.2.12.dist-info}/METADATA +1 -1
- cua_computer-0.2.12.dist-info/RECORD +36 -0
- cua_computer-0.2.10.dist-info/RECORD +0 -31
- {cua_computer-0.2.10.dist-info → cua_computer-0.2.12.dist-info}/WHEEL +0 -0
- {cua_computer-0.2.10.dist-info → cua_computer-0.2.12.dist-info}/entry_points.txt +0 -0
computer/__init__.py
CHANGED
@@ -6,14 +6,14 @@ import sys
|
|
6
6
|
__version__ = "0.1.0"
|
7
7
|
|
8
8
|
# Initialize logging
|
9
|
-
logger = logging.getLogger("
|
9
|
+
logger = logging.getLogger("computer")
|
10
10
|
|
11
11
|
# Initialize telemetry when the package is imported
|
12
12
|
try:
|
13
13
|
# Import from core telemetry
|
14
14
|
from core.telemetry import (
|
15
|
-
is_telemetry_enabled,
|
16
15
|
flush,
|
16
|
+
is_telemetry_enabled,
|
17
17
|
record_event,
|
18
18
|
)
|
19
19
|
|
computer/computer.py
CHANGED
@@ -85,7 +85,7 @@ class Computer:
|
|
85
85
|
experiments: Optional list of experimental features to enable (e.g. ["app-use"])
|
86
86
|
"""
|
87
87
|
|
88
|
-
self.logger = Logger("
|
88
|
+
self.logger = Logger("computer", verbosity)
|
89
89
|
self.logger.info("Initializing Computer...")
|
90
90
|
|
91
91
|
# Store original parameters
|
@@ -106,7 +106,15 @@ class Computer:
|
|
106
106
|
# The default is currently to use non-ephemeral storage
|
107
107
|
if storage and ephemeral and storage != "ephemeral":
|
108
108
|
raise ValueError("Storage path and ephemeral flag cannot be used together")
|
109
|
-
|
109
|
+
|
110
|
+
# Windows Sandbox always uses ephemeral storage
|
111
|
+
if self.provider_type == VMProviderType.WINSANDBOX:
|
112
|
+
if not ephemeral and storage != None and storage != "ephemeral":
|
113
|
+
self.logger.warning("Windows Sandbox storage is always ephemeral. Setting ephemeral=True.")
|
114
|
+
self.ephemeral = True
|
115
|
+
self.storage = "ephemeral"
|
116
|
+
else:
|
117
|
+
self.storage = "ephemeral" if ephemeral else storage
|
110
118
|
|
111
119
|
# For Lumier provider, store the first shared directory path to use
|
112
120
|
# for VM file sharing
|
@@ -124,11 +132,11 @@ class Computer:
|
|
124
132
|
|
125
133
|
# Configure root logger
|
126
134
|
self.verbosity = verbosity
|
127
|
-
self.logger = Logger("
|
135
|
+
self.logger = Logger("computer", verbosity)
|
128
136
|
|
129
137
|
# Configure component loggers with proper hierarchy
|
130
|
-
self.vm_logger = Logger("
|
131
|
-
self.interface_logger = Logger("
|
138
|
+
self.vm_logger = Logger("computer.vm", verbosity)
|
139
|
+
self.interface_logger = Logger("computer.interface", verbosity)
|
132
140
|
|
133
141
|
if not use_host_computer_server:
|
134
142
|
if ":" not in image or len(image.split(":")) != 2:
|
@@ -285,6 +293,15 @@ class Computer:
|
|
285
293
|
api_key=self.api_key,
|
286
294
|
verbose=verbose,
|
287
295
|
)
|
296
|
+
elif self.provider_type == VMProviderType.WINSANDBOX:
|
297
|
+
self.config.vm_provider = VMProviderFactory.create_provider(
|
298
|
+
self.provider_type,
|
299
|
+
port=port,
|
300
|
+
host=host,
|
301
|
+
storage=storage,
|
302
|
+
verbose=verbose,
|
303
|
+
ephemeral=ephemeral,
|
304
|
+
)
|
288
305
|
else:
|
289
306
|
raise ValueError(f"Unsupported provider type: {self.provider_type}")
|
290
307
|
self._provider_context = await self.config.vm_provider.__aenter__()
|
@@ -383,7 +400,6 @@ class Computer:
|
|
383
400
|
# Wait for VM to be ready with a valid IP address
|
384
401
|
self.logger.info("Waiting for VM to be ready with a valid IP address...")
|
385
402
|
try:
|
386
|
-
# Increased values for Lumier provider which needs more time for initial setup
|
387
403
|
if self.provider_type == VMProviderType.LUMIER:
|
388
404
|
max_retries = 60 # Increased for Lumier VM startup which takes longer
|
389
405
|
retry_delay = 3 # 3 seconds between retries for Lumier
|
@@ -513,7 +529,7 @@ class Computer:
|
|
513
529
|
return
|
514
530
|
|
515
531
|
# @property
|
516
|
-
async def get_ip(self, max_retries: int = 15, retry_delay: int =
|
532
|
+
async def get_ip(self, max_retries: int = 15, retry_delay: int = 3) -> str:
|
517
533
|
"""Get the IP address of the VM or localhost if using host computer server.
|
518
534
|
|
519
535
|
This method delegates to the provider's get_ip method, which waits indefinitely
|
computer/helpers.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
"""
|
2
2
|
Helper functions and decorators for the Computer module.
|
3
3
|
"""
|
4
|
+
import logging
|
4
5
|
import asyncio
|
5
6
|
from functools import wraps
|
6
7
|
from typing import Any, Callable, Optional, TypeVar, cast
|
@@ -8,6 +9,8 @@ from typing import Any, Callable, Optional, TypeVar, cast
|
|
8
9
|
# Global reference to the default computer instance
|
9
10
|
_default_computer = None
|
10
11
|
|
12
|
+
logger = logging.getLogger(__name__)
|
13
|
+
|
11
14
|
def set_default_computer(computer):
|
12
15
|
"""
|
13
16
|
Set the default computer instance to be used by the remote decorator.
|
@@ -41,7 +44,7 @@ def sandboxed(venv_name: str = "default", computer: str = "default", max_retries
|
|
41
44
|
try:
|
42
45
|
return await comp.venv_exec(venv_name, func, *args, **kwargs)
|
43
46
|
except Exception as e:
|
44
|
-
|
47
|
+
logger.error(f"Attempt {i+1} failed: {e}")
|
45
48
|
await asyncio.sleep(1)
|
46
49
|
if i == max_retries - 1:
|
47
50
|
raise e
|
computer/interface/factory.py
CHANGED
@@ -8,7 +8,7 @@ class InterfaceFactory:
|
|
8
8
|
|
9
9
|
@staticmethod
|
10
10
|
def create_interface_for_os(
|
11
|
-
os: Literal['macos', 'linux'],
|
11
|
+
os: Literal['macos', 'linux', 'windows'],
|
12
12
|
ip_address: str,
|
13
13
|
api_key: Optional[str] = None,
|
14
14
|
vm_name: Optional[str] = None
|
@@ -16,7 +16,7 @@ class InterfaceFactory:
|
|
16
16
|
"""Create an interface for the specified OS.
|
17
17
|
|
18
18
|
Args:
|
19
|
-
os: Operating system type ('macos' or '
|
19
|
+
os: Operating system type ('macos', 'linux', or 'windows')
|
20
20
|
ip_address: IP address of the computer to control
|
21
21
|
api_key: Optional API key for cloud authentication
|
22
22
|
vm_name: Optional VM name for cloud authentication
|
@@ -30,10 +30,13 @@ class InterfaceFactory:
|
|
30
30
|
# Import implementations here to avoid circular imports
|
31
31
|
from .macos import MacOSComputerInterface
|
32
32
|
from .linux import LinuxComputerInterface
|
33
|
+
from .windows import WindowsComputerInterface
|
33
34
|
|
34
35
|
if os == 'macos':
|
35
36
|
return MacOSComputerInterface(ip_address, api_key=api_key, vm_name=vm_name)
|
36
37
|
elif os == 'linux':
|
37
38
|
return LinuxComputerInterface(ip_address, api_key=api_key, vm_name=vm_name)
|
39
|
+
elif os == 'windows':
|
40
|
+
return WindowsComputerInterface(ip_address, api_key=api_key, vm_name=vm_name)
|
38
41
|
else:
|
39
|
-
raise ValueError(f"Unsupported OS type: {os}")
|
42
|
+
raise ValueError(f"Unsupported OS type: {os}")
|
computer/interface/linux.py
CHANGED
@@ -30,7 +30,7 @@ class LinuxComputerInterface(BaseComputerInterface):
|
|
30
30
|
self._command_lock = asyncio.Lock() # Lock to ensure only one command at a time
|
31
31
|
|
32
32
|
# Set logger name for Linux interface
|
33
|
-
self.logger = Logger("
|
33
|
+
self.logger = Logger("computer.interface.linux", LogLevel.NORMAL)
|
34
34
|
|
35
35
|
@property
|
36
36
|
def ws_uri(self) -> str:
|
computer/interface/macos.py
CHANGED
@@ -29,7 +29,7 @@ class MacOSComputerInterface(BaseComputerInterface):
|
|
29
29
|
self._command_lock = asyncio.Lock() # Lock to ensure only one command at a time
|
30
30
|
|
31
31
|
# Set logger name for macOS interface
|
32
|
-
self.logger = Logger("
|
32
|
+
self.logger = Logger("computer.interface.macos", LogLevel.NORMAL)
|
33
33
|
|
34
34
|
@property
|
35
35
|
def ws_uri(self) -> str:
|