mrok 0.2.3__py3-none-any.whl → 0.4.0__py3-none-any.whl

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 (56) hide show
  1. mrok/agent/devtools/__init__.py +0 -0
  2. mrok/agent/devtools/__main__.py +34 -0
  3. mrok/agent/devtools/inspector/__init__.py +0 -0
  4. mrok/agent/devtools/inspector/__main__.py +25 -0
  5. mrok/agent/devtools/inspector/app.py +556 -0
  6. mrok/agent/devtools/inspector/server.py +18 -0
  7. mrok/agent/sidecar/app.py +9 -10
  8. mrok/agent/sidecar/main.py +35 -16
  9. mrok/agent/ziticorn.py +27 -18
  10. mrok/cli/commands/__init__.py +2 -1
  11. mrok/cli/commands/admin/list/instances.py +24 -4
  12. mrok/cli/commands/admin/register/extensions.py +2 -2
  13. mrok/cli/commands/admin/register/instances.py +3 -3
  14. mrok/cli/commands/admin/unregister/extensions.py +2 -2
  15. mrok/cli/commands/admin/unregister/instances.py +2 -2
  16. mrok/cli/commands/agent/__init__.py +2 -0
  17. mrok/cli/commands/agent/dev/__init__.py +7 -0
  18. mrok/cli/commands/agent/dev/console.py +25 -0
  19. mrok/cli/commands/agent/dev/web.py +37 -0
  20. mrok/cli/commands/agent/run/asgi.py +35 -16
  21. mrok/cli/commands/agent/run/sidecar.py +29 -13
  22. mrok/cli/commands/agent/utils.py +5 -0
  23. mrok/cli/commands/controller/run.py +1 -5
  24. mrok/cli/commands/proxy/__init__.py +6 -0
  25. mrok/cli/commands/proxy/run.py +49 -0
  26. mrok/cli/utils.py +5 -0
  27. mrok/conf.py +6 -0
  28. mrok/controller/auth.py +2 -2
  29. mrok/controller/routes/extensions.py +9 -7
  30. mrok/datastructures.py +159 -0
  31. mrok/http/config.py +3 -6
  32. mrok/http/constants.py +22 -0
  33. mrok/http/forwarder.py +62 -23
  34. mrok/http/lifespan.py +29 -0
  35. mrok/http/middlewares.py +143 -0
  36. mrok/http/types.py +43 -0
  37. mrok/http/utils.py +90 -0
  38. mrok/logging.py +22 -0
  39. mrok/master.py +269 -0
  40. mrok/metrics.py +139 -0
  41. mrok/proxy/__init__.py +3 -0
  42. mrok/proxy/app.py +73 -0
  43. mrok/proxy/dataclasses.py +12 -0
  44. mrok/proxy/main.py +58 -0
  45. mrok/proxy/streams.py +124 -0
  46. mrok/proxy/types.py +12 -0
  47. mrok/proxy/ziti.py +173 -0
  48. mrok/ziti/identities.py +50 -20
  49. mrok/ziti/services.py +8 -8
  50. {mrok-0.2.3.dist-info → mrok-0.4.0.dist-info}/METADATA +7 -1
  51. mrok-0.4.0.dist-info/RECORD +92 -0
  52. {mrok-0.2.3.dist-info → mrok-0.4.0.dist-info}/WHEEL +1 -1
  53. mrok/http/master.py +0 -132
  54. mrok-0.2.3.dist-info/RECORD +0 -66
  55. {mrok-0.2.3.dist-info → mrok-0.4.0.dist-info}/entry_points.txt +0 -0
  56. {mrok-0.2.3.dist-info → mrok-0.4.0.dist-info}/licenses/LICENSE.txt +0 -0
