Author SHA1 Message Date
github-actions[bot] 5bce7b5345 chore: autonomously update netbird, opencode 2026-07-30 17:42:59 +00:00
github-actions[bot] 18bef94a96 chore: autonomously update codex, opencode, tailscale 2026-07-29 17:47:27 +00:00
github-actions[bot] 7b8347aee9 chore: autonomously update netbird, opencode, yandex-browser-stable 2026-07-28 17:45:44 +00:00
github-actions[bot] 73f99ad8dc chore: autonomously update opencode, telegram 2026-07-27 17:44:02 +00:00
github-actions[bot] 59c5942572 chore: autonomously update codex, opencode 2026-07-25 17:39:10 +00:00
Александр 0e0e474b72 vintner: bump to 0.5.0
No-PATH tool invocation, a concurrency lock for download/install, the
vintner doctor diagnostics command, an actionable wine-not-found
error, resumable downloads, and wedged-process recovery.
2026-07-25 18:22:09 +10:00
Александр 73b5e87e95 feat(max): add MAX messenger package, sandboxed with Firejail
MAX is the Russian state-affiliated messenger; the official Qt6 client is
repacked from download.max.ru with no independent security track record,
so it never runs unsandboxed. /usr/bin/max is a wrapper (not a symlink)
that always launches through `firejail --profile=max`; the .desktop Exec
goes through the same wrapper so there's no launch path that bypasses it.
The profile hides the rest of the home directory behind private ~/.config,
~/.cache and ~/.local/share/max subdirectories, drops capabilities, enables
seccomp, blocks system D-Bus, and blacklists the standalone crashpad_handler
crash-report uploader. Full elimination of first-party telemetry isn't
claimed or possible from the sandbox layer alone — documented in LICENSE.

Also wires up repo-wide bookkeeping now that all three package changes
(telegram-desktop -> telegram rename, vk-messenger, max) are in: README
package count/catalog rows, docs/maintenance.md naming table and directory
count, and tools/package_updates.sh + tools/validate_repo.py package lists.
2026-07-25 18:02:25 +10:00
Александр 6401c42a91 feat(vk-messenger): add VK Messenger package
Repacks the official Electron build vk.me/app serves directly (no GitHub
release or distro repo for this product). Upstream's own .deb installs
under the literal Cyrillic product name /opt/VK Мессенджер — package()
resolves that directory dynamically instead of hardcoding it, then lays
the payload out under a clean /opt/vk-messenger to match this repo's
convention.
2026-07-25 18:02:10 +10:00
Александр 3fdc6a608a rename(telegram-desktop): package ID to telegram
Same pattern already used for claude/claude-desktop: keep the upstream
desktop-id (org.telegram.desktop) and every installed path unchanged, only
rename the Stapler package directory and `name` field. `replaces` stays
self-name-only per the general rule in validate_repo.py (the old-name
carve-over in `replaces` is currently special-cased there for `claude`
only) — anyone with telegram-desktop installed needs `stplr install
telegram` + `stplr remove telegram-desktop` manually.
2026-07-25 18:01:52 +10:00
Александр 9833b202f9 fix(telegram-desktop): use Telegram's own external-updater opt-out instead
The DESKTOPINTEGRATION wrapper/environment.d combo only ever stopped the
shadow .desktop/.service self-install. It never addressed a second, worse
symptom: QGuiApplication::setDesktopFileName() in Platform::start() sets the
Wayland app_id to a per-install-path hashed "org.telegram.desktop._<hash>"
id whenever Core::UpdaterDisabled() is false — completely independent of
DESKTOPINTEGRATION — so the running window's app_id never matched our
shipped org.telegram.desktop.desktop and its icon/taskbar entry broke even
after the duplicate launcher file was silenced.

Telegram ships a documented opt-out for exactly this: Launcher::
ComputeExternalUpdater() (core/launcher.cpp) scans <exe-dir>/
externalupdater.d/ for a file containing a line equal to its own full
executable path, and calls SetUpdaterDisabledAtStartup() if found. That one
flag flips both Core::UpdaterDisabled() checks at once — InstallLauncher()
skips the shadow-file self-install AND desktopFileName() uses the plain
"org.telegram.desktop" id that matches our .desktop file, fixing the window
icon too. Drop the wrapper script and environment.d drop-in; back to a
plain /usr/bin symlink plus one externalupdater.d marker file.
2026-07-25 16:44:53 +10:00
Александр d3b2abecdd fix(telegram-desktop): also export DESKTOPINTEGRATION for the whole session
The /usr/bin wrapper only stops the shadow .desktop/.service self-install
when Telegram is actually launched through it. A stale GNOME Shell app-grid
entry (or anything else invoking /opt/telegram-desktop/Telegram directly)
still bypasses it and lets InstallLauncher() recreate the shadow files,
which was still reproducing after the previous fix.

