Compare commits

..

52 Commits

Author SHA1 Message Date
9cf01e5385 uv при сборке
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 47s
2026-01-11 13:46:36 +03:00
1b27f592c4 Kubuntu 2026-01-11 13:31:55 +03:00
59ccd6d172 Добавил пометку про ssh-agent на Linux
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 49s
2025-12-29 17:36:39 +03:00
a4e9ebcc17 Цитирование онлайн источников
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 53s
2025-12-24 12:58:42 +03:00
0310f9737c Цитирование по ГОСТу с Biblatex-GOST
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 1m4s
2025-12-24 12:50:35 +03:00
efd8c6b67b исправил ссылку в заметке про почту 2025-12-24 12:20:55 +03:00
2691222b55 uv рулит 2025-12-24 12:20:41 +03:00
974646161d Про swap файл
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 54s
2025-12-05 12:19:42 +03:00
813ee2b2c1 Опечатка в команде добавления алиаса почты
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 48s
2025-11-14 17:54:32 +03:00
00cc795a8e Почта on-premise
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 30s
2025-10-21 19:14:11 +03:00
d46c56d386 Про 80 порт при обновлении ssl 2025-10-19 20:33:46 +03:00
d180f06ab0 Про SSH_PORT gitea
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 24s
2025-10-13 01:08:11 +03:00
c729ca850b Тесты ci
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 24s
2025-10-12 18:49:43 +03:00
4de49b9d0e GItea в Docker + бэкапы
Some checks failed
Build MkDocs / build-and-deploy (push) Failing after 0s
2025-10-12 14:41:59 +03:00
d2a796e67f Шпаргалка по командам 2025-10-11 20:03:12 +03:00
96176d2f0c Скрипт для бэкапов
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 2s
2025-10-09 15:29:45 +03:00
4836eabc2c Мелкие правки
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 1s
2025-10-09 04:25:48 +03:00
63d729aec4 Заметка про сервер майнкрафт
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 2s
2025-10-09 04:07:35 +03:00
04737ff93c Небольшие уточнения по сетапу сервера
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 3s
2025-10-09 00:17:03 +03:00
9d7a9207f6 alt+r в bash 2025-10-09 00:15:42 +03:00
c22af5a786 Отдельная заметка по bash
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 3s
2025-09-27 22:12:48 +03:00
cf8a68b72a rename
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 3s
2025-09-09 10:36:53 +03:00
89a278d0bc перейти к реализации
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 1s
2025-08-18 21:08:34 +03:00
25be7bab30 перейти к определению типа
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 1s
2025-08-18 15:00:30 +03:00
cb5a34ac95 Reveal File in Explorer View hotkey
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 1s
2025-08-18 14:42:53 +03:00
2bd26f9d4d Добавил пример конфиг и proxy_params в заметку про Nginx
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 2s
2025-08-17 10:47:41 +03:00
31b893a85a Очистка места на сервере
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 1s
2025-08-16 22:41:54 +03:00
86506f744a Обновление PSReadLine
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 2s
2025-08-16 18:47:29 +03:00
5877dd00ba Добавил про проверку конфигов и логи nginx 2025-08-15 16:19:31 +03:00
78cda0c83d Алиас для git cherry-pick
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 2s
2025-08-12 10:18:49 +03:00
d490eb9131 Переход к определению
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 3s
2025-08-10 16:16:09 +03:00
d770dd669c Исправил ошибку в requirements
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 3s
2025-07-28 14:47:13 +03:00
2362332d15 Дебаг элементов, исчезающих при смене фокуса
Some checks failed
Build MkDocs / build-and-deploy (push) Failing after 1s
2025-07-28 14:41:29 +03:00
607719e7d9 Вернул плашку Made with MKDocs
Some checks failed
Build MkDocs / build-and-deploy (push) Failing after 1s
2025-07-28 12:56:49 +03:00
bf18fa9a3c Дополнил requirements 2025-07-28 12:55:35 +03:00
00eff22bf5 VS Code Сочетания клавиш для создания и удаления папок
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 3s
2025-07-28 12:52:28 +03:00
dd447678ad Настройки для очистки памяти
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 2s
2025-05-22 12:08:59 +03:00
b066e85b29 Замечание про отключение ufw
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 2s
2025-05-18 22:19:55 +03:00
fb4781f3ae Сохранил пару сочетаний клавиш
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 1s
2025-05-11 22:03:36 +03:00
eada80734a No more Windows Powershell
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 2s
2025-05-10 21:19:04 +03:00
265d6d5ce7 *nix which analog for powershell
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 3s
2025-05-10 20:37:09 +03:00
ec2b6e13b7 Уточнил инструкцию по поиску команд в bash
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 2s
2025-02-24 19:19:17 +03:00
07f4c78355 Поиск по истории команд в bash
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 3s
2025-02-22 00:09:27 +03:00
fbea0bc085 Заметка про SSH-agent
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 3s
2025-02-18 21:59:53 +03:00
fc76f14fca Когда-нибудь потом сделаю нормальное readme
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 2s
2025-02-04 20:03:09 +03:00
f7031b96ea Добавил про переключение между вкладками 2025-02-04 20:02:02 +03:00
19ec4e82f3 Очепятки
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 2s
2025-02-01 17:00:26 +03:00
a9a446d8b7 Вынес настройку сервера в отдельный раздел 2025-02-01 16:58:30 +03:00
7db0de6eda Добавление сайта Nginx 2025-02-01 16:50:48 +03:00
db68076181 Отслеживание SQL запросов django 2025-02-01 16:48:10 +03:00
c0b936f792 Заметка про установку Prometheus и Node Exporter 2025-02-01 16:45:46 +03:00
dd903276f7 Заметка про кастомизацию Gitea
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 2s
2025-01-27 16:06:59 +03:00
23 changed files with 2627 additions and 99 deletions

View File

