cluxion-agentplugin-preprocessing 0.3.1__tar.gz → 0.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/LICENSE +26 -22
- cluxion_agentplugin_preprocessing-0.3.3/PKG-INFO +138 -0
- cluxion_agentplugin_preprocessing-0.3.3/README.md +106 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/pyproject.toml +1 -1
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_agentplugin_preprocessing/__init__.py +1 -1
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_agentplugin_preprocessing/guard_watch.py +16 -19
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_agentplugin_preprocessing/schemas.py +5 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/adapters/contract.py +8 -2
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/core/dispatch_store.py +80 -48
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/resources/py_queue.py +87 -49
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/web/browser_bridge.py +9 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/runtime/test_contract.py +17 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/runtime/test_dispatch_store.py +72 -0
- cluxion_agentplugin_preprocessing-0.3.3/tests/runtime/test_py_queue_concurrency.py +160 -0
- cluxion_agentplugin_preprocessing-0.3.1/PKG-INFO +0 -125
- cluxion_agentplugin_preprocessing-0.3.1/README.md +0 -93
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/.github/profile/README.md +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/.gitignore +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/Docs/README.md +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/adapters/claude/.claude-plugin/plugin.json +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/adapters/claude/skills/preprocess/SKILL.md +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/adapters/codex/config-snippet.toml +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/cluxion-Docs/README.md +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/cluxion-Docs/architecture.md +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/cluxion-Docs/harness-logic.md +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/cluxion-Docs/honesty-preprocessing.md +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/cluxion-Docs/install-and-operations.md +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/cluxion-Docs/security.md +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/rust/cluxion_queue/Cargo.lock +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/rust/cluxion_queue/Cargo.toml +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/rust/cluxion_queue/pyproject.toml +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/rust/cluxion_queue/src/context.rs +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/rust/cluxion_queue/src/dispatch.rs +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/rust/cluxion_queue/src/guard.rs +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/rust/cluxion_queue/src/lib.rs +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/rust/cluxion_queue/src/main.rs +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/rust/cluxion_queue/src/queue.rs +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/rust/cluxion_queue/src/types.rs +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_agentplugin_preprocessing/cli.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_agentplugin_preprocessing/hermes_config.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_agentplugin_preprocessing/plugin.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_agentplugin_preprocessing/plugin.yaml +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_agentplugin_preprocessing/runner.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/__init__.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/__main__.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/adapters/__init__.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/adapters/grok_build.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/adapters/hermes.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/adapters/spec.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/bootstrap.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/cli.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/core/__init__.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/core/clarification.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/core/context_compress.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/core/harness.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/core/intent.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/core/ledger.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/core/ledger_codec.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/core/plan_codec.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/core/preprocess.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/core/types.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/core/work_queue.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/guard_daemon_host.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/models/__init__.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/models/supervisor.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/models/vllm_mlx.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/resources/__init__.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/resources/guard_bridge.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/resources/queue_bridge.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/resources/rust_bridge.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/src/cluxion_runtime/web/__init__.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/runtime/test_browser_bridge.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/runtime/test_clarification.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/runtime/test_cluxion_runtime_spine.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/runtime/test_context_compress.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/runtime/test_guard.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/runtime/test_ledger.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/runtime/test_queue_backends.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/runtime/test_runtime_adapter_cli.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/runtime/test_rust_queue.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/runtime/test_supervisor.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/test_bootstrap.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/test_guard_watch.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/test_hermes_config.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/test_packaging_policy.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/test_plugin.py +0 -0
- {cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/tests/test_runner.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Apache License
|
|
2
2
|
Version 2.0, January 2004
|
|
3
|
-
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
4
|
|
|
5
5
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
6
|
|
|
@@ -73,17 +73,18 @@
|
|
|
73
73
|
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
74
|
this License, each Contributor hereby grants to You a perpetual,
|
|
75
75
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
Contribution(s)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
87
88
|
|
|
88
89
|
4. Redistribution. You may reproduce and distribute copies of the
|
|
89
90
|
Work or Derivative Works thereof in any medium, with or without
|
|
@@ -130,6 +131,9 @@
|
|
|
130
131
|
any Contribution intentionally submitted for inclusion in the Work
|
|
131
132
|
by You to the Licensor shall be under the terms and conditions of
|
|
132
133
|
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
133
137
|
|
|
134
138
|
6. Trademarks. This License does not grant permission to use the trade
|
|
135
139
|
names, trademarks, service marks, or product names of the Licensor,
|
|
@@ -182,16 +186,16 @@
|
|
|
182
186
|
same "printed page" as the copyright notice for easier
|
|
183
187
|
identification within third-party archives.
|
|
184
188
|
|
|
185
|
-
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
186
190
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
190
194
|
|
|
191
|
-
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
192
196
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cluxion-agentplugin-preprocessing
|
|
3
|
+
Version: 0.3.3
|
|
4
|
+
Summary: Universal agent plugin for Cluxion preprocessing, honesty contracts, clarification, Rust work queue, and resource-aware harness handoff.
|
|
5
|
+
Project-URL: Homepage, https://github.com/cluxion/cluxion-Agentplugin-preprocessing
|
|
6
|
+
Project-URL: Repository, https://github.com/cluxion/cluxion-Agentplugin-preprocessing
|
|
7
|
+
Project-URL: Issues, https://github.com/cluxion/cluxion-Agentplugin-preprocessing/issues
|
|
8
|
+
Author-email: cluxion <algocean1204@users.noreply.github.com>
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai-agent,claude-code,cluxion,codex,hermes-agent,plugin,preprocessing
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Plugins
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: psutil>=5.9
|
|
22
|
+
Requires-Dist: pyyaml>=6.0
|
|
23
|
+
Provides-Extra: browser
|
|
24
|
+
Requires-Dist: playwright>=1.49; extra == 'browser'
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
29
|
+
Requires-Dist: twine>=6.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: wheel>=0.45; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
========= Written in Korean first, then English ==========
|
|
34
|
+
|
|
35
|
+
======== 한국어 ========
|
|
36
|
+
|
|
37
|
+
# cluxion-agentplugin-preprocessing
|
|
38
|
+
|
|
39
|
+
AI 코딩 에이전트(Hermes Agent, Claude Code, Codex)를 위한 전처리 플러그인입니다. 작업이 시작되기
|
|
40
|
+
*전에* 정리를 해 줍니다: 에이전트가 모르는 것은 모른다고 인정하게 하고, 애매한 요청은 행동하기 전에
|
|
41
|
+
사용자에게 명확히 묻게 하며, 긴 작업을 안정적으로 큐에 넣고, 폭주하는 프로세스가 기기를 다운시키지
|
|
42
|
+
못하게 막고, 대화가 너무 길어지면 자동으로 압축합니다.
|
|
43
|
+
|
|
44
|
+
## 설치
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install cluxion-agentplugin-preprocessing
|
|
48
|
+
|
|
49
|
+
# 선택: 사용자 본인의 로그인된 Chrome으로 웹 검색
|
|
50
|
+
pip install 'cluxion-agentplugin-preprocessing[browser]'
|
|
51
|
+
playwright install chromium
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Hermes Agent에서 사용
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
cluxion-preprocess enable # ~/.hermes/config.yaml 에 플러그인을 추가합니다
|
|
58
|
+
# 그 다음 Hermes 재시작
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Hermes를 통해 제공되는 로컬 모델(vLLM/MLX)에서도 동일하게 동작합니다.
|
|
62
|
+
|
|
63
|
+
## 기능
|
|
64
|
+
|
|
65
|
+
활성화하면 에이전트가 아래 도구들을 얻고, 자동으로 호출합니다.
|
|
66
|
+
|
|
67
|
+
- **정직함과 명확화** — 에이전트가 추측하기 전에 먼저 묻고, 근거를 댈 수 없는 답을 지어내지 않습니다.
|
|
68
|
+
- **작업 큐** — 긴 작업을 하나의 프롬프트에 넘치게 담는 대신 추적 가능한 세그먼트로 분할합니다.
|
|
69
|
+
- **자원 가드** — 폭주 프로세스가 RAM을 모두 잡아먹지 못하게 막는 가벼운 감시기. Hermes 세션마다
|
|
70
|
+
자동으로 시작됩니다.
|
|
71
|
+
- **컨텍스트 압축** — 대화가 모델 컨텍스트의 약 70%를 넘으면, 의도와 최근 대화를 보존하며 압축합니다.
|
|
72
|
+
- **내 Chrome으로 웹 검색** — 본인의 로그인된 브라우저 세션으로 Google / Naver / Perplexity / 사내
|
|
73
|
+
페이지를 검색합니다(위의 `[browser]` extra 필요).
|
|
74
|
+
|
|
75
|
+
## 문제 해결
|
|
76
|
+
|
|
77
|
+
| 증상 | 해결 |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `playwright_not_installed` | `pip install 'cluxion-agentplugin-preprocessing[browser]' && playwright install chromium` |
|
|
80
|
+
| Hermes에 도구가 안 보임 | `cluxion-preprocess enable` 실행 후 Hermes 재시작 |
|
|
81
|
+
|
|
82
|
+
## 라이선스
|
|
83
|
+
|
|
84
|
+
Apache-2.0
|
|
85
|
+
|
|
86
|
+
============ English ==========
|
|
87
|
+
|
|
88
|
+
# cluxion-agentplugin-preprocessing
|
|
89
|
+
|
|
90
|
+
A preprocessing plugin for AI coding agents (Hermes Agent, Claude Code, Codex). It tidies
|
|
91
|
+
things up *before* work starts: it makes the agent admit when it doesn't know, asks you to
|
|
92
|
+
clarify vague requests before acting, queues long tasks reliably, keeps runaway processes
|
|
93
|
+
from taking down your machine, and auto-compresses the conversation when it grows too long.
|
|
94
|
+
|
|
95
|
+
## Install
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip install cluxion-agentplugin-preprocessing
|
|
99
|
+
|
|
100
|
+
# optional: web search through your own logged-in Chrome
|
|
101
|
+
pip install 'cluxion-agentplugin-preprocessing[browser]'
|
|
102
|
+
playwright install chromium
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Use with Hermes Agent
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
cluxion-preprocess enable # adds the plugin to ~/.hermes/config.yaml
|
|
109
|
+
# then restart Hermes
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The tools also work with local models (vLLM/MLX) served through Hermes.
|
|
113
|
+
|
|
114
|
+
## What you get
|
|
115
|
+
|
|
116
|
+
Once enabled, your agent gains these tools and calls them automatically:
|
|
117
|
+
|
|
118
|
+
- **Honesty & clarification** — the agent asks before guessing and won't fake an answer it
|
|
119
|
+
can't back up.
|
|
120
|
+
- **Work queue** — long tasks are split into tracked segments instead of overflowing a
|
|
121
|
+
single prompt.
|
|
122
|
+
- **Resource guard** — a lightweight watcher that stops runaway processes from eating all
|
|
123
|
+
your RAM. It starts automatically with each Hermes session.
|
|
124
|
+
- **Context compression** — shrinks the conversation once it passes ~70% of the model's
|
|
125
|
+
window, keeping your intent and recent turns.
|
|
126
|
+
- **Web search via your Chrome** — searches Google / Naver / Perplexity / internal pages
|
|
127
|
+
through your own logged-in browser session (needs the `[browser]` extra above).
|
|
128
|
+
|
|
129
|
+
## Troubleshooting
|
|
130
|
+
|
|
131
|
+
| Problem | Fix |
|
|
132
|
+
|---|---|
|
|
133
|
+
| `playwright_not_installed` | `pip install 'cluxion-agentplugin-preprocessing[browser]' && playwright install chromium` |
|
|
134
|
+
| Tools don't appear in Hermes | run `cluxion-preprocess enable`, then restart Hermes |
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
Apache-2.0
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
========= Written in Korean first, then English ==========
|
|
2
|
+
|
|
3
|
+
======== 한국어 ========
|
|
4
|
+
|
|
5
|
+
# cluxion-agentplugin-preprocessing
|
|
6
|
+
|
|
7
|
+
AI 코딩 에이전트(Hermes Agent, Claude Code, Codex)를 위한 전처리 플러그인입니다. 작업이 시작되기
|
|
8
|
+
*전에* 정리를 해 줍니다: 에이전트가 모르는 것은 모른다고 인정하게 하고, 애매한 요청은 행동하기 전에
|
|
9
|
+
사용자에게 명확히 묻게 하며, 긴 작업을 안정적으로 큐에 넣고, 폭주하는 프로세스가 기기를 다운시키지
|
|
10
|
+
못하게 막고, 대화가 너무 길어지면 자동으로 압축합니다.
|
|
11
|
+
|
|
12
|
+
## 설치
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install cluxion-agentplugin-preprocessing
|
|
16
|
+
|
|
17
|
+
# 선택: 사용자 본인의 로그인된 Chrome으로 웹 검색
|
|
18
|
+
pip install 'cluxion-agentplugin-preprocessing[browser]'
|
|
19
|
+
playwright install chromium
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Hermes Agent에서 사용
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
cluxion-preprocess enable # ~/.hermes/config.yaml 에 플러그인을 추가합니다
|
|
26
|
+
# 그 다음 Hermes 재시작
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Hermes를 통해 제공되는 로컬 모델(vLLM/MLX)에서도 동일하게 동작합니다.
|
|
30
|
+
|
|
31
|
+
## 기능
|
|
32
|
+
|
|
33
|
+
활성화하면 에이전트가 아래 도구들을 얻고, 자동으로 호출합니다.
|
|
34
|
+
|
|
35
|
+
- **정직함과 명확화** — 에이전트가 추측하기 전에 먼저 묻고, 근거를 댈 수 없는 답을 지어내지 않습니다.
|
|
36
|
+
- **작업 큐** — 긴 작업을 하나의 프롬프트에 넘치게 담는 대신 추적 가능한 세그먼트로 분할합니다.
|
|
37
|
+
- **자원 가드** — 폭주 프로세스가 RAM을 모두 잡아먹지 못하게 막는 가벼운 감시기. Hermes 세션마다
|
|
38
|
+
자동으로 시작됩니다.
|
|
39
|
+
- **컨텍스트 압축** — 대화가 모델 컨텍스트의 약 70%를 넘으면, 의도와 최근 대화를 보존하며 압축합니다.
|
|
40
|
+
- **내 Chrome으로 웹 검색** — 본인의 로그인된 브라우저 세션으로 Google / Naver / Perplexity / 사내
|
|
41
|
+
페이지를 검색합니다(위의 `[browser]` extra 필요).
|
|
42
|
+
|
|
43
|
+
## 문제 해결
|
|
44
|
+
|
|
45
|
+
| 증상 | 해결 |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `playwright_not_installed` | `pip install 'cluxion-agentplugin-preprocessing[browser]' && playwright install chromium` |
|
|
48
|
+
| Hermes에 도구가 안 보임 | `cluxion-preprocess enable` 실행 후 Hermes 재시작 |
|
|
49
|
+
|
|
50
|
+
## 라이선스
|
|
51
|
+
|
|
52
|
+
Apache-2.0
|
|
53
|
+
|
|
54
|
+
============ English ==========
|
|
55
|
+
|
|
56
|
+
# cluxion-agentplugin-preprocessing
|
|
57
|
+
|
|
58
|
+
A preprocessing plugin for AI coding agents (Hermes Agent, Claude Code, Codex). It tidies
|
|
59
|
+
things up *before* work starts: it makes the agent admit when it doesn't know, asks you to
|
|
60
|
+
clarify vague requests before acting, queues long tasks reliably, keeps runaway processes
|
|
61
|
+
from taking down your machine, and auto-compresses the conversation when it grows too long.
|
|
62
|
+
|
|
63
|
+
## Install
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install cluxion-agentplugin-preprocessing
|
|
67
|
+
|
|
68
|
+
# optional: web search through your own logged-in Chrome
|
|
69
|
+
pip install 'cluxion-agentplugin-preprocessing[browser]'
|
|
70
|
+
playwright install chromium
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Use with Hermes Agent
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
cluxion-preprocess enable # adds the plugin to ~/.hermes/config.yaml
|
|
77
|
+
# then restart Hermes
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The tools also work with local models (vLLM/MLX) served through Hermes.
|
|
81
|
+
|
|
82
|
+
## What you get
|
|
83
|
+
|
|
84
|
+
Once enabled, your agent gains these tools and calls them automatically:
|
|
85
|
+
|
|
86
|
+
- **Honesty & clarification** — the agent asks before guessing and won't fake an answer it
|
|
87
|
+
can't back up.
|
|
88
|
+
- **Work queue** — long tasks are split into tracked segments instead of overflowing a
|
|
89
|
+
single prompt.
|
|
90
|
+
- **Resource guard** — a lightweight watcher that stops runaway processes from eating all
|
|
91
|
+
your RAM. It starts automatically with each Hermes session.
|
|
92
|
+
- **Context compression** — shrinks the conversation once it passes ~70% of the model's
|
|
93
|
+
window, keeping your intent and recent turns.
|
|
94
|
+
- **Web search via your Chrome** — searches Google / Naver / Perplexity / internal pages
|
|
95
|
+
through your own logged-in browser session (needs the `[browser]` extra above).
|
|
96
|
+
|
|
97
|
+
## Troubleshooting
|
|
98
|
+
|
|
99
|
+
| Problem | Fix |
|
|
100
|
+
|---|---|
|
|
101
|
+
| `playwright_not_installed` | `pip install 'cluxion-agentplugin-preprocessing[browser]' && playwright install chromium` |
|
|
102
|
+
| Tools don't appear in Hermes | run `cluxion-preprocess enable`, then restart Hermes |
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
Apache-2.0
|
{cluxion_agentplugin_preprocessing-0.3.1 → cluxion_agentplugin_preprocessing-0.3.3}/pyproject.toml
RENAMED
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cluxion-agentplugin-preprocessing"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.3"
|
|
8
8
|
description = "Universal agent plugin for Cluxion preprocessing, honesty contracts, clarification, Rust work queue, and resource-aware harness handoff."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -51,23 +51,27 @@ def post_tool_call(**_: Any) -> None:
|
|
|
51
51
|
let the existing owned-only fail-closed enforcement path terminate
|
|
52
52
|
candidates.
|
|
53
53
|
"""
|
|
54
|
-
global _last_watch_at
|
|
54
|
+
global _last_warning_at, _last_watch_at
|
|
55
55
|
|
|
56
56
|
now = time.monotonic()
|
|
57
|
-
with _lock:
|
|
58
|
-
if _last_watch_at is not None and now - _last_watch_at < _watch_interval_seconds():
|
|
59
|
-
return
|
|
60
|
-
_last_watch_at = now
|
|
61
|
-
|
|
62
57
|
try:
|
|
63
|
-
|
|
58
|
+
with _lock:
|
|
59
|
+
if _last_watch_at is not None and now - _last_watch_at < _watch_interval_seconds():
|
|
60
|
+
return
|
|
61
|
+
_last_watch_at = now
|
|
62
|
+
result = guard_bridge.auto_enforce([os.getpid()], dry_run=not _auto_apply_enabled())
|
|
63
|
+
should_warn = (
|
|
64
|
+
bool(result.get("triggered", False))
|
|
65
|
+
and bool(result.get("dry_run", True))
|
|
66
|
+
and (_last_warning_at is None or now - _last_warning_at >= WARNING_INTERVAL_SECONDS)
|
|
67
|
+
)
|
|
68
|
+
if should_warn:
|
|
69
|
+
_last_warning_at = now
|
|
64
70
|
except Exception as exc:
|
|
65
71
|
_warn(f"cluxion guard watch failed: {exc}")
|
|
66
72
|
return
|
|
67
|
-
if
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
_warn_triggered(result, now)
|
|
73
|
+
if should_warn:
|
|
74
|
+
_warn_triggered(result)
|
|
71
75
|
|
|
72
76
|
|
|
73
77
|
def _autostart_enabled() -> bool:
|
|
@@ -88,14 +92,7 @@ def _watch_interval_seconds() -> float:
|
|
|
88
92
|
return DEFAULT_WATCH_INTERVAL_SECONDS
|
|
89
93
|
|
|
90
94
|
|
|
91
|
-
def _warn_triggered(result: dict[str, Any]
|
|
92
|
-
global _last_warning_at
|
|
93
|
-
|
|
94
|
-
with _lock:
|
|
95
|
-
if _last_warning_at is not None and now - _last_warning_at < WARNING_INTERVAL_SECONDS:
|
|
96
|
-
return
|
|
97
|
-
_last_warning_at = now
|
|
98
|
-
|
|
95
|
+
def _warn_triggered(result: dict[str, Any]) -> None:
|
|
99
96
|
pids = [str(entry.get("pid")) for entry in result.get("candidates", []) if isinstance(entry, dict)]
|
|
100
97
|
reasons = [str(reason) for reason in result.get("trigger_reasons", [])]
|
|
101
98
|
_warn(
|
|
@@ -53,6 +53,11 @@ PLAN_SCHEMA = {
|
|
|
53
53
|
"expected_ram_mb": {"type": "integer", "minimum": 0, "default": 0},
|
|
54
54
|
"context_tokens": {"type": "integer", "minimum": 0, "default": 0},
|
|
55
55
|
"cwd": {"type": "string", "default": ""},
|
|
56
|
+
"clarification_answers": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"default": "",
|
|
59
|
+
"description": "Answers that resolve a prior clarification gate; required to enqueue an ambiguous or large task.",
|
|
60
|
+
},
|
|
56
61
|
},
|
|
57
62
|
"required": ["prompt"],
|
|
58
63
|
},
|
|
@@ -25,7 +25,7 @@ def work_item_from_adapter_payload(payload: Mapping[str, object], *, default_sur
|
|
|
25
25
|
model_route=str(payload.get("model_route", "host/default")),
|
|
26
26
|
expected_ram_mb=max(0, int(payload.get("expected_ram_mb", 0))),
|
|
27
27
|
context_tokens=max(0, int(payload.get("context_tokens", 0))),
|
|
28
|
-
metadata=_metadata(payload.get("metadata"), payload.get("cwd")),
|
|
28
|
+
metadata=_metadata(payload.get("metadata"), payload.get("cwd"), payload.get("clarification_answers")),
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
|
|
@@ -70,12 +70,18 @@ def _priority(value: object) -> WorkPriority:
|
|
|
70
70
|
return WorkPriority[str(value).upper()]
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
def _metadata(value: object, cwd: object) -> dict[str, str]:
|
|
73
|
+
def _metadata(value: object, cwd: object, clarification_answers: object = None) -> dict[str, str]:
|
|
74
74
|
metadata: dict[str, str] = {}
|
|
75
75
|
if isinstance(value, dict):
|
|
76
76
|
metadata.update({str(key): str(val) for key, val in value.items()})
|
|
77
77
|
if cwd is not None and cwd != "":
|
|
78
78
|
metadata["cwd"] = str(cwd)
|
|
79
|
+
# Top-level clarification_answers is the documented way the host answers a
|
|
80
|
+
# clarification gate; without merging it here the gate stays blocked and the
|
|
81
|
+
# work queue never engages, even for very long prompts. A nested
|
|
82
|
+
# metadata.clarification_answers (if present) is not overwritten by an empty top-level value.
|
|
83
|
+
if clarification_answers is not None and str(clarification_answers) != "":
|
|
84
|
+
metadata["clarification_answers"] = str(clarification_answers)
|
|
79
85
|
return metadata
|
|
80
86
|
|
|
81
87
|
|
|
@@ -5,11 +5,18 @@ from __future__ import annotations
|
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
7
|
import time
|
|
8
|
+
from collections.abc import Iterator
|
|
9
|
+
from contextlib import contextmanager
|
|
8
10
|
from dataclasses import dataclass
|
|
9
11
|
from pathlib import Path
|
|
10
12
|
from tempfile import NamedTemporaryFile
|
|
11
13
|
from typing import TYPE_CHECKING
|
|
12
14
|
|
|
15
|
+
try:
|
|
16
|
+
import fcntl as _fcntl
|
|
17
|
+
except ImportError: # pragma: no cover - exercised only on non-POSIX platforms.
|
|
18
|
+
_fcntl = None
|
|
19
|
+
|
|
13
20
|
if TYPE_CHECKING:
|
|
14
21
|
from cluxion_runtime.core.types import HarnessPlan, QueueSegment
|
|
15
22
|
|
|
@@ -63,48 +70,43 @@ def persist_dispatch_bundle(plan: HarnessPlan, *, dispatch_dir: Path | None = No
|
|
|
63
70
|
pass
|
|
64
71
|
target_dir.mkdir(parents=True, exist_ok=True)
|
|
65
72
|
path = _bundle_path(plan.item.work_id, target_dir)
|
|
66
|
-
|
|
73
|
+
with _exclusive_bundle_lock(path):
|
|
74
|
+
_atomic_write_json(path, bundle)
|
|
67
75
|
return path
|
|
68
76
|
|
|
69
77
|
|
|
70
78
|
def load_dispatch_bundle(work_id: str, *, dispatch_dir: Path | None = None) -> dict[str, object]:
|
|
71
79
|
"""Read the dispatch bundle for a work_id."""
|
|
72
80
|
path = _bundle_path(work_id, default_dispatch_dir() if dispatch_dir is None else dispatch_dir)
|
|
73
|
-
|
|
74
|
-
raise DispatchStoreError(f"dispatch bundle not found: {work_id}")
|
|
75
|
-
try:
|
|
76
|
-
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
77
|
-
except json.JSONDecodeError as exc:
|
|
78
|
-
raise DispatchStoreError(f"dispatch bundle is invalid JSON: {work_id}") from exc
|
|
79
|
-
if not isinstance(payload, dict):
|
|
80
|
-
raise DispatchStoreError(f"dispatch bundle must be an object: {work_id}")
|
|
81
|
-
return payload
|
|
81
|
+
return _load_dispatch_bundle_from_path(path, work_id)
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
def next_dispatch_step(work_id: str, *, dispatch_dir: Path | None = None) -> dict[str, object]:
|
|
85
85
|
"""Mark the next queued segment as running and return the payload for Hermes."""
|
|
86
86
|
target_dir = default_dispatch_dir() if dispatch_dir is None else dispatch_dir
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
step
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
87
|
+
path = _bundle_path(work_id, target_dir)
|
|
88
|
+
with _exclusive_bundle_lock(path):
|
|
89
|
+
bundle = _load_dispatch_bundle_from_path(path, work_id)
|
|
90
|
+
steps = _steps(bundle)
|
|
91
|
+
for step in steps:
|
|
92
|
+
if step.get("status") in {"queued", "retry_wait"}:
|
|
93
|
+
step["status"] = "running"
|
|
94
|
+
step["updated_at"] = time.time()
|
|
95
|
+
_atomic_write_json(path, bundle)
|
|
96
|
+
return {
|
|
97
|
+
"work_id": work_id,
|
|
98
|
+
"ready": True,
|
|
99
|
+
"step": _public_step(step),
|
|
100
|
+
"remaining": _remaining_count(steps),
|
|
101
|
+
"synthesis_ready": False,
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
"work_id": work_id,
|
|
105
|
+
"ready": False,
|
|
106
|
+
"step": {},
|
|
107
|
+
"remaining": _remaining_count(steps),
|
|
108
|
+
"synthesis_ready": all(step.get("status") == "succeeded" for step in steps),
|
|
109
|
+
}
|
|
108
110
|
|
|
109
111
|
|
|
110
112
|
def record_dispatch_result(
|
|
@@ -118,23 +120,25 @@ def record_dispatch_result(
|
|
|
118
120
|
) -> dict[str, object]:
|
|
119
121
|
"""Store the segment result produced by the Hermes model."""
|
|
120
122
|
target_dir = default_dispatch_dir() if dispatch_dir is None else dispatch_dir
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
step
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
123
|
+
path = _bundle_path(work_id, target_dir)
|
|
124
|
+
with _exclusive_bundle_lock(path):
|
|
125
|
+
bundle = _load_dispatch_bundle_from_path(path, work_id)
|
|
126
|
+
steps = _steps(bundle)
|
|
127
|
+
for step in steps:
|
|
128
|
+
if step.get("step_id") == step_id:
|
|
129
|
+
step["status"] = "succeeded" if succeeded else "failed"
|
|
130
|
+
step["result"] = result
|
|
131
|
+
step["error"] = error
|
|
132
|
+
step["updated_at"] = time.time()
|
|
133
|
+
_atomic_write_json(path, bundle)
|
|
134
|
+
return {
|
|
135
|
+
"work_id": work_id,
|
|
136
|
+
"step_id": step_id,
|
|
137
|
+
"recorded": True,
|
|
138
|
+
"status": step["status"],
|
|
139
|
+
"remaining": _remaining_count(steps),
|
|
140
|
+
"synthesis_ready": all(item.get("status") == "succeeded" for item in steps),
|
|
141
|
+
}
|
|
138
142
|
raise DispatchStoreError(f"dispatch step not found: {work_id}/{step_id}")
|
|
139
143
|
|
|
140
144
|
|
|
@@ -246,6 +250,34 @@ def _bundle_path(work_id: str, dispatch_dir: Path) -> Path:
|
|
|
246
250
|
return dispatch_dir / f"{safe}.json"
|
|
247
251
|
|
|
248
252
|
|
|
253
|
+
def _load_dispatch_bundle_from_path(path: Path, work_id: str) -> dict[str, object]:
|
|
254
|
+
if not path.exists():
|
|
255
|
+
raise DispatchStoreError(f"dispatch bundle not found: {work_id}")
|
|
256
|
+
try:
|
|
257
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
258
|
+
except json.JSONDecodeError as exc:
|
|
259
|
+
raise DispatchStoreError(f"dispatch bundle is invalid JSON: {work_id}") from exc
|
|
260
|
+
if not isinstance(payload, dict):
|
|
261
|
+
raise DispatchStoreError(f"dispatch bundle must be an object: {work_id}")
|
|
262
|
+
return payload
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@contextmanager
|
|
266
|
+
def _exclusive_bundle_lock(path: Path) -> Iterator[None]:
|
|
267
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
268
|
+
if _fcntl is None:
|
|
269
|
+
# Non-POSIX platforms keep atomic rename but skip advisory locking.
|
|
270
|
+
yield
|
|
271
|
+
return
|
|
272
|
+
lock_path = path.with_name(f"{path.name}.lock")
|
|
273
|
+
with lock_path.open("a+b") as lock_file:
|
|
274
|
+
_fcntl.flock(lock_file.fileno(), _fcntl.LOCK_EX)
|
|
275
|
+
try:
|
|
276
|
+
yield
|
|
277
|
+
finally:
|
|
278
|
+
_fcntl.flock(lock_file.fileno(), _fcntl.LOCK_UN)
|
|
279
|
+
|
|
280
|
+
|
|
249
281
|
def _atomic_write_json(path: Path, payload: dict[str, object]) -> None:
|
|
250
282
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
251
283
|
with NamedTemporaryFile("w", encoding="utf-8", dir=path.parent, delete=False) as handle:
|