feat: initial clawdie-iso skeleton
USB installer for Clawdie-AI. Combines FreeBSD base install, desktop-installer GPU/DE setup, and Clawdie-AI deployment into a single rc.firstboot wizard flow. Skeleton includes: - build.cfg: FreeBSD 15.0-RELEASE-p4, amd64, XFCE default - build.sh: 7-step build outline (fetch → inject → repack), stubs - installerconfig: bsdinstall post-install hook, copies firstboot/ to HDD - firstboot/rc.d/clawdie-firstboot: runs once on first HDD boot - firstboot/firstboot.sh: tiered bsddialog wizard (identity, desktop, pi profile, auto-generated secrets, AGENTS.md seeding, npm prefix setup) - firstboot/gpu-detect.sh: pciconf PCI ID → kld/xorg driver mapping - CLAWDIE-ISO.md: full design doc (copied from clawdie-ai) VirtualBox excluded. pkg latest default. LLM keys deferred to pi first-run. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
34075f3a5e
10 changed files with 1147 additions and 0 deletions
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Fetched at build time — do not commit
|
||||
cache/
|
||||
packages/*.pkg
|
||||
packages/*.txz
|
||||
packages/.repo/
|
||||
|
||||
# Build output
|
||||
*.img
|
||||
*.iso
|
||||
594
CLAWDIE-ISO.md
Normal file
594
CLAWDIE-ISO.md
Normal file
|
|
@ -0,0 +1,594 @@
|
|||
# CLAWDIE-ISO — USB Installer Plan
|
||||
|
||||
**Status:** Draft brainstorm — 2026-03-17
|
||||
**Target repo:** `codeberg.org/Clawdie/clawdie-iso` (to be created)
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
Create a bootable USB image that reduces a complete Clawdie-AI deployment to a
|
||||
single physical step: boot USB, answer a few screens, unplug USB, done.
|
||||
|
||||
The installed system is a plain FreeBSD + desktop + Clawdie-AI — nothing exotic,
|
||||
nothing persistent on the USB. The USB is a deployment vehicle only.
|
||||
|
||||
The install combines three procedures that currently run separately:
|
||||
|
||||
| # | Path | Tool | What it does |
|
||||
|---|------|------|--------------|
|
||||
| 1 | **FreeBSD base** | bsdinstall | Disk layout, ZFS, base OS, bootloader, root/user accounts |
|
||||
| 2 | **Desktop setup** | desktop-installer | GPU detection, Xorg/Wayland, DE packages, login manager, audio |
|
||||
| 3 | **Clawdie-AI** | setup.sh → install-all | Bastille jails, PostgreSQL, nginx/CMS, rc.d service, bot config |
|
||||
|
||||
Current state: these three are completely separate. An operator must install
|
||||
FreeBSD, reboot, install desktop-installer, run it, reboot (sometimes), run
|
||||
setup.sh, configure. Minimum 2 reboots, typically 3+ operator interventions.
|
||||
|
||||
Target state: one ISO, one bsddialog wizard, one reboot.
|
||||
|
||||
---
|
||||
|
||||
## Desktop-Installer Reference
|
||||
|
||||
[outpaddling/desktop-installer](https://github.com/outpaddling/desktop-installer)
|
||||
is a FreeBSD ports package (`pkg install desktop-installer`) that automates the
|
||||
post-install desktop configuration step:
|
||||
|
||||
- Detects GPU via `pciconf -lv`
|
||||
- Installs the right kld modules (i915kms, nvidia-modeset, drm, vboxguest, etc.)
|
||||
- Writes `kld_list` to `/etc/rc.conf` for persistent driver loading
|
||||
- Installs the chosen DE (KDE Plasma, XFCE, MATE, GNOME, Lumina, etc.)
|
||||
- Configures dbus, CUPS, sound (OSS/PulseAudio), login manager (SDDM/lightdm/slim)
|
||||
- Handles VirtualBox, VMware, Parallels, bhyve guest additions automatically
|
||||
- Does NOT typically require a reboot — kld can be loaded live or on next boot
|
||||
- Respects quarterly vs latest pkg repository configured on the system
|
||||
|
||||
We borrow the detection and configuration logic from desktop-installer but do not
|
||||
run the interactive script. Our build process will vendor the GPU detection
|
||||
function and call it from our own bsddialog wizard.
|
||||
|
||||
---
|
||||
|
||||
## Reboot Analysis — Can We Do 1 Reboot?
|
||||
|
||||
Three potential reboot triggers:
|
||||
|
||||
**Reboot 1 (mandatory): bsdinstall completes**
|
||||
bsdinstall always reboots after base install. Can't be avoided. The system must
|
||||
boot from HDD before userspace can run.
|
||||
|
||||
**Reboot 2 (optional): GPU driver loading**
|
||||
Most GPU drivers on FreeBSD are kernel modules (klds). desktop-installer writes
|
||||
them to `kld_list` in rc.conf, but they can also be `kldload`ed into a live system.
|
||||
The key question: can we detect the GPU and write correct config WITHOUT having
|
||||
the module loaded yet?
|
||||
|
||||
Answer: **yes**. GPU detection uses `pciconf -lv` (reads PCI device tree, no kld
|
||||
needed). The output gives us PCI device ID and vendor ID. We match against a
|
||||
known table to select the right kld and write it to `rc.conf`. On first boot from
|
||||
HDD, the kld loads automatically. X11/Wayland starts clean. No intermediate reboot
|
||||
is needed to make this work.
|
||||
|
||||
VirtualBox is explicitly out of scope for this installer. Target: real hardware
|
||||
and bhyve only. VBox guest packages are not bundled on the USB image.
|
||||
|
||||
**Reboot 3 (not needed): Clawdie setup**
|
||||
setup.sh and all jail provisioning steps run in userspace. No reboot needed.
|
||||
rc.d service starts without reboot via `service clawdie onestart`.
|
||||
|
||||
**Verdict: 1 reboot is achievable for all common hardware.**
|
||||
Two reboots may still be the safer default for first release if GPU detection
|
||||
reliability is uncertain. See Options section.
|
||||
|
||||
---
|
||||
|
||||
## Pkg Branch Selection
|
||||
|
||||
FreeBSD ships two pkg repository branches:
|
||||
|
||||
| Branch | URL suffix | Update cadence | Best for |
|
||||
|--------|-----------|---------------|---------|
|
||||
| `quarterly` | `/quarterly/` | Stable snapshots, security fixes only | Servers, production |
|
||||
| `latest` | `/latest/` | Tracks ports HEAD, newest versions | Desktops, GPU drivers, Node24 |
|
||||
|
||||
**Recommendation: default to latest.** GPU drivers (drm-kmod, nvidia-driver),
|
||||
Node 24, and many Bastille-related packages are updated sooner in `latest`.
|
||||
Quarterly has caused GPU driver installation failures on new hardware.
|
||||
|
||||
**How to implement the toggle:**
|
||||
|
||||
During the install wizard (bsddialog), present a radiolist before any packages
|
||||
are installed:
|
||||
|
||||
```
|
||||
Package repository branch:
|
||||
(*) Latest — newest packages, best driver support [recommended]
|
||||
( ) Quarterly — stable branch, security updates only
|
||||
```
|
||||
|
||||
The selected branch sets `/usr/local/etc/pkg/repos/FreeBSD.conf`:
|
||||
|
||||
```ucl
|
||||
FreeBSD: {
|
||||
url: "pkg+https://pkg.FreeBSD.org/${ABI}/latest",
|
||||
mirror_type: "srv",
|
||||
enabled: yes
|
||||
}
|
||||
```
|
||||
|
||||
For offline install from USB, the bundled pkg repo must match the selected branch.
|
||||
Build the USB with `latest` packages. Add a note: if the user selects `quarterly`,
|
||||
the offline install falls back to the USB-bundled packages (which are `latest`
|
||||
builds) and the system upgrades to quarterly on first internet connection.
|
||||
Simpler alternative: only support `latest` offline, add quarterly as a post-install
|
||||
option in the wizard.
|
||||
|
||||
---
|
||||
|
||||
## Options
|
||||
|
||||
### Option A — Pre-configured silent install (1 reboot, no interaction)
|
||||
|
||||
All choices are baked into a config file on the USB (`clawdie.conf`). The operator
|
||||
edits this file before booting:
|
||||
|
||||
```sh
|
||||
PKG_BRANCH=latest
|
||||
DESKTOP=xfce
|
||||
AGENT_NAME=clawdie
|
||||
ASSISTANT_NAME="Clawdie"
|
||||
TZ=Europe/Ljubljana
|
||||
```
|
||||
|
||||
bsdinstall runs from `installerconfig`. After base install, a post-install script
|
||||
reads `clawdie.conf`, installs everything offline, writes all config. Reboot once
|
||||
into a fully configured system.
|
||||
|
||||
**Pro:** Fully automated, reproducible, CI-friendly, no bsddialog needed
|
||||
**Con:** Requires USB preparation before boot. No interactive personalization.
|
||||
**Reboots:** 1
|
||||
**Best for:** Reproducible deploys, CI testing, pre-provisioned hardware
|
||||
|
||||
---
|
||||
|
||||
### Option B — Interactive bsddialog wizard in installerconfig (1 reboot, guided)
|
||||
|
||||
After bsdinstall's base install completes but before the system reboots,
|
||||
`installerconfig` runs a bsddialog wizard. The wizard covers:
|
||||
|
||||
1. Pkg branch (latest / quarterly)
|
||||
2. Desktop environment (XFCE / KDE Plasma / MATE / Headless)
|
||||
3. Agent name + assistant name
|
||||
4. Telegram bot token (optional, can be skipped)
|
||||
5. Summary screen → confirm → install
|
||||
|
||||
GPU is detected from the live USB environment via `pciconf -lv` before chroot.
|
||||
Detection result is written into the target system's rc.conf. All packages are
|
||||
installed offline from the USB repo. Clawdie setup runs in chroot.
|
||||
Single reboot into configured system.
|
||||
|
||||
**Pro:** Best UX, matches existing bsddialog pattern, interactive but 1-step
|
||||
**Con:** bsddialog in post-install chroot requires careful path setup
|
||||
**Reboots:** 1
|
||||
**Best for:** First install on new hardware, operator-configured deployments
|
||||
|
||||
---
|
||||
|
||||
### Option C — rc.firstboot script (2 reboots, most reliable)
|
||||
|
||||
Standard bsdinstall base install. Minimal `installerconfig` just copies the
|
||||
wizard script and enables a `firstboot` rc.d service. On first real boot from HDD
|
||||
(reboot 1), the firstboot service runs the bsddialog wizard on the live system.
|
||||
Packages install, GPU loads live via kldload, DE and Clawdie come up. Optional
|
||||
second reboot (reboot 2) for clean DM start.
|
||||
|
||||
**Pro:** Runs on real hardware (not in chroot), GPU kldload works immediately,
|
||||
most reliable path, easier debugging
|
||||
**Con:** 2 reboots minimum. Wizard runs after first reboot, which can feel slow.
|
||||
**Reboots:** 2
|
||||
**Best for:** Conservative first release, maximum hardware compatibility
|
||||
|
||||
---
|
||||
|
||||
### Option D — Extended bsdinstall menus (1 reboot, most native)
|
||||
|
||||
Modify bsdinstall to add extra menu screens: pkg branch, DE selection, Clawdie
|
||||
agent config. The installer feels like a fully native FreeBSD install with
|
||||
Clawdie options integrated into the standard installer flow. Post-install runs
|
||||
silently based on selections.
|
||||
|
||||
**Pro:** Most seamless UX — everything happens inside one familiar installer
|
||||
**Con:** Requires forking/patching bsdinstall (sh scripts in /usr/libexec/bsdinstall).
|
||||
Fragile against FreeBSD updates. Significant maintenance burden.
|
||||
**Reboots:** 1
|
||||
**Best for:** Polished v2.0+ release, not first iteration
|
||||
|
||||
---
|
||||
|
||||
### Option E — Live USB + install-to-disk (2+ reboots, most impressive)
|
||||
|
||||
The USB boots as a live Clawdie desktop system. User can interact with a running
|
||||
agent demo before committing to install. An "Install Clawdie to Disk" button/command
|
||||
runs the installer. After install + reboot, the HDD system is fully configured.
|
||||
|
||||
**Pro:** Demo capability, validate hardware/agent before committing, impressive
|
||||
**Con:** Most complex build (~2–3GB image), live persistence layer, 2 reboots,
|
||||
significant extra build infrastructure
|
||||
**Reboots:** 2+
|
||||
**Best for:** Demo/showcase events, evaluators who want to try before installing
|
||||
|
||||
---
|
||||
|
||||
## Recommendation
|
||||
|
||||
**Start with Option C (rc.firstboot, 2 reboots) for v1 of the repo.**
|
||||
|
||||
Reason: GPU detection running on the real live system (not in chroot) eliminates
|
||||
the most common failure mode. The 2-reboot tradeoff is acceptable for a v1 that
|
||||
works reliably. The wizard runs on boot from HDD, using the same bsddialog
|
||||
infrastructure already in Clawdie's setup.sh — no new tooling needed.
|
||||
|
||||
**Upgrade path to Option B (1 reboot) for v1.1:**
|
||||
|
||||
Once we have Option C working and tested on multiple hardware configs, we port the
|
||||
wizard to run in the `installerconfig` post-install chroot. At that point we drop
|
||||
the firstboot service and the second reboot.
|
||||
|
||||
**Skip Option D and E for now.** Neither is worth the complexity at this stage.
|
||||
Option A can be added as a `--unattended` flag on top of Option B/C.
|
||||
|
||||
---
|
||||
|
||||
## Three-Path Merge Flow (Option C)
|
||||
|
||||
```
|
||||
USB boot
|
||||
└─ bsdinstall
|
||||
├─ Disk partitioning (ZFS, GPT+EFI)
|
||||
├─ Base system distribution
|
||||
├─ Bootloader
|
||||
├─ Root password
|
||||
├─ User account (clawdie)
|
||||
└─ installerconfig:
|
||||
├─ Copy /usr/local/share/clawdie-iso/firstboot.sh to HDD
|
||||
├─ Install clawdie-iso rc.d firstboot service
|
||||
└─ Reboot from HDD
|
||||
|
||||
First boot from HDD (reboot 1)
|
||||
└─ rc.firstboot (clawdie-firstboot service)
|
||||
├─ bsddialog: pkg branch (latest [default] / quarterly)
|
||||
├─ bsddialog: desktop environment (XFCE / KDE / MATE / Headless)
|
||||
├─ bsddialog: agent name + assistant name
|
||||
├─ bsddialog: Telegram bot token (optional)
|
||||
├─ bsddialog: summary + confirm
|
||||
│
|
||||
├─ [pkg] Write /usr/local/etc/pkg/repos/FreeBSD.conf (selected branch)
|
||||
├─ [pkg] pkg install from USB offline repo (desktop-installer deps)
|
||||
│
|
||||
├─ [gpu] pciconf -lv → detect GPU vendor/device ID
|
||||
├─ [gpu] kldload appropriate driver(s)
|
||||
├─ [gpu] Write kld_list to /etc/rc.conf
|
||||
├─ [gpu] Write /etc/X11/xorg.conf.d/ if needed
|
||||
│
|
||||
├─ [de] Install chosen desktop environment from USB repo
|
||||
├─ [de] Enable dbus, hald, login manager in rc.conf
|
||||
├─ [de] Configure .xinitrc / session for clawdie user
|
||||
│
|
||||
├─ [clawdie] Extract clawdie-ai.tar.gz to /home/clawdie/clawdie-ai
|
||||
├─ [clawdie] Set up .env defaults (AGENT_NAME, ASSISTANT_NAME, TZ)
|
||||
├─ [clawdie] cd /home/clawdie/clawdie-ai && npm run install-all
|
||||
│ ├─ environment (host pkg baseline from USB)
|
||||
│ ├─ jails (worker + db + git + cms)
|
||||
│ ├─ db (PostgreSQL + pgvector)
|
||||
│ ├─ skills-memory (if artifacts/skills.db present on USB)
|
||||
│ ├─ cms (nginx + Astro)
|
||||
│ ├─ service (rc.d clawdie)
|
||||
│ └─ verify
|
||||
│
|
||||
└─ Disable clawdie-firstboot service (runs once only)
|
||||
|
||||
Running system (no reboot 2 needed for Option C baseline)
|
||||
└─ Login manager starts → desktop session → clawdie agent running
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Repo Structure (`clawdie-iso`)
|
||||
|
||||
```
|
||||
clawdie-iso/
|
||||
├── build.sh ← main build script
|
||||
├── build.cfg ← version pins, image size, pkg list
|
||||
├── installerconfig ← bsdinstall post-install hook
|
||||
├── firstboot/
|
||||
│ ├── firstboot.sh ← the bsddialog wizard + setup runner
|
||||
│ ├── rc.d/
|
||||
│ │ └── clawdie-firstboot ← FreeBSD rc.d service (runs once)
|
||||
│ └── gpu-detect.sh ← pciconf → kld table lookup
|
||||
├── packages/ ← pre-fetched .pkg files (gitignored, fetched by build.sh)
|
||||
│ ├── .repo/ ← generated by pkg repo
|
||||
│ └── *.pkg
|
||||
└── CLAWDIE-ISO.md ← this document (copy)
|
||||
```
|
||||
|
||||
`build.sh` responsibilities:
|
||||
1. Fetch FreeBSD memstick installer image (verified checksum)
|
||||
2. Fetch all required .pkg files to `packages/` (node24, bastille, desktop-installer deps, etc.)
|
||||
3. Run `pkg repo packages/` to generate repo metadata
|
||||
4. Unpack memstick image
|
||||
5. Inject into memstick: `installerconfig`, `firstboot/` tree, `packages/` repo, `clawdie-ai.tar.gz`
|
||||
6. Repack image
|
||||
7. Output: `clawdie-iso-YYYYMMDD.img`
|
||||
|
||||
---
|
||||
|
||||
## USB Package Manifest
|
||||
|
||||
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`, `postgresql17-client`, `py311-pillow`, `dejavu`
|
||||
|
||||
**Desktop-installer and Xorg baseline:**
|
||||
`desktop-installer`, `xorg-minimal`, `xf86-video-intel`, `drm-kmod`,
|
||||
`xf86-input-libinput`, `dbus`, `hal`
|
||||
|
||||
Note: `xf86-video-vboxvideo` and VirtualBox guest additions are **excluded**.
|
||||
Target hardware is real machines and bhyve only. VirtualBox is out of scope.
|
||||
|
||||
**XFCE (default DE):**
|
||||
`xfce`, `xfce4-goodies`, `lightdm`, `lightdm-gtk-greeter`
|
||||
|
||||
**KDE Plasma (optional, large):**
|
||||
`plasma5-plasma`, `kde-baseapps`, `sddm`
|
||||
|
||||
**MATE (optional, lightweight alt):**
|
||||
`mate`, `mate-extra`, `lightdm`, `lightdm-gtk-greeter`
|
||||
|
||||
**NVIDIA support (optional):**
|
||||
`nvidia-driver`, `nvidia-settings`
|
||||
|
||||
Total estimated size (XFCE path, latest): ~2–3 GB packages + ~600 MB FreeBSD base
|
||||
|
||||
---
|
||||
|
||||
## Node.js Shared Modules Setup
|
||||
|
||||
A critical user-space tweak that must be reproduced exactly on the installed
|
||||
system: npm's global package prefix must be set to the clawdie user's home
|
||||
directory, not to `/usr/local`.
|
||||
|
||||
**Why it matters:** On FreeBSD, `/usr/local` is owned by root. Running
|
||||
`npm install -g` as the `clawdie` user fails without this. The prefix setting
|
||||
redirects globals to a user-owned path that is in `PATH`.
|
||||
|
||||
**Current production setup** (from `/home/clawdie/.npmrc`):
|
||||
```
|
||||
prefix=/home/clawdie/.npm-global
|
||||
```
|
||||
|
||||
**PATH addition** (must be in `/home/clawdie/.profile` or `.shrc`):
|
||||
```sh
|
||||
export PATH="$HOME/.npm-global/bin:$PATH"
|
||||
```
|
||||
|
||||
**firstboot.sh must:**
|
||||
1. Write `/home/clawdie/.npmrc` with `prefix=/home/clawdie/.npm-global`
|
||||
2. Create `/home/clawdie/.npm-global/` (with correct ownership)
|
||||
3. Append PATH export to `/home/clawdie/.profile`
|
||||
4. Pre-populate the global npm cache from USB-bundled packages so that
|
||||
`npm install` inside clawdie-ai runs fully offline
|
||||
|
||||
**Build-time:** `build.sh` must pre-fetch npm packages and bundle them as a
|
||||
local npm cache tarball (`npm-cache.tar.gz`) to be extracted to
|
||||
`/home/clawdie/.npm` during firstboot. This eliminates network calls to
|
||||
registry.npmjs.org during install.
|
||||
|
||||
---
|
||||
|
||||
## AGENTS.md Seeding (Planning)
|
||||
|
||||
AGENTS.md is the agent's identity and behavior file. It must be seeded during
|
||||
firstboot with values derived from the install wizard (assistant name, locale,
|
||||
agent domain, etc.).
|
||||
|
||||
**Plan (not yet implemented):**
|
||||
|
||||
- A template `AGENTS.md.tpl` is bundled on the USB inside the clawdie-ai
|
||||
tarball at `groups/global/AGENTS.md.tpl` (or `groups/main/AGENTS.md.tpl`)
|
||||
- During firstboot, after `.env` is written, `firstboot.sh` renders the
|
||||
template by substituting:
|
||||
- `{{ASSISTANT_NAME}}` → value from wizard
|
||||
- `{{AGENT_NAME}}` → derived system name
|
||||
- `{{AGENT_DOMAIN}}` → configured domain
|
||||
- `{{ASSISTANT_LOCALE}}` → selected locale
|
||||
- `{{TZ}}` → selected timezone
|
||||
- Output written to `groups/global/AGENTS.md` before `npm run install-all` runs
|
||||
- This ensures the agent has a fully personalized identity from first boot
|
||||
|
||||
**Scope:** Template design and rendering are handled in the `clawdie-ai` repo.
|
||||
The `clawdie-iso` repo only needs to call the render step during firstboot.
|
||||
|
||||
---
|
||||
|
||||
## .env Generation Strategy
|
||||
|
||||
The installed system requires 65 environment variables to be set before
|
||||
Clawdie-AI can start. They fall into four categories:
|
||||
|
||||
| Category | Count | Source |
|
||||
|----------|-------|--------|
|
||||
| Identity / locale | 8 | Human provides via wizard |
|
||||
| API keys / tokens | 14 | Human provides (all optional / deferrable) |
|
||||
| Auto-generated secrets | 9 | `openssl rand -base64 32` at install time |
|
||||
| Derived structural | 34 | Calculated from identity (jail IPs, names, etc.) |
|
||||
|
||||
Three options for how the wizard handles this:
|
||||
|
||||
### .env Option 1 — Minimal wizard (5 questions, everything else silent)
|
||||
|
||||
The wizard asks only the irreducible human questions:
|
||||
|
||||
1. Assistant name → derives `AGENT_NAME` automatically
|
||||
2. Timezone
|
||||
3. Domain name (default: `<agentname>.local`)
|
||||
4. Primary LLM provider + key (Anthropic / OpenAI / skip)
|
||||
5. Telegram bot token (or skip)
|
||||
|
||||
All 9 secrets: auto-generated silently with `openssl rand`.
|
||||
All 34 structural vars: derived from identity choices (fixed subnet, fixed jail
|
||||
IPs, names from AGENT_NAME, etc.).
|
||||
API keys not asked: left blank in `.env`, operator fills post-install.
|
||||
|
||||
**Pro:** Fastest path. 5 screens. Least friction. Works completely offline.
|
||||
**Con:** No visibility into generated credentials during install. Operator must
|
||||
read `.env` to find database passwords. No way to customize subnet or features.
|
||||
**Best for:** First-time users, fast re-deploys, CI/automated testing.
|
||||
|
||||
### LLM Key Entry — Deferred to pi first-run (design decision)
|
||||
|
||||
LLM provider keys are **not collected by the ISO installer wizard**. This is
|
||||
intentional. The installer is decoupled from provider choice — keys can change,
|
||||
providers can rotate, and keys should not be baked into USB media.
|
||||
|
||||
Instead the wizard collects only the pi *profile* (which provider class:
|
||||
Anthropic / OpenAI / Ollama / skip). The key itself is entered the first time
|
||||
pi is used:
|
||||
|
||||
**Path A — pi first-run prompt (TTY detected):**
|
||||
On the operator's first interaction with the agent (or on firstboot post-install
|
||||
if a TTY is present), `pi-config` checks for the provider key. If missing and a
|
||||
TTY is available: bsddialog password input → written to `.env` → service
|
||||
restarted automatically.
|
||||
|
||||
**Path B — manual .env edit (headless / operator preference):**
|
||||
Operator edits `/home/clawdie/clawdie-ai/.env` directly, sets the appropriate
|
||||
key var (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc.), then runs
|
||||
`service clawdie restart`. pi-config detects the key is present and skips
|
||||
prompting on all future runs.
|
||||
|
||||
**Detection logic (to be implemented in `clawdie-ai`):**
|
||||
```
|
||||
pi-config runs →
|
||||
selected provider key var present in .env? → skip, continue
|
||||
not present AND TTY available? → bsddialog password input → write .env
|
||||
not present AND no TTY? → log instruction, continue (non-fatal)
|
||||
```
|
||||
|
||||
This is a **gap in the current codebase** — the non-blocking warning exists but
|
||||
the TTY-triggered bsddialog prompt does not. This is a scoped task for the
|
||||
`clawdie-ai` repo, not the `clawdie-iso` repo.
|
||||
|
||||
**Task (clawdie-ai):** Add TTY-aware key prompt to `setup/pi-config.ts`:
|
||||
check for TTY (`process.stdin.isTTY`), if true and key missing, run
|
||||
`bsddialog --passwordbox "Enter <PROVIDER> API key" ...`, write result
|
||||
to `.env`, emit restart.
|
||||
|
||||
---
|
||||
|
||||
### .env Option 2 — Tiered wizard with credentials summary (recommended)
|
||||
|
||||
**Tier 1 (always shown, 4 screens):**
|
||||
- Name + timezone + domain → derive/generate everything → write `.env`
|
||||
|
||||
**Tier 2 (shown with "Configure now? [Y/n]"):**
|
||||
- LLM provider selection radiolist → key input
|
||||
- Telegram bot token input
|
||||
|
||||
**Tier 3 (shown only if operator presses "Advanced"):**
|
||||
- Custom subnet base (default 10.0.0.0/24)
|
||||
- Feature flags: Ollama (local inference), Gitea (self-hosted git), Management jail
|
||||
- ZFS dataset layout (default vs Clawdie-optimized)
|
||||
|
||||
**End screen — Credentials Summary:**
|
||||
```
|
||||
┌─ Clawdie Installation Complete ──────────────────────────┐
|
||||
│ │
|
||||
│ Agent: Clawdie (clawdie) │
|
||||
│ Domain: clawdie.local │
|
||||
│ Timezone: Europe/Ljubljana │
|
||||
│ │
|
||||
│ DB password: Xy7qP2mR... (see /home/clawdie/.env) │
|
||||
│ Telegram: ✓ configured │
|
||||
│ LLM: Anthropic ✓ │
|
||||
│ │
|
||||
│ Press Enter to start installation. │
|
||||
└───────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Pro:** Best balance. Guided but not overwhelming. Summary screen prevents
|
||||
lost credentials. Advanced tier serves power users without cluttering the default path.
|
||||
**Con:** More wizard screens to implement. Summary screen needs careful design.
|
||||
**Best for:** Standard production deployments, first installs on new hardware.
|
||||
|
||||
### .env Option 3 — Pre-boot config file on USB (clawdie.conf)
|
||||
|
||||
The USB image contains a `clawdie.conf` template file on a FAT32 partition that
|
||||
is readable/writable from any OS (Windows, macOS, Linux, FreeBSD). Operator
|
||||
edits it before booting:
|
||||
|
||||
```sh
|
||||
# Edit this file on the USB before booting.
|
||||
# Leave a field blank to be prompted during install.
|
||||
ASSISTANT_NAME=Clawdie
|
||||
TZ=Europe/Ljubljana
|
||||
AGENT_DOMAIN=clawdie.local
|
||||
PKG_BRANCH=latest # latest or quarterly
|
||||
DESKTOP=xfce # xfce, kde, mate, headless
|
||||
TELEGRAM_BOT_TOKEN= # optional, leave blank to skip
|
||||
ANTHROPIC_API_KEY= # optional
|
||||
```
|
||||
|
||||
Firstboot reads `clawdie.conf` first. Blank required fields trigger bsddialog
|
||||
prompts. Blank optional fields are silently skipped. All secrets still
|
||||
auto-generated regardless. A fully pre-filled `clawdie.conf` = zero-interaction install.
|
||||
|
||||
**Pro:** Supports fully automated reproducible deploys without modifying ISO.
|
||||
Familiar to sysadmins (edit a config file → boot). Works offline. Can be
|
||||
scripted or templated externally.
|
||||
**Con:** Operator must mount USB on another machine before install (FAT32
|
||||
partition is accessible but requires extra step). Blank fields still need wizard.
|
||||
**Best for:** Reproducible testing, staging/production fleet deployments, CI.
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **GPU detection table**: desktop-installer uses a shell function with a PCI ID
|
||||
lookup table. We should vendor `gpu-detect.sh` from desktop-installer source
|
||||
rather than reimplementing it. Verify license (BSD 2-clause — fine).
|
||||
|
||||
2. **pkg offline vs online**: If target machine has internet, `pkg install` will
|
||||
upgrade from online repo after we've pointed at USB. That's fine and expected.
|
||||
The USB repo is only for air-gapped or offline installs.
|
||||
|
||||
3. **ZFS layout**: Should we mirror bsdinstall's default ZFS layout or enforce a
|
||||
Clawdie-specific layout (separate datasets for bastille jails, var/db)?
|
||||
Decision needed before writing `installerconfig`.
|
||||
|
||||
4. **ARM64 / Raspberry Pi**: build.sh should detect host arch and fetch the
|
||||
matching FreeBSD image. For Pi, memstick is replaced with a different image
|
||||
type. Scope for v1: amd64 only, Pi deferred.
|
||||
|
||||
5. **clawdie user UID**: bsdinstall creates the user interactively. Should we
|
||||
override/enforce UID 1001 for `clawdie`? Or let operator choose during
|
||||
bsdinstall and then reference `$CLAWDIE_USER` in firstboot.sh?
|
||||
|
||||
6. **Clawdie-AI source bundling**: Bundle a tagged release tarball (clawdie-ai
|
||||
`vX.Y.Z`) or pull from Codeberg during firstboot? Offline-first → bundle.
|
||||
build.sh should accept `--clawdie-version X.Y.Z` and fetch the tarball.
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Create `codeberg.org/Clawdie/clawdie-iso` repo (empty, README only)
|
||||
2. Copy this document as the repo's `CLAWDIE-ISO.md`
|
||||
3. Write `build.cfg` with FreeBSD 15.0-RELEASE-p4, amd64, XFCE default
|
||||
4. Write `gpu-detect.sh` (vendor from desktop-installer, simplify)
|
||||
5. Write `firstboot.sh` (bsddialog wizard + exec setup.sh)
|
||||
6. Write `installerconfig` (minimal, just copies firstboot/ tree)
|
||||
7. Write `build.sh` skeleton (fetch → inject → repack)
|
||||
8. Test: boot in bhyve VM on this host, validate firstboot flow
|
||||
31
README.md
Normal file
31
README.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Clawdie-ISO
|
||||
|
||||
Bootable USB installer for [Clawdie-AI](https://codeberg.org/Clawdie/Clawdie-AI).
|
||||
|
||||
Installs FreeBSD + desktop + Clawdie-AI to a target hard drive in a single
|
||||
guided flow. All packages are bundled on the USB — no internet required.
|
||||
|
||||
## What it does
|
||||
|
||||
1. Boot from USB
|
||||
2. Standard FreeBSD install to HDD (bsdinstall)
|
||||
3. On first HDD boot: bsddialog wizard asks name, timezone, domain, desktop, provider
|
||||
4. All secrets auto-generated, packages installed offline, agent started
|
||||
|
||||
See [CLAWDIE-ISO.md](CLAWDIE-ISO.md) for the full design.
|
||||
|
||||
## Build
|
||||
|
||||
```sh
|
||||
# on a FreeBSD host
|
||||
./build.sh
|
||||
# output: clawdie-iso-YYYYMMDD.img
|
||||
|
||||
# write to USB (replace daX with your device)
|
||||
dd if=clawdie-iso-YYYYMMDD.img of=/dev/daX bs=1M status=progress
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
Early skeleton — build.sh stubs are not yet functional.
|
||||
See CLAWDIE-ISO.md for implementation plan and open questions.
|
||||
20
build.cfg
Normal file
20
build.cfg
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#!/bin/sh
|
||||
# clawdie-iso build configuration
|
||||
# Sourced by build.sh — edit before building
|
||||
|
||||
FREEBSD_VERSION="15.0-RELEASE-p4"
|
||||
FREEBSD_ARCH="amd64"
|
||||
FREEBSD_MEMSTICK_URL="https://download.freebsd.org/releases/${FREEBSD_ARCH}/${FREEBSD_VERSION}/FreeBSD-${FREEBSD_VERSION}-${FREEBSD_ARCH}-memstick.img"
|
||||
FREEBSD_MEMSTICK_SHA256_URL="${FREEBSD_MEMSTICK_URL}.SHA256"
|
||||
|
||||
# Output image
|
||||
IMAGE_SIZE="8G"
|
||||
IMAGE_NAME="clawdie-iso-$(date +%Y%m%d).img"
|
||||
|
||||
# Clawdie-AI release to bundle (fetched from Codeberg)
|
||||
CLAWDIE_VERSION="0.8.2"
|
||||
CLAWDIE_TARBALL_URL="https://codeberg.org/Clawdie/Clawdie-AI/archive/v${CLAWDIE_VERSION}.tar.gz"
|
||||
|
||||
# Default installer choices (can be overridden by clawdie.conf on USB)
|
||||
DEFAULT_PKG_BRANCH="latest" # latest or quarterly
|
||||
DEFAULT_DESKTOP="xfce" # xfce, kde, mate, headless
|
||||
94
build.sh
Normal file
94
build.sh
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
#!/bin/sh
|
||||
# clawdie-iso build script
|
||||
# Produces a bootable FreeBSD memstick image with Clawdie-AI pre-bundled.
|
||||
#
|
||||
# Usage:
|
||||
# ./build.sh # build with defaults from build.cfg
|
||||
# ./build.sh --clawdie-version 0.9.0 # override Clawdie version
|
||||
# ./build.sh --skip-fetch # skip pkg/tarball fetch (use existing)
|
||||
#
|
||||
# Requirements (run on FreeBSD host):
|
||||
# pkg install curl gnupg2 md5 xorriso
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
|
||||
. "${SCRIPT_DIR}/build.cfg"
|
||||
|
||||
# --- argument parsing ---
|
||||
SKIP_FETCH=0
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--clawdie-version) CLAWDIE_VERSION="$2"; shift 2 ;;
|
||||
--skip-fetch) SKIP_FETCH=1; shift ;;
|
||||
*) echo "Unknown arg: $1"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "==> clawdie-iso build"
|
||||
echo " FreeBSD : ${FREEBSD_VERSION} ${FREEBSD_ARCH}"
|
||||
echo " Clawdie : v${CLAWDIE_VERSION}"
|
||||
echo " Desktop : ${DEFAULT_DESKTOP}"
|
||||
echo " Pkg : ${DEFAULT_PKG_BRANCH}"
|
||||
echo ""
|
||||
|
||||
# --- step 1: fetch FreeBSD memstick ---
|
||||
MEMSTICK="${SCRIPT_DIR}/cache/FreeBSD-${FREEBSD_VERSION}-${FREEBSD_ARCH}-memstick.img"
|
||||
if [ "$SKIP_FETCH" -eq 0 ] || [ ! -f "$MEMSTICK" ]; then
|
||||
echo "==> [1/7] Fetching FreeBSD memstick..."
|
||||
mkdir -p "${SCRIPT_DIR}/cache"
|
||||
curl -L -o "$MEMSTICK" "$FREEBSD_MEMSTICK_URL"
|
||||
curl -L -o "${MEMSTICK}.SHA256" "$FREEBSD_MEMSTICK_SHA256_URL"
|
||||
sha256 -c "${MEMSTICK}.SHA256" || { echo "Checksum mismatch!"; exit 1; }
|
||||
else
|
||||
echo "==> [1/7] FreeBSD memstick already cached, skipping fetch."
|
||||
fi
|
||||
|
||||
# --- step 2: fetch pkg dependencies ---
|
||||
if [ "$SKIP_FETCH" -eq 0 ]; then
|
||||
echo "==> [2/7] Fetching packages to packages/..."
|
||||
# TODO: read package list from packages/pkg-list.txt
|
||||
# pkg fetch --yes --dependencies --output packages/ <pkg-list>
|
||||
echo " (stub — implement pkg fetch loop from packages/pkg-list.txt)"
|
||||
else
|
||||
echo "==> [2/7] Skipping package fetch."
|
||||
fi
|
||||
|
||||
# --- step 3: generate local pkg repo metadata ---
|
||||
echo "==> [3/7] Generating offline pkg repo metadata..."
|
||||
# TODO: pkg repo packages/
|
||||
echo " (stub — run: pkg repo packages/)"
|
||||
|
||||
# --- step 4: fetch Clawdie-AI tarball ---
|
||||
CLAWDIE_TARBALL="${SCRIPT_DIR}/cache/clawdie-ai-v${CLAWDIE_VERSION}.tar.gz"
|
||||
if [ "$SKIP_FETCH" -eq 0 ] || [ ! -f "$CLAWDIE_TARBALL" ]; then
|
||||
echo "==> [4/7] Fetching Clawdie-AI v${CLAWDIE_VERSION}..."
|
||||
curl -L -o "$CLAWDIE_TARBALL" \
|
||||
"https://codeberg.org/Clawdie/Clawdie-AI/archive/v${CLAWDIE_VERSION}.tar.gz"
|
||||
else
|
||||
echo "==> [4/7] Clawdie-AI tarball already cached, skipping."
|
||||
fi
|
||||
|
||||
# --- step 5: unpack memstick image ---
|
||||
echo "==> [5/7] Unpacking memstick image..."
|
||||
# TODO: mdconfig, mount, prepare working copy
|
||||
echo " (stub — mount memstick image via mdconfig)"
|
||||
WORK_IMG="${SCRIPT_DIR}/cache/work.img"
|
||||
cp "$MEMSTICK" "$WORK_IMG"
|
||||
|
||||
# --- step 6: inject payload into image ---
|
||||
echo "==> [6/7] Injecting payload into image..."
|
||||
# TODO: mount work image, copy into it:
|
||||
# - installerconfig → /etc/installerconfig
|
||||
# - firstboot/ → /usr/local/share/clawdie-iso/firstboot/
|
||||
# - packages/ → /usr/local/share/clawdie-iso/packages/
|
||||
# - clawdie-ai tarball → /usr/local/share/clawdie-iso/clawdie-ai.tar.gz
|
||||
# - build.cfg defaults → /usr/local/share/clawdie-iso/build.cfg
|
||||
echo " (stub — mount + copy injection)"
|
||||
|
||||
# --- step 7: finalize and output ---
|
||||
echo "==> [7/7] Writing output image..."
|
||||
cp "$WORK_IMG" "${SCRIPT_DIR}/${IMAGE_NAME}"
|
||||
echo ""
|
||||
echo " Done: ${SCRIPT_DIR}/${IMAGE_NAME}"
|
||||
echo " Write to USB: dd if=${IMAGE_NAME} of=/dev/daX bs=1M status=progress"
|
||||
245
firstboot/firstboot.sh
Normal file
245
firstboot/firstboot.sh
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
#!/bin/sh
|
||||
# firstboot.sh — Clawdie-AI first-boot setup wizard
|
||||
#
|
||||
# Runs once on first HDD boot via clawdie-firstboot rc.d service.
|
||||
# Uses bsddialog for all interactive prompts.
|
||||
#
|
||||
# Flow:
|
||||
# Tier 1 (always): pkg branch → DE selection → agent identity → domain
|
||||
# Tier 2 (optional): pi provider profile selection
|
||||
# Tier 3 (advanced): subnet, feature flags (press 'A' on summary screen)
|
||||
# Secrets: all DB/service passwords auto-generated
|
||||
# LLM keys: deferred — entered via pi on first agent run
|
||||
# Summary screen: shows generated credentials before install starts
|
||||
# Then: install packages → GPU setup → desktop → Clawdie-AI
|
||||
|
||||
set -e
|
||||
|
||||
SHARE="/usr/local/share/clawdie-iso"
|
||||
CLAWDIE_HOME="/home/clawdie"
|
||||
CLAWDIE_AI="${CLAWDIE_HOME}/clawdie-ai"
|
||||
LOG="/var/log/clawdie-firstboot.log"
|
||||
ENV_FILE="${CLAWDIE_AI}/.env"
|
||||
|
||||
. "${SHARE}/build.cfg"
|
||||
|
||||
# --- helpers ---
|
||||
|
||||
dialog() { bsddialog --backtitle "Clawdie-AI Setup" "$@" ; }
|
||||
|
||||
die() { echo "ERROR: $1" >&2; exit 1; }
|
||||
|
||||
gen_secret() {
|
||||
openssl rand -base64 32 | tr -d '\n/+=' | head -c 32
|
||||
}
|
||||
|
||||
write_env() {
|
||||
KEY="$1"; VALUE="$2"
|
||||
if grep -q "^${KEY}=" "$ENV_FILE" 2>/dev/null; then
|
||||
sed -i '' "s|^${KEY}=.*|${KEY}=${VALUE}|" "$ENV_FILE"
|
||||
else
|
||||
echo "${KEY}=${VALUE}" >> "$ENV_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- screen: welcome ---
|
||||
|
||||
dialog --msgbox \
|
||||
"\nWelcome to Clawdie-AI Setup.\n\nThis wizard will configure your system.\nAll packages install offline from this USB.\n\nPress Enter to begin." \
|
||||
12 60
|
||||
|
||||
# --- tier 1: pkg branch ---
|
||||
|
||||
PKG_BRANCH=$(dialog --radiolist \
|
||||
"Package repository branch:" 12 60 2 \
|
||||
"latest" "Newest packages — best GPU/driver support [recommended]" on \
|
||||
"quarterly" "Stable branch — security updates only" off \
|
||||
3>&1 1>&2 2>&3) || die "Cancelled."
|
||||
|
||||
# Write pkg repo config
|
||||
mkdir -p /usr/local/etc/pkg/repos
|
||||
ABI=$(pkg config abi 2>/dev/null || echo "FreeBSD:15:amd64")
|
||||
cat > /usr/local/etc/pkg/repos/FreeBSD.conf <<EOF
|
||||
FreeBSD: {
|
||||
url: "pkg+https://pkg.FreeBSD.org/${ABI}/${PKG_BRANCH}",
|
||||
mirror_type: "srv",
|
||||
enabled: yes
|
||||
}
|
||||
EOF
|
||||
|
||||
# Point pkg at offline USB repo for this install
|
||||
cat > /usr/local/etc/pkg/repos/Clawdie-USB.conf <<EOF
|
||||
Clawdie-USB: {
|
||||
url: "file://${SHARE}/packages",
|
||||
enabled: yes,
|
||||
priority: 100
|
||||
}
|
||||
EOF
|
||||
|
||||
# --- tier 1: desktop environment ---
|
||||
|
||||
DESKTOP=$(dialog --radiolist \
|
||||
"Desktop environment:" 14 60 4 \
|
||||
"xfce" "XFCE — lightweight, recommended" on \
|
||||
"kde" "KDE Plasma — full-featured, needs 8GB+" off \
|
||||
"mate" "MATE — classic desktop, mid-weight" off \
|
||||
"headless" "Headless — no desktop, server only" off \
|
||||
3>&1 1>&2 2>&3) || die "Cancelled."
|
||||
|
||||
# --- tier 1: agent identity ---
|
||||
|
||||
ASSISTANT_NAME=$(dialog --inputbox \
|
||||
"Assistant name (displayed to users):" 8 50 "Clawdie" \
|
||||
3>&1 1>&2 2>&3) || die "Cancelled."
|
||||
|
||||
# Derive AGENT_NAME: lowercase, alphanumeric + hyphen only
|
||||
AGENT_NAME=$(echo "$ASSISTANT_NAME" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-' | sed 's/-*$//')
|
||||
|
||||
AGENT_DOMAIN=$(dialog --inputbox \
|
||||
"Domain name for this agent:" 8 50 "${AGENT_NAME}.local" \
|
||||
3>&1 1>&2 2>&3) || die "Cancelled."
|
||||
|
||||
TZ=$(dialog --inputbox \
|
||||
"Timezone (IANA format):" 8 50 "UTC" \
|
||||
3>&1 1>&2 2>&3) || die "Cancelled."
|
||||
|
||||
# --- tier 2: pi provider profile (optional) ---
|
||||
|
||||
DO_PI=$(dialog --yesno \
|
||||
"Configure LLM provider profile now?\n\n(You can also set this up later — the agent will prompt you on first run.)" \
|
||||
9 60 3>&1 1>&2 2>&3 && echo yes || echo no)
|
||||
|
||||
PI_PROFILE="operator"
|
||||
PI_PROVIDER="anthropic"
|
||||
if [ "$DO_PI" = "yes" ]; then
|
||||
PI_PROVIDER=$(dialog --radiolist \
|
||||
"LLM provider:" 16 60 6 \
|
||||
"anthropic" "Anthropic (Claude)" on \
|
||||
"openai" "OpenAI (GPT-4)" off \
|
||||
"openrouter" "OpenRouter" off \
|
||||
"groq" "Groq" off \
|
||||
"ollama" "Ollama (local)" off \
|
||||
"zai" "ZAI / GLM" off \
|
||||
3>&1 1>&2 2>&3) || PI_PROVIDER="anthropic"
|
||||
|
||||
# Note: key entry is deferred to pi first-run
|
||||
dialog --msgbox \
|
||||
"\nProvider set to: ${PI_PROVIDER}\n\nYour API key will be requested the first time the agent runs.\nYou can also add it manually to:\n ${ENV_FILE}\n\nThen restart: service ${AGENT_NAME} restart" \
|
||||
13 60
|
||||
fi
|
||||
|
||||
# --- auto-generate secrets ---
|
||||
|
||||
POSTGRES_ADMIN_PASSWORD=$(gen_secret)
|
||||
SKILLS_DB_PASSWORD=$(gen_secret)
|
||||
MEMORY_DB_PASSWORD=$(gen_secret)
|
||||
STRAPI_DB_PASSWORD=$(gen_secret)
|
||||
STRAPI_APP_KEYS="$(gen_secret),$(gen_secret)"
|
||||
STRAPI_API_TOKEN_SALT=$(gen_secret)
|
||||
STRAPI_ADMIN_JWT_SECRET=$(gen_secret)
|
||||
STRAPI_TRANSFER_TOKEN_SALT=$(gen_secret)
|
||||
STRAPI_JWT_SECRET=$(gen_secret)
|
||||
SCREENSHOTS_PASSWORD=$(gen_secret)
|
||||
|
||||
# --- summary screen ---
|
||||
|
||||
dialog --msgbox \
|
||||
"\nReady to install. Summary:\n\
|
||||
\n Agent : ${ASSISTANT_NAME} (${AGENT_NAME})\
|
||||
\n Domain : ${AGENT_DOMAIN}\
|
||||
\n Timezone : ${TZ}\
|
||||
\n Desktop : ${DESKTOP}\
|
||||
\n Pkg : ${PKG_BRANCH}\
|
||||
\n Provider : ${PI_PROVIDER}\
|
||||
\n\nGenerated credentials written to:\n ${ENV_FILE}\n\nInstallation will take 15–30 minutes.\nPress Enter to begin." \
|
||||
20 65
|
||||
|
||||
# --- write .env ---
|
||||
|
||||
mkdir -p "$CLAWDIE_AI"
|
||||
touch "$ENV_FILE"
|
||||
chmod 600 "$ENV_FILE"
|
||||
|
||||
write_env "AGENT_NAME" "$AGENT_NAME"
|
||||
write_env "ASSISTANT_NAME" "$ASSISTANT_NAME"
|
||||
write_env "AGENT_DOMAIN" "$AGENT_DOMAIN"
|
||||
write_env "AGENT_INTERNAL_DOMAIN" "${AGENT_NAME}.home.arpa"
|
||||
write_env "TZ" "$TZ"
|
||||
write_env "SETUP_LOCALE" "en-US"
|
||||
write_env "DISPLAY_LOCALE" "en-US"
|
||||
write_env "ASSISTANT_LOCALE" "en-US"
|
||||
write_env "SYSTEM_LOCALE" "en_US.UTF-8"
|
||||
write_env "PI_TUI_PROVIDER" "$PI_PROVIDER"
|
||||
write_env "PI_TUI_PROFILE" "$PI_PROFILE"
|
||||
write_env "AGENT_SUBNET_BASE" "10.0.0"
|
||||
write_env "WARDEN_SUBNET" "10.0.0.0/24"
|
||||
write_env "WARDEN_GATEWAY" "10.0.0.1"
|
||||
write_env "WARDEN_DB_IP" "10.0.0.3"
|
||||
write_env "WARDEN_GIT_IP" "10.0.0.4"
|
||||
write_env "POSTGRES_ADMIN_PASSWORD" "$POSTGRES_ADMIN_PASSWORD"
|
||||
write_env "SKILLS_DB_PASSWORD" "$SKILLS_DB_PASSWORD"
|
||||
write_env "MEMORY_DB_PASSWORD" "$MEMORY_DB_PASSWORD"
|
||||
write_env "STRAPI_DB_PASSWORD" "$STRAPI_DB_PASSWORD"
|
||||
write_env "STRAPI_APP_KEYS" "$STRAPI_APP_KEYS"
|
||||
write_env "STRAPI_API_TOKEN_SALT" "$STRAPI_API_TOKEN_SALT"
|
||||
write_env "STRAPI_ADMIN_JWT_SECRET" "$STRAPI_ADMIN_JWT_SECRET"
|
||||
write_env "STRAPI_TRANSFER_TOKEN_SALT" "$STRAPI_TRANSFER_TOKEN_SALT"
|
||||
write_env "STRAPI_JWT_SECRET" "$STRAPI_JWT_SECRET"
|
||||
write_env "SCREENSHOTS_PASSWORD" "$SCREENSHOTS_PASSWORD"
|
||||
write_env "SCREENSHOTS_USER" "$AGENT_NAME"
|
||||
|
||||
# --- node.js: set up npm prefix for clawdie user ---
|
||||
echo "prefix=${CLAWDIE_HOME}/.npm-global" > "${CLAWDIE_HOME}/.npmrc"
|
||||
mkdir -p "${CLAWDIE_HOME}/.npm-global/bin"
|
||||
if ! grep -q '\.npm-global' "${CLAWDIE_HOME}/.profile" 2>/dev/null; then
|
||||
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> "${CLAWDIE_HOME}/.profile"
|
||||
fi
|
||||
chown -R clawdie:clawdie "${CLAWDIE_HOME}/.npmrc" "${CLAWDIE_HOME}/.npm-global" "${CLAWDIE_HOME}/.profile"
|
||||
|
||||
# --- gpu detection ---
|
||||
echo "Detecting GPU..."
|
||||
eval $(sh "${SHARE}/firstboot/gpu-detect.sh")
|
||||
echo " GPU: ${GPU_VENDOR} → kld: ${GPU_KLD}"
|
||||
for kld in $GPU_KLD; do
|
||||
kldload "$kld" 2>/dev/null || true
|
||||
done
|
||||
sysrc kld_list+="${GPU_KLD}"
|
||||
[ -n "$GPU_NOTES" ] && echo " Note: ${GPU_NOTES}"
|
||||
|
||||
# --- pkg install: desktop packages ---
|
||||
echo "Installing packages offline..."
|
||||
# TODO: install DE-specific package list
|
||||
# pkg install -y $(cat "${SHARE}/packages/pkg-list-${DESKTOP}.txt")
|
||||
|
||||
# --- extract clawdie-ai ---
|
||||
echo "Extracting Clawdie-AI..."
|
||||
tar -xzf "${SHARE}/clawdie-ai.tar.gz" -C "$CLAWDIE_HOME"
|
||||
chown -R clawdie:clawdie "$CLAWDIE_AI"
|
||||
|
||||
# --- seed AGENTS.md from template ---
|
||||
AGENTS_TPL="${CLAWDIE_AI}/groups/global/AGENTS.md.tpl"
|
||||
AGENTS_OUT="${CLAWDIE_AI}/groups/global/AGENTS.md"
|
||||
if [ -f "$AGENTS_TPL" ]; then
|
||||
sed \
|
||||
-e "s|{{ASSISTANT_NAME}}|${ASSISTANT_NAME}|g" \
|
||||
-e "s|{{AGENT_NAME}}|${AGENT_NAME}|g" \
|
||||
-e "s|{{AGENT_DOMAIN}}|${AGENT_DOMAIN}|g" \
|
||||
-e "s|{{ASSISTANT_LOCALE}}|en-US|g" \
|
||||
-e "s|{{TZ}}|${TZ}|g" \
|
||||
"$AGENTS_TPL" > "$AGENTS_OUT"
|
||||
chown clawdie:clawdie "$AGENTS_OUT"
|
||||
fi
|
||||
|
||||
# --- run clawdie-ai setup ---
|
||||
echo "Running Clawdie-AI install..."
|
||||
cd "$CLAWDIE_AI"
|
||||
su -m clawdie -c "npm install 2>&1" | tee -a "$LOG"
|
||||
su -m clawdie -c "npm run install-all 2>&1" | tee -a "$LOG"
|
||||
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo " Clawdie-AI setup complete."
|
||||
echo " Agent : ${ASSISTANT_NAME}"
|
||||
echo " Domain: ${AGENT_DOMAIN}"
|
||||
echo " Logs : ${LOG}"
|
||||
echo "============================================"
|
||||
73
firstboot/gpu-detect.sh
Normal file
73
firstboot/gpu-detect.sh
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#!/bin/sh
|
||||
# gpu-detect.sh — detect GPU via pciconf and return the appropriate kld module
|
||||
#
|
||||
# Usage:
|
||||
# gpu-detect.sh
|
||||
#
|
||||
# Output:
|
||||
# Prints shell variable assignments to stdout, to be eval'd by caller:
|
||||
# GPU_VENDOR="Intel"
|
||||
# GPU_KLD="i915kms"
|
||||
# GPU_XORG_DRIVER="intel"
|
||||
# GPU_NOTES=""
|
||||
#
|
||||
# Based on detection logic from outpaddling/desktop-installer (BSD 2-clause).
|
||||
# Simplified to target hardware only — VirtualBox excluded.
|
||||
|
||||
set -e
|
||||
|
||||
# Default: vesa fallback
|
||||
GPU_VENDOR="Unknown"
|
||||
GPU_KLD="vesa"
|
||||
GPU_XORG_DRIVER="vesa"
|
||||
GPU_NOTES=""
|
||||
|
||||
# Parse pciconf for display controllers (class 0x03)
|
||||
PCI_DISPLAY=$(pciconf -lv 2>/dev/null | awk '
|
||||
/class=0x03/ { found=1 }
|
||||
found && /vendor=/ { vendor=$0 }
|
||||
found && /device=/ { device=$0; print vendor; print device; found=0 }
|
||||
')
|
||||
|
||||
VENDOR_ID=$(pciconf -l 2>/dev/null | awk -F'[@ :]' '/^vgapci|^drm/ { print $5 }' | head -1)
|
||||
|
||||
case "$VENDOR_ID" in
|
||||
# Intel
|
||||
8086)
|
||||
GPU_VENDOR="Intel"
|
||||
GPU_KLD="i915kms"
|
||||
GPU_XORG_DRIVER="intel"
|
||||
;;
|
||||
# NVIDIA
|
||||
10de)
|
||||
GPU_VENDOR="NVIDIA"
|
||||
GPU_KLD="nvidia-modeset nvidia"
|
||||
GPU_XORG_DRIVER="nvidia"
|
||||
GPU_NOTES="Requires nvidia-driver package"
|
||||
;;
|
||||
# AMD / ATI
|
||||
1002)
|
||||
GPU_VENDOR="AMD"
|
||||
GPU_KLD="amdgpu"
|
||||
GPU_XORG_DRIVER="amdgpu"
|
||||
GPU_NOTES="amdgpu preferred; radeon fallback for older cards"
|
||||
;;
|
||||
# VMware (kept for bhyve SVGA compatibility)
|
||||
15ad)
|
||||
GPU_VENDOR="VMware/bhyve"
|
||||
GPU_KLD="vmwgfx"
|
||||
GPU_XORG_DRIVER="vmware"
|
||||
;;
|
||||
*)
|
||||
GPU_VENDOR="Unknown (${VENDOR_ID})"
|
||||
GPU_KLD="vesa"
|
||||
GPU_XORG_DRIVER="vesa"
|
||||
GPU_NOTES="Falling back to VESA. Check pciconf -lv for GPU details."
|
||||
;;
|
||||
esac
|
||||
|
||||
# Output as eval-able shell vars
|
||||
echo "GPU_VENDOR=\"${GPU_VENDOR}\""
|
||||
echo "GPU_KLD=\"${GPU_KLD}\""
|
||||
echo "GPU_XORG_DRIVER=\"${GPU_XORG_DRIVER}\""
|
||||
echo "GPU_NOTES=\"${GPU_NOTES}\""
|
||||
42
firstboot/rc.d/clawdie-firstboot
Normal file
42
firstboot/rc.d/clawdie-firstboot
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# PROVIDE: clawdie_firstboot
|
||||
# REQUIRE: NETWORKING LOGIN
|
||||
# BEFORE: clawdie
|
||||
# KEYWORD: firstboot
|
||||
#
|
||||
# clawdie-firstboot — runs once on first HDD boot to complete Clawdie-AI setup.
|
||||
# Self-disables on completion.
|
||||
|
||||
. /etc/rc.subr
|
||||
|
||||
name="clawdie_firstboot"
|
||||
rcvar="${name}_enable"
|
||||
start_cmd="${name}_start"
|
||||
stop_cmd=":"
|
||||
|
||||
SHARE="/usr/local/share/clawdie-iso"
|
||||
LOG="/var/log/clawdie-firstboot.log"
|
||||
|
||||
clawdie_firstboot_start()
|
||||
{
|
||||
echo "Starting Clawdie first-boot setup..."
|
||||
|
||||
# Run on ttyv0 so bsddialog has a real TTY for the wizard
|
||||
/usr/bin/script -q -a "$LOG" \
|
||||
/bin/sh "${SHARE}/firstboot/firstboot.sh"
|
||||
|
||||
RC=$?
|
||||
|
||||
if [ "$RC" -eq 0 ]; then
|
||||
echo "Clawdie first-boot setup complete. Disabling service."
|
||||
sysrc -x clawdie_firstboot_enable
|
||||
rm -rf "$SHARE"
|
||||
else
|
||||
echo "Clawdie first-boot setup failed (exit $RC). Check $LOG"
|
||||
fi
|
||||
}
|
||||
|
||||
load_rc_config "$name"
|
||||
: "${clawdie_firstboot_enable:=NO}"
|
||||
run_rc_command "$1"
|
||||
39
installerconfig
Normal file
39
installerconfig
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#!/bin/sh
|
||||
# installerconfig — bsdinstall post-install hook
|
||||
#
|
||||
# bsdinstall sources this file automatically after base system installation
|
||||
# completes. Runs in the context of the live USB environment, with the
|
||||
# target HDD mounted at /mnt.
|
||||
#
|
||||
# Responsibilities:
|
||||
# 1. Copy firstboot payload from USB to installed HDD
|
||||
# 2. Enable the clawdie-firstboot rc.d service (runs once on first HDD boot)
|
||||
# 3. That's it — all real work happens in firstboot.sh on first boot
|
||||
|
||||
set -e
|
||||
|
||||
USB_SHARE="/usr/local/share/clawdie-iso"
|
||||
HDD_SHARE="/mnt/usr/local/share/clawdie-iso"
|
||||
HDD_RCD="/mnt/usr/local/etc/rc.d"
|
||||
|
||||
echo "clawdie-iso: injecting firstboot payload..."
|
||||
|
||||
# Copy firstboot scripts
|
||||
mkdir -p "$HDD_SHARE"
|
||||
cp -r "${USB_SHARE}/firstboot" "${HDD_SHARE}/"
|
||||
cp -r "${USB_SHARE}/packages" "${HDD_SHARE}/"
|
||||
cp "${USB_SHARE}/clawdie-ai.tar.gz" "${HDD_SHARE}/"
|
||||
cp "${USB_SHARE}/build.cfg" "${HDD_SHARE}/"
|
||||
|
||||
chmod +x "${HDD_SHARE}/firstboot/firstboot.sh"
|
||||
chmod +x "${HDD_SHARE}/firstboot/gpu-detect.sh"
|
||||
|
||||
# Install firstboot rc.d service
|
||||
mkdir -p "$HDD_RCD"
|
||||
cp "${USB_SHARE}/firstboot/rc.d/clawdie-firstboot" "${HDD_RCD}/clawdie-firstboot"
|
||||
chmod +x "${HDD_RCD}/clawdie-firstboot"
|
||||
|
||||
# Enable service in rc.conf on HDD
|
||||
echo 'clawdie_firstboot_enable="YES"' >> /mnt/etc/rc.conf
|
||||
|
||||
echo "clawdie-iso: firstboot payload installed. Rebooting to HDD..."
|
||||
0
packages/.gitkeep
Normal file
0
packages/.gitkeep
Normal file
Loading…
Add table
Reference in a new issue