Merge pull request 'chore(freebsd): align host baseline with Python 3.12' (#13) from chore/python312-baseline into main

This commit is contained in:
clawdie 2026-06-17 16:16:53 +02:00
commit 3f50711ff8
9 changed files with 59 additions and 64 deletions

View file

@ -341,8 +341,8 @@ clawdie-iso/
Derived from `setup/environment.ts` host baseline + desktop-installer + DE packages:
**Clawdie host baseline:**
`node24`, `npm`, `bsddialog`, `bastille`, `git`, `tmux`, `python311`, `uv`,
`ripgrep`, `fd-find`, `rsync`, `postgresql18-client`, `py311-pillow`, `dejavu`
`node24`, `npm`, `bsddialog`, `bastille`, `git`, `tmux`, `python312`, `uv`,
`ripgrep`, `fd-find`, `rsync`, `postgresql18-client`, `dejavu`
**Xorg baseline:**
`xorg-minimal`, `xf86-video-intel`, `drm-kmod`,

View file

@ -53,7 +53,7 @@ See [docs/public/architecture/warden.md](docs/public/architecture/warden.md).
git clone https://codeberg.org/Clawdie/Clawdie-AI.git /home/clawdie/clawdie-ai
cd /home/clawdie/clawdie-ai
pkg install node24 npm git python312 py312-uv rsync
pkg install node24 npm git python312 uv rsync
npm install
npm install -g @earendil-works/pi-coding-agent
# If setup.sh did not launch onboarding automatically:
@ -102,12 +102,14 @@ The current FreeBSD deployment depends on:
### `controlplane`
Role:
- main Clawdie control-plane jail
- Telegram intake
- scheduling
- Warden task dispatch
Profile:
- `freebsd-jail`
- `thick`
- `vnet`
@ -116,10 +118,12 @@ Profile:
### `db`
Role:
- PostgreSQL memory backend
- persistent service
Profile:
- `freebsd-jail`
- `thick`
- `vnet`
@ -134,19 +138,23 @@ Profile:
## Snapshot Policy
Manual milestone snapshots:
- human-named
- day-first
- month abbreviation
Examples:
- `@postgres18-ready-08.mar.2026`
- `@fresh-08.mar.2026`
Automatic snapshots:
- handled by Sanoid
- keep Sanoid's internal `autosnap_...` naming
Current automated Sanoid targets:
- `zroot/clawdie-runtime/jails/db`
- `zroot/clawdie-runtime/jails/controlplane`

View file

@ -155,12 +155,12 @@ just setup-cms # npm run setup -- --step cms
- Internet access for `pkg` and `npm`
- `bash` and `git` available before first run
- **`just`** — command runner (`pkg install just` on FreeBSD; preinstalled on Clawdie ISO)
- **At least one agent CLI on `PATH`**: one of `pi`, `aider`, `claude`, `codex`, or `gemini`. The controlplane harness uses Aider+Pi as the primary driver. Onboarding fails fast if none are present (see [`doc/AGENT-CLI-VALIDATION.md`](doc/AGENT-CLI-VALIDATION.md) for the validated install paths). The Clawdie ISO ships claude/gemini/pi via the npm-globals bundle, aider via `py311-aider_chat` pkg, and codex via `pkg install codex`.
- **At least one agent CLI on `PATH`**: one of `pi`, `aider`, `claude`, `codex`, or `gemini`. The controlplane harness uses Aider+Pi as the primary driver. Onboarding fails fast if none are present (see [`doc/AGENT-CLI-VALIDATION.md`](doc/AGENT-CLI-VALIDATION.md) for the validated install paths). The Clawdie ISO ships claude/gemini/pi via the npm-globals bundle, provisions Aider in a Python 3.12 venv, and includes codex via `pkg install codex`.
Recommended explicit host baseline before first run:
```sh
sudo pkg install -y bash git bastille node24 npm tmux btop python311 uv ripgrep fd-find rsync postgresql18-client py311-pillow dejavu py311-aider_chat edk2-bhyve just
sudo pkg install -y bash git bastille node24 npm tmux btop python312 uv ripgrep fd-find rsync postgresql18-client dejavu edk2-bhyve just
```
The `edk2-bhyve` package provides UEFI firmware required for optional browser-vm support
@ -169,15 +169,16 @@ The `edk2-bhyve` package provides UEFI firmware required for optional browser-vm
On FreeBSD, use `fd-find`. It provides the `fd` command that `pi` expects and
avoids colliding with the unrelated `fd` file manager package.
The host baseline also includes `py311-pillow` and `dejavu` so tmux screenshot
capture works without a separate `uv pip install Pillow` step.
Python package-flavored extras stay out of the baseline until the FreeBSD
quarterly repository publishes Python 3.12 flavors. Install Aider/Pillow-style
tools into explicit Python 3.12 venvs when needed.
If the host still has both Python 3.11 and 3.12 installed, pin `uv` to 3.11
If the host still has multiple Python minors installed, pin `uv` to 3.12
explicitly until the generic `python3` path is cleaned up:
```sh
uv venv --python 3.11
uv run --python 3.11 <command>
uv venv --python 3.12
uv run --python 3.12 <command>
```
### Operator Glasspane
@ -215,7 +216,7 @@ First use:
```bash
# 1. Install the recommended FreeBSD host baseline
sudo pkg install -y bash git bastille node24 npm tmux btop python311 uv ripgrep fd-find rsync postgresql18-client py311-pillow dejavu py311-aider_chat just
sudo pkg install -y bash git bastille node24 npm tmux btop python312 uv ripgrep fd-find rsync postgresql18-client dejavu just
# 2. Clone the repository
git clone https://codeberg.org/Clawdie/Clawdie-AI.git /home/clawdie/clawdie-ai

View file

@ -30,26 +30,22 @@ export PATH=/opt/clawdie/cargo/bin:$PATH
## Working install (tested)
1) Install Aider from packages (adds many deps but works):
1. Create a project-local Python 3.12 venv. The FreeBSD quarterly repo still
publishes only older Python-flavored Aider packages, so keep Aider out of the
host pkg baseline and install it into a venv:
```sh
sudo pkg install py311-aider_chat
python3.12 -m venv /home/clawdie/clawdie-ai/tmp/aider-venv
```
2) Create a project-local venv that can override litellm:
```sh
python3.11 -m venv --system-site-packages /home/clawdie/clawdie-ai/tmp/aider-venv
```
3) Upgrade Aider + pin litellm to the expected version inside the venv:
2. Upgrade Aider + pin litellm to the expected version inside the venv:
```sh
/home/clawdie/clawdie-ai/tmp/aider-venv/bin/pip install --no-user --no-deps --upgrade --ignore-installed aider-chat==0.86.2
/home/clawdie/clawdie-ai/tmp/aider-venv/bin/pip install --no-user --no-deps --upgrade --ignore-installed litellm==1.81.10
```
4) Align tree-sitter with the system tree-sitter-languages package (fixes repo-map crash):
3. Align tree-sitter with the system tree-sitter-languages package (fixes repo-map crash):
```sh
env TMPDIR=/home/clawdie/clawdie-ai/tmp \
@ -63,7 +59,7 @@ FreeBSD ships `tree_sitter_languages` from packages (1.10.2) which expects
TypeError: __init__() takes exactly 1 argument (2 given)
```
4) Run Aider (use the venv binary):
4. Run Aider (use the venv binary):
```sh
env AIDER_ANALYTICS_DISABLE=1 /home/clawdie/clawdie-ai/tmp/aider-venv/bin/aider \

View file

@ -9,19 +9,18 @@ tmux
btop
bsddialog
codex
python311
python312
uv
ripgrep
fd-find
rsync
postgresql18-client
dnsmasq
py311-pillow
dejavu
rust
# Controlplane harness (Aider + Pi multi-agent orchestrator)
py311-aider_chat
# Controlplane harness helpers. Aider is installed into a venv until the
# quarterly FreeBSD repo publishes a Python 3.12 package flavor.
just
# Wayland display stack — used by worker jails (cage) and operator sessions

View file

@ -71,8 +71,8 @@ install_host_pkg_baseline() {
tmux)
command -v tmux >/dev/null 2>&1 || missing_pkgs+=("$pkg")
;;
python311)
command -v python3 >/dev/null 2>&1 || missing_pkgs+=("$pkg")
python312)
command -v python3.12 >/dev/null 2>&1 || missing_pkgs+=("$pkg")
;;
uv)
command -v uv >/dev/null 2>&1 || missing_pkgs+=("$pkg")
@ -92,9 +92,6 @@ install_host_pkg_baseline() {
rust)
command -v rustc >/dev/null 2>&1 || missing_pkgs+=("$pkg")
;;
py311-pillow)
python3 -c "import PIL" >/dev/null 2>&1 || missing_pkgs+=("$pkg")
;;
dejavu)
[ -f /usr/local/share/fonts/dejavu/DejaVuSansMono.ttf ] || missing_pkgs+=("$pkg")
;;

