standkit 0.3.7__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 (58) hide show
  1. standkit-0.3.7/LICENSE +21 -0
  2. standkit-0.3.7/PKG-INFO +155 -0
  3. standkit-0.3.7/README.md +130 -0
  4. standkit-0.3.7/pyproject.toml +55 -0
  5. standkit-0.3.7/setup.cfg +4 -0
  6. standkit-0.3.7/standkit/__init__.py +11 -0
  7. standkit-0.3.7/standkit/health.py +163 -0
  8. standkit-0.3.7/standkit/lifecycle.py +191 -0
  9. standkit-0.3.7/standkit/logs.py +221 -0
  10. standkit-0.3.7/standkit/models.py +211 -0
  11. standkit-0.3.7/standkit/platform.py +174 -0
  12. standkit-0.3.7/standkit/registry.py +223 -0
  13. standkit-0.3.7/standkit/secrets.py +144 -0
  14. standkit-0.3.7/standkit.egg-info/PKG-INFO +155 -0
  15. standkit-0.3.7/standkit.egg-info/SOURCES.txt +56 -0
  16. standkit-0.3.7/standkit.egg-info/dependency_links.txt +1 -0
  17. standkit-0.3.7/standkit.egg-info/entry_points.txt +4 -0
  18. standkit-0.3.7/standkit.egg-info/requires.txt +9 -0
  19. standkit-0.3.7/standkit.egg-info/top_level.txt +3 -0
  20. standkit-0.3.7/standkit_agent/__init__.py +9 -0
  21. standkit-0.3.7/standkit_agent/__main__.py +192 -0
  22. standkit-0.3.7/standkit_agent/audit.py +93 -0
  23. standkit-0.3.7/standkit_agent/security.py +300 -0
  24. standkit-0.3.7/standkit_agent/server.py +386 -0
  25. standkit-0.3.7/standkit_hub/__init__.py +13 -0
  26. standkit-0.3.7/standkit_hub/__main__.py +131 -0
  27. standkit-0.3.7/standkit_hub/agent_control.py +216 -0
  28. standkit-0.3.7/standkit_hub/assets/bpmkit-icon.ico +0 -0
  29. standkit-0.3.7/standkit_hub/assets/icon.png +0 -0
  30. standkit-0.3.7/standkit_hub/client.py +159 -0
  31. standkit-0.3.7/standkit_hub/config.py +156 -0
  32. standkit-0.3.7/standkit_hub/logs_browser.py +174 -0
  33. standkit-0.3.7/standkit_hub/redis_min.py +326 -0
  34. standkit-0.3.7/standkit_hub/security.py +146 -0
  35. standkit-0.3.7/standkit_hub/server.py +881 -0
  36. standkit-0.3.7/standkit_hub/shortcut.py +264 -0
  37. standkit-0.3.7/standkit_hub/web/app.js +803 -0
  38. standkit-0.3.7/standkit_hub/web/bpmkit-logo-dark.svg +5 -0
  39. standkit-0.3.7/standkit_hub/web/bpmkit-logo.svg +5 -0
  40. standkit-0.3.7/standkit_hub/web/favicon.png +0 -0
  41. standkit-0.3.7/standkit_hub/web/favicon.svg +4 -0
  42. standkit-0.3.7/standkit_hub/web/index.html +267 -0
  43. standkit-0.3.7/standkit_hub/web/style.css +843 -0
  44. standkit-0.3.7/tests/test_agent_security.py +303 -0
  45. standkit-0.3.7/tests/test_agent_server_integration.py +192 -0
  46. standkit-0.3.7/tests/test_health.py +146 -0
  47. standkit-0.3.7/tests/test_hub_agent_control.py +192 -0
  48. standkit-0.3.7/tests/test_hub_config.py +140 -0
  49. standkit-0.3.7/tests/test_hub_logs_browser.py +238 -0
  50. standkit-0.3.7/tests/test_hub_redis_min.py +247 -0
  51. standkit-0.3.7/tests/test_hub_server.py +1002 -0
  52. standkit-0.3.7/tests/test_hub_shortcut.py +203 -0
  53. standkit-0.3.7/tests/test_lifecycle.py +126 -0
  54. standkit-0.3.7/tests/test_logs.py +205 -0
  55. standkit-0.3.7/tests/test_models.py +73 -0
  56. standkit-0.3.7/tests/test_registry.py +78 -0
  57. standkit-0.3.7/tests/test_registry_resolver.py +140 -0
  58. standkit-0.3.7/tests/test_secrets.py +106 -0
