Merge pull request #675 from trycua/feat/android-docker-support-&-docs

- Support for Android Docker image (`trycua/cua-qemu-android`) to the Python Computer SDK.
- Docker publish workflow for publishing to registry
- Adds "Android on Docker" in quickstart docs
This commit is contained in:
Harsh Verma
2025-12-23 01:56:49 +05:30
committed by GitHub
32 changed files with 3189 additions and 2982 deletions

View File

@@ -0,0 +1,29 @@
name: Build and Publish CUA Android Container
on:
push:
branches:
- main
tags:
- "docker-cua-qemu-android-v*.*.*"
paths:
- "libs/qemu-docker/android/**"
- ".github/workflows/docker-publish-cua-qemu-android.yml"
- ".github/workflows/docker-reusable-publish.yml"
pull_request:
paths:
- "libs/qemu-docker/android/**"
- ".github/workflows/docker-publish-cua-qemu-android.yml"
- ".github/workflows/docker-reusable-publish.yml"
jobs:
publish:
uses: ./.github/workflows/docker-reusable-publish.yml
with:
image_name: cua-qemu-android
context_dir: libs/qemu-docker/android
dockerfile_path: Dockerfile
tag_prefix: docker-cua-qemu-android-v
docker_hub_org: trycua
secrets:
DOCKER_HUB_TOKEN: ${{ secrets.DOCKER_HUB_TOKEN }}

View File

@@ -69,6 +69,7 @@ jobs:
- name: Build & push digest (PR)
if: github.event_name == 'pull_request'
id: build-pr
continue-on-error: true
uses: docker/build-push-action@v5
with:
context: ./${{ inputs.context_dir }}
@@ -93,6 +94,7 @@ jobs:
- name: Build & push digest (main)
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
id: build-main
continue-on-error: true
uses: docker/build-push-action@v5
with:
context: ./${{ inputs.context_dir }}
@@ -120,6 +122,7 @@ jobs:
- name: Build & push digest (semver)
if: startsWith(github.ref, format('refs/tags/{0}', inputs.tag_prefix))
id: build-semver
continue-on-error: true
uses: docker/build-push-action@v5
with:
context: ./${{ inputs.context_dir }}
@@ -134,12 +137,23 @@ jobs:
- name: Export digest
id: export-digest
if: |
(steps.build-pr.outcome == 'success') ||
(steps.build-main.outcome == 'success') ||
(steps.build-semver.outcome == 'success')
run: |
mkdir -p /tmp/digests
digest="${{ steps.build-pr.outputs.digest || steps.build-main.outputs.digest || steps.build-semver.outputs.digest }}"
echo "$digest" > "/tmp/digests/${{ steps.platform.outputs.tag }}.txt"
if [ -n "$digest" ]; then
echo "$digest" > "/tmp/digests/${{ steps.platform.outputs.tag }}.txt"
echo "Digest exported for platform ${{ matrix.platform }}: $digest"
else
echo "No digest to export for platform ${{ matrix.platform }}"
exit 1
fi
- name: Upload digest artifact (unique per platform)
if: steps.export-digest.outcome == 'success'
uses: actions/upload-artifact@v4
with:
name: digests-${{ steps.platform.outputs.tag }}
@@ -150,6 +164,7 @@ jobs:
runs-on: ubuntu-latest
needs:
- build-and-push
if: success() || failure() # Run even if some platform builds failed
steps:
- name: Set up Docker Buildx
@@ -196,6 +211,23 @@ jobs:
path: /tmp/digests
merge-multiple: true
- name: Check available digests
id: check-digests
run: |
DIGEST_COUNT=$(find /tmp/digests -type f -name "*.txt" | wc -l)
echo "digest_count=$DIGEST_COUNT" >> $GITHUB_OUTPUT
if [ "$DIGEST_COUNT" -eq 0 ]; then
echo "Error: No platform builds succeeded!"
exit 1
fi
echo "Found $DIGEST_COUNT platform digest(s)"
for f in $(find /tmp/digests -type f -name "*.txt"); do
platform=$(basename "$f" .txt)
echo " - $platform"
done
- name: Create & push multi-arch manifest
run: |
IMAGE="${{ inputs.docker_hub_org }}/${{ inputs.image_name }}"