@@ -8,21 +8,33 @@ on:
jobs: jobs:
build-and-deploy: build-and-deploy:
runs-on: self-hosted runs-on: self-hosted
container:
image: astral/uv:python3.12-bookworm-slim
options: --volume /var/www/knowledge-base:/var/www/knowledge-base
steps: steps:
- name: Clone repository - name: Install git
run: |
apt-get update
apt-get install -y --no-install-recommends git
- name: Checkout repository
run: | run: |
git clone --depth 1 --branch main https://git.tishenko.dev/tish/knowledge-base.git git clone --depth 1 --branch main https://git.tishenko.dev/tish/knowledge-base.git
- name: Sync deps (locked)
run: |
cd knowledge-base
uv sync --frozen
- name: Build site - name: Build site
run: | run: |
cd knowledge-base cd knowledge-base
pip install -r requirements.txt uv run mkdocs build
mkdocs build
- name: Deploy site - name: Deploy site
run: | run: |
# Удаляем только содержимое /var/www/knowledge-base,
# чтобы сохранить права и владельца самой папки
cd knowledge-base cd knowledge-base
rm -rf /var/www/knowledge-base/* # Удаляем старую версию сайта
rm -rf /var/www/knowledge-base/site
# Копируем новую
cp -r site/ /var/www/knowledge-base/site/ cp -r site/ /var/www/knowledge-base/site/

1
.python-version Normal file
View File

@@ -0,0 +1 @@
3.12

View File

@@ -1,3 +1,3 @@
Просто склад моих заметок доступный всем. Просто склад моих заметок, доступный всем.
Можно предлагать правки и дополнения на [GitHub](https://github.com/Arity-T/knowledge-base). Можно предлагать правки и дополнения на [GitHub](https://github.com/Arity-T/knowledge-base).

View File

@@ -13,6 +13,8 @@
`ctrl + shift + p` - главное сочетание клавиш, которое стоит запомнить. `ctrl + shift + p` - главное сочетание клавиш, которое стоит запомнить.
`alt + 1/2/3/...` - переключение между открытыми вкладками в порядке их расположения.
Удалить множественные курсоры можно с помощью `Esc`. Удалить множественные курсоры можно с помощью `Esc`.
Если с помощью `ctrl + d` выделили лишнее, то сочетанием `ctrl + u` можно убрать последнее выделение. Если с помощью `ctrl + d` выделили лишнее, то сочетанием `ctrl + u` можно убрать последнее выделение.
@@ -21,3 +23,60 @@
`alt + z` - крайне полезно при работе с Latex, Markdown и другими "широкими" файлами. `alt + z` - крайне полезно при работе с Latex, Markdown и другими "широкими" файлами.
`ctrl + j` - скрыть/показать нижнюю панель (терминал, логи и т.д.).
`alt + shift + r` - показать файл, выбранный в левой панели с файлами, в проводнике.
`ctrl + shift + .` - breadcrumbs
## Кастомные сочетания клавиш
Сочетания клавиш для быстрого создания папок и файлов через проводник внутри VS Code.
- `a` - создать файл.
- `shift + a` - создать папку.
Быстрый переход к определению и типу функции, метода, переменной, etc. В дополнение к `F12`. Удобно использовать вместе с `alt + ←/→`.
- `alt + d` - перейти к определению.
- `alt + t` - перейти к определению типа.
- `alt + i` - перейти к реализации (актуально для Java).
Обычно я отключаю `explorer.autoReveal` в настройках, чтобы боковая панель с проводником не изменялась, когда я переключаюсь по файлам. Если мне всё-таки требуется выделить открытый файл в `Explorer View`, то я использую дополнительное сочетание клавиш.
- `alt + shift + r` - показать открытый файл в `Explorer View`.
```json
[
{
"key": "a",
"command": "explorer.newFile",
"when": "filesExplorerFocus && !inputFocus"
},
{
"key": "shift+a",
"command": "explorer.newFolder",
"when": "filesExplorerFocus && !inputFocus"
},
{
"key": "alt+d",
"command": "editor.action.revealDefinition",
"when": "editorHasDefinitionProvider && editorTextFocus"
},
{
"key": "alt+t",
"command": "editor.action.goToTypeDefinition",
"when": "editorHasTypeDefinitionProvider && editorTextFocus"
},
{
"key": "alt+i",
"command": "editor.action.goToImplementation",
"when": "editorHasImplementationProvider && editorTextFocus"
},
{
"key": "shift+alt+r",
"command": "workbench.files.action.showActiveFileInExplorer",
"when": "editorTextFocus"
}
]
```

View File

@@ -1,6 +1,8 @@
# LaTeX в VS Code # LaTeX
## Подготовка ## LaTeX в VS Code
### Подготовка
Вся работа с LaTeX в VS Code завязана на расширении [LaTeX Workshop](https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop). Вот [документация](https://github.com/James-Yu/LaTeX-Workshop/wiki/Install) по установке. Вкратце: Вся работа с LaTeX в VS Code завязана на расширении [LaTeX Workshop](https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop). Вот [документация](https://github.com/James-Yu/LaTeX-Workshop/wiki/Install) по установке. Вкратце:
@@ -10,12 +12,12 @@
После установки можно открыть любой `.tex` файл и попробовать скомпилировать его (`ctrl + alt + b`), а затем открыть предпросмотр (`ctrl + alt + v`). При сохранении файл будет автоматически компилироваться, а предпросмотр обновляться. После установки можно открыть любой `.tex` файл и попробовать скомпилировать его (`ctrl + alt + b`), а затем открыть предпросмотр (`ctrl + alt + v`). При сохранении файл будет автоматически компилироваться, а предпросмотр обновляться.
## Сниппеты ### Сниппеты
### Создание #### Создание
В LaTeX очень много повторяющихся конструкций, грех не использовать сниппеты. Нажимаем `ctrl + shift + p` (или `f1`), ищем пункт `Snippets: Configure Snippets`, затем ищем `latex`. Автоматически будет создан файл `latex.json`, в который можно добавлять сниппеты. В LaTeX очень много повторяющихся конструкций, грех не использовать сниппеты. Нажимаем `ctrl + shift + p` (или `f1`), ищем пункт `Snippets: Configure Snippets`, затем ищем `latex`. Автоматически будет создан файл `latex.json`, в который можно добавлять сниппеты.
### Использование #### Использование
Примеры [сниппетов](attachments/latex.json), которые я использую. Примеры [сниппетов](attachments/latex.json), которые я использую.
@@ -24,7 +26,7 @@
- `\tablex` и `\table` - для вставки таблиц. - `\tablex` и `\table` - для вставки таблиц.
- `\pdf` - для вставки PDF файлов. - `\pdf` - для вставки PDF файлов.
## Примечание ### Примечание
- Работать с текстом в VS Code намного удобнее, в первую очередь за счёт [горячих клавиш](hotkeys.md). В особенности полезно сочетание `alt + z`, с помощью которого включается автоматический перенос строк. - Работать с текстом в VS Code намного удобнее, в первую очередь за счёт [горячих клавиш](hotkeys.md). В особенности полезно сочетание `alt + z`, с помощью которого включается автоматический перенос строк.
- При написании отчётов, которые обычно обрастают множеством правок, полезно использовать `Git`. `MikTex` создаёт много временных файлов, которые в `Git` добавлять не нужно, поэтому ниже представлена заготовка для `.gitignore`. - При написании отчётов, которые обычно обрастают множеством правок, полезно использовать `Git`. `MikTex` создаёт много временных файлов, которые в `Git` добавлять не нужно, поэтому ниже представлена заготовка для `.gitignore`.
@@ -37,3 +39,68 @@
!img !img
!img/* !img/*
``` ```
## Цитирование по ГОСТу с Biblatex-GOST
Пакет [Biblatex-GOST](https://ctan.org/pkg/biblatex-gost) автоматически формирует список литературы по ГОСТу и при этом, позволяет использовать привычное для LaTeX цитирование с помощью команды `\cite`. У пакета есть [документация](https://mirror.macomnet.net/pub/CTAN/macros/latex/contrib/biblatex-contrib/biblatex-gost/doc/biblatex-gost.pdf) и [GitHub репозиторий](https://github.com/odomanov/biblatex-gost/). MikTex автоматически установит пакет при первом использовании.
=== "LaTeX"
```latex
\documentclass[a4paper,12pt]{article}
\usepackage[T2A]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[russian]{babel}
% Рекомендуется для biblatex (кавычки/локализация цитат и т.п.)
\usepackage{csquotes}
% ГОСТ-стили для biblatex
\usepackage[
backend=biber,
bibstyle=gost-numeric, % ссылки вида: [1]
citestyle=gost-numeric,
sorting=none % порядок в списке = по первому цитированию
]{biblatex}
% Все источники хранятся в отдельном файле
\addbibresource{refs.bib}
\begin{document}
Цитируем как обычно, например: \cite{whisper}.
Также при цитировании можно указывать страницу
или раздел, например: \cite[с. 10]{whisper}.
Сайты и статьи в интернете можно цитировать так: \cite{overleaf}.
% Выводим список литературы
\printbibliography
\end{document}
```
=== "refs.bib"
Код `bibtex` обычно генерируется автоматически на сайтах с научными статьями. Например, на Arxiv по нажатию кнопки `Export BibTeX Citation`, на ScienceDirect - `Cite -> Export citation to BibTeX`.
```bibtex
@misc{whisper,
title={Robust Speech Recognition via Large-Scale Weak Supervision},
author={Alec Radford and Jong Wook Kim and Tao Xu and
Greg Brockman and Christine McLeavey and Ilya Sutskever},
year={2022},
eprint={2212.04356},
archivePrefix={arXiv},
primaryClass={eess.AS},
url={https://arxiv.org/abs/2212.04356},
}
@online{overleaf,
title = {Bibliography management with biblatex},
author = {{Overleaf}},
url = {https://www.overleaf.com/learn/latex/Bibliography_management_with_biblatex},
urldate = {2025-12-24},
}
```

View File

@@ -8,6 +8,7 @@ git config --global alias.ci commit
git config --global alias.cim "commit -m" git config --global alias.cim "commit -m"
git config --global alias.st status git config --global alias.st status
git config --global alias.br branch git config --global alias.br branch
git config --global alias.cp cherry-pick
git config --global alias.hist "log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short" git config --global alias.hist "log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short"
git config --global alias.histt "log --pretty=format:'%h %cd | %s%d [%an]' --graph --date=iso" git config --global alias.histt "log --pretty=format:'%h %cd | %s%d [%an]' --graph --date=iso"
git config --global alias.ad "add -A" git config --global alias.ad "add -A"

5
docs/misc/devtools.md Normal file
View File

@@ -0,0 +1,5 @@
# Заметка о Chrome DevTools
## Дебаг элементов, исчезающих при смене фокуса
В DevTools нажимаем сочетание клавиш `ctrl + shift + p` и ищем раздел `Show Rendering`. Устанавливаем флажок `Emulate a focused page`. Теперь элементы, исчезающие при смене фокуса (датапикеры, модальные окна, etc.), не будут исчезать при переключении на DevTools, а значит их код можно спокойно инспектировать.

View File

@@ -1,86 +1,451 @@
# Кастомизация Gitea # Gitea
Шпаргалки актуальны для Gitea 1.24.
## Установка с Docker
Эта заметка лишь дополнение к [документации](https://docs.gitea.com/installation/install-with-docker#basics).
=== "Терминал"
```sh
# Создаём служебного пользователя
sudo useradd --create-home --shell /bin/bash --system gitea
# Даём пользователю права использовать Docker
# Строго говоря это необязательно для разворачивания Gitea,
# но скорее всего и ранеры будут запускаться от этого пользователя,
# так что ему в любом случае потребуются права на Docker
sudo usermod -aG docker gitea
# Переключаемся на пользователя gitea
sudo su - gitea
# Узнаём его uid и gid
id
# Создаём docker-compose.yml по примеру
# Указываем переменные USER, USER_UID и USER_GID и порты
nano docker-compose.yml
# Создаём директорию для данных Gitea из-под пользователя gitea,
# иначе Docker сам создаст её из-под root
# см. секцию "volumes" в docker-compose.yml
mkdir data
# Запускаем Gitea
docker compose up -d
```
=== "docker-compose.yml"
```yaml
networks:
gitea:
external: false
services:
server:
image: docker.gitea.com/gitea:1.24.6
container_name: gitea
environment:
- USER=<user>
- USER_UID=<uid>
- USER_GID=<gid>
restart: always
networks:
- gitea
volumes:
- ./data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "28500:3000"
- "28522:22"
```
Теперь можно перейти по адресу `http://<IPv4>:28500` и завершить установку Gitea.
Подключиться к контейнеру можно командой.
```sh ```sh
sudo su - git docker exec --user gitea -it gitea bash
cd /var/lib/gitea/custom
# Уже внутри контейнера можно запускать бинарник gitea
/usr/local/bin/gitea help
``` ```
## Создание бэкапа
Ссылка на [документацию](https://docs.gitea.com/administration/backup-and-restore).
### Gitea установлена из бинарника
```sh ```sh
mkdir -p /var/lib/gitea/custom/public/css # gitea - это пользователь под которым запущен Gitea, часто это просто git
mkdir -p /var/lib/gitea/custom/templates/custom sudo su - gitea
mkdir gitea-backup
cd gitea-backup
# Нужно указать актуальный путь к конфигу
/usr/local/bin/gitea dump -c /etc/gitea/app.ini
# Если планируется восстановление с другой базой данных,
# то нужно указать параметр --database <sqlite3|mysql|postgres>
/usr/local/bin/gitea dump --database sqlite3 -c /etc/gitea/app.ini
``` ```
### Gitea установлена с Docker
Предполагается, что Gitea развёрнута с помощью Docker Compose как описано в инструкции [выше](#установка-с-docker).
Подключаемся к контейнеру.
```sh ```sh
sudo nano /var/lib/gitea/custom/templates/custom docker exec --user gitea -it gitea bash
``` ```
```html Внутри контейнера выполняем.
<link rel="stylesheet" href="/custom/css/custom.css">
```
```sh ```sh
sudo -u git mkdir css # Бэкап нужно создать в директории /data, потому что она прокинута на хост
sudo -u git nano css/custom.css cd /data
# (Опционально) Создаём директорию для бэкапов
mkdir backups
cd backups
# Нужно указать актуальный путь к конфигу
/usr/local/bin/gitea dump -c /data/gitea/conf/app.ini
# Если планируется восстановление с другой базой данных,
# то нужно указать параметр --database <sqlite3|mysql|postgres>
/usr/local/bin/gitea dump --database sqlite3 -c /data/gitea/conf/app.ini
``` ```
То же самое в одну команду.
```sh
docker exec -u gitea -w /data gitea /usr/local/bin/gitea dump -c /data/gitea/conf/app.ini
```
### Перенос бэкапа на другую машину
Перенести бэкап на другую машину можно, например, так:
```sh
# Запускаем HTTP сервер (с scp загрузка больших файлов займёт много времени)
python3 -m http.server 8080
# На другой машине скачиваем бэкап
curl -O http://<IP>:8080/gitea-dump-1760203345.zip
```
## Восстановление из бэкапа в Docker
Предполагается, что Gitea разворачивается из [бэкапа](#создание-бэкапа) с помощью Docker Compose как описано в инструкции [выше](#установка-с-docker). В [документации](https://docs.gitea.com/1.24/administration/backup-and-restore#using-docker-restore) есть соответствующая инструкция, однако она не полная и содержит ошибки.
```sh
# Если создавали пользователя по инструкции выше,
# то команды выполняем от него
sudo su - gitea
# Распаковываем бэкап
unzip -q gitea-dump-*.zip -d dump
# Создаём директорию для данных Gitea
mkdir data
# Копируем данные
cp -r dump/data/ data/gitea/
# Копируем репозитории
mkdir data/git/
cp -r dump/repos/ data/git/repositories/
# Копируем кастомные стили и шаблоны
cp -r dump/custom/. data/gitea/
# Если Gitea в Docker будет работать с SQLite,
# то восстановить базу данных можно так.
# Команды для других баз данныех есть в документации
sqlite3 data/gitea/gitea.db < dump/gitea-db.sql
# Копируем конфиг
mkdir data/gitea/conf
cp dump/app.ini data/gitea/conf/app.ini
```
Если до этого Gitea была запущена не через Docker, то нужно отредактировать конфиг.
=== "Терминал"
```sh
vim data/gitea/conf/app.ini
```
=== "`app.ini` для Docker"
Это не полноценный конфиг, а лишь часть настроек для запуска Gitea Docker.
Подразумевается, что конфиг был перенесён из бэкапа.
`SSH_PORT` нужно указать тот же, что и в `docker-compose.yml`.
```ini
RUN_USER = gitea
WORK_PATH = /data/gitea
[server]
LOCAL_ROOT_URL = http://localhost:3000
APP_DATA_PATH = /data/gitea
DOMAIN = localhost
SSH_DOMAIN = localhost
HTTP_PORT = 3000
SSH_PORT = 28522
[repository]
ROOT = /data/git/repositories
[database]
PATH = /data/gitea/gitea.db
DB_TYPE = sqlite3
HOST = localhost:3306
NAME = gitea
USER = root
PASSWD =
LOG_SQL = false
[lfs]
PATH = /data/git/lfs
[log]
ROOT_PATH = /data/gitea/log
```
Запускаем Gitea, скорее всего она начнёт падать с ошибкой `permission denied`, а Docker будет пытаться её перезапустить. При первом запуске Gitea создаёт директории для ssh ключей, но по какой-то причине они создаются из-под `root`, а не из-под пользователя `gitea`.
```sh
# Специально без -d, ждём когда в логах повалятся ошибки и нажимаем Ctrl+C
docker compose up
```
Теперь нужно из-под `root` или с помощью `sudo` указать нужные права.
```sh
# Выходим из пользователя gitea (Ctrl + D или exit)
# и выполняем команду с root правами
sudo chown -R gitea:gitea ~gitea/data
```
Снова запускаем Gitea.
```sh
sudo su - gitea
docker compose up -d
```
Если всё работает корректно, то файлы бэкапа можно удалить.
```sh
rm -rf dump gitea-dump-*.zip
```
## Кастомизация Gitea
Во всех командах подразумевается, что Gitea [установлена из бинарника](https://docs.gitea.com/installation/install-from-binary) и [запускается как `systemd` сервис](https://docs.gitea.com/installation/linux-service).
В документации есть страница, посвящённая [кастомизации Gitea](https://docs.gitea.com/administration/customizing-gitea).
### Свой `css`
Добавляем ссылку на свой файл со стилями.
=== "Терминал"
```sh
# Путь по умолчанию
export GITEA_CUSTOM=/var/lib/gitea/custom
sudo -u git mkdir -p $GITEA_CUSTOM/templates/custom
sudo -u git nano $GITEA_CUSTOM/templates/custom/header.tmpl
```
=== "header.tmpl"
```html
<link rel="stylesheet" href="/assets/css/custom.css">
```
Создаём файл со стилями.
=== "Терминал"
```sh
sudo -u git mkdir -p $GITEA_CUSTOM/public/assets/css
sudo -u git nano $GITEA_CUSTOM/public/assets/css/custom.css
```
=== "Пример custom.css"
```css
/* Стили для git.tishenko.dev */
* {
transition: all 0.125s;
}
/* Список переменных и их значения по умолчанию
*
* Для светлой темы
* https://github.com/go-gitea/gitea/blob/main/web_src/css/themes/theme-gitea-light.css
*
* Для тёмной темы
* https://github.com/go-gitea/gitea/blob/main/web_src/css/themes/theme-gitea-dark.css
*/
/* Переопределения переменных для светлой и тёмной темы одновременно */
:root {
/* Основной цвет */
--color-primary: #6674c4;
--color-primary-contrast: #ffffff;
/* https://maketintsandshades.com/#4051B5 */
--color-primary-dark-1: #7985cb;
--color-primary-dark-2: #8c97d3;
--color-primary-dark-3: #a0a8da;
--color-primary-dark-4: #b3b9e1;
--color-primary-dark-5: #c6cbe9;
--color-primary-dark-6: #d9dcf0;
--color-primary-dark-7: #eceef8;
--color-primary-light-1: #5362bc;
--color-primary-light-2: #4051b5;
--color-primary-light-3: #3a49a3;
--color-primary-light-4: #334191;
--color-primary-light-5: #2d397f;
--color-primary-light-6: #26316d;
--color-primary-light-7: #20295b;
--color-primary-alpha-10: #6674c419;
--color-primary-alpha-20: #6674c433;
--color-primary-alpha-30: #6674c44b;
--color-primary-alpha-40: #6674c466;
--color-primary-alpha-50: #6674c480;
--color-primary-alpha-60: #6674c499;
--color-primary-alpha-70: #6674c4b3;
--color-primary-alpha-80: #6674c4cc;
--color-primary-alpha-90: #6674c4e1;
}
/* Переопределения переменных для светлой темы */
@media (prefers-color-scheme: light) {
:root {}
#navbar-logo {
padding: 5px !important;
background: #14151A;
}
#navbar-logo img {
width: 27px !important;
height: 27px !important;
}
#navbar-logo:hover {
background: #14151A !important;
}
}
/* Переопределения переменных для тёмной темы */
@media (prefers-color-scheme: dark) {
:root {
/* Шапка */
--color-nav-bg: #14151A;
--color-secondary-nav-bg: #14151A;
--color-nav-text: #BEC1C6;
--color-nav-hover-bg: #272A35;
/* Тёмно-серый фон основной */
--color-body: #1E2129;
--color-input-background: #14151A;
--color-menu: #14151A;
--color-card: #14151A;
--color-button: #14151A;
}
}
```
Перезапускаем Gitea.
```sh ```sh
sudo systemctl restart gitea sudo systemctl restart gitea
``` ```
После изменения стилей, страницу в браузере нужно обновить с помощью `ctrl + f5`.
### Настройка `app.ini`
Перечень всех возможных настроек представлен в [документации](https://docs.gitea.com/administration/config-cheat-sheet).
=== "Терминал"
```sh
sudo nano /etc/gitea/app.ini
```
=== "Пример параметров app.ini"
```ini
[server]
LANDING_PAGE = explore
[other]
SHOW_FOOTER_VERSION = false
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false
SHOW_FOOTER_POWERED_BY = false
ENABLE_FEED = false
[i18n]
LANGS = en-US,ru-RU
NAMES = English,Русский
[repository]
DISABLE_STARS = true
[cron]
ENABLED = true
[ui.meta]
AUTHOR = Artem Tishenko: Personal Git Repository Hub
DESCRIPTION = A personal hub for managing Git repositories by Artem Tishenko.
KEYWORDS = Artem Tishenko, Artyom Tishchenko, Git, self-hosted, personal projects, repositories, Gitea
```
Перезапускаем Gitea.
```sh ```sh
sudo nano /etc/gitea/app.ini sudo systemctl restart gitea
```
https://docs.gitea.com/administration/customizing-gitea
https://docs.gitea.com/administration/config-cheat-sheet
```ini title="app.ini"
[server]
LANDING_PAGE = explore
[other]
SHOW_FOOTER_VERSION = false
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false
SHOW_FOOTER_POWERED_BY = false
ENABLE_FEED = false
[i18n]
LANGS = en-US,ru-RU
NAMES = English,Русский
[repository]
DISABLE_STARS = true
[ui.meta]
AUTHOR = Artem Tishenko: Personal Git Repository Hub
DESCRIPTION = A personal hub for managing Git repositories by Artem Tishenko.
KEYWORDS = Artem Tishenko, Artyom Tishchenko, Git, self-hosted, personal projects, repositories, Gitea
``` ```
### Изменение шаблонов страниц
Ищем шаблон для нужной версии в [репозитории Gitea](https://github.com/go-gitea/gitea/tree/main/templates), загружаем с помощью `wget` по такому же пути в `$GITEA_CUSTOM/templates` и редактируем.
Так, например, можно убрать пункт "Помощь" с ссылкой на `https://docs.gitea.com` из основного меню.
```sh ```sh
mkdir -p /var/lib/gitea/custom/templates/base # Путь по умолчанию
cd /var/lib/gitea/custom/templates/base export GITEA_CUSTOM=/var/lib/gitea/custom
# перейти туда
wget https://raw.githubusercontent.com/go-gitea/gitea/refs/tags/v1.22.3/templates/base/footer_content.tmpl
# remove help - https://docs.gitea.com # gitea --version
# remove explore - explore.repos export GITEA_VERSION=v1.22.3
# remove sign_in (just visit /user/login)
wget https://github.com/go-gitea/gitea/raw/refs/tags/v1.22.3/templates/base/head_navbar.tmpl
sudo -u git mkdir -p $GITEA_CUSTOM/templates/base
mkdir -p /var/lib/gitea/custom/templates/repo wget -P $GITEA_CUSTOM/templates/base https://raw.githubusercontent.com/go-gitea/gitea/refs/tags/$GITEA_VERSION/templates/base/head_navbar.tmpl
cd /var/lib/gitea/custom/templates/repo sudo -u git nano $GITEA_CUSTOM/templates/base/head_navbar.tmpl
# remove packages
# remove wiki
# remove repo.activity
# remove repo.issues
# remove repo.pulls
# remove watch, fork
wget https://raw.githubusercontent.com/go-gitea/gitea/refs/tags/v1.22.3/templates/repo/header.tmpl
``` ```
Перезапускаем Gitea.
```sh ```sh
# <a class="item" href="https://kb.tishenko.dev/" target="_blank">Knowledge base</a> sudo systemctl restart gitea
/var/lib/gitea/custom/templates/custom/extra_links.tmpl
``` ```

