syspanel 0.1.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 (65) hide show
  1. syspanel-0.1.0/.gitignore +57 -0
  2. syspanel-0.1.0/LICENSE +21 -0
  3. syspanel-0.1.0/PKG-INFO +102 -0
  4. syspanel-0.1.0/README.md +63 -0
  5. syspanel-0.1.0/pyproject.toml +69 -0
  6. syspanel-0.1.0/src/system_tools/__init__.py +7 -0
  7. syspanel-0.1.0/src/system_tools/__main__.py +10 -0
  8. syspanel-0.1.0/src/system_tools/cli.py +54 -0
  9. syspanel-0.1.0/src/system_tools/config.py +54 -0
  10. syspanel-0.1.0/src/system_tools/core/__init__.py +5 -0
  11. syspanel-0.1.0/src/system_tools/core/audit.py +51 -0
  12. syspanel-0.1.0/src/system_tools/core/capabilities.py +57 -0
  13. syspanel-0.1.0/src/system_tools/core/elevation.py +58 -0
  14. syspanel-0.1.0/src/system_tools/core/errors.py +66 -0
  15. syspanel-0.1.0/src/system_tools/core/models.py +192 -0
  16. syspanel-0.1.0/src/system_tools/core/safety.py +189 -0
  17. syspanel-0.1.0/src/system_tools/providers/__init__.py +5 -0
  18. syspanel-0.1.0/src/system_tools/providers/base.py +61 -0
  19. syspanel-0.1.0/src/system_tools/providers/common/__init__.py +5 -0
  20. syspanel-0.1.0/src/system_tools/providers/common/diskscan.py +219 -0
  21. syspanel-0.1.0/src/system_tools/providers/common/processes.py +85 -0
  22. syspanel-0.1.0/src/system_tools/providers/common/resources.py +139 -0
  23. syspanel-0.1.0/src/system_tools/providers/windows/__init__.py +5 -0
  24. syspanel-0.1.0/src/system_tools/providers/windows/battery.py +141 -0
  25. syspanel-0.1.0/src/system_tools/providers/windows/cleanup.py +115 -0
  26. syspanel-0.1.0/src/system_tools/providers/windows/power.py +67 -0
  27. syspanel-0.1.0/src/system_tools/providers/windows/services.py +80 -0
  28. syspanel-0.1.0/src/system_tools/providers/windows/startup.py +217 -0
  29. syspanel-0.1.0/src/system_tools/services/__init__.py +5 -0
  30. syspanel-0.1.0/src/system_tools/services/battery_service.py +25 -0
  31. syspanel-0.1.0/src/system_tools/services/cleanup_service.py +290 -0
  32. syspanel-0.1.0/src/system_tools/services/disk_service.py +126 -0
  33. syspanel-0.1.0/src/system_tools/services/power_service.py +99 -0
  34. syspanel-0.1.0/src/system_tools/services/process_service.py +68 -0
  35. syspanel-0.1.0/src/system_tools/services/resource_service.py +25 -0
  36. syspanel-0.1.0/src/system_tools/services/service_watch.py +140 -0
  37. syspanel-0.1.0/src/system_tools/services/startup_service.py +47 -0
  38. syspanel-0.1.0/src/system_tools/web/__init__.py +5 -0
  39. syspanel-0.1.0/src/system_tools/web/app.py +109 -0
  40. syspanel-0.1.0/src/system_tools/web/deps.py +29 -0
  41. syspanel-0.1.0/src/system_tools/web/routes/__init__.py +5 -0
  42. syspanel-0.1.0/src/system_tools/web/routes/api_battery.py +20 -0
  43. syspanel-0.1.0/src/system_tools/web/routes/api_capabilities.py +40 -0
  44. syspanel-0.1.0/src/system_tools/web/routes/api_cleanup.py +56 -0
  45. syspanel-0.1.0/src/system_tools/web/routes/api_disk.py +105 -0
  46. syspanel-0.1.0/src/system_tools/web/routes/api_power.py +44 -0
  47. syspanel-0.1.0/src/system_tools/web/routes/api_processes.py +35 -0
  48. syspanel-0.1.0/src/system_tools/web/routes/api_resources.py +41 -0
  49. syspanel-0.1.0/src/system_tools/web/routes/api_services.py +56 -0
  50. syspanel-0.1.0/src/system_tools/web/routes/api_startup.py +49 -0
  51. syspanel-0.1.0/src/system_tools/web/routes/pages.py +76 -0
  52. syspanel-0.1.0/src/system_tools/web/static/css/components.css +296 -0
  53. syspanel-0.1.0/src/system_tools/web/static/css/tokens.css +82 -0
  54. syspanel-0.1.0/src/system_tools/web/static/js/core.js +235 -0
  55. syspanel-0.1.0/src/system_tools/web/static/js/sparkline.js +36 -0
  56. syspanel-0.1.0/src/system_tools/web/static/js/treemap.js +120 -0
  57. syspanel-0.1.0/src/system_tools/web/templates/base.html +112 -0
  58. syspanel-0.1.0/src/system_tools/web/templates/battery.html +79 -0
  59. syspanel-0.1.0/src/system_tools/web/templates/cleanup.html +95 -0
  60. syspanel-0.1.0/src/system_tools/web/templates/dashboard.html +84 -0
  61. syspanel-0.1.0/src/system_tools/web/templates/disk.html +222 -0
  62. syspanel-0.1.0/src/system_tools/web/templates/power.html +101 -0
  63. syspanel-0.1.0/src/system_tools/web/templates/processes.html +107 -0
  64. syspanel-0.1.0/src/system_tools/web/templates/services.html +111 -0
  65. syspanel-0.1.0/src/system_tools/web/templates/startup.html +95 -0
