epidash 0.2.0__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 (37) hide show
  1. epidash-0.2.0/.github/workflows/ci.yml +26 -0
  2. epidash-0.2.0/.github/workflows/publish.yml +55 -0
  3. epidash-0.2.0/.gitignore +23 -0
  4. epidash-0.2.0/Dockerfile +27 -0
  5. epidash-0.2.0/LICENSE +21 -0
  6. epidash-0.2.0/PKG-INFO +153 -0
  7. epidash-0.2.0/README.md +135 -0
  8. epidash-0.2.0/docker-compose.yml +27 -0
  9. epidash-0.2.0/epidash/__init__.py +3 -0
  10. epidash-0.2.0/epidash/api/__init__.py +1 -0
  11. epidash-0.2.0/epidash/api/routes_public.py +284 -0
  12. epidash-0.2.0/epidash/cli.py +76 -0
  13. epidash-0.2.0/epidash/config.py +113 -0
  14. epidash-0.2.0/epidash/epidbot_client.py +152 -0
  15. epidash-0.2.0/epidash/logging.py +22 -0
  16. epidash-0.2.0/epidash/main.py +133 -0
  17. epidash-0.2.0/epidash/mcpsrv/__init__.py +1 -0
  18. epidash-0.2.0/epidash/mcpsrv/audit.py +44 -0
  19. epidash-0.2.0/epidash/mcpsrv/auth.py +104 -0
  20. epidash-0.2.0/epidash/mcpsrv/server.py +406 -0
  21. epidash-0.2.0/epidash/schema.sql +78 -0
  22. epidash-0.2.0/epidash/static/app.js +739 -0
  23. epidash-0.2.0/epidash/static/epidbot.webp +0 -0
  24. epidash-0.2.0/epidash/static/geo/br-states.json +1 -0
  25. epidash-0.2.0/epidash/static/index.html +37 -0
  26. epidash-0.2.0/epidash/static/kwar-ai-logo.webp +0 -0
  27. epidash-0.2.0/epidash/static/style.css +314 -0
  28. epidash-0.2.0/epidash/static/vendor/plotly.min.js +3882 -0
  29. epidash-0.2.0/epidash/store.py +503 -0
  30. epidash-0.2.0/pyproject.toml +46 -0
  31. epidash-0.2.0/tests/conftest.py +58 -0
  32. epidash-0.2.0/tests/test_mcp_auth.py +89 -0
  33. epidash-0.2.0/tests/test_mcp_tools.py +285 -0
  34. epidash-0.2.0/tests/test_public_api.py +139 -0
  35. epidash-0.2.0/tests/test_store.py +316 -0
  36. epidash-0.2.0/tests/test_tweak_flow.py +243 -0
  37. epidash-0.2.0/uv.lock +1232 -0
