From 338c49b0ee07dc9dffcc5f5fae3e79e4828e4dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A7=E9=A9=8A?= Date: Fri, 6 Nov 2026 01:03:59 +0800 Subject: [PATCH] Box 5: one browser profile, controls read even when Chromium calls its window hidden, the launcher never waits on itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found on the server's box after the update to 4: - Chromium called its whole window "not showing" (it took itself for minimized) while its page was on the screen; the reader (like XBot's) skipped anything not showing, so a page had no numbers. Now: visible with a place on the screen counts, containers are walked through, and only a page that isn't showing (a background tab) is left out. - Two browser profiles: Chromium's own (~/.config/chromium, where a person logged in by hand) and AnyBot's (~/.config/anybot-browser); the launcher used AnyBot's and Threads showed as logged out. One profile now (anybot-profile → Chromium's own; a box with only AnyBot's keeps that); both folders stay as they were. - The launcher's lock fd went to Chromium, which then held it for its whole life: the next start waited 30 s and failed. And its flags line had been lost in an edit. box_numbers_are_exact passes twice on a box from this image; the launcher answers in 0.09 s when the browser runs; box_screen_is_exact passes. Co-Authored-By: Claude Opus 5.5 --- box/Dockerfile | 2 +- box/a11y | 13 ++++++++++--- box/browser | 26 +++++++++++++++++++------- src/cloudbox.rs | 2 +- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/box/Dockerfile b/box/Dockerfile index af97a46..5d7db30 100644 --- a/box/Dockerfile +++ b/box/Dockerfile @@ -27,6 +27,6 @@ RUN chmod +x /usr/local/bin/anybot-box-start /usr/local/bin/anybot-a11y /usr/loc # Accessibility on for every app, including those started over `docker exec`. ENV DISPLAY=:1 SCREEN_SIZE=1280x800x24 GTK_MODULES=atk-bridge GTK_A11Y=atspi GNOME_ACCESSIBILITY=1 NO_AT_BRIDGE=0 EXPOSE 6080 -LABEL anybot.box.version="4" +LABEL anybot.box.version="5" ENTRYPOINT ["/usr/bin/tini", "--"] CMD ["/usr/local/bin/anybot-box-start"] diff --git a/box/a11y b/box/a11y index 7c63738..9752cd1 100644 --- a/box/a11y +++ b/box/a11y @@ -4,7 +4,8 @@ in screen pixels. Web pages are in it too (Chromium runs with renderer accessibility on). AnyBot numbers them on the bot's picture; a click by number lands on the centre. -Adapted from XBot's box/box-a11y. Usage: a11y [--limit N] [--budget SECONDS] +Adapted from XBot's box/box-a11y (which skips anything not "showing"; Chromium can say that of +its whole window). Usage: a11y [--limit N] [--budget SECONDS] Exit 2 when AT-SPI itself is unreachable. """ import json @@ -92,10 +93,16 @@ def main(): except Exception: # noqa: BLE001 return st = state(node) - if depth > 1 and (st is None or not (st.contains(Atspi.StateType.SHOWING) and st.contains(Atspi.StateType.VISIBLE))): + showing = bool(st and st.contains(Atspi.StateType.SHOWING)) + visible = bool(st and st.contains(Atspi.StateType.VISIBLE)) + # A page that isn't showing is a tab in the background: none of it is on the screen. + if role in ("document web", "document frame") and not showing: return + # Otherwise "showing" isn't trusted: Chromium can call its whole window not showing (it + # took itself for minimized under this window manager) while its page is on the screen. + # Visible with a place on the screen is what counts; containers are walked through. box = extents(node) - if depth > 1 and (box is None or not onscreen(box)): + if depth > 1 and (not visible or box is None or not onscreen(box)): return try: name = (node.get_name() or "").strip() diff --git a/box/browser b/box/browser index b68077d..d78dee9 100644 --- a/box/browser +++ b/box/browser @@ -11,8 +11,18 @@ if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" && -r /tmp/anybot-dbus.env ]]; then source /tmp/anybot-dbus.env export DBUS_SESSION_BUS_ADDRESS fi -PROFILE=/home/bot/.config/anybot-browser -mkdir -p "$PROFILE" +# One profile. The box used to have two: Chromium's own (~/.config/chromium, what its menu entry +# opened, where a person logged in by hand) and AnyBot's (~/.config/anybot-browser, what the +# browser tools opened), so a site could be logged in in one and not the other. Chromium's own +# is the one now (the other stays as it was); a box that only has AnyBot's keeps that. +PROFILE=/home/bot/.config/anybot-profile +if [[ ! -e "$PROFILE" ]]; then + if [[ -d /home/bot/.config/chromium/Default || ! -d /home/bot/.config/anybot-browser/Default ]]; then + mkdir -p /home/bot/.config/chromium && ln -s /home/bot/.config/chromium "$PROFILE" + else + ln -s /home/bot/.config/anybot-browser "$PROFILE" + fi +fi FLAGS=(--no-sandbox --test-type --user-data-dir="$PROFILE" --remote-debugging-address=127.0.0.1 --remote-debugging-port=9222 --remote-allow-origins=* --force-renderer-accessibility --disable-blink-features=AutomationControlled --disable-dev-shm-usage --disable-gpu --use-angle=swiftshader --enable-unsafe-swiftshader --password-store=basic @@ -22,14 +32,16 @@ FLAGS=(--no-sandbox --test-type --user-data-dir="$PROFILE" --remote-debugging-ad # over the profile ("in use"): one at a time until the first is listening. exec 9>/tmp/.anybot-browser.lock flock -w 30 9 || true -if pgrep -u bot -f -- "--user-data-dir=$PROFILE" >/dev/null; then +if pgrep -u bot -f -- "--user-data-dir=$PROFILE" >/dev/null || pgrep -u bot -f -- "--user-data-dir=$(readlink -f "$PROFILE")" >/dev/null; then flock -u 9 - exec chromium "${FLAGS[@]}" "$@" + exec chromium "${FLAGS[@]}" "$@" 9>&- fi # A profile from before this box (a new container) still names the old one in its lock. -rm -f "$PROFILE/SingletonLock" "$PROFILE/SingletonSocket" "$PROFILE/SingletonCookie" -setsid chromium "${FLAGS[@]}" "${@:-about:blank}" >/tmp/anybot-browser.log 2>&1 < /dev/null & -for _ in $(seq 1 60); do +rm -f "$PROFILE"/SingletonLock "$PROFILE"/SingletonSocket "$PROFILE"/SingletonCookie +# (The lock's fd is not handed to the browser: it would hold the lock for its whole life, and the +# next start would wait on it.) +setsid chromium "${FLAGS[@]}" "${@:-about:blank}" >/tmp/anybot-browser.log 2>&1 < /dev/null 9>&- & +for _ in $(seq 1 100); do python3 -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:9222/json/version', timeout=1).close()" 2>/dev/null && break sleep 0.25 done diff --git a/src/cloudbox.rs b/src/cloudbox.rs index 3164f0f..0337474 100644 --- a/src/cloudbox.rs +++ b/src/cloudbox.rs @@ -2003,7 +2003,7 @@ mod tests { assert!(!in_schedule_window("03:00", t("02:59"))); assert!(in_schedule_window("23:30", t("00:10"))); assert!(!in_schedule_window("bad", t("00:10"))); - assert_eq!(wanted_version(), "4"); + assert_eq!(wanted_version(), "5"); } #[test]