uricontrol 0.1.16__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.
- uricontrol-0.1.16/LICENSE +21 -0
- uricontrol-0.1.16/PKG-INFO +251 -0
- uricontrol-0.1.16/README.md +219 -0
- uricontrol-0.1.16/core/python/uri_control/__init__.py +42 -0
- uricontrol-0.1.16/core/python/uri_control/cli.py +123 -0
- uricontrol-0.1.16/core/python/uri_control/dispatcher.py +167 -0
- uricontrol-0.1.16/core/python/uri_control/edge/__init__.py +31 -0
- uricontrol-0.1.16/core/python/uri_control/edge/call/__init__.py +6 -0
- uricontrol-0.1.16/core/python/uri_control/edge/call/pipeline.py +71 -0
- uricontrol-0.1.16/core/python/uri_control/edge/call/resolver_hook.py +72 -0
- uricontrol-0.1.16/core/python/uri_control/edge/cli.py +164 -0
- uricontrol-0.1.16/core/python/uri_control/edge/compose.py +157 -0
- uricontrol-0.1.16/core/python/uri_control/edge/env.py +129 -0
- uricontrol-0.1.16/core/python/uri_control/edge/flow/__init__.py +16 -0
- uricontrol-0.1.16/core/python/uri_control/edge/flow/loader.py +46 -0
- uricontrol-0.1.16/core/python/uri_control/edge/flow/runner.py +104 -0
- uricontrol-0.1.16/core/python/uri_control/edge/flow/steps.py +68 -0
- uricontrol-0.1.16/core/python/uri_control/edge/flow_artifacts.py +40 -0
- uricontrol-0.1.16/core/python/uri_control/edge/flow_expect.py +156 -0
- uricontrol-0.1.16/core/python/uri_control/edge/flow_refs.py +176 -0
- uricontrol-0.1.16/core/python/uri_control/edge/http.py +148 -0
- uricontrol-0.1.16/core/python/uri_control/edge/manifest.py +83 -0
- uricontrol-0.1.16/core/python/uri_control/edge/risk_policy.py +57 -0
- uricontrol-0.1.16/core/python/uri_control/edge/runtime.py +270 -0
- uricontrol-0.1.16/core/python/uri_control/envelope.py +25 -0
- uricontrol-0.1.16/core/python/uri_control/errors.py +22 -0
- uricontrol-0.1.16/core/python/uri_control/event_store.py +65 -0
- uricontrol-0.1.16/core/python/uri_control/handlers.py +144 -0
- uricontrol-0.1.16/core/python/uri_control/models.py +127 -0
- uricontrol-0.1.16/core/python/uri_control/parser.py +45 -0
- uricontrol-0.1.16/core/python/uri_control/policy.py +71 -0
- uricontrol-0.1.16/core/python/uri_control/projection.py +70 -0
- uricontrol-0.1.16/core/python/uri_control/registry.py +233 -0
- uricontrol-0.1.16/core/python/uri_control/resolver.py +11 -0
- uricontrol-0.1.16/core/python/uri_control/runtime_handlers.py +89 -0
- uricontrol-0.1.16/core/python/uri_control/transport.py +11 -0
- uricontrol-0.1.16/core/python/uri_control/urisys_handlers.py +120 -0
- uricontrol-0.1.16/core/python/uri_control/urisys_handlers_test_support.py +29 -0
- uricontrol-0.1.16/core/python/uricontrol.egg-info/PKG-INFO +251 -0
- uricontrol-0.1.16/core/python/uricontrol.egg-info/SOURCES.txt +59 -0
- uricontrol-0.1.16/core/python/uricontrol.egg-info/dependency_links.txt +1 -0
- uricontrol-0.1.16/core/python/uricontrol.egg-info/entry_points.txt +2 -0
- uricontrol-0.1.16/core/python/uricontrol.egg-info/requires.txt +19 -0
- uricontrol-0.1.16/core/python/uricontrol.egg-info/top_level.txt +1 -0
- uricontrol-0.1.16/pyproject.toml +101 -0
- uricontrol-0.1.16/setup.cfg +4 -0
- uricontrol-0.1.16/tests/test_dispatcher.py +49 -0
- uricontrol-0.1.16/tests/test_envelope.py +24 -0
- uricontrol-0.1.16/tests/test_flow_expect.py +32 -0
- uricontrol-0.1.16/tests/test_flow_inputs.py +20 -0
- uricontrol-0.1.16/tests/test_flow_refs.py +85 -0
- uricontrol-0.1.16/tests/test_flow_runner.py +22 -0
- uricontrol-0.1.16/tests/test_operation_policy.py +72 -0
- uricontrol-0.1.16/tests/test_parser.py +11 -0
- uricontrol-0.1.16/tests/test_register.py +92 -0
- uricontrol-0.1.16/tests/test_registry.py +28 -0
- uricontrol-0.1.16/tests/test_resolver.py +139 -0
- uricontrol-0.1.16/tests/test_risk_policy.py +29 -0
- uricontrol-0.1.16/tests/test_runtime_handlers.py +73 -0
- uricontrol-0.1.16/tests/test_transport.py +76 -0
- uricontrol-0.1.16/tests/test_urisys_handlers.py +90 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 uricore contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: uricontrol
|
|
3
|
+
Version: 0.1.16
|
|
4
|
+
Summary: Thin URI-native control-plane core for software, services and devices.
|
|
5
|
+
Author: uricontrol contributors
|
|
6
|
+
Author-email: Tom Sapletta <tom@sapletta.com>
|
|
7
|
+
License-Expression: Apache-2.0
|
|
8
|
+
Keywords: uri,control-plane,cqrs,event-sourcing,devices,automation
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: PyYAML>=6.0
|
|
18
|
+
Requires-Dist: uriguard>=0.1.0
|
|
19
|
+
Provides-Extra: mesh
|
|
20
|
+
Requires-Dist: uriresolver>=0.1.0; extra == "mesh"
|
|
21
|
+
Requires-Dist: uritransport>=0.1.0; extra == "mesh"
|
|
22
|
+
Provides-Extra: mqtt
|
|
23
|
+
Requires-Dist: uriresolver>=0.1.0; extra == "mqtt"
|
|
24
|
+
Requires-Dist: uritransport[mqtt]>=0.1.0; extra == "mqtt"
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
27
|
+
Requires-Dist: ruff>=0.6.0; extra == "dev"
|
|
28
|
+
Requires-Dist: goal>=2.1.0; python_version >= "3.12" and extra == "dev"
|
|
29
|
+
Requires-Dist: costs>=0.1.20; extra == "dev"
|
|
30
|
+
Requires-Dist: pfix>=0.1.60; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# uricontrol
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## AI Cost Tracking
|
|
37
|
+
|
|
38
|
+
   
