The v0.7.2 GitHub fallback is now functionally broken: the GitHub mirror
is being made private, so anonymous raw fetches return 401/403. Rather
than ship a silent-failure path to a dead URL, remove the fallback
entirely.
Changes:
larry.sh
- LARRY_BASE_URL_FALLBACK / LARRY_ORIGIN_DEFAULT_GITHUB removed
- sync_from_manifest_with_fallback and _fetch_with_fallback retained
by name (call-site compat) but are now single-source wrappers; on
origin unreachable they warn "auto-update skipped this launch" and
proceed with locally cached files (no crash)
- status-line _origin_badge collapses to "" (default) or "custom"
(user-pinned HTTPS URL); legacy github / fallback badges gone
- /origin slash command simplified:
/origin show effective origin + pin file
/origin gitea pin to the default Gitea URL
/origin auto clear the pin
/origin <https-url> pin to an arbitrary HTTPS mirror
/origin github returns a clear error (mirror is private)
- /help text updated to reflect single-source model
- LARRY_VERSION 0.7.3 -> 0.7.4
install-larry.sh
- LARRY_BASE_URL_FALLBACK removed; single-origin install path
- fetch() dies with a clear error when origin unreachable:
"install failed: cannot reach LARRY_BASE_URL=... — verify the URL
or set LARRY_BASE_URL to a reachable mirror"
- post-install fallback-warning block removed
VERSION: 0.7.3 -> 0.7.4
Migration: stale $LARRY_HOME/.origin files containing the legacy keyword
"github" are treated as invalid — Larry warns once at startup and reverts
to the default Gitea origin. We deliberately do NOT auto-rewrite the
file (so the user can choose) and do NOT translate it to the GitHub raw
URL (which would just 401 on the next fetch).
Verification:
- bash -n larry.sh / install-larry.sh: pass
- larry --version: prints 0.7.4
- LARRY_BASE_URL=https://invalid.example.invalid + stale .last-sync-version:
logs "warn: ... unreachable, auto-update skipped this launch", no crash
- /origin auto clears pin file; /origin (no arg) shows current effective
origin and pin file; /origin <https-url> persisted; /origin github
returns clear error; /origin gitea re-pins to default
- stale .origin containing "github" -> startup warn + revert to default
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
211 lines
12 KiB
Bash
Executable File
211 lines
12 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# install-larry.sh — bootstrap Larry-Anywhere on a fresh remote shell.
|
|
# No root, no package install, no sudo. Writes only into $LARRY_HOME.
|
|
#
|
|
# Usage:
|
|
# curl -fsSL <BASE_URL>/install-larry.sh | bash
|
|
#
|
|
# Or with explicit base URL:
|
|
# LARRY_BASE_URL=https://example.com/larry-anywhere bash install-larry.sh
|
|
#
|
|
# Env vars:
|
|
# LARRY_HOME install location (default: $HOME/.larry)
|
|
# LARRY_BASE_URL where to fetch files from (no trailing slash)
|
|
# LARRY_BIN_DIR where to symlink the `larry` command (default: $HOME/bin)
|
|
set -eu
|
|
|
|
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
|
# Canonical hosting (v0.7.4): self-hosted Gitea at git.bjnoela.com is the
|
|
# single source. The v0.7.2 GitHub fallback was removed after the GitHub
|
|
# mirror was made private (anonymous raw fetches now 401/403, so the
|
|
# fallback was functionally broken). Override LARRY_BASE_URL via env if you
|
|
# fork or mirror elsewhere.
|
|
#
|
|
# IMPORTANT: the Gitea repo must be PUBLIC for unauthenticated raw-URL reads
|
|
# to succeed. If the install fetch fails, set LARRY_BASE_URL to a reachable
|
|
# mirror or check repo visibility on git.bjnoela.com.
|
|
LARRY_BASE_URL="${LARRY_BASE_URL:-https://git.bjnoela.com/bryan/cloverleaf-larry/raw/branch/main}"
|
|
LARRY_BIN_DIR="${LARRY_BIN_DIR:-$HOME/bin}"
|
|
|
|
C_RESET=$'\033[0m'; C_BOLD=$'\033[1m'; C_GREEN=$'\033[32m'
|
|
C_YELLOW=$'\033[33m'; C_RED=$'\033[31m'; C_CYAN=$'\033[36m'
|
|
|
|
say() { printf '%s%sinstall-larry>%s %s\n' "$C_CYAN" "$C_BOLD" "$C_RESET" "$*"; }
|
|
ok() { printf ' %s✓%s %s\n' "$C_GREEN" "$C_RESET" "$*"; }
|
|
warn() { printf ' %s!%s %s\n' "$C_YELLOW" "$C_RESET" "$*"; }
|
|
die() { printf '%serror:%s %s\n' "$C_RED" "$C_RESET" "$*" >&2; exit 1; }
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# Detect platform
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
UNAME_S="$(uname -s 2>/dev/null || echo unknown)"
|
|
PLATFORM=""
|
|
case "$UNAME_S" in
|
|
Linux*) PLATFORM="linux" ;;
|
|
Darwin*) PLATFORM="darwin" ;;
|
|
CYGWIN*|MINGW*|MSYS*) PLATFORM="windows-cygwin" ;; # MobaXterm lives here
|
|
*) PLATFORM="unknown" ;;
|
|
esac
|
|
|
|
ARCH="$(uname -m 2>/dev/null || echo unknown)"
|
|
case "$ARCH" in
|
|
x86_64|amd64) ARCH_NORM="amd64" ;;
|
|
aarch64|arm64) ARCH_NORM="arm64" ;;
|
|
i?86) ARCH_NORM="i386" ;;
|
|
*) ARCH_NORM="$ARCH" ;;
|
|
esac
|
|
|
|
say "platform: $PLATFORM/$ARCH_NORM • LARRY_HOME=$LARRY_HOME"
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# Check required commands
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
command -v bash >/dev/null 2>&1 || die "bash not found"
|
|
command -v curl >/dev/null 2>&1 || die "curl not found"
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# Make dirs
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
mkdir -p "$LARRY_HOME"/{agents,sessions,bin,lib} || die "cannot create $LARRY_HOME"
|
|
chmod 700 "$LARRY_HOME" 2>/dev/null || true
|
|
ok "created $LARRY_HOME"
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# Fetch the scripts. If LARRY_BASE_URL is not set, try to detect being run
|
|
# from a local checkout (sibling files present) — copy locally instead.
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" 2>/dev/null && pwd)" || SCRIPT_DIR=""
|
|
|
|
# v0.7.4 single-source: install-larry.sh is the FIRST contact with the
|
|
# origin — it runs before larry.sh exists, so it must succeed in one shot.
|
|
# No fallback; if $LARRY_BASE_URL is unreachable we die with a clear error
|
|
# telling the user to verify the URL or set an alternate mirror.
|
|
|
|
fetch() {
|
|
# $1 = remote relative path, $2 = local destination
|
|
if [ -n "$LARRY_BASE_URL" ]; then
|
|
say "fetching $1"
|
|
if curl -fsSL --max-time 30 "$LARRY_BASE_URL/$1" -o "$2" 2>/dev/null && [ -s "$2" ]; then
|
|
ok "$2"
|
|
return 0
|
|
fi
|
|
rm -f "$2"
|
|
die "install failed: cannot reach LARRY_BASE_URL=$LARRY_BASE_URL (fetching $1) — verify the URL or set LARRY_BASE_URL to a reachable mirror"
|
|
elif [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/$1" ]; then
|
|
cp "$SCRIPT_DIR/$1" "$2" && ok "copied $1 (local)"
|
|
else
|
|
die "no LARRY_BASE_URL set and $1 not found in script dir"
|
|
fi
|
|
}
|
|
|
|
fetch larry.sh "$LARRY_HOME/larry.sh"
|
|
fetch larry-tunnel.sh "$LARRY_HOME/larry-tunnel.sh"
|
|
fetch agents/larry.md "$LARRY_HOME/agents/larry.md"
|
|
fetch agents/clover.md "$LARRY_HOME/agents/clover.md"
|
|
fetch agents/cloverleaf-cheatsheet.md "$LARRY_HOME/agents/cloverleaf-cheatsheet.md"
|
|
fetch agents/regress.md "$LARRY_HOME/agents/regress.md"
|
|
fetch larry-rollback.sh "$LARRY_HOME/larry-rollback.sh"
|
|
fetch larry-auth.sh "$LARRY_HOME/larry-auth.sh"
|
|
fetch lib/oauth.sh "$LARRY_HOME/lib/oauth.sh"
|
|
fetch lib/ssh-helper.sh "$LARRY_HOME/lib/ssh-helper.sh"
|
|
fetch lib/lessons.sh "$LARRY_HOME/lib/lessons.sh"
|
|
fetch lib/hl7-sanitize.sh "$LARRY_HOME/lib/hl7-sanitize.sh"
|
|
fetch lib/hl7-desanitize.sh "$LARRY_HOME/lib/hl7-desanitize.sh"
|
|
fetch lib/each.sh "$LARRY_HOME/lib/each.sh"
|
|
fetch lib/each-site.sh "$LARRY_HOME/lib/each-site.sh"
|
|
fetch lib/len2nl.sh "$LARRY_HOME/lib/len2nl.sh"
|
|
fetch lib/csv-to-table.sh "$LARRY_HOME/lib/csv-to-table.sh"
|
|
fetch lib/table-to-csv.sh "$LARRY_HOME/lib/table-to-csv.sh"
|
|
fetch lib/nc-engine.sh "$LARRY_HOME/lib/nc-engine.sh"
|
|
fetch lib/nc-status.sh "$LARRY_HOME/lib/nc-status.sh"
|
|
fetch lib/nc-table.sh "$LARRY_HOME/lib/nc-table.sh"
|
|
fetch lib/nc-xlate.sh "$LARRY_HOME/lib/nc-xlate.sh"
|
|
fetch lib/nc-smat-diff.sh "$LARRY_HOME/lib/nc-smat-diff.sh"
|
|
fetch lib/nc-create-thread.sh "$LARRY_HOME/lib/nc-create-thread.sh"
|
|
fetch lib/nc-tclgen.sh "$LARRY_HOME/lib/nc-tclgen.sh"
|
|
fetch lib/nc-parse.sh "$LARRY_HOME/lib/nc-parse.sh"
|
|
fetch lib/nc-inbound.sh "$LARRY_HOME/lib/nc-inbound.sh"
|
|
fetch lib/nc-make-jump.sh "$LARRY_HOME/lib/nc-make-jump.sh"
|
|
fetch lib/hl7-field.sh "$LARRY_HOME/lib/hl7-field.sh"
|
|
fetch lib/hl7-schema.sh "$LARRY_HOME/lib/hl7-schema.sh"
|
|
fetch lib/nc-msgs.sh "$LARRY_HOME/lib/nc-msgs.sh"
|
|
fetch lib/nc-document.sh "$LARRY_HOME/lib/nc-document.sh"
|
|
fetch lib/nc-diff-interface.sh "$LARRY_HOME/lib/nc-diff-interface.sh"
|
|
fetch lib/nc-find.sh "$LARRY_HOME/lib/nc-find.sh"
|
|
fetch lib/nc-insert-protocol.sh "$LARRY_HOME/lib/nc-insert-protocol.sh"
|
|
fetch lib/hl7-diff.sh "$LARRY_HOME/lib/hl7-diff.sh"
|
|
fetch lib/nc-regression.sh "$LARRY_HOME/lib/nc-regression.sh"
|
|
fetch lib/journal.sh "$LARRY_HOME/lib/journal.sh"
|
|
fetch VERSION "$LARRY_HOME/VERSION"
|
|
fetch MANUAL.md "$LARRY_HOME/MANUAL.md"
|
|
chmod +x "$LARRY_HOME/larry.sh" "$LARRY_HOME/larry-tunnel.sh" "$LARRY_HOME/larry-rollback.sh" "$LARRY_HOME/larry-auth.sh" "$LARRY_HOME/lib/"*.sh
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# jq fallback — download static binary into $LARRY_HOME/bin/ if missing
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
if ! command -v jq >/dev/null 2>&1 && [ ! -x "$LARRY_HOME/bin/jq" ]; then
|
|
say "jq not found — fetching static binary into $LARRY_HOME/bin/jq"
|
|
JQ_BASE="https://github.com/jqlang/jq/releases/download/jq-1.7.1"
|
|
JQ_URL=""
|
|
case "$PLATFORM/$ARCH_NORM" in
|
|
linux/amd64) JQ_URL="$JQ_BASE/jq-linux-amd64" ;;
|
|
linux/arm64) JQ_URL="$JQ_BASE/jq-linux-arm64" ;;
|
|
linux/i386) JQ_URL="$JQ_BASE/jq-linux-i386" ;;
|
|
darwin/amd64) JQ_URL="$JQ_BASE/jq-macos-amd64" ;;
|
|
darwin/arm64) JQ_URL="$JQ_BASE/jq-macos-arm64" ;;
|
|
windows-cygwin/amd64) JQ_URL="$JQ_BASE/jq-windows-amd64.exe" ;;
|
|
windows-cygwin/i386) JQ_URL="$JQ_BASE/jq-windows-i386.exe" ;;
|
|
*) warn "no jq binary known for $PLATFORM/$ARCH_NORM — install jq manually" ;;
|
|
esac
|
|
if [ -n "$JQ_URL" ]; then
|
|
local_jq="$LARRY_HOME/bin/jq"
|
|
case "$PLATFORM" in windows-cygwin) local_jq="$LARRY_HOME/bin/jq.exe" ;; esac
|
|
if curl -fsSL --max-time 60 "$JQ_URL" -o "$local_jq"; then
|
|
chmod +x "$local_jq"
|
|
ok "jq -> $local_jq"
|
|
else
|
|
warn "could not download jq from $JQ_URL"
|
|
fi
|
|
fi
|
|
else
|
|
ok "jq available"
|
|
fi
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# Drop a `larry` shim onto PATH (best-effort)
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
mkdir -p "$LARRY_BIN_DIR" 2>/dev/null || true
|
|
if [ -d "$LARRY_BIN_DIR" ] && [ -w "$LARRY_BIN_DIR" ]; then
|
|
cat > "$LARRY_BIN_DIR/larry" <<EOF
|
|
#!/usr/bin/env bash
|
|
# Auto-generated by install-larry.sh
|
|
export LARRY_HOME="${LARRY_HOME}"
|
|
exec "${LARRY_HOME}/larry.sh" "\$@"
|
|
EOF
|
|
chmod +x "$LARRY_BIN_DIR/larry"
|
|
ok "shim: $LARRY_BIN_DIR/larry"
|
|
case ":$PATH:" in
|
|
*":$LARRY_BIN_DIR:"*) : ;;
|
|
*) warn "$LARRY_BIN_DIR is not on PATH — add 'export PATH=\"$LARRY_BIN_DIR:\$PATH\"' to your shell rc" ;;
|
|
esac
|
|
else
|
|
warn "cannot write to $LARRY_BIN_DIR — invoke larry directly as: $LARRY_HOME/larry.sh"
|
|
fi
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# Done
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
say "install complete (no system changes were made; everything lives under $LARRY_HOME)"
|
|
say "origin (single-source, v0.7.4): $LARRY_BASE_URL"
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " 1) export ANTHROPIC_API_KEY=sk-ant-... (or larry will prompt on first run)"
|
|
echo " 2) larry (or $LARRY_HOME/larry.sh)"
|
|
echo " 3) larry /path/to/cloverleaf/site_root (to start with a working dir)"
|
|
echo ""
|
|
echo "Reverse SSH tunnel (optional, run in another shell or backgrounded):"
|
|
echo " $LARRY_HOME/larry-tunnel.sh --serveo # zero-config"
|
|
echo " $LARRY_HOME/larry-tunnel.sh --hop=user@bjnoela.com:22 # your hop"
|
|
echo ""
|