mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
feat: add universal install script for macOS & Linux
- Add install.sh with curl one-liner installation - Supports macOS (Homebrew), Debian/Ubuntu (apt), Fedora (dnf), Arch (pacman), Alpine (apk) - Auto-detects OS, architecture, and Linux distro - Checks/installs Node.js 18+, GNU Screen, warns about Claude CLI - Clones to ~/.claudeman/app, builds, adds to PATH - Optional systemd service setup on Linux - Add postinstall.js for npm install verification - Update README with simplified installation section Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -201,45 +201,34 @@ Run **20 parallel sessions** with full visibility:
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
## Installation
|
||||
|
||||
### macOS & Linux
|
||||
|
||||
```bash
|
||||
# Clone and build
|
||||
git clone https://github.com/Ark0N/claudeman.git
|
||||
cd claudeman
|
||||
npm install && npm run build
|
||||
curl -fsSL https://raw.githubusercontent.com/Ark0N/claudeman/master/install.sh | bash
|
||||
```
|
||||
|
||||
# Start the web interface
|
||||
claudeman web
|
||||
### npm (alternative)
|
||||
|
||||
# Open http://localhost:3000
|
||||
```bash
|
||||
npm install -g claudeman
|
||||
```
|
||||
|
||||
### Requirements
|
||||
|
||||
- Node.js 18+
|
||||
- [Claude CLI](https://docs.anthropic.com/en/docs/claude-code/getting-started) installed
|
||||
- GNU Screen (`apt install screen` / `brew install screen`)
|
||||
|
||||
## Getting Started
|
||||
|
||||
```bash
|
||||
claudeman web --https
|
||||
# Open https://localhost:3000
|
||||
# Press Ctrl+Enter to start your first session
|
||||
```
|
||||
|
||||
### More Options
|
||||
|
||||
```bash
|
||||
# Custom port
|
||||
claudeman web -p 8080
|
||||
|
||||
# Development mode (no build needed)
|
||||
npx tsx src/index.ts web
|
||||
|
||||
# HTTPS (required for browser notifications)
|
||||
claudeman web --https
|
||||
|
||||
# Run as systemd service (auto-restarts, survives logout)
|
||||
ln -sf $(pwd)/scripts/claudeman-web.service ~/.config/systemd/user/
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable --now claudeman-web
|
||||
loginctl enable-linger $USER
|
||||
```
|
||||
|
||||
**Requirements:**
|
||||
- Node.js 18+
|
||||
- Claude CLI installed (PATH is auto-resolved from common locations like `~/.local/bin`)
|
||||
- GNU Screen (`apt install screen` / `brew install screen`)
|
||||
|
||||
---
|
||||
|
||||
## Keyboard Shortcuts
|
||||
|
||||
Executable
+722
@@ -0,0 +1,722 @@
|
||||
#!/usr/bin/env bash
|
||||
# Claudeman Universal Installer
|
||||
# https://github.com/Ark0N/claudeman
|
||||
#
|
||||
# Usage: curl -fsSL https://raw.githubusercontent.com/Ark0N/claudeman/master/install.sh | bash
|
||||
#
|
||||
# Environment variables:
|
||||
# CLAUDEMAN_NONINTERACTIVE=1 - Skip all prompts (for CI/automation)
|
||||
# CLAUDEMAN_INSTALL_DIR - Custom install directory (default: ~/.claudeman/app)
|
||||
# CLAUDEMAN_SKIP_SYSTEMD=1 - Skip systemd service setup prompt
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ============================================================================
|
||||
# Configuration
|
||||
# ============================================================================
|
||||
|
||||
INSTALL_DIR="${CLAUDEMAN_INSTALL_DIR:-$HOME/.claudeman/app}"
|
||||
REPO_URL="https://github.com/Ark0N/claudeman.git"
|
||||
MIN_NODE_VERSION=18
|
||||
NONINTERACTIVE="${CLAUDEMAN_NONINTERACTIVE:-0}"
|
||||
SKIP_SYSTEMD="${CLAUDEMAN_SKIP_SYSTEMD:-0}"
|
||||
|
||||
# Claude CLI search paths (from src/session.ts)
|
||||
CLAUDE_SEARCH_PATHS=(
|
||||
"$HOME/.local/bin/claude"
|
||||
"$HOME/.claude/local/claude"
|
||||
"/usr/local/bin/claude"
|
||||
"$HOME/.npm-global/bin/claude"
|
||||
"$HOME/bin/claude"
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# Color Output (from scripts/screen-manager.sh pattern)
|
||||
# ============================================================================
|
||||
|
||||
# Check if terminal supports colors
|
||||
if [[ -t 1 ]] && [[ -n "${TERM:-}" ]] && command -v tput &>/dev/null; then
|
||||
ncolors=$(tput colors 2>/dev/null || echo 0)
|
||||
if [[ "$ncolors" -ge 8 ]]; then
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
MAGENTA='\033[0;35m'
|
||||
BOLD='\033[1m'
|
||||
DIM='\033[2m'
|
||||
NC='\033[0m'
|
||||
else
|
||||
RED='' GREEN='' YELLOW='' BLUE='' CYAN='' MAGENTA='' BOLD='' DIM='' NC=''
|
||||
fi
|
||||
else
|
||||
RED='' GREEN='' YELLOW='' BLUE='' CYAN='' MAGENTA='' BOLD='' DIM='' NC=''
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Output Helpers
|
||||
# ============================================================================
|
||||
|
||||
info() {
|
||||
echo -e "${BLUE}==>${NC} ${BOLD}$1${NC}"
|
||||
}
|
||||
|
||||
success() {
|
||||
echo -e "${GREEN}==>${NC} ${BOLD}$1${NC}"
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e "${YELLOW}Warning:${NC} $1"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}Error:${NC} $1" >&2
|
||||
}
|
||||
|
||||
die() {
|
||||
error "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# System Detection
|
||||
# ============================================================================
|
||||
|
||||
detect_os() {
|
||||
local os
|
||||
os="$(uname -s)"
|
||||
case "$os" in
|
||||
Darwin) echo "macos" ;;
|
||||
Linux) echo "linux" ;;
|
||||
*) die "Unsupported operating system: $os" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
detect_arch() {
|
||||
local arch
|
||||
arch="$(uname -m)"
|
||||
case "$arch" in
|
||||
x86_64|amd64) echo "x64" ;;
|
||||
aarch64|arm64) echo "arm64" ;;
|
||||
*) die "Unsupported architecture: $arch" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
detect_linux_distro() {
|
||||
if [[ ! -f /etc/os-release ]]; then
|
||||
echo "unknown"
|
||||
return
|
||||
fi
|
||||
|
||||
# Source os-release to get ID
|
||||
# shellcheck source=/dev/null
|
||||
source /etc/os-release
|
||||
|
||||
case "${ID:-}" in
|
||||
debian|ubuntu|linuxmint|pop|elementary|zorin|kali)
|
||||
echo "debian"
|
||||
;;
|
||||
fedora|rhel|centos|rocky|alma|ol)
|
||||
echo "fedora"
|
||||
;;
|
||||
arch|manjaro|endeavouros|garuda)
|
||||
echo "arch"
|
||||
;;
|
||||
opensuse*|suse*)
|
||||
echo "suse"
|
||||
;;
|
||||
alpine)
|
||||
echo "alpine"
|
||||
;;
|
||||
*)
|
||||
# Try ID_LIKE as fallback
|
||||
case "${ID_LIKE:-}" in
|
||||
*debian*|*ubuntu*) echo "debian" ;;
|
||||
*fedora*|*rhel*) echo "fedora" ;;
|
||||
*arch*) echo "arch" ;;
|
||||
*suse*) echo "suse" ;;
|
||||
*) echo "unknown" ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Dependency Checks
|
||||
# ============================================================================
|
||||
|
||||
check_node() {
|
||||
if ! command -v node &>/dev/null; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local version
|
||||
version=$(node --version 2>/dev/null | sed 's/^v//' | cut -d. -f1)
|
||||
if [[ -z "$version" ]] || [[ "$version" -lt "$MIN_NODE_VERSION" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
check_npm() {
|
||||
command -v npm &>/dev/null
|
||||
}
|
||||
|
||||
check_git() {
|
||||
command -v git &>/dev/null
|
||||
}
|
||||
|
||||
check_screen() {
|
||||
command -v screen &>/dev/null
|
||||
}
|
||||
|
||||
check_claude() {
|
||||
# Check PATH first
|
||||
if command -v claude &>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check known install locations
|
||||
for path in "${CLAUDE_SEARCH_PATHS[@]}"; do
|
||||
if [[ -x "$path" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
get_claude_path() {
|
||||
if command -v claude &>/dev/null; then
|
||||
command -v claude
|
||||
return
|
||||
fi
|
||||
|
||||
for path in "${CLAUDE_SEARCH_PATHS[@]}"; do
|
||||
if [[ -x "$path" ]]; then
|
||||
echo "$path"
|
||||
return
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Dependency Installation
|
||||
# ============================================================================
|
||||
|
||||
install_node_macos() {
|
||||
info "Installing Node.js via Homebrew..."
|
||||
|
||||
if ! command -v brew &>/dev/null; then
|
||||
info "Installing Homebrew first..."
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
|
||||
# Add Homebrew to PATH for Apple Silicon
|
||||
if [[ -f /opt/homebrew/bin/brew ]]; then
|
||||
eval "$(/opt/homebrew/bin/brew shellenv)"
|
||||
fi
|
||||
fi
|
||||
|
||||
brew install node
|
||||
}
|
||||
|
||||
install_node_debian() {
|
||||
info "Installing Node.js via NodeSource..."
|
||||
|
||||
# Check if we can use sudo
|
||||
if ! command -v sudo &>/dev/null; then
|
||||
die "sudo is required to install Node.js. Please install sudo or Node.js manually."
|
||||
fi
|
||||
|
||||
# Install prerequisites
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ca-certificates curl gnupg
|
||||
|
||||
# Add NodeSource repository
|
||||
sudo mkdir -p /etc/apt/keyrings
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
||||
|
||||
NODE_MAJOR=20
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y nodejs
|
||||
}
|
||||
|
||||
install_node_fedora() {
|
||||
info "Installing Node.js via dnf..."
|
||||
|
||||
if ! command -v sudo &>/dev/null; then
|
||||
die "sudo is required to install Node.js. Please install sudo or Node.js manually."
|
||||
fi
|
||||
|
||||
sudo dnf install -y nodejs npm
|
||||
}
|
||||
|
||||
install_node_arch() {
|
||||
info "Installing Node.js via pacman..."
|
||||
|
||||
if ! command -v sudo &>/dev/null; then
|
||||
die "sudo is required to install Node.js. Please install sudo or Node.js manually."
|
||||
fi
|
||||
|
||||
sudo pacman -Sy --noconfirm nodejs npm
|
||||
}
|
||||
|
||||
install_node_alpine() {
|
||||
info "Installing Node.js via apk..."
|
||||
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
apk add --no-cache nodejs npm
|
||||
else
|
||||
if ! command -v sudo &>/dev/null; then
|
||||
die "sudo is required to install Node.js. Please install sudo or Node.js manually."
|
||||
fi
|
||||
sudo apk add --no-cache nodejs npm
|
||||
fi
|
||||
}
|
||||
|
||||
install_screen_macos() {
|
||||
info "Installing GNU Screen via Homebrew..."
|
||||
brew install screen
|
||||
}
|
||||
|
||||
install_screen_debian() {
|
||||
info "Installing GNU Screen via apt..."
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y screen
|
||||
}
|
||||
|
||||
install_screen_fedora() {
|
||||
info "Installing GNU Screen via dnf..."
|
||||
sudo dnf install -y screen
|
||||
}
|
||||
|
||||
install_screen_arch() {
|
||||
info "Installing GNU Screen via pacman..."
|
||||
sudo pacman -Sy --noconfirm screen
|
||||
}
|
||||
|
||||
install_screen_alpine() {
|
||||
info "Installing GNU Screen via apk..."
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
apk add --no-cache screen
|
||||
else
|
||||
sudo apk add --no-cache screen
|
||||
fi
|
||||
}
|
||||
|
||||
install_git_macos() {
|
||||
info "Installing Git via Homebrew..."
|
||||
brew install git
|
||||
}
|
||||
|
||||
install_git_debian() {
|
||||
info "Installing Git via apt..."
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y git
|
||||
}
|
||||
|
||||
install_git_fedora() {
|
||||
info "Installing Git via dnf..."
|
||||
sudo dnf install -y git
|
||||
}
|
||||
|
||||
install_git_arch() {
|
||||
info "Installing Git via pacman..."
|
||||
sudo pacman -Sy --noconfirm git
|
||||
}
|
||||
|
||||
install_git_alpine() {
|
||||
info "Installing Git via apk..."
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
apk add --no-cache git
|
||||
else
|
||||
sudo apk add --no-cache git
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Interactive Prompts
|
||||
# ============================================================================
|
||||
|
||||
prompt_yes_no() {
|
||||
local prompt="$1"
|
||||
local default="${2:-y}"
|
||||
|
||||
if [[ "$NONINTERACTIVE" == "1" ]]; then
|
||||
[[ "$default" == "y" ]]
|
||||
return
|
||||
fi
|
||||
|
||||
local yn_hint
|
||||
if [[ "$default" == "y" ]]; then
|
||||
yn_hint="[Y/n]"
|
||||
else
|
||||
yn_hint="[y/N]"
|
||||
fi
|
||||
|
||||
while true; do
|
||||
echo -en "${CYAN}$prompt${NC} $yn_hint "
|
||||
read -r answer
|
||||
answer="${answer:-$default}"
|
||||
case "$answer" in
|
||||
[Yy]|[Yy][Ee][Ss]) return 0 ;;
|
||||
[Nn]|[Nn][Oo]) return 1 ;;
|
||||
*) echo "Please answer yes or no." ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# PATH Management
|
||||
# ============================================================================
|
||||
|
||||
detect_shell_profile() {
|
||||
local shell_name
|
||||
shell_name="$(basename "${SHELL:-/bin/bash}")"
|
||||
|
||||
case "$shell_name" in
|
||||
zsh)
|
||||
if [[ -f "$HOME/.zshrc" ]]; then
|
||||
echo "$HOME/.zshrc"
|
||||
else
|
||||
echo "$HOME/.zprofile"
|
||||
fi
|
||||
;;
|
||||
bash)
|
||||
if [[ -f "$HOME/.bashrc" ]]; then
|
||||
echo "$HOME/.bashrc"
|
||||
elif [[ -f "$HOME/.bash_profile" ]]; then
|
||||
echo "$HOME/.bash_profile"
|
||||
else
|
||||
echo "$HOME/.profile"
|
||||
fi
|
||||
;;
|
||||
fish)
|
||||
echo "$HOME/.config/fish/config.fish"
|
||||
;;
|
||||
*)
|
||||
echo "$HOME/.profile"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
add_to_path() {
|
||||
local bin_dir="$1"
|
||||
local profile
|
||||
profile=$(detect_shell_profile)
|
||||
|
||||
# Check if already in PATH
|
||||
if echo "$PATH" | tr ':' '\n' | grep -qx "$bin_dir"; then
|
||||
info "PATH already includes $bin_dir"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check if already in profile
|
||||
if [[ -f "$profile" ]] && grep -q "$bin_dir" "$profile" 2>/dev/null; then
|
||||
info "PATH export already in $profile"
|
||||
return 0
|
||||
fi
|
||||
|
||||
info "Adding $bin_dir to PATH in $profile"
|
||||
|
||||
# Create profile directory if needed (for fish)
|
||||
mkdir -p "$(dirname "$profile")"
|
||||
|
||||
local shell_name
|
||||
shell_name="$(basename "${SHELL:-/bin/bash}")"
|
||||
|
||||
if [[ "$shell_name" == "fish" ]]; then
|
||||
echo "fish_add_path $bin_dir" >> "$profile"
|
||||
else
|
||||
echo "" >> "$profile"
|
||||
echo "# Added by Claudeman installer" >> "$profile"
|
||||
echo "export PATH=\"$bin_dir:\$PATH\"" >> "$profile"
|
||||
fi
|
||||
|
||||
success "Added to $profile - restart your shell or run: source $profile"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Systemd Service Setup (Linux only)
|
||||
# ============================================================================
|
||||
|
||||
setup_systemd_service() {
|
||||
local service_dir="$HOME/.config/systemd/user"
|
||||
local service_file="$service_dir/claudeman-web.service"
|
||||
|
||||
info "Setting up systemd user service..."
|
||||
|
||||
mkdir -p "$service_dir"
|
||||
|
||||
# Create service file
|
||||
cat > "$service_file" << EOF
|
||||
[Unit]
|
||||
Description=Claudeman Web Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=$INSTALL_DIR/dist/index.js web --https
|
||||
WorkingDirectory=$HOME
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment=NODE_ENV=production
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
EOF
|
||||
|
||||
# Reload systemd
|
||||
systemctl --user daemon-reload
|
||||
|
||||
# Enable service
|
||||
systemctl --user enable claudeman-web.service
|
||||
|
||||
# Enable lingering (allows service to run after logout)
|
||||
if command -v loginctl &>/dev/null; then
|
||||
loginctl enable-linger "$USER" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
success "Systemd service installed at $service_file"
|
||||
info "Start with: systemctl --user start claudeman-web"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Main Installation
|
||||
# ============================================================================
|
||||
|
||||
print_banner() {
|
||||
echo -e "${CYAN}${BOLD}"
|
||||
echo " ____ _ _ "
|
||||
echo " / ___|| | __ _ _ _ __| | ___ _ __ ___ __ _ _ __ "
|
||||
echo "| | | |/ _\` | | | |/ _\` |/ _ \\ '_ \` _ \\ / _\` | '_ \\ "
|
||||
echo "| |____| | (_| | |_| | (_| | __/ | | | | | (_| | | | | "
|
||||
echo " \\_____|_|\\__,_|\\__,_|\\__,_|\\___|_| |_| |_|\\__,_|_| |_| "
|
||||
echo ""
|
||||
echo -e "${NC}${DIM} The missing control plane for Claude Code${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
main() {
|
||||
print_banner
|
||||
|
||||
# Detect system
|
||||
local os arch distro=""
|
||||
os=$(detect_os)
|
||||
arch=$(detect_arch)
|
||||
|
||||
if [[ "$os" == "linux" ]]; then
|
||||
distro=$(detect_linux_distro)
|
||||
fi
|
||||
|
||||
info "Detected: $os ($arch)${distro:+ - $distro}"
|
||||
echo ""
|
||||
|
||||
# ========================================================================
|
||||
# Check/Install Dependencies
|
||||
# ========================================================================
|
||||
|
||||
# Git
|
||||
info "Checking Git..."
|
||||
if ! check_git; then
|
||||
if prompt_yes_no "Git is not installed. Install it now?"; then
|
||||
case "$os-$distro" in
|
||||
macos-*) install_git_macos ;;
|
||||
linux-debian) install_git_debian ;;
|
||||
linux-fedora) install_git_fedora ;;
|
||||
linux-arch) install_git_arch ;;
|
||||
linux-alpine) install_git_alpine ;;
|
||||
*) die "Don't know how to install Git on this system. Please install it manually." ;;
|
||||
esac
|
||||
else
|
||||
die "Git is required to install Claudeman."
|
||||
fi
|
||||
else
|
||||
success "Git is installed"
|
||||
fi
|
||||
|
||||
# Node.js
|
||||
info "Checking Node.js (v$MIN_NODE_VERSION+)..."
|
||||
if ! check_node; then
|
||||
local node_version=""
|
||||
if command -v node &>/dev/null; then
|
||||
node_version=$(node --version 2>/dev/null || echo "unknown")
|
||||
warn "Node.js $node_version is installed but version $MIN_NODE_VERSION+ is required."
|
||||
fi
|
||||
|
||||
if prompt_yes_no "Install Node.js $MIN_NODE_VERSION+?"; then
|
||||
case "$os-$distro" in
|
||||
macos-*) install_node_macos ;;
|
||||
linux-debian) install_node_debian ;;
|
||||
linux-fedora) install_node_fedora ;;
|
||||
linux-arch) install_node_arch ;;
|
||||
linux-alpine) install_node_alpine ;;
|
||||
*) die "Don't know how to install Node.js on this system. Please install it manually." ;;
|
||||
esac
|
||||
else
|
||||
die "Node.js $MIN_NODE_VERSION+ is required to run Claudeman."
|
||||
fi
|
||||
else
|
||||
local node_ver
|
||||
node_ver=$(node --version 2>/dev/null)
|
||||
success "Node.js $node_ver is installed"
|
||||
fi
|
||||
|
||||
# Verify npm (should come with Node.js)
|
||||
if ! check_npm; then
|
||||
die "npm is not available. Please reinstall Node.js."
|
||||
fi
|
||||
|
||||
# GNU Screen
|
||||
info "Checking GNU Screen..."
|
||||
if ! check_screen; then
|
||||
if prompt_yes_no "GNU Screen is not installed. Install it now?"; then
|
||||
case "$os-$distro" in
|
||||
macos-*) install_screen_macos ;;
|
||||
linux-debian) install_screen_debian ;;
|
||||
linux-fedora) install_screen_fedora ;;
|
||||
linux-arch) install_screen_arch ;;
|
||||
linux-alpine) install_screen_alpine ;;
|
||||
*) die "Don't know how to install GNU Screen on this system. Please install it manually." ;;
|
||||
esac
|
||||
else
|
||||
die "GNU Screen is required for session persistence."
|
||||
fi
|
||||
else
|
||||
success "GNU Screen is installed"
|
||||
fi
|
||||
|
||||
# Claude CLI (warning only)
|
||||
info "Checking Claude CLI..."
|
||||
if ! check_claude; then
|
||||
echo ""
|
||||
warn "Claude CLI is not installed!"
|
||||
echo -e " ${DIM}Claudeman requires Claude CLI to manage AI sessions.${NC}"
|
||||
echo -e " ${DIM}Install it with:${NC}"
|
||||
echo ""
|
||||
echo -e " ${CYAN}npm install -g @anthropic-ai/claude-code${NC}"
|
||||
echo ""
|
||||
echo -e " ${DIM}Or see: https://docs.anthropic.com/en/docs/claude-code${NC}"
|
||||
echo ""
|
||||
else
|
||||
local claude_path
|
||||
claude_path=$(get_claude_path)
|
||||
success "Claude CLI found at $claude_path"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ========================================================================
|
||||
# Clone/Update Repository
|
||||
# ========================================================================
|
||||
|
||||
info "Installing Claudeman to $INSTALL_DIR..."
|
||||
|
||||
if [[ -d "$INSTALL_DIR/.git" ]]; then
|
||||
info "Existing installation found, updating..."
|
||||
cd "$INSTALL_DIR"
|
||||
git fetch --quiet origin
|
||||
git reset --hard origin/master --quiet
|
||||
else
|
||||
# Create parent directory
|
||||
mkdir -p "$(dirname "$INSTALL_DIR")"
|
||||
|
||||
# Clone repository
|
||||
git clone --quiet "$REPO_URL" "$INSTALL_DIR"
|
||||
cd "$INSTALL_DIR"
|
||||
fi
|
||||
|
||||
success "Repository ready"
|
||||
|
||||
# ========================================================================
|
||||
# Build
|
||||
# ========================================================================
|
||||
|
||||
info "Installing dependencies..."
|
||||
npm install --quiet --no-fund --no-audit
|
||||
|
||||
info "Building..."
|
||||
npm run build --quiet
|
||||
|
||||
success "Build complete"
|
||||
|
||||
# ========================================================================
|
||||
# Add to PATH
|
||||
# ========================================================================
|
||||
|
||||
local bin_dir="$INSTALL_DIR/dist"
|
||||
add_to_path "$bin_dir"
|
||||
|
||||
# Create symlink in a common PATH location if possible
|
||||
local symlink_dir="$HOME/.local/bin"
|
||||
if [[ -d "$symlink_dir" ]] || mkdir -p "$symlink_dir" 2>/dev/null; then
|
||||
ln -sf "$INSTALL_DIR/dist/index.js" "$symlink_dir/claudeman"
|
||||
info "Created symlink: $symlink_dir/claudeman"
|
||||
fi
|
||||
|
||||
# ========================================================================
|
||||
# Systemd Service (Linux only)
|
||||
# ========================================================================
|
||||
|
||||
if [[ "$os" == "linux" ]] && [[ "$SKIP_SYSTEMD" != "1" ]] && command -v systemctl &>/dev/null; then
|
||||
echo ""
|
||||
if prompt_yes_no "Set up systemd service for auto-start?"; then
|
||||
setup_systemd_service
|
||||
fi
|
||||
fi
|
||||
|
||||
# ========================================================================
|
||||
# Success!
|
||||
# ========================================================================
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}${BOLD}============================================================${NC}"
|
||||
echo -e "${GREEN}${BOLD} Claudeman installed successfully!${NC}"
|
||||
echo -e "${GREEN}${BOLD}============================================================${NC}"
|
||||
echo ""
|
||||
echo -e " ${BOLD}Quick Start:${NC}"
|
||||
echo ""
|
||||
echo -e " ${CYAN}# Start the web server${NC}"
|
||||
echo -e " claudeman web"
|
||||
echo ""
|
||||
echo -e " ${CYAN}# Start with HTTPS (enables browser notifications)${NC}"
|
||||
echo -e " claudeman web --https"
|
||||
echo ""
|
||||
echo -e " ${CYAN}# Open in browser${NC}"
|
||||
echo -e " https://localhost:3000"
|
||||
echo ""
|
||||
|
||||
if [[ "$os" == "linux" ]] && [[ -f "$HOME/.config/systemd/user/claudeman-web.service" ]]; then
|
||||
echo -e " ${BOLD}Systemd Service:${NC}"
|
||||
echo ""
|
||||
echo -e " ${CYAN}systemctl --user start claudeman-web${NC} # Start"
|
||||
echo -e " ${CYAN}systemctl --user status claudeman-web${NC} # Check status"
|
||||
echo -e " ${CYAN}journalctl --user -u claudeman-web -f${NC} # View logs"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo -e " ${BOLD}Documentation:${NC}"
|
||||
echo -e " https://github.com/Ark0N/claudeman"
|
||||
echo ""
|
||||
|
||||
if ! check_claude; then
|
||||
echo -e " ${YELLOW}${BOLD}Reminder:${NC} Install Claude CLI to start using Claudeman:"
|
||||
echo -e " ${CYAN}npm install -g @anthropic-ai/claude-code${NC}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Remind to reload shell if PATH was modified
|
||||
local profile
|
||||
profile=$(detect_shell_profile)
|
||||
if ! echo "$PATH" | tr ':' '\n' | grep -qx "$bin_dir"; then
|
||||
echo -e " ${DIM}Restart your shell or run: source $profile${NC}"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# Wrap in main to prevent partial execution on curl | bash
|
||||
main "$@"
|
||||
Generated
+6
-5
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"name": "claudeman",
|
||||
"version": "0.1370",
|
||||
"version": "0.1375",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "claudeman",
|
||||
"version": "0.1370",
|
||||
"version": "0.1375",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/static": "^8.0.0",
|
||||
@@ -2921,9 +2922,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "11.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz",
|
||||
"integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==",
|
||||
"version": "11.2.5",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz",
|
||||
"integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import { existsSync } from 'fs';
|
||||
import { homedir } from 'os';
|
||||
import { join } from 'path';
|
||||
|
||||
// Colors (with fallback for no-color environments)
|
||||
const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
||||
const green = (s) => useColor ? `\x1b[32m${s}\x1b[0m` : s;
|
||||
const yellow = (s) => useColor ? `\x1b[33m${s}\x1b[0m` : s;
|
||||
const red = (s) => useColor ? `\x1b[31m${s}\x1b[0m` : s;
|
||||
const bold = (s) => useColor ? `\x1b[1m${s}\x1b[0m` : s;
|
||||
|
||||
console.log(bold('Claudeman postinstall check...'));
|
||||
|
||||
let hasWarnings = false;
|
||||
|
||||
// 1. Check Node.js version >= 18
|
||||
const nodeVersion = process.versions.node;
|
||||
const majorVersion = parseInt(nodeVersion.split('.')[0], 10);
|
||||
|
||||
if (majorVersion < 18) {
|
||||
console.log(red(`✗ Node.js v${nodeVersion} is too old (requires >=18)`));
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log(green(`✓ Node.js v${nodeVersion}`) + ' (meets >=18 requirement)');
|
||||
}
|
||||
|
||||
// 2. Check if GNU Screen is installed
|
||||
let screenFound = false;
|
||||
try {
|
||||
execSync('which screen', { stdio: 'pipe' });
|
||||
screenFound = true;
|
||||
console.log(green('✓ GNU Screen found'));
|
||||
} catch {
|
||||
hasWarnings = true;
|
||||
console.log(yellow('⚠ GNU Screen not found'));
|
||||
console.log(' Install:');
|
||||
console.log(' Ubuntu/Debian: sudo apt install screen');
|
||||
console.log(' macOS: brew install screen');
|
||||
}
|
||||
|
||||
// 3. Check if Claude CLI is found
|
||||
const home = homedir();
|
||||
const claudePaths = [
|
||||
join(home, '.local/bin/claude'),
|
||||
join(home, '.claude/local/claude'),
|
||||
'/usr/local/bin/claude',
|
||||
join(home, '.npm-global/bin/claude'),
|
||||
join(home, 'bin/claude'),
|
||||
];
|
||||
|
||||
let claudeFound = false;
|
||||
|
||||
// First try `which claude`
|
||||
try {
|
||||
execSync('which claude', { stdio: 'pipe' });
|
||||
claudeFound = true;
|
||||
} catch {
|
||||
// Check known paths
|
||||
for (const p of claudePaths) {
|
||||
if (existsSync(p)) {
|
||||
claudeFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (claudeFound) {
|
||||
console.log(green('✓ Claude CLI found'));
|
||||
} else {
|
||||
hasWarnings = true;
|
||||
console.log(yellow('⚠ Claude CLI not found'));
|
||||
console.log(' Install: curl -fsSL https://claude.ai/install.sh | bash');
|
||||
}
|
||||
|
||||
// Print next steps
|
||||
console.log('');
|
||||
console.log(bold('Next steps:'));
|
||||
console.log(' npm run build');
|
||||
console.log(' claudeman web');
|
||||
|
||||
if (hasWarnings) {
|
||||
console.log('');
|
||||
console.log(yellow('Note: Resolve warnings above for full functionality.'));
|
||||
}
|
||||
Reference in New Issue
Block a user