Skip to content

Commit 0d2c1e4

Browse files
committed
Give a test run its own home, and split six themes out of _handlers
The suite wrote the account's real ~/.je_auto_control: the audit chain, address book, quarantine list, run history and 419 error screenshots. test/conftest.py now hands each run a temporary home, which only works for paths resolved when they are used -- nine were bound while their module was imported, and the pytest11 plugin imports the package before any conftest. Those are functions now, the four stores built during the import keep only an explicit path, and HistoryStore takes a resolver. An AST scan and a subprocess that searches every module global and object attribute for the home path keep it that way. The same change re-measures the map, which is why the MCP handler split rides along: six themes (input, screen, system, runs, scheduling, remote) that nothing else in the file reaches moved verbatim into their own modules, taking _handlers.py from 4,389 to 2,992 lines.
1 parent dfb0e5e commit 0d2c1e4

31 files changed

Lines changed: 2015 additions & 1671 deletions

‎CHANGELOG.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@ it shipped into a `## [x.y.z] - date` section of their own; the tag's
2424

2525
### Changed
2626

27+
- **Per-user state paths are resolved when used, not at import.** Nine
28+
defaults under `~/.je_auto_control/` (the action signing and encryption
29+
keys, the host fingerprint and known hosts, the host service config, the
30+
WebRTC inbox, and the A/B locator, cost and self-healing logs) were fixed
31+
when their module was imported, so setting `HOME` / `USERPROFILE`
32+
afterwards had no effect on them. The class attributes
33+
`ABStore.DEFAULT_PATH`, `CostStore.DEFAULT_PATH` and
34+
`HealEventLog.DEFAULT_PATH` are replaced by the functions
35+
`default_stats_path()`, `default_cost_log_path()` and
36+
`default_heal_log_path()` in the same modules. Migration: call the
37+
function where the attribute was read.
38+
2739
- **The log file moved out of the current directory.** Importing the package
2840
opened `AutoControlGUI.log` relative to the cwd, so every process that
2941
imported it — including every pytest run on a machine where it is installed,

‎Progress.md‎

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
| 檔案 | 行數 | 為何還沒拆 |
2626
| --- | ---: | --- |
27-
| `utils/mcp_server/tools/_handlers.py` | 4,389 | 676 個 MCP 工具的處理函式本體(QA 主題的 34 個已拆到 `_handlers_qa.py`)。與 `_factories.py`(表)不同,這裡是邏輯,應該依主題繼續拆(input/screen/window/a11y/agent…)。拆點不完全乾淨:`# === Semantic locators` 一節(約 2,400 行)與 `# === WebRunner bridge` 一節早已混進不相干的 adapter,要先按主題重排再切。 |
27+
| `utils/mcp_server/tools/_handlers.py` | 2,992 | 676 個 MCP 工具的處理函式本體。邊界乾淨的七個主題已經拆出去(`_handlers_qa`/`_input`/`_screen`/`_system`/`_runs`/`_scheduling`/`_remote`)。剩下的是 `# === Semantic locators`(2,437 行、421 個 adapter,長年被當成雜物間)與 `# === WebRunner bridge`(542)兩節,**要先按主題重排才能再切**——照現有標題切只會把不相干的東西一起搬走。 |
2828
| `gui/remote_desktop/webrtc_panel.py` | 2,545 | 單一 Qt 面板,但已含連線、監視器選擇、頻寬自適應、麥克風、錄影五組互動狀態。應拆成 panel + 各控制器。 |
2929
| `utils/accessibility/backends/windows_backend.py` | 923 | 已拆出 `windows_query.py`(170)與 `windows_state.py`(98)。剩下的是同一套 UIA COM 生命週期管理,再拆會把 `CoInitialize`/介面釋放的配對邏輯切散。**2026-08-24 從 918 長到 923**:見下面的說明。 |
3030