246
docs/misc/kubuntu.md Normal file
View File

@@ -0,0 +1,246 @@
# Kubuntu
## Внешний вид KDE Plasma
Настройки актуальны для KDE plasma 6.4.5.
```sh
plasmashell --version
```
Во все пункты настроек можно перейти с помощью KRunner (`alt + space`).
Прозрачность для Application Launcher и Context Menu - `Application Style -> Breeze -> Configure Style... (иконка карандаша) -> Transparancy (~60%)`, `Desktop Effects -> Blur (поставить галочку) -> Configure... (иконка шестерёнки) -> Blur strength ~30% Noise strength = 0`, `ПКМ по панели задач -> Show Panel Configuration -> Opacity -> Translucent`.
Эффекты при переносе окон - `Desktop Effects -> Wobbly Windows`.
Кнопки в верхней панели окон - `Window Decorations -> More Actions (три точки) -> Configure Titlebar Buttons`.
Запускать чистую рабочую сессию без перезапуска незакрытых приложений - `Desktop Session -> Start with an empty session`.
Убрать индикатор проигрывания звука у приложений в панели задач - `ПКМ по панели задач -> Icons-Only Task Manager Settings -> Appearance -> General -> Show an indicator when a task is playing audio`.
Убрать уведомления о переключении раскладки в центре экрана - `Keyboard -> Configure Switching... -> Show ODS popup on layout change`.
Убрать звуки при изменении громкости - `Sound -> Configure Volume controls... -> Play audio feedback for changes to`.
### Системный шрифт
Скачиваем шрифт, например, [Inter](https://fonts.google.com/specimen/Inter), устанавливаем, затем в найстройках `Fonts -> везде, кроме Fixed Width, меняем на Inter`.
### Системные иконки
Скачиваем архив с набором иконок, например, [Tela](https://store.kde.org/p/1279924/), затем в настройках `Icons -> Install from file -> Выбираем скачанный архив`. Теперь в этом же разделе настроект можно выбрать набор иконок Tela.
### Быстрое переключение между тёмной и светлой темой
Чтобы создавать свои темы, нужно установить пакет `plasma-sdk`.
```sh
sudo apt install plasma-sdk
```
Настраиваем тёмную или светлую тему, затем открываем `Plasma Global Theme Explorer -> New Theme... -> во всех полях указываем MyDark или MyLight`. Повторяем для обеих тем. Теперь переключаться между ними можно с помощью команды.
```sh
plasma-apply-lookandfeel -a MyDark
plasma-apply-lookandfeel -a MyLight
```
Можно настроить сочетание клавиш для быстрого переключения между темами. Для этого нужно создать простой скрипт.
=== "Терминал"
```sh
nano ~/kde-scripts/toggle-theme.sh
chmod +x ~/kde-scripts/toggle-theme.sh
```
=== "toggle-theme.sh"
```sh
#!/bin/bash
CURRENT=$(grep '^LookAndFeelPackage=' ~/.config/kdeglobals | cut -d= -f2)
if [[ "$CURRENT" == "MyDark" ]]; then
plasma-apply-lookandfeel -a MyLight
notify-send "Theme switched" "☀ Light mode enabled"
else
plasma-apply-lookandfeel -a MyDark
notify-send "Theme switched" "🌙 Dark mode enabled"
fi
```
Затем назначить сочетание клавиш для его исполнения в `Shortcuts -> Add New -> Command or Script...` и указать путь к скрипту и сочетание клавиш, например, `meta (win) + alt + t`.
### Настройки Dolphin
В Dolphin можно добавить кнопку "Вверх", `ПКМ по панельке с кнопками -> Configure Toolbars... -> перенести действие Up в правую панель`.
Добавить панель с показом свободного дискового пространства в правом нижнем углу можно через `Application Menu (иконка бургер) -> Settings -> Configure Dolphin -> Status & Location Bars -> Status Bar` поставить галочку `Full Width`. Тут же можно поставить галочку в `Location Bar -> Show full path inside location bar`.
В Dolphin можно переопределять сочетания клавиш для любых действий через меню `Settings -> Configure Keyboard Shortcuts...`. Например, можно переопределить создание папок на `Shift + a`, а создание файлов на `a`.
В контекстное меню Dolphin (открывается при нажатии ПКМ) можно добавлять новые пункты. Например, `Open With Cursor` для открытия папок в Cursor IDE. Для этого [нужно создать `.desktop` файл в `~/.local/share/kio/servicemenus/`](https://develop.kde.org/docs/apps/dolphin/service-menus/).
=== "Терминал"
```sh
nano ~/.local/share/kio/servicemenus/open-in-cursor.desktop
chmod +x ~/.local/share/kio/servicemenus/open-in-cursor.desktop
```
=== "open-in-cursor.desktop"
```ini
[Desktop Entry]
Type=Service
MimeType=inode/directory;
Actions=OpenInCursor;
[Desktop Action OpenInCursor]
Name=Open in Cursor
Icon=cursor
Exec=cursor "%f"
```
Открывать архивы как папки - `Application Menu (иконка бургер) -> Settings -> Configure Dolphin -> View -> General -> Open archives as folder`.
### Настройки Konsole
Прозрачный фон терминала - `Application Menu (иконка бургер) -> Settings -> Edit current profile -> Appearance -> Edit -> ставим галочку на Blur Background и меняем Background Color Transparancy (~7%)`.
Перед установкой какого-нибудь Oh My Zsh нужно установить шрифты для терминала - `Application Menu (иконка бургер) -> Settings -> Edit current profile -> Appearance -> Choose Font`. Шрифты предварительно нужно скачать и установить.
Можно переопределить `ctrl + v` для вставки текста в терминал - `Application Menu (иконка бургер) -> Settings -> Configure Keyboard Shortcuts...`. Там можно добавить альтернативное сочетание клавиш для действия `paste`.
В настройках VS Code тоже можно переопределить `ctrl + v` для вставки текста в терминал.
```json
[
{
"key": "ctrl+v",
"command": "workbench.action.terminal.paste",
"when": "terminalFocus"
}
]
```
### Дополнительные действия при нажатии ПКМ на иконки в панели задач
Например, можно добавить кнопку для быстрого открытия проекта в VS Code или Cursor.
=== "Терминал"
```sh
cp /usr/share/applications/cursor.desktop ~/.local/share/applications/
nano ~/.local/share/applications/cursor.desktop
```
=== "Пример cursor.desktop"
Действие нужно добавить в поле `Actions`, а затем описать его в отдельной секции.
```ini
[Desktop Entry]
Name=Cursor
Comment=The AI Code Editor.
GenericName=Text Editor
Exec=/usr/share/cursor/cursor %F
Icon=co.anysphere.cursor
Type=Application
StartupNotify=false
StartupWMClass=Cursor
Categories=TextEditor;Development;IDE;
MimeType=application/x-cursor-workspace;
Actions=knowledge-base;new-empty-window;edit-actions
Keywords=cursor;
[Desktop Action new-empty-window]
Name=New Empty Window
Name[ru]=Новое пустое окно
Exec=/usr/share/cursor/cursor --new-window %F
Icon=co.anysphere.cursor
[Desktop Action knowledge-base]
Name=Open knowledge-base
Exec=cursor --new-window ~/Main/knowledge-base
Icon=folder
[Desktop Action edit-actions]
Name=Edit Task Manager Actions
Exec=cursor ~/.local/share/applications/cursor.desktop
Icon=edit
```
## Настройка Oh My Zsh
Установить Zsh и сделать его shell по умолчанию.
```sh
sudo apt install zsh
chsh -s $(which zsh)
```
В настройках терминала нужно выбрать Zsh как основной shell. Также установить любой [Nerd Font](https://www.nerdfonts.com/) (например, Mononoki Nerd Font).
```sh
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
```
Одна из самых популярных тем для Zsh - [powerlevel10k](https://github.com/romkatv/powerlevel10k). После [установки](https://github.com/romkatv/powerlevel10k?tab=readme-ov-file#oh-my-zsh) нужно выполнить команду для интерактивной настройки.
```sh
p10k configure
```
В VS Code также нужно установить Zsh как основной shell и задать правильные шрифты.
```json
{
"terminal.integrated.defaultProfile.linux": "zsh",
"terminal.integrated.profiles.linux": {
"zsh": {
"path": "/bin/zsh"
}
},
"terminal.integrated.fontFamily": "Mononoki Nerd Font Mono",
"terminal.integrated.fontSize": 15
}
```
В Oh My Zsh можно установливать плагины на любой вкус и цвет, например: [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/INSTALL.md), [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions).
## Горячие клавиши на русской раскладке в VS Code
Без этой настройки на русской раскладке могут не работать некоторые горячие клавиши в VS Code.
```json
{
"keyboard.dispatch": "keyCode"
}
```
## Автоматическая отчистка старых файлов в Downloads
=== "Терминал"
```sh
sudo nano /etc/tmpfiles.d/downloads.conf
# Можно сразу применить и удалить старые файлы
sudo systemd-tmpfiles --clean
```
=== "downloads.conf"
```ini
# Type Path Mode UID GID Age Argument
d /home/USERNAME/Downloads 0755 USER USER 7d
```

View File

@@ -0,0 +1,282 @@
# Minecraft Server
[docker-minecraft-server](https://github.com/itzg/docker-minecraft-server) - самый простой и удобный способ запуска своего Minecraft сервера. У проекта есть хорошая [документация](https://docker-minecraft-server.readthedocs.io/en/latest/), но всё же есть некоторые нюансы, которые стоит записать.
## Запуск сервера
1. Устанавливаем [`docker`](https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository). Не забываем про [post-install steps](https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user).
2. Создаём папку для сервера.
```sh
mkdir minecraft-server
cd minecraft-server
```
3. Создаём файл `docker-compose.yml`.
=== "Терминал"
```sh
nano docker-compose.yml
```
=== "docker-compose.yml"
```yaml
# https://docker-minecraft-server.readthedocs.io/en/latest/
services:
mc:
image: itzg/minecraft-server
tty: true
stdin_open: true
ports:
# Порт 25565 является стандартным, все клиенты по умолчанию
# подключаются к нему, если порт не задан явно
- "25565:25565"
environment:
# Полный список переменных можно найти в документации
# https://docker-minecraft-server.readthedocs.io/en/latest/variables/
EULA: "TRUE"
# Не забудьте указать нужную версию Minecraft
VERSION: 1.19.4
# Разрешаем подключаться без лицензии Minecraft
ONLINE_MODE: false
# Указываем название игрового мира (будет храниться в ./data/<LEVEL>)
# Чтобы изменить мир, достаточно изменить значение этой переменной
# и перезапустить контейнер
LEVEL: MyWorld
# Описание сервера
MOTD: |
Tish's Minecraft Server on %VERSION%
# Иконка сервера
# Надо разместить файл icon.png в ./data/
# ICON: /data/icon.png
# OVERRIDE_ICON: true
# Автоотключение пустого сервера
# ENABLE_AUTOSTOP: TRUE
# AUTOSTOP_TIMEOUT_EST: 300
# AUTOSTOP_TIMEOUT_INIT: 600
volumes:
- ./data:/data
```
4. Запускаем сервер.
```sh
docker compose up -d
```
## Остановка сервера
```sh
docker compose stop
```
## Консоль сервера
`ctrl + p ctrl + q` - отключиться от консоли.
Если просто нажать `ctrl + c`, то сервер остановится.
```sh
docker compose attach mc
```
### Шпаргалка по командам
1. `/list` — список игроков.
2. `/op <nickname>` — сделать игрока оператором.
3. `/deop <nickname>` — удалить из списка операторов.
4. Отдельной команды для просмотра списка операторов нет, но он хранится в файле `data/ops.json`.
5. `/whitelist <on|off>` — включить или выключить белый список игроков.
6. `/whitelist list` — вывести белый список игроков.
7. `/whitelist add <nickname>` — добавить игрока в белый список.
8. `/whitelist remove <nickname>` — удалить игрока из белого списка.
9. `/gamemode <0|1|2|3>` — изменить режим игры (0 — выживание, 1 — креатив, 2 — приключение, 3 — наблюдатель).
10. `/time set <day|night|<число>>` — установить время суток.
11. `/weather <clear|rain|thunder>` — изменить погоду.
12. `/tp <nickname1> <nickname2>` — телепортировать первого игрока ко второму.
13. `/difficulty <peaceful|easy|normal|hard>` — изменить уровень сложности сервера.
## Домен для сервера
Если порт стандартный (`25565`), то при подключении можно просто указать домен в A-записи которого указан IP-адрес сервера. Однако если порт нестандартный или хочется сделать несколько доменных имён для одного сервера, то можно использовать SRV-записи.
Например, если я создам домен `minecraft.tishenko.dev` и запущу сервер на порте `12345`, то чтобы подключаться к серверу без указания порта, мне нужно будет добавить следующую SRV-запись для `minecraft.tishenko.dev`:
```dns
_minecraft._tcp.minecraft.tishenko.dev. 3600 IN SRV 0 5 12345 minecraft.tishenko.dev.
```
Обычно ДНС провайдеры предоставляют UI для создания записей, в нём скорее всего будут следующие поля:
- Поддомен: `minecraft`
- Сервис: `minecraft` (SpaceWeb, например, сам подставляет `_`)
- Протокол: `tcp`
- TTL: `3600`
- Приоритет: `0`
- Вес: `5`
- Порт: `12345`
- Целевой домен: `minecraft.tishenko.dev.` (точка в конце обязательна)
Целевой домен не обязательно должен совпадать с поддоменом. Можно сделать подключение к серверу по домену `tishenko.dev` указан следующую SRV-запись для домена `tishenko.dev`:
```dns
_minecraft._tcp.tishenko.dev. 3600 IN SRV 0 5 12345 minecraft.tishenko.dev.
```
Домен `minecraft.tishenko.dev` в своей A-записи должен указывать на IP-адрес сервера, но при этом совершенно неважно какая A-запись будет у `tishenko.dev`.
DNS-записи распространяются не мгновенно, но обычно это занимает 15-30 минут. Отслеживать распространение записей можно с помощью сайта [DNS Checker](https://dnschecker.org/#SRV/_minecraft._tcp.tishenko.dev).
!!! note "Клиенты Minecraft читают SRV-запись"
С помощью SRV-записи можно подменять как порт, так и домен сервера. Стандартный клиент Minecraft Java Edition при подключении по домену автоматически ищет SRV-запись _minecraft._tcp.<домен> в DNS. Если такая запись есть, клиент использует указанные в ней порт и хост. С Bedrock Edition могут возникнуть сложности, там не все клиенты читают SRV-запись.
## Автоматическое создание бэкапов
Скрипт ниже автоматически создаёт бэкапы при завершении работы сервера. Особенно удобно сочетать его с настройкой `ENABLE_AUTOSTOP`, тогда при завершении игровой сессии бэкап будет создан автоматически.
=== "Терминал"
```sh
# Создаём скрипт в директории рядом с docker-compose.yml
nano run-and-backup.sh
# После создания скрипта надо дать ему права на выполнение
chmod u+x run-and-backup.sh
```
=== "run-and-backup.sh"
```bash
#!/usr/bin/env bash
set -euo pipefail
# === настройки ===
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DATA_DIR="${PROJECT_DIR}/data"
BACKUP_DIR="${PROJECT_DIR}/backups"
KEEP_BACKUPS=3
TAR_COMPRESS_FLAGS="-czf"
mkdir -p "$BACKUP_DIR"
ts() { date +"%Y-%m-%d_%H-%M-%S"; }
show_help() {
cat <<'EOF'
run-and-backup.sh — запускает Minecraft-сервер через Docker Compose
и создаёт резервную копию каталога ./data после завершения работы.
ИСПОЛЬЗОВАНИЕ:
./run-and-backup.sh — запустить сервер в foreground
./run-and-backup.sh -d — запустить в background (логи в run-and-backup.log)
./run-and-backup.sh -b — только сделать бэкап и выйти
./run-and-backup.sh -h — показать эту справку
ЧТО ДЕЛАЕТ:
1) docker compose up (следит за завершением контейнера)
2) по Ctrl+C или штатному выходу — docker compose down
3) создаёт архив ./backups/mc-data-YYYY-MM-DD_HH-MM-SS.tar.gz
4) хранит последние KEEP_BACKUPS бэкапов
КАК ВОССТАНОВИТЬ СЕРВЕР ИЗ БЭКАПА:
1) Остановите сервер:
docker compose down
2) Очистите или перенесите текущие данные:
mv ./data ./data.old
mkdir ./data
ИЛИ
rm -rf ./data/*
3) Распакуйте нужный архив в ./data:
tar -xzf ./backups/mc-data-YYYY-MM-DD_HH-MM-SS.tar.gz -C ./data
4) Запустите сервер
ПРИМЕЧАНИЯ:
• Архив содержит всё из ./data (мир, плагины, конфиги, whitelist, ops и т.д.)
• Для фонового режима логи скрипта пишутся в ./run-and-backup.log
EOF
}
backup() {
local stamp archive
stamp="$(ts)"
archive="${BACKUP_DIR}/mc-data-${stamp}.tar.gz"
if [[ ! -d "$DATA_DIR" ]]; then
echo "[!] Нет каталога DATA_DIR: $DATA_DIR" >&2
exit 1
fi
echo "[*] Бэкап ${DATA_DIR} -> ${archive}"
tar $TAR_COMPRESS_FLAGS "$archive" -C "$DATA_DIR" .
echo "[+] Готово: $archive"
echo "[*] Ротация: оставляю последние ${KEEP_BACKUPS}"
ls -1t "${BACKUP_DIR}"/mc-data-*.tar.* 2>/dev/null | tail -n +$((KEEP_BACKUPS+1)) | xargs -r rm -f
}
graceful_down_and_backup() {
echo "[*] Останавливаю docker compose (graceful)..."
docker compose down || true
backup
}
# ---- разбор флагов ----
bg=false
do_backup_only=false
while getopts ":dbh" opt; do
case "$opt" in
d) bg=true ;;
b) do_backup_only=true ;;
h) show_help; exit 0 ;;
\?) echo "Неизвестный флаг: -$OPTARG" >&2; show_help; exit 2 ;;
esac
done
# -b имеет приоритет: просто делаем бэкап и выходим
if $do_backup_only; then
backup
exit 0
fi
# запуск в фоне
if $bg; then
echo "[*] Запускаю в background (логи: ${PROJECT_DIR}/run-and-backup.log)"
nohup "$0" >"${PROJECT_DIR}/run-and-backup.log" 2>&1 &
echo "[✓] PID: $!"
exit 0
fi
# ---- основной режим ----
trap graceful_down_and_backup INT TERM
echo "[*] Запускаю docker compose в foreground (Ctrl+C для остановки)..."
if docker compose up; then
echo "[*] docker compose завершился сам — делаю бэкап..."
backup
else
echo "[!] docker compose завершился с ошибкой; если это был Ctrl+C, бэкап уже выполнен ловушкой."
fi
echo "[✓] Готово."
```
Теперь можно запускать сервер командой `./run-and-backup.sh`.
```sh
./run-and-backup.sh -d
```
Вывести справку можно командой.
```sh
./run-and-backup.sh -h
```

View File

@@ -1,8 +1,38 @@
# Заметка о Powershell # Заметка о Powershell
## Мой Powershell profile ## Установка Powershell
Открыть файл настроек Powershell: По умолчанию в Windows установлен устаревший Windows PowerShell с кучей багов и отсутствующей поддержкой базовых операторов (`&&`, `|`, `||`). Установить новый Powershell можно по [инструкции в репозитории](https://github.com/PowerShell/PowerShell?tab=readme-ov-file#get-powershell).
```powershell
winget search Microsoft.PowerShell
winget install --id Microsoft.PowerShell --source winget
```
Затем стоит [установить PowerShell как профиль по умолчанию](https://stackoverflow.com/a/75891592/17341937).
Чтобы VSCode использовал PowerShell в качестве профиля по умолчанию, нужно добавить в `settings.json` следующий параметр:
```json
{
"terminal.integrated.defaultProfile.windows": "PowerShell"
}
```
## Обновление PSReadLine
На старых версиях PSReadLine, который используется под капотом PowerShell, возникают различные баги: [иногда не печатаются заглавные буквы](https://github.com/PowerShell/PowerShell/issues/10794#issuecomment-542319327), [не работает `Ctrl + C` при запуске с русской раскладкой](https://github.com/PowerShell/PSReadLine/issues/1393#issuecomment-2065423282). Так что стоит сразу его обновить.
Закрыть открытые PowerShell, в том числе внутри VS Code или других IDE. Запустить `cmd` от имени администратора и выполнить:
```cmd
"C:\Program Files\PowerShell\7\pwsh.exe" -noprofile -command "Install-Module PSReadLine -Force -SkipPublisherCheck -AllowPrerelease"
```
Баг с `Ctrl + C` исправлен частично. Если открыть PowerShell с русской раскладкой, то вместо `Ctrl + C` всё равно будет появляться буква `с`, но теперь достаточно переключить раскладку на английскую и всё заработает.
## Мой PowerShell profile
Открыть файл настроек PowerShell:
```powershell ```powershell
code $profile # или notepad $profile code $profile # или notepad $profile
``` ```
@@ -12,12 +42,14 @@ code $profile # или notepad $profile
# Лучше заменить настоящим wget # Лучше заменить настоящим wget
# https://eternallybored.org/misc/wget/ # https://eternallybored.org/misc/wget/
# Скачать EXE для 64-bit и добавить в папку в PATH # Скачать EXE для 64-bit и добавить в папку в PATH
remove-item alias:wget # Удалять алиас нужно только в Windows PowerShell
# remove-item alias:wget
# Заменяем Invoke-WebRequest нормальным curl # Заменяем Invoke-WebRequest нормальным curl
# Скачиваем curl for 64-bit тут https://curl.se/windows/ # Скачиваем curl for 64-bit тут https://curl.se/windows/
# Из папки bin архива переносим curl.exe в папку в PATH # Из папки bin архива переносим curl.exe в папку в PATH
remove-item alias:curl # Удалять алиас нужно только в Windows PowerShell
# remove-item alias:curl
# Алиасы # Алиасы
new-alias actvenv venv/Scripts/activate new-alias actvenv venv/Scripts/activate
@@ -28,8 +60,11 @@ function crtvenv {
actvenv actvenv
} }
function python-path { # Аналог команды which в Linux
python -c "import sys; print(sys.executable)" # https://stackoverflow.com/a/16949127/17341937
function which($name)
{
Get-Command $name | Select-Object -ExpandProperty Definition
} }
# Модуль для автокомплита GIT https://github.com/dahlbyk/posh-git # Модуль для автокомплита GIT https://github.com/dahlbyk/posh-git
@@ -45,7 +80,7 @@ Import-Module DockerCompletion
# Более удобное автодополнение # Более удобное автодополнение
Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete
# Перемещаться по истории использования команды с помощью стрелочек # Удобный поиск по истории команд с помощью стрелочек
Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward
Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward
``` ```
@@ -56,21 +91,6 @@ Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward
Set-ExecutionPolicy RemoteSigned Set-ExecutionPolicy RemoteSigned
``` ```
## Баг с uppercase
В какой-то момент заглавные буквы просто перестают печататься в Powershell, это означает, что нужно обновить `PSReadLine`.
Запустить Powershell от имени администратора и выполнить:
```powershell
Install-Module -Name PowerShellGet -Force
```
Перезапустить от имени администратора и выполнить:
```powershell
Install-Module PSReadLine -AllowPrerelease -Force
```
## Крутой аналог grep - ripgrep ## Крутой аналог grep - ripgrep
[Скачать](https://github.com/BurntSushi/ripgrep/releases/) релиз для винды и добавить в PATH [Скачать](https://github.com/BurntSushi/ripgrep/releases/) релиз для винды и добавить в PATH

14
docs/python/django.md Normal file
View File

@@ -0,0 +1,14 @@
## Отслеживание SQL запросов
```python
from django.db import connection, reset_queries
# Сбрасываем счетчик запросов
reset_queries()
# Код, который работает с БД
# Теперь выводим все запросы, которые были зафиксированы
for query in connection.queries:
print(query)
```

51
docs/servers/bash.md Normal file
View File

@@ -0,0 +1,51 @@
# Bash
## Поиск по истории команд в bash (`↑/↓`)
Включаем поиск по истории команд `bash` по префиксу.
=== "Терминал"
```sh
nano ~/.inputrc
```
=== ".inputrc"
```sh
"\e[A": history-search-backward
"\e[B": history-search-forward
```
После обновления `.inputrc` нужно либо начать сеанс заново, либо выполнить команду.
```sh
bind -f ~/.inputrc
```
## Поиск по истории команд (`ctrl + r`)
`ctrl + r` — начать поиск по истории команд. Дальше можно вводить любую часть команды, поиск будет инкрементально обновляться с каждым введённым символом.
- При повторном нажатии `ctrl + r` будет выведена следующая подходящая команда.
- `Esc` или `ctrl + g` — прервать поиск и вернуться в обычный режим.
- `` или `ctrl + j` — вставить найденную команду в командную строку, но не выполнять. Пригождается, если нужно предварительно отредактировать команду.
- `Enter` — выполнить найденную команду.
## Сочетания клавиш
В Bash есть множество сочетаний клавиш для быстрой навигации и редактирования команд.
Это функции [GNU Readline](https://en.wikipedia.org/wiki/GNU_Readline) (по этой же ссылке можно найти полный список сочетаний).
- `ctrl + l` — очистить экран (аналог `clear`).
- `alt + .` — вставить последний аргумент предыдущей команды.
- `ctrl + a` — в начало строки.
- `ctrl + e` — в конец строки.
- `alt + b` — назад на одно слово.
- `alt + f` — вперед на одно слово.
- `ctrl + b` и `ctrl + f` — назад/вперед на один символ (аналог `← / →`, но не нужно тянуться к стрелочкам).
- `ctrl + p` и `ctrl + n` — переход по истории команд (аналог `↑ / ↓`, но не нужно тянуться к стрелочкам).
- `alt + r` — отменить изменения в команде и вернуть к виду, в котором она хранится в истории команд.
- `alt + d` — удалить слово справа от курсора.
- `ctrl + w` — удалить слово слева от курсора.

75
docs/servers/cleanup.md Normal file
View File

@@ -0,0 +1,75 @@
# Очистка места на сервере
## Полезные команды
```sh
# Проверить место во всех разделах
df -h
# Размер файла/папки
du -sh <path>
# Показать самые большие директории в корне
sudo du -h -d1 / | sort -hr
# В домашнем каталоге
du -h -d1 ~ | sort -hr
# Показать файлы больше 100MB
find . -type f -size +100M -exec ls -lh {} \; | awk '{print $5, $9}' | sort -hr
```
## Логи `journalctl`
```sh
# Посмотреть сколько занимают логи журнала
sudo journalctl --disk-usage
# Оставить только 100MB самых актуальных логов
sudo journalctl --rotate
sudo journalctl --vacuum-size=100M
# Можно задать параметры SystemMaxUse и RuntimeMaxUse
# Вместо MB надо использовать M
sudo nano /etc/systemd/journald.conf
# Применить изменения в конфиге
sudo systemctl restart systemd-journald
```
## Другие логи
```sh
# Посмотреть сколько места занимают логи
sudo du -h -d1 /var/log | sort -hr
# Очищать файлы с логами лучше командой, чтобы процессы могли
# дальше писать логи в этим файлы
sudo truncate -s 0
```
## Кэш пакетов
```sh
sudo apt-get clean
sudo apt-get autoremove --purge
```
## Docker
```sh
# Посмотреть сколько место занимает докер
docker system df
# Показать детализацию по образам, контейнерам, волюмам
docker system df -v
# Удалить все нетегированные образы, остановленные контейнеры,
# неиспользуемые сети, кэш сборки
docker system prune
# Очистит также все образы, с которыми не связан ни один контейнер
docker system prune -a
# Удалить волюмы, с которыми не связан ни один контейнер
docker volume prune
```

83
docs/servers/grafana.md Normal file
View File

@@ -0,0 +1,83 @@
## Запуск Prometheus с помощью systemd
Ссылку на последнюю версию `Prometheus` можно найти на [странице загрузок](https://prometheus.io/download/).
```sh
# Скачиваем и распаковываем релиз
wget <link>
tar xvf prometheus-*.*-amd64.tar.gz
cd prometheus-*.*
# Создаём отдельного пользователя и группу для запуска prometheus
sudo adduser --system --no-create-home --group prometheus
# Конфиг
sudo mkdir /etc/prometheus
sudo cp prometheus.yml /etc/prometheus/
sudo chown -R prometheus:prometheus /etc/prometheus
# Папка для данных
sudo mkdir /var/lib/prometheus
sudo chown -R prometheus:prometheus /var/lib/prometheus
# Бинарники prometheus и promtool
sudo cp prometheus /usr/local/bin/
sudo chown prometheus:prometheus /usr/local/bin/prometheus
sudo cp promtool /usr/local/bin/
sudo chown prometheus:prometheus /usr/local/bin/promtool
```
Создаём `systemd` сервис. Список возможных параметров запуска `Prometheus` представлен в [документации](https://prometheus.io/docs/prometheus/latest/command-line/prometheus/).
=== "Терминал"
```sh
sudo nano /etc/systemd/system/prometheus.service
```
=== "prometheus.service"
```ini
[Unit]
Description=Prometheus Server
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Restart=on-failure
ExecStart=/usr/local/bin/prometheus \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.path=/var/lib/prometheus \
--storage.tsdb.retention.size=500MB
[Install]
WantedBy=multi-user.target
```
Добавляем `Prometheus` в автозагрузку и запускаем.
```sh
sudo systemctl daemon-reload
sudo systemctl enable prometheus
sudo systemctl start prometheus
sudo systemctl status prometheus
```
При обновлении конфига, нужно будет перезапустить сервис.
```sh
sudo systemctl restart prometheus
```
## Node Exporter
Устанавливаем `Node Exporter` по инструкции из [документации](https://prometheus.io/docs/guides/node-exporter/). Сервис в `systemd` для `Node Exporter` будет создан автоматически.
```sh
sudo systemctl status node_exporter.service
```
[Пример](https://grafana.com/grafana/dashboards/1860-node-exporter-full/) дашборда `Grafana` для `Node Exporter`.

454
docs/servers/mail.md Normal file
View File

@@ -0,0 +1,454 @@
# Почта on-premise
Настройка почты на своём сервере со своим доменным именем. В качестве почтового сервера используется [Docker Mailserver](https://github.com/docker-mailserver/docker-mailserver) версии 15.1.0. Вебклиент — [Roundcube Webmail](https://github.com/roundcube/roundcubemail) версии 1.6.11.
## Открытие портов для почты
Многие хостинг провайдеры блокируют исходящие соединения на портах 25, 465 и 587, чтобы предотвратить спам или вредоносные рассылки с их серверов. Можно проверить доступность портов с помощью команды `nc`.
```sh
# Выведет "Connection to smtp.gmail.com 25 port [tcp/*] succeeded!",
# если порт открыт
nc -vz smtp.gmail.com 25
nc -vz smtp.gmail.com 465
nc -vz smtp.gmail.com 587
```
Если порты закрыты, то можно обратиться в поддержку хостинг провайдера с запросом на открытие портов.
??? abstract "Пример обращения в поддержку"
```
Добрый день!
Прошу разблокировать исходящие соединения на порты 25/tcp, 465/tcp и 587/tcp
для моего сервера (<IP-адрес сервера>).
Сервер используется для личного почтового домена <домен>, не для массовых рассылок.
Спасибо!
```
## Настройка DNS
Настройка DNS на примере домена `tishenko.dev` (почта `@tishenko.dev`). Более подробное описание всех настроек можно прочитать в документации Docker Mailserver: [[1]](https://docker-mailserver.github.io/docker-mailserver/latest/usage/#minimal-dns-setup) и [[2]](https://docker-mailserver.github.io/docker-mailserver/latest/config/best-practices/dkim_dmarc_spf/).
Со стороны DNS провайдера:
1. Добавляем поддомен для почты, например, `mail.tishenko.dev` и `www.mail.tishenko.dev`. В A-записи поддомена указываем IP-адрес сервера.
2. Добавляем MX-запись для основного домена `tishenko.dev`, именно этот домен будет использоваться для отправки и получения почты `@tishenko.dev`.
```dns
10 mail.tishenko.dev.
```
Обязательно с точкой в конце. Число 10 это приоритет MX-записи, чем меньше число, тем выше приоритет. Приоритет не играет роли, если запись только одна. MX-записи, созданные DNS провайдером, нужно удалить.
3. Добавляем TXT-запись для DMARC. Запись надо создать для домена `_dmarc.tishenko.dev.`.
```dns
v=DMARC1; p=quarantine; sp=quarantine; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; rua=mailto:dmarc.report@tishenko.dev; ruf=mailto:dmarc.report@tishenko.dev
```
DMARC-записи, созданные DNS провайдером, нужно удалить.
4. Добавляем TXT-запись для SPF. Запись надо создать для домена `tishenko.dev.`.
```dns
v=spf1 mx -all
```
SPF-записи, созданные DNS провайдером, нужно удалить.
5. Для окончательной настройки нужно также добавить TXT-запись для DKIM. Однако это можно сделать только после настройки почтового сервера и создания пары ключей. Подробнее про настройку DKIM написано [ниже](#настройка-dkim).
Со стороны владельца IP-адреса, как правило это VDS провайдер, создаём или редактируем PTR запись для IP-адреса почтового сервера. Указываем в ней почтовый адрес: `mail.tishenko.dev.`.
??? question "Как проверить настройки DNS?"
Можно использовать [DNS Checker](https://dnschecker.org/) для проверки [A](https://dnschecker.org/#A/mail.tishenko.dev), [MX](https://dnschecker.org/#MX/tishenko.dev), [PTR](https://dnschecker.org/#PTR/146.103.98.219), [DMARC](https://dnschecker.org/#TXT/_dmarc.tishenko.dev) и [SPF](https://dnschecker.org/#TXT/tishenko.dev) записей.
Либо использовать команду `dig`.
```sh
# Команда должна вывести IP-адрес сервера
dig @1.1.1.1 +short A mail.tishenko.dev
# 10 mail.tishenko.dev.
dig @1.1.1.1 +short MX tishenko.dev
# mail.tishenko.dev.
dig @1.1.1.1 +short -x 146.103.98.219
# Проверить DMARC
dig @1.1.1.1 +short TXT _dmarc.tishenko.dev
# Проверить SPF
dig @1.1.1.1 +short TXT tishenko.dev
```
Обновление DNS происходит не мгновенно, обычно это занимает около 20 минут.
??? abstract "Пример файла зоны"
В панели управления DNS провайдера можно выгрузить файл зоны и убедиться, что все записи добавились корректно.
```dns
IN MX 10 mail.tishenko.dev.
mail IN A 146.103.98.219
www.mail IN A 146.103.98.219
@ 600 IN TXT "v=spf1 mx -all"
_dmarc 600 IN TXT "v=DMARC1; p=quarantine; sp=quarantine; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; rua=mailto:dmarc.report@tishenko.dev; ruf=mailto:dmarc.report@tishenko.dev"
```
## Docker Mailserver
Docker Mailserver (DMS) имеет отличную [документацию](https://docker-mailserver.github.io/docker-mailserver/latest/usage/). И DNS и почтовый сервер можно настроить просто пройдясь по ней. Эта заметка лишь дополняет документацию.
```sh
# Открываем порты для почты (если используется ufw)
sudo ufw allow 25,143,465,587,993/tcp
# Создаём отдельного пользователя для управления почтой
# Пользователь vmail в контейнере DMS по умолчанию имеет uid 5000,
# поэтому желательно, чтобы и на хосте он имел такой же uid
sudo useradd -u 5000 --create-home --shell /bin/bash vmail
sudo usermod -aG docker vmail
# Переключаемся на пользователя vmail
sudo su - vmail
# Скачиваем compose.yaml и mailserver.env из репозитория DMS
# Версия 15.1.0
DMS_GITHUB_URL="https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/refs/tags/v15.1.0"
wget "${DMS_GITHUB_URL}/compose.yaml"
wget "${DMS_GITHUB_URL}/mailserver.env"
```
Теперь нужно отредактировать `compose.yaml` и `mailserver.env`.
=== "Терминал"
```sh
nano compose.yaml
```
=== "compose.yaml"
В `image` указываем конкретную версию вместо `:latest`. В `hostname` указываем почтовый домен (e. g. mail.tishenko.dev).
Также прокидываем в контейнер волюм `/etc/letsencrypt` для подключения SSL сертификатов, актуально если на хосте используется letsencrypt.
```yaml
services:
mailserver:
image: ghcr.io/docker-mailserver/docker-mailserver:15.1.0
container_name: mailserver
# Provide the FQDN of your mail server here (Your DNS MX record should point to this value)
hostname: mail.tishenko.dev
env_file: mailserver.env
# More information about the mail-server ports:
# https://docker-mailserver.github.io/docker-mailserver/latest/config/security/understanding-the-ports/
ports:
- "25:25" # SMTP (explicit TLS => STARTTLS, Authentication is DISABLED => use port 465/587 instead)
- "143:143" # IMAP4 (explicit TLS => STARTTLS)
- "465:465" # ESMTP (implicit TLS)
- "587:587" # ESMTP (explicit TLS => STARTTLS)
- "993:993" # IMAP4 (implicit TLS)
volumes:
- ./docker-data/dms/mail-data/:/var/mail/
- ./docker-data/dms/mail-state/:/var/mail-state/
- ./docker-data/dms/mail-logs/:/var/log/mail/
- ./docker-data/dms/config/:/tmp/docker-mailserver/
- /etc/localtime:/etc/localtime:ro
- /etc/letsencrypt:/etc/letsencrypt:ro
restart: always
stop_grace_period: 1m
# Uncomment if using `ENABLE_FAIL2BAN=1`:
# cap_add:
# - NET_ADMIN
healthcheck:
test: "ss --listening --ipv4 --tcp | grep --silent ':smtp' || exit 1"
timeout: 3s
retries: 0
```
=== "mailserver.env"
В этой заметке DMS настраивается с антиспамом Rspamd. Как отмечено в [документации](https://docker-mailserver.github.io/docker-mailserver/v15.1/config/security/rspamd/), его планируется использовать по умолчанию в будущих версиях DMS. На той же странице документации перечислены legacy проверки, которые нужно отключить. Тут они также продублированы.
```sh
# Указываем тип SSL сертификатов
SSL_TYPE=letsencrypt
# Включаем Rspamd
ENABLE_RSPAMD=1
# Отключаем legacy проверки, т. к. они уже включены в Rspamd
ENABLE_OPENDKIM=0
ENABLE_OPENDMARC=0
ENABLE_POLICYD_SPF=0
ENABLE_AMAVIS=0
RSPAMD_GREYLISTING=1
```
```sh
# Создаём директории для волюмов DMS заранее,
# чтобы у них был правильный владелец (vmail)
mkdir -p ./docker-data/dms/{mail-data,mail-state,mail-logs,config}
# Запускаем DMS
docker compose up -d
# В течение двух минут после первого запуска DMS нужно создать хотя бы один
# почтовый адрес, иначе контейнер завершится с ошибкой
# Команда предложит задать пароль для нового почтового аккаунта
docker exec -it mailserver setup email add artem@tishenko.dev
# Обязательно добавляем alias для адреса postmaster
docker exec -it mailserver setup alias add postmaster@tishenko.dev artem@tishenko.dev
```
### Настройка SSL
В документации DMS есть отдельная [страница](https://docker-mailserver.github.io/docker-mailserver/latest/config/security/ssl/) про настройку SSL сертификатов.
Далее подразумевается, что волюм `/etc/letsencrypt` уже прокинут в контейнер, а также в `mailserver.env` указана переменная `SSL_TYPE=letsencrypt`.
Получаем сертификаты. Команды нужно выполнять от пользователя с правом использовать `sudo`. `certbot` можно установить с помощью [pip](https://certbot.eff.org/instructions?ws=other&os=pip).
```sh
# Порт 80 нужен для получения и обновления сертификатов
sudo ufw allow 80/tcp
# Если на сервере есть nginx или другой веб-сервер, используем соответствующий флаг
sudo certbot certonly --nginx -d mail.tishenko.dev -d www.mail.tishenko.dev
# Если на сервере нет nginx (порт 80 не должен быть занят)
sudo certbot certonly --standalone -d mail.tishenko.dev -d www.mail.tishenko.dev
```
### Настройка DKIM
В документации DMS есть отдельная [страница](https://docker-mailserver.github.io/docker-mailserver/latest/config/best-practices/dkim_dmarc_spf/) про настройку DKIM, DMARC и SPF. Про настройку DMARC и SPF написано [выше](#настройка-dns), а вот для настройки DKIM нужно сначала сгенерировать пару ключей. Публичный ключ как раз и указывается в DKIM.
Команда для генерации ключей. DMS должен быть запущен.
```sh
# Выведет в консоль значение для TXT-записи
# Также её можно узнать в файле
# cat ./docker-data/dms/config/rspamd/dkim/rsa-2048-mail-tishenko.dev.public.dns.txt
docker exec -it mailserver setup config dkim domain tishenko.dev
```
Теперь добавляем TXT запись для DKIM в DNS. Имя записи должно быть `mail._domainkey.tishenko.dev.`. Проверить запись можно с помощью сайта [DNS Checker](https://dnschecker.org/#TXT/mail._domainkey.tishenko.dev) или команды `dig`.
```sh
# Должна вывести "v=DKIM1; k=rsa; p=<публичный ключ>"
dig @1.1.1.1 +short TXT mail._domainkey.tishenko.dev
```
### Проверка
Работоспособность и настройки DMS можно проверить с помощью сайта [Mail-Tester](https://www.mail-tester.com/).
Пример команды для отправки тестового письма.
```sh
docker exec -it mailserver swaks \
--to <адрес с mail-tester> \
--from artem@tishenko.dev \
--server mail.tishenko.dev \
--port 587 \
--tls \
--auth LOGIN \
--auth-user artem@tishenko.dev \
--auth-password 'password'
```
Если письмо дойдёт до тестового адреса, то сайт выведет результаты проверки DNS-записей и общую оценку настройки почтового сервера. Если всё сделано правильно, то оценка будет 10/10.
Дополнительно можно проверить настройки почтового сервера с помощью сайта [MX Toolbox](https://mxtoolbox.com/emailhealth).
### Администрирование
Администрировать DMS можно через скрипт `setup` внутри контейнера. Для этого можно подключиться к контейнеру с помощью команды.
```sh
docker exec -it mailserver bash
# Уже внутри контейнера
setup help
```
Если нужно выполнить всего одну команду, то можно не запускать bash.
```sh
docker exec -it mailserver setup help
```
## Roundcube
Roundcube проще всего развернуть с помощью Docker Compose. Актуальную версию образа можно выбрать на [Docker Hub](https://hub.docker.com/r/roundcube/roundcubemail). Там же можно посмотреть список переменных окружения и их значения. Далее подразумевается, что выбран образ `apache-nonroot`, а также что для управления почтой создан отдельный пользователь `vmail` с uid/gid 5000, как показано в инструкции [выше](#docker-mailserver).
=== "Терминал"
```sh
# Переключаемся на пользователя vmail
sudo su - vmail
mkdir ./roundcube
cd ./roundcube
# Создаём директории для волюмов Roundcube заранее,
# чтобы у них был правильный владелец (vmail)
mkdir -p ./roundcube/{app,config,db,tmp}
nano docker-compose.yml
```
=== "Пример docker-compose.yml"
```yaml
services:
roundcube:
image: roundcube/roundcubemail:1.6.11-apache-nonroot
container_name: roundcube
restart: always
user: "5000:5000"
ports:
- "25025:8000"
environment:
# IMAP
- ROUNDCUBEMAIL_DEFAULT_HOST=ssl://mail.tishenko.dev
- ROUNDCUBEMAIL_DEFAULT_PORT=993
# SMTP
- ROUNDCUBEMAIL_SMTP_SERVER=tls://mail.tishenko.dev
- ROUNDCUBEMAIL_SMTP_PORT=587
- ROUNDCUBEMAIL_SMTP_USER=%u
- ROUNDCUBEMAIL_SMTP_PASS=%p
# DB
- ROUNDCUBEMAIL_DB_TYPE=sqlite
# Misc
- ROUNDCUBEMAIL_USERNAME_DOMAIN=tishenko.dev
volumes:
- ./roundcube/app:/var/www/html
- ./roundcube/config:/var/roundcube/config
- ./roundcube/db:/var/roundcube/db
- ./roundcube/tmp:/tmp/roundcube-temp
```
Теперь Roundcube доступен на `http://localhost:25025`, чтобы его можно было использовать извне, нужно настроить nginx или аналогичный веб-сервер.
=== "Терминал"
```sh
sudo nano /etc/nginx/sites-available/mail.conf
sudo ln -s /etc/nginx/sites-available/mail.conf /etc/nginx/sites-enabled/
```
=== "Пример nginx конфига"
```nginx
server {
listen 80;
server_name mail.tishenko.dev www.mail.tishenko.dev;
client_max_body_size 25m;
location / {
proxy_pass http://127.0.0.1:25025;
include proxy_params;
}
}
```
Установить SSL сертификат можно с помощью certbot. Причём если сертификат уже был получен на этапе [настройки DMS](#настройка-ssl), то certbot предложит использовать его.
```sh
sudo certbot --nginx -d mail.tishenko.dev -d www.mail.tishenko.dev
```
### Конфиг
Некоторые настройки Roundcube нельзя задать через переменные окружения, они задаются в файле `config.inc.php`. Например, "название продукта", оно отображается в заголовке страницы, на странице входа и в других местах. По умолчанию это `Roundcube Webmail`. Его можно изменить в `config/config.inc.php`.
=== "Терминал"
```sh
nano roundcube/config/config.inc.php
```
=== "config.inc.php"
```php
<?php
$config['product_name'] = 'Tish\'s Mail';
```
### Настройки аккаунта
Настройки, относящиеся к конкретному аккаунту, можно задать через UI. Как минимум стоит установить отображаемое имя, иначе у получателей будет отображаться только адрес электронной почты: `Настройки` -> `Профили` -> `Отображаемое имя`. В разделе с профилями также можно указать подпись для писем.
Roundcube позволяет задать несколько профилей и соответствующих адресов для одного аккаунта и легко переключаться между ними через UI. Для этого в DMS нужно создать алиас для основного адреса, а затем добавить профиль через UI Roundcube в разделе `Настройки` -> `Профили`.
```sh
docker exec -it mailserver setup alias add <алиас> <основной адрес>
```
### Внешний вид
Подразумевается, что прокинут волюм `./roundcube/app:/var/www/html`. Чтобы внешние изменения не терялись при перезапуске контейнера, нужно создать свою тему для Roundcube на основе темы по умолчанию и изменять её.
```sh
# Желательно выполнять команды от пользователя vmail,
# чтобы не было проблем с правами
# Либо подключиться к контейнеру через docker exec -it roundcube bash
# и редактировать темы из контейнера
sudo su - vmail
# Переходим в директорию с docker-compose.yml для Roundcube
cd ./roundcube
# В разделе environment добавляем переменную окружения
# ROUNDCUBEMAIL_SKIN=custom
nano docker-compose.yml
# Копируем тему по умолчанию
# Roundcube должен был быть запущен хотя бы один раз,
# чтобы тема по умолчанию появилась в волюме
cp -r ./roundcube/app/skins/elastic/ ./roundcube/app/skins/custom/
# Перезапускаем Roundcube
docker compose down
docker compose up -d
```
Теперь любые изменения в теме `custom` будут сохраняться при перезапуске контейнера.
Подробнее про темы Roundcube можно посмотреть в [документации](https://github.com/roundcube/roundcubemail/wiki/Skins).
#### Favicon
Favicon находится в `skins/custom/images/favicon.ico`. Достаточно просто заменить его на свой.
```sh
cp your-favicon.ico ./roundcube/app/skins/custom/images/favicon.ico
```
#### Логотип
Логотип находится в `skins/custom/images/logo.svg`. Достаточно просто заменить его на свой.
```sh
cp your-logo.svg ./roundcube/app/skins/custom/images/logo.svg
```
## Логотип в письмах
Для того чтобы у получателей вместо плейсхолдера рядом с именем отправителя отображался логотип, нужно добавить BIMI TXT-запись в DNS для домена `default._bimi.tishenko.dev.`. Нужно будет настроить Nginx или другой веб-сервер, чтобы логотип был доступен по указанному в BIMI адресу.
```dns
v=BIMI1; l=https://tishenko.dev/logo.svg;
```
Однако в gmail и некоторых других почтовых клиентах он всё равно не будет отображаться, так как они требуют для этого платные VMC сертификаты.

View File

@@ -1,10 +1,118 @@
## Добавление сайта
Создаём конфиг.
=== "Терминал"
```sh
sudo nano /etc/nginx/sites-available/new-site.conf
```
=== "Статический сайт"
```nginx
server {
server_name example.com www.example.com;
listen 80;
root /var/www/new-site;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
```
=== "Веб-приложение"
```nginx
server {
server_name giga-chill.ru www.giga-chill.ru;
listen 80;
# Все запросы к /api/* перенаправляются на бэкенд
location /api/ {
proxy_pass http://127.0.0.1:8081/;
include proxy_params;
}
# Спецификация API в формате OpenAPI
location = /api/openapi.yml {
alias /var/www/giga-chill/openapi.yml;
types { text/yaml yml yaml; }
charset utf-8;
charset_types text/yaml application/yaml text/x-yaml application/x-yaml;
}
# Документация API в Swagger UI
location = /api/swagger { return 301 /api/swagger/; }
location /api/swagger/ {
proxy_pass http://127.0.0.1:1240/;
include proxy_params;
}
# Документация API в Redocly
location = /api/redoc { return 301 /api/redoc/; }
location /api/redoc/ {
alias /var/www/giga-chill/redoc/;
index index.html;
}
# Все остальные запросы направляются на фронтенд
location / {
proxy_pass http://127.0.0.1:3000;
include proxy_params;
}
}
```
Активируем конфиг.
```sh
sudo ln -s /etc/nginx/sites-available/new-site.conf /etc/nginx/sites-enabled/
sudo systemctl reload nginx.service
```
Конфиг можно проверить на наличие синтаксических ошибок.
```sh
sudo nginx -t
```
??? question "`open() "/etc/nginx/proxy_params" failed (2: No such file or directory)`"
Обычно файл `/etc/nginx/proxy_params` создаётся автоматически при установке Nginx, однако его несложно добавить самостоятельно, если по каким-то причинам он не был создан или был удалён.
=== "Терминал"
```sh
sudo nano /etc/nginx/proxy_params
```
=== "proxy_params"
```nginx
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
```
## Просмотр логов
По умолчанию логи находятся в `access.log` и `error.log` файлах.
```sh
sudo tail -n 20 /var/log/nginx/access.log
sudo tail -n 20 /var/log/nginx/error.log
```
## SSL-сертификат с certbot ## SSL-сертификат с certbot
!!! tip "Актуальная версия Python" !!! tip "Актуальная версия Python"
Обычно системый `Python` достаточно старый. Для установки `certbot` может потребоваться более новая версия. Минимальные требования можно узнать на [pypi](https://pypi.org/project/certbot/). Обычно системный `Python` достаточно старый. Для установки `certbot` может потребоваться более новая версия. Минимальные требования можно узнать на [pypi](https://pypi.org/project/certbot/).
```sh ```sh
sudo apt update sudo apt update
@@ -33,7 +141,23 @@ SSL-сертификат получается и устанавливается
# Автообновление сертификатов # Автообновление сертификатов
echo "0 0,12 * * * root /opt/certbot/bin/python -c 'import random; import time; time.sleep(random.random() * 3600)' && sudo certbot renew -q" | sudo tee -a /etc/crontab > /dev/null echo "0 0,12 * * * root /opt/certbot/bin/python -c 'import random; import time; time.sleep(random.random() * 3600)' && sudo certbot renew -q" | sudo tee -a /etc/crontab > /dev/null
``` ```
??? tip "Отключение UFW"
Может потребоваться временно отключить UFW.
```sh
sudo ufw disable
# Получаем сертификат
sudo ufw enable
```
Либо насовсем открыть порт 80, тогда и `renew` точно будет работать.
```sh
sudo ufw allow 80/tcp
```
Несколько полезных команд. Несколько полезных команд.

View File

@@ -10,7 +10,7 @@ ssh root@<IPv4>
```sh { .code-wrap } ```sh { .code-wrap }
# Создаём пользователя и наделяем правом использовать `sudo`. # Создаём пользователя и наделяем правом использовать `sudo`.
adduser <user> adduser <user>
adduser <username> sudo adduser <user> sudo
# Переключаемся на нового пользователя # Переключаемся на нового пользователя
su - <user> su - <user>
@@ -42,8 +42,11 @@ sudo reboot
Можно придумать серверу имя, оно будет отображаться в терминале после `<user>@`. Можно придумать серверу имя, оно будет отображаться в терминале после `<user>@`.
```sh ```sh
sudo nano /etc/hostname sudo nano /etc/hostname
sudo nano /etc/hosts
sudo systemctl restart systemd-hostnamed sudo systemctl restart systemd-hostnamed
# В hosts тоже иногда есть строчка вида
# 127.0.1.1 <hostname>
sudo nano /etc/hosts
``` ```
@@ -61,6 +64,10 @@ sudo nano /etc/ssh/sshd_config
```sh ```sh
# На некоторых системах ssh вместо sshd # На некоторых системах ssh вместо sshd
sudo systemctl reload sshd sudo systemctl reload sshd
# Иногда дополнительно нужно выполнить
systemctl daemon-reload
systemctl restart ssh.socket
``` ```
??? question "А что будет, если потерять SSH-ключ?" ??? question "А что будет, если потерять SSH-ключ?"
@@ -93,6 +100,30 @@ ssh <host>
ssh <user>@<IPv4> -p <ssh-port> ssh <user>@<IPv4> -p <ssh-port>
``` ```
## Создание SWAP-файла
Лучше пожертвовать пару гигабайт от объёма диска сервера на SWAP-файл, чтобы уменьшить вероятность того, что в один прекрасный момент сервер крашнется из-за нехватки памяти.
```sh
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo swapon --show
# Чтобы swap-файл подключался при перезагрузке сервера
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
```
Чтобы удалить SWAP-файл, нужно выполнить следующие команды.
```sh
sudo swapoff /swapfile
# Удаляем строку со swap-файлом
sudo nano /etc/fstab
```
## Настройка фаерволла c UFW ## Настройка фаерволла c UFW
@@ -156,7 +187,7 @@ sudo ufw status verbose
## Настройка Fail2Ban ## Настройка Fail2Ban
[Fail2Ban](https://github.com/fail2ban/fail2ban) - базовая защита сервера от brute-force аттак. [Fail2Ban](https://github.com/fail2ban/fail2ban) - базовая защита сервера от brute-force атак.
```sh ```sh
sudo apt update sudo apt update

66
docs/servers/ssh.md Normal file
View File

@@ -0,0 +1,66 @@
# Заметка по SSH
## SSH-agent
### Установка на Windows
`ssh-agent` является частью OpenSSH. Начиная с Windows 10, OpenSSH устанавливается вместе с системой, однако службу `ssh-agent` надо включить вручную. Для этого нужно запустить `Powershell` от имени администратора и выполнить несколько команд.
```powershell
Set-Service -Name ssh-agent -StartupType Automatic
Start-Service ssh-agent
```
Проверить состояние `ssh-agent` можно с помощью команды.
```powershell
Get-Service -Name ssh-agent | select -property status,name,starttype
```
На Windows `git` по умолчанию использует свою службу `ssh` вместо системной, поэтому `ssh-agent` не будет с ним работать и пароли всё равно придётся вводить вручную. Однако можно настроить `git` так, чтобы он использовал системный `ssh`.
```powershell
git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"
```
### Установка на Linux
На Linux `ssh-agent` как правило уже установлен и активирован из коробки. При перезагрузке системы нужно будет добавлять ключи заново. Можно настроить `ssh` так, чтобы ключи добавлялись автоматически, тогда пароль от каждого ключа будет запрашиваться только один раз.
=== "Терминал"
```sh
nano ~/.ssh/config
```
=== "~/.ssh/config"
```ini
Host *
AddKeysToAgent yes
```
### Использование
!!! warning "Security warning"
На Windows доступ к ключам сохраняется даже после перезагрузки системы.
```sh
# Добавляет ключи из ~/.ssh/
ssh-add
# Можно указать путь
ssh-add path/to/id_rsa
# Список добавленных ключей
ssh-add -l
# Удалить все ключи из памяти агента
ssh-add -D
```
Иногда нужно, чтобы при подключении на сервер, были доступны приватные ключи из локального `ssh-agent`. Для этого можно использовать команду `ssh -A`.
```sh
ssh -A user@server
```
Либо указать `ForwardAgent yes` в конфиге `ssh`.

View File

@@ -1,4 +1,4 @@
site_name: Tishenko's knowledge base site_name: Tish Knowledge Base
site_url: https://kb.tishenko.dev site_url: https://kb.tishenko.dev
copyright: Copyright &copy; 2025 copyright: Copyright &copy; 2025
repo_url: https://github.com/Arity-T/knowledge-base repo_url: https://github.com/Arity-T/knowledge-base
@@ -37,7 +37,7 @@ theme:
icon: material/brightness-4 icon: material/brightness-4
name: Switch to light mode name: Switch to light mode
extra: extra:
generator: false # generator: false
social: social:
- icon: fontawesome/brands/github - icon: fontawesome/brands/github
link: https://github.com/Arity-T link: https://github.com/Arity-T

12
pyproject.toml Normal file
View File

@@ -0,0 +1,12 @@
[project]
name = "knowledge-base"
version = "0.1.0"
description = "Just a knowledge base"
requires-python = ">=3.12"
dependencies = [
"click==8.2.1",
"mkdocs>=1.6.1",
"mkdocs-glightbox>=0.5.2",
"mkdocs-material>=9.7.1",
"pymdown-extensions>=10.19.1",
]

View File

@@ -1,2 +1,4 @@
mkdocs mkdocs
pymdown-extensions
mkdocs-material
mkdocs-glightbox mkdocs-glightbox

558
uv.lock generated Normal file
View File

@@ -0,0 +1,558 @@
version = 1
revision = 3
requires-python = ">=3.12"
[[package]]
name = "babel"
version = "2.17.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" },
]
[[package]]
name = "backrefs"
version = "6.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/86/e3/bb3a439d5cb255c4774724810ad8073830fac9c9dee123555820c1bcc806/backrefs-6.1.tar.gz", hash = "sha256:3bba1749aafe1db9b915f00e0dd166cba613b6f788ffd63060ac3485dc9be231", size = 7011962, upload-time = "2025-11-15T14:52:08.323Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/ee/c216d52f58ea75b5e1841022bbae24438b19834a29b163cb32aa3a2a7c6e/backrefs-6.1-py310-none-any.whl", hash = "sha256:2a2ccb96302337ce61ee4717ceacfbf26ba4efb1d55af86564b8bbaeda39cac1", size = 381059, upload-time = "2025-11-15T14:51:59.758Z" },
{ url = "https://files.pythonhosted.org/packages/e6/9a/8da246d988ded941da96c7ed945d63e94a445637eaad985a0ed88787cb89/backrefs-6.1-py311-none-any.whl", hash = "sha256:e82bba3875ee4430f4de4b6db19429a27275d95a5f3773c57e9e18abc23fd2b7", size = 392854, upload-time = "2025-11-15T14:52:01.194Z" },
{ url = "https://files.pythonhosted.org/packages/37/c9/fd117a6f9300c62bbc33bc337fd2b3c6bfe28b6e9701de336b52d7a797ad/backrefs-6.1-py312-none-any.whl", hash = "sha256:c64698c8d2269343d88947c0735cb4b78745bd3ba590e10313fbf3f78c34da5a", size = 398770, upload-time = "2025-11-15T14:52:02.584Z" },
{ url = "https://files.pythonhosted.org/packages/eb/95/7118e935b0b0bd3f94dfec2d852fd4e4f4f9757bdb49850519acd245cd3a/backrefs-6.1-py313-none-any.whl", hash = "sha256:4c9d3dc1e2e558965202c012304f33d4e0e477e1c103663fd2c3cc9bb18b0d05", size = 400726, upload-time = "2025-11-15T14:52:04.093Z" },
{ url = "https://files.pythonhosted.org/packages/1d/72/6296bad135bfafd3254ae3648cd152980a424bd6fed64a101af00cc7ba31/backrefs-6.1-py314-none-any.whl", hash = "sha256:13eafbc9ccd5222e9c1f0bec563e6d2a6d21514962f11e7fc79872fd56cbc853", size = 412584, upload-time = "2025-11-15T14:52:05.233Z" },
{ url = "https://files.pythonhosted.org/packages/02/e3/a4fa1946722c4c7b063cc25043a12d9ce9b4323777f89643be74cef2993c/backrefs-6.1-py39-none-any.whl", hash = "sha256:a9e99b8a4867852cad177a6430e31b0f6e495d65f8c6c134b68c14c3c95bf4b0", size = 381058, upload-time = "2025-11-15T14:52:06.698Z" },
]
[[package]]
name = "certifi"
version = "2025.11.12"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" },
]
[[package]]
name = "charset-normalizer"
version = "3.4.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" },
{ url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" },
{ url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" },
{ url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" },
{ url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" },
{ url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" },
{ url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" },
{ url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" },
{ url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" },
{ url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" },
{ url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" },
{ url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" },
{ url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" },
{ url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" },
{ url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" },
{ url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" },
{ url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
{ url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
{ url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
{ url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
{ url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
{ url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
{ url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
{ url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
{ url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
{ url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
{ url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
{ url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
{ url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
{ url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
{ url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
{ url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
{ url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
{ url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
{ url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
{ url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
{ url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
{ url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
{ url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
{ url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
{ url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
{ url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
{ url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
{ url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
{ url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
{ url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
{ url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
{ url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
{ url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
]
[[package]]
name = "click"
version = "8.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "ghp-import"
version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "python-dateutil" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" },
]
[[package]]
name = "idna"
version = "3.11"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
]
[[package]]
name = "jinja2"
version = "3.1.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
]
[[package]]
name = "knowledge-base"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "click" },
{ name = "mkdocs" },
{ name = "mkdocs-glightbox" },
{ name = "mkdocs-material" },
{ name = "pymdown-extensions" },
]
[package.metadata]
requires-dist = [
{ name = "click", specifier = "==8.2.1" },
{ name = "mkdocs", specifier = ">=1.6.1" },
{ name = "mkdocs-glightbox", specifier = ">=0.5.2" },
{ name = "mkdocs-material", specifier = ">=9.7.1" },
{ name = "pymdown-extensions", specifier = ">=10.19.1" },
]
[[package]]
name = "markdown"
version = "3.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/7dd27d9d863b3376fcf23a5a13cb5d024aed1db46f963f1b5735ae43b3be/markdown-3.10.tar.gz", hash = "sha256:37062d4f2aa4b2b6b32aefb80faa300f82cc790cb949a35b8caede34f2b68c0e", size = 364931, upload-time = "2025-11-03T19:51:15.007Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl", hash = "sha256:b5b99d6951e2e4948d939255596523444c0e677c669700b1d17aa4a8a464cb7c", size = 107678, upload-time = "2025-11-03T19:51:13.887Z" },
]
[[package]]
name = "markupsafe"
version = "3.0.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" },
{ url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" },
{ url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" },
{ url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" },
{ url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" },
{ url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" },
{ url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" },
{ url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" },
{ url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" },
{ url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" },
{ url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" },
{ url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" },
{ url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" },
{ url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" },
{ url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" },
{ url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" },
{ url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" },
{ url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" },
{ url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" },
{ url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" },
{ url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" },
{ url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" },
{ url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" },
{ url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" },
{ url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" },
{ url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" },
{ url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" },
{ url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" },
{ url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" },
{ url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" },
{ url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" },
{ url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" },
{ url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" },
{ url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" },
{ url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" },
{ url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" },
{ url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" },
{ url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" },
{ url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" },
{ url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" },
{ url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" },
{ url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" },
{ url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" },
{ url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" },
{ url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" },
{ url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" },
{ url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" },
{ url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" },
{ url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" },
{ url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" },
{ url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" },
{ url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" },
{ url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" },
{ url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" },
{ url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
]
[[package]]
name = "mergedeep"
version = "1.3.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" },
]
[[package]]
name = "mkdocs"
version = "1.6.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "ghp-import" },
{ name = "jinja2" },
{ name = "markdown" },
{ name = "markupsafe" },
{ name = "mergedeep" },
{ name = "mkdocs-get-deps" },
{ name = "packaging" },
{ name = "pathspec" },
{ name = "pyyaml" },
{ name = "pyyaml-env-tag" },
{ name = "watchdog" },
]
sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" },
]
[[package]]
name = "mkdocs-get-deps"
version = "0.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mergedeep" },
{ name = "platformdirs" },
{ name = "pyyaml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239, upload-time = "2023-11-20T17:51:09.981Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521, upload-time = "2023-11-20T17:51:08.587Z" },
]
[[package]]
name = "mkdocs-glightbox"
version = "0.5.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "selectolax" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8d/26/c793459622da8e31f954c6f5fb51e8f098143fdfc147b1e3c25bf686f4aa/mkdocs_glightbox-0.5.2.tar.gz", hash = "sha256:c7622799347c32310878e01ccf14f70648445561010911c80590cec0353370ac", size = 510586, upload-time = "2025-10-23T14:55:18.909Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4e/ca/03624e017e5ee2d7ce8a08d89f81c1e535eb3c30d7b2dc4a435ea3fbbeae/mkdocs_glightbox-0.5.2-py3-none-any.whl", hash = "sha256:23a431ea802b60b1030c73323db2eed6ba859df1a0822ce575afa43e0ea3f47e", size = 26458, upload-time = "2025-10-23T14:55:17.43Z" },
]
[[package]]
name = "mkdocs-material"
version = "9.7.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "babel" },
{ name = "backrefs" },
{ name = "colorama" },
{ name = "jinja2" },
{ name = "markdown" },
{ name = "mkdocs" },
{ name = "mkdocs-material-extensions" },
{ name = "paginate" },
{ name = "pygments" },
{ name = "pymdown-extensions" },
{ name = "requests" },
]
sdist = { url = "https://files.pythonhosted.org/packages/27/e2/2ffc356cd72f1473d07c7719d82a8f2cbd261666828614ecb95b12169f41/mkdocs_material-9.7.1.tar.gz", hash = "sha256:89601b8f2c3e6c6ee0a918cc3566cb201d40bf37c3cd3c2067e26fadb8cce2b8", size = 4094392, upload-time = "2025-12-18T09:49:00.308Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-py3-none-any.whl", hash = "sha256:3f6100937d7d731f87f1e3e3b021c97f7239666b9ba1151ab476cabb96c60d5c", size = 9297166, upload-time = "2025-12-18T09:48:56.664Z" },
]
[[package]]
name = "mkdocs-material-extensions"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" },
]
[[package]]
name = "packaging"
version = "25.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
]
[[package]]
name = "paginate"
version = "0.5.7"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" },
]
[[package]]
name = "pathspec"
version = "0.12.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" },
]
[[package]]
name = "platformdirs"
version = "4.5.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" },
]
[[package]]
name = "pygments"
version = "2.19.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
]
[[package]]
name = "pymdown-extensions"
version = "10.19.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown" },
{ name = "pyyaml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/72/2d/9f30cee56d4d6d222430d401e85b0a6a1ae229819362f5786943d1a8c03b/pymdown_extensions-10.19.1.tar.gz", hash = "sha256:4969c691009a389fb1f9712dd8e7bd70dcc418d15a0faf70acb5117d022f7de8", size = 847839, upload-time = "2025-12-14T17:25:24.42Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fb/35/b763e8fbcd51968329b9adc52d188fc97859f85f2ee15fe9f379987d99c5/pymdown_extensions-10.19.1-py3-none-any.whl", hash = "sha256:e8698a66055b1dc0dca2a7f2c9d0ea6f5faa7834a9c432e3535ab96c0c4e509b", size = 266693, upload-time = "2025-12-14T17:25:22.999Z" },
]
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "six" },
]
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
]
[[package]]
name = "pyyaml"
version = "6.0.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
{ url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
{ url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
{ url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
{ url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
{ url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
{ url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
{ url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
{ url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
{ url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
{ url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
{ url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
{ url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
{ url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
{ url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
{ url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
{ url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
{ url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
{ url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
{ url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
{ url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
{ url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
{ url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
{ url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
{ url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
{ url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
{ url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
{ url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
{ url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
{ url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
{ url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
{ url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
{ url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
{ url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
{ url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
]
[[package]]
name = "pyyaml-env-tag"
version = "1.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyyaml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" },
]
[[package]]
name = "requests"
version = "2.32.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "charset-normalizer" },
{ name = "idna" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
]
[[package]]
name = "selectolax"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/fb/c5/959b8661d200d9fd3cf52061ce6f1d7087ec94823bb324fe1ca76c80b250/selectolax-0.4.6.tar.gz", hash = "sha256:bd9326cfc9bbd5bfcda980b0452b9761b3cf134015154e95d83fa32cbfbb751b", size = 4793248, upload-time = "2025-12-06T12:35:48.513Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ee/81/1fdf6633df840afd9d7054a3441f04cfb1fc9d098c6c9f3bd46a64fb632e/selectolax-0.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:20615062d6062b197b61fd646e667591e987be3a894e8a8408d2a482ccddc747", size = 2051021, upload-time = "2025-12-06T12:34:38.495Z" },
{ url = "https://files.pythonhosted.org/packages/cc/54/d59738d090cb4df3a3a6297b7ec216b86d3ba7f320013c4bc8d4841c9f5d/selectolax-0.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c436006e2af6ade80b96411cf46652c11ced4f230032e25e1f5210b7522a4fe3", size = 2047409, upload-time = "2025-12-06T12:34:39.875Z" },
{ url = "https://files.pythonhosted.org/packages/fc/67/3b163ec18a128df3a3b59ce676a2dacfb26e714da81ba8a98e184b4ef187/selectolax-0.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:705a70b6f4e553e8c5299881269d3735a7df8a23711927a33caa16b4eaef580f", size = 2237052, upload-time = "2025-12-06T12:34:41.24Z" },
{ url = "https://files.pythonhosted.org/packages/f0/04/c3ae4a77e8cfa647b9177e727a7e80f64b160b65ad0db0dcb3738a4ef4a0/selectolax-0.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c04fd180689ed9450ad2453a3cba74cff2475a4281f76db9e18a658b7823e594", size = 2275615, upload-time = "2025-12-06T12:34:43.114Z" },
{ url = "https://files.pythonhosted.org/packages/12/de/aaa016c44e63a1efd5525f6da6eac807388a06c70671091c735d93f13b74/selectolax-0.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cb33eb0809e70ba4a475105d164c3f90a4bb711744ca69e20037298256b8e9d7", size = 2249186, upload-time = "2025-12-06T12:34:44.84Z" },
{ url = "https://files.pythonhosted.org/packages/76/9a/a9cf9f0158b0804c7ea404d790af031830eb6452a4948853f7582eea6c51/selectolax-0.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:97f30b7c731f9f3328e9c6aef7ca3c17fbcbc4495e286a2cdad9a77bcadfadf1", size = 2282041, upload-time = "2025-12-06T12:34:46.19Z" },
{ url = "https://files.pythonhosted.org/packages/4c/ea/85de7ab8a9fc0301d1b428e69dc0bced9c1cd7379872d576a2b88eb91933/selectolax-0.4.6-cp312-cp312-win32.whl", hash = "sha256:f4375b352b609508e4a6980431dc6cc1812b97658ad1aa8caa61e01565de0d7d", size = 1727544, upload-time = "2025-12-06T12:34:47.541Z" },
{ url = "https://files.pythonhosted.org/packages/50/70/4aac2df64920112672cda846941d85c90b8152b2eddc9cf2615181551957/selectolax-0.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:1d02637a6746bf1ba7de1dfc00a0344ffb30bedd1b5d4e61727c960225bf6ce0", size = 1827825, upload-time = "2025-12-06T12:34:49.283Z" },
{ url = "https://files.pythonhosted.org/packages/8d/b0/09648383afed1a10df97ce30527d30714edc4072086915b4bb1a0d81a728/selectolax-0.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:bb0b371c3e2a94e6658ba4b5af88fc35aaf44f57f5a066ecaf96b4875a47aec4", size = 1775233, upload-time = "2025-12-06T12:34:51.576Z" },
{ url = "https://files.pythonhosted.org/packages/4d/87/7ed053cce7de8b29746c602851c67654287e25ec404d575911c6f40b671f/selectolax-0.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8303af0eeef6ab2f06f2b18e3c825df4f6984405a862c8e9e654621c26558aca", size = 2050412, upload-time = "2025-12-06T12:34:53.086Z" },
{ url = "https://files.pythonhosted.org/packages/44/74/e8cd9b9b53da0e849b27a2ef68580915321ee52a662f015275a1cf6cca85/selectolax-0.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d8b1fb5507d90e719de705d93eaa3bdd5f2d69facf012fb67b7da8a9cd20ea6b", size = 2046513, upload-time = "2025-12-06T12:34:54.583Z" },
{ url = "https://files.pythonhosted.org/packages/a0/ca/c1cc7f03b681d272dbb0dc47cf9d0df857ae156d7ea54d88cc25ec23c8e9/selectolax-0.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:30ac51bd5bfcd6bffe58720b1fc5f97666681f0793d79d292069b3a3f8599ef0", size = 2234404, upload-time = "2025-12-06T12:34:55.971Z" },
{ url = "https://files.pythonhosted.org/packages/df/00/a6e5c4d65243147fbd8837951267f098d9bee66ada4dc0c99d97259e052c/selectolax-0.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cfee4d1269fd462256641abdf6c2ee4dd799ba82c578acab0bcde07547637826", size = 2272678, upload-time = "2025-12-06T12:34:57.3Z" },
{ url = "https://files.pythonhosted.org/packages/ab/a2/05351e0f0da62d1bc01b7820a990f1e4dec688206e0278ee8faeeb878940/selectolax-0.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fce09eeb5dd19fba672a829f63e3c40238af4a929ce1e5fd16dcbc4fd253e300", size = 2247471, upload-time = "2025-12-06T12:34:59.048Z" },
{ url = "https://files.pythonhosted.org/packages/ea/95/1ab9e3ad2d53dbcd7141af149c7259c693dc2dc46e27d72b7a68f17f3364/selectolax-0.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4e97ca71d545cab9c57ba3b18358cbc96ef0dcd01920ea903a531386ca1f849", size = 2278271, upload-time = "2025-12-06T12:35:00.487Z" },
{ url = "https://files.pythonhosted.org/packages/04/41/ca80ca68fb9990cb46d753363d8dc0771225bc9bb9a77247a1987e52d2d5/selectolax-0.4.6-cp313-cp313-win32.whl", hash = "sha256:98fce08839cb8fd5d8788cbed2724cd892c78182cd923e246ef586d552a29a94", size = 1727514, upload-time = "2025-12-06T12:35:02.081Z" },
{ url = "https://files.pythonhosted.org/packages/f0/47/f048994ab32773fda8eda57a874afb30d0b2bf30952c009006e81737e07e/selectolax-0.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:cdc8e7833d2456331e519fe901d1c8844d120a26b21b6429f47ae946e65b0c04", size = 1829070, upload-time = "2025-12-06T12:35:03.857Z" },
{ url = "https://files.pythonhosted.org/packages/50/94/ab0d86b1a719c090e2bde9f7d9037439900e86fb50c258b5fd9f6530521b/selectolax-0.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:d6f8c976ad067a6607b2a052b141149ae23584819b73288c32f08a1ece23d60a", size = 1774935, upload-time = "2025-12-06T12:35:05.323Z" },
{ url = "https://files.pythonhosted.org/packages/d2/38/93b4907c596487e13f8261129b1663559c96fc37ea2c973c98a393307484/selectolax-0.4.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:961c037ac41a12bf9db35a95d6076461f9b127d361c9ef26a77f76c45b1056eb", size = 2069131, upload-time = "2025-12-06T12:35:06.737Z" },
{ url = "https://files.pythonhosted.org/packages/f6/04/d29769a8313ccb9db6278401840e79662f341b716d268f7468b6270d15e1/selectolax-0.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f2be076a69a12657a38933b99d31c604d95d63911e0799f89305da8e89918874", size = 2066134, upload-time = "2025-12-06T12:35:08.163Z" },
{ url = "https://files.pythonhosted.org/packages/29/a2/1c26b4cc6a708d234e39199bd75acdc1cfdcf4f3138e16d25e3e7aa0295d/selectolax-0.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25ae5dd7c20035312211de43952934981e8ff4e1502467ce78665f57bc5eaf7f", size = 2240810, upload-time = "2025-12-06T12:35:09.556Z" },
{ url = "https://files.pythonhosted.org/packages/1f/de/b021342d306bd08c92d0d9e9072a3ed6a3038f78387187d37fb775196fa0/selectolax-0.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cfe1274930affe2b986fd20cbf2916ddf4a760edf30a7eeb9151b31e8cbe6027", size = 2274401, upload-time = "2025-12-06T12:35:11.023Z" },
{ url = "https://files.pythonhosted.org/packages/29/77/b2816be2f4878f4e86fabca5932610401dc88d956948d97a82947e27d8bc/selectolax-0.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e52174a75236c1321f0f0353b5d97ba575c61428f16c2d78159cb154fa407e97", size = 2271054, upload-time = "2025-12-06T12:35:12.878Z" },
{ url = "https://files.pythonhosted.org/packages/1f/3a/ae48b9febf2af21a0fdd4fba07861ae3e13bd7235df3fa39918d33004058/selectolax-0.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5032187ed8f8d77da6dee5467a87fe9b1553c10c63671fe830e87a2d347ef8ae", size = 2297773, upload-time = "2025-12-06T12:35:14.383Z" },
{ url = "https://files.pythonhosted.org/packages/f9/8c/4e120b2b6dc55543bf2ffc43e78589022e59c02fa93dea0b4d22dfe3fc27/selectolax-0.4.6-cp314-cp314-win32.whl", hash = "sha256:8a4f50eb37abffe118730c062827a204a732cc1b9cd28b5dbf40752c371e9339", size = 1838289, upload-time = "2025-12-06T12:35:16.278Z" },
{ url = "https://files.pythonhosted.org/packages/c4/2f/51f15b06f4fcf9d5845d6bba534f7f2ed531f148c77ed8fff105cd770aa5/selectolax-0.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:11527cd430cb6d1d1a422209d87aec5767befff424f2affaa3daa2789878cf9f", size = 1938201, upload-time = "2025-12-06T12:35:17.694Z" },
{ url = "https://files.pythonhosted.org/packages/f3/8d/c099dcbb385e7d2145c4f19da183639bf4e429e1524dcba31ea311c5d276/selectolax-0.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:254d7f59580631dac1fcb861bb01f45039e3ac53187f07d8ccc3519110bacad0", size = 1886188, upload-time = "2025-12-06T12:35:19.538Z" },
{ url = "https://files.pythonhosted.org/packages/54/ce/f3fa904517745ecd289b6ce18845ae968cf7d0b17e65ab781259f2746254/selectolax-0.4.6-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:48a9bce389c281fec8bf3b5a36bd376f1ad1f36ff072dcedaa977188b3882be1", size = 2088107, upload-time = "2025-12-06T12:35:21.061Z" },
{ url = "https://files.pythonhosted.org/packages/4f/18/359da9723d3ec1235819cea0958bc417ce6a12699977920854b300ad4529/selectolax-0.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ff1632c1767f6005adc2dff7b72ea9d0b6493702e86e04ee5cf934ab630172aa", size = 2090836, upload-time = "2025-12-06T12:35:22.421Z" },
{ url = "https://files.pythonhosted.org/packages/e2/20/199f2a05ca8dfd9bc146af03fbfaa1e2050601d3141267070a2a023ea68f/selectolax-0.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:64a56dd91c22e809305e127bbd4cd75ad1016dd329a4e945a176c6f3376d00e2", size = 2248608, upload-time = "2025-12-06T12:35:23.946Z" },
{ url = "https://files.pythonhosted.org/packages/f9/67/261c06cdae29fad287008d39f51a80431f3fce66c2865b880e61d04cdfd2/selectolax-0.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:223471d2c9095406d69faf461aa217782575d62362d36809e350f6d3c2f69f4e", size = 2275653, upload-time = "2025-12-06T12:35:25.423Z" },
{ url = "https://files.pythonhosted.org/packages/10/80/f2519487a86b5366acadd9e07c212b38fb657bb62b9e01de9fb24c3ada27/selectolax-0.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8c1043e61df29f4f8ef49f9f623b3d710f0c545d9a7939eee52c49987b06aef7", size = 2283495, upload-time = "2025-12-06T12:35:26.843Z" },
{ url = "https://files.pythonhosted.org/packages/66/cc/35a0fd896371e0b5a84365b03f88c7dbe8984862e34ce32baed81ee6f486/selectolax-0.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b323ad4ebcf2edad2488022402fbc73ee158ffe81607ec1ce5eb1039eab94086", size = 2300207, upload-time = "2025-12-06T12:35:28.23Z" },
{ url = "https://files.pythonhosted.org/packages/51/b2/dc83cce2f38a3c72d6bf9268ef6c1708ab1b4d546074384e7e0d097bf4f6/selectolax-0.4.6-cp314-cp314t-win32.whl", hash = "sha256:f5017f6e2408160604c87fb21490d9af802d09dbc1b91ac89acd9922b7b04d31", size = 1890595, upload-time = "2025-12-06T12:35:29.625Z" },
{ url = "https://files.pythonhosted.org/packages/87/f9/8da49637643b1dffbcadc8972f1fee519c126978acaf5df59405e48424f4/selectolax-0.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:56adb0f014ab55627f20f53888a7bf1ec53aac8189fe344aec3d5077a7ad9889", size = 2005252, upload-time = "2025-12-06T12:35:31.116Z" },
{ url = "https://files.pythonhosted.org/packages/94/7f/f783e2254db082df4f6bc00fe3b32b9dd27c3b7302a44c8c37728bb67fb7/selectolax-0.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:66558cfb1c7402fed0f47b9a2692eed53e3e2f345526314b493b5093cb951e21", size = 1906079, upload-time = "2025-12-06T12:35:32.951Z" },
]
[[package]]
name = "six"
version = "1.17.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
]
[[package]]
name = "urllib3"
version = "2.6.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930, upload-time = "2025-12-11T15:56:40.252Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182, upload-time = "2025-12-11T15:56:38.584Z" },
]
[[package]]
name = "watchdog"
version = "6.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" },
{ url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" },
{ url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" },
{ url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" },
{ url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" },
{ url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" },
{ url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" },
{ url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" },
{ url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" },
{ url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" },
{ url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" },
{ url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" },
{ url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" },
{ url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" },
{ url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" },
{ url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" },
]