Files
HangmanLab.Server.T2/scripts/setup-plugins.sh
orion 601262abec refactor: setup-plugins writes config via openclaw config set
Instead of just exporting env vars (which are ephemeral), the script
now calls 'openclaw config set' to persist each plugin's sensitive
fields (apiKey, tokens, ports, etc.) under plugins.entries.<id>.config.

Config key mapping per plugin is derived from each plugin's
openclaw.plugin.json configSchema. .env.example updated with all
available variables and their corresponding plugin IDs.
2026-04-16 08:02:54 +00:00

270 lines
8.6 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
}
run_install_script() {
local name="$1" dir="$2" action="$3"
local script
script=$(find_install_script "$dir")
if [[ -n "$script" ]]; then
log " Running $(basename "$script") --$action"
node "$script" "--$action" || {
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