#!/bin/bash set -e # Update this URL when a new version of Claude Desktop is released CLAUDE_DOWNLOAD_URL="https://storage.googleapis.com/osprey-downloads-c02f6a0d-347c-492b-a752-3e0651722e97/nest-win-x64/Claude-Setup-x64.exe" # Check for Debian-based system if [ ! -f "/etc/debian_version" ]; then echo "❌ This script requires a Debian-based Linux distribution" exit 1 fi # Check for root/sudo if [ "$EUID" -ne 0 ]; then echo "Please run with sudo to install dependencies" exit 1 fi # Print system information echo "System Information:" echo "Distribution: $(cat /etc/os-release | grep "PRETTY_NAME" | cut -d'"' -f2)" echo "Debian version: $(cat /etc/debian_version)" # Function to check if a command exists check_command() { if ! command -v "$1" &> /dev/null; then echo "❌ $1 not found" return 1 else echo "✓ $1 found" return 0 fi } # Check and install dependencies echo "Checking dependencies..." DEPS_TO_INSTALL="" # Check system package dependencies for cmd in p7zip wget wrestool icotool convert npx dpkg-deb; do if ! check_command "$cmd"; then case "$cmd" in "p7zip") DEPS_TO_INSTALL="$DEPS_TO_INSTALL p7zip-full" ;; "wget") DEPS_TO_INSTALL="$DEPS_TO_INSTALL wget" ;; "wrestool"|"icotool") DEPS_TO_INSTALL="$DEPS_TO_INSTALL icoutils" ;; "convert") DEPS_TO_INSTALL="$DEPS_TO_INSTALL imagemagick" ;; "npx") DEPS_TO_INSTALL="$DEPS_TO_INSTALL nodejs npm" ;; "dpkg-deb") DEPS_TO_INSTALL="$DEPS_TO_INSTALL dpkg-dev" ;; esac fi done # Install system dependencies if any if [ ! -z "$DEPS_TO_INSTALL" ]; then echo "Installing system dependencies: $DEPS_TO_INSTALL" apt update apt install -y $DEPS_TO_INSTALL echo "System dependencies installed successfully" fi # Install electron globally via npm if not present if ! check_command "electron"; then echo "Installing electron via npm..." npm install -g electron if ! check_command "electron"; then echo "Failed to install electron. Please install it manually:" echo "sudo npm install -g electron" exit 1 fi echo "Electron installed successfully" fi # Extract version from the installer filename VERSION=$(basename "$CLAUDE_DOWNLOAD_URL" | grep -oP 'Claude-Setup-x64\.exe' | sed 's/Claude-Setup-x64\.exe/0.7.7/') PACKAGE_NAME="claude-desktop" ARCHITECTURE="amd64" MAINTAINER="Claude Desktop Linux Maintainers" DESCRIPTION="Claude Desktop for Linux" # Create working directories WORK_DIR="$(pwd)/build" DEB_ROOT="$WORK_DIR/deb-package" INSTALL_DIR="$DEB_ROOT/usr" # Clean previous build rm -rf "$WORK_DIR" mkdir -p "$WORK_DIR" mkdir -p "$DEB_ROOT/DEBIAN" mkdir -p "$INSTALL_DIR/lib/$PACKAGE_NAME" mkdir -p "$INSTALL_DIR/share/applications" mkdir -p "$INSTALL_DIR/share/icons" mkdir -p "$INSTALL_DIR/bin" # Install asar if needed if ! npm list -g asar > /dev/null 2>&1; then echo "Installing asar package globally..." npm install -g asar fi # Download Claude Windows installer echo "📥 Downloading Claude Desktop installer..." CLAUDE_EXE="$WORK_DIR/Claude-Setup-x64.exe" if ! wget -O "$CLAUDE_EXE" "$CLAUDE_DOWNLOAD_URL"; then echo "❌ Failed to download Claude Desktop installer" exit 1 fi echo "✓ Download complete" # Extract resources echo "📦 Extracting resources..." cd "$WORK_DIR" if ! 7z x -y "$CLAUDE_EXE"; then echo "❌ Failed to extract installer" exit 1 fi if ! 7z x -y "AnthropicClaude-$VERSION-full.nupkg"; then echo "❌ Failed to extract nupkg" exit 1 fi echo "✓ Resources extracted" # Extract and convert icons echo "🎨 Processing icons..." if ! wrestool -x -t 14 "lib/net45/claude.exe" -o claude.ico; then echo "❌ Failed to extract icons from exe" exit 1 fi if ! icotool -x claude.ico; then echo "❌ Failed to convert icons" exit 1 fi echo "✓ Icons processed" # Map icon sizes to their corresponding extracted files declare -A icon_files=( ["16"]="claude_13_16x16x32.png" ["24"]="claude_11_24x24x32.png" ["32"]="claude_10_32x32x32.png" ["48"]="claude_8_48x48x32.png" ["64"]="claude_7_64x64x32.png" ["256"]="claude_6_256x256x32.png" ) # Install icons for size in 16 24 32 48 64 256; do icon_dir="$INSTALL_DIR/share/icons/hicolor/${size}x${size}/apps" mkdir -p "$icon_dir" if [ -f "${icon_files[$size]}" ]; then echo "Installing ${size}x${size} icon..." install -Dm 644 "${icon_files[$size]}" "$icon_dir/claude-desktop.png" else echo "Warning: Missing ${size}x${size} icon" fi done # Process app.asar mkdir -p electron-app cp "lib/net45/resources/app.asar" electron-app/ cp -r "lib/net45/resources/app.asar.unpacked" electron-app/ cd electron-app npx asar extract app.asar app.asar.contents # Replace native module with stub implementation echo "Creating stub native module..." cat > app.asar.contents/node_modules/claude-native/index.js << EOF // Stub implementation of claude-native using KeyboardKey enum values const KeyboardKey = { Backspace: 43, Tab: 280, Enter: 261, Shift: 272, Control: 61, Alt: 40, CapsLock: 56, Escape: 85, Space: 276, PageUp: 251, PageDown: 250, End: 83, Home: 154, LeftArrow: 175, UpArrow: 282, RightArrow: 262, DownArrow: 81, Delete: 79, Meta: 187 }; Object.freeze(KeyboardKey); module.exports = { getWindowsVersion: () => "10.0.0", setWindowEffect: () => {}, removeWindowEffect: () => {}, getIsMaximized: () => false, flashFrame: () => {}, clearFlashFrame: () => {}, showNotification: () => {}, setProgressBar: () => {}, clearProgressBar: () => {}, setOverlayIcon: () => {}, clearOverlayIcon: () => {}, KeyboardKey }; EOF # Copy Tray icons mkdir -p app.asar.contents/resources cp ../lib/net45/resources/Tray* app.asar.contents/resources/ # Repackage app.asar npx asar pack app.asar.contents app.asar # Create native module with keyboard constants mkdir -p "$INSTALL_DIR/lib/$PACKAGE_NAME/app.asar.unpacked/node_modules/claude-native" cat > "$INSTALL_DIR/lib/$PACKAGE_NAME/app.asar.unpacked/node_modules/claude-native/index.js" << EOF // Stub implementation of claude-native using KeyboardKey enum values const KeyboardKey = { Backspace: 43, Tab: 280, Enter: 261, Shift: 272, Control: 61, Alt: 40, CapsLock: 56, Escape: 85, Space: 276, PageUp: 251, PageDown: 250, End: 83, Home: 154, LeftArrow: 175, UpArrow: 282, RightArrow: 262, DownArrow: 81, Delete: 79, Meta: 187 }; Object.freeze(KeyboardKey); module.exports = { getWindowsVersion: () => "10.0.0", setWindowEffect: () => {}, removeWindowEffect: () => {}, getIsMaximized: () => false, flashFrame: () => {}, clearFlashFrame: () => {}, showNotification: () => {}, setProgressBar: () => {}, clearProgressBar: () => {}, setOverlayIcon: () => {}, clearOverlayIcon: () => {}, KeyboardKey }; EOF # Copy app files cp app.asar "$INSTALL_DIR/lib/$PACKAGE_NAME/" cp -r app.asar.unpacked "$INSTALL_DIR/lib/$PACKAGE_NAME/" # Create desktop entry cat > "$INSTALL_DIR/share/applications/claude-desktop.desktop" << EOF [Desktop Entry] Name=Claude Exec=claude-desktop %u Icon=claude-desktop Type=Application Terminal=false Categories=Office;Utility; MimeType=x-scheme-handler/claude; EOF # Create launcher script cat > "$INSTALL_DIR/bin/claude-desktop" << EOF #!/bin/bash electron /usr/lib/claude-desktop/app.asar "\$@" EOF chmod +x "$INSTALL_DIR/bin/claude-desktop" # Create control file cat > "$DEB_ROOT/DEBIAN/control" << EOF Package: claude-desktop Version: $VERSION Architecture: $ARCHITECTURE Maintainer: $MAINTAINER Depends: nodejs, npm, p7zip-full Description: $DESCRIPTION Claude is an AI assistant from Anthropic. This package provides the desktop interface for Claude. . Supported on Debian-based Linux distributions (Debian, Ubuntu, Linux Mint, MX Linux, etc.) Requires: nodejs (>= 12.0.0), npm EOF # Build .deb package echo "📦 Building .deb package..." DEB_FILE="$(pwd)/claude-desktop_${VERSION}_${ARCHITECTURE}.deb" if ! dpkg-deb --build "$DEB_ROOT" "$DEB_FILE"; then echo "❌ Failed to build .deb package" exit 1 fi if [ -f "$DEB_FILE" ]; then echo "✓ Package built successfully at: $DEB_FILE" echo "🎉 Done! You can now install the package with: sudo dpkg -i $DEB_FILE" else echo "❌ Package file not found at expected location: $DEB_FILE" exit 1 fi