Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/docker-publish-cua-windows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Build and Publish CUA Windows Container

on:
push:
branches:
- main
tags:
- "docker-cua-windows-v*.*.*"
paths:
- "libs/qemu-docker/windows/**"
- ".github/workflows/docker-publish-cua-windows.yml"
- ".github/workflows/docker-reusable-publish.yml"
pull_request:
paths:
- "libs/qemu-docker/windows/**"
- ".github/workflows/docker-publish-cua-windows.yml"
- ".github/workflows/docker-reusable-publish.yml"

jobs:
publish:
uses: ./.github/workflows/docker-reusable-publish.yml
with:
image_name: cua-windows
context_dir: libs/qemu-docker/windows
dockerfile_path: Dockerfile
tag_prefix: docker-cua-windows-v
docker_hub_org: trycua
secrets:
DOCKER_HUB_TOKEN: ${{ secrets.DOCKER_HUB_TOKEN }}
14 changes: 14 additions & 0 deletions libs/qemu-docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# QEMU Docker Containers

Docker containers running desktop operating systems via QEMU/KVM for Computer-Using Agents (CUA).

## Structure

```
qemu-docker/
└── windows/ # Windows 11 container with CUA computer-server
```

## Windows Container

See [windows/README.md](windows/README.md) for complete documentation on the Windows 11 QEMU container.
15 changes: 15 additions & 0 deletions libs/qemu-docker/windows/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM trycua/windows-local:latest

COPY src/vm/setup/. /oem/

COPY --chmod=755 src/entry.sh /entry.sh

ENV RAM_SIZE="8G"
ENV CPU_CORES="8"
ENV VERSION="win11x64-enterprise-eval"
ENV DISK_SIZE="30G"
ENV ARGUMENTS="-qmp tcp:0.0.0.0:7200,server,nowait"

EXPOSE 5000 8006

ENTRYPOINT ["/entry.sh"]
159 changes: 159 additions & 0 deletions libs/qemu-docker/windows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# CUA Windows Container

Containerized Windows 11 virtual desktop for Computer-Using Agents (CUA). Utilizes QEMU/KVM with Windows 11 and computer-server pre-installed for remote computer control.

## Features

- Windows 11 Enterprise running in QEMU/KVM
- Pre-installed CUA computer-server for remote computer control
- Caddy reverse proxy (port 9222 → 1337) for browser automation
- noVNC access for visual desktop interaction
- Automated setup via unattended installation
- Support for both dev (shared folder) and azure (OEM folder) deployment modes
- Python 3.12 with isolated virtual environment for CUA computer-server
- Services run hidden in background via Windows scheduled tasks
- Essential tools pre-installed (Chrome, LibreOffice, VLC, GIMP, VSCode, Thunderbird)

## Quick Start

### 1. Download and Prepare setup.iso

**Download Windows 11 Evaluation ISO:**

