feat: create autonomous Nivora package repository

Maintain a validated cross-distribution package catalog.
Automate upstream updates, isolated builds, diagnostics, and direct publication.
Build the official GitHub Desktop sources for Linux with working OAuth.
This commit is contained in:
Александр
2026-07-25 00:49:32 +10:00
commit f405ae2bcf
168 changed files with 8483 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
* text=auto eol=lf
*.png binary
*.svg text eol=lf
*.sh text eol=lf
Staplerfile text eol=lf
@@ -0,0 +1,42 @@
name: Подготовить stplr-spec
description: Устанавливает закреплённые Go, системные инструменты и stplr-spec
inputs:
system-packages:
description: Дополнительные пакеты Ubuntu через пробел
required: false
default: ""
runs:
using: composite
steps:
- name: Установить Go
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5
with:
go-version: 1.26.5
cache: false
- name: Установить системные инструменты
if: ${{ inputs.system-packages != '' }}
shell: bash
env:
SYSTEM_PACKAGES: ${{ inputs.system-packages }}
run: |
sudo apt-get update
read -r -a packages <<<"$SYSTEM_PACKAGES"
sudo apt-get install -y "${packages[@]}"
- name: Собрать закреплённый stplr-spec
shell: bash
env:
STPLR_UTILS_COMMIT: c6ddbb5e4e5637d97bb7b2587729178d715c6c52
run: |
git clone --filter=blob:none \
https://altlinux.space/stapler/stplr-utils.git \
"$RUNNER_TEMP/stplr-utils"
git -C "$RUNNER_TEMP/stplr-utils" checkout "$STPLR_UTILS_COMMIT"
(
cd "$RUNNER_TEMP/stplr-utils"
GOBIN="$RUNNER_TEMP/bin" go install ./cmd/stplr-spec
)
echo "$RUNNER_TEMP/bin" >>"$GITHUB_PATH"
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

