feat(iso): stage colibri test agent and provider key help (Sam & Codex) #53

Closed
clawdie wants to merge 1 commit from feat/colibri-test-agent-provider-help into main
7 changed files with 76 additions and 73 deletions

View file

@ -82,7 +82,7 @@ Colibri-backed. The image includes:
```text
/usr/local/bin/colibri-daemon
/usr/local/bin/colibri
/usr/local/bin/colibri-smoke-agent
/usr/local/bin/colibri-test-agent
/usr/local/bin/colibri-mcp # MCP bridge for Zed/Claude Code/Cursor
/usr/local/bin/colibri-tui # if present in the artifact dir
/usr/local/etc/rc.d/colibri_daemon

View file

@ -334,7 +334,7 @@ sudo mdconfig -d -u md0
## Level 2: bhyve Verification Gate
Use bhyve as the standard pre-hardware gate when the ML350p lane is available.
This is no longer just an opportunistic smoke test; it is the default
This is no longer just an opportunistic startup check; it is the default
middle-stage verification between static artifact checks and physical hardware.
Treat bhyve as authoritative for boot/runtime plumbing and still treat real
@ -558,7 +558,7 @@ proprietary NVIDIA stack.
Linux baseline probe:
[linux-hardware.org/?probe=efd5b5b389](https://linux-hardware.org/?probe=efd5b5b389)
This is a real deploy target. First-boot smoke set for this machine
This is a real deploy target. First-boot check set for this machine
(or any AMD Ryzen U-series laptop) once the image lands:
```sh
@ -783,7 +783,7 @@ mdo -u root fwget -n # dry run
### What to do when the chipset is unsupported
The live USB image is still useful — all daemons bind to `lo0` too, so
`sshd`, Avahi, and the local stack can be smoke-tested without network.
`sshd`, Avahi, and the local stack can be validated without network.
For end-to-end validation, swap to a supported dongle from the lists
above.
@ -1069,7 +1069,7 @@ Mark the image good only when all of these are true:
- [ ] `/` is read-write, `/tmp` and `/var/log` are tmpfs, and `/var/tmp` stays on disk
- [ ] `~/.cache` points at `/tmp/clawdie/cache`
- [ ] `/usr/local/etc/xdg/xfce4/xinitrc` and `clawdie` xinitrc fallbacks are executable
- [ ] `xinit`/`startx`, `clawdie-startx`, `clawdie-gui`, and `xterm` are present for minimal Xorg rescue/smoke testing
- [ ] `xinit`/`startx`, `clawdie-startx`, `clawdie-gui`, and `xterm` are present for minimal Xorg rescue/startup checking
- [ ] `/var/lib/xkb` exists and XKB keymap compilation succeeds
- [ ] `XDG_RUNTIME_DIR` resolves to `/var/run/user/<uid>`
- [ ] `clawdie_live_gpu` log shows a sensible path or a clear fallback

View file

