2026-05-28 00:46:24 +02:00
|
|
|
#!/bin/sh
|
|
|
|
|
# Stage prebuilt Colibri FreeBSD service artifacts into an ISO/image root.
|
|
|
|
|
#
|
|
|
|
|
# This script does not build Colibri. Build or provide artifacts first:
|
2026-06-05 00:38:25 +02:00
|
|
|
# (cd /home/clawdie/ai/colibri && cargo build --workspace --release)
|
2026-05-28 00:46:24 +02:00
|
|
|
#
|
|
|
|
|
# Usage:
|
2026-06-05 00:38:25 +02:00
|
|
|
# COLIBRI_REPO=/home/clawdie/ai/colibri scripts/stage-colibri-iso.sh /path/to/image-root
|
2026-05-28 00:46:24 +02:00
|
|
|
# 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)
|
2026-06-05 00:38:25 +02:00
|
|
|
COLIBRI_REPO=${COLIBRI_REPO:-"/home/clawdie/ai/colibri"}
|
2026-05-28 00:46:24 +02:00
|
|
|
COLIBRI_ARTIFACT_DIR=${COLIBRI_ARTIFACT_DIR:-"${COLIBRI_REPO}/target/release"}
|
2026-06-04 12:58:24 +02:00
|
|
|
COLIBRI_STAGE_ENABLE=${COLIBRI_STAGE_ENABLE:-YES}
|
2026-05-28 00:46:24 +02:00
|
|
|
COLIBRI_STAGE_INCLUDE_TUI=${COLIBRI_STAGE_INCLUDE_TUI:-1}
|
2026-06-21 07:55:24 +02:00
|
|
|
COLIBRI_STAGE_TEST_AGENT=${COLIBRI_STAGE_TEST_AGENT:-NO}
|
2026-05-28 00:46:24 +02:00
|
|
|
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"
|
2026-06-02 09:00:33 +02:00
|
|
|
NEWSYSLOG_DIR="${DESTDIR}/usr/local/etc/newsyslog.conf.d"
|
2026-05-28 00:46:24 +02:00
|
|
|
RC_SOURCE="${COLIBRI_REPO}/packaging/freebsd/colibri_daemon.in"
|
2026-06-02 09:00:33 +02:00
|
|
|
NEWSYSLOG_SOURCE="${COLIBRI_REPO}/packaging/freebsd/newsyslog-colibri.conf"
|
2026-05-28 00:46:24 +02:00
|
|
|
|
|
|
|
|
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}"
|
2026-06-02 09:00:33 +02:00
|
|
|
require_file "${NEWSYSLOG_SOURCE}"
|
|
|
|
|
mkdir -p "${BIN_DIR}" "${RC_DIR}" "${ETC_DIR}" "${NEWSYSLOG_DIR}" "${DB_DIR}" "${RUN_DIR}" "${LOG_DIR}"
|
2026-05-28 00:46:24 +02:00
|
|
|
|
|
|
|
|
copy_bin colibri-daemon
|
|
|
|
|
copy_bin colibri
|
2026-06-13 19:29:31 +02:00
|
|
|
copy_bin colibri-mcp
|
2026-05-28 00:46:24 +02:00
|
|
|
|
2026-06-21 07:55:24 +02:00
|
|
|
if [ "${COLIBRI_STAGE_TEST_AGENT}" = "YES" ]; then
|
|
|
|
|
copy_bin colibri-test-agent
|
|
|
|
|
fi
|
|
|
|
|
|
2026-05-28 00:46:24 +02:00
|
|
|
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"
|
2026-06-02 09:00:33 +02:00
|
|
|
install -m 0644 "${NEWSYSLOG_SOURCE}" "${NEWSYSLOG_DIR}/colibri.conf"
|
|
|
|
|
|
|
|
|
|
if ! grep -q '^command="/usr/sbin/daemon"' "${RC_DIR}/colibri_daemon" || \
|
2026-06-14 23:24:41 +02:00
|
|
|
! grep -q -- '-o .*colibri_daemon_binary' "${RC_DIR}/colibri_daemon" || \
|
2026-06-15 07:37:16 +02:00
|
|
|
! grep -q 'colibri_daemon_provider_env' "${RC_DIR}/colibri_daemon" || \
|
2026-06-15 09:10:52 +02:00
|
|
|
! 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
|
2026-06-14 23:24:41 +02:00
|
|
|
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
|
2026-06-02 09:00:33 +02:00
|
|
|
exit 66
|
|
|
|
|
fi
|
2026-05-28 00:46:24 +02:00
|
|
|
|
2026-06-14 23:25:22 +02:00
|
|
|
if grep -q -- '-u \${colibri_daemon_user}' "${RC_DIR}/colibri_daemon"; then
|
2026-06-14 23:24:41 +02:00
|
|
|
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
|
2026-06-14 22:09:54 +02:00
|
|
|
fi
|
2026-06-04 12:58:24 +02:00
|
|
|
|
2026-05-28 00:46:24 +02:00
|
|
|
cat > "${ETC_DIR}/rc.conf.sample" <<EOF
|
|
|
|
|
# Colibri control plane service defaults for the Clawdie ISO.
|
|
|
|
|
# Merge into /etc/rc.conf or /etc/rc.conf.d/colibri_daemon.
|
|
|
|
|
colibri_daemon_enable="${COLIBRI_STAGE_ENABLE}"
|
|
|
|
|
colibri_daemon_user="colibri"
|
|
|
|
|
colibri_daemon_group="colibri"
|
|
|
|
|
colibri_daemon_data_dir="/var/db/colibri"
|
|
|
|
|
colibri_daemon_run_dir="/var/run/colibri"
|
|
|
|
|
colibri_daemon_socket="/var/run/colibri/colibri.sock"
|
|
|
|
|
colibri_daemon_db_path="/var/db/colibri/colibri.sqlite"
|
|
|
|
|
colibri_daemon_logfile="/var/log/colibri/daemon.log"
|
2026-06-15 07:37:16 +02:00
|
|
|
colibri_daemon_provider_env="/usr/local/etc/colibri/provider.env"
|
2026-06-15 09:10:52 +02:00
|
|
|
colibri_daemon_host="\$(/bin/hostname)"
|
|
|
|
|
colibri_daemon_cost_mode="${COLIBRI_COST_MODE}"
|
2026-05-28 00:46:24 +02:00
|
|
|
EOF
|
|
|
|
|
|
2026-06-20 07:27:51 +02:00
|
|
|
cat > "${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"
|
2026-06-21 18:53:41 +02:00
|
|
|
|
|
|
|
|
# Auto-spawn one Pi agent on daemon startup once a DeepSeek key is present
|
|
|
|
|
# (the Operator Image OOTB flow). The daemon sources this file, so the spawned
|
|
|
|
|
# Pi inherits the provider keys set here.
|
|
|
|
|
COLIBRI_AUTOSPAWN_PI="YES"
|
2026-06-20 07:27:51 +02:00
|
|
|
EOF
|
|
|
|
|
chmod 0600 "${ETC_DIR}/provider.env" 2>/dev/null || true
|
|
|
|
|
|
2026-06-21 18:53:41 +02:00
|
|
|
# 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
|
|
|
|
|
|
2026-06-15 07:37:16 +02:00
|
|
|
cat > "${ETC_DIR}/provider.env.sample" <<'EOF'
|
2026-06-20 07:27:51 +02:00
|
|
|
# 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="..."
|
2026-06-15 07:37:16 +02:00
|
|
|
#
|
2026-06-20 07:27:51 +02:00
|
|
|
# Direct provider keys (optional when Vaultwarden provisioning is used):
|
2026-06-15 07:37:16 +02:00
|
|
|
# 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-chat"
|
2026-06-21 18:53:41 +02:00
|
|
|
#
|
|
|
|
|
# Behavior toggles (non-secret):
|
|
|
|
|
# COLIBRI_AUTOSPAWN_PI="YES" # auto-spawn one Pi on daemon startup
|
|
|
|
|
# COLIBRI_PI_BINARY="pi" # Pi executable name/path
|
|
|
|
|
# COLIBRI_AUTOSPAWN_PI_ARGS="--mode json" # Pi argv
|
|
|
|
|
# COLIBRI_MCP_EXTERNAL_CALL="1" # allow Pi (via colibri-mcp) to call
|
|
|
|
|
# # external MCP servers; set by
|
|
|
|
|
# # clawdie-enable-mother
|
2026-06-15 07:37:16 +02:00
|
|
|
EOF
|
|
|
|
|
|
2026-05-28 00:46:24 +02:00
|
|
|
cat > "${ETC_DIR}/README.iso" <<'EOF'
|
|
|
|
|
Colibri ISO staging notes
|
|
|
|
|
=========================
|
|
|
|
|
|
2026-06-02 09:00:33 +02:00
|
|
|
The ISO build creates the colibri user/group and stages the rc.d service.
|
2026-06-04 12:58:24 +02:00
|
|
|
The colibri-daemon runs under daemon(8) supervision and is enabled at boot.
|
|
|
|
|
If the daemon fails, it restarts automatically without blocking SDDM/XFCE.
|
2026-06-15 07:37:16 +02:00
|
|
|
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.
|
|
|
|
|
|
2026-06-04 12:58:24 +02:00
|
|
|
Runtime validation:
|
2026-05-28 00:46:24 +02:00
|
|
|
|
|
|
|
|
service colibri_daemon start
|
|
|
|
|
colibri status
|
2026-06-15 07:37:16 +02:00
|
|
|
colibri create-task --title "iso check"
|
2026-05-28 00:46:24 +02:00
|
|
|
colibri list-tasks --status queued
|
2026-06-13 19:29:31 +02:00
|
|
|
colibri-mcp tools
|
|
|
|
|
COLIBRI_MCP_WRITE=1 colibri-mcp tools # trusted write-capable MCP profile
|
2026-05-28 00:46:24 +02:00
|
|
|
service colibri_daemon stop
|
|
|
|
|
EOF
|
|
|
|
|
|
2026-06-04 12:58:24 +02:00
|
|
|
chmod 0755 "${DB_DIR}" "${RUN_DIR}" "${LOG_DIR}"
|
2026-05-28 00:46:24 +02:00
|
|
|
|
|
|
|
|
cat <<EOF
|
|
|
|
|
Staged Colibri into ${DESTDIR}
|
|
|
|
|
artifacts: ${COLIBRI_ARTIFACT_DIR}
|
|
|
|
|
rc.d : ${RC_SOURCE}
|
|
|
|
|
enable : ${COLIBRI_STAGE_ENABLE}
|
|
|
|
|
cost : ${COLIBRI_COST_MODE}
|
2026-06-21 07:55:24 +02:00
|
|
|
test bin : ${COLIBRI_STAGE_TEST_AGENT}
|
2026-05-28 00:46:24 +02:00
|
|
|
EOF
|