mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-28 04:24:51 +02:00
Merge remote-tracking branch 'origin/eric/dev' into eric/browser-merged
This commit is contained in:
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
# One-time castlabs EVS setup, so a Mac release never stalls on missing Widevine creds again.
|
||||
#
|
||||
# EVS signs the packaged app so Widevine DRM works, which is what makes Spotify and Netflix play in
|
||||
# the embedded browser. scripts/build-app.sh hard-fails without it rather than shipping a build
|
||||
# whose DRM is quietly dead.
|
||||
#
|
||||
# What this does: creates (or reuses) an EVS account, stores the password in your login keychain,
|
||||
# and drops a loader into your shell profile so every future terminal already has it. Run it once.
|
||||
#
|
||||
# bash scripts/setup-evs.sh
|
||||
#
|
||||
# The password is read with `read -s`, never passed as an argument, so it stays out of `ps` and
|
||||
# your shell history.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
KEYCHAIN_SERVICE="openswarm-evs"
|
||||
VENV="$HOME/.openswarm-evs-venv"
|
||||
PROFILE="${ZDOTDIR:-$HOME}/.zshrc"
|
||||
|
||||
echo "==> castlabs EVS setup"
|
||||
echo
|
||||
|
||||
if [[ ! -x "$VENV/bin/python" ]]; then
|
||||
echo "installing the castlabs-evs client into $VENV ..."
|
||||
python3 -m venv "$VENV"
|
||||
"$VENV/bin/pip" install -q --upgrade pip castlabs-evs
|
||||
fi
|
||||
EVS="$VENV/bin/python -m castlabs_evs.account"
|
||||
|
||||
read -r -p "EVS account name (an email; use a NEW one if you're creating a fresh account): " ACCOUNT
|
||||
read -r -s -p "EVS password (pick a strong one; it is never echoed): " PASSWD
|
||||
echo
|
||||
echo
|
||||
|
||||
echo "1) Do you already have an EVS account with that name?"
|
||||
echo " [n] no, create one [y] yes, I know the password [r] yes, but reset it"
|
||||
read -r -p "> " CHOICE
|
||||
|
||||
case "$CHOICE" in
|
||||
n|N)
|
||||
read -r -p "First name: " FIRST
|
||||
read -r -p "Last name: " LAST
|
||||
read -r -p "Organization: " ORG
|
||||
# signup prompts for the emailed code itself, so do NOT ask again afterwards.
|
||||
$EVS signup -A "$ACCOUNT" -P "$PASSWD" -E "$ACCOUNT" \
|
||||
-F "$FIRST" -L "$LAST" -O "$ORG"
|
||||
;;
|
||||
r|R)
|
||||
$EVS reset -A "$ACCOUNT"
|
||||
read -r -p "Confirmation code from your email: " CODE
|
||||
$EVS confirm-reset -A "$ACCOUNT" -C "$CODE" -P "$PASSWD"
|
||||
;;
|
||||
*)
|
||||
echo "using the existing account as-is"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo
|
||||
echo "2) proving the credentials actually work ..."
|
||||
if ! EVS_ACCOUNT_NAME="$ACCOUNT" EVS_PASSWD="$PASSWD" $EVS reauth >/dev/null 2>&1; then
|
||||
echo " FAILED: EVS rejected that account/password pair. Nothing was saved."
|
||||
exit 1
|
||||
fi
|
||||
echo " authenticated."
|
||||
|
||||
echo
|
||||
echo "3) storing the password in your login keychain ..."
|
||||
security delete-generic-password -s "$KEYCHAIN_SERVICE" -a "$ACCOUNT" >/dev/null 2>&1 || true
|
||||
security add-generic-password -s "$KEYCHAIN_SERVICE" -a "$ACCOUNT" -w "$PASSWD" -U
|
||||
echo " stored (service=$KEYCHAIN_SERVICE account=$ACCOUNT)"
|
||||
|
||||
MARK="# openswarm: castlabs EVS creds for signed Mac releases"
|
||||
if ! grep -qF "$MARK" "$PROFILE" 2>/dev/null; then
|
||||
echo "4) adding a loader to $PROFILE ..."
|
||||
{
|
||||
echo ""
|
||||
echo "$MARK"
|
||||
echo "export EVS_ACCOUNT_NAME='$ACCOUNT'"
|
||||
echo "export EVS_PASSWD=\"\$(security find-generic-password -s $KEYCHAIN_SERVICE -a '$ACCOUNT' -w 2>/dev/null)\""
|
||||
echo "export APPLE_TEAM_ID=Y26NUZH4NG"
|
||||
} >> "$PROFILE"
|
||||
echo " added."
|
||||
else
|
||||
echo "4) $PROFILE already loads them; leaving it alone."
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Done. Open a NEW terminal, then:"
|
||||
echo " GH_TOKEN=\$(gh auth token) bash publish.sh"
|
||||
echo
|
||||
echo "Windows CI keeps its own copy, so if you changed the password, also run:"
|
||||
echo " gh secret set EVS_ACCOUNT_NAME --body '$ACCOUNT'"
|
||||
echo " gh secret set EVS_PASSWD --body '<the password you just chose>'"
|
||||
Executable
+126
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env bash
|
||||
# Smoke a SIGNED, NOTARIZED Mac build the way a user receives it.
|
||||
#
|
||||
# "Works in dev" has repeatedly not meant "works packaged" here: dictation died in prod because a
|
||||
# Finder-launched app inherits a PATH with no brew, and the bundled Python and 9Router live at
|
||||
# different paths than dev. So this runs the real .app out of the real DMG, dequarantined the way
|
||||
# a download would be, and checks the things that have actually broken before.
|
||||
#
|
||||
# bash scripts/smoke-packaged-mac.sh path/to/OpenSwarm-arm64.dmg
|
||||
#
|
||||
# Exits non-zero on the first hard failure. Every check prints PASS or FAIL with what it saw, so a
|
||||
# red line is a finding and not a puzzle.
|
||||
|
||||
set -uo pipefail
|
||||
DMG="${1:?usage: smoke-packaged-mac.sh <path-to-dmg>}"
|
||||
MNT="/tmp/osw-smoke-$$"
|
||||
APP=""
|
||||
PASS=0
|
||||
FAIL=0
|
||||
|
||||
ok() { PASS=$((PASS+1)); printf " PASS %s%s\n" "$1" "${2:+ ($2)}"; }
|
||||
bad() { FAIL=$((FAIL+1)); printf " FAIL %s%s\n" "$1" "${2:+ ($2)}"; }
|
||||
step() { printf "\n=== %s ===\n" "$1"; }
|
||||
|
||||
cleanup() {
|
||||
# Order matters and so does patience: the app holds the volume open, and rm-ing a still-mounted
|
||||
# DMG spews hundreds of "Read-only file system" lines that bury the actual results.
|
||||
pkill -f "/tmp/osw-smoke-run-$$/OpenSwarm.app" 2>/dev/null
|
||||
[ -n "${APP:-}" ] && pkill -f "$MNT/OpenSwarm.app" 2>/dev/null
|
||||
sleep 2
|
||||
hdiutil detach "$MNT" -force -quiet 2>/dev/null || hdiutil detach "$MNT" -quiet 2>/dev/null
|
||||
mount | grep -q "$MNT" || rmdir "$MNT" 2>/dev/null
|
||||
rm -rf "/tmp/osw-smoke-run-$$"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
step "0. Nothing else is already pretending to be OpenSwarm"
|
||||
# An OpenSwarm that is already up owns the single-instance lock, so the copy under test quits the
|
||||
# instant it launches and step 5 reports "the backend never answered". It answered fine; you were
|
||||
# just talking to nobody. Refuse to run rather than hand back a scary lie.
|
||||
STRAY=$(pgrep -f "OpenSwarm.app/Contents/MacOS/OpenSwarm" | tr '\n' ' ')
|
||||
if [ -n "${STRAY// /}" ]; then
|
||||
bad "another OpenSwarm is running" "pids: $STRAY -- kill it, then re-run"
|
||||
exit 1
|
||||
fi
|
||||
ok "no other OpenSwarm running"
|
||||
|
||||
step "1. Mount the DMG the way a download arrives"
|
||||
mkdir -p "$MNT"
|
||||
if hdiutil attach "$DMG" -mountpoint "$MNT" -nobrowse -quiet; then
|
||||
ok "mounted" "$(basename "$DMG")"
|
||||
else
|
||||
bad "could not mount the DMG"; exit 1
|
||||
fi
|
||||
APP="$MNT/OpenSwarm.app"
|
||||
[ -d "$APP" ] && ok "OpenSwarm.app present" || { bad "no .app inside the DMG"; exit 1; }
|
||||
|
||||
step "2. Signing, notarization and DRM"
|
||||
codesign --verify --deep --strict "$APP" 2>/dev/null && ok "codesign valid" || bad "codesign INVALID"
|
||||
# -dvv prints the Authority chain; --requirements prints the requirement string, which does NOT
|
||||
# contain the authority name and made this read as unsigned on a correctly signed build.
|
||||
AUTH=$(codesign -dvv "$APP" 2>&1 | grep -m1 "^Authority=")
|
||||
grep -q "Developer ID Application" <<<"$AUTH" \
|
||||
&& ok "signed with a Developer ID" "${AUTH#Authority=}" || bad "not a Developer ID signature" "$AUTH"
|
||||
SPCTL=$(spctl -a -vvv -t install "$APP" 2>&1 | tr '\n' ' ')
|
||||
grep -q "Notarized Developer ID" <<<"$SPCTL" && ok "notarized" || bad "NOT notarized" "$SPCTL"
|
||||
xcrun stapler validate "$APP" >/dev/null 2>&1 && ok "notarization stapled" || bad "staple missing"
|
||||
# The Widevine signature is what makes Spotify/Netflix play in the embedded browser. Shipped builds
|
||||
# carried a DEVELOPMENT certificate for a month because sign-pkg was handed the wrong path.
|
||||
FW="$APP/Contents/Frameworks/Electron Framework.framework"
|
||||
[ -f "$FW/Resources/Electron Framework.sig" ] \
|
||||
&& ok "Widevine VMP signature present" || bad "no VMP signature (DRM will be dead)"
|
||||
|
||||
step "3. The version and the code actually inside the bundle"
|
||||
VER=$(defaults read "$APP/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null)
|
||||
[ -n "$VER" ] && ok "version" "$VER" || bad "no version in Info.plist"
|
||||
RES="$APP/Contents/Resources"
|
||||
# The build is only worth smoking if it contains the fixes it claims to.
|
||||
grep -rq "pending_continuation" "$RES/backend/apps/agents/manager/run/TurnRunner.py" 2>/dev/null \
|
||||
&& ok "MCP activation hard-stop is in the bundle" \
|
||||
|| bad "MCP hard-stop MISSING (stale build)"
|
||||
grep -rq "lend_credential_for_cloud" "$RES/backend/apps/workflows/cloud/handover.py" 2>/dev/null \
|
||||
&& ok "cloud credential lease wiring is in the bundle" \
|
||||
|| bad "credential lease wiring MISSING (cloud runs cannot work)"
|
||||
grep -rq "sign-in has expired" "$RES/backend/apps/tools_lib/mcp_failure_reason.py" 2>/dev/null \
|
||||
&& ok "readable MCP failures are in the bundle" \
|
||||
|| bad "MCP failure translation MISSING"
|
||||
|
||||
step "4. Bundled runtimes, at their packaged paths"
|
||||
PY=$(ls -d "$RES/python-env/bin/python3"* 2>/dev/null | head -1)
|
||||
[ -n "$PY" ] && ok "bundled Python present" "$(basename "$PY")" || bad "no bundled Python"
|
||||
[ -n "$PY" ] && { "$PY" -c "import fastapi, anthropic" 2>/dev/null \
|
||||
&& ok "bundled Python imports its deps" || bad "bundled Python cannot import fastapi/anthropic"; }
|
||||
ls "$RES/router" >/dev/null 2>&1 && ok "9Router bundled" || bad "9Router missing from Resources"
|
||||
# The dictation regression: whisper shelled out to ffmpeg at boot, and a Finder launch has no brew.
|
||||
grep -rq -- "--convert" "$RES/backend/apps" 2>/dev/null \
|
||||
&& bad "whisper --convert is back (dictation dies without brew on PATH)" \
|
||||
|| ok "no whisper --convert (the prod dictation killer)"
|
||||
|
||||
step "5. Launch it with a Finder-like PATH and see the backend come up"
|
||||
# Copy it off the DMG first, because that is what a user does and because running from the
|
||||
# read-only volume makes the auto-updater throw and take the whole app down about a second in,
|
||||
# which reads as "the backend never started" and is nothing of the sort.
|
||||
RUNDIR="/tmp/osw-smoke-run-$$"
|
||||
rm -rf "$RUNDIR"; mkdir -p "$RUNDIR"
|
||||
cp -R "$APP" "$RUNDIR/" && ok "copied to a writable volume" || bad "could not copy the app off the DMG"
|
||||
RUNAPP="$RUNDIR/OpenSwarm.app"
|
||||
xattr -dr com.apple.quarantine "$RUNAPP" 2>/dev/null
|
||||
PATH="/usr/bin:/bin:/usr/sbin:/sbin" "$RUNAPP/Contents/MacOS/OpenSwarm" >/tmp/osw-smoke.log 2>&1 &
|
||||
LAUNCHED=$!
|
||||
BOOTED=0
|
||||
for _ in $(seq 1 60); do
|
||||
sleep 2
|
||||
curl -s -m 3 -o /dev/null "http://127.0.0.1:8324/api/settings" && { BOOTED=1; break; }
|
||||
kill -0 "$LAUNCHED" 2>/dev/null || break
|
||||
done
|
||||
if [ "$BOOTED" = 1 ]; then
|
||||
ok "backend answered on :8324 from a brew-less PATH"
|
||||
else
|
||||
bad "backend never answered" "see /tmp/osw-smoke.log"
|
||||
fi
|
||||
kill "$LAUNCHED" 2>/dev/null
|
||||
|
||||
printf "\n%s\n" "$(printf '=%.0s' {1..60})"
|
||||
printf "PACKAGED SMOKE: %d passed, %d failed\n" "$PASS" "$FAIL"
|
||||
[ "$FAIL" -eq 0 ] || exit 1
|
||||
Reference in New Issue
Block a user