#!/bin/sh # beacon.host installer: the publish-website agent skill, then the beacon CLI. # # curl -fsSL https://beacon.host/install.sh | sh # # Never uses sudo. Environment overrides: # BEACON_INSTALL_DIR where the beacon binary goes (default ~/.local/bin) # BEACON_SKIP_SKILL=1 don't install the agent skill # BEACON_SKIP_CLI=1 install only the agent skill set -eu BASE_URL="${BEACON_BASE_URL:-https://beacon.host}" INSTALL_DIR="${BEACON_INSTALL_DIR:-$HOME/.local/bin}" SKILL_NAME="publish-website" TMP_DIR="$(mktemp -d)" cleanup() { rm -rf "$TMP_DIR" } trap cleanup EXIT INT TERM need() { if ! command -v "$1" >/dev/null 2>&1; then echo "install.sh: missing required command: $1" >&2 exit 1 fi } need curl # 1. The agent skill. Claude Code reads ~/.claude/skills; Codex, Cursor and # most other agents read ~/.agents/skills. With it installed, future agent # sessions know how to publish to beacon.host without being told. if [ "${BEACON_SKIP_SKILL:-}" != "1" ]; then SKILL_FILE="$TMP_DIR/SKILL.md" curl -fsSL "$BASE_URL/skill.md?ref=install-sh" -o "$SKILL_FILE" if [ "$(head -n 1 "$SKILL_FILE")" != "---" ] || ! grep -q "^name: $SKILL_NAME\$" "$SKILL_FILE"; then echo "install.sh: $BASE_URL/skill.md is not a valid skill file; not installing it" >&2 exit 1 fi for dir in "$HOME/.claude/skills/$SKILL_NAME" "$HOME/.agents/skills/$SKILL_NAME"; do mkdir -p "$dir" cp "$SKILL_FILE" "$dir/SKILL.md" done echo "Skill installed: future agent sessions will use beacon.host when you say \"publish this\" or \"deploy an API\"" fi if [ "${BEACON_SKIP_CLI:-}" = "1" ]; then exit 0 fi # 2. The beacon CLI, into a directory you own. need tar OS="$(uname -s | tr '[:upper:]' '[:lower:]')" ARCH="$(uname -m)" case "$OS" in darwin) OS="darwin" ;; linux) OS="linux" ;; *) echo "install.sh: unsupported OS: $OS" >&2; exit 1 ;; esac case "$ARCH" in arm64|aarch64) ARCH="arm64" ;; x86_64|amd64) ARCH="amd64" ;; *) echo "install.sh: unsupported architecture: $ARCH" >&2; exit 1 ;; esac LATEST="$TMP_DIR/latest.json" curl -fsSL "$BASE_URL/downloads/beacon/latest.json" -o "$LATEST" VERSION="$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$LATEST" | head -n 1)" if [ -z "$VERSION" ]; then echo "install.sh: could not read version from latest.json" >&2 exit 1 fi NAME="beacon-$OS-$ARCH.tar.gz" URL="$BASE_URL/downloads/beacon/$VERSION/$NAME" ARCHIVE="$TMP_DIR/$NAME" curl -fsSL "$URL" -o "$ARCHIVE" curl -fsSL "$BASE_URL/downloads/beacon/$VERSION/checksums.txt" -o "$TMP_DIR/checksums.txt" if command -v shasum >/dev/null 2>&1; then (cd "$TMP_DIR" && grep " $NAME\$" checksums.txt | shasum -a 256 -c -) elif command -v sha256sum >/dev/null 2>&1; then (cd "$TMP_DIR" && grep " $NAME\$" checksums.txt | sha256sum -c -) else echo "install.sh: warning: shasum/sha256sum not found; skipping checksum verification" >&2 fi tar -xzf "$ARCHIVE" -C "$TMP_DIR" chmod +x "$TMP_DIR/beacon" if ! mkdir -p "$INSTALL_DIR" 2>/dev/null || [ ! -w "$INSTALL_DIR" ]; then echo "install.sh: cannot write to $INSTALL_DIR; set BEACON_INSTALL_DIR to a directory you own" >&2 exit 1 fi mv "$TMP_DIR/beacon" "$INSTALL_DIR/beacon" echo "Installed beacon $VERSION to $INSTALL_DIR/beacon" case ":$PATH:" in *":$INSTALL_DIR:"*) ;; *) case "${SHELL:-}" in */zsh) RC="~/.zshrc" ;; */bash) RC="~/.bashrc" ;; *) RC="~/.profile" ;; esac echo "" echo "$INSTALL_DIR is not on your PATH. Add it, then open a new terminal:" echo " echo 'export PATH=\"$INSTALL_DIR:\$PATH\"' >> $RC" echo "Until then, run it as $INSTALL_DIR/beacon" ;; esac