ci/woodpecker/pr/ci Pipeline is pending approval
The installer's promise is that one command turns a bare host into a working one, but Node was carved out of that: it was checked as a prerequisite and the run died on a greenfield host. That made the documented one-command install a two-command install whose first command always failed. It now installs a user-local Node under ~/.mosaic/node when the system Node is missing or too old, from the official nodejs.org tarballs, verified against SHASUMS256.txt. User-local rather than apt/dnf/brew: no root, one code path on every distro, and it works on an immutable host. A system Node that is already new enough is preferred and left untouched. --no-node-install (or MOSAIC_NO_NODE_INSTALL=1) keeps the old refuse-and-explain behaviour, and neither --check nor --uninstall provisions anything. PATH now lands in the login profile as well as the interactive rc. Writing only ~/.bashrc looked right interactively and was invisible to every way an agent seat actually starts -- bash -lc, ssh host cmd, a systemd unit -- because Debian's .bashrc returns early when non-interactive. Verified end to end on mosaic-sbx-dev rolled back to its greenfield snapshot: red on origin/next (rc=1, "Required command not found: node"), green with this change (Node v22.23.2 fetched and verified, CLI 0.0.50-next.2413 installed), and a fresh `bash -lc` finds both. tools/install-node-provisioning.test.sh pins the behaviour offline against a file:// dist fixture, including the refusals and the checksum gate. The next-lane test's Node 20 case moves to --no-node-install: the >= 22 gate must still fire before anything is installed, but refusing is no longer the outcome when provisioning is allowed. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01WYgWocp36goy8hj2ui6ps1