kuma-stats 0.3.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 (36) hide show
  1. kuma_stats-0.3.0/.env.example +9 -0
  2. kuma_stats-0.3.0/.github/workflows/ci.yml +40 -0
  3. kuma_stats-0.3.0/.github/workflows/live-kuma.yml +252 -0
  4. kuma_stats-0.3.0/.gitignore +12 -0
  5. kuma_stats-0.3.0/CHANGELOG.md +42 -0
  6. kuma_stats-0.3.0/CONTRIBUTING.md +24 -0
  7. kuma_stats-0.3.0/LICENSE +21 -0
  8. kuma_stats-0.3.0/Makefile +32 -0
  9. kuma_stats-0.3.0/PKG-INFO +197 -0
  10. kuma_stats-0.3.0/README.md +157 -0
  11. kuma_stats-0.3.0/RESEARCH.md +68 -0
  12. kuma_stats-0.3.0/SECURITY.md +14 -0
  13. kuma_stats-0.3.0/docs/RELEASING.md +155 -0
  14. kuma_stats-0.3.0/pyproject.toml +85 -0
  15. kuma_stats-0.3.0/renovate.json +61 -0
  16. kuma_stats-0.3.0/scripts/bump-version.sh +142 -0
  17. kuma_stats-0.3.0/scripts/prep-release.sh +67 -0
  18. kuma_stats-0.3.0/scripts/release.sh +171 -0
  19. kuma_stats-0.3.0/src/kuma_stats/__init__.py +3 -0
  20. kuma_stats-0.3.0/src/kuma_stats/__main__.py +5 -0
  21. kuma_stats-0.3.0/src/kuma_stats/cli.py +328 -0
  22. kuma_stats-0.3.0/src/kuma_stats/core/__init__.py +1 -0
  23. kuma_stats-0.3.0/src/kuma_stats/core/adapter.py +103 -0
  24. kuma_stats-0.3.0/src/kuma_stats/core/config.py +94 -0
  25. kuma_stats-0.3.0/src/kuma_stats/core/errors.py +44 -0
  26. kuma_stats-0.3.0/src/kuma_stats/core/retry.py +51 -0
  27. kuma_stats-0.3.0/src/kuma_stats/core/session.py +21 -0
  28. kuma_stats-0.3.0/src/kuma_stats/output/__init__.py +54 -0
  29. kuma_stats-0.3.0/src/kuma_stats/py.typed +0 -0
  30. kuma_stats-0.3.0/src/kuma_stats/services/__init__.py +1 -0
  31. kuma_stats-0.3.0/src/kuma_stats/services/monitors.py +111 -0
  32. kuma_stats-0.3.0/tests/integration/test_installed_cli.py +95 -0
  33. kuma_stats-0.3.0/tests/integration/test_live_kuma.py +136 -0
  34. kuma_stats-0.3.0/tests/test_cli.py +269 -0
  35. kuma_stats-0.3.0/tests/test_release_scripts.py +280 -0
  36. kuma_stats-0.3.0/uv.lock +1154 -0
