Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,26 @@ it shipped into a `## [x.y.z] - date` section of their own; the tag's

## Unreleased

### Changed

- `is_package_name` (the gate behind `AC_add_package_to_executor` and
`JE_AUTOCONTROL_ALLOWED_PACKAGES`) no longer accepts a name with a trailing
newline. The pattern it replaced let one through because `$` matches before
a final newline; such a name could never be imported.
- `agent_trace.operation` records an `error` span when its block is left by a
`KeyboardInterrupt`, `SystemExit` or `GeneratorExit`, where it used to record
nothing. The exception propagates as before.
- The signaling server logs a traceback when the config store fails; the HTTP
responses are unchanged.

### Security

- `examples/18_slack_daily_report.py` resolves both paths it writes (the HTML
report and the PDF) with `os.path.realpath` and refuses one that leaves the
report directory. The PDF path was not checked before.
- The REST dashboard reports a failed refresh on its panels instead of leaving
the promise rejection unhandled.

## [1.0.0] - 2026-10-09

The first release under semantic versioning. `docs/API_LIFECYCLE.md` says what
Expand Down
1 change: 1 addition & 0 deletions Progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ U-20261009-18 的每一個裝置面呼叫都只對假的 ADB/uiautomator2/WD
這台機器沒有 free-threaded 直譯器:驗證用的是模擬繼承與真的 `-X thread_inherit_context=1`(3.14.7,GIL 開著);`free-threaded-scope` job(3.14t)是第一次真的跑。
- **能力探測**:Windows 的鎖定工作站、session 0、低完整性與擷取失敗分支只用假資料判斷;macOS 的三個 preflight 呼叫只對假物件跑過,
pyobjc 的 `Quartz` 有沒有 `CGPreflightListenEventAccess` 未知(沒有的話 `recording`/`stop_shortcut` 回 `unknown`)。
- **SonarCloud/Codacy 的抑制還沒被下一次分析確認**(U-20261009-42):`web_viewer/index.html` 裡內嵌 script 的四個 `// NOSONAR`(S9382,同檔先前的 NOSONAR 就沒被認)、`run_history/history_store.py` 分兩行寫的 `nosemgrep`、以及 `examples/18_slack_daily_report.py` 的 realpath 檢查是否被 Sonar 認成 sanitizer(S2083)。分析跑完後還開著的:內嵌 script 搬到 `.js` 檔,其餘在儀表板標 accepted。
- **Windows 的版面鍵名**(`slash` 等 8 個)只在美式配置上實際呼叫過。
- **狀態機 `on_enter` 的單一動作寫法**以前一直被拒絕、從沒執行過(已修,U-20261009-24):依賴「它不會跑」的既有狀態機現在會跑它。

Expand Down
124 changes: 62 additions & 62 deletions architecture_explore.md

Large diffs are not rendered by default.

9 changes: 4 additions & 5 deletions benchmarks/gui_startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ def run(runs: int) -> Dict[str, Any]:
}


def main(argv: Optional[List[str]] = None) -> int:
"""Command line entry point."""
def main(argv: Optional[List[str]] = None) -> None:
"""Command line entry point; a failure leaves as an exception, so there is no status to return."""
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("--runs", type=int, default=5, help="cold starts to measure (default 5)")
parser.add_argument("--output", help="write the JSON report here instead of stdout")
Expand All @@ -109,10 +109,9 @@ def main(argv: Optional[List[str]] = None) -> int:
_child()
if args.compare:
bench.emit(bench.compare(bench.load(args.compare[0]), bench.load(args.compare[1])), args.output)
return 0
return
bench.emit(run(args.runs), args.output)
return 0


