cua-computer 0.4.5__tar.gz → 0.4.7__tar.gz

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.
Files changed (37) hide show
  1. {cua_computer-0.4.5 → cua_computer-0.4.7}/PKG-INFO +1 -1
  2. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/computer.py +53 -27
  3. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/providers/winsandbox/provider.py +27 -6
  4. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/providers/winsandbox/setup_script.ps1 +33 -8
  5. {cua_computer-0.4.5 → cua_computer-0.4.7}/pyproject.toml +3 -3
  6. {cua_computer-0.4.5 → cua_computer-0.4.7}/README.md +0 -0
  7. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/__init__.py +0 -0
  8. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/diorama_computer.py +0 -0
  9. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/helpers.py +0 -0
  10. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/interface/__init__.py +0 -0
  11. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/interface/base.py +0 -0
  12. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/interface/factory.py +0 -0
  13. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/interface/generic.py +0 -0
  14. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/interface/linux.py +0 -0
  15. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/interface/macos.py +0 -0
  16. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/interface/models.py +0 -0
  17. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/interface/windows.py +0 -0
  18. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/logger.py +0 -0
  19. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/models.py +0 -0
  20. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/providers/__init__.py +0 -0
  21. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/providers/base.py +0 -0
  22. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/providers/cloud/__init__.py +0 -0
  23. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/providers/cloud/provider.py +0 -0
  24. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/providers/docker/__init__.py +0 -0
  25. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/providers/docker/provider.py +0 -0
  26. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/providers/factory.py +0 -0
  27. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/providers/lume/__init__.py +0 -0
  28. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/providers/lume/provider.py +0 -0
  29. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/providers/lume_api.py +0 -0
  30. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/providers/lumier/__init__.py +0 -0
  31. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/providers/lumier/provider.py +0 -0
  32. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/providers/winsandbox/__init__.py +0 -0
  33. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/ui/__init__.py +0 -0
  34. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/ui/__main__.py +0 -0
  35. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/ui/gradio/__init__.py +0 -0
  36. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/ui/gradio/app.py +0 -0
  37. {cua_computer-0.4.5 → cua_computer-0.4.7}/computer/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cua-computer
3
- Version: 0.4.5
3
+ Version: 0.4.7
4
4
  Summary: Computer-Use Interface (CUI) framework powering Cua
5
5
  Author-Email: TryCua <gh@trycua.com>
6
6
  Requires-Python: >=3.11
@@ -154,8 +154,8 @@ class Computer:
154
154
  self.interface_logger = Logger("computer.interface", verbosity)
155
155
 
156
156
  if not use_host_computer_server:
157
- if ":" not in image or len(image.split(":")) != 2:
158
- raise ValueError("Image must be in the format <image_name>:<tag>")
157
+ if ":" not in image:
158
+ image = f"{image}:latest"
159
159
 
160
160
  if not name:
161
161
  # Normalize the name to be used for the VM
@@ -794,19 +794,33 @@ class Computer:
794
794
  Tuple of (stdout, stderr) from the installation command
