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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

## Table of Contents

- [What's new (2026-06-20) — JSONPath Querying](#whats-new-2026-06-20--jsonpath-querying)
- [What's new (2026-06-20) — Multi-Channel Webhook Notifications](#whats-new-2026-06-20--multi-channel-webhook-notifications)
- [What's new (2026-06-20) — Outbound CloudEvents Emitter](#whats-new-2026-06-20--outbound-cloudevents-emitter)
- [What's new (2026-06-20) — Environment-Scoped Typed Asset Store](#whats-new-2026-06-20--environment-scoped-typed-asset-store)
Expand Down Expand Up @@ -103,6 +104,12 @@

---

## What's new (2026-06-20) — JSONPath Querying

Query API/DB JSON with wildcards, recursion, filters. Full reference: [`docs/source/Eng/doc/new_features/v51_features_doc.rst`](docs/source/Eng/doc/new_features/v51_features_doc.rst).

- **`json_query` / `json_query_one` / `json_extract`** (`AC_json_query` / `AC_json_extract`, `ac_*`): the executor's path walker only split on `.` and indexed — this adds a JSONPath subset (`$`, `.key`, `[n]`/`[-n]`, `*`/`[*]`, `..` recursive descent, `[?(@.k op v)]` filters) over parsed JSON, so array-bearing API/DB responses are easy to extract from. `json_extract` runs a `{key: path}` mapping into a flat dict. Pure-stdlib `re`; the path engine `AC_http_to_var` and DB-row flows were missing.

## What's new (2026-06-20) — Multi-Channel Webhook Notifications

Alert Teams/Discord/Slack/webhook. Full reference: [`docs/source/Eng/doc/new_features/v50_features_doc.rst`](docs/source/Eng/doc/new_features/v50_features_doc.rst).
Expand Down
7 changes: 7 additions & 0 deletions README/README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

## 目录

- [本次更新 (2026-06-20) — JSONPath 查询](#本次更新-2026-06-20--jsonpath-查询)
- [本次更新 (2026-06-20) — 多通道 Webhook 通知](#本次更新-2026-06-20--多通道-webhook-通知)
- [本次更新 (2026-06-20) — 对外 CloudEvents 发送器](#本次更新-2026-06-20--对外-cloudevents-发送器)
- [本次更新 (2026-06-20) — 环境范围的带类型资产存储](#本次更新-2026-06-20--环境范围的带类型资产存储)
Expand Down Expand Up @@ -102,6 +103,12 @@

---

## 本次更新 (2026-06-20) — JSONPath 查询

以通配符、递归、过滤查询 API/DB JSON。完整参考:[`docs/source/Zh/doc/new_features/v51_features_doc.rst`](../docs/source/Zh/doc/new_features/v51_features_doc.rst)。

- **`json_query` / `json_query_one` / `json_extract`**(`AC_json_query` / `AC_json_extract`、`ac_*`):执行器的路径遍历只会以 `.` 切分并索引 —— 本功能在已解析 JSON 上加入 JSONPath 子集(`$`、`.key`、`[n]`/`[-n]`、`*`/`[*]`、`..` 递归下降、`[?(@.k op v)]` 过滤),让含数组的 API/DB 响应易于提取。`json_extract` 以 `{key: path}` 映射提取成扁平 dict。纯标准库 `re`;这是 `AC_http_to_var` 与 DB-row 流程所缺的路径引擎。

## 本次更新 (2026-06-20) — 多通道 Webhook 通知

通知 Teams/Discord/Slack/webhook。完整参考:[`docs/source/Zh/doc/new_features/v50_features_doc.rst`](../docs/source/Zh/doc/new_features/v50_features_doc.rst)。
Expand Down
7 changes: 7 additions & 0 deletions README/README_zh-TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

## 目錄

- [本次更新 (2026-06-20) — JSONPath 查詢](#本次更新-2026-06-20--jsonpath-查詢)
- [本次更新 (2026-06-20) — 多通道 Webhook 通知](#本次更新-2026-06-20--多通道-webhook-通知)
- [本次更新 (2026-06-20) — 對外 CloudEvents 發送器](#本次更新-2026-06-20--對外-cloudevents-發送器)
- [本次更新 (2026-06-20) — 環境範圍的具型別資產儲存](#本次更新-2026-06-20--環境範圍的具型別資產儲存)
Expand Down Expand Up @@ -102,6 +103,12 @@

---

## 本次更新 (2026-06-20) — JSONPath 查詢

以萬用字元、遞迴、過濾查詢 API/DB JSON。完整參考:[`docs/source/Zh/doc/new_features/v51_features_doc.rst`](../docs/source/Zh/doc/new_features/v51_features_doc.rst)。

- **`json_query` / `json_query_one` / `json_extract`**(`AC_json_query` / `AC_json_extract`、`ac_*`):執行器的路徑走訪只會以 `.` 切分並索引 —— 本功能在已解析 JSON 上加入 JSONPath 子集(`$`、`.key`、`[n]`/`[-n]`、`*`/`[*]`、`..` 遞迴下降、`[?(@.k op v)]` 過濾),讓含陣列的 API/DB 回應易於擷取。`json_extract` 以 `{key: path}` 對應擷取成扁平 dict。純標準函式庫 `re`;這是 `AC_http_to_var` 與 DB-row 流程所缺的路徑引擎。

## 本次更新 (2026-06-20) — 多通道 Webhook 通知

通知 Teams/Discord/Slack/webhook。完整參考:[`docs/source/Zh/doc/new_features/v50_features_doc.rst`](../docs/source/Zh/doc/new_features/v50_features_doc.rst)。
Expand Down
54 changes: 54 additions & 0 deletions docs/source/Eng/doc/new_features/v51_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
JSONPath Querying
=================

The executor's built-in path walker only splits on ``.`` and indexes — it can't
do wildcards, recursive descent, or filters, so API/DB responses with arrays are
awkward to extract from. ``json_query`` adds a focused JSONPath subset over
already-parsed JSON:

================ ===================================================
Syntax Meaning
================ ===================================================
``$`` root (optional prefix)
``.name`` member access
``[n]`` ``[-n]`` list index (negative from the end)
``*`` ``[*]`` wildcard (all members / elements)
``..`` recursive descent
``[?(@.k op v)]`` filter array elements (``op`` ∈ ``== != < <= > >=``)
================ ===================================================

Pure standard library (``re``); imports no ``PySide6``.

Headless API
------------

.. code-block:: python

from je_auto_control import json_query, json_query_one, json_extract

json_query(data, "$.store.books[*].title") # every title
json_query(data, "$.store.books[?(@.price > 8)].title") # filtered
json_query(data, "$..price") # recursive descent

json_query_one(data, "$.user.name", default="?") # first match or default
json_extract(data, {"name": "$.user.name", # mapping -> flat dict
"first_tag": "$.tags[0]"})

``json_query`` returns **all** matches (a list); ``json_query_one`` returns the
first (or a default); ``json_extract`` runs a ``{key: path}`` mapping into a flat
dict (first match per path). This is the path engine the existing
``AC_http_to_var`` / API / DB-row flows were missing.

Executor commands
-----------------

================================ ===================================================
Command Effect
================================ ===================================================
``AC_json_query`` ``{matches}`` — all values matching a JSONPath.
``AC_json_extract`` ``{result}`` — a ``{key: path}`` mapping extracted.
================================ ===================================================

``data`` (and ``mapping``) accept a JSON object or a JSON string (so the visual
builder works). The same operations are exposed as MCP tools (``ac_json_query`` /
``ac_json_extract``) and as Script Builder commands under **Data**.
1 change: 1 addition & 0 deletions docs/source/Eng/eng_index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Comprehensive guides for all AutoControl features.
doc/new_features/v48_features_doc
doc/new_features/v49_features_doc
doc/new_features/v50_features_doc
doc/new_features/v51_features_doc
doc/ocr_backends/ocr_backends_doc
doc/observability/observability_doc
doc/operations_layer/operations_layer_doc
Expand Down
51 changes: 51 additions & 0 deletions docs/source/Zh/doc/new_features/v51_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
JSONPath 查詢
=============

執行器內建的路徑走訪只會以 ``.`` 切分並索引 —— 無法做萬用字元、遞迴下降或過濾,因此含
陣列的 API/DB 回應很難擷取。``json_query`` 在已解析的 JSON 上加入聚焦的 JSONPath 子集:

================ ===================================================
語法 意義
================ ===================================================
``$`` 根(可省略前綴)
``.name`` 成員存取
``[n]`` ``[-n]`` 串列索引(負數由尾端起算)
``*`` ``[*]`` 萬用字元(所有成員 / 元素)
``..`` 遞迴下降
``[?(@.k op v)]`` 過濾陣列元素(``op`` ∈ ``== != < <= > >=``)
================ ===================================================

純標準函式庫(``re``);不匯入 ``PySide6``。

無頭 API
--------

.. code-block:: python

from je_auto_control import json_query, json_query_one, json_extract

json_query(data, "$.store.books[*].title") # 每個 title
json_query(data, "$.store.books[?(@.price > 8)].title") # 過濾
json_query(data, "$..price") # 遞迴下降

json_query_one(data, "$.user.name", default="?") # 第一個或預設
json_extract(data, {"name": "$.user.name", # 對應 -> 扁平 dict
"first_tag": "$.tags[0]"})

``json_query`` 回傳**所有**符合項(清單);``json_query_one`` 回傳第一個(或預設);
``json_extract`` 以 ``{key: path}`` 對應擷取成扁平 dict(每路徑取第一個符合項)。這正是
既有 ``AC_http_to_var`` / API / DB-row 流程所缺的路徑引擎。

執行器指令
----------

================================ ===================================================
指令 效果
================================ ===================================================
``AC_json_query`` ``{matches}`` —— 符合 JSONPath 的所有值。
``AC_json_extract`` ``{result}`` —— 擷取 ``{key: path}`` 對應。
================================ ===================================================

``data``(與 ``mapping``)接受 JSON 物件或 JSON 字串(因此視覺化建構器可用)。相同操作亦
提供為 MCP 工具(``ac_json_query`` / ``ac_json_extract``),以及 Script Builder 中 **Data**
分類下的指令。
1 change: 1 addition & 0 deletions docs/source/Zh/zh_index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ AutoControl 所有功能的完整使用指南。
doc/new_features/v48_features_doc
doc/new_features/v49_features_doc
doc/new_features/v50_features_doc
doc/new_features/v51_features_doc
doc/ocr_backends/ocr_backends_doc
doc/observability/observability_doc
doc/operations_layer/operations_layer_doc
Expand Down
5 changes: 5 additions & 0 deletions je_auto_control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,10 @@
from je_auto_control.utils.notify_channels import (
WebhookChannel, WebhookResult, notify_webhook, set_default_poster,
)
# JSONPath-style querying over parsed JSON
from je_auto_control.utils.jsonpath import (
json_extract, json_query, json_query_one,
)
# Background popup/interrupt watchdog (unattended automation)
from je_auto_control.utils.watchdog import (
PopupWatchdog, WatchdogRule, default_popup_watchdog,
Expand Down Expand Up @@ -738,6 +742,7 @@ def start_autocontrol_gui(*args, **kwargs):
"Asset", "AssetStore", "AssetValue", "active_environment",
"EventEmitter", "post_cloudevent", "to_cloudevent",
"WebhookChannel", "WebhookResult", "notify_webhook", "set_default_poster",
"json_extract", "json_query", "json_query_one",
# MCP server
"AuditLogger", "HttpMCPServer", "MCPContent", "MCPPrompt",
"MCPPromptArgument", "MCPResource", "MCPServer", "MCPTool",
Expand Down
18 changes: 18 additions & 0 deletions je_auto_control/gui/script_builder/command_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,24 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None:
),
description="Send a Slack/Discord/Teams/raw webhook notification.",
))
specs.append(CommandSpec(
"AC_json_query", "Data", "JSONPath: Query",
fields=(
FieldSpec("data", FieldType.STRING,
placeholder='{"a": [1, 2]}'),
FieldSpec("path", FieldType.STRING, placeholder="$.a[*]"),
),
description="Query parsed JSON with a JSONPath subset (all matches).",
))
specs.append(CommandSpec(
"AC_json_extract", "Data", "JSONPath: Extract Mapping",
fields=(
FieldSpec("data", FieldType.STRING),
FieldSpec("mapping", FieldType.STRING,
placeholder='{"name": "$.user.name"}'),
),
description="Extract a {key: jsonpath} mapping into a flat object.",
))
specs.append(CommandSpec(
"AC_generate_sop", "Report", "Generate SOP Document",
fields=(
Expand Down
22 changes: 22 additions & 0 deletions je_auto_control/utils/executor/action_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3296,6 +3296,26 @@ def _notify_webhook(url: str, text: str, transport: str = "raw",
"transport": outcome.transport}


def _json_query(data: Any, path: str) -> Dict[str, Any]:
"""Adapter: return all JSONPath matches in data (JSON string or object)."""
import json
from je_auto_control.utils.jsonpath import json_query
if isinstance(data, str):
data = json.loads(data)
return {"matches": json_query(data, path)}


def _json_extract(data: Any, mapping: Any) -> Dict[str, Any]:
"""Adapter: extract a {key: path} mapping from data into a flat dict."""
import json
from je_auto_control.utils.jsonpath import json_extract
if isinstance(data, str):
data = json.loads(data)
if isinstance(mapping, str):
mapping = json.loads(mapping)
return {"result": json_extract(data, mapping)}


class Executor:
"""
Executor
Expand Down Expand Up @@ -3576,6 +3596,8 @@ def __init__(self):
"AC_list_assets": _list_assets,
"AC_emit_event": _emit_event,
"AC_notify_webhook": _notify_webhook,
"AC_json_query": _json_query,
"AC_json_extract": _json_extract,
"AC_a11y_record_start": _a11y_record_start,
"AC_a11y_record_stop": _a11y_record_stop,
"AC_a11y_record_events": _a11y_record_events,
Expand Down
6 changes: 6 additions & 0 deletions je_auto_control/utils/jsonpath/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Minimal JSONPath-style querying over parsed JSON data."""
from je_auto_control.utils.jsonpath.jsonpath import (
json_extract, json_query, json_query_one,
)

__all__ = ["json_extract", "json_query", "json_query_one"]
Loading
Loading