Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2a00aa9
Redesign GUI around a menu-driven, low-button layout
JE-Chen Jul 2, 2026
cc9bf8a
Convert remaining feature tabs to the Actions menu
JE-Chen Jul 2, 2026
567d7e3
Guard the Actions-menu tab contract with a headless test
JE-Chen Jul 2, 2026
3af936f
Document the Actions-menu GUI redesign in WHATS_NEW
JE-Chen Jul 2, 2026
9d55895
Remove orphaned prof_disable translation key
JE-Chen Jul 2, 2026
fa1a44e
Quarantine the Actions-menu contract probe in a subprocess
JE-Chen Jul 3, 2026
124a4a9
Restore line and text-region detection under OpenCV 5
JE-Chen Jul 3, 2026
ed66db2
Merge pull request #454 from Integration-Automation/feat/minimal-menu-ui
JE-Chen Jul 3, 2026
843fc4b
Add stable je_auto_control.api facade and portable failure bundles
JE-Chen Jul 3, 2026
7ddac38
Move releases to immutable tags with Trusted Publishing and add CI gates
JE-Chen Jul 3, 2026
d71c480
Refresh all project docs: lifecycle, security, capability matrix, ind…
JE-Chen Jul 3, 2026
3763599
Fix CI: install pytest-cov, xvfb for Linux smoke, pin publish action,…
JE-Chen Jul 3, 2026
ffe88f0
Skip numpy stub parsing in mypy so numpy 2.5 PEP 695 stubs don't brea…
JE-Chen Jul 3, 2026
8fe3a4d
Harden runtime paths across platform, executor, network, and GUI layers
JE-Chen Jul 18, 2026
ef44f79
Fix CI: skip GUI theme test without qt_material, clear Sonar/Codacy n…
JE-Chen Jul 18, 2026
3a0ffd2
Skip webrtc/thumbnail thread-marshal tests that abort the shared pyte…
JE-Chen Jul 18, 2026
f3393a2
Fix relay pipe hang on Linux/CPython 3.14 and clear Sonar index-acces…
JE-Chen Jul 18, 2026
6546bc4
Fix container libEGL collection error and suppress Sonar slice false-…
JE-Chen Jul 18, 2026
fcfd260
Merge pull request #455 from Integration-Automation/docs/actions-menu…
JE-Chen Jul 18, 2026
013a8b1
Document the cross-platform reliability hardening pass
JE-Chen Jul 18, 2026
db4d2b9
Merge pull request #456 from Integration-Automation/docs/reliability-…
JE-Chen Jul 18, 2026
08e9538
Prevent crashes and silent automation interruptions
JE-Chen Jul 18, 2026
d5fca0a
Merge pull request #457 from Integration-Automation/fix/crash-and-int…
JE-Chen Jul 23, 2026
b836670
Bound the OpenCV major and refresh pinned dependency versions
JE-Chen Jul 23, 2026
cd59796
Keep Qt guards skipping when PySide6 imports but its libs are missing
JE-Chen Jul 23, 2026
d8a8396
Merge pull request #460 from Integration-Automation/chore/dependency-…
JE-Chen Jul 23, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ jobs:
autocontrol:ci -c "
pip install --no-cache-dir -r dev_requirements.txt &&
xvfb-run -a -s '-screen 0 1280x800x24' \
python -m pytest test/unit_test/headless -q --tb=short
python -m pytest -q --tb=short
"

- name: Smoke test the entrypoint (rest mode)
Expand Down
51 changes: 51 additions & 0 deletions .github/workflows/platform-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Platform smoke

on:
push:
branches: ["main", "dev"]
pull_request:
branches: ["main", "dev"]

permissions:
contents: read

