fixos 2.2.24__tar.gz → 2.2.26__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.24 → fixos-2.2.26}/CHANGELOG.md +29 -0
- {fixos-2.2.24 → fixos-2.2.26}/PKG-INFO +5 -5
- {fixos-2.2.24 → fixos-2.2.26}/README.md +4 -4
- {fixos-2.2.24 → fixos-2.2.26}/fixos/__init__.py +1 -1
- {fixos-2.2.24 → fixos-2.2.26}/fixos/agent/autonomous_session.py +3 -2
- {fixos-2.2.24 → fixos-2.2.26}/fixos/agent/hitl_session.py +3 -2
- {fixos-2.2.24 → fixos-2.2.26}/fixos/agent/session_handlers.py +2 -1
- {fixos-2.2.24 → fixos-2.2.26}/fixos/platform_utils.py +1 -1
- fixos-2.2.26/fixos/utils/__init__.py +3 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/utils/anonymizer.py +27 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos.egg-info/PKG-INFO +5 -5
- {fixos-2.2.24 → fixos-2.2.26}/pyproject.toml +1 -1
- {fixos-2.2.24 → fixos-2.2.26}/setup.py +1 -1
- {fixos-2.2.24 → fixos-2.2.26}/tests/e2e/test_anonymization_layers.py +110 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/unit/test_core.py +85 -0
- fixos-2.2.24/fixos/utils/__init__.py +0 -3
- {fixos-2.2.24 → fixos-2.2.26}/.env.example +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/LICENSE +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/MANIFEST.in +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/README.md +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/TEST_RESULTS.md +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/TEST_RESULTS_V2.md +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/alpine/Dockerfile +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/arch/Dockerfile +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/base/Dockerfile +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/broken-audio/Dockerfile +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/broken-full/Dockerfile +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/broken-network/Dockerfile +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/broken-thumbnails/Dockerfile +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/debian/Dockerfile +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/docker-compose.multi-system.yml +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/docker-compose.yml +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/fedora/Dockerfile +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/test-multi-system.sh +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docker/ubuntu/Dockerfile +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docs/examples/advanced_usage.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/docs/examples/quickstart.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/agent/__init__.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/agent/autonomous.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/agent/hitl.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/agent/session_core.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/agent/session_io.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/anonymizer.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/__init__.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/ask_cmd.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/cleanup_cmd.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/config_cmd.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/features_cmd.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/fix_cmd.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/history_cmd.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/main.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/orchestrate_cmd.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/profile_cmd.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/provider_cmd.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/quickfix_cmd.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/report_cmd.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/rollback_cmd.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/scan_cmd.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/shared.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/token_cmd.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli/watch_cmd.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/cli.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/config.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/config_interactive.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/constants.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/__init__.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/checks/__init__.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/checks/_shared.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/checks/audio.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/checks/hardware.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/checks/resources.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/checks/security.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/checks/system_core.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/checks/thumbnails.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/dev_project_analyzer.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/disk_analyzer.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/flatpak_analyzer.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/service_cleanup.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/service_details.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/service_scanner.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/storage_analyzer.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/diagnostics/system_checks.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/features/__init__.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/features/auditor.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/features/catalog.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/features/installer.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/features/profiles.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/features/renderer.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/fixes/__init__.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/interactive/__init__.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/interactive/cleanup_planner.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/llm_shell.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/orchestrator/__init__.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/orchestrator/executor.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/orchestrator/graph.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/orchestrator/orchestrator.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/orchestrator/rollback.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/plugins/__init__.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/plugins/base.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/plugins/builtin/__init__.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/plugins/builtin/audio.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/plugins/builtin/disk.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/plugins/builtin/hardware.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/plugins/builtin/resources.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/plugins/builtin/security.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/plugins/builtin/thumbnails.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/plugins/registry.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/profiles/__init__.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/providers/__init__.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/providers/llm.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/providers/llm_analyzer.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/providers/schemas.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/system_checks.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/utils/terminal.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/utils/timeout.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/utils/web_search.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos/watch.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos.egg-info/SOURCES.txt +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos.egg-info/dependency_links.txt +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos.egg-info/entry_points.txt +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos.egg-info/requires.txt +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/fixos.egg-info/top_level.txt +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/pytest.ini +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/requirements-dev.txt +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/requirements.txt +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/scripts/pyqual-calibrate.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/setup.cfg +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/__init__.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/conftest.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/e2e/__init__.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/e2e/test_audio_broken.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/e2e/test_cli.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/e2e/test_executor.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/e2e/test_multi_system.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/e2e/test_network_broken.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/e2e/test_thumbnails_broken.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/test_fixos.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/unit/__init__.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/unit/test_anonymizer.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/unit/test_executor.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/unit/test_orchestrator.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/unit/test_service_cleanup.py +0 -0
- {fixos-2.2.24 → fixos-2.2.26}/tests/unit/test_service_scanner.py +0 -0
|
@@ -150,6 +150,35 @@ 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.26] - 2026-05-04
|
|
154
|
+
|
|
155
|
+
### Docs
|
|
156
|
+
- Update README.md
|
|
157
|
+
|
|
158
|
+
### Test
|
|
159
|
+
- Update tests/e2e/test_anonymization_layers.py
|
|
160
|
+
|
|
161
|
+
### Other
|
|
162
|
+
- Update uv.lock
|
|
163
|
+
|
|
164
|
+
## [2.2.25] - 2026-05-04
|
|
165
|
+
|
|
166
|
+
### Docs
|
|
167
|
+
- Update README.md
|
|
168
|
+
- Update REFACTORING_PROGRESS.md
|
|
169
|
+
|
|
170
|
+
### Test
|
|
171
|
+
- Update tests/unit/test_core.py
|
|
172
|
+
|
|
173
|
+
### Other
|
|
174
|
+
- Update fixos/agent/autonomous_session.py
|
|
175
|
+
- Update fixos/agent/hitl_session.py
|
|
176
|
+
- Update fixos/agent/session_handlers.py
|
|
177
|
+
- Update fixos/platform_utils.py
|
|
178
|
+
- Update fixos/utils/__init__.py
|
|
179
|
+
- Update fixos/utils/anonymizer.py
|
|
180
|
+
- Update uv.lock
|
|
181
|
+
|
|
153
182
|
## [2.2.24] - 2026-05-04
|
|
154
183
|
|
|
155
184
|
### Docs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fixos
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.26
|
|
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,11 +63,11 @@ AI-powered OS Diagnostics
|
|
|
63
63
|
|
|
64
64
|
## AI Cost Tracking
|
|
65
65
|
|
|
66
|
-
     
