diff --git a/examples/docker_examples.py b/examples/docker_examples.py new file mode 100644 index 00000000..42dcf3ac --- /dev/null +++ b/examples/docker_examples.py @@ -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()) diff --git a/libs/kasm/README.md b/libs/kasm/README.md index 65f9ca2c..b529e947 100644 --- a/libs/kasm/README.md +++ b/libs/kasm/README.md @@ -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 } ) ``` diff --git a/libs/python/computer/computer/computer.py b/libs/python/computer/computer/computer.py index 854a6cce..644aaa31 100644 --- a/libs/python/computer/computer/computer.py +++ b/libs/python/computer/computer/computer.py @@ -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}") diff --git a/libs/python/computer/computer/providers/docker/provider.py b/libs/python/computer/computer/providers/docker/provider.py index e02945ca..28ab9a5f 100644 --- a/libs/python/computer/computer/providers/docker/provider.py +++ b/libs/python/computer/computer/providers/docker/provider.py @@ -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"):