standkit-0.3.7/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 standkit contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,155 @@
1
+ Metadata-Version: 2.4
2
+ Name: standkit
3
+ Version: 0.3.7
4
+ Summary: Свободное ядро (MIT) для управления жизненным циклом локальных и удалённых стендов BPMSoft: headless start/stop/restart, health-пробы, логи, реестр стендов. Часть экосистемы BPMkit.
5
+ Author: standkit contributors
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/thinkquattro/BPMkitStand
8
+ Project-URL: Экосистема BPMkit, https://github.com/thinkquattro/BPMkit-dev
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: Topic :: System :: Systems Administration
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Provides-Extra: secrets
19
+ Requires-Dist: keyring>=24; extra == "secrets"
20
+ Provides-Extra: desktop
21
+ Requires-Dist: pywebview>=5; extra == "desktop"
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=7.0; extra == "dev"
24
+ Dynamic: license-file
25
+
26
+ <div align="center">
27
+
28
+ <img src="https://raw.githubusercontent.com/thinkquattro/BPMkitStand/main/standkit_hub/web/bpmkit-logo.svg" alt="BPMkit" width="300"/>
29
+
30
+ # BPMkitStand
31
+
32
+ **Свободный диспетчер стендов BPMSoft.**
33
+ Локальный веб-дашборд для запуска, остановки и мониторинга ваших стендов — в один клик, без консоли.
34
+
35
+ [![License: MIT](https://img.shields.io/badge/License-MIT-f9763d.svg)](LICENSE)
36
+ [![Python 3.10+](https://img.shields.io/badge/Python-3.10%2B-blue.svg)](https://www.python.org/)
37
+ [![Platform](https://img.shields.io/badge/OS-Windows%20%7C%20Linux-lightgrey.svg)]()
38
+
39
+ [Сайт](https://bpmkit.pro) · [Telegram](https://t.me/quattrolife) · [Companion-версия](https://bpmkit.pro)
40
+
41
+ </div>
42
+
43
+ ---
44
+
45
+ ## Что это
46
+
47
+ **BPMkitStand** — бесплатный инструмент экосистемы [BPMkit](https://bpmkit.pro) для управления
48
+ локальными и удалёнными стендами BPMSoft. Открывает лёгкий локальный веб-дашборд в браузере:
49
+ видно состояние каждого стенда, его можно запустить/остановить/перезапустить, посмотреть логи
50
+ текущей сессии и очистить Redis — всё без ручных команд в PowerShell.
51
+
52
+ Ядро (`standkit`) написано на чистой стандартной библиотеке Python — без тяжёлых зависимостей,
53
+ работает на Windows и Linux.
54
+
55
+ ## Возможности
56
+
57
+ - **Дашборд стендов** — список из общего реестра BPMkit (`projects.json`), состояние в реальном времени.
58
+ - **Жизненный цикл** — старт / стоп / рестарт стенда с честной обратной связью (спиннер до готовности, без ложных «не поднялся» на прогреве).
59
+ - **Логи** — просмотр логов текущей сессии стенда, открытие папки логов стенда и папки BPMkit-проекта.
60
+ - **Redis** — очистка кэша стенда (номер БД берётся из конфигурации стенда).
61
+ - **Тёмная и светлая тема** с переключателем.
62
+ - **Безопасность по умолчанию** — дашборд слушает только `127.0.0.1`, сессионный токен, защита мутаций (CSRF + проверка Origin).
63
+ - **Удалённые стенды** *(в развитии)* — через лёгкие кроссплатформенные headless-агенты с TLS/mTLS.
64
+
65
+ ## Скриншоты
66
+
67
+ | Дашборд (светлая тема) | Дашборд (тёмная тема) |
68
+ |---|---|
69
+ | ![Дашборд, светлая тема](https://raw.githubusercontent.com/thinkquattro/BPMkitStand/main/docs/img/dashboard-light.png) | ![Дашборд, тёмная тема](https://raw.githubusercontent.com/thinkquattro/BPMkitStand/main/docs/img/dashboard-dark.png) |
70
+
71
+ <div align="center">
72
+ <img src="https://raw.githubusercontent.com/thinkquattro/BPMkitStand/main/docs/img/about.png" alt="Модальное окно «О программе»" width="360"/>
73
+ </div>
74
+
75
+ ## Как это работает
76
+
77
+ BPMkitStand состоит из **ядра и двух оболочек**:
78
+
79
+ - **`standkit`** — ядро (MIT) на чистой стандартной библиотеке Python: движок жизненного цикла
80
+ над реестром `projects.json` (старт/стоп/рестарт процесса стенда), health-пробы (процесс / HTTP
81
+ / порт БД / порт Redis), tail лога, Secret-first доступ к секретам. Без веб-слоя и сетевых
82
+ зависимостей — можно использовать и как библиотеку/CLI.
83
+ - **`standkit_hub`** — локальный веб-дашборд (то, что видно на скриншотах). Сам себя отдаёт по
84
+ HTTP через `stdlib http.server` (vanilla JS/CSS, без CDN и сборки) и открывается в системном
85
+ браузере; опционально — в нативном окне (`standkit[desktop]`, `--desktop`). Устанавливается
86
+ только на машину оператора.
87
+ - **`standkit_agent`** — лёгкий headless-демон на хосте удалённого стенда (см. ниже).
88
+
89
+ Дашборд — **федеративный клиент**: он собирает в один список и локальные стенды (управляет ими
90
+ напрямую через ядро), и удалённые (ходит к их агентам по HTTP). В таблице колонка «Транспорт»
91
+ показывает, как дашборд дотягивается до стенда: `local` или `agent`.
92
+
93
+ Реестр стендов — **единый с MCP BPMkit**: один `projects.json` (стенды под ключом `projects`),
94
+ путь резолвится через `BPMSOFT_PROJECTS_FILE` → `%APPDATA%\BPMkit\projects.json`
95
+ (`~/.config/BPMkit/...` на Linux) → `./projects.json`. Секреты (пароли БД, токены агентов) в
96
+ реестре не хранятся — только ссылки на них (Secret-first).
97
+
98
+ Запуск стенда честный: дашборд поднимает `dotnet <stand_dll>` и держит спиннер до реального
99
+ ответа web-хоста по HTTP, а не рапортует «запущено» по факту создания процесса.
100
+
101
+ ## Удалённые стенды
102
+
103
+ Стенды на других хостах (виртуалки, серверы, контуры заказчика) управляются через **федерацию
104
+ лёгких кроссплатформенных агентов** (`standkit_agent`, Windows/Linux, только stdlib). На хосте
105
+ стенда поднимается агент, дашборд оператора ходит к нему по HTTPS с Bearer-токеном; стенд
106
+ объявляется удалённым одним полем `transport: "agent"` в реестре. Агент — RCE-поверхность по
107
+ дизайну, поэтому защищён secure-defaults: loopback по умолчанию, fail-closed на non-loopback без
108
+ TLS, TLS 1.2+/mTLS, скоупы control/readonly, lockout по IP, аудит.
109
+
110
+ **Полное описание, установка агента, TLS/mTLS, служба и траблшутинг — в
111
+ [docs/REMOTE_STANDS.md](docs/REMOTE_STANDS.md).**
112
+
113
+ ## Установка
114
+
115
+ Нужен Python 3.10+.
116
+
117
+ ```bash
118
+ pip install "git+https://github.com/thinkquattro/BPMkitStand.git"
119
+ ```
120
+
121
+ Запуск дашборда:
122
+
123
+ ```bash
124
+ standkit-hub
125
+ ```
126
+
127
+ Откроется браузер с локальным дашбордом. Реестр стендов берётся из
128
+ `%APPDATA%\BPMkit\projects.json` (или из переменной окружения `BPMSOFT_PROJECTS_FILE`).
129
+
130
+ ## Реестр стендов
131
+
132
+ BPMkitStand использует тот же реестр, что и MCP BPMkit — единый `projects.json`.
133
+ Образец формата — [`projects.sample.json`](projects.sample.json).
134
+
135
+ ## Безопасность
136
+
137
+ Дашборд и агент проектировались с расчётом на прод-контур. Модель угроз, харденинг и чек-лист —
138
+ в [SECURITY.md](SECURITY.md). Кратко: fail-closed bind на loopback, TLS 1.2+/mTLS для агента,
139
+ скоупы токена, per-IP lockout, аудит без утечки секретов.
140
+
141
+ ## BPMkitStand и Companion
142
+
143
+ Бесплатная версия — полноценный диспетчер стендов. **Companion-версия** дополнительно даёт
144
+ автообновление MCP BPMkit и контроль лицензии; поставляется в составе установщика MCP-клиента.
145
+ Подробнее — на [bpmkit.pro](https://bpmkit.pro).
146
+
147
+ ## Лицензия
148
+
149
+ [MIT](LICENSE) © Владимир Терновский
150
+
151
+ ---
152
+
153
+ <div align="center">
154
+ <sub>Часть экосистемы <a href="https://bpmkit.pro">BPMkit</a> — AI-ассистента для разработки на BPMSoft.</sub>
155
+ </div>
@@ -0,0 +1,130 @@
1
+ <div align="center">
2
+
3
+ <img src="https://raw.githubusercontent.com/thinkquattro/BPMkitStand/main/standkit_hub/web/bpmkit-logo.svg" alt="BPMkit" width="300"/>
4
+
5
+ # BPMkitStand
6
+
7
+ **Свободный диспетчер стендов BPMSoft.**
8
+ Локальный веб-дашборд для запуска, остановки и мониторинга ваших стендов — в один клик, без консоли.
9
+
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-f9763d.svg)](LICENSE)
11
+ [![Python 3.10+](https://img.shields.io/badge/Python-3.10%2B-blue.svg)](https://www.python.org/)
12
+ [![Platform](https://img.shields.io/badge/OS-Windows%20%7C%20Linux-lightgrey.svg)]()
13
+
14
+ [Сайт](https://bpmkit.pro) · [Telegram](https://t.me/quattrolife) · [Companion-версия](https://bpmkit.pro)
15
+
16
+ </div>
17
+
18
+ ---
19
+
20
+ ## Что это
21
+
22
+ **BPMkitStand** — бесплатный инструмент экосистемы [BPMkit](https://bpmkit.pro) для управления
23
+ локальными и удалёнными стендами BPMSoft. Открывает лёгкий локальный веб-дашборд в браузере:
24
+ видно состояние каждого стенда, его можно запустить/остановить/перезапустить, посмотреть логи
25
+ текущей сессии и очистить Redis — всё без ручных команд в PowerShell.
26
+
27
+ Ядро (`standkit`) написано на чистой стандартной библиотеке Python — без тяжёлых зависимостей,
28
+ работает на Windows и Linux.
29
+
30
+ ## Возможности
31
+
32
+ - **Дашборд стендов** — список из общего реестра BPMkit (`projects.json`), состояние в реальном времени.
33
+ - **Жизненный цикл** — старт / стоп / рестарт стенда с честной обратной связью (спиннер до готовности, без ложных «не поднялся» на прогреве).
34
+ - **Логи** — просмотр логов текущей сессии стенда, открытие папки логов стенда и папки BPMkit-проекта.
35
+ - **Redis** — очистка кэша стенда (номер БД берётся из конфигурации стенда).
36
+ - **Тёмная и светлая тема** с переключателем.
37
+ - **Безопасность по умолчанию** — дашборд слушает только `127.0.0.1`, сессионный токен, защита мутаций (CSRF + проверка Origin).
38
+ - **Удалённые стенды** *(в развитии)* — через лёгкие кроссплатформенные headless-агенты с TLS/mTLS.
39
+
40
+ ## Скриншоты
41
+
42
+ | Дашборд (светлая тема) | Дашборд (тёмная тема) |
43
+ |---|---|
44
+ | ![Дашборд, светлая тема](https://raw.githubusercontent.com/thinkquattro/BPMkitStand/main/docs/img/dashboard-light.png) | ![Дашборд, тёмная тема](https://raw.githubusercontent.com/thinkquattro/BPMkitStand/main/docs/img/dashboard-dark.png) |
45
+
46
+ <div align="center">
47
+ <img src="https://raw.githubusercontent.com/thinkquattro/BPMkitStand/main/docs/img/about.png" alt="Модальное окно «О программе»" width="360"/>
48
+ </div>
49
+
50
+ ## Как это работает
51
+
52
+ BPMkitStand состоит из **ядра и двух оболочек**:
53
+
54
+ - **`standkit`** — ядро (MIT) на чистой стандартной библиотеке Python: движок жизненного цикла
55
+ над реестром `projects.json` (старт/стоп/рестарт процесса стенда), health-пробы (процесс / HTTP
56
+ / порт БД / порт Redis), tail лога, Secret-first доступ к секретам. Без веб-слоя и сетевых
57
+ зависимостей — можно использовать и как библиотеку/CLI.
58
+ - **`standkit_hub`** — локальный веб-дашборд (то, что видно на скриншотах). Сам себя отдаёт по
59
+ HTTP через `stdlib http.server` (vanilla JS/CSS, без CDN и сборки) и открывается в системном
60
+ браузере; опционально — в нативном окне (`standkit[desktop]`, `--desktop`). Устанавливается
61
+ только на машину оператора.
62
+ - **`standkit_agent`** — лёгкий headless-демон на хосте удалённого стенда (см. ниже).
63
+
64
+ Дашборд — **федеративный клиент**: он собирает в один список и локальные стенды (управляет ими
65
+ напрямую через ядро), и удалённые (ходит к их агентам по HTTP). В таблице колонка «Транспорт»
66
+ показывает, как дашборд дотягивается до стенда: `local` или `agent`.
67
+
68
+ Реестр стендов — **единый с MCP BPMkit**: один `projects.json` (стенды под ключом `projects`),
69
+ путь резолвится через `BPMSOFT_PROJECTS_FILE` → `%APPDATA%\BPMkit\projects.json`
70
+ (`~/.config/BPMkit/...` на Linux) → `./projects.json`. Секреты (пароли БД, токены агентов) в
71
+ реестре не хранятся — только ссылки на них (Secret-first).
72
+
73
+ Запуск стенда честный: дашборд поднимает `dotnet <stand_dll>` и держит спиннер до реального
74
+ ответа web-хоста по HTTP, а не рапортует «запущено» по факту создания процесса.
75
+
76
+ ## Удалённые стенды
77
+
78
+ Стенды на других хостах (виртуалки, серверы, контуры заказчика) управляются через **федерацию
79
+ лёгких кроссплатформенных агентов** (`standkit_agent`, Windows/Linux, только stdlib). На хосте
80
+ стенда поднимается агент, дашборд оператора ходит к нему по HTTPS с Bearer-токеном; стенд
81
+ объявляется удалённым одним полем `transport: "agent"` в реестре. Агент — RCE-поверхность по
82
+ дизайну, поэтому защищён secure-defaults: loopback по умолчанию, fail-closed на non-loopback без
83
+ TLS, TLS 1.2+/mTLS, скоупы control/readonly, lockout по IP, аудит.
84
+
85
+ **Полное описание, установка агента, TLS/mTLS, служба и траблшутинг — в
86
+ [docs/REMOTE_STANDS.md](docs/REMOTE_STANDS.md).**
87
+
88
+ ## Установка
89
+
90
+ Нужен Python 3.10+.
91
+
92
+ ```bash
93
+ pip install "git+https://github.com/thinkquattro/BPMkitStand.git"
94
+ ```
95
+
96
+ Запуск дашборда:
97
+
98
+ ```bash
99
+ standkit-hub
100
+ ```
101
+
102
+ Откроется браузер с локальным дашбордом. Реестр стендов берётся из
103
+ `%APPDATA%\BPMkit\projects.json` (или из переменной окружения `BPMSOFT_PROJECTS_FILE`).
104
+
105
+ ## Реестр стендов
106
+
107
+ BPMkitStand использует тот же реестр, что и MCP BPMkit — единый `projects.json`.
108
+ Образец формата — [`projects.sample.json`](projects.sample.json).
109
+
110
+ ## Безопасность
111
+
112
+ Дашборд и агент проектировались с расчётом на прод-контур. Модель угроз, харденинг и чек-лист —
113
+ в [SECURITY.md](SECURITY.md). Кратко: fail-closed bind на loopback, TLS 1.2+/mTLS для агента,
114
+ скоупы токена, per-IP lockout, аудит без утечки секретов.
115
+
116
+ ## BPMkitStand и Companion
117
+
118
+ Бесплатная версия — полноценный диспетчер стендов. **Companion-версия** дополнительно даёт
119
+ автообновление MCP BPMkit и контроль лицензии; поставляется в составе установщика MCP-клиента.
120
+ Подробнее — на [bpmkit.pro](https://bpmkit.pro).
121
+
122
+ ## Лицензия
123
+
124
+ [MIT](LICENSE) © Владимир Терновский
125
+
126
+ ---
127
+
128
+ <div align="center">
129
+ <sub>Часть экосистемы <a href="https://bpmkit.pro">BPMkit</a> — AI-ассистента для разработки на BPMSoft.</sub>
130
+ </div>
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "standkit"
7
+ version = "0.3.7"
8
+ description = "Свободное ядро (MIT) для управления жизненным циклом локальных и удалённых стендов BPMSoft: headless start/stop/restart, health-пробы, логи, реестр стендов. Часть экосистемы BPMkit."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ authors = [{ name = "standkit contributors" }]
12
+ requires-python = ">=3.10"
13
+ classifiers = [
14
+ "License :: OSI Approved :: MIT License",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Operating System :: OS Independent",
18
+ "Intended Audience :: System Administrators",
19
+ "Topic :: System :: Systems Administration",
20
+ ]
21
+ # Ядро и агент — БЕЗ обязательных сторонних зависимостей (stdlib-only).
22
+ dependencies = []
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/thinkquattro/BPMkitStand"
26
+ "Экосистема BPMkit" = "https://github.com/thinkquattro/BPMkit-dev"
27
+
28
+ [project.optional-dependencies]
29
+ # Опциональный backend хранения секретов (standkit.secrets: set_secret/
30
+ # get_secret/has_secret/delete_secret) — ядро остаётся stdlib-only без него,
31
+ # просто SecretError на set_secret/delete_secret ("нет keyring").
32
+ secrets = ["keyring>=24"]
33
+ # Нативное окно вместо системного браузера для веб-дашборда (standkit_hub
34
+ # --desktop). Требует нативный вебдвижок ОС: WebView2 на Windows,
35
+ # WebKitGTK на Linux — сам pywebview его не тянет как pip-зависимость.
36
+ desktop = ["pywebview>=5"]
37
+ # Инструменты разработки/тестирования ядра, агента и хаба.
38
+ dev = ["pytest>=7.0"]
39
+
40
+ [project.scripts]
41
+ standkit-agent = "standkit_agent.__main__:main"
42
+ standkit-gui = "standkit_hub.__main__:main"
43
+ standkit-hub = "standkit_hub.__main__:main"
44
+
45
+ [tool.setuptools]
46
+ include-package-data = true
47
+
48
+ [tool.setuptools.packages.find]
49
+ include = ["standkit*", "standkit_agent*", "standkit_hub*"]
50
+
51
+ [tool.setuptools.package-data]
52
+ standkit_hub = ["assets/*", "web/*"]
53
+
54
+ [tool.pytest.ini_options]
55
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,11 @@
1
+ """
2
+ standkit — свободное (MIT) ядро управления жизненным циклом стендов BPMSoft.
3
+
4
+ Пакет не содержит лицензионно-чувствительного контента платформы BPMSoft и не
5
+ зависит от сторонних библиотек (stdlib-only). Полная документация — см. README.md
6
+ и docs/ARCHITECTURE.md в корне репозитория.
7
+ """
8
+
9
+ __version__ = "0.3.7"
10
+
11
+ __all__ = ["__version__"]
@@ -0,0 +1,163 @@
1
+ """
2
+ Health-пробы стенда: жив ли процесс, отвечает ли HTTP, открыт ли TCP-порт
3
+ (используется как поверхностная проверка живости БД/Redis).
4
+
5
+ Все пробы — быстрые и не требуют сторонних зависимостей (только stdlib:
6
+ ``socket``, ``urllib``). Глубокие проверки (реальный SQL-запрос к БД, PING к
7
+ Redis по протоколу) — сознательно вынесены в TODO под опциональный флаг,
8
+ чтобы базовый health-чек оставался лёгким и не тянул psycopg2/pyodbc/redis-py
9
+ в обязательные зависимости ядра.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import socket
15
+ import urllib.error
16
+ import urllib.request
17
+ from pathlib import Path
18
+ from typing import Optional
19
+
20
+ from standkit.models import ProbeState, Stand, StandStatus
21
+
22
+
23
+ def process_alive(pidfile: Path) -> bool:
24
+ """
25
+ Проверяет, жив ли процесс, чей pid записан в ``pidfile``.
26
+
27
+ Если файла нет или он не читается — считается, что процесс не запущен.
28
+ Импортирует standkit.platform лениво, чтобы health.py можно было
29
+ использовать и для проверки "чужих" процессов без завязки на lifecycle.
30
+ """
31
+ from standkit import platform as _platform # локальный импорт — избегаем цикла
32
+
33
+ if not pidfile.exists():
34
+ return False
35
+ try:
36
+ pid = int(pidfile.read_text(encoding="utf-8").strip())
37
+ except (ValueError, OSError):
38
+ return False
39
+ return _platform.is_alive(pid)
40
+
41
+
42
+ def process_running(
43
+ pidfile: Optional[Path],
44
+ host: Optional[str] = None,
45
+ port: Optional[int] = None,
46
+ ) -> bool:
47
+ """
48
+ Считает процесс стенда "живым", если ЛИБО жив pidfile standkit (стенд
49
+ поднят самим ядром), ЛИБО слушается TCP-порт стенда (стенд поднят извне
50
+ standkit — вручную, через IIS/systemd/сторонний скрипт и т.п.).
51
+
52
+ Это расширение process_alive: тот проверяет только pidfile, этот —
53
+ комбинирует обе приметы живости, потому что реальные стенды часто
54
+ поднимаются не через lifecycle.start().
55
+ """
56
+ if pidfile is not None and process_alive(pidfile):
57
+ return True
58
+ if host and port:
59
+ return tcp_open(host, port)
60
+ return False
61
+
62
+
63
+ def http_ok(url: str, *, timeout: float = 3.0) -> bool:
64
+ """
65
+ Проверяет, отвечает ли HTTP(S)-эндпоинт (любой код ответа < 500 считается
66
+ "живым" — стенд может честно отдавать 401/403 до логина, это не авария).
67
+
68
+ Сетевые ошибки (отказано в соединении, DNS, таймаут) → False, без исключений
69
+ наружу — это намеренно проба, а не операция, которая должна падать.
70
+ """
71
+ try:
72
+ req = urllib.request.Request(url, method="GET")
73
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
74
+ return resp.status < 500
75
+ except urllib.error.HTTPError as exc:
76
+ # Сервер ответил (пусть и ошибкой) — значит, живой.
77
+ return exc.code < 500
78
+ except (urllib.error.URLError, TimeoutError, OSError, ValueError):
79
+ return False
80
+
81
+
82
+ def tcp_open(host: str, port: int, *, timeout: float = 2.0) -> bool:
83
+ """
84
+ Проверяет, открыт ли TCP-порт (используется как поверхностная liveness-проба
85
+ БД/Redis — не подменяет полноценный запрос к сервису).
86
+ """
87
+ if not host or not port:
88
+ return False
89
+ try:
90
+ with socket.create_connection((host, port), timeout=timeout):
91
+ return True
92
+ except (OSError, ValueError):
93
+ return False
94
+
95
+
96
+ def db_deep_check(stand: Stand) -> ProbeState:
97
+ """
98
+ TODO(следующая итерация): полноценная проверка БД (реальный SELECT 1 через
99
+ psycopg2 для postgres / pyodbc для mssql). Требует опциональных
100
+ зависимостей, которые НЕ должны стать обязательными для ядра — включать
101
+ только по явному флагу вызывающей стороны.
102
+
103
+ Пока — заглушка, всегда возвращающая SKIPPED, чтобы вызывающий код мог
104
+ отличить "проверка не выполнялась" от "проверка провалилась".
105
+ """
106
+ return ProbeState.SKIPPED
107
+
108
+
109
+ def redis_deep_check(stand: Stand) -> ProbeState:
110
+ """TODO(следующая итерация): полноценный PING к Redis (redis-py, опциональная зависимость)."""
111
+ return ProbeState.SKIPPED
112
+
113
+
114
+ def check_stand(
115
+ stand: Stand,
116
+ *,
117
+ pidfile: Optional[Path] = None,
118
+ http_path: str = "/",
119
+ deep_db: bool = False,
120
+ deep_redis: bool = False,
121
+ ) -> StandStatus:
122
+ """
123
+ Собирает сводный StandStatus по всем доступным быстрым пробам.
124
+
125
+ ``pidfile`` — если не передан, процесс-проба пропускается (UNKNOWN) —
126
+ вызывающая сторона (lifecycle) знает свой путь к pidfile лучше, чем этот
127
+ модуль по умолчанию.
128
+ """
129
+ status = StandStatus(name=stand.name)
130
+
131
+ if pidfile is not None or (stand.stand_host and stand.stand_port):
132
+ is_up = process_running(pidfile, stand.stand_host, stand.stand_port)
133
+ status.process = ProbeState.OK if is_up else ProbeState.DOWN
134
+ else:
135
+ status.process = ProbeState.UNKNOWN
136
+
137
+ if stand.stand_host and stand.stand_port:
138
+ url = f"http://{stand.stand_host}:{stand.stand_port}{http_path}"
139
+ status.http = ProbeState.OK if http_ok(url) else ProbeState.DOWN
140
+ else:
141
+ status.http = ProbeState.UNKNOWN
142
+
143
+ if stand.db_host and stand.db_port:
144
+ status.db = ProbeState.OK if tcp_open(stand.db_host, stand.db_port) else ProbeState.DOWN
145
+ if deep_db:
146
+ status.db = db_deep_check(stand)
147
+ else:
148
+ status.db = ProbeState.UNKNOWN
149
+
150
+ redis_host = stand.extra.get("redis_host")
151
+ redis_port = stand.extra.get("redis_port")
152
+ if redis_host and redis_port:
153
+ status.redis = ProbeState.OK if tcp_open(redis_host, int(redis_port)) else ProbeState.DOWN
154
+ if deep_redis:
155
+ status.redis = redis_deep_check(stand)
156
+ else:
157
+ status.redis = ProbeState.UNKNOWN
158
+
159
+ # TODO: last_deploy — задел на будущее, источник данных пока не определён
160
+ # (кандидат — метаданные из BPMkit deploy_status/deploy_verify).
161
+ status.last_deploy = ProbeState.UNKNOWN
162
+
163
+ return status