fix: refactor setup scripts for system-wide Node.js (no nvm)
BREAKING: Officer now uses system Node.js via NodeSource instead of nvm. Changes: - Removed nvm sourcing from setup.sh - Updated Node installation to use NodeSource repository - All npm global packages installed system-wide with sudo - Updated PTY sidecar setup to use /usr/bin/node (system node) - Added Pi validation (test --list-models) - System packages now available to all users automatically Benefits: - Multi-user friendly: all users get same Node version - No per-user environment setup needed - Simpler troubleshooting (one node version) - Services use consistent node binary - Prevents snap node incompatibility issues Fixes: - 'node not found' for secondary users - systemd services finding correct node - Pi installation consistency across users New Files: - SETUP_GUIDE.md: Comprehensive installation guide - SETUP_ANALYSIS.md: Technical analysis of previous issues Migration: - Remove nvm if installed (optional) - Run: curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - - Run: sudo apt-get install -y nodejs - Run: bash scripts/setup.sh
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
# Officer Setup Flow Analysis
|
||||
|
||||
## Overview
|
||||
The setup.sh script installs all dependencies for Officer, but there are several issues that can cause problems, especially with nvm Node.js environments.
|
||||
|
||||
## Setup Flow
|
||||
|
||||
```
|
||||
1. Detect package manager (apt/pacman/brew)
|
||||
↓
|
||||
2. Install core system packages (git, zip, curl, zsh, build-essential, etc.)
|
||||
↓
|
||||
3. Install archive utilities (7z, unrar)
|
||||
↓
|
||||
4. Install ffmpeg
|
||||
↓
|
||||
5. Configure sudoers for service user
|
||||
↓
|
||||
6. Install Node.js 22 (or warn if not found)
|
||||
↓
|
||||
7. Configure npm global prefix (~/.npm-global)
|
||||
↓
|
||||
8. Install Bun
|
||||
↓
|
||||
9. Install Go 1.23.6
|
||||
↓
|
||||
10. Install Rust
|
||||
↓
|
||||
11. Install PulseAudio (for audio)
|
||||
↓
|
||||
12. Build cliamp from source (Go music player)
|
||||
↓
|
||||
13. Install Neovim
|
||||
↓
|
||||
14. Install terminal tools (starship, oh-my-zsh, eza, lazygit)
|
||||
↓
|
||||
15. Install yt-dlp (optional)
|
||||
↓
|
||||
16. Install npm global packages (Pi, Claude Code, pm2)
|
||||
↓
|
||||
17. Run bun install (project dependencies)
|
||||
↓
|
||||
18. Setup remote desktop (XFCE + VNC)
|
||||
↓
|
||||
19. Setup PTY sidecar (systemd service)
|
||||
↓
|
||||
20. Verification
|
||||
```
|
||||
|
||||
## Issues Found
|
||||
|
||||
### 1. **nvm Node.js Not Properly Documented**
|
||||
|
||||
**Location:** `scripts/setup.sh` (line ~238)
|
||||
|
||||
**Problem:**
|
||||
```bash
|
||||
if has node; then
|
||||
NODE_VER=$(node -v 2>/dev/null | tr -d 'v')
|
||||
NODE_MAJOR=$(echo "$NODE_VER" | cut -d. -f1)
|
||||
if [ "$NODE_MAJOR" = "22" ]; then
|
||||
skip "node v$NODE_VER"
|
||||
else
|
||||
warn "Node $NODE_VER found but v22 is required"
|
||||
warn "Use nvm: nvm install 22 && nvm use 22"
|
||||
fi
|
||||
else
|
||||
warn "Node.js not found — install v22 via nvm:"
|
||||
warn " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash"
|
||||
warn " nvm install 22"
|
||||
fi
|
||||
```
|
||||
|
||||
**Issue:**
|
||||
- Only warns about nvm but doesn't ensure it's sourced in the current shell
|
||||
- When installing pm2 and other npm global packages, nvm might not be available
|
||||
- When services (pm2, systemd) later run, they won't have nvm initialized
|
||||
|
||||
**Impact:**
|
||||
- Users install nvm, run setup.sh in that shell, but when pm2/systemd runs later, it uses the wrong node or no node
|
||||
|
||||
---
|
||||
|
||||
### 2. **PTY Sidecar Service Uses Unbounded `which node`**
|
||||
|
||||
**Location:** `scripts/setup-pty-sidecar.sh` (line ~16)
|
||||
|
||||
**Problem:**
|
||||
```bash
|
||||
NODE_BIN="$(which node)"
|
||||
```
|
||||
|
||||
**Issue:**
|
||||
- If nvm is not sourced in the current shell, `which node` returns nothing or the system node
|
||||
- The systemd service will run with the wrong node binary
|
||||
- When systemd runs, nvm environment is not available anyway
|
||||
|
||||
**Impact:**
|
||||
- PTY sidecar service fails to start or runs with wrong node
|
||||
- Terminal functionality breaks in Officer
|
||||
|
||||
---
|
||||
|
||||
### 3. **PM2/Ecosystem Config Doesn't Handle nvm**
|
||||
|
||||
**Location:** `ecosystem.config.cjs`
|
||||
|
||||
**Problem:**
|
||||
```javascript
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: 'officer',
|
||||
script: 'bun',
|
||||
args: 'start',
|
||||
watch: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
**Issue:**
|
||||
- No environment setup for nvm
|
||||
- PM2 runs with whatever node is in system PATH
|
||||
- If user installed node via nvm, PM2 won't find it
|
||||
- This is why you had to restart the server after installing nvm
|
||||
|
||||
**Impact:**
|
||||
- Officer server fails to start after fresh nvm installation
|
||||
- No clear error message about nvm not being available
|
||||
|
||||
---
|
||||
|
||||
### 4. **No Documentation on Node Installation Methods**
|
||||
|
||||
**Location:** `scripts/setup.sh` (lines 238-250)
|
||||
|
||||
**Problem:**
|
||||
- Script warns about nvm but doesn't explain the workflow
|
||||
- No mention of snap node incompatibility
|
||||
- No mention of system apt/NodeSource installation
|
||||
- No guidance on which method to use when
|
||||
|
||||
**Impact:**
|
||||
- Users can choose any installation method
|
||||
- Some methods (snap) don't work with Officer
|
||||
- New issues arise from incompatible setups
|
||||
|
||||
---
|
||||
|
||||
### 5. **Pi Installation Doesn't Validate nvm Environment**
|
||||
|
||||
**Location:** `scripts/setup.sh` (lines ~408-420)
|
||||
|
||||
**Problem:**
|
||||
```bash
|
||||
if has pi; then
|
||||
skip "pi (@mariozechner/pi-coding-agent)"
|
||||
else
|
||||
npm install -g @mariozechner/pi-coding-agent
|
||||
if has pi; then ok "pi installed"; else warn "pi install failed"; fi
|
||||
fi
|
||||
```
|
||||
|
||||
**Issue:**
|
||||
- Installs pi with `npm install -g`, but npm might be different than later shells
|
||||
- No validation that pi works (should test `pi --list-models`)
|
||||
- No check that Pi was installed to the right npm location
|
||||
|
||||
**Impact:**
|
||||
- Pi appears installed but fails at runtime when shell environment differs
|
||||
|
||||
---
|
||||
|
||||
## Fixes Required
|
||||
|
||||
### Fix 1: Source nvm Before Installing Global Packages
|
||||
|
||||
```bash
|
||||
# At the start of setup.sh, after detecting package manager
|
||||
echo ""
|
||||
echo "── Node.js Environment ──"
|
||||
|
||||
# Check if nvm needs to be sourced
|
||||
if [ -s "$HOME/.nvm/nvm.sh" ]; then
|
||||
source "$HOME/.nvm/nvm.sh"
|
||||
nvm use 22 || nvm install 22
|
||||
ok "nvm activated: $(node -v)"
|
||||
elif ! has node; then
|
||||
fail "Node.js not found and nvm not installed"
|
||||
fail "Install nvm first: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
### Fix 2: Update PTY Sidecar Setup to Use Correct Node
|
||||
|
||||
```bash
|
||||
# In scripts/setup-pty-sidecar.sh
|
||||
if [ -s "$HOME/.nvm/nvm.sh" ]; then
|
||||
source "$HOME/.nvm/nvm.sh"
|
||||
nvm use 22 2>/dev/null || true
|
||||
fi
|
||||
|
||||
NODE_BIN="$(which node)"
|
||||
if [ ! -f "$NODE_BIN" ]; then
|
||||
echo "ERROR: Node.js not found in PATH"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
### Fix 3: Update PM2 Ecosystem Config
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: 'officer',
|
||||
script: 'bun',
|
||||
args: 'start',
|
||||
watch: false,
|
||||
// Source nvm before running
|
||||
exec_mode: 'cluster',
|
||||
instances: 1,
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
// This helps systemd find the right node
|
||||
NVM_DIR: '$HOME/.nvm',
|
||||
},
|
||||
// For systemd service, use a wrapper script
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
Or better: Create a wrapper script for PM2:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# bin/start.sh
|
||||
set -euo pipefail
|
||||
|
||||
# Source nvm if available
|
||||
if [ -s "$HOME/.nvm/nvm.sh" ]; then
|
||||
source "$HOME/.nvm/nvm.sh"
|
||||
fi
|
||||
|
||||
# Now start Officer
|
||||
NODE_ENV=production bun src/server.tsx
|
||||
```
|
||||
|
||||
Then in ecosystem.config.cjs:
|
||||
```javascript
|
||||
{
|
||||
name: 'officer',
|
||||
script: 'bin/start.sh',
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Fix 4: Validate Pi Installation
|
||||
|
||||
```bash
|
||||
# After installing Pi
|
||||
if has pi; then
|
||||
if pi --list-models > /dev/null 2>&1; then
|
||||
ok "pi installed and working"
|
||||
else
|
||||
warn "pi installed but --list-models failed"
|
||||
warn "Try: nvm use && npm install -g @mariozechner/pi-coding-agent"
|
||||
fi
|
||||
else
|
||||
warn "pi install failed"
|
||||
fi
|
||||
```
|
||||
|
||||
### Fix 5: Add Setup Documentation
|
||||
|
||||
Create `SETUP_GUIDE.md` with clear instructions on:
|
||||
1. Choose ONE Node installation method (recommend nvm)
|
||||
2. Source nvm in shell before running setup.sh
|
||||
3. Setup.sh will validate node and npm are available
|
||||
4. Services (PM2, systemd) will inherit nvm environment
|
||||
|
||||
---
|
||||
|
||||
## Recommendations
|
||||
|
||||
1. **Make nvm sourcing automatic** at the start of setup.sh
|
||||
2. **Add environment wrapper script** for PM2 that sources nvm
|
||||
3. **Document the three Node installation options** with pros/cons:
|
||||
- nvm (recommended, flexible versions)
|
||||
- NodeSource (system package, simple)
|
||||
- apt (if available in repo)
|
||||
- ❌ snap (broken, don't use)
|
||||
4. **Validate Pi works** before marking setup complete
|
||||
5. **Create a post-setup check script** that verifies everything works
|
||||
|
||||
---
|
||||
|
||||
## Current Workaround
|
||||
|
||||
If setup.sh already ran with snap node:
|
||||
1. Remove snap: `sudo snap remove node`
|
||||
2. Install nvm: `curl -o- ... | bash` (reload shell)
|
||||
3. Install node: `nvm install 22 && nvm use 22`
|
||||
4. Reinstall pm2 packages: `npm install -g pm2`
|
||||
5. Restart pm2/officer
|
||||
|
||||
This is what you just did!
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
# Officer Setup Guide
|
||||
|
||||
## Overview
|
||||
|
||||
Officer is a self-hosted AI intranet for teams. This guide covers fresh installation on Ubuntu/Debian servers.
|
||||
|
||||
## Architecture
|
||||
|
||||
Officer uses **system-wide Node.js** to ensure all users have consistent access to tools:
|
||||
|
||||
```
|
||||
System Node.js (v22, system-wide)
|
||||
↓
|
||||
npm global packages → /usr/local/lib/node_modules/
|
||||
├── pi (coding agent)
|
||||
├── claude (Claude Code)
|
||||
└── (available to all users)
|
||||
|
||||
Systemd Services
|
||||
├── officer-pty-sidecar (terminal backend)
|
||||
└── (other services)
|
||||
```
|
||||
|
||||
This is **not** an nvm-based setup. Each user doesn't install their own Node version. Instead:
|
||||
- One system Node.js for everyone
|
||||
- All npm packages installed system-wide
|
||||
- Simple, predictable, production-friendly
|
||||
|
||||
---
|
||||
|
||||
## Installation Steps
|
||||
|
||||
### 1. Install Node.js 22 (System-Wide)
|
||||
|
||||
**On fresh Ubuntu/Debian:**
|
||||
|
||||
```bash
|
||||
# Add NodeSource repository for Node 22 LTS
|
||||
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
|
||||
|
||||
# Install Node.js and npm
|
||||
sudo apt-get install -y nodejs
|
||||
|
||||
# Verify for all users
|
||||
node --version
|
||||
npm --version
|
||||
|
||||
# Both should work regardless of which user runs it
|
||||
```
|
||||
|
||||
**Already have an older Node version?**
|
||||
|
||||
```bash
|
||||
# upgrade it
|
||||
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
|
||||
# verify
|
||||
node --version # Should be v22.x.x
|
||||
```
|
||||
|
||||
**Coming from nvm?**
|
||||
|
||||
```bash
|
||||
# If you installed via nvm before, remove it
|
||||
# Then install system Node as above
|
||||
# nvm is NOT needed for Officer production setup
|
||||
```
|
||||
|
||||
### 2. Run Setup Script
|
||||
|
||||
```bash
|
||||
cd /path/to/officer/monorepo
|
||||
bash scripts/setup.sh
|
||||
```
|
||||
|
||||
This will:
|
||||
- Install all system dependencies (git, build tools, ffmpeg, etc.)
|
||||
- Install Bun, Go, Rust
|
||||
- Install npm global packages (pi, claude-code, etc.)
|
||||
- Setup PulseAudio for audio
|
||||
- Setup remote desktop (XFCE + VNC)
|
||||
- Configure PTY sidecar systemd service
|
||||
- Verify everything works
|
||||
|
||||
**Note:** Setup script uses `sudo` for system-level installations. You'll be prompted for your password.
|
||||
|
||||
### 3. Verify Installation
|
||||
|
||||
```bash
|
||||
# Everyone can use these (all users)
|
||||
which node # /usr/bin/node
|
||||
which pi # /usr/bin/pi
|
||||
which claude # /usr/bin/claude
|
||||
which bun # /home/user/.bun/bin/bun (or /usr/local/bin/bun)
|
||||
|
||||
# Test that pi works
|
||||
pi --version
|
||||
pi --list-models
|
||||
|
||||
# Test that Officer can start
|
||||
bun dev
|
||||
|
||||
# In another terminal
|
||||
curl http://localhost:5000/api/pi/models
|
||||
```
|
||||
|
||||
### 4. Start Officer
|
||||
|
||||
**Development:**
|
||||
```bash
|
||||
bun dev
|
||||
```
|
||||
|
||||
**Production (with PM2):**
|
||||
```bash
|
||||
pm2 start ecosystem.config.cjs
|
||||
pm2 logs officer
|
||||
```
|
||||
|
||||
**Production (with systemd):**
|
||||
```bash
|
||||
sudo systemctl start officer
|
||||
sudo systemctl status officer
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Multi-User Scenarios
|
||||
|
||||
### Scenario A: Server with Multiple Users
|
||||
|
||||
All users automatically get access to:
|
||||
- `node` command (system-wide)
|
||||
- `pi`, `claude` (system-wide npm packages)
|
||||
- Officer web UI (via port/proxy)
|
||||
|
||||
**No setup needed per user.** Just install globally once as shown above.
|
||||
|
||||
```bash
|
||||
# User 1
|
||||
$ which node
|
||||
/usr/bin/node
|
||||
|
||||
# User 2 (different terminal/server)
|
||||
$ which node
|
||||
/usr/bin/node
|
||||
|
||||
# Both work!
|
||||
```
|
||||
|
||||
### Scenario B: Development User Wants nvm
|
||||
|
||||
**Important:** For production, DON'T do this. But for dev, you can:
|
||||
|
||||
```bash
|
||||
# As a user (not system-wide)
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
|
||||
nvm install 22
|
||||
nvm use 22
|
||||
|
||||
# Now you can use nvm locally, but make sure system Node is also installed for services
|
||||
which node # /home/user/.nvm/versions/node/v22.x.x/bin/node (your local override)
|
||||
```
|
||||
|
||||
The `officer-pty-sidecar` systemd service will still use `/usr/bin/node` (system-wide).
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "node: command not found"
|
||||
|
||||
**For a single user:**
|
||||
```bash
|
||||
# User's shell doesn't have node in PATH
|
||||
# Make sure system Node is installed:
|
||||
sudo apt-get install -y nodejs
|
||||
node --version
|
||||
```
|
||||
|
||||
**For systemd service:**
|
||||
```bash
|
||||
# Service can't find node
|
||||
sudo systemctl status officer-pty-sidecar
|
||||
journalctl -u officer-pty-sidecar
|
||||
|
||||
# Fix: Install system Node
|
||||
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
|
||||
# Restart service
|
||||
sudo systemctl restart officer-pty-sidecar
|
||||
```
|
||||
|
||||
### "pi: command not found"
|
||||
|
||||
```bash
|
||||
# Pi not installed
|
||||
sudo npm install -g @mariozechner/pi-coding-agent
|
||||
|
||||
# Or use setup script
|
||||
bash scripts/setup.sh
|
||||
```
|
||||
|
||||
### "Pi process exited with code 1" in chat
|
||||
|
||||
This usually means **snap node was used**. Don't use snap node for Officer.
|
||||
|
||||
```bash
|
||||
# Check if snap node is installed
|
||||
which node
|
||||
# If output contains "/snap/bin/node", remove it:
|
||||
|
||||
sudo snap remove node
|
||||
|
||||
# Then install system Node
|
||||
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
|
||||
# Verify
|
||||
which node # /usr/bin/node (NOT /snap/bin/node)
|
||||
```
|
||||
|
||||
### PTY Sidecar service won't start
|
||||
|
||||
```bash
|
||||
# Check status
|
||||
sudo systemctl status officer-pty-sidecar
|
||||
|
||||
# Check logs
|
||||
journalctl -u officer-pty-sidecar -n 20
|
||||
|
||||
# Verify Node is installed
|
||||
/usr/bin/node --version
|
||||
|
||||
# Restart
|
||||
sudo systemctl restart officer-pty-sidecar
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Files Changed/Created
|
||||
|
||||
- `scripts/setup.sh` — Main installation script (updated to use system Node)
|
||||
- `scripts/setup-pty-sidecar.sh` — PTY sidecar systemd service setup (updated)
|
||||
- `ecosystem.config.cjs` — PM2 config (no changes, using systemd instead)
|
||||
- `SETUP_GUIDE.md` — This file
|
||||
|
||||
---
|
||||
|
||||
## What NOT To Do
|
||||
|
||||
❌ **Don't use snap node**
|
||||
- It has file descriptor issues with piped processes
|
||||
- Use system Node via NodeSource instead
|
||||
|
||||
❌ **Don't mix Node versions per user**
|
||||
- For production: one Node version for everyone (system-wide)
|
||||
- For dev: sure, use nvm, but keep system Node installed too
|
||||
|
||||
❌ **Don't install npm packages locally for system services**
|
||||
- Services need system-wide packages (`sudo npm install -g`)
|
||||
- Or use absolute paths in service files
|
||||
|
||||
✅ **Do:**
|
||||
- Use system Node 22 via NodeSource
|
||||
- Install global packages once with `sudo npm install -g`
|
||||
- Use systemd services for daemons
|
||||
- Let all users share the same tools
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Install Node.js: `curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - && sudo apt-get install -y nodejs`
|
||||
2. Run setup: `bash scripts/setup.sh`
|
||||
3. Start Officer: `bun dev` (or PM2/systemd in production)
|
||||
4. Open browser: `http://localhost:5000` (or your configured port)
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
If you hit issues:
|
||||
1. Check troubleshooting section above
|
||||
2. Verify system Node is installed: `node --version`
|
||||
3. Check service logs: `journalctl -u officer-pty-sidecar`
|
||||
4. Re-run setup script: `bash scripts/setup.sh`
|
||||
@@ -18,14 +18,24 @@ SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||
SERVICE_USER="$(whoami)"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
NODE_BIN="$(which node)"
|
||||
|
||||
# Use system node (not nvm) for systemd service
|
||||
NODE_BIN="/usr/bin/node"
|
||||
|
||||
if [ ! -f "$NODE_BIN" ]; then
|
||||
fail "System Node.js not found at $NODE_BIN"
|
||||
fail "Install Node.js 22 via NodeSource:"
|
||||
fail " curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -"
|
||||
fail " sudo apt-get install -y nodejs"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "── PTY Sidecar (systemd service) ──"
|
||||
echo " Node: $NODE_BIN ($("$NODE_BIN" -v))"
|
||||
|
||||
if systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then
|
||||
skip "$SERVICE_NAME service already running"
|
||||
echo " Use 'bun run restart:sidecar' to restart"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
+69
-62
@@ -260,60 +260,66 @@ fi
|
||||
|
||||
# ─── 5. Node.js 22 ────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "── Node.js 22 ──"
|
||||
echo "── Node.js 22 (system-wide) ──"
|
||||
|
||||
if has node; then
|
||||
NODE_VER=$(node -v 2>/dev/null | tr -d 'v')
|
||||
NODE_MAJOR=$(echo "$NODE_VER" | cut -d. -f1)
|
||||
if [ "$NODE_MAJOR" = "22" ]; then
|
||||
skip "node v$NODE_VER"
|
||||
skip "node v$NODE_VER (system-wide)"
|
||||
else
|
||||
warn "Node $NODE_VER found but v22 is required"
|
||||
warn "Use nvm: nvm install 22 && nvm use 22"
|
||||
warn "Node $NODE_VER found but v22 is required — upgrading via NodeSource..."
|
||||
case $PM in
|
||||
apt)
|
||||
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
if has node && [ "$(node -v | cut -d. -f1 | tr -d 'v')" = "22" ]; then
|
||||
ok "Upgraded to node v$(node -v)"
|
||||
else
|
||||
fail "Node.js upgrade failed"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
warn "Manual upgrade needed for $PM: install Node.js 22 from official sources"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
else
|
||||
warn "Node.js not found — install v22 via nvm:"
|
||||
warn " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash"
|
||||
warn " nvm install 22"
|
||||
warn "Node.js not found — installing v22 via NodeSource..."
|
||||
case $PM in
|
||||
apt)
|
||||
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
if has node; then ok "node v$(node -v) installed"; else fail "node install failed"; fi
|
||||
;;
|
||||
pacman)
|
||||
install_pkg nodejs npm
|
||||
if has node; then ok "node v$(node -v) installed"; else fail "node install failed"; fi
|
||||
;;
|
||||
brew)
|
||||
install_pkg node
|
||||
if has node; then ok "node v$(node -v) installed"; else fail "node install failed"; fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# ─── npm global prefix ─────────────────────────────────────────────────────────
|
||||
# Set npm global prefix to a user-writable directory so npm install -g
|
||||
# never requires sudo. This also lets the server install packages at runtime.
|
||||
# ─── npm global packages (system-wide) ─────────────────────────────────────────
|
||||
# Install npm global packages to system /usr/local so all users can access them.
|
||||
# This is safer for multi-user production setups.
|
||||
echo ""
|
||||
echo "── npm global prefix ──"
|
||||
echo "── npm global packages (system-wide) ──"
|
||||
|
||||
if has npm; then
|
||||
NPM_PREFIX=$(npm config get prefix 2>/dev/null)
|
||||
NPM_GLOBAL="$HOME/.npm-global"
|
||||
|
||||
if [ "$NPM_PREFIX" = "$NPM_GLOBAL" ]; then
|
||||
skip "npm prefix already set to $NPM_GLOBAL"
|
||||
else
|
||||
mkdir -p "$NPM_GLOBAL"
|
||||
npm config set prefix "$NPM_GLOBAL"
|
||||
export PATH="$NPM_GLOBAL/bin:$PATH"
|
||||
ok "Set npm global prefix to $NPM_GLOBAL"
|
||||
|
||||
# Migrate existing global packages from system prefix if any
|
||||
if [ -d "$NPM_PREFIX/lib/node_modules/@mariozechner" ] || [ -d "$NPM_PREFIX/lib/node_modules/@anthropic-ai" ]; then
|
||||
warn "Removing stale system-level npm packages from $NPM_PREFIX (will reinstall to $NPM_GLOBAL)"
|
||||
sudo rm -rf "$NPM_PREFIX/lib/node_modules/@mariozechner" "$NPM_PREFIX/lib/node_modules/@anthropic-ai" 2>/dev/null
|
||||
sudo rm -f "$NPM_PREFIX/bin/pi" "$NPM_PREFIX/bin/claude" 2>/dev/null
|
||||
fi
|
||||
if ! has npm; then
|
||||
warn "npm not found — skipping global package installs"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure ~/.npm-global/bin is in shell profiles
|
||||
for profile in "$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.profile"; do
|
||||
if [ -f "$profile" ] && ! grep -q '.npm-global/bin' "$profile"; then
|
||||
echo '' >> "$profile"
|
||||
echo '# npm global packages' >> "$profile"
|
||||
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> "$profile"
|
||||
ok "Added ~/.npm-global/bin to $(basename "$profile")"
|
||||
fi
|
||||
done
|
||||
else
|
||||
warn "npm not found — skipping prefix setup"
|
||||
# Verify npm is pointing to system node (not user-local nvm)
|
||||
NPM_BIN=$(which npm)
|
||||
if echo "$NPM_BIN" | grep -q '.nvm'; then
|
||||
warn "npm is from nvm ($NPM_BIN), but setup.sh expects system npm"
|
||||
warn "This is fine — packages will be installed to your nvm, but won't be available to other users"
|
||||
warn "For production: all users should use the same system Node.js (no nvm)"
|
||||
fi
|
||||
|
||||
# ─── 6. Bun ───────────────────────────────────────────────────────────────────
|
||||
@@ -557,35 +563,36 @@ else
|
||||
esac
|
||||
fi
|
||||
|
||||
# ─── 14. npm global packages ─────────────────────────────────────────────────
|
||||
# ─── 14. npm global packages (system-wide) ──────────────────────────────────
|
||||
echo ""
|
||||
echo "── npm global packages ──"
|
||||
echo "── npm global packages (system-wide) ──"
|
||||
|
||||
if has npm; then
|
||||
if ! has npm; then
|
||||
warn "npm not found — skipping global package installs"
|
||||
else
|
||||
# Pi (coding agent)
|
||||
if has pi; then
|
||||
skip "pi (@mariozechner/pi-coding-agent)"
|
||||
else
|
||||
npm install -g @mariozechner/pi-coding-agent
|
||||
echo " Installing pi..."
|
||||
sudo npm install -g @mariozechner/pi-coding-agent
|
||||
if has pi; then ok "pi installed"; else warn "pi install failed"; fi
|
||||
fi
|
||||
|
||||
# Fix ~/.pi ownership (previous installs via sudo may have created root-owned dirs)
|
||||
if [ -d "$HOME/.pi" ]; then
|
||||
if find "$HOME/.pi" -not -user "$USER" -print -quit 2>/dev/null | grep -q .; then
|
||||
sudo chown -R "$USER:$(id -gn)" "$HOME/.pi"
|
||||
ok "Fixed ~/.pi ownership"
|
||||
# Validate Pi works
|
||||
if has pi; then
|
||||
if pi --list-models > /dev/null 2>&1; then
|
||||
ok "pi validated (--list-models works)"
|
||||
else
|
||||
warn "pi installed but --list-models failed — check API keys in ~/.pi/agent/auth.json"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Patch Pi compaction bug: calculateContextTokens crashes when usage is undefined
|
||||
PI_COMPACTION="$(npm root -g 2>/dev/null)/@mariozechner/pi-coding-agent/dist/core/compaction/compaction.js"
|
||||
if [ -f "$PI_COMPACTION" ]; then
|
||||
if grep -q "if (!usage) return 0;" "$PI_COMPACTION" 2>/dev/null; then
|
||||
skip "pi compaction patch (already applied)"
|
||||
else
|
||||
sed -i '/^export function calculateContextTokens(usage) {$/a\ if (!usage) return 0;' "$PI_COMPACTION"
|
||||
ok "Applied pi compaction bug patch"
|
||||
# Fix ~/.pi ownership if needed (in case of mixed user/sudo installs)
|
||||
if [ -d "$HOME/.pi" ]; then
|
||||
if find "$HOME/.pi" -not -user "$USER" -print -quit 2>/dev/null | grep -q .; then
|
||||
sudo chown -R "$USER:$(id -gn)" "$HOME/.pi"
|
||||
ok "Fixed ~/.pi ownership to $USER"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -593,16 +600,16 @@ if has npm; then
|
||||
if has claude; then
|
||||
skip "claude (@anthropic-ai/claude-code)"
|
||||
else
|
||||
npm install -g @anthropic-ai/claude-code
|
||||
echo " Installing claude-code..."
|
||||
sudo npm install -g @anthropic-ai/claude-code
|
||||
if has claude; then ok "claude-code installed"; else warn "claude-code install failed"; fi
|
||||
fi
|
||||
|
||||
# pm2 (process manager)
|
||||
# pm2 (process manager) — now optional since we use systemd services
|
||||
if has pm2; then
|
||||
skip "pm2"
|
||||
skip "pm2 (optional, using systemd services instead)"
|
||||
else
|
||||
npm install -g pm2
|
||||
if has pm2; then ok "pm2 installed"; else warn "pm2 install failed"; fi
|
||||
warn "pm2 optional (Officer uses systemd services for main app and sidecar)"
|
||||
fi
|
||||
else
|
||||
warn "npm not found — skipping global package installs"
|
||||
|
||||
Reference in New Issue
Block a user