1. Visit [Microsoft Evaluation Center](https://info.microsoft.com/ww-landing-windows-11-enterprise.html)
2. Accept the Terms of Service
3. Download **Windows 11 Enterprise Evaluation (90-day trial, English, United States)** ISO file [~6GB]
4. After downloading, rename the file to `setup.iso`
5. Copy it to the directory `src/vm/image/`

This ISO is used for automated Windows installation on first run.

### 2. Build the Image

```bash
docker build -t cua-windows:dev .
```

### 3. First Run - Create Golden Image

On first run, the container will install Windows from scratch and create a golden image. This takes 15-30 minutes.

```bash
# Create storage directory
mkdir -p ./storage

# Run with setup.iso to create golden image
docker run -it --rm \
--device=/dev/kvm \
--platform linux/amd64 \
--name cua-windows \
--mount type=bind,source=$(pwd)/src/vm/image/setup.iso,target=/custom.iso \
--cap-add NET_ADMIN \
-v $(pwd)/storage:/storage \
-p 8006:8006 \
-p 5000:5000 \
-e RAM_SIZE=8G \
-e CPU_CORES=4 \
-e DISK_SIZE=20G \
cua-windows:dev
```

**What happens during first run:**

1. Windows 11 installs automatically using unattended configuration
2. Setup scripts install Python 3.12, Git, and CUA computer-server in isolated venv
3. Windows scheduled tasks created for CUA server and Caddy proxy (run hidden in background)
4. Golden image is saved to `/storage` directory
5. Container exits after setup completes

### 4. Subsequent Runs - Use Golden Image

After the golden image is created, subsequent runs boot much faster (30 sec - 2 min):

```bash
# Run without setup.iso - uses existing golden image
docker run -it --rm \
--device=/dev/kvm \
--platform linux/amd64 \
--name cua-windows \
--cap-add NET_ADMIN \
-v $(pwd)/storage:/storage \
-p 8006:8006 \
-p 5000:5000 \
-e RAM_SIZE=8G \
-e CPU_CORES=4 \
cua-windows:dev
```

**Access points:**

- **Computer Server API**: `http://localhost:5000`
- **noVNC Browser**: `http://localhost:8006`

## Container Configuration

### Ports

- **5000**: CUA computer-server API endpoint
- **8006**: noVNC web interface for visual desktop access

### Environment Variables

- `RAM_SIZE`: RAM allocated to Windows VM (default: "8G", recommended: "8G" for WSL2)
- `CPU_CORES`: CPU cores allocated to VM (default: "8")
- `DISK_SIZE`: VM disk size (default: "30G", minimum: "20G")
- `VERSION`: Windows version (default: "win11x64-enterprise-eval")

### Volumes

- `/storage`: Persistent VM storage (golden image, disk, firmware)
- `/custom.iso`: Mount point for setup.iso (only needed for first run)

## Architecture

```
┌─────────────────────────────────────────────────────────┐
│ Docker Container (Linux host) │
│ │
│ • Port forwarding: localhost:5000 → EMULATOR_IP:5000 │
│ • Exposes: 5000 (API), 8006 (noVNC) │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ QEMU VM (Windows 11) │ │
│ │ │ │
│ │ • CUA computer-server listens on 5000 │ │
│ │ │ │
│ └────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
```

**Communication Flow:**

1. External client → `localhost:5000` (host)
2. Docker port mapping → Container's `localhost:5000`
3. socat port forwarding → `20.20.20.21:5000` (VM)
4. CUA computer-server in Windows VM processes request

## Development

### Modifying Setup Scripts

Setup scripts are in `src/vm/setup/`:

- `install.bat`: Entry point called by Windows setup
- `setup.ps1`: Main setup orchestration (installs software, configures Windows)
- `setup-cua-server.ps1`: CUA server installation with isolated venv
- `on-logon.ps1`: Runs on user logon (starts scheduled tasks)
- `setup-utils.psm1`: Helpers functions for setup

After modifying, rebuild the image:

```bash
docker build -t cua-windows:dev .
```

## Credits

- Built on [Dockur Windows](https://github.com/dockur/windows) base image
- Inspired by [Windows Agent Arena](https://github.com/microsoft/WindowsAgentArena)
67 changes: 67 additions & 0 deletions libs/qemu-docker/windows/src/entry.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/bin/bash

cleanup() {
echo "Received signal, shutting down gracefully..."
if [ -n "$VM_PID" ]; then
kill -TERM "$VM_PID" 2>/dev/null
wait "$VM_PID" 2>/dev/null
fi
exit 0
}

# Install trap for signals
trap cleanup SIGTERM SIGINT SIGHUP SIGQUIT

# Create windows.boot file if it doesn't exist (required for proper boot)
if [ -d "/storage" -a ! -f "/storage/windows.boot" ]; then
echo "Creating windows.boot file in /storage..."
touch /storage/windows.boot
fi

# Start the VM in the background
echo "Starting Windows VM..."
/usr/bin/tini -s /run/entry.sh &
VM_PID=$!
echo "Live stream accessible at localhost:8006"

echo "Waiting for Windows to boot and CUA computer-server to start..."

VM_IP=""
while true; do
# Wait from VM and get the IP
if [ -z "$VM_IP" ]; then
VM_IP=$(ps aux | grep dnsmasq | grep -oP '(?<=--dhcp-range=)[0-9.]+' | head -1)
if [ -n "$VM_IP" ]; then
echo "Detected VM IP: $VM_IP"
else
echo "Waiting for VM to start..."
sleep 5
continue
fi
fi

# Check if server is ready
response=$(curl --write-out '%{http_code}' --silent --output /dev/null $VM_IP:5000/status)

if [ "${response:-0}" -eq 200 ]; then
break
fi

echo "Waiting for CUA computer-server to be ready. This might take a while..."
sleep 5
done

echo "VM is up and running, and the CUA Computer Server is ready!"

echo "Computer server accessible at localhost:5000"

# Detect initial setup by presence of custom ISO
CUSTOM_ISO=$(find / -maxdepth 1 -type f -iname "*.iso" -print -quit 2>/dev/null || true)
if [ -n "$CUSTOM_ISO" ]; then
echo "Preparation complete. Shutting down gracefully..."
cleanup
fi

# Keep container alive for golden image boots
echo "Container running. Press Ctrl+C to stop."
tail -f /dev/null
9 changes: 9 additions & 0 deletions libs/qemu-docker/windows/src/vm/image/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
> Add your Win11E setup.iso to this folder

**Download Windows 11 Evaluation ISO:**

1. Visit [Microsoft Evaluation Center](https://info.microsoft.com/ww-landing-windows-11-enterprise.html)
2. Accept the Terms of Service
3. Download **Windows 11 Enterprise Evaluation (90-day trial, English, United States)** ISO file [~6GB]
4. After downloading, rename the file to `setup.iso`
5. Copy it to the current directory.
31 changes: 31 additions & 0 deletions libs/qemu-docker/windows/src/vm/setup/install.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@echo off

SET ScriptFolder=C:\OEM
SET LogFile=%ScriptFolder%\ps_script_log.txt

echo Running PowerShell script... > %LogFile%

:: Check for PowerShell availability
where powershell >> %LogFile% 2>&1
if %ERRORLEVEL% neq 0 (
echo PowerShell is not available! >> %LogFile%
echo PowerShell is not available!
exit /b 1
)

:: Add a 30-second delay
echo Waiting for 30 seconds before continuing... >> %LogFile%
timeout /t 30 /nobreak >> %LogFile% 2>&1

:: Run PowerShell script with ExecutionPolicy Bypass and log errors
echo Running setup.ps1... >> %LogFile%

powershell -ExecutionPolicy Bypass -File "%ScriptFolder%\setup.ps1" >> %LogFile% 2>&1

if %ERRORLEVEL% neq 0 (
echo An error occurred. See %LogFile% for details.
) else (
echo PowerShell script has completed successfully.
)

echo PowerShell script has completed.
2 changes: 2 additions & 0 deletions libs/qemu-docker/windows/src/vm/setup/on-logon.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Write-Host "Starting CUA Computer Server task..."
Start-ScheduledTask -TaskName "CUA-Computer-Server"
Loading