diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..c80aea1 --- /dev/null +++ b/.env.example @@ -0,0 +1,18 @@ +# HangmanLab.Server.T2 — Plugin Configuration +# Copy to .env and fill in values. This file is gitignored. + +# ── OpenClaw ───────────────────────────────────────────────────────────────── +OPENCLAW_PATH=~/.openclaw + +# ── HarborForge Plugin ─────────────────────────────────────────────────────── +HF_BACKEND_URL=https://hf-api.hangman-lab.top +HF_API_KEY= +HF_MONITOR_IDENTIFIER= + +# ── Yonexus.Server ─────────────────────────────────────────────────────────── +YONEXUS_PORT=18900 +YONEXUS_SECRET= + +# ── ContractorAgent ────────────────────────────────────────────────────────── +CONTRACTOR_BRIDGE_PORT=18800 +CONTRACTOR_BRIDGE_API_KEY= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env diff --git a/scripts/setup-plugins.sh b/scripts/setup-plugins.sh new file mode 100755 index 0000000..5f5389e --- /dev/null +++ b/scripts/setup-plugins.sh @@ -0,0 +1,211 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +PLUGINS_DIR="$PROJECT_ROOT/plugins" +ENV_FILE="$PROJECT_ROOT/.env" + +# ── Helpers ────────────────────────────────────────────────────────────────── + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[0;33m' +CYAN='\033[0;36m'; NC='\033[0m' + +log() { printf "${CYAN}[setup]${NC} %s\n" "$*"; } +ok() { printf "${GREEN} ✓${NC} %s\n" "$*"; } +warn() { printf "${YELLOW} ⚠${NC} %s\n" "$*"; } +err() { printf "${RED} ✗${NC} %s\n" "$*"; } + +usage() { + cat < Path to .env file (default: /.env) + --list List available plugins and exit + --uninstall Uninstall plugins instead of installing + --skip-deps Skip npm install step + -h, --help Show this help + +Plugins: PaddedCell, ContractorAgent, Dirigent, HarborForge.OpenclawPlugin, Yonexus.Server + +Sensitive configuration is read from .env (see .env.example for reference). +EOF + exit 0 +} + +# ── Parse args ─────────────────────────────────────────────────────────────── + +UNINSTALL=false +SKIP_DEPS=false +SELECTED_PLUGINS=() + +while [[ $# -gt 0 ]]; do + case "$1" in + --env) ENV_FILE="$2"; shift 2 ;; + --list) ls "$PLUGINS_DIR"; exit 0 ;; + --uninstall) UNINSTALL=true; shift ;; + --skip-deps) SKIP_DEPS=true; shift ;; + -h|--help) usage ;; + -*) err "Unknown option: $1"; usage ;; + *) SELECTED_PLUGINS+=("$1"); shift ;; + esac +done + +# ── Load .env ──────────────────────────────────────────────────────────────── + +if [[ -f "$ENV_FILE" ]]; then + log "Loading env from $ENV_FILE" + set -a + # shellcheck disable=SC1090 + source "$ENV_FILE" + set +a + ok "Loaded $(grep -cE '^[A-Z_]+=' "$ENV_FILE") variables" +else + warn "No .env file found at $ENV_FILE — proceeding with current environment" +fi + +# ── Submodules ─────────────────────────────────────────────────────────────── + +log "Ensuring git submodules are initialised" +cd "$PROJECT_ROOT" +git submodule update --init --recursive +ok "Submodules ready" + +# ── Resolve plugin list ───────────────────────────────────────────────────── + +ALL_PLUGINS=() +for d in "$PLUGINS_DIR"/*/; do + ALL_PLUGINS+=("$(basename "$d")") +done + +if [[ ${#SELECTED_PLUGINS[@]} -eq 0 ]]; then + SELECTED_PLUGINS=("${ALL_PLUGINS[@]}") +fi + +for p in "${SELECTED_PLUGINS[@]}"; do + if [[ ! -d "$PLUGINS_DIR/$p" ]]; then + err "Plugin not found: $p" + exit 1 + fi +done + +# ── Per-plugin install/uninstall ───────────────────────────────────────────── + +install_deps() { + local dir="$1" + if [[ "$SKIP_DEPS" == true ]]; then return; fi + if [[ -f "$dir/package.json" ]]; then + log " npm install (root)" + (cd "$dir" && npm install --no-audit --no-fund 2>&1 | tail -3) + fi + if [[ -f "$dir/plugin/package.json" ]]; then + log " npm install (plugin/)" + (cd "$dir/plugin" && npm install --no-audit --no-fund 2>&1 | tail -3) + fi +} + +find_install_script() { + local dir="$1" + for candidate in \ + "$dir/scripts/install.mjs" \ + "$dir/install.mjs" \ + "$dir/scripts/install.js" \ + "$dir/install.js"; do + if [[ -f "$candidate" ]]; then + echo "$candidate" + return + fi + done +} + +run_plugin() { + local name="$1" + local dir="$PLUGINS_DIR/$name" + local action="install" + [[ "$UNINSTALL" == true ]] && action="uninstall" + + echo + log "[$action] $name" + + if [[ "$UNINSTALL" == true ]]; then + local script + script=$(find_install_script "$dir") + if [[ -n "$script" ]]; then + node "$script" --uninstall || warn "$name uninstall script returned non-zero" + else + warn "No install script found for $name — skipping" + fi + return + fi + + # Install dependencies + install_deps "$dir" + + # Plugin-specific env setup (pass through to install scripts via env) + case "$name" in + HarborForge.OpenclawPlugin) + export HF_BACKEND_URL="${HF_BACKEND_URL:-}" + export HF_API_KEY="${HF_API_KEY:-}" + export HF_MONITOR_IDENTIFIER="${HF_MONITOR_IDENTIFIER:-}" + ;; + Yonexus.Server) + export YONEXUS_PORT="${YONEXUS_PORT:-}" + export YONEXUS_SECRET="${YONEXUS_SECRET:-}" + ;; + ContractorAgent) + export CONTRACTOR_BRIDGE_PORT="${CONTRACTOR_BRIDGE_PORT:-18800}" + export CONTRACTOR_BRIDGE_API_KEY="${CONTRACTOR_BRIDGE_API_KEY:-}" + ;; + esac + + # Run install script + local script + script=$(find_install_script "$dir") + if [[ -n "$script" ]]; then + log " Running $(basename "$script")" + node "$script" --install || { + err "$name install script failed" + return 1 + } + ok "$name installed" + else + # Some plugins (like PaddedCell) may have a different layout + if [[ -f "$dir/package.json" ]] && grep -q '"install-plugin"' "$dir/package.json" 2>/dev/null; then + log " Running npm run install-plugin" + (cd "$dir" && npm run install-plugin) || { + err "$name install failed" + return 1 + } + ok "$name installed" + elif [[ -f "$dir/package.json" ]] && grep -q '"postinstall"' "$dir/package.json" 2>/dev/null; then + log " Running npm run postinstall" + (cd "$dir" && npm run postinstall) || { + err "$name install failed" + return 1 + } + ok "$name installed" + else + warn "No install script found for $name — submodule present but not auto-installable" + fi + fi +} + +# ── Main ───────────────────────────────────────────────────────────────────── + +FAILURES=0 + +for plugin in "${SELECTED_PLUGINS[@]}"; do + run_plugin "$plugin" || ((FAILURES++)) || true +done + +echo +if [[ $FAILURES -gt 0 ]]; then + err "$FAILURES plugin(s) had errors" + exit 1 +else + ok "All plugins processed successfully" +fi