|
|
67
|
+
  
|
|
68
68
|
|
|
69
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
70
|
-
- 👤 **Human dev:** ~$
|
|
69
|
+
- 🤖 **LLM usage:** $7.5000 (125 commits)
|
|
70
|
+
- 👤 **Human dev:** ~$2603 (26.0h @ $100/h, 30min dedup)
|
|
71
71
|
|
|
72
72
|
Generated on 2026-05-04 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
73
73
|
|
|
@@ -19,11 +19,11 @@ AI-powered OS Diagnostics
|
|
|
19
19
|
|
|
20
20
|
## AI Cost Tracking
|
|
21
21
|
|
|
22
|
-
     
|
|
23
|
+
  
|
|
24
24
|
|
|
25
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
26
|
-
- 👤 **Human dev:** ~$
|
|
25
|
+
- 🤖 **LLM usage:** $7.5000 (125 commits)
|
|
26
|
+
- 👤 **Human dev:** ~$2603 (26.0h @ $100/h, 30min dedup)
|
|
27
27
|
|
|
28
28
|
Generated on 2026-05-04 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
29
29
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""fixos – AI-powered Linux/Windows diagnostics and repair."""
|
|
2
|
-
__version__ = "2.2.
|
|
2
|
+
__version__ = "2.2.26"
|
|
@@ -11,7 +11,7 @@ from dataclasses import dataclass, field
|
|
|
11
11
|
from typing import Optional, List, Dict, Any
|
|
12
12
|
|
|
13
13
|
from ..providers.llm import LLMClient, LLMError
|
|
14
|
-
from ..utils.anonymizer import anonymize, display_anonymized_preview
|
|
14
|
+
from ..utils.anonymizer import anonymize, deanonymize, display_anonymized_preview
|
|
15
15
|
from ..utils.web_search import search_all, format_results_for_llm
|
|
16
16
|
from ..config import FixOsConfig
|
|
17
17
|
from ..utils.timeout import SessionTimeout
|
|
@@ -297,7 +297,8 @@ class AutonomousSession:
|
|
|
297
297
|
})
|
|
298
298
|
return False
|
|
299
299
|
|
|
300
|
-
cmd =
|
|
300
|
+
cmd = deanonymize(cmd_raw)
|
|
301
|
+
cmd = self._add_sudo(cmd)
|
|
301
302
|
print(f" ▶️ Wykonuję: {cmd}")
|
|
302
303
|
|
|
303
304
|
ok, out = self._execute_command(cmd)
|
|
@@ -7,7 +7,7 @@ import time
|
|
|
7
7
|
from typing import Dict, Any, List, Tuple
|
|
8
8
|
|
|
9
9
|
from ..providers.llm import LLMClient, LLMError
|
|
10
|
-
from ..utils.anonymizer import anonymize, display_anonymized_preview
|
|
10
|
+
from ..utils.anonymizer import anonymize, deanonymize, display_anonymized_preview
|
|
11
11
|
from ..utils.web_search import search_all, format_results_for_llm
|
|
12
12
|
from ..config import FixOsConfig
|
|
13
13
|
from ..constants import (
|
|
@@ -150,7 +150,8 @@ class HITLSession:
|
|
|
150
150
|
io.clear_thinking()
|
|
151
151
|
|
|
152
152
|
io.print_llm_reply(reply)
|
|
153
|
-
|
|
153
|
+
raw_fixes = extract_fixes(reply)
|
|
154
|
+
self.last_fixes = [(deanonymize(cmd), comment) for cmd, comment in raw_fixes]
|
|
154
155
|
|
|
155
156
|
if self._check_low_confidence(reply):
|
|
156
157
|
return True
|
|
@@ -18,7 +18,7 @@ from ..constants import (
|
|
|
18
18
|
from ..platform_utils import (
|
|
19
19
|
is_dangerous, is_interactive_blocker, elevate_cmd, run_command,
|
|
20
20
|
)
|
|
21
|
-
from ..utils.anonymizer import anonymize
|
|
21
|
+
from ..utils.anonymizer import anonymize, deanonymize
|
|
22
22
|
from ..utils.web_search import search_all, format_results_for_llm
|
|
23
23
|
from . import session_io as io
|
|
24
24
|
from .session_core import CmdResult, extract_fixes
|
|
@@ -223,6 +223,7 @@ def handle_free_text(user_in: str, messages: list) -> bool:
|
|
|
223
223
|
|
|
224
224
|
def run_single_command(cmd: str, comment: str) -> CmdResult:
|
|
225
225
|
"""Run a command with full transparency and safety checks."""
|
|
226
|
+
cmd = deanonymize(cmd)
|
|
226
227
|
cmd = elevate_cmd(cmd)
|
|
227
228
|
|
|
228
229
|
# Check for dangerous commands
|
|
@@ -95,7 +95,7 @@ def is_interactive_blocker(cmd: str) -> Optional[str]:
|
|
|
95
95
|
(r"\bnewgrp\b", "newgrp replaces the shell and waits for input"),
|
|
96
96
|
(r"\bsu\s+-(\s+|$)", "su - starts a new login shell"),
|
|
97
97
|
(r"\bexec\s+bash\b", "exec replaces the process"),
|
|
98
|
-
(r"\btop\b(?!.*\
|
|
98
|
+
(r"\btop\b(?!.*\s-b(?:\s|$))", "top is interactive unless run in batch mode (-b)"),
|
|
99
99
|
(r"\bvim?\b", "editors require terminal interaction"),
|
|
100
100
|
(r"\bnano\b", "editors require terminal interaction"),
|
|
101
101
|
(r"\bless\b", "pagers require terminal interaction"),
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
from .anonymizer import anonymize, deanonymize, display_anonymized_preview, AnonymizationReport
|
|
2
|
+
from .web_search import search_all, format_results_for_llm
|
|
3
|
+
__all__ = ["anonymize", "deanonymize", "display_anonymized_preview", "AnonymizationReport", "search_all", "format_results_for_llm"]
|
|
@@ -116,6 +116,33 @@ def anonymize(data_str: str) -> tuple[str, AnonymizationReport]:
|
|
|
116
116
|
return data_str, report
|
|
117
117
|
|
|
118
118
|
|
|
119
|
+
def deanonymize(text: str) -> str:
|
|
120
|
+
"""
|
|
121
|
+
Reverses anonymization placeholders back to real values for execution.
|
|
122
|
+
Handles [USER], [HOSTNAME], and [HOME].
|
|
123
|
+
"""
|
|
124
|
+
if not isinstance(text, str):
|
|
125
|
+
return text
|
|
126
|
+
|
|
127
|
+
sensitive = _get_sensitive()
|
|
128
|
+
|
|
129
|
+
# 1. Hostname
|
|
130
|
+
if sensitive.get("hostname"):
|
|
131
|
+
text = text.replace("[HOSTNAME]", sensitive["hostname"])
|
|
132
|
+
|
|
133
|
+
# 2. Home directory
|
|
134
|
+
if sensitive.get("home"):
|
|
135
|
+
text = text.replace("[HOME]", sensitive["home"])
|
|
136
|
+
# Some LLMs might use /home/[USER] literally
|
|
137
|
+
# We replace [USER] next, which covers /home/[USER]
|
|
138
|
+
|
|
139
|
+
# 3. Username
|
|
140
|
+
if sensitive.get("username"):
|
|
141
|
+
text = text.replace("[USER]", sensitive["username"])
|
|
142
|
+
|
|
143
|
+
return text
|
|
144
|
+
|
|
145
|
+
|
|
119
146
|
def display_anonymized_preview(data_str: str, report: AnonymizationReport, max_lines: int = 80):
|
|
120
147
|
"""
|
|
121
148
|
Wyświetla użytkownikowi zanonimizowane dane przed wysłaniem do LLM.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fixos
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.26
|
|
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,11 +63,11 @@ AI-powered OS Diagnostics
|
|
|
63
63
|
|
|
64
64
|
## AI Cost Tracking
|
|
65
65
|
|
|
66
|
-
     
