feat(09-tooling-portable-setup-02): create setup script skeleton with args, prereqs, and interactive prompts

- Script header with D-06 through D-10 references and snapshot date
- getopts argument parsing with defaults for all 9 parameters
- Usage display with -h flag
- Prerequisite checks (Hermes CLI, Docker, SSH keys, repo paths)
- prompt_secret function for masked input (T-09-05 mitigation)
- Directory creation for scripts, hindsight, skills, archive
- Backup existing config.yaml before modification (T-09-07 mitigation)
This commit is contained in:
2026-06-15 23:26:34 +08:00
parent 7ea639567d
commit 2de51b119c

210
ngn-agent/setup-ngn-agent.sh Executable file
View File

@@ -0,0 +1,210 @@
#!/bin/bash
# setup-ngn-agent.sh — Portable ngn-agent configuration setup
#
# Phase 9, Plan 2 — recreates all configuration on a fresh macOS machine
# Assumes Hermes v0.16+ is installed (per D-07)
#
# Embedded file snapshots frozen at: 2026-06-15
# Regenerate by re-running this phase.
#
# D-06: Single script recreating all ngn-agent configuration
# D-07: Requires Hermes v0.16+ on PATH
# D-08: Interactive secrets: JIRA_API_TOKEN, JIRA_EMAIL, TELEGRAM_BOT_TOKEN, OPENROUTER_API_KEY
# D-09: Configurable paths via arguments (SSH keys, repos, timezone)
# D-10: Creates/updates: config.yaml, .env, hindsight/config.json, scripts, skills, cron jobs
#
set -euo pipefail
# ---- Usage ----
usage() {
cat <<'USAGE'
usage: setup-ngn-agent.sh [OPTIONS]
Portable ngn-agent configuration setup for macOS + Hermes v0.16+
Options:
-s1, --ssh-key-1 PATH SSH private key path 1 (default: ~/.ssh/id_ed25519razer)
-s2, --ssh-key-2 PATH SSH private key path 2 (default: ~/.ssh/id_rsa)
-sc, --ssh-config PATH SSH config path (default: ~/.ssh/config)
-sh, --ssh-known-hosts PATH SSH known_hosts path (default: ~/.ssh/known_hosts)
-r1, --repo-ops PATH rai-ops repo path (default: ~/Razer/rai-ops)
-r2, --repo-deploy PATH rai-deployment repo path (default: ~/Razer/rai-deployment)
-r3, --repo-devtools PATH rai-devtools repo path (default: ~/Razer/rai-devtools)
-t, --timezone ZONE Timezone (default: Asia/Singapore)
-d, --docker-image TAG Docker image tag (default: ngn-agent:latest)
-y, --yes Non-interactive mode (skip prompts, use env vars)
-h, --help Show this help message
Secrets are prompted interactively with masked input unless -y is set,
in which case they are read from environment variables.
USAGE
}
# ---- Argument defaults ----
SSH_KEY_1="${SSH_KEY_1:-$HOME/.ssh/id_ed25519razer}"
SSH_KEY_2="${SSH_KEY_2:-$HOME/.ssh/id_rsa}"
SSH_CONFIG="${SSH_CONFIG:-$HOME/.ssh/config}"
SSH_KNOWN_HOSTS="${SSH_KNOWN_HOSTS:-$HOME/.ssh/known_hosts}"
REPO_OPS="${REPO_OPS:-$HOME/Razer/rai-ops}"
REPO_DEPLOY="${REPO_DEPLOY:-$HOME/Razer/rai-deployment}"
REPO_DEVTOOLS="${REPO_DEVTOOLS:-$HOME/Razer/rai-devtools}"
TIMEZONE="${TIMEZONE:-Asia/Singapore}"
DOCKER_IMAGE="${DOCKER_IMAGE:-ngn-agent:latest}"
NONINTERACTIVE=false
# ---- Argument parsing (per D-09) ----
while [[ $# -gt 0 ]]; do
case "$1" in
-s1|--ssh-key-1)
SSH_KEY_1="$2"; shift 2 ;;
-s2|--ssh-key-2)
SSH_KEY_2="$2"; shift 2 ;;
-sc|--ssh-config)
SSH_CONFIG="$2"; shift 2 ;;
-sh|--ssh-known-hosts)
SSH_KNOWN_HOSTS="$2"; shift 2 ;;
-r1|--repo-ops)
REPO_OPS="$2"; shift 2 ;;
-r2|--repo-deploy)
REPO_DEPLOY="$2"; shift 2 ;;
-r3|--repo-devtools)
REPO_DEVTOOLS="$2"; shift 2 ;;
-t|--timezone)
TIMEZONE="$2"; shift 2 ;;
-d|--docker-image)
DOCKER_IMAGE="$2"; shift 2 ;;
-y|--yes)
NONINTERACTIVE=true; shift ;;
-h|--help)
usage; exit 0 ;;
*)
echo "Unknown option: $1"
usage; exit 1 ;;
esac
done
# ---- Interactive secret prompt (per D-08) ----
# T-09-05 mitigation: read -s for masked input, no echo to terminal
prompt_secret() {
local var_name="$1"
local prompt_text="$2"
local is_optional="${3:-false}"
local val=""
# If env var is already set (e.g., user exported it), skip prompt
if [ -n "${!var_name:-}" ]; then
echo "${var_name} already set (using environment value)"
echo "${!var_name}"
return
fi
while [ -z "$val" ]; do
read -s -p "${prompt_text}" val
echo
if [ -z "$val" ] && [ "$is_optional" = "true" ]; then
# Optional and empty — return empty string
echo ""
return
elif [ -z "$val" ]; then
echo " ⚠ Value cannot be empty. Press Ctrl+C to cancel."
fi
done
echo "$val"
}
# ---- Prerequisite checks ----
check_prerequisites() {
echo " → Checking prerequisites..."
# 1. Hermes CLI installed (per D-07)
if ! command -v hermes >/dev/null 2>&1; then
echo " ERROR: Hermes CLI not found — install v0.16+ first."
echo " See: https://github.com/nousresearch/hermes"
exit 1
fi
echo " ✓ Hermes CLI found: $(hermes --version 2>/dev/null || echo 'unknown version')"
# 2. Docker running
if ! docker info >/dev/null 2>&1; then
echo " ERROR: Docker is not running."
echo " Start Docker Desktop or Orbstack first."
exit 1
fi
echo " ✓ Docker is running"
# 3. SSH key files exist
if [ ! -f "$SSH_KEY_1" ]; then
echo " ⚠ SSH key not found: ${SSH_KEY_1}"
else
echo " ✓ SSH key 1: ${SSH_KEY_1}"
fi
if [ ! -f "$SSH_KEY_2" ]; then
echo " ⚠ SSH key not found: ${SSH_KEY_2}"
else
echo " ✓ SSH key 2: ${SSH_KEY_2}"
fi
if [ ! -f "$SSH_CONFIG" ]; then
echo " ⚠ SSH config not found: ${SSH_CONFIG}"
else
echo " ✓ SSH config: ${SSH_CONFIG}"
fi
if [ ! -f "$SSH_KNOWN_HOSTS" ]; then
echo " ⚠ SSH known_hosts not found: ${SSH_KNOWN_HOSTS}"
else
echo " ✓ SSH known_hosts: ${SSH_KNOWN_HOSTS}"
fi
# 4. Repo paths exist
if [ ! -d "$REPO_OPS" ]; then
echo " ⚠ Repo not found: ${REPO_OPS}"
else
echo " ✓ Repo (ops): ${REPO_OPS}"
fi
if [ ! -d "$REPO_DEPLOY" ]; then
echo " ⚠ Repo not found: ${REPO_DEPLOY}"
else
echo " ✓ Repo (deploy): ${REPO_DEPLOY}"
fi
if [ ! -d "$REPO_DEVTOOLS" ]; then
echo " ⚠ Repo not found: ${REPO_DEVTOOLS}"
else
echo " ✓ Repo (devtools): ${REPO_DEVTOOLS}"
fi
}
# ---- Print path summary ----
print_summary() {
echo ""
echo " Configuration paths:"
echo " SSH key 1: ${SSH_KEY_1}"
echo " SSH key 2: ${SSH_KEY_2}"
echo " SSH config: ${SSH_CONFIG}"
echo " SSH known_hosts: ${SSH_KNOWN_HOSTS}"
echo " Repo (ops): ${REPO_OPS}"
echo " Repo (deploy): ${REPO_DEPLOY}"
echo " Repo (devtools): ${REPO_DEVTOOLS}"
echo " Timezone: ${TIMEZONE}"
echo " Docker image: ${DOCKER_IMAGE}"
echo ""
}
# ---- Create config directories ----
create_directories() {
echo " → Creating config directories..."
mkdir -p "$HOME/.hermes/scripts"
mkdir -p "$HOME/.hermes/hindsight"
mkdir -p "$HOME/.hermes/skills/ngn-agent"
mkdir -p "$HOME/.hermes/archive/sessions"
echo " ✓ Directories created"
}
# ---- Backup existing config (per Anti-Pattern 4, T-09-07 mitigation) ----
backup_config() {
if [ -f "$HOME/.hermes/config.yaml" ]; then
local bak_file="$HOME/.hermes/config.yaml.bak.$(date +%Y%m%d_%H%M%S)"
cp "$HOME/.hermes/config.yaml" "$bak_file"
echo " ✓ Backed up config.yaml → $(basename ${bak_file})"
else
echo " → No existing config.yaml to backup"
fi
}