mrok/proxy/streams.py ADDED
@@ -0,0 +1,124 @@
1
+ import asyncio
2
+
3
+ from mrok.proxy.types import ConnectionCache, ConnectionKey
4
+
5
+
6
+ class CachedStreamReader:
7
+ def __init__(
8
+ self,
9
+ reader: asyncio.StreamReader,
10
+ key: ConnectionKey,
11
+ manager: ConnectionCache,
12
+ ):
13
+ self._reader = reader
14
+ self._key = key
15
+ self._manager = manager
16
+
17
+ async def read(self, n: int = -1) -> bytes:
18
+ try:
19
+ return await self._reader.read(n)
20
+ except (
21
+ asyncio.CancelledError,
22
+ asyncio.IncompleteReadError,
23
+ asyncio.LimitOverrunError,
24
+ BrokenPipeError,
25
+ ConnectionAbortedError,
26
+ ConnectionResetError,
27
+ RuntimeError,
28
+ TimeoutError,
29
+ UnicodeDecodeError,
30
+ ):
31
+ asyncio.create_task(self._manager.invalidate(self._key))
32
+ raise
33
+
34
+ async def readexactly(self, n: int) -> bytes:
35
+ try:
36
+ return await self._reader.readexactly(n)
37
+ except (
38
+ asyncio.CancelledError,
39
+ asyncio.IncompleteReadError,
40
+ asyncio.LimitOverrunError,
41
+ BrokenPipeError,
42
+ ConnectionAbortedError,
43
+ ConnectionResetError,
44
+ RuntimeError,
45
+ TimeoutError,
46
+ UnicodeDecodeError,
47
+ ):
48
+ asyncio.create_task(self._manager.invalidate(self._key))
49
+ raise
50
+
51
+ async def readline(self) -> bytes:
52
+ try:
53
+ return await self._reader.readline()
54
+ except (
55
+ asyncio.CancelledError,
56
+ asyncio.IncompleteReadError,
57
+ asyncio.LimitOverrunError,
58
+ BrokenPipeError,
59
+ ConnectionAbortedError,
60
+ ConnectionResetError,
61
+ RuntimeError,
62
+ TimeoutError,
63
+ UnicodeDecodeError,
64
+ ):
65
+ asyncio.create_task(self._manager.invalidate(self._key))
66
+ raise
67
+
68
+ def at_eof(self) -> bool:
69
+ return self._reader.at_eof()
70
+
71
+ @property
72
+ def underlying(self) -> asyncio.StreamReader:
73
+ return self._reader
74
+
75
+
76
+ class CachedStreamWriter:
77
+ def __init__(
78
+ self,
79
+ writer: asyncio.StreamWriter,
80
+ key: ConnectionKey,
81
+ manager: ConnectionCache,
82
+ ):
83
+ self._writer = writer
84
+ self._key = key
85
+ self._manager = manager
86
+
87
+ def write(self, data: bytes) -> None:
88
+ try:
89
+ return self._writer.write(data)
90
+ except (RuntimeError, TypeError):
91
+ asyncio.create_task(self._manager.invalidate(self._key))
92
+ raise
93
+
94
+ async def drain(self) -> None:
95
+ try:
96
+ return await self._writer.drain()
97
+ except (
98
+ asyncio.CancelledError,
99
+ BrokenPipeError,
100
+ ConnectionAbortedError,
101
+ ConnectionResetError,
102
+ RuntimeError,
103
+ TimeoutError,
104
+ ):
105
+ asyncio.create_task(self._manager.invalidate(self._key))
106
+ raise
107
+
108
+ def close(self) -> None:
109
+ return self._writer.close()
110
+
111
+ async def wait_closed(self) -> None:
112
+ try:
113
+ return await self._writer.wait_closed()
114
+ except (ConnectionResetError, BrokenPipeError):
115
+ asyncio.create_task(self._manager.invalidate(self._key))
116
+ raise
117
+
118
+ @property
119
+ def transport(self):
120
+ return self._writer.transport
121
+
122
+ @property
123
+ def underlying(self) -> asyncio.StreamWriter:
124
+ return self._writer
mrok/proxy/types.py ADDED
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Protocol
4
+
5
+ from mrok.http.types import StreamReader, StreamWriter
6
+
7
+ ConnectionKey = tuple[str, str | None]
8
+ CachedStream = tuple[StreamReader, StreamWriter]
9
+
10
+
11
+ class ConnectionCache(Protocol):
12
+ async def invalidate(self, key: ConnectionKey) -> None: ...
mrok/proxy/ziti.py ADDED
@@ -0,0 +1,173 @@
1
+ """Ziti-backed connection manager for the proxy.
2
+
3
+ This manager owns creation of connections via an OpenZiti context, wraps
4
+ streams to observe IO errors, evicts idle entries, and serializes creation
5
+ per-key.
6
+ """
7
+
8
+ import asyncio
9
+ import logging
10
+ import time
11
+ from pathlib import Path
12
+
13
+ # typing imports intentionally minimized
14
+ import openziti
15
+
16
+ from mrok.http.types import StreamReader, StreamWriter
17
+ from mrok.proxy.dataclasses import CachedStreamEntry
18
+ from mrok.proxy.streams import CachedStreamReader, CachedStreamWriter
19
+ from mrok.proxy.types import CachedStream, ConnectionKey
20
+
21
+ logger = logging.getLogger("mrok.proxy")
22
+
23
+
24
+ class ZitiConnectionManager:
25
+ def __init__(
26
+ self,
27
+ identity_file: str | Path,
28
+ ziti_timeout_ms: int = 10000,
29
+ ttl_seconds: float = 60.0,
30
+ purge_interval: float = 10.0,
31
+ ):
32
+ self._identity_file = identity_file
33
+ self._ziti_ctx = None
34
+ self._ziti_timeout_ms = ziti_timeout_ms
35
+ self._ttl = float(ttl_seconds)
36
+ self._purge_interval = float(purge_interval)
37
+ self._cache: dict[ConnectionKey, CachedStreamEntry] = {}
38
+ self._lock = asyncio.Lock()
39
+ self._in_progress: dict[ConnectionKey, asyncio.Lock] = {}
40
+ self._purge_task: asyncio.Task | None = None
41
+
42
+ async def get(self, target: str) -> tuple[StreamReader, StreamWriter] | tuple[None, None]:
43
+ head, _, tail = target.partition(".")
44
+ terminator = target if head and tail else ""
45
+ service = tail if tail else head
46
+ r, w = await self._get_or_create_key((service, terminator))
47
+ return r, w
48
+
49
+ async def invalidate(self, key: ConnectionKey) -> None:
50
+ async with self._lock:
51
+ item = self._cache.pop(key, None)
52
+ if item is None:
53
+ return
54
+ await self._close_writer(item.writer)
55
+
56
+ async def start(self) -> None:
57
+ if self._ziti_ctx is None:
58
+ ctx, err = openziti.load(str(self._identity_file), timeout=self._ziti_timeout_ms)
59
+ if err != 0:
60
+ raise Exception(f"Cannot create a Ziti context from the identity file: {err}")
61
+ self._ziti_ctx = ctx
62
+ if self._purge_task is None:
63
+ self._purge_task = asyncio.create_task(self._purge_loop())
64
+ logger.info("Ziti connection manager started")
65
+
66
+ async def stop(self) -> None:
67
+ if self._purge_task is not None:
68
+ self._purge_task.cancel()
69
+ try:
70
+ await self._purge_task
71
+ except asyncio.CancelledError:
72
+ logger.debug("Purge task was cancelled")
73
+ except Exception as e:
74
+ logger.warning(f"An error occurred stopping the purge task: {e}")
75
+ self._purge_task = None
76
+ logger.info("Ziti connection manager stopped")
77
+
78
+ async with self._lock:
79
+ items = list(self._cache.items())
80
+ self._cache.clear()
81
+
82
+ for _, item in items:
83
+ await self._close_writer(item.writer)
84
+
85
+ async def _purge_loop(self) -> None:
86
+ try:
87
+ while True:
88
+ await asyncio.sleep(self._purge_interval)
89
+ await self._purge_once()
90
+ except asyncio.CancelledError:
91
+ return
92
+
93
+ async def _purge_once(self) -> None:
94
+ to_close: list[tuple[StreamReader, StreamWriter]] = []
95
+ async with self._lock:
96
+ now = time.time()
97
+ for key, item in list(self._cache.items()):
98
+ if now - item.last_access > self._ttl:
99
+ to_close.append((item.reader, item.writer))
100
+ del self._cache[key]
101
+
102
+ for _, writer in to_close:
103
+ writer.close()
104
+ await self._close_writer(writer)
105
+
106
+ def _is_writer_closed(self, writer: StreamWriter) -> bool:
107
+ return writer.transport.is_closing()
108
+
109
+ async def _close_writer(self, writer: StreamWriter) -> None:
110
+ writer.close()
111
+ try:
112
+ await writer.wait_closed()
113
+ except Exception as e:
114
+ logger.debug(f"Error closing writer: {e}")
115
+
116
+ async def _get_or_create_key(self, key: ConnectionKey) -> CachedStream:
117
+ """Internal: create or return a cached wrapped pair for the concrete key."""
118
+ await self._purge_once()
119
+ to_close = None
120
+ async with self._lock:
121
+ if key in self._cache:
122
+ now = time.time()
123
+ item = self._cache[key]
124
+ reader, writer = item.reader, item.writer
125
+ if not self._is_writer_closed(writer) and not reader.at_eof():
126
+ self._cache[key] = CachedStreamEntry(reader, writer, now)
127
+ return reader, writer
128
+ to_close = writer
129
+ del self._cache[key]
130
+
131
+ lock = self._in_progress.get(key)
132
+ if lock is None:
133
+ lock = asyncio.Lock()
134
+ self._in_progress[key] = lock
135
+
136
+ if to_close:
137
+ await self._close_writer(to_close)
138
+
139
+ async with lock:
140
+ try:
141
+ # # double-check cache after acquiring the per-key lock
142
+ # async with self._lock:
143
+ # now = time.time()
144
+ # if key in self._cache:
145
+ # r, w, _ = self._cache[key]
146
+ # if not self._is_writer_closed(w) and not r.at_eof():
147
+ # self._cache[key] = (r, w, now)
148
+ # return r, w
149
+
150
+ # perform creation via ziti context
151
+ extension, instance = key
152
+ logger.info(f"Create connection to {extension}: {instance}")
153
+ # loop = asyncio.get_running_loop()
154
+ # sock = await loop.run_in_executor(None, self._ziti_ctx.connect,
155
+ # extension, instance)
156
+ if instance:
157
+ sock = self._ziti_ctx.connect(
158
+ extension, terminator=instance
159
+ ) # , terminator=instance)
160
+ else:
161
+ sock = self._ziti_ctx.connect(extension)
162
+ orig_reader, orig_writer = await asyncio.open_connection(sock=sock)
163
+
164
+ reader = CachedStreamReader(orig_reader, key, self)
165
+ writer = CachedStreamWriter(orig_writer, key, self)
166
+
167
+ async with self._lock:
168
+ self._cache[key] = CachedStreamEntry(reader, writer, time.time())
169
+
170
+ return reader, writer
171
+ finally:
172
+ async with self._lock:
173
+ self._in_progress.pop(key, None)
mrok/ziti/identities.py CHANGED
@@ -1,8 +1,10 @@
1
+ import copy
1
2
  import logging
