Skip to content

Commit 9106ef3

Browse files
authored
Merge pull request #509 from Integration-Automation/feat/progress-sweep
Close the approved Progress items: input, windows and DPI, scopes, signing, roles, MCP roots, USB ids, package gate
2 parents 0f17c90 + 95e3d9c commit 9106ef3

186 files changed

Lines changed: 11399 additions & 1694 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/platform-smoke.yml‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ jobs:
2727
# back to building from source and CMake could not
2828
# configure for ARM64. Twelve minutes, then failure.
2929
# cryptography — wheels stop at 46.0.3; 46.0.4 onwards ship none.
30-
# Our floor is >=48.0.1 and that is a security floor
31-
# (GHSA-537c-gmf6-5ccf), so it cannot be lowered.
30+
# Our floor is >=50.0.0 and that is a security floor
31+
# (GHSA-537c-gmf6-5ccf, GHSA-g6cj-pr64-35w5), so it
32+
# cannot be lowered.
3233
#
3334
# Nothing in the package imports either one at import time, so what
3435
# this square proves is real: the install succeeds and the stable API
@@ -38,7 +39,7 @@ jobs:
3839
#
3940
# pip install --dry-run --only-binary=:all: --platform win_arm64 \
4041
# --python-version 3.12 --target /tmp/probe \
41-
# 'opencv-python>=4.8,<6' 'cryptography>=48.0.1'
42+
# 'opencv-python>=4.8,<6' 'cryptography>=50.0.0'
4243
#
4344
# When both resolve, drop the markers from pyproject.toml.
4445
os: [windows-2022, ubuntu-22.04, macos-14, ubuntu-22.04-arm, windows-11-arm]

‎.github/workflows/quality.yml‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
run: "pip install --only-binary :all: ruff==0.16.0"
4141

4242
- name: Run ruff
43-
run: ruff check je_auto_control/
43+
run: ruff check je_auto_control/ je_auto_control_pytest.py
4444

4545
security:
4646
runs-on: ubuntu-latest
@@ -57,7 +57,7 @@ jobs:
5757
run: "pip install --only-binary :all: bandit==1.9.4"
5858

5959
- name: Run bandit (recursive, skip tests + i18n dicts)
60-
run: bandit -r je_auto_control/ -c pyproject.toml
60+
run: bandit -r je_auto_control/ je_auto_control_pytest.py -c pyproject.toml
6161

6262
pytest-headless:
6363
# The suite ran on Windows alone for its whole life, so every
@@ -150,10 +150,13 @@ jobs:
150150
# would hide exactly the breakage this job exists to find.
151151
#
152152
# `coverage run -m pytest`, NOT `pytest --cov`, and that is load-bearing:
153-
# this package registers a `pytest11` entry point, so pytest imports
154-
# `je_auto_control.utils.pytest_plugin.plugin` while loading plugins —
155-
# which executes `je_auto_control/__init__.py`, the facade, and with it
156-
# several hundred modules. pytest-cov only starts measuring after that,
153+
# this package registers a `pytest11` entry point, and while it pointed
154+
# at `je_auto_control.utils.pytest_plugin.plugin` pytest imported that
155+
# submodule while loading plugins — which executes
156+
# `je_auto_control/__init__.py`, the facade, and with it several hundred
157+
# modules. (The entry point is the top-level `je_auto_control_pytest`
158+
# now; `coverage run` stays because it does not depend on what a plugin
159+
# imports.) pytest-cov only starts measuring after that,
157160
# so every one of those modules' import-time lines was recorded as never
158161
# executed. Measured on this tree: 11,962 statements, 24 points, and the
159162
# worst-hit files were the biggest ones (`action_executor` +786,
@@ -196,7 +199,7 @@ jobs:
196199
with:
197200
python-version: "3.12"
198201
- 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
199-
- run: "pip install --only-binary :all: mypy==2.3.0"
202+
- run: "pip install --only-binary :all: mypy==2.4.0"
200203
# Deliberately NOT installing the optional extras: the contract forces
201204
# every non-base third-party module to `Any` so the result cannot depend
202205
# on what is installed, and installing them here would only hide a

