9e70e1bf23
- themes/midnight-orchid.json: custom dark purple theme. Deep aubergine background (#1a1525), violet/magenta accents, sage/amber/cyan syntax for contrast. All 51 required color tokens defined. - scripts/install-client.sh: adds a themes sync step that rsync's the repo's themes/ into ~/.pi/agent/themes/ on each new machine. Activate with /settings in pi or by setting "theme": "midnight-orchid" in ~/.pi/agent/settings.json. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
129 lines
4.7 KiB
Bash
Executable File
129 lines
4.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# install-client.sh — Bootstrap a pi client machine for the ai-server extension.
|
|
#
|
|
# Run this from a checked-out copy of the pi-extensions repo. Idempotent:
|
|
# re-running only overwrites targets with --force.
|
|
#
|
|
# What it does:
|
|
# 1. Creates ~/.pi/agent/{certs,extensions} if missing.
|
|
# 2. Fetches client.crt, client.key, and root-ca.pem from the Caddy host
|
|
# via scp, installing them at the canonical paths the extension expects.
|
|
# 3. Rsync's this repo's ai-server/ and local-llama.ts into the extensions dir.
|
|
# 4. Ensures key-auth to the ai-server SSH target so admin slash commands work.
|
|
# 5. Probes the mTLS endpoint to confirm the full chain works.
|
|
#
|
|
# Usage:
|
|
# scripts/install-client.sh [options]
|
|
# --caddy-host USER@HOST default: shahondin1624@192.168.2.2
|
|
# --caddy-cert-dir PATH default: /mnt/ssdpool/@docker/caddy/certs
|
|
# --ai-server-host USER@HOST default: ai-server@192.168.2.3
|
|
# --pi-dir PATH default: $HOME/.pi/agent
|
|
# --no-ssh-setup skip ssh-copy-id to ai-server
|
|
# --no-certs skip cert fetch (useful if already present)
|
|
# --force overwrite existing cert files
|
|
# --help show this message and exit
|
|
|
|
set -euo pipefail
|
|
|
|
CADDY_HOST="${CADDY_HOST:-shahondin1624@192.168.2.2}"
|
|
CADDY_CERT_DIR="${CADDY_CERT_DIR:-/mnt/ssdpool/@docker/caddy/certs}"
|
|
AI_SERVER_HOST="${AI_SERVER_HOST:-ai-server@192.168.2.3}"
|
|
AI_SERVER_URL="${AI_SERVER_URL:-https://ai.shahondin1624.de}"
|
|
PI_DIR="${PI_DIR:-$HOME/.pi/agent}"
|
|
DO_SSH_SETUP=1
|
|
DO_CERTS=1
|
|
FORCE=0
|
|
|
|
usage() { sed -n '2,/^$/p' "$0" | sed 's/^#\{0,1\} \{0,1\}//'; exit 0; }
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--caddy-host) CADDY_HOST="$2"; shift 2 ;;
|
|
--caddy-cert-dir) CADDY_CERT_DIR="$2"; shift 2 ;;
|
|
--ai-server-host) AI_SERVER_HOST="$2"; shift 2 ;;
|
|
--pi-dir) PI_DIR="$2"; shift 2 ;;
|
|
--no-ssh-setup) DO_SSH_SETUP=0; shift ;;
|
|
--no-certs) DO_CERTS=0; shift ;;
|
|
--force) FORCE=1; shift ;;
|
|
-h|--help) usage ;;
|
|
*) echo "Unknown arg: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$REPO_ROOT"
|
|
|
|
[[ -d ai-server ]] || { echo "Run from a checked-out pi-extensions repo (ai-server/ not found)" >&2; exit 1; }
|
|
|
|
echo "==> pi-extensions client bootstrap"
|
|
echo " repo root: $REPO_ROOT"
|
|
echo " pi dir: $PI_DIR"
|
|
echo " caddy host: $CADDY_HOST"
|
|
echo " ai-server: $AI_SERVER_HOST"
|
|
|
|
mkdir -p "$PI_DIR/certs" "$PI_DIR/extensions"
|
|
chmod 700 "$PI_DIR/certs"
|
|
|
|
# 1) mTLS certs
|
|
if [[ $DO_CERTS -eq 1 ]]; then
|
|
echo; echo "==> Installing mTLS certs"
|
|
for pair in "client.crt:client.pem" "client.key:client-key.pem" "root-ca.pem:root-ca.pem"; do
|
|
src="${pair%%:*}"; dst="${pair##*:}"
|
|
target="$PI_DIR/certs/$dst"
|
|
if [[ -f "$target" && $FORCE -eq 0 ]]; then
|
|
echo " $dst already exists, skipping (--force to overwrite)"
|
|
continue
|
|
fi
|
|
echo " scp $CADDY_HOST:$CADDY_CERT_DIR/$src -> $target"
|
|
scp -q "$CADDY_HOST:$CADDY_CERT_DIR/$src" "$target"
|
|
done
|
|
chmod 600 "$PI_DIR"/certs/*.pem 2>/dev/null || true
|
|
fi
|
|
|
|
# 2) Extensions
|
|
echo; echo "==> Syncing extensions into $PI_DIR/extensions"
|
|
rsync -a --exclude='.git' ai-server "$PI_DIR/extensions/"
|
|
[[ -f local-llama.ts ]] && rsync -a local-llama.ts "$PI_DIR/extensions/"
|
|
echo " synced ai-server/ and local-llama.ts"
|
|
|
|
# 2b) Themes (if present in the repo)
|
|
if [[ -d themes ]]; then
|
|
echo; echo "==> Syncing themes into $PI_DIR/themes"
|
|
mkdir -p "$PI_DIR/themes"
|
|
rsync -a themes/ "$PI_DIR/themes/"
|
|
echo " $(ls themes/*.json 2>/dev/null | wc -l) theme file(s) installed"
|
|
fi
|
|
|
|
# 3) SSH key-auth to the ai-server host (for admin commands)
|
|
if [[ $DO_SSH_SETUP -eq 1 ]]; then
|
|
echo; echo "==> Checking SSH key-auth to $AI_SERVER_HOST"
|
|
if ssh -o BatchMode=yes -o ConnectTimeout=5 "$AI_SERVER_HOST" 'true' 2>/dev/null; then
|
|
echo " already have key-auth"
|
|
else
|
|
echo " no key-auth; attempting ssh-copy-id (will prompt for password)"
|
|
if [[ ! -f "$HOME/.ssh/id_ed25519.pub" && ! -f "$HOME/.ssh/id_rsa.pub" ]]; then
|
|
echo " no ssh key found locally; generating ed25519"
|
|
ssh-keygen -t ed25519 -N '' -f "$HOME/.ssh/id_ed25519"
|
|
fi
|
|
ssh-copy-id "$AI_SERVER_HOST"
|
|
fi
|
|
fi
|
|
|
|
# 4) Sanity check — mTLS handshake + router health
|
|
echo; echo "==> Verifying mTLS reachability"
|
|
if curl -sS --max-time 5 \
|
|
--cert "$PI_DIR/certs/client.pem" \
|
|
--key "$PI_DIR/certs/client-key.pem" \
|
|
--cacert "$PI_DIR/certs/root-ca.pem" \
|
|
"$AI_SERVER_URL/health" | grep -q '"ok"'; then
|
|
echo " ✓ mTLS handshake + /health OK"
|
|
else
|
|
echo " ✗ could not verify — check cert files and server reachability"
|
|
exit 1
|
|
fi
|
|
|
|
echo
|
|
echo "==> Done."
|
|
echo " Next: open pi and run /reload, then /ai-server-status to confirm."
|