feat(macos): prompt for CLI install

This commit is contained in:
Peter Steinberger
2026-01-11 10:15:37 +00:00
parent 7551415db9
commit 6d2928888c
25 changed files with 204 additions and 602 deletions

View File

@@ -22,7 +22,6 @@ if [[ "${BUILD_ARCHS_VALUE}" == "all" ]]; then
fi
IFS=' ' read -r -a BUILD_ARCHS <<< "$BUILD_ARCHS_VALUE"
PRIMARY_ARCH="${BUILD_ARCHS[0]}"
BUNDLED_RUNTIME="${BUNDLED_RUNTIME:-node}"
SPARKLE_PUBLIC_ED_KEY="${SPARKLE_PUBLIC_ED_KEY:-AGCY8w5vHirVfGGDGc8Szc5iuOqupZSh9pMj/Qs67XI=}"
SPARKLE_FEED_URL="${SPARKLE_FEED_URL:-https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml}"
AUTO_CHECKS=true
@@ -108,220 +107,6 @@ merge_framework_machos() {
done < <(find "$primary" -type f -print0)
}
build_relay_binary() {
local arch="$1"
local out="$2"
local define_arg="__CLAWDBOT_VERSION__=\\\"$PKG_VERSION\\\""
local bun_bin="bun"
local -a cmd=("$bun_bin" build "$ROOT_DIR/dist/macos/relay.js" --compile --bytecode --outfile "$out" -e electron --define "$define_arg")
if [[ "$arch" == "x86_64" ]]; then
if ! arch -x86_64 /usr/bin/true >/dev/null 2>&1; then
echo "ERROR: Rosetta is required to build the x86_64 relay. Install Rosetta and retry." >&2
exit 1
fi
local bun_x86="${BUN_X86_64_BIN:-$HOME/.bun-x64/bun-darwin-x64/bun}"
if [[ ! -x "$bun_x86" ]]; then
bun_x86="$HOME/.bun-x64/bin/bun"
fi
if [[ "$bun_x86" == *baseline* ]]; then
echo "ERROR: x86_64 relay builds are locked to AVX2; baseline Bun is not allowed." >&2
echo "Set BUN_X86_64_BIN to a non-baseline Bun (bun-darwin-x64)." >&2
exit 1
fi
if [[ -x "$bun_x86" ]]; then
cmd=("$bun_x86" build "$ROOT_DIR/dist/macos/relay.js" --compile --bytecode --outfile "$out" -e electron --define "$define_arg")
fi
arch -x86_64 "${cmd[@]}"
else
"${cmd[@]}"
fi
}
resolve_node_version() {
if [[ -n "${NODE_VERSION:-}" ]]; then
echo "${NODE_VERSION#v}"
return
fi
local mirror="${NODE_DIST_MIRROR:-https://nodejs.org/dist}"
local latest
if latest="$(/usr/bin/curl -fsSL "$mirror/index.tab" 2>/dev/null | /usr/bin/awk 'NR==2 {print $1}')" && [[ -n "$latest" ]]; then
echo "${latest#v}"
return
fi
if command -v node >/dev/null 2>&1; then
node -p "process.versions.node"
return
fi
echo "22.12.0"
}
node_dist_filename() {
local version="$1"
local arch="$2"
local node_arch="$arch"
if [[ "$arch" == "x86_64" ]]; then
node_arch="x64"
fi
echo "node-v${version}-darwin-${node_arch}.tar.gz"
}
download_node_binary() {
local version="$1"
local arch="$2"
local out="$3"
local mirror="${NODE_DIST_MIRROR:-https://nodejs.org/dist}"
local tarball
tarball="$(node_dist_filename "$version" "$arch")"
local tmp_dir
tmp_dir="$(mktemp -d)"
local url="$mirror/v${version}/${tarball}"
echo "⬇️ Downloading Node ${version} (${arch})"
/usr/bin/curl -fsSL "$url" -o "$tmp_dir/node.tgz"
/usr/bin/tar -xzf "$tmp_dir/node.tgz" -C "$tmp_dir"
local node_arch="$arch"
if [[ "$arch" == "x86_64" ]]; then
node_arch="x64"
fi
local node_src="$tmp_dir/node-v${version}-darwin-${node_arch}/bin/node"
if [[ ! -f "$node_src" ]]; then
echo "ERROR: Node binary missing in $tarball" >&2
rm -rf "$tmp_dir"
exit 1
fi
cp "$node_src" "$out"
chmod +x "$out"
rm -rf "$tmp_dir"
}
stage_relay_deps() {
local relay_dir="$1"
if [[ "${SKIP_RELAY_DEPS:-0}" == "1" ]]; then
echo "📦 Skipping relay dependency staging (SKIP_RELAY_DEPS=1)"
return
fi
local stage_dir="$relay_dir/.relay-deploy"
rm -rf "$stage_dir"
mkdir -p "$stage_dir"
echo "📦 Staging relay dependencies (pnpm deploy --prod --legacy)"
(cd "$ROOT_DIR" && pnpm --filter . deploy "$stage_dir" --prod --legacy)
rm -rf "$relay_dir/node_modules"
cp -a "$stage_dir/node_modules" "$relay_dir/node_modules"
rm -rf "$stage_dir"
}
stage_relay_dist() {
local relay_dir="$1"
echo "📦 Copying relay dist payload"
rm -rf "$relay_dir/dist"
mkdir -p "$relay_dir/dist"
# Only ship runtime JS payload; exclude build artifacts (app/zips/dmgs) to avoid
# recursive bundling and notarization failures.
/usr/bin/rsync -a --delete \
--exclude 'Clawdbot.app' \
--exclude 'Clawdbot-*.zip' \
--exclude 'Clawdbot-*.dmg' \
--exclude 'Clawdbot-*.notary.zip' \
--exclude 'Clawdbot-*.dSYM.zip' \
--exclude 'Clawdbot-*.dSYM' \
"$ROOT_DIR/dist/" "$relay_dir/dist/"
}
stage_relay_payload() {
local relay_dir="$1"
stage_relay_deps "$relay_dir"
stage_relay_dist "$relay_dir"
}
write_relay_wrapper() {
local relay_dir="$1"
local wrapper="$relay_dir/clawdbot"
cat > "$wrapper" <<SH
#!/bin/sh
set -e
DIR="\$(cd "\$(dirname "\$0")" && pwd)"
NODE="\$DIR/node"
REL="\$DIR/dist/macos/relay.js"
export CLAWDBOT_BUNDLED_VERSION="\${CLAWDBOT_BUNDLED_VERSION:-$PKG_VERSION}"
export CLAWDBOT_IMAGE_BACKEND="\${CLAWDBOT_IMAGE_BACKEND:-sips}"
NODE_PATH="\$DIR/node_modules\${NODE_PATH:+:\$NODE_PATH}"
export NODE_PATH
exec "\$NODE" "\$REL" "\$@"
SH
chmod +x "$wrapper"
}
package_relay_bun() {
local relay_dir="$1"
RELAY_CMD="$relay_dir/clawdbot"
if ! command -v bun >/dev/null 2>&1; then
echo "ERROR: bun missing. Install bun or set BUNDLED_RUNTIME=node." >&2
exit 1
fi
echo "🧰 Building bundled relay (bun --compile)"
local relay_build_dir="$relay_dir/.relay-build"
rm -rf "$relay_build_dir"
mkdir -p "$relay_build_dir"
for arch in "${BUILD_ARCHS[@]}"; do
local relay_arch_out="$relay_build_dir/clawdbot-$arch"
build_relay_binary "$arch" "$relay_arch_out"
chmod +x "$relay_arch_out"
done
if [[ "${#BUILD_ARCHS[@]}" -gt 1 ]]; then
/usr/bin/lipo -create "$relay_build_dir"/clawdbot-* -output "$RELAY_CMD"
else
cp "$relay_build_dir/clawdbot-${BUILD_ARCHS[0]}" "$RELAY_CMD"
fi
rm -rf "$relay_build_dir"
}
package_relay_node() {
local relay_dir="$1"
RELAY_CMD="$relay_dir/clawdbot"
local node_version
node_version="$(resolve_node_version)"
echo "🧰 Preparing bundled Node runtime (v${node_version})"
local relay_node="$relay_dir/node"
local relay_node_build_dir="$relay_dir/.node-build"
rm -rf "$relay_node_build_dir"
mkdir -p "$relay_node_build_dir"
for arch in "${BUILD_ARCHS[@]}"; do
local node_arch_out="$relay_node_build_dir/node-$arch"
download_node_binary "$node_version" "$arch" "$node_arch_out"
done
if [[ "${#BUILD_ARCHS[@]}" -gt 1 ]]; then
/usr/bin/lipo -create "$relay_node_build_dir"/node-* -output "$relay_node"
else
cp "$relay_node_build_dir/node-${BUILD_ARCHS[0]}" "$relay_node"
fi
chmod +x "$relay_node"
if [[ "${STRIP_NODE:-0}" == "1" ]]; then
/usr/bin/strip -x "$relay_node" 2>/dev/null || true
fi
rm -rf "$relay_node_build_dir"
stage_relay_payload "$relay_dir"
write_relay_wrapper "$relay_dir"
}
validate_bundled_runtime() {
case "$BUNDLED_RUNTIME" in
node|bun) return 0 ;;
*)
echo "ERROR: Unsupported BUNDLED_RUNTIME=$BUNDLED_RUNTIME (use node|bun)" >&2
exit 1
;;
esac
}
echo "📦 Ensuring deps (pnpm install)"
(cd "$ROOT_DIR" && pnpm install --no-frozen-lockfile --config.node-linker=hoisted)
if [[ "${SKIP_TSC:-0}" != "1" ]]; then
@@ -352,7 +137,6 @@ 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/Resources/Relay"
mkdir -p "$APP_ROOT/Contents/Frameworks"
echo "📄 Copying Info.plist template"
@@ -432,61 +216,6 @@ else
echo "WARN: ClawdbotKit resource bundle not found at $CLAWDBOTKIT_BUNDLE (continuing)" >&2
fi
RELAY_DIR="$APP_ROOT/Contents/Resources/Relay"
if [[ "${SKIP_GATEWAY_PACKAGE:-0}" != "1" ]]; then
validate_bundled_runtime
mkdir -p "$RELAY_DIR"
if [[ "$BUNDLED_RUNTIME" == "bun" ]]; then
package_relay_bun "$RELAY_DIR"
else
package_relay_node "$RELAY_DIR"
fi
echo "🧪 Verifying bundled relay (version)"
"$RELAY_CMD" --version >/dev/null
echo "🎨 Copying gateway A2UI host assets"
rm -rf "$RELAY_DIR/a2ui"
cp -R "$ROOT_DIR/src/canvas-host/a2ui" "$RELAY_DIR/a2ui"
echo "🎛 Copying Control UI assets"
rm -rf "$RELAY_DIR/control-ui"
cp -R "$ROOT_DIR/dist/control-ui" "$RELAY_DIR/control-ui"
echo "🧠 Copying bundled skills"
rm -rf "$RELAY_DIR/skills"
cp -R "$ROOT_DIR/skills" "$RELAY_DIR/skills"
echo "📄 Writing embedded runtime package.json (Pi compatibility)"
cat > "$RELAY_DIR/package.json" <<JSON
{
"name": "clawdbot-embedded",
"version": "$PKG_VERSION",
"type": "module",
"piConfig": {
"name": "pi",
"configDir": ".pi"
}
}
JSON
echo "🎨 Copying Pi theme payload (optional)"
PI_ENTRY_URL="$(cd "$ROOT_DIR" && node --input-type=module -e "console.log(import.meta.resolve('@mariozechner/pi-coding-agent'))")"
PI_ENTRY="$(cd "$ROOT_DIR" && node --input-type=module -e "console.log(new URL(process.argv[1]).pathname)" "$PI_ENTRY_URL")"
PI_DIR="$(cd "$(dirname "$PI_ENTRY")/.." && pwd)"
THEME_SRC="$PI_DIR/dist/modes/interactive/theme"
if [ -d "$THEME_SRC" ]; then
rm -rf "$RELAY_DIR/theme"
cp -R "$THEME_SRC" "$RELAY_DIR/theme"
else
echo "WARN: Pi theme dir missing at $THEME_SRC (continuing)" >&2
fi
else
echo "🧰 Skipping gateway payload packaging (SKIP_GATEWAY_PACKAGE=1)"
fi
echo "⏹ Stopping any running Clawdbot"
killall -q Clawdbot 2>/dev/null || true