‎CHANGELOG.md‎

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,38 @@ it shipped into a `## [x.y.z] - date` section of their own; the tag's
1515

1616
### Added
1717

18+
- Action files can be signed with an Ed25519 key pair, so an endpoint that
19+
verifies does not hold what signs: `create_signing_keypair(private_path,
20+
public_path)` / `AC_create_signing_keypair`, `private_key_path=` on
21+
`sign_action_file`, `public_key_path=` on `verify_action_file`, and the
22+
environment variables `JE_AUTOCONTROL_ACTION_SIGNING_PRIVATE_KEY`,
23+
`JE_AUTOCONTROL_ACTION_SIGNING_PUBLIC_KEY` and
24+
`JE_AUTOCONTROL_ACCEPT_LEGACY_ACTION_SIGNATURES`. Nothing changes while none
25+
of them is set. `CryptographyUnavailableError` (an `AutoControlException` and
26+
a `RuntimeError`) replaces the bare `RuntimeError` raised without the
27+
`cryptography` package.
28+
- Opt-in roles for the REST API and the MCP HTTP transport:
29+
`JE_AUTOCONTROL_RBAC_USERS=<user store file>` (or `user_store=`, or `--users`
30+
on the REST entry points). A bearer token then names a user, each route,
31+
tool and privileged `AC_*` command needs a capability of that user's role,
32+
and audit entries carry the user id. `UserStore`, `UserRecord`,
33+
`AuthorizationContext`, `AuthorizationError`, `UserAuthError` and
34+
`authorization_scope` are exported; `Capability.SIGN_ACTIONS` is new.
35+
- Opt-in confinement of MCP file arguments: `JE_AUTOCONTROL_MCP_PATH_ROOTS`,
36+
`JE_AUTOCONTROL_MCP_PATH_ROOTS_FROM_CLIENT` and, for `env://` references,
37+
`JE_AUTOCONTROL_MCP_ENV_REF_ALLOW`. Tool schemas mark file-path arguments
38+
with `"format": "path"`. `PathPolicy` and `PathNotAllowedError` are exported.
39+
- `execution_scope(variables=None)`: a fresh variable scope for a block of
40+
runs.
41+
- `JE_AUTOCONTROL_ALLOWED_PACKAGES` and `je_auto_control run --allow-package
42+
NAME` allow packages at the package gate without Python glue.
43+
- USB passthrough requests carry an optional `request_id` that a current host
44+
echoes; `UsbClientDesynchronized`, `ClientHandle.reusable`,
45+
`UsbPassthroughClient.reusable`.
46+
- `remote_desktop_registry` records who opened each host and viewer
47+
(`adopt`, `evict`, `release`, `owner_of`, `owned`; `owner=` on the stop and
48+
disconnect methods), and every `*_status` result gains an `"owner"` key.
49+
- `mark_screen` returns `image_origin`; `render_marks(..., origin=)`.
1850
- GUI: a navigation panel on the left of the main window lists every tab by
1951
category, with a search box (`Ctrl+K`; `Ctrl+B` hides the panel).
2052
`AutoControlGUIWidget.activate_tab(key)` opens a tab or brings it to the
@@ -93,8 +125,8 @@ it shipped into a `## [x.y.z] - date` section of their own; the tag's
93125
in one call: the clicks land on one point, `interval` seconds apart.
94126
Defaults keep the single click, and a single click records the same
95127
action as before. `AC_click_mouse`, the `ac_click_mouse` MCP tool and the
96-
Script Builder take both parameters. On macOS the clicks still arrive as
97-
separate single clicks (`Progress.md`).
128+
Script Builder take both parameters. On macOS the n-th click sets the
129+
click-state field (unverified on hardware, `Progress.md`).
98130
- **Drag pacing: `tween_drag` / `drag_path` take `step_delay_s` and
99131
`settle_s`** (seconds, default 0). `step_delay_s` rests after each move;
100132
`settle_s` rests on the start before the press, after the press and before
@@ -161,6 +193,84 @@ it shipped into a `## [x.y.z] - date` section of their own; the tag's
161193

