#!/bin/sh # Stage prebuilt Colibri FreeBSD service artifacts into an ISO/image root. # # This script does not build Colibri. Build or provide artifacts first: # (cd /home/clawdie/ai/colibri && cargo build --workspace --release) # # Usage: # COLIBRI_REPO=/home/clawdie/ai/colibri scripts/stage-colibri-iso.sh /path/to/image-root # COLIBRI_ARTIFACT_DIR=/path/to/release scripts/stage-colibri-iso.sh /path/to/image-root set -eu if [ "${1:-}" = "" ]; then echo "usage: $0 DESTDIR" >&2 exit 64 fi DESTDIR=$1 SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) REPO_ROOT=$(CDPATH= cd -- "${SCRIPT_DIR}/.." && pwd) COLIBRI_REPO=${COLIBRI_REPO:-"/home/clawdie/ai/colibri"} COLIBRI_ARTIFACT_DIR=${COLIBRI_ARTIFACT_DIR:-"${COLIBRI_REPO}/target/release"} COLIBRI_STAGE_ENABLE=${COLIBRI_STAGE_ENABLE:-YES} COLIBRI_STAGE_INCLUDE_TUI=${COLIBRI_STAGE_INCLUDE_TUI:-1} COLIBRI_STAGE_TEST_AGENT=${COLIBRI_STAGE_TEST_AGENT:-NO} COLIBRI_COST_MODE=${COLIBRI_COST_MODE:-smart} BIN_DIR="${DESTDIR}/usr/local/bin" RC_DIR="${DESTDIR}/usr/local/etc/rc.d" ETC_DIR="${DESTDIR}/usr/local/etc/colibri" DB_DIR="${DESTDIR}/var/db/colibri" RUN_DIR="${DESTDIR}/var/run/colibri" LOG_DIR="${DESTDIR}/var/log/colibri" NEWSYSLOG_DIR="${DESTDIR}/usr/local/etc/newsyslog.conf.d" RC_SOURCE="${COLIBRI_REPO}/packaging/freebsd/colibri_daemon.in" NEWSYSLOG_SOURCE="${COLIBRI_REPO}/packaging/freebsd/newsyslog-colibri.conf" require_file() { if [ ! -f "$1" ]; then echo "missing required Colibri artifact: $1" >&2 exit 66 fi } require_exec() { if [ ! -x "$1" ]; then echo "missing executable Colibri artifact: $1" >&2 echo "hint: (cd ${COLIBRI_REPO} && cargo build --workspace --release)" >&2 exit 66 fi } copy_bin() { require_exec "${COLIBRI_ARTIFACT_DIR}/$1" install -m 0555 "${COLIBRI_ARTIFACT_DIR}/$1" "${BIN_DIR}/$1" } require_file "${RC_SOURCE}" require_file "${NEWSYSLOG_SOURCE}" mkdir -p "${BIN_DIR}" "${RC_DIR}" "${ETC_DIR}" "${NEWSYSLOG_DIR}" "${DB_DIR}" "${RUN_DIR}" "${LOG_DIR}" copy_bin colibri-daemon copy_bin colibri copy_bin colibri-mcp if [ "${COLIBRI_STAGE_TEST_AGENT}" = "YES" ]; then copy_bin colibri-test-agent fi if [ "${COLIBRI_STAGE_INCLUDE_TUI}" != "0" ] && [ -x "${COLIBRI_ARTIFACT_DIR}/colibri-tui" ]; then copy_bin colibri-tui fi install -m 0555 "${RC_SOURCE}" "${RC_DIR}/colibri_daemon" install -m 0644 "${NEWSYSLOG_SOURCE}" "${NEWSYSLOG_DIR}/colibri.conf" if ! grep -q '^command="/usr/sbin/daemon"' "${RC_DIR}/colibri_daemon" || \ ! grep -q -- '-o .*colibri_daemon_binary' "${RC_DIR}/colibri_daemon" || \ ! grep -q 'colibri_daemon_provider_env' "${RC_DIR}/colibri_daemon" || \ ! grep -q 'colibri_daemon_cost_mode' "${RC_DIR}/colibri_daemon" || \ ! grep -q 'rm -f "${pidfile}" "${supervisor_pidfile}"' "${RC_DIR}/colibri_daemon" || \ ! grep -q 'export PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"' "${RC_DIR}/colibri_daemon"; then echo "ERROR: staged colibri_daemon rc.d script is missing required live USB supervision hooks" >&2 echo " Update COLIBRI_REPO (${COLIBRI_REPO}) before building." >&2 exit 66 fi if grep -q -- '-u \${colibri_daemon_user}' "${RC_DIR}/colibri_daemon"; then echo "ERROR: staged colibri_daemon rc.d script has unsupported live USB command wiring" >&2 echo " Update COLIBRI_REPO (${COLIBRI_REPO}) before building." >&2 exit 66 fi cat > "${ETC_DIR}/rc.conf.sample" < "${ETC_DIR}/provider.env" <<'EOF' # Non-secret Clawdie defaults. Keep this file mode 0600: operators may add # provider keys and Vaultwarden bootstrap credentials here after boot. VAULT_SERVER="https://vault.smilepowered.org" BW_SERVER="https://vault.smilepowered.org" # Auto-spawn one zot agent on daemon startup once a DeepSeek key is present # (the Operator Image OOTB flow). The daemon sources this file, so the spawned # agent inherits the provider keys set here. COLIBRI_AUTOSPAWN="YES" # zot is the default harness — pi is available but zot has richer provider # support (DeepSeek native, OpenRouter, ~25 providers) and a built-in # Telegram bot mode. Set COLIBRI_AUTOSPAWN_BINARY=pi to switch back. COLIBRI_AUTOSPAWN_BINARY="zot" # Telegram bot token — set this to enable the bot channel (@your_bot). # Leave blank to use CLI/TUI/Dashboard channels only. # TELEGRAM_BOT_TOKEN="" EOF chmod 0600 "${ETC_DIR}/provider.env" 2>/dev/null || true # External MCP server registry — empty by default. The "Enable Mother Link" # action (clawdie-enable-mother) adds a server entry here; colibri-mcp reads it # when launched with COLIBRI_MCP_EXTERNAL_CALL=1. Path matches colibri-mcp's # default COLIBRI_MCP_EXTERNAL_CONFIG. cat > "${ETC_DIR}/external-mcp.json" <<'EOF' { "servers": {} } EOF chmod 0644 "${ETC_DIR}/external-mcp.json" 2>/dev/null || true cat > "${ETC_DIR}/provider.env.sample" <<'EOF' # Optional provider keys and Vaultwarden bootstrap credentials for # colibri_daemon. The ISO already stages provider.env with the non-secret # Clawdie Vaultwarden endpoint; copy values from here into provider.env, # keep it mode 0600, then restart the service. # # Baked non-secret defaults: VAULT_SERVER="https://vault.smilepowered.org" BW_SERVER="https://vault.smilepowered.org" # # Vaultwarden bootstrap credentials (secret; operator-provided): # BW_CLIENTID="..." # BW_CLIENTSECRET="..." # BW_PASSWORD="..." # # Direct provider keys (optional when Vaultwarden provisioning is used): # DEEPSEEK_API_KEY="sk-..." # OPENROUTER_API_KEY="sk-or-..." # ANTHROPIC_API_KEY="sk-ant-..." # # Optional endpoints/models: # DEEPSEEK_ENDPOINT="https://api.deepseek.com/chat/completions" # DEEPSEEK_MODEL="deepseek-v4-pro" # # Telegram bot (optional): # TELEGRAM_BOT_TOKEN="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" # # Behavior toggles (non-secret): # COLIBRI_AUTOSPAWN="YES" # auto-spawn one agent on daemon startup # COLIBRI_AUTOSPAWN_BINARY="zot" # agent harness: zot (default) | pi # COLIBRI_AUTOSPAWN_ARGS="rpc" # agent argv; default is harness-derived # # (zot -> "rpc", pi -> "--mode json") # COLIBRI_MCP_EXTERNAL_CALL="1" # allow agent (via colibri-mcp) to call # # external MCP servers; set by # # clawdie-enable-mother EOF cat > "${ETC_DIR}/README.iso" <<'EOF' Colibri ISO staging notes ========================= The ISO build creates the colibri user/group and stages the rc.d service. The colibri-daemon runs under daemon(8) supervision and is enabled at boot. If the daemon fails, it restarts automatically without blocking SDDM/XFCE. Provider keys are optional and live in /usr/local/etc/colibri/provider.env. Keep that file root-owned and mode 0600, then restart colibri_daemon. Runtime validation: service colibri_daemon start colibri status colibri create-task --title "iso check" colibri list-tasks --status queued colibri-mcp tools COLIBRI_MCP_WRITE=1 colibri-mcp tools # trusted write-capable MCP profile service colibri_daemon stop EOF chmod 0755 "${DB_DIR}" "${RUN_DIR}" "${LOG_DIR}" cat <