From 3ce3ddfc8df302d83357ba1d3ecf0b04f4a6d6de Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Thu, 4 Jun 2026 04:38:31 +0200 Subject: [PATCH 1/3] Seed Pi tmux defaults and search tools (Sam & Codex) --- build.sh | 20 +++++++++++++++++++- packages/pkg-list-live-operator.txt | 4 ++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 6fc5eee4..d12783df 100755 --- a/build.sh +++ b/build.sh @@ -1342,22 +1342,40 @@ if [ -n "${ZSH:-}" ] && [ -r "${ZSH}/oh-my-zsh.sh" ]; then source "${ZSH}/oh-my-zsh.sh" fi EOF + cat > "${MOUNT_POINT}/home/clawdie/.tmux.conf" <<'EOF' +# Clawdie operator tmux defaults. +# Pi uses modified Enter/key chords; tmux must pass CSI-u extended keys or Pi +# warns that modified Enter keys may not work. +set -g extended-keys on +set -g extended-keys-format csi-u +set -g escape-time 10 +set -g mouse on +set -g base-index 1 +setw -g pane-base-index 1 +set -g renumber-windows on +EOF + mkdir -p "${MOUNT_POINT}/usr/local/etc" + cp "${MOUNT_POINT}/home/clawdie/.tmux.conf" "${MOUNT_POINT}/usr/local/etc/tmux.conf" cp "${MOUNT_POINT}/home/clawdie/.profile" "${MOUNT_POINT}/etc/skel/.profile" cp "${MOUNT_POINT}/home/clawdie/.bash_profile" "${MOUNT_POINT}/etc/skel/.bash_profile" cp "${MOUNT_POINT}/home/clawdie/.bashrc" "${MOUNT_POINT}/etc/skel/.bashrc" cp "${MOUNT_POINT}/home/clawdie/.zprofile" "${MOUNT_POINT}/etc/skel/.zprofile" cp "${MOUNT_POINT}/home/clawdie/.zshrc" "${MOUNT_POINT}/etc/skel/.zshrc" + cp "${MOUNT_POINT}/home/clawdie/.tmux.conf" "${MOUNT_POINT}/etc/skel/.tmux.conf" chmod 0644 \ "${MOUNT_POINT}/home/clawdie/.profile" \ "${MOUNT_POINT}/home/clawdie/.bash_profile" \ "${MOUNT_POINT}/home/clawdie/.bashrc" \ "${MOUNT_POINT}/home/clawdie/.zprofile" \ "${MOUNT_POINT}/home/clawdie/.zshrc" \ + "${MOUNT_POINT}/home/clawdie/.tmux.conf" \ + "${MOUNT_POINT}/usr/local/etc/tmux.conf" \ "${MOUNT_POINT}/etc/skel/.profile" \ "${MOUNT_POINT}/etc/skel/.bash_profile" \ "${MOUNT_POINT}/etc/skel/.bashrc" \ "${MOUNT_POINT}/etc/skel/.zprofile" \ - "${MOUNT_POINT}/etc/skel/.zshrc" + "${MOUNT_POINT}/etc/skel/.zshrc" \ + "${MOUNT_POINT}/etc/skel/.tmux.conf" install -m 0755 "${LIVE_SESSION_DIR}/xprofile" \ "${MOUNT_POINT}/home/clawdie/.xprofile" cat > "${MOUNT_POINT}/home/clawdie/.xinitrc" <<'EOF' diff --git a/packages/pkg-list-live-operator.txt b/packages/pkg-list-live-operator.txt index b35dc689..9f4bd4c4 100644 --- a/packages/pkg-list-live-operator.txt +++ b/packages/pkg-list-live-operator.txt @@ -37,6 +37,10 @@ bastille # and Micro for friendly terminal/SSH edits without pulling an Electron IDE. geany micro +# Pi/operator terminal search helpers. Keep these in the live image, not only +# the host package baseline, because Pi and debugging run directly from XFCE. +ripgrep +fd-find # Vulkan diagnostics. Zed is deferred off the live rootfs until Intel+AMD # Vulkan are proven, but vulkaninfo is tiny and gives Pi Builder a direct -- 2.45.3 From 4426776b675dd67ca692a5479f3b588d933281a7 Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Thu, 4 Jun 2026 06:46:32 +0200 Subject: [PATCH 2/3] Show hostname in Pi footer on operator images (Sam & Codex) --- build.sh | 38 +++++++++++++++ firstboot/patch-pi-footer-hostname.js | 69 +++++++++++++++++++++++++++ firstboot/shell-npm-globals.sh | 33 +++++++++++++ firstboot/shell-system.sh | 6 +++ 4 files changed, 146 insertions(+) create mode 100755 firstboot/patch-pi-footer-hostname.js diff --git a/build.sh b/build.sh index d12783df..cf4c2f1f 100755 --- a/build.sh +++ b/build.sh @@ -944,6 +944,8 @@ install_live_npm_globals() { ln -sf "/opt/clawdie/npm-global/bin/$(basename "${_bin}")" "${MOUNT_POINT}/usr/local/bin/$(basename "${_bin}")" done + patch_live_pi_footer_hostname "${_live_prefix}" + # npm runs as root on the build host and writes everything as # root:wheel. configure_live_operator_session() used to do this # chown but ran BEFORE install_live_npm_globals (caller order at @@ -973,6 +975,33 @@ install_live_npm_globals() { fi } +patch_live_pi_footer_hostname() { + _npm_prefix="$1" + _patch_script="${SCRIPT_DIR}/firstboot/patch-pi-footer-hostname.js" + _patched=0 + + if [ ! -f "${_patch_script}" ]; then + echo "ERROR: Pi footer patch script missing: ${_patch_script}" + exit 1 + fi + + for _footer in \ + "${_npm_prefix}/lib/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.js" \ + "${_npm_prefix}/lib/node_modules/@mariozechner/pi-coding-agent/dist/modes/interactive/components/footer.js" + do + [ -f "${_footer}" ] || continue + echo " patch Pi footer hostname: ${_footer}" + node "${_patch_script}" "${_footer}" + node --check "${_footer}" + _patched=1 + done + + if [ "${_patched}" -ne 1 ]; then + echo "ERROR: Pi footer.js not found under ${_npm_prefix}" + exit 1 + fi +} + seed_live_ai_source_repo() { _repo_src="$1" _repo_name="$2" @@ -1273,6 +1302,8 @@ export npm_config_prefix="${_clawdie_npm_prefix}" export NPM_CONFIG_PREFIX="${_clawdie_npm_prefix}" export NPM_CONFIG_UPDATE_NOTIFIER=false export NO_UPDATE_NOTIFIER=1 +export LANG="${LANG:-en_US.UTF-8}" +export LC_ALL="${LC_ALL:-en_US.UTF-8}" if [ -z "${PATH:-}" ]; then PATH="${_clawdie_base_path}" else @@ -1391,6 +1422,13 @@ EOF "${MOUNT_POINT}/home/clawdie/.config/xfce4/xinitrc" \ "${MOUNT_POINT}/etc/skel/.xinitrc" \ "${MOUNT_POINT}/etc/skel/.config/xfce4/xinitrc" + if [ -f "${MOUNT_POINT}/usr/local/etc/xdg/tumbler/tumbler.rc" ]; then + mkdir -p "${MOUNT_POINT}/home/clawdie/.config/tumbler" "${MOUNT_POINT}/etc/skel/.config/tumbler" + install -m 0644 "${MOUNT_POINT}/usr/local/etc/xdg/tumbler/tumbler.rc" \ + "${MOUNT_POINT}/home/clawdie/.config/tumbler/tumbler.rc" + install -m 0644 "${MOUNT_POINT}/usr/local/etc/xdg/tumbler/tumbler.rc" \ + "${MOUNT_POINT}/etc/skel/.config/tumbler/tumbler.rc" + fi chroot "$MOUNT_POINT" chown -R clawdie:clawdie /home/clawdie mkdir -p "${MOUNT_POINT}/usr/local/share/applications" diff --git a/firstboot/patch-pi-footer-hostname.js b/firstboot/patch-pi-footer-hostname.js new file mode 100755 index 00000000..0671c044 --- /dev/null +++ b/firstboot/patch-pi-footer-hostname.js @@ -0,0 +1,69 @@ +#!/usr/bin/env node +// Patch the bundled Pi footer so operator sessions show the runtime hostname. +// This is intentionally a post-install patch because the live image ships Pi as +// an offline npm-global tarball rather than as source built in this repo. + +const fs = require("node:fs"); + +const file = process.argv[2]; +if (!file) { + console.error("usage: patch-pi-footer-hostname.js FOOTER_JS"); + process.exit(64); +} + +let src = fs.readFileSync(file, "utf8"); + +if (!src.includes('import { hostname } from "node:os";')) { + if (src.includes('import { isAbsolute, relative, resolve, sep } from "node:path";\n')) { + src = src.replace( + 'import { isAbsolute, relative, resolve, sep } from "node:path";\n', + 'import { isAbsolute, relative, resolve, sep } from "node:path";\nimport { hostname } from "node:os";\n', + ); + } else { + src = src.replace(/^(import .*?;\n)/, 'import { hostname } from "node:os";\n$1'); + } +} + +const oldPwdBlock = ` const pwdLine = truncateToWidth(theme.fg("dim", pwd), width, theme.fg("dim", "...")); + const lines = [pwdLine, dimStatsLeft + dimRemainder];`; + +const newPwdBlock = ` // Show runtime hostname on the right of the cwd line. Use the kernel + // hostname instead of parsing FreeBSD rc.conf so Pi stays portable and + // reflects changes made after boot. + const host = hostname(); + const minPwdHostPadding = 2; + let hostForLine = host; + let hostWidth = visibleWidth(hostForLine); + if (hostWidth > width) { + hostForLine = truncateToWidth(hostForLine, width, "..."); + hostWidth = visibleWidth(hostForLine); + } + const availableForPwd = width - hostWidth - minPwdHostPadding; + let pwdLine; + if (hostForLine && availableForPwd > 0) { + const pwdForLine = visibleWidth(pwd) > availableForPwd ? truncateToWidth(pwd, availableForPwd, "...") : pwd; + const pwdWidth = visibleWidth(pwdForLine); + const padding = " ".repeat(Math.max(minPwdHostPadding, width - pwdWidth - hostWidth)); + pwdLine = theme.fg("dim", pwdForLine) + padding + theme.fg("dim", hostForLine); + } + else if (hostForLine) { + pwdLine = theme.fg("dim", hostForLine); + } + else { + pwdLine = truncateToWidth(theme.fg("dim", pwd), width, theme.fg("dim", "...")); + } + const lines = [pwdLine, dimStatsLeft + dimRemainder];`; + +if (!src.includes("const minPwdHostPadding = 2;")) { + if (!src.includes(oldPwdBlock)) { + throw new Error(`Pi footer cwd block not found in ${file}`); + } + src = src.replace(oldPwdBlock, newPwdBlock); +} + +if (!src.includes('lines.push("");\n return lines;')) { + src = src.replace(' return lines;\n', ' lines.push("");\n return lines;\n'); +} + +fs.writeFileSync(file, src); +console.log(`patched ${file}`); diff --git a/firstboot/shell-npm-globals.sh b/firstboot/shell-npm-globals.sh index f85a7788..9d2caf00 100755 --- a/firstboot/shell-npm-globals.sh +++ b/firstboot/shell-npm-globals.sh @@ -84,11 +84,44 @@ clawdie_shell_npm_globals_install() { return 0 fi + clawdie_shell_npm_globals_patch_pi_footer + echo "[NPM-GLOBALS] SUCCESS" >> "$PROGRESS_FILE" log_msg "[npm-globals] Install complete" return 0 } +clawdie_shell_npm_globals_patch_pi_footer() { + _patch_script="${SHARE}/firstboot/patch-pi-footer-hostname.js" + _patched=0 + + if [ ! -f "$_patch_script" ]; then + log_msg "[npm-globals] ERROR: Pi footer patch script missing: $_patch_script" + return 1 + fi + + for footer in \ + "$NPM_PREFIX/lib/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.js" \ + "$NPM_PREFIX/lib/node_modules/@mariozechner/pi-coding-agent/dist/modes/interactive/components/footer.js" + do + [ -f "$footer" ] || continue + log_msg "[npm-globals] Patching Pi footer hostname: $footer" + if node "$_patch_script" "$footer" >>"$LOG_FILE" 2>&1 && node --check "$footer" >>"$LOG_FILE" 2>&1; then + _patched=1 + else + log_msg "[npm-globals] ERROR: failed to patch Pi footer hostname" + return 1 + fi + done + + if [ "$_patched" -ne 1 ]; then + log_msg "[npm-globals] ERROR: Pi footer.js not found under $NPM_PREFIX" + return 1 + fi + + return 0 +} + # ============================================================================ # LOGGING HELPER # ============================================================================ diff --git a/firstboot/shell-system.sh b/firstboot/shell-system.sh index 7c12ad49..067ce312 100755 --- a/firstboot/shell-system.sh +++ b/firstboot/shell-system.sh @@ -444,8 +444,14 @@ if [ -n "${ZSH:-}" ] && [ -r "${ZSH}/oh-my-zsh.sh" ]; then source "${ZSH}/oh-my-zsh.sh" fi EOF + if [ -f /usr/local/etc/xdg/tumbler/tumbler.rc ]; then + mkdir -p /home/clawdie/.config/tumbler + cp /usr/local/etc/xdg/tumbler/tumbler.rc /home/clawdie/.config/tumbler/tumbler.rc + fi + chown -R clawdie:clawdie /home/clawdie/.config/tumbler 2>/dev/null || true chown clawdie:clawdie /home/clawdie/.profile /home/clawdie/.bash_profile /home/clawdie/.bashrc /home/clawdie/.zprofile /home/clawdie/.zshrc 2>/dev/null || true chmod 644 /home/clawdie/.profile /home/clawdie/.bash_profile /home/clawdie/.bashrc /home/clawdie/.zprofile /home/clawdie/.zshrc 2>/dev/null || true + chmod 644 /home/clawdie/.config/tumbler/tumbler.rc 2>/dev/null || true fi log_msg "[system] Created $clawdie_profile" -- 2.45.3 From 96b52baca598920d7bd19622c44ed36926be015b Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Thu, 4 Jun 2026 19:53:27 +0200 Subject: [PATCH 3/3] docs: format deployment and Poudriere plans (Sam & Codex) --- docs/ISO-DEPLOYMENT-TARGET-ZFS.md | 36 +++++++++++++++++-------------- docs/POUDRIERE-BUILD-SERVER.md | 25 +++++++++++++-------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/docs/ISO-DEPLOYMENT-TARGET-ZFS.md b/docs/ISO-DEPLOYMENT-TARGET-ZFS.md index 81f5c619..f0aa1ca9 100644 --- a/docs/ISO-DEPLOYMENT-TARGET-ZFS.md +++ b/docs/ISO-DEPLOYMENT-TARGET-ZFS.md @@ -11,16 +11,16 @@ ## Target machine -| Detail | Value | -|---|---| -| **Make / Model** | HPE ProLiant ML350p Gen8 tower | -| **Serial** | `CZ22160QQY` | -| **Product ID** | `646676-421` | -| **Management** | iLO 4 (firmware 2.76 → needs 2.82 update) | -| **iLO License** | Advanced (remote console + virtual media) | -| **iLO IP** | `10.0.0.2` (dedicated iLO management port) | -| **Server NICs** | 4× onboard GbE (MAC 9c:8e:99:4c:43:e6–e9) | -| **Server IP** | DHCP from LAN port 1 (currently no OS booted) | +| Detail | Value | +| ---------------- | -------------------------------------------------- | +| **Make / Model** | HPE ProLiant ML350p Gen8 tower | +| **Serial** | `CZ22160QQY` | +| **Product ID** | `646676-421` | +| **Management** | iLO 4 (firmware 2.76 → needs 2.82 update) | +| **iLO License** | Advanced (remote console + virtual media) | +| **iLO IP** | `10.0.0.2` (dedicated iLO management port) | +| **Server NICs** | 4× onboard GbE (MAC 9c:8e:99:4c:43:e6–e9) | +| **Server IP** | DHCP from LAN port 1 (currently no OS booted) | | **iLO password** | Physical pull-tab tag on chassis (factory default) | ## Network layout (sanitised) @@ -63,6 +63,7 @@ ipmitool -H 10.0.0.2 -U Administrator -P chassis power reset ### Phase 2 — USB live boots on server Once the ISO boots on the server hardware: + 1. Server gets DHCP on its LAN port (visible in ARP) 2. `colibri-daemon` starts, skills catalog loaded 3. `service clawdie health` passes @@ -106,6 +107,7 @@ Target: 2.82 (Aug 2023) Download: https://support.hpe.com/ → ProLiant ML350p Gen8 → Firmware → iLO 4 **Method A (from USB live):** + ```sh # Upload firmware via iLO REST API curl -sk -u Administrator: -X POST \ @@ -114,6 +116,7 @@ curl -sk -u Administrator: -X POST \ ``` **Method B (via iLO web UI):** + 1. Log into `https://10.0.0.2/` 2. Administration → Firmware → Upload 3. Select `ilo4_282.bin`, apply, iLO reboots (~2 min) @@ -121,6 +124,7 @@ curl -sk -u Administrator: -X POST \ ## System ROM / BIOS Check version after iLO login: + ```sh curl -sk -u Administrator: https://10.0.0.2/xmldata?item=all | grep -i rom ``` @@ -129,13 +133,13 @@ Likely needs update — Gen8 latest is 2019.05.00 (P79). Check HPE support. ## Required packages on ISO -| Package | Purpose | -|---|---| +| Package | Purpose | +| ---------- | ------------------------------------------------ | | `ipmitool` | IPMI/BMC management (power, sensors, boot order) | -| `freeipmi` | Alternative IPMI toolset (optional, heavier) | -| `curl` | iLO REST API calls ✅ already included | -| `openssl` | Certificate handling ✅ already included | -| `python3` | Scripting + JSON ✅ already included | +| `freeipmi` | Alternative IPMI toolset (optional, heavier) | +| `curl` | iLO REST API calls ✅ already included | +| `openssl` | Certificate handling ✅ already included | +| `python3` | Scripting + JSON ✅ already included | ## Notes diff --git a/docs/POUDRIERE-BUILD-SERVER.md b/docs/POUDRIERE-BUILD-SERVER.md index 7897c0dd..6b80a1dd 100644 --- a/docs/POUDRIERE-BUILD-SERVER.md +++ b/docs/POUDRIERE-BUILD-SERVER.md @@ -31,6 +31,7 @@ ml350p build server (Poudriere) ``` Benefits: + - Versioned packages with dependencies - Clean-room builds (no host contamination) - `pkg upgrade colibri` on deployed machines @@ -158,6 +159,7 @@ sysutils/colibri/ ``` **Makefile** (Rust port pattern): + ```makefile PORTNAME= colibri PORTVERSION= 0.0.1 @@ -205,6 +207,7 @@ service nginx start ### 3.2 Client config On ISO builds and deployed machines: + ```sh # /usr/local/etc/pkg/repos/clawdie.conf clawdie: { @@ -223,6 +226,7 @@ pkg -r ${MOUNT_POINT} install colibri ``` This gives us: + - `colibri` - `colibri-daemon` - `colibri-tui` @@ -270,6 +274,7 @@ zfs create zroot/bhyve/freebsd-test ### 6.4 Test VMs **FreeBSD ISO test VM** (boots clawdie-iso after each build): + ```sh vm create -t freebsd iso-test vm install iso-test clawdie-iso.iso @@ -278,12 +283,14 @@ vm start iso-test ``` **Linux cross-compile test VM** (validates non-FreeBSD targets): + ```sh vm create -t linux linux-test # → test colibri builds on Linux target ``` **FreeBSD Poudriere test jail VM** (full pkg build validation): + ```sh vm create -t freebsd freebsd-test # → clone poudriere setup, run bulk build as validation @@ -301,15 +308,15 @@ vm-bhyve ## Timeline -| Step | Effort | Depends on | -|---|---|---| -| 1. Server provision (ZFS, base system) | ~1h | iLO password, ISO boots | -| 2. Poudriere setup | ~30m | base system running | -| 3. colibri port creation | ~1h | Poudriere running | -| 4. First pkg build | ~30m (compile) | port ready | -| 5. pkg repo + nginx | ~15m | packages built | -| 6. ISO integration | ~15m | repo hosted | -| 7. bhyve + test VMs | ~30m | base system + ZFS | +| Step | Effort | Depends on | +| -------------------------------------- | -------------- | ----------------------- | +| 1. Server provision (ZFS, base system) | ~1h | iLO password, ISO boots | +| 2. Poudriere setup | ~30m | base system running | +| 3. colibri port creation | ~1h | Poudriere running | +| 4. First pkg build | ~30m (compile) | port ready | +| 5. pkg repo + nginx | ~15m | packages built | +| 6. ISO integration | ~15m | repo hosted | +| 7. bhyve + test VMs | ~30m | base system + ZFS | **Total: ~4h** once iLO password is available. -- 2.45.3