Ship a systemd environment.d drop-in setting DESKTOPINTEGRATION=1 for the
whole graphical session, so the guard applies regardless of which launcher
or cached desktop entry starts the binary. Requires a full logout/login (or
reboot) to take effect, same as the desktop-shell cache refresh this bug
already needs on Wayland.
2026-07-25 16:30:19 +10:00
Александр b9e2887b2f fix(telegram-desktop): actually stop the shadow desktop-file self-install
The Exec-path and DBusActivatable matching from the previous two commits
were treating the wrong cause: InstallLauncher() in tdesktop's
specific_linux.cpp unconditionally wipes and rewrites its own
~/.local/share/applications/*.desktop and dbus-1/services/*.service on
every launch, regardless of what the installed .desktop file says — unless
the DESKTOPINTEGRATION env var is non-empty, which is upstream's documented
opt-out for exactly this kind of repack.

/usr/bin/telegram-desktop is now a wrapper script that sets
DESKTOPINTEGRATION=1 before exec'ing the real binary, covering every launch
path (desktop file, D-Bus activation, terminal). The .desktop file's
Exec/TryExec/DBusActivatable revert to their original values since the
field-matching theory they were built on didn't hold.
2026-07-25 16:11:20 +10:00
Александр f33ec4b124 fix(telegram-desktop): declare DBusActivatable=true to match Telegram's own check
Matching Exec to the real binary path wasn't enough: Telegram also compares
the installed .desktop's DBusActivatable field against what it expects and
still self-installs a shadow copy in ~/.local/share/applications when it
sees DBusActivatable=false, producing the same duplicate-icon symptom.
Setting it to true stops the shadow copy. Bumped release and checksum.
2026-07-25 15:56:05 +10:00
Александр c0929c030a vintner: bump to 0.4.0 - DirectX SDK support, legacy PlatformToolset aliasing
Two real new capabilities from upstream, both developed and verified
against a real years-old game client build: --with-dxsdk fetches the
DirectX SDK (June 2010) for legacy D3DX9 code, and old PlatformToolset-
pinned .vcxproj files (v90-v143) now transparently resolve to the one
installed compiler instead of failing MSB8020. Added cabextract as a
package dependency (needed at runtime for --with-dxsdk, same reasoning
as the existing msitools dependency).
2026-07-25 15:46:43 +10:00
Александр f02fc98097 vintner: bump to 0.3.1 - real toolrelay.exe mt.exe fix
Upstream fixed a real bug found while building Ogre3D through
vintner: toolrelay.exe's mt.exe detection compared the whole path
against "mt.exe" instead of just the filename, since it only checked
for a backslash separator on a path that actually arrives with
forward slashes - so the CMake-compatibility exit-code translation
this whole helper exists for had never actually fired.
2026-07-25 11:57:55 +10:00
Александр 08104de30c vintner: bump to 0.3.0, auto-install shell completion
Wires vintner's own `completion bash|zsh` output into Stapler's
install-completion helper (bash -> /usr/share/bash-completion/
completions/vintner, zsh -> /usr/share/zsh/site-functions/_vintner -
both verified against a real built package, since neither path nor
the required paired files-find() registration is documented anywhere
reachable). Completion now works immediately after `stplr install`,
no manual `source <(vintner completion bash)` needed.

0.3.0 also picks up upstream's signal-forwarding fix (a wine
subprocess no longer survives `vintner` being killed by PID) and its
own expanded test coverage.
2026-07-25 11:33:17 +10:00
Александр 91fe77b794 Rename msvc-go-wine package to vintner, bump to 0.2.0
Upstream rebranded to vintner (github.com/Cheviiot/vintner) and
cut a new release with WDK driver-build support, short command
aliases, and English/Russian CLI localization. Updates the package
directory, Staplerfile (name, urls, checksums, replaces), catalog
entry in README.md, and the two tools/ scripts that hardcode the
package name list.
2026-07-25 04:06:07 +10:00
Александр 5dc78222f6 fix(telegram-desktop): point Exec at the real binary to stop duplicate launcher icons
Telegram's own Linux runtime checks whether the installed .desktop file's
Exec matches its actual binary path, and silently writes a private copy to
~/.local/share/applications with a randomized name whenever it doesn't.
Our desktop entry pointed at the /usr/bin/telegram-desktop symlink, which
never matched /opt/telegram-desktop/Telegram, so Telegram regenerated a
shadow launcher on every start, producing a second, unmatched icon.

Exec/TryExec now point directly at the installed binary so Telegram's own
check finds a match. Bumped release and updated the checksum for the
changed desktop file.
2026-07-25 03:43:58 +10:00
github-actions[bot] 71d6f41d92 chore: autonomously update msvc-go-wine 2026-07-24 17:42:49 +00:00
Александр a68fb112d7 fix(telegram-desktop): correct ALT Linux dependency package names
libbrotli, libbz2, libfontconfig, libffi, libglib2 and hicolor-icon-theme
don't exist as such in ALT's repos. Verified real names against this
machine's apt-cache: libbrotlicommon/libbrotlidec, bzlib, libfontconfig1,
libffi8, glib2, icon-theme-hicolor.
2026-07-25 02:35:06 +10:00
Александр 84a1bbea8a fix(telegram-desktop): ignore archive ownership on extraction
tsetup.tar.xz bakes in uid/gid 1000 from Telegram's release tooling;
the sandboxed builder can't chown to it, so the build failed with
"Невозможно изменить владельца". --no-same-owner/--no-same-permissions
matches the pineconemc package's handling of the same issue.
2026-07-25 02:30:28 +10:00
Александр 8564f84ed5 fix: avoid substring collision in sync_readme_versions.py
Wrap the install command in backticks when matching README rows, so
nivora/claude no longer matches inside the nivora/claude-alt row.
2026-07-25 02:28:09 +10:00
Александр 07d13aca64 feat: add Telegram Desktop package
Repackages the official static tsetup.7.0.5.tar.xz Linux build for
amd64, keeping the upstream org.telegram.desktop desktop-id and
TelegramDesktop WM class.
2026-07-25 02:28:05 +10:00
Александр c34650a941 chore: update msvc-go-wine catalog version 2026-07-25 01:49:32 +10:00
Александр fdb4a492ef feat: add Nivora CLI and msvc-go-wine 2026-07-25 01:43:11 +10:00
Александр 3cc4217790 feat: rename Claude package 2026-07-25 01:22:52 +10:00
Александр f3af93802f feat: split ClaudeAlt into independent package 2026-07-25 01:18:17 +10:00
Александр 1801be3549 fix(yandex-browser): bind window to visible launcher 2026-07-25 00:55:17 +10:00
73 changed files with 2290 additions and 861 deletions
+64
View File
@@ -2,6 +2,70 @@
В файле фиксируются значимые изменения Nivora. В файле фиксируются значимые изменения Nivora.
## 2026-07-25 — Vintner
### Изменено
- Пакет `msvc-go-wine` переименован в `vintner` вслед за ребрендингом
апстрима (github.com/Cheviiot/vintner); `replaces` обновлён для чистого
перехода при обновлении.
- Версия поднята до `0.2.0`: добавлена поддержка сборки KMDF/UMDF-драйверов
через Windows Driver Kit (`download --with-wdk`), короткие алиасы команд
и локализация CLI на английский и русский (`VINTNER_LANG`).
- Версия поднята до `0.3.0`: пакет теперь сам регистрирует автодополнение
для bash (`/usr/share/bash-completion/completions/vintner`) и zsh
(`/usr/share/zsh/site-functions/_vintner`) через хелпер Stapler
`install-completion` — работает сразу после `stplr install`, без
ручного `source <(vintner completion bash)`. Также добавлена корректная
остановка процессов Wine при завершении `vintner` по PID.
- Версия поднята до `0.3.1`: исправлен реальный баг в `toolrelay.exe`
определение `mt.exe` по имени файла никогда не срабатывало (путь
приходил с прямыми слэшами, а проверка искала обратный), из-за чего
перевод кода возврата `mt.exe` для совместимости с CMake фактически не
работал ни разу. Найдено и исправлено при реальной сборке движка Ogre3D
через CMake+Ninja+cl.exe.
- Версия поднята до `0.4.0`: добавлена поддержка DirectX SDK (июнь 2010)
для сборки устаревшего кода на D3DX9 (`download --with-dxsdk`), и
алиасинг старых имён PlatformToolset (v90v143) на единственный
установленный компилятор — теперь старые `.vcxproj`, закреплённые за
давними версиями тулсета, тоже собираются. В зависимости добавлен
`cabextract` (нужен рантайму для `--with-dxsdk`).
- Версия поднята до `0.5.0`: инструменты MSVC теперь можно вызывать без
добавления `<dest>/bin/<arch>` в PATH — напрямую как `vintner cl ...`,
`vintner msbuild ...` и т.д. Добавлена команда `vintner doctor`,
диагностирующая неисправную настройку wine/toolchain. `download` теперь
докачивает прерванные загрузки вместо перезапуска с нуля, а
`download`/`install` берут файловую блокировку на каталог назначения,
чтобы два параллельных запуска не повредили дерево файлов. Ошибка
«wine не найден» теперь называет конкретный пакет для установки.
Wine-процессы больше не зависают навечно при обрыве сборки (принудительно
отключён node reuse MSBuild, добавлена поддержка таймаута через
`VINTNER_TIMEOUT`).
## 2026-07-25 — Telegram Desktop
### Добавлено
- В каталог добавлен Telegram Desktop 7.0.5 для `amd64` из официального
статически собранного Linux-архива `tsetup.<version>.tar.xz`.
- Сохранены официальные desktop-id `org.telegram.desktop` и WM-класс
`TelegramDesktop`; иконка взята из официального репозитория
`telegramdesktop/tdesktop` для закреплённого тега релиза.
- Пакет добавляет команду `telegram-desktop`; сборка распространяется под
GPL-3.0-only с отдельным разрешением на линковку с OpenSSL.
## 2026-07-25 — Nivora CLI
### Изменено
- Пакет `nivora-stplr` полностью переработан и переименован в `nivora-cli`.
- Новая команда `nv` получила компактное интерактивное меню, русскую и
английскую локализацию, диагностику, dry-run и генерацию автодополнения.
- Повседневные операции доступны отдельными командами в стиле eepm:
`nvi`, `nve`, `nvu`, `nvs`, `nvqi`, `nvqa`, `nvr`, `nvrl`, `nvf`, `nvd`
и `nvc`.
- Прежние команды семейства `sl*` удалены без слоя совместимости.
## 2026-07-24 — GitHub Desktop ## 2026-07-24 — GitHub Desktop
### Добавлено ### Добавлено
+13 -8
View File
@@ -11,7 +11,7 @@ Nivora предлагает открытые рецепты упаковки д
официальный репозиторий Stapler и не официальные пакеты upstream-проектов. официальный репозиторий Stapler и не официальные пакеты upstream-проектов.
<!-- package-count --> <!-- package-count -->
**19 пакетов** · **6 категорий** · `amd64`, `arm64` и `all` **24 пакета** · **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) [![Проверка Nivora](https://github.com/Cheviiot/Nivora/actions/workflows/quality.yml/badge.svg)](https://github.com/Cheviiot/Nivora/actions/workflows/quality.yml)
@@ -47,9 +47,12 @@ stplr info nivora/codex
|:--|:--:|:--:|:--| |:--|:--:|:--:|:--|
| [Clash Verge Rev](https://github.com/clash-verge-rev/clash-verge-rev) | `2.5.2` | `amd64`, `arm64` | `stplr install nivora/clash-verge-rev` | | [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` | | [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` | | [MAX](https://max.ru/) (в песочнице Firejail) | `26.24.0~75261` | `amd64` | `stplr install nivora/max` |
| [Tailscale](https://tailscale.com/) | `1.98.9` | `amd64`, `arm64` | `stplr install nivora/tailscale` | | [NetBird](https://netbird.io/) | `0.76.0` | `amd64`, `arm64` | `stplr install nivora/netbird` |
| [Яндекс Браузер](https://browser.yandex.ru/) | `26.4.1.1110` | `amd64` | `stplr install nivora/yandex-browser-stable` | | [Tailscale](https://tailscale.com/) | `1.98.10` | `amd64`, `arm64` | `stplr install nivora/tailscale` |
| [Telegram](https://desktop.telegram.org/) | `7.0.6` | `amd64` | `stplr install nivora/telegram` |
| [VK Messenger](https://vk.me/app) | `6.0.57` | `amd64` | `stplr install nivora/vk-messenger` |
| [Яндекс Браузер](https://browser.yandex.ru/) | `26.6.1.1003` | `amd64` | `stplr install nivora/yandex-browser-stable` |
### Удалённый доступ ### Удалённый доступ
@@ -62,10 +65,12 @@ stplr info nivora/codex
| Приложение | Версия | Архитектуры | Установка | | Приложение | Версия | Архитектуры | Установка |
|:--|:--:|:--:|:--| |:--|:--:|:--:|:--|
| [Chatbox](https://chatboxai.app/ru) | `1.20.3` | `amd64`, `arm64` | `stplr install nivora/chatbox` | | [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` | | [Claude](docs/packages/claude.md) | `1.24012.0` | `amd64`, `arm64` | `stplr install nivora/claude` |
| [Codex](docs/packages/codex.md) | `26.721.31836` | `amd64` | `stplr install nivora/codex` | | [ClaudeAlt](docs/packages/claude-alt.md) | `1.24012.0` | `amd64`, `arm64` | `stplr install nivora/claude-alt` |
| [Codex](docs/packages/codex.md) | `26.721.81911` | `amd64` | `stplr install nivora/codex` |
| [GitHub Desktop](docs/packages/github-desktop.md) | `3.6.3` | `amd64`, `arm64` | `stplr install nivora/github-desktop` | | [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` | | [OpenCode](https://opencode.ai/) | `1.18.10` | `amd64`, `arm64` | `stplr install nivora/opencode` |
| [Vintner](https://github.com/Cheviiot/vintner) | `0.5.0` | `amd64`, `arm64` | `stplr install nivora/vintner` |
### Рабочий стол ### Рабочий стол
@@ -87,7 +92,7 @@ stplr info nivora/codex
|:--|:--:|:--:|:--| |:--|:--:|:--:|:--|
| [balenaEtcher](https://etcher.balena.io/) | `2.1.6` | `amd64` | `stplr install nivora/balena-etcher` | | [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` | | [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` | | [Nivora CLI](docs/packages/nivora-cli.md) | `1.0.0` | `all` | `stplr install nivora/nivora-cli` |
| [Ventoy](docs/packages/ventoy.md) | `1.1.17` | `amd64`, `arm64` | `stplr install nivora/ventoy` | | [Ventoy](docs/packages/ventoy.md) | `1.1.17` | `amd64`, `arm64` | `stplr install nivora/ventoy` |
## Обновление ## Обновление
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
set -euo pipefail
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
exec "${root}/tools/package_updates.sh" check claude-alt
+134
View File
@@ -0,0 +1,134 @@
name='claude-alt'
version=1.24012.0
release=1
summary='Independent secondary Claude desktop application'
summary_ru='Независимое второе приложение Claude'
group='Development/Tools'
desc='A fully independent secondary Claude Desktop installation with its own runtime, profile, desktop identity and Nivora icons.'
desc_ru='Полностью независимая вторая установка Claude Desktop с собственным runtime, профилем, desktop-идентичностью и иконками Nivora.'
homepage='https://github.com/Cheviiot/Nivora'
maintainer='Nivora <https://github.com/Cheviiot/Nivora>'
architectures=('amd64' 'arm64')
license=('Custom')
nonfree=1
nonfree_msg='ClaudeAlt использует проприетарный Claude Desktop и условия Anthropic.'
nonfree_url='https://www.anthropic.com/legal/consumer-terms'
provides=()
replaces=('claude-alt')
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')
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')
deps_arch=('gtk3' 'libnotify' 'nss' 'xdg-utils' 'at-spi2-core' 'libdrm' 'libgbm' 'libxcb' 'libsecret' 'glibc' 'libxtst' 'libutil-linux' 'xdg-desktop-portal')
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')
deps_altlinux=('libgtk+3' 'libnotify' 'libnss' 'xdg-utils' 'libat-spi2-core' 'libdrm' 'libgbm' 'libxcb' 'libsecret' 'glibc' 'libXtst' 'libuuid' 'xdg-desktop-portal')
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"
"https://raw.githubusercontent.com/Cheviiot/Nivora/main/claude/claude-alt.png?~archive=false&~name=claude-alt.png"
"https://raw.githubusercontent.com/Cheviiot/Nivora/main/claude/claude-alt-tray-turquoise.png?~archive=false&~name=claude-alt-tray.png"
"local:///patch-asar.py"
)
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"
"https://raw.githubusercontent.com/Cheviiot/Nivora/main/claude/claude-alt.png?~archive=false&~name=claude-alt.png"
"https://raw.githubusercontent.com/Cheviiot/Nivora/main/claude/claude-alt-tray-turquoise.png?~archive=false&~name=claude-alt-tray.png"
"local:///patch-asar.py"
)
checksums=(
'sha256:1096a8063956f8430fdbe504bb1c5e02985f31d0f45d082e4966e5d30ea7e3ac'
'sha256:b5264918d7cc3436bde057057e5790f617e42ccfcd1237d9170d26aea12799b8'
'sha256:f23de36a74c3f6ddae92511fc0dc659816853bf1d28520a6e0232fe2606fd0b4'
'sha256:21d1db80c30921346252ed405fa36cb5c48c287afdd8ebe474073389fd88e85e'
'sha256:14e0a5ce4da6afb25b13b3cee0b0264fbd896430dae85ade1f3394d8ca999e7d'
)
checksums_arm64=(
'sha256:37dd5dc1b0bfcb3192561acbd33cb59037806fe9b95221a3ddf38244203196f1'
'sha256:b5264918d7cc3436bde057057e5790f617e42ccfcd1237d9170d26aea12799b8'
'sha256:f23de36a74c3f6ddae92511fc0dc659816853bf1d28520a6e0232fe2606fd0b4'
'sha256:21d1db80c30921346252ed405fa36cb5c48c287afdd8ebe474073389fd88e85e'
'sha256:14e0a5ce4da6afb25b13b3cee0b0264fbd896430dae85ade1f3394d8ca999e7d'
)
scripts=(
['postinstall']='postinstall.sh'
['postremove']='postremove.sh'
)
package() {
cd "${srcdir}"
mkdir -p extracted
ar x claude-desktop.deb
tar -xf data.tar.* -C extracted
install -dm755 "${pkgdir}/usr/lib"
cp -a extracted/usr/lib/claude-desktop "${pkgdir}/usr/lib/claude-alt"
mv "${pkgdir}/usr/lib/claude-alt/claude-desktop" \
"${pkgdir}/usr/lib/claude-alt/claude-alt-bin"
chmod 0755 "${pkgdir}/usr/lib/claude-alt/claude-alt-bin" \
"${pkgdir}/usr/lib/claude-alt/chrome-sandbox"
python3 patch-asar.py "${pkgdir}/usr/lib/claude-alt/resources/app.asar"
install -Dm755 claude-alt "${pkgdir}/usr/lib/claude-alt/claude-alt"
install -Dm644 claude-alt.png "${pkgdir}/usr/lib/claude-alt/resources/icon.png"
for icon in TrayIconLinux.png TrayIconLinux-Dark.png; do
install -Dm644 claude-alt-tray.png \
"${pkgdir}/usr/lib/claude-alt/resources/${icon}"
done
install -dm755 "${pkgdir}/usr/bin"
ln -s /usr/lib/claude-alt/claude-alt "${pkgdir}/usr/bin/claude-alt"
ln -s claude-alt "${pkgdir}/usr/bin/claude-code-alt"
install -Dm644 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=/usr/bin/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=/usr/bin/claude-alt claude://claude.ai/new
[Desktop Action NewCode]
Name=New Claude Code session
Name[ru]=Новая сессия Claude Code
Exec=/usr/bin/claude-alt claude://code/new
EOF
install-license extracted/usr/share/doc/claude-desktop/copyright \
"claude-alt/copyright"
}
files() {
files-find "/usr/lib/claude-alt/**/*"
files-find-binary
files-find-desktop
files-find-license
files-find "/usr/share/icons/hicolor/*/apps/claude-alt.png"
}
+36
View File
@@ -0,0 +1,36 @@
#!/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}"
CHROME_DESKTOP='com.anthropic.ClaudeAlt.desktop'
export CHROME_DESKTOP
exec "${claude_alt_executable}" \
--user-data-dir="${data_dir}" \
--class=com.anthropic.ClaudeAlt \
"$@"
+123
View File
@@ -0,0 +1,123 @@
#!/usr/bin/env python3
"""Give ClaudeAlt an independent Electron identity without changing its protocol."""
import copy
import hashlib
import json
import struct
import sys
from pathlib import Path
path = Path(sys.argv[1])
data = 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()
counts = {"package": 0, "socket": 0, "user_agent": 0}
def integrity(content: bytes, block_size: int) -> dict:
blocks = [
hashlib.sha256(content[index : index + block_size]).hexdigest()
for index in range(0, len(content), block_size)
] or [hashlib.sha256(b"").hexdigest()]
return {
"algorithm": "SHA256",
"hash": hashlib.sha256(content).hexdigest(),
"blockSize": block_size,
"blocks": blocks,
}
def rebuild(node: dict, prefix: str = "") -> dict:
result = {}
for name, child in node.get("files", {}).items():
item_path = f"{prefix}/{name}" if prefix else name
if "files" in child:
result[name] = {
**{key: value for key, value in child.items() if key != "files"},
"files": rebuild(child, item_path),
}
continue
entry = copy.deepcopy(child)
if entry.get("unpacked"):
result[name] = entry
continue
offset, size = int(entry["offset"]), int(entry["size"])
content = data[base_offset + offset : base_offset + offset + size]
changed = False
if item_path == "package.json":
package = json.loads(content)
expected = {
"productName": "Claude",
"desktopName": "com.anthropic.Claude.desktop",
}
for field, value in expected.items():
if package.get(field) != value:
raise SystemExit(
f"unexpected Claude package.json {field}: "
f"{package.get(field)!r} != {value!r}"
)
package["productName"] = "ClaudeAlt"
package["desktopName"] = "com.anthropic.ClaudeAlt.desktop"
content = (json.dumps(package, indent=2) + "\n").encode()
counts["package"] += 1
changed = True
old_socket = b"claude-cowork-vm.sock"
socket_count = content.count(old_socket)
if socket_count:
content = content.replace(old_socket, b"claude-alt-cowork-vm.sock")
counts["socket"] += socket_count
changed = True
marker = b'P.app.isPackaged||P.app.setName("Claude");RRn();'
marker_count = content.count(marker)
if marker_count:
content = content.replace(
marker,
b'P.app.isPackaged||P.app.setName("Claude");'
b'P.app.userAgentFallback=P.app.userAgentFallback'
b'.replaceAll("ClaudeAlt","Claude");RRn();',
)
counts["user_agent"] += marker_count
changed = True
entry["offset"] = str(len(output))
if changed:
entry["size"] = len(content)
block_size = entry.get("integrity", {}).get("blockSize", 4194304)
entry["integrity"] = integrity(content, block_size)
output.extend(content)
result[name] = entry
return result
rebuilt_header = {"files": rebuild(header)}
if counts != {"package": 1, "socket": 1, "user_agent": 1}:
raise SystemExit(f"unexpected ClaudeAlt patch counts: {counts}")
header_bytes = json.dumps(rebuilt_header, separators=(",", ":")).encode()
padded_size = (len(header_bytes) + 3) & ~3
patched = (
struct.pack("<IIII", 4, padded_size + 8, padded_size + 4, len(header_bytes))
+ header_bytes
+ b"\0" * (padded_size - len(header_bytes))
+ bytes(output)
)
required = (
b"com.anthropic.ClaudeAlt.desktop",
b'"productName": "ClaudeAlt"',
b'.replaceAll("ClaudeAlt","Claude")',
b"claude-alt-cowork-vm.sock",
)
if any(patched.count(value) != 1 for value in required):
raise SystemExit("ClaudeAlt identity verification failed")
path.write_bytes(patched)
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
set -euo pipefail
sandbox=/usr/lib/claude-alt/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
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
-388
View File
@@ -1,388 +0,0 @@
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"
}
+138
View File
@@ -0,0 +1,138 @@
name='claude'
version=1.24012.0
release=1
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' '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-tray-orange.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-tray-orange.png"
'local:///LICENSE'
)
checksums=(
'sha256:1096a8063956f8430fdbe504bb1c5e02985f31d0f45d082e4966e5d30ea7e3ac'
'sha256:be51081c0e0b2eec3489c25da38194dccf68fe7445928b32c30e1b8051eb557b'
'sha256:40efe7a2755d02c9fe13f98cae18705e2d31e5bbb24816ebbd4951c5e3a56b3f'
)
checksums_arm64=(
'sha256:37dd5dc1b0bfcb3192561acbd33cb59037806fe9b95221a3ddf38244203196f1'
'sha256:be51081c0e0b2eec3489c25da38194dccf68fe7445928b32c30e1b8051eb557b'
'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
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 -dm755 "${pkgdir}/usr/bin"
ln -sfn /usr/lib/claude-desktop/claude-desktop "${pkgdir}/usr/bin/claude-desktop"
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/share/icons/hicolor/*/apps/claude-desktop.png"
}

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 193 KiB

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

+2 -2
View File
@@ -1,5 +1,5 @@
name='codex' name='codex'
version=26.721.31836 version=26.721.81911
release=1 release=1
summary='Unofficial Linux repack of the Codex desktop app' summary='Unofficial Linux repack of the Codex desktop app'
summary_ru='Неофициальная Linux-перепаковка десктопного приложения Codex' summary_ru='Неофициальная Linux-перепаковка десктопного приложения Codex'
@@ -51,7 +51,7 @@ sources=(
) )
checksums=( checksums=(
'sha256:f48c0ae3fca556d66525b863dd009a0bd0702115517f83c39f8a44cf4d128eb3' 'sha256:87798af5884c55a1faab57c8fef818a827a9f0243241c94d19a467d8216aef31'
'sha256:8cde2dac642947cdfab23d302056ef81b574cc7f5684f25f7116f80411ec551c' 'sha256:8cde2dac642947cdfab23d302056ef81b574cc7f5684f25f7116f80411ec551c'
'sha256:fab36489be4f1e92591d2ebcd28a96a95f788b5d1ba87824985623ab7df5e3f4' 'sha256:fab36489be4f1e92591d2ebcd28a96a95f788b5d1ba87824985623ab7df5e3f4'
'sha256:9259dc4b9ae217855d55414b2266db08d9b40c5b0cdbca20a183234ff55ba006' 'sha256:9259dc4b9ae217855d55414b2266db08d9b40c5b0cdbca20a183234ff55ba006'
+5 -3
View File
@@ -2,7 +2,7 @@
## Инварианты ## Инварианты
- В репозитории ровно 19 каталогов с `Staplerfile`. - В репозитории ровно 24 каталога с `Staplerfile`.
- Каталог совпадает с `name` и командой в README. - Каталог совпадает с `name` и командой в README.
- Upstream-версия не меняется из-за патча рецепта; для этого повышается `release`. - Upstream-версия не меняется из-за патча рецепта; для этого повышается `release`.
- Desktop-id, AppStream component-id, units и пути данных не меняются без отдельной миграции. - Desktop-id, AppStream component-id, units и пути данных не меняются без отдельной миграции.
@@ -17,10 +17,12 @@ Stapler сам добавляет текущее `name` в generated `Provides`
| Package ID | Основание | | Package ID | Основание |
|:--|:--| |:--|:--|
| `clash-verge-rev` | Upstream-репозиторий и release title: Clash Verge Rev | | `clash-verge-rev` | Upstream-репозиторий и release title: Clash Verge Rev |
| `claude-desktop` | Upstream DEB: `Package: claude-desktop`; desktop-id `com.anthropic.Claude` сохранён | | `claude` | Upstream DEB: `Package: claude-desktop`; desktop-id `com.anthropic.Claude` сохранён; старое имя пакета `claude-desktop` заменяется |
| `codex` | Отображаемое имя `Codex`; команда и desktop-id `codex-app` сохранены | | `codex` | Отображаемое имя `Codex`; команда и desktop-id `codex-app` сохранены |
| `github-desktop` | Официальный upstream `desktop/desktop`; Linux-сборка без стороннего форка | | `github-desktop` | Официальный upstream `desktop/desktop`; Linux-сборка без стороннего форка |
| `nivora-stplr` | Собственный helper Nivora | | `max` | Upstream package name `max`; версия хранится как `VERSION~BUILD` (совпадает с номером сборки в имени deb-файла, как у ALT eepm) для воспроизводимости; пакет всегда запускает бинарник через Firejail (`/etc/firejail/max.profile`), `/usr/bin/max` — не симлинк, а обёртка с `exec firejail` |
| `nivora-cli` | Многоязычная оболочка Nivora для Stapler |
| `telegram` | Upstream-тарбол не даёт своего package ID; desktop-id `org.telegram.desktop` сохранён; каталог и package ID переименованы с `telegram-desktop`. В отличие от `claude`/`claude-desktop`, `replaces` не включает старое имя (это разрешено только для `claude` в `validate_repo.py`) — у кого установлен `telegram-desktop`, нужно вручную `stplr install telegram` и `stplr remove telegram-desktop` |
## Локальные проверки ## Локальные проверки
+12
View File
@@ -0,0 +1,12 @@
# ClaudeAlt
Самостоятельная вторая установка Claude, не входящая в пакет
`claude`:
```bash
sudo stplr install nivora/claude-alt
```
Использует собственные runtime, профиль и desktop-идентичность. Иконки Nivora
переносятся без изменений. `claude` и `claude-alt` можно устанавливать,
обновлять и удалять независимо друг от друга.
@@ -1,20 +1,20 @@
# Claude Desktop # Claude
Пакет `claude-desktop` переупаковывает DEB из APT-источника Anthropic и сохраняет upstream Пакет `claude` переупаковывает DEB из APT-источника Anthropic и сохраняет upstream
desktop-id `com.anthropic.Claude`. desktop-id `com.anthropic.Claude`.
```bash ```bash
sudo stplr install nivora/claude-desktop sudo stplr install nivora/claude
``` ```
## Два независимых приложения При установке новый пакет заменяет прежнее имя `nivora/claude-desktop`.
Исполняемая команда `/usr/bin/claude-desktop` сохраняется, чтобы не
конфликтовать с Claude Code CLI, использующим команду `claude`.
- `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`, `claude-alt` устанавливается отдельной командой и имеет собственные
executable/resources tree, Electron `productName`, Wayland `app_id`,
X11 `WM_CLASS`, desktop-файл, Chromium-профиль, Cowork VM socket, оконную и X11 `WM_CLASS`, desktop-файл, Chromium-профиль, Cowork VM socket, оконную и
tray-иконки. В системном лотке используется штатный глиф Claude: оранжевый у tray-иконки. В системном лотке используется штатный глиф Claude: оранжевый у
основного приложения и бирюзовый у ClaudeAlt. Большие app-иконки окон при этом основного приложения и бирюзовый у ClaudeAlt. Большие app-иконки окон при этом
+112
View File
@@ -0,0 +1,112 @@
# Nivora CLI
Nivora CLI — компактная многоязычная оболочка для Stapler. Она упрощает
установку, удаление, обновление и поиск пакетов, управление репозиториями и
диагностику системы.
```bash
sudo stplr install nivora/nivora-cli
```
## Интерактивный режим
Команда `nv` без аргументов открывает компактное меню:
```text
╭─ NIVORA ─────────────────────────────╮
│ Простое управление пакетами Stapler │
╰──────────────────────────────────────╯
Пакеты
[i] Установить nvi
[e] Удалить nve
[u] Обновить nvu
[s] Найти nvs
[q] Информация nvqi
[a] Все пакеты nvqa
Система
[r] Обновить репозитории nvr
[l] Репозитории nvrl
[f] Исправить nvf
[d] Диагностика nvd
```
В конвейере или другом неинтерактивном окружении `nv` выводит обычную справку.
## Короткие команды
| Команда | Операция Stapler |
|:--|:--|
| `nvi package` | установить `nivora/package` |
| `nve package` | удалить `nivora/package` |
| `nvu` | обновить установленные пакеты |
| `nvs query` | найти пакет |
| `nvqi package` | показать информацию о пакете |
| `nvqa` | показать каталог пакетов |
| `nvr` | обновить индексы репозиториев |
| `nvrl` | показать подключённые репозитории |
| `nvf` | исправить состояние Stapler |
| `nvd` | проверить окружение |
| `nvc` | показать или изменить конфигурацию Stapler |
Основная команда принимает и полные названия операций:
```bash
nv install codex
nv search editor
nv info codex
nv repo list
```
## Репозитории
Короткое имя автоматически получает префикс `nivora`:
```bash
nvi codex # stplr install nivora/codex
nvi other/package # stplr install other/package
```
Репозиторий можно изменить или отключить:
```bash
nvi --repo other package
nvi --repo none package
NIVORA_REPO=other nvi package
```
Локальные пути и файлы пакетов никогда не получают префикс.
## Язык и оформление
Русский или английский язык выбирается по системной локали. Явное
переопределение:
```bash
nv --lang en help
NIVORA_LANG=ru nvd
```
Цветной вывод включается только для терминала. Его можно отключить стандартной
переменной `NO_COLOR`, параметром `--no-color` или значением
`NIVORA_COLOR=never`.
## Безопасность
Изменяющие систему операции запускаются через `sudo`. Другую команду повышения
привилегий можно задать в `NIVORA_SUDO`. Аргументы передаются массивом без
вычисления shell-строк. Параметр `--dry-run` показывает итоговую команду без её
выполнения.
| Переменная | Назначение |
|:--|:--|
| `NIVORA_LANG` | язык интерфейса: `ru` или `en` |
| `NIVORA_REPO` | репозиторий коротких имён |
| `NIVORA_SUDO` | команда повышения привилегий |
| `NIVORA_COLOR` | режим цвета; `never` отключает цвет |
| `NIVORA_QUIET=1` | не печатать запускаемую команду |
| `NO_COLOR` | стандартное отключение цвета |
Автодополнение для Bash, Fish или Zsh генерируется командой
`nv completion <shell>`.
-43
View File
@@ -1,43 +0,0 @@
# 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`.
+33
View File
@@ -0,0 +1,33 @@
Сведения о лицензии пакета max
Лицензия: Custom (проприетарная).
Исходный продукт: https://max.ru/
Официальный deb-репозиторий: https://download.max.ru/linux/deb/
Нюансы пакета:
- Пакет перепаковывает официальный Linux deb-артефакт MAX Messenger Client
из deb-репозитория max.ru; отдельного публичного репозитория исходников
или GitHub-релиза у продукта нет.
- Приложение собрано на Qt6 и распространяется полностью самодостаточным
бандлом (собственные библиотеки Qt в `lib64/`).
- Пакет запускает приложение только через Firejail-песочницу (профиль
`/etc/firejail/max.profile`): домашний каталог пользователя виден
приложению только через отдельные `~/.config/max`, `~/.cache/max`,
`~/.local/share/max`; capabilities сброшены, seccomp включён,
системная D-Bus недоступна. Отдельный процесс `crashpad_handler`
(сбор и отправка крэш-дампов) заблокирован явным `blacklist`, чтобы
ограничить исходящую телеметрию, не относящуюся к функциям обмена
сообщениями.
- Полная гарантия отсутствия любой сетевой телеметрии невозможна:
трафик самого мессенджера к серверам max.ru неотличим от служебного
на уровне песочницы. Firejail ограничивает доступ к системе и файлам,
а не содержимое протокола приложения.
- Для полноценной работы требуется учетная запись, привязанная к
российскому номеру телефона.
- Использование мессенджера, учетной записи и сетевых функций
регулируется условиями MAX.
- Пакет помечен как несвободный через `nonfree=1`.
Официальные условия:
https://legal.max.ru/ps
https://legal.max.ru/pp
+105
View File
@@ -0,0 +1,105 @@
name='max'
version='26.24.0~75261'
release=1
summary='MAX messenger client, sandboxed with Firejail'
summary_ru='Клиент мессенджера MAX в песочнице Firejail'
group='Networking/Instant messaging'
desc='MAX is the Russian state-affiliated instant messaging client. This package always launches it inside a Firejail sandbox that hides the rest of the home directory and blocks the standalone crash-report uploader, since the app has no independent security track record and no reason to see the whole system.'
desc_ru='MAX — российский государственно-аффилированный клиент мгновенных сообщений. Пакет всегда запускает его в песочнице Firejail, которая скрывает остальной домашний каталог и блокирует отдельный процесс отправки крэш-дампов — у приложения нет независимой истории аудита безопасности и нет причин видеть всю систему.'
homepage='https://max.ru/'
maintainer='max.ru <https://max.ru/>'
architectures=('amd64')
license=('Custom')
nonfree=1
nonfree_msg='MAX является проприетарным приложением. Использование мессенджера, учетной записи и сетевых функций регулируется условиями MAX.'
nonfree_url='https://legal.max.ru/ps'
provides=()
replaces=('max')
conflicts=()
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
deps=('firejail')
deps_debian=('firejail' 'libxcb-xinerama0' 'libxcb-composite0' 'libxcb-ewmh2' 'libva-x11-2' 'libva-drm2' 'libvdpau1' 'libnotify4' 'libxcb-dri2-0' 'libopengl0' 'libxcb-cursor0' 'libxkbcommon-x11-0' 'libxcb-icccm4' 'libxcb-keysyms1' 'libxss1' 'libglib2.0-0' 'gsettings-desktop-schemas' 'ca-certificates')
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=('firejail' 'libxcb' 'libva' 'libvdpau' 'libnotify' 'libglvnd-opengl' 'xcb-util-cursor' 'libxkbcommon-x11' 'xcb-util-wm' 'xcb-util-keysyms' 'libXScrnSaver' 'glib2' 'gsettings-desktop-schemas' 'ca-certificates')
deps_arch=('firejail' 'libxcb' 'libva' 'libvdpau' 'libnotify' 'libglvnd' 'xcb-util-cursor' 'libxkbcommon-x11' 'xcb-util-wm' 'xcb-util-keysyms' 'libxss' 'glib2' 'gsettings-desktop-schemas' 'ca-certificates')
deps_opensuse=('firejail' 'libxcb1' 'libva2' 'libvdpau1' 'libnotify4' 'libxcb-cursor0' 'libxkbcommon-x11-0' 'xcb-util-wm0' 'xcb-util-keysyms0' 'libXss1' 'libglib-2_0-0' 'gsettings-desktop-schemas' 'ca-certificates')
deps_altlinux=('firejail' 'libxcb' 'libxcbutil-cursor' 'libxcbutil-icccm' 'libxcbutil-keysyms' 'libXScrnSaver' 'libva' 'libvdpau' 'libnotify' 'libOpenGL' 'libxkbcommon-x11' 'glib2' 'gsettings-desktop-schemas' 'ca-certificates')
opt_deps=()
opt_deps_debian=('desktop-file-utils' 'hicolor-icon-theme' 'pipewire')
opt_deps_ubuntu=("${opt_deps_debian[@]}")
opt_deps_fedora=('desktop-file-utils' 'hicolor-icon-theme' 'pipewire')
opt_deps_arch=('desktop-file-utils' 'hicolor-icon-theme' 'pipewire')
opt_deps_opensuse=('desktop-file-utils' 'hicolor-icon-theme' 'pipewire')
opt_deps_altlinux=('desktop-file-utils' 'icon-theme-hicolor' 'pipewire')
build_deps=('binutils')
sources=(
"https://download.max.ru/linux/deb/pool/main/m/max/MAX-26.24.0.75261.deb?~archive=false&~name=max.deb"
'local:///max.desktop'
'local:///max.profile'
'local:///LICENSE'
)
checksums=(
'sha256:7151aa2559e5fe22dd4dd6cf037b5d5cd38d302f84e49d4c4be94e1c4fe1f7d5'
'sha256:80ec5c0f4777ab00c433c25ffa70ef23d1752785e6584288d189518b8b43d613'
'sha256:cf8c986f7f1a9841be33b43cfd8e34e6c75dac6ffea07aad07869177fc7768c7'
'sha256:ddfcad273084c8b5690f6276fb29d8990ff4a7114db8a89b3f46255f8675e6b6'
)
scripts=(
['postinstall']='postinstall.sh'
['postremove']='postremove.sh'
)
package() {
cd "${srcdir}"
mkdir -p "${srcdir}/extracted"
ar x max.deb
tar -xf data.tar.* -C "${srcdir}/extracted"
install -dm755 "${pkgdir}/usr/share"
cp -a "${srcdir}/extracted/usr/share/max" "${pkgdir}/usr/share/max"
install -Dm644 "${srcdir}/extracted/usr/share/pixmaps/max.png" \
"${pkgdir}/usr/share/pixmaps/max.png"
for icon in "${srcdir}"/extracted/usr/share/icons/hicolor/*/apps/max.png; do
size="$(basename "$(dirname "$(dirname "$icon")")")"
install -Dm644 "$icon" \
"${pkgdir}/usr/share/icons/hicolor/${size}/apps/max.png"
done
install -Dm644 max.profile "${pkgdir}/etc/firejail/max.profile"
# The desktop file and every other launch path go through this wrapper —
# MAX never runs unsandboxed regardless of how it's started.
install -Dm755 /dev/stdin "${pkgdir}/usr/bin/max" <<'EOF'
#!/bin/sh
exec firejail --profile=max -- /usr/share/max/bin/max "$@"
EOF
install -Dm644 max.desktop "${pkgdir}/usr/share/applications/max.desktop"
install-license "${srcdir}/LICENSE" "max/LICENSE.nivora"
}
files() {
files-find-binary
files-find-desktop
files-find-license
files-find \
"/etc/firejail/max.profile" \
"/usr/share/max/**/*" \
"/usr/share/pixmaps/max.png" \
"/usr/share/icons/hicolor/*/apps/max.png"
}
+16
View File
@@ -0,0 +1,16 @@
[Desktop Entry]
Type=Application
Terminal=false
Name=MAX
Exec=/usr/bin/max %U
Icon=max
MimeType=x-scheme-handler/max;
Comment=MAX Messenger Client Application
Comment[ru]=Клиентское приложение мессенджера MAX
StartupWMClass=max
Categories=Chat;Network;InstantMessaging;Qt;
Keywords=max;chat;im;messaging;messenger;sms;
DBusActivatable=false
SingleMainWindow=true
X-GNOME-UsesNotifications=true
X-GNOME-SingleWindow=true
+53
View File
@@ -0,0 +1,53 @@
# Firejail profile for MAX Messenger (Nivora package)
# MAX is a proprietary Russian messenger; this profile keeps it away from
# the rest of the user's files and system, and blocks its standalone
# crash-report uploader.
include max.local
include globals.local
noblacklist ${HOME}/.config/max
noblacklist ${HOME}/.cache/max
noblacklist ${HOME}/.local/share/max
include disable-common.inc
include disable-devel.inc
include disable-exec.inc
include disable-interpreters.inc
include disable-programs.inc
include landlock-common.inc
mkdir ${HOME}/.config/max
mkdir ${HOME}/.cache/max
mkdir ${HOME}/.local/share/max
whitelist ${HOME}/.config/max
whitelist ${HOME}/.cache/max
whitelist ${HOME}/.local/share/max
include whitelist-common.inc
include whitelist-run-common.inc
include whitelist-usr-share-common.inc
include whitelist-var-common.inc
caps.drop all
netfilter
nodvd
nogroups
nonewprivs
noroot
notv
nou2f
protocol unix,inet,inet6
seccomp
restrict-namespaces
disable-mnt
private-dev
private-tmp
# Chromium/Qt-style standalone crash uploader: no messaging function, only
# phones home with crash dumps. Block it outright rather than trust a
# network-level filter.
blacklist /usr/share/max/bin/crashpad_handler
blacklist /usr/share/max/bin/max-service/bin/crashpad_handler
dbus-system none
+29
View File
@@ -0,0 +1,29 @@
#!/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
if ! command -v firejail >/dev/null 2>&1; then
echo "Предупреждение: firejail не найден в PATH — MAX запускается только через firejail, пакет firejail должен быть установлен." >&2
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 @@
include = "../stapler-repo.toml"
+3 -3
View File
@@ -1,5 +1,5 @@
name='netbird' name='netbird'
version=0.75.0 version=0.76.0
release=1 release=1
summary='WireGuard-based mesh VPN client' summary='WireGuard-based mesh VPN client'
summary_ru='Mesh VPN-клиент на базе WireGuard' summary_ru='Mesh VPN-клиент на базе WireGuard'
@@ -48,7 +48,7 @@ sources_arm64=(
) )
checksums=( checksums=(
'sha256:b65f65d7f32831639ebdd44c241e7a2310287723b64dbc03c93292853042ee6f' 'sha256:0fdc5ee0336c71d58223c5ea92019b062d8bc2d5a73dbc4692c6d2313307a0f4'
'sha256:71b133636e91eb67f221f64592f64fa04a3b2337f4d616be36492efae0cff643' 'sha256:71b133636e91eb67f221f64592f64fa04a3b2337f4d616be36492efae0cff643'
'sha256:abd8064281462a4ce12c2fdf2e9436d9ae6932ff3b366668a175f2a556169012' 'sha256:abd8064281462a4ce12c2fdf2e9436d9ae6932ff3b366668a175f2a556169012'
'sha256:bb5c44ab330db0d63a82b582f2c508681a6922ea65f3bf2becaeed87693215b1' 'sha256:bb5c44ab330db0d63a82b582f2c508681a6922ea65f3bf2becaeed87693215b1'
@@ -56,7 +56,7 @@ checksums=(
) )
checksums_arm64=( checksums_arm64=(
'sha256:9e57e854da0bbaf392cc6f99e37db75809e21c3effde0fca9f3bc9dc90245c02' 'sha256:b8f0f8cb47df28f9ec0061c38eba5c96af43b25f49f4f28c544919f34f34cbe8'
'sha256:71b133636e91eb67f221f64592f64fa04a3b2337f4d616be36492efae0cff643' 'sha256:71b133636e91eb67f221f64592f64fa04a3b2337f4d616be36492efae0cff643'
'sha256:abd8064281462a4ce12c2fdf2e9436d9ae6932ff3b366668a175f2a556169012' 'sha256:abd8064281462a4ce12c2fdf2e9436d9ae6932ff3b366668a175f2a556169012'
'sha256:bb5c44ab330db0d63a82b582f2c508681a6922ea65f3bf2becaeed87693215b1' 'sha256:bb5c44ab330db0d63a82b582f2c508681a6922ea65f3bf2becaeed87693215b1'
+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 @@
Сведения о лицензии пакета Nivora CLI
Лицензия: MIT.
Исходный проект: https://github.com/Cheviiot/Nivora
Нюансы пакета:
- Пакет устанавливает оболочку Stapler с командами `nv`, `nvi`, `nve`, `nvu`, `nvs`, `nvqi`, `nvqa`, `nvr`, `nvrl`, `nvf`, `nvd` и `nvc`.
- Nivora CLI не заменяет Stapler: все операции выполняются через системную команду `stplr`.
- Через команду повышения привилегий запускаются только `install`, `remove`, `up`, `fix` и `refresh`.
- Команды чтения `search`, `info` и `list` выполняются без повышения привилегий.
- Префикс репозитория задается через `--repo <name>` или `NIVORA_REPO`; по умолчанию используется репозиторий `nivora`.
- Пути вида `repo/package` не изменяются.
- Nivora CLI не исполняет произвольные shell-строки через `sudo`.
Полный официальный текст MIT:
https://opensource.org/licenses/MIT
+55
View File
@@ -0,0 +1,55 @@
name='nivora-cli'
version='1.0.0'
release=1
summary='Friendly multilingual command shell for Stapler'
summary_ru='Удобная многоязычная оболочка для Stapler'
desc='Nivora CLI is a compact bilingual command shell for everyday Stapler package management, repository administration, diagnostics, interactive navigation and shell completion.'
desc_ru='Nivora CLI — компактная двуязычная оболочка для управления пакетами и репозиториями Stapler, диагностики, интерактивной навигации и автодополнения.'
group='System/Configuration/Packaging'
homepage='https://github.com/Cheviiot/Nivora'
maintainer='Cheviiot <https://github.com/Cheviiot>'
architectures=('all')
license=('MIT')
provides=()
replaces=('nivora-cli')
conflicts=()
auto_reqprov_method='dirty'
auto_req=0
auto_prov=0
deps=()
deps_debian=('bash' 'sudo')
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=('bash' 'sudo')
deps_arch=('bash' 'sudo')
deps_altlinux=('bash' 'sudo')
deps_opensuse=('bash' 'sudo')
sources=(
'local:///nivora'
'local:///LICENSE'
)
checksums=(
'sha256:438a5b3b73cd8f69108174b25e647b0f8013b3c16f4fcd5ca9e9769fd19f98b7'
'sha256:16f1438f500706ac85b363c2549b392daef3a920b24139fca6b30f5648a7801a'
)
package() {
install -Dm755 "${srcdir}/nivora" "${pkgdir}/usr/bin/nv"
shortcut=''
for shortcut in nvi nve nvu nvs nvqi nvqa nvr nvrl nvf nvd nvc; do
ln -sfn nv "${pkgdir}/usr/bin/${shortcut}"
done
install-license "${srcdir}/LICENSE" 'nivora-cli/LICENSE.nivora'
}
files() {
files-find-binary
files-find-license
}
+546
View File
@@ -0,0 +1,546 @@
#!/bin/bash
set -euo pipefail
readonly NIVORA_VERSION='1.0.0'
readonly DEFAULT_REPOSITORY='nivora'
language="${NIVORA_LANG:-}"
repository="${NIVORA_REPO:-${DEFAULT_REPOSITORY}}"
dry_run=0
color_mode="${NIVORA_COLOR:-auto}"
detect_language() {
[[ -n "$language" ]] && return
case "${LC_ALL:-${LC_MESSAGES:-${LANG:-en}}}" in
ru* | RU*) language='ru' ;;
*) language='en' ;;
esac
}
message() {
local key="$1"
case "${language}:${key}" in
ru:title) printf 'Простое управление пакетами Stapler' ;;
ru:usage) printf 'Использование' ;;
ru:packages) printf 'Пакеты' ;;
ru:system) printf 'Система' ;;
ru:install) printf 'Установить' ;;
ru:remove) printf 'Удалить' ;;
ru:upgrade) printf 'Обновить' ;;
ru:search) printf 'Найти' ;;
ru:info) printf 'Информация' ;;
ru:all_packages) printf 'Все пакеты' ;;
ru:refresh) printf 'Обновить репозитории' ;;
ru:repositories) printf 'Репозитории' ;;
ru:fix) printf 'Исправить' ;;
ru:doctor) printf 'Диагностика' ;;
ru:config) printf 'Конфигурация' ;;
ru:help) printf 'Справка' ;;
ru:exit) printf 'Выход' ;;
ru:choose) printf 'Выберите действие' ;;
ru:package_prompt) printf 'Имя пакета' ;;
ru:query_prompt) printf 'Поисковый запрос' ;;
ru:unknown_command) printf 'неизвестная команда' ;;
ru:unknown_choice) printf 'нет такого пункта' ;;
ru:missing_argument) printf 'необходим аргумент' ;;
ru:invalid_language) printf 'поддерживаются языки ru и en' ;;
ru:invalid_repository) printf 'некорректное имя репозитория' ;;
ru:stapler_missing) printf 'команда stplr не найдена' ;;
ru:privilege_missing) printf 'команда повышения привилегий не найдена' ;;
ru:running) printf 'Выполняется' ;;
ru:preview) printf 'Предпросмотр' ;;
ru:diagnostics) printf 'Диагностика окружения' ;;
ru:ready) printf 'Система готова к работе' ;;
ru:problems) printf 'Обнаружены проблемы' ;;
ru:available) printf 'доступен' ;;
ru:missing) printf 'не найден' ;;
ru:language) printf 'Язык' ;;
ru:repository) printf 'Репозиторий' ;;
ru:privileges) printf 'Привилегии' ;;
ru:commands) printf 'Короткие команды' ;;
ru:options) printf 'Параметры' ;;
ru:examples) printf 'Примеры' ;;
en:title) printf 'Simple Stapler package management' ;;
en:usage) printf 'Usage' ;;
en:packages) printf 'Packages' ;;
en:system) printf 'System' ;;
en:install) printf 'Install' ;;
en:remove) printf 'Remove' ;;
en:upgrade) printf 'Upgrade' ;;
en:search) printf 'Search' ;;
en:info) printf 'Information' ;;
en:all_packages) printf 'All packages' ;;
en:refresh) printf 'Refresh repositories' ;;
en:repositories) printf 'Repositories' ;;
en:fix) printf 'Repair' ;;
en:doctor) printf 'Diagnostics' ;;
en:config) printf 'Configuration' ;;
en:help) printf 'Help' ;;
en:exit) printf 'Exit' ;;
en:choose) printf 'Choose an action' ;;
en:package_prompt) printf 'Package name' ;;
en:query_prompt) printf 'Search query' ;;
en:unknown_command) printf 'unknown command' ;;
en:unknown_choice) printf 'unknown choice' ;;
en:missing_argument) printf 'an argument is required' ;;
en:invalid_language) printf 'supported languages are ru and en' ;;
en:invalid_repository) printf 'invalid repository name' ;;
en:stapler_missing) printf 'stplr command was not found' ;;
en:privilege_missing) printf 'privilege command was not found' ;;
en:running) printf 'Running' ;;
en:preview) printf 'Preview' ;;
en:diagnostics) printf 'Environment diagnostics' ;;
en:ready) printf 'The system is ready' ;;
en:problems) printf 'Problems found' ;;
en:available) printf 'available' ;;
en:missing) printf 'missing' ;;
en:language) printf 'Language' ;;
en:repository) printf 'Repository' ;;
en:privileges) printf 'Privileges' ;;
en:commands) printf 'Short commands' ;;
en:options) printf 'Options' ;;
en:examples) printf 'Examples' ;;
*) printf '%s' "$key" ;;
esac
}
setup_style() {
if [[ "$color_mode" == 'never' || -n "${NO_COLOR:-}" || ! -t 1 ]]; then
bold=''; dim=''; cyan=''; blue=''; green=''; red=''; reset=''
return
fi
bold=$'\033[1m'
dim=$'\033[2m'
cyan=$'\033[38;5;44m'
blue=$'\033[38;5;75m'
green=$'\033[38;5;78m'
red=$'\033[38;5;203m'
reset=$'\033[0m'
}
fail() {
printf '%s%sNivora:%s %s\n' "$bold" "$red" "$reset" "$*" >&2
exit 2
}
validate_settings() {
case "$language" in
ru | en) ;;
*)
local invalid_language="$language"
language='en'
fail "$(message invalid_language): ${invalid_language}"
;;
esac
if [[ "$repository" != 'none' &&
! "$repository" =~ ^[[:alnum:]_.-]+$ ]]; then
fail "$(message invalid_repository): ${repository}"
fi
}
parse_options() {
arguments=()
while [[ "$#" -gt 0 ]]; do
case "$1" in
--lang)
[[ "$#" -ge 2 ]] || fail "$(message missing_argument): --lang"
language="$2"
shift 2
;;
--lang=*)
language="${1#--lang=}"
shift
;;
--repo)
[[ "$#" -ge 2 ]] || fail "$(message missing_argument): --repo"
repository="$2"
shift 2
;;
--repo=*)
repository="${1#--repo=}"
shift
;;
--dry-run)
dry_run=1
shift
;;
--no-color)
color_mode='never'
shift
;;
--)
shift
arguments+=("$@")
break
;;
*)
arguments+=("$1")
shift
;;
esac
done
setup_style
validate_settings
}
print_header() {
printf '%s%s╭─ NIVORA ─────────────────────────────╮%s\n' "$bold" "$cyan" "$reset"
if [[ "$language" == 'ru' ]]; then
printf '%s%s│%s Простое управление пакетами Stapler %s%s│%s\n' \
"$cyan" "$bold" "$reset" "$bold" "$cyan" "$reset"
else
printf '%s%s│%s Simple Stapler package management %s%s│%s\n' \
"$cyan" "$bold" "$reset" "$bold" "$cyan" "$reset"
fi
printf '%s%s╰──────────────────────────────────────╯%s\n' "$bold" "$cyan" "$reset"
}
menu_row() {
local key="$1"
local label="$2"
local shortcut="$3"
local padding=$((23 - ${#label}))
[[ "$padding" -lt 1 ]] && padding=1
printf ' %s[%s]%s %s' "$cyan" "$key" "$reset" "$label"
printf '%*s' "$padding" ''
printf '%s%s%s\n' "$dim" "$shortcut" "$reset"
}
interactive_menu() {
if [[ ! -t 0 || ! -t 1 ]]; then
show_help
return
fi
print_header
printf '\n %s%s%s\n' "$bold" "$(message packages)" "$reset"
menu_row i "$(message install)" nvi
menu_row e "$(message remove)" nve
menu_row u "$(message upgrade)" nvu
menu_row s "$(message search)" nvs
menu_row q "$(message info)" nvqi
menu_row a "$(message all_packages)" nvqa
printf '\n %s%s%s\n' "$bold" "$(message system)" "$reset"
menu_row r "$(message refresh)" nvr
menu_row l "$(message repositories)" nvrl
menu_row f "$(message fix)" nvf
menu_row d "$(message doctor)" nvd
local help_label
help_label="$(message help)"
printf '\n %s[h]%s %s' "$cyan" "$reset" "$help_label"
printf '%*s' "$((23 - ${#help_label}))" ''
printf '%s[x]%s %s\n\n' "$cyan" "$reset" "$(message exit)"
printf '%s ' "$(message choose)"
read -r choice
case "$choice" in
i) prompt_and_dispatch install package ;;
e) prompt_and_dispatch remove package ;;
u) dispatch upgrade ;;
s) prompt_and_dispatch search query ;;
q) prompt_and_dispatch info package ;;
a) dispatch list ;;
r) dispatch refresh ;;
l) dispatch repo-list ;;
f) dispatch fix ;;
d) doctor ;;
h) show_help ;;
x | q!) return ;;
*) fail "$(message unknown_choice): ${choice}" ;;
esac
}
prompt_and_dispatch() {
local command_name="$1"
local prompt_type="$2"
local value
if [[ "$prompt_type" == 'package' ]]; then
printf '%s ' "$(message package_prompt)"
else
printf '%s ' "$(message query_prompt)"
fi
read -r value
[[ -n "$value" ]] || fail "$(message missing_argument): ${command_name}"
dispatch "$command_name" "$value"
}
show_help() {
print_header
if [[ "$language" == 'ru' ]]; then
cat <<EOF
${bold}$(message usage)${reset}
nv интерактивное меню
nv <команда> [аргументы] выполнить команду
${bold}$(message commands)${reset}
${cyan}nvi${reset} <пакет...> установить ${cyan}nvr${reset} обновить репозитории
${cyan}nve${reset} <пакет...> удалить ${cyan}nvrl${reset} показать репозитории
${cyan}nvu${reset} обновить пакеты ${cyan}nvf${reset} исправить Stapler
${cyan}nvs${reset} <запрос> найти ${cyan}nvd${reset} запустить диагностику
${cyan}nvqi${reset} <пакет> информация ${cyan}nvc${reset} конфигурация Stapler
${cyan}nvqa${reset} каталог пакетов
${bold}$(message options)${reset}
--repo <имя|none> репозиторий коротких имён (по умолчанию: nivora)
--lang <ru|en> язык интерфейса
--dry-run показать команду без выполнения
--no-color отключить цвета
${bold}$(message examples)${reset}
nvi codex
nvs editor
nvqi codex
nv --repo none install other/package
EOF
else
cat <<EOF
${bold}$(message usage)${reset}
nv interactive menu
nv <command> [arguments] run a command
${bold}$(message commands)${reset}
${cyan}nvi${reset} <package...> install ${cyan}nvr${reset} refresh repositories
${cyan}nve${reset} <package...> remove ${cyan}nvrl${reset} list repositories
${cyan}nvu${reset} upgrade packages ${cyan}nvf${reset} repair Stapler
${cyan}nvs${reset} <query> search ${cyan}nvd${reset} run diagnostics
${cyan}nvqi${reset} <package> information ${cyan}nvc${reset} configure Stapler
${cyan}nvqa${reset} package catalog
${bold}$(message options)${reset}
--repo <name|none> repository for short names (default: nivora)
--lang <ru|en> interface language
--dry-run print the command without running it
--no-color disable colors
${bold}$(message examples)${reset}
nvi codex
nvs editor
nvqi codex
nv --repo none install other/package
EOF
fi
}
need_stapler() {
command -v stplr >/dev/null 2>&1 || fail "$(message stapler_missing)"
}
privilege_command() {
[[ "$EUID" -eq 0 ]] && return
local command_name="${NIVORA_SUDO:-sudo}"
command -v "$command_name" >/dev/null 2>&1 ||
fail "$(message privilege_missing): ${command_name}"
printf '%s\n' "$command_name"
}
is_package_name() {
local value="$1"
[[ -n "$value" &&
"$value" != -* &&
"$value" != */* &&
"$value" != ./* &&
"$value" != ../* &&
"$value" != /* &&
"$value" != *.rpm &&
"$value" != *.deb &&
"$value" != *.pkg.tar.* ]]
}
normalize_packages() {
local argument
for argument in "$@"; do
if [[ "$repository" != 'none' ]] && is_package_name "$argument"; then
printf '%s/%s\n' "$repository" "$argument"
else
printf '%s\n' "$argument"
fi
done
}
print_command() {
local label
if [[ "$dry_run" -eq 1 ]]; then
label="$(message preview)"
else
label="$(message running)"
fi
printf '%s%s%s%s %s%s' "$bold" "$blue" "$label" "$reset" "$cyan" "$reset" >&2
printf ' %q' "$@" >&2
printf '\n' >&2
}
run_stapler() {
local access="$1"
shift
need_stapler
local -a command=()
local privilege
if [[ "$access" == 'privileged' ]]; then
privilege="$(privilege_command)"
[[ -n "$privilege" ]] && command+=("$privilege")
fi
command+=(stplr "$@")
[[ "${NIVORA_QUIET:-0}" == '1' ]] || print_command "${command[@]}"
[[ "$dry_run" -eq 1 ]] && return
exec "${command[@]}"
}
require_arguments() {
local command_name="$1"
local count="$2"
[[ "$count" -gt 0 ]] || fail "$(message missing_argument): ${command_name}"
}
doctor() {
local failures=0
local privilege="${NIVORA_SUDO:-sudo}"
printf '%s%s%s\n\n' "$bold" "$(message diagnostics)" "$reset"
if command -v stplr >/dev/null 2>&1; then
printf ' %s●%s %-14s %s\n' "$green" "$reset" 'Stapler' "$(message available)"
else
printf ' %s●%s %-14s %s\n' "$red" "$reset" 'Stapler' "$(message missing)"
failures=1
fi
if [[ "$EUID" -eq 0 ]] || command -v "$privilege" >/dev/null 2>&1; then
printf ' %s●%s %-14s %s\n' \
"$green" "$reset" "$(message privileges)" \
"$([[ "$EUID" -eq 0 ]] && printf 'root' || printf '%s' "$privilege")"
else
printf ' %s●%s %-14s %s\n' \
"$red" "$reset" "$(message privileges)" "$(message missing)"
failures=1
fi
printf ' %s●%s %-14s %s\n' "$green" "$reset" "$(message language)" "$language"
printf ' %s●%s %-14s %s\n\n' \
"$green" "$reset" "$(message repository)" "$repository"
if [[ "$failures" -eq 0 ]]; then
printf '%s✓ %s%s\n' "$green" "$(message ready)" "$reset"
else
printf '%s✗ %s%s\n' "$red" "$(message problems)" "$reset"
fi
return "$failures"
}
completion() {
local shell="${1:-}"
case "$shell" in
bash)
cat <<'EOF'
_nivora() {
local commands='install remove upgrade search info list refresh repo-list fix doctor config completion version help'
if (( COMP_CWORD == 1 )); then
COMPREPLY=($(compgen -W "$commands" -- "${COMP_WORDS[COMP_CWORD]}"))
fi
}
complete -F _nivora nv
EOF
;;
fish)
cat <<'EOF'
complete -c nv -f -n '__fish_use_subcommand' -a 'install remove upgrade search info list refresh repo-list fix doctor config completion version help'
EOF
;;
zsh)
cat <<'EOF'
#compdef nv
_arguments '1:command:(install remove upgrade search info list refresh repo-list fix doctor config completion version help)' '*::argument:->arguments'
EOF
;;
*) fail "$(message missing_argument): completion <bash|fish|zsh>" ;;
esac
}
dispatch() {
local command_name="${1:-help}"
[[ "$#" -gt 0 ]] && shift
case "$command_name" in
install | i)
require_arguments "$command_name" "$#"
local -a packages=()
mapfile -t packages < <(normalize_packages "$@")
run_stapler privileged install "${packages[@]}"
;;
remove | erase | e)
require_arguments "$command_name" "$#"
local -a packages=()
mapfile -t packages < <(normalize_packages "$@")
run_stapler privileged remove "${packages[@]}"
;;
upgrade | update | u) run_stapler privileged up "$@" ;;
search | s)
require_arguments "$command_name" "$#"
run_stapler user search "$@"
;;
info | query-info | qi)
require_arguments "$command_name" "$#"
local -a packages=()
mapfile -t packages < <(normalize_packages "$@")
run_stapler user info "${packages[@]}"
;;
list | packages | query-all | qa) run_stapler user list "$@" ;;
refresh | r) run_stapler privileged refresh "$@" ;;
repo-list | rl) run_stapler user repo list "$@" ;;
repo)
if [[ "$#" -eq 0 ]]; then
run_stapler user repo list
elif [[ "${1:-}" == 'list' || "${1:-}" == 'ls' ]]; then
run_stapler user repo "$@"
else
run_stapler privileged repo "$@"
fi
;;
fix | f) run_stapler privileged fix "$@" ;;
doctor | d) doctor ;;
config | c)
if [[ "$#" -eq 0 ]]; then
run_stapler user config show
else
run_stapler user config "$@"
fi
;;
completion) completion "$@" ;;
support) run_stapler user support "$@" ;;
version | v | --version)
printf 'Nivora CLI %s\n' "$NIVORA_VERSION"
command -v stplr >/dev/null 2>&1 && stplr version
;;
help | h | -h | --help) show_help ;;
*) fail "$(message unknown_command): ${command_name}" ;;
esac
}
main() {
detect_language
setup_style
parse_options "$@"
set -- "${arguments[@]}"
case "$(basename "$0")" in
nv)
if [[ "$#" -eq 0 ]]; then interactive_menu; else dispatch "$@"; fi
;;
nvi) dispatch install "$@" ;;
nve) dispatch remove "$@" ;;
nvu) dispatch upgrade "$@" ;;
nvs) dispatch search "$@" ;;
nvqi) dispatch info "$@" ;;
nvqa) dispatch list "$@" ;;
nvr) dispatch refresh "$@" ;;
nvrl) dispatch repo-list "$@" ;;
nvf) dispatch fix "$@" ;;
nvd) doctor ;;
nvc) dispatch config "$@" ;;
nivora) dispatch "$@" ;;
*) fail "$(message unknown_command): $(basename "$0")" ;;
esac
}
main "$@"
+1
View File
@@ -0,0 +1 @@
include = "../stapler-repo.toml"
-16
View File
@@ -1,16 +0,0 @@
Сведения о лицензии пакета Nivora Stapler Helper
Лицензия: MIT.
Исходный проект: https://github.com/Cheviiot/Nivora
Нюансы пакета:
- Пакет устанавливает CLI-helper для Stapler с командами `sl`, `sli`, `slr`, `slu`, `slf`, `slref`, `sls`, `slii` и `sll`.
- Helper не заменяет Stapler: все операции выполняются через системную команду `stplr`.
- Через команду повышения привилегий запускаются только `install`, `remove`, `up`, `fix` и `refresh`.
- Команды чтения `search`, `info` и `list` выполняются без повышения привилегий.
- Префикс репозитория задается через `--repo <name>` или `NIVORA_STPLR_REPO`; по умолчанию короткие имена не привязаны к одному репозиторию.
- Пути вида `repo/package` не изменяются.
- Helper не исполняет произвольные shell-строки через `sudo`.
Полный официальный текст MIT:
https://opensource.org/licenses/MIT
-55
View File
@@ -1,55 +0,0 @@
name='nivora-stplr'
version='0.3.0'
release=1
summary='Short command helper for Stapler'
summary_ru='Короткие команды для Stapler'
desc='Nivora Stapler Helper provides short commands for common Stapler operations across configured repositories and elevates only operations that modify packages or caches.'
desc_ru='Nivora Stapler Helper добавляет короткие команды для типовых операций Stapler и повышает привилегии только для изменения пакетов и кэша.'
group='System/Configuration/Packaging'
homepage='https://github.com/Cheviiot/Nivora'
maintainer='Cheviiot <https://github.com/Cheviiot>'
architectures=('all')
license=('MIT')
provides=()
replaces=('nivora-stplr')
conflicts=()
auto_reqprov_method='dirty'
auto_req=0
auto_prov=0
deps=()
deps_debian=('bash' 'sudo')
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=('bash' 'sudo')
deps_arch=('bash' 'sudo')
deps_altlinux=('bash' 'sudo')
deps_opensuse=('bash' 'sudo')
sources=(
'local:///nivora-stplr'
'local:///LICENSE'
)
checksums=(
'sha256:7b0c2a69aed30bc39ac5546aabd0cb82d45b857a52408b4326ad1fd667684ecb'
'sha256:c5674e6873211a8ea0f08ee3a950f809c9145c60750746dafff9aee7e1c21cf1'
)
package() {
install -Dm755 "${srcdir}/nivora-stplr" "${pkgdir}/usr/bin/sl"
shortcut=''
for shortcut in sli slr slu slf slref sls slii sll; do
ln -sfn sl "${pkgdir}/usr/bin/${shortcut}"
done
install-license "${srcdir}/LICENSE" 'nivora-stplr/LICENSE.nivora'
}
files() {
files-find-binary
files-find-license
}
-239
View File
@@ -1,239 +0,0 @@
#!/bin/bash
set -euo pipefail
repo_prefix="${NIVORA_STPLR_REPO:-}"
usage() {
cat <<'EOF'
Nivora Stapler Helper
Usage:
sl <command> [args...]
sli <package> [stplr args...] sudo stplr install <package>
slr <package> [stplr args...] sudo stplr remove <package>
slu [stplr args...] sudo stplr up
slf [stplr args...] sudo stplr fix
slref [stplr args...] sudo stplr refresh
sls <query> [stplr args...] stplr search <query>
slii <package> [stplr args...] stplr info <package>
sll [stplr args...] stplr list
Commands:
install, in, i install package with sudo
remove, rm, r remove package with sudo
up, upgrade, u upgrade installed packages with sudo
fix, f run stplr fix with sudo
refresh, ref refresh repositories with sudo
search, s search packages
info, show show package info
list, ls list repository packages
help show this help
Options:
--repo <name> prefix short package names with repository, e.g. sli --repo nivora parsec
Environment:
NIVORA_STPLR_REPO optional repository prefix for short names, default: disabled
NIVORA_STPLR_SUDO privilege command, default: sudo
NIVORA_STPLR_QUIET=1 do not print command before execution
EOF
}
die() {
echo "nivora-stplr: $*" >&2
exit 2
}
need_stplr() {
command -v stplr >/dev/null 2>&1 || die "команда stplr не найдена"
}
sudo_cmd() {
if [[ "${EUID}" -eq 0 ]]; then
return 0
fi
local command_name="${NIVORA_STPLR_SUDO:-sudo}"
command -v "${command_name}" >/dev/null 2>&1 || die "команда ${command_name} не найдена"
printf '%s\n' "${command_name}"
}
is_package_token() {
local value="$1"
[[ -n "${value}" ]] || return 1
[[ "${value}" == -* ]] && return 1
[[ "${value}" == */* ]] && return 1
[[ "${value}" == ./* || "${value}" == ../* || "${value}" == /* ]] && return 1
[[ "${value}" == *.rpm || "${value}" == *.deb || "${value}" == *.pkg.tar.* ]] && return 1
return 0
}
normalize_package() {
local value="$1"
if [[ -n "${repo_prefix}" ]] && is_package_token "${value}"; then
printf '%s/%s\n' "${repo_prefix}" "${value}"
else
printf '%s\n' "${value}"
fi
}
normalize_package_args() {
local arg
for arg in "$@"; do
normalize_package "${arg}"
done
}
extract_repo_option() {
local -n output_ref="$1"
shift
output_ref=()
while [[ "$#" -gt 0 ]]; do
case "$1" in
--repo)
[[ "$#" -ge 2 ]] || die "для --repo нужно имя репозитория"
repo_prefix="$2"
shift 2
;;
--repo=*)
repo_prefix="${1#--repo=}"
shift
;;
*)
output_ref+=("$1")
shift
;;
esac
done
}
print_and_exec() {
local -a command=("$@")
if [[ "${NIVORA_STPLR_QUIET:-0}" != "1" ]]; then
printf '=>'
printf ' %q' "${command[@]}"
printf '\n'
fi
exec "${command[@]}"
}
run_stplr() {
local mode="$1"
shift
need_stplr
local -a command=()
if [[ "${mode}" == "sudo" ]]; then
local privilege
privilege="$(sudo_cmd)"
if [[ -n "${privilege}" ]]; then
command+=("${privilege}")
fi
fi
command+=(stplr "$@")
print_and_exec "${command[@]}"
}
require_args() {
local command_name="$1"
local count="$2"
if [[ "${count}" -eq 0 ]]; then
die "для команды ${command_name} нужен аргумент"
fi
}
dispatch() {
local command_name="$1"
shift
case "${command_name}" in
install | in | i)
require_args "${command_name}" "$#"
local -a args=()
extract_repo_option args "$@"
require_args "${command_name}" "${#args[@]}"
mapfile -t normalized < <(normalize_package_args "${args[@]}")
run_stplr sudo install "${normalized[@]}"
;;
remove | rm | r | erase | delete | del)
require_args "${command_name}" "$#"
local -a args=()
extract_repo_option args "$@"
require_args "${command_name}" "${#args[@]}"
mapfile -t normalized < <(normalize_package_args "${args[@]}")
run_stplr sudo remove "${normalized[@]}"
;;
up | upgrade | u)
run_stplr sudo up "$@"
;;
fix | f)
run_stplr sudo fix "$@"
;;
refresh | ref)
run_stplr sudo refresh "$@"
;;
search | s)
require_args "${command_name}" "$#"
run_stplr user search "$@"
;;
info | show)
require_args "${command_name}" "$#"
local -a args=()
extract_repo_option args "$@"
require_args "${command_name}" "${#args[@]}"
mapfile -t normalized < <(normalize_package_args "${args[@]}")
run_stplr user info "${normalized[@]}"
;;
list | ls | l)
run_stplr user list "$@"
;;
version | support)
run_stplr user "${command_name}" "$@"
;;
help | -h | --help | "")
usage
;;
*)
die "неизвестная команда: ${command_name}. Запустите: sl help"
;;
esac
}
main() {
local invoked
invoked="$(basename "$0")"
case "${invoked}" in
sli) dispatch install "$@" ;;
slr) dispatch remove "$@" ;;
slu) dispatch up "$@" ;;
slf) dispatch fix "$@" ;;
slref) dispatch refresh "$@" ;;
sls) dispatch search "$@" ;;
slii) dispatch info "$@" ;;
sll) dispatch list "$@" ;;
sl)
if [[ "$#" -eq 0 ]]; then
usage
return 0
fi
local command_name="$1"
shift
dispatch "${command_name}" "$@"
;;
*)
die "неизвестное имя запуска: ${invoked}"
;;
esac
}
main "$@"
+3 -3
View File
@@ -1,5 +1,5 @@
name='opencode' name='opencode'
version=1.18.4 version=1.18.10
release=1 release=1
summary='Open source AI coding agent desktop app' summary='Open source AI coding agent desktop app'
summary_ru='Десктопное приложение AI-агента OpenCode' summary_ru='Десктопное приложение AI-агента OpenCode'
@@ -50,12 +50,12 @@ sources_arm64=(
) )
checksums=( checksums=(
'sha256:a67bc47a98ded10275e7a8f32fa3e16480b55996cd52232da91450432b4d6599' 'sha256:ebc1f7650eb132ee8cb3cfc29bd2ae4d64d9d69ec85b799201602417c31e3f00'
'sha256:5c888de95824a9a5752d1ee3de95e80200eb057eec4078df892a95ac34333888' 'sha256:5c888de95824a9a5752d1ee3de95e80200eb057eec4078df892a95ac34333888'
) )
checksums_arm64=( checksums_arm64=(
'sha256:09bd2be9951ba5a7a333db818de49524edc6c98fedcca20d13a4a0f1c6c5b9e1' 'sha256:d7ee7a8c28989c858e2beffcf32cedc12610500fbfb72aa419436e1682475ce1'
'sha256:5c888de95824a9a5752d1ee3de95e80200eb057eec4078df892a95ac34333888' 'sha256:5c888de95824a9a5752d1ee3de95e80200eb057eec4078df892a95ac34333888'
) )
+8 -8
View File
@@ -1,6 +1,6 @@
name='tailscale' name='tailscale'
version='1.98.9' version=1.98.10
release=3 release=1
summary='Mesh VPN built on WireGuard' summary='Mesh VPN built on WireGuard'
summary_ru='Mesh VPN на базе WireGuard' summary_ru='Mesh VPN на базе WireGuard'
group='Networking/Other' group='Networking/Other'
@@ -44,15 +44,15 @@ sources_arm64=(
) )
checksums=( checksums=(
'11be30ad301d48f84ff52fec34f8a2f78eb3e3dee1be4e9624d19fccc8df5540' 'sha256:52490ce0832b245857e2afef7426d6ae5a4b49fb391412833cc95729bd23f7de'
'fc0e166481e2f97c21dca773ad4bfe673842268935ce1628c14b96af98e70555' 'sha256:fc0e166481e2f97c21dca773ad4bfe673842268935ce1628c14b96af98e70555'
'3c7a7d6d7f09890d5e682901acaeb12bda9542fc6450a709c9dc296a91e726a5' 'sha256:3c7a7d6d7f09890d5e682901acaeb12bda9542fc6450a709c9dc296a91e726a5'
) )
checksums_arm64=( checksums_arm64=(
'fa554ee808d7d07ee8e3ebbc0215ea087157e2a0abbf408e6e18ea7532554db6' 'sha256:d74a84e07cb1948d9f09a23ae161417c6127e562949773705c95d0762be2809d'
'fc0e166481e2f97c21dca773ad4bfe673842268935ce1628c14b96af98e70555' 'sha256:fc0e166481e2f97c21dca773ad4bfe673842268935ce1628c14b96af98e70555'
'3c7a7d6d7f09890d5e682901acaeb12bda9542fc6450a709c9dc296a91e726a5' 'sha256:3c7a7d6d7f09890d5e682901acaeb12bda9542fc6450a709c9dc296a91e726a5'
) )
scripts=( scripts=(
+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")"
+24
View File
@@ -0,0 +1,24 @@
Сведения о лицензии пакета Telegram Desktop
Лицензия: GPL-3.0-only (с отдельным разрешением на линковку с OpenSSL).
Исходный проект: https://github.com/telegramdesktop/tdesktop
Официальный сайт: https://desktop.telegram.org/
Условия использования сервиса: https://telegram.org/tos
Нюансы пакета:
- Пакет перепаковывает официальный статически собранный Linux-архив
Telegram Desktop (`tsetup.<version>.tar.xz`), публикуемый Telegram напрямую
и через официальные релизы `telegramdesktop/tdesktop` на GitHub.
- Собственный код Telegram Desktop распространяется на условиях GNU GPL
версии 3 с отдельным разрешением на линковку с OpenSSL; часть bundled-
зависимостей (Qt, ffmpeg и другие) распространяется на собственных
условиях соответствующих проектов.
- Учетные записи, серверы, боты, Premium-подписка и прочие сетевые функции
Telegram в пакет не входят и регулируются условиями Telegram.
- Локальные данные, кэш и сессии создаются приложением в домашнем каталоге
пользователя после запуска и не поставляются внутри пакета.
- Пакет добавляет команду `telegram-desktop` и использует официальные
desktop-id `org.telegram.desktop`.
Полный официальный текст GPL-3.0:
https://www.gnu.org/licenses/gpl-3.0.txt
+101
View File
@@ -0,0 +1,101 @@
name='telegram'
version=7.0.6
release=1
summary='Official Telegram messaging client'
summary_ru='Официальный клиент мессенджера Telegram'
group='Networking/Instant messaging'
desc='Telegram Desktop is the official Telegram messaging client with cloud-synced chats, voice and video calls, and no limits on file size or media history.'
desc_ru='Telegram Desktop — официальный клиент мессенджера Telegram с облачной синхронизацией чатов, голосовыми и видеозвонками и без ограничений на размер файлов и историю медиа.'
homepage='https://desktop.telegram.org/'
maintainer='Telegram Messenger LLP <https://github.com/telegramdesktop/tdesktop>'
architectures=('amd64')
license=('GPL-3.0-only')
provides=()
replaces=('telegram')
conflicts=()
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
deps=()
deps_debian=('libc6' 'libglib2.0-0t64 | libglib2.0-0' 'libfontconfig1' 'libfreetype6' 'libharfbuzz0b' 'libpng16-16' 'libpcre2-8-0' 'libbrotli1' 'libbz2-1.0' 'libblkid1' 'libmount1' 'libselinux1' 'libffi8' 'libexpat1' 'libgraphite2-3' 'zlib1g' 'libx11-6' 'hicolor-icon-theme')
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=('glibc' 'glib2' 'fontconfig' 'freetype' 'harfbuzz' 'libpng' 'pcre2' 'brotli' 'bzip2-libs' 'libblkid' 'libmount' 'libselinux' 'libffi' 'expat' 'graphite2' 'zlib' 'libX11' 'hicolor-icon-theme')
deps_arch=('glibc' 'glib2' 'fontconfig' 'freetype2' 'harfbuzz' 'libpng' 'pcre2' 'brotli' 'bzip2' 'util-linux-libs' 'libselinux' 'libffi' 'expat' 'graphite' 'zlib' 'libx11' 'hicolor-icon-theme')
deps_opensuse=('glibc' 'libglib-2_0-0' 'libfontconfig1' 'libfreetype6' 'libharfbuzz0' 'libpng16-16' 'libpcre2-8-0' 'libbrotli1' 'libbz2-1' 'libblkid1' 'libmount1' 'libselinux1' 'libffi8' 'libexpat1' 'libgraphite2-3' 'libz1' 'libX11-6' 'hicolor-icon-theme')
deps_altlinux=('glibc' 'glib2' 'libfontconfig1' 'libfreetype' 'libharfbuzz' 'libpng16' 'libpcre2' 'libbrotlicommon' 'libbrotlidec' 'bzlib' 'libblkid' 'libmount' 'libselinux' 'libffi8' 'libexpat' 'libgraphite2' 'zlib' 'libX11' 'icon-theme-hicolor')
opt_deps=()
opt_deps_debian=('xdg-utils' 'libayatana-appindicator3-1 | libappindicator3-1' 'ca-certificates' 'libasound2t64 | libasound2 | pulseaudio' 'libgl1' 'xdg-desktop-portal' 'xdg-desktop-portal-gtk | xdg-desktop-portal-gnome | xdg-desktop-portal-kde' 'pipewire')
opt_deps_ubuntu=("${opt_deps_debian[@]}")
opt_deps_fedora=('xdg-utils' 'libappindicator-gtk3' 'ca-certificates' 'alsa-lib' 'pulseaudio-libs' 'mesa-libGL' 'xdg-desktop-portal' 'xdg-desktop-portal-gtk' 'pipewire')
opt_deps_arch=('xdg-utils' 'libappindicator-gtk3' 'ca-certificates' 'alsa-lib' 'pulseaudio' 'libglvnd' 'xdg-desktop-portal' 'xdg-desktop-portal-gtk' 'pipewire')
opt_deps_opensuse=('xdg-utils' 'libappindicator3-1' 'ca-certificates' 'libasound2' 'libpulse0' 'Mesa-libGL1' 'xdg-desktop-portal' 'xdg-desktop-portal-gtk' 'pipewire')
opt_deps_altlinux=('xdg-utils' 'libappindicator-gtk3' 'ca-certificates' 'libalsa' 'libGL' 'xdg-desktop-portal' 'xdg-desktop-portal-gtk' 'pipewire')
sources=(
"https://td.telegram.org/tlinux/tsetup.${version}.tar.xz?~archive=false&~name=tsetup.tar.xz"
'local:///org.telegram.desktop.desktop'
'local:///telegram-desktop.png'
'local:///LICENSE'
)
checksums=(
'sha256:a9463f13c5f479e1dd951629ab9aa3686386cade4eb61138e0141db2efcfad6d'
'sha256:8ceb3f6aea1003f84f1b5757cd857f8237b294bb18456e5e0e54d96e6fff04ba'
'sha256:3fb1400c7dc9bbc3b5cb3ffedcbf4a9b09c53e28b57a7ff33a8a6b9048864090'
'sha256:f3ad1b5ecdc1c9e5ab3719a0b084a3e0b4fc87ad424fbf81043df9637be3d1f5'
)
scripts=(
['postinstall']='postinstall.sh'
['postremove']='postremove.sh'
)
package() {
cd "${srcdir}"
tar --no-same-owner --no-same-permissions -xJf tsetup.tar.xz
install -dm755 "${pkgdir}/opt/telegram-desktop"
install -Dm755 "Telegram/Telegram" "${pkgdir}/opt/telegram-desktop/Telegram"
install -dm755 "${pkgdir}/usr/bin"
ln -sfn /opt/telegram-desktop/Telegram "${pkgdir}/usr/bin/telegram-desktop"
# Telegram's own Launcher::ComputeExternalUpdater() (SourceFiles/core/
# launcher.cpp) looks for a file under <exe-dir>/externalupdater.d/ that
# contains a line matching its own full executable path; if found, it
# calls SetUpdaterDisabledAtStartup(), which is the *documented* way for
# distro/repo packagers to tell Telegram "I manage updates, not you".
# This one flag fixes two things at once: it makes InstallLauncher() skip
# self-writing a shadow ~/.local/share/applications/*.desktop and D-Bus
# .service file on every launch, and it makes QGuiApplication's
# desktopFileName() (the Wayland app_id used to match the running window
# to an icon) use the plain "org.telegram.desktop" id instead of a
# per-install-path hashed one — which is what was breaking the
# taskbar/window icon even after the shadow-file duplicate was silenced.
install -Dm644 /dev/stdin \
"${pkgdir}/opt/telegram-desktop/externalupdater.d/nivora" <<'EOF'
/opt/telegram-desktop/Telegram
EOF
install-desktop org.telegram.desktop.desktop
install -Dm644 telegram-desktop.png \
"${pkgdir}/usr/share/icons/hicolor/256x256/apps/org.telegram.desktop.png"
install-license LICENSE "telegram/LICENSE.nivora"
}
files() {
files-find-binary
files-find-desktop
files-find-license
files-find \
"/opt/telegram-desktop/**/*" \
"/usr/share/icons/hicolor/256x256/apps/org.telegram.desktop.png"
}
+22
View File
@@ -0,0 +1,22 @@
[Desktop Entry]
Name=Telegram
Comment=New era of messaging
TryExec=telegram-desktop
Exec=telegram-desktop -- %U
Icon=org.telegram.desktop
Terminal=false
StartupWMClass=TelegramDesktop
Type=Application
Categories=Chat;Network;InstantMessaging;Qt;
MimeType=x-scheme-handler/tg;x-scheme-handler/tonsite;
Keywords=tg;chat;im;messaging;messenger;sms;tdesktop;
Actions=quit;
DBusActivatable=false
SingleMainWindow=true
X-GNOME-UsesNotifications=true
X-GNOME-SingleWindow=true
[Desktop Action quit]
Exec=telegram-desktop -quit
Name=Quit Telegram
Icon=application-exit
+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 -q -t -f /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 -q -t -f /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"
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

+15 -4
View File
@@ -2,10 +2,12 @@
set -euo pipefail set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
launcher="${repo_root}/claude-desktop/claude-alt" launcher="${repo_root}/claude-alt/claude-alt"
icon="${repo_root}/claude-desktop/claude-alt.png" icon="${repo_root}/claude/claude-alt.png"
primary_tray_icon="${repo_root}/claude-desktop/claude-tray-orange.png" primary_tray_icon="${repo_root}/claude/claude-tray-orange.png"
alternate_tray_icon="${repo_root}/claude-desktop/claude-alt-tray-turquoise.png" alternate_tray_icon="${repo_root}/claude/claude-alt-tray-turquoise.png"
primary_recipe="${repo_root}/claude/Staplerfile"
alternate_recipe="${repo_root}/claude-alt/Staplerfile"
temp_dir="$(mktemp -d)" temp_dir="$(mktemp -d)"
cleanup() { cleanup() {
@@ -14,6 +16,15 @@ cleanup() {
} }
trap cleanup EXIT trap cleanup EXIT
if grep -Eq '/usr/lib/claude-alt|com\\.anthropic\\.ClaudeAlt|claude-alt\\.png' \
"$primary_recipe"; then
echo 'claude-desktop must not ship ClaudeAlt files' >&2
exit 1
fi
grep -Fq 'python3 patch-asar.py' "$alternate_recipe"
grep -Fq 'com.anthropic.ClaudeAlt.desktop' "$alternate_recipe"
python3 -m py_compile "${repo_root}/claude-alt/patch-asar.py"
mkdir -p "${temp_dir}/bin" "${temp_dir}/home" mkdir -p "${temp_dir}/bin" "${temp_dir}/home"
log="${temp_dir}/launch.log" log="${temp_dir}/launch.log"
-51
View File
@@ -1,51 +0,0 @@
#!/bin/bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
temp_dir="$(mktemp -d)"
cleanup() {
find "$temp_dir" -mindepth 1 -delete
rmdir "$temp_dir"
}
trap cleanup EXIT
mkdir -p "${temp_dir}/bin"
log="${temp_dir}/commands.log"
cat >"${temp_dir}/bin/stplr" <<'EOF'
#!/bin/bash
printf 'stplr' >>"$NIVORA_TEST_LOG"
printf ' %s' "$@" >>"$NIVORA_TEST_LOG"
printf '\n' >>"$NIVORA_TEST_LOG"
EOF
cat >"${temp_dir}/bin/test-sudo" <<'EOF'
#!/bin/bash
printf 'sudo' >>"$NIVORA_TEST_LOG"
printf ' %s' "$@" >>"$NIVORA_TEST_LOG"
printf '\n' >>"$NIVORA_TEST_LOG"
exec "$@"
EOF
chmod 0755 "${temp_dir}/bin/"*
for alias in sli slii sl; do
ln -s "$repo_root/nivora-stplr/nivora-stplr" "${temp_dir}/bin/${alias}"
done
export NIVORA_TEST_LOG="$log"
export NIVORA_STPLR_SUDO=test-sudo
export NIVORA_STPLR_QUIET=1
export PATH="${temp_dir}/bin:${PATH}"
sli --repo nivora parsec --clean
slii nivora/codex
cat >"${temp_dir}/expected" <<'EOF'
sudo stplr install nivora/parsec --clean
stplr install nivora/parsec --clean
stplr info nivora/codex
EOF
diff -u "${temp_dir}/expected" "$log"
echo 'OK: Nivora helper'
+96
View File
@@ -0,0 +1,96 @@
#!/bin/bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
temp_dir="$(mktemp -d)"
cleanup() {
find "$temp_dir" -mindepth 1 -delete
rmdir "$temp_dir"
}
trap cleanup EXIT
mkdir -p "${temp_dir}/bin"
log="${temp_dir}/commands.log"
cat >"${temp_dir}/bin/stplr" <<'EOF'
#!/bin/bash
printf 'stplr' >>"$NIVORA_TEST_LOG"
printf ' %s' "$@" >>"$NIVORA_TEST_LOG"
printf '\n' >>"$NIVORA_TEST_LOG"
EOF
cat >"${temp_dir}/bin/test-sudo" <<'EOF'
#!/bin/bash
printf 'sudo' >>"$NIVORA_TEST_LOG"
printf ' %s' "$@" >>"$NIVORA_TEST_LOG"
printf '\n' >>"$NIVORA_TEST_LOG"
exec "$@"
EOF
chmod 0755 "${temp_dir}/bin/"*
for alias in nv nvi nve nvu nvs nvqi nvqa nvr nvrl nvf nvd nvc; do
ln -s "${repo_root}/nivora-cli/nivora" "${temp_dir}/bin/${alias}"
done
export NIVORA_TEST_LOG="$log"
export NIVORA_SUDO='test-sudo'
export NIVORA_QUIET=1
export PATH="${temp_dir}/bin:${PATH}"
nvi codex --clean
nve other/package
nvu
nvs editor
nvqi --json codex
nvqa --installed
nvr
nvrl
nvf
nvc
nv --repo none install local-name
cat >"${temp_dir}/expected" <<'EOF'
sudo stplr install nivora/codex --clean
stplr install nivora/codex --clean
sudo stplr remove other/package
stplr remove other/package
sudo stplr up
stplr up
stplr search editor
stplr info --json nivora/codex
stplr list --installed
sudo stplr refresh
stplr refresh
stplr repo list
sudo stplr fix
stplr fix
stplr config show
sudo stplr install local-name
stplr install local-name
EOF
diff -u "${temp_dir}/expected" "$log"
nv --lang en --help >"${temp_dir}/help-en"
nv --lang ru --help >"${temp_dir}/help-ru"
nv --lang ru completion fish >"${temp_dir}/completion"
nvd --lang en >"${temp_dir}/doctor"
NIVORA_QUIET=0 nv --lang en --dry-run install codex \
>"${temp_dir}/preview-stdout" 2>"${temp_dir}/preview"
grep -q 'Simple Stapler package management' "${temp_dir}/help-en"
grep -q 'Простое управление пакетами Stapler' "${temp_dir}/help-ru"
grep -q 'nvi.*установить' "${temp_dir}/help-ru"
grep -q '__fish_use_subcommand' "${temp_dir}/completion"
grep -q 'The system is ready' "${temp_dir}/doctor"
grep -q 'Preview.*test-sudo stplr install nivora/codex' "${temp_dir}/preview"
[[ ! -s "${temp_dir}/preview-stdout" ]]
if nv --lang de help >"${temp_dir}/invalid-out" 2>"${temp_dir}/invalid"; then
echo 'Ожидалась ошибка для неподдерживаемого языка' >&2
exit 1
fi
grep -q 'supported languages are ru and en' "${temp_dir}/invalid"
echo 'OK: Nivora CLI'
+1
View File
@@ -13,6 +13,7 @@ grep -Fq 'compatibility_desktop=' "$recipe"
grep -Fq 'canonical_desktop=' "$recipe" grep -Fq 'canonical_desktop=' "$recipe"
grep -Fq "sed -i '/^NoDisplay=true\$/d'" "$recipe" grep -Fq "sed -i '/^NoDisplay=true\$/d'" "$recipe"
grep -Fq "sed -i '/^\\[Desktop Entry\\]\$/a NoDisplay=true'" "$recipe" grep -Fq "sed -i '/^\\[Desktop Entry\\]\$/a NoDisplay=true'" "$recipe"
grep -Fq 'StartupWMClass=Yandex-browser' "$recipe"
grep -Fq 'update_codecs' "$postinstall" grep -Fq 'update_codecs' "$postinstall"
if grep -Eq '/etc/(cron|xdg/autostart)' "$recipe"; then if grep -Eq '/etc/(cron|xdg/autostart)' "$recipe"; then
+58 -4
View File
@@ -7,18 +7,23 @@ readonly -a PACKAGES=(
balena-etcher balena-etcher
chatbox chatbox
clash-verge-rev clash-verge-rev
claude-desktop claude-alt
claude
codex codex
fisher fisher
github-desktop github-desktop
happ happ
max
netbird netbird
nivora-stplr nivora-cli
opencode opencode
parsec parsec
pineconemc pineconemc
tailscale tailscale
telegram
ventoy ventoy
vintner
vk-messenger
vual vual
yandex-browser-stable yandex-browser-stable
) )
@@ -211,6 +216,51 @@ latest_yandex_browser() {
printf '%s\n' "$version" printf '%s\n' "$version"
} }
latest_max() {
local filename ver build
filename="$(
curl --retry 3 --retry-delay 2 --retry-all-errors \
--connect-timeout 30 --max-time 120 -fsSL \
'https://download.max.ru/linux/deb/dists/stable/main/binary-amd64/Packages.gz' |
gzip -dc |
awk '
/^Package: max$/ { selected = 1; next }
/^Package: / { selected = 0 }
selected && /^Filename: / { print $2 }
' |
sort -V |
tail -1
)"
[[ -n "$filename" ]] || die 'cannot determine latest MAX version'
filename="$(basename "$filename")"
filename="${filename#MAX-}"
filename="${filename%.deb}"
ver="${filename%.*}"
build="${filename##*.}"
[[ -n "$ver" && -n "$build" ]] || die 'cannot parse latest MAX filename'
printf '%s~%s\n' "$ver" "$build"
}
latest_vk_messenger() {
local temp_dir version
temp_dir="$(mktemp -d)"
curl --retry 3 --retry-delay 2 --retry-all-errors \
--connect-timeout 30 --max-time 300 -fsSL \
-o "${temp_dir}/vk-messenger.deb" \
'https://upload.object2.vk-apps.com/vk-me-desktop-dev-5837a06d-5f28-484a-ac22-045903cb1b1a/latest/vk-messenger.deb'
(
cd "$temp_dir"
ar x vk-messenger.deb
tar -xOf control.tar.* ./control
) >"${temp_dir}/control"
version="$(awk '$1 == "Version:" {print $2; exit}' "${temp_dir}/control")"
version="${version%-*}"
[[ -n "$version" ]] || die 'cannot determine latest VK Messenger version'
printf '%s\n' "$version"
find "$temp_dir" -mindepth 1 -delete
rmdir "$temp_dir"
}
latest_version() { latest_version() {
case "$1" in case "$1" in
adwyra) github_latest_release Cheviiot/Adwyra ;; adwyra) github_latest_release Cheviiot/Adwyra ;;
@@ -218,20 +268,24 @@ latest_version() {
balena-etcher) github_latest_release balena-io/etcher ;; balena-etcher) github_latest_release balena-io/etcher ;;
chatbox) latest_chatbox ;; chatbox) latest_chatbox ;;
clash-verge-rev) github_latest_release clash-verge-rev/clash-verge-rev ;; clash-verge-rev) github_latest_release clash-verge-rev/clash-verge-rev ;;
claude-desktop) latest_claude_desktop ;; claude | claude-alt) latest_claude_desktop ;;
codex) github_latest_release Boria138/codex-app-linux ;; codex) github_latest_release Boria138/codex-app-linux ;;
fisher) github_latest_release jorgebucaran/fisher ;; fisher) github_latest_release jorgebucaran/fisher ;;
github-desktop) github-desktop)
github_latest_release desktop/desktop | sed 's/^release-//' github_latest_release desktop/desktop | sed 's/^release-//'
;; ;;
happ) github_latest_release Happ-proxy/happ-desktop ;; happ) github_latest_release Happ-proxy/happ-desktop ;;
max) latest_max ;;
netbird) github_latest_release netbirdio/netbird ;; netbird) github_latest_release netbirdio/netbird ;;
nivora-stplr) current_version nivora-stplr ;; nivora-cli) current_version nivora-cli ;;
opencode) github_latest_release anomalyco/opencode ;; opencode) github_latest_release anomalyco/opencode ;;
parsec) latest_parsec ;; parsec) latest_parsec ;;
pineconemc) github_latest_release ElyPrismLauncher/Launcher ;; pineconemc) github_latest_release ElyPrismLauncher/Launcher ;;
tailscale) latest_tailscale ;; tailscale) latest_tailscale ;;
telegram) github_latest_release telegramdesktop/tdesktop ;;
ventoy) github_latest_release ventoy/Ventoy ;; ventoy) github_latest_release ventoy/Ventoy ;;
vintner) github_latest_release Cheviiot/vintner ;;
vk-messenger) latest_vk_messenger ;;
vual) github_latest_release Cheviiot/Vual ;; vual) github_latest_release Cheviiot/Vual ;;
yandex-browser-stable) latest_yandex_browser ;; yandex-browser-stable) latest_yandex_browser ;;
*) die "unknown package: $1" ;; *) die "unknown package: $1" ;;
+1 -1
View File
@@ -32,7 +32,7 @@ if [[ "${#python_files[@]}" -gt 0 ]]; then
fi fi
python3 -m unittest discover -s tests -p 'test_*.py' -v python3 -m unittest discover -s tests -p 'test_*.py' -v
bash tests/test_helper.sh bash tests/test_nivora_cli.sh
bash tests/test_claude_alt.sh bash tests/test_claude_alt.sh
bash tests/test_codex_computer_use.sh bash tests/test_codex_computer_use.sh
bash tests/test_happ_theme.sh bash tests/test_happ_theme.sh
+1 -1
View File
@@ -41,7 +41,7 @@ def main() -> None:
) )
for package in package_dirs: for package in package_dirs:
command = f"stplr install nivora/{package}" command = f"`stplr install nivora/{package}`"
version, architectures = package_metadata(package) version, architectures = package_metadata(package)
matching_lines = [ matching_lines = [
index index
+2 -2
View File
@@ -26,9 +26,9 @@ esac
# package | command | persistent state marker | desktop/unit | icon # package | command | persistent state marker | desktop/unit | icon
readonly -a lifecycle_packages=( readonly -a lifecycle_packages=(
'clash-verge-rev|/usr/bin/clash-verge|/home/nivora-test/.local/share/io.github.clash-verge-rev.clash-verge-rev/nivora-lifecycle-state|/usr/share/applications/Clash Verge.desktop|/usr/share/icons/hicolor/128x128/apps/clash-verge.png' 'clash-verge-rev|/usr/bin/clash-verge|/home/nivora-test/.local/share/io.github.clash-verge-rev.clash-verge-rev/nivora-lifecycle-state|/usr/share/applications/Clash Verge.desktop|/usr/share/icons/hicolor/128x128/apps/clash-verge.png'
'claude-desktop|/usr/bin/claude-desktop|/home/nivora-test/.config/Claude/nivora-lifecycle-state|/usr/share/applications/com.anthropic.Claude.desktop|/usr/share/icons/hicolor/128x128/apps/claude-desktop.png' 'claude|/usr/bin/claude-desktop|/home/nivora-test/.config/Claude/nivora-lifecycle-state|/usr/share/applications/com.anthropic.Claude.desktop|/usr/share/icons/hicolor/128x128/apps/claude-desktop.png'
'codex|/usr/bin/codex-app|/home/nivora-test/.codex/nivora-lifecycle-state|/usr/share/applications/codex-app.desktop|/usr/share/icons/hicolor/512x512/apps/codex-app.png' 'codex|/usr/bin/codex-app|/home/nivora-test/.codex/nivora-lifecycle-state|/usr/share/applications/codex-app.desktop|/usr/share/icons/hicolor/512x512/apps/codex-app.png'
'nivora-stplr|/usr/bin/sl|/home/nivora-test/.config/nivora-stplr/nivora-lifecycle-state|-|-' 'nivora-cli|/usr/bin/nv|/home/nivora-test/.config/nivora-cli/nivora-lifecycle-state|-|-'
'opencode|/usr/bin/opencode-desktop|/home/nivora-test/.config/opencode/nivora-lifecycle-state|/usr/share/applications/opencode-desktop.desktop|/usr/share/icons/hicolor/128x128/apps/ai.opencode.desktop.png' 'opencode|/usr/bin/opencode-desktop|/home/nivora-test/.config/opencode/nivora-lifecycle-state|/usr/share/applications/opencode-desktop.desktop|/usr/share/icons/hicolor/128x128/apps/ai.opencode.desktop.png'
'tailscale|/usr/bin/tailscale|/var/lib/tailscale/nivora-lifecycle-state|/usr/lib/systemd/system/tailscaled.service|-' 'tailscale|/usr/bin/tailscale|/var/lib/tailscale/nivora-lifecycle-state|/usr/lib/systemd/system/tailscaled.service|-'
'netbird|/usr/bin/netbird|/var/lib/netbird/nivora-lifecycle-state|/usr/lib/systemd/system/netbird.service|-' 'netbird|/usr/bin/netbird|/var/lib/netbird/nivora-lifecycle-state|/usr/lib/systemd/system/netbird.service|-'
+18 -7
View File
@@ -17,18 +17,23 @@ EXPECTED_PACKAGES = (
"balena-etcher", "balena-etcher",
"chatbox", "chatbox",
"clash-verge-rev", "clash-verge-rev",
"claude-desktop", "claude",
"claude-alt",
"codex", "codex",
"fisher", "fisher",
"github-desktop", "github-desktop",
"happ", "happ",
"max",
"netbird", "netbird",
"nivora-stplr", "nivora-cli",
"opencode", "opencode",
"parsec", "parsec",
"pineconemc", "pineconemc",
"tailscale", "tailscale",
"telegram",
"ventoy", "ventoy",
"vintner",
"vk-messenger",
"vual", "vual",
"yandex-browser-stable", "yandex-browser-stable",
) )
@@ -42,11 +47,12 @@ REQUIRED_ROOT_FILES = {
Path("stapler-repo.toml"), Path("stapler-repo.toml"),
Path("docs/maintenance.md"), Path("docs/maintenance.md"),
Path("docs/security-model.md"), Path("docs/security-model.md"),
Path("docs/packages/claude-desktop.md"), Path("docs/packages/claude.md"),
Path("docs/packages/claude-alt.md"),
Path("docs/packages/codex.md"), Path("docs/packages/codex.md"),
Path("docs/packages/github-desktop.md"), Path("docs/packages/github-desktop.md"),
Path("docs/packages/opencode.md"), Path("docs/packages/opencode.md"),
Path("docs/packages/nivora-stplr.md"), Path("docs/packages/nivora-cli.md"),
Path("docs/packages/ventoy.md"), Path("docs/packages/ventoy.md"),
} }
@@ -164,8 +170,13 @@ def validate_package(package: str, errors: list[str]) -> dict[str, object]:
if provides != [] or conflicts != []: if provides != [] or conflicts != []:
errors.append(f"{package}: provides/conflicts must not contain binary aliases") errors.append(f"{package}: provides/conflicts must not contain binary aliases")
if replaces != [package]: expected_replaces = [package]
errors.append(f"{package}: replaces must contain only its own base package name") if package == "claude":
expected_replaces.append("claude-desktop")
if replaces != expected_replaces:
errors.append(
f"{package}: replaces must be {expected_replaces}, got {replaces}"
)
if "package()" not in text or "files()" not in text: if "package()" not in text or "files()" not in text:
errors.append(f"{package}: package() or files() is missing") errors.append(f"{package}: package() or files() is missing")
@@ -239,7 +250,7 @@ def validate_readme(metadata: dict[str, dict[str, object]], errors: list[str]) -
errors.append("README.md: catalog must contain exactly six categories") errors.append("README.md: catalog must contain exactly six categories")
for package, values in metadata.items(): for package, values in metadata.items():
command = f"stplr install nivora/{package}" command = f"`stplr install nivora/{package}`"
catalog_rows = [ catalog_rows = [
line for line in text.splitlines() if line.startswith("|") and command in line line for line in text.splitlines() if line.startswith("|") and command in line
] ]
+13 -11
View File
@@ -93,17 +93,23 @@ for package in "${packages[@]}"; do
} }
fi fi
if [[ "$package" == 'claude-desktop' ]]; then if [[ "$package" == 'claude' ]]; then
for forbidden_path in \
/usr/bin/claude-alt \
/usr/lib/claude-alt/claude-alt-bin \
/usr/share/applications/com.anthropic.ClaudeAlt.desktop; do
contains_path "$forbidden_path" && {
echo "${package}: обнаружен компонент отдельного пакета ClaudeAlt: ${forbidden_path}" >&2
exit 1
}
done
fi
if [[ "$package" == 'claude-alt' ]]; then
for required_path in \ for required_path in \
/usr/bin/claude-alt \ /usr/bin/claude-alt \
/usr/bin/claude-desktop-account2 \
/usr/lib/claude-desktop/resources/TrayIconLinux.png \
/usr/lib/claude-desktop/resources/TrayIconLinux-Dark.png \
/usr/lib/claude-alt/claude-alt-bin \ /usr/lib/claude-alt/claude-alt-bin \
/usr/lib/claude-alt/resources/app.asar \ /usr/lib/claude-alt/resources/app.asar \
/usr/lib/claude-alt/resources/icon.png \
/usr/lib/claude-alt/resources/TrayIconLinux.png \
/usr/lib/claude-alt/resources/TrayIconLinux-Dark.png \
/usr/share/applications/com.anthropic.ClaudeAlt.desktop \ /usr/share/applications/com.anthropic.ClaudeAlt.desktop \
/usr/share/icons/hicolor/512x512/apps/claude-alt.png; do /usr/share/icons/hicolor/512x512/apps/claude-alt.png; do
contains_path "$required_path" || { contains_path "$required_path" || {
@@ -111,10 +117,6 @@ for package in "${packages[@]}"; do
exit 1 exit 1
} }
done done
contains_path '/usr/share/applications/claude-desktop-account2.desktop' && {
echo "${package}: обнаружен устаревший desktop-файл второго профиля" >&2
exit 1
}
fi fi
if [[ "$package" == 'balena-etcher' ]]; then if [[ "$package" == 'balena-etcher' ]]; then
+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")"
+25
View File
@@ -0,0 +1,25 @@
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.
This license covers vintner's own source code. It does not cover the
Microsoft Visual C++ Build Tools / Windows SDK that `vintner download`
fetches and unpacks, which remain governed by Microsoft's own license terms.
+76
View File
@@ -0,0 +1,76 @@
name='vintner'
version=0.5.0
release=1
summary='Cross compile with MSVC on Linux via Wine'
summary_ru='Кросс-компиляция с MSVC на Linux через Wine'
group='Development/Tools'
desc='vintner is a single-binary tool for cross compiling with the real MSVC toolchain on Linux via Wine. Once you run its download/install subcommands, cl, link, lib, rc, midl, mc, mt, dumpbin, msbuild, nmake, ml, ml64, armasm, armasm64, cmd and findstr all work as drop-in commands, exactly like on Windows, or can be invoked directly as "vintner cl", "vintner msbuild", etc. without touching PATH. Supports building real KMDF/UMDF drivers via the Windows Driver Kit (--with-wdk), legacy D3DX9 code via the DirectX SDK (--with-dxsdk), old PlatformToolset-pinned .vcxproj files against the one installed compiler, resumes interrupted downloads, and "vintner doctor" diagnoses a broken wine/toolchain setup. Its own CLI is available in English and Russian.'
desc_ru='vintner — однобинарный инструмент для кросс-компиляции с реальным MSVC на Linux через Wine. После выполнения его подкоманд download/install команды cl, link, lib, rc, midl, mc, mt, dumpbin, msbuild, nmake, ml, ml64, armasm, armasm64, cmd и findstr работают как обычные команды, точно как в Windows, либо вызываются напрямую как «vintner cl», «vintner msbuild» и т.д., не трогая PATH. Поддерживает сборку настоящих KMDF/UMDF-драйверов через Windows Driver Kit (--with-wdk), устаревший код D3DX9 через DirectX SDK (--with-dxsdk), старые .vcxproj с закреплённым PlatformToolset против единственного установленного компилятора, докачивает прерванные загрузки, а «vintner doctor» диагностирует неисправную настройку wine/toolchain. Сам CLI доступен на английском и русском.'
homepage='https://github.com/Cheviiot/vintner'
maintainer='Cheviiot <https://github.com/Cheviiot>'
architectures=('amd64' 'arm64')
license=('MIT')
provides=()
replaces=('vintner')
conflicts=()
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
# wine/msitools/git/cabextract are needed at runtime by `vintner
# download`/`install` and by the tool wrappers themselves, not just at
# build time. cabextract is only exercised by --with-dxsdk, but kept a
# hard dep for the same reason msitools is: small, no heavy transitive
# deps, and simpler than making package deps conditional on a flag.
deps=()
deps_debian=('wine' 'msitools' 'git' 'cabextract')
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=('wine' 'msitools' 'git' 'cabextract')
deps_arch=('wine' 'msitools' 'git' 'cabextract')
deps_opensuse=('wine' 'msitools' 'git' 'cabextract')
deps_altlinux=('wine' 'msitools' 'git' 'cabextract')
build_deps=('binutils')
# Prebuilt by vintner's own CI (.github/workflows/release.yml) and
# published to its GitHub Releases; renamed to a common local name so
# package() doesn't need to branch on architecture.
sources=(
"https://github.com/Cheviiot/vintner/releases/download/v${version}/vintner-linux-amd64?~archive=false&~name=vintner"
'local:///LICENSE'
)
sources_arm64=(
"https://github.com/Cheviiot/vintner/releases/download/v${version}/vintner-linux-arm64?~archive=false&~name=vintner"
'local:///LICENSE'
)
checksums=(
'sha256:c06edc2c6469f3a6bac63bd2a1e811f6fe03cc05c49871413a8f58d5e382732a'
'sha256:e377b84adc5315cf372c3d80ecbd3008402d8b07f248e711c7553784e0c33168'
)
checksums_arm64=(
'sha256:4ddb97ad6c193058589564f99781b3ef5e5725d243d3c1cbb895b81c35799144'
'sha256:e377b84adc5315cf372c3d80ecbd3008402d8b07f248e711c7553784e0c33168'
)
package() {
cd "${srcdir}"
chmod +x "${srcdir}/vintner"
install-binary "${srcdir}/vintner"
install-license "${srcdir}/LICENSE" "vintner/LICENSE"
"${srcdir}/vintner" completion bash | install-completion bash vintner
"${srcdir}/vintner" completion zsh | install-completion zsh vintner
}
files() {
files-find-binary
files-find-license
files-find "/usr/share/bash-completion/completions/vintner"
files-find "/usr/share/zsh/site-functions/_vintner"
}
+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")"
+19
View File
@@ -0,0 +1,19 @@
Сведения о лицензии пакета vk-messenger
Лицензия: Custom (проприетарная).
Исходный продукт: https://vk.me/app
Официальный deb/rpm-артефакт: https://vk.me/app (объектное хранилище VK)
Нюансы пакета:
- Пакет перепаковывает официальный Linux-артефакт VK Messenger, распространяемый
напрямую с vk.me — отдельного публичного репозитория или GitHub-релиза
у продукта нет, поэтому используется прямая ссылка на объектное хранилище VK.
- Приложение собрано на Electron/Chromium; лицензии этих компонентов
устанавливаются рядом с остальными лицензиями пакета.
- Для полноценной работы требуется учетная запись VK ID.
- Использование мессенджера, учетной записи и сетевых функций регулируется
условиями VK.
- Пакет помечен как несвободный через `nonfree=1`.
Официальные условия:
https://vk.com/terms
+102
View File
@@ -0,0 +1,102 @@
name='vk-messenger'
version='6.0.57'
release=1
summary='VK Messenger client'
summary_ru='Официальный клиент VK Messenger'
group='Networking/Instant messaging'
desc='VK Messenger is the official VK instant messaging client, distributed as an Electron desktop application directly from vk.me.'
desc_ru='VK Messenger — официальный клиент мессенджера VK, распространяемый в виде Electron-приложения напрямую с vk.me.'
homepage='https://vk.me/app'
maintainer='VK Support <support@vk.com>'
architectures=('amd64')
license=('Custom')
nonfree=1
nonfree_msg='VK Messenger является проприетарным приложением VK. Использование мессенджера, учетной записи VK ID и сетевых функций регулируется условиями VK.'
nonfree_url='https://vk.com/terms'
provides=()
replaces=('vk-messenger')
conflicts=()
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
deps=()
deps_debian=('libc6' 'libglib2.0-0t64 | libglib2.0-0' 'libgtk-3-0t64 | libgtk-3-0' 'libnotify4' 'libnss3' 'libxss1' 'libxtst6' 'xdg-utils' 'libatspi2.0-0' 'libuuid1' 'libsecret-1-0' 'libasound2t64 | libasound2 | pulseaudio' 'libgbm1' 'libdrm2' 'pulseaudio | pulseaudio-utils | pipewire-pulse')
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=('glibc' 'glib2' 'gtk3' 'libnotify' 'nss' 'libXScrnSaver' 'libXtst' 'xdg-utils' 'at-spi2-core' 'libuuid' 'libsecret' 'alsa-lib' 'mesa-libgbm' 'libdrm' 'pulseaudio-utils')
deps_arch=('glibc' 'glib2' 'gtk3' 'libnotify' 'nss' 'libxss' 'libxtst' 'xdg-utils' 'at-spi2-core' 'libutil-linux' 'libsecret' 'alsa-lib' 'libgbm' 'libdrm' 'libpulse')
deps_opensuse=('glibc' 'libglib-2_0-0' 'gtk3' 'libnotify4' 'mozilla-nss' 'libXss1' 'libXtst6' 'xdg-utils' 'at-spi2-core' 'libuuid1' 'libsecret-1-0' 'libasound2' 'libgbm1' 'libdrm2' 'pulseaudio-utils')
deps_altlinux=('glibc' 'glib2' 'libgtk+3' 'libnotify' 'libnss' 'libXScrnSaver' 'libXtst' 'xdg-utils' 'libat-spi2-core' 'libuuid' 'libsecret' 'libalsa' 'libgbm' 'libdrm' 'pulseaudio-utils')
opt_deps=()
opt_deps_debian=('libayatana-appindicator3-1 | libappindicator3-1')
opt_deps_ubuntu=("${opt_deps_debian[@]}")
opt_deps_fedora=('libappindicator-gtk3')
opt_deps_arch=('libappindicator-gtk3')
opt_deps_opensuse=('libappindicator3-1')
opt_deps_altlinux=('libappindicator-gtk3')
build_deps=('binutils')
sources=(
"https://upload.object2.vk-apps.com/vk-me-desktop-dev-5837a06d-5f28-484a-ac22-045903cb1b1a/latest/vk-messenger.deb?~archive=false&~name=vk-messenger.deb"
'local:///LICENSE'
)
checksums=(
'sha256:cb4fa7a8fb3c6af5c432696e4653751393c9c5c655b6bf0e2d944ff7483ec784'
'sha256:ab672f11775fc2a579a2001bef3c8b75d78159a67e004dd55339befe98f9e232'
)
scripts=(
['postinstall']='postinstall.sh'
['postremove']='postremove.sh'
)
package() {
cd "${srcdir}"
mkdir -p "${srcdir}/extracted"
ar x vk-messenger.deb
tar -xf data.tar.* -C "${srcdir}/extracted"
# Upstream's own installer uses the literal (Cyrillic, spaced) product name
# as the /opt directory, not a clean slug — find it rather than hardcode it.
opt_src="$(find "${srcdir}/extracted/opt" -mindepth 1 -maxdepth 1 -type d | head -1)"
[[ -n "$opt_src" ]] || { echo "не найден каталог /opt/* внутри vk-messenger.deb" >&2; exit 1; }
install -dm755 "${pkgdir}/opt/vk-messenger"
cp -a "${opt_src}/." "${pkgdir}/opt/vk-messenger/"
chmod 0755 "${pkgdir}/opt/vk-messenger/chrome-sandbox"
install -dm755 "${pkgdir}/usr/bin"
ln -sfn /opt/vk-messenger/vk-messenger "${pkgdir}/usr/bin/vk-messenger"
install -Dm644 "${srcdir}/extracted/usr/share/applications/vk-messenger.desktop" \
"${pkgdir}/usr/share/applications/vk-messenger.desktop"
for icon in "${srcdir}"/extracted/usr/share/icons/hicolor/*/apps/vk-messenger.*; do
size="$(basename "$(dirname "$(dirname "$icon")")")"
install -Dm644 "$icon" \
"${pkgdir}/usr/share/icons/hicolor/${size}/apps/$(basename "$icon")"
done
install-license "${srcdir}/LICENSE" "vk-messenger/LICENSE.nivora"
install -Dm644 "${opt_src}/LICENSE.electron.txt" \
"${pkgdir}/usr/share/licenses/vk-messenger/LICENSE.electron.txt"
install -Dm644 "${opt_src}/LICENSES.chromium.html" \
"${pkgdir}/usr/share/licenses/vk-messenger/LICENSES.chromium.html"
}
files() {
files-find-binary
files-find-desktop
files-find-license
files-find \
"/opt/vk-messenger/**/*" \
"/usr/share/icons/hicolor/*/apps/vk-messenger.*"
}
+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/vk-messenger/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 @@
include = "../stapler-repo.toml"
+6 -3
View File
@@ -1,6 +1,6 @@
name='yandex-browser-stable' name='yandex-browser-stable'
version='26.4.1.1110' version=26.6.1.1003
release=3 release=1
summary='Fast and secure Chromium-based web browser from Yandex' summary='Fast and secure Chromium-based web browser from Yandex'
summary_ru='Быстрый и безопасный браузер Яндекса на основе Chromium' summary_ru='Быстрый и безопасный браузер Яндекса на основе Chromium'
group='Networking/WWW' group='Networking/WWW'
@@ -79,7 +79,7 @@ sources=(
) )
checksums=( checksums=(
'sha256:27c8570c68a5e1b475304aaa4e8accefbc21e150b644a9cbc998ed32d952c8ba' 'sha256:740006518ea877303b70a69e063dcc8531fe05b4d1582f270b783e22a83cf852'
'sha256:36fb82bc8a2bb99eea41807712d9abf810edd562fa521955444e2dbef26ff40d' 'sha256:36fb82bc8a2bb99eea41807712d9abf810edd562fa521955444e2dbef26ff40d'
) )
@@ -110,6 +110,9 @@ package() {
canonical_desktop="${pkgdir}/usr/share/applications/ru.yandex.desktop.browser.desktop" canonical_desktop="${pkgdir}/usr/share/applications/ru.yandex.desktop.browser.desktop"
sed -i '/^NoDisplay=true$/d' "${compatibility_desktop}" "${canonical_desktop}" sed -i '/^NoDisplay=true$/d' "${compatibility_desktop}" "${canonical_desktop}"
sed -i '/^\[Desktop Entry\]$/a NoDisplay=true' "${compatibility_desktop}" sed -i '/^\[Desktop Entry\]$/a NoDisplay=true' "${compatibility_desktop}"
sed -i '/^StartupWMClass=/d' "${canonical_desktop}"
sed -i '/^\[Desktop Entry\]$/a StartupWMClass=Yandex-browser' \
"${canonical_desktop}"
cp -a "${srcdir}/extracted/usr/share/appdata" "${pkgdir}/usr/share/" cp -a "${srcdir}/extracted/usr/share/appdata" "${pkgdir}/usr/share/"
cp -a "${srcdir}/extracted/usr/share/gnome-control-center" \ cp -a "${srcdir}/extracted/usr/share/gnome-control-center" \
"${pkgdir}/usr/share/" "${pkgdir}/usr/share/"