Files
apress/deploy/uno-q/package-onboard-app.sh
T
Omar SobhandClaude Opus 4.8 838a0ba334 feat(onboard): distributable "APESS Onboard" App Lab app + packaging
Close the last onboarding gap: one shareable App Lab app a student imports
and Runs, no adb / no host install.

- package-onboard-app.sh: assemble a self-contained bundle — ZeroClaw binary
  (matrix_text + i2c_scan), single-agent config, skills, responder sketch, and
  the BAKED cloud token. Ships without .secret_key (each board mints its own)
  or a team Telegram token; dist/ is gitignored.
- onboard-app/config.toml: the canonical packaged config (proven anthropic.max
  single 'default' agent, matrix + i2c_scan allowlisted, Telegram-ready,
  secrets stripped).
- ONBOARDING.md: the full flow — instructor packages once, student imports +
  Runs, then the wizard. Notes APESS_URL (mDNS/per-team) + LAN reachability.

Validated on-hardware: a freshly-imported bundle mints its key, boots the
cloud agent, and runs the matrix + i2c_scan prompts in-container.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-22 13:23:12 -07:00

97 lines
4.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# package-onboard-app.sh — assemble a SELF-CONTAINED, distributable App Lab app
# ("APESS Onboard") that a student imports and clicks Run. Everything is baked in:
# the ZeroClaw binary, the single-agent config, the cloud token, the skills, and
# the resident responder sketch. No adb, no host install, no per-board setup.
#
# The instructor runs this ONCE to produce the bundle, then shares it via the App
# Lab UI (share → QR). Students scan the QR to import, open the app, click Run:
# the node comes up in one container, flashes the responder, and self-registers to
# the team's APESS laptop.
#
# Usage:
# export ANTHROPIC_OAUTH_TOKEN=sk-ant-oat01-… # baked into the app
# ./deploy/uno-q/package-onboard-app.sh
#
# Env:
# ANTHROPIC_OAUTH_TOKEN (required) cloud Max token, baked into .zeroclaw/oauth_token
# ZEROCLAW_BIN aarch64 binary (default: the built release-fast one)
# APESS_URL where the board self-registers (default: mDNS apess-api.local)
# FLEET_SECRET shared fleet secret (default: apess2026)
# OUT output bundle dir (default: deploy/uno-q/dist/apess-onboard)
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
REPO="$(cd "$HERE/../.." && pwd)"
NODE_SRC="${NODE_SRC:-$HOME/projects/zeroclaw/firmware/zeroclaw-node}"
ZEROCLAW_BIN="${ZEROCLAW_BIN:-$HOME/projects/zeroclaw/target/aarch64-unknown-linux-gnu/release-fast/zeroclaw}"
APESS_URL="${APESS_URL:-http://apess-api.local:3000}"
FLEET_SECRET="${FLEET_SECRET:-apess2026}"
OUT="${OUT:-$HERE/dist/apess-onboard}"
ok(){ printf ' \033[32m✓\033[0m %s\n' "$*"; }
bad(){ printf ' \033[31m✗\033[0m %s\n' "$*"; exit 1; }
[ -n "${ANTHROPIC_OAUTH_TOKEN:-}" ] || bad "ANTHROPIC_OAUTH_TOKEN not set (it gets baked into the app)"
[ -f "$ZEROCLAW_BIN" ] || bad "binary not found: $ZEROCLAW_BIN (build it first)"
[ -f "$NODE_SRC/app.yaml" ] || bad "node app source not found: $NODE_SRC"
[ -f "$HERE/onboard-app/config.toml" ] || bad "canonical config missing: onboard-app/config.toml"
echo "→ assembling the bundle at $OUT"
rm -rf "$OUT"
mkdir -p "$OUT/bin" "$OUT/.zeroclaw/shared"
# App Lab app scaffold (manifest + entrypoint + resident sketch)
cp "$NODE_SRC/app.yaml" "$OUT/app.yaml"
cp -r "$NODE_SRC/python" "$OUT/python"
cp -r "$NODE_SRC/sketch" "$OUT/sketch"
ok "app.yaml + python + sketch (responder w/ i2c_scan)"
# The ZeroClaw binary (matrix_text + i2c_scan + the works)
install -m755 "$ZEROCLAW_BIN" "$OUT/bin/zeroclaw"
ok "binary ($(du -h "$OUT/bin/zeroclaw" | cut -f1))"
# Single-agent config (proven: anthropic.max, matrix + i2c_scan allowlisted,
# telegram-ready, secrets stripped so a fresh board mints its own .secret_key)
cp "$HERE/onboard-app/config.toml" "$OUT/.zeroclaw/config.toml"
ok "config (single 'default' agent, matrix + i2c_scan)"
# Skills — the resident copy the daemon seeds each agent's workspace from
cp -r "$HERE/skills" "$OUT/.zeroclaw/shared/skills"
ok "skills ($(ls "$HERE/skills" | wc -l | tr -d ' ') bundles)"
# BAKED cloud token (per the workshop decision) — the instructor's Max token,
# shared across the fleet. Kept in the app bundle only, never in the repo.
printf '%s' "$ANTHROPIC_OAUTH_TOKEN" > "$OUT/.zeroclaw/oauth_token"
chmod 600 "$OUT/.zeroclaw/oauth_token"
ok "cloud token baked in (.zeroclaw/oauth_token)"
# Self-register inputs. KIT_ID + CLAIM_CODE are per-board; the packaged defaults
# are placeholders the app regenerates a code from if unset. APESS_URL points the
# board at the team's laptop stack (default: mDNS name the deploy/lan box advertises).
cat > "$OUT/.zeroclaw/apess-node.env" <<EOF
KIT_ID=
CLAIM_CODE=
FLEET_SECRET=$FLEET_SECRET
APESS_URL=$APESS_URL
GATEWAY_PORT=8080
EOF
ok "apess-node.env (APESS_URL=$APESS_URL)"
# Optional embedded ZeroClaw dashboard (served at :8080/). Carried if present.
if [ -d "$HOME/projects/zeroclaw/web-dist" ]; then
cp -r "$HOME/projects/zeroclaw/web-dist" "$OUT/web-dist"; ok "web dashboard"
else
echo " (no web-dist — the node dashboard link will 404; students use the APESS wizard)"
fi
echo
ok "bundle ready: $OUT"
cat <<EOF
Distribute it:
1. Import onto a board (instructor test):
arduino-app-cli app new --from-app "$OUT"
2. Or share to students: open the app in App Lab → Share → QR. Students scan to
import, then Run. Set APESS_URL per team if not using mDNS
(edit .zeroclaw/apess-node.env, or point apess-api.local at the laptop).
EOF