|
|
67
|
+
  
|
|
68
68
|
|
|
69
|
-
- 🤖 **LLM usage:** $7.5000 (
|
|
70
|
-
- 👤 **Human dev:** ~$
|
|
69
|
+
- 🤖 **LLM usage:** $7.5000 (125 commits)
|
|
70
|
+
- 👤 **Human dev:** ~$2603 (26.0h @ $100/h, 30min dedup)
|
|
71
71
|
|
|
72
72
|
Generated on 2026-05-04 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
73
73
|
|
|
@@ -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.25",
|
|
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",
|
|
@@ -589,3 +589,113 @@ def _make_sensitive_string() -> str:
|
|
|
589
589
|
"sk-abc123def456ghi789jkl012mno345pqr password=mysecretpass123 "
|
|
590
590
|
"UUID=a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
|
591
591
|
)
|
|
592
|
+
|
|
593
|
+
# ══════════════════════════════════════════════════════════
|
|
594
|
+
# WARSTWA 7: Deanonimizacja (LLM → wykonanie)
|
|
595
|
+
# ══════════════════════════════════════════════════════════
|
|
596
|
+
|
|
597
|
+
class TestDeanonymizationLayer:
|
|
598
|
+
"""Testy odwracania anonimizacji dla komend z LLM."""
|
|
599
|
+
|
|
600
|
+
def test_deanonymize_base_function(self):
|
|
601
|
+
from fixos.utils.anonymizer import deanonymize
|
|
602
|
+
|
|
603
|
+
cmd = "ping [HOSTNAME]"
|
|
604
|
+
assert deanonymize(cmd) == f"ping {REAL_HOSTNAME}"
|
|
605
|
+
|
|
606
|
+
cmd = "ls /home/[USER]/.cache"
|
|
607
|
+
assert deanonymize(cmd) == f"ls /home/{REAL_USER}/.cache"
|
|
608
|
+
|
|
609
|
+
cmd = "chown [USER]:[USER] [HOME]/file"
|
|
610
|
+
res = deanonymize(cmd)
|
|
611
|
+
assert REAL_USER in res
|
|
612
|
+
assert REAL_HOME in res
|
|
613
|
+
assert "[USER]" not in res
|
|
614
|
+
assert "[HOME]" not in res
|
|
615
|
+
|
|
616
|
+
@patch("fixos.providers.llm.openai")
|
|
617
|
+
def test_hitl_deanonymize_extracted_fixes(self, mock_openai, mock_cfg):
|
|
618
|
+
from fixos.agent.hitl_session import HITLSession
|
|
619
|
+
|
|
620
|
+
def capture(**kwargs):
|
|
621
|
+
resp = MagicMock()
|
|
622
|
+
resp.choices[0].message.content = (
|
|
623
|
+
"**Komenda:** `rm -rf /home/[USER]/tmp`\n"
|
|
624
|
+
"**Co robi:** clean\n"
|
|
625
|
+
)
|
|
626
|
+
resp.usage.total_tokens = 10
|
|
627
|
+
return resp
|
|
628
|
+
|
|
629
|
+
mock_openai.OpenAI.return_value.chat.completions.create.side_effect = capture
|
|
630
|
+
|
|
631
|
+
session = HITLSession(diagnostics={}, config=mock_cfg, show_data=False)
|
|
632
|
+
# Mock _initialize_messages and inputs
|
|
633
|
+
session._initialize_messages = MagicMock(return_value=True)
|
|
634
|
+
session.web_search_count = 0
|
|
635
|
+
|
|
636
|
+
with patch("fixos.agent.session_io.get_user_input", return_value="q"), \
|
|
637
|
+
patch("fixos.agent.session_io.ask_execute_prompt", return_value="n"), \
|
|
638
|
+
patch("fixos.agent.session_handlers.run_single_command") as mock_run:
|
|
639
|
+
|
|
640
|
+
session.run()
|
|
641
|
+
|
|
642
|
+
assert len(session.last_fixes) == 1
|
|
643
|
+
cmd, comment = session.last_fixes[0]
|
|
644
|
+
assert cmd == f"rm -rf /home/{REAL_USER}/tmp"
|
|
645
|
+
assert "[USER]" not in cmd
|
|
646
|
+
|
|
647
|
+
@patch("fixos.providers.llm.openai")
|
|
648
|
+
@patch("fixos.agent.autonomous_session.subprocess.run")
|
|
649
|
+
def test_autonomous_deanonymize_exec(self, mock_subproc, mock_openai, mock_cfg):
|
|
650
|
+
from fixos.agent.autonomous import run_autonomous_session
|
|
651
|
+
|
|
652
|
+
def capture(**kwargs):
|
|
653
|
+
resp = MagicMock()
|
|
654
|
+
resp.choices[0].message.content = json.dumps({
|
|
655
|
+
"analysis": "test",
|
|
656
|
+
"severity": "low",
|
|
657
|
+
"action": "EXEC",
|
|
658
|
+
"command": "echo [HOSTNAME] [USER] [HOME]",
|
|
659
|
+
"reason": "test",
|
|
660
|
+
"next_step": "done",
|
|
661
|
+
})
|
|
662
|
+
resp.usage.total_tokens = 50
|
|
663
|
+
return resp
|
|
664
|
+
|
|
665
|
+
mock_openai.OpenAI.return_value.chat.completions.create.side_effect = capture
|
|
666
|
+
|
|
667
|
+
# Mock subprocess.run to avoid actual execution
|
|
668
|
+
mock_proc = MagicMock()
|
|
669
|
+
mock_proc.returncode = 0
|
|
670
|
+
mock_proc.stdout = "ok"
|
|
671
|
+
mock_proc.stderr = ""
|
|
672
|
+
mock_subproc.return_value = mock_proc
|
|
673
|
+
|
|
674
|
+
with patch("builtins.input", return_value="yes"):
|
|
675
|
+
report = run_autonomous_session(
|
|
676
|
+
diagnostics={},
|
|
677
|
+
config=mock_cfg,
|
|
678
|
+
show_data=False,
|
|
679
|
+
max_fixes=1,
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
assert len(report.fixes_applied) == 1
|
|
683
|
+
executed_cmd = report.fixes_applied[0].command
|
|
684
|
+
assert REAL_HOSTNAME in executed_cmd
|
|
685
|
+
assert REAL_USER in executed_cmd
|
|
686
|
+
assert "[HOSTNAME]" not in executed_cmd
|
|
687
|
+
assert "[USER]" not in executed_cmd
|
|
688
|
+
|
|
689
|
+
@patch("fixos.agent.session_handlers.run_command")
|
|
690
|
+
def test_run_single_command_deanonymizes(self, mock_run):
|
|
691
|
+
from fixos.agent.session_handlers import run_single_command
|
|
692
|
+
|
|
693
|
+
mock_run.return_value = (True, "ok", "", 0)
|
|
694
|
+
|
|
695
|
+
with patch("fixos.agent.session_io.ask_execute_prompt", return_value="y"):
|
|
696
|
+
run_single_command("ls /home/[USER]", "test comment")
|
|
697
|
+
|
|
698
|
+
# run_command is called with the deanonymized command
|
|
699
|
+
args, kwargs = mock_run.call_args
|
|
700
|
+
assert args[0] == f"ls /home/{REAL_USER}"
|
|
701
|
+
assert "[USER]" not in args[0]
|
|
@@ -122,3 +122,88 @@ class TestWebSearch:
|
|
|
122
122
|
from fixos.utils.web_search import _http_get
|
|
123
123
|
result = _http_get("http://240.0.0.1/nonexistent", timeout=1)
|
|
124
124
|
assert result is None
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class TestSortFixesByPriority:
|
|
128
|
+
def test_cleanup_before_upgrade(self):
|
|
129
|
+
from fixos.agent.session_handlers import _sort_fixes_by_priority
|
|
130
|
+
fixes = [
|
|
131
|
+
("sudo dnf upgrade -y", "upgrade"),
|
|
132
|
+
("sudo journalctl --vacuum-size=200M", "clean logs"),
|
|
133
|
+
]
|
|
134
|
+
result = _sort_fixes_by_priority(fixes)
|
|
135
|
+
assert result[0][0] == "sudo journalctl --vacuum-size=200M"
|
|
136
|
+
assert result[1][0] == "sudo dnf upgrade -y"
|
|
137
|
+
|
|
138
|
+
def test_disk_hungry_sorted_to_end(self):
|
|
139
|
+
from fixos.agent.session_handlers import _sort_fixes_by_priority
|
|
140
|
+
fixes = [
|
|
141
|
+
("sudo dnf upgrade -y", "upgrade"),
|
|
142
|
+
("sudo apt full-upgrade -y", "upgrade"),
|
|
143
|
+
("sudo dnf remove oldkernel", "remove"),
|
|
144
|
+
("sudo rm -rf /var/cache", "clean cache"),
|
|
145
|
+
]
|
|
146
|
+
result = _sort_fixes_by_priority(fixes)
|
|
147
|
+
# Both remove and rm are cleanup (score 0), upgrades are score 2.
|
|
148
|
+
# Stable sort preserves original order among equal scores.
|
|
149
|
+
assert result[0][0] == "sudo dnf remove oldkernel"
|
|
150
|
+
assert result[1][0] == "sudo rm -rf /var/cache"
|
|
151
|
+
assert result[2][0] == "sudo dnf upgrade -y"
|
|
152
|
+
assert result[3][0] == "sudo apt full-upgrade -y"
|
|
153
|
+
|
|
154
|
+
def test_unknown_commands_mid_priority(self):
|
|
155
|
+
from fixos.agent.session_handlers import _sort_fixes_by_priority
|
|
156
|
+
fixes = [
|
|
157
|
+
("sudo dnf upgrade -y", "upgrade"),
|
|
158
|
+
("echo 'restart service'", "info"),
|
|
159
|
+
]
|
|
160
|
+
result = _sort_fixes_by_priority(fixes)
|
|
161
|
+
assert result[0][0] == "echo 'restart service'"
|
|
162
|
+
assert result[1][0] == "sudo dnf upgrade -y"
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class TestDiagnosticOnlyCommand:
|
|
166
|
+
def test_simple_diagnostic_is_filtered(self):
|
|
167
|
+
from fixos.agent.session_core import _is_diagnostic_only_command
|
|
168
|
+
assert _is_diagnostic_only_command("df -h") is True
|
|
169
|
+
assert _is_diagnostic_only_command("free -h") is True
|
|
170
|
+
assert _is_diagnostic_only_command("systemctl status auditd") is True
|
|
171
|
+
|
|
172
|
+
def test_repair_command_is_not_diagnostic(self):
|
|
173
|
+
from fixos.agent.session_core import _is_diagnostic_only_command
|
|
174
|
+
assert _is_diagnostic_only_command("sudo systemctl restart auditd") is False
|
|
175
|
+
assert _is_diagnostic_only_command("dnf upgrade -y") is False
|
|
176
|
+
assert _is_diagnostic_only_command("rm -rf /var/cache") is False
|
|
177
|
+
|
|
178
|
+
def test_journalctl_vacuum_is_not_diagnostic(self):
|
|
179
|
+
from fixos.agent.session_core import _is_diagnostic_only_command
|
|
180
|
+
assert _is_diagnostic_only_command("journalctl --vacuum-size=200M") is False
|
|
181
|
+
assert _is_diagnostic_only_command("journalctl --flush") is False
|
|
182
|
+
|
|
183
|
+
def test_compound_command_any_repair_keeps_all(self):
|
|
184
|
+
from fixos.agent.session_core import _is_diagnostic_only_command
|
|
185
|
+
# Compound with both diagnostic and repair → not filtered
|
|
186
|
+
assert _is_diagnostic_only_command("df -h && sudo dnf remove kernel") is False
|
|
187
|
+
assert _is_diagnostic_only_command("cat /etc/fstab || systemctl restart auditd") is False
|
|
188
|
+
|
|
189
|
+
def test_compound_all_diagnostic_is_filtered(self):
|
|
190
|
+
from fixos.agent.session_core import _is_diagnostic_only_command
|
|
191
|
+
assert _is_diagnostic_only_command("df -h && free -h") is True
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class TestInteractiveBlocker:
|
|
195
|
+
def test_newgrp_blocked(self):
|
|
196
|
+
from fixos.platform_utils import is_interactive_blocker
|
|
197
|
+
assert is_interactive_blocker("sudo usermod -aG video $USER && newgrp video") is not None
|
|
198
|
+
|
|
199
|
+
def test_su_dash_blocked(self):
|
|
200
|
+
from fixos.platform_utils import is_interactive_blocker
|
|
201
|
+
assert is_interactive_blocker("su - tom") is not None
|
|
202
|
+
|
|
203
|
+
def test_top_not_blocked_if_batch(self):
|
|
204
|
+
from fixos.platform_utils import is_interactive_blocker
|
|
205
|
+
assert is_interactive_blocker("top -b -n1") is None
|
|
206
|
+
|
|
207
|
+
def test_regular_command_not_blocked(self):
|
|
208
|
+
from fixos.platform_utils import is_interactive_blocker
|
|
209
|
+
assert is_interactive_blocker("dnf upgrade -y") is None
|
|
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
|