syscheck-cli 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. syscheck_cli-0.2.0/PKG-INFO +14 -0
  2. syscheck_cli-0.2.0/README.md +138 -0
  3. syscheck_cli-0.2.0/pyproject.toml +32 -0
  4. syscheck_cli-0.2.0/setup.cfg +4 -0
  5. syscheck_cli-0.2.0/syscheck/__init__.py +2 -0
  6. syscheck_cli-0.2.0/syscheck/__main__.py +4 -0
  7. syscheck_cli-0.2.0/syscheck/cli.py +134 -0
  8. syscheck_cli-0.2.0/syscheck/cmdlang.py +109 -0
  9. syscheck_cli-0.2.0/syscheck/commands/__init__.py +8 -0
  10. syscheck_cli-0.2.0/syscheck/commands/cpu.py +45 -0
  11. syscheck_cli-0.2.0/syscheck/commands/disk.py +61 -0
  12. syscheck_cli-0.2.0/syscheck/commands/net.py +59 -0
  13. syscheck_cli-0.2.0/syscheck/commands/proc.py +35 -0
  14. syscheck_cli-0.2.0/syscheck/commands/ram.py +49 -0
  15. syscheck_cli-0.2.0/syscheck/commands/sys.py +43 -0
  16. syscheck_cli-0.2.0/syscheck/commands/temp.py +50 -0
  17. syscheck_cli-0.2.0/syscheck/commands/watch.py +115 -0
  18. syscheck_cli-0.2.0/syscheck/palette.py +30 -0
  19. syscheck_cli-0.2.0/syscheck/plugin.py +89 -0
  20. syscheck_cli-0.2.0/syscheck/plugins/__init__.py +1 -0
  21. syscheck_cli-0.2.0/syscheck/plugins/example.py +38 -0
  22. syscheck_cli-0.2.0/syscheck/providers.py +705 -0
  23. syscheck_cli-0.2.0/syscheck/screens.py +265 -0
  24. syscheck_cli-0.2.0/syscheck/shellguard.py +126 -0
  25. syscheck_cli-0.2.0/syscheck/tui.py +1086 -0
  26. syscheck_cli-0.2.0/syscheck/utils.py +111 -0
  27. syscheck_cli-0.2.0/syscheck_cli.egg-info/PKG-INFO +14 -0
  28. syscheck_cli-0.2.0/syscheck_cli.egg-info/SOURCES.txt +36 -0
  29. syscheck_cli-0.2.0/syscheck_cli.egg-info/dependency_links.txt +1 -0
  30. syscheck_cli-0.2.0/syscheck_cli.egg-info/entry_points.txt +2 -0
  31. syscheck_cli-0.2.0/syscheck_cli.egg-info/requires.txt +10 -0
  32. syscheck_cli-0.2.0/syscheck_cli.egg-info/top_level.txt +1 -0
  33. syscheck_cli-0.2.0/tests/test_cli_entry.py +23 -0
  34. syscheck_cli-0.2.0/tests/test_cmdlang.py +30 -0
  35. syscheck_cli-0.2.0/tests/test_providers.py +77 -0
  36. syscheck_cli-0.2.0/tests/test_providers_new.py +70 -0
  37. syscheck_cli-0.2.0/tests/test_safety.py +65 -0
  38. syscheck_cli-0.2.0/tests/test_utils.py +44 -0
