fixos 2.2.30__tar.gz → 2.2.32__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.30 → fixos-2.2.32}/CHANGELOG.md +20 -0
- {fixos-2.2.30 → fixos-2.2.32}/PKG-INFO +2 -2
- {fixos-2.2.30 → fixos-2.2.32}/README.md +1 -1
- {fixos-2.2.30 → fixos-2.2.32}/fixos/__init__.py +1 -1
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/cleanup_cmd.py +7 -1
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/main.py +3 -1
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/output_formatter.py +4 -2
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/shared.py +12 -2
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/service_cleanup.py +12 -5
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/service_scanner.py +93 -6
- {fixos-2.2.30 → fixos-2.2.32}/fixos.egg-info/PKG-INFO +2 -2
- {fixos-2.2.30 → fixos-2.2.32}/pyproject.toml +1 -1
- {fixos-2.2.30 → fixos-2.2.32}/setup.py +1 -1
- {fixos-2.2.30 → fixos-2.2.32}/tests/unit/test_service_cleanup.py +8 -0
- fixos-2.2.32/tests/unit/test_service_scanner.py +102 -0
- fixos-2.2.30/tests/unit/test_service_scanner.py +0 -37
- {fixos-2.2.30 → fixos-2.2.32}/.env.example +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/LICENSE +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/MANIFEST.in +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/README.md +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/TEST_RESULTS.md +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/TEST_RESULTS_V2.md +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/alpine/Dockerfile +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/arch/Dockerfile +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/base/Dockerfile +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/broken-audio/Dockerfile +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/broken-full/Dockerfile +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/broken-network/Dockerfile +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/broken-thumbnails/Dockerfile +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/debian/Dockerfile +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/docker-compose.multi-system.yml +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/docker-compose.yml +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/fedora/Dockerfile +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/test-multi-system.sh +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/test-scenarios.sh +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/ubuntu/Dockerfile +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docker/validate-scenario.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docs/examples/advanced_usage.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/docs/examples/quickstart.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/agent/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/agent/autonomous.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/agent/autonomous_session.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/agent/hitl.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/agent/hitl_session.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/agent/session_core.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/agent/session_handlers.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/agent/session_io.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/anonymizer.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/_cleanup_flatpak.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/_cleanup_home.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/_cleanup_snap.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/_cleanup_system.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/_cleanup_utils.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/ask_cmd.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/config_cmd.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/features_cmd.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/fix_cmd.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/history_cmd.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/orchestrate_cmd.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/profile_cmd.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/provider_cmd.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/quickfix_cmd.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/report_cmd.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/rollback_cmd.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/scan_cmd.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/token_cmd.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli/watch_cmd.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/cli.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/config.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/config_interactive.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/constants.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/_flatpak_analysis_mixin.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/_flatpak_execution_mixin.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/_flatpak_recommendations_mixin.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/_storage_container_mixin.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/_storage_system_mixin.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/_storage_user_mixin.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/checks/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/checks/_shared.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/checks/audio.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/checks/file_analysis.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/checks/hardware.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/checks/packages.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/checks/resources.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/checks/security.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/checks/storage_optimization.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/checks/system_core.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/checks/thumbnails.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/dev_project_analyzer.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/disk_analyzer.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/flatpak_analyzer.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/service_details.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/storage_analyzer.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/system_checks.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/diagnostics/utils.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/features/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/features/auditor.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/features/catalog.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/features/installer.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/features/profiles.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/features/renderer.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/fixes/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/interactive/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/interactive/cleanup_planner.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/llm_shell.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/orchestrator/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/orchestrator/executor.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/orchestrator/graph.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/orchestrator/orchestrator.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/orchestrator/rollback.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/platform_utils.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/plugins/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/plugins/base.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/plugins/builtin/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/plugins/builtin/audio.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/plugins/builtin/disk.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/plugins/builtin/hardware.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/plugins/builtin/resources.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/plugins/builtin/security.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/plugins/builtin/thumbnails.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/plugins/registry.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/profiles/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/providers/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/providers/llm.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/providers/llm_analyzer.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/providers/schemas.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/system_checks.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/utils/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/utils/anonymizer.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/utils/terminal.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/utils/timeout.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/utils/web_search.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos/watch.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos.egg-info/SOURCES.txt +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos.egg-info/dependency_links.txt +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos.egg-info/entry_points.txt +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos.egg-info/requires.txt +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/fixos.egg-info/top_level.txt +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/pytest.ini +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/requirements-dev.txt +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/requirements.txt +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/scripts/pyqual-calibrate.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/setup.cfg +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/conftest.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/e2e/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/e2e/test_anonymization_layers.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/e2e/test_audio_broken.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/e2e/test_cli.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/e2e/test_diagnostics_integration.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/e2e/test_executor.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/e2e/test_multi_system.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/e2e/test_network_broken.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/e2e/test_thumbnails_broken.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/test_fixos.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/unit/__init__.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/unit/test_anonymizer.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/unit/test_core.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/unit/test_executor.py +0 -0
- {fixos-2.2.30 → fixos-2.2.32}/tests/unit/test_orchestrator.py +0 -0
|
@@ -150,6 +150,26 @@ 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.32] - 2026-06-16
|
|
154
|
+
|
|
155
|
+
### Test
|
|
156
|
+
- Update tests/unit/test_service_cleanup.py
|
|
157
|
+
- Update tests/unit/test_service_scanner.py
|
|
158
|
+
|
|
159
|
+
### Other
|
|
160
|
+
- Update fixos/cli/cleanup_cmd.py
|
|
161
|
+
- Update fixos/cli/main.py
|
|
162
|
+
- Update fixos/cli/output_formatter.py
|
|
163
|
+
- Update fixos/cli/shared.py
|
|
164
|
+
- Update fixos/diagnostics/service_cleanup.py
|
|
165
|
+
- Update fixos/diagnostics/service_scanner.py
|
|
166
|
+
- Update uv.lock
|
|
167
|
+
|
|
168
|
+
## [2.2.31] - 2026-06-16
|
|
169
|
+
|
|
170
|
+
### Other
|
|
171
|
+
- Update uv.lock
|
|
172
|
+
|
|
153
173
|
## [2.2.30] - 2026-06-16
|
|
154
174
|
|
|
155
175
|
### Docs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fixos
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.32
|
|
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)
|
|
@@ -52,7 +52,13 @@ def _display_service_item(svc: dict) -> None:
|
|
|
52
52
|
f"{safe_icon} {click.style(svc['name'], fg='yellow', bold=True)} - {size_str}"
|
|
53
53
|
)
|
|
54
54
|
click.echo(f" {svc['description']}")
|
|
55
|
-
|
|
55
|
+
paths = svc.get("details", {}).get("paths") or [svc["path"]]
|
|
56
|
+
if len(paths) > 1:
|
|
57
|
+
click.echo(f" Ścieżki ({len(paths)}):")
|
|
58
|
+
for path in paths:
|
|
59
|
+
click.echo(f" • {path}")
|
|
60
|
+
else:
|
|
61
|
+
click.echo(f" Ścieżka: {svc['path']}")
|
|
56
62
|
click.echo(f" {safe_text}")
|
|
57
63
|
|
|
58
64
|
# Show details for specific services
|
|
@@ -3,6 +3,8 @@ Main CLI entry point for fixOS
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import click
|
|
6
|
+
|
|
7
|
+
from fixos import __version__
|
|
6
8
|
from fixos.cli.shared import BANNER, NaturalLanguageGroup
|
|
7
9
|
from fixos.config import FixOsConfig
|
|
8
10
|
|
|
@@ -38,7 +40,7 @@ def cli(ctx, dry_run, version) -> None:
|
|
|
38
40
|
fixos fix --help
|
|
39
41
|
"""
|
|
40
42
|
if version:
|
|
41
|
-
click.echo("fixos
|
|
43
|
+
click.echo(f"fixos v{__version__}")
|
|
42
44
|
return
|
|
43
45
|
|
|
44
46
|
if ctx.invoked_subcommand is None:
|
|
@@ -18,6 +18,8 @@ import yaml
|
|
|
18
18
|
|
|
19
19
|
import click
|
|
20
20
|
|
|
21
|
+
from fixos import __version__
|
|
22
|
+
|
|
21
23
|
|
|
22
24
|
class OutputFormat(Enum):
|
|
23
25
|
"""Supported output formats."""
|
|
@@ -114,7 +116,7 @@ class OutputFormatter:
|
|
|
114
116
|
) -> str:
|
|
115
117
|
"""Format full diagnostic result with metadata envelope."""
|
|
116
118
|
envelope = {
|
|
117
|
-
"fixos_version":
|
|
119
|
+
"fixos_version": __version__,
|
|
118
120
|
"timestamp": timestamp or datetime.now().isoformat(),
|
|
119
121
|
"format": self.fmt.value,
|
|
120
122
|
}
|
|
@@ -132,7 +134,7 @@ class OutputFormatter:
|
|
|
132
134
|
) -> str:
|
|
133
135
|
"""Format scan results with optional disk analysis."""
|
|
134
136
|
result: dict[str, Any] = {
|
|
135
|
-
"fixos_version":
|
|
137
|
+
"fixos_version": __version__,
|
|
136
138
|
"timestamp": datetime.now().isoformat(),
|
|
137
139
|
"scan": data,
|
|
138
140
|
}
|
|
@@ -4,15 +4,25 @@ Shared utilities for fixOS CLI commands
|
|
|
4
4
|
|
|
5
5
|
import click
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
from fixos import __version__
|
|
8
|
+
|
|
9
|
+
_BANNER_TEMPLATE = r"""
|
|
8
10
|
___ _ ___ ____
|
|
9
11
|
/ _(_)_ __ / _ \/ ___|
|
|
10
12
|
| |_| \ \/ / | | | \___ \
|
|
11
13
|
| _| |> < | |_| |___) |
|
|
12
14
|
|_| |_/_/\_\ \___/|____/
|
|
13
|
-
AI-powered OS Diagnostics •
|
|
15
|
+
AI-powered OS Diagnostics • v{version}
|
|
14
16
|
"""
|
|
15
17
|
|
|
18
|
+
|
|
19
|
+
def get_banner() -> str:
|
|
20
|
+
"""Return the CLI banner with the installed package version."""
|
|
21
|
+
return _BANNER_TEMPLATE.format(version=__version__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
BANNER = get_banner()
|
|
25
|
+
|
|
16
26
|
COMMON_OPTIONS = [
|
|
17
27
|
click.option(
|
|
18
28
|
"--provider",
|
|
@@ -265,7 +265,7 @@ class ServiceCleaner:
|
|
|
265
265
|
descriptions = {
|
|
266
266
|
# Containers
|
|
267
267
|
ServiceType.DOCKER: "Docker images, containers, and volumes",
|
|
268
|
-
ServiceType.OLLAMA: "Ollama AI
|
|
268
|
+
ServiceType.OLLAMA: "Ollama AI model files",
|
|
269
269
|
ServiceType.CONTAINERD: "Containerd container runtime data",
|
|
270
270
|
ServiceType.PODMAN: "Podman containers and images",
|
|
271
271
|
# JS/Node
|
|
@@ -274,7 +274,7 @@ class ServiceCleaner:
|
|
|
274
274
|
ServiceType.PNPM: "PNPM store cache",
|
|
275
275
|
# Python
|
|
276
276
|
ServiceType.PIP: "Python pip cache",
|
|
277
|
-
ServiceType.CONDA: "Conda
|
|
277
|
+
ServiceType.CONDA: "Conda package cache (pkgs directories)",
|
|
278
278
|
ServiceType.POETRY: "Poetry virtual environments and cache",
|
|
279
279
|
# Java
|
|
280
280
|
ServiceType.GRADLE: "Gradle build cache",
|
|
@@ -410,7 +410,13 @@ class ServiceCleaner:
|
|
|
410
410
|
largest profile caches untouched, so we remove common cache
|
|
411
411
|
directories under the scanned profile path as well.
|
|
412
412
|
"""
|
|
413
|
-
|
|
413
|
+
expanded_path = os.path.expanduser(path).rstrip("/")
|
|
414
|
+
cache_root = os.path.expanduser("~/.cache/google-chrome").rstrip("/")
|
|
415
|
+
quoted_path = shlex.quote(expanded_path)
|
|
416
|
+
|
|
417
|
+
if expanded_path == cache_root or expanded_path.startswith(f"{cache_root}/"):
|
|
418
|
+
return f"rm -rf {quoted_path}"
|
|
419
|
+
|
|
414
420
|
cache_dir_names = [
|
|
415
421
|
"Cache",
|
|
416
422
|
"Code Cache",
|
|
@@ -424,8 +430,9 @@ class ServiceCleaner:
|
|
|
424
430
|
f"-name {shlex.quote(name)}" for name in cache_dir_names
|
|
425
431
|
)
|
|
426
432
|
return (
|
|
427
|
-
"rm -rf ~/.cache/google-chrome
|
|
428
|
-
f"find {quoted_path} -type d \\( {find_expr} \\)
|
|
433
|
+
"rm -rf ~/.cache/google-chrome 2>/dev/null || true; "
|
|
434
|
+
f"find {quoted_path} -type d \\( {find_expr} \\) "
|
|
435
|
+
"-prune -exec rm -rf {} + 2>/dev/null || true"
|
|
429
436
|
)
|
|
430
437
|
|
|
431
438
|
@staticmethod
|
|
@@ -9,6 +9,7 @@ Refactored: Now uses ServiceDetailsProvider and ServiceCleaner for detailed oper
|
|
|
9
9
|
import os
|
|
10
10
|
import glob
|
|
11
11
|
import json
|
|
12
|
+
import subprocess
|
|
12
13
|
from typing import Dict, List, Any, Optional
|
|
13
14
|
from dataclasses import dataclass, field
|
|
14
15
|
from enum import Enum
|
|
@@ -98,14 +99,24 @@ class ServiceDataScanner:
|
|
|
98
99
|
|
|
99
100
|
SERVICE_PATHS = {
|
|
100
101
|
ServiceType.DOCKER: ["/var/lib/docker", "~/.docker"],
|
|
101
|
-
ServiceType.OLLAMA: [
|
|
102
|
+
ServiceType.OLLAMA: [
|
|
103
|
+
"~/.ollama/models",
|
|
104
|
+
"~/.ollama/blobs",
|
|
105
|
+
"/usr/share/ollama/.ollama/models",
|
|
106
|
+
],
|
|
102
107
|
ServiceType.CONTAINERD: ["/var/lib/containerd", "/run/containerd"],
|
|
103
108
|
ServiceType.PODMAN: ["~/.local/share/containers", "~/.config/containers"],
|
|
104
109
|
ServiceType.NPM: ["~/.npm", "~/.cache/npm"],
|
|
105
110
|
ServiceType.YARN: ["~/.cache/yarn", "~/.yarn", "~/.config/yarn"],
|
|
106
111
|
ServiceType.PNPM: ["~/.pnpm-store", "~/.local/share/pnpm"],
|
|
107
112
|
ServiceType.PIP: ["~/.cache/pip"],
|
|
108
|
-
ServiceType.CONDA: [
|
|
113
|
+
ServiceType.CONDA: [
|
|
114
|
+
"~/miniconda3/pkgs",
|
|
115
|
+
"~/anaconda3/pkgs",
|
|
116
|
+
"~/.conda/pkgs",
|
|
117
|
+
"~/miniconda3/envs/*/pkgs",
|
|
118
|
+
"~/anaconda3/envs/*/pkgs",
|
|
119
|
+
],
|
|
109
120
|
ServiceType.POETRY: ["~/.cache/pypoetry"],
|
|
110
121
|
ServiceType.GRADLE: ["~/.gradle", "~/.cache/gradle"],
|
|
111
122
|
ServiceType.MAVEN: ["~/.m2"],
|
|
@@ -114,7 +125,11 @@ class ServiceDataScanner:
|
|
|
114
125
|
ServiceType.FLUTTER: ["~/.flutter-sdk", "~/flutter", "~/.pub-cache"],
|
|
115
126
|
ServiceType.DART: ["~/.pub-cache"],
|
|
116
127
|
ServiceType.ANDROID: ["~/Android/Sdk", "~/.android"],
|
|
117
|
-
ServiceType.SNAP: [
|
|
128
|
+
ServiceType.SNAP: [
|
|
129
|
+
"/var/lib/snapd/snaps",
|
|
130
|
+
"/var/snap",
|
|
131
|
+
"/var/lib/snapd/cache",
|
|
132
|
+
],
|
|
118
133
|
ServiceType.FLATPAK: ["~/.local/share/flatpak", "/var/lib/flatpak"],
|
|
119
134
|
ServiceType.APPIMAGE: ["~/.local/share/AppImage", "~/.cache/AppImage"],
|
|
120
135
|
ServiceType.APT: ["/var/cache/apt/archives"],
|
|
@@ -140,8 +155,18 @@ class ServiceDataScanner:
|
|
|
140
155
|
ServiceType.FIREFOX: ["~/.cache/mozilla", "~/.mozilla/firefox/*/cache2"],
|
|
141
156
|
ServiceType.EDGE: ["~/.cache/microsoft-edge"],
|
|
142
157
|
ServiceType.VSCODE: ["~/.vscode/extensions", "~/.config/Code/Cache"],
|
|
143
|
-
ServiceType.CURSOR: [
|
|
144
|
-
|
|
158
|
+
ServiceType.CURSOR: [
|
|
159
|
+
"~/.config/Cursor/Cache",
|
|
160
|
+
"~/.config/Cursor/CachedData",
|
|
161
|
+
"~/.config/Cursor/CachedExtensionVSIXs",
|
|
162
|
+
"~/.config/Cursor/logs",
|
|
163
|
+
"~/.cursor/extensions",
|
|
164
|
+
],
|
|
165
|
+
ServiceType.JETBRAINS: [
|
|
166
|
+
"~/.cache/JetBrains",
|
|
167
|
+
"~/.local/share/JetBrains/*/caches",
|
|
168
|
+
"~/.local/share/JetBrains/*/index",
|
|
169
|
+
],
|
|
145
170
|
ServiceType.HUGGINGFACE: ["~/.cache/huggingface"],
|
|
146
171
|
ServiceType.AWS: ["~/.aws/sso/cache", "~/.aws/cli/cache"],
|
|
147
172
|
ServiceType.GCLOUD: ["~/.config/gcloud/logs", "~/.cache/gcloud"],
|
|
@@ -185,8 +210,38 @@ class ServiceDataScanner:
|
|
|
185
210
|
info = self._analyze_service_path(service_type, path)
|
|
186
211
|
if info and info.size_mb >= self.threshold_mb:
|
|
187
212
|
results.append(info)
|
|
213
|
+
if len(results) > 1:
|
|
214
|
+
return [self._merge_service_entries(results)]
|
|
188
215
|
return results
|
|
189
216
|
|
|
217
|
+
def _merge_service_entries(
|
|
218
|
+
self, results: List[ServiceDataInfo]
|
|
219
|
+
) -> ServiceDataInfo:
|
|
220
|
+
"""Combine multiple paths for the same service into one summary entry."""
|
|
221
|
+
primary = max(results, key=lambda item: item.size_mb)
|
|
222
|
+
total_mb = sum(item.size_mb for item in results)
|
|
223
|
+
paths = [item.path for item in results]
|
|
224
|
+
|
|
225
|
+
return ServiceDataInfo(
|
|
226
|
+
service_type=primary.service_type,
|
|
227
|
+
name=primary.name,
|
|
228
|
+
path=primary.path,
|
|
229
|
+
size_mb=round(total_mb, 2),
|
|
230
|
+
size_gb=round(total_mb / 1024, 3),
|
|
231
|
+
description=primary.description,
|
|
232
|
+
can_cleanup=primary.can_cleanup,
|
|
233
|
+
cleanup_command=primary.cleanup_command,
|
|
234
|
+
preview_command=primary.preview_command,
|
|
235
|
+
safe_to_cleanup=all(item.safe_to_cleanup for item in results),
|
|
236
|
+
impact="high" if total_mb / 1024 > 1.0 else "medium",
|
|
237
|
+
items_count=primary.items_count,
|
|
238
|
+
details={
|
|
239
|
+
**primary.details,
|
|
240
|
+
"paths": paths,
|
|
241
|
+
"merged_count": len(results),
|
|
242
|
+
},
|
|
243
|
+
)
|
|
244
|
+
|
|
190
245
|
def _analyze_service_path(
|
|
191
246
|
self, service_type: ServiceType, path: str
|
|
192
247
|
) -> Optional[ServiceDataInfo]:
|
|
@@ -229,12 +284,36 @@ class ServiceDataScanner:
|
|
|
229
284
|
)
|
|
230
285
|
|
|
231
286
|
def _get_path_size_mb(self, path: str) -> float:
|
|
232
|
-
"""Get size of path in MB."""
|
|
287
|
+
"""Get size of path in MB using du, falling back to os.walk."""
|
|
288
|
+
try:
|
|
289
|
+
result = subprocess.run(
|
|
290
|
+
["du", "-sk", "--", path],
|
|
291
|
+
capture_output=True,
|
|
292
|
+
text=True,
|
|
293
|
+
timeout=120,
|
|
294
|
+
check=False,
|
|
295
|
+
)
|
|
296
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
297
|
+
kb = int(result.stdout.strip().splitlines()[-1].split()[0])
|
|
298
|
+
return kb / 1024
|
|
299
|
+
except (OSError, ValueError, subprocess.TimeoutExpired, IndexError):
|
|
300
|
+
pass
|
|
301
|
+
|
|
233
302
|
total_size = 0
|
|
234
303
|
if os.path.isfile(path):
|
|
235
304
|
return os.path.getsize(path) / (1024 * 1024)
|
|
305
|
+
|
|
236
306
|
try:
|
|
307
|
+
root_dev = os.stat(path).st_dev if os.path.exists(path) else None
|
|
237
308
|
for dirpath, dirnames, filenames in os.walk(path):
|
|
309
|
+
if root_dev is not None:
|
|
310
|
+
dirnames[:] = [
|
|
311
|
+
name
|
|
312
|
+
for name in dirnames
|
|
313
|
+
if self._should_descend(
|
|
314
|
+
os.path.join(dirpath, name), root_dev
|
|
315
|
+
)
|
|
316
|
+
]
|
|
238
317
|
for filename in filenames:
|
|
239
318
|
filepath = os.path.join(dirpath, filename)
|
|
240
319
|
try:
|
|
@@ -245,6 +324,14 @@ class ServiceDataScanner:
|
|
|
245
324
|
pass
|
|
246
325
|
return total_size / (1024 * 1024)
|
|
247
326
|
|
|
327
|
+
@staticmethod
|
|
328
|
+
def _should_descend(path: str, root_dev: int) -> bool:
|
|
329
|
+
"""Skip mount points so loop-mounted snaps are not double-counted."""
|
|
330
|
+
try:
|
|
331
|
+
return os.stat(path).st_dev == root_dev
|
|
332
|
+
except OSError:
|
|
333
|
+
return False
|
|
334
|
+
|
|
248
335
|
def get_cleanup_plan(self, selected_services: List[str] = None) -> Dict[str, Any]:
|
|
249
336
|
"""Generate cleanup plan for services."""
|
|
250
337
|
return self._cleaner.get_cleanup_plan(selected_services)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fixos
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.32
|
|
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)
|
|
@@ -5,7 +5,7 @@ long_description = (Path(__file__).parent / "README.md").read_text(encoding="utf
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="fixos",
|
|
8
|
-
version="2.2.
|
|
8
|
+
version="2.2.32",
|
|
9
9
|
description="AI-powered Linux/Windows diagnostics and repair with anonymization",
|
|
10
10
|
long_description=long_description,
|
|
11
11
|
long_description_content_type="text/markdown",
|
|
@@ -19,6 +19,14 @@ class TestChromeCleanup:
|
|
|
19
19
|
assert "GPUCache" in command
|
|
20
20
|
assert "Service Worker" in command
|
|
21
21
|
|
|
22
|
+
def test_chrome_cache_cleanup_does_not_run_find_on_removed_path(self):
|
|
23
|
+
path = "/home/tom/.cache/google-chrome"
|
|
24
|
+
|
|
25
|
+
command = ServiceCleaner.get_cleanup_command(ServiceType.CHROME, path)
|
|
26
|
+
|
|
27
|
+
assert command == f"rm -rf {path}"
|
|
28
|
+
assert "find" not in command
|
|
29
|
+
|
|
22
30
|
def test_cleanup_service_reports_freed_space_for_chrome(self, monkeypatch):
|
|
23
31
|
path = "/home/tom/.config/google-chrome"
|
|
24
32
|
initial_size_mb = 537.0
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Testy jednostkowe dla ServiceDataScanner."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from fixos.diagnostics.service_scanner import (
|
|
6
|
+
ServiceDataInfo,
|
|
7
|
+
ServiceDataScanner,
|
|
8
|
+
ServiceType,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestChromeSafetyClassification:
|
|
13
|
+
def test_chrome_profile_is_marked_for_review(self, monkeypatch):
|
|
14
|
+
scanner = ServiceDataScanner(threshold_mb=1)
|
|
15
|
+
profile_path = "/home/tom/.config/google-chrome"
|
|
16
|
+
|
|
17
|
+
monkeypatch.setattr(scanner, "_get_path_size_mb", lambda path: 537.0)
|
|
18
|
+
monkeypatch.setattr(
|
|
19
|
+
scanner._details_provider, "get_details", lambda service_type, path: {}
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
info = scanner._analyze_service_path(ServiceType.CHROME, profile_path)
|
|
23
|
+
|
|
24
|
+
assert info is not None
|
|
25
|
+
assert info.safe_to_cleanup is False
|
|
26
|
+
assert profile_path in info.cleanup_command
|
|
27
|
+
|
|
28
|
+
def test_chrome_cache_path_is_marked_safe(self, monkeypatch):
|
|
29
|
+
scanner = ServiceDataScanner(threshold_mb=1)
|
|
30
|
+
cache_path = "/home/tom/.cache/google-chrome"
|
|
31
|
+
|
|
32
|
+
monkeypatch.setattr(scanner, "_get_path_size_mb", lambda path: 40.0)
|
|
33
|
+
monkeypatch.setattr(
|
|
34
|
+
scanner._details_provider, "get_details", lambda service_type, path: {}
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
info = scanner._analyze_service_path(ServiceType.CHROME, cache_path)
|
|
38
|
+
|
|
39
|
+
assert info is not None
|
|
40
|
+
assert info.safe_to_cleanup is True
|
|
41
|
+
assert cache_path in info.cleanup_command
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TestServiceMerge:
|
|
45
|
+
def test_scan_service_merges_multiple_paths(self, monkeypatch):
|
|
46
|
+
scanner = ServiceDataScanner(threshold_mb=1)
|
|
47
|
+
|
|
48
|
+
def fake_analyze(service_type, path):
|
|
49
|
+
sizes = {
|
|
50
|
+
"/home/tom/.config/Cursor/Cache": 16000.0,
|
|
51
|
+
"/home/tom/.cursor/extensions": 800.0,
|
|
52
|
+
}
|
|
53
|
+
size_mb = sizes.get(path, 0.0)
|
|
54
|
+
if size_mb <= 0:
|
|
55
|
+
return None
|
|
56
|
+
return ServiceDataInfo(
|
|
57
|
+
service_type=service_type,
|
|
58
|
+
name=service_type.value.title(),
|
|
59
|
+
path=path,
|
|
60
|
+
size_mb=size_mb,
|
|
61
|
+
size_gb=round(size_mb / 1024, 3),
|
|
62
|
+
description="Cursor editor cache",
|
|
63
|
+
can_cleanup=True,
|
|
64
|
+
cleanup_command="rm -rf cache",
|
|
65
|
+
preview_command="du -sh",
|
|
66
|
+
safe_to_cleanup=False,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
monkeypatch.setattr(scanner, "_analyze_service_path", fake_analyze)
|
|
70
|
+
monkeypatch.setattr(
|
|
71
|
+
"fixos.diagnostics.service_scanner.glob.glob",
|
|
72
|
+
lambda pattern: [pattern],
|
|
73
|
+
)
|
|
74
|
+
monkeypatch.setattr(
|
|
75
|
+
"fixos.diagnostics.service_scanner.os.path.exists", lambda path: True
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
results = scanner.scan_service(ServiceType.CURSOR)
|
|
79
|
+
|
|
80
|
+
assert len(results) == 1
|
|
81
|
+
assert results[0].size_mb == 16800.0
|
|
82
|
+
assert results[0].details["merged_count"] == 2
|
|
83
|
+
assert len(results[0].details["paths"]) == 2
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class TestServicePathTargets:
|
|
87
|
+
def test_conda_paths_scan_package_cache_only(self):
|
|
88
|
+
paths = ServiceDataScanner.SERVICE_PATHS[ServiceType.CONDA]
|
|
89
|
+
assert paths
|
|
90
|
+
assert all("pkgs" in path for path in paths)
|
|
91
|
+
assert "~/miniconda3" not in paths
|
|
92
|
+
|
|
93
|
+
def test_ollama_paths_avoid_whole_system_tree(self):
|
|
94
|
+
paths = ServiceDataScanner.SERVICE_PATHS[ServiceType.OLLAMA]
|
|
95
|
+
assert paths
|
|
96
|
+
assert "/usr/share/ollama" not in paths
|
|
97
|
+
assert "~/.ollama/models" in paths
|
|
98
|
+
|
|
99
|
+
def test_jetbrains_paths_target_cache_directories(self):
|
|
100
|
+
paths = ServiceDataScanner.SERVICE_PATHS[ServiceType.JETBRAINS]
|
|
101
|
+
assert "~/.cache/JetBrains" in paths
|
|
102
|
+
assert "~/.JetBrains" not in paths
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
"""Testy jednostkowe dla ServiceDataScanner."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from fixos.diagnostics.service_scanner import ServiceDataScanner, ServiceType
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestChromeSafetyClassification:
|
|
9
|
-
def test_chrome_profile_is_marked_for_review(self, monkeypatch):
|
|
10
|
-
scanner = ServiceDataScanner(threshold_mb=1)
|
|
11
|
-
profile_path = "/home/tom/.config/google-chrome"
|
|
12
|
-
|
|
13
|
-
monkeypatch.setattr(scanner, "_get_path_size_mb", lambda path: 537.0)
|
|
14
|
-
monkeypatch.setattr(
|
|
15
|
-
scanner._details_provider, "get_details", lambda service_type, path: {}
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
info = scanner._analyze_service_path(ServiceType.CHROME, profile_path)
|
|
19
|
-
|
|
20
|
-
assert info is not None
|
|
21
|
-
assert info.safe_to_cleanup is False
|
|
22
|
-
assert profile_path in info.cleanup_command
|
|
23
|
-
|
|
24
|
-
def test_chrome_cache_path_is_marked_safe(self, monkeypatch):
|
|
25
|
-
scanner = ServiceDataScanner(threshold_mb=1)
|
|
26
|
-
cache_path = "/home/tom/.cache/google-chrome"
|
|
27
|
-
|
|
28
|
-
monkeypatch.setattr(scanner, "_get_path_size_mb", lambda path: 40.0)
|
|
29
|
-
monkeypatch.setattr(
|
|
30
|
-
scanner._details_provider, "get_details", lambda service_type, path: {}
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
info = scanner._analyze_service_path(ServiceType.CHROME, cache_path)
|
|
34
|
-
|
|
35
|
-
assert info is not None
|
|
36
|
-
assert info.safe_to_cleanup is True
|
|
37
|
-
assert cache_path in info.cleanup_command
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|