feat(iso): stage zot agent (pinned) + populate ZOT_HOME/auth.json (Sam & Claude) #38

Merged
clawdie merged 1 commit from feat/stage-zot-agent into main 2026-06-13 10:35:42 +02:00
3 changed files with 166 additions and 0 deletions

View file

@ -116,6 +116,22 @@ FEATURE_CLAWDIE="${FEATURE_CLAWDIE:-NO}"
CLAWDIE_ARTIFACT_DIR="${CLAWDIE_ARTIFACT_DIR:-}"
CLAWDIE_ENABLE="${CLAWDIE_ENABLE:-NO}"
# zot agent harness (the consolidation target — see colibri ADR
# docs/ADR-agent-harness-consolidation.md). zot is a single static Go binary
# (providers incl. DeepSeek, Telegram bot, json/rpc modes, SKILL.md, extensions).
# Opt-in (default NO) while Pi stays the default harness during migration.
# Pin a tag for reproducible images; zot has no FreeBSD release, so the build
# host compiles it: (cd $ZOT_REPO && git checkout $ZOT_VERSION &&
# GOOS=freebsd GOARCH=amd64 go build -trimpath -o bin/zot ./cmd/zot)
FEATURE_ZOT="${FEATURE_ZOT:-NO}"
ZOT_VERSION="${ZOT_VERSION:-v0.2.29}"
ZOT_REPO="${ZOT_REPO:-/home/clawdie/ai/zot}"
ZOT_ARTIFACT_DIR="${ZOT_ARTIFACT_DIR:-}"
# Optional: bake the operator's DeepSeek key into $ZOT_HOME/auth.json (0600).
# Leave blank to stage an auth.json.sample template the operator fills in, or
# to configure at runtime via `zot` env / `zot telegram-bot setup`.
ZOT_DEEPSEEK_KEY="${ZOT_DEEPSEEK_KEY:-}"
# Local LLM runtime (optional)
# Choices: none | ollama | llama_cpp
LOCAL_LLM_PROVIDER="${LOCAL_LLM_PROVIDER:-none}"

View file

