fixos 2.2.32__tar.gz → 2.2.34__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.
- {fixos-2.2.32 → fixos-2.2.34}/CHANGELOG.md +18 -0
- {fixos-2.2.32 → fixos-2.2.34}/PKG-INFO +2 -2
- {fixos-2.2.32 → fixos-2.2.34}/README.md +1 -1
- {fixos-2.2.32 → fixos-2.2.34}/fixos/__init__.py +1 -1
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/cleanup_cmd.py +4 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/constants.py +1 -0
- fixos-2.2.34/fixos/diagnostics/cache_discovery.py +251 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/service_cleanup.py +161 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/service_scanner.py +90 -2
- {fixos-2.2.32 → fixos-2.2.34}/fixos.egg-info/PKG-INFO +2 -2
- {fixos-2.2.32 → fixos-2.2.34}/fixos.egg-info/SOURCES.txt +2 -0
- {fixos-2.2.32 → fixos-2.2.34}/pyproject.toml +1 -1
- {fixos-2.2.32 → fixos-2.2.34}/setup.py +1 -1
- fixos-2.2.34/tests/unit/test_cache_discovery.py +116 -0
- {fixos-2.2.32 → fixos-2.2.34}/.env.example +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/LICENSE +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/MANIFEST.in +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/README.md +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/TEST_RESULTS.md +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/TEST_RESULTS_V2.md +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/alpine/Dockerfile +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/arch/Dockerfile +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/base/Dockerfile +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/broken-audio/Dockerfile +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/broken-full/Dockerfile +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/broken-network/Dockerfile +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/broken-thumbnails/Dockerfile +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/debian/Dockerfile +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/docker-compose.multi-system.yml +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/docker-compose.yml +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/fedora/Dockerfile +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/test-multi-system.sh +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/test-scenarios.sh +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/ubuntu/Dockerfile +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docker/validate-scenario.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docs/examples/advanced_usage.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/docs/examples/quickstart.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/agent/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/agent/autonomous.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/agent/autonomous_session.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/agent/hitl.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/agent/hitl_session.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/agent/session_core.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/agent/session_handlers.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/agent/session_io.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/anonymizer.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/_cleanup_flatpak.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/_cleanup_home.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/_cleanup_snap.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/_cleanup_system.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/_cleanup_utils.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/ask_cmd.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/config_cmd.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/features_cmd.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/fix_cmd.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/history_cmd.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/main.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/orchestrate_cmd.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/output_formatter.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/profile_cmd.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/provider_cmd.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/quickfix_cmd.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/report_cmd.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/rollback_cmd.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/scan_cmd.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/shared.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/token_cmd.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli/watch_cmd.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/cli.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/config.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/config_interactive.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/_flatpak_analysis_mixin.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/_flatpak_execution_mixin.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/_flatpak_recommendations_mixin.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/_storage_container_mixin.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/_storage_system_mixin.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/_storage_user_mixin.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/checks/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/checks/_shared.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/checks/audio.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/checks/file_analysis.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/checks/hardware.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/checks/packages.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/checks/resources.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/checks/security.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/checks/storage_optimization.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/checks/system_core.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/checks/thumbnails.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/dev_project_analyzer.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/disk_analyzer.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/flatpak_analyzer.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/service_details.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/storage_analyzer.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/system_checks.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/diagnostics/utils.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/features/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/features/auditor.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/features/catalog.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/features/installer.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/features/profiles.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/features/renderer.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/fixes/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/interactive/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/interactive/cleanup_planner.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/llm_shell.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/orchestrator/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/orchestrator/executor.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/orchestrator/graph.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/orchestrator/orchestrator.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/orchestrator/rollback.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/platform_utils.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/plugins/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/plugins/base.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/plugins/builtin/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/plugins/builtin/audio.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/plugins/builtin/disk.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/plugins/builtin/hardware.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/plugins/builtin/resources.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/plugins/builtin/security.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/plugins/builtin/thumbnails.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/plugins/registry.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/profiles/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/providers/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/providers/llm.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/providers/llm_analyzer.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/providers/schemas.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/system_checks.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/utils/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/utils/anonymizer.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/utils/terminal.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/utils/timeout.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/utils/web_search.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos/watch.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos.egg-info/dependency_links.txt +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos.egg-info/entry_points.txt +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos.egg-info/requires.txt +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/fixos.egg-info/top_level.txt +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/pytest.ini +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/requirements-dev.txt +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/requirements.txt +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/scripts/pyqual-calibrate.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/setup.cfg +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/conftest.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/e2e/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/e2e/test_anonymization_layers.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/e2e/test_audio_broken.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/e2e/test_cli.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/e2e/test_diagnostics_integration.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/e2e/test_executor.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/e2e/test_multi_system.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/e2e/test_network_broken.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/e2e/test_thumbnails_broken.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/test_fixos.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/unit/__init__.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/unit/test_anonymizer.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/unit/test_core.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/unit/test_executor.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/unit/test_orchestrator.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/unit/test_service_cleanup.py +0 -0
- {fixos-2.2.32 → fixos-2.2.34}/tests/unit/test_service_scanner.py +0 -0
|
@@ -150,6 +150,24 @@ fix(goal): code analysis engine
|
|
|
150
150
|
- **refactor(cli):** Usunięto zduplikowany kod ujednolicając funkcje analizy dysku do wspólnego helpera `_run_disk_analysis`.
|
|
151
151
|
- **refactor(ui):** Usunięto ikony Unicode z CLI i sformatowano wyjście `stderr` oraz standardowego logowania na czysty kod Markdown dla poprawy czytelności w oknach terminalowych.
|
|
152
152
|
|
|
153
|
+
## [2.2.34] - 2026-06-16
|
|
154
|
+
|
|
155
|
+
### Test
|
|
156
|
+
- Update tests/unit/test_cache_discovery.py
|
|
157
|
+
|
|
158
|
+
### Other
|
|
159
|
+
- Update fixos/cli/cleanup_cmd.py
|
|
160
|
+
- Update fixos/constants.py
|
|
161
|
+
- Update fixos/diagnostics/cache_discovery.py
|
|
162
|
+
- Update fixos/diagnostics/service_cleanup.py
|
|
163
|
+
- Update fixos/diagnostics/service_scanner.py
|
|
164
|
+
- Update uv.lock
|
|
165
|
+
|
|
166
|
+
## [2.2.33] - 2026-06-16
|
|
167
|
+
|
|
168
|
+
### Other
|
|
169
|
+
- Update uv.lock
|
|
170
|
+
|
|
153
171
|
## [2.2.32] - 2026-06-16
|
|
154
172
|
|
|
155
173
|
### Test
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fixos
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.34
|
|
4
4
|
Summary: AI-powered Linux/Windows diagnostics and repair – audio, hardware, system issues
|
|
5
5
|
Home-page: https://github.com/wronai/fixos
|
|
6
6
|
Author: fixos contributors
|
|
@@ -63,7 +63,7 @@ AI-powered OS Diagnostics
|
|
|
63
63
|
|
|
64
64
|
## AI Cost Tracking
|
|
65
65
|
|
|
66
|
-
    
|
|
67
67
|
  
|
|
68
68
|
|
|
69
69
|
- 🤖 **LLM usage:** $3.6649 (131 commits)
|
|
@@ -19,7 +19,7 @@ AI-powered OS Diagnostics
|
|
|
19
19
|
|
|
20
20
|
## AI Cost Tracking
|
|
21
21
|
|
|
22
|
-
    
|
|
23
23
|
  
|
|
24
24
|
|
|
25
25
|
- 🤖 **LLM usage:** $3.6649 (131 commits)
|
|
@@ -148,6 +148,10 @@ def _display_unsafe_services(services: list) -> None:
|
|
|
148
148
|
"flatpak": ServiceType.FLATPAK,
|
|
149
149
|
"docker": ServiceType.DOCKER,
|
|
150
150
|
"ollama": ServiceType.OLLAMA,
|
|
151
|
+
"steam": ServiceType.STEAM,
|
|
152
|
+
"minikube": ServiceType.MINIKUBE,
|
|
153
|
+
"lmstudio": ServiceType.LMSTUDIO,
|
|
154
|
+
"generic_cache": ServiceType.GENERIC_CACHE,
|
|
151
155
|
}
|
|
152
156
|
|
|
153
157
|
for service_type, svcs in service_groups.items():
|
|
@@ -98,6 +98,7 @@ MIN_ORPHANED_PACKAGES = 5
|
|
|
98
98
|
MIN_HOME_LARGE_FILE_MB = 200
|
|
99
99
|
MIN_HOME_LARGE_DIR_MB = 500
|
|
100
100
|
DEFAULT_CLEANUP_THRESHOLD_MB = 500
|
|
101
|
+
GENERIC_CACHE_THRESHOLD_MB = 1024
|
|
101
102
|
MAX_HOME_LARGE_FILES_DISPLAY = 30
|
|
102
103
|
MAX_HOME_LARGE_DIRS_DISPLAY = 20
|
|
103
104
|
MIN_STALE_DAYS = 90
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Generic cache discovery for fixOS cleanup.
|
|
3
|
+
|
|
4
|
+
Finds large cache directories under ~/.cache and Electron app caches under
|
|
5
|
+
~/.config that are not already covered by known service scanners.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import glob
|
|
11
|
+
import os
|
|
12
|
+
from typing import Callable, Iterable, Set, TYPE_CHECKING
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from .service_scanner import ServiceDataInfo, ServiceType
|
|
16
|
+
|
|
17
|
+
from ..constants import GENERIC_CACHE_THRESHOLD_MB
|
|
18
|
+
|
|
19
|
+
# Top-level ~/.cache names already handled by dedicated ServiceType scanners.
|
|
20
|
+
KNOWN_CACHE_DIR_NAMES = frozenset(
|
|
21
|
+
{
|
|
22
|
+
"pip",
|
|
23
|
+
"npm",
|
|
24
|
+
"yarn",
|
|
25
|
+
"pypoetry",
|
|
26
|
+
"huggingface",
|
|
27
|
+
"google-chrome",
|
|
28
|
+
"microsoft-edge",
|
|
29
|
+
"mozilla",
|
|
30
|
+
"thumbnails",
|
|
31
|
+
"gcloud",
|
|
32
|
+
"JetBrains",
|
|
33
|
+
"gradle",
|
|
34
|
+
"uv",
|
|
35
|
+
"torch",
|
|
36
|
+
"nvidia",
|
|
37
|
+
"mesa_shader_cache",
|
|
38
|
+
"ms-playwright",
|
|
39
|
+
"puppeteer",
|
|
40
|
+
"helm",
|
|
41
|
+
"bazel",
|
|
42
|
+
"gh",
|
|
43
|
+
"lm-studio",
|
|
44
|
+
"BraveSoftware",
|
|
45
|
+
"spotify",
|
|
46
|
+
"log",
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Electron/Chromium apps scanned explicitly elsewhere or via dedicated types.
|
|
51
|
+
KNOWN_CONFIG_APPS = frozenset(
|
|
52
|
+
{
|
|
53
|
+
"google-chrome",
|
|
54
|
+
"microsoft-edge",
|
|
55
|
+
"BraveSoftware",
|
|
56
|
+
"Code",
|
|
57
|
+
"Cursor",
|
|
58
|
+
"discord",
|
|
59
|
+
"Slack",
|
|
60
|
+
"spotify",
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
ELECTRON_CACHE_DIR_NAMES = (
|
|
65
|
+
"Cache",
|
|
66
|
+
"Code Cache",
|
|
67
|
+
"GPUCache",
|
|
68
|
+
"DawnCache",
|
|
69
|
+
"GrShaderCache",
|
|
70
|
+
"ShaderCache",
|
|
71
|
+
"Service Worker",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
GENERIC_SAFE_NAME_HINTS = (
|
|
75
|
+
"cache",
|
|
76
|
+
"shader",
|
|
77
|
+
"tmp",
|
|
78
|
+
"temp",
|
|
79
|
+
"log",
|
|
80
|
+
"crash",
|
|
81
|
+
"thumb",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def normalize_path(path: str) -> str:
|
|
86
|
+
return os.path.realpath(os.path.expanduser(path))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def path_is_covered(path: str, covered_paths: Iterable[str]) -> bool:
|
|
90
|
+
"""Return True when path is already represented by a known service scan."""
|
|
91
|
+
normalized = normalize_path(path)
|
|
92
|
+
for covered in covered_paths:
|
|
93
|
+
covered_norm = normalize_path(covered)
|
|
94
|
+
if normalized == covered_norm:
|
|
95
|
+
return True
|
|
96
|
+
if normalized.startswith(f"{covered_norm}/"):
|
|
97
|
+
return True
|
|
98
|
+
if covered_norm.startswith(f"{normalized}/"):
|
|
99
|
+
return True
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def is_generic_cache_safe(path: str) -> bool:
|
|
104
|
+
"""Heuristic safety check for unknown cache directories."""
|
|
105
|
+
base = os.path.basename(path.rstrip("/")).lower()
|
|
106
|
+
return any(hint in base for hint in GENERIC_SAFE_NAME_HINTS)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def discover_additional_caches(
|
|
110
|
+
get_size_mb: Callable[[str], float],
|
|
111
|
+
threshold_mb: int,
|
|
112
|
+
covered_paths: Set[str],
|
|
113
|
+
) -> list["ServiceDataInfo"]:
|
|
114
|
+
"""Discover large caches not already covered by known service scanners."""
|
|
115
|
+
from .service_scanner import ServiceDataInfo, ServiceType
|
|
116
|
+
|
|
117
|
+
generic_threshold = max(threshold_mb, GENERIC_CACHE_THRESHOLD_MB)
|
|
118
|
+
results: list[ServiceDataInfo] = []
|
|
119
|
+
seen_paths: set[str] = set()
|
|
120
|
+
|
|
121
|
+
for info in _discover_xdg_cache_dirs(get_size_mb, generic_threshold, covered_paths):
|
|
122
|
+
norm = normalize_path(info.path)
|
|
123
|
+
if norm not in seen_paths:
|
|
124
|
+
seen_paths.add(norm)
|
|
125
|
+
results.append(info)
|
|
126
|
+
|
|
127
|
+
for info in _discover_electron_caches(get_size_mb, threshold_mb, covered_paths):
|
|
128
|
+
norm = normalize_path(info.path)
|
|
129
|
+
if norm not in seen_paths:
|
|
130
|
+
seen_paths.add(norm)
|
|
131
|
+
results.append(info)
|
|
132
|
+
|
|
133
|
+
results.sort(key=lambda item: item.size_mb, reverse=True)
|
|
134
|
+
return results
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _discover_xdg_cache_dirs(
|
|
138
|
+
get_size_mb: Callable[[str], float],
|
|
139
|
+
threshold_mb: int,
|
|
140
|
+
covered_paths: Set[str],
|
|
141
|
+
) -> list["ServiceDataInfo"]:
|
|
142
|
+
cache_root = os.path.expanduser("~/.cache")
|
|
143
|
+
if not os.path.isdir(cache_root):
|
|
144
|
+
return []
|
|
145
|
+
|
|
146
|
+
results: list[ServiceDataInfo] = []
|
|
147
|
+
try:
|
|
148
|
+
entries = sorted(os.listdir(cache_root))
|
|
149
|
+
except OSError:
|
|
150
|
+
return []
|
|
151
|
+
|
|
152
|
+
for entry in entries:
|
|
153
|
+
if entry in KNOWN_CACHE_DIR_NAMES:
|
|
154
|
+
continue
|
|
155
|
+
|
|
156
|
+
path = os.path.join(cache_root, entry)
|
|
157
|
+
if not os.path.isdir(path):
|
|
158
|
+
continue
|
|
159
|
+
if path_is_covered(path, covered_paths):
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
size_mb = get_size_mb(path)
|
|
163
|
+
if size_mb < threshold_mb:
|
|
164
|
+
continue
|
|
165
|
+
|
|
166
|
+
safe = is_generic_cache_safe(path)
|
|
167
|
+
results.append(
|
|
168
|
+
_build_generic_entry(
|
|
169
|
+
label=f"Cache: {entry}",
|
|
170
|
+
path=path,
|
|
171
|
+
size_mb=size_mb,
|
|
172
|
+
safe=safe,
|
|
173
|
+
description=f"Discovered cache directory (~/.cache/{entry})",
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
return results
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _discover_electron_caches(
|
|
181
|
+
get_size_mb: Callable[[str], float],
|
|
182
|
+
threshold_mb: int,
|
|
183
|
+
covered_paths: Set[str],
|
|
184
|
+
) -> list["ServiceDataInfo"]:
|
|
185
|
+
from .service_scanner import ServiceType
|
|
186
|
+
|
|
187
|
+
config_root = os.path.expanduser("~/.config")
|
|
188
|
+
if not os.path.isdir(config_root):
|
|
189
|
+
return []
|
|
190
|
+
|
|
191
|
+
results: list[ServiceDataInfo] = []
|
|
192
|
+
for app_dir in sorted(glob.glob(os.path.join(config_root, "*"))):
|
|
193
|
+
app_name = os.path.basename(app_dir)
|
|
194
|
+
if app_name in KNOWN_CONFIG_APPS or not os.path.isdir(app_dir):
|
|
195
|
+
continue
|
|
196
|
+
|
|
197
|
+
for cache_name in ELECTRON_CACHE_DIR_NAMES:
|
|
198
|
+
for path in glob.glob(os.path.join(app_dir, "*", cache_name)):
|
|
199
|
+
if not os.path.isdir(path):
|
|
200
|
+
continue
|
|
201
|
+
if path_is_covered(path, covered_paths):
|
|
202
|
+
continue
|
|
203
|
+
|
|
204
|
+
size_mb = get_size_mb(path)
|
|
205
|
+
if size_mb < threshold_mb:
|
|
206
|
+
continue
|
|
207
|
+
|
|
208
|
+
results.append(
|
|
209
|
+
_build_generic_entry(
|
|
210
|
+
label=f"{app_name} cache",
|
|
211
|
+
path=path,
|
|
212
|
+
size_mb=size_mb,
|
|
213
|
+
safe=True,
|
|
214
|
+
description=f"Electron/Chromium cache for {app_name}",
|
|
215
|
+
service_type=ServiceType.ELECTRON,
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
return results
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _build_generic_entry(
|
|
223
|
+
*,
|
|
224
|
+
label: str,
|
|
225
|
+
path: str,
|
|
226
|
+
size_mb: float,
|
|
227
|
+
safe: bool,
|
|
228
|
+
description: str,
|
|
229
|
+
service_type: "ServiceType" = None,
|
|
230
|
+
) -> "ServiceDataInfo":
|
|
231
|
+
from .service_cleanup import ServiceCleaner
|
|
232
|
+
from .service_scanner import ServiceDataInfo, ServiceType
|
|
233
|
+
|
|
234
|
+
if service_type is None:
|
|
235
|
+
service_type = ServiceType.GENERIC_CACHE
|
|
236
|
+
size_gb = size_mb / 1024
|
|
237
|
+
cleanup_command = ServiceCleaner.get_cleanup_command(service_type, path)
|
|
238
|
+
return ServiceDataInfo(
|
|
239
|
+
service_type=service_type,
|
|
240
|
+
name=label,
|
|
241
|
+
path=path,
|
|
242
|
+
size_mb=round(size_mb, 2),
|
|
243
|
+
size_gb=round(size_gb, 3),
|
|
244
|
+
description=description,
|
|
245
|
+
can_cleanup=True,
|
|
246
|
+
cleanup_command=cleanup_command,
|
|
247
|
+
preview_command=ServiceCleaner.get_preview_command(service_type, path),
|
|
248
|
+
safe_to_cleanup=safe,
|
|
249
|
+
impact="high" if size_gb > 1.0 else "medium",
|
|
250
|
+
details={"discovered": True, "source": "cache_discovery"},
|
|
251
|
+
)
|
|
@@ -150,6 +150,28 @@ class ServiceCleaner:
|
|
|
150
150
|
|
|
151
151
|
return False
|
|
152
152
|
|
|
153
|
+
if service_type == ServiceType.BRAVE:
|
|
154
|
+
normalized_path = os.path.expanduser(path or "").rstrip("/")
|
|
155
|
+
if "/Cache" in normalized_path or normalized_path.endswith(
|
|
156
|
+
("GPUCache", "Code Cache")
|
|
157
|
+
):
|
|
158
|
+
return True
|
|
159
|
+
return normalized_path.endswith("BraveSoftware") or "/BraveSoftware/" in (
|
|
160
|
+
normalized_path
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
if service_type == ServiceType.STEAM:
|
|
164
|
+
normalized_path = os.path.expanduser(path or "").rstrip("/")
|
|
165
|
+
safe_suffixes = ("shadercache", "appcache")
|
|
166
|
+
return any(normalized_path.endswith(suffix) for suffix in safe_suffixes)
|
|
167
|
+
|
|
168
|
+
if service_type in (ServiceType.GENERIC_CACHE, ServiceType.ELECTRON):
|
|
169
|
+
base = os.path.basename(os.path.expanduser(path or "").rstrip("/")).lower()
|
|
170
|
+
return any(
|
|
171
|
+
hint in base
|
|
172
|
+
for hint in ("cache", "shader", "tmp", "temp", "log", "crash", "thumb")
|
|
173
|
+
)
|
|
174
|
+
|
|
153
175
|
safe_services = {
|
|
154
176
|
# Package caches (can be re-downloaded)
|
|
155
177
|
ServiceType.NPM,
|
|
@@ -161,6 +183,19 @@ class ServiceCleaner:
|
|
|
161
183
|
ServiceType.MAVEN,
|
|
162
184
|
ServiceType.CARGO,
|
|
163
185
|
ServiceType.GO,
|
|
186
|
+
ServiceType.UV,
|
|
187
|
+
ServiceType.TORCH,
|
|
188
|
+
ServiceType.BUN,
|
|
189
|
+
ServiceType.PLAYWRIGHT,
|
|
190
|
+
ServiceType.CCACHE,
|
|
191
|
+
ServiceType.HELM,
|
|
192
|
+
ServiceType.BAZEL,
|
|
193
|
+
ServiceType.GH,
|
|
194
|
+
ServiceType.NVIDIA,
|
|
195
|
+
ServiceType.DISCORD,
|
|
196
|
+
ServiceType.SLACK,
|
|
197
|
+
ServiceType.SPOTIFY,
|
|
198
|
+
ServiceType.BRAVE,
|
|
164
199
|
# System caches
|
|
165
200
|
ServiceType.APT,
|
|
166
201
|
ServiceType.DNF,
|
|
@@ -255,6 +290,57 @@ class ServiceCleaner:
|
|
|
255
290
|
]
|
|
256
291
|
)
|
|
257
292
|
|
|
293
|
+
elif service_type == ServiceType.STEAM:
|
|
294
|
+
hints.extend(
|
|
295
|
+
[
|
|
296
|
+
"🎮 STEAM CLEANUP:",
|
|
297
|
+
" rm -rf ~/.local/share/Steam/steamapps/shadercache",
|
|
298
|
+
" # Safe: shader cache rebuilds automatically",
|
|
299
|
+
"",
|
|
300
|
+
" steamcmd +app_update ...",
|
|
301
|
+
" # Games themselves require manual uninstall in Steam UI",
|
|
302
|
+
"",
|
|
303
|
+
"📊 Check usage:",
|
|
304
|
+
" du -sh ~/.local/share/Steam/steamapps/common/* | sort -hr | head",
|
|
305
|
+
]
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
elif service_type == ServiceType.MINIKUBE:
|
|
309
|
+
hints.extend(
|
|
310
|
+
[
|
|
311
|
+
"☸️ MINIKUBE CLEANUP:",
|
|
312
|
+
" minikube stop",
|
|
313
|
+
" minikube delete --all",
|
|
314
|
+
" # Removes local Kubernetes cluster and VM data",
|
|
315
|
+
"",
|
|
316
|
+
"💡 Docker driver images may remain in Docker cache",
|
|
317
|
+
]
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
elif service_type == ServiceType.LMSTUDIO:
|
|
321
|
+
hints.extend(
|
|
322
|
+
[
|
|
323
|
+
"🤖 LM STUDIO CLEANUP:",
|
|
324
|
+
" ls ~/.lmstudio/models",
|
|
325
|
+
" # Review downloaded models before deleting",
|
|
326
|
+
"",
|
|
327
|
+
" rm -rf ~/.lmstudio/models/<model>",
|
|
328
|
+
" # Remove one model at a time",
|
|
329
|
+
]
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
elif service_type == ServiceType.GENERIC_CACHE:
|
|
333
|
+
hints.extend(
|
|
334
|
+
[
|
|
335
|
+
"📂 UNKNOWN CACHE:",
|
|
336
|
+
" du -sh ~/.cache/<dir>",
|
|
337
|
+
" # Inspect contents before deleting",
|
|
338
|
+
"",
|
|
339
|
+
" rm -rf <path>",
|
|
340
|
+
" # Only if you recognize it as rebuildable cache",
|
|
341
|
+
]
|
|
342
|
+
)
|
|
343
|
+
|
|
258
344
|
return hints
|
|
259
345
|
|
|
260
346
|
@staticmethod
|
|
@@ -326,6 +412,24 @@ class ServiceCleaner:
|
|
|
326
412
|
ServiceType.THUMBNAILS: "Thumbnail cache",
|
|
327
413
|
ServiceType.TRASH: "Trash/Recycle Bin",
|
|
328
414
|
ServiceType.LOGS: "Application logs",
|
|
415
|
+
ServiceType.NVIDIA: "NVIDIA and Mesa GPU shader cache",
|
|
416
|
+
ServiceType.UV: "uv Python package manager cache",
|
|
417
|
+
ServiceType.TORCH: "PyTorch hub and model cache",
|
|
418
|
+
ServiceType.BUN: "Bun JavaScript runtime cache",
|
|
419
|
+
ServiceType.PLAYWRIGHT: "Playwright/Puppeteer browser binaries",
|
|
420
|
+
ServiceType.CCACHE: "C/C++ compiler cache (ccache/sccache)",
|
|
421
|
+
ServiceType.HELM: "Helm chart cache",
|
|
422
|
+
ServiceType.MINIKUBE: "Minikube local Kubernetes cluster data",
|
|
423
|
+
ServiceType.STEAM: "Steam games, shaders and client cache",
|
|
424
|
+
ServiceType.LMSTUDIO: "LM Studio local AI models",
|
|
425
|
+
ServiceType.BRAVE: "Brave browser cache",
|
|
426
|
+
ServiceType.DISCORD: "Discord client cache",
|
|
427
|
+
ServiceType.SLACK: "Slack client cache",
|
|
428
|
+
ServiceType.SPOTIFY: "Spotify offline/cache data",
|
|
429
|
+
ServiceType.BAZEL: "Bazel build cache",
|
|
430
|
+
ServiceType.GH: "GitHub CLI cache",
|
|
431
|
+
ServiceType.ELECTRON: "Electron/Chromium application cache",
|
|
432
|
+
ServiceType.GENERIC_CACHE: "Discovered cache directory",
|
|
329
433
|
}
|
|
330
434
|
return descriptions.get(service_type, f"{service_type.value} data")
|
|
331
435
|
|
|
@@ -398,6 +502,24 @@ class ServiceCleaner:
|
|
|
398
502
|
ServiceType.THUMBNAILS: "rm -rf ~/.cache/thumbnails/* ~/.thumbnails/*",
|
|
399
503
|
ServiceType.TRASH: "rm -rf ~/.local/share/Trash/* ~/.Trash/*",
|
|
400
504
|
ServiceType.LOGS: "find ~/.cache/log ~/.local/state -name '*.log' -mtime +7 -delete 2>/dev/null; journalctl --vacuum-time=7d 2>/dev/null || true",
|
|
505
|
+
ServiceType.NVIDIA: "rm -rf ~/.cache/nvidia ~/.nv/ComputeCache ~/.cache/mesa_shader_cache",
|
|
506
|
+
ServiceType.UV: "uv cache clean || rm -rf ~/.cache/uv ~/.local/share/uv",
|
|
507
|
+
ServiceType.TORCH: "rm -rf ~/.cache/torch ~/.torch",
|
|
508
|
+
ServiceType.BUN: "rm -rf ~/.bun/install/cache",
|
|
509
|
+
ServiceType.PLAYWRIGHT: "rm -rf ~/.cache/ms-playwright ~/.cache/puppeteer",
|
|
510
|
+
ServiceType.CCACHE: "ccache -C 2>/dev/null || rm -rf ~/.ccache; rm -rf ~/.cache/sccache",
|
|
511
|
+
ServiceType.HELM: "helm cache cleanup 2>/dev/null || rm -rf ~/.cache/helm",
|
|
512
|
+
ServiceType.MINIKUBE: "minikube delete --all 2>/dev/null || rm -rf ~/.minikube",
|
|
513
|
+
ServiceType.STEAM: ServiceCleaner._steam_cleanup_command(path),
|
|
514
|
+
ServiceType.LMSTUDIO: "rm -rf ~/.lmstudio/models/* ~/.cache/lm-studio",
|
|
515
|
+
ServiceType.BRAVE: ServiceCleaner._brave_cleanup_command(path),
|
|
516
|
+
ServiceType.DISCORD: "rm -rf ~/.config/discord/Cache ~/.config/discord/Code Cache ~/.config/discord/GPUCache",
|
|
517
|
+
ServiceType.SLACK: "rm -rf ~/.config/Slack/Cache ~/.config/Slack/Code Cache ~/.config/Slack/Service Worker",
|
|
518
|
+
ServiceType.SPOTIFY: "rm -rf ~/.cache/spotify ~/.config/spotify/Data",
|
|
519
|
+
ServiceType.BAZEL: "rm -rf ~/.cache/bazel",
|
|
520
|
+
ServiceType.GH: "rm -rf ~/.cache/gh",
|
|
521
|
+
ServiceType.ELECTRON: f"rm -rf {shlex.quote(path)}",
|
|
522
|
+
ServiceType.GENERIC_CACHE: f"rm -rf {shlex.quote(path)}",
|
|
401
523
|
}
|
|
402
524
|
return commands.get(service_type, f"rm -rf {path}")
|
|
403
525
|
|
|
@@ -435,6 +557,29 @@ class ServiceCleaner:
|
|
|
435
557
|
"-prune -exec rm -rf {} + 2>/dev/null || true"
|
|
436
558
|
)
|
|
437
559
|
|
|
560
|
+
@staticmethod
|
|
561
|
+
def _brave_cleanup_command(path: str) -> str:
|
|
562
|
+
expanded_path = os.path.expanduser(path).rstrip("/")
|
|
563
|
+
quoted_path = shlex.quote(expanded_path)
|
|
564
|
+
cache_root = os.path.expanduser("~/.cache/BraveSoftware").rstrip("/")
|
|
565
|
+
if expanded_path == cache_root or expanded_path.startswith(f"{cache_root}/"):
|
|
566
|
+
return f"rm -rf {quoted_path}"
|
|
567
|
+
return (
|
|
568
|
+
"rm -rf ~/.cache/BraveSoftware 2>/dev/null || true; "
|
|
569
|
+
f"rm -rf {quoted_path} 2>/dev/null || true"
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
@staticmethod
|
|
573
|
+
def _steam_cleanup_command(path: str) -> str:
|
|
574
|
+
expanded_path = os.path.expanduser(path).rstrip("/")
|
|
575
|
+
quoted_path = shlex.quote(expanded_path)
|
|
576
|
+
if expanded_path.endswith(("shadercache", "appcache")):
|
|
577
|
+
return f"rm -rf {quoted_path}"
|
|
578
|
+
return (
|
|
579
|
+
"rm -rf ~/.local/share/Steam/steamapps/shadercache "
|
|
580
|
+
"~/.local/share/Steam/appcache 2>/dev/null || true"
|
|
581
|
+
)
|
|
582
|
+
|
|
438
583
|
@staticmethod
|
|
439
584
|
def get_preview_command(service_type, path: str) -> str:
|
|
440
585
|
"""Get preview command for service."""
|
|
@@ -504,6 +649,22 @@ class ServiceCleaner:
|
|
|
504
649
|
ServiceType.THUMBNAILS: "du -sh ~/.cache/thumbnails 2>/dev/null && find ~/.cache/thumbnails -type f | wc -l",
|
|
505
650
|
ServiceType.TRASH: "du -sh ~/.local/share/Trash 2>/dev/null || du -sh ~/.Trash",
|
|
506
651
|
ServiceType.LOGS: "find ~/.cache/log ~/.local/state /var/log ~/.var/log 2>/dev/null -name '*.log' | wc -l && du -sh ~/.cache/log 2>/dev/null || du -sh /var/log 2>/dev/null",
|
|
652
|
+
ServiceType.NVIDIA: "du -sh ~/.cache/nvidia ~/.nv/ComputeCache 2>/dev/null",
|
|
653
|
+
ServiceType.UV: "uv cache dir 2>/dev/null || du -sh ~/.cache/uv",
|
|
654
|
+
ServiceType.TORCH: "du -sh ~/.cache/torch 2>/dev/null",
|
|
655
|
+
ServiceType.BUN: "du -sh ~/.bun/install/cache 2>/dev/null",
|
|
656
|
+
ServiceType.PLAYWRIGHT: "du -sh ~/.cache/ms-playwright ~/.cache/puppeteer 2>/dev/null",
|
|
657
|
+
ServiceType.CCACHE: "ccache -s 2>/dev/null || du -sh ~/.ccache",
|
|
658
|
+
ServiceType.HELM: "helm cache stats 2>/dev/null || du -sh ~/.cache/helm",
|
|
659
|
+
ServiceType.MINIKUBE: "minikube status 2>/dev/null || du -sh ~/.minikube",
|
|
660
|
+
ServiceType.STEAM: "du -sh ~/.local/share/Steam/steamapps/common 2>/dev/null | sort -hr | head -10",
|
|
661
|
+
ServiceType.LMSTUDIO: "du -sh ~/.lmstudio/models 2>/dev/null || ls ~/.lmstudio/models",
|
|
662
|
+
ServiceType.BRAVE: "du -sh ~/.cache/BraveSoftware 2>/dev/null",
|
|
663
|
+
ServiceType.DISCORD: "du -sh ~/.config/discord/Cache 2>/dev/null",
|
|
664
|
+
ServiceType.SLACK: "du -sh ~/.config/Slack/Cache 2>/dev/null",
|
|
665
|
+
ServiceType.SPOTIFY: "du -sh ~/.cache/spotify 2>/dev/null",
|
|
666
|
+
ServiceType.BAZEL: "du -sh ~/.cache/bazel 2>/dev/null",
|
|
667
|
+
ServiceType.GH: "du -sh ~/.cache/gh 2>/dev/null",
|
|
507
668
|
}
|
|
508
669
|
return previews.get(
|
|
509
670
|
service_type, f"du -sh {path} 2>/dev/null && ls -la {path} | head -20"
|
|
@@ -70,6 +70,24 @@ class ServiceType(Enum):
|
|
|
70
70
|
THUMBNAILS = "thumbnails"
|
|
71
71
|
TRASH = "trash"
|
|
72
72
|
LOGS = "logs"
|
|
73
|
+
NVIDIA = "nvidia"
|
|
74
|
+
UV = "uv"
|
|
75
|
+
TORCH = "torch"
|
|
76
|
+
BUN = "bun"
|
|
77
|
+
PLAYWRIGHT = "playwright"
|
|
78
|
+
CCACHE = "ccache"
|
|
79
|
+
HELM = "helm"
|
|
80
|
+
MINIKUBE = "minikube"
|
|
81
|
+
STEAM = "steam"
|
|
82
|
+
LMSTUDIO = "lmstudio"
|
|
83
|
+
BRAVE = "brave"
|
|
84
|
+
DISCORD = "discord"
|
|
85
|
+
SLACK = "slack"
|
|
86
|
+
SPOTIFY = "spotify"
|
|
87
|
+
BAZEL = "bazel"
|
|
88
|
+
GH = "gh"
|
|
89
|
+
ELECTRON = "electron"
|
|
90
|
+
GENERIC_CACHE = "generic_cache"
|
|
73
91
|
UNKNOWN = "unknown"
|
|
74
92
|
|
|
75
93
|
|
|
@@ -179,8 +197,54 @@ class ServiceDataScanner:
|
|
|
179
197
|
ServiceType.THUMBNAILS: ["~/.cache/thumbnails", "~/.thumbnails"],
|
|
180
198
|
ServiceType.TRASH: ["~/.local/share/Trash", "~/.Trash"],
|
|
181
199
|
ServiceType.LOGS: ["~/.cache/log", "~/.local/state"],
|
|
200
|
+
ServiceType.NVIDIA: [
|
|
201
|
+
"~/.cache/nvidia",
|
|
202
|
+
"~/.nv/ComputeCache",
|
|
203
|
+
"~/.cache/mesa_shader_cache",
|
|
204
|
+
],
|
|
205
|
+
ServiceType.UV: ["~/.cache/uv", "~/.local/share/uv"],
|
|
206
|
+
ServiceType.TORCH: ["~/.cache/torch", "~/.torch"],
|
|
207
|
+
ServiceType.BUN: ["~/.bun/install/cache"],
|
|
208
|
+
ServiceType.PLAYWRIGHT: ["~/.cache/ms-playwright", "~/.cache/puppeteer"],
|
|
209
|
+
ServiceType.CCACHE: ["~/.ccache", "~/.cache/sccache"],
|
|
210
|
+
ServiceType.HELM: ["~/.cache/helm"],
|
|
211
|
+
ServiceType.MINIKUBE: ["~/.minikube"],
|
|
212
|
+
ServiceType.STEAM: [
|
|
213
|
+
"~/.local/share/Steam/steamapps/shadercache",
|
|
214
|
+
"~/.local/share/Steam/appcache",
|
|
215
|
+
"~/.local/share/Steam",
|
|
216
|
+
"~/.steam",
|
|
217
|
+
],
|
|
218
|
+
ServiceType.LMSTUDIO: [
|
|
219
|
+
"~/.cache/lm-studio",
|
|
220
|
+
"~/.lmstudio/models",
|
|
221
|
+
"~/.lmstudio/.internal/cache",
|
|
222
|
+
],
|
|
223
|
+
ServiceType.BRAVE: [
|
|
224
|
+
"~/.cache/BraveSoftware",
|
|
225
|
+
"~/.config/BraveSoftware/Brave-Browser/*/Cache",
|
|
226
|
+
"~/.config/BraveSoftware/Brave-Browser/*/Code Cache",
|
|
227
|
+
"~/.config/BraveSoftware/Brave-Browser/*/GPUCache",
|
|
228
|
+
],
|
|
229
|
+
ServiceType.DISCORD: [
|
|
230
|
+
"~/.config/discord/Cache",
|
|
231
|
+
"~/.config/discord/Code Cache",
|
|
232
|
+
"~/.config/discord/GPUCache",
|
|
233
|
+
],
|
|
234
|
+
ServiceType.SLACK: [
|
|
235
|
+
"~/.config/Slack/Cache",
|
|
236
|
+
"~/.config/Slack/Code Cache",
|
|
237
|
+
"~/.config/Slack/Service Worker",
|
|
238
|
+
],
|
|
239
|
+
ServiceType.SPOTIFY: ["~/.cache/spotify", "~/.config/spotify/Data"],
|
|
240
|
+
ServiceType.BAZEL: ["~/.cache/bazel"],
|
|
241
|
+
ServiceType.GH: ["~/.cache/gh"],
|
|
182
242
|
}
|
|
183
243
|
|
|
244
|
+
_SKIP_ENUM_SCAN = frozenset(
|
|
245
|
+
{ServiceType.UNKNOWN, ServiceType.GENERIC_CACHE, ServiceType.ELECTRON}
|
|
246
|
+
)
|
|
247
|
+
|
|
184
248
|
def __init__(self, threshold_mb: int = None):
|
|
185
249
|
self.threshold_mb = threshold_mb or self.DEFAULT_THRESHOLD_MB
|
|
186
250
|
self.threshold_gb = self.threshold_mb / 1024
|
|
@@ -189,15 +253,39 @@ class ServiceDataScanner:
|
|
|
189
253
|
|
|
190
254
|
def scan_all_services(self) -> List[ServiceDataInfo]:
|
|
191
255
|
"""Scan all known services for data above threshold."""
|
|
192
|
-
|
|
256
|
+
from .cache_discovery import discover_additional_caches
|
|
257
|
+
|
|
258
|
+
results: List[ServiceDataInfo] = []
|
|
193
259
|
for service_type in ServiceType:
|
|
194
|
-
if service_type
|
|
260
|
+
if service_type in self._SKIP_ENUM_SCAN:
|
|
195
261
|
continue
|
|
196
262
|
service_data = self.scan_service(service_type)
|
|
197
263
|
results.extend(service_data)
|
|
264
|
+
|
|
265
|
+
covered_paths = self._collect_covered_paths(results)
|
|
266
|
+
results.extend(
|
|
267
|
+
discover_additional_caches(
|
|
268
|
+
self._get_path_size_mb, self.threshold_mb, covered_paths
|
|
269
|
+
)
|
|
270
|
+
)
|
|
198
271
|
results.sort(key=lambda x: x.size_mb, reverse=True)
|
|
199
272
|
return results
|
|
200
273
|
|
|
274
|
+
def _collect_covered_paths(self, results: List[ServiceDataInfo]) -> set[str]:
|
|
275
|
+
"""Paths already represented by dedicated service scanners."""
|
|
276
|
+
covered: set[str] = set()
|
|
277
|
+
for result in results:
|
|
278
|
+
covered.add(result.path)
|
|
279
|
+
for path in result.details.get("paths", []):
|
|
280
|
+
covered.add(path)
|
|
281
|
+
|
|
282
|
+
for paths in self.SERVICE_PATHS.values():
|
|
283
|
+
for pattern in paths:
|
|
284
|
+
expanded = os.path.expanduser(pattern)
|
|
285
|
+
for path in glob.glob(expanded) or [expanded]:
|
|
286
|
+
covered.add(path)
|
|
287
|
+
return covered
|
|
288
|
+
|
|
201
289
|
def scan_service(self, service_type: ServiceType) -> List[ServiceDataInfo]:
|
|
202
290
|
"""Scan specific service type for data."""
|
|
203
291
|
results = []
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fixos
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.34
|
|
4
4
|
Summary: AI-powered Linux/Windows diagnostics and repair – audio, hardware, system issues
|
|
5
5
|
Home-page: https://github.com/wronai/fixos
|
|
6
6
|
Author: fixos contributors
|
|
@@ -63,7 +63,7 @@ AI-powered OS Diagnostics
|
|
|
63
63
|
|
|
64
64
|
## AI Cost Tracking
|
|
65
65
|
|
|
66
|
-
    
|
|
67
67
|
  
|
|
68
68
|
|
|
69
69
|
- 🤖 **LLM usage:** $3.6649 (131 commits)
|