diff --git a/live/operator-session/clawdie-join-hive.sh b/live/operator-session/clawdie-join-hive.sh
index d138a022..1d0984d4 100755
--- a/live/operator-session/clawdie-join-hive.sh
+++ b/live/operator-session/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,56 +37,108 @@ 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
+# 4. Apply identity wallpaper as visual confirmation
echo "[4/4] Agent ${AGENT_NAME} is live on the Colibri board."
echo ""
+
+if have clawdie-wallpaper-gen; then
+ echo " Setting identity wallpaper..."
+ if have xfconf-query; then
+ WP="/tmp/clawdie-wallpaper.png"
+ clawdie-wallpaper-gen "$WP" 2>/dev/null && \
+ xfconf-query -c xfce4-desktop -p /backdrop/screen0/monitor0/workspace0/last-image -s "$WP" 2>/dev/null
+ fi
+fi
+
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/live/operator-session/colibri-panel-indicator.sh b/live/operator-session/colibri-panel-indicator.sh
index 09213f6c..11465ef5 100755
--- a/live/operator-session/colibri-panel-indicator.sh
+++ b/live/operator-session/colibri-panel-indicator.sh
@@ -1,32 +1,39 @@
#!/bin/sh
# xfce4-genmon panel indicator โ polls colibri daemon health.
-# Install: add a Generic Monitor panel item pointing to this script.
-# Refresh interval: 30s.
-#
-# Output: green/red dot + agent count + task count.
+# Wired into a Generic Monitor panel item with 30s refresh.
+# Output: genmon XML with green/red dot + agent count + task count.
# ๐ข 2 agents ยท 3 tasks
# ๐ด daemon down
SOCKET="${COLIBRI_SOCKET:-/var/run/colibri/colibri.sock}"
-TOOLTIP=""
-if [ -S "$SOCKET" ]; then
- STATUS=$(printf '{"cmd":"status"}\n' | nc -U "$SOCKET" -w 2 2>/dev/null)
- if [ -n "$STATUS" ]; then
- AGENTS=$(echo "$STATUS" | python3 -c "import sys,json; d=json.load(sys.stdin)['data']; print(d['agents'])" 2>/dev/null)
- TASKS=$(echo "$STATUS" | python3 -c "import sys,json; d=json.load(sys.stdin)['data']; t=d['tasks']; print(t['started'])" 2>/dev/null)
- HOST=$(echo "$STATUS" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['host'])" 2>/dev/null)
+have() {
+ command -v "$1" >/dev/null 2>&1
+}
- echo "๐ข ${AGENTS} agents ยท ${TASKS} tasks"
- TOOLTIP="Colibri: ${HOST} | socket: ${SOCKET} | agents: ${AGENTS} | started tasks: ${TASKS}"
- else
- echo "๐ด no response"
- TOOLTIP="Colibri socket exists but not responding"
- fi
-else
- echo "๐ด down"
- TOOLTIP="Colibri daemon not running (no socket at ${SOCKET})"
+if ! have nc || ! have python3; then
+ echo "โ missing deps"
+ echo "nc and python3 required for colibri panel indicator"
+ exit 0
fi
-echo "${TOOLTIP}"
+if [ ! -S "$SOCKET" ]; then
+ echo "๐ด down"
+ echo "Colibri daemon not running (no socket at ${SOCKET})"
+ exit 0
+fi
+
+STATUS=$(printf '{"cmd":"status"}\n' | nc -U "$SOCKET" -w 2 2>/dev/null)
+if [ -z "$STATUS" ]; then
+ echo "๐ด no response"
+ echo "Colibri socket exists but not responding"
+ exit 0
+fi
+
+AGENTS=$(echo "$STATUS" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['agents'])" 2>/dev/null)
+TASKS=$(echo "$STATUS" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['tasks']['started'])" 2>/dev/null)
+HOST=$(echo "$STATUS" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['host'])" 2>/dev/null)
+
+echo "๐ข ${AGENTS:-?} agents ยท ${TASKS:-?} tasks"
+echo "Colibri: ${HOST:-?} | socket: ${SOCKET} | agents: ${AGENTS:-?} | started tasks: ${TASKS:-?}"
echo "xfce4-terminal --title 'Colibri Status' --command 'colibri status'"