clawdie-iso/SHELL-MODULES.md

770 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Clawdie Shell Modules — Implementation Reference
**Version:** v0.9.0
**Implementation Date:** 26.mar.2026
**Status:** ✅ Complete (12/12 modules)
---
## Overview
The Clawdie Shell comprises 12 POSIX-compliant shell modules that orchestrate the complete installation and configuration of Clawdie-AI from ISO boot to running system.
Each module is independent and can be sourced individually for testing. Together, they form the firstboot pipeline that executes on first HDD boot.
---
## Module Index
| Module | Function | Lines | Purpose | Status |
|--------|----------|-------|---------|--------|
| shell-zfs.sh | `clawdie_shell_zfs_detect()` | 192 | ZFS pool detection | ✅ Complete |
| shell-gpu.sh | `clawdie_shell_gpu_detect()` | 184 | GPU detection + kld_list | ✅ Complete |
| shell-nvidia.sh | `clawdie_shell_nvidia_detect()` | 229 | NVIDIA driver versioning | ✅ Complete |
| shell-pkg.sh | `clawdie_shell_pkg_setup()` | 199 | Package repos + USB cache | ✅ Complete |
| shell-ssh.sh | `clawdie_shell_ssh_setup()` | 260 | SSH keys + system passwords | ✅ Complete |
| shell-env.sh | `clawdie_shell_env_generate()` | 192 | .env + secrets generation | ✅ Complete |
| shell-system.sh | `clawdie_shell_system_config()` | 233 | rc.conf + hostname + services | ✅ Complete |
| shell-desktop.sh | `clawdie_shell_desktop_detect()` | 101 | Desktop detection + enablement | ✅ Complete |
| shell-pf.sh | `clawdie_shell_pf()` | 148 | PF firewall + jail NAT | ✅ Complete |
| shell-tailscale.sh | `clawdie_shell_tailscale_setup()` | 121 | Tailscale remote access (optional) | ✅ Complete |
| shell-npm-globals.sh | `clawdie_shell_npm_globals_install()` | 101 | Bundled npm CLIs | ✅ Complete |
| shell-deploy.sh | `clawdie_shell_deploy()` | 424 | Tarball extract + just install | ✅ Complete |
**Total:** 2,384 lines of production code (POSIX shell, no bash-isms)
---
## Module: shell-gpu.sh
**File:** `firstboot/shell-gpu.sh`
**Main Function:** `clawdie_shell_gpu_detect()`
### Purpose
Detect the system's GPU (if any) and configure appropriate kernel modules for X11/Wayland rendering. Writes `kld_list` to `/etc/rc.conf`.
### Entry Point
```bash
. firstboot/shell-gpu.sh
clawdie_shell_gpu_detect
```
### Functions
#### `clawdie_shell_gpu_detect()`
Main orchestrator. Detects GPU vendor via `pciconf`, matches to driver, writes rc.conf.
**Sets:** `DETECTED_GPU` (intel, amd, nvidia, vmware, or vesa)
**Side effects:** Writes to rc.conf, progress file, log file
#### `clawdie_shell_gpu_detect_pci()`
Queries `pciconf -lv` to find VGA device (class 0x030000).
**Returns:** GPU vendor string (intel, amd, nvidia, vmware, or empty)
#### `clawdie_shell_gpu_match_driver(vendor)`
Maps vendor to FreeBSD kernel module list.
**Mappings:**
| Vendor | Module | Note |
|--------|--------|------|
| intel | i915kms | Intel integrated GPU (iGPU) |
| amd | amdgpu | AMD Radeon / RDNA |
| nvidia | nvidia-modeset nvidia | NVIDIA discrete GPU (see shell-nvidia.sh for version) |
| vmware | vmwgfx | VMware SVGA 3D |
| (other) | (empty) | VESA fallback, no kld needed |
#### `clawdie_shell_gpu_write_rcconf(kld_list)`
Writes `kld_list="..."` to `/etc/rc.conf`, replacing any existing kld_list.
**Side effects:**
- Removes existing kld_list (idempotent)
- Appends new kld_list entry
- Skips if kld_list is empty (VESA case)
#### `clawdie_shell_gpu_validate()`
Verifies rc.conf has valid kld_list.
### Environment Variables
**Input (from caller):**
- (none required; uses pciconf on system)
**Output (sets for downstream modules):**
- `DETECTED_GPU` — GPU vendor (used by shell-nvidia.sh)
**Configuration (can override for testing):**
- `RC_CONF` (default: `/etc/rc.conf`)
- `LOG_FILE` (default: `/var/log/clawdie-firstboot.log`)
- `PROGRESS_FILE` (default: `/var/log/clawdie-firstboot.progress`)
### Error Handling
Returns 0 on success. Returns 1 on critical failure (e.g., can't write rc.conf).
Gracefully handles:
- Missing pciconf output → defaults to VESA
- Unknown GPU vendor → defaults to VESA
- Read-only /etc/rc.conf → returns error
### Testing
```bash
# Quick test
export RC_CONF="/tmp/test-rc.conf"
export LOG_FILE="/tmp/test.log"
. firstboot/shell-gpu.sh
clawdie_shell_gpu_detect
cat /tmp/test-rc.conf # Should have kld_list
```
---
## Module: shell-nvidia.sh
**File:** `firstboot/shell-nvidia.sh`
**Main Function:** `clawdie_shell_nvidia_detect()`
### Purpose
For NVIDIA GPUs, detect the specific GPU model and select the correct driver version (590, 470, or 390). Modern GPUs use 590; Maxwell-era use 470; Kepler and older use 390.
### Entry Point
```bash
export DETECTED_GPU="nvidia" # Set by shell-gpu.sh
. firstboot/shell-nvidia.sh
clawdie_shell_nvidia_detect
```
### Functions
#### `clawdie_shell_nvidia_detect()`
Main orchestrator. Only executes if `DETECTED_GPU=nvidia`.
**Sets:** `NVIDIA_DRIVER` (590, 470, or 390)
**Side effects:** Writes updated kld_list to rc.conf
#### `clawdie_shell_nvidia_get_device_id()`
Queries pciconf for NVIDIA GPU device ID (PCI vendor 0x10de).
**Returns:** 4-digit hex device ID (e.g., "2388" for RTX 4090)
#### `clawdie_shell_nvidia_select_driver(device_id)`
Maps device ID to driver version using lookup table.
**Device ranges:**
| Range | GPU Era | Driver |
|-------|---------|--------|
| 0x2700+ (≥9984) | Ada (RTX 40xx) | 590 |
| 0x20600x2500 (82889472) | Turing/Ampere (RTX 20xx/30xx) | 590 |
| 0x13400x2186 (49288582) | Maxwell (GTX 750980 Ti) | 470 |
| 0x11800x139F (44805023) | Kepler (GTX 650780 Ti) | 390 |
| (unknown) | — | 590 (safe default) |
#### `clawdie_shell_nvidia_write_rcconf(nvidia_version)`
Updates rc.conf with correct nvidia-driver and nvidia-modeset modules.
**Side effects:**
- Removes any existing nvidia entries from kld_list
- Adds `nvidia-modeset nvidia` at front of kld_list
- Preserves other kld modules (e.g., i915kms from dual GPU)
#### `clawdie_shell_nvidia_validate()`
Checks that nvidia is in rc.conf.
### Environment Variables
**Input (required):**
- `DETECTED_GPU` — Must equal "nvidia" to execute
**Output (sets):**
- `NVIDIA_DRIVER` — Selected version (590, 470, or 390)
**Configuration (can override):**
- `RC_CONF` (default: `/etc/rc.conf`)
- `LOG_FILE` (default: `/var/log/clawdie-firstboot.log`)
- `PROGRESS_FILE` (default: `/var/log/clawdie-firstboot.progress`)
### Fallback Behavior
- No GPU detected → Silently returns 0 (NVIDIA module skipped)
- Unknown device ID → Defaults to driver 590
### Testing
```bash
# Test with simulated NVIDIA GPU
export DETECTED_GPU="nvidia"
export RC_CONF="/tmp/test-rc.conf"
. firstboot/shell-nvidia.sh
clawdie_shell_nvidia_detect
cat /tmp/test-rc.conf # Should have nvidia-modeset nvidia
```
---
## Module: shell-pkg.sh
**File:** `firstboot/shell-pkg.sh`
**Main Function:** `clawdie_shell_pkg_setup()`
### Purpose
Configure FreeBSD package repositories (online and offline USB) and seed Bastille jail cache with pre-downloaded packages for offline provisioning.
### Entry Point
```bash
. firstboot/shell-pkg.sh
clawdie_shell_pkg_setup
```
### Functions
#### `clawdie_shell_pkg_setup()`
Main orchestrator. Detects ABI, writes repo configs, seeds cache.
**Returns:** 0 on success, 1 on critical error
**Side effects:** Creates /etc/pkg/repos, /var/cache/pkg/bastille directories
#### `clawdie_shell_pkg_detect_abi()`
Queries `pkg config abi` or derives from `uname`.
**Returns:** ABI string like "FreeBSD:15:amd64"
#### `clawdie_shell_pkg_write_freebsd_conf(abi)`
Writes `/etc/pkg/repos/FreeBSD.conf` pointing to pkg.FreeBSD.org.
**Uses:** `DEFAULT_PKG_BRANCH` from build.cfg (latest or quarterly)
**Output file format:**
```
FreeBSD: {
url: "pkg+https://pkg.FreeBSD.org/${ABI}/latest",
mirror_type: "srv",
enabled: yes
}
```
#### `clawdie_shell_pkg_write_clawdie_conf()`
Writes `/etc/pkg/repos/Clawdie-USB.conf` pointing to bundled USB packages.
**Output file format:**
```
Clawdie-USB: {
url: "file:///usr/local/share/clawdie-iso/packages",
mirror_type: "none",
enabled: yes,
priority: 100
}
```
Priority 100 ensures USB packages take precedence if online repo is also available.
#### `clawdie_shell_pkg_seed_cache()`
Copies all .pkg files from USB to `/var/cache/pkg/bastille`.
**Purpose:** Allows jails to install packages offline after first boot
**Side effects:**
- Creates /var/cache/pkg/bastille if missing
- Copies *.pkg files (non-fatal if USB path missing)
### Environment Variables
**Input (optional):**
- `DEFAULT_PKG_BRANCH` — "latest" or "quarterly" (from build.cfg)
**Configuration (can override):**
- `PKG_CONF_DIR` (default: `/etc/pkg/repos`)
- `FREEBSD_REPO_CONF` (default: `/etc/pkg/repos/FreeBSD.conf`)
- `CLAWDIE_USB_REPO_CONF` (default: `/etc/pkg/repos/Clawdie-USB.conf`)
- `USB_PKG_PATH` (default: `/usr/local/share/clawdie-iso/packages`)
- `BASTILLE_PKG_CACHE` (default: `/var/cache/pkg/bastille`)
- `LOG_FILE`, `PROGRESS_FILE`
### Error Handling
Non-fatal errors:
- Missing /etc/pkg/repos directory → created
- Missing USB packages → skipped with warning
- FreeBSD repo already exists → replaced
Returns 1 only if critical failure (e.g., can't write config).
### Testing
```bash
# Test with mock directories
export PKG_CONF_DIR="/tmp/test-repos"
export USB_PKG_PATH="/tmp/fake-usb/packages"
mkdir -p "$USB_PKG_PATH"
touch "$USB_PKG_PATH/test.pkg"
. firstboot/shell-pkg.sh
clawdie_shell_pkg_setup
# Verify
cat /tmp/test-repos/FreeBSD.conf
cat /tmp/test-repos/Clawdie-USB.conf
```
---
## Module: shell-env.sh
**File:** `firstboot/shell-env.sh`
**Main Function:** `clawdie_shell_env_generate()`
### Purpose
Generate `.env` configuration file with secrets, identity variables, and jail IP allocation. This file is used by Clawdie-AI at runtime.
### Entry Point
```bash
export ASSISTANT_NAME="MyAssistant"
export AGENT_DOMAIN="clawdie.local"
export TZ="Europe/Ljubljana"
. firstboot/shell-env.sh
clawdie_shell_env_generate
```
### Functions
#### `clawdie_shell_env_generate()`
Main orchestrator. Validates inputs, generates secrets, writes .env file.
**Requires:**
- `ASSISTANT_NAME` (from wizard)
- `AGENT_DOMAIN` (from wizard)
- `TZ` (from wizard, defaults to UTC)
**Side effects:**
- Creates /home/clawdie/.env
- Sets permissions 600
- Changes owner to clawdie:clawdie
#### `clawdie_shell_env_write_file()`
Generates complete .env file with:
- Identity (ASSISTANT_NAME, AGENT_DOMAIN, TZ)
- Auto-generated secrets (JWT_SECRET, API_KEY, DB_PASSWORD, REDIS_PASSWORD)
- Jail IP allocation (derived from AGENT_SUBNET_BASE)
- Feature flags (FEATURE_MANAGEMENT_JAIL, FEATURE_OLLAMA, FEATURE_TELEGRAM)
- LLM provider config
- System metadata
**File format:** Shell-compatible KEY="value" (can be sourced in other scripts)
**Variables generated:**
- Identity: ASSISTANT_NAME, AGENT_NAME, AGENT_DOMAIN, TZ
- Secrets: JWT_SECRET, ANTHROPIC_API_KEY, DB_PASSWORD, REDIS_PASSWORD (32-char base64 each)
- Network: AGENT_SUBNET_BASE, AGENT_GATEWAY_IP, MGMT_JAIL_IP, DB_JAIL_IP, GIT_JAIL_IP, CMS_JAIL_IP, WORKER_JAIL_IP_START
- Database: DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD
- Features: FEATURE_MANAGEMENT_JAIL, FEATURE_OLLAMA, FEATURE_TELEGRAM
- Telegram: TELEGRAM_BOT_TOKEN, TELEGRAM_ADMIN_IDS (optional, blank initially)
- LLM: LLM_PROVIDER
- SSH: SSH_PUBLIC_KEY (if provided at install)
- System: FREEBSD_VERSION, INSTALL_DATE
#### `clawdie_shell_env_gen_secret()`
Generates random 32-character secrets suitable for JWT/API keys.
**Method:** `openssl rand -base64 32` with fallback to /dev/urandom
**Returns:** 32-char base64 string (alphanumeric + +/)
#### `clawdie_shell_env_validate()`
Verifies .env file has:
- Correct permissions (600)
- All required variables
- Non-empty secret values
### Environment Variables
**Input (required):**
- `ASSISTANT_NAME` — Display name for agent
- `AGENT_DOMAIN` — FQDN for internal network
- `TZ` — Timezone (e.g., Europe/Ljubljana)
**Input (optional):**
- `SSH_PUBLIC_KEY` — User's SSH public key (if provided at install)
**Configuration (can override):**
- `CLAWDIE_HOME` (default: `/home/clawdie`)
- `ENV_FILE` (default: `/home/clawdie/.env`)
- `AGENT_SUBNET_BASE` (default: `10.0.0`)
- `LOG_FILE`, `PROGRESS_FILE`
### Jail IP Allocation
IPs are auto-derived from AGENT_SUBNET_BASE:
| Slot | Service | IP |
|------|---------|-----|
| .1 | Gateway (host bridge) | 10.0.0.1 |
| .2 | Management jail | 10.0.0.2 |
| .3 | Database (PostgreSQL) | 10.0.0.3 |
| .4 | Code service (git mirror) | 10.0.0.4 |
| .5 | Web service (nginx) | 10.0.0.5 |
| .101+ | Worker jails (dynamic) | 10.0.0.101+ |
### Testing
```bash
# Test .env generation
export ASSISTANT_NAME="TestAgent"
export AGENT_DOMAIN="test.local"
export TZ="UTC"
export CLAWDIE_HOME="/tmp/test-clawdie"
. firstboot/shell-env.sh
clawdie_shell_env_generate
# Verify
cat /tmp/test-clawdie/.env
wc -l /tmp/test-clawdie/.env # Should have ~45 lines
grep "JWT_SECRET" /tmp/test-clawdie/.env
```
---
## Module: shell-deploy.sh
**File:** `firstboot/shell-deploy.sh`
**Main Function:** `clawdie_shell_deploy()`
### Purpose
Extract Clawdie-AI tarball to /home/clawdie/ and run `just install`, which provisions jails, installs databases, configures services.
### Entry Point
```bash
. firstboot/shell-deploy.sh
clawdie_shell_deploy
```
### Functions
#### `clawdie_shell_deploy()`
Main orchestrator. Extracts tarball, ensures dependencies exist (offline tarball includes `node_modules`), runs installer, verifies services.
**Requires:**
- `ASSISTANT_NAME`, `AGENT_DOMAIN`, `TZ` (from env module)
**Returns:** 0 on success, 1 on failure
**Side effects:** Creates /home/clawdie/clawdie-ai, provisions jails, starts services
#### `clawdie_shell_deploy_extract_tarball()`
Extracts clawdie-ai.tar.gz to /home/clawdie/.
**Tarball location:** `/usr/local/share/clawdie-iso/clawdie-ai.tar.gz`
**Side effects:**
- Creates /home/clawdie/clawdie-ai
- Renames Clawdie-AI/ → clawdie-ai/ if needed
- Fixes ownership to clawdie:clawdie
#### `clawdie_shell_deploy_run_install_all()`
Executes `just install` from /home/clawdie/clawdie-ai/.
**What it does (from Clawdie-AI's package.json):**
- Uses bundled `node_modules` (offline); if missing, deploy falls back to `npm ci` (network required)
- Creates and starts Bastille jails (worker, db, git, cms)
- Sets up PostgreSQL in db jail
- Configures nginx in cms jail
- Installs rc.d service script for clawdie agent
- Enables service
**Runs as:** clawdie user (drops from root if needed via `su - clawdie`)
#### `clawdie_shell_deploy_verify()`
Checks post-install state:
- At least one jail running (jls -N)
- clawdie service enabled in rc.conf
- .env file exists
**Warnings:** Non-fatal if any check fails (jails may still be starting)
### Environment Variables
**Input (optional):**
- `ASSISTANT_NAME`, `AGENT_DOMAIN`, `TZ` (from env module if available)
**Configuration (can override):**
- `CLAWDIE_HOME` (default: `/home/clawdie`)
- `CLAWDIE_AI_DIR` (default: `/home/clawdie/clawdie-ai`)
- `CLAWDIE_TARBALL` (default: `/usr/local/share/clawdie-iso/clawdie-ai.tar.gz`)
- `ENV_FILE` (default: `/home/clawdie/.env`)
- `LOG_FILE`, `PROGRESS_FILE`
### Error Handling
Returns 1 (fails) if:
- Tarball not found
- Extraction fails
- package.json not found after extraction
- just install returns error
Returns 0 (succeeds) even if:
- Verification finds missing jails (they may still be starting)
- .env file missing (it's created by env module, but this module doesn't require it)
### Testing
```bash
# Minimal test (mock)
export CLAWDIE_HOME="/tmp/test-clawdie"
export CLAWDIE_AI_DIR="$CLAWDIE_HOME/clawdie-ai"
mkdir -p "$CLAWDIE_AI_DIR"
# Create mock package.json
cat > "$CLAWDIE_AI_DIR/package.json" <<'EOF'
{"name":"clawdie-ai","version":"0.9.0","scripts":{"install":"echo 'Mock install'"}}
EOF
. firstboot/shell-deploy.sh
clawdie_shell_deploy
# Should complete without errors
```
---
## Module: shell-system.sh
**File:** `firstboot/shell-system.sh`
**Main Function:** `clawdie_shell_system_config()`
### Purpose
Configure system-level settings: timezone, hostname, rc.conf services (dbus, hald, seatd, lightdm), and Lumina desktop environment.
### Functions
#### `clawdie_shell_system_config()`
Main orchestrator.
**Requires:**
- `TZ` (timezone from wizard)
- `AGENT_DOMAIN` (from wizard, used as hostname)
**Side effects:**
- Writes /etc/rc.conf
- Writes /etc/hostname
- Creates /etc/profile.d/clawdie.sh
- Enables services
#### `clawdie_shell_system_write_rcconf()`
Configures rc.conf with:
- Timezone
- Service enables (dbus, hald, seatd, lightdm)
- Lumina desktop settings
#### `clawdie_shell_system_set_hostname()`
Sets hostname from AGENT_DOMAIN
#### `clawdie_shell_system_setup_env()`
Creates /etc/profile.d/clawdie.sh with environment variables
#### `clawdie_shell_system_enable_services()`
Enables required services in rc.conf:
- dbus (message bus)
- hald (hardware abstraction)
- seatd (seat management for Wayland)
- lightdm (display manager for Lumina)
---
## Module: shell-ssh.sh
**File:** `firstboot/shell-ssh.sh`
**Main Function:** `clawdie_shell_ssh_setup()`
### Purpose
Configure SSH access and system user passwords. If SSH key is provided, disables password auth. Otherwise, generates secure password for clawdie user.
### Functions
#### `clawdie_shell_ssh_setup()`
Main orchestrator.
**Optional input:**
- `SSH_PUBLIC_KEY` (from install, if provided)
**Configures:**
- SSH keys for clawdie user
- System user passwords (root + clawdie)
- rc.conf for sshd
---
## Module: shell-zfs.sh
**Purpose:** Detect existing ZFS pools and choose boot mode
**Main Function:** `clawdie_shell_zfs_detect()`
**Outputs:**
- `CLAWDIE_BOOT_MODE` — install | upgrade | maintenance
- `POOL_NAME` — detected pool name (if present)
---
## Module: shell-desktop.sh
**Purpose:** Detect display hardware and enable desktop stack when appropriate
**Main Function:** `clawdie_shell_desktop_detect()`
**Outputs:**
- Desktop enablement flags in rc.conf (headless when skipped)
---
## Module: shell-pf.sh
**Purpose:** Configure PF firewall, NAT, and glasspane VNC rules
**Main Function:** `clawdie_shell_pf()`
**Outputs:**
- `/etc/pf.conf`, `/usr/local/etc/rc.d/pf_reload`, rc.conf PF settings
---
## Module: shell-tailscale.sh
**Purpose:** Optional Tailscale install + enablement for remote access
**Main Function:** `clawdie_shell_tailscale_setup()`
**Outputs:**
- tailscaled enablement in rc.conf, optional auth flow
---
## Module: shell-npm-globals.sh
**Purpose:** Install bundled npm CLI tools from ISO cache
**Main Function:** `clawdie_shell_npm_globals_install()`
**Outputs:**
- `/opt/clawdie/npm-global/bin` populated with CLI binaries
---
## Execution Order (firstboot.sh)
The modules execute in this sequence (see firstboot.sh run_step block):
```
1. clawdie_shell_zfs_detect → Sets CLAWDIE_BOOT_MODE (baremetal only)
2. clawdie_shell_gpu_detect → Sets DETECTED_GPU
3. clawdie_shell_nvidia_detect → Sets NVIDIA_DRIVER_VERSION
4. clawdie_shell_pkg_setup → Configures repos
5. clawdie_shell_ssh_setup → SSH + passwords
6. clawdie_shell_env_generate → Generates .env
7. clawdie_shell_system_config → Configures system
8. clawdie_shell_desktop_detect → Desktop enablement
9. clawdie_shell_pf → PF firewall + NAT
10. clawdie_shell_tailscale_setup → Tailscale remote access
11. clawdie_shell_npm_globals_install → Bundled npm CLIs
12. clawdie_shell_deploy → Extracts + installs
```
**State handoff:**
- ZFS module → sets CLAWDIE_BOOT_MODE + POOL_NAME
- GPU module → sets DETECTED_GPU
- env module → creates .env (used by deploy)
- deploy module → provisions jails + services
---
## POSIX Compliance
All modules use POSIX shell features only (no bash-isms):
- ✅ No `[[ ]]` (use `[ ]`)
- ✅ No `${var#pattern}` (avoid)
- ✅ No `function` keyword (use `name()`)
- ✅ No `local` (use plain variables in functions, prefix with `_` to avoid collisions)
- ✅ No `>&2` redirects without careful quoting
This ensures compatibility with FreeBSD /bin/sh.
---
## Error Handling Pattern
All modules follow this pattern:
```bash
function_name() {
# Validate inputs
if [ -z "${VAR:-}" ]; then
log_msg "ERROR: VAR not set"
return 1
fi
# Do work
if ! some_command; then
log_msg "ERROR: some_command failed"
return 1
fi
# Log success
log_msg "Task complete"
return 0
}
```
Each module defines its own `log_msg()` function to write to `LOG_FILE`.
---
## Testing Individual Modules
Each module can be tested standalone:
```bash
# Test GPU detection
export RC_CONF="/tmp/rc.conf"
. firstboot/shell-gpu.sh
clawdie_shell_gpu_detect
# Test environment generation
export ASSISTANT_NAME="Test"
export AGENT_DOMAIN="test.local"
export TZ="UTC"
. firstboot/shell-env.sh
clawdie_shell_env_generate
```
---
## Integration with firstboot.sh
The orchestrator `firstboot.sh` (lines 1282):
1. Sources all 12 shell-*.sh modules
2. Collects wizard inputs (or uses pre-baked config)
3. Exports inputs as environment variables
4. Calls each module's main function in sequence
5. Checks progress file for [MODULE] COMPLETE entries
6. On success, disables clawdie-firstboot service
See [firstboot/firstboot.sh](firstboot/firstboot.sh) for full flow.
---
## References
- [TESTING.md](TESTING.md) — Testing procedures
- [IMPLEMENTATION-PLAN.md](IMPLEMENTATION-PLAN.md) — Task status
- [BUILD.md](BUILD.md) — Build process