Folds the last unmerged workshop work into main: - ResiliencePanel — the failure-injection 'theater' UI (cloud outage → on-board Qwen live demo) that visualizes the [agents.chaos] (custom.dead → llamacpp) failover, plus Module2 integration + api/nodes support. - build-deploy.sh — one-command aarch64-musl cross-build + adb deploy helper, REFRESHED for 0.8.3: adds the web SPA prebuild step and the full feature set (hardware, peripheral-rpi, embedded-web, gateway-voice-duplex) + the embedded-web/web_dist_dir gotcha. Config conflict resolved as a union: the branch's custom.dead + [agents.chaos] demo blocks alongside this session's channels/voice/skills/risk-profile edits. Verified: tsc clean, web 229 + api 58 tests green. Co-Authored-By: Claude Opus 4.8 <[email protected]>
104 lines
5.9 KiB
Bash
Executable File
104 lines
5.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# build-deploy.sh — cross-build ZeroClaw for the Arduino Uno Q (aarch64) on this
|
|
# Mac and deploy it to the board over adb, as a proper drop-in replacement for
|
|
# /home/arduino/zeroclaw. One command for the "make a fix → get it on the board"
|
|
# loop.
|
|
#
|
|
# WHY THE FLAGS (learned the hard way — see memory apess-node-modalities-and-083):
|
|
# hardware MANDATORY. Without it the build silently drops the 10
|
|
# Uno Q peripheral tools (GPIO bridge, FLASH, sysfs_led,
|
|
# camera, network, i2cdetect). Symptom: agent load log
|
|
# shows "before:48" tools instead of "before:58".
|
|
# peripheral-rpi the rppal-backed Linux peripheral path (sysfs LED, etc.).
|
|
# embedded-web bakes the web dashboard SPA (web/dist) into the binary,
|
|
# so the board serves the ZeroClaw dashboard at :8080/.
|
|
# REQUIRES web/dist to be built FIRST (step 1) — and built
|
|
# with base="/_app/" (the production vite build), else the
|
|
# gateway serves index.html for /assets/* and the SPA won't
|
|
# boot. If the embed ever goes stale, set
|
|
# `[gateway] web_dist_dir = "/home/arduino/web-dist"` and
|
|
# push web/dist to the board as a runtime fallback.
|
|
# zeroclaw-gateway/gateway-voice-duplex
|
|
# browser-mic streaming voice over /ws/chat (namespaced —
|
|
# no root-crate alias).
|
|
# musl target the deployed binary is static (glibc-independent).
|
|
# cargo-zigbuild cross-links from macOS via zig (no cross toolchain).
|
|
# (Alt without zig: cargo build with
|
|
# CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-musl-gcc.)
|
|
#
|
|
# PREREQS (already set up on this Mac): rustup targets aarch64-unknown-linux-musl,
|
|
# cargo-zigbuild + zig on PATH, node/npm for the web build, adb, the board on USB.
|
|
#
|
|
# Usage:
|
|
# deploy/uno-q/build-deploy.sh # build + deploy + verify
|
|
# ZC_REPO=/path/to/zeroclaw deploy/uno-q/build-deploy.sh
|
|
# SERIAL=65301572 deploy/uno-q/build-deploy.sh
|
|
# deploy/uno-q/build-deploy.sh --build-only # build + verify, no deploy
|
|
# deploy/uno-q/build-deploy.sh --rollback # restore the previous binary from backup
|
|
set -euo pipefail
|
|
|
|
ZC_REPO="${ZC_REPO:-$HOME/projects/zeroclaw}"
|
|
SERIAL="${SERIAL:-65301572}"
|
|
TARGET="aarch64-unknown-linux-musl"
|
|
FEATURES="hardware,peripheral-rpi,embedded-web,zeroclaw-gateway/gateway-voice-duplex"
|
|
BIN="$ZC_REPO/target/$TARGET/release/zeroclaw"
|
|
REMOTE="/home/arduino/zeroclaw"
|
|
SUPERVISOR="/home/arduino/zeroclaw-supervisor.sh"
|
|
adbsh() { adb -s "$SERIAL" shell "$1" </dev/null; } # </dev/null so adb always returns (avoids the setsid-hang)
|
|
|
|
require_device() {
|
|
[ "$(adb -s "$SERIAL" get-state 2>/dev/null)" = "device" ] || { echo "✗ board $SERIAL not connected (adb get-state != device)"; exit 1; }
|
|
}
|
|
|
|
rollback() {
|
|
require_device
|
|
echo "→ rolling back to $REMOTE.prev.bak"
|
|
adbsh "test -f $REMOTE.prev.bak" || { echo "✗ no backup at $REMOTE.prev.bak"; exit 1; }
|
|
adbsh "pkill -f '[z]eroclaw-supervisor'; sleep 1"
|
|
adbsh "pkill -9 -f 'zeroclaw daemon'; sleep 1"
|
|
adbsh "mv -f $REMOTE $REMOTE.rolledback.bak; mv -f $REMOTE.prev.bak $REMOTE; chmod +x $REMOTE"
|
|
adbsh "setsid sh -c 'exec $SUPERVISOR' >/home/arduino/zeroclaw-supervisor.log 2>&1 </dev/null &"
|
|
echo "→ rolled back; supervisor relaunched. sha:$(adbsh "sha256sum $REMOTE | cut -c1-16")"
|
|
exit 0
|
|
}
|
|
|
|
[ "${1:-}" = "--rollback" ] && rollback
|
|
|
|
echo "=== 1/5 prebuild web dashboard SPA (embedded-web bakes web/dist into the binary) ==="
|
|
# gen the API TS bindings, then the production vite build (base=/_app/). embedded-web's
|
|
# include_dir! reads web/dist at compile time, so this MUST run before the cargo build.
|
|
( cd "$ZC_REPO" && cargo run -q -p xtask --bin web -- gen-api && cd web && npm run build )
|
|
|
|
echo "=== 2/5 cross-build (aarch64 musl; hardware + peripherals + embedded web + voice) ==="
|
|
( cd "$ZC_REPO" && cargo zigbuild --target "$TARGET" --release --locked --features "$FEATURES" --bin zeroclaw )
|
|
|
|
echo "=== 3/5 verify artifact ==="
|
|
# grep the binary directly (grep -a) rather than `strings | grep -q`: under
|
|
# `set -o pipefail`, grep -q closes the pipe on first match and `strings` dies
|
|
# with SIGPIPE, making the pipeline exit non-zero even on a MATCH.
|
|
file "$BIN" | grep -q "aarch64" || { echo "✗ not an aarch64 binary"; exit 1; }
|
|
grep -qa "Uno Q tools added" "$BIN" || { echo "✗ peripheral tools MISSING — did the hardware feature build?"; exit 1; }
|
|
grep -qa "uno_q_flash" "$BIN" || { echo "✗ flash tool missing"; exit 1; }
|
|
echo "✓ $(ls -la "$BIN" | awk '{print $5}') bytes, peripheral+flash tools present, sha:$(shasum -a256 "$BIN" | cut -c1-16)"
|
|
|
|
[ "${1:-}" = "--build-only" ] && { echo "build-only: done (artifact at $BIN)"; exit 0; }
|
|
|
|
require_device
|
|
echo "=== 4/5 push + swap (backup kept at $REMOTE.prev.bak) ==="
|
|
adb -s "$SERIAL" push "$BIN" "$REMOTE.new"
|
|
L=$(shasum -a256 "$BIN" | cut -d' ' -f1); R=$(adbsh "sha256sum $REMOTE.new | cut -d' ' -f1")
|
|
[ "$L" = "$R" ] || { echo "✗ sha mismatch after push ($L != $R)"; exit 1; }
|
|
adbsh "pkill -f '[z]eroclaw-supervisor'; sleep 1"
|
|
adbsh "pkill -9 -f 'zeroclaw daemon'; sleep 1"
|
|
adbsh "cp -f $REMOTE $REMOTE.prev.bak; mv -f $REMOTE.new $REMOTE; chmod +x $REMOTE"
|
|
adbsh "setsid sh -c 'exec $SUPERVISOR' >/home/arduino/zeroclaw-supervisor.log 2>&1 </dev/null &"
|
|
|
|
echo "=== 5/5 wait for daemon health (:8080 via host forward) ==="
|
|
adb -s "$SERIAL" forward tcp:8080 tcp:8080 >/dev/null 2>&1 || true
|
|
for i in $(seq 1 30); do
|
|
[ "$(curl -s -m 3 -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/health 2>/dev/null)" = "200" ] && { echo "✓ daemon healthy on new binary (sha:$(adbsh "sha256sum $REMOTE | cut -c1-16"))"; exit 0; }
|
|
sleep 3
|
|
done
|
|
echo "✗ daemon did not report healthy in time — check the board; roll back with: $0 --rollback"
|
|
exit 1
|