wbxcallprov/docker/remote-agent/deploy/install.sh
jmcqueen 93b060bc8b Modernize bot to Node.js 22 with modular architecture and remote SIW agent
- Refactor monolithic index.js (2646 lines) into src/{webex,integrations,
  cards,flows,commands,services} modules; replace node-fetch/form-data
  with native fetch/FormData; move all secrets to .env via dotenv
- Add dockerized remote SIW agent (docker/remote-agent/) with cross-arch
  buildx packaging (arm64 Mac -> linux/amd64), idempotent install.sh
  deploy bundle, and docker-free ZIP inspector for arch verification
- Bot hosts a WebSocket server; agent proxies SIW requests with a
  per-request insecure:true flag, replacing the process-wide
  NODE_TLS_REJECT_UNAUTHORIZED bypass
- Add ESLint flat config + Prettier, rewrite Dockerfile as non-root
  multi-stage node:22-alpine build, README covering setup / deploy /
  remote agent workflow
- Fix parseStoreArg to read trigger.prompt correctly (was indexing past
  the framework's post-match slice); register /help as regex (string
  matcher only compares the first token); switch catch-all to /.+/
  (previous /.*/gim was stateful due to the g flag); remove
  /fixDisplayNames command and its flow/card

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 14:48:51 -04:00

138 lines
5.3 KiB
Bash
Executable file

#!/usr/bin/env bash
#
# wbxStoreProvision Remote Agent — install / (re)start on a remote host.
#
# Run this after extracting the deploy ZIP:
# unzip wbxprov-remote-agent-<version>.zip
# cd wbxprov-remote-agent-<version>
# ./install.sh
#
# On first run: verifies the image tarball, loads it into Docker, and drops
# a starter .env so you can fill in WS_URL / WS_TOKEN. Re-runs are safe —
# the script is idempotent.
set -euo pipefail
# Move to the script's own directory so relative paths work regardless of
# where the user invoked it from.
cd "$(dirname "$0")"
RED=$'\033[0;31m'
GRN=$'\033[0;32m'
YLW=$'\033[1;33m'
RST=$'\033[0m'
log() { printf '%s[install]%s %s\n' "$GRN" "$RST" "$*"; }
warn() { printf '%s[install]%s %s\n' "$YLW" "$RST" "$*"; }
die() { printf '%s[install]%s %s\n' "$RED" "$RST" "$*" >&2; exit 1; }
# --- 1. Preflight ----------------------------------------------------------
command -v docker >/dev/null 2>&1 || die "Docker not found on PATH."
docker info >/dev/null 2>&1 \
|| die "Cannot talk to the Docker daemon. Is it running / do you have permission?"
# Detect either `docker compose` (v2 plugin) or the legacy `docker-compose`.
if docker compose version >/dev/null 2>&1; then
COMPOSE=(docker compose)
elif command -v docker-compose >/dev/null 2>&1; then
COMPOSE=(docker-compose)
else
die "Neither 'docker compose' nor 'docker-compose' is available. Install Docker Compose."
fi
[[ -f VERSION ]] || die "VERSION file missing from bundle — is this a valid deploy ZIP?"
VERSION="$(cat VERSION)"
IMAGE_TARBALL="wbxprov-remote-agent-${VERSION}.tar.gz"
[[ -f "$IMAGE_TARBALL" ]] || die "Image tarball not found: $IMAGE_TARBALL"
# --- 2. Verify checksum (optional; skip gracefully if no shasum tool) -----
if [[ -f SHA256SUMS ]]; then
if command -v sha256sum >/dev/null 2>&1; then
log "Verifying SHA256 checksum..."
sha256sum -c SHA256SUMS >/dev/null || die "Checksum verification FAILED."
elif command -v shasum >/dev/null 2>&1; then
log "Verifying SHA256 checksum (macOS shasum)..."
shasum -a 256 -c SHA256SUMS >/dev/null || die "Checksum verification FAILED."
else
warn "No sha256sum/shasum tool found — skipping integrity check."
fi
log "Checksum OK."
else
warn "No SHA256SUMS file in bundle — skipping integrity check."
fi
# --- 3. Load the image -----------------------------------------------------
# ALWAYS docker load, unconditionally. `docker load` reassigns the tag to
# whatever's in the tarball and is a fast no-op when the layers are already
# present, so this is safe to re-run. We intentionally do NOT "skip if the
# tag already exists" — a stale image left over from a previous wrong-arch
# attempt has this exact tag, and skipping the load would hide the correct
# image in the tarball we're actually holding.
IMAGE_TAG="wbxprov-remote-agent:${VERSION}"
log "Loading Docker image from ${IMAGE_TARBALL}..."
docker load -i "$IMAGE_TARBALL"
# --- 3a. Platform sanity check --------------------------------------------
# Inspect the image we just (re)loaded — this is the ground truth for what
# was actually shipped in this ZIP, independent of anything that was on the
# host before. If the tarball was built for a different CPU architecture
# than this host, Docker will let it "run" but tini (and node) fail with
# cryptic errors like "exec format error". Catch that up front with a
# clear message.
IMAGE_ARCH="$(docker image inspect --format '{{.Architecture}}' "$IMAGE_TAG" 2>/dev/null || true)"
IMAGE_ID_SHORT="$(docker image inspect --format '{{.Id}}' "$IMAGE_TAG" 2>/dev/null | sed 's|^sha256:||' | cut -c1-12)"
HOST_ARCH_RAW="$(uname -m)"
case "$HOST_ARCH_RAW" in
x86_64|amd64) HOST_ARCH="amd64" ;;
aarch64|arm64) HOST_ARCH="arm64" ;;
armv7l) HOST_ARCH="arm" ;;
*) HOST_ARCH="$HOST_ARCH_RAW" ;;
esac
log "Loaded ${IMAGE_TAG} (id: ${IMAGE_ID_SHORT:-unknown}, arch: ${IMAGE_ARCH:-unknown}); host arch: ${HOST_ARCH}."
if [[ -z "$IMAGE_ARCH" ]]; then
die "Could not read image architecture from Docker — is the image really loaded?"
fi
if [[ "$IMAGE_ARCH" != "$HOST_ARCH" ]]; then
warn "Image architecture (${IMAGE_ARCH}) does not match this host (${HOST_ARCH})."
warn "The container would fail to start with 'exec /sbin/tini: exec format error'."
warn ""
warn "This means the ZIP was built for the wrong CPU. On the build host:"
warn " 1. git pull # make sure you have the fixed package.sh"
warn " 2. ./docker/remote-agent/package.sh --platform linux/${HOST_ARCH}"
warn "The updated package.sh verifies the architecture during build and"
warn "refuses to produce a ZIP that would fail this check."
die "Aborting install. Ship a linux/${HOST_ARCH} bundle and re-run this script."
fi
# --- 4. Bootstrap .env -----------------------------------------------------
if [[ ! -f .env ]]; then
if [[ -f .env.example ]]; then
cp .env.example .env
warn ".env did not exist — copied .env.example into place."
warn "EDIT .env now to set WS_URL and WS_TOKEN, then re-run this script."
exit 0
else
die ".env is missing and no .env.example is bundled. Cannot proceed."
fi
fi
# --- 5. Start the container -----------------------------------------------
log "Starting wbxprov-remote-agent (version ${VERSION})..."
"${COMPOSE[@]}" up -d
log "Done. Tail logs with:"
log " ${COMPOSE[*]} logs -f"
log "Stop with:"
log " ${COMPOSE[*]} down"