Skip to content

Commit e34f264

Browse files
authored
Merge pull request #185 from Integration-Automation/dev
Document new features and clean up homepage duplicates
2 parents 4ecc694 + edc1b8b commit e34f264

3 files changed

Lines changed: 363 additions & 4 deletions

File tree

docs/source/Eng/doc/new_features/new_features_doc.rst

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,3 +910,189 @@ These backends target legitimate use cases — accessibility software,
910910
GUI testing of games that lock out user-mode input, controlling a
911911
remote game-running machine from a headless setup — and aren't a
912912
generic anti-cheat bypass.
913+
914+
915+
Per-action profiler
916+
===================
917+
918+
Records wall-clock duration for every ``AC_*`` action so you can answer
919+
"which step is dominating this script's runtime?" without external
920+
tooling. Profiling is opt-in — when disabled, the executor wrapper has
921+
zero overhead::
922+
923+
import je_auto_control as ac
924+
ac.default_profiler.enable()
925+
ac.execute_action([["AC_locate_image_center", {"image": "btn.png"}],
926+
["AC_click_mouse"]])
927+
for row in ac.default_profiler.hot_spots(limit=5):
928+
print(row.name, row.calls, row.average_seconds)
929+
930+
Action-JSON commands::
931+
932+
[["AC_profiler_enable"]]
933+
[["AC_profiler_stats", {"limit": 10}]]
934+
[["AC_profiler_hot_spots", {"limit": 5}]]
935+
[["AC_profiler_reset"]]
936+
[["AC_profiler_disable"]]
937+
938+
GUI: **Profiler** tab — live hot-spot table (calls / total / avg / min /
939+
max / share) refreshed every second. Toggle recording, reset stats, or
940+
export the snapshot through the headless API.
941+
942+
943+
Run history timeline + failure thumbnails
944+
=========================================
945+
946+
The Run History tab gains a Gantt-style strip beneath the filter row:
947+
each scheduler / trigger / hotkey / webhook / email fire is rendered as
948+
a coloured bar on a horizontal time axis (green = ok, red = error,
949+
amber = still running). Selecting a bar syncs the table row, and a
950+
right-hand preview panel surfaces the failure screenshot already
951+
captured by the artifact manager.
952+
953+
Headless callers query the same data through the existing run history
954+
store::
955+
956+
import je_auto_control as ac
957+
for row in ac.default_history_store.list_runs(limit=20):
958+
print(row.id, row.status, row.duration_seconds, row.artifact_path)
959+
960+
No new commands — the store API is unchanged. The GUI is purely a
961+
thin visualization wrapper over the existing `runs` table.
962+
963+
964+
Encrypted secret manager
965+
========================
966+
967+
Action scripts that need API tokens, IMAP passwords, etc. should never
968+
embed plaintext. The new vault stores Fernet-encrypted entries under
969+
``~/.je_auto_control/secrets/vault.json``; a passphrase derives the
970+
key via PBKDF2-HMAC-SHA256 (600,000 iterations, 16-byte salt)::
971+
972+
import je_auto_control as ac
973+
ac.default_secret_manager.initialize("my-vault-passphrase")
974+
ac.default_secret_manager.set("github_token", "ghp_xxxxx")
975+
ac.default_secret_manager.lock()
976+
977+
# later — in the same process or a new run:
978+
ac.default_secret_manager.unlock("my-vault-passphrase")
979+
980+
Action-JSON commands::
981+
982+
[["AC_secret_init", {"passphrase": "..."}]]
983+
[["AC_secret_unlock", {"passphrase": "..."}]]
984+
[["AC_secret_set", {"name": "github_token", "value": "ghp_xxx"}]]
985+
[["AC_secret_list"]]
986+
[["AC_secret_remove", {"name": "github_token"}]]
987+
[["AC_secret_lock"]]
988+
[["AC_secret_status"]]
989+
990+
Action scripts reference vault entries through ``${secrets.NAME}``
991+
placeholders. The interpolator routes the ``secrets.`` namespace to the
992+
vault rather than the regular variable scope, so plaintext values never
993+
land in the variable bag::
994+
995+
[["AC_shell_command",
996+
{"command": "curl -H \"Authorization: Bearer ${secrets.github_token}\" ..."}]]
997+
998+
GUI: **Secrets** tab — initialize the vault, unlock it, add / remove
999+
entries, change passphrase. The vault file is created with mode 0o600
1000+
on POSIX systems; on Windows the default ACL already restricts
1001+
read access to the owning user.
1002+
1003+
1004+
Webhook (HTTP push) trigger
1005+
===========================
1006+
1007+
A bundled :mod:`http.server` dispatcher fires an action script when an
1008+
external service POSTs to a registered path. Configure path, allowed
1009+
methods, and an optional bearer token; the request method, path, query,
1010+
headers, raw body, and parsed JSON are seeded into the variable scope::
1011+
1012+
import je_auto_control as ac
1013+
ac.default_webhook_server.add(
1014+
path="/jobs/build", script_path="hooks/on_build.json",
1015+
methods=["POST"], token="topsecret",
1016+
)
1017+
host, port = ac.default_webhook_server.start("127.0.0.1", 0)
1018+
print("listening on", host, port)
1019+
1020+
The bound script reads the request through ``${webhook.*}`` placeholders::
1021+
1022+
[
1023+
["AC_set_var", {"name": "branch", "value": "${webhook.query.ref}"}],
1024+
["AC_shell_command",
1025+
{"command": "echo received build for ${webhook.body}"}]
1026+
]
1027+
1028+
Action-JSON commands::
1029+
1030+
[["AC_webhook_start", {"host": "127.0.0.1", "port": 8765}]]
1031+
[["AC_webhook_add", {"path": "/jobs", "script_path": "...",
1032+
"methods": ["POST"], "token": "..."}]]
1033+
[["AC_webhook_list"]]
1034+
[["AC_webhook_remove", {"webhook_id": "abcd1234"}]]
1035+
[["AC_webhook_status"]]
1036+
[["AC_webhook_stop"]]
1037+
1038+
Each fire is recorded in run history as ``trigger`` with source id
1039+
``webhook:<id>`` so the dashboard surfaces webhook activity alongside
1040+
other triggers. The body is capped at 1 MiB and bearer-token comparison
1041+
uses :func:`hmac.compare_digest`. Bind to ``127.0.0.1`` unless the
1042+
listener genuinely needs to be reachable from elsewhere on the network.
1043+
1044+
GUI: **Webhooks** tab — start/stop the server, register paths, view the
1045+
fire counter and auth state per route.
1046+
1047+
1048+
IMAP email trigger
1049+
==================
1050+
1051+
Poll-based watcher that logs into a mailbox on a configurable interval
1052+
and runs an action script once per matching message::
1053+
1054+
import je_auto_control as ac
1055+
ac.default_email_trigger_watcher.add(
1056+
host="imap.gmail.com", username="user@example.com",
1057+
password="app-specific-password",
1058+
script_path="hooks/on_alert.json",
1059+
mailbox="INBOX", search_criteria='UNSEEN FROM "alerts@..."',
1060+
poll_seconds=120, mark_seen=True,
1061+
)
1062+
ac.default_email_trigger_watcher.start()
1063+
1064+
The bound script sees the message metadata via ``${email.*}``::
1065+
1066+
[
1067+
["AC_if_var", {
1068+
"name": "email.subject", "op": "contains", "value": "CRITICAL",
1069+
"then": [["AC_hotkey", {"keys": ["ctrl", "alt", "p"]}]]
1070+
}]
1071+
]
1072+
1073+
Variables seeded per fire: ``email.uid``, ``email.from``, ``email.to``,
1074+
``email.subject``, ``email.message_id``, ``email.date``, ``email.body``.
1075+
1076+
Action-JSON commands::
1077+
1078+
[["AC_email_trigger_add", {"host": "...", "username": "...",
1079+
"password": "${secrets.imap_pw}",
1080+
"script_path": "...",
1081+
"mailbox": "INBOX",
1082+
"search_criteria": "UNSEEN",
1083+
"poll_seconds": 120,
1084+
"mark_seen": true,
1085+
"use_ssl": true}]]
1086+
[["AC_email_trigger_start"]]
1087+
[["AC_email_trigger_poll_once"]]
1088+
[["AC_email_trigger_list"]]
1089+
[["AC_email_trigger_remove", {"trigger_id": "abcd1234"}]]
1090+
[["AC_email_trigger_stop"]]
1091+
1092+
The watcher tracks already-fired UIDs in process memory, and optionally
1093+
flags messages ``\\Seen`` so the same mail isn't replayed across
1094+
restarts. TLS is pinned at 1.2 minimum. Combine ``AC_email_trigger_add``
1095+
with ``${secrets.NAME}`` so passwords never appear in the JSON.
1096+
1097+
GUI: **Email Triggers** tab — register IMAP triggers, start/stop the
1098+
watcher, run a manual poll, inspect last error and fire counter.