795
795
  """
796
796
  requirements = requirements or []
797
-
798
- # Create virtual environment if it doesn't exist
799
- venv_path = f"~/.venvs/{venv_name}"
800
- create_cmd = f"mkdir -p ~/.venvs && python3 -m venv {venv_path}"
801
-
802
- # Check if venv exists, if not create it
803
- check_cmd = f"test -d {venv_path} || ({create_cmd})"
804
- _ = await self.interface.run_command(check_cmd)
805
-
806
- # Install packages
807
- requirements_str = " ".join(requirements)
808
- install_cmd = f". {venv_path}/bin/activate && pip install {requirements_str}"
809
- return await self.interface.run_command(install_cmd)
797
+ # Windows vs POSIX handling
798
+ if self.os_type == "windows":
799
+ # Use %USERPROFILE% for home directory and cmd.exe semantics
800
+ venv_path = f"%USERPROFILE%\\.venvs\\{venv_name}"
801
+ ensure_dir_cmd = "if not exist \"%USERPROFILE%\\.venvs\" mkdir \"%USERPROFILE%\\.venvs\""
802
+ create_cmd = f"if not exist \"{venv_path}\" python -m venv \"{venv_path}\""
803
+ requirements_str = " ".join(requirements)
804
+ # Activate via activate.bat and install
805
+ install_cmd = f"call \"{venv_path}\\Scripts\\activate.bat\" && pip install {requirements_str}" if requirements_str else f"echo No requirements to install"
806
+ await self.interface.run_command(ensure_dir_cmd)
807
+ await self.interface.run_command(create_cmd)
808
+ return await self.interface.run_command(install_cmd)
809
+ else:
810
+ # POSIX (macOS/Linux)
811
+ venv_path = f"$HOME/.venvs/{venv_name}"
812
+ create_cmd = f"mkdir -p \"$HOME/.venvs\" && python3 -m venv \"{venv_path}\""
813
+ # Check if venv exists, if not create it
814
+ check_cmd = f"test -d \"{venv_path}\" || ({create_cmd})"
815
+ _ = await self.interface.run_command(check_cmd)
816
+ # Install packages
817
+ requirements_str = " ".join(requirements)
818
+ install_cmd = (
819
+ f". \"{venv_path}/bin/activate\" && pip install {requirements_str}"
820
+ if requirements_str
821
+ else "echo No requirements to install"
822
+ )
823
+ return await self.interface.run_command(install_cmd)
810
824
 
811
825
  async def venv_cmd(self, venv_name: str, command: str):
812
826
  """Execute a shell command in a virtual environment.
@@ -818,18 +832,30 @@ class Computer:
818
832
  Returns:
819
833
  Tuple of (stdout, stderr) from the command execution
820
834
  """
821
- venv_path = f"~/.venvs/{venv_name}"
822
-
823
- # Check if virtual environment exists
824
- check_cmd = f"test -d {venv_path}"
825
- result = await self.interface.run_command(check_cmd)
826
-
827
- if result.stderr or "test:" in result.stdout: # venv doesn't exist
828
- return "", f"Virtual environment '{venv_name}' does not exist. Create it first using venv_install."
829
-
830
- # Activate virtual environment and run command
831
- full_command = f". {venv_path}/bin/activate && {command}"
832
- return await self.interface.run_command(full_command)
835
+ if self.os_type == "windows":
836
+ # Windows (cmd.exe)
837
+ venv_path = f"%USERPROFILE%\\.venvs\\{venv_name}"
838
+ # Check existence and signal if missing
839
+ check_cmd = f"if not exist \"{venv_path}\" (echo VENV_NOT_FOUND) else (echo VENV_FOUND)"
840
+ result = await self.interface.run_command(check_cmd)
841
+ if "VENV_NOT_FOUND" in getattr(result, "stdout", ""):
842
+ # Auto-create the venv with no requirements
843
+ await self.venv_install(venv_name, [])
844
+ # Activate and run the command
845
+ full_command = f"call \"{venv_path}\\Scripts\\activate.bat\" && {command}"
846
+ return await self.interface.run_command(full_command)
847
+ else:
848
+ # POSIX (macOS/Linux)
849
+ venv_path = f"$HOME/.venvs/{venv_name}"
850
+ # Check if virtual environment exists
851
+ check_cmd = f"test -d \"{venv_path}\""
852
+ result = await self.interface.run_command(check_cmd)
853
+ if result.stderr or "test:" in result.stdout: # venv doesn't exist
854
+ # Auto-create the venv with no requirements
855
+ await self.venv_install(venv_name, [])
856
+ # Activate virtual environment and run command
857
+ full_command = f". \"{venv_path}/bin/activate\" && {command}"
858
+ return await self.interface.run_command(full_command)
833
859
 
834
860
  async def venv_exec(self, venv_name: str, python_func, *args, **kwargs):
835
861
  """Execute Python function in a virtual environment using source code extraction.
@@ -5,6 +5,7 @@ import asyncio
5
5
  import logging
