From 47440ffde4fb68915ff649c4c1abf33be55ec959 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Sun, 30 Aug 2026 11:43:19 +0000 Subject: [PATCH] fix(installer): install the CLI on macOS and Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The installer never installed the CLI on macOS or Windows. detect_os returns Darwin there, which set PLATFORM_KIND=desktop-client and took a branch that skipped install_global_package "$PKG_NAME" entirely and instead ran `pnpm add -g @profullstack/threatcrush-desktop`. That package has never been published to npm — apps/desktop is an Electron app shipped to GitHub Releases as .dmg / .exe / .AppImage / .deb. So the install 404'd, and under `set -e` the 404 aborted the whole script, leaving the machine with no threatcrush at all: ERR_PNPM_FETCH_404 GET .../@profullstack%2Fthreatcrush-desktop Always install the CLI, on every platform. When the machine is a desktop, point at the matching GitHub Releases asset instead of pretending the bundle is an npm package. Same fix in `threatcrush update`, which pushed the same nonexistent package onto its update command list. `remove` is untouched — it already guards on packageLooksInstalled. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01TuQfdnSCZAC5bDTCgTwSVW --- apps/cli/src/index.ts | 6 +- apps/web/public/install.sh | 76 +++++++++++-------- apps/web/src/__tests__/install-script.test.ts | 18 +++-- 3 files changed, 60 insertions(+), 40 deletions(-) diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index 53da808..5e84c78 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -509,12 +509,10 @@ program console.log(chalk.dim(` Detected package manager: ${pm}`)); console.log(chalk.dim(` Install mode: ${installMode}\n`)); + // The desktop app ships as a GitHub Releases bundle, not an npm package, + // so there is nothing to update globally for it here. const commands = [getGlobalInstallCommand(pm, PKG_NAME, "update")]; - if (installMode === "desktop") { - commands.push(getGlobalInstallCommand(pm, DESKTOP_PKG_NAME, "update")); - } - try { for (const cmd of commands) { console.log(chalk.green(` → ${cmd}\n`)); diff --git a/apps/web/public/install.sh b/apps/web/public/install.sh index b97e7be..b243649 100644 --- a/apps/web/public/install.sh +++ b/apps/web/public/install.sh @@ -12,7 +12,9 @@ DIM="\033[2m" RESET="\033[0m" PKG_NAME="@profullstack/threatcrush" -DESKTOP_PKG_NAME="@profullstack/threatcrush-desktop" +# The desktop app is an Electron bundle published to GitHub Releases (.dmg / +# .exe / .AppImage / .deb), not to npm. There is nothing to install globally. +DESKTOP_RELEASES_URL="https://github.com/profullstack/threatcrush/releases/latest" MISE_INSTALL_URL="https://mise.run" CONFIG_DIR="$HOME/.threatcrush" CONFIG_PATH="$CONFIG_DIR/install.json" @@ -68,6 +70,15 @@ detect_os() { uname -s 2>/dev/null || echo "unknown" } +detect_arch() { + ARCH_NAME=$(uname -m 2>/dev/null || echo "unknown") + case "$ARCH_NAME" in + x86_64|amd64) echo "x64" ;; + arm64|aarch64) echo "arm64" ;; + *) echo "$ARCH_NAME" ;; + esac +} + detect_install_mode() { OS_NAME=$(detect_os) @@ -226,16 +237,31 @@ install_global_package() { esac } -install_desktop_bundle() { +announce_desktop_bundle() { OS_NAME=$(detect_os) + ARCH=$(detect_arch) + case "$OS_NAME" in - Linux|Darwin|MINGW*|MSYS*|CYGWIN*|Windows_NT) - install_global_package "$DESKTOP_PKG_NAME" + Darwin) + DESKTOP_ASSET="threatcrush-desktop--${ARCH}.dmg" + ;; + Linux) + DESKTOP_ASSET="threatcrush-desktop--x86_64.AppImage or -amd64.deb" + ;; + MINGW*|MSYS*|CYGWIN*|Windows_NT) + DESKTOP_ASSET="threatcrush-desktop--x64-setup.exe" ;; *) - say "${YELLOW}Desktop mode detected, but automatic desktop package install is not ready on ${OS_NAME}.${RESET}" + DESKTOP_ASSET="" ;; esac + + if [ -n "$DESKTOP_ASSET" ]; then + say " ${DIM}Download:${RESET} ${DESKTOP_ASSET}" + say " ${DIM}From:${RESET} ${DESKTOP_RELEASES_URL}" + else + say "${YELLOW}Desktop builds are not published for ${OS_NAME}.${RESET}" + fi } NODE_VERSION=$(detect_node) @@ -281,17 +307,12 @@ if [ -z "$NODE_VERSION" ] || [ -z "$PM" ]; then say "" fi -if [ "$PLATFORM_KIND" = "desktop-client" ]; then - say "${GREEN}→ Desktop client platform detected. Installing desktop app bundle...${RESET}" - install_desktop_bundle -else - install_global_package "$PKG_NAME" +install_global_package "$PKG_NAME" - if [ "$INSTALL_MODE" = "desktop" ]; then - say "" - say "${GREEN}→ Linux desktop detected. Installing desktop bundle too...${RESET}" - install_desktop_bundle - fi +if [ "$INSTALL_MODE" = "desktop" ]; then + say "" + say "${GREEN}→ Desktop platform detected. The desktop app is a separate download:${RESET}" + announce_desktop_bundle fi write_install_config "$INSTALL_MODE" "$(detect_pm)" "$PLATFORM_KIND" @@ -304,17 +325,12 @@ if command_exists threatcrush; then say " ${BOLD}Detected install mode:${RESET} ${INSTALL_MODE}" say " ${BOLD}Platform kind:${RESET} ${PLATFORM_KIND}" say " ${BOLD}Preferred usage:${RESET}" - if [ "$PLATFORM_KIND" = "desktop-client" ]; then - say " ${GREEN}ThreatCrush Desktop${RESET} ${DIM}# Connect to a ThreatCrush server${RESET}" - say " ${GREEN}threatcrush update${RESET} ${DIM}# Upgrade the installed desktop bundle${RESET}" - say " ${GREEN}threatcrush remove${RESET} ${DIM}# Uninstall the installed desktop bundle${RESET}" - else - say " ${GREEN}threatcrush${RESET} ${DIM}# Setup / help${RESET}" - say " ${GREEN}threatcrush init${RESET} ${DIM}# Auto-detect services and generate config${RESET}" - say " ${GREEN}threatcrush monitor${RESET} ${DIM}# Real-time monitoring${RESET}" - say " ${GREEN}threatcrush update${RESET} ${DIM}# Upgrade CLI later using the same blessed path${RESET}" - say " ${GREEN}threatcrush remove${RESET} ${DIM}# Uninstall the installed bundle${RESET}" - fi + say " ${GREEN}threatcrush${RESET} ${DIM}# Setup / help${RESET}" + say " ${GREEN}threatcrush init${RESET} ${DIM}# Auto-detect services and generate config${RESET}" + say " ${GREEN}threatcrush monitor${RESET} ${DIM}# Real-time monitoring${RESET}" + say " ${GREEN}threatcrush monitor --tui${RESET} ${DIM}# Interactive dashboard${RESET}" + say " ${GREEN}threatcrush update${RESET} ${DIM}# Upgrade CLI later using the same blessed path${RESET}" + say " ${GREEN}threatcrush remove${RESET} ${DIM}# Uninstall the installed bundle${RESET}" say "" say " ${BOLD}Install model:${RESET}" say " ${DIM}• Primary install:${RESET} curl -fsSL https://threatcrush.com/install.sh | sh" @@ -322,12 +338,10 @@ if command_exists threatcrush; then say " ${DIM}• Platform kind:${RESET} ${PLATFORM_KIND}" say " ${DIM}• Upgrades later:${RESET} threatcrush update" say " ${DIM}• Bare machines:${RESET} installer can bootstrap Node.js with mise" - if [ "$PLATFORM_KIND" = "desktop-client" ]; then - say " ${DIM}• Desktop client:${RESET} desktop app only — connects to a ThreatCrush server elsewhere" - elif [ "$INSTALL_MODE" = "desktop" ]; then - say " ${DIM}• Linux desktop:${RESET} CLI + desktop app" + if [ "$INSTALL_MODE" = "desktop" ]; then + say " ${DIM}• Desktop:${RESET} CLI installed — the desktop app is an optional separate download" else - say " ${DIM}• Linux server:${RESET} CLI only" + say " ${DIM}• Server:${RESET} CLI only" fi else say "${RED}Installation completed but 'threatcrush' was not found on PATH.${RESET}" diff --git a/apps/web/src/__tests__/install-script.test.ts b/apps/web/src/__tests__/install-script.test.ts index df8291d..ea52486 100644 --- a/apps/web/src/__tests__/install-script.test.ts +++ b/apps/web/src/__tests__/install-script.test.ts @@ -33,10 +33,18 @@ describe("install.sh", () => { expect(installScript).toContain("Platform kind:"); }); - it("treats desktop clients differently from Linux server installs", () => { - expect(installScript).toContain('DESKTOP_PKG_NAME="@profullstack/threatcrush-desktop"'); - expect(installScript).toContain('desktop-client'); - expect(installScript).toContain('Linux server'); - expect(installScript).toContain('install_desktop_bundle'); + it("installs the CLI on every platform, including macOS and Windows", () => { + // Regression: the desktop-client branch skipped the CLI entirely and tried + // to `add -g @profullstack/threatcrush-desktop`, which has never been + // published. Under `set -e` the 404 aborted the whole install on macOS. + expect(installScript).toContain('install_global_package "$PKG_NAME"'); + expect(installScript).not.toContain("@profullstack/threatcrush-desktop"); + }); + + it("points desktop users at the GitHub Releases bundle", () => { + expect(installScript).toContain("DESKTOP_RELEASES_URL"); + expect(installScript).toContain("releases/latest"); + expect(installScript).toContain("announce_desktop_bundle"); + expect(installScript).toContain("desktop-client"); }); });