if __name__ == "__main__":
sys.exit(main())
main()
27 changes: 20 additions & 7 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,27 @@ ARG DEBIAN_FRONTEND=noninteractive
# - libusb-1.0-0: USB enumeration via pyusb / libusb.
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
xvfb xauth x11-utils xdotool \
libgl1 libglib2.0-0 \
libxkbcommon-x11-0 libdbus-1-3 \
libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 \
libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxcb-sync1 \
libxcb-xfixes0 libxcb-xinerama0 libxcb-xkb1 \
libusb-1.0-0 \
ca-certificates \
libdbus-1-3 \
libgl1 \
libglib2.0-0 \
libusb-1.0-0 \
libxcb-cursor0 \
libxcb-icccm4 \
libxcb-image0 \
libxcb-keysyms1 \
libxcb-randr0 \
libxcb-render-util0 \
libxcb-shape0 \
libxcb-sync1 \
libxcb-xfixes0 \
libxcb-xinerama0 \
libxcb-xkb1 \
libxkbcommon-x11-0 \
x11-utils \
xauth \
xdotool \
xvfb \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app
Expand Down
10 changes: 7 additions & 3 deletions docker/Dockerfile.eis
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,14 @@ ARG DEBIAN_FRONTEND=noninteractive
# libgl1 and libglib2.0-0 are opencv-python's hard import-time requirements.
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libei1 libeis1 \
grim wlr-randr wtype \
libgl1 libglib2.0-0 \
ca-certificates \
grim \
libei1 \
libeis1 \
libgl1 \
libglib2.0-0 \
wlr-randr \
wtype \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app
Expand Down
15 changes: 11 additions & 4 deletions docker/Dockerfile.portal
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,18 @@ ARG DEBIAN_FRONTEND=noninteractive
# libgl1 and libglib2.0-0 are opencv-python's hard import-time requirements.
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libei1 libeis1 liboeffis1 \
dbus gir1.2-glib-2.0 python3-gi \
grim wlr-randr wtype \
libgl1 libglib2.0-0 \
ca-certificates \
dbus \
gir1.2-glib-2.0 \
grim \
libei1 \
libeis1 \
libgl1 \
libglib2.0-0 \
liboeffis1 \
python3-gi \
wlr-randr \
wtype \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app
Expand Down
12 changes: 9 additions & 3 deletions docker/Dockerfile.seat
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,16 @@ RUN printf 'deb https://deb.debian.org/debian sid main\n' \
&& apt-get update \
&& apt-get install -y --no-install-recommends -t sid ydotool \
&& apt-get install -y --no-install-recommends \
grim sway wlr-randr wtype \
dmz-cursor-theme libinput-tools udev \
libgl1 libglib2.0-0 \
ca-certificates \
dmz-cursor-theme \
grim \
libgl1 \
libglib2.0-0 \
libinput-tools \
sway \
udev \
wlr-randr \
wtype \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app
Expand Down
10 changes: 7 additions & 3 deletions docker/Dockerfile.wayland
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,14 @@ ARG DEBIAN_FRONTEND=noninteractive
# libgthread-2.0.so.0 at import, so the package cannot even load without them.
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
grim sway wlr-randr wtype \
libei1 \
libgl1 libglib2.0-0 \
ca-certificates \
grim \
libei1 \
libgl1 \
libglib2.0-0 \
sway \
wlr-randr \
wtype \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app
Expand Down
23 changes: 17 additions & 6 deletions docker/Dockerfile.x11
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,20 @@ ARG DEBIAN_FRONTEND=noninteractive
# libgthread-2.0.so.0 at import, so the package cannot even load without them.
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
xvfb xauth x11-utils x11-xserver-utils xdotool \
imagemagick openbox xterm \
at-spi2-core dbus-x11 zenity \
libgl1 libglib2.0-0 \
at-spi2-core \
ca-certificates \
dbus-x11 \
imagemagick \
libgl1 \
libglib2.0-0 \
openbox \
x11-utils \
x11-xserver-utils \
xauth \
xdotool \
xterm \
xvfb \
zenity \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app
Expand All @@ -90,8 +99,9 @@ COPY docker/x11_verify.py /opt/verify/x11_verify.py
COPY docker/x11_window_verify.py /opt/verify/x11_window_verify.py
COPY docker/x11_atspi_verify.py /opt/verify/x11_atspi_verify.py
COPY docker/entrypoint-x11.sh /usr/local/bin/autocontrol-x11-verify
RUN chmod +x /usr/local/bin/autocontrol-x11-verify

# One RUN layer: make the entrypoint executable, then create the user below.
#
# Unprivileged, unlike the Wayland verification image. That one runs as root
# because XDG_RUNTIME_DIR ownership would otherwise be the only thing
# standing between it and an answer; X11 has no such requirement — the
Expand All @@ -100,7 +110,8 @@ RUN chmod +x /usr/local/bin/autocontrol-x11-verify
# between this container and the desktop it stands in for.
# /app is the working directory, and the library opens its log file in the
# cwd at import, so the verifying user has to own it.
RUN useradd --create-home --shell /bin/sh verify \
RUN chmod +x /usr/local/bin/autocontrol-x11-verify \
&& useradd --create-home --shell /bin/sh verify \
&& chown -R verify:verify /app
USER verify

Expand Down
41 changes: 30 additions & 11 deletions docker/Dockerfile.xfce
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,38 @@ FROM python:3.12-slim AS runtime

ARG DEBIAN_FRONTEND=noninteractive