6
6
  import time
7
7
  from typing import Dict, Any, Optional, List
8
+ from pathlib import Path
8
9
 
9
10
  from ..base import BaseVMProvider, VMProviderType
10
11
 
@@ -242,8 +243,15 @@ class WinSandboxProvider(BaseVMProvider):
242
243
 
243
244
  networking = run_opts.get("networking", self.networking)
244
245
 
245
- # Create folder mappers if shared directories are specified
246
+ # Create folder mappers; always map a persistent venv directory on host for caching packages
246
247
  folder_mappers = []
248
+ # Ensure host side persistent venv directory exists (Path.home()/wsb_venv)
249
+ host_wsb_env = Path.home() / ".cua" / "wsb_cache"
250
+ try:
251
+ host_wsb_env.mkdir(parents=True, exist_ok=True)
252
+ except Exception:
253
+ # If cannot create, continue without persistent mapping
254
+ host_wsb_env = None
247
255
  shared_directories = run_opts.get("shared_directories", [])
248
256
  for shared_dir in shared_directories:
249
257
  if isinstance(shared_dir, dict):
@@ -255,6 +263,15 @@ class WinSandboxProvider(BaseVMProvider):
255
263
 
256
264
  if host_path and os.path.exists(host_path):
257
265
  folder_mappers.append(winsandbox.FolderMapper(host_path))
266
+
267
+ # Add mapping for the persistent venv directory (read/write) so it appears in Sandbox Desktop
268
+ if host_wsb_env is not None and host_wsb_env.exists():
269
+ try:
270
+ folder_mappers.append(
271
+ winsandbox.FolderMapper(str(host_wsb_env), read_only=False)
272
+ )
273
+ except Exception as e:
274
+ self.logger.warning(f"Failed to map host winsandbox_venv: {e}")
258
275
 
259
276
  self.logger.info(f"Creating Windows Sandbox: {name}")
260
277
  self.logger.info(f"Memory: {memory_mb}MB, Networking: {networking}")
@@ -290,8 +307,10 @@ class WinSandboxProvider(BaseVMProvider):
290
307
 
291
308
  self.logger.info(f"Windows Sandbox {name} created successfully")
292
309
 
310
+ venv_exists = (host_wsb_env / "venv" / "Lib" / "site-packages" / "computer_server").exists() if host_wsb_env else False
311
+
293
312
  # Setup the computer server in the sandbox
294
- await self._setup_computer_server(sandbox, name)
313
+ await self._setup_computer_server(sandbox, name, wait_for_venv=(not venv_exists))
295
314
 
