$ customer-installation
Despliega Boost con Jamf Pro MDM
docs/customer-installation/mdm/jamf-pro; esta página los renderiza directamente para que los cambios en el repositorio actualicen el sitio en el próximo build.Entorno probado
Esta ruta de despliegue se probó con Jamf Pro. Futuros proveedores MDM deben seguir el mismo diseño bajo docs/customer-installation/mdm/<provider>.
Cómo funciona el despliegue
Añade el script de instalación
Crea un script Jamf Pro desde install.sh. Instala Boost para el usuario de consola, inicializa las integraciones de agente seleccionadas y actualiza el inventario Jamf.
Crea una política de despliegue
Ejecuta el script como root en los Mac objetivo. Usa el parámetro 4 de Jamf para elegir integraciones Boost y combina la política con un disparador Login para Macs sin usuario de consola activo.
Añade el atributo de extensión
Crea un Extension Attribute de Jamf Pro desde extension-attribute.sh para mostrar estado de instalación, versión, alcance de ruta, integraciones, herramientas de IA detectadas y hora de recolección.
Configuración de política Jamf Pro
En Jamf Pro, añade el instalador como script y adjúntalo a una política para los Mac que deben recibir Boost. Mantén la política idempotente: reejecutarla actualiza el binario y reaplica las integraciones seleccionadas.
- Script
- Pega
install.shen un script de Jamf Pro. - Ejecutar como
- Root. El script cambia al usuario con sesión para
boost init. - Ruta de instalación predeterminada
$HOME/.local/bin/boostpara el usuario de consola- Parámetro 4
- Objetivos Boost opcionales separados por espacios, como
cursor claude.
Ejemplos de objetivos
| Despliegue | Parámetro 4 de Jamf |
|---|---|
| Despliegue de agente local predeterminado | cursor claude |
| Cursor, Claude y Codex | cursor claude codex |
| Equipos CLI-first | codex |
#!/bin/bash
# Boost CLI - Jamf install (per-user, macOS).
# Installs to $HOME/.local/bin/boost for the console user, runs `boost init`
# as them, removes the legacy /usr/local/bin/boost if present, and refreshes
# Jamf inventory so the Boost CLI Status EA reflects this run immediately.
#
# Optional:
# BOOST_TARGETS default: "cursor claude"
# Jamf parameter $4 same as BOOST_TARGETS
set -euo pipefail
repo="jfrog/boost"
targets="${4:-${BOOST_TARGETS:-cursor claude}}"
log() { printf '[boost] %s\n' "$*"; }
die() { log "ERROR: $*" >&2; exit 1; }
# curl with retries — handles transient corporate-network resets (curl 56,
# 7, 18, 35, …). --retry-all-errors covers mid-stream resets that plain
# --retry won't catch.
fetch() {
curl -fsSL \
--retry 5 --retry-delay 2 --retry-all-errors \
--connect-timeout 10 --max-time 300 \
"$@"
}
# Repair root-owned leftovers in the dirs `boost init` writes to. Earlier
# POC runs may have created these as root, leaving the user unable to
# overwrite them. Recursive chown is safe: if the dir itself is root-owned,
# the user couldn't have created files inside it anyway.
normalize_boost_paths() {
local user="$1" home="$2" path owner
for path in \
"$home/.local" \
"$home/.boost" \
"$home/.claude" \
"$home/.cursor" \
"$home/.codex"; do
[ -e "$path" ] || continue
owner="$(/usr/bin/stat -f '%Su' "$path" 2>/dev/null || true)"
if [ "$owner" != "$user" ]; then
log "fixing ownership: $path (was $owner)"
/usr/sbin/chown -R "$user" "$path" || log "chown failed for $path (non-fatal)"
fi
done
}
# ---- Console user is required for per-user install ----------------------
user="$(/usr/bin/stat -f "%Su" /dev/console 2>/dev/null || true)"
if [ -z "$user" ] || [ "$user" = "root" ] || [ "$user" = "loginwindow" ]; then
log "no console user; per-user install needs a logged-in account — skipping"
# Exit 0 so Jamf doesn't flag this as a failure and storm retries.
# Pair with a Login trigger on the policy to catch this machine later.
exit 0
fi
uid="$(/usr/bin/id -u "$user")"
home="$(/usr/bin/dscl . -read "/Users/$user" NFSHomeDirectory 2>/dev/null | /usr/bin/awk '{print $2}')"
[ -n "$home" ] || home="/Users/$user"
install_dir="$home/.local/bin"
boost="$install_dir/boost"
# ---- Architecture --------------------------------------------------------
arch="$(uname -m)"
case "$arch" in arm64|aarch64) arch=arm64 ;; x86_64|amd64) arch=amd64 ;; *) die "unsupported arch: $arch" ;; esac
archive="boost-darwin-${arch}.tar.gz"
# ---- Resolve latest tag and download ------------------------------------
tag="$(fetch -I -o /dev/null -w '%{url_effective}' "https://github.com/$repo/releases/latest" | sed 's#.*/tag/##')"
[ -n "$tag" ] || die "could not resolve latest Boost release"
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
log "downloading $archive ($tag)"
fetch "https://github.com/$repo/releases/download/$tag/$archive" -o "$tmp/$archive"
tar -xzf "$tmp/$archive" -C "$tmp"
[ -f "$tmp/boost" ] || die "archive missing boost binary"
# ---- Install into the user's ~/.local/bin, owned by them ----------------
/bin/mkdir -p "$install_dir"
/usr/sbin/chown "$user" "$home/.local" "$install_dir" 2>/dev/null || true
/usr/bin/install -o "$user" -g staff -m 0755 "$tmp/boost" "$boost"
log "installed $(/usr/bin/sudo -H -u "$user" "$boost" version 2>/dev/null || echo "$tag") at $boost"
# ---- Remove legacy system install so the EA reports the right path ------
# The EA prefers $HOME/.local/bin/boost but falls back to /usr/local/bin/boost.
# Leaving the old one around means PATH lookups for the user may still hit
# the stale binary depending on shell config. Self-cleanup on every run.
if [ -e /usr/local/bin/boost ]; then
log "removing legacy /usr/local/bin/boost"
/bin/rm -f /usr/local/bin/boost || log "legacy removal failed (non-fatal)"
fi
# ---- Fix any root-owned config leftovers before init --------------------
normalize_boost_paths "$user" "$home"
# ---- Run `boost init` as the console user -------------------------------
flags=(--accept-terms)
for target in $targets; do flags+=(--"$target"); done
log "running '$boost init ${flags[*]}' as $user"
/bin/launchctl asuser "$uid" /usr/bin/sudo -H -u "$user" \
/usr/bin/env HOME="$home" USER="$user" LOGNAME="$user" \
"$boost" init "${flags[@]}"
# ---- Push Jamf inventory so the EA picks up this run --------------------
if [ -x /usr/local/bin/jamf ]; then
log "triggering jamf recon"
/usr/local/bin/jamf recon >/dev/null 2>&1 || log "recon failed (non-fatal)"
fiRaw source: install.sh
#!/bin/bash
# Boost CLI — Jamf Pro Extension Attribute
#
# Looks for boost in this priority order:
# 1. $home/.local/bin/boost (per-user, current layout)
# 2. /usr/local/bin/boost (legacy fallback during migration)
# 3. /Users/*/.local/bin/boost (last resort if no console user)
#
# IMPORTANT: EA values refresh ONLY when `jamf recon` runs. The Boost
# install script must call `/usr/local/bin/jamf recon` at its end (or the
# install policy must include a Maintenance → Update Inventory payload),
# otherwise this attribute will keep showing the previously-recorded value.
#
# Deployment:
# Jamf Pro → Settings → Computer Management → Extension Attributes → New
# Display Name: Boost CLI Status
# Inventory: Applications
# Data Type: String
# Input Type: Script
set -u
export PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin"
home=""
boost=""
boost_scope=""
emit() { printf '%s=%s\n' "$1" "$2"; }
resolve_home_for() {
local u="$1" h
h="$(/usr/bin/dscl . -read "/Users/$u" NFSHomeDirectory 2>/dev/null | /usr/bin/awk '{print $2}')"
[ -z "$h" ] && h="/Users/$u"
printf '%s' "$h"
}
have_cli() {
local name="$1" p
for p in \
"$home/.local/bin/$name" \
"/usr/local/bin/$name" \
"/opt/homebrew/bin/$name" \
"$home/.npm-global/bin/$name" \
"$home/.bun/bin/$name" \
"$home/.volta/bin/$name"; do
[ -x "$p" ] && return 0
done
return 1
}
echo "<result>"
# ---- EA metadata (most useful field for diagnosing stale inventory) ----
emit ea_collected_at "$(/bin/date '+%Y-%m-%dT%H:%M:%S%z')"
# ---- System --------------------------------------------------------------
emit os "$(/usr/bin/sw_vers -productName 2>/dev/null) $(/usr/bin/sw_vers -productVersion 2>/dev/null)"
emit os_build "$(/usr/bin/sw_vers -buildVersion 2>/dev/null)"
emit arch "$(/usr/bin/uname -m 2>/dev/null)"
# ---- Console user --------------------------------------------------------
console_user="$(/usr/bin/stat -f "%Su" /dev/console 2>/dev/null || echo "")"
if [ -z "$console_user" ] || [ "$console_user" = "root" ] || [ "$console_user" = "loginwindow" ]; then
emit console_user none
console_user=""
else
emit console_user "$console_user"
emit console_uid "$(/usr/bin/id -u "$console_user")"
home="$(resolve_home_for "$console_user")"
emit home "$home"
shell="$(/usr/bin/dscl . -read "/Users/$console_user" UserShell 2>/dev/null | /usr/bin/awk '{print $2}')"
emit shell "${shell:-unknown}"
fi
# ---- Locate boost (priority: console user → system → other users) -------
if [ -n "$home" ] && [ -x "$home/.local/bin/boost" ]; then
boost="$home/.local/bin/boost"
boost_scope="user"
elif [ -x "/usr/local/bin/boost" ]; then
boost="/usr/local/bin/boost"
boost_scope="system"
else
# No console user, or console user has nothing — scan other user homes
# so overnight recon on an unattended Mac still finds boost.
for udir in /Users/*/; do
[ -d "$udir" ] || continue
[ "${udir%/}" = "/Users/Shared" ] && continue
[ "${udir%/}" = "$home" ] && continue
cand="${udir%/}/.local/bin/boost"
if [ -x "$cand" ]; then
boost="$cand"
boost_scope="other_user"
[ -z "$home" ] && home="${udir%/}"
break
fi
done
fi
if [ -z "$boost" ]; then
emit status not_installed
echo "</result>"
exit 0
fi
# ---- Boost binary report -------------------------------------------------
emit status installed
emit boost_path "$boost"
emit boost_scope "$boost_scope"
emit boost_mtime "$(/bin/date -r "$boost" '+%Y-%m-%dT%H:%M:%S' 2>/dev/null)"
emit boost_sha12 "$(/usr/bin/shasum -a 256 "$boost" 2>/dev/null | /usr/bin/awk '{print substr($1,1,12)}')"
# Pass HOME so a per-user binary reads the right config dir, not /var/root.
if [ -n "$home" ] && [ -d "$home" ]; then
raw_version="$(/usr/bin/env HOME="$home" "$boost" version 2>/dev/null | /usr/bin/head -n1 | /usr/bin/tr -d '\r')"
else
raw_version="$("$boost" version 2>/dev/null | /usr/bin/head -n1 | /usr/bin/tr -d '\r')"
fi
parsed_version="$(printf '%s\n' "$raw_version" | /usr/bin/grep -Eo 'v?[0-9]+\.[0-9]+\.[0-9]+([.-][A-Za-z0-9]+)*' | /usr/bin/head -n1)"
emit version "${parsed_version:-unknown}"
emit version_raw "${raw_version:-empty}"
# ---- Integrations + AI tools (need a home dir) --------------------------
if [ -n "$home" ] && [ -d "$home" ]; then
integrations=()
[ -f "$home/.cursor/hooks/boost-rewrite.sh" ] && integrations+=("cursor")
[ -f "$home/.claude/hooks/boost-rewrite.sh" ] && integrations+=("claude")
[ -f "$home/.codex/BOOST.md" ] && integrations+=("codex")
if [ ${#integrations[@]} -eq 0 ]; then
emit boost_integrations none
else
IFS=','; emit boost_integrations "${integrations[*]}"; unset IFS
fi
ai_tools=()
[ -d "/Applications/Cursor.app" ] && ai_tools+=("cursor_app")
[ -d "/Applications/Claude.app" ] && ai_tools+=("claude_app")
have_cli claude && ai_tools+=("claude_cli")
have_cli codex && ai_tools+=("codex_cli")
if [ ${#ai_tools[@]} -eq 0 ]; then
emit ai_tools none
else
IFS=','; emit ai_tools "${ai_tools[*]}"; unset IFS
fi
if [ -f "$home/.boost/config.toml" ]; then
emit config_global present
else
emit config_global absent
fi
last_init=""
for ag in cursor claude codex; do
hook="$home/.$ag/hooks/boost-rewrite.sh"
if [ -f "$hook" ]; then
last_init="$(/bin/date -r "$hook" '+%Y-%m-%dT%H:%M:%S' 2>/dev/null)"
[ -n "$last_init" ] && break
fi
done
[ -n "$last_init" ] && emit last_init "$last_init"
fi
echo "</result>"Raw source: extension-attribute.sh
Inventario Jamf Pro
Añade el atributo de extensión para que el inventario Jamf informe si Boost está instalado, qué versión hay, dónde se encontró el binario, qué integraciones están activas y qué herramientas de IA detectó para el usuario de consola.
- Nombre para mostrar
- Boost CLI Status
- Inventario
- Applications
- Tipo de dato
- String
- Tipo de entrada
- Script
Ejemplos de Smart Group
status=not_installedencuentra Macs sin el binario Boost.version=v0.6.4identifica máquinas en una versión antigua.ai_tools contains cursormásboost_integrations does not contain cursorencuentra usuarios de Cursor sin hooks Boost.
Notas operativas
- El instalador requiere un usuario de consola con sesión porque Boost se instala por usuario bajo
$HOME/.local/bin. - Si no hay usuario de consola, el script sale con éxito sin instalar; usa un disparador Login para capturar la máquina después.
- El script elimina un binario heredado
/usr/local/bin/boostpara que shells y el atributo de extensión prefieran la instalación por usuario. - Antes de
boost init --accept-terms, el script repara directorios de config Boost y de agente propiedad de root de ejecuciones anteriores. - El script ejecuta
jamf reconcuando Jamf está disponible para que el atributo de extensión refleje la instalación de inmediato.
Documentación relacionada
Para configuración individual de desarrolladores, usa la guía rápida de Boost. Para detalles del flujo de instalación, consulta la especificación del flujo de instalación.