nthlayer-bench 1.0.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.
- nthlayer_bench/__init__.py +6 -0
- nthlayer_bench/app.py +86 -0
- nthlayer_bench/cli.py +21 -0
- nthlayer_bench-1.0.0.dist-info/METADATA +13 -0
- nthlayer_bench-1.0.0.dist-info/RECORD +8 -0
- nthlayer_bench-1.0.0.dist-info/WHEEL +5 -0
- nthlayer_bench-1.0.0.dist-info/entry_points.txt +2 -0
- nthlayer_bench-1.0.0.dist-info/top_level.txt +1 -0
nthlayer_bench/app.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""NthLayer Bench Textual application.
|
|
2
|
+
|
|
3
|
+
Single-screen TUI that connects to core's HTTP API and displays
|
|
4
|
+
connection status. Foundation for situation board, case bench,
|
|
5
|
+
and case detail screens (Phase 4).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import httpx
|
|
11
|
+
from textual.app import App, ComposeResult
|
|
12
|
+
from textual.containers import Container
|
|
13
|
+
from textual.widgets import Footer, Header, Static
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ConnectionStatus(Static):
|
|
17
|
+
"""Displays connection status to nthlayer-core."""
|
|
18
|
+
|
|
19
|
+
CONNECTED = "[green]● Connected[/green]"
|
|
20
|
+
DISCONNECTED = "[red]● Disconnected[/red] — Core unreachable"
|
|
21
|
+
DEGRADED = "[yellow]● Degraded[/yellow] — Data may be stale"
|
|
22
|
+
|
|
23
|
+
def __init__(self, core_url: str) -> None:
|
|
24
|
+
super().__init__(self.DISCONNECTED)
|
|
25
|
+
self.core_url = core_url.rstrip("/")
|
|
26
|
+
self._connected = False
|
|
27
|
+
|
|
28
|
+
def on_mount(self) -> None:
|
|
29
|
+
self.set_interval(5.0, self._check_health)
|
|
30
|
+
# Check immediately on mount
|
|
31
|
+
self.call_later(self._check_health)
|
|
32
|
+
|
|
33
|
+
async def _check_health(self) -> None:
|
|
34
|
+
try:
|
|
35
|
+
async with httpx.AsyncClient(timeout=5.0) as client:
|
|
36
|
+
resp = await client.get(f"{self.core_url}/health")
|
|
37
|
+
if resp.status_code == 200:
|
|
38
|
+
self._connected = True
|
|
39
|
+
self.update(self.CONNECTED)
|
|
40
|
+
else:
|
|
41
|
+
self._connected = False
|
|
42
|
+
self.update(self.DEGRADED)
|
|
43
|
+
except (httpx.RequestError, OSError):
|
|
44
|
+
self._connected = False
|
|
45
|
+
self.update(self.DISCONNECTED)
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def is_connected(self) -> bool:
|
|
49
|
+
return self._connected
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class BenchApp(App):
|
|
53
|
+
"""NthLayer Bench — operator terminal UI."""
|
|
54
|
+
|
|
55
|
+
TITLE = "NthLayer Bench"
|
|
56
|
+
CSS = """
|
|
57
|
+
#status-bar {
|
|
58
|
+
dock: top;
|
|
59
|
+
height: 3;
|
|
60
|
+
padding: 1;
|
|
61
|
+
background: $surface;
|
|
62
|
+
}
|
|
63
|
+
#main {
|
|
64
|
+
padding: 1;
|
|
65
|
+
}
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
BINDINGS = [
|
|
69
|
+
("q", "quit", "Quit"),
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
def __init__(self, core_url: str = "http://localhost:8000") -> None:
|
|
73
|
+
super().__init__()
|
|
74
|
+
self.core_url = core_url
|
|
75
|
+
|
|
76
|
+
def compose(self) -> ComposeResult:
|
|
77
|
+
yield Header()
|
|
78
|
+
yield Container(
|
|
79
|
+
ConnectionStatus(self.core_url),
|
|
80
|
+
id="status-bar",
|
|
81
|
+
)
|
|
82
|
+
yield Container(
|
|
83
|
+
Static("NthLayer Bench v1.5.0a1\n\nPhase 4 screens coming soon: Situation Board, Case Bench, Case Detail"),
|
|
84
|
+
id="main",
|
|
85
|
+
)
|
|
86
|
+
yield Footer()
|
nthlayer_bench/cli.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""NthLayer Bench CLI — start the operator TUI."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main():
|
|
7
|
+
parser = argparse.ArgumentParser(description="NthLayer Bench — operator TUI")
|
|
8
|
+
parser.add_argument("-V", "--version", action="version", version="%(prog)s 1.5.0a1")
|
|
9
|
+
parser.add_argument("--core-url", default="http://localhost:8000",
|
|
10
|
+
help="Core API URL (default: http://localhost:8000)")
|
|
11
|
+
|
|
12
|
+
args = parser.parse_args()
|
|
13
|
+
|
|
14
|
+
from nthlayer_bench.app import BenchApp
|
|
15
|
+
|
|
16
|
+
app = BenchApp(core_url=args.core_url)
|
|
17
|
+
app.run()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if __name__ == "__main__":
|
|
21
|
+
main()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nthlayer-bench
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: NthLayer Bench — Tier 3 operator TUI for case management and situation awareness
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: nthlayer-common>=0.1.8
|
|
8
|
+
Requires-Dist: textual>=1.0
|
|
9
|
+
Requires-Dist: httpx>=0.27
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: pytest>=8.2; extra == "dev"
|
|
12
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
13
|
+
Requires-Dist: ruff>=0.8; extra == "dev"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
nthlayer_bench/__init__.py,sha256=YezdN1vaIBB9kLbaKGy3m5ZtP1lDO8GyLeoum2z2shg,200
|
|
2
|
+
nthlayer_bench/app.py,sha256=FfHe_SIe2w3wvrmVmITGuPiKVTH-KDEUBNVWUz8fL3k,2484
|
|
3
|
+
nthlayer_bench/cli.py,sha256=SFhpd44AScqvLWQk1raZQ3PRKdmh_8BrT5PnyCmlr74,581
|
|
4
|
+
nthlayer_bench-1.0.0.dist-info/METADATA,sha256=Z1-T2nrEoYSx8Z0TgYYhHkSW70-M_mJO_GNQ8AMLC2c,463
|
|
5
|
+
nthlayer_bench-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
6
|
+
nthlayer_bench-1.0.0.dist-info/entry_points.txt,sha256=rFr1LVxt_mMZN5bLWKJQhAU3Too3SUU_-Gs3JWHi7bk,59
|
|
7
|
+
nthlayer_bench-1.0.0.dist-info/top_level.txt,sha256=DgrsmUZOu5NGnWpDBB8PZ0egpmsFEFGNrYT1wPphaOg,15
|
|
8
|
+
nthlayer_bench-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nthlayer_bench
|