@ -337,7 +337,7 @@ preflight_colibri_artifacts() {
echo " Set COLIBRI_REPO=/path/to/colibri or FEATURE_COLIBRI=NO."
exit 1
fi
for _colibri_bin in colibri-daemon colibri colibri-smoke-agent colibri-mcp; do
for _colibri_bin in colibri-daemon colibri colibri-test-agent colibri-mcp; do
if [ ! -x "${_resolved_colibri_artifact_dir}/${_colibri_bin}" ]; then
echo "ERROR: Colibri release binary missing: ${_resolved_colibri_artifact_dir}/${_colibri_bin}"
command -v cargo >/dev/null 2>&1 || \
@ -818,6 +818,7 @@ install_colibri_service() {
set_config_line "${MOUNT_POINT}/etc/rc.conf" 'colibri_daemon_socket="/var/run/colibri/colibri.sock"'
set_config_line "${MOUNT_POINT}/etc/rc.conf" 'colibri_daemon_db_path="/var/db/colibri/colibri.sqlite"'
set_config_line "${MOUNT_POINT}/etc/rc.conf" 'colibri_daemon_logfile="/var/log/colibri/daemon.log"'
set_config_line "${MOUNT_POINT}/etc/rc.conf" 'colibri_daemon_provider_env="/usr/local/etc/colibri/provider.env"'
set_config_line "${MOUNT_POINT}/etc/rc.conf" 'colibri_daemon_host="$(hostname)"'
set_config_line "${MOUNT_POINT}/etc/rc.conf" "colibri_cost_mode=\"${COLIBRI_COST_MODE:-smart}\""
@ -851,7 +852,7 @@ install_colibri_service() {
_now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
sqlite3 "${_colibri_db}" "INSERT OR IGNORE INTO skills (id, name, description, category, created_at) VALUES
('$(uuidgen || echo 00000000-0000-0000-0000-000000000001)', 'freebsd-live-usb', 'FreeBSD live USB operator workstation procedures', 'freebsd', '${_now}'),
('$(uuidgen || echo 00000000-0000-0000-0000-000000000002)', 'colibri-smoke', 'Colibri daemon smoke test and validation', 'colibri', '${_now}'),
('$(uuidgen || echo 00000000-0000-0000-0000-000000000002)', 'colibri-test', 'Colibri daemon startup check and validation', 'colibri', '${_now}'),
('$(uuidgen || echo 00000000-0000-0000-0000-000000000003)', 'iso-build', 'Clawdie ISO build and staging workflow', 'iso', '${_now}'),
('$(uuidgen || echo 00000000-0000-0000-0000-000000000004)', 'tailscale-join', 'Tailscale mesh join procedure for operator USB', 'networking', '${_now}'),
('$(uuidgen || echo 00000000-0000-0000-0000-000000000005)', 'disk-deploy', 'Deploy from USB live to permanent disk install. Provisions ZFS pool, installs FreeBSD boot environment, migrates config, and prepares for the future deployed-system clawdie service.', 'clawdie', '${_now}'),

View file

@ -25,7 +25,7 @@ For rebuilds, clone a fresh working checkout into a separate directory such as
## Required live packages
Future ISOs should include these packages to make this path available out of the
Live operator ISOs include these packages to make this path available out of the
box:
```text
@ -102,7 +102,7 @@ Expected outputs:
```text
target/release/colibri-daemon
target/release/colibri
target/release/colibri-smoke-agent
target/release/colibri-test-agent
target/release/colibri-mcp
target/release/colibri-tui
```
@ -122,7 +122,7 @@ mdo -u root rm -f /var/run/colibri/colibri-daemon-supervisor.pid
mdo -u root install -m 0555 target/release/colibri-daemon /usr/local/bin/colibri-daemon
mdo -u root install -m 0555 target/release/colibri /usr/local/bin/colibri
mdo -u root install -m 0555 target/release/colibri-smoke-agent /usr/local/bin/colibri-smoke-agent
mdo -u root install -m 0555 target/release/colibri-test-agent /usr/local/bin/colibri-test-agent
mdo -u root install -m 0555 target/release/colibri-mcp /usr/local/bin/colibri-mcp
mdo -u root install -m 0555 target/release/colibri-tui /usr/local/bin/colibri-tui
mdo -u root install -m 0555 packaging/freebsd/colibri_daemon.in /usr/local/etc/rc.d/colibri_daemon
@ -132,9 +132,8 @@ mdo -u root service colibri_daemon start
```
`service colibri_daemon start` should return to the shell after a few seconds. If
it stays in the foreground, check that the rc.d script uses
`colibri_daemon_binary`, not `colibri_daemon_program`, and that `command=` is
`/usr/sbin/daemon`.
it does not, stop the command, collect the service file and daemon log, and hand
those back through git before continuing.
## Validate runtime
@ -197,20 +196,21 @@ mdo -u root service colibri_daemon restart
Sanity checks:
```sh
grep -n 'colibri_daemon_program\|colibri_daemon_binary\|^command=\|^command_args=' /usr/local/etc/rc.d/colibri_daemon
sh -n /usr/local/etc/rc.d/colibri_daemon
service colibri_daemon status
colibri status
```
Expected:
- `colibri_daemon_binary` exists.
- `command="/usr/sbin/daemon"` exists.
- no `colibri_daemon_program` remains.
- no `-u ${colibri_daemon_user}` remains in `command_args`.
- the rc.d script has valid shell syntax.
- `service colibri_daemon start` returns to the shell.
- `colibri status` can connect to `/var/run/colibri/colibri.sock`.
## Future ISO improvement
Add a helper script, tentatively `/usr/local/bin/colibri-live-rebuild`, that
automates this runbook:
A future helper script, tentatively `/usr/local/bin/colibri-live-rebuild`, should
automate this runbook:
1. clone/update `/home/clawdie/ai/colibri-build`
2. checkout requested branch or commit

View file

@ -90,6 +90,7 @@
<li><code>colibri-daemon</code> — agent supervisor, skills catalog, Glasspane state machine</li>
<li><code>colibri-tui</code> — live ratatui dashboard (agent states, spawn/kill, sessions)</li>
<li><code>colibri-mcp</code> — MCP bridge for Zed, Claude Code, Cursor, and other MCP clients</li>
<li><code>colibri-test-agent</code> — local no-network Colibri launch check</li>
<li>Firefox browser</li>
<li>Tailscale package (needs auth key)</li>
<li><code>pi</code> coding agent harness (npm global)</li>
@ -120,6 +121,7 @@ colibri list-tasks # Coordination board
colibri list-skills # Skills catalog
colibri-mcp tools # MCP bridge, read-only by default
COLIBRI_MCP_WRITE=1 colibri-mcp tools # trusted write-capable profile
colibri spawn-local /usr/local/bin/colibri-test-agent --session-id local-check
colibri create-task --title "check network"
colibri list-tasks --status queued</pre>
<p>
@ -127,6 +129,24 @@ colibri list-tasks --status queued</pre>
<code>/usr/local/share/clawdie-iso/mcp-examples/</code>.
</p>
<h2>LLM provider keys</h2>
<p>
Colibri can run local checks without a key. Remote providers need a key
in <code>/usr/local/etc/colibri/provider.env</code>. Keep this file
root-owned and mode <code>0600</code>; it is read when
<code>colibri_daemon</code> starts.
</p>
<pre>
mdo -u root cp /usr/local/etc/colibri/provider.env.sample /usr/local/etc/colibri/provider.env
mdo -u root chmod 600 /usr/local/etc/colibri/provider.env
mdo -u root ee /usr/local/etc/colibri/provider.env
mdo -u root service colibri_daemon restart</pre>
<p>Put only the providers you use in that file:</p>
<pre>
DEEPSEEK_API_KEY="sk-..."
OPENROUTER_API_KEY="sk-or-..."
ANTHROPIC_API_KEY="sk-ant-..."</pre>
<h2>pi assistant</h2>
<pre>
pi # interactive session

View file

@ -21,6 +21,10 @@ bash
zsh
ohmyzsh
git
# Live Colibri rebuild lane: lets the running USB rebuild and redeploy Colibri
# without a full ISO rebuild for every small service/runtime iteration.
rust
pkgconf
tailscale
avahi-app
nss_mdns

View file

@ -60,7 +60,7 @@ mkdir -p "${BIN_DIR}" "${RC_DIR}" "${ETC_DIR}" "${NEWSYSLOG_DIR}" "${DB_DIR}" "$
copy_bin colibri-daemon
copy_bin colibri
copy_bin colibri-smoke-agent
copy_bin colibri-test-agent
copy_bin colibri-mcp
if [ "${COLIBRI_STAGE_INCLUDE_TUI}" != "0" ] && [ -x "${COLIBRI_ARTIFACT_DIR}/colibri-tui" ]; then
@ -71,62 +71,22 @@ 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 -Eq -- '-o .*colibri_daemon_(program|binary)' "${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
! grep -q -- '-o .*colibri_daemon_binary' "${RC_DIR}/colibri_daemon" || \
! grep -q 'colibri_daemon_provider_env' "${RC_DIR}/colibri_daemon" || \
! grep -q 'rm -f "${colibri_daemon_socket}" "${pidfile}" "${supervisor_pidfile}"' "${RC_DIR}/colibri_daemon" || \
! grep -q 'chmod 644 "${pidfile}"' "${RC_DIR}/colibri_daemon" || \
! grep -q 'chmod 660 "${colibri_daemon_socket}"' "${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
# 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. Newer Colibri sources
# may already use colibri_daemon_binary; this replacement is intentionally
# harmless when there is no old variable left.
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.
# Use awk instead of sed append syntax: BSD sed's multi-line append form is
# easy to get wrong and can corrupt the rc.d script by appending to every line.
# Newer Colibri sources may already carry these chmods; do not duplicate them.
if ! grep -q 'chmod 644 "${pidfile}"' "${RC_DIR}/colibri_daemon"; then
_rc_tmp="${RC_DIR}/colibri_daemon.tmp"
awk '
{ print }
/socket ready/ {
print " chmod 644 \"${pidfile}\" 2>/dev/null || true"
print " chmod 660 \"${colibri_daemon_socket}\" 2>/dev/null || true"
}
' "${RC_DIR}/colibri_daemon" > "${_rc_tmp}"
mv "${_rc_tmp}" "${RC_DIR}/colibri_daemon"
chmod 0555 "${RC_DIR}/colibri_daemon"
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
# 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" <<EOF
# Colibri control plane service defaults for the Clawdie ISO.
# Merge into /etc/rc.conf or /etc/rc.conf.d/colibri_daemon.
@ -138,10 +98,24 @@ 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"
colibri_daemon_provider_env="/usr/local/etc/colibri/provider.env"
colibri_daemon_host="\$(hostname)"
colibri_cost_mode="${COLIBRI_COST_MODE}"
EOF
cat > "${ETC_DIR}/provider.env.sample" <<'EOF'
# Optional provider keys for colibri_daemon. Copy this file to provider.env,
# chmod it 0600, fill in only the providers you use, then restart the service.
#
# 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"
EOF
cat > "${ETC_DIR}/README.iso" <<'EOF'
Colibri ISO staging notes
=========================
@ -149,11 +123,15 @@ 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 smoke"
colibri spawn-local /usr/local/bin/colibri-test-agent --session-id iso-check
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