diff --git a/skills/iso-visuals/SKILL.md b/skills/iso-visuals/SKILL.md index 2239045..fbd6b1a 100644 --- a/skills/iso-visuals/SKILL.md +++ b/skills/iso-visuals/SKILL.md @@ -14,65 +14,92 @@ triggers: # ISO Visuals — Out-of-Box Desktop Experience Three improvements that replace the "boot to a terminal" feeling with a calm, -informative desktop: a panel health indicator, a machine-identity wallpaper, -and a one-click Join Hive launcher. +informative desktop: a panel health indicator helper, a machine-identity wallpaper +helper, and a one-click Join Hive launcher. -## 1. Panel Health Indicator +Keep this skill honest: scripts can be staged before every script is wired into +the default panel/session. Do not claim default-panel or first-login behavior until +it is proven in the live ISO or by mounted-image plus runtime checks. -Shows Colibri daemon status in the Xfce panel. Green/red dot with agent count. -Click to open a terminal with full `colibri status` output. +## 1. Panel Health Indicator Helper + +Shows Colibri daemon status as Xfce genmon markup. When wired into a Generic Monitor +panel item, it displays a green/red dot with agent count and started-task count; +clicking opens a terminal with full `colibri status` output. **Script:** `scripts/colibri-panel-indicator.sh` -**ISO integration:** +**ISO staging:** ```bash -# Copy the script into the live filesystem -cp scripts/colibri-panel-indicator.sh /usr/local/bin/colibri-panel-indicator -chmod 755 /usr/local/bin/colibri-panel-indicator +install -m 755 scripts/colibri-panel-indicator.sh \ + "${LIVE_ROOT}/usr/local/bin/colibri-panel-indicator" +``` -# Add to Xfce panel via xfconf (or manually: Panel → Add New Items → Generic Monitor) +**Optional runtime/manual panel wiring:** + +```bash +# Requires xfce4-genmon-plugin. Add manually from Panel → Add New Items → Generic Monitor, +# or wire into seeded panel XML only after that plugin is in the ISO package list. xfconf-query -c xfce4-panel -p /plugins/plugin-20 -n -t string -s genmon xfconf-query -c xfce4-panel -p /plugins/plugin-20/command -n -t string -s /usr/local/bin/colibri-panel-indicator ``` -The indicator polls every 30s, uses ``/``/`` genmon markup. -Zero dependencies beyond `nc` and `python3` (both in the base jail bootstrap). +The indicator uses ``/``/`` genmon markup. It depends on +`nc` and `python3`; the Clawdie ISO stages Python 3.12 and creates `python3` / +`python` symlinks. -## 2. Identity Wallpaper +## 2. Identity Wallpaper Helper -Generates a desktop background with machine identity overlaid: -hostname, Tailscale IP, Colibri port, and FreeBSD release. +Generates a desktop background with machine identity overlaid: hostname, +Tailscale IP when available, Colibri socket/port status, and FreeBSD release. **Script:** `scripts/clawdie-wallpaper-gen.sh` -**ISO integration:** +**ISO staging:** ```bash -# Run on first boot, set as wallpaper -cp scripts/clawdie-wallpaper-gen.sh /usr/local/bin/clawdie-wallpaper-gen -chmod 755 /usr/local/bin/clawdie-wallpaper-gen - -# In clawdie-bootstrap-launch.sh or xfce-session: -/usr/local/bin/clawdie-wallpaper-gen /usr/local/share/backgrounds/clawdie-default.png - -# Set as wallpaper (Xfce): -xfconf-query -c xfce4-desktop -p /backdrop/screen0/monitor0/workspace0/last-image \ - -s /usr/local/share/backgrounds/clawdie-default.png +install -m 755 scripts/clawdie-wallpaper-gen.sh \ + "${LIVE_ROOT}/usr/local/bin/clawdie-wallpaper-gen" ``` -Requires ImageMagick (`pkg install ImageMagick` — add to ISO package list). -Falls back to a solid dark background if no default wallpaper exists. +**Preferred base wallpaper:** + +```text +/usr/local/share/clawdie-iso/wallpapers/clawdie-operator-bg.png +``` + +The helper falls back to the stock XFCE background, then to a generated dark solid +background under project-local `tmp/` when a checkout is available. On the live USB, +where there is usually no project root, it uses an app-owned cache directory such as +`$XDG_CACHE_HOME/clawdie` or `$HOME/.cache/clawdie`. It should not try to create +files under `/usr/local/share` at runtime unless explicitly requested by the operator. + +**Optional runtime/session wiring:** + +```bash +PROJECT_ROOT=$(git rev-parse --show-toplevel) +PROJECT_TMP="$PROJECT_ROOT/tmp" +mkdir -p "$PROJECT_TMP" +/usr/local/bin/clawdie-wallpaper-gen "$PROJECT_TMP/clawdie-wallpaper.png" +xfconf-query -c xfce4-desktop -p /backdrop/screen0/monitor0/workspace0/last-image \ + -s "$PROJECT_TMP/clawdie-wallpaper.png" +``` + +Requires ImageMagick (`ImageMagick7` in the ISO package list; `magick` or +`convert` command accepted). Tailscale is optional; absence should produce +`tailscale offline`, not an error. **Format:** -``` + +```text ┌────────────────────────────────────┐ │ │ │ Clawdie OS │ │ │ │ hostname osa │ │ tailscale 100.72.229.63 │ -│ colibri 9190 │ +│ colibri socket 9190 │ │ jail 15.0-RELEASE-p4 │ │ │ └────────────────────────────────────┘ @@ -80,48 +107,72 @@ Falls back to a solid dark background if no default wallpaper exists. ## 3. Join Hive Launcher -Desktop icon that runs the full agent registration flow in a visible terminal: -check daemon → verify vault creds → detect capabilities → register → done. +Desktop icon that runs agent registration in a visible terminal: check daemon → +check Vaultwarden bootstrap fields without printing secrets → detect capabilities → +register → pause for operator review. **Script:** `scripts/clawdie-join-hive.sh` + **Desktop entry:** `scripts/clawdie-join-hive.desktop` -**ISO integration:** +**ISO staging:** ```bash -cp scripts/clawdie-join-hive.sh /usr/local/bin/clawdie-join-hive.sh -cp scripts/clawdie-join-hive.desktop /usr/local/share/applications/ -cp scripts/clawdie-join-hive.desktop /home/clawdie/Desktop/ -chmod 755 /usr/local/bin/clawdie-join-hive.sh +install -m 755 scripts/clawdie-join-hive.sh \ + "${LIVE_ROOT}/usr/local/bin/clawdie-join-hive" +install -m 644 scripts/clawdie-join-hive.desktop \ + "${LIVE_ROOT}/usr/local/share/applications/Clawdie Join Hive.desktop" +install -m 644 scripts/clawdie-join-hive.desktop \ + "${LIVE_ROOT}/home/clawdie/Desktop/Join Hive.desktop" ``` -The launcher runs in `xfce4-terminal` with geometry 80x24, pauses on "Press Enter" -so the operator reads the result before the window closes. +The desktop entry must call the installed binary path, without a `.sh` suffix: -**Idempotent** — safe to double-click multiple times. Already-registered agents -are detected and skipped. +```ini +Exec=xfce4-terminal --title "Join Hive" --geometry=80x24 --command "/usr/local/bin/clawdie-join-hive" +``` -## Build integration (clawdie-iso build.sh) +The launcher runs in `xfce4-terminal` with geometry 80x24 and pauses on +"Press Enter" so the operator reads the result before the window closes. -Add these lines to the ISO build script: +**Idempotency expectations:** + +- safe to double-click multiple times; +- treats already-registered / `UNIQUE constraint` agent-name responses as an + idempotent success; +- does not fail just because `/usr/local/etc/colibri/provider.env` is mode `0600`; +- uses `mdo -u root` for privileged checks/start where available; +- never prints Vaultwarden credentials or provider keys. + +## Build integration (`clawdie-iso` `build.sh`) + +Current staging pattern: ```bash -# --- ISO Visuals --- install -m 755 scripts/colibri-panel-indicator.sh "${LIVE_ROOT}/usr/local/bin/colibri-panel-indicator" install -m 755 scripts/clawdie-wallpaper-gen.sh "${LIVE_ROOT}/usr/local/bin/clawdie-wallpaper-gen" install -m 755 scripts/clawdie-join-hive.sh "${LIVE_ROOT}/usr/local/bin/clawdie-join-hive" -install -m 644 scripts/clawdie-join-hive.desktop "${LIVE_ROOT}/usr/local/share/applications/" -install -m 644 scripts/clawdie-join-hive.desktop "${LIVE_ROOT}/home/clawdie/Desktop/" +install -m 644 scripts/clawdie-join-hive.desktop "${LIVE_ROOT}/usr/local/share/applications/Clawdie Join Hive.desktop" +install -m 644 scripts/clawdie-join-hive.desktop "${LIVE_ROOT}/home/clawdie/Desktop/Join Hive.desktop" ``` -And add `ImageMagick` to the ISO package list for the wallpaper generator. +Add/keep `ImageMagick7` in the ISO package list for the wallpaper generator. ## Verification After booting the ISO: -1. **Panel:** green dot with agent count visible in the Xfce panel. - Clicking opens `colibri status` in a terminal. -2. **Wallpaper:** identity text in the bottom-left corner. -3. **Desktop:** "Join Hive" icon present. Double-click → terminal opens, - walks through registration, pauses for operator to read. +1. **Mounted-image:** `/usr/local/bin/clawdie-join-hive`, + `/usr/local/bin/clawdie-wallpaper-gen`, and `/usr/local/bin/colibri-panel-indicator` + exist and are executable. +2. **Desktop launcher:** `Join Hive.desktop` exists; its `Exec=` points to + `/usr/local/bin/clawdie-join-hive` with no `.sh` suffix. +3. **Join Hive:** double-click opens a terminal, starts/checks `colibri_daemon`, + reports provider.env status without exposing secrets, registers or reports + already registered, then waits for Enter. +4. **Wallpaper helper:** running `/usr/local/bin/clawdie-wallpaper-gen` writes an + image under project-local `tmp/` when run from a checkout, or an app-owned live + cache directory when no project root exists, using the Clawdie wallpaper as base. +5. **Panel indicator helper:** running `/usr/local/bin/colibri-panel-indicator` + prints genmon XML. Only claim panel visibility after genmon is packaged and + wired into the seeded panel or configured manually. diff --git a/skills/iso-visuals/scripts/clawdie-join-hive.desktop b/skills/iso-visuals/scripts/clawdie-join-hive.desktop index 23c545a..2673284 100644 --- a/skills/iso-visuals/scripts/clawdie-join-hive.desktop +++ b/skills/iso-visuals/scripts/clawdie-join-hive.desktop @@ -3,7 +3,7 @@ Type=Application Version=1.0 Name=Join Hive Comment=Register this machine as a Colibri agent -Exec=xfce4-terminal --title "Join Hive" --geometry=80x24 --command "/usr/local/bin/clawdie-join-hive.sh" +Exec=xfce4-terminal --title "Join Hive" --geometry=80x24 --command "/usr/local/bin/clawdie-join-hive" Icon=network-server Terminal=false Categories=System;Utility; diff --git a/skills/iso-visuals/scripts/clawdie-join-hive.sh b/skills/iso-visuals/scripts/clawdie-join-hive.sh index d138a02..4095da5 100755 --- a/skills/iso-visuals/scripts/clawdie-join-hive.sh +++ b/skills/iso-visuals/scripts/clawdie-join-hive.sh @@ -2,11 +2,33 @@ # One-click "Join Hive" — registers this machine as a Colibri agent. # Runs in a visible terminal so the operator sees the result. # Idempotent: safe to re-run on an already-registered agent. -set -e SOCKET="${COLIBRI_SOCKET:-/var/run/colibri/colibri.sock}" PROVIDER_ENV="/usr/local/etc/colibri/provider.env" +finish() { + _code="${1:-0}" + echo "" + echo "Press Enter to close." + read -r _ + exit "${_code}" +} + +have() { + command -v "$1" >/dev/null 2>&1 +} + +provider_env_has_bw_creds() { + _check='test -f "$1" && grep -Eq "^BW_CLIENTID=.+" "$1" && grep -Eq "^BW_CLIENTSECRET=.+" "$1" && grep -Eq "^BW_PASSWORD=.+" "$1"' + + if have mdo; then + mdo -u root sh -c "${_check}" sh "${PROVIDER_ENV}" >/dev/null 2>&1 + return $? + fi + + sh -c "${_check}" sh "${PROVIDER_ENV}" >/dev/null 2>&1 +} + echo "========================================" echo " Clawdie — Join Hive" echo "========================================" @@ -15,48 +37,90 @@ echo "" # 1. Check daemon if [ ! -S "$SOCKET" ]; then echo "[1/4] Starting colibri daemon..." - mdo -u root service colibri_daemon start - sleep 2 + if have mdo; then + if ! mdo -u root service colibri_daemon start; then + echo " WARNING: could not start colibri_daemon via mdo." + fi + elif [ "$(id -u)" -eq 0 ]; then + if ! service colibri_daemon start; then + echo " WARNING: could not start colibri_daemon." + fi + else + echo " WARNING: mdo is unavailable and this user is not root." + fi + + _tries=0 + while [ "$_tries" -lt 5 ] && [ ! -S "$SOCKET" ]; do + sleep 1 + _tries=$(( _tries + 1 )) + done else echo "[1/4] Daemon already running." fi -# 2. Vault creds +if [ ! -S "$SOCKET" ]; then + echo " ERROR: Colibri socket is still missing: ${SOCKET}" + echo " Check: mdo -u root service colibri_daemon status" + finish 1 +fi + +# 2. Vault creds. provider.env is intentionally 0600, so check via mdo when possible. echo "[2/4] Checking vault credentials..." -if [ -f "$PROVIDER_ENV" ] && grep -q 'BW_CLIENTID=.' "$PROVIDER_ENV" 2>/dev/null; then - echo " provider.env present with credentials." +if provider_env_has_bw_creds; then + echo " provider.env contains Vaultwarden bootstrap credential fields." else - echo " WARNING: provider.env missing or empty." - echo " Vault provisioning will be skipped." - echo " Edit ${PROVIDER_ENV} and re-run." + echo " WARNING: provider.env is missing, unreadable, or lacks BW_CLIENTID/BW_CLIENTSECRET/BW_PASSWORD." + echo " Vault provisioning will be skipped until the operator adds them." + echo " Edit ${PROVIDER_ENV} and keep it mode 0600, then re-run." fi # 3. Detect capabilities -HOST=$(hostname) -OS=$(uname -s | tr '[:upper:]' '[:lower:]') +HOST=$(hostname 2>/dev/null || echo "clawdie-live") +OS=$(uname -s 2>/dev/null | tr '[:upper:]' '[:lower:]') +[ -n "$OS" ] || OS="unknown" CAPS="$OS,shell" # Add optional capabilities -which colibri >/dev/null 2>&1 && CAPS="$CAPS,colibri" -which hermes >/dev/null 2>&1 && CAPS="$CAPS,hermes" -which pi >/dev/null 2>&1 && CAPS="$CAPS,pi" -tailscale status >/dev/null 2>&1 && CAPS="$CAPS,tailscale" +have colibri && CAPS="$CAPS,colibri" +have hermes && CAPS="$CAPS,hermes" +have pi && CAPS="$CAPS,pi" +if have tailscale && tailscale status >/dev/null 2>&1; then + CAPS="$CAPS,tailscale" +fi [ "$OS" = "freebsd" ] && CAPS="$CAPS,rc.d,jail,zfs" +if have python3; then + CAPS_JSON=$(printf '%s' "$CAPS" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().strip().split(",")))' 2>/dev/null) +else + CAPS_JSON='["shell"]' +fi +[ -n "$CAPS_JSON" ] || CAPS_JSON='["shell"]' + AGENT_NAME="${HOST}" echo "[3/4] Registering agent: ${AGENT_NAME}" echo " capabilities: ${CAPS}" +if ! have nc; then + echo " ERROR: nc is not installed; cannot talk to ${SOCKET}." + finish 1 +fi + RESP=$(printf '{"cmd":"register-agent","name":"%s","capabilities":%s}\n' \ - "$AGENT_NAME" "$(echo "$CAPS" | python3 -c "import sys; print(__import__('json').dumps(sys.stdin.read().strip().split(',')))")" \ + "$AGENT_NAME" "$CAPS_JSON" \ | nc -U "$SOCKET" -w 3 2>/dev/null) if echo "$RESP" | grep -q '"ok":true'; then echo " registered." -elif echo "$RESP" | grep -q 'already exists'; then +elif echo "$RESP" | grep -Eiq 'already exists|unique constraint|constraint failed|agents\.name'; then echo " already registered (idempotent)." else - echo " registration note: ${RESP}" + echo " registration did not complete cleanly." + if [ -n "$RESP" ]; then + echo " response: ${RESP}" + else + echo " no response from ${SOCKET}" + fi + finish 1 fi # 4. Start poll loop @@ -66,5 +130,5 @@ echo " Check: colibri status" echo " Tasks: colibri list-tasks --status started" echo " Board: colibri list-agents" echo "" -echo "Hive joined. Press Enter to close." -read -r _ +echo "Hive joined." +finish 0 diff --git a/skills/iso-visuals/scripts/clawdie-wallpaper-gen.sh b/skills/iso-visuals/scripts/clawdie-wallpaper-gen.sh index bca2bdd..9069d71 100755 --- a/skills/iso-visuals/scripts/clawdie-wallpaper-gen.sh +++ b/skills/iso-visuals/scripts/clawdie-wallpaper-gen.sh @@ -1,33 +1,88 @@ #!/bin/sh # Generate a wallpaper with machine identity overlaid. -# Run once on first boot, caches result in /tmp/clawdie-wallpaper.png. -# Requires: ImageMagick (convert), tailscale, colibri socket. +# Safe to run on first boot; caches result in project-local tmp/ when a project +# root is available, otherwise in the live user's app-owned cache directory. +# Requires ImageMagick (magick or convert). Tailscale is optional. -set -e +CLAWDIE_BG="/usr/local/share/clawdie-iso/wallpapers/clawdie-operator-bg.png" +XFCE_BG="/usr/local/share/backgrounds/xfce/default.png" -OUT="${1:-/tmp/clawdie-wallpaper.png}" -BG="/usr/local/share/backgrounds/xfce/default.png" +have() { + command -v "$1" >/dev/null 2>&1 +} -HOST=$(hostname) -TS_IP=$(tailscale ip -4 2>/dev/null || echo "offline") -COLIBRI_SOCK="/var/run/colibri/colibri.sock" -COLIBRI_PORT="9190" -JAIL_RELEASE=$(freebsd-version 2>/dev/null || uname -r) +project_root() { + if [ -n "${CLAWDIE_PROJECT_ROOT:-}" ]; then + printf '%s\n' "$CLAWDIE_PROJECT_ROOT" + elif have git && git rev-parse --show-toplevel >/dev/null 2>&1; then + git rev-parse --show-toplevel + else + return 1 + fi +} -# Fall back to a solid colour if no background image exists -if [ ! -f "$BG" ]; then - convert -size 1920x1080 xc:'#1a1a2e' "$BG" 2>/dev/null || true +scratch_dir() { + if [ -n "${CLAWDIE_TMP:-}" ]; then + printf '%s\n' "$CLAWDIE_TMP" + elif _root=$(project_root); then + printf '%s/tmp\n' "$_root" + elif [ -n "${XDG_CACHE_HOME:-}" ]; then + printf '%s/clawdie\n' "$XDG_CACHE_HOME" + elif [ -n "${HOME:-}" ]; then + printf '%s/.cache/clawdie\n' "$HOME" + else + printf '%s\n' "/var/cache/clawdie" + fi +} + +if have magick; then + im() { magick "$@"; } +elif have convert; then + im() { convert "$@"; } +else + echo "ERROR: ImageMagick is not installed; expected magick or convert." >&2 + exit 1 fi -# One-liner draw: place identity text in the bottom-left corner -convert "$BG" \ - -font Helvetica -pointsize 18 -fill '#e0e0e0' \ +SCRATCH_DIR=$(scratch_dir) +mkdir -p "$SCRATCH_DIR" +OUT="${1:-${SCRATCH_DIR}/clawdie-wallpaper.png}" +FALLBACK_BG="${SCRATCH_DIR}/clawdie-wallpaper-base.png" + +HOST=$(hostname 2>/dev/null || echo "clawdie-live") +if have tailscale; then + TS_IP=$(tailscale ip -4 2>/dev/null | head -n 1) +fi +[ -n "${TS_IP:-}" ] || TS_IP="offline" +COLIBRI_SOCK="/var/run/colibri/colibri.sock" +COLIBRI_PORT="9190" +JAIL_RELEASE=$(freebsd-version 2>/dev/null || uname -r 2>/dev/null || echo "unknown") + +if [ -f "$CLAWDIE_BG" ]; then + BG="$CLAWDIE_BG" +elif [ -f "$XFCE_BG" ]; then + BG="$XFCE_BG" +else + BG="$FALLBACK_BG" + im -size 1920x1080 xc:'#1a1a2e' "$BG" +fi + +SOCK_STATUS="down" +[ -S "$COLIBRI_SOCK" ] && SOCK_STATUS="socket ${COLIBRI_PORT}" + +# Overlay identity text in the bottom-left corner. Do not require a specific +# font: ImageMagick's default font is more portable across FreeBSD package sets. +if ! im "$BG" \ + -pointsize 18 -fill '#e0e0e0' \ -annotate +40+900 "hostname ${HOST}" \ -annotate +40+930 "tailscale ${TS_IP}" \ - -annotate +40+960 "colibri ${COLIBRI_PORT}" \ + -annotate +40+960 "colibri ${SOCK_STATUS}" \ -annotate +40+990 "jail ${JAIL_RELEASE}" \ - -font Helvetica-Bold -pointsize 28 -fill '#8b5cf6' \ + -pointsize 28 -fill '#8b5cf6' \ -annotate +40+850 "Clawdie OS" \ - "$OUT" + "$OUT"; then + echo "WARNING: identity overlay failed; copying base wallpaper instead." >&2 + cp "$BG" "$OUT" +fi echo "Wallpaper: ${OUT}"