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-21) — JSON Pointer, Patch & Merge Patch](#whats-new-2026-06-21--json-pointer-patch--merge-patch)
- [What's new (2026-06-21) — Client-Side Rate Limiting](#whats-new-2026-06-21--client-side-rate-limiting)
- [What's new (2026-06-21) — JSON Web Tokens (JWT)](#whats-new-2026-06-21--json-web-tokens-jwt)
- [What's new (2026-06-21) — License Policy Gate](#whats-new-2026-06-21--license-policy-gate)
Expand Down Expand Up @@ -115,6 +116,12 @@

---

## What's new (2026-06-21) — JSON Pointer, Patch & Merge Patch

Address, diff and patch JSON. Full reference: [`docs/source/Eng/doc/new_features/v63_features_doc.rst`](docs/source/Eng/doc/new_features/v63_features_doc.rst).

- **`resolve_pointer` / `make_patch` / `apply_patch` / `merge_patch` / `make_merge_patch`** (`AC_resolve_pointer`, `AC_apply_json_patch`, `AC_make_json_patch`, `AC_merge_patch`): `jsonpath` is read-only and `approval` compares whole artifacts — nothing could address one location, compute a structured delta, or apply a partial update. This adds the three IETF primitives — JSON Pointer (RFC 6901), JSON Patch (RFC 6902, all six ops, **atomic** apply), and JSON Merge Patch (RFC 7386, `null` deletes) — for config-drift detection, partial updates, HTTP PATCH bodies, and golden-master deltas. Pure-stdlib `json`+`copy`, validated against the RFC test vectors.

## What's new (2026-06-21) — Client-Side Rate Limiting

Stay under API quotas. Full reference: [`docs/source/Eng/doc/new_features/v62_features_doc.rst`](docs/source/Eng/doc/new_features/v62_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-21) — JSON Pointer、Patch 与 Merge Patch](#本次更新-2026-06-21--json-pointerpatch-与-merge-patch)
- [本次更新 (2026-06-21) — 客户端速率限制](#本次更新-2026-06-21--客户端速率限制)
- [本次更新 (2026-06-21) — JSON Web Token(JWT)](#本次更新-2026-06-21--json-web-tokenjwt)
- [本次更新 (2026-06-21) — 许可证策略闸门](#本次更新-2026-06-21--许可证策略闸门)
Expand Down Expand Up @@ -114,6 +115,12 @@

---

## 本次更新 (2026-06-21) — JSON Pointer、Patch 与 Merge Patch

定址、取差异并修补 JSON。完整参考:[`docs/source/Zh/doc/new_features/v63_features_doc.rst`](../docs/source/Zh/doc/new_features/v63_features_doc.rst)。

- **`resolve_pointer` / `make_patch` / `apply_patch` / `merge_patch` / `make_merge_patch`**(`AC_resolve_pointer`、`AC_apply_json_patch`、`AC_make_json_patch`、`AC_merge_patch`):`jsonpath` 是只读的,`approval` 比较整份产物 —— 没有任何东西能定址单一位置、计算结构化差异或套用部分更新。本功能补上三个 IETF 原语 —— JSON Pointer(RFC 6901)、JSON Patch(RFC 6902,全六种操作,**原子**套用)、JSON Merge Patch(RFC 7386,`null` 删除)—— 适用于配置漂移检测、部分更新、HTTP PATCH 内容与 golden-master 差异。纯标准库 `json`+`copy`,以 RFC 测试向量验证。

## 本次更新 (2026-06-21) — 客户端速率限制

守在 API 配额之内。完整参考:[`docs/source/Zh/doc/new_features/v62_features_doc.rst`](../docs/source/Zh/doc/new_features/v62_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-21) — JSON Pointer、Patch 與 Merge Patch](#本次更新-2026-06-21--json-pointerpatch-與-merge-patch)
- [本次更新 (2026-06-21) — 用戶端速率限制](#本次更新-2026-06-21--用戶端速率限制)
- [本次更新 (2026-06-21) — JSON Web Token(JWT)](#本次更新-2026-06-21--json-web-tokenjwt)
- [本次更新 (2026-06-21) — 授權政策閘門](#本次更新-2026-06-21--授權政策閘門)
Expand Down Expand Up @@ -114,6 +115,12 @@

---

## 本次更新 (2026-06-21) — JSON Pointer、Patch 與 Merge Patch

定址、取差異並修補 JSON。完整參考:[`docs/source/Zh/doc/new_features/v63_features_doc.rst`](../docs/source/Zh/doc/new_features/v63_features_doc.rst)。

- **`resolve_pointer` / `make_patch` / `apply_patch` / `merge_patch` / `make_merge_patch`**(`AC_resolve_pointer`、`AC_apply_json_patch`、`AC_make_json_patch`、`AC_merge_patch`):`jsonpath` 是唯讀的,`approval` 比較整份產物 —— 沒有任何東西能定址單一位置、計算結構化差異或套用部分更新。本功能補上三個 IETF 原語 —— JSON Pointer(RFC 6901)、JSON Patch(RFC 6902,全六種操作,**原子**套用)、JSON Merge Patch(RFC 7386,`null` 刪除)—— 適用於設定漂移偵測、部分更新、HTTP PATCH 內容與 golden-master 差異。純標準函式庫 `json`+`copy`,以 RFC 測試向量驗證。

## 本次更新 (2026-06-21) — 用戶端速率限制

守在 API 配額之內。完整參考:[`docs/source/Zh/doc/new_features/v62_features_doc.rst`](../docs/source/Zh/doc/new_features/v62_features_doc.rst)。
Expand Down
53 changes: 53 additions & 0 deletions docs/source/Eng/doc/new_features/v63_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
JSON Pointer, Patch & Merge Patch
=================================

``jsonpath`` queries are read-only and ``approval`` compares whole artifacts by
equality, but nothing could address a single location, compute a structured
*delta*, or apply a partial update to a JSON document. This adds the three IETF
primitives that fill that gap:

* **RFC 6901 JSON Pointer** — address one location (``/a/b/0``).
* **RFC 6902 JSON Patch** — an ordered op list
(add/remove/replace/move/copy/test); plus ``make_patch`` to diff two docs.
* **RFC 7386 JSON Merge Patch** — a recursive merge where ``null`` deletes.

Useful for config-drift detection, partial updates in flows, HTTP PATCH bodies,
and reporting golden-master deltas. Pure standard library (``json`` + ``copy``);
fully deterministic; imports no ``PySide6``.

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

.. code-block:: python

from je_auto_control import (
resolve_pointer, make_patch, apply_patch, merge_patch,
make_merge_patch, set_pointer, remove_pointer)

doc = {"user": {"name": "Jo", "tags": ["a", "b"]}}

resolve_pointer(doc, "/user/tags/0") # "a"

patch = make_patch(doc, {"user": {"name": "Joe", "tags": ["a"]}})
# [{"op": "replace", "path": "/user/name", "value": "Joe"},
# {"op": "remove", "path": "/user/tags/1"}]
apply_patch(doc, patch) # the updated document

merge_patch({"a": 1, "b": 2}, {"b": None, "c": 3}) # {"a": 1, "c": 3}

``apply_patch`` is **atomic** — it applies to a copy and only returns on full
success, so a failing ``test`` op leaves the original untouched. The six ops
follow RFC 6902 exactly (``add`` inserts into arrays, ``test`` does deep
equality keeping ``true`` distinct from ``1``, ``move`` rejects moving a value
into its own child). ``set_pointer`` / ``remove_pointer`` are pure
single-location convenience helpers. ``merge_patch`` follows RFC 7386 (a
``null`` value deletes the key; a non-object patch replaces wholesale).

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

``AC_resolve_pointer`` (``{value}``), ``AC_apply_json_patch`` (``{result}``),
``AC_make_json_patch`` (``{patch}``) and ``AC_merge_patch`` (``{result}``) take
their JSON inputs as objects or JSON strings. Each is also exposed as an MCP
tool (``ac_resolve_pointer`` / ``ac_apply_json_patch`` / ``ac_make_json_patch``
/ ``ac_merge_patch``) and as a Script Builder command 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 @@ -85,6 +85,7 @@ Comprehensive guides for all AutoControl features.
doc/new_features/v60_features_doc
doc/new_features/v61_features_doc
doc/new_features/v62_features_doc
doc/new_features/v63_features_doc
doc/ocr_backends/ocr_backends_doc
doc/observability/observability_doc
doc/operations_layer/operations_layer_doc
Expand Down
47 changes: 47 additions & 0 deletions docs/source/Zh/doc/new_features/v63_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
JSON Pointer、Patch 與 Merge Patch
==================================

``jsonpath`` 查詢是唯讀的,而 ``approval`` 以相等性比較整份產物,但沒有任何東西能定址單一位置、
計算結構化*差異*,或對 JSON 文件套用部分更新。本功能補上填補此缺口的三個 IETF 原語:

* **RFC 6901 JSON Pointer** —— 定址單一位置(``/a/b/0``)。
* **RFC 6902 JSON Patch** —— 有序的操作清單(add/remove/replace/move/copy/test);另含
``make_patch`` 以對兩份文件取差異。
* **RFC 7386 JSON Merge Patch** —— 遞迴合併,其中 ``null`` 代表刪除。

適用於設定漂移偵測、流程中的部分更新、HTTP PATCH 內容,以及回報 golden-master 差異。純標準
函式庫(``json`` + ``copy``);完全具決定性;不匯入 ``PySide6``。

無頭 API
--------

.. code-block:: python

from je_auto_control import (
resolve_pointer, make_patch, apply_patch, merge_patch,
make_merge_patch, set_pointer, remove_pointer)

doc = {"user": {"name": "Jo", "tags": ["a", "b"]}}

resolve_pointer(doc, "/user/tags/0") # "a"

patch = make_patch(doc, {"user": {"name": "Joe", "tags": ["a"]}})
# [{"op": "replace", "path": "/user/name", "value": "Joe"},
# {"op": "remove", "path": "/user/tags/1"}]
apply_patch(doc, patch) # 更新後的文件

merge_patch({"a": 1, "b": 2}, {"b": None, "c": 3}) # {"a": 1, "c": 3}

``apply_patch`` 是**原子的** —— 它套用在副本上,只有完全成功才回傳,因此失敗的 ``test`` 操作
會讓原始文件保持不變。六個操作完全遵循 RFC 6902(``add`` 會插入陣列、``test`` 做深度相等且讓
``true`` 與 ``1`` 相異、``move`` 拒絕把值移入自身子節點)。``set_pointer`` / ``remove_pointer``
是純粹的單一位置便利函式。``merge_patch`` 遵循 RFC 7386(``null`` 值刪除該鍵;非物件的 patch
整體取代)。

執行器命令
----------

``AC_resolve_pointer``(``{value}``)、``AC_apply_json_patch``(``{result}``)、
``AC_make_json_patch``(``{patch}``)與 ``AC_merge_patch``(``{result}``)接受物件或 JSON 字串
作為輸入。每個亦以 MCP 工具(``ac_resolve_pointer`` / ``ac_apply_json_patch`` /
``ac_make_json_patch`` / ``ac_merge_patch``)以及 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 @@ -85,6 +85,7 @@ AutoControl 所有功能的完整使用指南。
doc/new_features/v60_features_doc
doc/new_features/v61_features_doc
doc/new_features/v62_features_doc
doc/new_features/v63_features_doc
doc/ocr_backends/ocr_backends_doc
doc/observability/observability_doc
doc/operations_layer/operations_layer_doc
Expand Down
8 changes: 8 additions & 0 deletions je_auto_control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,11 @@
from je_auto_control.utils.rate_limit import (
SlidingWindowLimiter, TokenBucket, throttle,
)
# JSON Pointer / Patch / Merge Patch (RFC 6901 / 6902 / 7386)
from je_auto_control.utils.json_patch import (
PatchError, PatchTestFailed, apply_patch, make_merge_patch, make_patch,
merge_patch, remove_pointer, resolve_pointer, set_pointer,
)
# Background popup/interrupt watchdog (unattended automation)
from je_auto_control.utils.watchdog import (
PopupWatchdog, WatchdogRule, default_popup_watchdog,
Expand Down Expand Up @@ -803,6 +808,9 @@ def start_autocontrol_gui(*args, **kwargs):
"ClaimsPolicy", "ExpiredTokenError", "InvalidSignatureError", "JwtError",
"decode_jwt", "encode_jwt",
"SlidingWindowLimiter", "TokenBucket", "throttle",
"PatchError", "PatchTestFailed", "apply_patch", "make_merge_patch",
"make_patch", "merge_patch", "remove_pointer", "resolve_pointer",
"set_pointer",
# MCP server
"AuditLogger", "HttpMCPServer", "MCPContent", "MCPPrompt",
"MCPPromptArgument", "MCPResource", "MCPServer", "MCPTool",
Expand Down
34 changes: 34 additions & 0 deletions je_auto_control/gui/script_builder/command_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,40 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None:
),
description="Validate JSON against a JSON Schema; returns {ok, errors}.",
))
specs.append(CommandSpec(
"AC_resolve_pointer", "Data", "JSON Pointer: Resolve",
fields=(
FieldSpec("doc", FieldType.STRING, placeholder='{"a": {"b": [1, 2]}}'),
FieldSpec("pointer", FieldType.STRING, placeholder="/a/b/0"),
),
description="Resolve an RFC 6901 JSON Pointer; returns {value}.",
))
specs.append(CommandSpec(
"AC_apply_json_patch", "Data", "JSON Patch: Apply",
fields=(
FieldSpec("doc", FieldType.STRING, placeholder='{"a": 1}'),
FieldSpec("patch", FieldType.STRING,
placeholder='[{"op": "add", "path": "/b", "value": 2}]'),
),
description="Apply an RFC 6902 JSON Patch; returns {result}.",
))
specs.append(CommandSpec(
"AC_make_json_patch", "Data", "JSON Patch: Diff",
fields=(
FieldSpec("old", FieldType.STRING, placeholder='{"a": 1}'),
FieldSpec("new", FieldType.STRING, placeholder='{"a": 2}'),
),
description="Compute an RFC 6902 patch from old to new; returns {patch}.",
))
specs.append(CommandSpec(
"AC_merge_patch", "Data", "JSON Merge Patch: Apply",
fields=(
FieldSpec("doc", FieldType.STRING, placeholder='{"a": 1, "b": 2}'),
FieldSpec("patch", FieldType.STRING,
placeholder='{"b": null, "c": 3}'),
),
description="Apply an RFC 7386 merge patch (null deletes); returns {result}.",
))
specs.append(CommandSpec(
"AC_scan_vulns", "Security", "Scan Dependencies for Vulnerabilities",
fields=(
Expand Down
46 changes: 46 additions & 0 deletions je_auto_control/utils/executor/action_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2928,6 +2928,48 @@ def _rate_limit(name: str, rate: float = 1.0, capacity: float = 1.0,
"wait": round(bucket.time_until_available(float(n)), 4)}


def _resolve_pointer(doc: Any, pointer: str) -> Dict[str, Any]:
"""Adapter: resolve a JSON Pointer in doc (a dict/list or JSON string)."""
import json
from je_auto_control.utils.json_patch import resolve_pointer
if isinstance(doc, str):
doc = json.loads(doc)
return {"value": resolve_pointer(doc, pointer)}


def _apply_json_patch(doc: Any, patch: Any) -> Dict[str, Any]:
"""Adapter: apply an RFC 6902 JSON Patch (each a list/object or JSON str)."""
import json
from je_auto_control.utils.json_patch import apply_patch
if isinstance(doc, str):
doc = json.loads(doc)
if isinstance(patch, str):
patch = json.loads(patch)
return {"result": apply_patch(doc, patch)}


def _make_json_patch(old: Any, new: Any) -> Dict[str, Any]:
"""Adapter: compute an RFC 6902 patch turning old into new."""
import json
from je_auto_control.utils.json_patch import make_patch
if isinstance(old, str):
old = json.loads(old)
if isinstance(new, str):
new = json.loads(new)
return {"patch": make_patch(old, new)}


def _merge_patch(doc: Any, patch: Any) -> Dict[str, Any]:
"""Adapter: apply an RFC 7386 JSON Merge Patch (null deletes)."""
import json
from je_auto_control.utils.json_patch import merge_patch
if isinstance(doc, str):
doc = json.loads(doc)
if isinstance(patch, str):
patch = json.loads(patch)
return {"result": merge_patch(doc, patch)}


def _jwt_encode(claims: Any, key: str, alg: str = "HS256") -> Dict[str, Any]:
"""Adapter: sign a compact JWT from claims (a dict or JSON string)."""
import json
Expand Down Expand Up @@ -3739,6 +3781,10 @@ def __init__(self):
"AC_jwt_encode": _jwt_encode,
"AC_jwt_decode": _jwt_decode,
"AC_rate_limit": _rate_limit,
"AC_resolve_pointer": _resolve_pointer,
"AC_apply_json_patch": _apply_json_patch,
"AC_make_json_patch": _make_json_patch,
"AC_merge_patch": _merge_patch,
"AC_generate_sop": _generate_sop,
"AC_tween_drag": _tween_drag,
"AC_list_plugins": _list_plugins,
Expand Down
11 changes: 11 additions & 0 deletions je_auto_control/utils/json_patch/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""JSON Pointer (6901), JSON Patch (6902) and Merge Patch (7386) helpers."""
from je_auto_control.utils.json_patch.json_patch import (
PatchError, PatchTestFailed, apply_patch, escape_token, make_merge_patch,
make_patch, merge_patch, remove_pointer, resolve_pointer, set_pointer,
)

__all__ = [
"PatchError", "PatchTestFailed", "apply_patch", "escape_token",
"make_merge_patch", "make_patch", "merge_patch", "remove_pointer",
"resolve_pointer", "set_pointer",
]
Loading
Loading