First working version

This commit is contained in:
Dillon DuPont
2025-08-13 14:37:06 -04:00
parent 95f510734d
commit b4f29abc58
4 changed files with 84 additions and 13 deletions

View File

@@ -0,0 +1,43 @@
import asyncio
from computer.providers.factory import VMProviderFactory
from computer import Computer, VMProviderType
import os
async def main():
# # Create docker provider
# provider = VMProviderFactory.create_provider(
# provider_type="docker",
# image="cua-ubuntu:latest", # Your CUA Ubuntu image
# port=8080,
# vnc_port=6901
# )
# # Run a container
# async with provider:
# vm_info = await provider.run_vm(
# image="cua-ubuntu:latest",
# name="my-cua-container",
# run_opts={
# "memory": "4GB",
# "cpu": 2,
# "vnc_port": 6901,
# "api_port": 8080
# }
# )
# print(vm_info)
computer = Computer(
os_type="linux",
provider_type=VMProviderType.DOCKER,
name="my-cua-container",
image="cua-ubuntu:latest",
)
await computer.run()
screenshot = await computer.interface.screenshot()
with open("screenshot_docker.png", "wb") as f:
f.write(screenshot)
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -22,11 +22,11 @@ docker build -t cua-ubuntu:latest .
### Running the Container Manually
```bash
docker run --rm -it --shm-size=512m -p 6901:6901 -p 8080:8080 -e VNC_PW=password cua-ubuntu:latest
docker run --rm -it --shm-size=512m -p 6901:6901 -p 8000:8000 -e VNC_PW=password cua-ubuntu:latest
```
- **VNC Access**: Available at `http://localhost:6901` (username: `kasm-user`, password: `password`)
- **Computer Server API**: Available at `http://localhost:8080`
- **Computer Server API**: Available at `http://localhost:8000`
### Using with CUA Docker Provider
@@ -39,8 +39,8 @@ from computer.providers.factory import VMProviderFactory
provider = VMProviderFactory.create_provider(
provider_type="docker",
image="cua-ubuntu:latest",
port=8080, # computer-server API port
vnc_port=6901 # VNC port
port=8000, # computer-server API port
noVNC_port=6901 # VNC port
)
# Run a container
@@ -52,7 +52,7 @@ async with provider:
"memory": "4GB",
"cpu": 2,
"vnc_port": 6901,
"api_port": 8080
"api_port": 8000
}
)
```

View File

@@ -301,6 +301,19 @@ class Computer:
storage=storage,
verbose=verbose,
ephemeral=ephemeral,
noVNC_port=noVNC_port,
)
elif self.provider_type == VMProviderType.DOCKER:
self.config.vm_provider = VMProviderFactory.create_provider(
self.provider_type,
port=port,
host=host,
storage=storage,
shared_path=shared_path,
image=image or "cua-ubuntu:latest",
verbose=verbose,
ephemeral=ephemeral,
noVNC_port=noVNC_port,
)
else:
raise ValueError(f"Unsupported provider type: {self.provider_type}")

View File

@@ -37,7 +37,7 @@ class DockerProvider(BaseVMProvider):
def __init__(
self,
port: Optional[int] = 8080,
port: Optional[int] = 8000,
host: str = "localhost",
storage: Optional[str] = None,
shared_path: Optional[str] = None,
@@ -49,7 +49,7 @@ class DockerProvider(BaseVMProvider):
"""Initialize the Docker VM Provider.
Args:
port: Port for the computer-server API (default: 8080)
port: Port for the computer-server API (default: 8000)
host: Hostname for the API server (default: localhost)
storage: Path for persistent VM storage
shared_path: Path for shared folder between host and container
@@ -159,11 +159,15 @@ class DockerProvider(BaseVMProvider):
# Get port mappings
ports = {}
if "Ports" in network_settings and network_settings["Ports"]:
for port_info in network_settings["Ports"]:
if port_info.get("PublicPort"):
container_port = f"{port_info['PrivatePort']}/{port_info['Type']}"
host_port = port_info["PublicPort"]
ports[container_port] = host_port
# network_settings["Ports"] is a dict like:
# {'6901/tcp': [{'HostIp': '0.0.0.0', 'HostPort': '6901'}, ...], ...}
for container_port, port_mappings in network_settings["Ports"].items():
if port_mappings: # Check if there are any port mappings
# Take the first mapping (usually the IPv4 one)
for mapping in port_mappings:
if mapping.get("HostPort"):
ports[container_port] = mapping["HostPort"]
break # Use the first valid mapping
return {
"name": name,
@@ -179,6 +183,8 @@ class DockerProvider(BaseVMProvider):
except Exception as e:
logger.error(f"Error getting VM info for {name}: {e}")
import traceback
traceback.print_exc()
return {
"name": name,
"status": "error",
@@ -208,6 +214,8 @@ class DockerProvider(BaseVMProvider):
return []
except Exception as e:
logger.error(f"Error listing VMs: {e}")
import traceback
traceback.print_exc()
return []
async def run_vm(self, image: str, name: str, run_opts: Dict[str, Any], storage: Optional[str] = None) -> Dict[str, Any]:
@@ -264,7 +272,7 @@ class DockerProvider(BaseVMProvider):
if vnc_port:
cmd.extend(["-p", f"{vnc_port}:6901"]) # VNC port
if api_port:
cmd.extend(["-p", f"{api_port}:8080"]) # computer-server API port
cmd.extend(["-p", f"{api_port}:8000"]) # computer-server API port
# Add volume mounts if storage is specified
storage_path = storage or self.storage
@@ -433,6 +441,13 @@ class DockerProvider(BaseVMProvider):
if vm_info["status"] == "error":
raise Exception(f"VM is in error state: {vm_info.get('error', 'Unknown error')}")
# TODO: for now, return localhost
# it seems the docker container is not accessible from the host
# on WSL2, unless you port forward? not sure
if True:
logger.warning("Overriding container IP with localhost")
return "localhost"
# Check if we got a valid IP
ip = vm_info.get("ip_address", None)
if ip and ip != "unknown" and not ip.startswith("0.0.0.0"):