docs/source/Zh/doc/new_features/new_features_doc.rst

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,3 +854,180 @@ BattlEye)依然可以列舉 Interception / ViGEmBus / 新建立的 uinput
854854

855855
這三個後端針對的是合法用途 — 輔助科技、遊戲 GUI 測試、從 headless
856856
環境控制執行遊戲的遠端機器 — **不是** 通用反作弊繞過工具。
857+
858+
859+
效能分析器(Profiler)
860+
====================
861+
862+
針對每個 ``AC_*`` 動作記錄執行時間,讓你不用外部工具就能回答
863+
「哪一步是腳本的瓶頸?」分析是 opt-in 的,關閉時 executor 包裝層
864+
零開銷::
865+
866+
import je_auto_control as ac
867+
ac.default_profiler.enable()
868+
ac.execute_action([["AC_locate_image_center", {"image": "btn.png"}],
869+
["AC_click_mouse"]])
870+
for row in ac.default_profiler.hot_spots(limit=5):
871+
print(row.name, row.calls, row.average_seconds)
872+
873+
Action JSON 指令::
874+
875+
[["AC_profiler_enable"]]
876+
[["AC_profiler_stats", {"limit": 10}]]
877+
[["AC_profiler_hot_spots", {"limit": 5}]]
878+
[["AC_profiler_reset"]]
879+
[["AC_profiler_disable"]]
880+
881+
GUI: **Profiler** 分頁 — 每秒重新整理熱點表(次數 / 總時間 / 平均 /
882+
最短 / 最長 / 佔比),支援開關記錄、清除統計,或透過 headless API 匯出
883+
快照。
884+
885+
886+
執行紀錄時間軸 + 失敗截圖
887+
=========================
888+
889+
Run History 分頁在篩選列下方多了一條 Gantt 風格的時間軸:每筆
890+
scheduler / trigger / hotkey / webhook / email 觸發都繪成橫向時間
891+
軸上的色條(綠 = ok、紅 = error、琥珀 = 進行中)。點選色條會同步
892+
表格列,右側預覽面板顯示由 artifact manager 已捕捉的失敗截圖。
893+
894+
Headless 端讀取相同資料用既有的 run history store::
895+
896+
import je_auto_control as ac
897+
for row in ac.default_history_store.list_runs(limit=20):
898+
print(row.id, row.status, row.duration_seconds, row.artifact_path)
899+
900+
沒有新增指令 — store API 維持不變,GUI 只是 ``runs`` 表的薄殼可視化。
901+
902+
903+
密鑰管理器(Secret Manager)
904+
==========================
905+
906+
需要 API token、IMAP 密碼等敏感資訊的腳本,絕不該明文嵌入。新的
907+
密鑰庫把 Fernet 加密過的條目存在 ``~/.je_auto_control/secrets/vault.json``;
908+
通行碼透過 PBKDF2-HMAC-SHA256(60 萬次迭代,16 byte 鹽值)推導出金鑰::
909+
910+
import je_auto_control as ac
911+
ac.default_secret_manager.initialize("my-vault-passphrase")
912+
ac.default_secret_manager.set("github_token", "ghp_xxxxx")
913+
ac.default_secret_manager.lock()
914+
915+
# 之後 — 在同一個程序或新的 run:
916+
ac.default_secret_manager.unlock("my-vault-passphrase")
917+
918+
Action JSON 指令::
919+
920+
[["AC_secret_init", {"passphrase": "..."}]]
921+
[["AC_secret_unlock", {"passphrase": "..."}]]
922+
[["AC_secret_set", {"name": "github_token", "value": "ghp_xxx"}]]
923+
[["AC_secret_list"]]
924+
[["AC_secret_remove", {"name": "github_token"}]]
925+
[["AC_secret_lock"]]
926+
[["AC_secret_status"]]
927+
928+
腳本透過 ``${secrets.NAME}`` 佔位符引用 vault 條目。插補器會把
929+
``secrets.`` 命名空間導向 vault,而不是普通變數作用域,所以明文值
930+
永遠不會進到變數袋裡::
931+
932+
[["AC_shell_command",
933+
{"command": "curl -H \"Authorization: Bearer ${secrets.github_token}\" ..."}]]
934+
935+
GUI: **Secrets** 分頁 — 建立 vault、解鎖、新增 / 移除條目、變更
936+
通行碼。POSIX 系統上 vault 檔以 0o600 建立;Windows 預設 ACL 已限制
937+
只有擁有者能讀取。
938+
939+
940+
Webhook(HTTP push)觸發
941+
=======================
942+
943+
內建的 :mod:`http.server` dispatcher 在外部服務 POST 到註冊路徑時
944+
觸發腳本。可設定路徑、允許的方法、可選 bearer token;請求方法、
945+
路徑、query、headers、原始 body、解析後 JSON 都會種到變數作用域::
946+
947+
import je_auto_control as ac
948+
ac.default_webhook_server.add(
949+
path="/jobs/build", script_path="hooks/on_build.json",
950+
methods=["POST"], token="topsecret",
951+
)
952+
host, port = ac.default_webhook_server.start("127.0.0.1", 0)
953+
print("listening on", host, port)
954+
955+
綁定的腳本透過 ``${webhook.*}`` 佔位符讀取請求::
956+
957+
[
958+
["AC_set_var", {"name": "branch", "value": "${webhook.query.ref}"}],
959+
["AC_shell_command",
960+
{"command": "echo received build for ${webhook.body}"}]
961+
]
962+
963+
Action JSON 指令::
964+
965+
[["AC_webhook_start", {"host": "127.0.0.1", "port": 8765}]]
966+
[["AC_webhook_add", {"path": "/jobs", "script_path": "...",
967+
"methods": ["POST"], "token": "..."}]]
968+
[["AC_webhook_list"]]
969+
[["AC_webhook_remove", {"webhook_id": "abcd1234"}]]
970+
[["AC_webhook_status"]]
971+
[["AC_webhook_stop"]]
972+
973+
每次觸發以 ``trigger`` 來源寫入 run history,source id 為
974+
``webhook:<id>``,讓 dashboard 把 webhook 活動和其他 trigger 並排
975+
顯示。Body 上限 1 MiB,bearer token 比對用
976+
:func:`hmac.compare_digest`。除非你真的需要從網路其他地方連入,
977+
否則綁定 ``127.0.0.1``。
978+
979+
GUI: **Webhooks** 分頁 — 啟動 / 停止伺服器、註冊路徑、檢視每條
980+
路由的觸發次數與驗證狀態。
981+
982+
983+
IMAP Email 觸發
984+
===============
985+
986+
輪詢式 watcher,依設定週期登入信箱,每封符合條件的郵件執行一次
987+
腳本::
988+
989+
import je_auto_control as ac
990+
ac.default_email_trigger_watcher.add(
991+
host="imap.gmail.com", username="user@example.com",
992+
password="app-specific-password",
993+
script_path="hooks/on_alert.json",
994+
mailbox="INBOX", search_criteria='UNSEEN FROM "alerts@..."',
995+
poll_seconds=120, mark_seen=True,
996+
)
997+
ac.default_email_trigger_watcher.start()
998+
999+
腳本透過 ``${email.*}`` 看到郵件中繼資料::
1000+
1001+
[
1002+
["AC_if_var", {
1003+
"name": "email.subject", "op": "contains", "value": "CRITICAL",
1004+
"then": [["AC_hotkey", {"keys": ["ctrl", "alt", "p"]}]]
1005+
}]
1006+
]
1007+
1008+
每次觸發種入的變數: ``email.uid``、``email.from``、``email.to``、
1009+
``email.subject``、``email.message_id``、``email.date``、``email.body``。
1010+
1011+
Action JSON 指令::
1012+
1013+
[["AC_email_trigger_add", {"host": "...", "username": "...",
1014+
"password": "${secrets.imap_pw}",
1015+
"script_path": "...",
1016+
"mailbox": "INBOX",
1017+
"search_criteria": "UNSEEN",
1018+
"poll_seconds": 120,
1019+
"mark_seen": true,
1020+
"use_ssl": true}]]
1021+
[["AC_email_trigger_start"]]
1022+
[["AC_email_trigger_poll_once"]]
1023+
[["AC_email_trigger_list"]]
1024+
[["AC_email_trigger_remove", {"trigger_id": "abcd1234"}]]
1025+
[["AC_email_trigger_stop"]]
1026+
1027+
Watcher 在 process 記憶體中追蹤已觸發的 UID,可選擇把訊息標為
1028+
``\\Seen`` 確保跨重啟不會重複觸發。TLS 強制最低 1.2 版。把
1029+
``AC_email_trigger_add`` 跟 ``${secrets.NAME}`` 搭配使用,
1030+
密碼就不會出現在 JSON 裡。
1031+
1032+
GUI: **Email Triggers** 分頁 — 註冊 IMAP 觸發、啟動 / 停止 watcher、
1033+
手動觸發一次輪詢、檢視最近錯誤與觸發次數。

0 commit comments

Comments
 (0)