גרסה חדשהv0.12.4Aug 19, 2026

גרסה חדשה של Boost זמינהSmarter savings and cleaner output

$ customer-installation

פריסת Boost עם Jamf Pro MDM

השתמשו בנכסי Jamf Pro אלה להתקנת Boost בצי macOS מנוהל ולמלאי התוצאה. הסקריפטים נמצאים תחת docs/customer-installation/mdm/jamf-pro; עמוד זה מציג אותם ישירות כך שעריכות במאגר מעדכנות את האתר ב-build הבא.

סביבה שנבדקה

נתיב פריסה זה נבדק עם Jamf Pro. ספקי MDM עתידיים צריכים לעקוב אחר אותה פריסת קבצים תחת docs/customer-installation/mdm/<provider>.


איך הפריסה עובדת

1

הוספת סקריפט ההתקנה

צרו סקריפט Jamf Pro מ-install.sh. הוא מתקין Boost למשתמש הקונסולה, מאתחל אינטגרציות סוכן שנבחרו ומרענן מלאי Jamf.

2

יצירת מדיניות פריסה

הריצו את הסקריפט כ-root על ה-Mac היעד. השתמשו בפרמטר 4 של Jamf לבחירת אינטגרציות Boost ושלבו Login trigger ל-Mac בלי משתמש קונסולה פעיל.

3

הוספת 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 ו-Codexcursor claude codex
צוותים CLI-firstcodex
docs/customer-installation/mdm/jamf-pro/install.sh
#!/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)"
fi

Raw source: install.sh

docs/customer-installation/mdm/jamf-pro/extension-attribute.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. לפרטי זרימת התקנה, ראו מפרט זרימת ההתקנה.