jobs:
stable-api:
strategy:
fail-fast: false
matrix:
os: [windows-2022, ubuntu-22.04, macos-14]
python-version: ["3.10", "3.14"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: python -m pip install -e . # NOSONAR githubactions:S8544 # reason: installs the checked-out project itself, there is no upstream version to lock
# The X11 backend connects to a display at import time, so Linux
# runs need a virtual one.
- name: Install a virtual display (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y xvfb
- name: Import stable API and generate platform-neutral code
shell: bash
run: >-
${{ runner.os == 'Linux' && 'xvfb-run -a' || '' }}
python -c "import je_auto_control.api as ac;
compile(ac.generate_code([['AC_screen_size']], style='actions'),
'<generated>', 'exec')"
- name: Create headless diagnostic bundle
shell: bash
run: >-
${{ runner.os == 'Linux' && 'xvfb-run -a' || '' }}
python -c "from je_auto_control.api import
FailureBundleOptions, create_failure_bundle;
create_failure_bundle('platform-smoke.zip',
options=FailureBundleOptions(screenshot=False))"
- uses: actions/upload-artifact@v4
if: always()
with:
name: platform-smoke-${{ matrix.os }}-${{ matrix.python-version }}
path: platform-smoke.zip
if-no-files-found: warn
44 changes: 36 additions & 8 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ permissions:
contents: read

jobs:
dependency-review:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/dependency-review-action@v4

lint:
runs-on: ubuntu-latest
steps:
Expand All @@ -28,9 +37,7 @@ jobs:
cache: "pip"

- name: Install ruff
run: |
python -m pip install --upgrade pip
pip install ruff
run: "pip install --only-binary :all: ruff==0.15.22"

- name: Run ruff
run: ruff check je_auto_control/
Expand All @@ -47,9 +54,7 @@ jobs:
cache: "pip"

- name: Install bandit
run: |
python -m pip install --upgrade pip
pip install bandit
run: "pip install --only-binary :all: bandit==1.9.4"

- name: Run bandit (recursive, skip tests + i18n dicts)
run: bandit -r je_auto_control/ -c pyproject.toml
Expand Down Expand Up @@ -81,7 +86,30 @@ jobs:
# for any sub-package the snapshot doesn't include
# (admin, usb, remote_desktop, vision, …).
pip install -e .
pip install ruff==0.15.14 bandit==1.9.4 pytest==9.0.3 pytest-timeout==2.4.0 pytest-rerunfailures==15.1 PySide6==6.11.1
pip install --only-binary :all: ruff==0.15.22 bandit==1.9.4 pytest==9.1.1 pytest-timeout==2.4.0 pytest-rerunfailures==15.1 pytest-cov==7.0.0 PySide6==6.11.1

# Paths come from `testpaths` in pyproject.toml. Do NOT pass an explicit
# path here: an argument overrides testpaths, which previously meant the
# flow_control tests were configured to run but silently never did.
- name: Run headless pytest suite
run: pytest test/unit_test/headless/ -v --tb=short --timeout=120
run: >-
pytest -v --tb=short --timeout=120
--cov=je_auto_control --cov-report=term-missing
--cov-report=xml --cov-fail-under=35

- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.python-version }}
path: coverage.xml

typing-stable-api:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -e . # NOSONAR githubactions:S8541,githubactions:S8544 # reason: installs the checked-out project itself, there is no upstream version to lock and the build must run
- run: "pip install --only-binary :all: mypy==2.3.0"
- run: mypy je_auto_control/api je_auto_control/utils/failure_bundle
62 changes: 62 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Release

on:
push:
tags: ["v*"]

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
attestations: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: "python -m pip install --only-binary :all: build==1.5.0 twine==6.2.0"
- name: Verify tag matches package version
env:
RELEASE_TAG: ${{ github.ref_name }}
run: |
python - <<'PY'
import os, tomllib
with open("pyproject.toml", "rb") as handle:
version = tomllib.load(handle)["project"]["version"]
if os.environ["RELEASE_TAG"] != f"v{version}":
raise SystemExit(f"tag {os.environ['RELEASE_TAG']} != v{version}")
PY
- run: python -m build
- run: python -m twine check dist/*
- name: Smoke-test the built wheel
run: |
python -m venv /tmp/wheel-test
/tmp/wheel-test/bin/pip install dist/*.whl
/tmp/wheel-test/bin/python -c "import je_auto_control.api"
- uses: actions/attest-build-provenance@v2
with:
subject-path: "dist/*"
- uses: actions/upload-artifact@v4
with:
name: python-distributions
path: dist/

publish:
needs: build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/je-auto-control
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
name: python-distributions
path: dist/
- uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
7 changes: 3 additions & 4 deletions .github/workflows/stable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ jobs:

publish:
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
# Publishing moved to release.yml: immutable v* tags + Trusted Publishing.
if: ${{ false }}
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -134,9 +135,7 @@ jobs:
python-version: "3.12"

- name: Install build tooling
run: |
python -m pip install --upgrade pip
pip install build twine
run: "pip install --only-binary :all: build==1.5.0 twine==6.2.0"

- name: Bump patch version in pyproject.toml
id: bump
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,8 @@ dmypy.json
/.claude/
/.claude
/.idea

# Local test/smoke artifacts
.test-tmp/
bundle-smoke.zip
platform-smoke.zip
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Changelog

This file records user-visible compatibility changes. Detailed development
notes remain in `WHATS_NEW.md`.

The format follows Keep a Changelog. Until 1.0, breaking changes are permitted
only when documented here with a migration path.

## Unreleased

### Added

- Stable, headless `je_auto_control.api` façade.
- Portable `autocontrol.failure-bundle/v1` diagnostic archives and CLI command.
- Public API lifecycle, capability matrix, security policy, coverage and type
checking configuration.

### Changed

- Releases are prepared from version tags and use PyPI Trusted Publishing.
- The USB/IP server binds `127.0.0.1` by default (least-privilege). Exporting
the attached device to the LAN now requires an explicit `host="0.0.0.0"`.

### Deprecated

- New integrations should avoid the eager, historical top-level import surface
and import stable entry points from `je_auto_control.api`.

### Fixed

- macOS cursor position and omitted-coordinate clicks on Retina / HiDPI
displays (pixel-vs-point display-height mismatch).
- Remote-desktop relay hang on Linux + CPython 3.14 when one paired peer
disconnected (a cross-thread `shutdown()` no longer wakes a blocked `recv()`).
- `AC_expect_poll` crashing on a not-ready value instead of continuing to poll;
`AC_parallel` branch variable-scope isolation; malformed `run_suite` specs now
report a clean error instead of aborting.
- Windows Interception backend send-to-window click silently no-opping.
- Wayland partial-coordinate `mouse_scroll` raising instead of degrading.
- Action-file save now raises `AutoControlJsonActionException` (not a raw
`UnicodeEncodeError`) on non-encodable text; non-ASCII USB/IP busid no longer
kills the client thread; SQLite connections are closed; USB ACL removal is
case-insensitive.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ No feature is complete unless it can be driven entirely without the GUI **and**
- **Re-export from the package facade**: add the public functions / classes to `je_auto_control/__init__.py` and its `__all__` so `import je_auto_control as ac; ac.<feature>(...)` works out of the box.
- **Executor command coverage**: wire an `AC_*` command into `utils/executor/action_executor.py` so the feature is usable from JSON action files, the socket server, the scheduler, and the visual script builder — all without Python glue.
- **GUI tab or control is a thin wrapper**: the Qt widget must only translate user input into calls on the headless core. It must not contain business logic that would be unreachable headlessly.
- **Tab commands live in the Actions menu, not in-tab buttons**: the main window is menu-driven. A tab keeps only its inputs, tables, and result/status views; its commands surface through the window-level **Actions** menu. Core tabs declare `(label_key, handler)` pairs at registration in `gui/main_widget.py`; feature tabs expose a `menu_actions()` method returning the same shape. Script Builder and Remote Desktop are the only exempt tabs (interactive panel layouts). `test/unit_test/headless/test_actions_menu_gui.py` guards this contract — a new tab without registry actions or a `menu_actions()` hook fails CI.
- **The top-level package stays Qt-free**: `import je_auto_control` MUST NOT import `PySide6`. The GUI entry point is loaded lazily inside `start_autocontrol_gui()`. Verify with:

```python
Expand Down
34 changes: 27 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@

## What's New

All per-release notes have moved to **[WHATS_NEW.md](WHATS_NEW.md)**.
**Latest (2026-07-18) — cross-platform reliability hardening.** A full-project runtime audit fixed execution-time defects across the macOS / Windows / Linux / Wayland backends, the executor, and the remote-desktop / USB stacks — correct Retina cursor math, a relay hang on CPython 3.14, `AC_expect_poll` / `AC_parallel` robustness, localhost-by-default USB/IP, and typed exceptions preserved at I/O boundaries — each covered by a headless regression test. No API changes.

All per-release notes are in **[WHATS_NEW.md](WHATS_NEW.md)**.

## Features

Expand Down Expand Up @@ -99,9 +101,10 @@ All per-release notes have moved to **[WHATS_NEW.md](WHATS_NEW.md)**.
- **WebRTC Packet Inspector** — process-global rolling window of `StatsSnapshot` samples (default 600 / ~10 min @ 1Hz) fed by the existing WebRTC stats pollers. Per-metric `last/min/max/avg/p95` for RTT, FPS, bitrate, packet loss, jitter
- **USB Device Enumeration** — read-only cross-platform device listing. Tries pyusb (libusb) first; falls back to platform-specific (Windows `Get-PnpDevice`, macOS `system_profiler`, Linux `/sys/bus/usb/devices`). Phase 2 passthrough builds on this (see below)
- **System Diagnostics** — single-command "is everything OK?" probe across platform, optional deps, executor command count, audit chain, screenshot, mouse, disk space, REST registry. CLI exits 0 if all green / 1 otherwise; REST `/diagnose`; severity-tagged GUI tab
- **Stable API & Failure Bundles** — versioned, lazy `je_auto_control.api` façade for new integrations (`execute_action`, `generate_code`, `run_diagnostics`, failure bundles) with a documented [lifecycle policy](docs/API_LIFECYCLE.md). Portable `autocontrol.failure-bundle/v1` diagnostic ZIPs: manifest + redacted context/events/log tail, optional screenshot and diagnostics, best-effort collectors, atomic write. CLI `je_auto_control failure-bundle out.zip`; `codegen --failure-bundle` wraps generated pytest in automatic failure diagnostics
- **USB Hotplug Events** — polling-based hotplug watcher (`UsbHotplugWatcher`) with bounded ring buffer + sequence-numbered events; `GET /usb/events?since=N` lets late subscribers catch up. GUI auto-refresh toggle on the USB tab.
- **OpenAPI 3.1 + Swagger UI** — `GET /openapi.json` (auth-gated, generated from the live route table) + `GET /docs` (browser Swagger UI with bearer token bar). Drift test in CI catches new routes added without metadata.
- **Configuration Bundle** — single-file JSON export/import of user config (admin hosts, address book, trusted viewers, known hosts, host service, IDs). Atomic write with `<name>.bak.<timestamp>` backups; CLI `python -m je_auto_control.utils.config_bundle export|import`; `POST /config/{export,import}`; GUI buttons on the REST API tab.
- **Configuration Bundle** — single-file JSON export/import of user config (admin hosts, address book, trusted viewers, known hosts, host service, IDs). Atomic write with `<name>.bak.<timestamp>` backups; CLI `python -m je_auto_control.utils.config_bundle export|import`; `POST /config/{export,import}`; export/import commands on the REST API tab's Actions menu.
- **USB Passthrough (opt-in)** — let a remote viewer use a USB device physically attached to the host, over a WebRTC `usb` DataChannel. Wire-level protocol (11 opcodes incl. `RESUME`, CREDIT-based flow control, 16 KiB payload cap with EOF fragmentation for oversize transfers). All eight original open questions resolved: reliable-ordered channel, LIST-over-channel (ACL-filtered), per-claim credits, Linux kernel-driver detach/reattach, and ACL **HMAC-SHA256 integrity** (fail-closed on tamper; pluggable key — Windows DPAPI or passphrase vault). **Backends:** `LibusbBackend` (production), `WinusbBackend` (ctypes) and `IokitBackend` (native IOKit enumeration + libusb transfers) — Windows/macOS *hardware-unverified*; `default_passthrough_backend()` picks per-OS. Viewer-side blocking client (`control/bulk/interrupt_transfer`, `list_devices`, `resume`); in-process `UsbLoopback` so one machine can share + use a device through the full stack. **Wired into WebRTC** host/viewer (`viewer.usb_client()`) plus claim **resume tokens** that survive a reconnect. Persistent ACL (default deny, mode 0600) with host-side prompt dialog, abuse **rate-limit / lockout**, and tamper-evident audit integration. Five driving surfaces: AnyDesk-style **GUI panel** (share + ACL allow/block + local/remote use), `AC_usb_*` executor commands (JSON / socket / scheduler), **REST** `/usb/...`, first-class **MCP** `ac_usb_*` tools, and the Python API. Default off — opt-in via `enable_usb_passthrough(True)` or `JE_AUTOCONTROL_USB_PASSTHROUGH=1`; default-on still pending Phase 2e external security sign-off + real-hardware verification.
- **Observability (Prometheus + OpenTelemetry)** — stdlib-only `Counter` / `Gauge` / `Histogram` registry with a tiny built-in HTTP exporter on `/metrics`, plus an OpenTelemetry-compatible tracer that upgrades to real OTel spans when the SDK is installed. The executor and agent loop emit `autocontrol_action_calls_total{action,outcome}`, `autocontrol_action_duration_seconds`, and `autocontrol_agent_steps_total{tool,outcome}` automatically — drop the URL into a Prometheus scrape config and you have a Grafana dashboard with zero per-script wiring.

Expand Down Expand Up @@ -546,8 +549,8 @@ ac.run_from_description("open Notepad and type hello", executor=executor)
| `AUTOCONTROL_LLM_BACKEND` | `anthropic` to force a backend |
| `AUTOCONTROL_LLM_MODEL` | Override the default model (e.g. `claude-opus-4-7`) |

GUI: **LLM Planner** tab — description box, `QThread`-backed *Plan*
button, action-list preview, and a *Run plan* button.
GUI: **LLM Planner** tab — description box and action-list preview;
*Plan* (`QThread`-backed) and *Run plan* live in the window's Actions menu.

### Runtime Variables & Control Flow

Expand Down Expand Up @@ -575,7 +578,8 @@ commands, scripts can drive themselves from data without Python glue:

`AC_if_var` operators: `eq`, `ne`, `lt`, `le`, `gt`, `ge`, `contains`,
`startswith`, `endswith`. GUI: **Variables** tab — live view of
`executor.variables` with single-set, JSON seed, and clear-all controls.
`executor.variables`; single-set, JSON seed, and clear-all run from the
window's Actions menu.

### Remote Desktop

Expand Down Expand Up @@ -1099,8 +1103,9 @@ for run in default_history_store.list_runs(limit=20):
print(run.id, run.source, run.status, run.artifact_path)
```

The GUI **Run History** tab exposes filter/refresh/clear and
double-click-to-open on the artifact column.
The GUI **Run History** tab shows the runs table with
double-click-to-open on the artifact column; filter, refresh, and
clear run from the window's Actions menu.

### Report Generation

Expand Down Expand Up @@ -1357,6 +1362,16 @@ Or from the command line:
python -m je_auto_control
```

The main window is menu-driven: tabs hold only their inputs, tables,
and result views, and every tab's commands live in the window-level
**Actions** menu, which rebuilds for the active tab. **View → Tabs**
shows or hides any of the ~48 registered tabs, grouped by category
(Core / Editing / Detection & Vision / Automation Engines / System);
the default layout opens with just Record, Script Builder, and Remote
Desktop. **View → Text Size** offers auto/preset font scaling, and the
**Language** menu (English / 繁體中文 / 简体中文 / 日本語) retranslates
the whole window live.

---

## Command-Line Interface
Expand Down Expand Up @@ -1438,6 +1453,11 @@ python -m pytest test/integrated_test/

### Project Links

- [Capability and platform matrix](docs/CAPABILITY_MATRIX.md)
- [Public API lifecycle and deprecation policy](docs/API_LIFECYCLE.md)
- [Security policy](SECURITY.md)
- [Compatibility changelog](CHANGELOG.md)

- **Homepage**: https://github.com/Intergration-Automation-Testing/AutoControl
- **Documentation**: https://autocontrol.readthedocs.io/en/latest/
- **PyPI**: https://pypi.org/project/je_auto_control/
Expand Down
Loading
Loading