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"