+201
View File
@@ -0,0 +1,201 @@
name: CI · Автономное обновление пакетов
on:
schedule:
# 03:00 Asia/Vladivostok (UTC+10)
- cron: "0 17 * * *"
workflow_dispatch:
permissions:
contents: write
issues: write
concurrency:
group: nivora-autonomous-updates
cancel-in-progress: false
defaults:
run:
shell: bash
jobs:
detect:
name: Найти обновления
runs-on: ubuntu-24.04
timeout-minutes: 30
outputs:
packages: ${{ steps.updates.outputs.packages }}
has_updates: ${{ steps.updates.outputs.has_updates }}
github_desktop_update: ${{ steps.updates.outputs.github_desktop_update }}
github_desktop_version: ${{ steps.updates.outputs.github_desktop_version }}
steps:
- name: Получить репозиторий
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Подготовить инструменты
uses: ./.github/actions/setup-stplr-spec
with:
system-packages: jq
- name: Сформировать план обновления
id: updates
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
packages_output="$(tools/package_updates.sh outdated)"
mapfile -t packages <<<"$packages_output"
if [[ -z "$packages_output" ]]; then
packages=()
fi
if [[ "${#packages[@]}" -eq 0 ]]; then
{
echo 'packages=[]'
echo 'has_updates=false'
echo 'github_desktop_update=false'
echo 'github_desktop_version='
} >>"$GITHUB_OUTPUT"
exit 0
fi
packages_json="$(
printf '%s\n' "${packages[@]}" |
jq -Rsc 'split("\n") | map(select(length > 0))'
)"
github_desktop_update=false
github_desktop_version=''
for package in "${packages[@]}"; do
if [[ "$package" == github-desktop ]]; then
github_desktop_update=true
read -r _ github_desktop_version < <(
tools/package_updates.sh check github-desktop
)
fi
done
{
echo "packages=${packages_json}"
echo 'has_updates=true'
echo "github_desktop_update=${github_desktop_update}"
echo "github_desktop_version=${github_desktop_version}"
} >>"$GITHUB_OUTPUT"
github-desktop:
name: Подготовить GitHub Desktop
needs: detect
if: ${{ needs.detect.outputs.github_desktop_update == 'true' }}
uses: ./.github/workflows/github-desktop-linux.yml
with:
version: ${{ needs.detect.outputs.github_desktop_version }}
publish: true
permissions:
contents: write
update:
name: Изолированно обновить и опубликовать
needs:
- detect
- github-desktop
if: >-
${{
always() &&
needs.detect.result == 'success' &&
needs.detect.outputs.has_updates == 'true'
}}
runs-on: ubuntu-24.04
timeout-minutes: 360
steps:
- name: Получить репозиторий
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
ref: main
fetch-depth: 0
- name: Подготовить инструменты
uses: ./.github/actions/setup-stplr-spec
with:
system-packages: jq rpm shellcheck
- name: Изолированно обновить и проверить каждый пакет
id: isolated
env:
GITHUB_TOKEN: ${{ github.token }}
PACKAGES_JSON: ${{ needs.detect.outputs.packages }}
AUTONOMOUS_UPDATE_RESULTS_DIR: ${{ runner.temp }}/update-results
run: |
mapfile -t packages < <(jq -r '.[]' <<<"$PACKAGES_JSON")
tools/autonomous_package_updates.sh "${packages[@]}"
successful_json="$(
jq -Rsc 'split("\n") | map(select(length > 0))' \
<"$AUTONOMOUS_UPDATE_RESULTS_DIR/successful-packages"
)"
failed_json="$(
cut -f1 "$AUTONOMOUS_UPDATE_RESULTS_DIR/failed-packages" |
jq -Rsc 'split("\n") | map(select(length > 0))'
)"
echo "successful=${successful_json}" >>"$GITHUB_OUTPUT"
echo "failed=${failed_json}" >>"$GITHUB_OUTPUT"
- name: Сохранить журналы и diff каждого пакета
if: ${{ always() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: autonomous-update-diagnostics-${{ github.run_id }}
path: ${{ runner.temp }}/update-results
if-no-files-found: error
retention-days: 30
- name: Применить только успешные обновления
env:
AUTONOMOUS_UPDATE_RESULTS_DIR: ${{ runner.temp }}/update-results
run: |
while IFS= read -r package; do
[[ -n "$package" ]] || continue
git apply \
"$AUTONOMOUS_UPDATE_RESULTS_DIR/${package}/update.patch"
done <"$AUTONOMOUS_UPDATE_RESULTS_DIR/successful-packages"
tools/sync_readme_versions.py
tools/run_checks.sh
- name: Отправить обновления прямо в main
id: publish
env:
SUCCESSFUL_PACKAGES: ${{ steps.isolated.outputs.successful }}
run: |
if git diff --quiet; then
echo 'Изменений после обновления нет'
exit 0
fi
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git add README.md ./*/Staplerfile
packages="$(jq -r 'join(", ")' <<<"$SUCCESSFUL_PACKAGES")"
git commit -m "chore: autonomously update ${packages}"
git pull --rebase origin main
git push origin HEAD:main
- name: Синхронизировать постоянные отчёты о сбоях
if: ${{ always() && steps.isolated.outcome == 'success' }}
env:
AUTONOMOUS_UPDATE_RESULTS_DIR: ${{ runner.temp }}/update-results
GH_TOKEN: ${{ github.token }}
run: tools/report_update_failures.sh
- name: Сообщить о несовместимых пакетах
if: ${{ always() && steps.isolated.outputs.failed != '[]' }}
env:
FAILED_PACKAGES: ${{ steps.isolated.outputs.failed }}
AUTONOMOUS_UPDATE_RESULTS_DIR: ${{ runner.temp }}/update-results
run: |
{
echo '## Требуется ручная диагностика'
echo
echo 'Обновление остальных пакетов продолжено и опубликовано.'
echo 'Для несовместимых пакетов сохранён artifact с логами и diff:'
echo
while IFS=$'\t' read -r package phase; do
printf -- '- **%s**: сбой на фазе **%s**\n' "$package" "$phase"
done <"$AUTONOMOUS_UPDATE_RESULTS_DIR/failed-packages"
} >>"$GITHUB_STEP_SUMMARY"
echo "::warning::Требуется ручная диагностика: $(jq -r 'join(\", \")' <<<"$FAILED_PACKAGES")"
+47
View File
@@ -0,0 +1,47 @@
name: CI · Ручная сборка пакетов
on:
workflow_dispatch:
inputs:
packages:
description: "all или package ID через пробел"
required: true
default: all
type: string
permissions:
contents: read
concurrency:
group: nivora-clean-build
cancel-in-progress: false
defaults:
run:
shell: bash
jobs:
build:
name: Изолированная сборка
runs-on: ubuntu-24.04
timeout-minutes: 300
steps:
- name: Получить репозиторий
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Подготовить инструменты
uses: ./.github/actions/setup-stplr-spec
- name: Собрать пакеты
env:
REQUESTED_PACKAGES: ${{ inputs.packages }}
run: |
if [[ "$REQUESTED_PACKAGES" == all ]]; then
tools/clean_build.sh --all
tools/verify_artifacts.sh --all
NIVORA_DEB_BUILD_MODE=host tools/test_package_lifecycle.sh
else
read -r -a packages <<<"$REQUESTED_PACKAGES"
tools/clean_build.sh "${packages[@]}"
tools/verify_artifacts.sh "${packages[@]}"
fi
+166
View File
@@ -0,0 +1,166 @@
name: CI · Сборка GitHub Desktop для Linux
on:
workflow_call:
inputs:
version:
description: "Версия desktop/desktop без префикса release-"
required: true
type: string
publish:
description: "Опубликовать tar.gz в GitHub Release Nivora"
required: false
default: true
type: boolean
workflow_dispatch:
inputs:
version:
description: "Версия desktop/desktop без префикса release-"
required: true
default: "3.6.3"
type: string
publish:
description: "Опубликовать tar.gz в GitHub Release Nivora"
required: true
default: true
type: boolean
permissions:
contents: write
concurrency:
group: nivora-github-desktop-linux-${{ inputs.version || '3.6.3' }}
cancel-in-progress: false
defaults:
run:
shell: bash
jobs:
build:
name: Linux ${{ matrix.arch }}
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-24.04
arch: x64
- runner: ubuntu-24.04-arm
arch: arm64
runs-on: ${{ matrix.runner }}
timeout-minutes: 180
steps:
- name: Установить Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: "24.15.0"
- name: Установить системные зависимости
run: |
sudo apt-get update
sudo apt-get install -y build-essential libsecret-1-dev pkg-config python3
- name: Получить официальные исходники GitHub Desktop
env:
VERSION: ${{ inputs.version || '3.6.3' }}
run: |
git clone \
--branch "release-${VERSION}" \
--depth 1 \
--recurse-submodules \
--shallow-submodules \
https://github.com/desktop/desktop.git \
github-desktop
git -C github-desktop remote set-url origin \
https://github.com/desktop/desktop.git
- name: Добавить поддержку OAuth callback в Linux
working-directory: github-desktop
run: |
main_process='app/src/main-process/main.ts'
perl -0pi -e \
"s/if \\(__DEV_SECRETS__\\) \\{\\n possibleProtocols\\.add\\('x-github-desktop-dev-auth'\\)\\n\\} else \\{\\n possibleProtocols\\.add\\('x-github-desktop-auth'\\)\\n\\}/possibleProtocols.add('x-github-desktop-auth')\\nif (__DEV_SECRETS__) {\\n possibleProtocols.add('x-github-desktop-dev-auth')\\n}/" \
"$main_process"
perl -0pi -e \
"s/if \\(__WIN32__ && args\\['protocol-launcher'\\] === true\\)/if ((__WIN32__ || __LINUX__) \\&\\& args['protocol-launcher'] === true)/" \
"$main_process"
grep -Fq "possibleProtocols.add('x-github-desktop-auth')" \
"$main_process"
grep -Fq "(__WIN32__ || __LINUX__)" "$main_process"
- name: Установить зависимости из upstream lockfile
working-directory: github-desktop
run: |
node vendor/yarn-1.21.1.js install --frozen-lockfile --non-interactive
- name: Собрать официальный Electron-клиент
working-directory: github-desktop
env:
NODE_ENV: production
RELEASE_CHANNEL: production
run: node vendor/yarn-1.21.1.js build:prod
- name: Упаковать Linux-сборку
env:
ARCH: ${{ matrix.arch }}
VERSION: ${{ inputs.version || '3.6.3' }}
run: |
dist="github-desktop/dist/desktop-linux-${ARCH}"
test -x "${dist}/desktop"
grep -aFq 'x-github-desktop-auth' \
"${dist}/resources/app/main.js"
mkdir -p package/github-desktop
cp -a "${dist}/." package/github-desktop/
printf '%s\n' "$(git -C github-desktop rev-parse HEAD)" \
>package/github-desktop/UPSTREAM_COMMIT
tar \
--sort=name \
--mtime='UTC 2020-01-01' \
--owner=0 \
--group=0 \
--numeric-owner \
-C package \
-cf "github-desktop-${VERSION}-linux-${ARCH}.tar" \
github-desktop
gzip -n -9 "github-desktop-${VERSION}-linux-${ARCH}.tar"
- name: Сохранить артефакт
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: github-desktop-${{ inputs.version || '3.6.3' }}-linux-${{ matrix.arch }}
path: github-desktop-${{ inputs.version || '3.6.3' }}-linux-${{ matrix.arch }}.tar.gz
if-no-files-found: error
retention-days: 14
publish:
name: Опубликовать Linux-артефакты
if: ${{ inputs.publish }}
needs: build
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- name: Скачать обе архитектуры
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
pattern: github-desktop-${{ inputs.version || '3.6.3' }}-linux-*
merge-multiple: true
- name: Создать контрольные суммы
run: sha256sum github-desktop-*.tar.gz >SHA256SUMS
- name: Создать или обновить GitHub Release
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
VERSION: ${{ inputs.version || '3.6.3' }}
run: |
tag="github-desktop-${VERSION}-linux"
if ! gh release view "$tag" >/dev/null 2>&1; then
gh release create "$tag" \
--title "GitHub Desktop ${VERSION} for Linux" \
--notes "Linux artifacts built by Nivora CI from the official desktop/desktop release-${VERSION} source and its pinned submodules."
fi
gh release upload "$tag" \
github-desktop-*.tar.gz \
SHA256SUMS \
--clobber
+49
View File
@@ -0,0 +1,49 @@
name: CI · Проверка репозитория
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: nivora-quality-${{ github.ref }}
cancel-in-progress: true
defaults:
run:
shell: bash
jobs:
quality:
name: Статика, метаданные и тесты
runs-on: ubuntu-24.04
timeout-minutes: 35
steps:
- name: Получить репозиторий
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Подготовить инструменты
uses: ./.github/actions/setup-stplr-spec
with:
system-packages: jq rpm shellcheck
- name: Выполнить проверки
run: tools/run_checks.sh
- name: Сверить upstream-версии
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
set +e
tools/package_updates.sh check-all
status=$?
set -e
if [[ "$status" -eq 10 ]]; then
echo "::warning::Для одного или нескольких пакетов доступны обновления"
elif [[ "$status" -ne 0 ]]; then
exit "$status"
fi
+11
View File
@@ -0,0 +1,11 @@
*.deb
*.rpm
*.pkg.tar.*
*.apk
.stplr-spec-bc-export/
__pycache__/
*.pyc
.venv/
.idea/
.vscode/
*~
+92
View File
@@ -0,0 +1,92 @@
# История изменений
В файле фиксируются значимые изменения Nivora.
## 2026-07-24 — GitHub Desktop
### Добавлено
- Добавлен GitHub Desktop 3.6.3 для `amd64` и `arm64`, собираемый напрямую из
официального `desktop/desktop` и официальных submodule-репозиториев GitHub.
- Сторонний Linux-форк `shiftkey/desktop` и готовые артефакты других авторов не
используются.
- Тяжёлая сборка вынесена в отдельный GitHub Actions workflow для нативных
`x64`/`arm64` runner’ов; результаты и SHA-256 публикуются в релизе Nivora.
## 2026-07-24 — очистка переходной инфраструктуры
### Изменено
- Удалены одноразовый мигратор прежнего каталога, переходные package ID,
совместимые команды и относящиеся к ним документация и тесты.
- Транзакционная проверка теперь тестирует обновление внутри Nivora без фиктивных
пакетов стороннего каталога.
- Codex обновлён до `26.721.31836`; новый upstream-сбросил `release` в `1`.
- У Яндекс Браузера canonical desktop-id совпадает с идентификатором окна и XDG
Portal, а прежний ID скрыт и сохранён только для совместимости: GNOME больше
не разделяет launcher и запущенное окно на два приложения.
## 2026-07-24 — Яндекс Браузер
### Добавлено
- В каталог добавлен Яндекс Браузер 26.4.1.1110 из официального stable-репозитория
для `amd64`.
- Сохранены штатные desktop-id, XDG Portal-интеграция, MIME-типы и иконки.
- Обновления браузера переданы Stapler: upstream cron-задача и автозапуск
системной настройки не устанавливаются.
## 2026-07-24 — Ventoy
### Добавлено
- В каталог добавлен Ventoy 1.1.16 с официальным нативным Linux GUI.
- Пакет поддерживает `amd64` и `arm64`, отображается в меню приложений как
**Ventoy** и запрашивает права на работу с накопителями через `pkexec`.
### Обновлено
- Clash Verge Rev обновлён до 2.5.2, Claude — до 1.24012.0,
Codex — до 26.721.30844, Happ — до 3.3.6, NetBird — до 0.75.0,
OpenCode — до 1.18.4.
- Tray-иконки Happ переработаны на основе оригинального разорванного H-знака:
графитовая версия для отключённого состояния и лаймовые акценты для соединения.
- Codex и OpenCode возвращены к чистым upstream-бинарникам: удалены runtime/ASAR-патчи,
wrapper OpenCode, лимиты памяти и GPU-fallback; сохранены только системная упаковка,
отображаемые названия и иконки.
- Для Codex восстановлен предусмотренный Linux Computer Use: bundled plugin и
backend `computer-use-linux` 0.4.1 собираются из закреплённых upstream-исходников,
а Linux-патчи взяты из `ilysenko/codex-desktop-linux` без обхода серверного
feature gate.
## 2026-07-19 — ClaudeAlt и balenaEtcher
### Добавлено
- В каталог добавлен balenaEtcher 2.1.6 для безопасной записи образов на
SD-карты и USB-накопители (`amd64`).
### Изменено
- Второй профиль Claude заменён самостоятельным приложением ClaudeAlt с отдельными
executable/resources tree, Electron `productName`, Wayland `app_id`, X11 `WM_CLASS`,
desktop-файлом, каталогом данных и Cowork VM socket.
- Старые команды второго аккаунта сохранены как совместимые алиасы ClaudeAlt.
- Существующий каталог `Claude-Account-2` автоматически используется после обновления,
чтобы сохранить авторизацию второго аккаунта.
- ClaudeAlt и Nivora получили новые PNG-иконки с настоящей
прозрачностью вне скруглённых границ.
- Иконка ClaudeAlt подключена к desktop-файлу и Electron `BrowserWindow`.
- Штатный tray-глиф Claude окрашен в оранжевый для основного приложения и в
бирюзовый для ClaudeAlt; большая app-иконка больше не используется в лотке.
- Размер и визуальные отступы иконки ClaudeAlt нормализованы под
upstream-иконку Claude.
## 2026-07-17 — первый релиз
### Добавлено
- Независимый Stapler-каталог из 15 пакетов.
- Новые документация, логотип, валидатор, тесты и GitHub Actions.
- Изолированная сборка всех пакетов и проверка фактического содержимого RPM.
- Транзакционные тесты установки, обновления и удаления DEB/RPM для критичных пакетов.
+40
View File
@@ -0,0 +1,40 @@
# Участие в Nivora
Проект принимает исправления рецептов, автоматизации и документации.
## Перед изменением
1. Проверьте официальное название, package ID, desktop-id и AppStream component-id.
2. Найдите официальный HTTPS-источник для каждой архитектуры.
3. Уточните лицензию и возможность переупаковки.
4. Сверьте зависимости на поддерживаемых дистрибутивах.
## Требования к пакету
- Имя каталога точно совпадает с `name`.
- Версия совпадает с upstream; изменение рецепта повышает `release`.
- Скачиваемые sources привязаны к версии и защищены SHA-256.
- `local:///`-файлы присутствуют в каталоге, а скрипты имеют executable bit.
- `provides`, `replaces` и `conflicts` описывают package names, а не имена бинарников или desktop-id.
- Lifecycle-скрипты идемпотентны и не удаляют пользовательские данные.
- `files()` описывает весь payload и не захватывает чужие пути.
## Проверка
```bash
tools/run_checks.sh
stplr-spec verify-checksums --path package/Staplerfile
tools/clean_build.sh package
```
Для изменения package ID нужны отдельные DEB/RPM-тесты замены и проверка
сохранности данных.
## Pull request
Один pull request должен решать одну задачу. В описании укажите:
- что изменилось;
- ссылку на upstream;
- выполненные проверки и сборки;
- оставшиеся ограничения.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Cheviiot
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+124
View File
@@ -0,0 +1,124 @@
<p align="center">
<img src=".github/assets/nivora.png" width="96" height="96" alt="Nivora">
</p>
# Nivora
**Независимый каталог Linux-приложений для Stapler.**
Nivora предлагает открытые рецепты упаковки десктопных приложений и
инструментов, которых может не быть в системном репозитории. Это не
официальный репозиторий Stapler и не официальные пакеты upstream-проектов.
<!-- package-count -->
**19 пакетов** · **6 категорий** · `amd64`, `arm64` и `all`
[![Проверка Nivora](https://github.com/Cheviiot/Nivora/actions/workflows/quality.yml/badge.svg)](https://github.com/Cheviiot/Nivora/actions/workflows/quality.yml)
## Подключение
Требуется [Stapler](https://stplr.dev/docs/intro/) `v0.1.1` или новее.
```bash
sudo stplr repo add nivora https://github.com/Cheviiot/Nivora.git
sudo stplr refresh
```
Установка пакета:
```bash
sudo stplr install nivora/codex
```
Проверить описание до установки:
```bash
stplr info nivora/codex
```
## Каталог
Название ведёт на источник приложения. `all` означает, что сам пакет не
содержит архитектурно-зависимых бинарников.
### Интернет, сеть и VPN
| Приложение | Версия | Архитектуры | Установка |
|:--|:--:|:--:|:--|
| [Clash Verge Rev](https://github.com/clash-verge-rev/clash-verge-rev) | `2.5.2` | `amd64`, `arm64` | `stplr install nivora/clash-verge-rev` |
| [Happ](https://happ.su/) | `3.3.6` | `amd64`, `arm64` | `stplr install nivora/happ` |
| [NetBird](https://netbird.io/) | `0.75.0` | `amd64`, `arm64` | `stplr install nivora/netbird` |
| [Tailscale](https://tailscale.com/) | `1.98.9` | `amd64`, `arm64` | `stplr install nivora/tailscale` |
| [Яндекс Браузер](https://browser.yandex.ru/) | `26.4.1.1110` | `amd64` | `stplr install nivora/yandex-browser-stable` |
### Удалённый доступ
| Приложение | Версия | Архитектуры | Установка |
|:--|:--:|:--:|:--|
| [Parsec](https://parsec.app/downloads) | `150-104a` | `amd64` | `stplr install nivora/parsec` |
### AI и разработка
| Приложение | Версия | Архитектуры | Установка |
|:--|:--:|:--:|:--|
| [Chatbox](https://chatboxai.app/ru) | `1.20.3` | `amd64`, `arm64` | `stplr install nivora/chatbox` |
| [Claude](https://code.claude.com/docs/en/desktop-quickstart) | `1.24012.0` | `amd64`, `arm64` | `stplr install nivora/claude-desktop` |
| [Codex](docs/packages/codex.md) | `26.721.31836` | `amd64` | `stplr install nivora/codex` |
| [GitHub Desktop](docs/packages/github-desktop.md) | `3.6.3` | `amd64`, `arm64` | `stplr install nivora/github-desktop` |
| [OpenCode](https://opencode.ai/) | `1.18.4` | `amd64`, `arm64` | `stplr install nivora/opencode` |
### Рабочий стол
| Приложение | Версия | Архитектуры | Установка |
|:--|:--:|:--:|:--|
| [Adwyra](https://github.com/Cheviiot/Adwyra) | `0.6.1` | `all` | `stplr install nivora/adwyra` |
| [AniDesk](https://github.com/theDesConnet/AniDesk) | `0.0.1-beta.7` | `amd64` | `stplr install nivora/anidesk` |
### Игры
| Приложение | Версия | Архитектуры | Установка |
|:--|:--:|:--:|:--|
| [PineconeMC](https://pineconemc.com/) | `11.0.3` | `amd64`, `arm64` | `stplr install nivora/pineconemc` |
| [Vual](https://github.com/Cheviiot/Vual) | `0.3.1` | `all` | `stplr install nivora/vual` |
### Системные инструменты
| Приложение | Версия | Архитектуры | Установка |
|:--|:--:|:--:|:--|
| [balenaEtcher](https://etcher.balena.io/) | `2.1.6` | `amd64` | `stplr install nivora/balena-etcher` |
| [Fisher](https://github.com/jorgebucaran/fisher) | `4.4.8` | `all` | `stplr install nivora/fisher` |
| [Nivora Stapler Helper](docs/packages/nivora-stplr.md) | `0.3.0` | `all` | `stplr install nivora/nivora-stplr` |
| [Ventoy](docs/packages/ventoy.md) | `1.1.17` | `amd64`, `arm64` | `stplr install nivora/ventoy` |
## Обновление
```bash
sudo stplr refresh
sudo stplr upgrade
```
Рецепты сохраняют пути пользовательских конфигураций. Обычное обновление и
удаление пакета не должно сбрасывать настройки или выполнять logout.
## Безопасность и доверие
- Каждый `Staplerfile` доступен для проверки.
- Файлы загружаются из указанных upstream-источников.
- SHA-256 проверяет целостность загрузки, но не делает upstream автоматически безопасным.
- Условия проприетарных приложений определяются их разработчиками.
- Наличие CI не обещает абсолютную безопасность или совместимость с любой системой.
Подробнее: [модель доверия](docs/security-model.md) и [политика безопасности](SECURITY.md).
## Для сопровождающих
```bash
tools/run_checks.sh
tools/package_updates.sh check-all
tools/clean_build.sh --all
tools/verify_artifacts.sh --all
tools/test_package_lifecycle.sh
```
Правила изменений описаны в [CONTRIBUTING.md](CONTRIBUTING.md), а порядок сопровождения — в
[docs/maintenance.md](docs/maintenance.md).
+27
View File
@@ -0,0 +1,27 @@
# Политика безопасности
## Что относится к Nivora
Сообщайте о проблемах в:
- `Staplerfile`, wrappers и lifecycle-скриптах;
- неверных source URL или checksum;
- опасном поведении при установке, обновлении или удалении;
- потере или неожиданной передаче пользовательских данных из-за логики пакета;
- секретах, случайно попавших в репозиторий.
Уязвимости самого приложения следует передавать upstream-разработчику.
## Как сообщить
Для обычной ошибки создайте [issue](https://github.com/Cheviiot/Nivora/issues) и укажите:
- пакет и архитектуру;
- дистрибутив и версию Stapler;
- точную команду и полный текст ошибки;
- ожидаемое и фактическое поведение.
Не публикуйте пароли, API-ключи, setup keys, cookies и диагностические архивы без
предварительной проверки.
Подробная модель доверия: [docs/security-model.md](docs/security-model.md).
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
package_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
repo_root="$(cd "${package_dir}/.." && pwd)"
exec "${repo_root}/tools/package_updates.sh" check "$(basename "$package_dir")"
+13
View File
@@ -0,0 +1,13 @@
Сведения о лицензии пакета Adwyra
Лицензия: GPL-3.0-or-later.
Исходный проект: https://github.com/Cheviiot/Adwyra
Нюансы пакета:
- Пакет устанавливает исходный Python/GTK-проект Adwyra в системную файловую структуру.
- Код приложения распространяется на условиях GNU GPL версии 3 или любой более поздней версии.
- При распространении измененных версий нужно сохранять условия GPL и предоставлять соответствующий исходный код.
- Пакет не добавляет закрытых бинарных компонентов.
Полный официальный текст GPL-3.0:
https://www.gnu.org/licenses/gpl-3.0.txt
+123
View File
@@ -0,0 +1,123 @@
name='adwyra'
version='0.6.1'
release=3
summary='Minimal GNOME application launcher'
summary_ru='Минималистичный лаунчер приложений для GNOME'
desc='Adwyra is a compact GTK 4/libadwaita application launcher for GNOME with an application grid, search, folders, pinned apps, drag-and-drop ordering, themes, hotkeys and plugin support.'
desc_ru='Adwyra — компактный лаунчер приложений для GNOME на GTK 4/libadwaita с сеткой приложений, поиском, папками, закреплением, drag-and-drop сортировкой, темами, горячими клавишами и плагинами.'
group='Graphical desktop/GNOME'
homepage='https://github.com/Cheviiot/Adwyra'
maintainer='Cheviiot <https://github.com/Cheviiot>'
architectures=('all')
license=('GPL-3.0-or-later')
provides=()
replaces=('adwyra')
conflicts=()
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
# Зависимости для запуска
deps=(
'python3'
'gtk4'
'libadwaita'
)
# Скрипты установки/удаления
scripts=(
['postinstall']='postinstall.sh'
['postremove']='postremove.sh'
)
deps_debian=(
'python3'
'python3-gi'
'gir1.2-gtk-4.0'
'gir1.2-adw-1'
)
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=(
'python3'
'python3-gobject'
'gtk4'
'libadwaita'
)
deps_arch=(
'python'
'python-gobject'
'gtk4'
'libadwaita'
)
deps_altlinux=(
'python3'
'python3-module-pygobject3'
'libgtk4'
'libadwaita'
)
deps_opensuse=(
'python3'
'python3-gobject'
'gtk4'
'libadwaita'
)
# Источники - используем архив для предотвращения проблем с кэшем
sources=(
"https://github.com/Cheviiot/adwyra/archive/refs/tags/v${version}.tar.gz"
'local:///LICENSE'
)
checksums=(
'4acb67460a6d302d5c60c369fe68e616baff2aeafb760c421cb65a54dc8df39a'
'4acd6128d4fbff11fd374e862520cf93aacad91650de1fe16fc4e424bd1ddc14'
)
# AppStream
appstream_app_id='com.github.adwyra'
package() {
# GitHub создаёт архив с именем репозитория (Adwyra)
src="${srcdir}/Adwyra-${version}"
# Бинарный файл
install -Dm755 "${src}/adwyra" "${pkgdir}/usr/bin/adwyra"
# Python модуль
install -dm755 "${pkgdir}/usr/lib/adwyra"
cp -r "${src}/src/adwyra" "${pkgdir}/usr/lib/adwyra/"
find "${pkgdir}/usr/lib/adwyra" -depth -path '*/__pycache__*' -delete
# Обновляем путь в бинарнике
sed -i 's|os.path.join(os.path.dirname(os.path.abspath(__file__)), "src")|'\''/usr/lib/adwyra'\''|' \
"${pkgdir}/usr/bin/adwyra"
# Desktop файл
install-desktop "${src}/data/com.github.adwyra.desktop"
# Иконки всех размеров
for size in 16 24 32 48 64 128 256 512; do
install -Dm644 "${src}/data/icons/hicolor/${size}x${size}/apps/com.github.adwyra.png" \
"${pkgdir}/usr/share/icons/hicolor/${size}x${size}/apps/com.github.adwyra.png"
done
# AppStream metainfo
install -Dm644 "${src}/data/com.github.adwyra.metainfo.xml" \
"${pkgdir}/usr/share/metainfo/com.github.adwyra.metainfo.xml"
# Лицензия
install-license "${src}/LICENSE" "adwyra/LICENSE.upstream"
install-license "${srcdir}/LICENSE" "adwyra/LICENSE.nivora"
}
files() {
files-find-binary
files-find-desktop
files-find-license
files-find \
"/usr/lib/adwyra/**/*" \
"/usr/share/icons/hicolor/*/apps/com.github.adwyra.png" \
"/usr/share/metainfo/com.github.adwyra.metainfo.xml"
}
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
# Обновляем кэш иконок
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
# Обновляем desktop базу данных
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
# Обновляем кэш иконок
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
# Обновляем desktop базу данных
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
+1
View File
@@ -0,0 +1 @@
[inherit]
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
package_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
repo_root="$(cd "${package_dir}/.." && pwd)"
exec "${repo_root}/tools/package_updates.sh" check "$(basename "$package_dir")"
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
set -euo pipefail
new_version="${1:?new version is required}"
package_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
staplerfile="${package_dir}/Staplerfile"
debian_version="${new_version/-beta./.beta.}"
stplr-spec set-field --path "$staplerfile" version "$new_version"
stplr-spec set-field --path "$staplerfile" release 1
stplr-spec set-field --path "$staplerfile" debian_version "$debian_version"
stplr-spec update-checksums --path "$staplerfile"
+21
View File
@@ -0,0 +1,21 @@
Сведения о лицензии пакета AniDesk
Лицензия: GPL-2.0-only.
Исходный проект: https://github.com/theDesConnet/AniDesk
Официальная страница релизов: https://github.com/theDesConnet/AniDesk/releases
Сайт проекта: https://anidesk.ds1nc.ru
Нюансы пакета:
- Пакет перепаковывает официальный Linux deb-артефакт AniDesk из релиза theDesConnet/AniDesk.
- AniDesk является неофициальным desktop-клиентом Anixart; сервис Anixart, учетные записи, API и сетевые функции не входят в пакет и регулируются правилами соответствующих сервисов.
- Встроенный видеоплеер использует внешние источники и сетевые запросы приложения. Доступность отдельных источников зависит от upstream-приложения и внешних сервисов.
- Anime4K-апскейл использует WebGPU/Vulkan. Для него нужны рабочие драйверы GPU, Vulkan loader и Mesa/проприетарный Vulkan-драйвер; без них базовый запуск приложения возможен, но ускорение видео может быть недоступно.
- Команда `anidesk` запускает upstream-приложение без отключения GPU. Если из-за драйвера Vulkan/GLX приложение падает на старте, можно временно запустить `ANIDESK_DISABLE_GPU=1 anidesk`; этот режим отключает GPU-ускорение Chromium и не предназначен для Anime4K/WebGPU.
- Upstream deb поставляет иконку `anidesk.png` как ICO-контейнер с неверным расширением. Пакет Nivora заменяет ее на PNG-экспорт того же изображения, чтобы desktop-меню корректно показывали иконку.
- Upstream-приложение создает API-клиент только при старте renderer-страницы. Пакет Nivora добавляет runtime-патч: после смены endpoint API в настройках renderer автоматически перезагружается, чтобы контент сразу грузился с выбранного адреса.
- Discord Rich Presence работает только при установленном и запущенном Discord-клиенте с доступным локальным IPC.
- Пользовательские настройки, авторизация, кэш, история и загруженные данные создаются приложением в домашнем каталоге пользователя после запуска и не поставляются внутри пакета.
- В `/usr/share/licenses/anidesk` дополнительно устанавливаются bundled-лицензии Electron/Chromium, которые поставляет upstream-артефакт.
Полный официальный текст GPL-2.0:
https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+236
View File
@@ -0,0 +1,236 @@
name='anidesk'
version='0.0.1-beta.7'
debian_version='0.0.1.beta.7'
release=5
summary='Unofficial Anixart desktop client'
summary_ru='Неофициальный desktop-клиент Anixart'
group='Video/Players'
desc='AniDesk is an unofficial Electron desktop client for Anixart with account support, a built-in video player and Anime4K WebGPU upscaling.'
desc_ru='AniDesk — неофициальный Electron desktop-клиент Anixart с поддержкой аккаунта, встроенным видеоплеером и Anime4K-апскейлом через WebGPU.'
homepage='https://github.com/theDesConnet/AniDesk'
maintainer='DesConnet, hack1exe <https://github.com/theDesConnet/AniDesk>'
architectures=('amd64')
license=('GPL-2.0-only')
provides=()
replaces=('anidesk')
conflicts=()
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
deps=()
deps_debian=('libgtk-3-0' 'libnotify4' 'libnss3' 'xdg-utils' 'libatspi2.0-0' 'libdrm2' 'libgbm1' 'libxcb-dri3-0' 'libasound2' 'libvulkan1' 'mesa-vulkan-drivers')
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=('gtk3' 'libnotify' 'nss' 'xdg-utils' 'at-spi2-core' 'libdrm' 'mesa-libgbm' 'libxcb' 'alsa-lib' 'vulkan-loader' 'mesa-vulkan-drivers')
deps_arch=('gtk3' 'libnotify' 'nss' 'xdg-utils' 'at-spi2-core' 'libdrm' 'mesa' 'libxcb' 'alsa-lib' 'vulkan-icd-loader' 'vulkan-radeon')
deps_opensuse=('gtk3' 'libnotify4' 'mozilla-nss' 'xdg-utils' 'at-spi2-core' 'libdrm2' 'libgbm1' 'libxcb-dri3-0' 'libasound2' 'libvulkan1' 'Mesa-vulkan-device-select')
deps_altlinux=('libgtk+3' 'libnotify' 'libnss' 'xdg-utils' 'libat-spi2-core' 'libdrm' 'libgbm' 'libxcb' 'libalsa' 'libvulkan1' 'mesa-dri-drivers')
build_deps=('binutils')
build_deps_debian=('binutils' 'python3')
build_deps_ubuntu=("${build_deps_debian[@]}")
build_deps_fedora=('binutils' 'python3')
build_deps_arch=('binutils' 'python')
build_deps_opensuse=('binutils' 'python3')
build_deps_altlinux=('binutils' 'python3')
opt_deps=()
opt_deps_debian=('gnome-keyring' 'discord')
opt_deps_ubuntu=("${opt_deps_debian[@]}")
opt_deps_fedora=('gnome-keyring' 'discord')
opt_deps_arch=('gnome-keyring' 'discord')
opt_deps_opensuse=('gnome-keyring' 'discord')
opt_deps_altlinux=('gnome-keyring')
sources=(
"https://github.com/theDesConnet/AniDesk/releases/download/v${version}/anidesk_${debian_version}_amd64.deb?~archive=false&~name=anidesk.deb"
"local:///anidesk.png"
"local:///anidesk-wrapper"
"local:///LICENSE"
)
checksums=(
'sha256:70fc1207824757f7ddab63f9e210f31bb907ea43dee3d2827fe79c3ef00252b6'
'sha256:6d7791424bd7db712b121f7e66206289b1d31eb2a8ea998d9aeced2c7075dfe8'
'sha256:f58b9f720bd63ff49053f949dd3f0a738afc2aa2cee57fedbd2b73440c123721'
'sha256:892e6b268f44a850a102cc6367fec45853fe3556840d44a6106f08487553e6c4'
)
scripts=(
['postinstall']='postinstall.sh'
['postremove']='postremove.sh'
)
package() {
cd "${srcdir}"
mkdir -p "${srcdir}/extracted"
ar x anidesk.deb
tar -xf data.tar.* -C "${srcdir}/extracted"
install -dm755 "${pkgdir}/usr/lib"
cp -a "${srcdir}/extracted/usr/lib/anidesk" "${pkgdir}/usr/lib/"
chmod 0755 "${pkgdir}/usr/lib/anidesk/chrome-sandbox"
chmod 755 "${pkgdir}/usr/lib/anidesk/anidesk"
python3 - "${pkgdir}/usr/lib/anidesk/resources/app.asar" <<'PY'
import copy
import hashlib
import json
import struct
import sys
from pathlib import Path
asar_path = Path(sys.argv[1])
data = asar_path.read_bytes()
_, _, _, header_size = struct.unpack("<IIII", data[:16])
header = json.loads(data[16 : 16 + header_size])
base_offset = 16 + ((header_size + 3) & ~3)
def iter_files(node, prefix=""):
for name, entry in node.get("files", {}).items():
path_name = f"{prefix}{name}"
if "files" in entry:
yield from iter_files(entry, f"{path_name}/")
else:
yield path_name, entry
entries = []
for path_name, entry in iter_files(header):
if "offset" not in entry or "size" not in entry:
continue
offset = int(entry["offset"])
size = int(entry["size"])
content = data[base_offset + offset : base_offset + offset + size]
entries.append((path_name, content))
patched = {}
bundle_patch_applied = False
source_patch_applied = False
for path_name, content in entries:
if path_name == "public/build/bundle.js":
text = content.decode()
old = (
'onChangeCallback:(e,t)=>{x.set(t),set(o,!0),'
'baseSettings.restartRequired=!0}'
)
new = (
'onChangeCallback:(e,t)=>{get(n)!==t&&(x.set(t),set(o,!0),'
'baseSettings.restartRequired=!0,queueMicrotask(()=>location.reload()))}'
)
if old not in text:
raise SystemExit("AniDesk ASAR patch failed: endpoint dropdown marker not found")
patched[path_name] = text.replace(old, new, 1).encode()
bundle_patch_applied = True
elif path_name == "src/app/settings-pages/SettingsApp.svelte":
text = content.decode()
old = """ onChangeCallback={(e, v) => {
endpointUrlRaw.set(v);
restartRequired = true;
baseSettings.restartRequired = true;
}}
"""
new = """ onChangeCallback={(e, v) => {
if (endpointUrl === v) {
return;
}
endpointUrlRaw.set(v);
restartRequired = true;
baseSettings.restartRequired = true;
queueMicrotask(() => window.location.reload());
}}
"""
if old not in text:
raise SystemExit("AniDesk ASAR patch failed: endpoint source marker not found")
patched[path_name] = text.replace(old, new, 1).encode()
source_patch_applied = True
if not bundle_patch_applied:
raise SystemExit("AniDesk ASAR patch failed: runtime bundle was not patched")
if not source_patch_applied:
raise SystemExit("AniDesk ASAR patch failed: source view was not patched")
def rebuild(node, prefix="", offset_state=None, payload=None):
if offset_state is None:
offset_state = {"offset": 0}
if payload is None:
payload = []
new_node = copy.deepcopy(node)
for name, entry in list(new_node.get("files", {}).items()):
path_name = f"{prefix}{name}"
if "files" in entry:
new_node["files"][name] = rebuild(
entry, f"{path_name}/", offset_state, payload
)
continue
content = None
for candidate_path, candidate_content in entries:
if candidate_path == path_name:
content = patched.get(path_name, candidate_content)
break
if content is None:
continue
entry["size"] = len(content)
entry["offset"] = str(offset_state["offset"])
digest = hashlib.sha256(content).hexdigest()
entry["integrity"] = {
"algorithm": "SHA256",
"hash": digest,
"blockSize": 4194304,
"blocks": [digest],
}
offset_state["offset"] += len(content)
payload.append(content)
if prefix == "":
return new_node, payload
return new_node
new_header, payload = rebuild(header)
header_json = json.dumps(new_header, separators=(",", ":")).encode()
header_padded_size = (len(header_json) + 3) & ~3
asar_path.write_bytes(
struct.pack("<IIII", 4, header_padded_size + 8, header_padded_size + 4, len(header_json))
+ header_json
+ (b"\0" * (header_padded_size - len(header_json)))
+ b"".join(payload)
)
PY
install -dm755 "${pkgdir}/usr/bin"
install -Dm755 anidesk-wrapper "${pkgdir}/usr/bin/anidesk"
install -dm755 "${pkgdir}/usr/share"
cp -a "${srcdir}/extracted/usr/share/applications" "${pkgdir}/usr/share/"
cp -a "${srcdir}/extracted/usr/share/doc" "${pkgdir}/usr/share/"
cp -a "${srcdir}/extracted/usr/share/pixmaps" "${pkgdir}/usr/share/"
install -Dm644 anidesk.png "${pkgdir}/usr/share/pixmaps/anidesk.png"
install -dm755 "${pkgdir}/usr/share/icons/hicolor/256x256/apps"
install -Dm644 anidesk.png "${pkgdir}/usr/share/icons/hicolor/256x256/apps/anidesk.png"
install-license LICENSE "anidesk/LICENSE.nivora"
install-license "${srcdir}/extracted/usr/lib/anidesk/LICENSES.chromium.html" \
"anidesk/LICENSES.chromium.html"
install-license "${srcdir}/extracted/usr/share/doc/anidesk/copyright" \
"anidesk/LICENSE.electron"
}
files() {
files-find-binary
files-find-desktop
files-find-license
files-find \
"/usr/lib/anidesk/**/*" \
"/usr/share/doc/anidesk/*" \
"/usr/share/icons/hicolor/256x256/apps/anidesk.png" \
"/usr/share/pixmaps/anidesk.png"
}
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
set -euo pipefail
binary="${ANIDESK_BINARY:-/usr/lib/anidesk/anidesk}"
flags=()
case "${ANIDESK_DISABLE_GPU:-0}" in
1 | true | yes | on)
flags+=(
'--disable-gpu'
'--disable-gpu-compositing'
'--disable-software-rasterizer'
)
;;
esac
exec "${binary}" "${flags[@]}" "$@"
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

