messagefoundry-webconsole 0.2.15__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 (40) hide show
  1. messagefoundry_webconsole/__init__.py +128 -0
  2. messagefoundry_webconsole/_auth.py +841 -0
  3. messagefoundry_webconsole/_html.py +539 -0
  4. messagefoundry_webconsole/_security.py +253 -0
  5. messagefoundry_webconsole/_service.py +22 -0
  6. messagefoundry_webconsole/_static.py +78 -0
  7. messagefoundry_webconsole/mount.py +103 -0
  8. messagefoundry_webconsole/pages/__init__.py +25 -0
  9. messagefoundry_webconsole/pages/_common.py +21 -0
  10. messagefoundry_webconsole/pages/account.py +771 -0
  11. messagefoundry_webconsole/pages/admin.py +480 -0
  12. messagefoundry_webconsole/pages/audit.py +66 -0
  13. messagefoundry_webconsole/pages/config.py +113 -0
  14. messagefoundry_webconsole/pages/connections.py +519 -0
  15. messagefoundry_webconsole/pages/messages.py +689 -0
  16. messagefoundry_webconsole/pages/monitoring.py +853 -0
  17. messagefoundry_webconsole/pages/uploaded_logs.py +252 -0
  18. messagefoundry_webconsole/routes/__init__.py +6 -0
  19. messagefoundry_webconsole/routes/_common.py +45 -0
  20. messagefoundry_webconsole/routes/account.py +451 -0
  21. messagefoundry_webconsole/routes/admin.py +505 -0
  22. messagefoundry_webconsole/routes/audit.py +39 -0
  23. messagefoundry_webconsole/routes/config.py +67 -0
  24. messagefoundry_webconsole/routes/connection_writes.py +248 -0
  25. messagefoundry_webconsole/routes/core.py +1077 -0
  26. messagefoundry_webconsole/routes/monitoring.py +94 -0
  27. messagefoundry_webconsole/routes/monitoring_writes.py +205 -0
  28. messagefoundry_webconsole/routes/oidc.py +172 -0
  29. messagefoundry_webconsole/routes/search.py +204 -0
  30. messagefoundry_webconsole/routes/sso.py +88 -0
  31. messagefoundry_webconsole/routes/status.py +178 -0
  32. messagefoundry_webconsole/routes/uploaded_logs.py +181 -0
  33. messagefoundry_webconsole/static/app.css +345 -0
  34. messagefoundry_webconsole/static/app.js +1506 -0
  35. messagefoundry_webconsole/static/csp-probe.js +9 -0
  36. messagefoundry_webconsole-0.2.15.dist-info/METADATA +63 -0
  37. messagefoundry_webconsole-0.2.15.dist-info/RECORD +40 -0
  38. messagefoundry_webconsole-0.2.15.dist-info/WHEEL +4 -0
  39. messagefoundry_webconsole-0.2.15.dist-info/licenses/LICENSE +662 -0
  40. messagefoundry_webconsole-0.2.15.dist-info/licenses/NOTICE +31 -0