162194
### Changed
163195

196+
- **Breaking: the package gate refuses by default.**
197+
`AC_add_package_to_executor` / `AC_add_package_to_callback_executor` (and
198+
the `package_manager` methods) no longer import a package that has not been
199+
allowed; the `DeprecationWarning` release is over. Allow with
200+
`executor.allow_packages(...)`, `JE_AUTOCONTROL_ALLOWED_PACKAGES` or
201+
`--allow-package`; `executor.set_allow_arbitrary_packages(True)` restores
202+
the old behaviour. `allow_packages` now rejects a name that is not a dotted
203+
module name.
204+
- **Breaking: a file a remote-desktop host pushes lands below the viewer's
205+
download directory.** `host.send_file_to_viewers(src, dest_path)`:
206+
`dest_path` is now relative to `~/Downloads/AutoControl`
207+
(`JE_AUTOCONTROL_REMOTE_DOWNLOAD_DIR`); an absolute path, a drive or UNC
208+
path, `..` or a link leading outside fails the transfer. Viewer → host is
209+
unchanged, and `FileReceiver()` without `base_dir` is unconfined as before.
210+
- **Variables no longer survive a run.** `execute_action_with_vars`, REST
211+
`/execute` and `/execute_file`, every MCP tool call, each socket command,
212+
scheduler jobs, trigger / hotkey / webhook / e-mail firings, ChatOps `/run`
213+
and voice commands each get a fresh variable scope; a later `${name}` fails
214+
with `Unknown variable` instead of reading the previous caller's value.
215+
Direct `executor.execute_action(...)` from Python keeps the process scope.
216+
An `AC_parallel` branch starts with a copy of the caller's variables, and
217+
`AC_run_dag` local nodes run on the calling list's executor and scope.
218+
- **Windows is per-monitor DPI aware.** Importing the package asks for
219+
per-monitor v2 (falling back to system awareness). On a monitor whose scale
220+
differs from the primary's, coordinates and capture sizes are now physical
221+
pixels: a point at an offset from that monitor's corner moves by
222+
`its scale / primary scale`, and its screenshots are no longer downscaled.
223+
Coordinates, regions, window layouts and templates recorded on such a
224+
monitor have to be recorded again; the primary monitor is unaffected.
225+
- **Keyboard input on Windows and X11.** `write("Hi")` types `Hi` (it typed
226+
`hi`); `is_shift=True` is honoured by `type_keyboard`, `hotkey`, `write` and
227+
`press_keyboard_key`; CR LF is one Enter; `write_secret` and the Unicode
228+
typing helpers press Return / Tab / Backspace as keys instead of sending
229+
them as code points.
230+
- **`mouse_scroll` defaults to `scroll_direction="scroll_up"`**, so a positive
231+
amount scrolls up on every platform; on X11 / Wayland it used to scroll
232+
down. NaN, infinite and non-numeric coordinates raise before anything moves,
233+
and fractional coordinates are rounded rather than truncated.
234+
- **Window management on Windows.** `focus_window` raises
235+
`AutoControlActionException` when the window did not become the foreground
236+
one; `show_window_by_title` and the z-order calls return the real result;
237+
`list_windows` / `find_window` skip DWM-cloaked and zero-area windows;
238+
`post_key` sends one `WM_CHAR` for a printable character (pass the integer
239+
virtual-key code to get key messages) and `post_key_to_window` accepts
240+
`"enter"` / `"esc"`; `save_window_layout` stores the rectangle `MoveWindow`
241+
positions (re-save older layout files); snap / grid / cascade use the work
242+
area; `wait_for_window` never sleeps past its timeout.
243+
- Screenshots of a region on Windows (`pil_screenshot`, `screenshot`,
244+
`AC_screenshot`, `keyword_screenshot`, `capture_window`) capture any
245+
monitor instead of returning black outside the primary one; a region
246+
entirely off every monitor raises `AutoControlScreenException`.
247+
`grab_logical` clips a region to the desktop and returns the clipped
248+
origin, and set-of-marks renders the whole virtual desktop.
249+
- macOS: `click_mouse(clicks=n)` sets the click-state field on the n-th
250+
click's events, `list_windows` includes minimised windows and `restore`
251+
finds them, and `grab_logical` works in points across every display. None
252+
of this has been run on a Mac yet (`Progress.md`).
253+
- With an Ed25519 key configured, a legacy HMAC signature is refused unless
254+
`JE_AUTOCONTROL_ACCEPT_LEGACY_ACTION_SIGNATURES` is set, and an endpoint
255+
holding only the public key refuses `AC_sign_action_file`.
256+
- A remote-desktop panel reads, drives and disconnects only the session it
257+
opened; a panel whose session is replaced closes its window. Starting a
258+
host no longer disconnects a viewer.
259+
- The Anthropic agent backends no longer rewrite turns they have sent: past
260+
three screenshots the conversation restarts from a summary of the goal and
261+
the actions so far plus the latest screenshot.
262+
- The `pytest11` entry point is the top-level module `je_auto_control_pytest`,
263+
which imports only pytest; `je_auto_control.utils.pytest_plugin` re-exports
264+
it. Reinstall for the new entry point to take effect.
265+
- `cryptography>=50.0.0` (was `>=48.0.1`). There is no `macosx_10_9_x86_64`
266+
wheel at that floor, so an Intel Mac builds it from source.
267+
- Image and OCR: templates load from non-ASCII paths and may be grayscale; an
268+
unreadable or unsupported template raises `ImageNotFoundException`;
269+
`find_spans` finds a phrase that starts inside a long box; the centre of a
270+
box with negative coordinates is floored.
271+
- `keyboard_layout` reports `None` for a Shift half that prints no single
272+
character and labels ISO / UK / ABNT keys; a clipboard format named `None`
273+
normalises to `""`.
164274
- GUI tabs are built the first time they are opened. `AutoControlGUIWidget`
165275
registers all 48 tabs from `gui/tab_registry.py` but constructs only the
166276
three it opens on and its own forms; `list_registered_tabs()` builds