|
|
39
|
+
  
|
|
40
|
+
|
|
41
|
+
- 🤖 **LLM usage:** $0.7622 (29 commits)
|
|
42
|
+
- 👤 **Human dev:** ~$1710 (17.1h @ $100/h, 30min dedup)
|
|
43
|
+
|
|
44
|
+
Generated on 2026-10-04 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
`uricontrol` is a small URI-native control-plane core. It provides a thin deterministic layer over software, services, devices and operating-system capabilities.
|
|
49
|
+
|
|
50
|
+
The Python distribution is named **`uricontrol`** (renamed from `uricore` — that PyPI name is squatted by an unrelated project), while the runtime module is **`uri_control`**:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from uri_control import CapabilityRegistry, UriControlRuntime, JsonlEventStore
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**URI intent routing** (resolve targets, HTTP/MQTT delegate, wire envelopes) lives in the sibling package **[`uriresolver`](https://github.com/tellmesh/uriresolver)** (`uri_resolver`). `uri_control.resolver`, `.transport`, and `.envelope` are compatibility shims over `uri_resolver`.
|
|
57
|
+
|
|
58
|
+
The repository also contains placeholder SDK folders for Node/TypeScript, Go and PHP. Those SDKs intentionally remain thin: the source of truth is the URI, the manifest, and the protobuf-style command/event envelope.
|
|
59
|
+
|
|
60
|
+
## Edge runtime (`uri_control.edge`)
|
|
61
|
+
|
|
62
|
+
Edge HTTP server, flow runner, and pack composition — formerly the separate **``uri_control.edge``** package (removed 2026-06):
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from uri_control.edge.runtime import Runtime, run_flow
|
|
66
|
+
from uri_control.edge.compose import build_runtime
|
|
67
|
+
from uri_control.edge.http import serve
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Used by edge CLIs (`urirdpedge`, `urikvmedge`, …) and orchestrated by **[urisys](https://github.com/tellmesh/urisys)**.
|
|
71
|
+
|
|
72
|
+
`Runtime.call` resolves the URI via **[`uriresolver`](https://github.com/tellmesh/uriresolver)**
|
|
73
|
+
(`resolve_uri` → optional transport delegation), then — for local execution —
|
|
74
|
+
enforces resolver-declared policy (`uri_resolver.policy.check_operation_limits` /
|
|
75
|
+
`check_shell_policy`) **before** loading the handler, and emits a `*.policy_denied`
|
|
76
|
+
event on violation. Supported handler schemes: `python://`, `http(s)://`, `node://`,
|
|
77
|
+
`urisys://flow/<id>` (built-in process flow runner) and `runtime://resolver/<op>`
|
|
78
|
+
(resolver-bound placeholder: mock under dry-run/`environment: mock`, else a clear
|
|
79
|
+
`no_runtime_binding` error).
|
|
80
|
+
|
|
81
|
+
## Architecture
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
URI
|
|
85
|
+
↓
|
|
86
|
+
Capability manifest
|
|
87
|
+
↓
|
|
88
|
+
Policy decision
|
|
89
|
+
↓
|
|
90
|
+
Command / Query dispatch
|
|
91
|
+
↓
|
|
92
|
+
Handler in Python / Node / Go / PHP / any runtime
|
|
93
|
+
↓
|
|
94
|
+
Append-only event log
|
|
95
|
+
↓
|
|
96
|
+
Projection / status / result
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Core rules:
|
|
100
|
+
|
|
101
|
+
1. URI does not execute.
|
|
102
|
+
2. URI identifies a resource, operation or intent.
|
|
103
|
+
3. Manifest declares capabilities, handlers and policy.
|
|
104
|
+
4. Handler performs the technical work.
|
|
105
|
+
5. Event store records facts.
|
|
106
|
+
6. Projection builds read models from events.
|
|
107
|
+
|
|
108
|
+
## Repository layout
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
uricore/
|
|
112
|
+
core/
|
|
113
|
+
python/
|
|
114
|
+
uri_control/
|
|
115
|
+
parser.py
|
|
116
|
+
registry.py
|
|
117
|
+
dispatcher.py
|
|
118
|
+
event_store.py
|
|
119
|
+
projection.py
|
|
120
|
+
policy.py
|
|
121
|
+
node/uri-control/
|
|
122
|
+
go/uricontrol/
|
|
123
|
+
php/UriControl/
|
|
124
|
+
contracts/
|
|
125
|
+
proto/uricore/v1/envelope.proto
|
|
126
|
+
examples/
|
|
127
|
+
packs/browser_mock/
|
|
128
|
+
packs/systemd_mock/
|
|
129
|
+
call_browser_mock.py
|
|
130
|
+
call_systemd_mock.py
|
|
131
|
+
tests/
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Install locally
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
cd uricore
|
|
138
|
+
python -m pip install -e .
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
For tests:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
python -m pip install -e .[dev]
|
|
145
|
+
python -m pytest
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## CLI examples
|
|
149
|
+
|
|
150
|
+
Explain a URI against a manifest:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
uricore explain browser://default/page/open \
|
|
154
|
+
--manifest examples/packs/browser_mock/manifest.yaml
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Call a command:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
uricore call browser://default/page/open \
|
|
161
|
+
--manifest examples/packs/browser_mock/manifest.yaml \
|
|
162
|
+
--payload '{"url":"https://example.com","wait_until_loaded":true}' \
|
|
163
|
+
--approve \
|
|
164
|
+
--events output/events.jsonl
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Query a projection from JSONL events:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
uricore projection latest --events output/events.jsonl
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Minimal Python usage
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from uri_control import CapabilityRegistry, JsonlEventStore, UriControlRuntime
|
|
177
|
+
|
|
178
|
+
registry = CapabilityRegistry.from_manifest_files([
|
|
179
|
+
"examples/packs/browser_mock/manifest.yaml"
|
|
180
|
+
])
|
|
181
|
+
|
|
182
|
+
runtime = UriControlRuntime(
|
|
183
|
+
registry=registry,
|
|
184
|
+
event_store=JsonlEventStore("output/events.jsonl"),
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
result = runtime.call(
|
|
188
|
+
"browser://default/page/open",
|
|
189
|
+
payload={"url": "https://example.com"},
|
|
190
|
+
context={"approved": True, "environment": "mock"},
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
print(result.ok, result.result)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Manifest example
|
|
197
|
+
|
|
198
|
+
```yaml
|
|
199
|
+
id: browser-mock-pack
|
|
200
|
+
version: 1
|
|
201
|
+
scheme: browser
|
|
202
|
+
|
|
203
|
+
uri_patterns:
|
|
204
|
+
- pattern: browser://{session}/page/open
|
|
205
|
+
kind: command
|
|
206
|
+
operation: open_page
|
|
207
|
+
command_type: browser.v1.OpenPageCommand
|
|
208
|
+
success_event_type: browser.v1.PageOpenedEvent
|
|
209
|
+
side_effects: true
|
|
210
|
+
approval: required
|
|
211
|
+
|
|
212
|
+
handlers:
|
|
213
|
+
python:
|
|
214
|
+
open_page: python://examples.packs.browser_mock.handlers:open_page
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Relationship to `urisys`
|
|
218
|
+
|
|
219
|
+
`uricore` should stay small and deterministic. A future `urisys` project can reuse it and add:
|
|
220
|
+
|
|
221
|
+
- concrete device/software packs,
|
|
222
|
+
- HTTP or gRPC gateway,
|
|
223
|
+
- dashboard,
|
|
224
|
+
- scheduler,
|
|
225
|
+
- LLM planner,
|
|
226
|
+
- policy UI,
|
|
227
|
+
- multi-language runners.
|
|
228
|
+
|
|
229
|
+
`uricore` should not contain those heavier orchestration concerns.
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
## Ekosystem TellMesh
|
|
233
|
+
|
|
234
|
+
Orchestrator: **[urisys](https://github.com/tellmesh/urisys)** · Mapa: **[MESH.md](https://github.com/tellmesh/urisys/blob/main/docs/MESH.md)** · Model: **[ECOSYSTEM.md](https://github.com/tellmesh/urisys/blob/main/docs/ECOSYSTEM.md)**
|
|
235
|
+
|
|
236
|
+
| Pole | Wartość |
|
|
237
|
+
|------|---------|
|
|
238
|
+
| **Warstwa** | Control plane + edge runtime |
|
|
239
|
+
| **Moduł** | `uri_control`, `uri_control.edge` |
|
|
240
|
+
| **Zależności** | `uriguard` |
|
|
241
|
+
| **Rola** | CapabilityRegistry, policy, handlers, `Runtime`, `compose`, `http.serve` |
|
|
242
|
+
| **Uwaga** | Zastępuje legacy PyPI `uricore` i `urisysedge` (usunięty 2026-06) |
|
|
243
|
+
|
|
244
|
+
Runtime edge: **`uri_control.edge`** w pakiecie **`uricontrol`** (legacy PyPI `uricore` / `urisysedge` usunięty 2026-06).
|
|
245
|
+
Resolver intencji: **`uriresolver`** (`uri_resolver`) + transport w **`uritransport`**; policy gate: **`uriguard`** (`uri_guard`).
|
|
246
|
+
|
|
247
|
+
<!-- end-ecosystem -->
|
|
248
|
+
|
|
249
|
+
## License
|
|
250
|
+
|
|
251
|
+
Licensed under Apache-2.0.
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# uricontrol
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## AI Cost Tracking
|
|
5
|
+
|
|
6
|
+
   
|
|
7
|
+
  
|
|
8
|
+
|
|
9
|
+
- 🤖 **LLM usage:** $0.7622 (29 commits)
|
|
10
|
+
- 👤 **Human dev:** ~$1710 (17.1h @ $100/h, 30min dedup)
|
|
11
|
+
|
|
12
|
+
Generated on 2026-10-04 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
`uricontrol` is a small URI-native control-plane core. It provides a thin deterministic layer over software, services, devices and operating-system capabilities.
|
|
17
|
+
|
|
18
|
+
The Python distribution is named **`uricontrol`** (renamed from `uricore` — that PyPI name is squatted by an unrelated project), while the runtime module is **`uri_control`**:
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from uri_control import CapabilityRegistry, UriControlRuntime, JsonlEventStore
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**URI intent routing** (resolve targets, HTTP/MQTT delegate, wire envelopes) lives in the sibling package **[`uriresolver`](https://github.com/tellmesh/uriresolver)** (`uri_resolver`). `uri_control.resolver`, `.transport`, and `.envelope` are compatibility shims over `uri_resolver`.
|
|
25
|
+
|
|
26
|
+
The repository also contains placeholder SDK folders for Node/TypeScript, Go and PHP. Those SDKs intentionally remain thin: the source of truth is the URI, the manifest, and the protobuf-style command/event envelope.
|
|
27
|
+
|
|
28
|
+
## Edge runtime (`uri_control.edge`)
|
|
29
|
+
|
|
30
|
+
Edge HTTP server, flow runner, and pack composition — formerly the separate **``uri_control.edge``** package (removed 2026-06):
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from uri_control.edge.runtime import Runtime, run_flow
|
|
34
|
+
from uri_control.edge.compose import build_runtime
|
|
35
|
+
from uri_control.edge.http import serve
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Used by edge CLIs (`urirdpedge`, `urikvmedge`, …) and orchestrated by **[urisys](https://github.com/tellmesh/urisys)**.
|
|
39
|
+
|
|
40
|
+
`Runtime.call` resolves the URI via **[`uriresolver`](https://github.com/tellmesh/uriresolver)**
|
|
41
|
+
(`resolve_uri` → optional transport delegation), then — for local execution —
|
|
42
|
+
enforces resolver-declared policy (`uri_resolver.policy.check_operation_limits` /
|
|
43
|
+
`check_shell_policy`) **before** loading the handler, and emits a `*.policy_denied`
|
|
44
|
+
event on violation. Supported handler schemes: `python://`, `http(s)://`, `node://`,
|
|
45
|
+
`urisys://flow/<id>` (built-in process flow runner) and `runtime://resolver/<op>`
|
|
46
|
+
(resolver-bound placeholder: mock under dry-run/`environment: mock`, else a clear
|
|
47
|
+
`no_runtime_binding` error).
|
|
48
|
+
|
|
49
|
+
## Architecture
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
URI
|
|
53
|
+
↓
|
|
54
|
+
Capability manifest
|
|
55
|
+
↓
|
|
56
|
+
Policy decision
|
|
57
|
+
↓
|
|
58
|
+
Command / Query dispatch
|
|
59
|
+
↓
|
|
60
|
+
Handler in Python / Node / Go / PHP / any runtime
|
|
61
|
+
↓
|
|
62
|
+
Append-only event log
|
|
63
|
+
↓
|
|
64
|
+
Projection / status / result
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Core rules:
|
|
68
|
+
|
|
69
|
+
1. URI does not execute.
|
|
70
|
+
2. URI identifies a resource, operation or intent.
|
|
71
|
+
3. Manifest declares capabilities, handlers and policy.
|
|
72
|
+
4. Handler performs the technical work.
|
|
73
|
+
5. Event store records facts.
|
|
74
|
+
6. Projection builds read models from events.
|
|
75
|
+
|
|
76
|
+
## Repository layout
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
uricore/
|
|
80
|
+
core/
|
|
81
|
+
python/
|
|
82
|
+
uri_control/
|
|
83
|
+
parser.py
|
|
84
|
+
registry.py
|
|
85
|
+
dispatcher.py
|
|
86
|
+
event_store.py
|
|
87
|
+
projection.py
|
|
88
|
+
policy.py
|
|
89
|
+
node/uri-control/
|
|
90
|
+
go/uricontrol/
|
|
91
|
+
php/UriControl/
|
|
92
|
+
contracts/
|
|
93
|
+
proto/uricore/v1/envelope.proto
|
|
94
|
+
examples/
|
|
95
|
+
packs/browser_mock/
|
|
96
|
+
packs/systemd_mock/
|
|
97
|
+
call_browser_mock.py
|
|
98
|
+
call_systemd_mock.py
|
|
99
|
+
tests/
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Install locally
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
cd uricore
|
|
106
|
+
python -m pip install -e .
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
For tests:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
python -m pip install -e .[dev]
|
|
113
|
+
python -m pytest
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## CLI examples
|
|
117
|
+
|
|
118
|
+
Explain a URI against a manifest:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
uricore explain browser://default/page/open \
|
|
122
|
+
--manifest examples/packs/browser_mock/manifest.yaml
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Call a command:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
uricore call browser://default/page/open \
|
|
129
|
+
--manifest examples/packs/browser_mock/manifest.yaml \
|
|
130
|
+
--payload '{"url":"https://example.com","wait_until_loaded":true}' \
|
|
131
|
+
--approve \
|
|
132
|
+
--events output/events.jsonl
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Query a projection from JSONL events:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
uricore projection latest --events output/events.jsonl
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Minimal Python usage
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from uri_control import CapabilityRegistry, JsonlEventStore, UriControlRuntime
|
|
145
|
+
|
|
146
|
+
registry = CapabilityRegistry.from_manifest_files([
|
|
147
|
+
"examples/packs/browser_mock/manifest.yaml"
|
|
148
|
+
])
|
|
149
|
+
|
|
150
|
+
runtime = UriControlRuntime(
|
|
151
|
+
registry=registry,
|
|
152
|
+
event_store=JsonlEventStore("output/events.jsonl"),
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
result = runtime.call(
|
|
156
|
+
"browser://default/page/open",
|
|
157
|
+
payload={"url": "https://example.com"},
|
|
158
|
+
context={"approved": True, "environment": "mock"},
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
print(result.ok, result.result)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Manifest example
|
|
165
|
+
|
|
166
|
+
```yaml
|
|
167
|
+
id: browser-mock-pack
|
|
168
|
+
version: 1
|
|
169
|
+
scheme: browser
|
|
170
|
+
|
|
171
|
+
uri_patterns:
|
|
172
|
+
- pattern: browser://{session}/page/open
|
|
173
|
+
kind: command
|
|
174
|
+
operation: open_page
|
|
175
|
+
command_type: browser.v1.OpenPageCommand
|
|
176
|
+
success_event_type: browser.v1.PageOpenedEvent
|
|
177
|
+
side_effects: true
|
|
178
|
+
approval: required
|
|
179
|
+
|
|
180
|
+
handlers:
|
|
181
|
+
python:
|
|
182
|
+
open_page: python://examples.packs.browser_mock.handlers:open_page
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Relationship to `urisys`
|
|
186
|
+
|
|
187
|
+
`uricore` should stay small and deterministic. A future `urisys` project can reuse it and add:
|
|
188
|
+
|
|
189
|
+
- concrete device/software packs,
|
|
190
|
+
- HTTP or gRPC gateway,
|
|
191
|
+
- dashboard,
|
|
192
|
+
- scheduler,
|
|
193
|
+
- LLM planner,
|
|
194
|
+
- policy UI,
|
|
195
|
+
- multi-language runners.
|
|
196
|
+
|
|
197
|
+
`uricore` should not contain those heavier orchestration concerns.
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
## Ekosystem TellMesh
|
|
201
|
+
|
|
202
|
+
Orchestrator: **[urisys](https://github.com/tellmesh/urisys)** · Mapa: **[MESH.md](https://github.com/tellmesh/urisys/blob/main/docs/MESH.md)** · Model: **[ECOSYSTEM.md](https://github.com/tellmesh/urisys/blob/main/docs/ECOSYSTEM.md)**
|
|
203
|
+
|
|
204
|
+
| Pole | Wartość |
|
|
205
|
+
|------|---------|
|
|
206
|
+
| **Warstwa** | Control plane + edge runtime |
|
|
207
|
+
| **Moduł** | `uri_control`, `uri_control.edge` |
|
|
208
|
+
| **Zależności** | `uriguard` |
|
|
209
|
+
| **Rola** | CapabilityRegistry, policy, handlers, `Runtime`, `compose`, `http.serve` |
|
|
210
|
+
| **Uwaga** | Zastępuje legacy PyPI `uricore` i `urisysedge` (usunięty 2026-06) |
|
|
211
|
+
|
|
212
|
+
Runtime edge: **`uri_control.edge`** w pakiecie **`uricontrol`** (legacy PyPI `uricore` / `urisysedge` usunięty 2026-06).
|
|
213
|
+
Resolver intencji: **`uriresolver`** (`uri_resolver`) + transport w **`uritransport`**; policy gate: **`uriguard`** (`uri_guard`).
|
|
214
|
+
|
|
215
|
+
<!-- end-ecosystem -->
|
|
216
|
+
|
|
217
|
+
## License
|
|
218
|
+
|
|
219
|
+
Licensed under Apache-2.0.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""uricore Python runtime module.
|
|
2
|
+
|
|
3
|
+
The Python distribution is named ``uricore``. The importable runtime module is
|
|
4
|
+
``uri_control`` to keep the API descriptive and language-neutral.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from . import edge # noqa: F401 (migrated from urisysedge)
|
|
8
|
+
from .dispatcher import UriControlRuntime
|
|
9
|
+
from .event_store import EventStore, InMemoryEventStore, JsonlEventStore
|
|
10
|
+
from .handlers import load_handler, load_python_handler, make_remote_handler
|
|
11
|
+
from .models import (
|
|
12
|
+
CapabilityManifest,
|
|
13
|
+
DispatchResult,
|
|
14
|
+
EventEnvelope,
|
|
15
|
+
ParsedUri,
|
|
16
|
+
PolicyDecision,
|
|
17
|
+
Route,
|
|
18
|
+
)
|
|
19
|
+
from .parser import parse_uri
|
|
20
|
+
from .policy import PolicyEngine
|
|
21
|
+
from .projection import ProjectionBuilder
|
|
22
|
+
from .registry import CapabilityRegistry
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"CapabilityManifest",
|
|
26
|
+
"CapabilityRegistry",
|
|
27
|
+
"DispatchResult",
|
|
28
|
+
"EventEnvelope",
|
|
29
|
+
"EventStore",
|
|
30
|
+
"InMemoryEventStore",
|
|
31
|
+
"JsonlEventStore",
|
|
32
|
+
"load_handler",
|
|
33
|
+
"load_python_handler",
|
|
34
|
+
"make_remote_handler",
|
|
35
|
+
"ParsedUri",
|
|
36
|
+
"PolicyDecision",
|
|
37
|
+
"PolicyEngine",
|
|
38
|
+
"ProjectionBuilder",
|
|
39
|
+
"Route",
|
|
40
|
+
"UriControlRuntime",
|
|
41
|
+
"parse_uri",
|
|
42
|
+
]
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from .dispatcher import UriControlRuntime
|
|
9
|
+
from .event_store import JsonlEventStore
|
|
10
|
+
from .projection import ProjectionBuilder
|
|
11
|
+
from .registry import CapabilityRegistry
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _load_payload(value: str | None) -> dict[str, Any]:
|
|
15
|
+
if not value:
|
|
16
|
+
return {}
|
|
17
|
+
candidate = Path(value)
|
|
18
|
+
if candidate.exists():
|
|
19
|
+
return json.loads(candidate.read_text(encoding="utf-8"))
|
|
20
|
+
return json.loads(value)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _registry_from_args(args: argparse.Namespace) -> CapabilityRegistry:
|
|
24
|
+
if not args.manifest:
|
|
25
|
+
raise SystemExit("At least one --manifest path is required.")
|
|
26
|
+
return CapabilityRegistry.from_manifest_files(args.manifest)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def cmd_explain(args: argparse.Namespace) -> int:
|
|
30
|
+
registry = _registry_from_args(args)
|
|
31
|
+
print(json.dumps(registry.explain(args.uri), ensure_ascii=False, indent=2))
|
|
32
|
+
return 0
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def cmd_call(args: argparse.Namespace) -> int:
|
|
36
|
+
registry = _registry_from_args(args)
|
|
37
|
+
store = JsonlEventStore(args.events)
|
|
38
|
+
runtime = UriControlRuntime(registry=registry, event_store=store)
|
|
39
|
+
result = runtime.call(
|
|
40
|
+
args.uri,
|
|
41
|
+
payload=_load_payload(args.payload),
|
|
42
|
+
context={"approved": args.approve, "environment": args.environment},
|
|
43
|
+
)
|
|
44
|
+
print(json.dumps(result.to_dict(), ensure_ascii=False, indent=2))
|
|
45
|
+
return 0 if result.ok else 2
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def cmd_list(args: argparse.Namespace) -> int:
|
|
49
|
+
registry = _registry_from_args(args)
|
|
50
|
+
data = [
|
|
51
|
+
{
|
|
52
|
+
"manifest_id": route.manifest_id,
|
|
53
|
+
"scheme": route.scheme,
|
|
54
|
+
"pattern": route.pattern,
|
|
55
|
+
"kind": route.kind,
|
|
56
|
+
"operation": route.operation,
|
|
57
|
+
"side_effects": route.side_effects,
|
|
58
|
+
"approval": route.approval,
|
|
59
|
+
}
|
|
60
|
+
for route in registry.routes
|
|
61
|
+
]
|
|
62
|
+
print(json.dumps(data, ensure_ascii=False, indent=2))
|
|
63
|
+
return 0
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def cmd_projection_latest(args: argparse.Namespace) -> int:
|
|
67
|
+
store = JsonlEventStore(args.events)
|
|
68
|
+
builder = ProjectionBuilder(store)
|
|
69
|
+
print(json.dumps(builder.latest_by_source_uri(), ensure_ascii=False, indent=2))
|
|
70
|
+
return 0
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def cmd_projection_status(args: argparse.Namespace) -> int:
|
|
74
|
+
store = JsonlEventStore(args.events)
|
|
75
|
+
builder = ProjectionBuilder(store)
|
|
76
|
+
print(json.dumps(builder.status_by_source_uri(), ensure_ascii=False, indent=2))
|
|
77
|
+
return 0
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
81
|
+
parser = argparse.ArgumentParser(prog="uricontrol")
|
|
82
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
83
|
+
|
|
84
|
+
explain = sub.add_parser("explain", help="Explain how a URI matches a manifest route.")
|
|
85
|
+
explain.add_argument("uri")
|
|
86
|
+
explain.add_argument("--manifest", action="append", required=True)
|
|
87
|
+
explain.set_defaults(func=cmd_explain)
|
|
88
|
+
|
|
89
|
+
call = sub.add_parser("call", help="Call a URI through policy, handler and event store.")
|
|
90
|
+
call.add_argument("uri")
|
|
91
|
+
call.add_argument("--manifest", action="append", required=True)
|
|
92
|
+
call.add_argument("--payload", default="{}", help="JSON string or path to JSON file.")
|
|
93
|
+
call.add_argument("--events", default="output/events.jsonl")
|
|
94
|
+
call.add_argument("--environment", default="local")
|
|
95
|
+
call.add_argument("--approve", action="store_true")
|
|
96
|
+
call.set_defaults(func=cmd_call)
|
|
97
|
+
|
|
98
|
+
list_cmd = sub.add_parser("list", help="List capability routes from manifests.")
|
|
99
|
+
list_cmd.add_argument("--manifest", action="append", required=True)
|
|
100
|
+
list_cmd.set_defaults(func=cmd_list)
|
|
101
|
+
|
|
102
|
+
projection = sub.add_parser("projection", help="Build projections from events.")
|
|
103
|
+
projection_sub = projection.add_subparsers(dest="projection_command", required=True)
|
|
104
|
+
|
|
105
|
+
latest = projection_sub.add_parser("latest", help="Latest event by source URI.")
|
|
106
|
+
latest.add_argument("--events", default="output/events.jsonl")
|
|
107
|
+
latest.set_defaults(func=cmd_projection_latest)
|
|
108
|
+
|
|
109
|
+
status = projection_sub.add_parser("status", help="Status projection by source URI.")
|
|
110
|
+
status.add_argument("--events", default="output/events.jsonl")
|
|
111
|
+
status.set_defaults(func=cmd_projection_status)
|
|
112
|
+
|
|
113
|
+
return parser
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def main(argv: list[str] | None = None) -> int:
|
|
117
|
+
parser = build_parser()
|
|
118
|
+
args = parser.parse_args(argv)
|
|
119
|
+
return int(args.func(args))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
if __name__ == "__main__": # pragma: no cover
|
|
123
|
+
raise SystemExit(main())
|