From e0c4c5c832074d3bf094e61a5287b15e06314767 Mon Sep 17 00:00:00 2001 From: "synacktra.work@gmail.com" Date: Fri, 28 Nov 2025 16:20:52 +0530 Subject: [PATCH 1/2] push qemu linux setup --- .gitignore | 1 + libs/qemu-docker/linux/Dockerfile | 14 ++ libs/qemu-docker/linux/README.md | 146 ++++++++++++++++++ libs/qemu-docker/linux/src/entry.sh | 61 ++++++++ libs/qemu-docker/linux/src/vm/image/README.md | 7 + .../qemu-docker/linux/src/vm/setup/install.sh | 26 ++++ .../linux/src/vm/setup/setup-cua-server.sh | 135 ++++++++++++++++ libs/qemu-docker/linux/src/vm/setup/setup.sh | 33 ++++ 8 files changed, 423 insertions(+) create mode 100644 libs/qemu-docker/linux/Dockerfile create mode 100644 libs/qemu-docker/linux/README.md create mode 100644 libs/qemu-docker/linux/src/entry.sh create mode 100644 libs/qemu-docker/linux/src/vm/image/README.md create mode 100644 libs/qemu-docker/linux/src/vm/setup/install.sh create mode 100644 libs/qemu-docker/linux/src/vm/setup/setup-cua-server.sh create mode 100644 libs/qemu-docker/linux/src/vm/setup/setup.sh diff --git a/.gitignore b/.gitignore index c361d60c..bfff79d1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +**/image/setup.iso # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/libs/qemu-docker/linux/Dockerfile b/libs/qemu-docker/linux/Dockerfile new file mode 100644 index 00000000..28cdc6d4 --- /dev/null +++ b/libs/qemu-docker/linux/Dockerfile @@ -0,0 +1,14 @@ +FROM qemu-local:latest + +COPY src/vm/setup/. /oem/ + +COPY --chmod=755 src/entry.sh /entry.sh + +ENV RAM_SIZE="8G" +ENV CPU_CORES="8" +ENV DISK_SIZE="64G" +ENV ARGUMENTS="-qmp tcp:0.0.0.0:7200,server,nowait" + +EXPOSE 5000 8006 + +ENTRYPOINT ["/entry.sh"] \ No newline at end of file diff --git a/libs/qemu-docker/linux/README.md b/libs/qemu-docker/linux/README.md new file mode 100644 index 00000000..59cb7edc --- /dev/null +++ b/libs/qemu-docker/linux/README.md @@ -0,0 +1,146 @@ +# CUA Linux Container + +Containerized Ubuntu 22.04 LTS virtual desktop for Computer-Using Agents (CUA). Utilizes QEMU/KVM with Ubuntu Desktop and computer-server pre-installed for remote computer control. + +## Features + +- Ubuntu 22.04 LTS Desktop running in QEMU/KVM +- Automated installation via cloud-init autoinstall +- Pre-installed CUA computer-server for remote computer control +- Support for custom OEM scripts during setup +- noVNC access for visual desktop interaction + +## Quick Start + +### 1. Download Ubuntu Server ISO + +**Download Ubuntu 22.04 LTS Server ISO:** + +1. Visit & download the [server ISO](https://releases.ubuntu.com/22.04/ubuntu-22.04.5-live-server-amd64.iso) +2. After downloading, rename the file to `setup.iso` +3. Copy it to the directory `src/vm/image/` + +This ISO is used for automated Ubuntu installation with cloud-init on first run. + +### 2. Build the Image + +```bash +docker build -t cua-linux:dev . +``` + +### 3. First Run - Create Golden Image + +On first run, the container will install Ubuntu from scratch and create a golden image. This takes 15-30 minutes. + +```bash +# Create storage directory +mkdir -p ./storage + +# Run with ubuntu.iso to create golden image +docker run -it --rm \ + --device=/dev/kvm \ + --name cua-linux \ + --mount type=bind,source=/path/to/ubuntu.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=64G \ + cua-linux:dev +``` + +**What happens during first run:** + +1. Ubuntu 22.04 Server installs automatically using cloud-init autoinstall +2. Minimal desktop environment is installed with auto-login enabled +3. OEM setup scripts install Python 3, create venv, and install CUA computer-server +4. systemd service created for CUA server (runs automatically on login) +5. X11 access configured for GUI automation +6. Golden image is saved to `/storage` directory +7. 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 ubuntu.iso - uses existing golden image +docker run -it --rm \ + --device=/dev/kvm \ + --name cua-linux \ + --cap-add NET_ADMIN \ + -v $(pwd)/storage:/storage \ + -p 8006:8006 \ + -p 5000:5000 \ + -e RAM_SIZE=8G \ + -e CPU_CORES=4 \ + cua-linux: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 Ubuntu VM (default: "8G", recommended: "8G" for WSL2) +- `CPU_CORES`: CPU cores allocated to VM (default: "8") +- `DISK_SIZE`: VM disk size (default: "64G", minimum: "32G") + +### Volumes + +- `/storage`: Persistent VM storage (golden image, disk) +- `/custom.iso`: Mount point for ubuntu.iso (only needed for first run) +- `/oem`: Optional mount point for custom OEM scripts (built-in scripts included in image) + +## Architecture + +``` +┌─────────────────────────────────────────────────────────┐ +│ Docker Container (Linux host) │ +│ │ +│ • Port forwarding: localhost:5000 → EMULATOR_IP:5000 │ +│ • Exposes: 5000 (API), 8006 (noVNC) │ +│ │ +│ ┌────────────────────────────────────────────────────┐ │ +│ │ QEMU VM (Ubuntu 22.04) │ │ +│ │ │ │ +│ │ • CUA computer-server listens on 5000 │ │ +│ │ │ │ +│ └────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────┘ +``` + +**Communication Flow:** + +1. External client → `localhost:5000` (host) +2. Docker port mapping → Container's `localhost:5000` +3. Container detects VM IP and waits for server to be ready +4. CUA computer-server in Ubuntu VM processes request + +## Development + +### Modifying Setup Scripts + +Setup scripts are in `src/vm/setup/`: + +- `install.sh`: Entry point called after cloud-init installation (runs OEM setup) +- `setup.sh`: Main setup orchestration (copies scripts to /opt/oem) +- `setup-cua-server.sh`: CUA server installation with isolated venv and systemd service + +After modifying, rebuild the image: + +```bash +docker build -t cua-linux:dev . +``` diff --git a/libs/qemu-docker/linux/src/entry.sh b/libs/qemu-docker/linux/src/entry.sh new file mode 100644 index 00000000..901b45ab --- /dev/null +++ b/libs/qemu-docker/linux/src/entry.sh @@ -0,0 +1,61 @@ +#!/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 + +# Start the VM in the background +echo "Starting Ubuntu VM..." +/usr/bin/tini -s /run/entry.sh & +VM_PID=$! +echo "Live stream accessible at localhost:8006" + +echo "Waiting for Ubuntu to boot and CUA computer-server to start..." + +VM_IP="" +while true; do + # Wait for 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 diff --git a/libs/qemu-docker/linux/src/vm/image/README.md b/libs/qemu-docker/linux/src/vm/image/README.md new file mode 100644 index 00000000..2dd6bdf7 --- /dev/null +++ b/libs/qemu-docker/linux/src/vm/image/README.md @@ -0,0 +1,7 @@ +> Add your Ubuntu 22.04 live server setup.iso to this folder + +**Download Ubuntu 22.04 LTS Server ISO:** + +1. Visit & download the [server ISO](https://releases.ubuntu.com/22.04/ubuntu-22.04.5-live-server-amd64.iso) +2. After downloading, rename the file to `setup.iso` +3. Copy it to the current directory. diff --git a/libs/qemu-docker/linux/src/vm/setup/install.sh b/libs/qemu-docker/linux/src/vm/setup/install.sh new file mode 100644 index 00000000..6d3065ae --- /dev/null +++ b/libs/qemu-docker/linux/src/vm/setup/install.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# OEM Installation Entry Point for Linux +# This script is called by the OEM systemd service on first boot + +set -e + +SCRIPT_DIR="/opt/oem" +LOG_FILE="$SCRIPT_DIR/setup.log" + +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" +} + +log "=== Starting OEM Setup ===" + +# Run main setup script +if [ -f "$SCRIPT_DIR/setup.sh" ]; then + log "Running setup.sh..." + bash "$SCRIPT_DIR/setup.sh" 2>&1 | tee -a "$LOG_FILE" + log "setup.sh completed with exit code: $?" +else + log "ERROR: setup.sh not found at $SCRIPT_DIR/setup.sh" + exit 1 +fi + +log "=== OEM Setup Completed ===" diff --git a/libs/qemu-docker/linux/src/vm/setup/setup-cua-server.sh b/libs/qemu-docker/linux/src/vm/setup/setup-cua-server.sh new file mode 100644 index 00000000..54754dca --- /dev/null +++ b/libs/qemu-docker/linux/src/vm/setup/setup-cua-server.sh @@ -0,0 +1,135 @@ +#!/bin/bash +# Setup CUA Computer Server on Linux +# Creates a system-level systemd service to run computer server in background + +set -e + +USER_NAME="docker" +USER_HOME="/home/$USER_NAME" +SCRIPT_DIR="/opt/oem" +CUA_DIR="/opt/cua-server" +VENV_DIR="$CUA_DIR/venv" +SERVICE_NAME="cua-computer-server" +LOG_FILE="$SCRIPT_DIR/setup.log" + +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" +} + +log "=== Installing CUA Computer Server ===" + +# Install Python 3 and venv +log "Installing Python 3 and dependencies..." +sudo apt-get install -y python3 python3-venv python3-pip python3-tk python3-dev + +# Create CUA directory +log "Creating CUA directory at $CUA_DIR..." +sudo mkdir -p "$CUA_DIR" +sudo chown "$USER_NAME:$USER_NAME" "$CUA_DIR" + +# Create virtual environment +if [ -f "$VENV_DIR/bin/python" ]; then + log "Existing venv detected; skipping creation" +else + log "Creating Python virtual environment at $VENV_DIR..." + python3 -m venv "$VENV_DIR" + log "Virtual environment created successfully" +fi + +# Activate and install packages +log "Upgrading pip, setuptools, and wheel..." +"$VENV_DIR/bin/pip" install --upgrade pip setuptools wheel + +log "Installing cua-computer-server..." +"$VENV_DIR/bin/pip" install --upgrade cua-computer-server +log "cua-computer-server installed successfully" + +# Open firewall for port 5000 (if ufw is available) +if command -v ufw &> /dev/null; then + log "Opening firewall for port 5000..." + sudo ufw allow 5000/tcp || true + log "Firewall rule added" +fi + +# Create start script with auto-restart +START_SCRIPT="$CUA_DIR/start-server.sh" +log "Creating start script at $START_SCRIPT..." + +cat > "$START_SCRIPT" << 'EOF' +#!/bin/bash +# CUA Computer Server Start Script with auto-restart + +CUA_DIR="/opt/cua-server" +VENV_DIR="$CUA_DIR/venv" +LOG_FILE="$CUA_DIR/server.log" + +start_server() { + echo "$(date '+%Y-%m-%d %H:%M:%S') Updating cua-computer-server..." >> "$LOG_FILE" + "$VENV_DIR/bin/pip" install --upgrade cua-computer-server >> "$LOG_FILE" 2>&1 + + echo "$(date '+%Y-%m-%d %H:%M:%S') Starting CUA Computer Server on port 5000..." >> "$LOG_FILE" + "$VENV_DIR/bin/python" -m computer_server --port 5000 >> "$LOG_FILE" 2>&1 + return $? +} + +while true; do + start_server + EXIT_CODE=$? + echo "$(date '+%Y-%m-%d %H:%M:%S') Server exited with code: $EXIT_CODE. Restarting in 5s..." >> "$LOG_FILE" + sleep 5 +done +EOF + +chmod +x "$START_SCRIPT" +log "Start script created" + +# Create xhost script for X11 access +log "Creating xhost script..." +sudo tee /etc/X11/Xsession.d/99xauth > /dev/null << 'EOF' +#!/bin/sh +# Grant local X11 access for CUA Computer Server +export DISPLAY=:0 +xhost +local: 2>/dev/null || true +EOF +sudo chmod +x /etc/X11/Xsession.d/99xauth +log "X11 access script created" + +# Create system-level systemd service +log "Creating systemd system service..." + +sudo tee /etc/systemd/system/$SERVICE_NAME.service > /dev/null << EOF +[Unit] +Description=CUA Computer Server +After=graphical.target + +[Service] +Type=simple +ExecStart=$START_SCRIPT +Restart=always +RestartSec=5 +Environment=PYTHONUNBUFFERED=1 +Environment=DISPLAY=:0 +Environment=XAUTHORITY=$USER_HOME/.Xauthority +User=$USER_NAME +WorkingDirectory=$CUA_DIR + +[Install] +WantedBy=graphical.target +EOF + +log "Systemd service created at /etc/systemd/system/$SERVICE_NAME.service" + +# Ensure proper ownership of CUA directory +log "Setting ownership of $CUA_DIR to $USER_NAME..." +sudo chown -R "$USER_NAME:$USER_NAME" "$CUA_DIR" + +# Enable and start the service +log "Enabling systemd service..." +sudo systemctl daemon-reload +sudo systemctl enable "$SERVICE_NAME.service" + +log "Starting CUA Computer Server service..." +sudo systemctl start "$SERVICE_NAME.service" || true + +log "=== CUA Computer Server setup completed ===" +log "Service status: $(sudo systemctl is-active $SERVICE_NAME.service 2>/dev/null || echo 'unknown')" diff --git a/libs/qemu-docker/linux/src/vm/setup/setup.sh b/libs/qemu-docker/linux/src/vm/setup/setup.sh new file mode 100644 index 00000000..ff7758ab --- /dev/null +++ b/libs/qemu-docker/linux/src/vm/setup/setup.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# Main Setup Script for Linux +# Installs dependencies and sets up CUA Computer Server + +set -e + +SCRIPT_DIR="/opt/oem" +LOG_FILE="$SCRIPT_DIR/setup.log" + +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" +} + +log "=== Running Main Setup ===" + +# Update package lists +log "Updating package lists..." +sudo apt-get update + +# Install Git +log "Installing Git..." +sudo apt-get install -y git + +# Setup CUA Computer Server +log "Setting up CUA Computer Server..." +if [ -f "$SCRIPT_DIR/setup-cua-server.sh" ]; then + bash "$SCRIPT_DIR/setup-cua-server.sh" 2>&1 | tee -a "$LOG_FILE" + log "CUA Computer Server setup completed." +else + log "ERROR: setup-cua-server.sh not found at $SCRIPT_DIR/setup-cua-server.sh" +fi + +log "=== Main Setup Completed ===" From 75773080279bfc6b6d24841978c901ca79047ac2 Mon Sep 17 00:00:00 2001 From: "synacktra.work@gmail.com" Date: Tue, 9 Dec 2025 00:51:25 +0530 Subject: [PATCH 2/2] chore: add workflow for publishing docker image --- .../workflows/docker-publish-cua-linux.yml | 29 +++++++++++++++++++ libs/qemu-docker/linux/Dockerfile | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/docker-publish-cua-linux.yml diff --git a/.github/workflows/docker-publish-cua-linux.yml b/.github/workflows/docker-publish-cua-linux.yml new file mode 100644 index 00000000..03d83d2d --- /dev/null +++ b/.github/workflows/docker-publish-cua-linux.yml @@ -0,0 +1,29 @@ +name: Build and Publish CUA Linux Container + +on: + push: + branches: + - main + tags: + - "docker-cua-linux-v*.*.*" + paths: + - "libs/qemu-docker/linux/**" + - ".github/workflows/docker-publish-cua-linux.yml" + - ".github/workflows/docker-reusable-publish.yml" + pull_request: + paths: + - "libs/qemu-docker/linux/**" + - ".github/workflows/docker-publish-cua-linux.yml" + - ".github/workflows/docker-reusable-publish.yml" + +jobs: + publish: + uses: ./.github/workflows/docker-reusable-publish.yml + with: + image_name: cua-linux + context_dir: libs/qemu-docker/linux + dockerfile_path: Dockerfile + tag_prefix: docker-cua-linux-v + docker_hub_org: trycua + secrets: + DOCKER_HUB_TOKEN: ${{ secrets.DOCKER_HUB_TOKEN }} diff --git a/libs/qemu-docker/linux/Dockerfile b/libs/qemu-docker/linux/Dockerfile index 28cdc6d4..bfbdac7a 100644 --- a/libs/qemu-docker/linux/Dockerfile +++ b/libs/qemu-docker/linux/Dockerfile @@ -1,4 +1,4 @@ -FROM qemu-local:latest +FROM trycua/qemu-local:latest COPY src/vm/setup/. /oem/