Merge pull request 'feat: stage the simplified clawdie agent into the ISO (Sam & Claude)' (#5) from feat/clawdie-staging-onto-live-boot into main

Reviewed-on: #5
This commit is contained in:
clawdie 2026-06-02 09:18:00 +02:00
commit d028cc1e49
4 changed files with 267 additions and 0 deletions

View file

@ -81,6 +81,19 @@ COLIBRI_ARTIFACT_DIR="${COLIBRI_ARTIFACT_DIR:-}"
COLIBRI_DAEMON_ENABLE="${COLIBRI_DAEMON_ENABLE:-NO}" COLIBRI_DAEMON_ENABLE="${COLIBRI_DAEMON_ENABLE:-NO}"
COLIBRI_COST_MODE="${COLIBRI_COST_MODE:-smart}" COLIBRI_COST_MODE="${COLIBRI_COST_MODE:-smart}"
# Clawdie agent — the simplified, operator-friendly Colibri lane.
# One small Rust binary (glasspane + herdr + DeepSeek/Telegram), built from the
# same ../colibri checkout. Opt-in (default NO) so it does not disturb the
# current default build; flip to YES for the simplified operator image.
# The two credentials are normally baked into the binary at build time:
# (cd ../colibri && CLAWDIE_TG_TOKEN=... CLAWDIE_DEEPSEEK_KEY=... \
# cargo build --release -p clawdie)
# Leave them blank to ship a "bring your own key" binary configured via the
# rc.d env file (/usr/local/etc/clawdie/clawdie.env) at runtime instead.
FEATURE_CLAWDIE="${FEATURE_CLAWDIE:-NO}"
CLAWDIE_ARTIFACT_DIR="${CLAWDIE_ARTIFACT_DIR:-}"
CLAWDIE_ENABLE="${CLAWDIE_ENABLE:-YES}"
# Local LLM runtime (optional) # Local LLM runtime (optional)
# Choices: none | ollama | llama_cpp # Choices: none | ollama | llama_cpp
LOCAL_LLM_PROVIDER="${LOCAL_LLM_PROVIDER:-none}" LOCAL_LLM_PROVIDER="${LOCAL_LLM_PROVIDER:-none}"

101
build.sh
View file

@ -121,6 +121,7 @@ echo " Pkg : ${DEFAULT_PKG_BRANCH}"
echo " GPU : ${GPU_DRIVER:-auto-detect}" echo " GPU : ${GPU_DRIVER:-auto-detect}"
echo " Target : ${TARGET:-baremetal}" echo " Target : ${TARGET:-baremetal}"
echo " Colibri : ${FEATURE_COLIBRI:-NO}" echo " Colibri : ${FEATURE_COLIBRI:-NO}"
echo " Clawdie : ${FEATURE_CLAWDIE:-NO}"
echo "" echo ""
# Name the output for the thing we are actually building. # Name the output for the thing we are actually building.
@ -310,6 +311,41 @@ preflight_colibri_artifacts() {
done done
} }
# clawdie shares the ../colibri checkout; CLAWDIE_ARTIFACT_DIR overrides where
# the prebuilt `clawdie` binary is found.
resolve_clawdie_paths() {
resolve_colibri_paths
_resolved_clawdie_repo="${_resolved_colibri_repo}"
if [ -n "${CLAWDIE_ARTIFACT_DIR:-}" ]; then
_resolved_clawdie_artifact_dir="${CLAWDIE_ARTIFACT_DIR}"
case "${_resolved_clawdie_artifact_dir}" in
/*) ;;
*) _resolved_clawdie_artifact_dir="${SCRIPT_DIR}/${_resolved_clawdie_artifact_dir}" ;;
esac
else
_resolved_clawdie_artifact_dir="${_resolved_clawdie_repo}/target/release"
fi
}
preflight_clawdie_artifacts() {
[ "${FEATURE_CLAWDIE:-NO}" = "YES" ] || return 0
[ "${FETCH_ONLY:-0}" -eq 0 ] || return 0
resolve_clawdie_paths
_clawdie_rc="${_resolved_clawdie_repo}/packaging/freebsd/clawdie.in"
if [ ! -f "${_clawdie_rc}" ]; then
echo "ERROR: clawdie rc.d source missing: ${_clawdie_rc}"
echo " Set COLIBRI_REPO=/path/to/colibri or FEATURE_CLAWDIE=NO."
exit 1
fi
if [ ! -x "${_resolved_clawdie_artifact_dir}/clawdie" ]; then
echo "ERROR: clawdie release binary missing: ${_resolved_clawdie_artifact_dir}/clawdie"
echo " Build first: (cd ${_resolved_clawdie_repo} && cargo build --release -p clawdie)"
echo " Or set FEATURE_CLAWDIE=NO to skip clawdie staging."
exit 1
fi
}
override_networkmgr_package() { override_networkmgr_package() {
_networkmgr_pkg=$(find "${PKG_REPO_DIR}/All/Hashed" -maxdepth 1 -type f -name 'networkmgr-*.pkg' | sort | tail -1) _networkmgr_pkg=$(find "${PKG_REPO_DIR}/All/Hashed" -maxdepth 1 -type f -name 'networkmgr-*.pkg' | sort | tail -1)
if [ -z "${_networkmgr_pkg:-}" ]; then if [ -z "${_networkmgr_pkg:-}" ]; then
@ -745,6 +781,69 @@ install_colibri_service() {
fi fi
} }
install_clawdie_service() {
[ "${FEATURE_CLAWDIE:-NO}" = "YES" ] || {
echo " Clawdie agent staging disabled (FEATURE_CLAWDIE=${FEATURE_CLAWDIE:-NO})"
return 0
}
echo " Staging Clawdie agent..."
resolve_clawdie_paths
env \
COLIBRI_REPO="${_resolved_clawdie_repo}" \
CLAWDIE_ARTIFACT_DIR="${_resolved_clawdie_artifact_dir}" \
CLAWDIE_STAGE_ENABLE="${CLAWDIE_ENABLE:-YES}" \
"${SCRIPT_DIR}/scripts/stage-clawdie-iso.sh" "${MOUNT_POINT}"
if ! /usr/sbin/pw -R "${MOUNT_POINT}" groupshow clawdie >/dev/null 2>&1; then
/usr/sbin/pw -R "${MOUNT_POINT}" groupadd clawdie
fi
if ! /usr/sbin/pw -R "${MOUNT_POINT}" usershow clawdie >/dev/null 2>&1; then
/usr/sbin/pw -R "${MOUNT_POINT}" useradd clawdie \
-g clawdie \
-d /var/db/clawdie \
-s /usr/sbin/nologin \
-c "Clawdie Agent"
fi
mkdir -p \
"${MOUNT_POINT}/var/db/clawdie" \
"${MOUNT_POINT}/var/run/clawdie" \
"${MOUNT_POINT}/var/log/clawdie"
chroot "${MOUNT_POINT}" chown -R clawdie:clawdie \
/var/db/clawdie \
/var/run/clawdie \
/var/log/clawdie
chmod 0750 \
"${MOUNT_POINT}/var/db/clawdie" \
"${MOUNT_POINT}/var/run/clawdie" \
"${MOUNT_POINT}/var/log/clawdie"
set_config_line "${MOUNT_POINT}/etc/rc.conf" "clawdie_enable=\"${CLAWDIE_ENABLE:-YES}\""
set_config_line "${MOUNT_POINT}/etc/rc.conf" 'clawdie_user="clawdie"'
set_config_line "${MOUNT_POINT}/etc/rc.conf" 'clawdie_group="clawdie"'
set_config_line "${MOUNT_POINT}/etc/rc.conf" 'clawdie_data_dir="/var/db/clawdie"'
set_config_line "${MOUNT_POINT}/etc/rc.conf" 'clawdie_run_dir="/var/run/clawdie"'
set_config_line "${MOUNT_POINT}/etc/rc.conf" 'clawdie_socket="/var/run/clawdie/clawdie.sock"'
set_config_line "${MOUNT_POINT}/etc/rc.conf" 'clawdie_logfile="/var/log/clawdie/clawdie.log"'
set_config_line "${MOUNT_POINT}/etc/rc.conf" 'clawdie_host="$(hostname)"'
if [ ! -x "${MOUNT_POINT}/usr/local/bin/clawdie" ]; then
echo "ERROR: clawdie binary missing from live image"
exit 1
fi
if [ ! -x "${MOUNT_POINT}/usr/local/etc/rc.d/clawdie" ]; then
echo "ERROR: clawdie rc.d script missing from live image"
exit 1
fi
if ! /usr/sbin/pw -R "${MOUNT_POINT}" usershow clawdie >/dev/null 2>&1; then
echo "ERROR: clawdie service user missing from live image"
exit 1
fi
}
install_live_npm_globals() { install_live_npm_globals() {
echo " Installing bundled npm globals into live image..." echo " Installing bundled npm globals into live image..."
@ -1517,6 +1616,7 @@ EOF
} }
preflight_colibri_artifacts preflight_colibri_artifacts
preflight_clawdie_artifacts
# --- step 1: fetch FreeBSD memstick --- # --- step 1: fetch FreeBSD memstick ---
MEMSTICK="${CACHE_DIR}/FreeBSD-${FREEBSD_VERSION}-${FREEBSD_ARCH}-memstick.img" MEMSTICK="${CACHE_DIR}/FreeBSD-${FREEBSD_VERSION}-${FREEBSD_ARCH}-memstick.img"
@ -1907,6 +2007,7 @@ rm -f "${MOUNT_POINT}/etc/installerconfig"
install_live_runtime_packages install_live_runtime_packages
configure_live_operator_session configure_live_operator_session
install_colibri_service install_colibri_service
install_clawdie_service
# Copy payload # Copy payload
# Rebuild payload paths from scratch inside the reusable work image. A failed # Rebuild payload paths from scratch inside the reusable work image. A failed

107
scripts/stage-clawdie-iso.sh Executable file
View file

@ -0,0 +1,107 @@
#!/bin/sh
# Stage the prebuilt `clawdie` FreeBSD binary + rc.d service into an image root.
#
# `clawdie` is the simplified, operator-friendly Colibri agent: one small binary
# (glasspane + herdr + DeepSeek/Telegram). This script does NOT build it — build
# or provide the artifact first, optionally with baked credentials:
#
# (cd ../colibri && CLAWDIE_TG_TOKEN=... CLAWDIE_DEEPSEEK_KEY=... \
# cargo build --release -p clawdie)
#
# Usage:
# COLIBRI_REPO=../colibri scripts/stage-clawdie-iso.sh /path/to/image-root
# CLAWDIE_ARTIFACT_DIR=/path/to/release scripts/stage-clawdie-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:-"${REPO_ROOT}/../colibri"}
CLAWDIE_ARTIFACT_DIR=${CLAWDIE_ARTIFACT_DIR:-"${COLIBRI_REPO}/target/release"}
CLAWDIE_STAGE_ENABLE=${CLAWDIE_STAGE_ENABLE:-YES}
BIN_DIR="${DESTDIR}/usr/local/bin"
RC_DIR="${DESTDIR}/usr/local/etc/rc.d"
ETC_DIR="${DESTDIR}/usr/local/etc/clawdie"
DB_DIR="${DESTDIR}/var/db/clawdie"
RUN_DIR="${DESTDIR}/var/run/clawdie"
LOG_DIR="${DESTDIR}/var/log/clawdie"
RC_SOURCE="${COLIBRI_REPO}/packaging/freebsd/clawdie.in"
require_file() {
if [ ! -f "$1" ]; then
echo "missing required clawdie artifact: $1" >&2
exit 66
fi
}
require_exec() {
if [ ! -x "$1" ]; then
echo "missing executable clawdie artifact: $1" >&2
echo "hint: (cd ${COLIBRI_REPO} && cargo build --release -p clawdie)" >&2
exit 66
fi
}
require_file "${RC_SOURCE}"
require_exec "${CLAWDIE_ARTIFACT_DIR}/clawdie"
mkdir -p "${BIN_DIR}" "${RC_DIR}" "${ETC_DIR}" "${DB_DIR}" "${RUN_DIR}" "${LOG_DIR}"
install -m 0555 "${CLAWDIE_ARTIFACT_DIR}/clawdie" "${BIN_DIR}/clawdie"
install -m 0555 "${RC_SOURCE}" "${RC_DIR}/clawdie"
cat > "${ETC_DIR}/rc.conf.sample" <<EOF
# Clawdie agent service defaults for the Clawdie ISO.
# Merge into /etc/rc.conf or /etc/rc.conf.d/clawdie.
clawdie_enable="${CLAWDIE_STAGE_ENABLE}"
clawdie_user="clawdie"
clawdie_group="clawdie"
clawdie_data_dir="/var/db/clawdie"
clawdie_run_dir="/var/run/clawdie"
clawdie_socket="/var/run/clawdie/clawdie.sock"
clawdie_logfile="/var/log/clawdie/clawdie.log"
clawdie_host="\$(hostname)"
clawdie_env_file="/usr/local/etc/clawdie/clawdie.env"
EOF
# Per-host credential override template (binary keeps its baked build-flag
# defaults; this file is optional and only read if present + readable).
if [ ! -f "${ETC_DIR}/clawdie.env" ]; then
cat > "${ETC_DIR}/clawdie.env.sample" <<'EOF'
# Optional per-host credential overrides for clawdie.
# Copy to clawdie.env (chmod 0600) to override the baked build-flag values.
# CLAWDIE_TG_TOKEN=123456:telegram-bot-token
# CLAWDIE_DEEPSEEK_KEY=sk-deepseek-key
EOF
fi
cat > "${ETC_DIR}/README.iso" <<'EOF'
Clawdie agent ISO staging notes
===============================
The ISO build creates the clawdie user/group and enables the rc.d service
according to build.cfg. Out of the box clawdie offers exactly: a Telegram bot
and a DeepSeek lane — both normally baked at build time. Runtime validation:
service clawdie start
service clawdie status
sockstat | grep clawdie # Herdr socket bound
service clawdie stop
Lifted on purpose: cost modes, quotas, multi-provider fallback, per-user limits.
EOF
chmod 0750 "${DB_DIR}" "${RUN_DIR}" "${LOG_DIR}"
cat <<EOF
Staged clawdie into ${DESTDIR}
artifact : ${CLAWDIE_ARTIFACT_DIR}/clawdie
rc.d : ${RC_SOURCE}
enable : ${CLAWDIE_STAGE_ENABLE}
EOF

View file

@ -187,6 +187,52 @@ rm -rf /tmp/colibri-*
`colibri-tui` is optional in the staging script, but desired for this operator `colibri-tui` is optional in the staging script, but desired for this operator
USB target and should be verified with the other three binaries. USB target and should be verified with the other three binaries.
### Clawdie agent preflight (optional simplified lane)
`FEATURE_CLAWDIE` (default `NO`) stages the simplified `clawdie` agent — one
small binary (glasspane + herdr + DeepSeek/Telegram) built from the same
`../colibri` checkout. Enable it for the simplified operator image. The two
credentials are normally baked at build time:
```sh
cd /home/clawdie/colibri
CLAWDIE_TG_TOKEN="<telegram-bot-token>" \
CLAWDIE_DEEPSEEK_KEY="<deepseek-key>" \
cargo build --release -p clawdie
ls -lh target/release/clawdie
file target/release/clawdie
```
Leave the flags unset to ship a "bring your own key" binary configured via the
rc.d env file (`/usr/local/etc/clawdie/clawdie.env`) at runtime. Same cleanup
invariant as Colibri: do **not** `cargo clean` until the ISO build has consumed
`../colibri/target/release/clawdie`. ISO-repo preflight:
```sh
cd /home/clawdie/clawdie-iso
sh -n build.sh
sh -n scripts/stage-clawdie-iso.sh
```
See `../colibri/docs/CLAWDIE-BUILD.md` and `../colibri/docs/CLAWDIE-AGENT-WIKI.md`.
### Next build: carry the XFCE operator-USB fixes
The next operator-USB image must retain the proven XFCE/live-GUI fixes
(`PLAN-OPERATOR-USB-NEXT.md`, `doc/AMD-ASUS-XFCE-LIVE-USB-FINDINGS.md`):
- **SDDM over LightDM** — load-bearing; do not revert (LightDM was the silent
live-GUI blocker on Intel + AMD).
- **`clawdie-live-gpu`** pre-SDDM rc.d KMS pick (conservative; NVIDIA/AMD safe).
- **Hardened USB-root power policy** (`b9290ab`): `powerdxx`, C3 C-state default,
`hw.usb.no_suspend=1`, boot-time `power_profile`.
- Base fixes already landed: resolver bootstrap, internal-audio preference,
touchpad XInput guards, loader branding, hardware-report capture.
Real hardware remains final proof for XFCE/session/input/audio — bhyve and
static image inspection are build gates only.
## Choose fetch mode ## Choose fetch mode
Use the lightest safe flag: Use the lightest safe flag: