Getting the deb to work with local packages only

This commit is contained in:
aaddrick
2025-04-02 22:19:20 -04:00
parent 852ad4d2a3
commit ea467f2a57
3 changed files with 516 additions and 292 deletions

View File

@@ -31,16 +31,20 @@ fi
# Check for root/sudo
IS_SUDO=false
ORIGINAL_USER=""
ORIGINAL_HOME=""
if [ "$EUID" -eq 0 ]; then
IS_SUDO=true
# Check if running via sudo (and not directly as root)
if [ -n "$SUDO_USER" ]; then
ORIGINAL_USER="$SUDO_USER"
ORIGINAL_HOME=$(eval echo ~$ORIGINAL_USER)
ORIGINAL_HOME=$(getent passwd "$SUDO_USER" | cut -d: -f6) # More reliable way to get home dir
echo "Running with sudo as user: $ORIGINAL_USER (Home: $ORIGINAL_HOME)"
else
# Running directly as root, no original user context
ORIGINAL_USER="root"
ORIGINAL_HOME="/root"
echo "Running directly as root."
fi
else
echo "Please run with sudo to install dependencies"
@@ -52,17 +56,19 @@ if [ "$IS_SUDO" = true ] && [ "$ORIGINAL_USER" != "root" ] && [ -d "$ORIGINAL_HO
echo "Found NVM installation for user $ORIGINAL_USER, attempting to preserve npm/npx path..."
# Source NVM script to set up NVM environment variables temporarily
export NVM_DIR="$ORIGINAL_HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
if [ -s "$NVM_DIR/nvm.sh" ]; then
\. "$NVM_DIR/nvm.sh" # This loads nvm
# Find the path to the currently active or default Node version's bin directory
NODE_BIN_PATH=$(nvm which current | xargs dirname 2>/dev/null || find "$NVM_DIR/versions/node" -maxdepth 2 -type d -name 'bin' | sort -V | tail -n 1)
# Find the path to the currently active or default Node version's bin directory
# nvm_find_node_version might not be available, try finding the latest installed version
NODE_BIN_PATH=$(find "$NVM_DIR/versions/node" -maxdepth 2 -type d -name 'bin' | sort -V | tail -n 1)
if [ -n "$NODE_BIN_PATH" ] && [ -d "$NODE_BIN_PATH" ]; then
echo "Adding $NODE_BIN_PATH to PATH"
export PATH="$NODE_BIN_PATH:$PATH"
if [ -n "$NODE_BIN_PATH" ] && [ -d "$NODE_BIN_PATH" ]; then
echo "Adding $NODE_BIN_PATH to PATH"
export PATH="$NODE_BIN_PATH:$PATH"
else
echo "Warning: Could not determine NVM Node bin path. npm/npx might not be found."
fi
else
echo "Warning: Could not determine NVM Node bin path. npm/npx might not be found."
echo "Warning: nvm.sh script not found or not sourceable."
fi
fi
@@ -77,7 +83,8 @@ echo "Target Architecture: $ARCHITECTURE" # Display the target architecture
PACKAGE_NAME="claude-desktop"
MAINTAINER="Claude Desktop Linux Maintainers"
DESCRIPTION="Claude Desktop for Linux"
WORK_DIR="$(pwd)/build" # Top-level build directory
PROJECT_ROOT="$(pwd)" # Save project root
WORK_DIR="$PROJECT_ROOT/build" # Top-level build directory
APP_STAGING_DIR="$WORK_DIR/electron-app" # Staging for app files before packaging
VERSION="" # Will be determined after download
@@ -117,6 +124,43 @@ while true; do
done
echo "-------------------------------------" # Add separator after selection/before next steps
# --- End Build Format Selection ---
# --- Cleanup Selection ---
PERFORM_CLEANUP=false # Default to keeping files
display_cleanup_menu() {
echo -e "\n\033[1;34m====== Cleanup Build Files ======\033[0m"
echo -e "This refers to the intermediate files created in the '$WORK_DIR' directory."
echo -e "\033[1;32m [1] Yes, remove intermediate build files after completion\033[0m"
echo -e "\033[1;32m [2] No, keep intermediate build files\033[0m"
echo -e "\033[1;34m=================================\033[0m"
}
CLEANUP_CHOICE=""
while true; do
display_cleanup_menu
read -n 1 -p $'\nEnter choice (1 or 2, any other key to cancel prompt): ' CLEANUP_CHOICE
echo
case $CLEANUP_CHOICE in
"1")
echo -e "\033[1;36m✔ Intermediate build files will be removed upon completion.\033[0m"
PERFORM_CLEANUP=true
break
;;
"2")
echo -e "\033[1;36m✔ Intermediate build files will be kept.\033[0m"
PERFORM_CLEANUP=false
break
;;
*)
echo # Add newline for clarity
echo -e "\033[1;33m⚠ Skipping cleanup decision. Build files will be kept.\033[0m"
PERFORM_CLEANUP=false # Default to keeping files
break
;;
esac
done
echo "-------------------------------------"
# --- End Cleanup Selection ---
# Function to check if a command exists
@@ -137,7 +181,7 @@ DEPS_TO_INSTALL=""
COMMON_DEPS="p7zip wget wrestool icotool convert npx"
# Format specific dependencies
DEB_DEPS="dpkg-dev"
APPIMAGE_DEPS="" # Add appimagetool, fuse if needed later
APPIMAGE_DEPS="" # appimagetool handled by its script
ALL_DEPS_TO_CHECK="$COMMON_DEPS"
if [ "$BUILD_FORMAT" = "deb" ]; then
@@ -155,7 +199,6 @@ for cmd in $ALL_DEPS_TO_CHECK; do
"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" ;;
# Add cases for AppImage deps if needed
esac
fi
done
@@ -168,332 +211,315 @@ if [ ! -z "$DEPS_TO_INSTALL" ]; then
echo "System dependencies installed successfully"
fi
# Check for electron - first local, then global (Needed for both formats potentially)
LOCAL_ELECTRON="" # Initialize variable
# Check for local electron in node_modules
if [ -f "$(pwd)/node_modules/.bin/electron" ]; then
echo "✓ local electron found in node_modules"
LOCAL_ELECTRON="$(pwd)/node_modules/.bin/electron"
export PATH="$(pwd)/node_modules/.bin:$PATH"
elif ! check_command "electron"; then
echo "Installing electron via npm..."
# Try local installation first
if [ -f "package.json" ]; then
echo "Found package.json, installing electron locally..."
npm install --save-dev electron
if [ -f "$(pwd)/node_modules/.bin/electron" ]; then
echo "✓ Local electron installed successfully"
LOCAL_ELECTRON="$(pwd)/node_modules/.bin/electron"
export PATH="$(pwd)/node_modules/.bin:$PATH"
else
# Fall back to global installation if local fails
echo "Local electron install failed or not possible, trying global..."
npm install -g electron
# Attempt to fix permissions if installed globally
if check_command "electron"; then
ELECTRON_PATH=$(command -v electron)
echo "Attempting to fix permissions for global electron at $ELECTRON_PATH..."
chmod -R a+rx "$(dirname "$ELECTRON_PATH")/../lib/node_modules/electron" || echo "Warning: Failed to chmod global electron installation directory. Permissions might be incorrect."
fi
if ! check_command "electron"; then
echo "Failed to install electron globally. Please install it manually:"
echo "npm install -g electron # or npm install --save-dev electron in project root"
exit 1
fi
echo "Global electron installed successfully"
fi
else
# No package.json, try global installation
echo "No package.json found, trying global electron installation..."
npm install -g electron
# Attempt to fix permissions if installed globally
if check_command "electron"; then
ELECTRON_PATH=$(command -v electron)
echo "Attempting to fix permissions for global electron at $ELECTRON_PATH..."
chmod -R a+rx "$(dirname "$ELECTRON_PATH")/../lib/node_modules/electron" || echo "Warning: Failed to chmod global electron installation directory. Permissions might be incorrect."
fi
if ! check_command "electron"; then
echo "Failed to install electron globally. Please install it manually:"
echo "npm install -g electron"
exit 1
fi
echo "Global electron installed successfully"
fi
fi
# Clean previous build
# Clean previous build directory FIRST
rm -rf "$WORK_DIR"
mkdir -p "$WORK_DIR"
mkdir -p "$APP_STAGING_DIR" # Create the app staging directory explicitly
# Install asar if needed
if ! npm list -g asar > /dev/null 2>&1; then
echo "Installing asar package globally..."
npm install -g asar
# --- Electron & Asar Handling ---
CHOSEN_ELECTRON_MODULE_PATH="" # Path to the electron module directory to be copied
ASAR_EXEC="" # Path to the asar executable
TEMP_PACKAGE_JSON_CREATED=false
# Always ensure local Electron & Asar installation in WORK_DIR
echo "Ensuring local Electron and Asar installation in $WORK_DIR..."
cd "$WORK_DIR" # Change to build dir for local install
# Create dummy package.json if none exists in build dir
if [ ! -f "package.json" ]; then
echo "Creating temporary package.json in $WORK_DIR for local install..."
echo '{"name":"claude-desktop-build","version":"0.0.1","private":true}' > package.json
TEMP_PACKAGE_JSON_CREATED=true
fi
# Install electron and asar locally if not already present
# Check for Electron dist dir and Asar binary first
ELECTRON_DIST_PATH="$WORK_DIR/node_modules/electron/dist"
ASAR_BIN_PATH="$WORK_DIR/node_modules/.bin/asar"
INSTALL_NEEDED=false
if [ ! -d "$ELECTRON_DIST_PATH" ]; then
echo "Electron distribution not found."
INSTALL_NEEDED=true
fi
if [ ! -f "$ASAR_BIN_PATH" ]; then
echo "Asar binary not found."
INSTALL_NEEDED=true
fi
if [ "$INSTALL_NEEDED" = true ]; then
echo "Installing Electron and Asar locally into $WORK_DIR..."
# Use --no-save to avoid modifying the temp package.json unnecessarily
if ! npm install --no-save electron asar; then
echo "❌ Failed to install Electron and/or Asar locally."
cd "$PROJECT_ROOT"
exit 1
fi
echo "✓ Electron and Asar installation command finished."
else
echo "✓ Local Electron distribution and Asar binary already present."
fi
# Verify Electron installation by checking for the essential 'dist' directory
if [ -d "$ELECTRON_DIST_PATH" ]; then
echo "✓ Found Electron distribution directory at $ELECTRON_DIST_PATH."
CHOSEN_ELECTRON_MODULE_PATH="$(realpath "$WORK_DIR/node_modules/electron")"
echo "✓ Setting Electron module path for copying to $CHOSEN_ELECTRON_MODULE_PATH."
else
echo "❌ Failed to find Electron distribution directory at '$ELECTRON_DIST_PATH' after installation attempt."
echo " Cannot proceed without the Electron distribution files."
cd "$PROJECT_ROOT" # Go back before exiting
exit 1
fi
# Verify Asar installation
if [ -f "$ASAR_BIN_PATH" ]; then
ASAR_EXEC="$(realpath "$ASAR_BIN_PATH")"
echo "✓ Found local Asar binary at $ASAR_EXEC."
else
echo "❌ Failed to find Asar binary at '$ASAR_BIN_PATH' after installation attempt."
cd "$PROJECT_ROOT"
exit 1
fi
cd "$PROJECT_ROOT" # Go back to project root
# Final check for the chosen Electron module path (redundant but safe)
if [ -z "$CHOSEN_ELECTRON_MODULE_PATH" ] || [ ! -d "$CHOSEN_ELECTRON_MODULE_PATH" ]; then
echo "❌ Critical error: Could not resolve a valid Electron module path to copy."
exit 1
fi
echo "Using Electron module path: $CHOSEN_ELECTRON_MODULE_PATH"
echo "Using asar executable: $ASAR_EXEC"
# --- End Electron & Asar Handling ---
# Download Claude Windows installer for the target architecture
echo "📥 Downloading Claude Desktop installer for $ARCHITECTURE..."
CLAUDE_EXE_PATH="$WORK_DIR/$CLAUDE_EXE_FILENAME" # Use the arch-specific filename
CLAUDE_EXE_PATH="$WORK_DIR/$CLAUDE_EXE_FILENAME"
if ! wget -O "$CLAUDE_EXE_PATH" "$CLAUDE_DOWNLOAD_URL"; then
echo "❌ Failed to download Claude Desktop installer from $CLAUDE_DOWNLOAD_URL"
exit 1
fi
echo "✓ Download complete: $CLAUDE_EXE_FILENAME"
# Extract resources
echo "📦 Extracting resources from $CLAUDE_EXE_FILENAME..."
cd "$WORK_DIR"
if ! 7z x -y "$CLAUDE_EXE_PATH"; then # Use the arch-specific path
# Extract resources into a dedicated subdirectory to avoid conflicts
echo "📦 Extracting resources from $CLAUDE_EXE_FILENAME into separate directory..."
CLAUDE_EXTRACT_DIR="$WORK_DIR/claude-extract"
mkdir -p "$CLAUDE_EXTRACT_DIR"
if ! 7z x -y "$CLAUDE_EXE_PATH" -o"$CLAUDE_EXTRACT_DIR"; then # Extract to specific dir
echo "❌ Failed to extract installer"
exit 1
cd "$PROJECT_ROOT" && exit 1
fi
# Extract nupkg filename and version
# The nupkg name might differ slightly for arm64, adjust find pattern if needed
# Assuming it still starts with AnthropicClaude- for now
NUPKG_PATH=$(find . -name "AnthropicClaude-*.nupkg" | head -1)
if [ -z "$NUPKG_PATH" ]; then
echo "❌ Could not find AnthropicClaude nupkg file in $WORK_DIR"
exit 1
cd "$CLAUDE_EXTRACT_DIR" # Change into the extract dir to find files
NUPKG_PATH_RELATIVE=$(find . -maxdepth 1 -name "AnthropicClaude-*.nupkg" | head -1)
if [ -z "$NUPKG_PATH_RELATIVE" ]; then
echo "❌ Could not find AnthropicClaude nupkg file in $CLAUDE_EXTRACT_DIR"
cd "$PROJECT_ROOT" && exit 1
fi
echo "Found nupkg: $NUPKG_PATH"
NUPKG_PATH="$CLAUDE_EXTRACT_DIR/$NUPKG_PATH_RELATIVE" # Store full path
echo "Found nupkg: $NUPKG_PATH_RELATIVE (in $CLAUDE_EXTRACT_DIR)"
# Extract version from the nupkg filename (using LC_ALL=C for locale compatibility)
# Assuming version format is consistent across architectures
VERSION=$(echo "$NUPKG_PATH" | LC_ALL=C grep -oP 'AnthropicClaude-\K[0-9]+\.[0-9]+\.[0-9]+(?=-full|-arm64-full)') # Adjusted regex slightly
VERSION=$(echo "$NUPKG_PATH_RELATIVE" | LC_ALL=C grep -oP 'AnthropicClaude-\K[0-9]+\.[0-9]+\.[0-9]+(?=-full|-arm64-full)')
if [ -z "$VERSION" ]; then
echo "❌ Could not extract version from nupkg filename: $NUPKG_PATH"
exit 1
echo "❌ Could not extract version from nupkg filename: $NUPKG_PATH_RELATIVE"
cd "$PROJECT_ROOT" && exit 1
fi
echo "✓ Detected Claude version: $VERSION"
if ! 7z x -y "$NUPKG_PATH"; then
# Extract nupkg within its directory
if ! 7z x -y "$NUPKG_PATH_RELATIVE"; then # Use relative path since we are in CLAUDE_EXTRACT_DIR
echo "❌ Failed to extract nupkg"
exit 1
cd "$PROJECT_ROOT" && exit 1
fi
echo "✓ Resources extracted"
echo "✓ Resources extracted from nupkg"
# Extract and convert icons (needed by the packaging script later)
# Assuming claude.exe exists in the extracted structure for both archs
# Still operating within CLAUDE_EXTRACT_DIR
EXE_RELATIVE_PATH="lib/net45/claude.exe" # Check if this path is correct for arm64 too
if [ ! -f "$EXE_RELATIVE_PATH" ]; then
echo "❌ Cannot find claude.exe at expected path: $EXE_RELATIVE_PATH"
# Add alternative path check if needed for arm64
# find . -name claude.exe # Uncomment to help debug if needed
exit 1
echo "❌ Cannot find claude.exe at expected path within extraction dir: $CLAUDE_EXTRACT_DIR/$EXE_RELATIVE_PATH"
cd "$PROJECT_ROOT" && exit 1
fi
echo "🎨 Processing icons from $EXE_RELATIVE_PATH..."
if ! wrestool -x -t 14 "$EXE_RELATIVE_PATH" -o claude.ico; then
# Output icons within the extraction directory
if ! wrestool -x -t 14 "$EXE_RELATIVE_PATH" -o claude.ico; then # Output relative to current dir (CLAUDE_EXTRACT_DIR)
echo "❌ Failed to extract icons from exe"
exit 1
cd "$PROJECT_ROOT" && exit 1
fi
if ! icotool -x claude.ico; then
if ! icotool -x claude.ico; then # Input relative to current dir (CLAUDE_EXTRACT_DIR)
echo "❌ Failed to convert icons"
exit 1
cd "$PROJECT_ROOT" && exit 1
fi
echo "✓ Icons processed (will be used by packaging script)"
# Copy extracted icons to WORK_DIR for packaging scripts to find easily
cp claude_*.png "$WORK_DIR/"
echo "✓ Icons processed and copied to $WORK_DIR"
# Process app.asar
# Assuming app.asar structure is consistent across architectures
echo "⚙️ Processing app.asar..."
cp "lib/net45/resources/app.asar" "$APP_STAGING_DIR/"
cp -r "lib/net45/resources/app.asar.unpacked" "$APP_STAGING_DIR/"
# Copy resources to staging dir first, using full paths from the extraction dir
cp "$CLAUDE_EXTRACT_DIR/lib/net45/resources/app.asar" "$APP_STAGING_DIR/"
cp -a "$CLAUDE_EXTRACT_DIR/lib/net45/resources/app.asar.unpacked" "$APP_STAGING_DIR/" # Use -a to preserve links/permissions
cd "$APP_STAGING_DIR"
npx asar extract app.asar app.asar.contents
cd "$APP_STAGING_DIR" # Change to staging dir for asar processing
"$ASAR_EXEC" 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
};
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", // Keep this generic?
setWindowEffect: () => {},
removeWindowEffect: () => {},
getIsMaximized: () => false,
flashFrame: () => {},
clearFlashFrame: () => {},
showNotification: () => {},
setProgressBar: () => {},
clearProgressBar: () => {},
setOverlayIcon: () => {},
clearOverlayIcon: () => {},
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
mkdir -p app.asar.contents/resources/i18n
cp ../lib/net45/resources/Tray* app.asar.contents/resources/
# Copy only the language-specific JSON files (e.g., en-US.json)
cp ../lib/net45/resources/*-*.json app.asar.contents/resources/i18n/
# Copy from the extraction directory (use full path for clarity)
cp "$CLAUDE_EXTRACT_DIR/lib/net45/resources/Tray"* app.asar.contents/resources/
cp "$CLAUDE_EXTRACT_DIR/lib/net45/resources/"*-*.json app.asar.contents/resources/i18n/
echo "Downloading Main Window Fix Assets"
cd app.asar.contents
wget -O- https://github.com/emsi/claude-desktop/raw/refs/heads/main/assets/main_window.tgz | tar -zxvf -
cd ..
cd .. # Back to APP_STAGING_DIR
# Repackage app.asar
npx asar pack app.asar.contents app.asar
"$ASAR_EXEC" pack app.asar.contents app.asar
# Create native module stub within the staging area's unpacked directory
mkdir -p "$APP_STAGING_DIR/app.asar.unpacked/node_modules/claude-native"
cat > "$APP_STAGING_DIR/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
};
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
};
module.exports = { getWindowsVersion: () => "10.0.0", setWindowEffect: () => {}, removeWindowEffect: () => {}, getIsMaximized: () => false, flashFrame: () => {}, clearFlashFrame: () => {}, showNotification: () => {}, setProgressBar: () => {}, clearProgressBar: () => {}, setOverlayIcon: () => {}, clearOverlayIcon: () => {}, KeyboardKey };
EOF
# Copy local electron if available
if [ ! -z "$LOCAL_ELECTRON" ]; then
echo "Copying local electron to staging area..."
# Ensure the target node_modules directory exists in staging
mkdir -p "$APP_STAGING_DIR/node_modules"
# Copy the entire electron module directory
# Go up one level from the binary path to get the module root
ELECTRON_MODULE_PATH=$(dirname "$LOCAL_ELECTRON")/..
echo "Copying from $ELECTRON_MODULE_PATH to $APP_STAGING_DIR/node_modules/"
cp -r "$ELECTRON_MODULE_PATH" "$APP_STAGING_DIR/node_modules/"
# Copy the chosen electron installation to the staging area
echo "Copying chosen electron installation to staging area..."
# Ensure the target node_modules directory exists in staging
mkdir -p "$APP_STAGING_DIR/node_modules/"
# Extract the directory name to copy (e.g., "electron")
ELECTRON_DIR_NAME=$(basename "$CHOSEN_ELECTRON_MODULE_PATH")
echo "Copying from $CHOSEN_ELECTRON_MODULE_PATH to $APP_STAGING_DIR/node_modules/"
# Copy the directory itself into node_modules
cp -a "$CHOSEN_ELECTRON_MODULE_PATH" "$APP_STAGING_DIR/node_modules/" # Use cp -a to preserve links/permissions
# Explicitly set executable permission on the copied electron binary
STAGED_ELECTRON_BIN="$APP_STAGING_DIR/node_modules/$ELECTRON_DIR_NAME/dist/electron"
if [ -f "$STAGED_ELECTRON_BIN" ]; then
echo "Setting executable permission on staged Electron binary: $STAGED_ELECTRON_BIN"
chmod +x "$STAGED_ELECTRON_BIN"
else
echo "Warning: Staged Electron binary not found at expected path: $STAGED_ELECTRON_BIN"
fi
echo "✓ app.asar processed and staged in $APP_STAGING_DIR"
# Return to the original directory (project root) before calling the packaging script
# We were in $APP_STAGING_DIR which is $WORK_DIR/electron-app
cd .. # Go back from build/electron-app to build/
cd .. # Go back from build/ to the project root
cd "$PROJECT_ROOT"
# --- Call the appropriate packaging script ---
FINAL_OUTPUT_PATH="" # Initialize variable for final path
FINAL_DESKTOP_FILE_PATH="" # Initialize variable for desktop file path
if [ "$BUILD_FORMAT" = "deb" ]; then
echo "📦 Calling Debian packaging script for $ARCHITECTURE..."
# Ensure the script is executable
chmod +x scripts/build-deb-package.sh
# Execute the script, passing necessary variables including the detected ARCHITECTURE
scripts/build-deb-package.sh \
"$VERSION" \
"$ARCHITECTURE" \
"$WORK_DIR" \
"$APP_STAGING_DIR" \
"$PACKAGE_NAME" \
"$MAINTAINER" \
"$DESCRIPTION"
# Check the exit code of the packaging script
if [ $? -ne 0 ]; then
echo "❌ Debian packaging script failed."
exit 1
fi
# Capture the final deb file path (using the determined ARCHITECTURE)
# Find the .deb file in the work directory
"$VERSION" "$ARCHITECTURE" "$WORK_DIR" "$APP_STAGING_DIR" \
"$PACKAGE_NAME" "$MAINTAINER" "$DESCRIPTION"
if [ $? -ne 0 ]; then echo "❌ Debian packaging script failed."; exit 1; fi
DEB_FILE=$(find "$WORK_DIR" -maxdepth 1 -name "${PACKAGE_NAME}_${VERSION}_${ARCHITECTURE}.deb" | head -n 1)
echo "✓ Debian Build complete!"
if [ -n "$DEB_FILE" ] && [ -f "$DEB_FILE" ]; then
echo "Package created at: $DEB_FILE"
FINAL_OUTPUT_PATH="./${PACKAGE_NAME}_${VERSION}_${ARCHITECTURE}.deb"
mv "$DEB_FILE" "$FINAL_OUTPUT_PATH"
echo "Package created at: $FINAL_OUTPUT_PATH"
else
echo "Warning: Could not determine final .deb file path from $WORK_DIR for ${ARCHITECTURE}."
FINAL_OUTPUT_PATH="Not Found"
fi
elif [ "$BUILD_FORMAT" = "appimage" ]; then
echo "📦 Calling AppImage packaging script for $ARCHITECTURE..."
# Ensure the script is executable
chmod +x scripts/build-appimage.sh
# Execute the script, passing necessary variables
scripts/build-appimage.sh \
"$VERSION" \
"$ARCHITECTURE" \
"$WORK_DIR" \
"$APP_STAGING_DIR" \
"$PACKAGE_NAME" \
"$MAINTAINER" \
"$DESCRIPTION" # Maintainer/Desc might not be used but passed for consistency
# Check the exit code of the packaging script
if [ $? -ne 0 ]; then
echo "❌ AppImage packaging script failed."
exit 1
fi
# Capture the final AppImage file path (using the determined ARCHITECTURE)
# Find the .AppImage file in the work directory (adjust pattern if needed)
"$VERSION" "$ARCHITECTURE" "$WORK_DIR" "$APP_STAGING_DIR" "$PACKAGE_NAME"
if [ $? -ne 0 ]; then echo "❌ AppImage packaging script failed."; exit 1; fi
APPIMAGE_FILE=$(find "$WORK_DIR" -maxdepth 1 -name "${PACKAGE_NAME}-${VERSION}-${ARCHITECTURE}.AppImage" | head -n 1)
echo "✓ AppImage Build complete! (Placeholder)"
echo "✓ AppImage Build complete!"
if [ -n "$APPIMAGE_FILE" ] && [ -f "$APPIMAGE_FILE" ]; then
echo "Package created at: $APPIMAGE_FILE"
FINAL_OUTPUT_PATH="./${PACKAGE_NAME}-${VERSION}-${ARCHITECTURE}.AppImage"
mv "$APPIMAGE_FILE" "$FINAL_OUTPUT_PATH"
echo "Package created at: $FINAL_OUTPUT_PATH"
# --- Generate .desktop file for AppImage ---
FINAL_DESKTOP_FILE_PATH="./${PACKAGE_NAME}-appimage.desktop"
APPIMAGE_ABS_PATH=$(realpath "$FINAL_OUTPUT_PATH")
echo "📝 Generating .desktop file for AppImage at $FINAL_DESKTOP_FILE_PATH..."
cat > "$FINAL_DESKTOP_FILE_PATH" << EOF
[Desktop Entry]
Name=Claude (AppImage)
Comment=Claude Desktop (AppImage Version $VERSION)
Exec=$APPIMAGE_ABS_PATH %u
Icon=claude-desktop
Type=Application
Terminal=false
Categories=Office;Utility;Network;
MimeType=x-scheme-handler/claude;
StartupWMClass=Claude
X-AppImage-Version=$VERSION
X-AppImage-Name=Claude Desktop (AppImage)
EOF
echo "✓ .desktop file generated."
else
echo "Note: AppImage creation is currently a placeholder."
echo "Warning: Could not determine final .AppImage file path from $WORK_DIR for ${ARCHITECTURE}."
FINAL_OUTPUT_PATH="Not Found"
fi
fi
# Clean up intermediate files (optional, keep for debugging if needed)
# echo "🧹 Cleaning up intermediate files..."
# rm -rf "$WORK_DIR/lib" "$WORK_DIR/claude.ico" "$WORK_DIR"/*.png "$WORK_DIR/electron-app" "$WORK_DIR/package" "$WORK_DIR/RELEASES" "$WORK_DIR/Setup.exe" "$WORK_DIR/Setup.msi" "$WORK_DIR"/*.nupkg
# --- Set Final Package Ownership ---
if [ "$IS_SUDO" = true ] && [ "$ORIGINAL_USER" != "root" ]; then
if [ "$FINAL_OUTPUT_PATH" != "Not Found" ] && [ -e "$FINAL_OUTPUT_PATH" ]; then
echo "🔒 Setting ownership of $FINAL_OUTPUT_PATH to $ORIGINAL_USER..."
chown "$ORIGINAL_USER":"$(id -gn "$ORIGINAL_USER")" "$FINAL_OUTPUT_PATH"
echo "✓ Ownership set for package."
fi
# Set ownership for generated desktop file as well
if [ -n "$FINAL_DESKTOP_FILE_PATH" ] && [ -e "$FINAL_DESKTOP_FILE_PATH" ]; then
echo "🔒 Setting ownership of $FINAL_DESKTOP_FILE_PATH to $ORIGINAL_USER..."
chown "$ORIGINAL_USER":"$(id -gn "$ORIGINAL_USER")" "$FINAL_DESKTOP_FILE_PATH"
echo "✓ Ownership set for .desktop file."
fi
fi
# --- Cleanup ---
if [ "$PERFORM_CLEANUP" = true ]; then
echo "🧹 Cleaning up intermediate build files in $WORK_DIR..."
# Be careful not to delete the final output package or desktop file
# Keep the final package and desktop file, remove everything else in WORK_DIR
find "$WORK_DIR" -mindepth 1 -maxdepth 1 \
! -name "$(basename "$FINAL_OUTPUT_PATH" 2>/dev/null)" \
! -name "$(basename "$FINAL_DESKTOP_FILE_PATH" 2>/dev/null)" \
-exec rm -rf {} +
echo "✓ Cleanup complete."
else
echo "Skipping cleanup of intermediate build files in $WORK_DIR."
fi
# Clean up temporary package.json if created
if [ "$TEMP_PACKAGE_JSON_CREATED" = true ] && [ -f "$WORK_DIR/package.json" ]; then
echo "Removing temporary package.json from $WORK_DIR..."
rm "$WORK_DIR/package.json"
fi
echo "✅ Build process finished."
exit 0

267
scripts/build-appimage.sh Normal file → Executable file
View File

@@ -25,37 +25,250 @@ mkdir -p "$APPDIR_PATH/usr/share/icons/hicolor/256x256/apps"
mkdir -p "$APPDIR_PATH/usr/share/applications"
echo "📦 Staging application files into AppDir..."
# Copy the core application files (asar, unpacked resources)
cp -r "$APP_STAGING_DIR/"* "$APPDIR_PATH/usr/lib/" # Copy contents into usr/lib
# Copy the launcher script (needs modification for AppDir context)
# TODO: Create an AppRun script or adapt the existing launcher
# Copy icons
echo "🎨 Copying icons..."
ICON_SOURCE_PATH="$WORK_DIR/claude_6_256x256x32.png" # Assuming 256x256 icon exists from previous steps
if [ -f "$ICON_SOURCE_PATH" ]; then
cp "$ICON_SOURCE_PATH" "$APPDIR_PATH/usr/share/icons/hicolor/256x256/apps/${PACKAGE_NAME}.png"
# Also copy to top-level for AppImage icon
cp "$ICON_SOURCE_PATH" "$APPDIR_PATH/${PACKAGE_NAME}.png"
else
echo "Warning: Missing 256x256 icon at $ICON_SOURCE_PATH"
# Copy the core application files (asar, unpacked resources, node_modules if present)
# Explicitly copy required components to ensure hidden files/dirs like .bin are included
if [ -f "$APP_STAGING_DIR/app.asar" ]; then
cp -a "$APP_STAGING_DIR/app.asar" "$APPDIR_PATH/usr/lib/"
fi
if [ -d "$APP_STAGING_DIR/app.asar.unpacked" ]; then
cp -a "$APP_STAGING_DIR/app.asar.unpacked" "$APPDIR_PATH/usr/lib/"
fi
if [ -d "$APP_STAGING_DIR/node_modules" ]; then
echo "Copying node_modules from staging to AppDir..."
cp -a "$APP_STAGING_DIR/node_modules" "$APPDIR_PATH/usr/lib/"
fi
# Copy desktop file (needs modification for AppDir context)
echo "📝 Copying desktop file..."
DESKTOP_SOURCE_PATH="$APP_STAGING_DIR/../package/usr/share/applications/${PACKAGE_NAME}.desktop" # Assuming it was created for deb
# TODO: Adapt desktop file Exec= line for AppDir
# cp "$DESKTOP_SOURCE_PATH" "$APPDIR_PATH/" # Copy to top level
# Ensure Electron is bundled within the AppDir for portability
# Check if electron was copied into the staging dir's node_modules
# The actual executable is usually inside the 'dist' directory
BUNDLED_ELECTRON_PATH="$APPDIR_PATH/usr/lib/node_modules/electron/dist/electron"
echo "Checking for executable at: $BUNDLED_ELECTRON_PATH"
if [ ! -x "$BUNDLED_ELECTRON_PATH" ]; then # Check if it exists and is executable
echo "❌ Electron executable not found or not executable in staging area ($BUNDLED_ELECTRON_PATH)."
echo " AppImage requires Electron to be bundled. Ensure the main script copies it correctly."
exit 1
fi
# Ensure the bundled electron is executable (redundant check, but safe)
chmod +x "$BUNDLED_ELECTRON_PATH"
echo "🚧 AppImage building logic is not fully implemented yet."
echo " - Need to create/adapt AppRun script."
echo " - Need to adapt .desktop file."
echo " - Need to download and use appimagetool."
# --- Create AppRun Script ---
echo "🚀 Creating AppRun script..."
# Note: We use $VERSION and $PACKAGE_NAME from the build script environment here
# They will be embedded into the AppRun script.
DESKTOP_FILE_BASENAME="${PACKAGE_NAME}-${VERSION}.desktop" # Unique name
cat > "$APPDIR_PATH/AppRun" << EOF
#!/bin/bash
set -e
# Example placeholder for final step:
# appimagetool "$APPDIR_PATH" "$WORK_DIR/${PACKAGE_NAME}-${VERSION}-${ARCHITECTURE}.AppImage"
# Find the location of the AppRun script and the AppImage file itself
APPDIR=\$(dirname "\$0")
# Try to get the absolute path of the AppImage file being run
# $APPIMAGE is often set by the AppImage runtime, otherwise try readlink
APPIMAGE_PATH="\${APPIMAGE:-}"
if [ -z "\$APPIMAGE_PATH" ]; then
# Find the AppRun script itself, which should be $0
# Use readlink -f to get the absolute path, handling symlinks
# Go up one level from AppRun's dir to get the AppImage path (usually)
# This might be fragile if AppRun is not at the root, but it's standard.
APPIMAGE_PATH=\$(readlink -f "\$APPDIR/../$(basename "$APPDIR" .AppDir).AppImage" 2>/dev/null || readlink -f "\$0" 2>/dev/null)
# As a final fallback, just use $0, hoping it's the AppImage path
if [ -z "\$APPIMAGE_PATH" ] || [ ! -f "\$APPIMAGE_PATH" ]; then
APPIMAGE_PATH="\$0"
fi
fi
echo "--- AppImage Build Placeholder Finished ---"
# --- Attempt to Register claude:// URI Scheme Handler ---
register_uri_scheme() {
local desktop_file_basename="$1"
local appimage_exec_path="$2"
local scheme="claude"
echo "AppRun: Attempting to register x-scheme-handler/$scheme..."
# Check if necessary tools exist
if ! command -v xdg-mime >/dev/null 2>&1 || ! command -v update-desktop-database >/dev/null 2>&1; then
echo "AppRun: Warning - xdg-mime or update-desktop-database not found. Cannot register URI scheme."
return 1
fi
# Define user's local applications directory
local user_apps_dir="\$HOME/.local/share/applications"
mkdir -p "\$user_apps_dir"
local desktop_file_path="\$user_apps_dir/\$desktop_file_basename"
echo "AppRun: Creating desktop file at \$desktop_file_path"
# Create the .desktop file
# Use the determined absolute path to the AppImage for Exec
cat > "\$desktop_file_path" << DESKTOP_EOF
[Desktop Entry]
Name=Claude (AppImage $VERSION)
Comment=Claude Desktop (AppImage Version $VERSION)
Exec=$appimage_exec_path %u
Icon=$PACKAGE_NAME
Type=Application
Terminal=false
Categories=Office;Utility;Network;
MimeType=x-scheme-handler/$scheme;
StartupWMClass=Claude
X-AppImage-Version=$VERSION
X-AppImage-Name=Claude Desktop (AppImage)
DESKTOP_EOF
echo "AppRun: Running xdg-mime default..."
xdg-mime default "\$desktop_file_basename" "x-scheme-handler/\$scheme"
echo "AppRun: Running update-desktop-database..."
update-desktop-database "\$user_apps_dir"
echo "AppRun: URI scheme registration attempted."
}
# Run registration in the background to avoid delaying app start significantly
# Pass the unique desktop file name and the determined AppImage path
register_uri_scheme "$DESKTOP_FILE_BASENAME" "\$APPIMAGE_PATH" &
# --- End URI Scheme Handler Registration ---
# Set up environment variables if needed (e.g., LD_LIBRARY_PATH)
# export LD_LIBRARY_PATH="\$APPDIR/usr/lib:\$LD_LIBRARY_PATH"
# Detect if Wayland is likely running
IS_WAYLAND=false
if [ ! -z "\$WAYLAND_DISPLAY" ]; then
IS_WAYLAND=true
fi
# Path to the bundled Electron executable
# Use the path relative to AppRun within the 'electron/dist' module directory
ELECTRON_EXEC="\$APPDIR/usr/lib/node_modules/electron/dist/electron"
APP_PATH="\$APPDIR/usr/lib/app.asar"
# Base command arguments array
ELECTRON_ARGS=("\$APP_PATH")
# Add Wayland flags if Wayland is detected
if [ "\$IS_WAYLAND" = true ]; then
echo "AppRun: Wayland detected, adding flags."
ELECTRON_ARGS+=("--enable-features=UseOzonePlatform,WaylandWindowDecorations" "--ozone-platform=wayland")
fi
# Change to the application resources directory (where app.asar is)
cd "\$APPDIR/usr/lib" || exit 1
# Execute Electron with app path, flags, and script arguments passed to AppRun
echo "AppRun: Executing \$ELECTRON_EXEC \${ELECTRON_ARGS[@]} \$@"
exec "\$ELECTRON_EXEC" "\${ELECTRON_ARGS[@]}" "\$@"
EOF
chmod +x "$APPDIR_PATH/AppRun"
echo "✓ AppRun script created with URI scheme registration logic"
# --- Create Desktop Entry (Bundled inside AppDir) ---
echo "📝 Creating bundled desktop entry..."
# Use package name for icon (AppImage tools expect this)
ICON_NAME=$PACKAGE_NAME
# This is the desktop file *inside* the AppImage, used by tools like appimaged
cat > "$APPDIR_PATH/$PACKAGE_NAME.desktop" << EOF
[Desktop Entry]
Name=Claude
Exec=AppRun %u
Icon=$ICON_NAME
Type=Application
Terminal=false
Categories=Office;Utility;Network;
Comment=Claude Desktop for Linux
MimeType=x-scheme-handler/claude;
StartupWMClass=Claude
X-AppImage-Version=$VERSION
X-AppImage-Name=Claude Desktop
EOF
# Also place it in the standard location for tools like appimaged
cp "$APPDIR_PATH/$PACKAGE_NAME.desktop" "$APPDIR_PATH/usr/share/applications/"
echo "✓ Bundled desktop entry created"
# --- Copy Icons ---
echo "🎨 Copying icons..."
# Use the 256x256 icon as the main AppImage icon
ICON_SOURCE_PATH="$WORK_DIR/claude_6_256x256x32.png"
if [ -f "$ICON_SOURCE_PATH" ]; then
# Standard location within AppDir
cp "$ICON_SOURCE_PATH" "$APPDIR_PATH/usr/share/icons/hicolor/256x256/apps/${PACKAGE_NAME}.png"
# Top-level icon (used by appimagetool)
cp "$ICON_SOURCE_PATH" "$APPDIR_PATH/${ICON_NAME}.png"
echo "✓ Icon copied"
else
echo "Warning: Missing 256x256 icon at $ICON_SOURCE_PATH. AppImage icon might be missing."
fi
# --- Get appimagetool ---
APPIMAGETOOL_PATH=""
if command -v appimagetool &> /dev/null; then
APPIMAGETOOL_PATH=$(command -v appimagetool)
echo "✓ Found appimagetool in PATH: $APPIMAGETOOL_PATH"
elif [ -f "$WORK_DIR/appimagetool-x86_64.AppImage" ]; then # Check for specific arch first
APPIMAGETOOL_PATH="$WORK_DIR/appimagetool-x86_64.AppImage"
echo "✓ Found downloaded x86_64 appimagetool: $APPIMAGETOOL_PATH"
elif [ -f "$WORK_DIR/appimagetool-aarch64.AppImage" ]; then # Check for other arch
APPIMAGETOOL_PATH="$WORK_DIR/appimagetool-aarch64.AppImage"
echo "✓ Found downloaded aarch64 appimagetool: $APPIMAGETOOL_PATH"
else
echo "🛠️ Downloading appimagetool..."
# Determine architecture for download URL
TOOL_ARCH=""
case "$ARCHITECTURE" in
"amd64") TOOL_ARCH="x86_64" ;;
"arm64") TOOL_ARCH="aarch64" ;;
*) echo "❌ Unsupported architecture for appimagetool download: $ARCHITECTURE"; exit 1 ;;
esac
APPIMAGETOOL_URL="https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${TOOL_ARCH}.AppImage"
APPIMAGETOOL_PATH="$WORK_DIR/appimagetool-${TOOL_ARCH}.AppImage"
if wget -q -O "$APPIMAGETOOL_PATH" "$APPIMAGETOOL_URL"; then
chmod +x "$APPIMAGETOOL_PATH"
echo "✓ Downloaded appimagetool to $APPIMAGETOOL_PATH"
else
echo "❌ Failed to download appimagetool from $APPIMAGETOOL_URL"
rm -f "$APPIMAGETOOL_PATH" # Clean up partial download
exit 1
fi
fi
# --- Build AppImage ---
echo "📦 Building AppImage..."
OUTPUT_FILENAME="${PACKAGE_NAME}-${VERSION}-${ARCHITECTURE}.AppImage"
OUTPUT_PATH="$WORK_DIR/$OUTPUT_FILENAME"
# Ensure chrome-sandbox has correct permissions within AppDir before building
SANDBOX_PATH="$APPDIR_PATH/usr/lib/node_modules/electron/dist/chrome-sandbox"
if [ -f "$SANDBOX_PATH" ]; then
echo "Setting permissions for bundled chrome-sandbox..."
# No need for chown root:root inside AppDir, just setuid
chmod 4755 "$SANDBOX_PATH" || echo "Warning: Failed to chmod chrome-sandbox"
else
# Try alternative sandbox path sometimes found directly in electron dir
SANDBOX_PATH_ALT="$APPDIR_PATH/usr/lib/node_modules/electron/chrome-sandbox"
if [ -f "$SANDBOX_PATH_ALT" ]; then
echo "Setting permissions for bundled chrome-sandbox (alternative path)..."
chmod 4755 "$SANDBOX_PATH_ALT" || echo "Warning: Failed to chmod chrome-sandbox (alternative path)"
SANDBOX_PATH="$SANDBOX_PATH_ALT" # Update SANDBOX_PATH if found here
else
echo "Warning: Bundled chrome-sandbox not found at standard or alternative paths."
fi
fi
# Execute appimagetool
# Export ARCH instead of using env
export ARCH="$ARCHITECTURE"
echo "Using ARCH=$ARCH" # Debug output
if "$APPIMAGETOOL_PATH" "$APPDIR_PATH" "$OUTPUT_PATH"; then
echo "✓ AppImage built successfully: $OUTPUT_PATH"
else
echo "❌ Failed to build AppImage using $APPIMAGETOOL_PATH"
exit 1
fi
echo "--- AppImage Build Finished ---"
exit 0

View File

@@ -153,10 +153,8 @@ echo "✓ Launcher script created"
echo "📄 Creating control file..."
# Determine dependencies based on whether electron was packaged
DEPENDS="nodejs, npm, p7zip-full" # Base dependencies
if [ ! -d "$INSTALL_DIR/lib/$PACKAGE_NAME/node_modules/electron" ]; then
DEPENDS="$DEPENDS, electron" # Add electron if not packaged locally
echo "Adding 'electron' to Depends list as it was not packaged locally."
fi
# Electron is now always packaged locally, so it's not listed as an external dependency.
echo "Electron is packaged locally; not adding to external Depends list."
cat > "$PACKAGE_ROOT/DEBIAN/control" << EOF
Package: $PACKAGE_NAME
@@ -186,23 +184,10 @@ update-desktop-database /usr/share/applications &> /dev/null || true
# Set correct permissions for chrome-sandbox if electron is installed globally or locally packaged
echo "Setting chrome-sandbox permissions..."
SANDBOX_PATH=""
# Check for sandbox in locally packaged electron first
# Electron is always packaged locally now, so only check the local path.
LOCAL_SANDBOX_PATH="/usr/lib/$PACKAGE_NAME/node_modules/electron/dist/chrome-sandbox"
if [ -f "\$LOCAL_SANDBOX_PATH" ]; then
SANDBOX_PATH="\$LOCAL_SANDBOX_PATH"
# If not found locally, try to find it in the path of the globally installed electron
elif command -v electron >/dev/null 2>&1; then
ELECTRON_PATH=\$(command -v electron)
# Try to find sandbox relative to the electron binary path
# Common locations: ../lib/node_modules/electron/dist/ or directly beside electron binary
POTENTIAL_SANDBOX_1="\$(dirname "\$ELECTRON_PATH")/../lib/node_modules/electron/dist/chrome-sandbox"
POTENTIAL_SANDBOX_2="\$(dirname "\$ELECTRON_PATH")/chrome-sandbox"
if [ -f "\$POTENTIAL_SANDBOX_1" ]; then
SANDBOX_PATH="\$POTENTIAL_SANDBOX_1"
elif [ -f "\$POTENTIAL_SANDBOX_2" ]; then
SANDBOX_PATH="\$POTENTIAL_SANDBOX_2"
fi
fi
if [ -n "\$SANDBOX_PATH" ] && [ -f "\$SANDBOX_PATH" ]; then
@@ -211,7 +196,7 @@ if [ -n "\$SANDBOX_PATH" ] && [ -f "\$SANDBOX_PATH" ]; then
chmod 4755 "\$SANDBOX_PATH" || echo "Warning: Failed to chmod chrome-sandbox"
echo "Permissions set for \$SANDBOX_PATH"
else
echo "Warning: chrome-sandbox binary not found (checked local package and global path). Sandbox may not function correctly."
echo "Warning: chrome-sandbox binary not found in local package at \$LOCAL_SANDBOX_PATH. Sandbox may not function correctly."
fi
exit 0