‎CLAUDE.md‎

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,19 @@ python -m build # build
3333
```
3434

3535
**Coverage is measured with `coverage run -m pytest`, never `pytest --cov`.**
36-
This package registers a `pytest11` entry point, so pytest imports
37-
`je_auto_control.utils.pytest_plugin.plugin` — and with it the whole facade —
38-
while loading plugins, before pytest-cov starts. Several hundred modules then
39-
have their import-time lines recorded as never executed: measured, that is
40-
11,962 statements and ~24 percentage points (52.22% vs 72.05% on the same
41-
suite). `test/unit_test/headless/test_coverage_measurement.py` holds CI to the
42-
correct spelling.
36+
This package registers a `pytest11` entry point. While it pointed at
37+
`je_auto_control.utils.pytest_plugin.plugin`, pytest imported that submodule —
38+
and with it the whole facade — while loading plugins, before pytest-cov starts.
39+
Several hundred modules then had their import-time lines recorded as never
40+
executed: measured, that was 11,962 statements and ~24 percentage points
41+
(52.22% vs 72.05% on the same suite). The entry point is now the top-level
42+
module `je_auto_control_pytest`, which imports only pytest
43+
(`test_pytest_entrypoint_light.py` keeps it so), but `coverage run` remains the
44+
rule: it does not depend on what a plugin imports or on which build is
45+
installed, and `pytest --cov` has not been re-measured since.
46+
`test/unit_test/headless/test_coverage_measurement.py` holds CI to the
47+
correct spelling. After changing the entry point, reinstall (`pip install -e .`)
48+
for it to take effect.
4349

4450
**Measure it with the `[webrtc]` extra installed**, which is why it is in the
4551
line above. Eleven modules under `utils/remote_desktop` raise `ImportError` at

0 commit comments

Comments
 (0)