@@ -0,0 +1,57 @@
1
+ # Environment
2
+ env/
3
+ venv/
4
+ ENV/
5
+ VENV/
6
+ .venv/
7
+
8
+ # Python cache
9
+ __pycache__/
10
+ *.py[cod]
11
+ *$py.class
12
+ *.so
13
+ .Python
14
+
15
+ # Build and distribution
16
+ build/
17
+ dist/
18
+ *.egg-info/
19
+ .eggs/
20
+ *.egg
21
+ *.whl
22
+ *.tar.gz
23
+ pip-log.txt
24
+ pip-delete-this-directory.txt
25
+
26
+ # Testing / typing / linting
27
+ .pytest_cache/
28
+ .coverage
29
+ htmlcov/
30
+ .mypy_cache/
31
+ .ruff_cache/
32
+ .tox/
33
+
34
+ # Data and downloads
35
+ dl/
36
+ downloads/
37
+
38
+ # Databases
39
+ *.db
40
+ *.sqlite
41
+ *.sqlite3
42
+
43
+ # Secrets / local config
44
+ .env
45
+ *.local
46
+
47
+ # App runtime data
48
+ .system-tools/
49
+ *.jsonl
50
+
51
+ # IDE and OS
52
+ .DS_Store
53
+ .vscode/
54
+ .idea/
55
+ *.swp
56
+ *.swo
57
+ *~
syspanel-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pandiyaraj Karuppasamy
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,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: syspanel
3
+ Version: 0.1.0
4
+ Summary: A local Windows system dashboard: resource monitor, disk tree viewer, battery health, process manager, service watchlist, startup monitor and cleanup manager.
5
+ Project-URL: Repository, https://github.com/pandiyarajk/syspanel
6
+ Project-URL: Issues, https://github.com/pandiyarajk/syspanel/issues
7
+ Author-email: Pandiyaraj Karuppasamy <pandiyarajk@live.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: battery-health,dashboard,disk-usage,process-manager,system-monitor,windows
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: Microsoft :: Windows
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: System :: Monitoring
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: fastapi>=0.110
23
+ Requires-Dist: jinja2>=3.1
24
+ Requires-Dist: psutil>=6.0
25
+ Requires-Dist: pydantic-settings>=2.2
26
+ Requires-Dist: pydantic>=2.6
27
+ Requires-Dist: send2trash>=1.8
28
+ Requires-Dist: uvicorn[standard]>=0.29
29
+ Provides-Extra: dev
30
+ Requires-Dist: build>=1.0; extra == 'dev'
31
+ Requires-Dist: httpx>=0.27; extra == 'dev'
32
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
33
+ Requires-Dist: pytest>=7.0; extra == 'dev'
34
+ Requires-Dist: ruff>=0.4; extra == 'dev'
35
+ Requires-Dist: twine>=4.0; extra == 'dev'
36
+ Provides-Extra: windows
37
+ Requires-Dist: pywin32>=306; (sys_platform == 'win32') and extra == 'windows'
38
+ Description-Content-Type: text/markdown
39
+
40
+ # syspanel
41
+
42
+ A local, self-contained Windows system dashboard: resource monitor, disk tree
43
+ viewer, battery health, process manager, service watchlist, startup monitor,
44
+ cleanup manager, and restart control — one web app, no cloud, no build step.
45
+
46
+ ## Install
47
+
48
+ ```bash
49
+ pip install syspanel[windows]
50
+ ```
51
+
52
+ ## Run
53
+
54
+ ```bash
55
+ syspanel
56
+ ```
57
+
58
+ Opens `http://127.0.0.1:8765/` in your browser. Useful flags:
59
+
60
+ ```bash
61
+ syspanel --no-browser # don't auto-open a tab
62
+ syspanel --read-only # disable every mutating route
63
+ syspanel --host 0.0.0.0 --allow-remote # expose beyond localhost (prints a bearer token)
64
+ ```
65
+
66
+ ## Modules
67
+
68
+ | Module | What it shows |
69
+ |---|---|
70
+ | Resources | Live CPU / RAM / network / disk I/O, 1s push over WebSocket |
71
+ | Disk Tree | Drive-picker dropdown, squarified treemap + sortable/clickable table, background scan with progress that survives navigating away and back |
72
+ | Battery | Design vs. current capacity, cycle count, wear verdict (via `powercfg`) |
73
+ | Processes | Sortable/searchable process table; kill with denylist protection |
74
+ | Services | Full service list, a persisted watchlist with alerts, start/stop/restart |
75
+ | Startup | Registry Run/RunOnce, Startup folder, and logon scheduled tasks; enable/disable/delete |
76
+ | Cleanup | Declarative target catalog (Temp, crash dumps, Windows Update cache, Recycle Bin, custom age/retention rules); dry-run preview before every delete |
77
+ | Power | Schedule a restart with a cancellable countdown; requires typing a confirm phrase |
78
+
79
+ Admin-only actions (Windows\Temp, service control, killing other users'
80
+ processes) show a lock badge when unelevated; use the **Relaunch as
81
+ Administrator** button in the header to UAC-elevate in place.
82
+
83
+ Pages cache their data across navigation (instant reload, background
84
+ refresh every 5 minutes, plus a manual Refresh button), and Startup/Battery/
85
+ Cleanup are pre-warmed in the background while you're on another page.
86
+
87
+ ## Safety model
88
+
89
+ Every mutating action goes through one chokepoint (`core/safety.py`):
90
+ a denylist blocks system-critical paths/PIDs, and destructive operations
91
+ require a **preview → confirm token → execute** round trip so exactly what
92
+ was shown is what gets acted on. Every attempt is appended to an audit log
93
+ at `%LOCALAPPDATA%\syspanel\audit.jsonl`.
94
+
95
+ ## Development
96
+
97
+ ```bash
98
+ pip install -e .[dev,windows]
99
+ pytest -q
100
+ ```
101
+
102
+ See `CLAUDE.md` for repo conventions.
@@ -0,0 +1,63 @@
1
+ # syspanel
2
+
3
+ A local, self-contained Windows system dashboard: resource monitor, disk tree
4
+ viewer, battery health, process manager, service watchlist, startup monitor,
5
+ cleanup manager, and restart control — one web app, no cloud, no build step.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install syspanel[windows]
11
+ ```
12
+
13
+ ## Run
14
+
15
+ ```bash
16
+ syspanel
17
+ ```
18
+
19
+ Opens `http://127.0.0.1:8765/` in your browser. Useful flags:
20
+
21
+ ```bash
22
+ syspanel --no-browser # don't auto-open a tab
23
+ syspanel --read-only # disable every mutating route
24
+ syspanel --host 0.0.0.0 --allow-remote # expose beyond localhost (prints a bearer token)
25
+ ```
26
+
27
+ ## Modules
28
+
29
+ | Module | What it shows |
30
+ |---|---|
31
+ | Resources | Live CPU / RAM / network / disk I/O, 1s push over WebSocket |
32
+ | Disk Tree | Drive-picker dropdown, squarified treemap + sortable/clickable table, background scan with progress that survives navigating away and back |
33
+ | Battery | Design vs. current capacity, cycle count, wear verdict (via `powercfg`) |
34
+ | Processes | Sortable/searchable process table; kill with denylist protection |
35
+ | Services | Full service list, a persisted watchlist with alerts, start/stop/restart |
36
+ | Startup | Registry Run/RunOnce, Startup folder, and logon scheduled tasks; enable/disable/delete |
37
+ | Cleanup | Declarative target catalog (Temp, crash dumps, Windows Update cache, Recycle Bin, custom age/retention rules); dry-run preview before every delete |
38
+ | Power | Schedule a restart with a cancellable countdown; requires typing a confirm phrase |
39
+
40
+ Admin-only actions (Windows\Temp, service control, killing other users'
41
+ processes) show a lock badge when unelevated; use the **Relaunch as
42
+ Administrator** button in the header to UAC-elevate in place.
43
+
44
+ Pages cache their data across navigation (instant reload, background
45
+ refresh every 5 minutes, plus a manual Refresh button), and Startup/Battery/
46
+ Cleanup are pre-warmed in the background while you're on another page.
47
+
48
+ ## Safety model
49
+
50
+ Every mutating action goes through one chokepoint (`core/safety.py`):
51
+ a denylist blocks system-critical paths/PIDs, and destructive operations
52
+ require a **preview → confirm token → execute** round trip so exactly what
53
+ was shown is what gets acted on. Every attempt is appended to an audit log
54
+ at `%LOCALAPPDATA%\syspanel\audit.jsonl`.
55
+
56
+ ## Development
57
+
58
+ ```bash
59
+ pip install -e .[dev,windows]
60
+ pytest -q
61
+ ```
62
+
63
+ See `CLAUDE.md` for repo conventions.
@@ -0,0 +1,69 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "syspanel"
7
+ version = "0.1.0"
8
+ description = "A local Windows system dashboard: resource monitor, disk tree viewer, battery health, process manager, service watchlist, startup monitor and cleanup manager."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.11"
12
+ authors = [
13
+ { name = "Pandiyaraj Karuppasamy", email = "pandiyarajk@live.com" },
14
+ ]
15
+ keywords = ["system-monitor", "dashboard", "disk-usage", "battery-health", "process-manager", "windows"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Intended Audience :: System Administrators",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Operating System :: Microsoft :: Windows",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Topic :: System :: Monitoring",
27
+ ]
28
+ dependencies = [
29
+ "fastapi>=0.110",
30
+ "uvicorn[standard]>=0.29",
31
+ "psutil>=6.0",
32
+ "pydantic>=2.6",
33
+ "pydantic-settings>=2.2",
34
+ "jinja2>=3.1",
35
+ "send2trash>=1.8",
36
+ ]
37
+
38
+ [project.urls]
39
+ Repository = "https://github.com/pandiyarajk/syspanel"
40
+ Issues = "https://github.com/pandiyarajk/syspanel/issues"
41
+
42
+ [project.optional-dependencies]
43
+ windows = [
44
+ "pywin32>=306; sys_platform == 'win32'",
45
+ ]
46
+ dev = [
47
+ "build>=1.0",
48
+ "twine>=4.0",
49
+ "pytest>=7.0",
50
+ "pytest-asyncio>=0.23",
51
+ "httpx>=0.27",
52
+ "ruff>=0.4",
53
+ ]
54
+
55
+ [project.scripts]
56
+ syspanel = "system_tools.cli:main"
57
+
58
+ [tool.hatch.build.targets.wheel]
59
+ packages = ["src/system_tools"]
60
+
61
+ [tool.hatch.build.targets.sdist]
62
+ include = [
63
+ "/src/system_tools",
64
+ "/README.md",
65
+ "/LICENSE",
66
+ ]
67
+
68
+ [tool.pytest.ini_options]
69
+ testpaths = ["tests"]
@@ -0,0 +1,7 @@
1
+ """system_tools - A local Windows system dashboard.
2
+
3
+ Author: Pandiyaraj Karuppasamy
4
+ Date: Aug-02-2026
5
+ """
6
+
7
+ __version__ = "0.1.0"
@@ -0,0 +1,10 @@
1
+ """Enables `python -m system_tools`.
2
+
3
+ Author: Pandiyaraj Karuppasamy
4
+ Date: Aug-02-2026
5
+ """
6
+
7
+ from system_tools.cli import main
8
+
9
+ if __name__ == "__main__":
10
+ main()
@@ -0,0 +1,54 @@
1
+ """Command-line entry point: `syspanel` / `python -m system_tools`.
2
+
3
+ Author: Pandiyaraj Karuppasamy
4
+ Date: Aug-02-2026
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import argparse
10
+ import logging
11
+ import threading
12
+ import webbrowser
13
+
14
+ import uvicorn
15
+
16
+ from system_tools import __version__
17
+ from system_tools.config import settings
18
+
19
+
20
+ def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
21
+ parser = argparse.ArgumentParser(prog="syspanel", description="Local Windows system dashboard")
22
+ parser.add_argument("--host", default=settings.host, help="Bind host (default: 127.0.0.1)")
23
+ parser.add_argument("--port", type=int, default=settings.port, help="Bind port")
24
+ parser.add_argument("--no-browser", action="store_true", help="Don't auto-open a browser tab")
25
+ parser.add_argument("--read-only", action="store_true", help="Disable all mutating routes")
26
+ parser.add_argument(
27
+ "--allow-remote", action="store_true",
28
+ help="Allow binding beyond 127.0.0.1 (requires a bearer token, printed at startup)",
29
+ )
30
+ parser.add_argument("--version", action="version", version=f"syspanel {__version__}")
31
+ return parser.parse_args(argv)
32
+
33
+
34
+ def main(argv: list[str] | None = None) -> None:
35
+ """Parse CLI args, apply them to settings, and run the uvicorn server."""
36
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
37
+
38
+ args = _parse_args(argv)
39
+ settings.host = args.host
40
+ settings.port = args.port
41
+ settings.read_only = args.read_only
42
+ settings.allow_remote = args.allow_remote
43
+
44
+ if not args.no_browser and args.host in ("127.0.0.1", "localhost"):
45
+ def _open():
46
+ webbrowser.open(f"http://{args.host}:{args.port}/")
47
+
48
+ threading.Timer(1.0, _open).start()
49
+
50
+ uvicorn.run("system_tools.web.app:create_app", factory=True, host=args.host, port=args.port)
51
+
52
+
53
+ if __name__ == "__main__":
54
+ main()
@@ -0,0 +1,54 @@
1
+ """Runtime configuration, sourced from environment variables.
2
+
3
+ Author: Pandiyaraj Karuppasamy
4
+ Date: Aug-02-2026
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from pathlib import Path
10
+
11
+ from pydantic_settings import BaseSettings, SettingsConfigDict
12
+
13
+
14
+ class Settings(BaseSettings):
15
+ """Application settings. All values may be overridden via SYSTOOLS_* env vars."""
16
+
17
+ model_config = SettingsConfigDict(env_prefix="SYSTOOLS_", env_file=".env", extra="ignore")
18
+
19
+ host: str = "127.0.0.1"
20
+ port: int = 8765
21
+ open_browser: bool = True
22
+ read_only: bool = False
23
+ allow_remote: bool = False
24
+
25
+ sample_interval_seconds: float = 1.0
26
+ history_length: int = 300
27
+ service_watch_interval_seconds: float = 5.0
28
+ battery_report_cache_seconds: int = 3600
29
+
30
+ scan_roots: list[str] = ["C:\\"]
31
+ scan_prune_ratio: float = 0.001
32
+
33
+ confirm_token_ttl_seconds: int = 120
34
+
35
+ # User-defined age/retention-based cleanup targets, e.g. app logs or SQL
36
+ # backups: [{"id": "sql_backups", "label": "SQL backups", "roots": ["D:\\Backups"],
37
+ # "extensions": [".bak", ".trn"], "min_age_days": 14, "keep_newest_per_prefix": 2}]
38
+ custom_cleanup_targets: list[dict] = []
39
+
40
+ def data_dir(self) -> Path:
41
+ """Return the per-user app data directory, creating it if missing.
42
+
43
+ Returns:
44
+ Path: directory used for scan cache, audit log, and confirm tokens.
45
+ """
46
+ import os
47
+
48
+ base = Path(os.environ.get("LOCALAPPDATA", Path.home() / "AppData" / "Local"))
49
+ path = base / "syspanel"
50
+ path.mkdir(parents=True, exist_ok=True)
51
+ return path
52
+
53
+
54
+ settings = Settings()
@@ -0,0 +1,5 @@
1
+ """Core cross-cutting concerns: models, safety, audit, capabilities.
2
+
3
+ Author: Pandiyaraj Karuppasamy
4
+ Date: Aug-02-2026
5
+ """
@@ -0,0 +1,51 @@
1
+ """Append-only JSONL audit log for every attempted mutating action.
2
+
3
+ Author: Pandiyaraj Karuppasamy
4
+ Date: Aug-02-2026
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import getpass
10
+ import json
11
+ import logging
12
+ import time
13
+
14
+ from system_tools.config import settings
15
+ from system_tools.core.models import AuditEntry
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ def log_action(
21
+ action: str,
22
+ targets: list[str],
23
+ elevated: bool,
24
+ success: bool,
25
+ detail: str | None = None,
26
+ ) -> None:
27
+ """Append one audit record describing an attempted mutating action.
28
+
29
+ Args:
30
+ action: short identifier, e.g. "process.kill", "cleanup.execute".
31
+ targets: human-readable identifiers of what was acted on.
32
+ elevated: whether the process held admin rights when the action ran.
33
+ success: whether the action completed without error.
34
+ detail: optional free-text outcome summary.
35
+ """
36
+ entry = AuditEntry(
37
+ timestamp=time.time(),
38
+ action=action,
39
+ targets=targets,
40
+ elevated=elevated,
41
+ success=success,
42
+ detail=detail,
43
+ )
44
+ path = settings.data_dir() / "audit.jsonl"
45
+ try:
46
+ with path.open("a", encoding="utf-8") as fh:
47
+ record = entry.model_dump()
48
+ record["user"] = getpass.getuser()
49
+ fh.write(json.dumps(record) + "\n")
50
+ except OSError:
51
+ logger.exception("Failed to write audit log entry for action=%s", action)
@@ -0,0 +1,57 @@
1
+ """Resolve what this OS/privilege level supports, for UI card gating.
2
+
3
+ Author: Pandiyaraj Karuppasamy
4
+ Date: Aug-02-2026
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import sys
10
+
11
+ from system_tools.core.elevation import can_relaunch_elevated, is_admin
12
+ from system_tools.core.models import CapabilityState, Capabilities
13
+
14
+
15
+ def get_capabilities() -> Capabilities:
16
+ """Compute the current capability map for all modules.
17
+
18
+ Returns:
19
+ Capabilities: platform, admin state, and per-module support/lock info.
20
+ """
21
+ windows = sys.platform == "win32"
22
+ admin = is_admin()
23
+
24
+ modules: dict[str, CapabilityState] = {
25
+ "resources": CapabilityState(supported=True),
26
+ "disk_tree": CapabilityState(supported=True),
27
+ "battery": CapabilityState(supported=windows, reason=None if windows else "Windows only"),
28
+ "processes": CapabilityState(supported=True),
29
+ "process_kill_other_user": CapabilityState(
30
+ supported=windows, needs_admin=not admin,
31
+ reason=None if admin else "Killing other users' processes needs admin",
32
+ ),
33
+ "services": CapabilityState(supported=windows, reason=None if windows else "Windows only"),
34
+ "services_control": CapabilityState(
35
+ supported=windows, needs_admin=not admin,
36
+ reason=None if admin else "Starting/stopping services needs admin",
37
+ ),
38
+ "startup": CapabilityState(supported=windows, reason=None if windows else "Windows only"),
39
+ "cleanup_user_temp": CapabilityState(supported=True),
40
+ "cleanup_windows_temp": CapabilityState(
41
+ supported=windows, needs_admin=not admin,
42
+ reason=None if admin else "C:\\Windows\\Temp needs admin",
43
+ ),
44
+ "cleanup_windows_update_cache": CapabilityState(
45
+ supported=windows, needs_admin=not admin,
46
+ reason=None if admin else "SoftwareDistribution\\Download needs admin",
47
+ ),
48
+ "cleanup_recycle_bin": CapabilityState(supported=windows),
49
+ "power_restart": CapabilityState(supported=windows, reason=None if windows else "Windows only"),
50
+ }
51
+
52
+ return Capabilities(
53
+ platform=sys.platform,
54
+ is_admin=admin,
55
+ can_relaunch_elevated=can_relaunch_elevated(),
56
+ modules=modules,
57
+ )
@@ -0,0 +1,58 @@
1
+ """Admin-privilege detection and in-app UAC relaunch.
2
+
3
+ Author: Pandiyaraj Karuppasamy
4
+ Date: Aug-02-2026
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ import sys
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ def is_admin() -> bool:
16
+ """Return whether the current process holds Windows administrator rights.
17
+
18
+ Returns:
19
+ bool: True if elevated. Always False on non-Windows platforms.
20
+ """
21
+ if sys.platform != "win32":
22
+ return False
23
+ try:
24
+ import ctypes
25
+
26
+ return bool(ctypes.windll.shell32.IsUserAnAdmin())
27
+ except Exception:
28
+ logger.warning("Could not determine admin status", exc_info=True)
29
+ return False
30
+
31
+
32
+ def can_relaunch_elevated() -> bool:
33
+ """Return whether this platform supports the in-app UAC relaunch flow."""
34
+ return sys.platform == "win32"
35
+
36
+
37
+ def relaunch_elevated(host: str, port: int) -> bool:
38
+ """Re-spawn the current process elevated via the Windows UAC 'runas' verb.
39
+
40
+ Args:
41
+ host: host the elevated instance should bind to.
42
+ port: port the elevated instance should bind to.
43
+
44
+ Returns:
45
+ bool: True if the elevation request was accepted by Windows (a new
46
+ elevated process was launched); the caller should exit this process.
47
+ """
48
+ if sys.platform != "win32":
49
+ raise RuntimeError("Elevation relaunch is only supported on Windows")
50
+
51
+ import ctypes
52
+
53
+ params = f'-m system_tools --host {host} --port {port}'
54
+ result = ctypes.windll.shell32.ShellExecuteW(
55
+ None, "runas", sys.executable, params, None, 1
56
+ )
57
+ # ShellExecuteW returns a value > 32 on success.
58
+ return result > 32
@@ -0,0 +1,66 @@
1
+ """Application error types mapped to structured JSON responses.
2
+
3
+ Author: Pandiyaraj Karuppasamy
4
+ Date: Aug-02-2026
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from fastapi import Request
10
+ from fastapi.responses import JSONResponse
11
+
12
+
13
+ class AppError(Exception):
14
+ """Base application error carrying an HTTP status and a machine-readable code."""
15
+
16
+ status_code = 400
17
+ code = "app_error"
18
+
19
+ def __init__(self, detail: str, status_code: int | None = None) -> None:
20
+ super().__init__(detail)
21
+ self.detail = detail
22
+ if status_code is not None:
23
+ self.status_code = status_code
24
+
25
+
26
+ class NotSupportedError(AppError):
27
+ """Raised when a feature is unavailable on this OS."""
28
+
29
+ status_code = 501
30
+ code = "not_supported"
31
+
32
+
33
+ class NeedsElevationError(AppError):
34
+ """Raised when an action requires administrator privileges."""
35
+
36
+ status_code = 403
37
+ code = "needs_admin"
38
+
39
+
40
+ class ProtectedTargetError(AppError):
41
+ """Raised when a request targets a denylisted path/PID/service."""
42
+
43
+ status_code = 403
44
+ code = "protected_target"
45
+
46
+
47
+ class ConfirmTokenError(AppError):
48
+ """Raised when a mutating action's confirm token is missing, expired, or stale."""
49
+
50
+ status_code = 409
51
+ code = "confirm_token_invalid"
52
+
53
+
54
+ class ReadOnlyModeError(AppError):
55
+ """Raised when a mutating route is hit while the app is running with --read-only."""
56
+
57
+ status_code = 403
58
+ code = "read_only_mode"
59
+
60
+
61
+ async def app_error_handler(request: Request, exc: AppError) -> JSONResponse:
62
+ """FastAPI exception handler converting AppError subclasses to JSON."""
63
+ return JSONResponse(
64
+ status_code=exc.status_code,
65
+ content={"error": exc.code, "detail": exc.detail},
66
+ )