feat: add Gemini skill integration and multi-user AI setup
This commit is contained in:
435
tools/install.sh
435
tools/install.sh
@@ -1,30 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# ServerManager CLI Installer for Linux (headless / no-GUI)
|
||||
# ServerManager AI Integration Installer for Linux/macOS (headless / no-GUI)
|
||||
#
|
||||
# Устанавливает:
|
||||
# - ssh.py + encryption.py → ~/.server-connections/
|
||||
# - servers.json + settings.json → ~/.server-connections/ (если есть)
|
||||
# - CLAUDE.md → ~/.claude/
|
||||
# - ssh.md (скилл) → ~/.claude/commands/
|
||||
# - Python-зависимости для CLI (paramiko, cryptography, etc.)
|
||||
# Installs for each target home:
|
||||
# - ssh.py + encryption.py -> ~/.server-connections/
|
||||
# - Claude /ssh skill -> ~/.claude/commands/
|
||||
# - Codex server-manager skill -> ~/.codex/skills/server-manager/
|
||||
# - Gemini server-manager skill -> ~/.gemini/skills/server-manager/
|
||||
# - codex-ssh / gemini-ssh wrappers -> ~/.server-connections/
|
||||
# - CLAUDE.md / GEMINI.md (if available) -> ~/.claude/ / ~/.gemini/
|
||||
#
|
||||
# Запуск:
|
||||
# curl -sSL https://git.sensey24.ru/aibot777/server-manager/raw/branch/master/tools/install.sh | bash
|
||||
# или:
|
||||
# Optional per-target local config copy:
|
||||
# - servers.json + settings.json -> ~/.server-connections/
|
||||
#
|
||||
# Notes:
|
||||
# - servers.json is NEVER downloaded remotely.
|
||||
# - --all-users installs code/skills/wrappers for discovered homes, but skips
|
||||
# copying servers.json to avoid replicating credentials between users.
|
||||
# - Gemini also supports ~/.agents/skills, but this installer avoids placing
|
||||
# the same skill in both ~/.gemini/skills and ~/.agents/skills by default
|
||||
# because Gemini reports that as a duplicate-skill conflict.
|
||||
#
|
||||
# Usage:
|
||||
# bash install.sh
|
||||
# или с указанием источника файлов:
|
||||
# bash install.sh /path/to/server-manager/
|
||||
# bash install.sh /path/to/server-manager
|
||||
# bash install.sh --source-dir /path/to/server-manager --target-home /root
|
||||
# bash install.sh --all-users --source-dir /path/to/server-manager
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
# ── Colors ──
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
NC='\033[0m'
|
||||
|
||||
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||
@@ -32,33 +42,80 @@ warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $*"; }
|
||||
step() { echo -e "\n${CYAN}━━━ $* ━━━${NC}"; }
|
||||
|
||||
# ── Config ──
|
||||
CONN_DIR="$HOME/.server-connections"
|
||||
CLAUDE_DIR="$HOME/.claude"
|
||||
COMMANDS_DIR="$CLAUDE_DIR/commands"
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
ServerManager AI integration installer
|
||||
|
||||
Options:
|
||||
--source-dir PATH Use local repo as source of files
|
||||
--target-home PATH Install into a specific user's home
|
||||
--all-users Install into all discovered user homes on this machine
|
||||
--install-agents-mirror Also mirror Gemini skill into ~/.agents/skills
|
||||
-h, --help Show this help
|
||||
|
||||
Positional compatibility:
|
||||
install.sh /path/to/server-manager # same as --source-dir
|
||||
USAGE
|
||||
}
|
||||
|
||||
GITEA_RAW="https://git.sensey24.ru/aibot777/server-manager/raw/branch/master"
|
||||
SRC_DIR=""
|
||||
TARGET_HOME="${SERVER_MANAGER_TARGET_HOME:-${TARGET_HOME:-$HOME}}"
|
||||
INSTALL_ALL_USERS=0
|
||||
INSTALL_AGENTS_MIRROR=0
|
||||
|
||||
# Source directory (optional argument)
|
||||
SRC_DIR="${1:-}"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--source-dir)
|
||||
SRC_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--target-home)
|
||||
TARGET_HOME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--all-users)
|
||||
INSTALL_ALL_USERS=1
|
||||
shift
|
||||
;;
|
||||
--install-agents-mirror)
|
||||
INSTALL_AGENTS_MIRROR=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$SRC_DIR" ]]; then
|
||||
SRC_DIR="$1"
|
||||
shift
|
||||
else
|
||||
error "Неизвестный аргумент: $1"
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── Banner ──
|
||||
echo -e "${CYAN}"
|
||||
echo "╔══════════════════════════════════════════════╗"
|
||||
echo "║ ServerManager CLI Installer for Linux ║"
|
||||
echo "║ github: git.sensey24.ru/aibot777 ║"
|
||||
echo "╚══════════════════════════════════════════════╝"
|
||||
echo "╔══════════════════════════════════════════════════════╗"
|
||||
echo "║ ServerManager AI Integration Installer (headless) ║"
|
||||
echo "║ Claude + Codex + Gemini ║"
|
||||
echo "╚══════════════════════════════════════════════════════╝"
|
||||
echo -e "${NC}"
|
||||
|
||||
# ── Step 1: Check Python ──
|
||||
step "1/5 Проверка Python"
|
||||
|
||||
PYTHON=""
|
||||
for cmd in python3 python; do
|
||||
if command -v "$cmd" &>/dev/null; then
|
||||
ver=$("$cmd" --version 2>&1 | grep -oP '\d+\.\d+')
|
||||
major=$(echo "$ver" | cut -d. -f1)
|
||||
minor=$(echo "$ver" | cut -d. -f2)
|
||||
if [ "$major" -ge 3 ] && [ "$minor" -ge 8 ]; then
|
||||
if "$cmd" - <<'PY' &>/dev/null
|
||||
import sys
|
||||
raise SystemExit(0 if sys.version_info >= (3, 8) else 1)
|
||||
PY
|
||||
then
|
||||
PYTHON="$cmd"
|
||||
ok "Python найден: $($cmd --version)"
|
||||
break
|
||||
@@ -66,14 +123,11 @@ for cmd in python3 python; do
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$PYTHON" ]; then
|
||||
error "Python 3.8+ не найден!"
|
||||
echo " Установите: sudo apt install python3 python3-pip"
|
||||
echo " или: sudo yum install python3 python3-pip"
|
||||
if [[ -z "$PYTHON" ]]; then
|
||||
error "Python 3.8+ не найден"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check pip
|
||||
PIP=""
|
||||
for cmd in pip3 pip; do
|
||||
if command -v "$cmd" &>/dev/null; then
|
||||
@@ -81,22 +135,26 @@ for cmd in pip3 pip; do
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$PIP" ]; then
|
||||
# Try python -m pip
|
||||
if [[ -z "$PIP" ]]; then
|
||||
if $PYTHON -m pip --version &>/dev/null; then
|
||||
PIP="$PYTHON -m pip"
|
||||
else
|
||||
error "pip не найден!"
|
||||
echo " Установите: sudo apt install python3-pip"
|
||||
error "pip не найден"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
ok "pip найден: $($PIP --version 2>&1 | head -1)"
|
||||
|
||||
# ── Step 2: Install Python dependencies ──
|
||||
step "2/5 Установка Python-зависимостей"
|
||||
resolve_home() {
|
||||
"$PYTHON" - "$1" <<'PY'
|
||||
import os, sys
|
||||
print(os.path.abspath(os.path.expanduser(sys.argv[1])))
|
||||
PY
|
||||
}
|
||||
|
||||
TARGET_HOME="$(resolve_home "$TARGET_HOME")"
|
||||
|
||||
step "2/5 Установка Python-зависимостей"
|
||||
CLI_DEPS=(
|
||||
"paramiko>=3.4.0"
|
||||
"cryptography>=41.0.0"
|
||||
@@ -105,7 +163,6 @@ CLI_DEPS=(
|
||||
"redis>=5.0.0"
|
||||
"requests>=2.31.0"
|
||||
)
|
||||
|
||||
for dep in "${CLI_DEPS[@]}"; do
|
||||
pkg=$(echo "$dep" | sed 's/[>=<].*//')
|
||||
if $PYTHON -c "import $pkg" 2>/dev/null; then
|
||||
@@ -120,38 +177,26 @@ for dep in "${CLI_DEPS[@]}"; do
|
||||
fi
|
||||
done
|
||||
|
||||
# ── Step 3: Create directories ──
|
||||
step "3/5 Создание директорий"
|
||||
|
||||
mkdir -p "$CONN_DIR" "$COMMANDS_DIR"
|
||||
chmod 700 "$CONN_DIR" 2>/dev/null || true
|
||||
ok "$CONN_DIR"
|
||||
ok "$COMMANDS_DIR"
|
||||
|
||||
# ── Step 4: Copy/Download files ──
|
||||
step "4/5 Установка файлов"
|
||||
|
||||
copy_or_download() {
|
||||
local src_relative="$1"
|
||||
local dst="$2"
|
||||
local perms="$3"
|
||||
local desc="$4"
|
||||
|
||||
# Try local source first
|
||||
if [ -n "$SRC_DIR" ] && [ -f "$SRC_DIR/$src_relative" ]; then
|
||||
mkdir -p "$(dirname "$dst")"
|
||||
|
||||
if [[ -n "$SRC_DIR" && -f "$SRC_DIR/$src_relative" ]]; then
|
||||
cp "$SRC_DIR/$src_relative" "$dst"
|
||||
chmod "$perms" "$dst"
|
||||
chmod "$perms" "$dst" 2>/dev/null || true
|
||||
ok "$desc (из $SRC_DIR)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Try download from Gitea
|
||||
local url="$GITEA_RAW/$src_relative"
|
||||
if command -v curl &>/dev/null; then
|
||||
if curl -sSL -o "$dst" "$url" 2>/dev/null; then
|
||||
# Verify not empty and not HTML error page
|
||||
if [ -s "$dst" ] && ! head -1 "$dst" | grep -qi '<!doctype\|<html'; then
|
||||
chmod "$perms" "$dst"
|
||||
if curl -fsSL -o "$dst" "$url" 2>/dev/null; then
|
||||
if [[ -s "$dst" ]] && ! head -1 "$dst" | grep -qi '<!doctype\|<html'; then
|
||||
chmod "$perms" "$dst" 2>/dev/null || true
|
||||
ok "$desc (скачан с Gitea)"
|
||||
return 0
|
||||
fi
|
||||
@@ -159,8 +204,8 @@ copy_or_download() {
|
||||
fi
|
||||
elif command -v wget &>/dev/null; then
|
||||
if wget -q -O "$dst" "$url" 2>/dev/null; then
|
||||
if [ -s "$dst" ] && ! head -1 "$dst" | grep -qi '<!doctype\|<html'; then
|
||||
chmod "$perms" "$dst"
|
||||
if [[ -s "$dst" ]] && ! head -1 "$dst" | grep -qi '<!doctype\|<html'; then
|
||||
chmod "$perms" "$dst" 2>/dev/null || true
|
||||
ok "$desc (скачан с Gitea)"
|
||||
return 0
|
||||
fi
|
||||
@@ -172,86 +217,180 @@ copy_or_download() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# Core files (always install)
|
||||
copy_or_download "tools/ssh.py" "$CONN_DIR/ssh.py" "755" "ssh.py"
|
||||
copy_or_download "core/encryption.py" "$CONN_DIR/encryption.py" "644" "encryption.py"
|
||||
install_skill_tree() {
|
||||
local prefix="$1"
|
||||
local dst_root="$2"
|
||||
shift 2
|
||||
mkdir -p "$dst_root"
|
||||
local rel
|
||||
for rel in "$@"; do
|
||||
copy_or_download "$prefix/$rel" "$dst_root/$rel" 644 "$prefix/$rel" || true
|
||||
done
|
||||
find "$dst_root/scripts" -type f -name '*.sh' -exec chmod 755 {} + 2>/dev/null || true
|
||||
find "$dst_root/scripts" -type f -name '*.cmd' -exec chmod 644 {} + 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Claude Code skill
|
||||
copy_or_download "tools/skill-ssh.md" "$COMMANDS_DIR/ssh.md" "644" "ssh.md (скилл /ssh)"
|
||||
discover_homes() {
|
||||
local homes=()
|
||||
local uname_s
|
||||
uname_s="$(uname -s 2>/dev/null || echo Linux)"
|
||||
|
||||
# CLAUDE.md
|
||||
if [ -n "$SRC_DIR" ] && [ -f "$SRC_DIR/CLAUDE.md" ]; then
|
||||
cp "$SRC_DIR/CLAUDE.md" "$CLAUDE_DIR/CLAUDE.md"
|
||||
chmod 644 "$CLAUDE_DIR/CLAUDE.md"
|
||||
ok "CLAUDE.md"
|
||||
fi
|
||||
|
||||
# servers.json — only copy if exists locally, never download (contains encrypted creds)
|
||||
if [ -n "$SRC_DIR" ] && [ -f "$SRC_DIR/servers.json" ]; then
|
||||
cp "$SRC_DIR/servers.json" "$CONN_DIR/servers.json"
|
||||
chmod 600 "$CONN_DIR/servers.json"
|
||||
ok "servers.json (зашифрованный)"
|
||||
elif [ ! -f "$CONN_DIR/servers.json" ]; then
|
||||
warn "servers.json не найден — скопируйте с основной машины:"
|
||||
echo " scp user@main:~/.server-connections/servers.json $CONN_DIR/"
|
||||
fi
|
||||
|
||||
# settings.json
|
||||
if [ -n "$SRC_DIR" ] && [ -f "$SRC_DIR/settings.json" ]; then
|
||||
cp "$SRC_DIR/settings.json" "$CONN_DIR/settings.json"
|
||||
chmod 600 "$CONN_DIR/settings.json"
|
||||
ok "settings.json"
|
||||
elif [ ! -f "$CONN_DIR/settings.json" ]; then
|
||||
# Create minimal settings
|
||||
echo '{"language":"en","check_interval":60}' > "$CONN_DIR/settings.json"
|
||||
chmod 600 "$CONN_DIR/settings.json"
|
||||
ok "settings.json (создан по умолчанию)"
|
||||
fi
|
||||
|
||||
# ── Step 5: Verify ──
|
||||
step "5/5 Проверка установки"
|
||||
|
||||
ALL_OK=true
|
||||
|
||||
if [ -f "$CONN_DIR/ssh.py" ] && [ -x "$CONN_DIR/ssh.py" ]; then
|
||||
ok "ssh.py — исполняемый"
|
||||
else
|
||||
error "ssh.py — не найден или не исполняемый"
|
||||
ALL_OK=false
|
||||
fi
|
||||
|
||||
if [ -f "$CONN_DIR/encryption.py" ]; then
|
||||
ok "encryption.py"
|
||||
else
|
||||
error "encryption.py — не найден"
|
||||
ALL_OK=false
|
||||
fi
|
||||
|
||||
if [ -f "$COMMANDS_DIR/ssh.md" ]; then
|
||||
ok "ssh.md скилл"
|
||||
else
|
||||
warn "ssh.md скилл — не найден"
|
||||
fi
|
||||
|
||||
if [ -f "$CONN_DIR/servers.json" ]; then
|
||||
ok "servers.json"
|
||||
else
|
||||
warn "servers.json — отсутствует (нужно скопировать вручную)"
|
||||
fi
|
||||
|
||||
# Test ssh.py
|
||||
info "Тест ssh.py..."
|
||||
if $PYTHON "$CONN_DIR/ssh.py" --list &>/dev/null; then
|
||||
ok "ssh.py --list работает"
|
||||
else
|
||||
if [ ! -f "$CONN_DIR/servers.json" ]; then
|
||||
warn "ssh.py не может запуститься (нет servers.json)"
|
||||
if [[ "$INSTALL_ALL_USERS" -eq 1 ]]; then
|
||||
if [[ "$uname_s" == "Darwin" ]]; then
|
||||
[[ -d /var/root ]] && homes+=("/var/root")
|
||||
if [[ -d /Users ]]; then
|
||||
while IFS= read -r -d '' d; do homes+=("$d"); done < <(find /Users -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null)
|
||||
fi
|
||||
else
|
||||
[[ -d /root ]] && homes+=("/root")
|
||||
if [[ -d /home ]]; then
|
||||
while IFS= read -r -d '' d; do homes+=("$d"); done < <(find /home -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null)
|
||||
fi
|
||||
fi
|
||||
else
|
||||
warn "ssh.py вернул ошибку — проверьте зависимости"
|
||||
homes+=("$TARGET_HOME")
|
||||
fi
|
||||
|
||||
printf '%s\n' "${homes[@]}" | awk 'NF && !seen[$0]++'
|
||||
}
|
||||
|
||||
step "3/5 Подготовка директорий"
|
||||
TARGET_HOMES=()
|
||||
while IFS= read -r home; do
|
||||
[[ -n "$home" ]] || continue
|
||||
TARGET_HOMES+=("$home")
|
||||
ok "target home: $home"
|
||||
done < <(discover_homes)
|
||||
|
||||
if [[ "${#TARGET_HOMES[@]}" -eq 0 ]]; then
|
||||
error "Не удалось определить target home"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Summary ──
|
||||
step "4/5 Установка файлов"
|
||||
CODEX_SKILL_FILES=(
|
||||
"SKILL.md"
|
||||
"references/command-matrix.md"
|
||||
"references/project.md"
|
||||
"scripts/codex-ssh-wrapper.sh"
|
||||
"scripts/codex-ssh-wrapper.cmd"
|
||||
"scripts/server-manager-doctor.sh"
|
||||
"scripts/server-manager-doctor.cmd"
|
||||
)
|
||||
GEMINI_SKILL_FILES=(
|
||||
"SKILL.md"
|
||||
"references/command-matrix.md"
|
||||
"references/project.md"
|
||||
"scripts/gemini-ssh-wrapper.sh"
|
||||
"scripts/gemini-ssh-wrapper.cmd"
|
||||
"scripts/server-manager-gemini-doctor.sh"
|
||||
"scripts/server-manager-gemini-doctor.cmd"
|
||||
)
|
||||
|
||||
for HOME_DIR in "${TARGET_HOMES[@]}"; do
|
||||
CONN_DIR="$HOME_DIR/.server-connections"
|
||||
CLAUDE_DIR="$HOME_DIR/.claude"
|
||||
COMMANDS_DIR="$CLAUDE_DIR/commands"
|
||||
CODEX_DIR="$HOME_DIR/.codex/skills/server-manager"
|
||||
GEMINI_DIR="$HOME_DIR/.gemini"
|
||||
GEMINI_SKILL_DIR="$GEMINI_DIR/skills/server-manager"
|
||||
AGENTS_DIR="$HOME_DIR/.agents/skills/server-manager"
|
||||
|
||||
mkdir -p "$CONN_DIR" "$COMMANDS_DIR" "$CODEX_DIR" "$GEMINI_SKILL_DIR"
|
||||
chmod 700 "$CONN_DIR" 2>/dev/null || true
|
||||
|
||||
info "Устанавливаю в $HOME_DIR"
|
||||
|
||||
copy_or_download "tools/ssh.py" "$CONN_DIR/ssh.py" 755 "ssh.py"
|
||||
copy_or_download "core/encryption.py" "$CONN_DIR/encryption.py" 644 "encryption.py"
|
||||
copy_or_download "tools/skill-ssh.md" "$COMMANDS_DIR/ssh.md" 644 "ssh.md (скилл /ssh)"
|
||||
|
||||
if [[ -n "$SRC_DIR" && -f "$SRC_DIR/CLAUDE.md" ]]; then
|
||||
cp "$SRC_DIR/CLAUDE.md" "$CLAUDE_DIR/CLAUDE.md"
|
||||
chmod 644 "$CLAUDE_DIR/CLAUDE.md"
|
||||
ok "CLAUDE.md"
|
||||
elif [[ ! -f "$CLAUDE_DIR/CLAUDE.md" ]]; then
|
||||
copy_or_download "CLAUDE.md" "$CLAUDE_DIR/CLAUDE.md" 644 "CLAUDE.md" || true
|
||||
fi
|
||||
|
||||
if [[ -n "$SRC_DIR" && -f "$SRC_DIR/GEMINI.md" ]]; then
|
||||
cp "$SRC_DIR/GEMINI.md" "$GEMINI_DIR/GEMINI.md"
|
||||
chmod 644 "$GEMINI_DIR/GEMINI.md"
|
||||
ok "GEMINI.md"
|
||||
elif [[ ! -f "$GEMINI_DIR/GEMINI.md" ]]; then
|
||||
copy_or_download "GEMINI.md" "$GEMINI_DIR/GEMINI.md" 644 "GEMINI.md" || true
|
||||
fi
|
||||
|
||||
install_skill_tree ".codex/skills/server-manager" "$CODEX_DIR" "${CODEX_SKILL_FILES[@]}"
|
||||
install_skill_tree ".gemini/skills/server-manager" "$GEMINI_SKILL_DIR" "${GEMINI_SKILL_FILES[@]}"
|
||||
if [[ "$INSTALL_AGENTS_MIRROR" -eq 1 ]]; then
|
||||
mkdir -p "$AGENTS_DIR"
|
||||
install_skill_tree ".gemini/skills/server-manager" "$AGENTS_DIR" "${GEMINI_SKILL_FILES[@]}"
|
||||
ok "agents skill mirror"
|
||||
elif [[ -d "$AGENTS_DIR" ]]; then
|
||||
rm -rf "$AGENTS_DIR"
|
||||
ok "removed stale agents skill mirror to avoid Gemini conflict"
|
||||
fi
|
||||
|
||||
if [[ -f "$CODEX_DIR/scripts/codex-ssh-wrapper.sh" ]]; then
|
||||
cp "$CODEX_DIR/scripts/codex-ssh-wrapper.sh" "$CONN_DIR/codex-ssh"
|
||||
chmod 755 "$CONN_DIR/codex-ssh"
|
||||
ok "codex-ssh wrapper"
|
||||
else
|
||||
copy_or_download ".codex/skills/server-manager/scripts/codex-ssh-wrapper.sh" "$CONN_DIR/codex-ssh" 755 "codex-ssh wrapper" || true
|
||||
fi
|
||||
|
||||
if [[ -f "$GEMINI_SKILL_DIR/scripts/gemini-ssh-wrapper.sh" ]]; then
|
||||
cp "$GEMINI_SKILL_DIR/scripts/gemini-ssh-wrapper.sh" "$CONN_DIR/gemini-ssh"
|
||||
chmod 755 "$CONN_DIR/gemini-ssh"
|
||||
ok "gemini-ssh wrapper"
|
||||
else
|
||||
copy_or_download ".gemini/skills/server-manager/scripts/gemini-ssh-wrapper.sh" "$CONN_DIR/gemini-ssh" 755 "gemini-ssh wrapper" || true
|
||||
fi
|
||||
|
||||
if [[ "$INSTALL_ALL_USERS" -eq 0 ]]; then
|
||||
if [[ -n "$SRC_DIR" && -f "$SRC_DIR/servers.json" ]]; then
|
||||
cp "$SRC_DIR/servers.json" "$CONN_DIR/servers.json"
|
||||
chmod 600 "$CONN_DIR/servers.json"
|
||||
ok "servers.json (зашифрованный)"
|
||||
elif [[ ! -f "$CONN_DIR/servers.json" ]]; then
|
||||
warn "servers.json не найден для $HOME_DIR — скопируйте вручную"
|
||||
fi
|
||||
|
||||
if [[ -n "$SRC_DIR" && -f "$SRC_DIR/settings.json" ]]; then
|
||||
cp "$SRC_DIR/settings.json" "$CONN_DIR/settings.json"
|
||||
chmod 600 "$CONN_DIR/settings.json"
|
||||
ok "settings.json"
|
||||
elif [[ ! -f "$CONN_DIR/settings.json" ]]; then
|
||||
echo '{"language":"en","check_interval":60}' > "$CONN_DIR/settings.json"
|
||||
chmod 600 "$CONN_DIR/settings.json"
|
||||
ok "settings.json (создан по умолчанию)"
|
||||
fi
|
||||
else
|
||||
warn "all-users mode: servers.json/settings.json не копируются автоматически для $HOME_DIR"
|
||||
fi
|
||||
done
|
||||
|
||||
step "5/5 Проверка установки"
|
||||
ALL_OK=true
|
||||
for HOME_DIR in "${TARGET_HOMES[@]}"; do
|
||||
CONN_DIR="$HOME_DIR/.server-connections"
|
||||
COMMANDS_DIR="$HOME_DIR/.claude/commands"
|
||||
CODEX_DIR="$HOME_DIR/.codex/skills/server-manager"
|
||||
GEMINI_SKILL_DIR="$HOME_DIR/.gemini/skills/server-manager"
|
||||
|
||||
info "Проверка $HOME_DIR"
|
||||
|
||||
[[ -x "$CONN_DIR/ssh.py" ]] && ok "ssh.py — исполняемый" || { error "ssh.py — не найден или не исполняемый"; ALL_OK=false; }
|
||||
[[ -f "$CONN_DIR/encryption.py" ]] && ok "encryption.py" || { error "encryption.py — не найден"; ALL_OK=false; }
|
||||
[[ -f "$COMMANDS_DIR/ssh.md" ]] && ok "Claude /ssh skill" || warn "Claude /ssh skill — не найден"
|
||||
[[ -f "$CODEX_DIR/SKILL.md" ]] && ok "Codex skill" || { warn "Codex skill — не найден"; ALL_OK=false; }
|
||||
[[ -x "$CONN_DIR/codex-ssh" ]] && ok "codex-ssh wrapper" || { warn "codex-ssh wrapper — не найден"; ALL_OK=false; }
|
||||
[[ -f "$GEMINI_SKILL_DIR/SKILL.md" ]] && ok "Gemini skill" || { warn "Gemini skill — не найден"; ALL_OK=false; }
|
||||
[[ -x "$CONN_DIR/gemini-ssh" ]] && ok "gemini-ssh wrapper" || { warn "gemini-ssh wrapper — не найден"; ALL_OK=false; }
|
||||
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}━━━ Готово ━━━${NC}"
|
||||
echo ""
|
||||
@@ -260,17 +399,19 @@ if $ALL_OK; then
|
||||
else
|
||||
echo -e "${YELLOW}Установка завершена с предупреждениями.${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Файлы:"
|
||||
echo " $CONN_DIR/ssh.py — CLI-утилита"
|
||||
echo " $CONN_DIR/encryption.py — модуль шифрования"
|
||||
echo " $CONN_DIR/servers.json — серверы (зашифрованные)"
|
||||
echo " $COMMANDS_DIR/ssh.md — скилл /ssh для Claude Code"
|
||||
echo "Установлено для home:"
|
||||
printf ' - %s\n' "${TARGET_HOMES[@]}"
|
||||
echo ""
|
||||
echo "Использование:"
|
||||
echo " python3 ~/.server-connections/ssh.py --list"
|
||||
echo " python3 ~/.server-connections/ssh.py --info ALIAS"
|
||||
echo " python3 ~/.server-connections/ssh.py ALIAS \"command\""
|
||||
echo ""
|
||||
echo "Claude Code скилл: /ssh"
|
||||
echo " ~/.server-connections/codex-ssh --list"
|
||||
echo " ~/.server-connections/gemini-ssh --list"
|
||||
echo ""
|
||||
echo "Claude skill: ~/.claude/commands/ssh.md"
|
||||
echo "Codex skill: ~/.codex/skills/server-manager/"
|
||||
echo "Gemini skill: ~/.gemini/skills/server-manager/"
|
||||
if [[ "$INSTALL_AGENTS_MIRROR" -eq 1 ]]; then
|
||||
echo "Mirror skill: ~/.agents/skills/server-manager/"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user