2
3
  from typing import Any
3
4
 
4
5
  import jwt
5
6
 
7
+ from mrok.conf import Settings
6
8
  from mrok.ziti import pki
7
9
  from mrok.ziti.api import TagsType, ZitiClientAPI, ZitiManagementAPI
8
10
  from mrok.ziti.constants import (
@@ -16,31 +18,37 @@ from mrok.ziti.errors import (
16
18
  ServiceNotFoundError,
17
19
  UserIdentityNotFoundError,
18
20
  )
21
+ from mrok.ziti.services import register_service, unregister_service
19
22
 
20
23
  logger = logging.getLogger("mrok.ziti")
21
24
 
22
25
 
23
- async def register_instance(
26
+ async def register_identity(
27
+ settings: Settings,
24
28
  mgmt_api: ZitiManagementAPI,
25
29
  client_api: ZitiClientAPI,
26
- extension_id: str,
27
- instance_id: str,
30
+ service_external_id: str,
31
+ identity_external_id: str,
28
32
  tags: TagsType | None = None,
29
33
  ):
30
- service_name = extension_id.lower()
31
- tags = tags or {}
32
- tags[MROK_SERVICE_TAG_NAME] = service_name
33
- tags[MROK_IDENTITY_TYPE_TAG_NAME] = MROK_IDENTITY_TYPE_TAG_VALUE_INSTANCE
34
+ service_name = service_external_id.lower()
35
+ identity_tags = copy.copy(tags or {})
36
+ identity_tags[MROK_SERVICE_TAG_NAME] = service_name
37
+ identity_tags[MROK_IDENTITY_TYPE_TAG_NAME] = MROK_IDENTITY_TYPE_TAG_VALUE_INSTANCE
34
38
  service = await mgmt_api.search_service(service_name)
35
39
  if not service:
36
- raise ServiceNotFoundError(f"A service with name `{extension_id}` does not exists.")
40
+ raise ServiceNotFoundError(f"A service with name `{service_external_id}` does not exists.")
37
41
 
38
- identity_name = f"{instance_id.lower()}.{service_name}"
42
+ identity_name = f"{identity_external_id.lower()}.{service_name}"
39
43
  service_policy_name = f"{identity_name}:bind"
44
+ self_service_policy_name = f"self.{service_policy_name}"
40
45
 
41
46
  identity = await mgmt_api.search_identity(identity_name)
42
47
  if identity:
43
48
  service_policy = await mgmt_api.search_service_policy(service_policy_name)
49
+ if service_policy:
50
+ await mgmt_api.delete_service_policy(service_policy["id"])
51
+ service_policy = await mgmt_api.search_service_policy(self_service_policy_name)
44
52
  if service_policy:
45
53
  await mgmt_api.delete_service_policy(service_policy["id"])
46
54
  router_policy = await mgmt_api.search_router_policy(identity_name)
@@ -48,7 +56,7 @@ async def register_instance(
48
56
  await mgmt_api.delete_router_policy(router_policy["id"])
49
57
  await mgmt_api.delete_identity(identity["id"])
50
58
 
51
- identity_id = await mgmt_api.create_user_identity(identity_name, tags=tags)
59
+ identity_id = await mgmt_api.create_user_identity(identity_name, tags=identity_tags)
52
60
  identity = await mgmt_api.get_identity(identity_id)
53
61
 
54
62
  identity_json = await _enroll_identity(
@@ -58,33 +66,55 @@ async def register_instance(
58
66
  identity,
59
67
  mrok={
60
68
  "identity": identity_name,
61
- "extension": extension_id,
62
- "instance": instance_id,
69
+ "extension": service_external_id,
70
+ "instance": identity_external_id,
71
+ "domain": settings.proxy.domain,
72
+ "tags": identity_tags,
63
73
  },
64
74
  )
65
75
 
76
+ self_service = await mgmt_api.search_service(identity_name)
77
+ if not self_service:
78
+ self_service = await register_service(settings, mgmt_api, identity_name, tags)
79
+
66
80
  await mgmt_api.create_bind_service_policy(service_policy_name, service["id"], identity_id)
81
+ await mgmt_api.create_bind_service_policy(
82
+ self_service_policy_name,
83
+ self_service["id"],
84
+ identity_id,
85
+ )
67
86
  await mgmt_api.create_router_policy(identity_name, identity_id)
68
87
 
69
88
  return identity, identity_json
70
89
 
71
90
 
72
- async def unregister_instance(
91
+ async def unregister_identity(
92
+ settings: Settings,
73
93
  mgmt_api: ZitiManagementAPI,
74
- extension_id: str,
75
- instance_id: str,
94
+ service_external_id: str,
95
+ identity_external_id: str,
76
96
  ):
77
- service_name = extension_id.lower()
97
+ service_name = service_external_id.lower()
78
98
  service = await mgmt_api.search_service(service_name)
79
99
  if not service:
80
- raise ServiceNotFoundError(f"A service with name `{extension_id}` does not exists.")
100
+ raise ServiceNotFoundError(f"A service with name `{service_external_id}` does not exists.")
81
101
 
82
- identity_name = f"{instance_id.lower()}.{service_name}"
102
+ identity_name = f"{identity_external_id.lower()}.{service_name}"
83
103
  service_policy_name = f"{identity_name}:bind"
84
104
 
85
105
  identity = await mgmt_api.search_identity(identity_name)
86
106
  if not identity:
87
- raise UserIdentityNotFoundError(f"Instance `{instance_id}` not found.")
107
+ raise UserIdentityNotFoundError(f"Identity `{identity_external_id}` not found.")
108
+
109
+ self_service_policy_name = f"self.{service_policy_name}"
110
+
111
+ service_policy = await mgmt_api.search_service_policy(self_service_policy_name)
112
+ if service_policy:
113
+ await mgmt_api.delete_service_policy(service_policy["id"])
114
+
115
+ self_service = await mgmt_api.search_service(identity_name)
116
+ if self_service:
117
+ await unregister_service(settings, mgmt_api, identity_name)
88
118
 
89
119
  service_policy = await mgmt_api.search_service_policy(service_policy_name)
90
120
  if service_policy:
@@ -120,7 +150,7 @@ async def _enroll_identity(
120
150
  client_api: ZitiClientAPI,
121
151
  identity_id: str,
122
152
  identity: dict[str, Any] | None = None,
123
- mrok: dict[str, str] | None = None,
153
+ mrok: dict[str, str | dict] | None = None,
124
154
  ):
125
155
  if identity is None:
126
156
  identity = await mgmt_api.get_identity(identity_id)
mrok/ziti/services.py CHANGED
@@ -13,10 +13,10 @@ from mrok.ziti.errors import (
13
13
  logger = logging.getLogger(__name__)
14
14
 
15
15
 
16
- async def register_extension(
17
- settings: Settings, mgmt_api: ZitiManagementAPI, extension_id: str, tags: TagsType | None
16
+ async def register_service(
17
+ settings: Settings, mgmt_api: ZitiManagementAPI, external_id: str, tags: TagsType | None
18
18
  ) -> dict[str, Any]:
19
- service_name = extension_id.lower()
19
+ service_name = external_id.lower()
20
20
  registered = False
21
21
  proxy_identity = await mgmt_api.search_identity(settings.proxy.identity)
22
22
  if not proxy_identity:
@@ -58,17 +58,17 @@ async def register_extension(
58
58
  await mgmt_api.create_service_router_policy(service_name, service_id, tags=tags)
59
59
  registered = True
60
60
  if not registered:
61
- raise ServiceAlreadyRegisteredError(f"Extension `{extension_id}` already registered.")
61
+ raise ServiceAlreadyRegisteredError(f"Service `{external_id}` already registered.")
62
62
  return service
63
63
 
64
64
 
65
- async def unregister_extension(
66
- settings: Settings, mgmt_api: ZitiManagementAPI, extension_id: str
65
+ async def unregister_service(
66
+ settings: Settings, mgmt_api: ZitiManagementAPI, external_id: str
67
67
  ) -> None:
68
- service_name = extension_id.lower()
68
+ service_name = external_id.lower()
69
69
  service = await mgmt_api.search_service(service_name)
70
70
  if not service:
71
- raise ServiceNotFoundError(f"Extension `{extension_id}` not found.")
71
+ raise ServiceNotFoundError(f"Service `{external_id}` not found.")
72
72
 
73
73
  router_policy = await mgmt_api.search_service_router_policy(service_name)
74
74
  if router_policy:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mrok
3
- Version: 0.2.3
3
+ Version: 0.4.0
4
4
  Summary: MPT Extensions OpenZiti Orchestrator
5
5
  Author: SoftwareOne AG
6
6
  License: Apache License
@@ -212,14 +212,20 @@ Requires-Dist: dynaconf<4.0.0,>=3.2.11
212
212
  Requires-Dist: fastapi-pagination<0.15.0,>=0.14.1
213
213
  Requires-Dist: fastapi[standard]<0.120.0,>=0.119.0
214
214
  Requires-Dist: gunicorn<24.0.0,>=23.0.0
215
+ Requires-Dist: hdrhistogram<0.11.0,>=0.10.3
215
216
  Requires-Dist: httptools<0.8.0,>=0.7.1
216
217
  Requires-Dist: httpx<0.29.0,>=0.28.1
217
218
  Requires-Dist: openziti<2.0.0,>=1.3.1
219
+ Requires-Dist: psutil<8.0.0,>=7.1.3
218
220
  Requires-Dist: pydantic<3.0.0,>=2.11.7
219
221
  Requires-Dist: pyfiglet<2.0.0,>=1.0.4
220
222
  Requires-Dist: pyjwt<3.0.0,>=2.10.1
223
+ Requires-Dist: pytest-textual-snapshot<2.0.0,>=1.1.0
221
224
  Requires-Dist: pyyaml<7.0.0,>=6.0.2
225
+ Requires-Dist: pyzmq<28.0.0,>=27.1.0
222
226
  Requires-Dist: rich<15.0.0,>=14.1.0
227
+ Requires-Dist: textual-serve<2.0.0,>=1.1.3
228
+ Requires-Dist: textual<7.0.0,>=6.5.0
223
229
  Requires-Dist: typer<0.20.0,>=0.19.2
224
230
  Requires-Dist: uvicorn-worker<0.5.0,>=0.4.0
225
231
  Description-Content-Type: text/markdown
@@ -0,0 +1,92 @@
1
+ mrok/__init__.py,sha256=D1PUs3KtMCqG4bFLceVNG62L3RN53NS95uSCNXpgvzs,181
2
+ mrok/conf.py,sha256=_5Z-A5LyojQeY8J7W8C0QidsmrPl99r9qKYEoMf4kcI,840
3
+ mrok/datastructures.py,sha256=gp8KF2JoNOxIRzYStVZLKL_XVDbcIVSIDnmpQo4FNt0,4067
4
+ mrok/errors.py,sha256=ruNMDFr2_0ezCGXuCG1OswCEv-bHOIzMMd02J_0ABcs,37
5
+ mrok/logging.py,sha256=ZMWn0w4fJ-F_g-L37H_GM14BSXAIF2mFF_ougX5S7mg,2856
6
+ mrok/master.py,sha256=6_bUic39mXE37lt0PBf_BtY2D2mBR7KzPj65JP7HWPc,8287
7
+ mrok/metrics.py,sha256=asweK_7xiV5MtkDkvbEm9Tktqrl2KHM8VflF0AkNGI0,4036
8
+ mrok/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ mrok/agent/ziticorn.py,sha256=marXGcr6CbDdiNi8V3CZJhg8YCUbKw6ySuO3-0-zf8g,900
10
+ mrok/agent/devtools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ mrok/agent/devtools/__main__.py,sha256=R8ezbW7hCik5r45U3w2TgiTubg9SlbVsWA-bapILJXU,781
12
+ mrok/agent/devtools/inspector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ mrok/agent/devtools/inspector/__main__.py,sha256=HeYcRf1bjXPji2LKMPCcTU61afrRH2P1RqnFmHClRTc,524
14
+ mrok/agent/devtools/inspector/app.py,sha256=_pzxemMqIunE5EdMq5amjqpOGsMWIOw17GgiCtRAi6Q,16464
15
+ mrok/agent/devtools/inspector/server.py,sha256=C4uD6_1psSHMjJLUDCMPGvKdQYKaEwYTw27NAbwuuA0,636
16
+ mrok/agent/sidecar/__init__.py,sha256=DrjJGhqFyxsVODW06KI20Wpr6HsD2lD6qFCKUXc7GIE,59
17
+ mrok/agent/sidecar/app.py,sha256=1p6qWkXVq78zcJ2dhCYlw8CqfwPsgEtu07Lp5csK3Iw,874
18
+ mrok/agent/sidecar/main.py,sha256=h31wynUCcFmRckvqLHtH97w1QgMv4fzcmYjhRPUobxY,1076
19
+ mrok/cli/__init__.py,sha256=mtFEa8IeS1x6Gm4dUYoSnAxyEzNqbUVSmWxtuZUMR84,61
20
+ mrok/cli/main.py,sha256=DFcYPwDskXi8SKAgEsuP4GMFzaniIf_6bZaSDWvYKDk,2724
21
+ mrok/cli/rich.py,sha256=P3Dyu8EArUR9_0j7DPK7LRx85TWdYdZ1SaJzD_S1ZCE,511
22
+ mrok/cli/utils.py,sha256=m_olScdIUGks5IoC6p2F9D6CQIucWZ7LHyrvwm2bkJw,106
23
+ mrok/cli/commands/__init__.py,sha256=jihISOj3ZZQ_dn0rogXrJOx6b283KLRUfTw9USQgAhI,134
24
+ mrok/cli/commands/admin/__init__.py,sha256=WU49jpMF9p18UONjYywWEFzjF57zLpLKJ0qAZvrzcR4,414
25
+ mrok/cli/commands/admin/bootstrap.py,sha256=iOnHctYajgcHrG_Idjn5Y7VVSaWYRIhdgqKSw9TWq9I,1680
26
+ mrok/cli/commands/admin/utils.py,sha256=wQ-qQJGFyhikMJY_CWT-G6sTEIZb-LUdj1AUZisLPBw,1363
27
+ mrok/cli/commands/admin/list/__init__.py,sha256=kjCMcpn1gopcrQaaHxfFh8Kyngldepnle8R2br5dJ_0,195
28
+ mrok/cli/commands/admin/list/extensions.py,sha256=16fhDB5ucL8su2WQnSaQ1E6MhgC4vkP9-nuHAcPpzyE,4405
29
+ mrok/cli/commands/admin/list/instances.py,sha256=kaqeyidwUxgYqfaHXqp2m76rm5h2ErBsYyZcNeaBRwY,5912
30
+ mrok/cli/commands/admin/register/__init__.py,sha256=5Jb_bc2L47MEpQIrOcquzduTFWQ01Jd1U1MpqaR-Ekw,209
31
+ mrok/cli/commands/admin/register/extensions.py,sha256=p1qX5gSQX1IGpOQjO2MJzbc09v1ebdFuPo94QzJErKk,1485
32
+ mrok/cli/commands/admin/register/instances.py,sha256=XB6uAchc7Rm8uAu7o3-oHaN_rS8CCIBf0QKWZGW86fI,1940
33
+ mrok/cli/commands/admin/unregister/__init__.py,sha256=-GjjCPX1pISbWmJK6GpKO3ijGsDQb21URjU1hNu99O4,215
34
+ mrok/cli/commands/admin/unregister/extensions.py,sha256=GR3Iwzeksk_R0GkgmCSG7iHRcUrI7ABqDi25Gbes64Y,1016
35
+ mrok/cli/commands/admin/unregister/instances.py,sha256=-28wL8pTXTWHVHtw93y8-dqi-Dlf0OZOnlBCKOyGo80,1138
36
+ mrok/cli/commands/agent/__init__.py,sha256=ZAi7eTkKQtfwwV1c1mv3uvEEsyMMrhCQ_-id_0wksAQ,218
37
+ mrok/cli/commands/agent/utils.py,sha256=m_olScdIUGks5IoC6p2F9D6CQIucWZ7LHyrvwm2bkJw,106
38
+ mrok/cli/commands/agent/dev/__init__.py,sha256=ZfreyRuaLqO0AwPS8Ll1DIpsKacsu7_dTmbxV5QecOM,172
39
+ mrok/cli/commands/agent/dev/console.py,sha256=rrKAGoKXVQQBOC75H0JSuX1sYyvc2QSrV-dfMPK49p4,673
40
+ mrok/cli/commands/agent/dev/web.py,sha256=O9dYk-o1FV2E_sKLOezdEmLsnexwbJNDdsYL5pATZRQ,1028
41
+ mrok/cli/commands/agent/run/__init__.py,sha256=E_IJCl3BfMffqFASe8gzJwhhQgt5bQfjhuyekVwdEBA,164
42
+ mrok/cli/commands/agent/run/asgi.py,sha256=dCgzwJtTLv2eyEIP7v1tDfe_PrFBS02SfN5dSDw1Jzg,2054
43
+ mrok/cli/commands/agent/run/sidecar.py,sha256=Tj5inAeSX1E3yCVs2q4P3sP3trvvwk2lYMSUtyFfxo8,2098
44
+ mrok/cli/commands/controller/__init__.py,sha256=2xw-YVN0akiLiuGUU3XbYyZZ0ugOjQ6XhtTkzEKSmMA,161
45
+ mrok/cli/commands/controller/openapi.py,sha256=QLjVao9UkB2vBaGkFi_q_jrlg4Np4ldMRwDIJsrJ7A8,1175
46
+ mrok/cli/commands/controller/run.py,sha256=yl1p7oRHhQINWWjUKlRHtMIWUCV0KsxYdyVyazhX834,2406
47
+ mrok/cli/commands/proxy/__init__.py,sha256=Y9oluemsuWSaykDniLVsI2cyTurcEO3_GJDHgf-7BdU,120
48
+ mrok/cli/commands/proxy/run.py,sha256=QzKAjNCib-SS8IrGGHOxDjTtgTQxfeeeqmI3LaIkiLo,1293
49
+ mrok/controller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
+ mrok/controller/app.py,sha256=XxCIB7N1YE52vSYfvGW2UPgEEOZ9jxDMe2l9D2SfXi8,1866
51
+ mrok/controller/auth.py,sha256=hYa0OPJ5X0beGxRP6qbxwJOVXj5TmzHjmam2OjTBKn4,2704
52
+ mrok/controller/pagination.py,sha256=raYpYa34q8Ckl4BXBOEdpWlKkFj6z7e6QLWr2HT7dzI,2187
53
+ mrok/controller/schemas.py,sha256=AaF8_bEwZTHM02apVEBAzlUb2t71zoxYaG-VHtPNeMk,1705
54
+ mrok/controller/dependencies/__init__.py,sha256=voewk6gjkA0OarL6HFmfT_RLqBns0Fpl-VIqK5xVAEI,202
55
+ mrok/controller/dependencies/conf.py,sha256=2Pa8fxJHkZ29q6UL-w6hUP_wr7WnNELfw5LlzWg1Tec,162
56
+ mrok/controller/dependencies/ziti.py,sha256=fYoxeJb4s6p2_3gxbExbFSRabjpvp_gZMBb3ocXZV3Y,702
57
+ mrok/controller/openapi/__init__.py,sha256=U1dw45w76CcoQagyqg_FXdMuJF3qJZZM6wG8TeTe3Zo,101
58
+ mrok/controller/openapi/examples.py,sha256=ZI0BP7L6sI0z7Mq1I3uc2UrweGpzpPeGSIuf1bUKkgg,1419
59
+ mrok/controller/openapi/utils.py,sha256=Kn55ISAWlMJNwrJTum7iFrBvJvr81To76pCK8W-s79Q,1114
60
+ mrok/controller/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
+ mrok/controller/routes/extensions.py,sha256=zoY4sNz_BIZcbly6WXM7Rbpn2jmB89njS_0xdJkoKfs,9192
62
+ mrok/controller/routes/instances.py,sha256=v-fn_F6JHbDZ4YUNCIZzClgHp6aC1Eu5HB7k7qBG5pk,2202
63
+ mrok/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
+ mrok/http/config.py,sha256=k73-4hBo6jag1RpyZagJLtTCL6EQoebZaX8Vv-CMN_k,2050
65
+ mrok/http/constants.py,sha256=ao5gI2HFBWmrdd2Yc6XFK_RGaHk-omxI4AqvfIiGes8,409
66
+ mrok/http/forwarder.py,sha256=DakD9hrrCWAzB1B_4SgQaQaEHcDYLLI9WaYs5F0O36I,12977
67
+ mrok/http/lifespan.py,sha256=UdbOqjWZsHzJJjX0CTd2hY96Jpk5QWtdHJEzPG6Z4hQ,1288
68
+ mrok/http/middlewares.py,sha256=SGo4EwhTId2uJx1aMuqGbNy7MXgZlDEdZI0buzBYVv0,5011
69
+ mrok/http/protocol.py,sha256=ap8jbLUvgbAH81ZJZCBkQiYR7mkV_eL3rpfwEkoE8sU,392
70
+ mrok/http/server.py,sha256=Mj7C85fc-DXp-WTBWaOd7ag808oliLmFBH5bf-G2FHg,370
71
+ mrok/http/types.py,sha256=XpNrvbfpANKvmjOBYtLF1FmDHoJF3z_MIMQHXoJlvmE,1302
72
+ mrok/http/utils.py,sha256=sOixYu3R9-nNoMFYdifrreYvcFRIHYVtb6AAmtVzaLE,2125
73
+ mrok/proxy/__init__.py,sha256=vWXyImroqM1Eq8e_oFPBup8VJ3reyp8SVjFTbLzRkI8,51
74
+ mrok/proxy/app.py,sha256=MwJZ91MQ1oWkbmsX7-NooZSV9BkLVZPFmUKpNPC4HHQ,2490
75
+ mrok/proxy/dataclasses.py,sha256=DtX-Yuma-uOECOPefJnoQJhZMEtT6Za_27cd-lJE9Iw,237
76
+ mrok/proxy/main.py,sha256=ZXpticE6J4FABaslDB_8J5qklPsf3e7xIFSZmcPAAjQ,1588
77
+ mrok/proxy/streams.py,sha256=6TMZwrQPbSyQqpqavsoTeyUmS2O026pJfiCnxLopPqg,3425
78
+ mrok/proxy/types.py,sha256=dgWqAj6dFGVH_Q8-k8sU5h18yoUF_fTn-SRPIfEs_gA,308
79
+ mrok/proxy/ziti.py,sha256=dKd6UzmEAFu9-gey871sPEDUZTkt4YVPyCYRzeA5mlA,6539
80
+ mrok/ziti/__init__.py,sha256=20OWMiexRhOovZOX19zlX87-V78QyWnEnSZfyAftUdE,263
81
+ mrok/ziti/api.py,sha256=KvGiT9d4oSgC3JbFWLDQyuHcLX2HuZJoJ8nHmWtCDkY,16154
82
+ mrok/ziti/bootstrap.py,sha256=QIDhlkIxPW2QRuumFq2D1WDbD003P5f3z24pAUsyeBI,2696
83
+ mrok/ziti/constants.py,sha256=Urq1X3bCBQZfw8NbnEa1pqmY4oq1wmzkwPfzam3kbTw,339
84
+ mrok/ziti/errors.py,sha256=yYCbVDwktnR0AYduqtynIjo73K3HOhIrwA_vQimvEd4,368
85
+ mrok/ziti/identities.py,sha256=1BcwfqAJHMBhc3vRaf0aLaIkoHskj5Xe2Lsq2lO9Vs8,6735
86
+ mrok/ziti/pki.py,sha256=o2tySqHC8-7bvFuI2Tqxg9vX6H6ZSxWxfP_9x29e19M,1954
87
+ mrok/ziti/services.py,sha256=zR1PEBYwXVou20iJK4euh0ZZFAo9UB8PZk8f6SDmiUE,3194
88
+ mrok-0.4.0.dist-info/METADATA,sha256=39Su41kvqC4oAsQl1_ZG7b5v1xRr6c2YK6x84XgURi8,15796
89
+ mrok-0.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
90
+ mrok-0.4.0.dist-info/entry_points.txt,sha256=tloXwvU1uJicBJR2h-8HoVclPgwJWDwuREMHN8Zq-nU,38
91
+ mrok-0.4.0.dist-info/licenses/LICENSE.txt,sha256=6PaICaoA3yNsZKLv5G6OKqSfLSoX7MakYqTDgJoTCBs,11346
92
+ mrok-0.4.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any