Files
HangmanLab.Server.T2/scripts/setup-plugins.sh
orion 0166a92f93 setup-plugins: pass per-plugin install flags
HarborForge gets --install-cli, --install-monitor yes, and
--openclaw-profile-path. PaddedCell, Dirigent, Yonexus get
--openclaw-profile-path. ContractorAgent unchanged (no support).
2026-04-16 09:40:01 +00:00

288 lines
9.0 KiB
Bash
Executable File

#!/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" "$*"; }
oc_set_str() {
local key="$1" val="$2"
if [[ -n "$val" ]]; then
openclaw config set "$key" "\"$val\"" --json
ok "config: $key"
fi
}
oc_set_num() {
local key="$1" val="$2"
if [[ -n "$val" ]]; then
openclaw config set "$key" "$val" --json
ok "config: $key = $val"
fi
}
oc_set_bool() {
local key="$1" val="$2"
if [[ -n "$val" ]]; then
openclaw config set "$key" "$val" --json
ok "config: $key = $val"
fi
}
usage() {
cat <<EOF
Usage: $(basename "$0") [options] [plugin ...]
Install and configure OpenClaw plugins from the plugins/ directory.
If no plugin names are given, all plugins are processed.
Options:
--env <path> Path to .env file (default: <project-root>/.env)
--list List available plugins and exit
--uninstall Uninstall plugins instead of installing
--skip-deps Skip npm install step
--skip-config Skip openclaw config set 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)
and written to openclaw config via 'openclaw config set'.
EOF
exit 0
}
# ── Parse args ───────────────────────────────────────────────────────────────
UNINSTALL=false
SKIP_DEPS=false
SKIP_CONFIG=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 ;;
--skip-config) SKIP_CONFIG=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 helpers ───────────────────────────────────────────────────────
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
}
plugin_install_args() {
local name="$1" action="$2"
local args=("--$action")
if [[ "$action" == "install" ]]; then
case "$name" in
HarborForge.OpenclawPlugin)
args+=(--install-cli --install-monitor yes --openclaw-profile-path ~/.openclaw)
;;
PaddedCell|Dirigent|Yonexus.Server)
args+=(--openclaw-profile-path ~/.openclaw)
;;
esac
fi
echo "${args[@]}"
}
run_install_script() {
local name="$1" dir="$2" action="$3"
local script
script=$(find_install_script "$dir")
if [[ -n "$script" ]]; then
local -a args
read -ra args <<< "$(plugin_install_args "$name" "$action")"
log " Running $(basename "$script") ${args[*]}"
node "$script" "${args[@]}" || {
err "$name script failed"
return 1
}
return 0
fi
if [[ "$action" == "install" ]] && [[ -f "$dir/package.json" ]]; then
if grep -q "\"install-plugin\"" "$dir/package.json" 2>/dev/null; then
log " Running npm run install-plugin"
(cd "$dir" && npm run install-plugin)
return $?
elif grep -q "\"postinstall\"" "$dir/package.json" 2>/dev/null; then
log " Running npm run postinstall"
(cd "$dir" && npm run postinstall)
return $?
fi
fi
warn "No install script found for $name"
return 0
}
# ── Plugin config mapping ────────────────────────────────────────────────────
# Maps .env variables → openclaw config keys via 'openclaw config set'.
# Each plugin's config lives under plugins.entries.<id>.config.*
configure_plugin() {
local name="$1"
if [[ "$SKIP_CONFIG" == true ]]; then return; fi
log " Writing openclaw config"
case "$name" in
HarborForge.OpenclawPlugin)
local P="plugins.entries.harbor-forge.config"
oc_set_str "$P.backendUrl" "${HF_BACKEND_URL:-}"
oc_set_str "$P.apiKey" "${HF_API_KEY:-}"
oc_set_str "$P.identifier" "${HF_MONITOR_IDENTIFIER:-}"
oc_set_num "$P.monitor_port" "${HF_MONITOR_PORT:-}"
oc_set_num "$P.reportIntervalSec" "${HF_REPORT_INTERVAL:-}"
oc_set_str "$P.calendarApiKey" "${HF_CALENDAR_API_KEY:-}"
oc_set_bool "$P.enabled" "true"
;;
ContractorAgent)
local P="plugins.entries.contractor-agent.config"
oc_set_num "$P.bridgePort" "${CONTRACTOR_BRIDGE_PORT:-}"
oc_set_str "$P.bridgeApiKey" "${CONTRACTOR_BRIDGE_API_KEY:-}"
;;
Dirigent)
local P="plugins.entries.dirigent.config"
oc_set_str "$P.moderatorBotToken" "${DIRIGENT_MODERATOR_BOT_TOKEN:-}"
oc_set_num "$P.sideCarPort" "${DIRIGENT_SIDECAR_PORT:-}"
oc_set_str "$P.noReplyProvider" "${DIRIGENT_NO_REPLY_PROVIDER:-}"
oc_set_str "$P.noReplyModel" "${DIRIGENT_NO_REPLY_MODEL:-}"
oc_set_str "$P.scheduleIdentifier" "${DIRIGENT_SCHEDULE_IDENTIFIER:-}"
;;
PaddedCell)
local P="plugins.entries.padded-cell.config"
oc_set_str "$P.secretMgrPath" "${PADDEDCELL_SECRET_MGR_PATH:-}"
oc_set_str "$P.openclawProfilePath" "${PADDEDCELL_OPENCLAW_PATH:-}"
oc_set_bool "$P.enabled" "true"
;;
Yonexus.Server)
local P="plugins.entries.yonexus-server.config"
oc_set_str "$P.notifyBotToken" "${YONEXUS_NOTIFY_BOT_TOKEN:-}"
oc_set_str "$P.adminUserId" "${YONEXUS_ADMIN_USER_ID:-}"
oc_set_num "$P.listenPort" "${YONEXUS_PORT:-}"
oc_set_str "$P.publicWsUrl" "${YONEXUS_PUBLIC_WS_URL:-}"
;;
*)
warn "No config mapping for $name"
;;
esac
}
# ── Main loop ────────────────────────────────────────────────────────────────
run_plugin() {
local name="$1"
local dir="$PLUGINS_DIR/$name"
echo
if [[ "$UNINSTALL" == true ]]; then
log "[uninstall] $name"
run_install_script "$name" "$dir" "uninstall"
return $?
fi
log "[install] $name"
install_deps "$dir"
run_install_script "$name" "$dir" "install" || return $?
configure_plugin "$name"
ok "$name done"
}
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