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.
- standkit-0.3.7/LICENSE +21 -0
- standkit-0.3.7/PKG-INFO +155 -0
- standkit-0.3.7/README.md +130 -0
- standkit-0.3.7/pyproject.toml +55 -0
- standkit-0.3.7/setup.cfg +4 -0
- standkit-0.3.7/standkit/__init__.py +11 -0
- standkit-0.3.7/standkit/health.py +163 -0
- standkit-0.3.7/standkit/lifecycle.py +191 -0
- standkit-0.3.7/standkit/logs.py +221 -0
- standkit-0.3.7/standkit/models.py +211 -0
- standkit-0.3.7/standkit/platform.py +174 -0
- standkit-0.3.7/standkit/registry.py +223 -0
- standkit-0.3.7/standkit/secrets.py +144 -0
- standkit-0.3.7/standkit.egg-info/PKG-INFO +155 -0
- standkit-0.3.7/standkit.egg-info/SOURCES.txt +56 -0
- standkit-0.3.7/standkit.egg-info/dependency_links.txt +1 -0
- standkit-0.3.7/standkit.egg-info/entry_points.txt +4 -0
- standkit-0.3.7/standkit.egg-info/requires.txt +9 -0
- standkit-0.3.7/standkit.egg-info/top_level.txt +3 -0
- standkit-0.3.7/standkit_agent/__init__.py +9 -0
- standkit-0.3.7/standkit_agent/__main__.py +192 -0
- standkit-0.3.7/standkit_agent/audit.py +93 -0
- standkit-0.3.7/standkit_agent/security.py +300 -0
- standkit-0.3.7/standkit_agent/server.py +386 -0
- standkit-0.3.7/standkit_hub/__init__.py +13 -0
- standkit-0.3.7/standkit_hub/__main__.py +131 -0
- standkit-0.3.7/standkit_hub/agent_control.py +216 -0
- standkit-0.3.7/standkit_hub/assets/bpmkit-icon.ico +0 -0
- standkit-0.3.7/standkit_hub/assets/icon.png +0 -0
- standkit-0.3.7/standkit_hub/client.py +159 -0
- standkit-0.3.7/standkit_hub/config.py +156 -0
- standkit-0.3.7/standkit_hub/logs_browser.py +174 -0
- standkit-0.3.7/standkit_hub/redis_min.py +326 -0
- standkit-0.3.7/standkit_hub/security.py +146 -0
- standkit-0.3.7/standkit_hub/server.py +881 -0
- standkit-0.3.7/standkit_hub/shortcut.py +264 -0
- standkit-0.3.7/standkit_hub/web/app.js +803 -0
- standkit-0.3.7/standkit_hub/web/bpmkit-logo-dark.svg +5 -0
- standkit-0.3.7/standkit_hub/web/bpmkit-logo.svg +5 -0
- standkit-0.3.7/standkit_hub/web/favicon.png +0 -0
- standkit-0.3.7/standkit_hub/web/favicon.svg +4 -0
- standkit-0.3.7/standkit_hub/web/index.html +267 -0
- standkit-0.3.7/standkit_hub/web/style.css +843 -0
- standkit-0.3.7/tests/test_agent_security.py +303 -0
- standkit-0.3.7/tests/test_agent_server_integration.py +192 -0
- standkit-0.3.7/tests/test_health.py +146 -0
- standkit-0.3.7/tests/test_hub_agent_control.py +192 -0
- standkit-0.3.7/tests/test_hub_config.py +140 -0
- standkit-0.3.7/tests/test_hub_logs_browser.py +238 -0
- standkit-0.3.7/tests/test_hub_redis_min.py +247 -0
- standkit-0.3.7/tests/test_hub_server.py +1002 -0
- standkit-0.3.7/tests/test_hub_shortcut.py +203 -0
- standkit-0.3.7/tests/test_lifecycle.py +126 -0
- standkit-0.3.7/tests/test_logs.py +205 -0
- standkit-0.3.7/tests/test_models.py +73 -0
- standkit-0.3.7/tests/test_registry.py +78 -0
- standkit-0.3.7/tests/test_registry_resolver.py +140 -0
- 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.
|
standkit-0.3.7/PKG-INFO
ADDED
|
@@ -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)
|
|
36
|
+
[](https://www.python.org/)
|
|
37
|
+
[]()
|
|
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
|
+
|  |  |
|
|
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>
|
standkit-0.3.7/README.md
ADDED
|
@@ -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)
|
|
11
|
+
[](https://www.python.org/)
|
|
12
|
+
[]()
|
|
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
|
+
|  |  |
|
|
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"]
|
standkit-0.3.7/setup.cfg
ADDED
|
@@ -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
|