@@ -0,0 +1,128 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ # Copyright (C) 2026 MessageFoundry Organization and contributors
3
+ """MessageFoundry web console — the same-origin browser ops dashboard, mounted onto the engine.
4
+
5
+ A separately-versioned second distribution (``messagefoundry-webconsole``) that the engine MOUNTS
6
+ in-process, same-origin, via one :func:`mount_ui` call from ``create_app``'s ``serve_ui`` tail (Option
7
+ B, ADR 0065). It owns the entire ``/ui`` surface — rendering, the confined ``mf_session`` cookie auth,
8
+ the write-action registry, and every ``/ui`` route — and reaches the reused JSON handlers through the
9
+ typed :class:`~messagefoundry.api._ui_seam.UiDeps` bundle the engine injects. It imports only
10
+ ``fastapi``, ``messagefoundry.api.security``/``.models``/``.auth_models``/``._ui_seam``, ``messagefoundry.auth``,
11
+ and the pure ``messagefoundry.parsing`` lib — never ``pipeline``/``store``/``transports``/``config``
12
+ (CLAUDE.md §4).
13
+
14
+ The console pins itself against the engine's :data:`~messagefoundry.api._ui_seam.ENGINE_UI_SEAM` via
15
+ :data:`SUPPORTED_ENGINE_SEAMS` + :func:`assert_engine_seam`, so a NON-MATCHING engine fails LOUD at
16
+ startup (:class:`UiSeamMismatch`) rather than a raw ``TypeError`` from building the deps bundle.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from pathlib import Path
22
+
23
+ #: Independent version root (NOT lockstep with the engine — that is the departure from
24
+ #: ``messagefoundry-harness``); its own tag / changelog / PyPI cadence. Starts matched to the engine.
25
+ __version__ = "0.2.15"
26
+
27
+ #: The engine contract versions this console build supports (``api._ui_seam.ENGINE_UI_SEAM``). A pair
28
+ #: outside this set is refused at startup — the runtime backstop behind the PEP 508 compat range.
29
+ # Seam 9 is the S8a console-dashboard lane (#76/#131/#136, ADR 0065 + ADR 0007 amendments): the
30
+ # The console supports EXACTLY the engine seam it was built against — deliberately a single value,
31
+ # not a range (BACKLOG #279, resolved 2026-07-21 in favour of option (b)).
32
+ #
33
+ # It used to accept {2..N}, justified by "every new field has a default, so an older engine still
34
+ # renders on this console". That claim was never tested: CI installs ONE engine (HEAD) and runs the
35
+ # package suite against it, so N-1 of the N accepted seams were exercised by nothing. Worse, the two
36
+ # ways it silently stops being true — a new field WITHOUT a default, or a bare read of a new engine
37
+ # attribute where getattr-with-default was needed — do NOT trip this handshake, because the older
38
+ # engine's seam IS in the accepted set. The failure surfaced as a runtime AttributeError/TypeError on
39
+ # a live console instead of a loud refusal at startup, which is the opposite of what this gate exists
40
+ # for.
41
+ #
42
+ # Narrowing costs no extra per-bump work: a NEW seam was never in the old set either, so a bump has
43
+ # always required editing BOTH constants. It drops only the untested tail.
44
+ #
45
+ # If cross-seam support is ever genuinely wanted, re-widen this set AND add the CI matrix that
46
+ # installs the MIN and MAX supported engine builds — the claim and its test land together, or not
47
+ # at all.
48
+ SUPPORTED_ENGINE_SEAMS: frozenset[int] = frozenset({15})
49
+
50
+ #: The vendored static assets shipped in THIS wheel (mounted at /ui/static by :func:`mount_ui`).
51
+ STATIC_DIR = Path(__file__).parent / "static"
52
+
53
+
54
+ class UiSeamMismatch(RuntimeError):
55
+ """Raised when the mounted console does not support the engine's ``ENGINE_UI_SEAM``."""
56
+
57
+
58
+ def assert_engine_seam(engine_seam: int) -> None:
59
+ """Fail LOUD if the engine's seam is not one this console supports (called BEFORE the engine
60
+ builds :class:`~messagefoundry.api._ui_seam.UiDeps`, so a skew never surfaces as a raw kwargs
61
+ ``TypeError``). A second identical assert runs inside :func:`mount_ui` (belt-and-suspenders)."""
62
+ if engine_seam not in SUPPORTED_ENGINE_SEAMS:
63
+ raise UiSeamMismatch(
64
+ f"web console {__version__} supports engine UI seam(s) "
65
+ f"{sorted(SUPPORTED_ENGINE_SEAMS)}, but the engine provides {engine_seam}; install a "
66
+ "matching messagefoundry-webconsole (see the messagefoundry compat range)."
67
+ )
68
+
69
+
70
+ # Re-export the security/rendering surface at the package root (the old ``api.webui`` __init__ shape),
71
+ # so callers/tests reach it as ``messagefoundry_webconsole.{is_safe_ui_action, authorize_ui_ws, ...}``
72
+ # and ``messagefoundry_webconsole.pages``. Both are leaf modules (no cycle with :mod:`.mount`).
73
+ from . import pages # noqa: E402
74
+ from ._auth import ( # noqa: E402
75
+ COOKIE_NAME,
76
+ UI_CSP,
77
+ WEBAUTHN_EXTRA_MISSING_NOTICE,
78
+ WEBAUTHN_RP_CHANGED_NOTICE,
79
+ WEBAUTHN_RP_MISSING_NOTICE,
80
+ UiWriteAction,
81
+ assert_not_cross_site,
82
+ assert_same_origin,
83
+ authorize_ui_ws,
84
+ clear_session_cookie,
85
+ is_safe_ui_action,
86
+ is_unlock_action,
87
+ lookup_ui_action,
88
+ register_ui_action,
89
+ require_ui,
90
+ require_ui_reauth_only,
91
+ require_ui_step_up,
92
+ set_session_cookie,
93
+ webauthn_rp,
94
+ )
95
+
96
+ # Bottom import breaks the __init__ <-> mount cycle: STATIC_DIR / assert_engine_seam above are already
97
+ # bound when mount.py imports them, and mount.py's eager route-module imports fire every
98
+ # register_ui_action so the write-action registry is authoritative before serving.
99
+ from .mount import mount_ui # noqa: E402
100
+
101
+ __all__ = [
102
+ "COOKIE_NAME",
103
+ "STATIC_DIR",
104
+ "SUPPORTED_ENGINE_SEAMS",
105
+ "UI_CSP",
106
+ "WEBAUTHN_EXTRA_MISSING_NOTICE",
107
+ "WEBAUTHN_RP_CHANGED_NOTICE",
108
+ "WEBAUTHN_RP_MISSING_NOTICE",
109
+ "UiSeamMismatch",
110
+ "UiWriteAction",
111
+ "__version__",
112
+ "assert_engine_seam",
113
+ "assert_not_cross_site",
114
+ "assert_same_origin",
115
+ "authorize_ui_ws",
116
+ "clear_session_cookie",
117
+ "is_safe_ui_action",
118
+ "is_unlock_action",
119
+ "lookup_ui_action",
120
+ "mount_ui",
121
+ "pages",
122
+ "register_ui_action",
123
+ "require_ui",
124
+ "require_ui_reauth_only",
125
+ "require_ui_step_up",
126
+ "set_session_cookie",
127
+ "webauthn_rp",
128
+ ]