Restore live operator polish + formatting gate fixes (Codex) #28

Merged
clawdie merged 3 commits from fix/restore-live-operator-polish into main 2026-06-04 21:05:12 +02:00
7 changed files with 205 additions and 26 deletions

View file

@ -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
@ -1342,22 +1373,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'
@ -1373,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"

View file

@ -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:e6e9) |
| **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:e6e9) |
| **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 <tag-password> 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:<pw> -X POST \
@ -114,6 +116,7 @@ curl -sk -u Administrator:<pw> -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:<pw> -X POST \
## System ROM / BIOS
Check version after iLO login:
```sh
curl -sk -u Administrator:<pw> 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

View file

@ -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.

View file

@ -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}`);

View file

@ -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
# ============================================================================

View file

@ -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"

View file

@ -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