# One sorted list, so a package is easy to find and a diff touches one line:
# - display: xvfb, xauth, x11-utils, xdotool, x11vnc.
# - XFCE desktop (slim selection): xfce4, xfce4-terminal.
# - Qt / PySide6 + opencv runtime: libgl1, libglib2.0-0 (opencv needs its
# libgthread-2.0.so.0), libxkbcommon-x11-0, libdbus-1-3 and the libxcb-* set.
# - other deps: libusb-1.0-0, ca-certificates.
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
# display
xvfb xauth x11-utils xdotool x11vnc \
# XFCE desktop (slim selection)
xfce4 xfce4-terminal \
# Qt / PySide6 + opencv runtime (libgthread-2.0.so.0 ← libglib2.0-0)
libgl1 libglib2.0-0 libxkbcommon-x11-0 libdbus-1-3 \
libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 \
libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxcb-sync1 \
libxcb-xfixes0 libxcb-xinerama0 libxcb-xkb1 \
# other deps
libusb-1.0-0 ca-certificates \
ca-certificates \
libdbus-1-3 \
libgl1 \
libglib2.0-0 \
libusb-1.0-0 \
libxcb-cursor0 \
libxcb-icccm4 \
libxcb-image0 \
libxcb-keysyms1 \
libxcb-randr0 \
libxcb-render-util0 \
libxcb-shape0 \
libxcb-sync1 \
libxcb-xfixes0 \
libxcb-xinerama0 \
libxcb-xkb1 \
libxkbcommon-x11-0 \
x11-utils \
x11vnc \
xauth \
xdotool \
xfce4 \
xfce4-terminal \
xvfb \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app
Expand Down
7 changes: 5 additions & 2 deletions docker/Dockerfile.ydotool
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,12 @@ RUN printf 'deb https://deb.debian.org/debian sid main\n' \
&& apt-get update \
&& apt-get install -y --no-install-recommends -t sid ydotool \
&& apt-get install -y --no-install-recommends \
grim wlr-randr wtype \
libgl1 libglib2.0-0 \
ca-certificates \
grim \
libgl1 \
libglib2.0-0 \
wlr-randr \
wtype \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app
Expand Down
10 changes: 5 additions & 5 deletions docker/eis_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -640,17 +640,16 @@ def _helper_measurements_in_a_child() -> None:
"about the opt-in helper only and is not counted")


def _helper_measurements_main() -> int:
"""Entry point of the measurement child."""
def _helper_measurements_main() -> None:
"""Entry point of the measurement child; nothing here is counted, so it has no status."""
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from eis_server import RecordingEisServer, load_symbols
from je_auto_control.linux_wayland import libei
if load_symbols() is None:
print("note libeis is not installed; nothing to measure")
return 0
return
_run_helper_measurements(os.path.join(_scratch_dir(), "eis-helper"),
libei, RecordingEisServer)
return 0


def _run_helper_measurements(path, libei, server_class) -> None:
Expand Down Expand Up @@ -901,5 +900,6 @@ def main() -> int:

if __name__ == "__main__":
if _HELPER_FLAG in sys.argv[1:]:
sys.exit(_helper_measurements_main())
_helper_measurements_main()
sys.exit(0)
sys.exit(main())
18 changes: 10 additions & 8 deletions docker/entrypoint-x11.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ fi

GEOMETRY="${SCREEN_GEOMETRY:-1280x800x24}"
DISPLAY_NUM="${DISPLAY:-:99}"
# The rule drawn around every heading in the log.
RULE="========================================================================"

echo "starting Xvfb on ${DISPLAY_NUM} at ${GEOMETRY}"
Xvfb "${DISPLAY_NUM}" -screen 0 "${GEOMETRY}" +extension RANDR \
Expand Down Expand Up @@ -88,9 +90,9 @@ export PYTHONPATH=/opt/verify
# Runs one pass over one monitor layout; echoes the number of failed checks.
run_pass() {
label="$1"
echo "========================================================================"
echo "${RULE}"
echo "layout: ${label}"
echo "========================================================================"
echo "${RULE}"
xrandr --listmonitors || true
rc=0
python3 /opt/verify/x11_verify.py || rc=$?
Expand Down Expand Up @@ -121,21 +123,21 @@ run_pass "two monitors side by side" || total=$((total + $?))
# Window management does not depend on the monitor layout — it is about what
# the window manager agrees to — so it runs once, after both layout passes.
echo
echo "========================================================================"
echo "${RULE}"
echo "window management against a real window manager"
echo "========================================================================"
echo "${RULE}"
python3 /opt/verify/x11_window_verify.py || total=$((total + $?))

echo
echo "========================================================================"
echo "${RULE}"
echo "accessibility against a real AT-SPI bus"
echo "========================================================================"
echo "${RULE}"
python3 /opt/verify/x11_atspi_verify.py || total=$((total + $?))

echo
echo "========================================================================"
echo "${RULE}"
echo "total failed checks: ${total}"
echo "========================================================================"
echo "${RULE}"

kill "$OPENBOX_PID" 2>/dev/null || true
kill "$XVFB_PID" 2>/dev/null || true
Expand Down
Loading
Loading