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.
Files changed (61) hide show
  1. uricontrol-0.1.16/LICENSE +21 -0
  2. uricontrol-0.1.16/PKG-INFO +251 -0
  3. uricontrol-0.1.16/README.md +219 -0
  4. uricontrol-0.1.16/core/python/uri_control/__init__.py +42 -0
  5. uricontrol-0.1.16/core/python/uri_control/cli.py +123 -0
  6. uricontrol-0.1.16/core/python/uri_control/dispatcher.py +167 -0
  7. uricontrol-0.1.16/core/python/uri_control/edge/__init__.py +31 -0
  8. uricontrol-0.1.16/core/python/uri_control/edge/call/__init__.py +6 -0
  9. uricontrol-0.1.16/core/python/uri_control/edge/call/pipeline.py +71 -0
  10. uricontrol-0.1.16/core/python/uri_control/edge/call/resolver_hook.py +72 -0
  11. uricontrol-0.1.16/core/python/uri_control/edge/cli.py +164 -0
  12. uricontrol-0.1.16/core/python/uri_control/edge/compose.py +157 -0
  13. uricontrol-0.1.16/core/python/uri_control/edge/env.py +129 -0
  14. uricontrol-0.1.16/core/python/uri_control/edge/flow/__init__.py +16 -0
  15. uricontrol-0.1.16/core/python/uri_control/edge/flow/loader.py +46 -0
  16. uricontrol-0.1.16/core/python/uri_control/edge/flow/runner.py +104 -0
  17. uricontrol-0.1.16/core/python/uri_control/edge/flow/steps.py +68 -0
  18. uricontrol-0.1.16/core/python/uri_control/edge/flow_artifacts.py +40 -0
  19. uricontrol-0.1.16/core/python/uri_control/edge/flow_expect.py +156 -0
  20. uricontrol-0.1.16/core/python/uri_control/edge/flow_refs.py +176 -0
  21. uricontrol-0.1.16/core/python/uri_control/edge/http.py +148 -0
  22. uricontrol-0.1.16/core/python/uri_control/edge/manifest.py +83 -0
  23. uricontrol-0.1.16/core/python/uri_control/edge/risk_policy.py +57 -0
  24. uricontrol-0.1.16/core/python/uri_control/edge/runtime.py +270 -0
  25. uricontrol-0.1.16/core/python/uri_control/envelope.py +25 -0
  26. uricontrol-0.1.16/core/python/uri_control/errors.py +22 -0
  27. uricontrol-0.1.16/core/python/uri_control/event_store.py +65 -0
  28. uricontrol-0.1.16/core/python/uri_control/handlers.py +144 -0
  29. uricontrol-0.1.16/core/python/uri_control/models.py +127 -0
  30. uricontrol-0.1.16/core/python/uri_control/parser.py +45 -0
  31. uricontrol-0.1.16/core/python/uri_control/policy.py +71 -0
  32. uricontrol-0.1.16/core/python/uri_control/projection.py +70 -0
  33. uricontrol-0.1.16/core/python/uri_control/registry.py +233 -0
  34. uricontrol-0.1.16/core/python/uri_control/resolver.py +11 -0
  35. uricontrol-0.1.16/core/python/uri_control/runtime_handlers.py +89 -0
  36. uricontrol-0.1.16/core/python/uri_control/transport.py +11 -0
  37. uricontrol-0.1.16/core/python/uri_control/urisys_handlers.py +120 -0
  38. uricontrol-0.1.16/core/python/uri_control/urisys_handlers_test_support.py +29 -0
  39. uricontrol-0.1.16/core/python/uricontrol.egg-info/PKG-INFO +251 -0
  40. uricontrol-0.1.16/core/python/uricontrol.egg-info/SOURCES.txt +59 -0
  41. uricontrol-0.1.16/core/python/uricontrol.egg-info/dependency_links.txt +1 -0
  42. uricontrol-0.1.16/core/python/uricontrol.egg-info/entry_points.txt +2 -0
  43. uricontrol-0.1.16/core/python/uricontrol.egg-info/requires.txt +19 -0
  44. uricontrol-0.1.16/core/python/uricontrol.egg-info/top_level.txt +1 -0
  45. uricontrol-0.1.16/pyproject.toml +101 -0
  46. uricontrol-0.1.16/setup.cfg +4 -0
  47. uricontrol-0.1.16/tests/test_dispatcher.py +49 -0
  48. uricontrol-0.1.16/tests/test_envelope.py +24 -0
  49. uricontrol-0.1.16/tests/test_flow_expect.py +32 -0
  50. uricontrol-0.1.16/tests/test_flow_inputs.py +20 -0
  51. uricontrol-0.1.16/tests/test_flow_refs.py +85 -0
  52. uricontrol-0.1.16/tests/test_flow_runner.py +22 -0
  53. uricontrol-0.1.16/tests/test_operation_policy.py +72 -0
  54. uricontrol-0.1.16/tests/test_parser.py +11 -0
  55. uricontrol-0.1.16/tests/test_register.py +92 -0
  56. uricontrol-0.1.16/tests/test_registry.py +28 -0
  57. uricontrol-0.1.16/tests/test_resolver.py +139 -0
  58. uricontrol-0.1.16/tests/test_risk_policy.py +29 -0
  59. uricontrol-0.1.16/tests/test_runtime_handlers.py +73 -0
  60. uricontrol-0.1.16/tests/test_transport.py +76 -0
  61. 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
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.1.16-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
39
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$0.76-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-17.1h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
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
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.1.16-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$0.76-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-17.1h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
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())