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)"
|
SERVICE_USER="$(whoami)"
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
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 ""
|
||||||
echo "── PTY Sidecar (systemd service) ──"
|
echo "── PTY Sidecar (systemd service) ──"
|
||||||
|
echo " Node: $NODE_BIN ($("$NODE_BIN" -v))"
|
||||||
|
|
||||||
if systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then
|
if systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then
|
||||||
skip "$SERVICE_NAME service already running"
|
skip "$SERVICE_NAME service already running"
|
||||||
echo " Use 'bun run restart:sidecar' to restart"
|
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
+70
-63
@@ -260,60 +260,66 @@ fi
|
|||||||
|
|
||||||
# ─── 5. Node.js 22 ────────────────────────────────────────────────────────────
|
# ─── 5. Node.js 22 ────────────────────────────────────────────────────────────
|
||||||
echo ""
|
echo ""
|
||||||
echo "── Node.js 22 ──"
|
echo "── Node.js 22 (system-wide) ──"
|
||||||
|
|
||||||
if has node; then
|
if has node; then
|
||||||
NODE_VER=$(node -v 2>/dev/null | tr -d 'v')
|
NODE_VER=$(node -v 2>/dev/null | tr -d 'v')
|
||||||
NODE_MAJOR=$(echo "$NODE_VER" | cut -d. -f1)
|
NODE_MAJOR=$(echo "$NODE_VER" | cut -d. -f1)
|
||||||
if [ "$NODE_MAJOR" = "22" ]; then
|
if [ "$NODE_MAJOR" = "22" ]; then
|
||||||
skip "node v$NODE_VER"
|
skip "node v$NODE_VER (system-wide)"
|
||||||
else
|
else
|
||||||
warn "Node $NODE_VER found but v22 is required"
|
warn "Node $NODE_VER found but v22 is required — upgrading via NodeSource..."
|
||||||
warn "Use nvm: nvm install 22 && nvm use 22"
|
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
|
fi
|
||||||
else
|
else
|
||||||
warn "Node.js not found — install v22 via nvm:"
|
warn "Node.js not found — installing v22 via NodeSource..."
|
||||||
warn " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash"
|
case $PM in
|
||||||
warn " nvm install 22"
|
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
|
fi
|
||||||
|
|
||||||
# ─── npm global prefix ─────────────────────────────────────────────────────────
|
# ─── npm global packages (system-wide) ─────────────────────────────────────────
|
||||||
# Set npm global prefix to a user-writable directory so npm install -g
|
# Install npm global packages to system /usr/local so all users can access them.
|
||||||
# never requires sudo. This also lets the server install packages at runtime.
|
# This is safer for multi-user production setups.
|
||||||
echo ""
|
echo ""
|
||||||
echo "── npm global prefix ──"
|
echo "── npm global packages (system-wide) ──"
|
||||||
|
|
||||||
if has npm; then
|
if ! has npm; then
|
||||||
NPM_PREFIX=$(npm config get prefix 2>/dev/null)
|
warn "npm not found — skipping global package installs"
|
||||||
NPM_GLOBAL="$HOME/.npm-global"
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$NPM_PREFIX" = "$NPM_GLOBAL" ]; then
|
# Verify npm is pointing to system node (not user-local nvm)
|
||||||
skip "npm prefix already set to $NPM_GLOBAL"
|
NPM_BIN=$(which npm)
|
||||||
else
|
if echo "$NPM_BIN" | grep -q '.nvm'; then
|
||||||
mkdir -p "$NPM_GLOBAL"
|
warn "npm is from nvm ($NPM_BIN), but setup.sh expects system npm"
|
||||||
npm config set prefix "$NPM_GLOBAL"
|
warn "This is fine — packages will be installed to your nvm, but won't be available to other users"
|
||||||
export PATH="$NPM_GLOBAL/bin:$PATH"
|
warn "For production: all users should use the same system Node.js (no nvm)"
|
||||||
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
|
|
||||||
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"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ─── 6. Bun ───────────────────────────────────────────────────────────────────
|
# ─── 6. Bun ───────────────────────────────────────────────────────────────────
|
||||||
@@ -557,35 +563,36 @@ else
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ─── 14. npm global packages ─────────────────────────────────────────────────
|
# ─── 14. npm global packages (system-wide) ──────────────────────────────────
|
||||||
echo ""
|
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)
|
# Pi (coding agent)
|
||||||
if has pi; then
|
if has pi; then
|
||||||
skip "pi (@mariozechner/pi-coding-agent)"
|
skip "pi (@mariozechner/pi-coding-agent)"
|
||||||
else
|
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
|
if has pi; then ok "pi installed"; else warn "pi install failed"; fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Fix ~/.pi ownership (previous installs via sudo may have created root-owned dirs)
|
# Validate Pi works
|
||||||
if [ -d "$HOME/.pi" ]; then
|
if has pi; then
|
||||||
if find "$HOME/.pi" -not -user "$USER" -print -quit 2>/dev/null | grep -q .; then
|
if pi --list-models > /dev/null 2>&1; then
|
||||||
sudo chown -R "$USER:$(id -gn)" "$HOME/.pi"
|
ok "pi validated (--list-models works)"
|
||||||
ok "Fixed ~/.pi ownership"
|
else
|
||||||
|
warn "pi installed but --list-models failed — check API keys in ~/.pi/agent/auth.json"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Patch Pi compaction bug: calculateContextTokens crashes when usage is undefined
|
# Fix ~/.pi ownership if needed (in case of mixed user/sudo installs)
|
||||||
PI_COMPACTION="$(npm root -g 2>/dev/null)/@mariozechner/pi-coding-agent/dist/core/compaction/compaction.js"
|
if [ -d "$HOME/.pi" ]; then
|
||||||
if [ -f "$PI_COMPACTION" ]; then
|
if find "$HOME/.pi" -not -user "$USER" -print -quit 2>/dev/null | grep -q .; then
|
||||||
if grep -q "if (!usage) return 0;" "$PI_COMPACTION" 2>/dev/null; then
|
sudo chown -R "$USER:$(id -gn)" "$HOME/.pi"
|
||||||
skip "pi compaction patch (already applied)"
|
ok "Fixed ~/.pi ownership to $USER"
|
||||||
else
|
|
||||||
sed -i '/^export function calculateContextTokens(usage) {$/a\ if (!usage) return 0;' "$PI_COMPACTION"
|
|
||||||
ok "Applied pi compaction bug patch"
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -593,16 +600,16 @@ if has npm; then
|
|||||||
if has claude; then
|
if has claude; then
|
||||||
skip "claude (@anthropic-ai/claude-code)"
|
skip "claude (@anthropic-ai/claude-code)"
|
||||||
else
|
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
|
if has claude; then ok "claude-code installed"; else warn "claude-code install failed"; fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# pm2 (process manager)
|
# pm2 (process manager) — now optional since we use systemd services
|
||||||
if has pm2; then
|
if has pm2; then
|
||||||
skip "pm2"
|
skip "pm2 (optional, using systemd services instead)"
|
||||||
else
|
else
|
||||||
npm install -g pm2
|
warn "pm2 optional (Officer uses systemd services for main app and sidecar)"
|
||||||
if has pm2; then ok "pm2 installed"; else warn "pm2 install failed"; fi
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
warn "npm not found — skipping global package installs"
|
warn "npm not found — skipping global package installs"
|
||||||
|
|||||||
Reference in New Issue
Block a user