@@ -0,0 +1,14 @@
1
+ Metadata-Version: 2.4
2
+ Name: syscheck-cli
3
+ Version: 0.2.0
4
+ Summary: CLI utility for system diagnostics
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: psutil>=5.9.0
7
+ Requires-Dist: rich>=13.0.0
8
+ Requires-Dist: typer>=0.9.0
9
+ Requires-Dist: textual>=0.80.0
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
12
+ Requires-Dist: build>=1.0.0; extra == "dev"
13
+ Requires-Dist: twine>=4.0.0; extra == "dev"
14
+ Requires-Dist: pyinstaller>=6.0.0; extra == "dev"
@@ -0,0 +1,138 @@
1
+ # syscheck
2
+
3
+ CLI-утилита диагностики системы, как btop/htop, но с рабочими командами.
4
+ Минималистичный ASCII-вывод: без рамок и emoji, только суть.
5
+
6
+ ## Установка
7
+
8
+ ### Быстро (нужен Python 3.10+)
9
+
10
+ ```bash
11
+ pipx install syscheck-cli # или: pip install syscheck-cli
12
+ syscheck # живой дашборд
13
+ ```
14
+
15
+ Без PyPI — прямо из GitHub:
16
+
17
+ ```bash
18
+ pip install git+https://github.com/avofe/syscheck-cli
19
+ ```
20
+
21
+ ### Без Python (Windows): готовый .exe одной строкой
22
+
23
+ ```powershell
24
+ powershell -ExecutionPolicy Bypass -c "irm https://raw.githubusercontent.com/avofe/syscheck-cli/main/scripts/install.ps1 | iex"
25
+ ```
26
+
27
+ Скрипт скачивает `syscheck-windows-x86_64.exe` с GitHub Releases, кладёт в
28
+ `%LOCALAPPDATA%\syscheck` и добавляет в PATH. Python на машине не нужен.
29
+
30
+ ### Без Python (macOS/Linux)
31
+
32
+ ```bash
33
+ curl -sSL https://raw.githubusercontent.com/avofe/syscheck-cli/main/scripts/install.sh | bash
34
+ ```
35
+
36
+ ### Из исходников
37
+
38
+ ```bash
39
+ pip install -e . # запуск: python -m syscheck
40
+ ```
41
+
42
+ ### Сборка exe (без Python у пользователя)
43
+
44
+ ```powershell
45
+ .\scripts\build_exe.ps1 # → dist\syscheck-windows-x86_64.exe
46
+ ```
47
+
48
+ ### Публикация на PyPI (для автора)
49
+
50
+ ```bash
51
+ pip install -e .[dev]
52
+ python -m build # wheel + sdist в dist/
53
+ twine upload dist/* # нужен аккаунт на pypi.org, токен
54
+ ```
55
+
56
+ ## Команды
57
+
58
+ | Команда | Описание |
59
+ |---------|----------|
60
+ | `syscheck cpu` | Нагрузка CPU, ядра, частота |
61
+ | `syscheck ram` | Оперативная память, топ процессов по RAM |
62
+ | `syscheck disk` | Диски: место, состояние, I/O |
63
+ | `syscheck net --ping 8.8.8.8` | Сеть: интерфейсы, трафик, пинг |
64
+ | `syscheck proc --sort cpu` | Топ процессов (cpu/ram) |
65
+ | `syscheck temp` | Температуры (если поддерживается) |
66
+ | `syscheck sys` | ОС, uptime, hostname |
67
+ | `syscheck all` | Вся диагностика сразу |
68
+ | `syscheck watch` | Live-дашборд (обновление каждые N сек) |
69
+
70
+ Каждая команда поддерживает `--json` для машинного вывода.
71
+
72
+ ## Интерактивный режим (TUI)
73
+
74
+ Запуск `syscheck` без аргументов открывает интерактивную оболочку:
75
+ живой дашборд сверху (обновляется каждые 2 с) и строка ввода:
76
+
77
+ ```
78
+ > cpu # нагрузка процессора
79
+ > ram # память
80
+ > disk # диски
81
+ > net 8.8.8.8 # сеть и пинг
82
+ > proc # топ процессов
83
+ > temp # температуры
84
+ > sys # система
85
+ > all # всё сразу
86
+ > help # помощь
87
+ > clear # очистить
88
+ > exit / ctrl+c # выйти
89
+ ```
90
+
91
+ ## Безопасность `!shell`
92
+
93
+ Команда `!shell <cmd>` выполняет произвольные команды ОС прямо из TUI.
94
+ По умолчанию она **ВЫКЛЮЧЕНА** и включается только явным флагом:
95
+
96
+ ```bash
97
+ syscheck --enable-shell
98
+ ```
99
+
100
+ - При первом включении показывается предупреждение — нужно набрать `yes`.
101
+ - На **каждую** команду требуется подтверждение (повторить команду или `confirm`).
102
+ - Каждое выполнение записывается в audit-лог:
103
+ `~/.syscheck/shell_audit.log`.
104
+ - Настройки хранятся в `~/.syscheck/config.json`.
105
+
106
+ ## Плагины
107
+
108
+ Свои команды добавляются через плагины:
109
+
110
+ 1. Создай файл `syscheck/plugins/мой_плагин.py`
111
+ 2. Наследуй класс `PluginBase` из `syscheck.plugin`
112
+ 3. Реализуй `execute()`
113
+ 4. Плагин подхватится автоматически
114
+
115
+ ```python
116
+ from syscheck.plugin import PluginBase, register_plugin
117
+ from syscheck.utils import console, print_value
118
+
119
+ class MyPlugin(PluginBase):
120
+ name = "mycmd"
121
+ description = "Моя команда"
122
+ version = "0.1.0"
123
+
124
+ def execute(self, **kwargs):
125
+ print_value("hello", "syscheck!")
126
+
127
+ register_plugin(MyPlugin())
128
+ ```
129
+
130
+ Запуск: `syscheck plugins --list` и `syscheck plugins --run mycmd`
131
+
132
+ ## Пример использования
133
+
134
+ ```bash
135
+ syscheck sys # общее состояние системы
136
+ syscheck watch # живой дашборд
137
+ syscheck proc --sort ram # кто ест память
138
+ ```
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "syscheck-cli"
7
+ version = "0.2.0"
8
+ description = "CLI utility for system diagnostics"
9
+ requires-python = ">=3.10"
10
+ dependencies = [
11
+ "psutil>=5.9.0",
12
+ "rich>=13.0.0",
13
+ "typer>=0.9.0",
14
+ "textual>=0.80.0",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ dev = [
19
+ "pytest>=8.0.0",
20
+ "build>=1.0.0",
21
+ "twine>=4.0.0",
22
+ "pyinstaller>=6.0.0",
23
+ ]
24
+
25
+ [project.scripts]
26
+ syscheck = "syscheck.cli:main"
27
+
28
+ [tool.setuptools.packages.find]
29
+ include = ["syscheck*"]
30
+
31
+ [tool.pytest.ini_options]
32
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,2 @@
1
+ """syscheck — CLI-утилита диагностики системы."""
2
+ __version__ = "0.2.0"
@@ -0,0 +1,4 @@
1
+ """Allow running as: python -m syscheck"""
2
+ from syscheck.cli import main
3
+
4
+ main()
@@ -0,0 +1,134 @@
1
+ """Главный entry point для syscheck CLI."""
2
+ import sys
3
+ import typer
4
+ from rich.console import Console
5
+
6
+ # Windows console encoding fix (support emoji/unicode)
7
+ if sys.platform == "win32":
8
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
9
+ sys.stderr.reconfigure(encoding="utf-8", errors="replace")
10
+
11
+ from syscheck import __version__
12
+ from syscheck.plugin import get_plugins
13
+
14
+ app = typer.Typer(
15
+ name="syscheck",
16
+ help="syscheck — CLI-утилита диагностики системы",
17
+ no_args_is_help=True,
18
+ rich_markup_mode="rich",
19
+ )
20
+ console = Console()
21
+
22
+
23
+ def version_callback(value: bool):
24
+ if value:
25
+ console.print(f"syscheck v{__version__}")
26
+ raise typer.Exit()
27
+
28
+
29
+ @app.callback()
30
+ def main_cb(
31
+ version: bool = typer.Option(
32
+ False, "--version", "-v", callback=version_callback, is_eager=True,
33
+ help="Показать версию",
34
+ ),
35
+ ):
36
+ """syscheck — диагностика системы из терминала.
37
+
38
+ Запуск без аргументов открывает интерактивную оболочку (TUI).
39
+ syscheck <команда> — одиночная проверка. --help по командам.
40
+ """
41
+
42
+
43
+ # Импорт и регистрация команд
44
+ from syscheck.commands.cpu import cpu_cmd
45
+ from syscheck.commands.ram import ram_cmd
46
+ from syscheck.commands.disk import disk_cmd
47
+ from syscheck.commands.net import net_cmd
48
+ from syscheck.commands.proc import proc_cmd
49
+ from syscheck.commands.temp import temp_cmd
50
+ from syscheck.commands.sys import sys_cmd
51
+ from syscheck.commands.watch import watch_cmd
52
+
53
+ app.command("cpu")(cpu_cmd)
54
+ app.command("ram")(ram_cmd)
55
+ app.command("disk")(disk_cmd)
56
+ app.command("net")(net_cmd)
57
+ app.command("proc")(proc_cmd)
58
+ app.command("temp")(temp_cmd)
59
+ app.command("sys")(sys_cmd)
60
+ app.command("watch")(watch_cmd)
61
+
62
+
63
+ @app.command("all")
64
+ def all_cmd(
65
+ output_json: bool = typer.Option(False, "--json", "-j", help="Вывод в JSON"),
66
+ ):
67
+ """Показать все метрики системы."""
68
+ from syscheck.commands.cpu import show_cpu
69
+ from syscheck.commands.ram import show_ram
70
+ from syscheck.commands.disk import show_disk
71
+ from syscheck.commands.net import show_net
72
+ from syscheck.commands.temp import show_temp
73
+ from syscheck.commands.sys import show_sys
74
+
75
+ console.rule("syscheck — Полная диагностика")
76
+ console.print()
77
+ show_cpu()
78
+ console.print()
79
+ show_ram()
80
+ console.print()
81
+ show_disk()
82
+ console.print()
83
+ show_net()
84
+ console.print()
85
+ show_temp()
86
+ console.print()
87
+ show_sys()
88
+
89
+
90
+ @app.command("plugins")
91
+ def plugins_cmd(
92
+ list_plugins: bool = typer.Option(False, "--list", "-l", help="Список плагинов"),
93
+ plugin_name: str = typer.Option(None, "--run", "-r", help="Запустить плагин"),
94
+ ):
95
+ """Управление плагинами."""
96
+ plugins = get_plugins()
97
+
98
+ if not plugins:
99
+ console.print("[dim]Плагины не найдены. Добавьте .py файлы в syscheck/plugins/[/]")
100
+ return
101
+
102
+ if list_plugins or (not plugin_name):
103
+ from rich.table import Table
104
+ table = Table(title="Доступные плагины", show_header=True, header_style="bold cyan")
105
+ table.add_column("Имя", style="green")
106
+ table.add_column("Описание")
107
+ table.add_column("Версия", style="dim")
108
+ for name, p in plugins.items():
109
+ table.add_row(name, p.description, p.version)
110
+ console.print(table)
111
+ return
112
+
113
+ if plugin_name:
114
+ plugin = plugins.get(plugin_name)
115
+ if plugin:
116
+ plugin.execute()
117
+ else:
118
+ console.print(f"[red]Плагин '{plugin_name}' не найден.[/]")
119
+ console.print(f"Доступные: {', '.join(plugins.keys())}")
120
+
121
+
122
+ def main():
123
+ """Точка входа: без аргументов → TUI, с аргументами → CLI."""
124
+ enable_shell = False
125
+ args = list(sys.argv[1:])
126
+ if "--enable-shell" in args:
127
+ enable_shell = True
128
+ args.remove("--enable-shell")
129
+ sys.argv = [sys.argv[0]] + args
130
+ if len(args) <= 0:
131
+ from syscheck.tui import run as run_tui
132
+ run_tui(enable_shell=enable_shell)
133
+ else:
134
+ app()
@@ -0,0 +1,109 @@
1
+ """Командный язык syscheck.
2
+
3
+ Грамматика разделена на группы:
4
+
5
+ Info cpu, ram, gpu, disk, battery, temp
6
+ Processes process list/find/kill/stop/resume/restart/details/sort
7
+ Network network (speeds/interfaces/connections/ping)
8
+ Display watch, refresh
9
+ System system, all, help, clear, exit
10
+
11
+ Список COMMANDS — единый источник: он же питает Command Palette (Ctrl+P)
12
+ и справку `help`. Обработчики реализованы в TUI как методы `_cmd_*`;
13
+ этот модуль только описывает команды и генерирует текст справки/палитры.
14
+ """
15
+ from __future__ import annotations
16
+
17
+ CATEGORIES = ["Info", "Processes", "Network", "Battery", "Display", "System"]
18
+
19
+ # (команда, категория, описание, нужен ли фоновый поток)
20
+ COMMANDS = [
21
+ ("cpu", "Info", "CPU information", True),
22
+ ("ram", "Info", "Memory information", True),
23
+ ("gpu", "Info", "GPU information", True),
24
+ ("disk", "Info", "disk list | info X: | io", True),
25
+ ("battery", "Battery", "Battery status", True),
26
+ ("temp", "Info", "Temperatures", True),
27
+ ("network", "Network", "network | connections | interfaces | speeds | ping", True),
28
+ ("net", "Network", "alias: net [host]", True),
29
+ ("process", "Processes", "process list | find | kill | stop | resume | restart | details | sort", True),
30
+ ("proc", "Processes", "top processes by cpu|ram|name", True),
31
+ ("system", "System", "System information", True),
32
+ ("all", "System", "everything at once", True),
33
+ ("watch", "Display", "watch cpu | network | process [name] | off", False),
34
+ ("settings", "Display", "show settings & config path", False),
35
+ ("refresh", "Display", "refresh dashboard now", False),
36
+ ("help", "System", "show this help", False),
37
+ ("clear", "System", "clear terminal output", False),
38
+ ("exit", "System", "quit (alias: quit)", False),
39
+ ]
40
+
41
+ # скрытые алиасы (не показываются в справке/палитре)
42
+ _ALIASES = {
43
+ "quit": ("exit", ""),
44
+ }
45
+
46
+
47
+ def command_list() -> list:
48
+ """Полный список команд палитры (без алиасов)."""
49
+ out = []
50
+ for cmd, cat, desc, thread in COMMANDS:
51
+ out.append({"cmd": cmd, "cat": cat, "desc": desc, "thread": thread})
52
+ return out
53
+
54
+
55
+ def lookup(words: list) -> dict | None:
56
+ """Находит запись команды по первым словам (с учётом алиасов)."""
57
+ first = (words[0] if words else "").lower()
58
+ if first in _ALIASES:
59
+ cmd, _ = _ALIASES[first]
60
+ else:
61
+ cmd = first
62
+ for c, cat, desc, thread in COMMANDS:
63
+ if c == cmd:
64
+ return {"cmd": c, "cat": cat, "desc": desc, "thread": thread}
65
+ return None
66
+
67
+
68
+ def expected_args(cmd: str) -> str:
69
+ """Подсказка по аргументам команды (для справки по одной команде)."""
70
+ guide = {
71
+ "disk": "disk list | disk info C: | disk io",
72
+ "network": "network | network connections | network interfaces | network speeds | network ping <host>",
73
+ "net": "net [host]",
74
+ "process": "process list [name] | process find <name|pid> | process sort cpu|ram|name |"
75
+ " process details <name|pid> | process kill|stop|resume|restart <name|pid>",
76
+ "proc": "proc cpu | proc ram | proc name",
77
+ "watch": "watch cpu | watch network | watch process [name] | watch off",
78
+ "cpu": "cpu",
79
+ "ram": "ram",
80
+ "gpu": "gpu",
81
+ "battery": "battery",
82
+ "temp": "temp",
83
+ "system": "system",
84
+ "all": "all",
85
+ "settings": "settings",
86
+ "refresh": "refresh",
87
+ }
88
+ return guide.get(cmd, "")
89
+
90
+
91
+ def help_text() -> str:
92
+ """Полная справка по группам."""
93
+ lines = ["[bold]syscheck commands[/]"]
94
+ for cat in CATEGORIES:
95
+ items = [(c, d, t) for c, cc, d, t in COMMANDS if cc == cat]
96
+ if not items:
97
+ continue
98
+ lines.append(f" [dim]{cat}[/]")
99
+ for c, d, t in items:
100
+ lines.append(f" [cyan]{c:<10}[/] {d}")
101
+ lines.append("")
102
+ lines.append("[dim]ctrl+p command palette ↑↓ history · in processes table:[/]")
103
+ lines.append("[dim] Enter details, K kill, S stop, R restart, / search[/]")
104
+ return "\n".join(lines)
105
+
106
+
107
+ def help_for(cmd: str) -> str:
108
+ args = expected_args(cmd)
109
+ return f"[cyan]{cmd}[/] {args}" if args else f"[dim]nothing more about '{cmd}'[/]"
@@ -0,0 +1,8 @@
1
+ from syscheck.commands.cpu import cpu_cmd
2
+ from syscheck.commands.ram import ram_cmd
3
+ from syscheck.commands.disk import disk_cmd
4
+ from syscheck.commands.net import net_cmd
5
+ from syscheck.commands.proc import proc_cmd
6
+ from syscheck.commands.temp import temp_cmd
7
+ from syscheck.commands.sys import sys_cmd
8
+ from syscheck.commands.watch import watch_cmd
@@ -0,0 +1,45 @@
1
+ """Команда syscheck cpu — диагностика процессора."""
2
+ import json
3
+
4
+ import typer
5
+
6
+ from syscheck import providers
7
+ from syscheck.utils import console, make_bar, print_value, print_header, get_color_for_percent
8
+
9
+
10
+ def show_cpu() -> None:
11
+ data = providers.cpu_info()
12
+ per_cpu = data["per_cpu"] or []
13
+
14
+ print_header("cpu")
15
+
16
+ colour = get_color_for_percent(data["percent"])
17
+ console.print(
18
+ f"load [{colour}]{data['percent']}%[/] {make_bar(data['percent'])}"
19
+ )
20
+ print_value("cores", f"{data['count_physical'] or '?'} physical / {data['count_logical']} logical")
21
+ if data["freq_current"]:
22
+ freq = f"{data['freq_current']:.0f} MHz"
23
+ if data["freq_max"]:
24
+ freq += f" (max {data['freq_max']:.0f} MHz)"
25
+ print_value("freq", freq)
26
+ if per_cpu:
27
+ parts = " ".join(
28
+ f"[{get_color_for_percent(p)}]{i}: {p:.0f}[/]" for i, p in enumerate(per_cpu)
29
+ )
30
+ console.print(f"per-core {parts}")
31
+
32
+ if data["percent"] >= 90:
33
+ console.print(" [red][!] CPU сильно нагружен[/]")
34
+ elif data["percent"] >= 75:
35
+ console.print(" [yellow][!] CPU заметно нагружен[/]")
36
+
37
+
38
+ def cpu_cmd(
39
+ output_json: bool = typer.Option(False, "--json", "-j", help="Вывод в JSON"),
40
+ ):
41
+ """Диагностика процессора (нагрузка, ядра, частота)."""
42
+ if output_json:
43
+ console.print_json(json.dumps(providers.cpu_info(), indent=2))
44
+ else:
45
+ show_cpu()
@@ -0,0 +1,61 @@
1
+ """Команда syscheck disk — диагностика дисков."""
2
+ import json
3
+
4
+ import typer
5
+
6
+ from syscheck import providers
7
+ from syscheck.utils import (
8
+ console, make_bar, print_value, print_header, get_color_for_percent, bytes_to_human,
9
+ print_error, print_warning,
10
+ )
11
+
12
+
13
+ def show_disk() -> None:
14
+ data = providers.disk_info()
15
+
16
+ print_header("disks")
17
+
18
+ if not data["disks"]:
19
+ console.print("[dim]диски не найдены[/]")
20
+ return
21
+
22
+ for d in data["disks"]:
23
+ colour = get_color_for_percent(d["percent"])
24
+ console.print(
25
+ f"{d['device'].ljust(8)} [{colour}]{d['percent']:4.1f}%[/] {make_bar(d['percent'])} "
26
+ f"{bytes_to_human(d['used'])} / {bytes_to_human(d['total'])} "
27
+ f"(free {bytes_to_human(d['free'])})"
28
+ )
29
+
30
+ io = data["io"]
31
+ if io:
32
+ console.print()
33
+ print_header("io")
34
+ print_value("read", bytes_to_human(io.read_bytes))
35
+ print_value("write", bytes_to_human(io.write_bytes))
36
+
37
+ for d in data["disks"]:
38
+ if d["percent"] > 95:
39
+ print_error(f"{d['device']}: критически мало места!")
40
+ elif d["percent"] > 85:
41
+ print_warning(f"{d['device']}: мало свободного места")
42
+
43
+
44
+ def disk_cmd(
45
+ output_json: bool = typer.Option(False, "--json", "-j", help="Вывод в JSON"),
46
+ ):
47
+ """Диагностика дисков (место, состояние, I/O)."""
48
+ if output_json:
49
+ out = []
50
+ for d in providers.disk_info()["disks"]:
51
+ out.append({
52
+ "device": d["device"],
53
+ "mountpoint": d["mountpoint"],
54
+ "total": d["total"],
55
+ "used": d["used"],
56
+ "free": d["free"],
57
+ "percent": d["percent"],
58
+ })
59
+ console.print_json(json.dumps(out, indent=2))
60
+ else:
61
+ show_disk()
@@ -0,0 +1,59 @@
1
+ """Команда syscheck net — диагностика сети."""
2
+ import json
3
+
4
+ import typer
5
+
6
+ from syscheck import providers
7
+ from syscheck.utils import (
8
+ console, print_value, print_header, bytes_to_human, print_ok, print_warning, print_error,
9
+ )
10
+
11
+
12
+ def show_net(ping_host: str = "8.8.8.8") -> None:
13
+ data = providers.net_info()
14
+
15
+ print_header("network")
16
+
17
+ for i in data["interfaces"]:
18
+ status = "[green]up[/]" if i["up"] else "[dim]down[/]"
19
+ ip = ", ".join(i["ipv4"]) if i["ipv4"] else "[dim]N/A[/]"
20
+ speed = f"{i['speed']} Mbps" if i["speed"] else "-"
21
+ console.print(f" {status} {i['name'].ljust(28)} {ip.ljust(18)} {speed}")
22
+ if i["mac"]:
23
+ console.print(f"{'':2} mac: {', '.join(i['mac'])}")
24
+
25
+ io = data["io"]
26
+ if io:
27
+ console.print()
28
+ print_value("sent", bytes_to_human(io["bytes_sent"]))
29
+ print_value("recv", bytes_to_human(io["bytes_recv"]))
30
+ print_value("packets", f"{io['packets_sent']} / {io['packets_recv']}")
31
+
32
+ console.print()
33
+ print_header(f"ping {ping_host}")
34
+ result = providers.ping(ping_host)
35
+ if result["success"]:
36
+ if result["avg_ms"] is not None:
37
+ print_ok(f"{result['host']}: {result['avg_ms']:.1f} ms "
38
+ f"(min {result['min_ms']:.1f}, max {result['max_ms']:.1f})")
39
+ else:
40
+ print_ok(f"{result['host']}: пинг прошёл (время не определено)")
41
+ if result["lost"] > 0:
42
+ print_warning(f"потеряно {result['lost']}/{result['total']} пакетов")
43
+ if result["avg_ms"] and result["avg_ms"] > 100:
44
+ print_warning("высокий пинг")
45
+ else:
46
+ print_error(f"{ping_host}: недоступен")
47
+
48
+
49
+ def net_cmd(
50
+ output_json: bool = typer.Option(False, "--json", "-j", help="Вывод в JSON"),
51
+ ping_host: str = typer.Option("8.8.8.8", "--ping", "-p", help="Хост для пинга"),
52
+ ):
53
+ """Диагностика сети (интерфейсы, трафик, пинг)."""
54
+ if output_json:
55
+ data = providers.net_info()
56
+ data["ping"] = providers.ping(ping_host)
57
+ console.print_json(json.dumps(data, indent=2))
58
+ else:
59
+ show_net(ping_host)
@@ -0,0 +1,35 @@
1
+ """Команда syscheck proc — топ процессов."""
2
+ import json
3
+
4
+ import typer
5
+
6
+ from syscheck import providers
7
+ from syscheck.utils import console, print_header, get_color_for_percent
8
+
9
+
10
+ def show_proc(sort_by: str = "cpu", limit: int = 15) -> None:
11
+ data = providers.processes(sort_by=sort_by, limit=limit)
12
+
13
+ print_header(f"processes by {sort_by}")
14
+
15
+ for p in data:
16
+ cpu = p.get("cpu_percent") or 0
17
+ ram = p.get("memory_percent") or 0
18
+ status = p.get("status", "?")
19
+ console.print(
20
+ f" {p['pid']:<8} {(p['name'] or '?')[:28]:<28} "
21
+ f"cpu[{get_color_for_percent(cpu)}]{cpu:5.1f}[/] "
22
+ f"ram[{get_color_for_percent(ram)}]{ram:5.1f}[/] {status}"
23
+ )
24
+
25
+
26
+ def proc_cmd(
27
+ output_json: bool = typer.Option(False, "--json", "-j", help="Вывод в JSON"),
28
+ sort_by: str = typer.Option("cpu", "--sort", "-s", help="Сортировка: cpu, ram, name"),
29
+ limit: int = typer.Option(15, "--limit", "-n", help="Количество процессов"),
30
+ ):
31
+ """Топ процессов по CPU/RAM."""
32
+ if output_json:
33
+ console.print_json(json.dumps(providers.processes(sort_by=sort_by, limit=limit), indent=2))
34
+ else:
35
+ show_proc(sort_by, limit)