@@ -0,0 +1,26 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - name: Install uv
15
+ uses: astral-sh/setup-uv@v5
16
+ with:
17
+ python-version: '3.12'
18
+
19
+ - name: Sync dependencies
20
+ run: uv sync --frozen
21
+
22
+ - name: Type check
23
+ run: uv run ty check epidash/
24
+
25
+ - name: Run tests
26
+ run: uv run pytest -v
@@ -0,0 +1,55 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags: ['v*']
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - name: Install uv
15
+ uses: astral-sh/setup-uv@v5
16
+ with:
17
+ python-version: '3.12'
18
+
19
+ - name: Sync dependencies
20
+ run: uv sync --frozen
21
+
22
+ - name: Type check
23
+ run: uv run ty check epidash/
24
+
25
+ - name: Run tests
26
+ run: uv run pytest -v
27
+
28
+ publish:
29
+ needs: test
30
+ runs-on: ubuntu-latest
31
+ environment: pypi
32
+ permissions:
33
+ id-token: write # OIDC trusted publishing — no API token secret needed
34
+
35
+ steps:
36
+ - uses: actions/checkout@v4
37
+
38
+ - name: Install uv
39
+ uses: astral-sh/setup-uv@v5
40
+ with:
41
+ python-version: '3.12'
42
+
43
+ - name: Verify tag matches package version
44
+ run: |
45
+ VERSION=$(uv run --no-sync python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
46
+ if [ "v$VERSION" != "$GITHUB_REF_NAME" ]; then
47
+ echo "::error::Tag $GITHUB_REF_NAME does not match package version v$VERSION"
48
+ exit 1
49
+ fi
50
+
51
+ - name: Build package
52
+ run: uv build
53
+
54
+ - name: Publish to PyPI
55
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,23 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .venv/
6
+ venv/
7
+ dist/
8
+ build/
9
+
10
+ # Local state
11
+ data/
12
+ *.db
13
+ *.db-wal
14
+ *.db-shm
15
+
16
+ # Tooling
17
+ .pytest_cache/
18
+ .ty/
19
+ .ruff_cache/
20
+
21
+ # Secrets
22
+ .env
23
+ .env.local
@@ -0,0 +1,27 @@
1
+ FROM python:3.12-slim
2
+
3
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/
4
+
5
+ WORKDIR /app
6
+
7
+ # Install dependencies first (cached layer), then the app code
8
+ COPY pyproject.toml uv.lock README.md ./
9
+ RUN uv sync --frozen --no-dev --no-install-project
10
+
11
+ COPY epidash ./epidash
12
+ RUN uv sync --frozen --no-dev
13
+
14
+ ENV PATH="/app/.venv/bin:$PATH" \
15
+ EPIDASH_DB_PATH=/data/epidash.db
16
+
17
+ RUN useradd --system --create-home epidash \
18
+ && mkdir -p /data \
19
+ && chown -R epidash:epidash /app /data
20
+ USER epidash
21
+
22
+ EXPOSE 8000
23
+
24
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
25
+ CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/healthz', timeout=4)"
26
+
27
+ CMD ["uvicorn", "epidash.main:app", "--host", "0.0.0.0", "--port", "8000"]
epidash-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Deeplearn-PeD
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.
epidash-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,153 @@
1
+ Metadata-Version: 2.5
2
+ Name: epidash
3
+ Version: 0.2.0
4
+ Summary: EpiDash — light MCP-controlled dashboard service: EpidBot publishes and controls dashboards via MCP
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.12
8
+ Requires-Dist: fastapi>=0.115.0
9
+ Requires-Dist: httpx>=0.28.0
10
+ Requires-Dist: mcp<2,>=1.10
11
+ Requires-Dist: pydantic>=2.9
12
+ Requires-Dist: uvicorn[standard]>=0.32.0
13
+ Provides-Extra: dev
14
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
15
+ Requires-Dist: pytest>=8.0; extra == 'dev'
16
+ Requires-Dist: ty>=0.0.1a5; extra == 'dev'
17
+ Description-Content-Type: text/markdown
18
+
19
+ # EpiDash
20
+
21
+ [![CI](https://github.com/Deeplearn-PeD/epidash/actions/workflows/ci.yml/badge.svg)](https://github.com/Deeplearn-PeD/epidash/actions/workflows/ci.yml)
22
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
23
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
24
+ [![Managed with uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v1.json)](https://docs.astral.sh/uv/)
25
+
26
+ A light, MCP-controlled dashboard service. [EpidBot](https://github.com/Deeplearn-PeD/EpiDBot)
27
+ (and any MCP client holding a control key) builds, updates, publishes and
28
+ unpublishes dashboards through an MCP tool surface; anyone with a dashboard
29
+ link views it in a minimal web UI. Viewers can ask EpidBot to tweak a
30
+ dashboard from the browser.
31
+
32
+ ```
33
+ EpidBot agent ──(MCP client, dk_ key)──▶ EpiDash /mcp (FastMCP tools)
34
+ │
35
+ SQLite ◀─────────────┤ (dashboards, charts, keys, audit)
36
+ │
37
+ Viewer browser ──(public link)──▶ EpiDash / (vanilla JS + Plotly)
38
+ │ │
39
+ └── tweak request ──▶ POST /api/dashboards/{slug}/tweak
40
+ │ (X-Internal-Request: shared secret)
41
+ ▼
42
+ EpidBot /api/v1/chat ──agent──▶ EpiDash MCP tools
43
+ ```
44
+
45
+ ## Quick start
46
+
47
+ ```bash
48
+ uv sync
49
+ uv run uvicorn epidash.main:app --port 8000
50
+ ```
51
+
52
+ Then issue a control key and talk to the MCP endpoint:
53
+
54
+ ```bash
55
+ uv run python -m epidash.cli add-key "local"
56
+ # → dk_... (shown once)
57
+ ```
58
+
59
+ ```bash
60
+ # The MCP endpoint is stateless: one POST per call, no handshake needed.
61
+ curl -s -X POST http://localhost:8000/mcp \
62
+ -H "X-API-Key: dk_..." -H "Content-Type: application/json" \
63
+ -H "Accept: application/json, text/event-stream" \
64
+ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
65
+ ```
66
+
67
+ In an MCP client (e.g. Claude Desktop, Cursor, EpidBot):
68
+
69
+ ```json
70
+ {
71
+ "mcpServers": {
72
+ "epidash": {
73
+ "url": "http://localhost:8000/mcp",
74
+ "headers": { "X-API-Key": "dk_..." }
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ ## Configuration (environment variables)
81
+
82
+ | Variable | Default | Purpose |
83
+ |---|---|---|
84
+ | `EPIDASH_DB_PATH` | `data/epidash.db` | SQLite file (WAL) |
85
+ | `EPIDASH_BASE_URL` | `http://localhost:8000` | Public base URL for share links |
86
+ | `EPIDASH_MCP_PATH` | `/mcp` | Mount path of the MCP endpoint |
87
+ | `EPIDASH_ALLOWED_HOSTS` | `localhost,127.0.0.1` | Host header allowlist (DNS-rebinding protection) |
88
+ | `EPIDASH_BOOTSTRAP_KEY` | — | Pre-shared `dk_` key seeded at startup (infra automation) |
89
+ | `EPIDBOT_BASE_URL` | — | EpidBot base URL; enables the tweak flow |
90
+ | `EPIDBOT_INTERNAL_SECRET` | — | Shared secret (EpidBot `INTERNAL_API_SECRET`) or `ek_` API key |
91
+ | `EPIDASH_TWEAK_RATE_LIMIT_PER_HOUR` | `20` | Per-dashboard viewer tweak budget |
92
+ | `EPIDASH_ASK_RATE_LIMIT_PER_HOUR` | `30` | Per-dashboard viewer question budget |
93
+ | `EPIDASH_TWEAK_TIMEOUT_S` | `300` | Max wait for an EpidBot tweak turn |
94
+
95
+ ## MCP tools
96
+
97
+ Read-only: `list_dashboards`, `get_dashboard`, `get_view_stats`, `get_audit_log`.
98
+
99
+ Mutations (audited): `create_dashboard`, `update_dashboard`,
100
+ `publish_dashboard` (confirm), `unpublish_dashboard` (confirm),
101
+ `add_chart`, `update_chart`, `remove_chart` (confirm).
102
+
103
+ ### Chart data envelopes
104
+
105
+ | kind | data |
106
+ |---|---|
107
+ | `line`, `bar`, `pie`, `table` | `{"columns": ["month", "cases"], "rows": [["Jan", 120], ...]}` |
108
+ | `kpi` | `{"value": 1234, "label": "Cases", "unit": "", "delta": "+12%"}` |
109
+ | `choropleth` | `{"values": {"SP": 120, "RJ": 90}, "region_level": "uf", "label": "cases"}` |
110
+
111
+ `spec` accepts style overrides (`{"color": "#4ade80"}`, `{"y_label": "cases"}`) and
112
+ `position` controls ordering/width (`{"order": 1, "width": "half"}`).
113
+
114
+ ## Viewer tweak flow
115
+
116
+ A viewer types a request ("show only the Southeast after 2020") into the
117
+ dashboard's chat box. EpiDash forwards it as an EpidBot chat turn with the
118
+ dashboard context; the EpidBot agent uses its EpiDash MCP tools to update
119
+ the charts; the viewer's browser picks up the change on its next poll.
120
+
121
+ Viewers can also **ask questions** ("why did cases spike in March?") via
122
+ the Ask button — the same chat pipeline, but the prompt instructs EpidBot
123
+ to answer from the dashboard data without modifying it. No authentication
124
+ is required for either; questions are recorded in the `tweaks` table with
125
+ `kind='question'` and rate-limited separately from tweaks.
126
+
127
+ Requires `EPIDBOT_BASE_URL` + `EPIDBOT_INTERNAL_SECRET` (EpidBot must accept
128
+ `X-Internal-Request` on its chat endpoint — see the EpidBot-side integration
129
+ docs) or an `ek_` API key.
130
+
131
+ ## Deployment
132
+
133
+ Single container, single volume:
134
+
135
+ ```bash
136
+ docker compose up -d --build
137
+ ```
138
+
139
+ Production (KwarAI) deploys this via the Ansible `epidash` role behind
140
+ nginx at `dash.kwar-ai.com.br` — see the
141
+ [infrastructure repo](https://github.com/Deeplearn-PeD/infrastructure).
142
+
143
+ ## Development
144
+
145
+ ```bash
146
+ uv sync # deps
147
+ uv run pytest -v # tests
148
+ uv run ty check epidash/ # type check
149
+ ```
150
+
151
+ ## License
152
+
153
+ MIT
@@ -0,0 +1,135 @@
1
+ # EpiDash
2
+
3
+ [![CI](https://github.com/Deeplearn-PeD/epidash/actions/workflows/ci.yml/badge.svg)](https://github.com/Deeplearn-PeD/epidash/actions/workflows/ci.yml)
4
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+ [![Managed with uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v1.json)](https://docs.astral.sh/uv/)
7
+
8
+ A light, MCP-controlled dashboard service. [EpidBot](https://github.com/Deeplearn-PeD/EpiDBot)
9
+ (and any MCP client holding a control key) builds, updates, publishes and
10
+ unpublishes dashboards through an MCP tool surface; anyone with a dashboard
11
+ link views it in a minimal web UI. Viewers can ask EpidBot to tweak a
12
+ dashboard from the browser.
13
+
14
+ ```
15
+ EpidBot agent ──(MCP client, dk_ key)──▶ EpiDash /mcp (FastMCP tools)
16
+ │
17
+ SQLite ◀─────────────┤ (dashboards, charts, keys, audit)
18
+ │
19
+ Viewer browser ──(public link)──▶ EpiDash / (vanilla JS + Plotly)
20
+ │ │
21
+ └── tweak request ──▶ POST /api/dashboards/{slug}/tweak
22
+ │ (X-Internal-Request: shared secret)
23
+ ▼
24
+ EpidBot /api/v1/chat ──agent──▶ EpiDash MCP tools
25
+ ```
26
+
27
+ ## Quick start
28
+
29
+ ```bash
30
+ uv sync
31
+ uv run uvicorn epidash.main:app --port 8000
32
+ ```
33
+
34
+ Then issue a control key and talk to the MCP endpoint:
35
+
36
+ ```bash
37
+ uv run python -m epidash.cli add-key "local"
38
+ # → dk_... (shown once)
39
+ ```
40
+
41
+ ```bash
42
+ # The MCP endpoint is stateless: one POST per call, no handshake needed.
43
+ curl -s -X POST http://localhost:8000/mcp \
44
+ -H "X-API-Key: dk_..." -H "Content-Type: application/json" \
45
+ -H "Accept: application/json, text/event-stream" \
46
+ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
47
+ ```
48
+
49
+ In an MCP client (e.g. Claude Desktop, Cursor, EpidBot):
50
+
51
+ ```json
52
+ {
53
+ "mcpServers": {
54
+ "epidash": {
55
+ "url": "http://localhost:8000/mcp",
56
+ "headers": { "X-API-Key": "dk_..." }
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ ## Configuration (environment variables)
63
+
64
+ | Variable | Default | Purpose |
65
+ |---|---|---|
66
+ | `EPIDASH_DB_PATH` | `data/epidash.db` | SQLite file (WAL) |
67
+ | `EPIDASH_BASE_URL` | `http://localhost:8000` | Public base URL for share links |
68
+ | `EPIDASH_MCP_PATH` | `/mcp` | Mount path of the MCP endpoint |
69
+ | `EPIDASH_ALLOWED_HOSTS` | `localhost,127.0.0.1` | Host header allowlist (DNS-rebinding protection) |
70
+ | `EPIDASH_BOOTSTRAP_KEY` | — | Pre-shared `dk_` key seeded at startup (infra automation) |
71
+ | `EPIDBOT_BASE_URL` | — | EpidBot base URL; enables the tweak flow |
72
+ | `EPIDBOT_INTERNAL_SECRET` | — | Shared secret (EpidBot `INTERNAL_API_SECRET`) or `ek_` API key |
73
+ | `EPIDASH_TWEAK_RATE_LIMIT_PER_HOUR` | `20` | Per-dashboard viewer tweak budget |
74
+ | `EPIDASH_ASK_RATE_LIMIT_PER_HOUR` | `30` | Per-dashboard viewer question budget |
75
+ | `EPIDASH_TWEAK_TIMEOUT_S` | `300` | Max wait for an EpidBot tweak turn |
76
+
77
+ ## MCP tools
78
+
79
+ Read-only: `list_dashboards`, `get_dashboard`, `get_view_stats`, `get_audit_log`.
80
+
81
+ Mutations (audited): `create_dashboard`, `update_dashboard`,
82
+ `publish_dashboard` (confirm), `unpublish_dashboard` (confirm),
83
+ `add_chart`, `update_chart`, `remove_chart` (confirm).
84
+
85
+ ### Chart data envelopes
86
+
87
+ | kind | data |
88
+ |---|---|
89
+ | `line`, `bar`, `pie`, `table` | `{"columns": ["month", "cases"], "rows": [["Jan", 120], ...]}` |
90
+ | `kpi` | `{"value": 1234, "label": "Cases", "unit": "", "delta": "+12%"}` |
91
+ | `choropleth` | `{"values": {"SP": 120, "RJ": 90}, "region_level": "uf", "label": "cases"}` |
92
+
93
+ `spec` accepts style overrides (`{"color": "#4ade80"}`, `{"y_label": "cases"}`) and
94
+ `position` controls ordering/width (`{"order": 1, "width": "half"}`).
95
+
96
+ ## Viewer tweak flow
97
+
98
+ A viewer types a request ("show only the Southeast after 2020") into the
99
+ dashboard's chat box. EpiDash forwards it as an EpidBot chat turn with the
100
+ dashboard context; the EpidBot agent uses its EpiDash MCP tools to update
101
+ the charts; the viewer's browser picks up the change on its next poll.
102
+
103
+ Viewers can also **ask questions** ("why did cases spike in March?") via
104
+ the Ask button — the same chat pipeline, but the prompt instructs EpidBot
105
+ to answer from the dashboard data without modifying it. No authentication
106
+ is required for either; questions are recorded in the `tweaks` table with
107
+ `kind='question'` and rate-limited separately from tweaks.
108
+
109
+ Requires `EPIDBOT_BASE_URL` + `EPIDBOT_INTERNAL_SECRET` (EpidBot must accept
110
+ `X-Internal-Request` on its chat endpoint — see the EpidBot-side integration
111
+ docs) or an `ek_` API key.
112
+
113
+ ## Deployment
114
+
115
+ Single container, single volume:
116
+
117
+ ```bash
118
+ docker compose up -d --build
119
+ ```
120
+
121
+ Production (KwarAI) deploys this via the Ansible `epidash` role behind
122
+ nginx at `dash.kwar-ai.com.br` — see the
123
+ [infrastructure repo](https://github.com/Deeplearn-PeD/infrastructure).
124
+
125
+ ## Development
126
+
127
+ ```bash
128
+ uv sync # deps
129
+ uv run pytest -v # tests
130
+ uv run ty check epidash/ # type check
131
+ ```
132
+
133
+ ## License
134
+
135
+ MIT
@@ -0,0 +1,27 @@
1
+ services:
2
+ epidash:
3
+ build: .
4
+ image: epidash:latest
5
+ container_name: epidash
6
+ ports:
7
+ - '127.0.0.1:8310:8000'
8
+ volumes:
9
+ - ./data:/data
10
+ environment:
11
+ - EPIDASH_DB_PATH=/data/epidash.db
12
+ - EPIDASH_BASE_URL=${EPIDASH_BASE_URL:-http://localhost:8310}
13
+ - EPIDASH_ALLOWED_HOSTS=${EPIDASH_ALLOWED_HOSTS:-localhost,127.0.0.1}
14
+ - EPIDASH_BOOTSTRAP_KEY=${EPIDASH_BOOTSTRAP_KEY:-}
15
+ - EPIDBOT_BASE_URL=${EPIDBOT_BASE_URL:-}
16
+ - EPIDBOT_INTERNAL_SECRET=${EPIDBOT_INTERNAL_SECRET:-}
17
+ restart: unless-stopped
18
+ healthcheck:
19
+ test:
20
+ - CMD
21
+ - python
22
+ - -c
23
+ - "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/healthz', timeout=4)"
24
+ interval: 30s
25
+ timeout: 5s
26
+ retries: 3
27
+ start_period: 10s
@@ -0,0 +1,3 @@
1
+ """EpiDash — light MCP-controlled dashboard service."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1 @@
1
+ """Public REST API: published dashboards + the viewer tweak flow."""