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 CHANGED
@@ -6,14 +6,14 @@ import sys
6
6
  __version__ = "0.1.0"
7
7
 
8
8
  # Initialize logging
9
- logger = logging.getLogger("cua.computer")
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("cua.computer", verbosity)
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
- self.storage = "ephemeral" if ephemeral else storage
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("cua", verbosity)
135
+ self.logger = Logger("computer", verbosity)
128
136
 
129
137
  # Configure component loggers with proper hierarchy
130
- self.vm_logger = Logger("cua.vm", verbosity)
131
- self.interface_logger = Logger("cua.interface", verbosity)
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 = 2) -> str:
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
- print(f"Attempt {i+1} failed: {e}")
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
@@ -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 'linux')
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}")
@@ -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("cua.interface.linux", LogLevel.NORMAL)
33
+ self.logger = Logger("computer.interface.linux", LogLevel.NORMAL)
34
34
 
35
35
  @property
36
36
  def ws_uri(self) -> str:
@@ -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("cua.interface.macos", LogLevel.NORMAL)
32
+ self.logger = Logger("computer.interface.macos", LogLevel.NORMAL)
33
33
 
34
34
  @property
35
35
  def ws_uri(self) -> str: