Skip to content

Commit fa940b9

Browse files
authored
Merge pull request #182 from Integration-Automation/dev
Operations layer + USB Phase 2 chain (rounds 22-47): full-stack landing
2 parents 6349a14 + 51fc521 commit fa940b9

155 files changed

Lines changed: 28476 additions & 1185 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/dev.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
strategy:
1818
fail-fast: false
1919
matrix:
20-
python-version: [ "3.10", "3.11", "3.12" ]
20+
python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ]
2121

2222
steps:
2323
- uses: actions/checkout@v4

.github/workflows/quality.yml

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
name: AutoControl Code Quality
2+
3+
# Static analysis (ruff, bandit) plus the headless pytest suite added in
4+
# rounds 22-30. Decoupled from the existing dev/stable workflows, which
5+
# run legacy standalone test scripts and exist for hardware integration
6+
# coverage on Windows runners.
7+
8+
on:
9+
push:
10+
branches: [ "dev", "main", "stable" ]
11+
pull_request:
12+
branches: [ "dev", "main", "stable" ]
13+
workflow_dispatch:
14+
15+
permissions:
16+
contents: read
17+
18+
jobs:
19+
lint:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v4
23+
24+
- name: Set up Python
25+
uses: actions/setup-python@v5
26+
with:
27+
python-version: "3.12"
28+
cache: "pip"
29+
30+
- name: Install ruff
31+
run: |
32+
python -m pip install --upgrade pip
33+
pip install ruff
34+
35+
- name: Run ruff
36+
run: ruff check je_auto_control/
37+
38+
security:
39+
runs-on: ubuntu-latest
40+
steps:
41+
- uses: actions/checkout@v4
42+
43+
- name: Set up Python
44+
uses: actions/setup-python@v5
45+
with:
46+
python-version: "3.12"
47+
cache: "pip"
48+
49+
- name: Install bandit
50+
run: |
51+
python -m pip install --upgrade pip
52+
pip install bandit
53+
54+
- name: Run bandit (recursive, skip tests + i18n dicts)
55+
run: bandit -r je_auto_control/ -c pyproject.toml
56+
57+
pytest-headless:
58+
runs-on: windows-2022
59+
strategy:
60+
fail-fast: false
61+
matrix:
62+
python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ]
63+
steps:
64+
- uses: actions/checkout@v4
65+
66+
- name: Set up Python ${{ matrix.python-version }}
67+
uses: actions/setup-python@v5
68+
with:
69+
python-version: ${{ matrix.python-version }}
70+
cache: "pip"
71+
72+
- name: Install dependencies
73+
run: |
74+
python -m pip install --upgrade pip wheel
75+
# Install the editable package FIRST so its source dir is the
76+
# one Python sees on subsequent imports. We deliberately
77+
# avoid `pip install -r dev_requirements.txt` here because
78+
# that file pulls in `je_auto_control_dev` (a separate PyPI
79+
# package), which ships its own snapshot of `je_auto_control/`
80+
# straight into site-packages and masks the editable install
81+
# for any sub-package the snapshot doesn't include
82+
# (admin, usb, remote_desktop, vision, …).
83+
pip install -e .
84+
pip install ruff==0.15.9 bandit==1.9.4 pytest==9.0.2 pytest-timeout==2.4.0 pytest-rerunfailures==15.1 PySide6==6.11.0
85+
86+
- name: Run headless pytest suite
87+
run: pytest test/unit_test/headless/ -v --tb=short --timeout=120

.github/workflows/stable.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
strategy:
2222
fail-fast: false
2323
matrix:
24-
python-version: [ "3.10", "3.11", "3.12" ]
24+
python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ]
2525

2626
steps:
2727
- uses: actions/checkout@v4

README.md

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
- **OCR** — extract text from screen regions using Tesseract; wait for, click, or locate rendered text; regex search and full-region dump
6464
- **LLM Action Planner** — translate a plain-language description into a validated `AC_*` action list using Claude
6565
- **Runtime Variables & Control Flow**`${var}` substitution at execution time, plus `AC_set_var` / `AC_inc_var` / `AC_if_var` / `AC_for_each` / `AC_loop` / `AC_retry` for data-driven scripts
66-
- **Remote Desktop** — stream this machine's screen and accept remote input over a token-authenticated TCP protocol, *or* connect to another machine and view + control it (host + viewer GUIs included). Optional TLS (HTTPS-grade encryption), WebSocket transport (ws:// + wss:// for browser / firewall-friendly clients), persistent 9-digit Host ID, host→viewer audio streaming, bidirectional clipboard sync (text + image), and chunked file transfer (drag-drop + progress bar; arbitrary destination path; no size cap)
66+
- **Remote Desktop** — stream this machine's screen and accept remote input over a token-authenticated TCP protocol, *or* connect to another machine and view + control it (host + viewer GUIs included). Optional TLS (HTTPS-grade encryption), WebSocket transport (ws:// + wss:// for browser / firewall-friendly clients), persistent 9-digit Host ID, host→viewer audio streaming, bidirectional clipboard sync (text + image), and chunked file transfer (drag-drop + progress bar; arbitrary destination path; no size cap). Plus folder sync (additive mirror — local deletions never propagate) and a self-hosted coturn TURN config bundle generator (turnserver.conf + systemd unit + docker-compose + README). **AnyDesk-style popout**: when the viewer authenticates, the live remote desktop opens in its own resizable top-level window so the control panel stays uncluttered. The Remote Desktop tabs are wrapped in `QScrollArea` so the panel stays usable on small windows and stretches edge-to-edge on 4K displays. Driveable headlessly via `je_auto_control` and over MCP through the new `ac_remote_*` tools
6767
- **Clipboard** — read/write system clipboard text on Windows, macOS, and Linux
6868
- **Screenshot & Screen Recording** — capture full screen or regions as images, record screen to video (AVI/MP4)
6969
- **Action Recording & Playback** — record mouse/keyboard events and replay them
@@ -73,8 +73,8 @@
7373
- **Event Triggers** — fire scripts when an image appears, a window opens, a pixel changes, or a file is modified
7474
- **Run History** — SQLite-backed run log across scheduler / triggers / hotkeys / REST with auto error-screenshot artifacts
7575
- **Report Generation** — export test records as HTML, JSON, or XML reports with success/failure status
76-
- **MCP Server** — JSON-RPC 2.0 Model Context Protocol server (stdio + HTTP/SSE) so Claude Desktop / Claude Code / custom tool-use loops can drive AutoControl. ~90 tools, full protocol coverage (resources, prompts, sampling, roots, logging, progress, cancellation, elicitation), bearer-token auth + TLS, audit log, rate limit, plugin hot-reload, CI fake backend
77-
- **Remote Automation** — TCP socket server **and** REST API server to receive automation commands
76+
- **MCP Server** — JSON-RPC 2.0 Model Context Protocol server (stdio + HTTP/SSE) so Claude Desktop / Claude Code / custom tool-use loops can drive AutoControl. ~100 tools, full protocol coverage (resources, prompts, sampling, roots, logging, progress, cancellation, elicitation), bearer-token auth + TLS, audit log, rate limit, plugin hot-reload, CI fake backend. New in this release: `ac_remote_host_start` / `ac_remote_host_stop` / `ac_remote_host_status` / `ac_remote_viewer_connect` / `ac_remote_viewer_disconnect` / `ac_remote_viewer_status` / `ac_remote_viewer_send_input` wrap the same singleton remote-desktop registry the GUI uses, so a model can spin up a host, open a viewer to another machine, and forward mouse / keyboard / type / hotkey actions through the active session
77+
- **Remote Automation** — TCP socket server **and** hardened REST API: bearer-token auth, per-IP rate limit + lockout, SQLite audit hook, Prometheus `/metrics`, OpenAPI-style endpoint table (`/health`, `/screen_size`, `/sessions`, `/screenshot`, `/execute`, `/audit/list`, `/audit/verify`, `/inspector/recent`, `/usb/devices`, `/diagnose`, ...), and a vanilla-JS browser dashboard at `/dashboard` (any phone with HTTP reach can monitor the host)
7878
- **Plugin Loader** — drop `.py` files exposing `AC_*` callables into a directory and register them as executor commands at runtime
7979
- **Shell Integration** — execute shell commands within automation workflows with async output capture
8080
- **Callback Executor** — trigger automation functions with callback hooks for chaining operations
@@ -84,6 +84,15 @@
8484
- **GUI Application** — built-in PySide6 graphical interface with live language switching (English / 繁體中文 / 简体中文 / 日本語)
8585
- **CLI Runner**`python -m je_auto_control.cli run|list-jobs|start-server|start-rest`
8686
- **Cross-Platform** — unified API across Windows, macOS, and Linux (X11)
87+
- **Multi-Host Admin Console** — register N AutoControl REST endpoints in one address book, poll them in parallel for health/sessions/jobs, broadcast actions to all of them. Persisted to `~/.je_auto_control/admin_hosts.json` (mode 0600 on POSIX). Bad-token hosts surface as unhealthy with the actual HTTP error
88+
- **Tamper-Evident Audit Log** — SQLite events table with SHA-256 hash chain (`prev_hash` + `row_hash` per row); editing any past row breaks the chain. `verify_chain()` walks rows top-down and reports the first broken link. Legacy tables get backfilled at startup ("trust on first use")
89+
- **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
90+
- **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) intentionally deferred pending design review
91+
- **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
92+
- **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.
93+
- **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.
94+
- **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.
95+
- **USB Passthrough (experimental, opt-in)** — wire-level protocol over a WebRTC `usb` DataChannel (10 opcodes, CREDIT-based flow control, 16 KiB payload cap). Host-side `UsbPassthroughSession` end-to-end on the Linux libusb backend; Windows `WinUSB` backend with full ctypes wiring (hardware-unverified); macOS `IOKit` skeleton. Viewer-side blocking client (`UsbPassthroughClient``ClientHandle.control_transfer / bulk_transfer / interrupt_transfer`). Persistent ACL (`~/.je_auto_control/usb_acl.json`, default deny, mode 0600) with host-side prompt QDialog and tamper-evident audit-log integration. Default off — opt-in via `enable_usb_passthrough(True)` or `JE_AUTOCONTROL_USB_PASSTHROUGH=1`. Phase 2e external security review checklist included; default-on requires sign-off.
8796

8897
---
8998

@@ -105,6 +114,7 @@ flowchart LR
105114
APIUser[["Custom Anthropic /<br/>OpenAI tool loops"]]
106115
HTTPClient[["HTTP / SSE clients"]]
107116
TCPClient[["Socket / REST clients"]]
117+
Browser[["Browser<br/>(/dashboard · /docs)"]]
108118
GUIUser[["PySide6 GUI"]]
109119
CLIUser[["python -m<br/>je_auto_control[.cli]"]]
110120
Library[["Library users<br/>(import je_auto_control)"]]
@@ -114,8 +124,9 @@ flowchart LR
114124
direction TB
115125
Stdio["MCP stdio<br/>JSON-RPC 2.0"]
116126
HTTPMCP["MCP HTTP /<br/>SSE + auth + TLS"]
117-
REST["REST server<br/>:9939"]
127+
REST["REST server :9939<br/>bearer auth · rate-limit ·<br/>OpenAPI · /metrics · /dashboard"]
118128
Socket["Socket server<br/>:9938"]
129+
WebRTC["WebRTC sessions<br/>(remote desktop ·<br/>files · audio · USB)"]
119130
end
120131
121132
subgraph MCP["mcp_server/"]
@@ -137,6 +148,28 @@ flowchart LR
137148
IOUtils["clipboard/ · cv2_utils/ ·<br/>shell_process/ · json/"]
138149
end
139150
151+
subgraph Ops["Operations Layer (utils/)"]
152+
direction TB
153+
Admin["admin/<br/>multi-host poll +<br/>broadcast"]
154+
Audit["remote_desktop/<br/>audit_log<br/>(SHA-256 chain)"]
155+
Inspector["remote_desktop/<br/>webrtc_inspector"]
156+
Diag["diagnostics/<br/>self-test"]
157+
ConfigB["config_bundle/<br/>export/import"]
158+
end
159+
160+
subgraph USB["USB"]
161+
direction TB
162+
UsbEnum["usb/<br/>list + hotplug events"]
163+
UsbPass["usb/passthrough/<br/>session · client · ACL ·<br/>libusb · WinUSB · IOKit"]
164+
end
165+
166+
subgraph Remote["Remote Desktop (utils/remote_desktop/)"]
167+
direction TB
168+
RDHost["host · webrtc_host ·<br/>signaling · multi_viewer"]
169+
RDFiles["webrtc_files · file_sync ·<br/>clipboard_sync · audio"]
170+
RDTrust["trust_list · fingerprint ·<br/>turn_config · lan_discovery"]
171+
end
172+
140173
subgraph Backends["Per-OS Backends"]
141174
direction TB
142175
Win["windows/<br/>Win32 ctypes"]
@@ -149,6 +182,7 @@ flowchart LR
149182
HTTPClient --> HTTPMCP
150183
TCPClient --> Socket
151184
TCPClient --> REST
185+
Browser --> REST
152186
153187
Stdio --> Dispatcher
154188
HTTPMCP --> Dispatcher
@@ -167,13 +201,27 @@ flowchart LR
167201
Resources --> Wrapper
168202
169203
REST --> Executor
204+
REST --> Ops
205+
REST --> USB
170206
Socket --> Executor
207+
WebRTC --> Remote
208+
WebRTC --> UsbPass
171209
172210
GUIUser --> Wrapper
173211
GUIUser --> Recorder
212+
GUIUser --> Ops
213+
GUIUser --> USB
214+
GUIUser --> Remote
174215
CLIUser --> Executor
175216
Library --> Wrapper
176217
Library --> Executor
218+
Library --> Ops
219+
220+
Admin --> REST
221+
Inspector -.- WebRTC
222+
Audit -.- REST
223+
Audit -.- USB
224+
UsbPass --> Backends
177225
178226
Wrapper --> Backends
179227
Vision -.- Wrapper
@@ -203,11 +251,17 @@ je_auto_control/
203251
├── vision/ # VLM-based locator (Anthropic / OpenAI backends)
204252
├── ocr/ # Tesseract-backed text locator
205253
├── clipboard/ # Cross-platform clipboard (text + image)
254+
├── llm/ # Plain-language → AC_* action planner
206255
├── scheduler/ # Interval + cron scheduler
207256
├── hotkey/ # Global hotkey daemon
208257
├── triggers/ # Image/window/pixel/file triggers
209258
├── run_history/ # SQLite run log + error-screenshot artifacts
210-
├── rest_api/ # Stdlib HTTP/REST server
259+
├── rest_api/ # Stdlib HTTP/REST server — auth · audit · rate-limit · OpenAPI · /metrics · dashboard · Swagger UI
260+
├── admin/ # Multi-host AdminConsoleClient (poll + broadcast)
261+
├── diagnostics/ # System self-test runner + CLI
262+
├── config_bundle/ # Single-file user-config export / import
263+
├── usb/ # Cross-platform enumeration, hotplug events, passthrough/{protocol, session, viewer client, ACL, libusb / WinUSB / IOKit}
264+
├── remote_desktop/ # WebRTC host + viewer, signalling, multi-viewer, file/clipboard/audio sync, audit log (hash chain), trust list, TURN config, mDNS discovery, WebRTC stats inspector
211265
├── plugin_loader/ # Dynamic AC_* plugin discovery
212266
├── socket_server/ # TCP socket server for remote automation
213267
├── shell_process/ # Shell command manager
@@ -570,11 +624,17 @@ viewer = RemoteDesktopViewer(
570624
```
571625

572626
**Audio streaming (host → viewer).** Optional `sounddevice` dep; opt
573-
in with `enable_audio=True` on the host, attach an `AudioPlayer` (or
574-
your own callback) on the viewer:
627+
in with an `AudioCaptureConfig` on the host, attach an `AudioPlayer`
628+
(or your own callback) on the viewer:
575629

576630
```python
577-
host = RemoteDesktopHost(token="tok", enable_audio=True)
631+
from je_auto_control.utils.remote_desktop import AudioCaptureConfig
632+
host = RemoteDesktopHost(
633+
token="tok",
634+
audio_config=AudioCaptureConfig(enabled=True), # default mic
635+
)
636+
# Or pick a loopback / monitor device:
637+
# audio_config=AudioCaptureConfig(enabled=True, device=12)
578638

579639
from je_auto_control.utils.remote_desktop import AudioPlayer
580640
player = AudioPlayer(); player.start()

0 commit comments

Comments
 (0)