mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-28 18:18:38 +00:00
* refactor: extract agent core package Introduce packages/agent-core as the OpenClaw-owned home for reusable agent loop, harness, session, prompt, and runtime dependency contracts. * refactor: extract shared llm runtime Move provider model registries, stream wrappers, OAuth helpers, and LLM utilities into src/llm with plugin-sdk barrels instead of depending on the old embedded runtime layout. * refactor: remove pi runtime internals Rename remaining Pi-shaped agent surfaces to OpenClaw agent runtime names, delete obsolete Pi docs and package graph checks, and add the third-party notice for incorporated code. * refactor: tighten agent session runtime Make agent-core/runtime dependencies explicit, consolidate compaction and session transcript helpers, and move model/session helpers behind OpenClaw-owned contracts. * refactor: remove static model and pi auth paths Drop static model catalogs and Pi auth bridges, move model/provider facts to manifest-owned runtime contracts, and harden internal embedded-agent utilities. * refactor: remove legacy provider compat paths * docs: remove agent parity notes * fix: skip provider wildcard metadata parsing * refactor: share session extension sdk loading * refactor: inline acpx proxy error formatter * refactor: fold edit recovery into edit tool * fix: accept extension batch separator * test: align startup provider plugin expectations * fix: restore provider-scoped release discovery * test: align static asset packaging expectations * fix: run static provider catalogs during scoped discovery * fix: add provider entry catalogs for scoped live discovery * fix: load lightweight provider catalog entries * fix: refresh provider-scoped plugin metadata * fix: keep provider catalog entries on release live path * fix: keep static manifest models in release live checks * fix: harden release model discovery * fix: reduce OpenAI live cache probe reasoning * fix: disable OpenAI cache probe reasoning * ci: extend OpenAI gateway live timeout * fix: extend live gateway model budget * fix: stabilize release validation regressions * fix: honor provider aliases in model rows * fix: stabilize release validation lanes * fix: stabilize release memory qa * ci: stabilize release validation lanes * ci: prefer ipv4 for live docker node calls * fix: restore shared tool-call stream wrapper * ci: remove legacy pi test shard alias * fix: clean up embedded agent test drift * fix: stabilize runtime alias status * fix: clean up embedded agent ci drift * fix: restore release ci invariants * fix: clean up post-rebase runtime drift * fix: restore release ci checks * fix: restore release ci after rebase * fix: remove stale pi runtime path * test: align compaction runtime expectations * test: update plugin prerelease expectations * fix: handle claude live tool approvals * fix: stabilize release validation gates * fix: finish agent runtime import * test: finish post-rebase agent runtime mocks * fix: keep codex compaction native * fix: stabilize codex app-server hook tests * test: isolate codex diagnostic active run * test: remove codex diagnostic completion race # Conflicts: # extensions/codex/src/app-server/run-attempt.test.ts * ci: fix full release manifest performance run id * refactor: narrow llm plugin sdk boundary * chore: drop generated google boundary stamps * fix: repair rebase fallout * fix: clean up rebased runtime references * fix: decode codex jwt payloads as base64url * fix: preserve shipped pi runtime alias * fix: add scoped sdk virtual modules * fix: decode llm codex oauth jwt as base64url * fix: avoid stale vertex adc negative cache * fix: harden tool arg decoding and codeql path * fix: keep vertex adc negative checks live * refactor: consolidate codex jwt and edit helpers * fix: await codex oauth node runtime imports * fix: preserve sdk tool and notice contracts * fix: preserve shipped compat config boundaries * fix: align codex oauth callback host * fix: terminate agent-core loop streams on failure * fix: keep codex oauth callback alive during fallback * ci: include session tools in critical codeql scans * fix: keep Cloudflare Anthropic provider auth header * docs: redirect legacy pi runtime pages * fix: honor bundled web provider compat discovery * fix: protect session output spill files * fix: keep legacy agent dir env blocked * fix: contain auto-discovered skill symlinks * fix: harden agent core sdk proxy surfaces * fix: restore approval reaction sdk compat * fix: keep live docker runs bounded * fix: keep codex oauth redirect host aligned * fix: resolve post-rebase agent runtime drift * fix: redact anthropic oauth parse failures * fix: preserve responses strict tool shaping * fix: repair agent runtime rebase cleanup * docs: redirect retired parity pages * fix: bound auto-discovered resources to roots * fix: repair post-rebase agent test drift * fix: preserve bundled provider allowlist migration * fix: preserve manifest-owned provider aliases * fix: declare photon image dependency * fix: keep provider headers out of proxy body * fix: preserve shipped env aliases * fix: refresh control ui i18n generated state * fix: quote read fallback paths * fix: preview edits through configured backend * test: satisfy core test typecheck * fix: preserve ZAI usage auth fallback * test: repair codex diagnostic test * fix: repair agent runtime rebase drift * test: finish embedded runner import rename * fix: repair agent runtime rebase integrations * test: align compaction oauth fallback expectations * fix: allow sdk-auth session models * fix: update doctor tool schema import * fix: preserve bedrock plugin region * fix: stream harmony-like prose immediately * ci: include session runtime in codeql shards * fix: repair latest rebase integrations * fix: honor explicit codex websocket transport * fix: keep openai-compatible credentials provider-scoped * fix: refresh sdk api baseline after rebase * fix: route cli runtime aliases through openclaw harness * test: rename stale harness mock expectation * test: rename embedded agent overflow calls * test: clean embedded auth test wording * test: use openclaw stream types in deepinfra cache test * fix: refresh sdk api baseline on latest main * fix: honor bundled discovery compat allowlists * fix: refresh sdk api baseline after latest rebase * fix: remove stale rebase imports * test: rename stale model catalog mock * test: mock renamed doctor runtime modules * fix: map canonical kimi env auth * fix: use internal model registry in bench script * fix: migrate deepinfra provider catalog entry * fix: enforce builtin tool suppression * fix: route compaction auth and proxy payloads safely * refactor: prune unused llm registry leftovers * test: update codex hooks session import * test: fix model picker ci coverage * test: align model picker auth mock types
381 lines
13 KiB
Bash
Executable File
381 lines
13 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Build and bundle OpenClaw into a minimal .app we can open.
|
|
# Outputs to dist/OpenClaw.app
|
|
|
|
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
source "$ROOT_DIR/scripts/lib/plistbuddy.sh"
|
|
APP_ROOT="$ROOT_DIR/dist/OpenClaw.app"
|
|
BUILD_ROOT="$ROOT_DIR/apps/macos/.build"
|
|
PRODUCT="OpenClaw"
|
|
MLX_TTS_HELPER_PRODUCT="openclaw-mlx-tts"
|
|
MLX_TTS_HELPER_ROOT="$ROOT_DIR/apps/macos-mlx-tts"
|
|
MLX_TTS_HELPER_BUILD_ROOT="$MLX_TTS_HELPER_ROOT/.build"
|
|
BUNDLE_ID="${BUNDLE_ID:-ai.openclaw.mac.debug}"
|
|
PKG_VERSION="$(cd "$ROOT_DIR" && node -p "require('./package.json').version" 2>/dev/null || echo "0.0.0")"
|
|
BUILD_TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
GIT_COMMIT=$(cd "$ROOT_DIR" && git rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
|
GIT_BUILD_NUMBER=$(cd "$ROOT_DIR" && git rev-list --count HEAD 2>/dev/null || echo "0")
|
|
APP_VERSION="${APP_VERSION:-$PKG_VERSION}"
|
|
APP_BUILD="${APP_BUILD:-}"
|
|
BUILD_CONFIG="${BUILD_CONFIG:-debug}"
|
|
if [[ -n "${BUILD_ARCHS:-}" ]]; then
|
|
BUILD_ARCHS_VALUE="${BUILD_ARCHS}"
|
|
elif [[ "$BUILD_CONFIG" == "release" ]]; then
|
|
# Release packaging should be universal unless explicitly overridden.
|
|
BUILD_ARCHS_VALUE="all"
|
|
else
|
|
BUILD_ARCHS_VALUE="$(uname -m)"
|
|
fi
|
|
if [[ "${BUILD_ARCHS_VALUE}" == "all" ]]; then
|
|
BUILD_ARCHS_VALUE="arm64 x86_64"
|
|
fi
|
|
IFS=' ' read -r -a BUILD_ARCHS <<< "$BUILD_ARCHS_VALUE"
|
|
PRIMARY_ARCH="${BUILD_ARCHS[0]}"
|
|
SPARKLE_PUBLIC_ED_KEY="${SPARKLE_PUBLIC_ED_KEY:-AGCY8w5vHirVfGGDGc8Szc5iuOqupZSh9pMj/Qs67XI=}"
|
|
SPARKLE_FEED_URL="${SPARKLE_FEED_URL:-https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml}"
|
|
AUTO_CHECKS=true
|
|
if [[ "$BUNDLE_ID" == *.debug ]]; then
|
|
SPARKLE_FEED_URL=""
|
|
AUTO_CHECKS=false
|
|
fi
|
|
|
|
sparkle_canonical_build_from_version() {
|
|
node --import tsx "$ROOT_DIR/scripts/sparkle-build.ts" canonical-build "$1"
|
|
}
|
|
|
|
build_path_for_arch() {
|
|
echo "$BUILD_ROOT/$1"
|
|
}
|
|
|
|
bin_for_arch() {
|
|
echo "$(build_path_for_arch "$1")/$BUILD_CONFIG/$PRODUCT"
|
|
}
|
|
|
|
helper_build_path_for_arch() {
|
|
echo "$MLX_TTS_HELPER_BUILD_ROOT/$1"
|
|
}
|
|
|
|
helper_bin_for_arch() {
|
|
echo "$(helper_build_path_for_arch "$1")/$BUILD_CONFIG/$MLX_TTS_HELPER_PRODUCT"
|
|
}
|
|
|
|
sparkle_framework_for_arch() {
|
|
echo "$(build_path_for_arch "$1")/$BUILD_CONFIG/Sparkle.framework"
|
|
}
|
|
|
|
PNPM_CMD=()
|
|
|
|
resolve_pnpm_cmd() {
|
|
if command -v pnpm >/dev/null 2>&1; then
|
|
PNPM_CMD=(pnpm)
|
|
return 0
|
|
fi
|
|
|
|
if command -v corepack >/dev/null 2>&1 && (cd "$ROOT_DIR" && corepack pnpm --version >/dev/null 2>&1); then
|
|
PNPM_CMD=(corepack pnpm)
|
|
return 0
|
|
fi
|
|
|
|
echo "ERROR: pnpm is not on PATH and corepack pnpm is unavailable. Install pnpm or run with Node/Corepack on PATH." >&2
|
|
exit 1
|
|
}
|
|
|
|
run_pnpm() {
|
|
if [[ "${#PNPM_CMD[@]}" -eq 0 ]]; then
|
|
resolve_pnpm_cmd
|
|
fi
|
|
(cd "$ROOT_DIR" && "${PNPM_CMD[@]}" "$@")
|
|
}
|
|
|
|
merge_framework_machos() {
|
|
local primary="$1"
|
|
local dest="$2"
|
|
shift 2
|
|
local others=("$@")
|
|
|
|
archs_for() {
|
|
/usr/bin/lipo -info "$1" | /usr/bin/sed -E 's/.*are: //; s/.*architecture: //'
|
|
}
|
|
|
|
arch_in_list() {
|
|
local needle="$1"
|
|
shift
|
|
for item in "$@"; do
|
|
if [[ "$item" == "$needle" ]]; then
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
while IFS= read -r -d '' file; do
|
|
if /usr/bin/file "$file" | /usr/bin/grep -q "Mach-O"; then
|
|
local rel="${file#$primary/}"
|
|
local primary_archs
|
|
primary_archs=$(archs_for "$file")
|
|
IFS=' ' read -r -a primary_arch_array <<< "$primary_archs"
|
|
|
|
local missing_files=()
|
|
local tmp_dir
|
|
tmp_dir=$(mktemp -d)
|
|
for fw in "${others[@]}"; do
|
|
local other_file="$fw/$rel"
|
|
if [[ ! -f "$other_file" ]]; then
|
|
echo "ERROR: Missing $rel in $fw" >&2
|
|
rm -rf "$tmp_dir"
|
|
exit 1
|
|
fi
|
|
if /usr/bin/file "$other_file" | /usr/bin/grep -q "Mach-O"; then
|
|
local other_archs
|
|
other_archs=$(archs_for "$other_file")
|
|
IFS=' ' read -r -a other_arch_array <<< "$other_archs"
|
|
for arch in "${other_arch_array[@]}"; do
|
|
if ! arch_in_list "$arch" "${primary_arch_array[@]}"; then
|
|
local thin_file="$tmp_dir/$(echo "$rel" | tr '/' '_')-$arch"
|
|
/usr/bin/lipo -thin "$arch" "$other_file" -output "$thin_file"
|
|
missing_files+=("$thin_file")
|
|
primary_arch_array+=("$arch")
|
|
fi
|
|
done
|
|
fi
|
|
done
|
|
|
|
if [[ "${#missing_files[@]}" -gt 0 ]]; then
|
|
/usr/bin/lipo -create "$file" "${missing_files[@]}" -output "$dest/$rel"
|
|
fi
|
|
rm -rf "$tmp_dir"
|
|
fi
|
|
done < <(find "$primary" -type f -print0)
|
|
}
|
|
|
|
if [[ "${SKIP_PNPM_INSTALL:-0}" != "1" ]]; then
|
|
echo "📦 Ensuring deps (pnpm install --frozen-lockfile)"
|
|
run_pnpm install --frozen-lockfile --config.node-linker=hoisted
|
|
else
|
|
echo "📦 Skipping pnpm install (SKIP_PNPM_INSTALL=1)"
|
|
fi
|
|
|
|
if [[ -z "${APP_BUILD:-}" ]]; then
|
|
APP_BUILD="$GIT_BUILD_NUMBER"
|
|
if [[ "$APP_VERSION" =~ ^[0-9]{4}\.[0-9]{1,2}\.[0-9]{1,2}([.-].*)?$ ]]; then
|
|
CANONICAL_BUILD="$(sparkle_canonical_build_from_version "$APP_VERSION")" || {
|
|
echo "ERROR: Failed to derive canonical Sparkle APP_BUILD from APP_VERSION '$APP_VERSION'." >&2
|
|
exit 1
|
|
}
|
|
if [[ "$CANONICAL_BUILD" =~ ^[0-9]+$ ]] && (( CANONICAL_BUILD > APP_BUILD )); then
|
|
APP_BUILD="$CANONICAL_BUILD"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ "$AUTO_CHECKS" == "true" && ! "$APP_BUILD" =~ ^[0-9]+$ ]]; then
|
|
echo "ERROR: APP_BUILD must be numeric for Sparkle compare (CFBundleVersion). Got: $APP_BUILD" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "${SKIP_TSC:-0}" != "1" ]]; then
|
|
echo "📦 Building JS (pnpm build)"
|
|
run_pnpm build
|
|
else
|
|
echo "📦 Skipping JS build (SKIP_TSC=1)"
|
|
fi
|
|
|
|
if [[ "${SKIP_UI_BUILD:-0}" != "1" ]]; then
|
|
echo "🖥 Building Control UI (ui:build)"
|
|
(cd "$ROOT_DIR" && node scripts/ui.js build)
|
|
else
|
|
echo "🖥 Skipping Control UI build (SKIP_UI_BUILD=1)"
|
|
fi
|
|
|
|
cd "$ROOT_DIR/apps/macos"
|
|
|
|
echo "🔨 Building $PRODUCT ($BUILD_CONFIG) [${BUILD_ARCHS[*]}]"
|
|
for arch in "${BUILD_ARCHS[@]}"; do
|
|
BUILD_PATH="$(build_path_for_arch "$arch")"
|
|
swift build -c "$BUILD_CONFIG" --product "$PRODUCT" --build-path "$BUILD_PATH" --arch "$arch" -Xlinker -rpath -Xlinker @executable_path/../Frameworks
|
|
swift build --package-path "$MLX_TTS_HELPER_ROOT" -c "$BUILD_CONFIG" --product "$MLX_TTS_HELPER_PRODUCT" --build-path "$(helper_build_path_for_arch "$arch")" --arch "$arch"
|
|
done
|
|
|
|
BIN_PRIMARY="$(bin_for_arch "$PRIMARY_ARCH")"
|
|
echo "pkg: binary $BIN_PRIMARY" >&2
|
|
echo "🧹 Cleaning old app bundle"
|
|
rm -rf "$APP_ROOT"
|
|
mkdir -p "$APP_ROOT/Contents/MacOS"
|
|
mkdir -p "$APP_ROOT/Contents/Resources"
|
|
mkdir -p "$APP_ROOT/Contents/Frameworks"
|
|
|
|
echo "📄 Copying Info.plist template"
|
|
INFO_PLIST_SRC="$ROOT_DIR/apps/macos/Sources/OpenClaw/Resources/Info.plist"
|
|
if [ ! -f "$INFO_PLIST_SRC" ]; then
|
|
echo "ERROR: Info.plist template missing at $INFO_PLIST_SRC" >&2
|
|
exit 1
|
|
fi
|
|
cp "$INFO_PLIST_SRC" "$APP_ROOT/Contents/Info.plist"
|
|
plist_set_string_required "$APP_ROOT/Contents/Info.plist" CFBundleIdentifier "$BUNDLE_ID"
|
|
plist_set_string_required "$APP_ROOT/Contents/Info.plist" CFBundleShortVersionString "$APP_VERSION"
|
|
plist_set_string_required "$APP_ROOT/Contents/Info.plist" CFBundleVersion "$APP_BUILD"
|
|
plist_set_string_required "$APP_ROOT/Contents/Info.plist" OpenClawBuildTimestamp "$BUILD_TS"
|
|
plist_set_string_required "$APP_ROOT/Contents/Info.plist" OpenClawGitCommit "$GIT_COMMIT"
|
|
plist_set_or_add_string "$APP_ROOT/Contents/Info.plist" SUFeedURL "$SPARKLE_FEED_URL"
|
|
plist_set_or_add_string "$APP_ROOT/Contents/Info.plist" SUPublicEDKey "$SPARKLE_PUBLIC_ED_KEY"
|
|
plist_set_or_add_bool "$APP_ROOT/Contents/Info.plist" SUEnableAutomaticChecks "$AUTO_CHECKS"
|
|
|
|
echo "🚚 Copying binary"
|
|
cp "$BIN_PRIMARY" "$APP_ROOT/Contents/MacOS/OpenClaw"
|
|
if [[ "${#BUILD_ARCHS[@]}" -gt 1 ]]; then
|
|
BIN_INPUTS=()
|
|
for arch in "${BUILD_ARCHS[@]}"; do
|
|
BIN_INPUTS+=("$(bin_for_arch "$arch")")
|
|
done
|
|
/usr/bin/lipo -create "${BIN_INPUTS[@]}" -output "$APP_ROOT/Contents/MacOS/OpenClaw"
|
|
fi
|
|
chmod +x "$APP_ROOT/Contents/MacOS/OpenClaw"
|
|
# SwiftPM outputs ad-hoc signed binaries; strip the signature before install_name_tool to avoid warnings.
|
|
/usr/bin/codesign --remove-signature "$APP_ROOT/Contents/MacOS/OpenClaw" 2>/dev/null || true
|
|
|
|
echo "🚚 Copying MLX TTS helper"
|
|
cp "$(helper_bin_for_arch "$PRIMARY_ARCH")" "$APP_ROOT/Contents/MacOS/$MLX_TTS_HELPER_PRODUCT"
|
|
if [[ "${#BUILD_ARCHS[@]}" -gt 1 ]]; then
|
|
HELPER_BIN_INPUTS=()
|
|
for arch in "${BUILD_ARCHS[@]}"; do
|
|
HELPER_BIN_INPUTS+=("$(helper_bin_for_arch "$arch")")
|
|
done
|
|
/usr/bin/lipo -create "${HELPER_BIN_INPUTS[@]}" -output "$APP_ROOT/Contents/MacOS/$MLX_TTS_HELPER_PRODUCT"
|
|
fi
|
|
chmod +x "$APP_ROOT/Contents/MacOS/$MLX_TTS_HELPER_PRODUCT"
|
|
/usr/bin/codesign --remove-signature "$APP_ROOT/Contents/MacOS/$MLX_TTS_HELPER_PRODUCT" 2>/dev/null || true
|
|
|
|
SPARKLE_FRAMEWORK_PRIMARY="$(sparkle_framework_for_arch "$PRIMARY_ARCH")"
|
|
if [ -d "$SPARKLE_FRAMEWORK_PRIMARY" ]; then
|
|
echo "✨ Embedding Sparkle.framework"
|
|
cp -R "$SPARKLE_FRAMEWORK_PRIMARY" "$APP_ROOT/Contents/Frameworks/"
|
|
if [[ "${#BUILD_ARCHS[@]}" -gt 1 ]]; then
|
|
OTHER_FRAMEWORKS=()
|
|
for arch in "${BUILD_ARCHS[@]}"; do
|
|
if [[ "$arch" == "$PRIMARY_ARCH" ]]; then
|
|
continue
|
|
fi
|
|
OTHER_FRAMEWORKS+=("$(sparkle_framework_for_arch "$arch")")
|
|
done
|
|
merge_framework_machos "$SPARKLE_FRAMEWORK_PRIMARY" "$APP_ROOT/Contents/Frameworks/Sparkle.framework" "${OTHER_FRAMEWORKS[@]}"
|
|
fi
|
|
chmod -R a+rX "$APP_ROOT/Contents/Frameworks/Sparkle.framework"
|
|
fi
|
|
|
|
echo "📦 Copying Swift 6.2 compatibility libraries"
|
|
SWIFT_COMPAT_LIB="$(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-6.2/macosx/libswiftCompatibilitySpan.dylib"
|
|
if [ -f "$SWIFT_COMPAT_LIB" ]; then
|
|
cp "$SWIFT_COMPAT_LIB" "$APP_ROOT/Contents/Frameworks/"
|
|
chmod +x "$APP_ROOT/Contents/Frameworks/libswiftCompatibilitySpan.dylib"
|
|
else
|
|
echo "WARN: Swift compatibility library not found at $SWIFT_COMPAT_LIB (continuing)" >&2
|
|
fi
|
|
|
|
echo "🖼 Copying app icon"
|
|
cp "$ROOT_DIR/apps/macos/Sources/OpenClaw/Resources/OpenClaw.icns" "$APP_ROOT/Contents/Resources/OpenClaw.icns"
|
|
|
|
echo "📦 Copying device model resources"
|
|
rm -rf "$APP_ROOT/Contents/Resources/DeviceModels"
|
|
cp -R "$ROOT_DIR/apps/macos/Sources/OpenClaw/Resources/DeviceModels" "$APP_ROOT/Contents/Resources/DeviceModels"
|
|
|
|
echo "📦 Copying Control UI assets"
|
|
CONTROL_UI_SRC="$ROOT_DIR/dist/control-ui"
|
|
CONTROL_UI_DEST="$APP_ROOT/Contents/Resources/control-ui"
|
|
if [ -d "$CONTROL_UI_SRC" ] && [ -f "$CONTROL_UI_SRC/index.html" ]; then
|
|
rm -rf "$CONTROL_UI_DEST"
|
|
cp -R "$CONTROL_UI_SRC" "$CONTROL_UI_DEST"
|
|
else
|
|
echo "ERROR: Control UI assets missing at $CONTROL_UI_SRC. Run pnpm ui:build first." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "📦 Copying OpenClawKit resources"
|
|
OPENCLAWKIT_BUNDLE="$(build_path_for_arch "$PRIMARY_ARCH")/$BUILD_CONFIG/OpenClawKit_OpenClawKit.bundle"
|
|
if [ -d "$OPENCLAWKIT_BUNDLE" ]; then
|
|
rm -rf "$APP_ROOT/Contents/Resources/OpenClawKit_OpenClawKit.bundle"
|
|
cp -R "$OPENCLAWKIT_BUNDLE" "$APP_ROOT/Contents/Resources/OpenClawKit_OpenClawKit.bundle"
|
|
else
|
|
echo "ERROR: OpenClawKit resource bundle not found at $OPENCLAWKIT_BUNDLE" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "📦 Copying Textual resources"
|
|
TEXTUAL_BUNDLE_DIR="$(build_path_for_arch "$PRIMARY_ARCH")/$BUILD_CONFIG"
|
|
TEXTUAL_BUNDLE=""
|
|
for candidate in \
|
|
"$TEXTUAL_BUNDLE_DIR/textual_Textual.bundle" \
|
|
"$TEXTUAL_BUNDLE_DIR/Textual_Textual.bundle"
|
|
do
|
|
if [ -d "$candidate" ]; then
|
|
TEXTUAL_BUNDLE="$candidate"
|
|
break
|
|
fi
|
|
done
|
|
if [ -z "$TEXTUAL_BUNDLE" ]; then
|
|
TEXTUAL_BUNDLE="$(find "$BUILD_ROOT" -type d \( -name "textual_Textual.bundle" -o -name "Textual_Textual.bundle" \) -print -quit)"
|
|
fi
|
|
if [ -n "$TEXTUAL_BUNDLE" ] && [ -d "$TEXTUAL_BUNDLE" ]; then
|
|
rm -rf "$APP_ROOT/Contents/Resources/$(basename "$TEXTUAL_BUNDLE")"
|
|
cp -R "$TEXTUAL_BUNDLE" "$APP_ROOT/Contents/Resources/"
|
|
else
|
|
if [[ "${ALLOW_MISSING_TEXTUAL_BUNDLE:-0}" == "1" ]]; then
|
|
echo "WARN: Textual resource bundle not found (continuing due to ALLOW_MISSING_TEXTUAL_BUNDLE=1)" >&2
|
|
else
|
|
echo "ERROR: Textual resource bundle not found. Set ALLOW_MISSING_TEXTUAL_BUNDLE=1 to bypass." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
running_packaged_app_pids() {
|
|
command -v pgrep >/dev/null 2>&1 || return 0
|
|
local app_binary="$APP_ROOT/Contents/MacOS/OpenClaw"
|
|
local pid
|
|
pgrep -x "$PRODUCT" 2>/dev/null | while IFS= read -r pid; do
|
|
[[ "$pid" =~ ^[0-9]+$ ]] || continue
|
|
if command -v lsof >/dev/null 2>&1 &&
|
|
lsof -a -p "$pid" -d txt -Fn 2>/dev/null | sed 's/^n//' | grep -Fx "$app_binary" >/dev/null; then
|
|
printf '%s\n' "$pid"
|
|
continue
|
|
fi
|
|
local command_line
|
|
command_line="$(ps -p "$pid" -o command= 2>/dev/null || true)"
|
|
if [[ "$command_line" == "$app_binary" || "$command_line" == "$app_binary "* ]]; then
|
|
printf '%s\n' "$pid"
|
|
fi
|
|
done
|
|
}
|
|
|
|
stop_packaged_app_if_running() {
|
|
local pids=()
|
|
local pid
|
|
while IFS= read -r pid; do
|
|
[[ -n "$pid" ]] && pids+=("$pid")
|
|
done < <(running_packaged_app_pids)
|
|
if [[ "${#pids[@]}" -eq 0 ]]; then
|
|
return 0
|
|
fi
|
|
|
|
echo "⏹ Stopping packaged OpenClaw bundle (${pids[*]})"
|
|
kill "${pids[@]}" 2>/dev/null || true
|
|
for _ in $(seq 1 40); do
|
|
local alive=0
|
|
for pid in "${pids[@]}"; do
|
|
if kill -0 "$pid" 2>/dev/null; then
|
|
alive=1
|
|
fi
|
|
done
|
|
[[ "$alive" == "0" ]] && return 0
|
|
sleep 0.25
|
|
done
|
|
kill -KILL "${pids[@]}" 2>/dev/null || true
|
|
}
|
|
|
|
stop_packaged_app_if_running
|
|
|
|
echo "🔏 Signing bundle (auto-selects signing identity if SIGN_IDENTITY is unset)"
|
|
"$ROOT_DIR/scripts/codesign-mac-app.sh" "$APP_ROOT"
|
|
|
|
echo "✅ Bundle ready at $APP_ROOT"
|