@@ -172,22 +172,6 @@ DEBUG 都會流到 root 的 handler(用了 `basicConfig` 的程式會被灌爆
172172

173173
---
174174

175-
## 測試會寫進真正的 `~/.je_auto_control/`
176-
177-
`TODO` — `test/conftest.py` 只把記錄檔導到暫存目錄,其他每使用者狀態沒有
178-
179-
2026-09-23 跑整套測試的時段,家目錄的 `audit.db`(遠端桌面的稽核鏈)、`address_book.json`、
180-
`quarantine.json`、`run_history.sqlite` 都被改過,`artifacts/` 累積了 419 張錯誤截圖(日期
181-
對得上每一天的開發測試)。這些預設路徑大多在**呼叫時**才算 `Path.home()`(`audit_log.py:41`、
182-
`address_book.py:31`、`quarantine/store.py:39`、`run_history/history_store.py:81`、
183-
`run_history/artifact_manager.py:20`),所以在 `test/conftest.py` 把 `HOME`/`USERPROFILE`
184-
指到每次一個的暫存目錄就擋得住;**import 時就算好的**常數(`ab_locator/store.py:70`、
185-
`cost_telemetry/store.py:57`、`action_signing/{cipher,signer}.py`、`remote_desktop/fingerprint.py`、
186-
`host_service.py:36`、`webrtc_files.py:34`)擋不住,因為 pytest11 外掛比 conftest 早 import
187-
整個套件——要先確認測試有沒有碰到它們,碰到就改成呼叫時才算。
188-
189-
---
190-
191175
## libei 的 `ei_unref` 在半開交握上會 SIGSEGV
192176

193177
`BLOCKED` — 上游(libei 1.3.901)

‎architecture.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ wrapper/auto_control_record.record → OS listener (e.g. windows/record/win32_in
9898
3. Register the `AC_*` name in `Executor.event_dict` (`utils/executor/action_executor.py`); commands with nested
9999
action bodies go in `BLOCK_COMMANDS` (`utils/executor/flow_control.py`).
100100
4. Describe its parameters in `gui/script_builder/command_schema.py` (Script Builder form).
101-
5. Optional MCP tool: factory in `utils/mcp_server/tools/_factories.py`, adapter in `utils/mcp_server/tools/_handlers.py` (QA-theme adapters — assertions, suites, reports — in `_handlers_qa.py`).
101+
5. Optional MCP tool: factory in `utils/mcp_server/tools/_factories.py`, adapter in the themed handler module —
102+
`_handlers_input.py`, `_handlers_screen.py`, `_handlers_system.py`, `_handlers_runs.py`,
103+
`_handlers_scheduling.py`, `_handlers_remote.py`, `_handlers_qa.py`, or `_handlers.py` for the locator and
104+
WebRunner-bridge themes still in it.
102105
6. GUI: thin widget in `gui/`, registered in `gui/main_widget.py` (`_add_tab`) with commands exposed through
103106
`menu_actions()`; strings in every `gui/language_wrapper/*.py` catalogue.
104107
7. Headless test in `test/unit_test/headless/`.
@@ -148,6 +151,9 @@ Moving that WebRunner module breaks the bridge.
148151
only when a process opens it, and swapped for `os.devnull` with one `RuntimeWarning` when it cannot be opened.
149152
Consumers that must keep the log out of a shared file (a test suite) set the variable before importing;
150153
changing the working directory no longer redirects it.
154+
- No module reads the home directory at import; every `~/.je_auto_control/` path is resolved when used, so a
155+
consumer can redirect `HOME` / `USERPROFILE` after the import (this repo's `test/conftest.py` gives each test run
156+
a temporary home). `test/unit_test/headless/test_state_paths_follow_home.py` scans the package for violations.
151157

152158
**De-facto public:** `docs/API_LIFECYCLE.md` calls `je_auto_control.utils.*` internal, but the internal paths in the
153159
table above are used by sibling repos; treat renaming or removing them as a breaking change and check the consumers

‎architecture_explore.md‎

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ iOS(WebDriverAgent)。核心能力是滑鼠/鍵盤控制、影像辨識、
1919

2020
| 指標 | 數值 |
2121
| --- | ---: |
22-
| Python 模組總數(含周邊子專案) | 1,033 |
23-
| 程式碼總行數 | 141,752 |
22+
| Python 模組總數(含周邊子專案) | 1,039 |
23+
| 程式碼總行數 | 141,882 |
2424
| `je_auto_control/utils/` 子套件數 | 310 |
2525
| `AC_*` 動作指令數(`known_commands()` 實測) | 773 |
2626
| 套件門面 `__all__` 公開名稱數 | 1,238 |
@@ -268,12 +268,12 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。
268268

269269
### 5.4.1 執行引擎與腳本資產
270270

271-
> 24 個套件、約 12,979 行。
271+
> 24 個套件、約 12,991 行。
272272
273273
| 模組 | 行數 | 職責 |
274274
| --- | ---: | --- |
275275
| `utils/action_lint/` | 328 | action 檔 linter 與 JSON Schema 產生器(CI 用 `python -m` 進入點) |
276-
| `utils/action_signing/` | 248 | action 檔 HMAC-SHA256 簽章與 Fernet 加密,`execute_files` 會強制驗簽 |
276+
| `utils/action_signing/` | 260 | action 檔 HMAC-SHA256 簽章與 Fernet 加密,`execute_files` 會強制驗簽 |
277277
| `utils/checkpoint/` | 120 | 流程檢查點與續跑,讓長 action list 具持久性 |
278278
| `utils/codegen/` | 158 | 由 action list 產生可執行的 pytest / python / robot 測試碼 |
279279
| `utils/dag/` | 478 | 跨主機 DAG 編排器(圖模型 + runner) |
@@ -367,15 +367,15 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。
367367

368368
### 5.4.5 影像辨識與畫面分析
369369

370-
> 37 個套件、約 5,105 行。
370+
> 37 個套件、約 5,107 行。
371371
372372
| 模組 | 行數 | 職責 |
373373
| --- | ---: | --- |
374374
| `utils/annotate/` | 115 | 截圖標註:畫框、highlight、箭頭、標籤 |
375375
| `utils/barcode/` | 53 | 一維條碼(EAN/UPC)解碼,解碼器可注入 |
376376
| `utils/color_match/` | 105 | 在 HSV 通道上做顏色感知的樣板比對 |
377377
| `utils/color_region/` | 79 | 以顏色定位畫面區域(遮罩 + 連通元件) |
378-
| `utils/color_stats/` | 96 | 區域顏色統計:平均色與主色 |
378+
| `utils/color_stats/` | 98 | 區域顏色統計:平均色與主色 |
379379
| `utils/coordinate_space/` | 84 | 模型網格座標與實體像素之間的座標空間對映 |
380380
| `utils/cv2_utils/` | 637 | OpenCV 基礎層:擷取後端選擇(`screen_grabber`,Pillow/mss 或平台後端)、截圖、樣板比對(走 `grab_logical`,涵蓋所有螢幕)、螢幕錄影、影片錄製、連通元件、影像堆疊的取用口(`optional`,Windows arm64 沒有 wheel 時語意報錯) |
381381
| `utils/edge_lines/` | 120 | 以 Hough 轉換偵測線條/格線/分隔線 |
@@ -460,11 +460,11 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。
460460

461461
### 5.4.8 元素定位、自我修復與智慧等待
462462

463-
> 23 個套件、約 4,014 行。
463+
> 23 個套件、約 4,036 行。
464464
465465
| 模組 | 行數 | 職責 |
466466
| --- | ---: | --- |
467-
| `utils/ab_locator/` | 336 | A/B 定位器框架:同時競速 N 種策略並記錄各自勝率 |
467+
| `utils/ab_locator/` | 347 | A/B 定位器框架:同時競速 N 種策略並記錄各自勝率 |
468468
| `utils/adaptive_timeout/` | 84 | 由觀測到的步驟耗時推導等待逾時,而非硬猜 |
469469
| `utils/anchor_locator/` | 457 | 錨點定位器:以空間關係組合 影像/OCR/VLM/a11y 四種來源 |
470470
| `utils/app_idle/` | 108 | 等應用程式不再忙碌,再驅動下一步 |
@@ -483,14 +483,14 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。
483483
| `utils/observation_delta/` | 103 | token 預算內的觀察差異:兩個 UI 影格之間變了什麼 |
484484
| `utils/screen_state/` | 143 | 語義畫面狀態:快照/差異與結構化畫面描述 |
485485
| `utils/scroll_find/` | 84 | 捲動直到目標影像/文字可見 |
486-
| `utils/self_healing/` | 342 | 自癒定位器:先影像樣板、失敗改用 VLM,並留稽核記錄 |
486+
| `utils/self_healing/` | 353 | 自癒定位器:先影像樣板、失敗改用 VLM,並留稽核記錄 |
487487
| `utils/semantic_recording/` | 423 | 為錄製內容加上語義錨點,支援換機重播與自癒重播 |
488488
| `utils/settle_detector/` | 76 | 以純函式介面判定 UI 是否已靜止 |
489489
| `utils/smart_waits/` | 646 | 智慧等待:以影格差異取代 `time.sleep` |
490490

491491
### 5.4.9 AI / Agent / LLM
492492

493-
> 13 個套件、約 20,663 行。
493+
> 13 個套件、約 20,734 行。
494494
495495
| 模組 | 行數 | 職責 |
496496
| --- | ---: | --- |
@@ -499,11 +499,11 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。
499499
| `utils/agent_memory/` | 153 | agent 的持久化情節記憶(goal → trajectory → outcome) |
500500
| `utils/agent_replay/` | 63 | 可攜的 agent 軌跡追蹤(記錄 observation→action 並重播) |
501501
| `utils/agent_trace/` | 129 | agent 可觀測性:OpenTelemetry GenAI 慣例的 LLM span |
502-
| `utils/cost_telemetry/` | 292 | 每次呼叫的 LLM 成本遙測:token 數 + 估算美金 |
502+
| `utils/cost_telemetry/` | 303 | 每次呼叫的 LLM 成本遙測:token 數 + 估算美金 |
503503
| `utils/cua_action/` | 127 | 標準化 computer-use 動作結構(Anthropic/OpenAI → `AC_*`) |
504504
| `utils/llm/` | 357 | 自然語言 → action list 規劃器 + Anthropic/null 後端 |
505505
| `utils/mcp_registry/` | 92 | MCP registry `server.json` 資訊清單產生(可被發現) |
506-
| `utils/mcp_server/` | 17,371 | **無頭 MCP 伺服器**(16K LOC,預設註冊 676 個工具=657 個 `ac_*` + 19 個別名):stdio + HTTP 傳輸、工具工廠與處理器、資源、prompt、稽核、限流、外掛熱重載 |
506+
| `utils/mcp_server/` | 17,431 | **無頭 MCP 伺服器**(16K LOC,預設註冊 676 個工具=657 個 `ac_*` + 19 個別名):stdio + HTTP 傳輸、工具工廠與處理器、資源、prompt、稽核、限流、外掛熱重載 |
507507
| `utils/tool_use_schema/` | 180 | 把 `AC_*` 指令匯出成 Claude/OpenAI 的 tool-use schema |
508508
| `utils/trajectory_eval/` | 106 | agent 軌跡評估:依評分規準為一次執行打分 |
509509
| `utils/vision/` | 449 | VLM 元素定位器(依描述找元素)+ Anthropic/OpenAI/null 後端 |
@@ -554,7 +554,7 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。
554554

555555
### 5.4.12 報表、可觀測性與測試治理
556556

557-
> 34 個套件、約 6,906 行。
557+
> 34 個套件、約 6,921 行。
558558
559559
| 模組 | 行數 | 職責 |
560560
| --- | ---: | --- |
@@ -579,7 +579,7 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。
579579
| `utils/profiler/` | 426 | 逐動作效能剖析器 + 資源剖析器 |
580580
| `utils/quarantine/` | 190 | 易碎測試隔離區,讓套件執行器跳過已知不穩定案例 |
581581
| `utils/run_diff/` | 123 | 兩次執行軌跡的差異(LCS 對齊:新增/移除/狀態翻轉/退化) |
582-
| `utils/run_history/` | 377 | 執行歷史儲存與產出物管理 |
582+
| `utils/run_history/` | 392 | 執行歷史儲存與產出物管理 |
583583
| `utils/sarif/` | 134 | 以 SARIF 2.1.0 匯出發現項,供 GitHub/Azure code scanning |
584584
| `utils/slo/` | 112 | SLO 評估:SLI、錯誤預算與多視窗燃燒率告警 |
585585
| `utils/smoothing/` | 67 | 數列移動平均平滑 |
@@ -626,7 +626,7 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。
626626

627627
### 5.4.14 安全、機密與合規
628628

629-
> 13 個套件、約 2,294 行。
629+
> 13 個套件、約 2,302 行。
630630
631631
| 模組 | 行數 | 職責 |
632632
| --- | ---: | --- |
@@ -639,7 +639,7 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。
639639
| `utils/redaction/` | 467 | 截圖遮蔽層:規則偵測 + 政策 + 協調器(上傳 VLM 前先遮) |
640640
| `utils/sbom/` | 110 | SBOM(CycloneDX)產生 |
641641
| `utils/secret_ref/` | 126 | URI scheme 形式的值參照解析 |
642-
| `utils/secrets/` | 272 | 加密機密儲存庫,供 `${secrets.NAME}` 解析 |
642+
| `utils/secrets/` | 280 | 加密機密儲存庫,供 `${secrets.NAME}` 解析 |
643643
| `utils/secrets_scan/` | 98 | 掃描 action JSON/資料中應入庫卻硬編碼的機密 |
644644
| `utils/vex/` | 130 | OpenVEX 陳述撰寫與漏洞分類處置 |
645645
| `utils/vuln_scan/` | 188 | 以 OSV 比對 SBOM 元件的漏洞(純標準庫) |
@@ -702,13 +702,19 @@ socket server 有 8 MiB 讀取上限與 30 秒 handler timeout。
702702
| `action_schema.py` | 128 | action list 的結構驗證:形狀、參數型別、未知指令拒絕。單一走訪同時支援兩種消費方式:`validate_actions()` 遇到第一個問題就拋、`unknown_command_names()` 收齊全部不認得的名字(REST `/execute` 用它回 400)。 |
703703
| `mouse_aliases.py` | 39 | 單鍵點擊別名(`AC_click_left` 等),executor 與 callback executor 共用。 |
704704

705-
#### `utils/mcp_server/`(17,371 行,676 個工具)— 最大子系統
705+
#### `utils/mcp_server/`(17,431 行,676 個工具)— 最大子系統
706706

707707
| 檔案 | 行數 | 職責 |
708708
| --- | ---: | --- |
709-
| `tools/_factories.py` | 8,975 | 工具工廠:每個函式回傳一個領域的 `MCPTool` 清單(把 `AC_*` 能力包成 MCP 工具)。 |
710-
| `tools/_handlers.py` | 4,389 | 把 MCP 工具呼叫橋接到 AutoControl 無頭 API 的 adapter。 |
709+
| `tools/_factories.py` | 8,981 | 工具工廠:每個函式回傳一個領域的 `MCPTool` 清單(把 `AC_*` 能力包成 MCP 工具)。 |
710+
| `tools/_handlers.py` | 2,992 | 把 MCP 工具呼叫橋接到 AutoControl 無頭 API 的 adapter。 |
711711
| `tools/_handlers_qa.py` | 414 | 同一種 adapter,QA 主題:斷言 DSL、資料驅動、SQL/PDF/郵件/HTTP 步驟、codegen、視覺回歸、狀態機、flaky 偵測與隔離、suite runner、無障礙稽核、裝置矩陣、媒體斷言。從 `_handlers.py` 依主題拆出的第一塊(750 行上限);兩者互不引用。 |
712+
| `tools/_handlers_input.py` | 212 | 同一種 adapter,輸入主題:滑鼠、鍵盤、虛擬手把(ViGEm)。 |
713+
| `tools/_handlers_screen.py` | 304 | 同一種 adapter,螢幕主題:擷取、像素、影像與文字搜尋、螢幕錄影。 |
714+
| `tools/_handlers_system.py` | 559 | 同一種 adapter,桌面工作階段:視窗、行程與 shell、開檔、閒置與睡眠、音量、鎖定、輸入法狀態、欄位驗證與重試、色彩對比、變更排序、元件分類、剪貼簿。 |
715+
| `tools/_handlers_runs.py` | 110 | 同一種 adapter,執行主題:executor、執行歷史、錄製、動作檔。 |
716+
| `tools/_handlers_scheduling.py` | 200 | 同一種 adapter,排程主題:排程器、觸發器、熱鍵常駐。 |
717+
| `tools/_handlers_remote.py` | 66 | 同一種 adapter,遠端桌面的 host 與 viewer。 |
712718
| `server.py` | 717 | JSON-RPC 2.0 over stdio 的最小 MCP 伺服器:連線範圍狀態、行內/併發分派、工具與 resource/prompt 處理器。 |
713719
| `http_transport.py` | 521 | MCP 的 HTTP 傳輸。 |
714720
| `http_sessions.py` | 234 | MCP 的 HTTP 傳輸用的 session 身分:`Mcp-Session-Id` 註冊表,以及每個 session 那條常駐的 server→client SSE 串流。 |
@@ -1047,7 +1053,7 @@ socket 預設綁 `127.0.0.1`;資源一律用 `with`。
10471053
| 層/子系統 | 檔案數 | 行數 |
10481054
| --- | ---: | ---: |
10491055
| `gui/` | 90 | 26,722 |
1050-
| `utils/mcp_server/` | 22 | 17,371 |
1056+
| `utils/mcp_server/` | 28 | 17,431 |
10511057
| `utils/remote_desktop/` | 56 | 12,039 |
10521058
| `utils/executor/` | 6 | 9,081 |
10531059
| `utils/usb/` | 17 | 4,321 |
@@ -1066,6 +1072,6 @@ socket 預設綁 `127.0.0.1`;資源一律用 `with`。
10661072
| `osx/` | 17 | 919 |
10671073
| `autocontrol-lsp/` | 8 | 744 |
10681074
| `utils/hotkey/` | 7 | 738 |
1069-
| 其餘模組(約 286 個 `utils/` 子套件 + `android/`/`ios/`/周邊小工具) | 673 | 47,872 |
1070-
| **總計** | **1,027** | **141,687** |
1075+
| 其餘模組(約 286 個 `utils/` 子套件 + `android/`/`ios/`/周邊小工具) | 673 | 47,942 |
1076+
| **總計** | **1,033** | **141,817** |
10711077

0 commit comments

Comments
 (0)