better startup scripts

This commit is contained in:
2026-03-04 18:05:50 +00:00
parent 98a6a69477
commit ba36629cdc
3 changed files with 222 additions and 124 deletions
+10 -9
View File
@@ -36,11 +36,6 @@ echo ""
echo "── PTY Sidecar (systemd service) ──" echo "── PTY Sidecar (systemd service) ──"
echo " Node: $NODE_BIN ($("$NODE_BIN" -v))" echo " Node: $NODE_BIN ($("$NODE_BIN" -v))"
if systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then
skip "$SERVICE_NAME service already running"
exit 0
fi
sudo tee "$SERVICE_FILE" > /dev/null << EOF sudo tee "$SERVICE_FILE" > /dev/null << EOF
[Unit] [Unit]
Description=Officer PTY Sidecar Description=Officer PTY Sidecar
@@ -60,10 +55,16 @@ WantedBy=multi-user.target
EOF EOF
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl enable --now "$SERVICE_NAME" sudo systemctl enable "$SERVICE_NAME"
if systemctl is-active --quiet "$SERVICE_NAME"; then if systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null; then
ok "$SERVICE_NAME service installed and running" sudo systemctl restart "$SERVICE_NAME"
ok "$SERVICE_NAME service updated and restarted"
else else
warn "$SERVICE_NAME service failed to start — check 'journalctl -u $SERVICE_NAME'" sudo systemctl start "$SERVICE_NAME"
if systemctl is-active --quiet "$SERVICE_NAME"; then
ok "$SERVICE_NAME service installed and running"
else
warn "$SERVICE_NAME service failed to start — check 'journalctl -u $SERVICE_NAME'"
fi
fi fi
+167 -115
View File
@@ -95,6 +95,15 @@ if has make && has gcc; then skip "build tools (make, gcc, g++)"; else
esac esac
fi fi
# pkg-config — needed by cgo-based Go packages (e.g. ebitengine/oto for cliamp)
if has pkg-config; then skip "pkg-config"; else
case $PM in
apt) CORE_PKGS+=(pkg-config) ;;
pacman) CORE_PKGS+=(pkgconf) ;;
brew) CORE_PKGS+=(pkg-config) ;;
esac
fi
# python3 + pip + venv # python3 + pip + venv
if has python3; then skip "python3"; else if has python3; then skip "python3"; else
case $PM in case $PM in
@@ -270,130 +279,139 @@ else
skip "sleep targets already masked" skip "sleep targets already masked"
fi fi
# Configure logind to ignore idle # Configure logind to ignore idle — patch individual keys, don't overwrite the file
sudo tee /etc/systemd/logind.conf > /dev/null << 'LOGIND_EOF' set_logind_key() {
[Login] local key="$1" val="$2" file="/etc/systemd/logind.conf"
HandleLidSwitch=ignore if grep -qE "^${key}=" "$file" 2>/dev/null; then
HandleLidSwitchExternalPower=ignore sudo sed -i "s|^${key}=.*|${key}=${val}|" "$file"
HandlePowerKey=ignore elif grep -qE "^#${key}=" "$file" 2>/dev/null; then
IdleAction=none sudo sed -i "s|^#${key}=.*|${key}=${val}|" "$file"
RuntimeDirectorySize=10% else
LOGIND_EOF echo "${key}=${val}" | sudo tee -a "$file" > /dev/null
fi
}
set_logind_key HandleLidSwitch ignore
set_logind_key HandleLidSwitchExternalPower ignore
set_logind_key HandlePowerKey ignore
set_logind_key IdleAction none
set_logind_key RuntimeDirectorySize 10%
sudo systemctl restart systemd-logind sudo systemctl restart systemd-logind
ok "Configured logind to disable auto-suspend" ok "Configured logind to disable auto-suspend"
# ─── 5. Node.js 22 ──────────────────────────────────────────────────────────── # ─── 5. Node.js 22 (system-wide) ─────────────────────────────────────────────
echo "" echo ""
echo "── Node.js 22 (system-wide) ──" echo "── Node.js 22 (system-wide) ──"
if has node; then # Always check the canonical system path, not `which node` (which may resolve nvm).
NODE_VER=$(node -v 2>/dev/null | tr -d 'v') SYSTEM_NODE="/usr/bin/node"
NODE_MAJOR=$(echo "$NODE_VER" | cut -d. -f1)
if [ "$NODE_MAJOR" = "22" ]; then install_node22_apt() {
skip "node v$NODE_VER (system-wide)" echo " Setting up NodeSource repository..."
else curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
warn "Node $NODE_VER found but v22 is required — upgrading..." echo " Installing nodejs..."
case $PM in sudo apt-get install -y nodejs
apt) }
echo " Setting up NodeSource repository..."
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - case $PM in
sudo apt-get install -y nodejs apt)
if has node && [ "$(node -v | cut -d. -f1 | tr -d 'v')" = "22" ]; then NEED_INSTALL=0
ok "Upgraded to node v$(node -v)" if [ -f "$SYSTEM_NODE" ]; then
else SYS_MAJOR=$("$SYSTEM_NODE" -v 2>/dev/null | cut -d. -f1 | tr -d 'v')
fail "Node.js upgrade failed" if [ "$SYS_MAJOR" = "22" ]; then
skip "node v$("$SYSTEM_NODE" -v) (system-wide at $SYSTEM_NODE)"
else
warn "System node v$("$SYSTEM_NODE" -v) at $SYSTEM_NODE is not v22 — upgrading..."
NEED_INSTALL=1
fi
else
if has node; then
warn "node found at $(which node) (not system-wide, likely nvm) — installing Node 22 system-wide..."
else
echo " Node.js not found — installing v22..."
fi
NEED_INSTALL=1
fi
if [ "$NEED_INSTALL" = "1" ]; then
install_node22_apt
if [ -f "$SYSTEM_NODE" ] && [ "$("$SYSTEM_NODE" -v 2>/dev/null | cut -d. -f1 | tr -d 'v')" = "22" ]; then
ok "node v$("$SYSTEM_NODE" -v) installed at $SYSTEM_NODE"
if has node && [ "$(which node)" != "$SYSTEM_NODE" ]; then
warn "Shell resolves 'node' to $(which node) — system node is at $SYSTEM_NODE"
warn "nvm may shadow it in interactive shells; systemd services will use $SYSTEM_NODE"
fi fi
;; else
pacman) fail "Node.js 22 install failed — $SYSTEM_NODE not found or wrong version"
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
else
echo " Node.js not found — installing v22..."
case $PM in
apt)
echo " Setting up NodeSource repository (this may take a moment)..."
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
echo " Installing nodejs from apt..."
sudo apt-get install -y nodejs
if has node; then
ok "node v$(node -v) installed from NodeSource"
else
fail "Node.js install failed"
exit 1 exit 1
fi fi
;; fi
pacman) ;;
echo " Installing nodejs from pacman..." pacman)
if has node && [ "$(node -v 2>/dev/null | cut -d. -f1 | tr -d 'v')" = "22" ]; then
skip "node v$(node -v)"
else
install_pkg nodejs npm install_pkg nodejs npm
if has node; then ok "node v$(node -v) installed"; else fail "node install failed"; exit 1; fi if has node; then ok "node v$(node -v) installed"; else fail "node install failed"; exit 1; fi
;; fi
brew) ;;
echo " Installing node from brew..." brew)
if has node && [ "$(node -v 2>/dev/null | cut -d. -f1 | tr -d 'v')" = "22" ]; then
skip "node v$(node -v)"
else
install_pkg node install_pkg node
if has node; then ok "node v$(node -v) installed"; else fail "node install failed"; exit 1; fi if has node; then ok "node v$(node -v) installed"; else fail "node install failed"; exit 1; fi
;; fi
esac ;;
fi esac
# ─── 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 packages (system-wide) ──"
if ! has npm; then
warn "npm not found — skipping global package installs"
exit 1
fi
# 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 ─────────────────────────────────────────────────────────────────── # ─── 6. Bun ───────────────────────────────────────────────────────────────────
echo "" echo ""
echo "── Bun ──" echo "── Bun ──"
if has bun; then export BUN_INSTALL="$HOME/.bun"
export PATH="$BUN_INSTALL/bin:$PATH"
if [ -f "$BUN_INSTALL/bin/bun" ]; then
skip "bun ($(bun --version 2>/dev/null))" skip "bun ($(bun --version 2>/dev/null))"
else else
curl -fsSL https://bun.sh/install | bash curl -fsSL https://bun.sh/install | bash
export BUN_INSTALL="$HOME/.bun" if [ ! -f "$BUN_INSTALL/bin/bun" ]; then fail "bun install failed"; exit 1; fi
export PATH="$BUN_INSTALL/bin:$PATH" ok "bun $(bun --version) installed"
if has bun; then ok "bun installed"; else fail "bun install failed"; fi fi
# Symlink to system-wide path so all users and systemd services can access it
if [ ! -L /usr/local/bin/bun ] || [ "$(readlink /usr/local/bin/bun)" != "$BUN_INSTALL/bin/bun" ]; then
sudo ln -sf "$BUN_INSTALL/bin/bun" /usr/local/bin/bun
ok "bun symlinked to /usr/local/bin/bun"
else
skip "bun symlink at /usr/local/bin/bun"
fi fi
# ─── 7. Go ───────────────────────────────────────────────────────────────────── # ─── 7. Go ─────────────────────────────────────────────────────────────────────
echo "" echo ""
echo "── Go ──" echo "── Go ──"
GOLANG_VERSION=1.23.6 echo " Fetching latest Go version..."
GOLANG_VERSION=$(curl -fsSL "https://go.dev/dl/?mode=json" | jq -r '.[0].version' | tr -d 'go')
if [ -z "$GOLANG_VERSION" ]; then
warn "Could not fetch latest Go version — falling back to 1.23.6"
GOLANG_VERSION=1.23.6
fi
echo " Latest Go: $GOLANG_VERSION"
if has go; then install_go() {
skip "go ($(go version 2>/dev/null | awk '{print $3}'))"
else
case $PM in case $PM in
apt|pacman) apt|pacman)
echo " Installing Go ${GOLANG_VERSION} from official tarball..."
ARCH=$(uname -m) ARCH=$(uname -m)
case $ARCH in case $ARCH in
x86_64) GO_ARCH=amd64 ;; x86_64) GO_ARCH=amd64 ;;
aarch64) GO_ARCH=arm64 ;; aarch64) GO_ARCH=arm64 ;;
*) GO_ARCH=amd64 ;; *) GO_ARCH=amd64 ;;
esac esac
echo " Installing Go ${GOLANG_VERSION} from official tarball..."
curl -fsSL "https://go.dev/dl/go${GOLANG_VERSION}.linux-${GO_ARCH}.tar.gz" -o /tmp/go.tar.gz curl -fsSL "https://go.dev/dl/go${GOLANG_VERSION}.linux-${GO_ARCH}.tar.gz" -o /tmp/go.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf /tmp/go.tar.gz sudo tar -C /usr/local -xzf /tmp/go.tar.gz
rm /tmp/go.tar.gz rm /tmp/go.tar.gz
export PATH="/usr/local/go/bin:$PATH" export PATH="/usr/local/go/bin:$PATH"
@@ -402,7 +420,20 @@ else
brew install go brew install go
;; ;;
esac esac
if has go; then ok "go installed"; else warn "go not found — install manually from https://go.dev/dl/"; fi }
if has go; then
INSTALLED_GO=$(go version 2>/dev/null | awk '{print $3}' | tr -d 'go')
if [ "$INSTALLED_GO" = "$GOLANG_VERSION" ]; then
skip "go $INSTALLED_GO"
else
warn "go $INSTALLED_GO installed but latest is $GOLANG_VERSION — upgrading..."
install_go
if has go; then ok "go $(go version | awk '{print $3}') installed"; else warn "go upgrade failed"; fi
fi
else
install_go
if has go; then ok "go $(go version | awk '{print $3}') installed"; else warn "go not found — install manually from https://go.dev/dl/"; fi
fi fi
# ─── 8. Rust ────────────────────────────────────────────────────────────────── # ─── 8. Rust ──────────────────────────────────────────────────────────────────
@@ -531,6 +562,11 @@ else
if has starship; then ok "starship installed"; else warn "starship install failed"; fi if has starship; then ok "starship installed"; else warn "starship install failed"; fi
fi fi
# Deploy starship config
mkdir -p "$HOME/.config"
cp "$SCRIPT_DIR/starship.toml" "$HOME/.config/starship.toml"
ok "starship config deployed"
# Oh-My-Zsh # Oh-My-Zsh
if [ -d "$HOME/.oh-my-zsh" ]; then if [ -d "$HOME/.oh-my-zsh" ]; then
skip "oh-my-zsh (already at ~/.oh-my-zsh)" skip "oh-my-zsh (already at ~/.oh-my-zsh)"
@@ -540,23 +576,26 @@ else
fi fi
# eza # eza
EZA_VERSION=0.18.15
if has eza; then if has eza; then
skip "eza" skip "eza"
else else
case $PM in case $PM in
apt) apt)
ARCH=$(uname -m) echo " Fetching latest eza version..."
case $ARCH in EZA_VERSION=$(curl -fsSL "https://api.github.com/repos/eza-community/eza/releases/latest" | jq -r '.tag_name' | tr -d 'v')
x86_64) EZA_ARCH=x86_64 ;; if [ -z "$EZA_VERSION" ]; then warn "Could not fetch eza version — skipping"; else
aarch64) EZA_ARCH=aarch64 ;; ARCH=$(uname -m)
*) EZA_ARCH=x86_64 ;; case $ARCH in
esac x86_64) EZA_ARCH=x86_64 ;;
curl -fsSL "https://github.com/eza-community/eza/releases/download/v${EZA_VERSION}/eza_${EZA_ARCH}-unknown-linux-gnu.tar.gz" -o /tmp/eza.tar.gz aarch64) EZA_ARCH=aarch64 ;;
tar -xzf /tmp/eza.tar.gz -C /tmp *) EZA_ARCH=x86_64 ;;
sudo mv /tmp/eza /usr/local/bin/eza esac
sudo chmod +x /usr/local/bin/eza curl -fsSL "https://github.com/eza-community/eza/releases/download/v${EZA_VERSION}/eza_${EZA_ARCH}-unknown-linux-gnu.tar.gz" -o /tmp/eza.tar.gz
rm -rf /tmp/eza.tar.gz tar -xzf /tmp/eza.tar.gz -C /tmp
sudo mv /tmp/eza /usr/local/bin/eza
sudo chmod +x /usr/local/bin/eza
rm -f /tmp/eza.tar.gz
fi
;; ;;
pacman) install_pkg eza ;; pacman) install_pkg eza ;;
brew) install_pkg eza ;; brew) install_pkg eza ;;
@@ -565,23 +604,26 @@ else
fi fi
# lazygit # lazygit
LAZYGIT_VERSION=0.44.1
if has lazygit; then if has lazygit; then
skip "lazygit" skip "lazygit"
else else
case $PM in case $PM in
apt) apt)
ARCH=$(uname -m) echo " Fetching latest lazygit version..."
case $ARCH in LAZYGIT_VERSION=$(curl -fsSL "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" | jq -r '.tag_name' | tr -d 'v')
x86_64) LG_ARCH=x86_64 ;; if [ -z "$LAZYGIT_VERSION" ]; then warn "Could not fetch lazygit version — skipping"; else
aarch64) LG_ARCH=arm64 ;; ARCH=$(uname -m)
*) LG_ARCH=x86_64 ;; case $ARCH in
esac x86_64) LG_ARCH=x86_64 ;;
curl -fsSL "https://github.com/jesseduffield/lazygit/releases/download/v${LAZYGIT_VERSION}/lazygit_${LAZYGIT_VERSION}_Linux_${LG_ARCH}.tar.gz" -o /tmp/lazygit.tar.gz aarch64) LG_ARCH=arm64 ;;
tar -xzf /tmp/lazygit.tar.gz -C /tmp *) LG_ARCH=x86_64 ;;
sudo mv /tmp/lazygit /usr/local/bin/lazygit esac
sudo chmod +x /usr/local/bin/lazygit curl -fsSL "https://github.com/jesseduffield/lazygit/releases/download/v${LAZYGIT_VERSION}/lazygit_${LAZYGIT_VERSION}_Linux_${LG_ARCH}.tar.gz" -o /tmp/lazygit.tar.gz
rm -rf /tmp/lazygit.tar.gz /tmp/LICENSE /tmp/README.md tar -xzf /tmp/lazygit.tar.gz -C /tmp
sudo mv /tmp/lazygit /usr/local/bin/lazygit
sudo chmod +x /usr/local/bin/lazygit
rm -f /tmp/lazygit.tar.gz /tmp/LICENSE /tmp/README.md
fi
;; ;;
pacman) install_pkg lazygit ;; pacman) install_pkg lazygit ;;
brew) install_pkg lazygit ;; brew) install_pkg lazygit ;;
@@ -620,7 +662,12 @@ else
echo " Configuring npm global prefix to /usr/local..." echo " Configuring npm global prefix to /usr/local..."
sudo "$SYSTEM_NPM" config set prefix /usr/local sudo "$SYSTEM_NPM" config set prefix /usr/local
ok "npm prefix set to /usr/local" ok "npm prefix set to /usr/local"
# Update npm itself to latest
echo " Updating npm to latest..."
sudo "$SYSTEM_NPM" install -g npm@latest
ok "npm updated to v$(npm --version 2>/dev/null)"
# 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)"
@@ -656,11 +703,13 @@ else
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) — now optional since we use systemd services # pm2 (process manager)
if has pm2; then if has pm2; then
skip "pm2 (optional, using systemd services instead)" skip "pm2"
else else
warn "pm2 optional (Officer uses systemd services for main app and sidecar)" echo " Installing pm2..."
sudo "$SYSTEM_NPM" install -g pm2
if has pm2; then ok "pm2 installed"; else warn "pm2 install failed"; fi
fi fi
fi fi
@@ -752,6 +801,9 @@ echo ""
echo "AI agents:" echo "AI agents:"
check pi check pi
check claude check claude
echo ""
echo "Process manager:"
check pm2 check pm2
echo "" echo ""
+45
View File
@@ -0,0 +1,45 @@
add_newline = true
command_timeout = 200
format = """
$username$hostname:[$directory$git_branch$git_status]($style)
$character"""
[username]
format = "[$user]($style)@"
style_user = "bold green"
show_always = true
[hostname]
format = "[$hostname]($style)"
style = "bold yellow"
ssh_only = false
[character]
error_symbol = "[✗](bold #61afef)"
success_symbol = "[](bold #61afef)"
[directory]
home_symbol = "~"
truncation_length = 0
truncate_to_repo = false
[git_branch]
format = " [$branch]($style) "
style = "italic #61afef"
[git_status]
format = '[$all_status]($style)'
style = "#61afef"
ahead = "⇡${count} "
diverged = "⇕⇡${ahead_count}⇣${behind_count} "
behind = "⇣${count} "
conflicted = " "
up_to_date = " "
untracked = "? "
modified = " "
stashed = ""
staged = ""
renamed = ""
deleted = ""