@ -123,6 +123,7 @@ echo " NVIDIA universal : ${NVIDIA_UNIVERSAL:-NO}"
echo " Target : ${TARGET:-baremetal}"
echo " Colibri : ${FEATURE_COLIBRI:-NO}"
echo " Clawdie agent : ${FEATURE_CLAWDIE:-NO}"
echo " zot agent : ${FEATURE_ZOT:-NO} (${ZOT_VERSION:-})"
echo ""
# Name the output for the thing we are actually building.
@ -371,6 +372,38 @@ preflight_clawdie_artifacts() {
fi
}
resolve_zot_paths() {
_resolved_zot_repo="${ZOT_REPO:-${SCRIPT_DIR}/../zot}"
case "${_resolved_zot_repo}" in
/*) ;;
*) _resolved_zot_repo="${SCRIPT_DIR}/${_resolved_zot_repo}" ;;
esac
if [ -n "${ZOT_ARTIFACT_DIR:-}" ]; then
_resolved_zot_artifact_dir="${ZOT_ARTIFACT_DIR}"
case "${_resolved_zot_artifact_dir}" in
/*) ;;
*) _resolved_zot_artifact_dir="${SCRIPT_DIR}/${_resolved_zot_artifact_dir}" ;;
esac
else
_resolved_zot_artifact_dir="${_resolved_zot_repo}/bin"
fi
}
preflight_zot_artifacts() {
[ "${FEATURE_ZOT:-NO}" = "YES" ] || return 0
[ "${FETCH_ONLY:-0}" -eq 0 ] || return 0
resolve_zot_paths
if [ ! -x "${_resolved_zot_artifact_dir}/zot" ]; then
echo "ERROR: zot binary missing: ${_resolved_zot_artifact_dir}/zot"
echo " zot has no FreeBSD release — build it first:"
echo " (cd ${_resolved_zot_repo} && git checkout ${ZOT_VERSION:-v0.2.29} && \\"
echo " GOOS=freebsd GOARCH=amd64 go build -trimpath -o bin/zot ./cmd/zot)"
echo " Or set FEATURE_ZOT=NO to skip zot staging."
exit 1
fi
}
override_networkmgr_package() {
_networkmgr_pkg=$(find "${PKG_REPO_DIR}/All/Hashed" -maxdepth 1 -type f -name 'networkmgr-*.pkg' | sort | tail -1)
if [ -z "${_networkmgr_pkg:-}" ]; then
@ -854,6 +887,32 @@ install_colibri_service() {
fi
}
install_zot_agent() {
[ "${FEATURE_ZOT:-NO}" = "YES" ] || {
echo " zot agent staging disabled (FEATURE_ZOT=${FEATURE_ZOT:-NO})"
return 0
}
echo " Staging zot agent (${ZOT_VERSION:-})..."
resolve_zot_paths
env \
ZOT_ARTIFACT_DIR="${_resolved_zot_artifact_dir}" \
ZOT_OPERATOR="clawdie" \
ZOT_DEEPSEEK_KEY="${ZOT_DEEPSEEK_KEY:-}" \
"${SCRIPT_DIR}/scripts/stage-zot-iso.sh" "${MOUNT_POINT}"
# zot state lives in the operator's home; own it as the operator.
if /usr/sbin/pw -R "${MOUNT_POINT}" usershow clawdie >/dev/null 2>&1; then
chroot "${MOUNT_POINT}" chown -R clawdie:clawdie /home/clawdie/.local 2>/dev/null || true
fi
if [ ! -x "${MOUNT_POINT}/usr/local/bin/zot" ]; then
echo "ERROR: zot binary missing from live image"
exit 1
fi
}
install_clawdie_service() {
[ "${FEATURE_CLAWDIE:-NO}" = "YES" ] || {
echo " Clawdie agent staging disabled (FEATURE_CLAWDIE=${FEATURE_CLAWDIE:-NO})"
@ -1832,6 +1891,7 @@ EOF
preflight_colibri_artifacts
preflight_clawdie_artifacts
preflight_zot_artifacts
# --- step 1: fetch FreeBSD memstick ---
MEMSTICK="${CACHE_DIR}/FreeBSD-${FREEBSD_VERSION}-${FREEBSD_ARCH}-memstick.img"
@ -2223,6 +2283,7 @@ install_live_runtime_packages
configure_live_operator_session
install_colibri_service
install_clawdie_service
install_zot_agent
install_nvidia_universal_repo
# Copy payload

89
scripts/stage-zot-iso.sh Executable file
View file

@ -0,0 +1,89 @@
#!/bin/sh
# Stage the prebuilt `zot` agent binary + credentials into an image root.
#
# zot is the agent-harness consolidation target (one static Go binary). It has no
# FreeBSD release, so build it on the host first and point ZOT_ARTIFACT_DIR here:
# (cd ../zot && git checkout "$ZOT_VERSION" \
# && GOOS=freebsd GOARCH=amd64 go build -trimpath -o bin/zot ./cmd/zot)
#
# Credentials: zot resolves provider keys as --api-key -> provider env var ->
# $ZOT_HOME/auth.json. This stages auth.json (DeepSeek) under the operator's
# default ZOT_HOME (~/.local/state/zot). The Telegram token is configured
# separately at runtime via `zot telegram-bot setup` (it lives in zot state).
#
# Usage:
# ZOT_ARTIFACT_DIR=/path/to/bin scripts/stage-zot-iso.sh /path/to/image-root
set -eu
if [ "${1:-}" = "" ]; then
echo "usage: $0 DESTDIR" >&2
exit 64
fi
DESTDIR=$1
ZOT_ARTIFACT_DIR=${ZOT_ARTIFACT_DIR:?set ZOT_ARTIFACT_DIR to the dir holding the built zot binary}
ZOT_OPERATOR=${ZOT_OPERATOR:-clawdie}
ZOT_DEEPSEEK_KEY=${ZOT_DEEPSEEK_KEY:-}
BIN_SRC="${ZOT_ARTIFACT_DIR}/zot"
BIN_DIR="${DESTDIR}/usr/local/bin"
# zot's default ZOT_HOME on FreeBSD is ~/.local/state/zot
ZOT_HOME_REL=".local/state/zot"
OP_HOME="${DESTDIR}/home/${ZOT_OPERATOR}"
ZOT_HOME="${OP_HOME}/${ZOT_HOME_REL}"
if [ ! -x "${BIN_SRC}" ]; then
echo "missing executable zot artifact: ${BIN_SRC}" >&2
echo "hint: (cd \$ZOT_REPO && GOOS=freebsd GOARCH=amd64 go build -trimpath -o bin/zot ./cmd/zot)" >&2
exit 66
fi
mkdir -p "${BIN_DIR}" "${ZOT_HOME}"
install -m 0555 "${BIN_SRC}" "${BIN_DIR}/zot"
# auth.json: bake the DeepSeek key if provided (0600), else leave a template.
if [ -n "${ZOT_DEEPSEEK_KEY}" ]; then
umask 077
cat > "${ZOT_HOME}/auth.json" <<EOF
{
"deepseek": { "api_key": "${ZOT_DEEPSEEK_KEY}" }
}
EOF
chmod 0600 "${ZOT_HOME}/auth.json"
_cred_note="auth.json baked with DeepSeek key (0600)"
else
cat > "${ZOT_HOME}/auth.json.sample" <<'EOF'
{
"deepseek": { "api_key": "sk-REPLACE-ME" }
}
EOF
_cred_note="auth.json.sample staged (operator copies to auth.json, chmod 0600)"
fi
cat > "${ZOT_HOME}/README.iso" <<EOF
zot agent — ISO staging notes
=============================
Binary: /usr/local/bin/zot (pinned build; FreeBSD-native, no release tarball)
State (ZOT_HOME): ~/.local/state/zot (config.json, auth.json, sessions/, logs/)
Credentials (zot order: --api-key -> provider env -> auth.json):
- ${_cred_note}
- or export DEEPSEEK_API_KEY at runtime.
Telegram bridge (token stored in zot state, not auth.json):
zot telegram-bot setup # paste BotFather token
zot telegram-bot start
Supervision contract for Colibri glasspane:
zot --json "..." # newline-delimited json events
zot rpc # json-rpc loop
EOF
cat <<EOF
Staged zot into ${DESTDIR}
binary : ${BIN_DIR}/zot (from ${BIN_SRC})
state : home/${ZOT_OPERATOR}/${ZOT_HOME_REL}
creds : ${_cred_note}
EOF