$ customer-installation
פריסת Boost עם Jamf Pro MDM
docs/customer-installation/mdm/jamf-pro; עמוד זה מציג אותם ישירות כך שעריכות במאגר מעדכנות את האתר ב-build הבא.סביבה שנבדקה
נתיב פריסה זה נבדק עם Jamf Pro. ספקי MDM עתידיים צריכים לעקוב אחר אותה פריסת קבצים תחת docs/customer-installation/mdm/<provider>.
איך הפריסה עובדת
הוספת סקריפט ההתקנה
צרו סקריפט Jamf Pro מ-install.sh. הוא מתקין Boost למשתמש הקונסולה, מאתחל אינטגרציות סוכן שנבחרו ומרענן מלאי Jamf.
יצירת מדיניות פריסה
הריצו את הסקריפט כ-root על ה-Mac היעד. השתמשו בפרמטר 4 של Jamf לבחירת אינטגרציות Boost ושלבו Login trigger ל-Mac בלי משתמש קונסולה פעיל.
הוספת Extension Attribute
צרו Jamf Pro Extension Attribute מ-extension-attribute.sh כדי להציג מצב התקנה, גרסה, נתיב, אינטגרציות, כלי AI שזוהו וזמן איסוף.
הגדרת מדיניות Jamf Pro
ב-Jamf Pro הוסיפו את המתקין כסקריפט וחברו אותו למדיניות עבור ה-Mac שאמורים לקבל Boost. שמרו על idempotency: הרצה חוזרת מרעננת את הבינארי ומיישמת מחדש את האינטגרציות שנבחרו.
- סקריפט
- הדביקו
install.shלסקריפט Jamf Pro. - הרצה כ
- Root. הסקריפט עובר למשתמש המחובר עבור
boost init. - נתיב התקנה ברירת מחדל
$HOME/.local/bin/boostלמשתמש הקונסולה- פרמטר 4
- יעדי Boost אופציונליים מופרדים ברווח, למשל
cursor claude.
דוגמאות יעד
| פריסה | פרמטר 4 של Jamf |
|---|---|
| פריסת סוכן מקומי ברירת מחדל | cursor claude |
| Cursor, Claude ו-Codex | cursor claude codex |
| צוותים 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
מלאי Jamf Pro
הוסיפו Extension Attribute כדי שמלאי Jamf ידווח אם Boost מותקן, איזו גרסה קיימת, איפה נמצא הבינארי, אילו אינטגרציות פעילות ואילו כלי AI זוהו למשתמש הקונסולה.
- שם תצוגה
- Boost CLI Status
- מלאי
- Applications
- סוג נתונים
- String
- סוג קלט
- Script
דוגמאות Smart Group
status=not_installedמוצא Mac ללא בינארי Boost.version=v0.6.4מזהה מכונות בגרסה ישנה יותר.ai_tools contains cursorיחד עםboost_integrations does not contain cursorמוצא משתמשי Cursor בלי hooks של Boost.
הערות תפעול
- המתקין דורש משתמש קונסולה מחובר כי Boost מותקן per-user תחת
$HOME/.local/bin. - אם אין משתמש קונסולה, הסקריפט יוצא בהצלחה בלי להתקין; השתמשו ב-Login trigger לתפוס מאוחר יותר.
- הסקריפט מסיר בינארי ישן
/usr/local/bin/boostכדי ש-shells וה-Extension Attribute יעדיפו התקנה per-user. - לפני
boost init --accept-terms, הסקריפט מתקן תיקיות config של Boost וסוכן בבעלות root מריצות קודמות. - הסקריפט מריץ
jamf reconכש-Jamf זמין כדי שה-Extension Attribute ישקף את ההתקנה מיד.
מסמכים קשורים
להגדרת מפתח בודד, השתמשו ב-התחלה מהירה של Boost. לפרטי זרימת התקנה, ראו מפרט זרימת ההתקנה.