View File

@@ -23,8 +23,6 @@ jobs:
- name: Set up pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Set up Python
uses: actions/setup-python@v4

View File

@@ -7,6 +7,7 @@ This guide covers setting up and developing the CUA (Computer Use Agent) monorep
The project is organized as a monorepo with these main packages:
**Python Packages** (located in `libs/python/`):
- `core/` - Base package with telemetry support
- `computer/` - Computer-use interface (CUI) library
- `agent/` - AI agent library with multi-provider support
@@ -16,6 +17,7 @@ The project is organized as a monorepo with these main packages:
- `bench-ui/` - Benchmark UI utilities
**Other Packages**:
- `libs/lume/` - Lume CLI (Swift)
- `libs/typescript/` - TypeScript packages including `cua-cli`
@@ -24,39 +26,45 @@ All Python packages are part of a [uv workspace](https://docs.astral.sh/uv/works
## Quick Start
1. **Install Lume CLI** (required for local VM management):
```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/trycua/cua/main/libs/lume/scripts/install.sh)"
```
2. **Clone the repository**:
```bash
git clone https://github.com/trycua/cua.git
cd cua
```
3. **Create `.env.local`** in the root directory with your API keys:
```bash
ANTHROPIC_API_KEY=your_anthropic_key_here
OPENAI_API_KEY=your_openai_key_here
```
4. **Install Node.js dependencies**:
```bash
npm install -g pnpm # if not already installed
pnpm install
```
5. **Install Python dependencies**:
```bash
pip install uv # if not already installed
uv sync
```
6. **Open workspace in VS Code/Cursor**:
```bash
# For Python development
code .vscode/py.code-workspace
# For Lume (Swift) development
code .vscode/lume.code-workspace
```
@@ -159,6 +167,7 @@ Formatting configuration is defined in [`pyproject.toml`](./pyproject.toml). See
##### Python-specific settings
Python-specific IDE settings are configured in [`.vscode/settings.json`](.vscode/settings.json), including:
- Python interpreter path
- Format on save
- Code actions on save
@@ -250,11 +259,13 @@ pnpm install
#### Usage
- **Check formatting** (without making changes):
```bash
pnpm prettier:check
```
- **Automatically format files**:
```bash
pnpm prettier:format
```
@@ -342,6 +353,7 @@ To release a new version of the CUA CLI (`@trycua/cli`):
5. Click "Run workflow"
The workflow will:
- Build single-file executables for all supported platforms
- Publish the package to npm
- Create a GitHub release with the version tag (format: `cua-vX.Y.Z`)
@@ -367,6 +379,7 @@ The workflow will:
### 4. Update Documentation
Update any relevant documentation with the new version number, including:
- Example code in documentation
- Any version-specific instructions
- Compatibility matrices

View File

@@ -4,7 +4,11 @@ description: Supported computer-using agent loops and models
---
<Callout>
A corresponding <a href="https://github.com/trycua/cua/blob/main/notebooks/agent_nb.ipynb" target="_blank">Jupyter Notebook</a> is available for this documentation.
A corresponding{' '}
<a href="https://github.com/trycua/cua/blob/main/notebooks/agent_nb.ipynb" target="_blank">
Jupyter Notebook
</a>{' '}
is available for this documentation.
</Callout>
An agent can be thought of as a loop - it generates actions, executes them, and repeats until done:

View File

@@ -3,7 +3,14 @@ title: Customize ComputerAgent
---
<Callout>
A corresponding <a href="https://github.com/trycua/cua/blob/main/notebooks/customizing_computeragent.ipynb" target="_blank">Jupyter Notebook</a> is available for this documentation.
A corresponding{' '}
<a
href="https://github.com/trycua/cua/blob/main/notebooks/customizing_computeragent.ipynb"
target="_blank"
>
Jupyter Notebook
</a>{' '}
is available for this documentation.
</Callout>
The `ComputerAgent` interface provides an easy proxy to any computer-using model configuration, and it is a powerful framework for extending and building your own agentic systems.

View File

@@ -4,7 +4,11 @@ description: Use ComputerAgent with HUD for benchmarking and evaluation
---
<Callout>
A corresponding <a href="https://github.com/trycua/cua/blob/main/notebooks/eval_osworld.ipynb" target="_blank">Jupyter Notebook</a> is available for this documentation.
A corresponding{' '}
<a href="https://github.com/trycua/cua/blob/main/notebooks/eval_osworld.ipynb" target="_blank">
Jupyter Notebook
</a>{' '}
is available for this documentation.
</Callout>
The HUD integration allows an agent to be benchmarked using the [HUD framework](https://www.hud.so/). Through the HUD integration, the agent controls a computer inside HUD, where tests are run to evaluate the success of each task.

View File

@@ -11,14 +11,9 @@ The Cua CLI is a command-line tool for managing your Cua cloud sandboxes. Create
## Installation
<Tabs items={['macOS / Linux', 'Windows']}>
<Tab value="macOS / Linux">
```bash
curl -LsSf https://cua.ai/cli/install.sh | sh
```
</Tab>
<Tab value="macOS / Linux">```bash curl -LsSf https://cua.ai/cli/install.sh | sh ```</Tab>
<Tab value="Windows">
```powershell
powershell -ExecutionPolicy ByPass -c "irm https://cua.ai/cli/install.ps1 | iex"
```powershell powershell -ExecutionPolicy ByPass -c "irm https://cua.ai/cli/install.ps1 | iex"
```
</Tab>
</Tabs>

View File

@@ -4,7 +4,14 @@ slug: sandboxed-python
---
<Callout>
A corresponding <a href="https://github.com/trycua/cua/blob/main/examples/sandboxed_functions_examples.py" target="_blank">Python example</a> is available for this documentation.
A corresponding{' '}
<a
href="https://github.com/trycua/cua/blob/main/examples/sandboxed_functions_examples.py"
target="_blank"
>
Python example
</a>{' '}
is available for this documentation.
</Callout>
You can run Python functions securely inside a sandboxed virtual environment on a remote Cua Computer Framework. This is useful for executing untrusted user code, isolating dependencies, or providing a safe environment for automation tasks.

View File

@@ -86,7 +86,8 @@ CUA_API_KEY=sk_cua-api01...
CUA_CONTAINER_NAME=m-linux-...
```
Finally, setup your sandbox. Refer to the [quickstart guide](https://cua.ai/docs/get-started/quickstart) on how to setup the computer environment.
Finally, setup your sandbox. Refer to the [quickstart guide](https://cua.ai/docs/get-started/quickstart) on how to setup the computer environment.
</Step>
<Step>

View File

@@ -88,13 +88,13 @@ You can run your Cua sandbox in the cloud (recommended for easiest setup), local
</Tab>
<Tab value="QEMU Docker">
Run full Linux (Ubuntu Desktop) or Windows 11 VMs inside Docker containers using QEMU virtualization.
Run full Linux (Ubuntu Desktop), Windows 11, or Android 11 VMs inside Docker containers using QEMU virtualization.
<Callout type="warn">
QEMU images require a **golden image preparation step** on first use. This involves downloading an ISO and running a one-time installation.
Linux and Windows images require a **golden image preparation step** on first use. Android images start directly without preparation.
</Callout>
<Tabs items={['Linux', 'Windows']}>
<Tabs items={['Linux', 'Windows', 'Android']}>
<Tab value="Linux">
**1. Install Docker Desktop or Docker Engine**
@@ -163,6 +163,19 @@ You can run your Cua sandbox in the cloud (recommended for easiest setup), local
The container will install Windows 11 from the ISO and shut down when complete. Monitor progress at [http://localhost:8006](http://localhost:8006).
</Tab>
<Tab value="Android">
**1. Install Docker Desktop or Docker Engine**
**2. Pull the QEMU Android image:**
```bash
docker pull trycua/cua-qemu-android:latest
```
No golden image preparation needed - the Android emulator starts directly when you run it!
</Tab>
</Tabs>
</Tab>
@@ -306,7 +319,7 @@ Install Cua Computer Framework and verify your sandbox is working by performing
```
</Tab>
<Tab value="QEMU Docker">
<Tabs items={['Linux', 'Windows']}>
<Tabs items={['Linux', 'Windows', 'Android']}>
<Tab value="Linux">
```python
from computer import Computer
@@ -377,6 +390,40 @@ Install Cua Computer Framework and verify your sandbox is working by performing
asyncio.run(main())
```
</Tab>
<Tab value="Android">
```python
from computer import Computer
import asyncio
computer = Computer(
os_type="android",
provider_type="docker",
image="trycua/cua-qemu-android:latest",
timeout=150, # Emulator needs more time to boot
run_opts={
"devices": ["/dev/kvm"], # Required for Android emulator
"env": {
"EMULATOR_DEVICE": "Samsung Galaxy S10",
},
},
)
async def main():
await computer.run() # Launch & connect to Android emulator
try:
# Take a screenshot of the Android screen
screenshot = await computer.interface.screenshot()
# Simulate a left-click at coordinates (100, 100)
await computer.interface.left_click(100, 100)
# Type "Hello!" into the active application
await computer.interface.type_text("Hello!")
finally:
await computer.disconnect()
asyncio.run(main())
```
</Tab>
</Tabs>
</Tab>
<Tab value="macOS Sandbox">

View File

@@ -26,10 +26,12 @@ Computer-Use Sandboxes are isolated, controlled environments where AI agents can
## Key Features
With **Cua Computer Framework**, you can:
- Automate **Windows, Linux, and macOS** sandboxes with a consistent, pyautogui-like API
- Create & manage sandboxes locally or using **Cua Cloud**
With **Cua Agent Framework**, you can:
- Run computer-use models with a consistent schema
- Benchmark on **OSWorld-Verified**, **SheetBench-V2**, and **ScreenSpot**
- Combine UI grounding models with any LLM using **composed agents**
@@ -44,6 +46,14 @@ Check out our [tutorials](https://cua.ai/blog), [examples](https://github.com/tr
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 mt-4 text-sm">
<Card icon={<Rocket className="w-4 h-4" />} href="/get-started/quickstart" title="Quickstart" />
<Card icon={<Zap className="w-4 h-4" />} href="/agent-sdk/agent-loops" title="Agent Loops" />
<Card icon={<BookOpen className="w-4 h-4" />} href="/computer-sdk/computers" title="Cua Computer" />
<Card icon={<Monitor className="w-4 h-4" />} href="/example-usecases/form-filling" title="Examples" />
<Card
icon={<BookOpen className="w-4 h-4" />}
href="/computer-sdk/computers"
title="Cua Computer"
/>
<Card
icon={<Monitor className="w-4 h-4" />}
href="/example-usecases/form-filling"
title="Examples"
/>
</div>

View File

@@ -34,7 +34,9 @@ export const baseOptions: BaseLayoutProps = {
className="hidden dark:block"
alt="Logo"
/>
<span className="text-lg font-bold" style={{ fontFamily: 'var(--font-urbanist)' }}>Cua</span>
<span className="text-lg font-bold" style={{ fontFamily: 'var(--font-urbanist)' }}>
Cua
</span>
</div>
),
url: 'https://cua.ai',

View File

@@ -157,4 +157,4 @@ export function Footer() {
</div>
</footer>
);
}
}

View File

@@ -1,15 +1,18 @@
from __future__ import annotations
import time
from bench_ui import launch_window, get_element_rect, execute_javascript
from pathlib import Path
import os
import time
from pathlib import Path
from bench_ui import execute_javascript, get_element_rect, launch_window
def main():
os.environ["CUA_BENCH_UI_DEBUG"] = "1"
# Get the path to the gui folder
gui_folder = Path(__file__).parent / "gui"
# Launch a window serving the static folder
pid = launch_window(
folder=str(gui_folder),
@@ -30,7 +33,7 @@ def main():
# Check if button has been clicked
clicked = execute_javascript(pid, "document.getElementById('testButton').disabled")
print("Button clicked:", clicked)
# Get the page title
title = execute_javascript(pid, "document.title")
print("Page title:", title)

View File

@@ -51,7 +51,7 @@ SYSTEM_INFO = {
from .providers.base import VMProviderType
from .providers.factory import VMProviderFactory
OSType = Literal["macos", "linux", "windows"]
OSType = Literal["macos", "linux", "windows", "android"]
class Computer:

View File

@@ -59,6 +59,7 @@ class DockerProvider(BaseVMProvider):
- "trycua/cua-xfce:latest" (vanilla XFCE)
- "trycua/cua-qemu-linux:latest" (QEMU-based, only supports Ubuntu for now)
- "trycua/cua-qemu-windows:latest" (QEMU-based, only supports Windows 11 Enterprise for now)
- "trycua/cua-qemu-android:latest" (QEMU-based Android 11 emulator)
verbose: Enable verbose logging
ephemeral: Use ephemeral (temporary) storage
vnc_port: Port for VNC interface (default: 6901)
@@ -78,10 +79,11 @@ class DockerProvider(BaseVMProvider):
# Detect image type and configure user directory accordingly
self._detect_image_config()
if self._image_type == "qemu":
# QEMU Linux/Windows images require golden image storage
if self._image_type in ("qemu-linux", "qemu-windows"):
if not storage or Path(storage).is_dir() is False:
raise ValueError(
"Golden image storage path must be provided for QEMU-based images."
"Golden image storage path must be provided for QEMU-based Linux/Windows images."
)
self.storage = storage
elif ephemeral:
@@ -91,16 +93,26 @@ class DockerProvider(BaseVMProvider):
def _detect_image_config(self):
"""Detect image type and configure paths accordingly."""
# Detect if this is a XFCE, Kasm or QEMU image
# Detect if this is a XFCE, Kasm, or QEMU-based image
if "docker-xfce" in self.image.lower() or "xfce" in self.image.lower():
self._home_dir = "/home/cua"
self._image_type = "docker-xfce"
logger.info(f"Detected docker-xfce image: using {self._home_dir}")
elif any(x in self.image.lower() for x in ("qemu-linux", "qemu-windows")):
# QEMU-based images
elif "qemu-linux" in self.image.lower():
# QEMU-based Linux images
self._home_dir = ""
self._image_type = "qemu"
logger.info("Detected QEMU image: using /")
self._image_type = "qemu-linux"
logger.info("Detected QEMU Linux image: using /")
elif "qemu-windows" in self.image.lower():
# QEMU-based Windows images
self._home_dir = ""
self._image_type = "qemu-windows"
logger.info("Detected QEMU Windows image: using /")
elif "qemu-android" in self.image.lower():
# QEMU-based Android images
self._home_dir = "/home/androidusr"
self._image_type = "qemu-android"
logger.info("Detected QEMU Android image: using /home/androidusr")
else:
# Default to Kasm configuration
self._home_dir = "/home/kasm-user"
@@ -292,44 +304,58 @@ class DockerProvider(BaseVMProvider):
# Build docker run command
cmd = ["docker", "run", "-d", "--name", name]
if self._image_type != "qemu":
# Add memory limit if specified
if "memory" in run_opts:
memory_limit = self._parse_memory(run_opts["memory"])
cmd.extend(["--memory", memory_limit])
# Add memory limit if specified
if "memory" in run_opts:
memory_limit = self._parse_memory(run_opts["memory"])
cmd.extend(["--memory", memory_limit])
# Add CPU limit if specified
if "cpu" in run_opts:
cpu_count = str(run_opts["cpu"])
cmd.extend(["--cpus", cpu_count])
# Add CPU limit if specified
if "cpu" in run_opts:
cpu_count = str(run_opts["cpu"])
cmd.extend(["--cpus", cpu_count])
# Add devices if specified (e.g., /dev/kvm for QEMU)
warn_no_kvm = False
# Add devices if specified (e.g., /dev/kvm for QEMU and Android)
has_kvm = False
if "devices" in run_opts:
devices = run_opts["devices"]
warn_no_kvm = self._image_type == "qemu" and "/dev/kvm" not in devices
has_kvm = "/dev/kvm" in devices
for device in devices:
cmd.extend(["--device", device])
elif "device" in run_opts:
warn_no_kvm = self._image_type == "qemu" and run_opts["device"] != "/dev/kvm"
has_kvm = run_opts["device"] == "/dev/kvm"
cmd.extend(["--device", run_opts["device"]])
if warn_no_kvm:
# QEMU Android requires /dev/kvm
if self._image_type == "qemu-android" and not has_kvm:
raise ValueError(
"Android images requires KVM. "
'Add run_opts={"devices": ["/dev/kvm"]} to your Computer configuration.'
)
if self._image_type in ("qemu-linux", "qemu-windows") and not has_kvm:
logger.warning(
"/dev/kvm device is recommended for QEMU images for better performance."
)
# Add NET_ADMIN capability for QEMU images
if self._image_type == "qemu":
# Add NET_ADMIN capability for QEMU Linux/Windows images
if self._image_type in ("qemu-linux", "qemu-windows"):
cmd.extend(["--cap-add", "NET_ADMIN"])
# Add port mappings
vnc_port = run_opts.get("vnc_port", self.vnc_port)
api_port = run_opts.get("api_port", self.api_port)
# Port mappings differ for QEMU vs Kasm/XFCE images
internal_vnc_port = 8006 if self._image_type == "qemu" else 6901
internal_api_port = 5000 if self._image_type == "qemu" else 8000
# Port mappings differ by image type
if self._image_type in ("qemu-linux", "qemu-windows"):
internal_vnc_port = 8006
internal_api_port = 5000
elif self._image_type == "qemu-android":
internal_vnc_port = 6080
internal_api_port = 8000
else:
# Kasm/XFCE images
internal_vnc_port = 6901
internal_api_port = 8000
if vnc_port:
cmd.extend(["-p", f"{vnc_port}:{internal_vnc_port}"]) # VNC port
@@ -351,6 +377,13 @@ class DockerProvider(BaseVMProvider):
cmd.extend(["-e", "VNC_PW=password"]) # Set VNC password
cmd.extend(["-e", "VNCOPTIONS=-disableBasicAuth"]) # Disable VNC basic auth
# Add Android-specific default environment variables
if self._image_type == "qemu-android":
if "env" not in run_opts:
run_opts["env"] = {}
run_opts["env"].setdefault("EMULATOR_DEVICE", "Samsung Galaxy S10")
run_opts["env"]["WEB_VNC"] = "true"
# Add custom environment variables from run_opts
if "env" in run_opts and isinstance(run_opts["env"], dict):
for key, value in run_opts["env"].items():

View File

@@ -3,7 +3,7 @@
# ============================================================================
FROM eclipse-temurin:17-jdk AS builder
RUN apt-get update && apt-get install -y wget unzip && \
RUN apt-get update && apt-get install -y wget unzip dos2unix && \
mkdir -p /opt/android-sdk/cmdline-tools && \
cd /opt/android-sdk/cmdline-tools && \
wget -q https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip && \
@@ -17,11 +17,12 @@ ENV PATH="${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tool
RUN yes | sdkmanager --licenses && \
sdkmanager "platforms;android-30" "build-tools;30.0.3"
COPY wallpaper-manager /build/wallpaper-manager
COPY src/wallpaper-manager /build/wallpaper-manager
WORKDIR /build/wallpaper-manager
RUN curl -fsSL -o gradle/wrapper/gradle-wrapper.jar \
https://raw.githubusercontent.com/gradle/gradle/v7.6.0/gradle/wrapper/gradle-wrapper.jar && \
dos2unix gradlew && \
chmod +x gradlew
RUN ./gradlew assembleDebug --no-daemon
@@ -56,7 +57,7 @@ ENV PATH="/opt/venv/bin:$PATH"
RUN /opt/venv/bin/pip install --no-cache-dir --upgrade pip && \
/opt/venv/bin/pip install --no-cache-dir cua-computer-server
COPY entry.sh /usr/local/bin/entry.sh
COPY src/entry.sh /usr/local/bin/entry.sh
RUN chmod +x /usr/local/bin/entry.sh
# Make venv accessible to androidusr

View File

@@ -1,6 +1,6 @@
# Android Docker
# QEMU Android
Docker image that runs an Android emulator with CUA Computer Server integration, enabling programmatic control of Android devices via HTTP API.
Docker image that runs an Android emulator using QEMU/KVM with CUA Computer Server integration, enabling programmatic control of Android devices via HTTP API.
## Features
@@ -21,27 +21,26 @@ Docker image that runs an Android emulator with CUA Computer Server integration,
### Production Build
```bash
cd android-docker
docker build -t trycua/cua-droid .
docker build -t trycua/cua-qemu-android .
docker run -d -p 6080:6080 -p 8000:8000 \
-e EMULATOR_DEVICE="Samsung Galaxy S10" \
-e WEB_VNC=true \
--device /dev/kvm \
--name android-container \
trycua/cua-droid
trycua/cua-qemu-android
```
### Development Build
```bash
cd .. # Go to libs/ directory
docker build -f android-docker/dev.Dockerfile -t trycua/cua-droid:dev .
cd ../.. # Go to libs/ directory
docker build -f qemu-docker/android/dev.Dockerfile -t trycua/cua-qemu-android:dev .
docker run -d -p 6080:6080 -p 8000:8000 \
-e EMULATOR_DEVICE="Samsung Galaxy S10" \
-e WEB_VNC=true \
--device /dev/kvm \
--name android-container \
trycua/cua-droid:dev
trycua/cua-qemu-android:dev
```
## Access Points

View File

@@ -1,6 +1,6 @@
# ============================================================================
# Development Dockerfile - builds from libs/ directory with local sources
# Build command: docker build -f android-docker/dev.Dockerfile -t android-cua:dev .
# Build command: docker build -f qemu-docker/android/dev.Dockerfile -t cua-qemu-android:dev .
# ============================================================================
# ============================================================================
@@ -8,7 +8,7 @@
# ============================================================================
FROM eclipse-temurin:17-jdk AS builder
RUN apt-get update && apt-get install -y wget unzip && \
RUN apt-get update && apt-get install -y wget unzip dos2unix && \
mkdir -p /opt/android-sdk/cmdline-tools && \
cd /opt/android-sdk/cmdline-tools && \
wget -q https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip && \
@@ -22,11 +22,12 @@ ENV PATH="${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tool
RUN yes | sdkmanager --licenses && \
sdkmanager "platforms;android-30" "build-tools;30.0.3"
COPY android-docker/wallpaper-manager /build/wallpaper-manager
COPY qemu-docker/android/src/wallpaper-manager /build/wallpaper-manager
WORKDIR /build/wallpaper-manager
RUN curl -fsSL -o gradle/wrapper/gradle-wrapper.jar \
https://raw.githubusercontent.com/gradle/gradle/v7.6.0/gradle/wrapper/gradle-wrapper.jar && \
dos2unix gradlew && \
chmod +x gradlew
RUN ./gradlew assembleDebug --no-daemon
@@ -64,7 +65,7 @@ WORKDIR /tmp/computer-server
RUN /opt/venv/bin/pip install --no-cache-dir --upgrade pip && \
/opt/venv/bin/pip install --no-cache-dir -e .
COPY android-docker/entry.sh /usr/local/bin/entry.sh
COPY qemu-docker/android/src/entry.sh /usr/local/bin/entry.sh
RUN chmod +x /usr/local/bin/entry.sh
# Make venv accessible to androidusr

View File

@@ -52,4 +52,4 @@
"typescript": "^5.8.3",
"vitest": "^4.0.14"
}
}
}

View File

@@ -5,5 +5,6 @@
},
"devDependencies": {
"prettier": "^3.6.2"
}
},
"packageManager": "pnpm@9.0.4+sha1.b198ac6d38244fd829253720f9daafd6a606834d"
}

5838
uv.lock generated

File diff suppressed because it is too large Load Diff