+35
View File
@@ -0,0 +1,35 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
sandbox=/usr/lib/anidesk/chrome-sandbox
if [[ ! -f "$sandbox" ]]; then
echo "Ошибка: не найден Electron sandbox: ${sandbox}" >&2
exit 1
fi
if [[ -L /proc/self/ns/user ]] && command -v unshare >/dev/null 2>&1 \
&& unshare --user true 2>/dev/null; then
chmod 0755 "$sandbox"
else
chmod 4755 "$sandbox"
fi
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
if command -v kbuildsycoca6 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca6 --noincremental
elif command -v kbuildsycoca5 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca5 --noincremental
fi
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
if command -v kbuildsycoca6 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca6 --noincremental
elif command -v kbuildsycoca5 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca5 --noincremental
fi
+1
View File
@@ -0,0 +1 @@
include = "../stapler-repo.toml"
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
package_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
repo_root="$(cd "${package_dir}/.." && pwd)"
exec "${repo_root}/tools/package_updates.sh" check "$(basename "$package_dir")"
+144
View File
@@ -0,0 +1,144 @@
name='balena-etcher'
version='2.1.6'
release=1
summary='Safe SD card and USB drive image flasher'
summary_ru='Безопасная запись образов на SD-карты и USB-накопители'
group='System/Configuration/Hardware'
desc='balenaEtcher is a graphical utility for writing operating-system images to SD cards and USB drives with validation after flashing.'
desc_ru='balenaEtcher — графическая утилита для записи образов операционных систем на SD-карты и USB-накопители с проверкой данных после прошивки.'
homepage='https://etcher.balena.io/'
maintainer='Balena Ltd. <hello@balena.io>'
architectures=('amd64')
license=('Apache-2.0')
provides=()
replaces=('balena-etcher')
conflicts=()
# Electron и нативный helper поставляются в официальном DEB.
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
deps=()
deps_debian=(
'libgtk-3-0' 'libnotify4' 'libnss3' 'libxtst6' 'xdg-utils'
'libatspi2.0-0' 'libdrm2' 'libgbm1' 'libxcb-dri3-0' 'libasound2'
'libatk1.0-0' 'libc6' 'libcairo2' 'libcups2' 'libdbus-1-3' 'libexpat1'
'libfontconfig1' 'libfreetype6' 'libgcc1' 'libgdk-pixbuf2.0-0 | libgdk-pixbuf-2.0-0'
'libglib2.0-0' 'liblzma5' 'libnspr4' 'libpango1.0-0 | libpango-1.0-0'
'libstdc++6' 'libx11-6' 'libxcomposite1' 'libxcursor1' 'libxdamage1'
'libxext6' 'libxfixes3' 'libxi6' 'libxrandr2' 'libxrender1' 'libxss1'
'kde-cli-tools | kde-runtime | trash-cli | libglib2.0-bin | gvfs-bin'
'polkit-1-auth-agent | policykit-1-gnome | polkit-kde-1'
)
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=(
'gtk3' 'libnotify' 'nss' 'libXtst' 'xdg-utils' 'at-spi2-core'
'libdrm' 'mesa-libgbm' 'libxcb' 'alsa-lib' 'atk' 'glibc' 'cairo'
'cups-libs' 'dbus-libs' 'expat' 'fontconfig' 'freetype' 'libgcc'
'gdk-pixbuf2' 'glib2' 'xz-libs' 'nspr' 'pango' 'libstdc++' 'libX11'
'libXcomposite' 'libXcursor' 'libXdamage' 'libXext' 'libXfixes' 'libXi'
'libXrandr' 'libXrender' 'libXScrnSaver' 'polkit'
)
deps_arch=(
'gtk3' 'libnotify' 'nss' 'libxtst' 'xdg-utils' 'at-spi2-core'
'libdrm' 'mesa' 'libxcb' 'alsa-lib' 'atk' 'glibc' 'cairo' 'cups'
'dbus' 'expat' 'fontconfig' 'freetype2' 'gcc-libs' 'gdk-pixbuf2'
'glib2' 'xz' 'nspr' 'pango' 'libx11' 'libxcomposite' 'libxcursor'
'libxdamage' 'libxext' 'libxfixes' 'libxi' 'libxrandr' 'libxrender'
'libxss' 'polkit'
)
deps_opensuse=(
'gtk3' 'libnotify4' 'mozilla-nss' 'libXtst6' 'xdg-utils' 'at-spi2-core'
'libdrm2' 'libgbm1' 'libxcb-dri3-0' 'libasound2' 'libatk-1_0-0'
'glibc' 'libcairo2' 'libcups2' 'libdbus-1-3' 'libexpat1'
'fontconfig' 'libfreetype6' 'libgcc_s1' 'gdk-pixbuf-loader-rsvg'
'glib2' 'liblzma5' 'mozilla-nspr' 'libpango-1_0-0' 'libstdc++6'
'libX11-6' 'libXcomposite1' 'libXcursor1' 'libXdamage1' 'libXext6'
'libXfixes3' 'libXi6' 'libXrandr2' 'libXrender1' 'libXss1' 'polkit'
)
deps_altlinux=(
'libgtk+3' 'libnotify' 'libnss' 'libXtst' 'xdg-utils' 'libat-spi2-core'
'libdrm' 'libgbm' 'libxcb' 'libalsa' 'libatk' 'glibc' 'libcairo'
'libcups' 'libdbus' 'libexpat' 'fontconfig' 'libfreetype' 'libgcc1'
'libgdk-pixbuf' 'glib2' 'liblzma' 'libnspr' 'libpango' 'libstdc++6'
'libX11' 'libXcomposite' 'libXcursor' 'libXdamage' 'libXext' 'libXfixes'
'libXi' 'libXrandr' 'libXrender' 'libXScrnSaver' 'polkit'
)
opt_deps=()
opt_deps_debian=()
opt_deps_ubuntu=("${opt_deps_debian[@]}")
opt_deps_fedora=('polkit-gnome | polkit-kde' 'gvfs')
opt_deps_arch=('polkit-gnome | polkit-kde-agent' 'gvfs')
opt_deps_opensuse=('polkit-gnome | polkit-kde-agent-6' 'glib2-tools')
opt_deps_altlinux=(
'polkit-gnome | polkit-kde-agent | lxqt-policykit | xfce-polkit'
'gvfs'
)
build_deps=('binutils')
sources=(
"https://github.com/balena-io/etcher/releases/download/v${version}/balena-etcher_${version}_amd64.deb?~archive=false&~name=balena-etcher.deb"
"https://raw.githubusercontent.com/balena-io/etcher/v${version}/LICENSE?~archive=false&~name=LICENSE"
)
checksums=(
'sha256:2bdebb46c9f750a9abf11c188ff69a405b4a4fed114333d634c3b3fe59a64057'
'sha256:39445b459f86621683f9731fb6a7d070819dc379840e8ef52f62bb1c68942291'
)
scripts=(
['postinstall']='postinstall.sh'
['postremove']='postremove.sh'
)
package() {
cd "${srcdir}"
mkdir -p "${srcdir}/extracted"
ar x balena-etcher.deb
tar -xf data.tar.* -C "${srcdir}/extracted"
install -dm755 "${pkgdir}/usr/lib"
cp -a "${srcdir}/extracted/usr/lib/balena-etcher" "${pkgdir}/usr/lib/"
chmod 0755 "${pkgdir}/usr/lib/balena-etcher/chrome-sandbox"
chmod 0755 \
"${pkgdir}/usr/lib/balena-etcher/balena-etcher" \
"${pkgdir}/usr/lib/balena-etcher/resources/etcher-util"
# Upstream DEB содержит balenaEtcher-ссылку на каталог GitHub Actions.
# Сохраняем совместимое имя, но направляем его на локальный executable.
ln -sfn balena-etcher "${pkgdir}/usr/lib/balena-etcher/balenaEtcher"
install -dm755 "${pkgdir}/usr/bin"
ln -sfn ../lib/balena-etcher/balena-etcher \
"${pkgdir}/usr/bin/balena-etcher"
install -Dm644 \
"${srcdir}/extracted/usr/share/applications/balena-etcher.desktop" \
"${pkgdir}/usr/share/applications/balena-etcher.desktop"
install -Dm644 \
"${srcdir}/extracted/usr/share/pixmaps/balena-etcher.png" \
"${pkgdir}/usr/share/pixmaps/balena-etcher.png"
install-license LICENSE "balena-etcher/LICENSE"
install-license \
"${srcdir}/extracted/usr/share/doc/balena-etcher/copyright" \
"balena-etcher/copyright"
install-license \
"${srcdir}/extracted/usr/lib/balena-etcher/LICENSES.chromium.html" \
"balena-etcher/LICENSES.chromium.html"
}
files() {
files-find-binary
files-find-desktop
files-find-license
files-find \
"/usr/lib/balena-etcher/**/*" \
"/usr/share/pixmaps/balena-etcher.png"
}
+32
View File
@@ -0,0 +1,32 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
sandbox=/usr/lib/balena-etcher/chrome-sandbox
if [[ ! -f "$sandbox" ]]; then
echo "Ошибка: не найден Electron sandbox: ${sandbox}" >&2
exit 1
fi
if [[ -L /proc/self/ns/user ]] && command -v unshare >/dev/null 2>&1 \
&& unshare --user true 2>/dev/null; then
chmod 0755 "$sandbox"
else
chmod 4755 "$sandbox"
fi
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
if command -v kbuildsycoca6 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca6 --noincremental
elif command -v kbuildsycoca5 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca5 --noincremental
fi
+20
View File
@@ -0,0 +1,20 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
if command -v kbuildsycoca6 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca6 --noincremental
elif command -v kbuildsycoca5 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca5 --noincremental
fi
+1
View File
@@ -0,0 +1 @@
include = "../stapler-repo.toml"
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
package_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
repo_root="$(cd "${package_dir}/.." && pwd)"
exec "${repo_root}/tools/package_updates.sh" check "$(basename "$package_dir")"
+20
View File
@@ -0,0 +1,20 @@
Сведения о лицензии пакета Chatbox
Лицензия: GPL-3.0-only.
Исходный проект: https://github.com/chatboxai/chatbox
Официальный сайт: https://chatboxai.app/ru
Официальная страница загрузки: https://chatboxai.app/ru
Условия использования сервиса: https://chatboxai.app/ru/terms
Политика конфиденциальности: https://chatboxai.app/ru/privacy
Нюансы пакета:
- Пакет перепаковывает официальный Linux deb-артефакт Chatbox Desktop из релиза `chatboxai/chatbox`.
- Chatbox Community Edition распространяется как открытый клиент под GPL-3.0; bundled-зависимости Electron/Chromium устанавливаются вместе с upstream-артефактом.
- Последние релизы Chatbox могут выходить без Linux-артефактов. Пакет Nivora использует последний upstream-релиз, где реально опубликованы Linux deb-файлы для amd64 и arm64.
- Chatbox может работать с Chatbox AI Service, OpenAI, Claude, Gemini, Ollama и другими внешними провайдерами. Учетные записи, API-ключи, подписки, сетевые функции и модели регулируются условиями соответствующих сервисов.
- История чатов, настройки, кэш, авторизация и локальные базы данных создаются приложением в домашнем каталоге пользователя после запуска и не поставляются внутри пакета.
- Пакет добавляет команды `chatbox`, `chatbox-ai` и `chatbox-desktop` для совместимого запуска приложения.
- В `/usr/share/licenses/chatbox` дополнительно устанавливаются bundled-лицензии Electron/Chromium, если они присутствуют в upstream-артефакте.
Полный официальный текст GPL-3.0:
https://www.gnu.org/licenses/gpl-3.0.txt
+115
View File
@@ -0,0 +1,115 @@
name='chatbox'
version='1.20.3'
release=3
summary='Desktop AI client for multiple LLM providers'
summary_ru='Десктопный AI-клиент для разных LLM-провайдеров'
group='Development/Tools'
desc='Chatbox is a desktop AI client for Chatbox AI, OpenAI, Claude, Gemini, Ollama and other LLM providers with local chat history and file support.'
desc_ru='Chatbox — десктопный AI-клиент для Chatbox AI, OpenAI, Claude, Gemini, Ollama и других LLM-провайдеров с локальной историей чатов и поддержкой файлов.'
homepage='https://chatboxai.app/ru'
maintainer='Chatbox <https://github.com/chatboxai/chatbox>'
architectures=('amd64' 'arm64')
license=('GPL-3.0-only')
provides=()
replaces=('chatbox')
conflicts=()
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
deps=()
deps_debian=('libgtk-3-0' 'libnotify4' 'libnss3' 'libxss1' 'libxtst6' 'xdg-utils' 'libatspi2.0-0' 'libuuid1' 'libsecret-1-0' 'libdrm2' 'libgbm1' 'libxcb-dri3-0' 'libasound2')
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=('gtk3' 'libnotify' 'nss' 'libXScrnSaver' 'libXtst' 'xdg-utils' 'at-spi2-core' 'libuuid' 'libsecret' 'libdrm' 'mesa-libgbm' 'libxcb' 'alsa-lib')
deps_arch=('gtk3' 'libnotify' 'nss' 'libxss' 'libxtst' 'xdg-utils' 'at-spi2-core' 'libutil-linux' 'libsecret' 'libdrm' 'mesa' 'libxcb' 'alsa-lib')
deps_opensuse=('gtk3' 'libnotify4' 'mozilla-nss' 'libXScrnSaver1' 'libXtst6' 'xdg-utils' 'at-spi2-core' 'libuuid1' 'libsecret-1-0' 'libdrm2' 'libgbm1' 'libxcb-dri3-0' 'libasound2')
deps_altlinux=('libgtk+3' 'libnotify' 'libnss' 'libXScrnSaver' 'libXtst' 'xdg-utils' 'libat-spi2-core' 'libuuid' 'libsecret' 'libdrm' 'libgbm' 'libxcb' 'libalsa')
opt_deps=()
opt_deps_debian=('libappindicator3-1' 'gnome-keyring')
opt_deps_ubuntu=("${opt_deps_debian[@]}")
opt_deps_fedora=('libappindicator-gtk3' 'gnome-keyring')
opt_deps_arch=('libappindicator-gtk3' 'gnome-keyring')
opt_deps_opensuse=('libappindicator3-1' 'gnome-keyring')
opt_deps_altlinux=('libappindicator-gtk3' 'gnome-keyring')
build_deps=('binutils')
sources=(
"https://github.com/chatboxai/chatbox/releases/download/v${version}/Chatbox-${version}-amd64.deb?~archive=false&~name=chatbox.deb"
'local:///LICENSE'
)
sources_arm64=(
"https://github.com/chatboxai/chatbox/releases/download/v${version}/Chatbox-${version}-arm64.deb?~archive=false&~name=chatbox.deb"
'local:///LICENSE'
)
checksums=(
'sha256:7a5b86200c180142ee7d4ed5d56edaabba3a3b37b3b8d8c4eed32678d327e141'
'sha256:9408c95380fe46177c49bb9ba845dc0d706e88cdd2d637b5d2b9168730713acd'
)
checksums_arm64=(
'sha256:909c8ccbb5965a63967af2428210456eb2c9fb7baf88a1a6971e3b07ead81d60'
'sha256:9408c95380fe46177c49bb9ba845dc0d706e88cdd2d637b5d2b9168730713acd'
)
scripts=(
['postinstall']='postinstall.sh'
['postremove']='postremove.sh'
)
package() {
cd "${srcdir}"
mkdir -p "${srcdir}/extracted"
ar x chatbox.deb
tar -xf data.tar.* -C "${srcdir}/extracted"
install -dm755 "${pkgdir}/opt"
cp -a "${srcdir}/extracted/opt/Chatbox" "${pkgdir}/opt/"
chmod 0755 "${pkgdir}/opt/Chatbox/chrome-sandbox"
chmod 755 "${pkgdir}/opt/Chatbox/xyz.chatboxapp.app"
install -dm755 "${pkgdir}/usr/share"
cp -a "${srcdir}/extracted/usr/share/applications" "${pkgdir}/usr/share/"
cp -a "${srcdir}/extracted/usr/share/icons" "${pkgdir}/usr/share/"
if [[ -d "${srcdir}/extracted/usr/share/doc" ]]; then
cp -a "${srcdir}/extracted/usr/share/doc" "${pkgdir}/usr/share/"
fi
sed -i 's/^Categories=.*/Categories=Development;/' \
"${pkgdir}/usr/share/applications/xyz.chatboxapp.app.desktop"
install -dm755 "${pkgdir}/usr/bin"
ln -sfn /opt/Chatbox/xyz.chatboxapp.app "${pkgdir}/usr/bin/chatbox"
ln -sfn /opt/Chatbox/xyz.chatboxapp.app "${pkgdir}/usr/bin/chatbox-ai"
ln -sfn /opt/Chatbox/xyz.chatboxapp.app "${pkgdir}/usr/bin/chatbox-desktop"
install-license LICENSE "chatbox/LICENSE.nivora"
if [[ -f "${srcdir}/extracted/opt/Chatbox/LICENSE.electron.txt" ]]; then
install-license "${srcdir}/extracted/opt/Chatbox/LICENSE.electron.txt" \
"chatbox/LICENSE.electron.txt"
fi
if [[ -f "${srcdir}/extracted/opt/Chatbox/LICENSES.chromium.html" ]]; then
install-license "${srcdir}/extracted/opt/Chatbox/LICENSES.chromium.html" \
"chatbox/LICENSES.chromium.html"
fi
}
files() {
files-find-binary
files-find-desktop
files-find-license
files-find \
"/opt/Chatbox/**/*" \
"/usr/share/doc/xyz.chatboxapp.app/*" \
"/usr/share/icons/hicolor/*/apps/xyz.chatboxapp.app.png"
}
+35
View File
@@ -0,0 +1,35 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
sandbox=/opt/Chatbox/chrome-sandbox
if [[ ! -f "$sandbox" ]]; then
echo "Ошибка: не найден Electron sandbox: ${sandbox}" >&2
exit 1
fi
if [[ -L /proc/self/ns/user ]] && command -v unshare >/dev/null 2>&1 \
&& unshare --user true 2>/dev/null; then
chmod 0755 "$sandbox"
else
chmod 4755 "$sandbox"
fi
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
if command -v kbuildsycoca6 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca6 --noincremental
elif command -v kbuildsycoca5 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca5 --noincremental
fi
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
if command -v kbuildsycoca6 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca6 --noincremental
elif command -v kbuildsycoca5 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca5 --noincremental
fi
+1
View File
@@ -0,0 +1 @@
include = "../stapler-repo.toml"
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
package_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
repo_root="$(cd "${package_dir}/.." && pwd)"
exec "${repo_root}/tools/package_updates.sh" check "$(basename "$package_dir")"
+14
View File
@@ -0,0 +1,14 @@
Сведения о лицензии пакета Clash Verge Rev
Лицензия: GPL-3.0-only.
Исходный проект: https://github.com/clash-verge-rev/clash-verge-rev
Официальный сайт: https://www.clashverge.dev
Нюансы пакета:
- Пакет перепаковывает официальный deb-артефакт Clash Verge Rev для Linux.
- Приложение распространяется на условиях GNU GPL версии 3.
- GUI-клиент работает с профилями Mihomo/Clash; сами профили, подписки и внешние серверы в пакет не входят.
- При дальнейшем распространении измененных сборок нужно соблюдать требования GPL-3.0-only.
Полный официальный текст GPL-3.0:
https://www.gnu.org/licenses/gpl-3.0.txt
+91
View File
@@ -0,0 +1,91 @@
name='clash-verge-rev'
version=2.5.2
release=2
summary='Tauri GUI client for Mihomo/Clash proxy profiles'
summary_ru='GUI-клиент на Tauri для профилей Mihomo/Clash'
group='Networking/Other'
desc='Clash Verge Rev is a modern cross-platform GUI client based on Tauri for a tailored proxy experience with Mihomo/Clash profiles.'
desc_ru='Clash Verge Rev — современный кроссплатформенный GUI-клиент на Tauri для удобной работы с профилями прокси Mihomo/Clash.'
homepage='https://www.clashverge.dev'
maintainer='clash-verge-rev contributors <https://github.com/clash-verge-rev/clash-verge-rev>'
architectures=('amd64' 'arm64')
license=('GPL-3.0-only')
provides=()
replaces=('clash-verge-rev')
conflicts=()
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
deps=()
deps_debian=('libc6' 'libgtk-3-0' 'libwebkit2gtk-4.1-0' 'libayatana-appindicator3-1' 'libssl3 | libssl3t64' 'libcap2-bin')
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=('glibc' 'gtk3' 'webkit2gtk4.1' 'libayatana-appindicator' 'openssl-libs' 'libcap')
deps_arch=('glibc' 'gtk3' 'webkit2gtk-4.1' 'libayatana-appindicator' 'openssl' 'libcap')
deps_opensuse=('glibc' 'gtk3' 'libwebkit2gtk-4_1-0' 'libayatana-appindicator3-1' 'libopenssl3' 'libcap-progs')
deps_altlinux=('glibc' 'libgtk+3' 'libwebkit2gtk4.1' 'libayatana-appindicator3-1' 'libssl3' 'libcap-utils')
build_deps=('binutils')
sources=(
"https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v${version}/Clash.Verge_${version}_amd64.deb?~archive=false&~name=clash-verge.deb"
'local:///LICENSE'
)
sources_arm64=(
"https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v${version}/Clash.Verge_${version}_arm64.deb?~archive=false&~name=clash-verge.deb"
'local:///LICENSE'
)
checksums=(
'sha256:035c83ed14b16df1dd397e5d710b34bedd5d27beb6678549ceaeeadf9bc167ed'
'sha256:5b886bd36073c4604b97926ad81ad26ea64b9172f47f014c127b6a05f80e37a9'
)
checksums_arm64=(
'sha256:598a5a852d7bf9dc40a976780ef2afc9a4e5bfe7b99533e5f956f9e2f9def72f'
'sha256:5b886bd36073c4604b97926ad81ad26ea64b9172f47f014c127b6a05f80e37a9'
)
scripts=(
['postinstall']='postinstall.sh'
['postremove']='postremove.sh'
)
package() {
cd "${srcdir}"
mkdir -p "${srcdir}/extracted"
ar x clash-verge.deb
tar -xf data.tar.* -C "${srcdir}/extracted"
for path in usr opt etc; do
if [[ -d "${srcdir}/extracted/${path}" ]]; then
mkdir -p "${pkgdir}/${path}"
cp -a "${srcdir}/extracted/${path}/." "${pkgdir}/${path}/"
fi
done
chmod 755 \
"${pkgdir}/usr/bin/clash-verge" \
"${pkgdir}/usr/bin/clash-verge-service" \
"${pkgdir}/usr/bin/clash-verge-service-install" \
"${pkgdir}/usr/bin/clash-verge-service-uninstall" \
"${pkgdir}/usr/bin/verge-mihomo" \
"${pkgdir}/usr/bin/verge-mihomo-alpha"
install-license "${srcdir}/LICENSE" "clash-verge-rev/LICENSE.nivora"
}
files() {
files-find-binary
files-find-desktop
files-find-license
files-find \
"/usr/lib/Clash Verge/**/*" \
"/usr/share/icons/**/*"
}
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
+1
View File
@@ -0,0 +1 @@
[inherit]
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
package_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
repo_root="$(cd "${package_dir}/.." && pwd)"
exec "${repo_root}/tools/package_updates.sh" check "$(basename "$package_dir")"
+26
View File
@@ -0,0 +1,26 @@
Сведения о лицензии пакета Claude
Лицензия: Proprietary.
Исходный продукт: https://claude.ai/download
Документация desktop-приложения Claude: https://code.claude.com/docs/en/desktop-quickstart
Официальный Linux-репозиторий: https://downloads.claude.ai/claude-desktop/apt/stable
Нюансы пакета:
- Пакет перепаковывает официальный Linux deb-артефакт `claude-desktop` из APT-репозитория Anthropic.
- В пакет входит десктопное Electron-приложение Claude с поддержкой сценариев Claude Code.
- Команда `claude-desktop` запускает основное desktop-приложение.
- Для одновременной работы с двумя аккаунтами пакет добавляет самостоятельное приложение `ClaudeAlt` с командами `claude-alt` / `claude-code-alt`, отдельной app-иконкой, executable/resources tree и Electron-идентичностью `com.anthropic.ClaudeAlt`.
- Tray-иконки основного Claude и ClaudeAlt являются перекрашенными вариантами штатного глифа Anthropic и регулируются теми же проприетарными условиями, что и исходный продукт.
- ClaudeAlt использует отдельный каталог web-данных `${XDG_CONFIG_HOME:-~/.config}/ClaudeAlt`, чтобы не смешивать cookies, авторизацию, OAuth-кэш, IndexedDB, Local Storage и Chromium lock-файлы. При обновлении существующий `${XDG_CONFIG_HOME:-~/.config}/Claude-Account-2` продолжает использоваться для сохранения авторизации.
- Путь профиля можно переопределить через `CLAUDE_ALT_DATA_DIR`; прежняя переменная `CLAUDE_DESKTOP_ACCOUNT2_DIR` поддерживается для совместимости.
- Глобальная память и конфигурация Claude Code в `~/.claude` могут использоваться обоими приложениями, поскольку этот путь принадлежит Claude Code, а не Electron-профилю.
- Облачные чаты и память Claude не объединяются пакетом: эти данные принадлежат конкретной учетной записи и хранятся на стороне Anthropic.
- Команда `claude` намеренно не добавляется, чтобы не конфликтовать с отдельным Claude Code CLI.
- Для полноценной работы требуется учетная запись Anthropic и подходящая подписка или доступ к сервису Claude.
- Настройки, авторизация, история, рабочие каталоги и данные проектов создаются приложением после запуска и не поставляются внутри пакета.
- Функции, зависящие от виртуализации или интеграции окружения, могут требовать дополнительные системные компоненты: QEMU, virtiofsd, portal backend, keyring/kwallet и аудиоподсистему.
- Исходный продукт является проприетарным. Использование Claude, Claude Code, сетевых функций, моделей и учетной записи регулируется юридическими условиями Anthropic.
- Внутри upstream-пакета также поставляются компоненты Electron/Chromium и virtiofsd со своими лицензиями; их тексты устанавливаются в `/usr/share/licenses/claude-desktop`.
Официальные условия:
https://www.anthropic.com/legal/consumer-terms
+388
View File
@@ -0,0 +1,388 @@
name='claude-desktop'
version=1.24012.0
release=4
summary='Claude desktop application'
summary_ru='Десктопное приложение Claude'
group='Development/Tools'
desc='Claude is the Anthropic desktop application for Claude and Claude Code workflows with chat, project context, integrated terminal, editor and visual diff review.'
desc_ru='Claude — десктопное приложение Anthropic для Claude и сценариев Claude Code: чат, контекст проекта, встроенный терминал, редактор и визуальный просмотр diff.'
homepage='https://code.claude.com/docs/en/desktop-quickstart'
maintainer='Anthropic PBC <support@anthropic.com>'
architectures=('amd64' 'arm64')
license=('Custom')
nonfree=1
nonfree_msg='Claude Desktop является проприетарным приложением Anthropic. Использование Claude, Claude Code, учетной записи, подписки, сетевых функций и моделей регулируется условиями Anthropic.'
nonfree_url='https://www.anthropic.com/legal/consumer-terms'
provides=()
replaces=('claude-desktop')
conflicts=()
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
deps=()
deps_debian=('libgtk-3-0' 'libnotify4' 'libnss3' 'xdg-utils' 'libatspi2.0-0' 'libdrm2' 'libgbm1' 'libxcb-dri3-0' 'libsecret-1-0' 'libc6' 'libxtst6' 'libuuid1' 'xdg-desktop-portal' 'xdg-desktop-portal-gtk | xdg-desktop-portal-gnome | xdg-desktop-portal-kde')
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=('gtk3' 'libnotify' 'nss' 'xdg-utils' 'at-spi2-core' 'libdrm' 'mesa-libgbm' 'libxcb' 'libsecret' 'glibc' 'libXtst' 'libuuid' 'xdg-desktop-portal' 'xdg-desktop-portal-gtk')
deps_arch=('gtk3' 'libnotify' 'nss' 'xdg-utils' 'at-spi2-core' 'libdrm' 'libgbm' 'libxcb' 'libsecret' 'glibc' 'libxtst' 'libutil-linux' 'xdg-desktop-portal' 'xdg-desktop-portal-gtk')
deps_opensuse=('gtk3' 'libnotify4' 'mozilla-nss' 'xdg-utils' 'at-spi2-core' 'libdrm2' 'libgbm1' 'libxcb-dri3-0' 'libsecret-1-0' 'glibc' 'libXtst6' 'libuuid1' 'xdg-desktop-portal' 'xdg-desktop-portal-gtk')
deps_altlinux=('libgtk+3' 'libnotify' 'libnss' 'xdg-utils' 'libat-spi2-core' 'libdrm' 'libgbm' 'libxcb' 'libsecret' 'glibc' 'libXtst' 'libuuid' 'xdg-desktop-portal' 'xdg-desktop-portal-gtk')
opt_deps=()
opt_deps_debian=('libasound2t64 | libasound2 | pulseaudio' 'libayatana-appindicator3-1 | libappindicator3-1' 'ca-certificates' 'gnome-keyring | kwalletd6 | kwalletd5' 'qemu-system-x86' 'ovmf' 'virtiofsd')
opt_deps_ubuntu=("${opt_deps_debian[@]}")
opt_deps_fedora=('alsa-lib' 'pulseaudio-libs' 'libappindicator-gtk3' 'ca-certificates' 'gnome-keyring' 'kwallet' 'qemu-system-x86' 'edk2-ovmf' 'virtiofsd')
opt_deps_arch=('alsa-lib' 'pulseaudio' 'libappindicator-gtk3' 'ca-certificates' 'gnome-keyring' 'kwallet' 'qemu-system-x86' 'edk2-ovmf' 'virtiofsd')
opt_deps_opensuse=('libasound2' 'libpulse0' 'libappindicator3-1' 'ca-certificates' 'gnome-keyring' 'kwallet6' 'qemu-x86' 'ovmf' 'virtiofsd')
opt_deps_altlinux=('libalsa' 'libappindicator-gtk3' 'ca-certificates' 'gnome-keyring' 'qemu-system-x86' 'virtiofsd')
build_deps=('binutils')
build_deps_debian=('binutils' 'python3')
build_deps_ubuntu=("${build_deps_debian[@]}")
build_deps_fedora=('binutils' 'python3')
build_deps_arch=('binutils' 'python')
build_deps_opensuse=('binutils' 'python3')
build_deps_altlinux=('binutils' 'python3')
sources=(
"https://downloads.claude.ai/claude-desktop/apt/stable/pool/main/c/claude-desktop/claude-desktop_${version}_amd64.deb?~archive=false&~name=claude-desktop.deb"
"local:///claude-alt"
"local:///claude-alt.png"
"local:///claude-tray-orange.png"
"local:///claude-alt-tray-turquoise.png"
'local:///LICENSE'
)
sources_arm64=(
"https://downloads.claude.ai/claude-desktop/apt/stable/pool/main/c/claude-desktop/claude-desktop_${version}_arm64.deb?~archive=false&~name=claude-desktop.deb"
"local:///claude-alt"
"local:///claude-alt.png"
"local:///claude-tray-orange.png"
"local:///claude-alt-tray-turquoise.png"
'local:///LICENSE'
)
checksums=(
'sha256:1096a8063956f8430fdbe504bb1c5e02985f31d0f45d082e4966e5d30ea7e3ac'
'sha256:00d9fe6549d82f564a99c6e8ec54d1380fdf8f0a20c99bb9b3cda1f689adf57d'
'sha256:f23de36a74c3f6ddae92511fc0dc659816853bf1d28520a6e0232fe2606fd0b4'
'sha256:be51081c0e0b2eec3489c25da38194dccf68fe7445928b32c30e1b8051eb557b'
'sha256:21d1db80c30921346252ed405fa36cb5c48c287afdd8ebe474073389fd88e85e'
'sha256:40efe7a2755d02c9fe13f98cae18705e2d31e5bbb24816ebbd4951c5e3a56b3f'
)
checksums_arm64=(
'sha256:37dd5dc1b0bfcb3192561acbd33cb59037806fe9b95221a3ddf38244203196f1'
'sha256:00d9fe6549d82f564a99c6e8ec54d1380fdf8f0a20c99bb9b3cda1f689adf57d'
'sha256:f23de36a74c3f6ddae92511fc0dc659816853bf1d28520a6e0232fe2606fd0b4'
'sha256:be51081c0e0b2eec3489c25da38194dccf68fe7445928b32c30e1b8051eb557b'
'sha256:21d1db80c30921346252ed405fa36cb5c48c287afdd8ebe474073389fd88e85e'
'sha256:40efe7a2755d02c9fe13f98cae18705e2d31e5bbb24816ebbd4951c5e3a56b3f'
)
scripts=(
['postinstall']='postinstall.sh'
['postremove']='postremove.sh'
)
package() {
cd "${srcdir}"
mkdir -p "${srcdir}/extracted"
ar x claude-desktop.deb
tar -xf data.tar.* -C "${srcdir}/extracted"
install -dm755 "${pkgdir}/usr/lib"
cp -a "${srcdir}/extracted/usr/lib/claude-desktop" "${pkgdir}/usr/lib/"
chmod 0755 "${pkgdir}/usr/lib/claude-desktop/chrome-sandbox"
chmod 755 \
"${pkgdir}/usr/lib/claude-desktop/claude-desktop" \
"${pkgdir}/usr/lib/claude-desktop/resources/chrome-native-host" \
"${pkgdir}/usr/lib/claude-desktop/resources/cowork-linux-helper" \
"${pkgdir}/usr/lib/claude-desktop/resources/virtiofsd"
# Сохраняем штатный tray-глиф Claude, меняя только его цвет.
for icon_name in TrayIconLinux.png TrayIconLinux-Dark.png; do
install -Dm644 "${srcdir}/claude-tray-orange.png" \
"${pkgdir}/usr/lib/claude-desktop/resources/${icon_name}"
done
# ClaudeAlt получает отдельные executable и app.asar. Остальные неизменяемые
# Electron-файлы подключаются к основной копии относительными symlink-ами.
# Это сохраняет отдельный resourcesPath, но не удваивает пакет целиком.
install -dm755 \
"${pkgdir}/usr/lib/claude-alt" \
"${pkgdir}/usr/lib/claude-alt/resources"
cp -p "${pkgdir}/usr/lib/claude-desktop/claude-desktop" \
"${pkgdir}/usr/lib/claude-alt/claude-alt-bin"
for entry in "${pkgdir}/usr/lib/claude-desktop"/*; do
entry_name="${entry##*/}"
case "${entry_name}" in
claude-desktop | resources) continue ;;
esac
ln -s "../claude-desktop/${entry_name}" \
"${pkgdir}/usr/lib/claude-alt/${entry_name}"
done
for entry in "${pkgdir}/usr/lib/claude-desktop/resources"/*; do
entry_name="${entry##*/}"
case "${entry_name}" in
app.asar)
cp -p "${entry}" "${pkgdir}/usr/lib/claude-alt/resources/app.asar"
;;
icon.png | TrayIconLinux.png | TrayIconLinux-Dark.png)
# Эти ресурсы ниже заменяются иконкой ClaudeAlt.
;;
*)
ln -s "../../claude-desktop/resources/${entry_name}" \
"${pkgdir}/usr/lib/claude-alt/resources/${entry_name}"
;;
esac
done
# Большая иконка ClaudeAlt используется окном, а в tray устанавливается
# отдельный бирюзовый вариант штатного глифа Claude.
install -Dm644 "${srcdir}/claude-alt.png" \
"${pkgdir}/usr/lib/claude-alt/resources/icon.png"
for icon_name in TrayIconLinux.png TrayIconLinux-Dark.png; do
install -Dm644 "${srcdir}/claude-alt-tray-turquoise.png" \
"${pkgdir}/usr/lib/claude-alt/resources/${icon_name}"
done
alt_asar="${pkgdir}/usr/lib/claude-alt/resources/app.asar"
python3 - "${alt_asar}" <<'PY'
import copy
import hashlib
import json
import struct
import sys
from pathlib import Path
asar_path = Path(sys.argv[1])
data = asar_path.read_bytes()
if len(data) < 16:
raise SystemExit("ClaudeAlt app.asar is too small")
_, _, _, header_size = struct.unpack("<IIII", data[:16])
header = json.loads(data[16 : 16 + header_size])
base_offset = 16 + ((header_size + 3) & ~3)
output = bytearray()
patched_package_json = 0
patched_cowork_socket = 0
patched_user_agent = 0
def integrity_for(content: bytes, block_size: int = 4194304) -> dict:
blocks = [
hashlib.sha256(content[index : index + block_size]).hexdigest()
for index in range(0, len(content), block_size)
]
if not blocks:
blocks = [hashlib.sha256(b"").hexdigest()]
return {
"algorithm": "SHA256",
"hash": hashlib.sha256(content).hexdigest(),
"blockSize": block_size,
"blocks": blocks,
}
def rebuild_node(node: dict, prefix: str = "") -> dict:
global patched_cowork_socket, patched_package_json, patched_user_agent
rebuilt = {}
for name, child in node.get("files", {}).items():
path = f"{prefix}/{name}" if prefix else name
if "files" in child:
rebuilt[name] = {
**{key: value for key, value in child.items() if key != "files"},
"files": rebuild_node(child, path),
}
continue
entry = copy.deepcopy(child)
if entry.get("unpacked"):
rebuilt[name] = entry
continue
offset = int(entry["offset"])
size = int(entry["size"])
content = data[base_offset + offset : base_offset + offset + size]
content_changed = False
if path == "package.json":
package_json = json.loads(content)
expected = {
"productName": "Claude",
"desktopName": "com.anthropic.Claude.desktop",
}
for field, expected_value in expected.items():
if package_json.get(field) != expected_value:
raise SystemExit(
f"unexpected Claude package.json {field}: "
f"{package_json.get(field)!r} != {expected_value!r}"
)
# Отдельный productName нужен Electron для независимого instance
# lock. Официальный User-Agent восстанавливается ниже отдельно.
package_json["productName"] = "ClaudeAlt"
package_json["desktopName"] = "com.anthropic.ClaudeAlt.desktop"
content = (json.dumps(package_json, indent=2) + "\n").encode("utf-8")
patched_package_json += 1
content_changed = True
socket_name = b"claude-cowork-vm.sock"
socket_matches = content.count(socket_name)
if socket_matches:
content = content.replace(socket_name, b"claude-alt-cowork-vm.sock")
patched_cowork_socket += socket_matches
content_changed = True
user_agent_marker = (
b'P.app.isPackaged||P.app.setName("Claude");RRn();'
)
user_agent_matches = content.count(user_agent_marker)
if user_agent_matches:
content = content.replace(
user_agent_marker,
b'P.app.isPackaged||P.app.setName("Claude");'
b'P.app.userAgentFallback=P.app.userAgentFallback'
b'.replaceAll("ClaudeAlt","Claude");RRn();',
)
patched_user_agent += user_agent_matches
content_changed = True
entry["offset"] = str(len(output))
if content_changed:
entry["size"] = len(content)
entry["integrity"] = integrity_for(
content,
entry.get("integrity", {}).get("blockSize", 4194304),
)
output.extend(content)
rebuilt[name] = entry
return rebuilt
rebuilt_header = {"files": rebuild_node(header)}
if patched_package_json != 1:
raise SystemExit(
f"expected one ClaudeAlt package.json, got {patched_package_json}"
)
if patched_cowork_socket != 1:
raise SystemExit(
f"expected one ClaudeAlt Cowork socket, got {patched_cowork_socket}"
)
if patched_user_agent != 1:
raise SystemExit(
f"expected one ClaudeAlt User-Agent patch, got {patched_user_agent}"
)
header_bytes = json.dumps(rebuilt_header, separators=(",", ":")).encode("utf-8")
header_padded_size = (len(header_bytes) + 3) & ~3
patched_data = (
struct.pack(
"<IIII",
4,
header_padded_size + 8,
header_padded_size + 4,
len(header_bytes),
)
+ header_bytes
+ (b"\0" * (header_padded_size - len(header_bytes)))
+ bytes(output)
)
if patched_data.count(b"com.anthropic.ClaudeAlt.desktop") != 1:
raise SystemExit("ClaudeAlt desktopName was not written exactly once")
if patched_data.count(b'"productName": "ClaudeAlt"') != 1:
raise SystemExit("ClaudeAlt productName was not written exactly once")
if patched_data.count(b'.replaceAll("ClaudeAlt","Claude")') != 1:
raise SystemExit("ClaudeAlt official User-Agent patch is missing")
if patched_data.count(b"claude-alt-cowork-vm.sock") != 1:
raise SystemExit("ClaudeAlt Cowork socket was not written exactly once")
asar_path.write_bytes(patched_data)
PY
chmod 755 "${pkgdir}/usr/lib/claude-alt/claude-alt-bin"
install -Dm755 "${srcdir}/claude-alt" \
"${pkgdir}/usr/lib/claude-alt/claude-alt"
install -dm755 "${pkgdir}/usr/share"
cp -a "${srcdir}/extracted/usr/share/applications" "${pkgdir}/usr/share/"
cp -a "${srcdir}/extracted/usr/share/icons" "${pkgdir}/usr/share/"
desktop_file="$(find "${pkgdir}/usr/share/applications" -maxdepth 1 -type f -name "*.desktop" | head -1)"
[[ -n "${desktop_file}" ]] || {
echo "Claude desktop file not found" >&2
return 1
}
sed -i \
-e '0,/^Name=/{s/^Name=.*/Name=Claude\nName[ru]=Claude/}' \
-e 's/^Categories=.*/Categories=Development;/' \
"${desktop_file}"
install -Dm644 "${srcdir}/claude-alt.png" \
"${pkgdir}/usr/share/icons/hicolor/512x512/apps/claude-alt.png"
install -Dm644 /dev/stdin "${pkgdir}/usr/share/applications/com.anthropic.ClaudeAlt.desktop" <<'EOF'
[Desktop Entry]
Name=ClaudeAlt
Name[ru]=ClaudeAlt
Comment=Independent secondary Claude desktop application
Comment[ru]=Независимое второе приложение Claude
GenericName=AI Assistant
Keywords=AI;Chat;Assistant;Claude;Code;LLM;
Exec=claude-alt %U
Icon=claude-alt
Type=Application
StartupNotify=true
StartupWMClass=com.anthropic.ClaudeAlt
SingleMainWindow=true
Categories=Development;
Actions=NewChat;NewCode;
[Desktop Action NewChat]
Name=New chat
Name[ru]=Новый чат
Exec=claude-alt claude://claude.ai/new
[Desktop Action NewCode]
Name=New Claude Code session
Name[ru]=Новая сессия Claude Code
Exec=claude-alt claude://code/new
EOF
install -dm755 "${pkgdir}/usr/bin"
ln -sfn /usr/lib/claude-desktop/claude-desktop "${pkgdir}/usr/bin/claude-desktop"
ln -sfn /usr/lib/claude-alt/claude-alt "${pkgdir}/usr/bin/claude-alt"
ln -sfn /usr/lib/claude-alt/claude-alt "${pkgdir}/usr/bin/claude-code-alt"
ln -sfn /usr/lib/claude-alt/claude-alt "${pkgdir}/usr/bin/claude-desktop-account2"
install -dm755 "${pkgdir}/usr/share/licenses/claude-desktop"
install-license "${srcdir}/LICENSE" "claude-desktop/LICENSE.nivora"
install -Dm644 "${srcdir}/extracted/usr/share/doc/claude-desktop/copyright" \
"${pkgdir}/usr/share/licenses/claude-desktop/copyright"
install -Dm644 "${srcdir}/extracted/usr/lib/claude-desktop/LICENSES.chromium.html" \
"${pkgdir}/usr/share/licenses/claude-desktop/LICENSES.chromium.html"
install -Dm644 "${srcdir}/extracted/usr/lib/claude-desktop/resources/virtiofsd.LICENSE-APACHE" \
"${pkgdir}/usr/share/licenses/claude-desktop/virtiofsd.LICENSE-APACHE"
install -Dm644 "${srcdir}/extracted/usr/lib/claude-desktop/resources/virtiofsd.LICENSE-BSD-3-Clause" \
"${pkgdir}/usr/share/licenses/claude-desktop/virtiofsd.LICENSE-BSD-3-Clause"
}
files() {
files-find-binary
files-find-desktop
files-find-license
files-find \
"/usr/lib/claude-desktop/**/*" \
"/usr/lib/claude-alt/**/*" \
"/usr/share/icons/hicolor/*/apps/claude-desktop.png" \
"/usr/share/icons/hicolor/*/apps/claude-alt.png"
}
+40
View File
@@ -0,0 +1,40 @@
#!/bin/sh
set -eu
if [ -z "${HOME:-}" ] && [ -z "${XDG_CONFIG_HOME:-}" ]; then
echo "claude-alt: HOME or XDG_CONFIG_HOME is required" >&2
exit 1
fi
config_home="${XDG_CONFIG_HOME:-${HOME}/.config}"
default_data_dir="${config_home}/ClaudeAlt"
legacy_data_dir="${CLAUDE_DESKTOP_ACCOUNT2_DIR:-${config_home}/Claude-Account-2}"
claude_alt_executable="${CLAUDE_ALT_EXECUTABLE:-/usr/lib/claude-alt/claude-alt-bin}"
if [ -n "${CLAUDE_ALT_DATA_DIR:-}" ]; then
data_dir="${CLAUDE_ALT_DATA_DIR}"
elif [ -d "${default_data_dir}" ]; then
data_dir="${default_data_dir}"
elif [ -d "${legacy_data_dir}" ]; then
# Сохраняем авторизацию прежнего второго профиля после обновления пакета.
data_dir="${legacy_data_dir}"
else
data_dir="${default_data_dir}"
fi
if [ ! -x "${claude_alt_executable}" ]; then
echo "claude-alt: executable not found: ${claude_alt_executable}" >&2
exit 1
fi
mkdir -p "${data_dir}"
# Electron использует это имя для portal-интеграций. Основной Wayland app_id
# задаётся отдельным desktopName внутри ClaudeAlt app.asar.
CHROME_DESKTOP='com.anthropic.ClaudeAlt.desktop'
export CHROME_DESKTOP
exec "${claude_alt_executable}" \
--user-data-dir="${data_dir}" \
--class=com.anthropic.ClaudeAlt \
"$@"
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

