feat: add Nivora CLI and msvc-go-wine

This commit is contained in:
Александр
2026-07-25 01:43:11 +10:00
parent 3cc4217790
commit fdb4a492ef
22 changed files with 949 additions and 413 deletions
+12
View File
@@ -2,6 +2,18 @@
В файле фиксируются значимые изменения Nivora.
## 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
### Добавлено
+3 -2
View File
@@ -11,7 +11,7 @@ Nivora предлагает открытые рецепты упаковки д
официальный репозиторий Stapler и не официальные пакеты upstream-проектов.
<!-- package-count -->
**20 пакетов** · **6 категорий** · `amd64`, `arm64` и `all`
**21 пакет** · **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)
@@ -66,6 +66,7 @@ stplr info 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.31836` | `amd64` | `stplr install nivora/codex` |
| [GitHub Desktop](docs/packages/github-desktop.md) | `3.6.3` | `amd64`, `arm64` | `stplr install nivora/github-desktop` |
| [msvc-go-wine](https://github.com/Cheviiot/msvc-go-wine) | `0.1.0` | `amd64`, `arm64` | `stplr install nivora/msvc-go-wine` |
| [OpenCode](https://opencode.ai/) | `1.18.4` | `amd64`, `arm64` | `stplr install nivora/opencode` |
### Рабочий стол
@@ -88,7 +89,7 @@ stplr info nivora/codex
|:--|:--:|:--:|:--|
| [balenaEtcher](https://etcher.balena.io/) | `2.1.6` | `amd64` | `stplr install nivora/balena-etcher` |
| [Fisher](https://github.com/jorgebucaran/fisher) | `4.4.8` | `all` | `stplr install nivora/fisher` |
| [Nivora Stapler Helper](docs/packages/nivora-stplr.md) | `0.3.0` | `all` | `stplr install nivora/nivora-stplr` |
| [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` |
## Обновление
+1 -1
View File
@@ -20,7 +20,7 @@ Stapler сам добавляет текущее `name` в generated `Provides`
| `claude` | Upstream DEB: `Package: claude-desktop`; desktop-id `com.anthropic.Claude` сохранён; старое имя пакета `claude-desktop` заменяется |
| `codex` | Отображаемое имя `Codex`; команда и desktop-id `codex-app` сохранены |
| `github-desktop` | Официальный upstream `desktop/desktop`; Linux-сборка без стороннего форка |
| `nivora-stplr` | Собственный helper Nivora |
| `nivora-cli` | Многоязычная оболочка Nivora для Stapler |
## Локальные проверки
+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`.
+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 msvc-go-wine's own source code. It does not cover the
Microsoft Visual C++ Build Tools / Windows SDK that `msvc-go-wine download`
fetches and unpacks, which remain governed by Microsoft's own license terms.
+68
View File
@@ -0,0 +1,68 @@
name='msvc-go-wine'
version=0.1.1
release=1
summary='Cross compile with MSVC on Linux via Wine'
summary_ru='Кросс-компиляция с MSVC на Linux через Wine'
group='Development/Tools'
desc='msvc-go-wine 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, mt, dumpbin, msbuild, nmake, ml, ml64, armasm, armasm64, cmd and findstr all work as drop-in commands, exactly like on Windows.'
desc_ru='msvc-go-wine — однобинарный инструмент для кросс-компиляции с реальным MSVC на Linux через Wine. После выполнения его подкоманд download/install команды cl, link, lib, rc, midl, mt, dumpbin, msbuild, nmake, ml, ml64, armasm, armasm64, cmd и findstr работают как обычные команды, точно как в Windows.'
homepage='https://github.com/Cheviiot/msvc-go-wine'
maintainer='Cheviiot <https://github.com/Cheviiot>'
architectures=('amd64' 'arm64')
license=('MIT')
provides=()
replaces=('msvc-go-wine')
conflicts=()
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
# wine/msitools/git are needed at runtime by `msvc-go-wine download`/`install`
# and by the tool wrappers themselves, not just at build time.
deps=()
deps_debian=('wine' 'msitools' 'git')
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=('wine' 'msitools' 'git')
deps_arch=('wine' 'msitools' 'git')
deps_opensuse=('wine' 'msitools' 'git')
deps_altlinux=('wine' 'msitools' 'git')
build_deps=('binutils')
# Prebuilt by msvc-go-wine'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/msvc-go-wine/releases/download/v${version}/msvc-go-wine-linux-amd64?~archive=false&~name=msvc-go-wine"
'local:///LICENSE'
)
sources_arm64=(
"https://github.com/Cheviiot/msvc-go-wine/releases/download/v${version}/msvc-go-wine-linux-arm64?~archive=false&~name=msvc-go-wine"
'local:///LICENSE'
)
checksums=(
'sha256:c4193012bab8499cf2000faba61bcd10f77c6faaeea088fe5748e3531f7134e3'
'sha256:693a85757d0e3c527a78af6f12dc5068900c3cd363b92f0998ec41b607b15885'
)
checksums_arm64=(
'sha256:0bc74b95414085acba3e08a2b994063adc3c011cdff61f92e17810c34703ffbc'
'sha256:693a85757d0e3c527a78af6f12dc5068900c3cd363b92f0998ec41b607b15885'
)
package() {
cd "${srcdir}"
install-binary "${srcdir}/msvc-go-wine"
install-license "${srcdir}/LICENSE" "msvc-go-wine/LICENSE"
}
files() {
files-find-binary
files-find-license
}
+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 "$@"
-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 "$@"
-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'
+4 -2
View File
@@ -13,8 +13,9 @@ readonly -a PACKAGES=(
fisher
github-desktop
happ
msvc-go-wine
netbird
nivora-stplr
nivora-cli
opencode
parsec
pineconemc
@@ -226,8 +227,9 @@ latest_version() {
github_latest_release desktop/desktop | sed 's/^release-//'
;;
happ) github_latest_release Happ-proxy/happ-desktop ;;
msvc-go-wine) github_latest_release Cheviiot/msvc-go-wine ;;
netbird) github_latest_release netbirdio/netbird ;;
nivora-stplr) current_version nivora-stplr ;;
nivora-cli) current_version nivora-cli ;;
opencode) github_latest_release anomalyco/opencode ;;
parsec) latest_parsec ;;
pineconemc) github_latest_release ElyPrismLauncher/Launcher ;;
+1 -1
View File
@@ -32,7 +32,7 @@ if [[ "${#python_files[@]}" -gt 0 ]]; then
fi
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_codex_computer_use.sh
bash tests/test_happ_theme.sh
+1 -1
View File
@@ -28,7 +28,7 @@ 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'
'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'
'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'
'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|-'
+3 -2
View File
@@ -23,8 +23,9 @@ EXPECTED_PACKAGES = (
"fisher",
"github-desktop",
"happ",
"msvc-go-wine",
"netbird",
"nivora-stplr",
"nivora-cli",
"opencode",
"parsec",
"pineconemc",
@@ -48,7 +49,7 @@ REQUIRED_ROOT_FILES = {
Path("docs/packages/codex.md"),
Path("docs/packages/github-desktop.md"),
Path("docs/packages/opencode.md"),
Path("docs/packages/nivora-stplr.md"),
Path("docs/packages/nivora-cli.md"),
Path("docs/packages/ventoy.md"),
}