Conversation
README.md / README_zh-TW.md / README_zh-CN.md and the corresponding Sphinx new-features pages now cover: - Quick Connect screen as the AnyDesk-style default landing view - parse_remote_desktop_target() coordinator - on_pending_viewer approval callback + view-only mode - ip_allowlist (CIDR + exact IPs) - single_use_tokens (one-shot share codes) - RFC 6238 TOTP 2FA (totp_secret host param + viewer totp_code) - list_host_monitors() + monitor_index - Remote cursor overlay + enable_cursor_broadcast - broadcast_chat / send_chat / on_chat (Phase 5.2) - broadcast_viewer_cursor / on_viewer_cursor (Phase 5.1) - mouse_move_relative input action - Motion-aware capture (frame-hash dedup) - viewer.stats() rolling FPS/kbps snapshot - JpegSequenceRecorder (no PyAV) + RelayServer - host_service install/uninstall CLI per platform Sphinx RST parses without new warnings on top of the existing pre-existing role / Chinese-punctuation noise.
…e, WoL Six new subsystems landing in one batch: - 6.1 Wake-on-LAN GUI: right-click a Quick Connect recent entry to send a magic packet; MAC + broadcast persisted in AddressBook. - 6.2 EncryptedJpegSequenceRecorder: AES-256-GCM per-frame with a HMAC-SHA256-signed manifest; key derivation from passphrase (PBKDF2 600k SHA-256) and round-trip + tamper-detection tests. - 6.3 Visual regression framework: PIL-only golden image compare with per-pixel threshold, region masks, and pytest-friendly DiffResult.write_diff(). - 6.4 Semantic recording enrichment: walk a recording, ask the accessibility tree for the smallest element covering each click, attach role/name/app_name as an `anchor` payload. - 6.6 Resume tokens: in-memory TTL store; host ships a one-shot token inside AUTH_OK, viewer reconnects with it and skips the approval popup while keeping its saved permission (view-only preserves across the reconnect). - 6.7 Replay-anywhere: at replay, use the anchor to look up the current element via the accessibility tree, rewrite x/y, fall back to the recorded coordinates when lookup fails. 41 new headless tests, ruff clean, complexity all ≤ 10.
Headless side of Phase 6.5: a per-host base64 PNG screenshot fetched
in parallel via the existing /screenshot REST endpoint. Returns
{label: png_bytes} (None for hosts that errored), driven by the same
ThreadPoolExecutor as poll_all and broadcast_execute.
The GUI grid that consumes this is the next step — left as a
follow-up so the headless probe + tests can land independently.
6 new headless tests covering happy path, HTTP errors, malformed
responses, bad base64, label filtering, and the no-hosts case.
The headless ``AdminConsoleClient.fetch_thumbnails`` shipped in the previous commit; this binds it to the GUI: - Below the host table, a QListWidget in IconMode shows one tile per registered host (label + 200x150 scaled PNG icon). - A QSpinBox controls the auto-poll interval (0 disables the timer); defaults to 10 s, matched by a manual "Refresh now" button. - A QThread-backed ``_ThumbnailWorker`` keeps the network fetch off the GUI thread; the existing _PollWorker stays untouched. i18n strings for the three new controls landed in en / zh-TW / zh-CN / ja. 7 new headless tests, all green.
CodecProvider abstraction over the host capture loop:
- JpegPassthrough (default): emits the JPEG verbatim with NO tag
prefix, so existing clients see a byte-identical FRAME wire format.
- H264CodecProvider: libx264 via PyAV from the [webrtc] extra.
Constructor raises ImportError with the install hint when PyAV is
missing, so a stock install fails loudly instead of silently
reverting to JPEG.
Codec is advertised in the AUTH_OK JSON ("codec": "jpeg"|"h264"|
"hevc"). Viewer exposes ``viewer.negotiated_codec`` so the GUI can
pick the right decoder. Non-JPEG packets are prefixed with a 1-byte
codec tag (0x01/0x02/0x03) inside the FRAME body.
9 new headless tests (1 PyAV-dependent test skips when the extra
isn't installed).
Phase 6.9 (subset — "USB info passthrough"):
- New MessageType.USB_LIST_REQUEST / USB_LIST_RESPONSE.
- Viewer.list_remote_usb_devices(timeout=5.0) sends the request,
blocks for the matching reply, returns the host's UsbDevice list
as a dict. Times out cleanly when the host is silent; raises
RuntimeError when the viewer isn't connected.
- Host runs list_usb_devices() (the existing cross-platform
enumerator) and ships {backend, devices} JSON back.
Full virtual-device redirection (kernel-level USB injection on the
viewer side) is deliberately out of scope — it's a quarter of
driver-work per platform, not a phase.
Phase 6.10 (scaffold):
- autocontrol-lsp/ sibling directory laid out as a future PyPI +
VSCode extension publish target. Python LSP server is stdlib
JSON-RPC over stdio (no external LSP framework dep). Provides:
* initialize — capability advertisement
* completion — every AC_* command discovered from the
live executor dispatch table
* hover — docstring lookup for a known command
- VSCode side: package.json + tsconfig.json + extension.ts that
launches "python -m autocontrol_lsp.server" via
vscode-languageclient and routes JSON/JSONC documents through it.
- ``python -m autocontrol_lsp.server`` runs from this repo via
``sys.path`` manipulation in tests; production install via
``pip install ./autocontrol-lsp``.
16 new headless tests across both subsystems; ruff clean,
complexity all ≤ 10.
…ebRunner
Six subsystems landing in one batch:
- 7.1 docker/{Dockerfile,entrypoint.sh,docker-compose.yml,.dockerignore}
— Linux headless host on Xvfb. Three compose services: REST API,
Remote Desktop TCP host, signaling server, all behind the same
autocontrol:latest image.
- 7.2 utils/state_machine/: declarative FSM driver with on_enter
actions, guards (if_var_eq / predicate / after), max_steps +
global_timeout budgets, custom guard_eval hook.
- 7.8 utils/tool_use_schema/: introspect every AC_* command's signature
and emit Anthropic ``tools`` or OpenAI ``functions`` schema;
run_tool_call() bridges the model's chosen tool through the
existing executor.
- 7.9 utils/agent/: closed-loop Computer-Use driver. Observe → plan →
act → loop, pluggable backend (FakeAgentBackend ships for tests),
AgentBudget(max_steps, wall_seconds) safety net, tool errors
surfaced to the next turn without aborting.
- 7.10 utils/semantic_recording/self_healing.py: when a replay step
fails, ask the VLM to re-locate the anchored element from its
a11y role/name and retry up to max_retries times. Builds on
Phase 6.7 relocate_recording.
- 7.7 utils/webrunner_bridge/ + AC_web_* commands: AutoControl JSON
can now call into je_web_runner (~440 WR_* commands for Selenium
/ Playwright). AC_web_run / AC_web_run_actions / AC_web_available
/ AC_web_list_commands.
66 new headless tests (Docker artifacts, FSM, tool-use schema,
self-healing, agent loop, WebRunner bridge), ruff clean, complexity
all ≤ 10.
Four ops-focused subsystems closing out Phase 7: - 7.3 ResourceProfiler — psutil-backed CPU / RSS / FPS sampler tagged by AC_* action name. Speedscope-format ``sampled`` JSON export so the same data drops straight into https://www.speedscope.app/ for flame-graph view. Degrades to FPS-only when psutil is missing. - 7.4 ConfigSyncClient + ConfigBucket — last-write-wins merge with a ConflictRecord audit trail; HTTP client + bucket schema for the signaling-server-backed ``/config/<user_id>`` endpoint. Sections carry per-entry ``last_modified`` timestamps so deterministic merge is independent of clock skew on either side beyond the entry stamp. - 7.5 RBAC users + capability matrix — three baked-in roles (viewer / operator / admin), tokens stored as SHA-256 hashes with a fixed pepper, constant-time authenticate(), rotate_token(), set_role(). JSON-backed at ~/.je_auto_control/users.json with 600 chmod where the OS supports it. - 7.6 TLS ACME helpers — KeyMaterial / generate_csr / RenewalScheduler / HttpChallengeServer. Full ACME v2 wire protocol is intentionally delegated to the standard ``certbot`` binary via run_certbot(); this PR adds the scheduling + HTTP-01 + key-management plumbing around it so operators don't reinvent that part per deployment. 70 new headless tests, ruff clean, complexity all ≤ 10.
Phase 8.1 — complete ACME v2 (RFC 8555) client, no certbot:
je_auto_control/utils/acme_v2/
jws.py — RS256 JWS signer, RFC 7638 JWK thumbprint,
key_authorization, CSR-to-base64url helper.
client.py — AcmeClient with full state machine: directory →
new-nonce → new-account → new-order → fetch-authz
→ respond-to-challenge → poll-authz → finalize →
poll-order → download-cert. Pairs with the Phase
7.6 HttpChallengeServer for the HTTP-01 publish.
The client signs with the cryptography library; tests verify
signature round-trip with RSA pkcs1v15+SHA256 against the public
key. 15 headless tests including a full request_certificate flow
driven by a scripted ACME-shaped stub server.
Phase 8.2 — USB/IP wire protocol, server side:
je_auto_control/utils/usbip/
protocol.py — pack/unpack for OP_REQ_DEVLIST / OP_REP_DEVLIST /
OP_REQ_IMPORT / OP_REP_IMPORT and the URB-mode
USBIP_CMD_SUBMIT / USBIP_RET_SUBMIT messages.
All numbers network byte order, busid + path
null-padded to their fixed kernel sizes.
backend.py — UrbBackend abstract + FakeUrbBackend for tests.
Production backends (libusb / WinUSB) subclass
and forward URBs to a real device.
server.py — thread-per-connection TCP server on :3240.
Handles devlist + import + URB stream, routes
every CMD_SUBMIT through the backend.
vhci-hcd (Linux mainline) and usbip-win (cezanne/usbip-win) are
ready-made clients for this server. macOS clients are not
supported — Apple no longer issues the kernel-extension entitlement
needed for third-party USB drivers.
17 headless tests covering the wire format round-trips, OP error
paths, and a full TCP end-to-end where a scripted backend answers
an IN URB through the server.
32 new tests total; ruff clean, complexity all ≤ 10.
…9.5) Three subsystems landing in one batch: - 9.1 k8s/helm/autocontrol/: Helm chart for the three Docker services (REST / Remote / Signaling). One Chart.yaml, one shared values.yaml, per-service Deployment + Service templates, optional Ingress for the REST API. Shared Secret holds the AC_TOKEN; the chart refuses to install when ``auth.token`` is empty so production deployments must plug in a sealed secret / external-secrets. - 9.2 utils/action_lint/ + .github/workflows/action-json-lint.yml: JSON-Schema generator (draft 2020-12) that walks the executor's AC_* dispatch table; ActionLinter checks every item for unknown commands, missing required params (errors), and unknown params (warnings). ``python -m je_auto_control.utils.action_lint`` is the CLI; the reusable GitHub Actions workflow is one ``workflow_call`` away for downstream repos. - 9.5 utils/agent/backends/: AnthropicAgentBackend + OpenAIAgentBackend that turn the Phase 7.9 AgentLoop into a real Computer-Use system. Both lazy-import their SDKs and surface clear AgentBackendError messages when the SDK is missing. Screenshots ride along as image_url / base64 image attachments so the model sees the actual pixels. Tool results from previous turns are threaded back via tool_use_id / tool_call_id matching so the model knows which call the response belongs to. 45 new headless tests covering the chart manifests, the linter + schema, and the agent backends (with stub vendor clients that record the wire payloads). Ruff clean, complexity all ≤ 10.
…9.7) Four subsystems landing in one commit, all sharing a "headless first, backend optional" shape so the wider test suite keeps passing on a machine with none of the optional dependencies installed. - 9.3 ocr/backends/paddleocr_backend.py: PaddleOCRBackend wired into the existing tesseract/easyocr factory. Best-in-class CJK accuracy via PP-OCR; SDK lazy-imported so import-time cost stays at zero on machines that haven't installed paddlepaddle (a ~150 MB wheel). - 9.6 usbip/libusb_backend.py: LibUsbBackend wraps PyUSB to actually forward URBs through the Phase 8.2 UsbIpServer. Control / bulk / interrupt transfers go through ctrl_transfer / read / write with the standard kernel-style errno mapping (-110 timeout, -22 inval, -71 protocol). Isochronous deferred because vhci-hcd schedules iso locally anyway. - 9.7 android/adb_client.py + AC_android_* commands: ADB-based mobile backend (tap, swipe, key_event, text, screencap, shell, list_devices). Same action JSON file can mix desktop and Android steps. ADB binary must be on PATH; the constructor raises AdbNotAvailable with a clear install hint otherwise. iOS deliberately not in this phase — needs a paid Apple Developer cert to sideload WebDriverAgent. - 9.4 time_travel/player.py: TimelinePlayer joins a JpegSequenceRecorder manifest with an actions.jsonl action log. at_step / at_relative_time / actions_in_window let a scrubber walk the recording forwards AND backwards. Foundation for the GUI scrubber widget; the headless player is what the GUI binds to. 57 new headless tests across the four packages; ruff clean, complexity all ≤ 10.
A production deployment needs to answer "is this thing healthy?" and "where did that 30-second click hang?" without diffing log files. This adds a stdlib-only Counter / Gauge / Histogram registry rendered as Prometheus text, plus a no-op tracer that upgrades to real OTel spans when the SDK is importable. Hot-path instrumentation in the action executor and agent loop emits call counts, durations, and span trees out of the box -- no per-script wiring required. A tiny ThreadingHTTPServer exposes /metrics so any Grafana scrape job works against an unmodified install.
The in-flight refactor extracted Tesseract's OCR path into a backend protocol so EasyOCR (and later PaddleOCR) can share the engine's public surface. The new files were left untracked and the parser tests still imported the old _parse_matches helper, so test_ocr_engine.py broke on collection. This commit lands the missing backend files, rewrites the tests against the new backend protocol (injecting a stub backend so no real binary or neural model is needed), and trims the now-unused OCRBackend imports flagged by ruff.
…dows Same pattern as the recent webhook_server fix: when _send_json returns a 4xx response we now drain any unread request body before the socket closes. Without the drain Windows TCP turns close-with-unread-bytes into RST, and the client surfaces WinError 10053 *before* it can read the response — intermittently flaking test_authentication_rejects_missing_bearer when other tests run alongside it. Body drain is capped at 4x the body limit so a hostile Content-Length can't make us spin.
- examples/: seven copy-pasteable scripts covering screenshot+click,
OCR text matching, the headless scheduler, remote desktop host/viewer,
the agent loop, the Prometheus exporter + tracer, and JSON action
files. Each script uses only the package facade so it stays valid
as the internal layout changes.
- docs/source/{Eng,Zh}/doc/ocr_backends/: backend selection order,
language-code translation table, install paths and lazy-download
notes for tesseract / easyocr / paddleocr, plus a diagnostics
snippet for checking which backend is reachable.
- docs/source/{Eng,Zh}/doc/observability/: the new /metrics endpoint,
the built-in counter/histogram inventory, the traced decorator, and
a production deployment recipe with Grafana alerting rules.
- je_auto_control/__init__.py: re-export the observability primitives
(MetricCounter / Gauge / Histogram / Tracer / traced /
PrometheusExporter / default_*) so users can wire metrics through
the public facade per the headless-first rule.
… ops
Adds ten more example scripts so the directory now covers every major
feature of the package:
- 08_record_and_replay — record real input, save JSON, replay later
- 09_action_variables — ${name} placeholders + execute_action_with_vars
- 10_window_management — list/find/focus/wait_for_window
- 11_hotkey_daemon — bind a global hotkey to a JSON action file
- 12_image_trigger — auto-run a script when a template appears
- 13_html_report — generate a styled HTML report from the recorder
- 14_mcp_stdio_server — start the MCP stdio bridge for Claude Desktop
- 15_rest_api — start the REST API + dispatch over HTTP
- 16_secrets — Fernet-encrypted credentials vault
- 17_plugin_loading — load extra AC_* commands from a plugin file
Also fixes a long-standing bug in generate_html_report.generate_html:
the inline CSS in the HTML template contains literal { } which broke
str.format(); switching to a literal replace keeps the stylesheet
readable without doubling every brace. The earlier examples (03, 07)
plus the new ones were updated to the canonical [name, params] action
shape — the dict form I had used was rejected by the schema validator.
Five SonarCloud findings cleared, two Bandit B104 suppressions tightened,
one stray ruff F401 removed.
- pyproject.toml (text:S8565) — add uv.lock so transitive versions are
reproducible across CI / contributor machines (207 packages locked).
- host_service.py:403 (python:S8572) — replace
``logging.error("...: %r", error)`` with ``logging.exception(...)`` so
the traceback lands in the daemon's log.
- webrtc_transport.py:323 (python:S7483) — fix the NOSONAR placement
syntax. The em-dash separator confused Sonar's parser; explicit rule
key + ``# reason:`` clause makes the suppression registered. The
underlying use of ``asyncio.wait_for(timeout=...)`` stays because
``asyncio.timeout()`` only landed in 3.11 and the project supports 3.10.
- web_viewer/index.html:1224 (javascript:S7785) — same fix as above for
the service-worker registration: this file is a plain ``<script>`` so
top-level await is not legal.
- auto_control_exception_test.py:17 (python:S8518) — iterate the value
directly out of the enumerate(); drop the now-unused index.
Bandit B104 (hardcoded 0.0.0.0 bind):
- tls_acme/challenge.py and usbip/server.py — both already had
``# noqa: S104 # NOSONAR python:S5332 # reason: ...`` justifying the
external reachability requirement, but Bandit needs its own
``# nosec B104`` token on the same line. Added without removing the
existing context.
ruff F401:
- visual_regression/compare.py — drop the unused ``List`` import.
Three README languages updated to reflect what landed this session: - examples/: each README now has a short pointer to the 17-script directory at the top of Quick Start, before the API-snippet sections. - OCR backends: the OCR feature bullet and the OCR Quick Start section mention the three pluggable backends (Tesseract / EasyOCR / PaddleOCR), the AUTOCONTROL_OCR_BACKEND env var, and link to the per-language backend docs. - Observability: new Quick Start section + a feature bullet covering the Prometheus /metrics exporter and the OpenTelemetry-compatible tracer that AutoControl ships with stdlib-only. - uv.lock: Development > Setting Up shows how to use the committed lockfile (uv sync / uv lock --upgrade). No changes to docs/source/ — the new OCR-backends and observability pages were already wired into both language indexes earlier in the session.
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| ErrorProne | 1 critical 3 high |
| Security | 3 high 3 critical 2 medium |
🟢 Metrics 1857 complexity · 46 duplication
Metric Results Complexity 1857 Duplication 46
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
# Conflicts: # .github/workflows/quality.yml # dev_requirements.txt
Closes the 58 issues + 9 hotspots flagged on PR #194. Categorised: BLOCKERs (real fixes): - .github/workflows/action-json-lint.yml: pipe ``inputs.autocontrol_ref`` and ``inputs.files`` through env vars so script injection via workflow_call dispatch is no longer possible (S7630, 3 occurrences). CRITICALs (real refactors): - agent_loop._run_loop: extracted ``_take_one_step`` + ``_dispatch_tool`` so cognitive complexity drops from 17 → ≤10 (S3776). - action_lint.linter._check_required: extracted ``_scan_signature`` helper, complexity 19 → ≤10 (S3776). - test_acme_v2._install_stub: extracted ``_stub_response`` URL table, complexity 20 → ≤10 (S3776). BUGs (test correctness): - float-equality across test_observability, test_time_travel, test_config_sync, test_resource_profiler, test_visual_regression switched to ``pytest.approx`` (S1244, 13 occurrences). K8s (security defaults): - All three deployments now set ``automountServiceAccountToken: false`` (S6865), use ``Chart.AppVersion`` as the image-tag default instead of ``latest`` (S6596), and the resources block now requests ``ephemeral-storage`` alongside cpu/memory (S6897). Docker: - Dockerfile drops root after the apt + pip layers — adds a system ``autocontrol`` user (uid 1001) and ``USER autocontrol`` directive (S6471 hotspot). Cleanups: - Removed redundant ``TimeoutError`` from an exception tuple in admin_client (S5713) — already a subclass of OSError. - Dropped the unused ``list()`` wrapper + ``version`` local in usbip/server (S7504 / S1481). - Reformatted multi-line struct-format comments in usbip/protocol so Sonar stops misreading them as commented-out code (S125 ×2). - Renamed unused locals to ``_`` in test_usbip, test_admin_console_thumbnails_gui (S1481 ×3). - Removed the always-shadowed ``text`` assignment in test_observability (S1854). - Documented the empty ``encode(None)`` flush loop in video_codec so Sonar stops flagging it as an empty block (S108). - Wrapped the LSP ``run`` loop in try/except + transport-error path so it no longer always returns the same value (S3516). - Split the ``isinstance(...) and len(...) > 0`` asserts in test_acme_v2 into separate statements (S2589 ×3). NOSONAR with documented reasons (legitimate use that Sonar misreads): - USB 2.0 spec field names ``bmRequestType`` / ``bRequest`` / ``wValue`` / ``wIndex`` / ``wLength`` and the dataclass interface fields ``bInterfaceClass`` etc.: snake-case rename would diverge from libusb and the USB spec (S117 / S116, 8 markers total). - ``PaddleOCR`` local var: matches the upstream library class name. - Localhost HTTP in ``examples/15_rest_api.py`` (×2) plus the two admin-thumbnail test fixtures (×4): demo / fixture URLs, no real network exposure (S5332 hotspots). - The fake RBAC test token in test_rbac (S6418), the example-only vault passphrase in examples/16_secrets (S2068), and the ``/tmp`` literals embedded in JSON payloads / fake echoes in test_action_lint and test_tool_use_schema (S5443 ×3 / S2083 ×1). Out of scope for this commit: - autocontrol-lsp/vscode/package.json missing package-lock.json (text:S8564) — scaffold not yet published; lockfile will be added in the same change that wires the publish CI.
Sonar (3 left after the previous pass): - LSP run() complexity dropped from 16→<10 by extracting _build_reply() and collapsing the request==None / method==exit guards into one branch (python:S3776). - Dockerfile: merged the chmod + user-creation RUN layers so docker:S7031 is satisfied without extra image layers. - test_acme_v2: chained endswith → tuple form (python:S8513). Codacy: - Real bug: connection_screen called send_magic_packet(broadcast=...) but the wake_on_lan signature is broadcast_address=. Fixed. - Real bug: examples 03/11/12 called default_scheduler() / default_hotkey_daemon() / default_trigger_engine() but those are module-level instances, not factories. Dropped the parens. - autocontrol-lsp/vscode/package-lock.json: generated via ``npm install --package-lock-only`` so dependencies are reproducible (text:S8564 / Codacy lockfile rule). False positives — tool-specific suppressions with reason: - Semgrep dangerous-subprocess-use-audit in adb_client.py + tls_acme/ challenge.py + test_android_adb.py (Bandit B603 already justifies; the argv list is hard-coded / shutil.which-resolved). - Semgrep request-data-write in usbip/libusb_backend.py (rule expects a Django filesystem path; this is libusb's bulk-out endpoint write). - Semgrep openai.import-without-guardrails in agent/backends/openai.py (Guardrails is an unrelated content-filter SDK; safety is handled at the action-executor allowlist + audit layer). - Bandit B105 hardcoded-password in examples/16_secrets.py + B310 url-open in examples/06_observability.py + examples/15_rest_api.py (demo strings + loopback URLs). - Pylint W0622 ``format`` shadow in two ``log_message`` overrides (the parameter name comes from BaseHTTPRequestHandler — we can't rename it without breaking the protocol).
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Merges 20 commits from
devintomain. Highlights:RESUME_OK), Wake-on-LAN viewer flow, pluggable video codecs (PyAV-backed H.264/HEVC).AgentLoopwithAgentBackendprotocol, self-healing replay, WebRunner playwright bridge.Counter/Gauge/Histogramregistry, OpenTelemetry-compatible tracer with no-op fallback,/metricsHTTP exporter. Executor + agent loop instrumented automatically.examples/: 17 copy-pasteable scripts covering every major surface (screenshot+click, OCR, scheduler, remote desktop, agent loop, observability, recording/replay, runtime variables, windows, hotkeys, triggers, HTML report, MCP stdio, REST API, secrets vault, plugin loading).Eng/andZh/, READMEs updated in all three languages.uv.lockfor reproducible installs.Stats
Test plan
.github/workflows/dev.yml,quality.yml,action-json-lint.yml)python -m pytest test/unit_test/headless/→ 1149 passed, 13 skipped (verified locally)ruff check je_auto_control/clean (verified locally)docs/sourcebuilds with no new warnings (verified locally)import je_auto_controldoes not pull in PySide6 (verified locally — facade stays Qt-free)