+35
View File
@@ -0,0 +1,35 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
sandbox=/usr/lib/claude-desktop/chrome-sandbox
if [[ ! -f "$sandbox" ]]; then
echo "Ошибка: не найден Electron sandbox: ${sandbox}" >&2
exit 1
fi
if [[ -L /proc/self/ns/user ]] && command -v unshare >/dev/null 2>&1 \
&& unshare --user true 2>/dev/null; then
chmod 0755 "$sandbox"
else
chmod 4755 "$sandbox"
fi
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
if command -v kbuildsycoca6 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca6 --noincremental
elif command -v kbuildsycoca5 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca5 --noincremental
fi
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
if command -v kbuildsycoca6 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca6 --noincremental
elif command -v kbuildsycoca5 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca5 --noincremental
fi
+1
View File
@@ -0,0 +1 @@
include = "../stapler-repo.toml"
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
package_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
repo_root="$(cd "${package_dir}/.." && pwd)"
exec "${repo_root}/tools/package_updates.sh" check "$(basename "$package_dir")"
+21
View File
@@ -0,0 +1,21 @@
Сведения о лицензии пакета codex-app
Лицензия: Custom.
Источник перепаковки: https://github.com/Boria138/codex-app-linux
Linux-адаптация: https://github.com/ilysenko/codex-desktop-linux
Backend Computer Use: https://github.com/agent-sh/computer-use-linux
Нюансы пакета:
- Пакет устанавливает неофициальную Linux-перепаковку проприетарного настольного приложения.
- Исходный репозиторий перепаковки не публикует единую корневую лицензию для всего устанавливаемого артефакта.
- Внутри архива исходного проекта присутствуют отдельные лицензии Electron, Chromium и других компонентов; при сборке они устанавливаются в каталог лицензий пакета.
- Linux-патчи Computer Use из codex-desktop-linux и backend computer-use-linux
распространяются по лицензии MIT; их исходные файлы LICENSE устанавливаются
рядом с остальными лицензиями пакета.
- Использование приложения и сетевых функций регулируется условиями исходного сервиса.
- Пакет помечен как несвободный через `nonfree=1`.
Справочные ссылки:
- https://github.com/Boria138/codex-app-linux
- https://github.com/ilysenko/codex-desktop-linux
- https://github.com/agent-sh/computer-use-linux
+176
View File
@@ -0,0 +1,176 @@
name='codex'
version=26.721.31836
release=1
summary='Unofficial Linux repack of the Codex desktop app'
summary_ru='Неофициальная Linux-перепаковка десктопного приложения Codex'
group='Development/Tools'
desc='codex-app-linux repacks the Codex desktop application into Linux Electron artifacts and bundles a Linux codex CLI binary for local workflows.'
desc_ru='codex-app-linux перепаковывает десктопное приложение Codex в Linux-артефакты Electron и включает Linux-бинарник codex CLI для локальных сценариев.'
homepage='https://github.com/Boria138/codex-app-linux'
maintainer='Boria138 <https://github.com/Boria138>'
architectures=('amd64')
license=('Custom')
nonfree=1
nonfree_msg='codex-app устанавливает неофициальную Linux-перепаковку проприетарного настольного приложения. Использование приложения и сетевых функций регулируется условиями исходного сервиса.'
nonfree_url='https://github.com/Boria138/codex-app-linux'
provides=()
replaces=('codex')
conflicts=()
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
deps=()
deps_debian=('ca-certificates' 'libc6' 'libasound2' 'libatk-bridge2.0-0' 'libatk1.0-0' 'libatspi2.0-0' 'libcairo2' 'libcups2' 'libcurl3-gnutls | libcurl4' 'libdbus-1-3' 'libexpat1' 'libgbm1' 'libglib2.0-0' 'libgtk-3-0' 'libnspr4' 'libnss3' 'libpango-1.0-0' 'libsecret-1-0' 'libudev1' 'libx11-6' 'libxcb1' 'libxcomposite1' 'libxdamage1' 'libxext6' 'libxfixes3' 'libxkbcommon0' 'libxkbfile1' 'libxrandr2' 'libxss1' 'libxtst6' 'xdg-desktop-portal' 'xdg-utils')
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=('ca-certificates' 'glibc' 'alsa-lib' 'atk' 'at-spi2-atk' 'at-spi2-core' 'cairo' 'cups-libs' 'libcurl' 'dbus-libs' 'expat' 'mesa-libgbm' 'glib2' 'gtk3' 'nspr' 'nss' 'pango' 'libsecret' 'systemd-libs' 'libX11' 'libxcb' 'libXcomposite' 'libXdamage' 'libXext' 'libXfixes' 'libxkbcommon' 'libxkbfile' 'libXrandr' 'libXScrnSaver' 'libXtst' 'xdg-desktop-portal' 'xdg-utils')
deps_arch=('ca-certificates' 'glibc' 'alsa-lib' 'atk' 'at-spi2-atk' 'at-spi2-core' 'cairo' 'cups' 'curl' 'dbus' 'expat' 'libgbm' 'glib2' 'gtk3' 'nspr' 'nss' 'pango' 'libsecret' 'systemd-libs' 'libx11' 'libxcb' 'libxcomposite' 'libxdamage' 'libxext' 'libxfixes' 'libxkbcommon' 'libxkbfile' 'libxrandr' 'libxss' 'libxtst' 'xdg-desktop-portal' 'xdg-utils')
deps_opensuse=('ca-certificates' 'glibc' 'alsa' 'libatk-1_0-0' 'libatk-bridge-2_0-0' 'libatspi0' 'libcairo2' 'cups-libs' 'libcurl4' 'libdbus-1-3' 'libexpat1' 'libgbm1' 'glib2' 'gtk3' 'mozilla-nspr' 'mozilla-nss' 'pango' 'libsecret-1-0' 'libudev1' 'libX11-6' 'libxcb1' 'libXcomposite1' 'libXdamage1' 'libXext6' 'libXfixes3' 'libxkbcommon0' 'libxkbfile1' 'libXrandr2' 'libXss1' 'libXtst6' 'xdg-desktop-portal' 'xdg-utils')
deps_altlinux=('ca-certificates' 'glibc' 'libalsa' 'at-spi2-atk' 'libat-spi2-core' 'libatk' 'libcairo' 'libcups' 'libcurl' 'libdbus' 'libexpat' 'libgbm' 'glib2' 'libgtk+3' 'libnspr' 'libnss' 'libpango' 'libsecret' 'libudev0' 'libX11' 'libxcb' 'libXcomposite' 'libXdamage' 'libXext' 'libXfixes' 'libxkbcommon' 'libxkbfile' 'libXrandr' 'libXScrnSaver' 'libXtst' 'xdg-desktop-portal' 'xdg-utils')
build_deps=('binutils')
build_deps_debian=('binutils' 'build-essential' 'cargo' 'libwayland-dev' 'nodejs' 'pkg-config' 'python3' 'rustc')
build_deps_ubuntu=("${build_deps_debian[@]}")
build_deps_fedora=('binutils' 'cargo' 'gcc' 'gcc-c++' 'make' 'nodejs' 'pkgconf-pkg-config' 'python3' 'rust' 'wayland-devel')
build_deps_arch=('base-devel' 'binutils' 'cargo' 'nodejs' 'pkgconf' 'python' 'rust' 'wayland')
build_deps_opensuse=('binutils' 'cargo' 'gcc' 'gcc-c++' 'make' 'nodejs' 'pkg-config' 'python3' 'rust' 'wayland-devel')
build_deps_altlinux=('binutils' 'gcc-c++' 'libwayland-client-devel' 'make' 'node' 'pkg-config' 'python3' 'rust' 'rust-cargo')
sources=(
"https://github.com/Boria138/codex-app-linux/releases/download/${version}/codex-app-${version}-x86_64.tar.gz?~archive=false&~name=codex-app.tar.gz"
'https://github.com/ilysenko/codex-desktop-linux/archive/15f3bd4fabc44bc0afba85b16d0121f0b4abd052.tar.gz?~archive=false&~name=codex-linux-port.tar.gz'
'https://github.com/agent-sh/computer-use-linux/archive/refs/tags/v0.4.1.tar.gz?~archive=false&~name=computer-use-linux.tar.gz'
'local:///asar_tree.py'
'local:///patch-computer-use.js'
"local:///codex-app.png"
'local:///LICENSE'
)
checksums=(
'sha256:f48c0ae3fca556d66525b863dd009a0bd0702115517f83c39f8a44cf4d128eb3'
'sha256:8cde2dac642947cdfab23d302056ef81b574cc7f5684f25f7116f80411ec551c'
'sha256:fab36489be4f1e92591d2ebcd28a96a95f788b5d1ba87824985623ab7df5e3f4'
'sha256:9259dc4b9ae217855d55414b2266db08d9b40c5b0cdbca20a183234ff55ba006'
'sha256:91b3cc2a6b660e3962f24976a29a261dae9a38f879758e6e6463e1295477e172'
'sha256:5c4fe03d5777a9684e37ec9a2331302abf525f53db83762e6adaafd2fc5eb510'
'sha256:755f407295f07facd26c968b73a204db5a6f25deac890d230062b09c33eb395e'
)
scripts=(
['postinstall']='postinstall.sh'
['postremove']='postremove.sh'
)
package() {
cd "${srcdir}"
tar -xzf codex-app.tar.gz
tar -xzf codex-linux-port.tar.gz
tar -xzf computer-use-linux.tar.gz
src="$(find "${srcdir}" -maxdepth 1 -type d -name "codex-app-${version}-x64" | head -1)"
linux_port_src="$(find "${srcdir}" -maxdepth 1 -type d -name 'codex-desktop-linux-*' | head -1)"
computer_use_src="$(find "${srcdir}" -maxdepth 1 -type d -name 'computer-use-linux-*' | head -1)"
cargo build \
--manifest-path "${computer_use_src}/Cargo.toml" \
--target-dir "${srcdir}/computer-use-target" \
--release \
--locked
install -dm755 "${pkgdir}/opt/codex-app"
cp -a "${src}/." "${pkgdir}/opt/codex-app/"
codex_asar="${pkgdir}/opt/codex-app/resources/codex.asar"
extracted_asar="${srcdir}/codex-asar"
python3 "${srcdir}/asar_tree.py" extract "${codex_asar}" "${extracted_asar}"
node "${srcdir}/patch-computer-use.js" "${linux_port_src}" "${extracted_asar}"
python3 "${srcdir}/asar_tree.py" rebuild "${codex_asar}" "${extracted_asar}"
bundled_root="${pkgdir}/opt/codex-app/resources/plugins/openai-bundled"
plugin_dir="${bundled_root}/plugins/computer-use"
install -dm755 "${plugin_dir}"
cp -a \
"${linux_port_src}/plugins/openai-bundled/plugins/computer-use/." \
"${plugin_dir}/"
find "${plugin_dir}" -name '.gitkeep' -delete
install -Dm755 \
"${srcdir}/computer-use-target/release/computer-use-linux" \
"${plugin_dir}/bin/codex-computer-use-linux"
install -Dm755 \
"${srcdir}/computer-use-target/release/computer-use-linux-cosmic" \
"${plugin_dir}/bin/codex-computer-use-cosmic"
install -Dm755 \
"${srcdir}/computer-use-target/release/computer-use-linux-cosmic" \
"${plugin_dir}/bin/computer-use-linux-cosmic"
install -Dm644 codex-app.png "${plugin_dir}/assets/app-icon.png"
install -Dm644 /dev/stdin \
"${bundled_root}/.agents/plugins/marketplace.json" <<'EOF'
{
"plugins": [
{
"name": "computer-use",
"source": {
"source": "local",
"path": "./plugins/computer-use"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Productivity"
}
]
}
EOF
chmod 755 "${pkgdir}/opt/codex-app/codex-app"
chmod 755 "${pkgdir}/opt/codex-app/resources/codex"
if [[ -f "${pkgdir}/opt/codex-app/chrome-sandbox" ]]; then
chmod 0755 "${pkgdir}/opt/codex-app/chrome-sandbox"
fi
install -dm755 "${pkgdir}/usr/bin"
ln -sfn /opt/codex-app/codex-app "${pkgdir}/usr/bin/codex-app"
ln -sfn \
/opt/codex-app/resources/plugins/openai-bundled/plugins/computer-use/bin/codex-computer-use-linux \
"${pkgdir}/usr/bin/codex-computer-use-linux"
install -Dm644 codex-app.png \
"${pkgdir}/usr/share/icons/hicolor/512x512/apps/codex-app.png"
install -Dm644 "${src}/LICENSE.electron.txt" \
"${pkgdir}/usr/share/licenses/codex/LICENSE.electron.txt"
install -Dm644 "${src}/LICENSES.chromium.html" \
"${pkgdir}/usr/share/licenses/codex/LICENSES.chromium.html"
install -Dm644 "${linux_port_src}/LICENSE" \
"${pkgdir}/usr/share/licenses/codex/LICENSE.codex-desktop-linux"
install -Dm644 "${computer_use_src}/LICENSE" \
"${pkgdir}/usr/share/licenses/codex/LICENSE.computer-use-linux"
install-license "${srcdir}/LICENSE" "codex/LICENSE.nivora"
install -Dm644 /dev/stdin "${pkgdir}/usr/share/applications/codex-app.desktop" <<'EOF'
[Desktop Entry]
Name=Codex
Comment=Desktop coding agent client
Comment[ru]=Десктопный клиент coding agent
Exec=/usr/bin/codex-app %U
Terminal=false
Type=Application
Icon=codex-app
Categories=Development;IDE;
StartupWMClass=codex-app
EOF
}
files() {
files-find "/opt/codex-app/**/*"
files-find-binary
files-find-desktop
files-find-license
files-find "/usr/share/icons/hicolor/*/apps/codex-app.png"
}
+211
View File
@@ -0,0 +1,211 @@
#!/usr/bin/env python3
"""Extract and rebuild an Electron ASAR while preserving its metadata tree."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import struct
import tempfile
from pathlib import Path
from typing import Any, Iterator
class AsarError(RuntimeError):
pass
def align4(value: int) -> int:
return (value + 3) & ~3
def read_archive(path: Path) -> tuple[dict[str, Any], bytes, int]:
payload = path.read_bytes()
if len(payload) < 16:
raise AsarError(f"{path}: truncated ASAR header")
size_pickle_payload, header_pickle_size, header_payload_size, json_size = (
struct.unpack_from("<IIII", payload)
)
expected_header_payload_size = 4 + align4(json_size)
if (
size_pickle_payload != 4
or header_pickle_size != 4 + header_payload_size
or header_payload_size != expected_header_payload_size
):
raise AsarError(f"{path}: unsupported ASAR header layout")
data_offset = 8 + header_pickle_size
if data_offset > len(payload) or 16 + json_size > data_offset:
raise AsarError(f"{path}: invalid ASAR header size")
try:
header = json.loads(payload[16 : 16 + json_size].decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as error:
raise AsarError(f"{path}: invalid ASAR JSON header") from error
if not isinstance(header, dict) or not isinstance(header.get("files"), dict):
raise AsarError(f"{path}: ASAR header has no file tree")
return header, payload, data_offset
def safe_component(name: str) -> None:
if (
not name
or name in {".", ".."}
or "/" in name
or "\\" in name
or "\0" in name
):
raise AsarError(f"unsafe ASAR entry name: {name!r}")
def entries(
files: dict[str, Any], prefix: tuple[str, ...] = ()
) -> Iterator[tuple[tuple[str, ...], dict[str, Any]]]:
for name, metadata in files.items():
safe_component(name)
if not isinstance(metadata, dict):
raise AsarError(f"invalid metadata for {'/'.join((*prefix, name))}")
relative = (*prefix, name)
children = metadata.get("files")
if children is not None:
if not isinstance(children, dict):
raise AsarError(f"invalid directory entry: {'/'.join(relative)}")
yield from entries(children, relative)
else:
yield relative, metadata
def output_path(root: Path, relative: tuple[str, ...]) -> Path:
candidate = root.joinpath(*relative)
try:
candidate.resolve().relative_to(root.resolve())
except ValueError as error:
raise AsarError(f"entry escapes extraction root: {'/'.join(relative)}") from error
return candidate
def extract(archive: Path, destination: Path) -> None:
header, payload, data_offset = read_archive(archive)
destination.mkdir(parents=True, exist_ok=True)
for relative, metadata in entries(header["files"]):
if metadata.get("unpacked") is True or "link" in metadata:
continue
try:
size = int(metadata["size"])
offset = int(metadata["offset"])
except (KeyError, TypeError, ValueError) as error:
raise AsarError(f"invalid file entry: {'/'.join(relative)}") from error
start = data_offset + offset
end = start + size
if offset < 0 or size < 0 or end > len(payload):
raise AsarError(f"file lies outside archive: {'/'.join(relative)}")
target = output_path(destination, relative)
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(payload[start:end])
if metadata.get("executable") is True:
target.chmod(target.stat().st_mode | 0o111)
def integrity(content: bytes, block_size: int) -> dict[str, Any]:
blocks = [
hashlib.sha256(content[offset : offset + block_size]).hexdigest()
for offset in range(0, len(content), block_size)
]
if not blocks:
blocks = [hashlib.sha256(b"").hexdigest()]
return {
"algorithm": "SHA256",
"hash": hashlib.sha256(content).hexdigest(),
"blockSize": block_size,
"blocks": blocks,
}
def rebuild(archive: Path, source: Path, output: Path) -> None:
header, _, _ = read_archive(archive)
content_chunks: list[bytes] = []
offset = 0
for relative, metadata in entries(header["files"]):
if metadata.get("unpacked") is True or "link" in metadata:
continue
target = output_path(source, relative)
if not target.is_file():
raise AsarError(f"missing extracted file: {'/'.join(relative)}")
content = target.read_bytes()
metadata["size"] = len(content)
metadata["offset"] = str(offset)
if "integrity" in metadata:
block_size = int(metadata["integrity"].get("blockSize", 4 * 1024 * 1024))
metadata["integrity"] = integrity(content, block_size)
content_chunks.append(content)
offset += len(content)
json_payload = json.dumps(
header, ensure_ascii=False, separators=(",", ":")
).encode("utf-8")
padded_json_size = align4(len(json_payload))
header_payload_size = 4 + padded_json_size
prefix = struct.pack(
"<IIII",
4,
4 + header_payload_size,
header_payload_size,
len(json_payload),
)
result = (
prefix
+ json_payload
+ b"\0" * (padded_json_size - len(json_payload))
+ b"".join(content_chunks)
)
output.parent.mkdir(parents=True, exist_ok=True)
if output.resolve() == archive.resolve():
descriptor, temporary_name = tempfile.mkstemp(
prefix=f".{output.name}.", dir=output.parent
)
temporary = Path(temporary_name)
try:
with os.fdopen(descriptor, "wb") as stream:
stream.write(result)
temporary.chmod(archive.stat().st_mode)
temporary.replace(output)
finally:
temporary.unlink(missing_ok=True)
else:
output.write_bytes(result)
def main() -> int:
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="command", required=True)
extract_parser = subparsers.add_parser("extract")
extract_parser.add_argument("archive", type=Path)
extract_parser.add_argument("destination", type=Path)
rebuild_parser = subparsers.add_parser("rebuild")
rebuild_parser.add_argument("archive", type=Path)
rebuild_parser.add_argument("source", type=Path)
rebuild_parser.add_argument("output", type=Path, nargs="?")
arguments = parser.parse_args()
if arguments.command == "extract":
extract(arguments.archive, arguments.destination)
else:
rebuild(
arguments.archive,
arguments.source,
arguments.output or arguments.archive,
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
Binary file not shown.

After

Width:  |  Height:  |  Size: 482 KiB

+139
View File
@@ -0,0 +1,139 @@
#!/usr/bin/env node
"use strict";
// This file only adapts upstream patch discovery to the current hashed bundle
// name. Computer Use behavior remains sourced from codex-desktop-linux.
const fs = require("node:fs");
const path = require("node:path");
const linuxPortRoot = path.resolve(process.argv[2] ?? "");
const extractedApp = path.resolve(process.argv[3] ?? "");
if (!fs.existsSync(linuxPortRoot) || !fs.existsSync(extractedApp)) {
console.error("usage: patch-computer-use.js LINUX_PORT_ROOT EXTRACTED_APP");
process.exit(2);
}
process.env.CODEX_LINUX_ENABLE_COMPUTER_USE_UI = "1";
const { patchExtractedApp } = require(
path.join(linuxPortRoot, "scripts/patches/runner.js"),
);
const implementation = require(
path.join(linuxPortRoot, "scripts/patches/impl/computer-use.js"),
);
const coreRoot = path.join(
linuxPortRoot,
"scripts/patches/core/all-linux",
);
patchExtractedApp(extractedApp, {
corePatchRoot: path.join(coreRoot, "main-process/computer-use"),
});
patchExtractedApp(extractedApp, {
corePatchRoot: path.join(coreRoot, "webview/computer-use-ui"),
});
const assetsDirectory = path.join(extractedApp, "webview/assets");
const javaScriptAssets = fs.readdirSync(assetsDirectory)
.filter((name) => name.endsWith(".js"))
.map((name) => path.join(assetsDirectory, name));
function applyToUniqueAsset(label, patchFunction, alreadyPatched, isCandidate) {
const changed = [];
const existing = [];
for (const target of javaScriptAssets) {
const source = fs.readFileSync(target, "utf8");
if (alreadyPatched(source)) {
existing.push(target);
continue;
}
if (!isCandidate(source)) {
continue;
}
const patched = patchFunction(source);
if (patched !== source) {
changed.push({ target, patched });
}
}
if (existing.length === 1 && changed.length === 0) {
return;
}
if (existing.length !== 0 || changed.length !== 1) {
throw new Error(
`${label}: expected one target, found existing=${existing.length}, changed=${changed.length}`,
);
}
fs.writeFileSync(changed[0].target, changed[0].patched, "utf8");
console.log(`${label}: ${path.basename(changed[0].target)}`);
}
applyToUniqueAsset(
"Linux Computer Use host platform",
implementation.applyLinuxComputerUseHostPlatformPatch,
(source) =>
/featureName:`computer_use`[\s\S]{0,2200}?isHostCompatiblePlatform:[A-Za-z_$][\w$]*===`linux`\|\|/.test(
source,
),
(source) => source.includes("featureName:`computer_use`"),
);
applyToUniqueAsset(
"Linux Computer Use install flow",
implementation.applyLinuxComputerUseInstallFlowPatch,
(source) =>
source.includes("plugin detail query requires pluginName") &&
/let [A-Za-z_$][\w$]*=[A-Za-z_$][\w$]*&&[A-Za-z_$][\w$]*!==`computer-use`,/.test(
source,
),
(source) => source.includes("plugin detail query requires pluginName"),
);
const iconCandidates = fs.readdirSync(assetsDirectory)
.filter((name) => /^computer-use-plugin-icon-[^.]+\.png$/.test(name))
.filter((name) => name !== "computer-use-plugin-icon-linux.png");
if (iconCandidates.length !== 1) {
throw new Error(
`Computer Use icon: expected one source, found ${iconCandidates.length}`,
);
}
let iconReferences = 0;
for (const target of javaScriptAssets) {
const source = fs.readFileSync(target, "utf8");
if (!source.includes("computer-use-plugin-icon-linux.png")) {
continue;
}
fs.writeFileSync(
target,
source.replaceAll(
"computer-use-plugin-icon-linux.png",
iconCandidates[0],
),
"utf8",
);
iconReferences += 1;
}
if (iconReferences !== 1) {
throw new Error(
`Computer Use icon: expected one patched reference, found ${iconReferences}`,
);
}
const mainBundle = fs.readdirSync(path.join(extractedApp, ".vite/build"))
.filter((name) => /^main-[^.]+\.js$/.test(name));
if (mainBundle.length !== 1) {
throw new Error(`main bundle: expected one target, found ${mainBundle.length}`);
}
const mainSource = fs.readFileSync(
path.join(extractedApp, ".vite/build", mainBundle[0]),
"utf8",
);
for (const marker of [
"codexLinuxNativeDesktopApps(",
"codexLinuxRegisterComputerUseCursorHandler",
"platform!==`darwin`&&",
]) {
if (!mainSource.includes(marker)) {
throw new Error(`main bundle: missing upstream marker ${marker}`);
}
}
console.log("Linux Computer Use upstream patches applied");
+37
View File
@@ -0,0 +1,37 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
sandbox=/opt/codex-app/chrome-sandbox
if [[ ! -f "$sandbox" ]]; then
echo "Ошибка: не найден Electron sandbox: ${sandbox}" >&2
exit 1
fi
if [[ -L /proc/self/ns/user ]] && command -v unshare >/dev/null 2>&1 \
&& unshare --user true 2>/dev/null; then
chmod 0755 "$sandbox"
else
chmod 4755 "$sandbox"
fi
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
if command -v kbuildsycoca6 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca6 --noincremental
elif command -v kbuildsycoca5 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca5 --noincremental
fi
+25
View File
@@ -0,0 +1,25 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
if command -v kbuildsycoca6 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca6 --noincremental
elif command -v kbuildsycoca5 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca5 --noincremental
fi
+1
View File
@@ -0,0 +1 @@
[inherit]
+106
View File
@@ -0,0 +1,106 @@
# Сопровождение Nivora
## Инварианты
- В репозитории ровно 19 каталогов с `Staplerfile`.
- Каталог совпадает с `name` и командой в README.
- Upstream-версия не меняется из-за патча рецепта; для этого повышается `release`.
- Desktop-id, AppStream component-id, units и пути данных не меняются без отдельной миграции.
- `provides` и `conflicts` остаются пустыми, а `replaces` содержит только собственное
базовое имя пакета.
Stapler сам добавляет текущее `name` в generated `Provides` и `Conflicts`. Рецепты не
добавляют переходные package ID и не зависят от других Stapler-каталогов.
## Названия
| Package ID | Основание |
|:--|:--|
| `clash-verge-rev` | Upstream-репозиторий и release title: Clash Verge Rev |
| `claude-desktop` | Upstream DEB: `Package: claude-desktop`; desktop-id `com.anthropic.Claude` сохранён |
| `codex` | Отображаемое имя `Codex`; команда и desktop-id `codex-app` сохранены |
| `github-desktop` | Официальный upstream `desktop/desktop`; Linux-сборка без стороннего форка |
| `nivora-stplr` | Собственный helper Nivora |
## Локальные проверки
```bash
tools/run_checks.sh
tools/package_updates.sh check-all
tools/verify_artifacts.sh --all
tools/test_package_lifecycle.sh
```
`run_checks.sh` выполняет `bash -n`, ShellCheck, Python compile, unit-тесты, validator и чтение
всех `Staplerfile` через `stplr-spec`.
`verify_artifacts.sh` сопоставляет готовые RPM с `files()`, проверяет владельцев путей,
права, desktop-файлы, systemd units, иконки, лицензии и метаданные совместимости.
`test_package_lifecycle.sh` собирает настоящие DEB текущей версии и использует настоящие
RPM из clean-build. Минимальные fixtures изображают предыдущую версию того же пакета Nivora.
В одноразовых Ubuntu и ALT-контейнерах через APT проверяются:
1. обновление с предыдущей версии Nivora на текущую;
2. `Provides`, `Replaces` и `Conflicts`;
3. наличие команды, desktop-файла или systemd unit;
4. сохранение пользовательского состояния после обновления и удаления.
Полный жизненный цикл проверяется для восьми критичных пакетов. Остальные пакеты
покрываются validator, clean-build и проверкой payload.
Локально DEB собираются в привилегированном контейнере. На GitHub-hosted runner используется
`NIVORA_DEB_BUILD_MODE=host`: закреплённый stplr запускается непосредственно на одноразовом
Ubuntu runner, потому что вложенный sandbox stplr запрещён внутри Docker. Ubuntu 24.04 может
дополнительно блокировать непривилегированные user namespaces через AppArmor: тест временно
снимает только это ограничение, проверяет полный набор namespaces перед сборкой и
восстанавливает исходное значение при завершении. Для совместимости с моделью привилегий
Stapler временный builder включается в группу `wheel`, отсутствующую в Ubuntu по умолчанию.
Транзакционные сценарии в обоих режимах остаются изолированными в контейнерах.
## Обновление пакета
```bash
stplr-spec update-package package
stplr-spec verify-checksums --path package/Staplerfile
tools/run_checks.sh
tools/clean_build.sh package
```
Нестандартная логика обнаружения версий находится в `tools/package_updates.sh`, а каждый
`.stapler/update-check` вызывает его для своего package ID.
Плановый workflow обновляет пакеты автономно и отправляет проверенные изменения
прямо в `main`. Он запускается ежедневно в 03:00 по Владивостоку
(`17:00 UTC`). Каждый пакет обрабатывается в отдельном временном worktree, поэтому
несовместимое обновление одного upstream не блокирует остальные. При сбое workflow
сохраняет на 30 дней диагностический artifact с полным логом, фазой сбоя, diff и
получившимся `Staplerfile`; успешно собранные пакеты всё равно публикуются. Для
каждого несовместимого пакета создаётся один постоянный issue: повторные сбои
обновляют его, а успешное восстановление автоматически закрывает. Ожидаемый сбой
отдельного пакета помечается предупреждением и не делает весь этап обновления
неуспешным.
## Clean-build
```bash
tools/clean_build.sh package
tools/clean_build.sh --all
tools/verify_artifacts.sh --all
```
Скрипт всегда выполняет сборку в собственном одноразовом контейнере ALT и не
подключает сторонние Stapler-каталоги.
## Проверка жизненного цикла
Для каждого критичного пакета нужно:
1. Собрать fixture предыдущей версии и текущий RPM/DEB.
2. Создать тестовый файл в каталоге данных.
3. Обновить пакет до текущей версии.
4. Проверить `Provides/Replaces/Conflicts`, payload и тестовый файл.
5. Проверить удаление пакета без удаления пользовательского состояния.
Автоматизированная проверка выполняется командой `tools/test_package_lifecycle.sh` в
одноразовых контейнерах, а не на рабочей системе сопровождающего.
+52
View File
@@ -0,0 +1,52 @@
# Claude Desktop
Пакет `claude-desktop` переупаковывает DEB из APT-источника Anthropic и сохраняет upstream
desktop-id `com.anthropic.Claude`.
```bash
sudo stplr install nivora/claude-desktop
```
## Два независимых приложения
- `claude-desktop` запускает основное приложение Claude.
- `claude-alt` и `claude-code-alt` запускают отдельное приложение ClaudeAlt.
- `claude-desktop-account2` — дополнительное имя запуска ClaudeAlt.
- Команда `claude` не создаётся, чтобы не конфликтовать с Claude Code CLI.
ClaudeAlt имеет отдельные executable/resources tree, Electron `productName`, Wayland `app_id`,
X11 `WM_CLASS`, desktop-файл, Chromium-профиль, Cowork VM socket, оконную и
tray-иконки. В системном лотке используется штатный глиф Claude: оранжевый у
основного приложения и бирюзовый у ClaudeAlt. Большие app-иконки окон при этом
остаются отдельными. Поэтому окружение рабочего стола группирует Claude и ClaudeAlt
как разные приложения, а `requestSingleInstanceLock()` и Cowork runtime каждого
приложения работают независимо.
Новая установка ClaudeAlt хранит cookies, OAuth, IndexedDB и Chromium locks в
`${XDG_CONFIG_HOME:-~/.config}/ClaudeAlt`. Если уже существует прежний каталог
`${XDG_CONFIG_HOME:-~/.config}/Claude-Account-2`, launcher использует его, чтобы после обновления
не потерять авторизацию второго аккаунта. Глобальные данные Claude Code в `~/.claude` могут
по-прежнему использоваться обоими приложениями.
## Настройка
| Переменная | Назначение |
|:--|:--|
| `CLAUDE_ALT_DATA_DIR` | Явно задать каталог профиля ClaudeAlt |
| `CLAUDE_DESKTOP_ACCOUNT2_DIR` | Совместимое имя переменной прежнего второго профиля |
| `CLAUDE_ALT_EXECUTABLE` | Переопределить executable ClaudeAlt для диагностики |
Основной Claude продолжает использовать upstream desktop-id `com.anthropic.Claude`, а ClaudeAlt
использует `com.anthropic.ClaudeAlt`.
## Сетевая изоляция
Claude и ClaudeAlt имеют разные Chromium-профили, single-instance sockets,
cookies, browser storage, Crashpad-каталоги и Cowork VM sockets. Временные OAuth
callback-серверы по умолчанию получают свободный loopback-порт от ОС.
Приложения не являются сетевыми песочницами: оба выходят в Интернет с
одного хоста и публичного IP-адреса. Общий `claude://` URL handler намеренно
остаётся за основным Claude. Если в обоих приложениях вручную задать
один и тот же фиксированный OAuth callback-порт и запустить авторизацию
одновременно, второе приложение получит `EADDRINUSE`.
+57
View File
@@ -0,0 +1,57 @@
# Codex
Пакет устанавливает неофициальную Linux-перепаковку Codex Desktop и
предусмотренную Linux-реализацию Computer Use из проектов
[`codex-desktop-linux`](https://github.com/ilysenko/codex-desktop-linux) и
[`computer-use-linux`](https://github.com/agent-sh/computer-use-linux).
В пакет входят:
- приложение `codex-app`;
- bundled plugin `computer-use@openai-bundled`;
- нативный backend `codex-computer-use-linux`, собранный из исходников для
версии glibc текущего дистрибутива;
- интеграция с AT-SPI, XDG Desktop Portal и доступными средствами ввода Linux.
Патчи не включают серверную возможность в обход Codex. Они добавляют Linux в
список поддерживаемых локальных платформ и сохраняют проверку feature gate
`computer_use`, которую возвращает сервис.
## Первичная настройка
После установки полностью перезапустите Codex. Состояние backend проверяется
без изменения системы:
```bash
codex-computer-use-linux doctor
```
Рекомендуемую настройку доступности и ввода выполняет upstream-мастер:
```bash
codex-computer-use-linux setup
```
Для получения списка окон в GNOME может понадобиться отдельная настройка
расширения:
```bash
codex-computer-use-linux setup-window-targeting
```
Команда `setup` идемпотентно включает AT-SPI в текущем пользовательском сеансе;
она изменяет пользовательскую настройку доступности без отдельного запроса.
Разрешения на захват экрана и управление вводом выдаются пользователем через
системные диалоги портала.
## Диагностика
```bash
codex-computer-use-linux doctor
codex-computer-use-linux apps
codex-computer-use-linux windows
```
Если интерфейс всё ещё показывает недоступность, сначала убедитесь, что
установлен пакет Nivora с `release=2` или новее, затем закройте все процессы
Codex и запустите приложение заново.
+27
View File
@@ -0,0 +1,27 @@
# GitHub Desktop
Пакет собирает оригинальный GitHub Desktop для Linux непосредственно из
официального репозитория [`desktop/desktop`](https://github.com/desktop/desktop).
Готовые артефакты, патчи и исходники `shiftkey/desktop` либо других форков не
используются.
GitHub официально выпускает установщики только для macOS и Windows. Linux-сборка
в Nivora поэтому является неофициальным пакетом, но код приложения и закреплённые
submodule-исходники берутся только из официальных репозиториев GitHub.
Тяжёлая сборка выполняется workflow `Сборка GitHub Desktop для Linux` на
нативных GitHub-hosted runner’ах `x64` и `arm64`. Workflow публикует
детерминированно упакованные tar.gz и `SHA256SUMS` в релизе Nivora, а рецепт
скачивает эти готовые артефакты с закреплёнными контрольными суммами.
Установка:
```bash
sudo stplr install nivora/github-desktop
```
Команда запуска:
```bash
github-desktop
```
+43
View File
@@ -0,0 +1,43 @@
# Nivora Stapler Helper
`nivora-stplr` добавляет короткие команды для Stapler.
```bash
sudo stplr install nivora/nivora-stplr
```
## Команды
| Alias | Вызов Stapler |
|:--|:--|
| `sli` | `sudo stplr install` |
| `slr` | `sudo stplr remove` |
| `slu` | `sudo stplr up` |
| `slf` | `sudo stplr fix` |
| `slref` | `sudo stplr refresh` |
| `sls` | `stplr search` |
| `slii` | `stplr info` |
| `sll` | `stplr list` |
| `sl` | Диспетчер тех же команд |
Примеры:
```bash
sli --repo nivora parsec
slii nivora/codex
slu --clean
```
Короткое имя пакета не привязывается к Nivora по умолчанию. Префикс задаётся явно:
```bash
export NIVORA_STPLR_REPO=nivora
```
| Переменная | Назначение |
|:--|:--|
| `NIVORA_STPLR_REPO` | Префикс для коротких package names |
| `NIVORA_STPLR_SUDO` | Команда повышения привилегий, по умолчанию `sudo` |
| `NIVORA_STPLR_QUIET=1` | Не печатать команду перед запуском |
Helper не передаёт произвольные shell-строки через `sudo`.
+16
View File
@@ -0,0 +1,16 @@
# OpenCode
Пакет устанавливает официальный Linux desktop-артефакт OpenCode без изменений его кода и ресурсов.
```bash
sudo stplr install nivora/opencode
```
## Запуск
- из меню приложений — `OpenCode`;
- из терминала — `opencode-desktop`.
Команда `opencode-desktop` является прямой ссылкой на upstream-бинарник
`/opt/OpenCode/ai.opencode.desktop`: wrapper, лимиты памяти, GPU-fallback и другие поведенческие
патчи не применяются.
+21
View File
@@ -0,0 +1,21 @@
# Ventoy
Пакет устанавливает официальный нативный Linux GUI Ventoy и показывает его в
меню приложений под коротким названием **Ventoy**.
## Запуск
```bash
ventoy
```
При запуске Ventoy запрашивает административные права через `pkexec`, потому
что программа размечает накопители и записывает загрузочные структуры напрямую.
## Важно
- Перед установкой Ventoy внимательно проверьте выбранное устройство.
- Первичная установка стирает данные на выбранном накопителе.
- Обновление уже подготовленного Ventoy-накопителя сохраняет раздел с образами,
однако резервная копия важных данных всё равно рекомендуется.
- Пакет использует официальный архив upstream и поддерживает `amd64` и `arm64`.
+34
View File
@@ -0,0 +1,34 @@
# Модель доверия
Nivora — независимый community-репозиторий. Он не контролируется командой Stapler или
разработчиками упакованных приложений.
## Что можно проверить
- `Staplerfile` описывает sources, зависимости, сборку и состав payload.
- Локальные wrappers и hooks хранятся рядом с рецептом.
- Каждому source соответствует SHA-256.
- Валидатор запрещает HTTP, `SKIP`, отсутствующие локальные sources и незакреплённые Git URL.
## Что не гарантирует SHA-256
Контрольная сумма обнаруживает замену файла после фиксации рецепта. Она не доказывает:
- отсутствие уязвимостей в upstream-приложении;
- корректность сетевого сервиса;
- соответствие файла исходному коду без воспроизводимой upstream-сборки.
## Проприетарные пакеты
Для `Custom` и `Proprietary` Nivora переупаковывает опубликованный артефакт, но не изменяет
условия его владельца. Учётные записи, подписки, API и сетевые сервисы в пакет не входят.
## Данные пользователя
Пакетные scripts не должны удалять каталоги в `$HOME`, выполнять logout или сбрасывать учётную
запись при обычном удалении. Отдельные команды очистки Tailscale и NetBird требуют `--yes`.
## Границы CI
CI проверяет структуру, shell/Python, metadata, checksums и выбранные сборки. Разница в версиях
библиотек, драйверах, desktop-окружениях и внешних API остаётся ограничением.
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
package_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
repo_root="$(cd "${package_dir}/.." && pwd)"
exec "${repo_root}/tools/package_updates.sh" check "$(basename "$package_dir")"
+12
View File
@@ -0,0 +1,12 @@
Сведения о лицензии пакета Fisher
Лицензия: MIT.
Исходный проект: https://github.com/jorgebucaran/fisher
Нюансы пакета:
- Пакет устанавливает Fisher — менеджер плагинов для Fish shell.
- Код распространяется на условиях MIT License.
- Пакет не добавляет закрытых бинарных компонентов.
Полный официальный текст MIT:
https://opensource.org/licenses/MIT
+54
View File
@@ -0,0 +1,54 @@
name='fisher'
version='4.4.8'
release=3
summary='Fish plugin manager'
summary_ru='Менеджер плагинов для Fish shell'
desc='Fisher is a plugin manager for Fish shell — zero configuration, pure Fish, blazing fast concurrent downloads.'
desc_ru='Fisher — менеджер плагинов для Fish shell. Ноль конфигурации, чистый Fish, молниеносная параллельная загрузка.'
group='Shells'
homepage='https://github.com/jorgebucaran/fisher'
maintainer='Cheviiot <https://github.com/Cheviiot>'
architectures=('all')
license=('MIT')
provides=()
replaces=('fisher')
conflicts=()
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
deps=(
'fish'
)
deps_debian=('fish')
deps_ubuntu=('fish')
deps_fedora=('fish')
deps_arch=('fish')
deps_altlinux=('fish')
deps_opensuse=('fish')
sources=(
"https://raw.githubusercontent.com/jorgebucaran/fisher/${version}/functions/fisher.fish"
'local:///LICENSE'
)
checksums=(
'0fb6c81ae3003e95b5671766fa6c25c3597066e29965b7772f6c1b007387356d'
'567ce81f1514e7338d5c1a1e5c2d8ef772c1ff01d77d7b76501e67c148b14dbb'
)
package() {
install -Dm644 "${srcdir}/fisher.fish" \
"${pkgdir}/usr/share/fish/vendor_functions.d/fisher.fish"
install-license "${srcdir}/LICENSE" "fisher/LICENSE.nivora"
}
files() {
files-find \
"/usr/share/fish/vendor_functions.d/fisher.fish"
files-find-license
}
+1
View File
@@ -0,0 +1 @@
[inherit]
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
package_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
repo_root="$(cd "${package_dir}/.." && pwd)"
exec "${repo_root}/tools/package_updates.sh" check "$(basename "$package_dir")"
+109
View File
@@ -0,0 +1,109 @@
name='github-desktop'
version='3.6.3'
release=2
summary='GitHub Desktop built from the official source'
summary_ru='GitHub Desktop, собранный из официальных исходников'
group='Development/Tools'
desc='The original GitHub Desktop application, built for Linux by Nivora CI directly from the official desktop/desktop source tree without third-party forks.'
desc_ru='Оригинальное приложение GitHub Desktop, собранное для Linux в CI Nivora напрямую из официального дерева desktop/desktop без сторонних форков.'
homepage='https://github.com/desktop/desktop'
maintainer='GitHub <https://github.com/desktop>'
architectures=('amd64' 'arm64')
license=('MIT')
provides=()
replaces=('github-desktop')
conflicts=()
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
deps=()
deps_debian=('ca-certificates' 'git' 'libasound2' 'libatk-bridge2.0-0' 'libatk1.0-0' 'libatspi2.0-0' 'libcairo2' 'libcups2' 'libdbus-1-3' 'libexpat1' 'libgbm1' 'libglib2.0-0' 'libgtk-3-0' 'libnspr4' 'libnss3' 'libpango-1.0-0' 'libsecret-1-0' 'libudev1' 'libx11-6' 'libxcb1' 'libxcomposite1' 'libxdamage1' 'libxext6' 'libxfixes3' 'libxkbcommon0' 'libxrandr2' 'libxss1' 'libxtst6' 'xdg-utils')
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=('ca-certificates' 'git' 'alsa-lib' 'atk' 'at-spi2-atk' 'at-spi2-core' 'cairo' 'cups-libs' 'dbus-libs' 'expat' 'mesa-libgbm' 'glib2' 'gtk3' 'nspr' 'nss' 'pango' 'libsecret' 'systemd-libs' 'libX11' 'libxcb' 'libXcomposite' 'libXdamage' 'libXext' 'libXfixes' 'libxkbcommon' 'libXrandr' 'libXScrnSaver' 'libXtst' 'xdg-utils')
deps_arch=('ca-certificates' 'git' 'alsa-lib' 'atk' 'at-spi2-atk' 'at-spi2-core' 'cairo' 'cups' 'dbus' 'expat' 'libgbm' 'glib2' 'gtk3' 'nspr' 'nss' 'pango' 'libsecret' 'systemd-libs' 'libx11' 'libxcb' 'libxcomposite' 'libxdamage' 'libxext' 'libxfixes' 'libxkbcommon' 'libxrandr' 'libxss' 'libxtst' 'xdg-utils')
deps_opensuse=('ca-certificates' 'git' 'alsa' 'libatk-1_0-0' 'libatk-bridge-2_0-0' 'libatspi0' 'libcairo2' 'cups-libs' 'libdbus-1-3' 'libexpat1' 'libgbm1' 'glib2' 'gtk3' 'mozilla-nspr' 'mozilla-nss' 'pango' 'libsecret-1-0' 'libudev1' 'libX11-6' 'libxcb1' 'libXcomposite1' 'libXdamage1' 'libXext6' 'libXfixes3' 'libxkbcommon0' 'libXrandr2' 'libXss1' 'libXtst6' 'xdg-utils')
deps_altlinux=('ca-certificates' 'git' 'libalsa' 'at-spi2-atk' 'libat-spi2-core' 'libatk' 'libcairo' 'libcups' 'libdbus' 'libexpat' 'libgbm' 'glib2' 'libgtk+3' 'libnspr' 'libnss' 'libpango' 'libsecret' 'libudev0' 'libX11' 'libxcb' 'libXcomposite' 'libXdamage' 'libXext' 'libXfixes' 'libxkbcommon' 'libXrandr' 'libXScrnSaver' 'libXtst' 'xdg-utils')
build_deps=('binutils')
sources=(
"https://github.com/Cheviiot/Nivora/releases/download/github-desktop-${version}-linux/github-desktop-${version}-linux-x64.tar.gz?~archive=false&~name=github-desktop.tar.gz"
"https://raw.githubusercontent.com/desktop/desktop/release-${version}/LICENSE?~archive=false&~name=LICENSE"
"https://raw.githubusercontent.com/desktop/desktop/release-${version}/app/static/logos/prod/icon-logo.icon/Assets/1%20%E2%80%93%20Layer%203.svg?~archive=false&~name=github-desktop.svg"
)
sources_arm64=(
"https://github.com/Cheviiot/Nivora/releases/download/github-desktop-${version}-linux/github-desktop-${version}-linux-arm64.tar.gz?~archive=false&~name=github-desktop.tar.gz"
"https://raw.githubusercontent.com/desktop/desktop/release-${version}/LICENSE?~archive=false&~name=LICENSE"
"https://raw.githubusercontent.com/desktop/desktop/release-${version}/app/static/logos/prod/icon-logo.icon/Assets/1%20%E2%80%93%20Layer%203.svg?~archive=false&~name=github-desktop.svg"
)
checksums=(
'sha256:78d55b126a200c3585c7d0e3a32b95fe5d0d19e71921169c3e376af520d9a179'
'sha256:891d678cd6aa67c0712f663b5fee690f24d11d360795300814f7bf2eb91ba530'
'sha256:182efd5af36dccaa389086695e38140ff42af0befd2caaa6661e4cc0ffe614b7'
)
checksums_arm64=(
'sha256:b1934f1d8d621ee879ef6b7d7dabe9704134c4dc861813b776f42deedce5a038'
'sha256:891d678cd6aa67c0712f663b5fee690f24d11d360795300814f7bf2eb91ba530'
'sha256:182efd5af36dccaa389086695e38140ff42af0befd2caaa6661e4cc0ffe614b7'
)
scripts=(
['postinstall']='postinstall.sh'
['postremove']='postremove.sh'
)
package() {
cd "${srcdir}"
tar -xzf github-desktop.tar.gz
[[ -x "${srcdir}/github-desktop/desktop" ]]
install -dm755 "${pkgdir}/opt/github-desktop"
cp -a "${srcdir}/github-desktop/." "${pkgdir}/opt/github-desktop/"
chmod 0755 "${pkgdir}/opt/github-desktop/desktop"
if [[ -f "${pkgdir}/opt/github-desktop/chrome-sandbox" ]]; then
chmod 0755 "${pkgdir}/opt/github-desktop/chrome-sandbox"
fi
install -dm755 "${pkgdir}/usr/bin"
ln -sfn /opt/github-desktop/desktop "${pkgdir}/usr/bin/github-desktop"
install -Dm644 github-desktop.svg \
"${pkgdir}/usr/share/icons/hicolor/scalable/apps/github-desktop.svg"
install -Dm644 /dev/stdin \
"${pkgdir}/usr/share/applications/github-desktop.desktop" <<'EOF'
[Desktop Entry]
Name=GitHub Desktop
Comment=Simple collaboration from your desktop
Comment[ru]=Графический клиент для работы с GitHub
Exec=/usr/bin/github-desktop --protocol-launcher %U
Terminal=false
Type=Application
Icon=github-desktop
Categories=Development;RevisionControl;
MimeType=x-scheme-handler/x-github-desktop-auth;x-scheme-handler/x-github-client;x-scheme-handler/github-mac;
StartupWMClass=desktop
EOF
install-license LICENSE "github-desktop/LICENSE"
install -Dm644 "${srcdir}/github-desktop/LICENSE" \
"${pkgdir}/usr/share/licenses/github-desktop/LICENSE.electron.txt"
install -Dm644 "${srcdir}/github-desktop/LICENSES.chromium.html" \
"${pkgdir}/usr/share/licenses/github-desktop/LICENSES.chromium.html"
}
files() {
files-find "/opt/github-desktop/**/*"
files-find-binary
files-find-desktop
files-find-license
files-find "/usr/share/icons/hicolor/*/apps/github-desktop.svg"
}
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
set -euo pipefail
sandbox=/opt/github-desktop/chrome-sandbox
if [[ -f "$sandbox" ]]; then
if [[ -L /proc/self/ns/user ]] && command -v unshare >/dev/null 2>&1 \
&& unshare --user true 2>/dev/null; then
chmod 0755 "$sandbox"
else
chmod 4755 "$sandbox"
fi
fi
if command -v update-desktop-database >/dev/null 2>&1; then
update-desktop-database -q /usr/share/applications || true
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
gtk-update-icon-cache -f -q /usr/share/icons/hicolor || true
fi
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
set -euo pipefail
if command -v update-desktop-database >/dev/null 2>&1; then
update-desktop-database -q /usr/share/applications || true
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
gtk-update-icon-cache -f -q /usr/share/icons/hicolor || true
fi
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
package_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
repo_root="$(cd "${package_dir}/.." && pwd)"
exec "${repo_root}/tools/package_updates.sh" check "$(basename "$package_dir")"
+12
View File
@@ -0,0 +1,12 @@
Сведения о лицензии пакета Happ
Лицензия: Custom.
Официальный сайт: https://happ.su/
Условия лицензии: https://happ.su/license
Нюансы пакета:
- Пакет перепаковывает официальный deb-артефакт Happ Desktop для Linux.
- Happ является проприетарным приложением FlyFrog LLC.
- В составе приложения могут использоваться проприетарные компоненты и сетевые функции, связанные с xray-core/sing-box-сценариями.
- Использование клиента, подписок и подключений регулируется условиями Happ и провайдера сервиса.
- Пакет помечен как несвободный через `nonfree=1`.
+336
View File
@@ -0,0 +1,336 @@
name='happ'
version=3.3.6
release=4
summary='User-friendly GUI proxy client for xray-core'
summary_ru='Удобный GUI-прокси-клиент для xray-core'
group='Networking/Other'
desc='Happ is a GUI client for xray-core with TUN/VPN mode via sing-box, anti-censorship tools, QR code import and subscription management.'
desc_ru='Happ — GUI-клиент для xray-core с режимом TUN/VPN через sing-box, средствами обхода блокировок, импортом QR-кодов и управлением подписками.'
homepage='https://happ.su/'
maintainer='FlyFrog LLC <support@happ.su>'
architectures=('amd64' 'arm64')
license=('Custom')
nonfree=1
nonfree_msg='Happ является проприетарным приложением FlyFrog LLC. Использование клиента, подписок и сетевых функций регулируется условиями Happ.'
nonfree_url='https://happ.su/license'
provides=()
replaces=('happ')
conflicts=()
# auto_req отключен — Qt-приложение поставляется с bundled библиотеками
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
deps=()
deps_debian=('libc6' 'libssl3 | libssl3t64')
deps_ubuntu=('libc6' 'libssl3 | libssl3t64')
deps_fedora=('glibc' 'openssl-libs')
deps_arch=('glibc' 'openssl')
deps_opensuse=('glibc' 'libopenssl1_1')
deps_altlinux=('glibc' 'libssl3')
build_deps=('binutils')
build_deps_debian=('binutils' 'python3')
build_deps_ubuntu=("${build_deps_debian[@]}")
build_deps_fedora=('binutils' 'python3')
build_deps_arch=('binutils' 'python')
build_deps_opensuse=('binutils' 'python3')
build_deps_altlinux=('binutils' 'python3')
sources=(
"https://github.com/Happ-proxy/happ-desktop/releases/download/${version}/Happ.linux.x64.deb?~archive=false&~name=happ.deb"
'local:///patch-tray-icons.py'
'local:///happ-launcher'
'local:///tray-idle.svg'
'local:///tray-connected.svg'
'local:///LICENSE'
)
sources_arm64=(
"https://github.com/Happ-proxy/happ-desktop/releases/download/${version}/Happ.linux.arm64.deb?~archive=false&~name=happ.deb"
'local:///patch-tray-icons.py'
'local:///happ-launcher'
'local:///tray-idle.svg'
'local:///tray-connected.svg'
'local:///LICENSE'
)
checksums=(
'a7dac51277387bfe1049b1ad40f40f2e74af233a5eab020b5be1a622effc46a4'
'5b0106dda286b039d6bdce45208f44ea3eb7a2deb4e00455190ce7a07177dde0'
'fa4d741a4e2df82b0a19291e2c7bad4a8753a1e59237b2bfe5fef7de8c22efd2'
'af47a069497a8063b9028549dc6db111776589818c6165c7a46cb8b25a822ead'
'7e105f63e4535676e15e4d613426d6af843376a09fd25674bbbf367778668ae5'
'5f0fbc1b0b0e8399c8a04425512168e3dd87960381e85a7cb62a41d55b054b0f'
)
checksums_arm64=(
'a4d3d6dcab1db61db23cdf0f86bce736014887262b570927befe48cb49f14aa6'
'5b0106dda286b039d6bdce45208f44ea3eb7a2deb4e00455190ce7a07177dde0'
'fa4d741a4e2df82b0a19291e2c7bad4a8753a1e59237b2bfe5fef7de8c22efd2'
'af47a069497a8063b9028549dc6db111776589818c6165c7a46cb8b25a822ead'
'7e105f63e4535676e15e4d613426d6af843376a09fd25674bbbf367778668ae5'
'5f0fbc1b0b0e8399c8a04425512168e3dd87960381e85a7cb62a41d55b054b0f'
)
scripts=(
['postinstall']='postinstall.sh'
['postremove']='postremove.sh'
)
package() {
cd "${srcdir}"
mkdir -p "${srcdir}/happ-extracted"
ar x happ.deb
tar -xf data.tar.* -C "${srcdir}/happ-extracted"
cp -a "${srcdir}/happ-extracted/opt" "${pkgdir}/opt"
python3 patch-tray-icons.py \
"${pkgdir}/opt/happ/bin/Happ" \
tray-idle.svg \
tray-connected.svg
install-systemd "${srcdir}/happ-extracted/etc/systemd/system/happd.service"
sed -i 's|^Exec=/opt/happ/bin/Happ|Exec=/usr/bin/happ|' \
"${srcdir}/happ-extracted/usr/share/applications/Happ.desktop"
install-desktop "${srcdir}/happ-extracted/usr/share/applications/Happ.desktop"
python3 - \
"${srcdir}/happ-extracted/usr/share/icons/hicolor/256x256/apps/happ.png" \
"${pkgdir}/usr/share/icons/hicolor/512x512/apps/happ.png" <<'PY'
import math
import struct
import sys
import zlib
from pathlib import Path
PNG_SIG = b"\x89PNG\r\n\x1a\n"
def read_png(path):
data = Path(path).read_bytes()
if not data.startswith(PNG_SIG):
raise ValueError("not a PNG")
pos = len(PNG_SIG)
width = height = bit_depth = color_type = None
idat = bytearray()
while pos < len(data):
length = struct.unpack(">I", data[pos:pos + 4])[0]
pos += 4
chunk_type = data[pos:pos + 4]
pos += 4
chunk = data[pos:pos + length]
pos += length + 4
if chunk_type == b"IHDR":
width, height, bit_depth, color_type, _, _, _ = struct.unpack(
">IIBBBBB", chunk
)
elif chunk_type == b"IDAT":
idat.extend(chunk)
elif chunk_type == b"IEND":
break
if bit_depth != 8 or color_type not in (2, 6):
raise ValueError("unsupported PNG format")
channels = 4 if color_type == 6 else 3
raw = zlib.decompress(bytes(idat))
stride = width * channels
rows = []
prev = [0] * stride
index = 0
for _ in range(height):
filter_type = raw[index]
index += 1
encoded = list(raw[index:index + stride])
index += stride
row = [0] * stride
for x, value in enumerate(encoded):
left = row[x - channels] if x >= channels else 0
up = prev[x]
upper_left = prev[x - channels] if x >= channels else 0
if filter_type == 0:
decoded = value
elif filter_type == 1:
decoded = (value + left) & 255
elif filter_type == 2:
decoded = (value + up) & 255
elif filter_type == 3:
decoded = (value + ((left + up) // 2)) & 255
elif filter_type == 4:
predictor = left + up - upper_left
distances = (
abs(predictor - left),
abs(predictor - up),
abs(predictor - upper_left),
)
decoded = (value + (left, up, upper_left)[distances.index(min(distances))]) & 255
else:
raise ValueError("unsupported PNG filter")
row[x] = decoded
prev = row
rows.append(row)
pixels = []
for row in rows:
out_row = []
for x in range(width):
offset = x * channels
if channels == 4:
out_row.append(tuple(row[offset:offset + 4]))
else:
out_row.append((row[offset], row[offset + 1], row[offset + 2], 255))
pixels.append(out_row)
return width, height, pixels
def write_png(path, width, height, pixels):
def chunk(chunk_type, payload):
crc = zlib.crc32(chunk_type + payload) & 0xFFFFFFFF
return struct.pack(">I", len(payload)) + chunk_type + payload + struct.pack(">I", crc)
raw = bytearray()
for y in range(height):
raw.append(0)
for x in range(width):
raw.extend(bytes(pixels[y][x]))
ihdr = struct.pack(">IIBBBBB", width, height, 8, 6, 0, 0, 0)
Path(path).parent.mkdir(parents=True, exist_ok=True)
Path(path).write_bytes(
PNG_SIG + chunk(b"IHDR", ihdr) + chunk(b"IDAT", zlib.compress(bytes(raw), 9))
+ chunk(b"IEND", b"")
)
def rounded_rect_coverage(px, py, size, margin, radius, samples=4):
covered = 0
left = top = margin
right = bottom = size - margin
for sy in range(samples):
for sx in range(samples):
x = px + (sx + 0.5) / samples
y = py + (sy + 0.5) / samples
if x < left or x > right or y < top or y > bottom:
continue
cx = min(max(x, left + radius), right - radius)
cy = min(max(y, top + radius), bottom - radius)
if (x - cx) ** 2 + (y - cy) ** 2 <= radius ** 2:
covered += 1
return covered / (samples * samples)
def sample_alpha(alpha, x, y):
height = len(alpha)
width = len(alpha[0])
if x < 0 or y < 0 or x > width - 1 or y > height - 1:
return 0.0
x0 = int(math.floor(x))
y0 = int(math.floor(y))
x1 = min(x0 + 1, width - 1)
y1 = min(y0 + 1, height - 1)
dx = x - x0
dy = y - y0
top = alpha[y0][x0] * (1 - dx) + alpha[y0][x1] * dx
bottom = alpha[y1][x0] * (1 - dx) + alpha[y1][x1] * dx
return top * (1 - dy) + bottom * dy
src_path, dest_path = sys.argv[1], sys.argv[2]
src_width, src_height, source = read_png(src_path)
background = 43
logo_alpha = []
for y in range(src_height):
row = []
for x in range(src_width):
r, g, b, a = source[y][x]
luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b
row.append(max(0.0, min(1.0, (luminance - background) / (255 - background))) * (a / 255.0))
logo_alpha.append(row)
xs = [x for y in range(src_height) for x in range(src_width) if logo_alpha[y][x] > 0.04]
ys = [y for y in range(src_height) for x in range(src_width) if logo_alpha[y][x] > 0.04]
left, right = min(xs), max(xs)
top, bottom = min(ys), max(ys)
crop_width = right - left + 1
crop_height = bottom - top + 1
cropped = [
[logo_alpha[y][x] for x in range(left, right + 1)]
for y in range(top, bottom + 1)
]
size = 512
margin = 38
radius = 116
logo_height = 292
logo_width = round(crop_width * logo_height / crop_height)
logo_x = (size - logo_width) // 2
logo_y = (size - logo_height) // 2 + 6
pixels = [[(0, 0, 0, 0) for _ in range(size)] for _ in range(size)]
for y in range(size):
for x in range(size):
coverage = rounded_rect_coverage(x, y, size, margin, radius)
if coverage:
pixels[y][x] = (43, 43, 43, round(255 * coverage))
for y in range(logo_height):
source_y = (y + 0.5) * crop_height / logo_height - 0.5
target_y = logo_y + y
if target_y < 0 or target_y >= size:
continue
for x in range(logo_width):
source_x = (x + 0.5) * crop_width / logo_width - 0.5
target_x = logo_x + x
if target_x < 0 or target_x >= size:
continue
alpha = sample_alpha(cropped, source_x, source_y)
if alpha <= 0:
continue
dr, dg, db, da = pixels[target_y][target_x]
da_float = da / 255.0
out_alpha = alpha + da_float * (1 - alpha)
out_rgb = round((255 * alpha + dr * da_float * (1 - alpha)) / out_alpha)
pixels[target_y][target_x] = (out_rgb, out_rgb, out_rgb, round(out_alpha * 255))
write_png(dest_path, size, size, pixels)
PY
install -Dm644 "${srcdir}/happ-extracted/usr/share/mime/packages/happ-mime.xml" \
"${pkgdir}/usr/share/mime/packages/happ-mime.xml"
install -Dm755 "${srcdir}/happ-launcher" "${pkgdir}/usr/bin/happ"
install-license "${srcdir}/LICENSE" "happ/LICENSE.nivora"
}
files() {
files-find "/opt/happ/**/*"
files-find-binary
files-find-desktop
files-find-systemd
files-find-license
files-find \
"/usr/share/icons/hicolor/512x512/apps/happ.png" \
"/usr/share/mime/packages/happ-mime.xml"
}
+9
View File
@@ -0,0 +1,9 @@
#!/bin/sh
# Happ's bundled Qt falls back to the generic GNOME platform theme, which
# reports Qt::ColorScheme::Unknown. The bundled portal plugin follows the
# desktop color-scheme setting and emits updates when it changes.
QT_QPA_PLATFORMTHEME="${QT_QPA_PLATFORMTHEME:-xdgdesktopportal}"
export QT_QPA_PLATFORMTHEME
exec /opt/happ/bin/Happ "$@"
+94
View File
@@ -0,0 +1,94 @@
#!/usr/bin/env python3
from __future__ import annotations
import hashlib
import struct
import sys
from pathlib import Path
UPSTREAM_RESOURCES = {
"idle": (
"c0939b3a81a59983f0db07cdfa08eae90d6ddc211b7b790c425beb72628355ea",
b'<svg width="64" height="64"',
),
"connected": (
"5e1703683c6263b5a2cdda83b034dec23fe31e72497f6644a3853b97c6fdef85",
b'<svg width="15" height="15"',
),
}
def replace_resource(
binary: bytearray,
kind: str,
replacement: bytes,
expected_start: int | None = None,
) -> tuple[int, int]:
expected_hash, marker = UPSTREAM_RESOURCES[kind]
matches: list[tuple[int, int]] = []
offset = 0
while True:
start = binary.find(marker, offset)
if start < 0:
break
offset = start + 1
if start < 4:
continue
length = struct.unpack_from(">I", binary, start - 4)[0]
if length <= 0 or start + length > len(binary):
continue
payload = bytes(binary[start:start + length])
if (
hashlib.sha256(payload).hexdigest() == expected_hash
and (expected_start is None or start == expected_start)
):
matches.append((start, length))
if len(matches) != 1:
raise RuntimeError(
f"expected exactly one upstream {kind} tray resource, found {len(matches)}"
)
start, capacity = matches[0]
if len(replacement) > capacity:
raise RuntimeError(
f"{kind} tray replacement is too large: {len(replacement)} > {capacity}"
)
struct.pack_into(">I", binary, start - 4, len(replacement))
binary[start:start + len(replacement)] = replacement
binary[start + len(replacement):start + capacity] = b"\0" * (
capacity - len(replacement)
)
return start, capacity
def main() -> int:
if len(sys.argv) != 4:
print(
"usage: patch-tray-icons.py <Happ binary> <idle.svg> <connected.svg>",
file=sys.stderr,
)
return 2
binary_path = Path(sys.argv[1])
binary = bytearray(binary_path.read_bytes())
connected_start, connected_capacity = replace_resource(
binary, "connected", Path(sys.argv[3]).read_bytes()
)
replace_resource(
binary,
"idle",
Path(sys.argv[2]).read_bytes(),
connected_start + connected_capacity + 4,
)
binary_path.write_bytes(binary)
print("Patched Happ tray icons")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+32
View File
@@ -0,0 +1,32 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
# Обновляем MIME-базу данных
if command -v update-mime-database &>/dev/null; then
optional_refresh "MIME-базу" update-mime-database /usr/share/mime
fi
# Обновляем desktop базу данных
if command -v update-desktop-database &>/dev/null; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
# Обновляем кэш иконок
if command -v gtk-update-icon-cache &>/dev/null; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
if command -v systemctl >/dev/null 2>&1 && [[ -d /run/systemd/system ]]; then
systemctl daemon-reload
systemctl enable --now happd.service
fi
+38
View File
@@ -0,0 +1,38 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
unit=/usr/lib/systemd/system/happd.service
if [[ ! -e "$unit" ]] && command -v systemctl >/dev/null 2>&1 && [[ -d /run/systemd/system ]]; then
if systemctl is-active --quiet happd.service; then
systemctl stop happd.service
fi
if systemctl is-enabled --quiet happd.service; then
systemctl disable happd.service
fi
systemctl daemon-reload
fi
# Обновляем MIME-базу данных
if command -v update-mime-database &>/dev/null; then
optional_refresh "MIME-базу" update-mime-database /usr/share/mime
fi
# Обновляем desktop базу данных
if command -v update-desktop-database &>/dev/null; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
# Обновляем кэш иконок
if command -v gtk-update-icon-cache &>/dev/null; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
+1
View File
@@ -0,0 +1 @@
[inherit]
+6
View File
@@ -0,0 +1,6 @@
<svg width="64" height="64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="g" x1="9" y1="7" x2="55" y2="57" gradientUnits="userSpaceOnUse"><stop stop-color="#35432E"/><stop offset="1" stop-color="#1D261A"/></linearGradient></defs>
<rect x="3" y="3" width="58" height="58" rx="16" fill="url(#g)" stroke="#A8FF5D" stroke-width="2.5"/>
<path d="M27.7 28.3l2.2-12.8L18 27.3l-3.7 21.9 11.9-11.8.3-1.8H28l7.4-7.3h-7.7ZM46 37.3l3.7-21.8-11.9 11.8-.2 1h-.8l-7.4 7.3h7l-2.3 13.6L46 37.3ZM36.1 49.2l-.7 4.4h9.9L48 37.4 36.1 49.2ZM28.4 15.3l.8-4.9h-10l-2.8 16.7 12-11.8Z" fill="#FAFAFB"/>
<path d="M28.1 37.4 16.2 49.2l-.8 4.4h10l2.7-16.2ZM36.2 27.1l11.9-11.8.8-4.9H39l-2.8 16.7Z" fill="#A8FF5D"/>
</svg>

After

Width:  |  Height:  |  Size: 737 B

+5
View File
@@ -0,0 +1,5 @@
<svg width="64" height="64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient id="g" x1="9" y1="7" x2="55" y2="57" gradientUnits="userSpaceOnUse"><stop stop-color="#3B3D44"/><stop offset="1" stop-color="#202126"/></linearGradient></defs>
<rect x="3" y="3" width="58" height="58" rx="16" fill="url(#g)" stroke="#62656E" stroke-width="2"/>
<path d="M28.1 37.4 16.2 49.2l-.8 4.4h10l2.7-16.2ZM27.7 28.3l2.2-12.8L18 27.3l-3.7 21.9 11.9-11.8.3-1.8H28l7.4-7.3h-7.7ZM46 37.3l3.7-21.8-11.9 11.8-.2 1h-.8l-7.4 7.3h7l-2.3 13.6L46 37.3ZM36.1 49.2l-.7 4.4h9.9L48 37.4 36.1 49.2ZM28.4 15.3l.8-4.9h-10l-2.8 16.7 12-11.8ZM36.2 27.1l11.9-11.8.8-4.9H39l-2.8 16.7Z" fill="#FAFAFB"/>
</svg>

After

Width:  |  Height:  |  Size: 705 B

+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
package_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
repo_root="$(cd "${package_dir}/.." && pwd)"
exec "${repo_root}/tools/package_updates.sh" check "$(basename "$package_dir")"
+16
View File
@@ -0,0 +1,16 @@
Сведения о лицензии пакета NetBird
Лицензии: BSD-3-Clause и AGPL-3.0-only.
Исходный проект: https://github.com/netbirdio/netbird
Официальный сайт: https://netbird.io/
Нюансы пакета:
- Пакет перепаковывает официальный deb-артефакт NetBird CLI/daemon для Linux.
- Основные части репозитория NetBird распространяются под BSD-3-Clause.
- Отдельные серверные компоненты исходного репозитория, включая management, signal, relay и combined, лицензированы под AGPL-3.0.
- Данный пакет устанавливает клиентский бинарник и systemd-сервис; серверная инфраструктура NetBird в пакет не входит.
- Подключение к облачному или собственному management-серверу регулируется условиями соответствующего сервиса.
Официальные тексты лицензий:
- https://opensource.org/license/bsd-3-clause
- https://www.gnu.org/licenses/agpl-3.0.txt
+90
View File
@@ -0,0 +1,90 @@
name='netbird'
version=0.75.0
release=1
summary='WireGuard-based mesh VPN client'
summary_ru='Mesh VPN-клиент на базе WireGuard'
group='Networking/Other'
desc='NetBird is a WireGuard-based mesh VPN client with SSO, MFA and policy-based access control for connecting devices and private networks.'
desc_ru='NetBird — mesh VPN-клиент на базе WireGuard с SSO, MFA и политиками доступа для соединения устройств и приватных сетей.'
homepage='https://netbird.io/'
maintainer='NetBird GmbH <dev@netbird.io>'
architectures=('amd64' 'arm64')
license=('BSD-3-Clause' 'AGPL-3.0-only')
provides=()
replaces=('netbird')
conflicts=()
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
deps=()
deps_debian=('ca-certificates' 'iproute2' 'iptables')
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=('ca-certificates' 'iproute' 'iptables')
deps_arch=('ca-certificates' 'iproute2' 'iptables')
deps_opensuse=('ca-certificates' 'iproute2' 'iptables')
deps_altlinux=('ca-certificates' 'iproute2' 'iptables')
build_deps=('binutils')
sources=(
"https://github.com/netbirdio/netbird/releases/download/v${version}/netbird_${version}_linux_amd64.deb?~archive=false&~name=netbird.deb"
"local:///netbird.png"
"local:///netbird.service"
"local:///netbird-purge-data"
'local:///LICENSE'
)
sources_arm64=(
"https://github.com/netbirdio/netbird/releases/download/v${version}/netbird_${version}_linux_arm64.deb?~archive=false&~name=netbird.deb"
"local:///netbird.png"
"local:///netbird.service"
"local:///netbird-purge-data"
'local:///LICENSE'
)
checksums=(
'sha256:b65f65d7f32831639ebdd44c241e7a2310287723b64dbc03c93292853042ee6f'
'sha256:71b133636e91eb67f221f64592f64fa04a3b2337f4d616be36492efae0cff643'
'sha256:abd8064281462a4ce12c2fdf2e9436d9ae6932ff3b366668a175f2a556169012'
'sha256:bb5c44ab330db0d63a82b582f2c508681a6922ea65f3bf2becaeed87693215b1'
'sha256:11555e8308e949c3965a681f48b83f7f2488cff03b09772d8af4a20d376464e7'
)
checksums_arm64=(
'sha256:9e57e854da0bbaf392cc6f99e37db75809e21c3effde0fca9f3bc9dc90245c02'
'sha256:71b133636e91eb67f221f64592f64fa04a3b2337f4d616be36492efae0cff643'
'sha256:abd8064281462a4ce12c2fdf2e9436d9ae6932ff3b366668a175f2a556169012'
'sha256:bb5c44ab330db0d63a82b582f2c508681a6922ea65f3bf2becaeed87693215b1'
'sha256:11555e8308e949c3965a681f48b83f7f2488cff03b09772d8af4a20d376464e7'
)
scripts=(
['postinstall']='postinstall.sh'
['postremove']='postremove.sh'
)
package() {
cd "${srcdir}"
mkdir -p "${srcdir}/extracted"
ar x netbird.deb
tar -xf data.tar.* -C "${srcdir}/extracted"
install-binary "${srcdir}/extracted/usr/bin/netbird"
install-binary "${srcdir}/netbird-purge-data"
install-systemd "${srcdir}/netbird.service"
install-license "${srcdir}/LICENSE" "netbird/LICENSE.nivora"
install -Dm644 "${srcdir}/netbird.png" "${pkgdir}/usr/share/icons/hicolor/256x256/apps/netbird.png"
}
files() {
files-find-binary
files-find-systemd
files-find-license
files-find "/usr/share/icons/hicolor/256x256/apps/netbird.png"
}
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
set -euo pipefail
if [[ "${1:-}" != "--yes" || "$#" -ne 1 ]]; then
echo "Использование: sudo netbird-purge-data --yes" >&2
echo "Команда отменит регистрацию узла NetBird и безвозвратно удалит локальное состояние." >&2
exit 2
fi
if [[ "${EUID}" -ne 0 ]]; then
echo "Ошибка: очистку данных NetBird нужно запускать от root." >&2
exit 1
fi
if command -v netbird >/dev/null 2>&1; then
if ! netbird deregister; then
echo "Предупреждение: NetBird не подтвердил deregister; локальные данные всё равно будут удалены." >&2
fi
fi
if command -v systemctl >/dev/null 2>&1 && [[ -d /run/systemd/system ]]; then
systemctl stop netbird.service
fi
rm -rf -- /var/lib/netbird /var/cache/netbird /var/log/netbird
rm -f -- /run/netbird.sock /var/run/netbird.sock
echo "Локальные данные NetBird удалены."
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

+19
View File
@@ -0,0 +1,19 @@
[Unit]
Description=NetBird mesh network client
Documentation=https://docs.netbird.io/
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
EnvironmentFile=-/etc/sysconfig/netbird
ExecStart=/usr/bin/netbird service run --log-level info --daemon-addr unix:///run/netbird.sock --log-file /var/log/netbird/client.log
Restart=on-failure
RestartSec=5s
StateDirectory=netbird
StateDirectoryMode=0700
LogsDirectory=netbird
LogsDirectoryMode=0750
[Install]
WantedBy=multi-user.target
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
set -euo pipefail
legacy_unit=/etc/systemd/system/netbird.service
if [[ -f "$legacy_unit" ]] && grep -Fq 'ExecStart=/usr/bin/netbird "service" "run"' "$legacy_unit"; then
rm -f -- "$legacy_unit"
fi
if command -v systemctl >/dev/null 2>&1 && [[ -d /run/systemd/system ]]; then
systemctl daemon-reload
systemctl enable --now netbird.service
fi
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
set -euo pipefail
unit=/usr/lib/systemd/system/netbird.service
if [[ ! -e "$unit" ]] && command -v systemctl >/dev/null 2>&1 && [[ -d /run/systemd/system ]]; then
if systemctl is-active --quiet netbird.service; then
systemctl stop netbird.service
fi
if systemctl is-enabled --quiet netbird.service; then
systemctl disable netbird.service
fi
systemctl daemon-reload
fi
+1
View File
@@ -0,0 +1 @@
include = "../stapler-repo.toml"
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
package_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
repo_root="$(cd "${package_dir}/.." && pwd)"
exec "${repo_root}/tools/package_updates.sh" check "$(basename "$package_dir")"

Some files were not shown because too many files have changed in this diff Show More