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.
- syscheck_cli-0.2.0/PKG-INFO +14 -0
- syscheck_cli-0.2.0/README.md +138 -0
- syscheck_cli-0.2.0/pyproject.toml +32 -0
- syscheck_cli-0.2.0/setup.cfg +4 -0
- syscheck_cli-0.2.0/syscheck/__init__.py +2 -0
- syscheck_cli-0.2.0/syscheck/__main__.py +4 -0
- syscheck_cli-0.2.0/syscheck/cli.py +134 -0
- syscheck_cli-0.2.0/syscheck/cmdlang.py +109 -0
- syscheck_cli-0.2.0/syscheck/commands/__init__.py +8 -0
- syscheck_cli-0.2.0/syscheck/commands/cpu.py +45 -0
- syscheck_cli-0.2.0/syscheck/commands/disk.py +61 -0
- syscheck_cli-0.2.0/syscheck/commands/net.py +59 -0
- syscheck_cli-0.2.0/syscheck/commands/proc.py +35 -0
- syscheck_cli-0.2.0/syscheck/commands/ram.py +49 -0
- syscheck_cli-0.2.0/syscheck/commands/sys.py +43 -0
- syscheck_cli-0.2.0/syscheck/commands/temp.py +50 -0
- syscheck_cli-0.2.0/syscheck/commands/watch.py +115 -0
- syscheck_cli-0.2.0/syscheck/palette.py +30 -0
- syscheck_cli-0.2.0/syscheck/plugin.py +89 -0
- syscheck_cli-0.2.0/syscheck/plugins/__init__.py +1 -0
- syscheck_cli-0.2.0/syscheck/plugins/example.py +38 -0
- syscheck_cli-0.2.0/syscheck/providers.py +705 -0
- syscheck_cli-0.2.0/syscheck/screens.py +265 -0
- syscheck_cli-0.2.0/syscheck/shellguard.py +126 -0
- syscheck_cli-0.2.0/syscheck/tui.py +1086 -0
- syscheck_cli-0.2.0/syscheck/utils.py +111 -0
- syscheck_cli-0.2.0/syscheck_cli.egg-info/PKG-INFO +14 -0
- syscheck_cli-0.2.0/syscheck_cli.egg-info/SOURCES.txt +36 -0
- syscheck_cli-0.2.0/syscheck_cli.egg-info/dependency_links.txt +1 -0
- syscheck_cli-0.2.0/syscheck_cli.egg-info/entry_points.txt +2 -0
- syscheck_cli-0.2.0/syscheck_cli.egg-info/requires.txt +10 -0
- syscheck_cli-0.2.0/syscheck_cli.egg-info/top_level.txt +1 -0
- syscheck_cli-0.2.0/tests/test_cli_entry.py +23 -0
- syscheck_cli-0.2.0/tests/test_cmdlang.py +30 -0
- syscheck_cli-0.2.0/tests/test_providers.py +77 -0
- syscheck_cli-0.2.0/tests/test_providers_new.py +70 -0
- syscheck_cli-0.2.0/tests/test_safety.py +65 -0
- 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,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)
|