From b2a39a67453d2cb06860d96e60f14b1bd1f0a91c Mon Sep 17 00:00:00 2001 From: arkon Date: Fri, 20 Feb 2026 09:36:47 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20harden=20install.sh=20=E2=80=94=20Homebr?= =?UTF-8?q?ew=20bootstrap,=20dnf/yum=20fallback,=20Alpine=20sudo,=20PATH?= =?UTF-8?q?=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Extract ensure_homebrew() called from all macOS install functions (prevents crash if Git checked before Node) - Add dnf/yum fallback in Fedora install functions (fixes Amazon Linux 2 which only has yum) - Add ensure_sudo to Alpine install functions (consistent with all other distros) - Update Fedora/SUSE NodeSource setup to use GPG keyring method (replaces deprecated setup_XX.x script) - Remove dist/ directory from PATH (only use ~/.local/bin symlink) - Collapse identical fish/non-fish branches in setup_sc_alias Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 2 +- README.md | 14 +++ install.sh | 111 ++++++++++++------- package.json | 2 +- src/web/public/mobile.css | 217 ++++++++++++++++++++++++++++++++------ 5 files changed, 270 insertions(+), 76 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7fc7682e..5bc47dcc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,7 +35,7 @@ When user says "COM": 1. Increment version in BOTH `package.json` AND `CLAUDE.md` (verify they match with `grep version package.json && grep Version CLAUDE.md`) 2. Run: `git add -A && git commit -m "chore: bump version to X.XXXX" && git push && npm run build && systemctl --user restart claudeman-web` -**Version**: 0.1571 (must match `package.json` for npm publish) +**Version**: 0.1572 (must match `package.json` for npm publish) ## Project Overview diff --git a/README.md b/README.md index 2f82cad4..40b15f23 100644 --- a/README.md +++ b/README.md @@ -390,6 +390,20 @@ Click the chart icon on any session tab to see a complete timeline of what happe curl -fsSL https://raw.githubusercontent.com/Ark0N/claudeman/master/install.sh | bash ``` +### Windows (WSL) + +```powershell +wsl bash -c "curl -fsSL https://raw.githubusercontent.com/Ark0N/claudeman/master/install.sh | bash" +``` + +> **Prerequisites:** Claudeman requires tmux, so Windows users need [WSL](https://learn.microsoft.com/en-us/windows/wsl/install). If you don't have WSL yet: +> 1. Run `wsl --install` in an admin PowerShell, then reboot +> 2. Open **Ubuntu** from the Start menu to complete setup (create your Linux user) +> 3. Install Claude CLI inside WSL: `curl -fsSL https://claude.ai/install.sh | bash` +> 4. Then run the one-liner above +> +> After installing, `http://localhost:3000` is accessible from your Windows browser. + ### npm (alternative) ```bash diff --git a/install.sh b/install.sh index 5dfac15b..d231540d 100755 --- a/install.sh +++ b/install.sh @@ -298,21 +298,25 @@ run_as_root() { fi } -install_node_macos() { - info "Installing Node.js via Homebrew..." - - if ! command -v brew &>/dev/null; then - info "Installing Homebrew first..." - /bin/bash -c "$(download_to_stdout 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)" - elif [[ -f /usr/local/bin/brew ]]; then - eval "$(/usr/local/bin/brew shellenv)" - fi +ensure_homebrew() { + if command -v brew &>/dev/null; then + return 0 fi + info "Installing Homebrew first..." + /bin/bash -c "$(download_to_stdout 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)" + elif [[ -f /usr/local/bin/brew ]]; then + eval "$(/usr/local/bin/brew shellenv)" + fi +} + +install_node_macos() { + info "Installing Node.js via Homebrew..." + ensure_homebrew brew install node } @@ -344,9 +348,25 @@ install_node_fedora() { ensure_sudo - # Use NodeSource for consistent version across Fedora versions - download_to_stdout "https://rpm.nodesource.com/setup_$TARGET_NODE_VERSION.x" | run_as_root bash - - run_as_root dnf install -y nodejs + # Import NodeSource GPG key + run_as_root rpm --import https://rpm.nodesource.com/gpgkey/nodesource-repo.gpg.key + + # Create repo file (replaces deprecated setup_XX.x bash script) + cat << REPO_EOF | run_as_root tee /etc/yum.repos.d/nodesource.repo > /dev/null +[nodesource] +name=Node.js Packages for Linux RPM - nodesource +baseurl=https://rpm.nodesource.com/pub_${TARGET_NODE_VERSION}.x/nodistro/rpm/\$basearch +gpgcheck=1 +gpgkey=https://rpm.nodesource.com/gpgkey/nodesource-repo.gpg.key +enabled=1 +REPO_EOF + + # Use dnf if available (RHEL 8+, Fedora, AL2023), fall back to yum (RHEL 7, AL2) + if command -v dnf &>/dev/null; then + run_as_root dnf install -y nodejs + else + run_as_root yum install -y nodejs + fi } install_node_arch() { @@ -367,6 +387,7 @@ install_node_arch() { install_node_alpine() { info "Installing Node.js via apk..." + ensure_sudo run_as_root apk add --no-cache nodejs npm # Verify version @@ -383,13 +404,25 @@ install_node_suse() { ensure_sudo - # Use NodeSource for openSUSE - download_to_stdout "https://rpm.nodesource.com/setup_$TARGET_NODE_VERSION.x" | run_as_root bash - + # Import NodeSource GPG key + run_as_root rpm --import https://rpm.nodesource.com/gpgkey/nodesource-repo.gpg.key + + # Create repo file (replaces deprecated setup_XX.x bash script) + cat << REPO_EOF | run_as_root tee /etc/zypp/repos.d/nodesource.repo > /dev/null +[nodesource] +name=Node.js Packages for Linux RPM - nodesource +baseurl=https://rpm.nodesource.com/pub_${TARGET_NODE_VERSION}.x/nodistro/rpm/\$basearch +gpgcheck=1 +gpgkey=https://rpm.nodesource.com/gpgkey/nodesource-repo.gpg.key +enabled=1 +REPO_EOF + run_as_root zypper install -y nodejs } install_tmux_macos() { info "Installing tmux via Homebrew..." + ensure_homebrew brew install tmux } @@ -401,9 +434,13 @@ install_tmux_debian() { } install_tmux_fedora() { - info "Installing tmux via dnf..." + info "Installing tmux..." ensure_sudo - run_as_root dnf install -y tmux + if command -v dnf &>/dev/null; then + run_as_root dnf install -y tmux + else + run_as_root yum install -y tmux + fi } install_tmux_arch() { @@ -414,6 +451,7 @@ install_tmux_arch() { install_tmux_alpine() { info "Installing tmux via apk..." + ensure_sudo run_as_root apk add --no-cache tmux } @@ -425,6 +463,7 @@ install_tmux_suse() { install_git_macos() { info "Installing Git via Homebrew..." + ensure_homebrew brew install git } @@ -436,9 +475,13 @@ install_git_debian() { } install_git_fedora() { - info "Installing Git via dnf..." + info "Installing Git..." ensure_sudo - run_as_root dnf install -y git + if command -v dnf &>/dev/null; then + run_as_root dnf install -y git + else + run_as_root yum install -y git + fi } install_git_arch() { @@ -449,6 +492,7 @@ install_git_arch() { install_git_alpine() { info "Installing Git via apk..." + ensure_sudo run_as_root apk add --no-cache git } @@ -588,18 +632,9 @@ setup_sc_alias() { return 0 fi - local shell_name - shell_name="$(basename "${SHELL:-/bin/bash}")" - - if [[ "$shell_name" == "fish" ]]; then - echo "" >> "$profile" - echo "# Claudeman tmux session shortcut" >> "$profile" - echo "alias sc='tmux-chooser'" >> "$profile" - else - echo "" >> "$profile" - echo "# Claudeman tmux session shortcut" >> "$profile" - echo "alias sc='tmux-chooser'" >> "$profile" - fi + echo "" >> "$profile" + echo "# Claudeman tmux session shortcut" >> "$profile" + echo "alias sc='tmux-chooser'" >> "$profile" info "Added 'sc' alias for tmux-chooser" } @@ -838,10 +873,7 @@ main() { # Add to PATH # ======================================================================== - local bin_dir="$INSTALL_DIR/dist" - add_to_path "$bin_dir" - - # Create symlink in a common PATH location if possible + # Create symlink in a common PATH location local symlink_dir="$HOME/.local/bin" mkdir -p "$symlink_dir" 2>/dev/null || true if [[ -d "$symlink_dir" ]]; then @@ -862,7 +894,6 @@ main() { fi fi - # ======================================================================== # Systemd Service (Linux only) # ======================================================================== @@ -923,7 +954,7 @@ main() { # Remind to reload shell if PATH was modified local profile profile=$(detect_shell_profile) - if [[ ":$PATH:" != *":$bin_dir:"* ]]; then + if [[ ":$PATH:" != *":$symlink_dir:"* ]]; then echo -e " ${DIM}Restart your shell or run: source $profile${NC}" echo "" fi diff --git a/package.json b/package.json index ba1098e5..a6eebff7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claudeman", - "version": "0.1571", + "version": "0.1572", "description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence", "type": "module", "main": "dist/index.js", diff --git a/src/web/public/mobile.css b/src/web/public/mobile.css index 9b7f3782..550d6a56 100644 --- a/src/web/public/mobile.css +++ b/src/web/public/mobile.css @@ -880,6 +880,8 @@ html.mobile-init .file-browser-panel { max-height: 100%; border-radius: 0; margin: 0; + display: flex; + flex-direction: column; } /* Modal safe area padding - all sides for full-screen modals */ @@ -901,7 +903,9 @@ html.mobile-init .file-browser-panel { .modal-body { padding: 0.75rem 1rem; flex: 1; + min-height: 0; overflow-y: auto; + -webkit-overflow-scrolling: touch; } .modal-footer, @@ -1090,31 +1094,32 @@ html.mobile-init .file-browser-panel { align-items: stretch; } - /* Enable/Stop buttons — full-width touch targets */ + /* Enable/Stop buttons — compact inline */ .respawn-header .respawn-actions { display: flex; - gap: 0.5rem; + gap: 0.4rem; } .respawn-header .respawn-actions .btn-toolbar { flex: 1; - min-height: 44px !important; - font-size: 0.85rem !important; - border-radius: 8px; + min-height: 32px !important; + font-size: 0.75rem !important; + padding: 0.3rem 0.6rem !important; + border-radius: 6px; } - /* Duration preset buttons — 44px touch targets in a grid */ + /* Duration preset buttons — compact grid */ .duration-presets { display: grid; grid-template-columns: repeat(4, 1fr); - gap: 0.4rem; + gap: 0.3rem; } .duration-preset-btn { - min-height: 40px; - padding: 0.5rem 0.4rem; - font-size: 0.8rem; - border-radius: 8px; + min-height: 30px; + padding: 0.3rem 0.3rem; + font-size: 0.7rem; + border-radius: 6px; text-align: center; } @@ -1122,12 +1127,12 @@ html.mobile-init .file-browser-panel { .duration-custom { grid-column: 1 / -1; flex-wrap: wrap; - gap: 0.4rem; + gap: 0.3rem; } .duration-custom .duration-preset-btn { flex: 0 0 auto; - min-width: 80px; + min-width: 60px; } .duration-custom-input.visible { @@ -1136,50 +1141,51 @@ html.mobile-init .file-browser-panel { .duration-custom-input input { width: 100%; - min-height: 40px; + min-height: 30px; font-size: 16px; /* Prevents iOS zoom */ } - /* Preset selector row — stack vertically */ + /* Preset selector row — compact inline */ .preset-selector { display: flex; flex-wrap: wrap; - gap: 0.4rem; + gap: 0.3rem; } .preset-selector select { width: 100%; - min-height: 40px; + min-height: 32px; font-size: 16px; /* Prevents iOS zoom */ - border-radius: 8px; + border-radius: 6px; } .preset-selector .btn { flex: 1; - min-height: 40px; - font-size: 0.8rem; - border-radius: 8px; + min-height: 30px; + font-size: 0.7rem; + border-radius: 6px; } - /* Textareas — prevent iOS zoom with 16px font */ + /* Textareas — prevent iOS zoom with 16px font, compact height */ #sessionOptionsModal textarea { font-size: 16px; - min-height: 56px; - border-radius: 8px; - padding: 0.5rem 0.75rem; + min-height: 36px !important; + max-height: 64px; + border-radius: 6px; + padding: 0.35rem 0.6rem; } - /* Checkboxes — larger touch area */ + /* Checkboxes — compact touch area */ #sessionOptionsModal .checkbox-inline { - min-height: 40px; - font-size: 0.85rem; - padding: 0.25rem 0; - gap: 0.5rem; + min-height: 30px; + font-size: 0.75rem; + padding: 0.15rem 0; + gap: 0.4rem; } #sessionOptionsModal .checkbox-inline input[type="checkbox"] { - width: 20px; - height: 20px; + width: 18px; + height: 18px; } /* Respawn options row — stack if needed */ @@ -1187,10 +1193,153 @@ html.mobile-init .file-browser-panel { gap: 0.5rem; } - /* Form section headers — more breathing room */ + /* Form section headers — compact on mobile */ #sessionOptionsModal .form-section-header { + margin-top: 0.75rem; + padding-top: 0.5rem; + font-size: 0.65rem; + } + + /* Form rows — tighter spacing */ + #sessionOptionsModal .form-row { + margin-bottom: 0.5rem; + } + + #sessionOptionsModal .form-row label { + font-size: 0.65rem; + margin-bottom: 0.2rem; + } + + /* Form hints — compact */ + #sessionOptionsModal .form-hint { + font-size: 0.6rem; + margin-top: 0.2rem; + } + + /* ---- Context Tab: Mobile Optimizations ---- */ + + /* Context settings cards — tighter padding */ + .context-setting { + padding: 0.625rem; + } + + /* Stack context setting header vertically on narrow screens */ + .context-setting-header { + flex-wrap: wrap; + gap: 0.4rem; + } + + .context-setting-header .input-suffix-sm input { + width: 60px; + font-size: 16px; /* Prevents iOS zoom */ + min-height: 36px; + } + + .context-setting input[type="text"] { + font-size: 16px; /* Prevents iOS zoom */ + min-height: 36px; + padding: 0.4rem 0.6rem; + border-radius: 6px; + } + + /* Color picker — larger swatches for touch */ + .color-picker { + gap: 8px; + } + + .color-swatch { + width: 36px; + height: 36px; + border-radius: 6px; + } + + /* Toggle switches — larger touch area */ + .form-row-switch { + min-height: 40px; + gap: 0.4rem; + } + + /* Number inputs in context tab — prevent iOS zoom */ + .context-settings-grid input[type="number"] { + font-size: 16px; + min-height: 36px; + } + + /* ---- Ralph Tab: Mobile Optimizations ---- */ + + /* Ralph limits grid — 2 columns on mobile */ + .ralph-limits-grid { + grid-template-columns: 1fr 1fr; + gap: 0.5rem; + } + + .ralph-limits-grid .form-col input[type="number"] { + font-size: 16px; /* Prevents iOS zoom */ + min-height: 36px; + padding: 0.4rem 0.5rem; + border-radius: 6px; + } + + /* Ralph phrase input — prevent iOS zoom */ + #modalRalphPhrase { + font-size: 16px; + min-height: 40px; + border-radius: 6px; + } + + /* Save button — full width, proper touch target */ + .ralph-config-actions { margin-top: 1rem; - padding-top: 0.75rem; + } + + .ralph-config-actions .btn-toolbar { + width: 100%; + min-height: 44px; + font-size: 0.85rem; + border-radius: 8px; + } + + /* ---- Summary Tab: Mobile Optimizations ---- */ + + /* Filter buttons — larger touch targets */ + .run-summary-filters { + gap: 0.3rem; + margin-bottom: 0.5rem; + } + + .filter-btn { + padding: 0.35rem 0.75rem; + font-size: 0.7rem; + min-height: 32px; + } + + /* Timeline events — tighter on mobile */ + .timeline-event { + padding: 0.4rem 0.5rem; + margin-bottom: 0.25rem; + font-size: 0.7rem; + } + + /* Summary footer — stack vertically */ + .run-summary-footer { + flex-direction: column; + gap: 0.4rem; + padding: 0.5rem 0; + } + + .run-summary-actions { + flex-wrap: wrap; + gap: 0.3rem; + } + + .run-summary-actions .btn-toolbar { + flex: 1; + min-height: 36px; + font-size: 0.7rem; + white-space: nowrap; + } + + .auto-refresh-label { font-size: 0.7rem; }