@@ -0,0 +1,9 @@
1
+ # Uptime Kuma server URL (required)
2
+ KUMA_URL=https://kuma.example.com
3
+
4
+ # Use either a server-issued JWT token...
5
+ # KUMA_TOKEN=
6
+
7
+ # ...or a username and password. These are also required for `kuma-stats login`.
8
+ KUMA_USERNAME=
9
+ KUMA_PASSWORD=
@@ -0,0 +1,40 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubicloud-standard-2
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ python-version: ['3.10', '3.11', '3.12']
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+ - run: python -m pip install --upgrade pip
24
+ - run: python -m pip install '.[dev]' build twine
25
+ - run: python -m ruff check .
26
+ - run: python -m mypy src/kuma_stats
27
+ - run: python -m pytest -q
28
+ - run: python -m build
29
+ - run: python -m twine check dist/*
30
+ - name: Verify wheel installation
31
+ run: |
32
+ python -m venv /tmp/wheel-venv
33
+ /tmp/wheel-venv/bin/python -m pip install dist/*.whl
34
+ /tmp/wheel-venv/bin/kuma-stats --help
35
+ /tmp/wheel-venv/bin/python -m kuma_stats --help
36
+ - name: Verify sdist installation
37
+ run: |
38
+ python -m venv /tmp/sdist-venv
39
+ /tmp/sdist-venv/bin/python -m pip install dist/*.tar.gz
40
+ /tmp/sdist-venv/bin/kuma-stats --help
@@ -0,0 +1,252 @@
1
+ name: Live Kuma smoke test
2
+
3
+ # Maintainer-only Docker-backed Uptime Kuma tests.
4
+ #
5
+ # Preferred entry point: comment `/run-live-tests` on a same-repository PR.
6
+ # The manual dispatch path is retained for the Actions UI; both paths are
7
+ # restricted to the repository owner.
8
+ on:
9
+ issue_comment:
10
+ types: [created]
11
+ workflow_dispatch:
12
+ inputs:
13
+ pr_number:
14
+ description: PR number for a PR check and result comment
15
+ type: string
16
+ required: false
17
+ pr_sha:
18
+ description: PR head SHA to test (defaults to the workflow ref SHA)
19
+ type: string
20
+ required: false
21
+
22
+ permissions:
23
+ contents: read
24
+
25
+ jobs:
26
+ prepare:
27
+ name: Authorize Live Kuma Tests
28
+ # issue_comment workflows always run from the default branch. Resolve the
29
+ # PR head explicitly, and verify both the initial and re-run actor.
30
+ if: |
31
+ (
32
+ github.event_name == 'issue_comment' &&
33
+ github.event.issue.pull_request &&
34
+ github.event.comment.body == '/run-live-tests' &&
35
+ github.actor == github.repository_owner &&
36
+ github.triggering_actor == github.repository_owner
37
+ ) || (
38
+ github.event_name == 'workflow_dispatch' &&
39
+ github.actor == github.repository_owner &&
40
+ github.triggering_actor == github.repository_owner
41
+ )
42
+ runs-on: ubuntu-latest
43
+ permissions:
44
+ pull-requests: read
45
+ outputs:
46
+ pr_number: ${{ steps.pr.outputs.number }}
47
+ pr_sha: ${{ steps.pr.outputs.sha }}
48
+
49
+ steps:
50
+ - name: Restore originally authorized PR target on rerun
51
+ if: github.event_name == 'issue_comment' && github.run_attempt > 1
52
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
53
+ with:
54
+ name: live-kuma-target-${{ github.run_id }}
55
+ path: ${{ runner.temp }}/authorized-live-kuma-target
56
+ - name: Get PR information
57
+ id: pr
58
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
59
+ env:
60
+ PR_NUMBER: ${{ inputs.pr_number }}
61
+ PR_SHA: ${{ inputs.pr_sha }}
62
+ RESTORED_TARGET: ${{ runner.temp }}/authorized-live-kuma-target/target.json
63
+ with:
64
+ script: |
65
+ const fs = require('node:fs');
66
+ let target;
67
+
68
+ if (context.eventName === 'issue_comment' && context.runAttempt > 1) {
69
+ target = JSON.parse(fs.readFileSync(process.env.RESTORED_TARGET, 'utf8'));
70
+ } else if (context.eventName === 'workflow_dispatch' && !process.env.PR_NUMBER) {
71
+ target = { number: '', sha: process.env.PR_SHA || context.sha };
72
+ } else {
73
+ const pullNumber = context.eventName === 'issue_comment'
74
+ ? context.issue.number
75
+ : Number(process.env.PR_NUMBER);
76
+ const pr = await github.rest.pulls.get({
77
+ owner: context.repo.owner,
78
+ repo: context.repo.repo,
79
+ pull_number: pullNumber,
80
+ });
81
+
82
+ if (context.eventName === 'issue_comment' && pr.data.head.repo.full_name !== pr.data.base.repo.full_name) {
83
+ core.setFailed('Live Kuma tests cannot run on fork PRs.');
84
+ return;
85
+ }
86
+
87
+ target = { number: String(pr.data.number), sha: pr.data.head.sha };
88
+ if (process.env.PR_SHA && process.env.PR_SHA !== target.sha) {
89
+ core.setOutput('number', target.number);
90
+ core.setOutput('sha', target.sha);
91
+ core.setFailed('pr_sha must match the current head SHA of pr_number.');
92
+ return;
93
+ }
94
+ }
95
+
96
+ core.setOutput('number', target.number);
97
+ core.setOutput('sha', target.sha);
98
+ fs.writeFileSync(`${process.env.RUNNER_TEMP}/target.json`, JSON.stringify(target));
99
+ - name: Save authorized PR target
100
+ if: github.event_name == 'issue_comment' && github.run_attempt == 1
101
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
102
+ with:
103
+ name: live-kuma-target-${{ github.run_id }}
104
+ path: ${{ runner.temp }}/target.json
105
+ if-no-files-found: error
106
+
107
+ live-kuma:
108
+ name: Run Live Kuma Tests
109
+ needs: prepare
110
+ # A re-run can be initiated by another collaborator, so check the
111
+ # triggering actor again before executing PR-controlled code.
112
+ if: |
113
+ always() &&
114
+ !cancelled() &&
115
+ (
116
+ (
117
+ github.event_name == 'issue_comment' &&
118
+ needs.prepare.result == 'success' &&
119
+ github.actor == github.repository_owner &&
120
+ github.triggering_actor == github.repository_owner
121
+ ) ||
122
+ (
123
+ github.event_name == 'workflow_dispatch' &&
124
+ needs.prepare.result == 'success' &&
125
+ github.actor == github.repository_owner &&
126
+ github.triggering_actor == github.repository_owner
127
+ )
128
+ )
129
+ runs-on: ubuntu-latest
130
+ timeout-minutes: 10
131
+ # This job executes PR-controlled code; it deliberately has no write token.
132
+ permissions:
133
+ contents: read
134
+ outputs:
135
+ pr_number: ${{ steps.meta.outputs.pr_number }}
136
+ pr_sha: ${{ steps.meta.outputs.pr_sha }}
137
+
138
+ steps:
139
+ - name: Resolve target metadata
140
+ id: meta
141
+ run: |
142
+ echo "pr_number=${{ needs.prepare.outputs.pr_number }}" >> "$GITHUB_OUTPUT"
143
+ echo "pr_sha=${{ needs.prepare.outputs.pr_sha }}" >> "$GITHUB_OUTPUT"
144
+
145
+ - uses: actions/checkout@v4
146
+ with:
147
+ ref: ${{ steps.meta.outputs.pr_sha }}
148
+ persist-credentials: false
149
+ - uses: actions/setup-python@v5
150
+ with:
151
+ python-version: '3.12'
152
+ - run: python -m pip install --upgrade pip
153
+ - run: python -m pip install '.[dev]' build
154
+ - run: python -m build
155
+ - name: Install built wheel into a clean environment
156
+ run: |
157
+ python -m venv /tmp/kuma-live-venv
158
+ /tmp/kuma-live-venv/bin/python -m pip install dist/*.whl
159
+ - name: Test installed CLI against disposable Uptime Kuma
160
+ env:
161
+ RUN_LIVE_KUMA_TESTS: '1'
162
+ KUMA_STATS_CLI: /tmp/kuma-live-venv/bin/kuma-stats
163
+ KUMA_LOG_PATH: ${{ runner.temp }}/kuma-container.log
164
+ run: python -m pytest -q tests/integration/test_live_kuma.py
165
+ - name: Upload Uptime Kuma container logs
166
+ if: always()
167
+ uses: actions/upload-artifact@v4
168
+ with:
169
+ name: kuma-container-logs
170
+ path: ${{ runner.temp }}/kuma-container.log
171
+ if-no-files-found: ignore
172
+
173
+ report:
174
+ name: Report Live Kuma Result
175
+ needs: [prepare, live-kuma]
176
+ if: |
177
+ always() &&
178
+ (
179
+ needs.live-kuma.result != 'skipped' ||
180
+ (
181
+ github.event_name == 'workflow_dispatch' &&
182
+ needs.prepare.result == 'failure'
183
+ )
184
+ ) &&
185
+ (
186
+ (
187
+ github.event_name == 'issue_comment' &&
188
+ needs.prepare.result == 'success' &&
189
+ github.actor == github.repository_owner &&
190
+ github.triggering_actor == github.repository_owner
191
+ ) ||
192
+ (
193
+ github.event_name == 'workflow_dispatch' &&
194
+ inputs.pr_number != '' &&
195
+ github.actor == github.repository_owner &&
196
+ github.triggering_actor == github.repository_owner
197
+ )
198
+ )
199
+ runs-on: ubuntu-latest
200
+ permissions:
201
+ checks: write
202
+ pull-requests: write
203
+
204
+ steps:
205
+ - name: Create check and comment on PR
206
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
207
+ with:
208
+ script: |
209
+ const result = '${{ needs.live-kuma.result }}' === 'skipped'
210
+ ? 'failure'
211
+ : '${{ needs.live-kuma.result }}';
212
+ const conclusion = result === 'success'
213
+ ? 'success'
214
+ : result === 'cancelled'
215
+ ? 'cancelled'
216
+ : 'failure';
217
+ const sha = '${{ needs.live-kuma.outputs.pr_sha }}' || '${{ needs.prepare.outputs.pr_sha }}';
218
+ const prNumber = Number('${{ needs.live-kuma.outputs.pr_number }}' || '${{ needs.prepare.outputs.pr_number }}');
219
+ const logsUrl = '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}';
220
+ const title = conclusion === 'success'
221
+ ? 'Live Kuma tests passed'
222
+ : conclusion === 'cancelled'
223
+ ? 'Live Kuma tests cancelled'
224
+ : 'Live Kuma tests failed';
225
+ const body = conclusion === 'success'
226
+ ? `✅ Live Kuma tests passed. Tested commit: \`${sha}\`. [View logs](${logsUrl})`
227
+ : conclusion === 'cancelled'
228
+ ? `⚪ Live Kuma tests cancelled. Tested commit: \`${sha}\`. [View logs](${logsUrl})`
229
+ : `❌ Live Kuma tests failed. Tested commit: \`${sha}\`. [View logs](${logsUrl})`;
230
+
231
+ await github.rest.checks.create({
232
+ owner: context.repo.owner,
233
+ repo: context.repo.repo,
234
+ name: 'Live Kuma smoke test',
235
+ head_sha: sha,
236
+ status: 'completed',
237
+ conclusion,
238
+ details_url: logsUrl,
239
+ output: {
240
+ title,
241
+ summary: `[View live-test logs](${logsUrl})`,
242
+ },
243
+ });
244
+
245
+ if (Number.isFinite(prNumber) && prNumber > 0) {
246
+ await github.rest.issues.createComment({
247
+ owner: context.repo.owner,
248
+ repo: context.repo.repo,
249
+ issue_number: prNumber,
250
+ body,
251
+ });
252
+ }
@@ -0,0 +1,12 @@
1
+ .venv/
2
+ build/
3
+ dist/
4
+ __pycache__/
5
+ *.py[cod]
6
+ .mypy_cache/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ src/*.egg-info/
10
+ .env
11
+ .env.*
12
+ !.env.example
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [Unreleased]
8
+
9
+
10
+ ## [0.3.0] - 2026-07-20
11
+
12
+ ### Added
13
+
14
+ - Public-release documentation, packaging metadata, CI validation, and security policy.
15
+
16
+ ### Changed
17
+
18
+ - Normalized Uptime Kuma errors and clarified configuration behavior.
19
+ - Build and package management migrated from setuptools to uv, enabling reproducible builds via a lock file. ([#23])
20
+
21
+ ### Fixed
22
+
23
+ - Source distributions no longer include local agent artifacts that could prevent installation. ([#25])
24
+ - Connection timeout and configuration-validation errors are now reliably enforced and surfaced instead of silently ignored. ([d5ad17d])
25
+
26
+ [d5ad17d]: https://github.com/crcatala/kuma-stats/commit/d5ad17d
27
+ [#23]: https://github.com/crcatala/kuma-stats/pull/23
28
+ [#25]: https://github.com/crcatala/kuma-stats/pull/25
29
+
30
+
31
+ ## Release process
32
+
33
+ PyPI releases are immutable. Bump the version with:
34
+
35
+ ```bash
36
+ make bump # minor bump (0.2.0 → 0.3.0)
37
+ make bump/patch # patch bump (0.2.0 → 0.2.1)
38
+ make bump/major # major bump (0.2.0 → 1.0.0)
39
+ make bump/1.0.0 # explicit version
40
+ ```
41
+
42
+ This updates `__version__` and promotes `## [Unreleased]` into a dated `## [X.Y.Z]` section. Review with `git diff`, commit, push, then use `make release-dry` before `make release`; publish the resulting `dist/*` artifacts separately with `uv run twine upload dist/*`.
@@ -0,0 +1,24 @@
1
+ # Contributing
2
+
3
+ Thanks for your interest in `kuma-stats`.
4
+
5
+ This is a personally maintained project. Code contributions, pull requests, and feature requests are not accepted at this time. Unsolicited pull requests may be closed without review.
6
+
7
+ ## Bug reports
8
+
9
+ Bug reports are welcome when they include:
10
+
11
+ - clear reproduction steps;
12
+ - the `kuma-stats` version;
13
+ - operating system and Python version; and
14
+ - relevant, sanitized error output.
15
+
16
+ Please search existing issues before opening a new one. Do not include passwords, JWTs, server URLs, or other credentials in a public report.
17
+
18
+ ## Security issues
19
+
20
+ Do not open a public issue for a security vulnerability. Follow the private reporting guidance in [SECURITY.md](SECURITY.md).
21
+
22
+ ## Forks
23
+
24
+ You may fork and adapt this project under the terms of the [MIT License](LICENSE).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Christian Catalan
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,32 @@
1
+ .PHONY: setup format lint typecheck test live-test build verify bump release-prep release release-dry
2
+ setup:
3
+ uv sync --all-extras
4
+ format:
5
+ uv run ruff format .
6
+ lint:
7
+ uv run ruff check .
8
+ typecheck:
9
+ uv run mypy src/kuma_stats
10
+ test:
11
+ uv run pytest -q
12
+ # Requires Docker; starts a disposable, pinned Uptime Kuma container.
13
+ live-test:
14
+ uv run pytest -q tests/integration/test_live_kuma.py
15
+ build:
16
+ uv build
17
+ uv run twine check dist/*
18
+ verify: setup lint typecheck test build
19
+ # Summarize changes since the last v* tag and print an LLM prompt for release notes.
20
+ bump:
21
+ ./scripts/bump-version.sh
22
+ # Bump with explicit version or type: make bump VERSION=patch / make bump VERSION=1.2.3
23
+ bump/%:
24
+ ./scripts/bump-version.sh $*
25
+ release-prep:
26
+ ./scripts/prep-release.sh
27
+ # Preview the safeguards and commands for a GitHub release.
28
+ release-dry:
29
+ ./scripts/release.sh --dry-run
30
+ # Validate, tag, and create a GitHub Release. PyPI publishing remains manual.
31
+ release:
32
+ ./scripts/release.sh
@@ -0,0 +1,197 @@
1
+ Metadata-Version: 2.4
2
+ Name: kuma-stats
3
+ Version: 0.3.0
4
+ Summary: Read-only CLI for Uptime Kuma monitor status, uptime, and heartbeat data
5
+ Project-URL: Homepage, https://github.com/crcatala/kuma-stats
6
+ Project-URL: Repository, https://github.com/crcatala/kuma-stats
7
+ Project-URL: Issues, https://github.com/crcatala/kuma-stats/issues
8
+ Project-URL: Security, https://github.com/crcatala/kuma-stats/security/policy
9
+ Author-email: Christian Catalan <crcatala@gmail.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: cli,monitoring,status,uptime,uptime-kuma
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: System :: Monitoring
23
+ Classifier: Topic :: Utilities
24
+ Requires-Python: >=3.10
25
+ Requires-Dist: click>=8.0
26
+ Requires-Dist: keyring>=24.0
27
+ Requires-Dist: platformdirs>=4.0
28
+ Requires-Dist: python-dotenv>=1.0
29
+ Requires-Dist: pyyaml>=6.0
30
+ Requires-Dist: rich>=13.0
31
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
32
+ Requires-Dist: uptime-kuma-api>=1.2.1
33
+ Provides-Extra: dev
34
+ Requires-Dist: build>=1.2; extra == 'dev'
35
+ Requires-Dist: mypy>=1.13; extra == 'dev'
36
+ Requires-Dist: pytest>=8.0; extra == 'dev'
37
+ Requires-Dist: ruff>=0.8; extra == 'dev'
38
+ Requires-Dist: twine>=5.0; extra == 'dev'
39
+ Description-Content-Type: text/markdown
40
+
41
+ # kuma-stats
42
+
43
+ [![CI](https://github.com/crcatala/kuma-stats/actions/workflows/ci.yml/badge.svg)](https://github.com/crcatala/kuma-stats/actions/workflows/ci.yml)
44
+ [![PyPI](https://img.shields.io/pypi/v/kuma-stats)](https://pypi.org/project/kuma-stats/)
45
+ [![Python](https://img.shields.io/pypi/pyversions/kuma-stats)](https://pypi.org/project/kuma-stats/)
46
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
47
+
48
+ A read-only terminal dashboard for [Uptime Kuma](https://github.com/louislam/uptime-kuma): current monitor state, uptime percentages, response time, and heartbeat history.
49
+
50
+ > `kuma-stats` is an independent project and is not affiliated with or endorsed by Uptime Kuma.
51
+
52
+ ## Install
53
+
54
+ Requires Python 3.10 or later.
55
+
56
+ ```bash
57
+ pip install kuma-stats
58
+ # or install as an isolated command-line tool
59
+ uv tool install kuma-stats
60
+ # pipx install kuma-stats
61
+ ```
62
+
63
+ Verify the installation:
64
+
65
+ ```bash
66
+ kuma-stats --version
67
+ ```
68
+
69
+ ## Quick start
70
+
71
+ Set the Uptime Kuma URL and a server-issued JWT token. Environment variables are preferred for automation; do not pass passwords or tokens as command-line arguments because they can be exposed in shell history or process listings.
72
+
73
+ ```bash
74
+ export KUMA_URL=https://kuma.example.com
75
+ export KUMA_TOKEN=your-server-issued-jwt
76
+ kuma-stats list
77
+ kuma-stats --format json uptime
78
+ ```
79
+
80
+ Alternatively, create a `.env` file in the directory where you run the command:
81
+
82
+ ```dotenv
83
+ KUMA_URL=https://kuma.example.com
84
+ KUMA_TOKEN=your-server-issued-jwt
85
+ ```
86
+
87
+ `kuma-stats` loads this working-directory `.env` without overwriting environment variables. Keep it out of version control.
88
+
89
+ To create a token with username/password credentials already supplied through environment variables, use:
90
+
91
+ ```bash
92
+ export KUMA_USERNAME=your-username
93
+ export KUMA_PASSWORD=your-password
94
+ kuma-stats login --store-keyring
95
+ ```
96
+
97
+ The keyring is the preferred token store. `login --store-token` writes an owner-only `.env` file; use it only when the OS keyring is unavailable.
98
+
99
+ ## Commands
100
+
101
+ | Command | Description |
102
+ | --- | --- |
103
+ | `login [--store-token \| --store-keyring]` | Authenticate and print or securely save a server-issued JWT |
104
+ | `list` | List all monitors and current telemetry |
105
+ | `status [ID…]` | Show details and recent heartbeats; omit IDs for all monitors |
106
+ | `beats ID [--hours N]` | Show heartbeat history for one monitor |
107
+ | `uptime` | Show 24-hour, 30-day, and one-year uptime |
108
+
109
+ Examples:
110
+
111
+ ```bash
112
+ kuma-stats list
113
+ kuma-stats status 1 2
114
+ kuma-stats beats 1 --hours 48
115
+ kuma-stats --format yaml uptime
116
+ kuma-stats --format json list
117
+ ```
118
+
119
+ Table output is the default. Use `--format json` or `--format yaml` for structured output. JSON and YAML errors are written to stderr with a stable code and message.
120
+
121
+ ## Configuration
122
+
123
+ Values are resolved in this order, from highest to lowest precedence:
124
+
125
+ 1. Command-line options
126
+ 2. Environment variables
127
+ 3. A `.env` file in the working directory
128
+ 4. User configuration: `$XDG_CONFIG_HOME/kuma-stats/config.toml` on Linux
129
+ 5. An OS-keyring token
130
+
131
+ Use `KUMA_CONFIG_FILE` to select a different TOML file. Example:
132
+
133
+ ```toml
134
+ url = "https://kuma.example.com"
135
+ format = "table" # table, json, or yaml
136
+ timeout = 30
137
+ max_retries = 2
138
+ insecure = false
139
+ ```
140
+
141
+ | Variable | Purpose |
142
+ | --- | --- |
143
+ | `KUMA_URL` | Uptime Kuma server URL |
144
+ | `KUMA_TOKEN` | Server-issued JWT token |
145
+ | `KUMA_USERNAME`, `KUMA_PASSWORD` | Login credentials; required for `login` without a token |
146
+ | `KUMA_FORMAT` | Default output format: `table`, `json`, or `yaml` |
147
+ | `KUMA_TIMEOUT` | Per-connection timeout and overall retry budget in seconds |
148
+ | `KUMA_MAX_RETRIES` | Retry count for transient connection failures |
149
+ | `KUMA_INSECURE` | Set to `true` only for a trusted self-signed server |
150
+ | `NO_COLOR` | Disable terminal colour |
151
+
152
+ TLS certificate verification is enabled by default. Use `--insecure` only for a trusted self-signed server. Invalid or unreadable TOML configuration files fail with an `INVALID_CONFIGURATION` error instead of being ignored.
153
+
154
+ ## Compatibility
155
+
156
+ `kuma-stats` supports Python 3.10–3.12 and uses the maintained [`uptime-kuma-api`](https://github.com/lucasheld/uptime-kuma-api) client for Uptime Kuma communication. CI smoke-tests token authentication, password login, failed authentication, and `list` telemetry against a disposable [Uptime Kuma 1.23.16](https://hub.docker.com/layers/louislam/uptime-kuma/1.23.16/images/sha256-431fee3be822b04861cf0e35daf4beef6b7cb37391c5f26c3ad6e12ce280fe18) Docker container. It uses no external server or credentials.
157
+
158
+ ## Development
159
+
160
+ ```bash
161
+ make setup
162
+ make verify
163
+ make live-test # requires Docker
164
+ ```
165
+
166
+ `make verify` runs linting, type checks, tests, builds wheel/sdist artifacts, and validates their metadata. `make live-test` runs the separately gated Docker compatibility smoke test; it creates disposable credentials and state and never contacts a production server.
167
+
168
+ ### Releasing
169
+
170
+ Releases are intentionally manual. Draft notes from the commits since the last release:
171
+
172
+ ```bash
173
+ make release-prep
174
+ ```
175
+
176
+ Use the printed prompt with an LLM if useful, review its suggested Keep a Changelog entry, then update `CHANGELOG.md` and `src/kuma_stats/__init__.py`. Preview and create the GitHub Release:
177
+
178
+ ```bash
179
+ make release-dry
180
+ make release
181
+ ```
182
+
183
+ `make release` requires a clean `main` worktree, a new versioned changelog section, and confirmation. It validates the project, creates and pushes an annotated `vX.Y.Z` tag, then attaches the validated wheel and sdist to a GitHub Release. PyPI publishing is a separate deliberate step:
184
+
185
+ ```bash
186
+ uv run twine upload dist/*
187
+ ```
188
+
189
+ See [docs/RELEASING.md](docs/RELEASING.md) for PyPI/TestPyPI account setup, local token storage, testing, and troubleshooting.
190
+
191
+ ## Security and support
192
+
193
+ See [SECURITY.md](SECURITY.md) for private vulnerability reporting, [CONTRIBUTING.md](CONTRIBUTING.md) for the maintenance policy, and [CHANGELOG.md](CHANGELOG.md) for release history.
194
+
195
+ ## License
196
+
197
+ Distributed under the [MIT License](LICENSE).