296
315
  return {
297
316
  "success": True,
@@ -423,7 +442,7 @@ class WinSandboxProvider(BaseVMProvider):
423
442
  if total_attempts % 10 == 0:
424
443
  self.logger.info(f"Still waiting for Windows Sandbox {name} IP after {total_attempts} attempts...")
425
444
 
426
- async def _setup_computer_server(self, sandbox, name: str, visible: bool = False):
445
+ async def _setup_computer_server(self, sandbox, name: str, visible: bool = False, wait_for_venv: bool = True):
427
446
  """Setup the computer server in the Windows Sandbox using RPyC.
428
447
 
429
448
  Args:
@@ -471,10 +490,12 @@ class WinSandboxProvider(BaseVMProvider):
471
490
  creationflags=creation_flags,
472
491
  shell=False
473
492
  )
474
-
475
- # # Sleep for 30 seconds
476
- # await asyncio.sleep(30)
477
493
 
494
+ if wait_for_venv:
495
+ print("Waiting for venv to be created for the first time setup of Windows Sandbox...")
496
+ print("This may take a minute...")
497
+ await asyncio.sleep(120)
498
+
478
499
  ip = await self.get_ip(name)
479
500
  self.logger.info(f"Sandbox IP: {ip}")
480
501
  self.logger.info(f"Setup script started in background in sandbox {name} with PID: {process.pid}")
@@ -79,23 +79,48 @@ try {
79
79
  $pythonVersion = & $pythonExe --version 2>&1
80
80
  Write-Host "Python version: $pythonVersion"
81
81
 
82
- # Step 2: Install cua-computer-server directly
83
- Write-Host "Step 2: Installing cua-computer-server..."
82
+ # Step 2: Create a dedicated virtual environment in mapped Desktop folder (persistent)
83
+ Write-Host "Step 2: Creating virtual environment (if needed)..."
84
+ $cachePath = "C:\Users\WDAGUtilityAccount\Desktop\wsb_cache"
85
+ $venvPath = "C:\Users\WDAGUtilityAccount\Desktop\wsb_cache\venv"
86
+ if (!(Test-Path $venvPath)) {
87
+ Write-Host "Creating venv at: $venvPath"
88
+ & $pythonExe -m venv $venvPath
89
+ } else {
90
+ Write-Host "Venv already exists at: $venvPath"
91
+ }
92
+ # Hide the folder to keep Desktop clean
93
+ try {
94
+ $item = Get-Item $cachePath -ErrorAction SilentlyContinue
95
+ if ($item) {
96
+ if (-not ($item.Attributes -band [IO.FileAttributes]::Hidden)) {
97
+ $item.Attributes = $item.Attributes -bor [IO.FileAttributes]::Hidden
98
+ }
99
+ }
100
+ } catch { }
101
+ $venvPython = Join-Path $venvPath "Scripts\python.exe"
102
+ if (!(Test-Path $venvPython)) {
103
+ throw "Virtual environment Python not found at $venvPython"
104
+ }
105
+ Write-Host "Using venv Python: $venvPython"
106
+
107
+ # Step 3: Install cua-computer-server into the venv
108
+ Write-Host "Step 3: Installing cua-computer-server..."
84
109
 
85
110
  Write-Host "Upgrading pip..."
86
- & $pythonExe -m pip install --upgrade pip --quiet
111
+ & $venvPython -m pip install --upgrade pip --quiet
87
112
 
88
113
  Write-Host "Installing cua-computer-server..."
89
- & $pythonExe -m pip install cua-computer-server --quiet
114
+ & $venvPython -m pip install cua-computer-server
90
115
 
91
116
  Write-Host "cua-computer-server installation completed."
92
117
 
93
- # Step 3: Start computer server in background
94
- Write-Host "Step 3: Starting computer server in background..."
95
- Write-Host "Starting computer server with: $pythonExe"
118
+ # Step 4: Start computer server in background using the venv Python
119
+ Write-Host "Step 4: Starting computer server in background..."
120
+ Write-Host "Starting computer server with: $venvPython"
96
121
 
97
122
  # Start the computer server in the background
98
- $serverProcess = Start-Process -FilePath $pythonExe -ArgumentList "-m", "computer_server.main" -WindowStyle Hidden -PassThru
123
+ $serverProcess = Start-Process -FilePath $venvPython -ArgumentList "-m", "computer_server.main" -WindowStyle Hidden -PassThru
99
124
  Write-Host "Computer server started in background with PID: $($serverProcess.Id)"
100
125
 
101
126
  # Give it a moment to start
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
6
6
 
7
7
  [project]
8
8
  name = "cua-computer"
9
- version = "0.4.5"
9
+ version = "0.4.7"
10
10
  description = "Computer-Use Interface (CUI) framework powering Cua"
11
11
  readme = "README.md"
12
12
  authors = [
@@ -57,7 +57,7 @@ target-version = [
57
57
 
58
58
  [tool.ruff]
59
59
  line-length = 100
60
- target-version = "0.4.5"
60
+ target-version = "0.4.7"
61
61
  select = [
62
62
  "E",
63
63
  "F",
@@ -71,7 +71,7 @@ docstring-code-format = true
71
71
 
72
72
  [tool.mypy]
73
73
  strict = true
74
- python_version = "0.4.5"
74
+ python_version = "0.4.7"
75
75
  ignore_missing_imports = true
76
76
  disallow_untyped_defs = true
77
77
  check_untyped_defs = true
File without changes