View file

@ -38,7 +38,7 @@ export class NoAgentCliError extends Error {
[
'No agent CLI found on PATH.',
'Clawdie needs pi and aider for the Aider+Pi controlplane harness.',
'Install via the ISO bundle or: pkg install py311-aider_chat, npm install -g @earendil-works/pi-coding-agent.',
'Install via the ISO bundle or: npm install -g @earendil-works/pi-coding-agent; install Aider in a Python 3.12 venv if needed.',
'Alternative CLIs (claude, codex, gemini) are also accepted.',
].join(' '),
);

View file

@ -51,7 +51,7 @@ const HOST_PREREQUISITE_CHECKS: Record<
dnsmasq: { key: 'DNSMASQ', check: () => commandExists('dnsmasq') },
tmux: { key: 'TMUX', check: () => commandExists('tmux') },
btop: { key: 'BTOP', check: () => commandExists('btop') },
python311: { key: 'PYTHON3', check: () => commandExists('python3') },
python312: { key: 'PYTHON3', check: () => commandExists('python3.12') },
uv: { key: 'UV', check: () => commandExists('uv') },
ripgrep: { key: 'RIPGREP', check: () => commandExists('rg') },
'fd-find': { key: 'FD', check: () => commandExists('fd') },
@ -60,20 +60,8 @@ const HOST_PREREQUISITE_CHECKS: Record<
tailscale: { key: 'TAILSCALE', check: () => commandExists('tailscale') },
just: { key: 'JUST', check: () => commandExists('just') },
rust: { key: 'RUST', check: () => commandExists('rustc') },
'py311-aider_chat': { key: 'AIDER', check: () => commandExists('aider') },
node24: { key: 'NODE', check: () => commandExists('node') },
npm: { key: 'NPM', check: () => commandExists('npm') },
'py311-pillow': {
key: 'PILLOW',
check: () => {
try {
execSync('python3 -c "import PIL"', { stdio: 'ignore' });
return true;
} catch {
return false;
}
},
},
dejavu: {
key: 'DEJAVU_FONT',
check: () =>
@ -111,11 +99,9 @@ export async function run(_args: string[]): Promise<void> {
const service = commandExists('service');
const sudo = commandExists('sudo');
const jailConf = fs.existsSync('/etc/jail.conf');
const hasPython311 = commandExists('python3.11');
const hasPython312 = commandExists('python3.12');
const python3Version = commandVersion('python3');
const python3NeedsPinning =
hasPython311 && hasPython312 && python3Version.includes('3.12');
const python3NeedsPinning = hasPython312 && !python3Version.includes('3.12');
// Check for pi binary (path from env or default 'pi')
const piBin = PI_TUI_BIN;
@ -257,7 +243,6 @@ export async function run(_args: string[]): Promise<void> {
const psql = getPrereq('PSQL');
const node = getPrereq('NODE');
const npm = getPrereq('NPM');
const pillow = getPrereq('PILLOW');
const dejavuFont = getPrereq('DEJAVU_FONT');
const seatd = getPrereq('SEATD');
const weston = getPrereq('WESTON');
@ -322,13 +307,11 @@ export async function run(_args: string[]): Promise<void> {
psql: psql.present,
node: node.present,
npm: npm.present,
pillow: pillow.present,
dejavuFont: dejavuFont.present,
seatd: seatd.present,
cage: cage.present,
weston: weston.present,
vmBhyve: vmBhyve.present,
hasPython311,
hasPython312,
python3Version,
python3NeedsPinning,
@ -349,7 +332,7 @@ export async function run(_args: string[]): Promise<void> {
{
python3Version,
},
'Multiple Python versions detected. Pin uv to Python 3.11 until python3 is repointed to 3.11.',
'Python 3.12 is installed but python3 is not pinned to 3.12; use an explicit python3.12/uv pin for setup commands.',
);
}
@ -381,12 +364,11 @@ export async function run(_args: string[]): Promise<void> {
PYTHON3: python3.present,
PYTHON3_STATUS: python3.status,
PYTHON3_INSTALL_CMD: python3.installCmd,
PYTHON311: hasPython311,
PYTHON312: hasPython312,
PYTHON3_VERSION: python3Version || 'unknown',
UV_PYTHON_PIN_REQUIRED: python3NeedsPinning,
UV_PYTHON_HINT: python3NeedsPinning
? 'uv venv --python 3.11 && uv run --python 3.11 <command>'
? 'uv venv --python 3.12 && uv run --python 3.12 <command>'
: 'not_required',
NODE: node.present,
NODE_STATUS: node.status,
@ -409,9 +391,6 @@ export async function run(_args: string[]): Promise<void> {
PSQL: psql.present,
PSQL_STATUS: psql.status,
PSQL_INSTALL_CMD: psql.installCmd,
PILLOW: pillow.present,
PILLOW_STATUS: pillow.status,
PILLOW_INSTALL_CMD: pillow.installCmd,
DEJAVU_FONT: dejavuFont.present,
DEJAVU_FONT_STATUS: dejavuFont.status,
DEJAVU_FONT_INSTALL_CMD: dejavuFont.installCmd,

View file

@ -408,12 +408,21 @@ function printStep(
}
function hasPiAuthProvider(provider: string): boolean {
const authFile = path.join(process.env.HOME || '', '.pi', 'agent', 'auth.json');
const authFile = path.join(
process.env.HOME || '',
'.pi',
'agent',
'auth.json',
);
try {
const parsed = JSON.parse(fs.readFileSync(authFile, 'utf-8')) as Record<string, unknown>;
const parsed = JSON.parse(fs.readFileSync(authFile, 'utf-8')) as Record<
string,
unknown
>;
const entry = parsed?.[provider];
if (typeof entry === 'string') return entry.trim().length > 0;
if (entry && typeof entry === 'object') return Object.keys(entry).length > 0;
if (entry && typeof entry === 'object')
return Object.keys(entry).length > 0;
return false;
} catch {
return false;
@ -460,8 +469,12 @@ function printLlmStatus(envFile: string): void {
console.log(
`\n ${COLS.warn}No LLM provider auth found. Configure one after install and restart:${COLS.reset}`,
);
console.log(` ${COLS.skipped} Recommended: run pi, then /login and select ChatGPT Plus/Pro (Codex).${COLS.reset}`);
console.log(` ${COLS.skipped} Or add an API key such as ANTHROPIC_API_KEY=sk-ant-...${COLS.reset}`);
console.log(
` ${COLS.skipped} Recommended: run pi, then /login and select ChatGPT Plus/Pro (Codex).${COLS.reset}`,
);
console.log(
` ${COLS.skipped} Or add an API key such as ANTHROPIC_API_KEY=sk-ant-...${COLS.reset}`,
);
console.log(
` ${COLS.skipped} sudo service ${SERVICE_NAME} restart${COLS.reset}`,
);
@ -509,7 +522,7 @@ function printAiderTip(): void {
);
if (!hasAider) {
console.log(
` install: ${COLS.skipped}pkg install -y py311-aider_chat${COLS.reset}`,
` install: ${COLS.skipped}python3.12 -m venv /opt/clawdie/venv/aider && /opt/clawdie/venv/aider/bin/pip install aider-chat${COLS.reset}`,
);
}
console.log(` docs : ${COLS.skipped}https://aider.chat/docs/${COLS.reset}`);
@ -570,7 +583,9 @@ export async function run(argv: string[]): Promise<void> {
const opts = parseArgs(argv);
const projectRoot = process.cwd();
const envFile = path.join(projectRoot, '.env');
const envContent = fs.existsSync(envFile) ? fs.readFileSync(envFile, 'utf-8') : '';
const envContent = fs.existsSync(envFile)
? fs.readFileSync(envFile, 'utf-8')
: '';
if (getPlatform() !== 'freebsd') {
console.error('install orchestrator is FreeBSD only.');