#!/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_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-smoke-agent 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_program' "${RC_DIR}/colibri_daemon"; then echo "ERROR: staged colibri_daemon rc.d script does not supervise colibri-daemon with daemon(8)" >&2 echo " Update COLIBRI_REPO (${COLIBRI_REPO}) before building; the live USB must not block boot in rc.d." >&2 exit 66 fi # Fix procname: the upstream procname="/usr/sbin/daemon" doesn't match # daemon(8)'s process title ("daemon: name[pid] (daemon)"). Match the # first word instead. Since check_pidfile uses the pidfile (PID-scoped), # there's no collision with other daemon(8) instances. sed -i '' 's/^procname="\/usr\/sbin\/daemon"$/procname="daemon:"/' \ "${RC_DIR}/colibri_daemon" # Fix ${name}_program override: rc.subr line 1120 silently replaces # command= with ${name}_program if set, so colibri_daemon_program= # overrode command="/usr/sbin/daemon" — daemon(8) was never invoked. # Rename the variable so rc.subr leaves command= alone. sed -i '' 's/colibri_daemon_program/colibri_daemon_binary/g' \ "${RC_DIR}/colibri_daemon" # Remove -u from daemon(8) args: rc.subr already runs as colibri via su, # so daemon(8)'s own privilege drop double-drops and fails with # "failed to set user environment". sed -i '' 's/ -u \${colibri_daemon_user} //' \ "${RC_DIR}/colibri_daemon" # Fix pidfile permissions: daemon(8) -P creates the pidfile as 0600 # owned by the target user, which blocks non-root users (clawdie) from # running 'service colibri_daemon status'. Chmod in poststart. sed -i '' '/socket ready/a\ chmod 644 "${pidfile}" 2>/dev/null || true a\ chmod 660 "${colibri_daemon_socket}" 2>/dev/null || true' \ "${RC_DIR}/colibri_daemon" # Add DeepSeek API key and cache warming to daemon environment. # These are injected into the rc.d prestart so the daemon picks them up # without needing a separate config file. sed -i '' '/export COLIBRI_COST_MODE/a\ export DEEPSEEK_API_KEY="${DEEPSEEK_API_KEY:-}"\ export COLIBRI_CACHE_WARMING="true"\ export COLIBRI_CACHE_WARMING_INTERVAL_HOURS="6"' \ "${RC_DIR}/colibri_daemon" cat > "${ETC_DIR}/rc.conf.sample" < "${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. Runtime validation: service colibri_daemon start colibri status colibri create-task --title "iso smoke" colibri list-tasks --status queued service colibri_daemon stop EOF chmod 0755 "${DB_DIR}" "${RUN_DIR}" "${LOG_DIR}" cat <