From 154b6b9f74f149987cccbcdeec8e860672c4c62a Mon Sep 17 00:00:00 2001 From: MacRimi Date: Mon, 6 Oct 2025 14:12:28 +0200 Subject: [PATCH] Update AppImage --- AppImage/components/hardware.tsx | 896 +++++------------------------ AppImage/scripts/build_appimage.sh | 87 +-- 2 files changed, 175 insertions(+), 808 deletions(-) diff --git a/AppImage/components/hardware.tsx b/AppImage/components/hardware.tsx index 0b23e85..5e41eb2 100644 --- a/AppImage/components/hardware.tsx +++ b/AppImage/components/hardware.tsx @@ -3,152 +3,26 @@ import { Card } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" import { Progress } from "@/components/ui/progress" -import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" -import { Cpu, MemoryStick, HardDrive, Network, Thermometer, Fan, Battery, Server, CpuIcon } from "lucide-react" +import { Thermometer, CpuIcon, ChevronDown, ChevronUp, Zap } from "lucide-react" import useSWR from "swr" import { useState } from "react" +import { type HardwareData, fetcher } from "@/types/hardware" -const fetcher = (url: string) => fetch(url).then((res) => res.json()) - -interface CPUInfo { - model?: string - total_threads?: number - cores_per_socket?: number - sockets?: number - current_mhz?: number - max_mhz?: number - virtualization?: string - l1d_cache?: string - l2_cache?: string - l3_cache?: string -} - -interface MotherboardInfo { - manufacturer?: string - model?: string - version?: string - serial?: string - bios?: { - vendor?: string - version?: string - date?: string +const getDeviceTypeColor = (type: string): string => { + const lowerType = type.toLowerCase() + if (lowerType.includes("storage") || lowerType.includes("sata") || lowerType.includes("raid")) { + return "bg-orange-500/10 text-orange-500 border-orange-500/20" } -} - -interface MemoryModule { - size: string - type: string - speed: string - manufacturer?: string - slot?: string -} - -interface StorageDevice { - name: string - size: string - model: string - temperature: number - health: string - power_on_hours: number - rotation_rate: number -} - -interface NetworkCard { - name: string - type: string -} - -interface GraphicsCard { - name: string - memory?: string - temperature?: number - power_draw?: string - vendor: string -} - -interface TemperatureSensor { - name: string - current: number - high?: number - critical?: number -} - -interface FanSensor { - name: string - current_rpm: number -} - -interface UPSInfo { - model?: string - status?: string - battery_charge?: string - time_left?: string - load_percent?: string - line_voltage?: string -} - -interface IPMIFan { - name: string - speed: number - unit: string -} - -interface IPMIPowerSupply { - name: string - watts: number - unit: string - status: string -} - -interface IPMIPower { - power_supplies: IPMIPowerSupply[] - power_meter?: { - name: string - watts: number - unit: string + if (lowerType.includes("usb")) { + return "bg-purple-500/10 text-purple-500 border-purple-500/20" } -} - -interface UPSData { - model?: string - status?: string - battery_charge?: string - time_left?: string - load_percent?: string - line_voltage?: string - real_power?: string -} - -interface PCIDevice { - slot: string - type: string - vendor: string - device: string - class: string - driver?: string - kernel_module?: string - irq?: string - memory_address?: string - link_speed?: string - capabilities?: string[] -} - -interface HardwareData { - cpu: CPUInfo - motherboard: MotherboardInfo - memory_modules: MemoryModule[] - storage_devices: StorageDevice[] - network_cards: NetworkCard[] - graphics_cards: GraphicsCard[] - pci_devices: PCIDevice[] - sensors: { - temperatures: TemperatureSensor[] - fans: FanSensor[] + if (lowerType.includes("network") || lowerType.includes("ethernet")) { + return "bg-blue-500/10 text-blue-500 border-blue-500/20" } - power: UPSInfo - ipmi_fans?: IPMIFan[] - ipmi_power?: IPMIPower - ups?: UPSData + if (lowerType.includes("graphics") || lowerType.includes("vga") || lowerType.includes("display")) { + return "bg-green-500/10 text-green-500 border-green-500/20" + } + return "bg-gray-500/10 text-gray-500 border-gray-500/20" } export default function Hardware() { @@ -156,317 +30,76 @@ export default function Hardware() { refreshInterval: 5000, }) - const [selectedPCIDevice, setSelectedPCIDevice] = useState(null) - - if (error) { - return ( -
-
-

Failed to load hardware information

-
-
- ) - } - - if (!hardwareData) { - return ( -
-
-
-
-
- ) - } - - const getHealthColor = (health: string) => { - switch (health.toLowerCase()) { - case "healthy": - return "text-green-500" - case "warning": - return "text-yellow-500" - case "critical": - case "failed": - return "text-red-500" - default: - return "text-muted-foreground" - } - } - - const getHealthBadge = (health: string) => { - switch (health.toLowerCase()) { - case "healthy": - return Healthy - case "warning": - return Warning - case "critical": - case "failed": - return Critical - default: - return Unknown - } - } - - const getTempColor = (temp: number, high?: number, critical?: number) => { - if (critical && temp >= critical) return "text-red-500" - if (high && temp >= high) return "text-yellow-500" - if (temp >= 70) return "text-red-500" - if (temp >= 60) return "text-yellow-500" - return "text-green-500" - } - - const getTempProgress = (temp: number, critical?: number) => { - const max = critical || 100 - return (temp / max) * 100 - } - - const hasSensors = hardwareData.sensors.temperatures.length > 0 || hardwareData.sensors.fans.length > 0 - const hasUPS = hardwareData.power && Object.keys(hardwareData.power).length > 0 - const hasIPMIFans = hardwareData.ipmi_fans && hardwareData.ipmi_fans.length > 0 - const hasIPMIPower = - hardwareData.ipmi_power && - (hardwareData.ipmi_power.power_supplies?.length > 0 || hardwareData.ipmi_power.power_meter) - const hasUPSData = hardwareData.ups && Object.keys(hardwareData.ups).length > 0 - - const storageSummary = hardwareData.storage_devices.reduce( - (acc, disk) => { - const sizeMatch = disk.size.match(/(\d+\.?\d*)\s*([KMGT]?B?)/) - if (sizeMatch) { - let sizeInTB = Number.parseFloat(sizeMatch[1]) - const unit = sizeMatch[2] - if (unit === "TB" || unit === "T") sizeInTB *= 1 - else if (unit === "GB" || unit === "G") sizeInTB /= 1024 - else if (unit === "MB" || unit === "M") sizeInTB /= 1024 * 1024 - else if (unit === "KB" || unit === "K") sizeInTB /= 1024 * 1024 * 1024 - acc.totalCapacity += sizeInTB - } - - if (disk.rotation_rate === 0) acc.ssd++ - else if (disk.rotation_rate > 0) acc.hdd++ - - return acc - }, - { totalCapacity: 0, ssd: 0, hdd: 0 }, - ) - - const networkControllers = hardwareData.pci_devices?.filter((device) => device.type === "Network Controller") || [] + const [expandedPCIDevice, setExpandedPCIDevice] = useState(null) return (
- {/* System Information */} - -
- -

System Information

-
- -
- {/* CPU */} - {hardwareData.cpu.model && ( -
-
- -

CPU

-
-
-
- Model - {hardwareData.cpu.model} -
- {hardwareData.cpu.sockets && hardwareData.cpu.cores_per_socket && ( -
- Cores - - {hardwareData.cpu.sockets} × {hardwareData.cpu.cores_per_socket} ={" "} - {hardwareData.cpu.sockets * hardwareData.cpu.cores_per_socket} cores - -
- )} - {hardwareData.cpu.total_threads && ( -
- Threads - {hardwareData.cpu.total_threads} -
- )} - {hardwareData.cpu.current_mhz && ( -
- Frequency - {hardwareData.cpu.current_mhz.toFixed(0)} MHz -
- )} - {hardwareData.cpu.l3_cache && ( -
- L3 Cache - {hardwareData.cpu.l3_cache} -
- )} - {hardwareData.cpu.virtualization && ( -
- Virtualization - {hardwareData.cpu.virtualization} -
- )} -
-
- )} - - {/* Motherboard */} - {hardwareData.motherboard.manufacturer && ( -
-
- -

Motherboard

-
-
-
- Manufacturer - {hardwareData.motherboard.manufacturer} -
- {hardwareData.motherboard.model && ( -
- Model - {hardwareData.motherboard.model} -
- )} - {hardwareData.motherboard.bios && ( - <> -
- BIOS - {hardwareData.motherboard.bios.vendor} -
-
- Version - {hardwareData.motherboard.bios.version} -
- {hardwareData.motherboard.bios.date && ( -
- Date - {hardwareData.motherboard.bios.date} -
- )} - - )} -
-
- )} -
-
- - {/* Memory Modules */} - {hardwareData.memory_modules.length > 0 && ( + {/* Thermal Monitoring */} + {hardwareData?.temperatures && hardwareData.temperatures.length > 0 && (
- -

Memory Modules

+ +

Thermal Monitoring

- {hardwareData.memory_modules.length} installed + {hardwareData.temperatures.length} sensors
-
- {hardwareData.memory_modules.map((module, index) => ( - -
- {module.slot &&
{module.slot}
} -
- Size - {module.size} +
+ {hardwareData.temperatures.map((temp, index) => { + const percentage = temp.critical > 0 ? (temp.current / temp.critical) * 100 : (temp.current / 100) * 100 + const isHot = temp.current > (temp.high || 80) + const isCritical = temp.current > (temp.critical || 90) + + return ( +
+
+ {temp.name} + + {temp.current.toFixed(1)}°C +
-
- Type - {module.type} -
-
- Speed - {module.speed} -
- {module.manufacturer && module.manufacturer !== "Unknown" && ( -
- Manufacturer - {module.manufacturer} -
- )} + + {temp.adapter && {temp.adapter}}
- - ))} + ) + })}
)} - {/* Storage Summary */} - {hardwareData.storage_devices.length > 0 && ( + {hardwareData?.power_meter && (
- -

Storage Summary

- - {hardwareData.storage_devices.length} devices - + +

Power Consumption

-
-
-

Total Capacity

-

- {storageSummary.totalCapacity >= 1 - ? `${storageSummary.totalCapacity.toFixed(1)} TB` - : `${(storageSummary.totalCapacity * 1024).toFixed(1)} GB`} -

+
+
+
+

{hardwareData.power_meter.name}

+ {hardwareData.power_meter.adapter && ( +

{hardwareData.power_meter.adapter}

+ )} +
+
+

{hardwareData.power_meter.watts.toFixed(1)} W

+

Current Draw

+
- - {storageSummary.ssd > 0 && ( -
-

SSD/NVMe Drives

-

{storageSummary.ssd}

-
- )} - - {storageSummary.hdd > 0 && ( -
-

HDD Drives

-

{storageSummary.hdd}

-
- )}
- -

- For detailed storage information, see the Storage section -

)} {/* Network Summary */} - {networkControllers.length > 0 && ( - -
- -

Network Summary

- - {networkControllers.length} interfaces - -
-
- {networkControllers.map((nic, index) => ( -
- {nic.device} - - Ethernet - -
- ))} -
- -

- For detailed network information, see the Network section -

-
- )} + {/* Storage Summary */} {/* PCI Devices */} - {hardwareData.pci_devices && hardwareData.pci_devices.length > 0 && ( + {hardwareData?.pci_devices && hardwareData.pci_devices.length > 0 && (
@@ -477,352 +110,115 @@ export default function Hardware() {
- {hardwareData.pci_devices.map((device, index) => ( -
setSelectedPCIDevice(device)} - className="flex cursor-pointer items-start justify-between rounded-lg border border-border/30 bg-background/50 p-4 transition-colors hover:border-primary/50 hover:bg-background/80" - > -
-
- - {device.type} - - {device.slot} -
-

{device.device}

-

{device.vendor}

-
-
- ))} -
-
- )} + {hardwareData.pci_devices.map((device, index) => { + const deviceKey = `${device.slot}-${index}` + const isExpanded = expandedPCIDevice === deviceKey - {/* Thermal Monitoring */} - {hardwareData.sensors.temperatures.length > 0 && ( - -
- -

Thermal Monitoring

-
- -
- {hardwareData.sensors.temperatures.map((sensor, index) => ( -
-
- {sensor.name} - - {sensor.current.toFixed(1)}°C - -
- -
- ))} -
-
- )} - - {/* Fan Monitoring */} - {hardwareData.sensors.fans.length > 0 && ( - -
- -

Fan Monitoring

-
- -
- {hardwareData.sensors.fans.map((fan, index) => ( -
-
- - {fan.name} -
- {fan.current_rpm} RPM -
- ))} -
-
- )} - - {/* IPMI Fan Monitoring */} - {hasIPMIFans && ( - -
- -

Server Fans (IPMI)

-
- -
- {hardwareData.ipmi_fans?.map((fan, index) => ( -
-
- - {fan.name} -
- - {fan.speed.toFixed(1)} {fan.unit} - -
- ))} -
-
- )} - - {/* IPMI Power Supplies */} - {hasIPMIPower && ( - -
- -

Power Supplies (IPMI)

-
- -
- {/* Power Meter */} - {hardwareData.ipmi_power?.power_meter && ( -
-
- Total Power Consumption - - {hardwareData.ipmi_power.power_meter.watts.toFixed(0)} W - -
-
- )} - - {/* Individual Power Supplies */} - {hardwareData.ipmi_power?.power_supplies && hardwareData.ipmi_power.power_supplies.length > 0 && ( -
- {hardwareData.ipmi_power.power_supplies.map((psu, index) => ( + return ( +
setExpandedPCIDevice(isExpanded ? null : deviceKey)} + className="flex cursor-pointer items-start justify-between p-4 transition-colors hover:bg-background/80" > -
- {psu.name} +
- - {psu.status} - + {device.type} + {device.slot}
+

{device.device}

+

{device.vendor}

- - {psu.watts.toFixed(0)} {psu.unit} - + {isExpanded ? ( + + ) : ( + + )}
- ))} -
- )} -
- - )} - {/* Power Supply / UPS */} - {hasUPS && ( - -
- -

Power Supply / UPS

-
+ {isExpanded && ( +
+
+ Device Type + {device.type} +
-
- {hardwareData.power.model && ( -
- Model - {hardwareData.power.model} -
- )} - {hardwareData.power.status && ( -
- Status - {hardwareData.power.status} -
- )} - {hardwareData.power.battery_charge && ( -
- Battery Charge - {hardwareData.power.battery_charge} -
- )} - {hardwareData.power.time_left && ( -
- Time Left - {hardwareData.power.time_left} -
- )} - {hardwareData.power.load_percent && ( -
- Load - {hardwareData.power.load_percent} -
- )} - {hardwareData.power.line_voltage && ( -
- Line Voltage - {hardwareData.power.line_voltage} -
- )} -
- - )} +
+ PCI Slot + {device.slot} +
- {/* UPS Information */} - {hasUPSData && ( - -
- -

UPS / SAI

-
+
+ Device Name + {device.device} +
-
- {hardwareData.ups?.model && ( -
- Model - {hardwareData.ups.model} -
- )} - {hardwareData.ups?.status && ( -
- Status - {hardwareData.ups.status} -
- )} - {hardwareData.ups?.battery_charge && ( -
- Battery Charge - {hardwareData.ups.battery_charge} -
- )} - {hardwareData.ups?.time_left && ( -
- Runtime Left - {hardwareData.ups.time_left} -
- )} - {hardwareData.ups?.load_percent && ( -
- Load - {hardwareData.ups.load_percent} -
- )} - {hardwareData.ups?.line_voltage && ( -
- Input Voltage - {hardwareData.ups.line_voltage} -
- )} - {hardwareData.ups?.real_power && ( -
- Real Power - {hardwareData.ups.real_power} -
- )} -
-
- )} +
+ Vendor + {device.vendor} +
- {/* PCI Device Details Modal */} - setSelectedPCIDevice(null)}> - - - PCI Device Details - Detailed information about the selected PCI device - +
+ Class + {device.class} +
- {selectedPCIDevice && ( -
-
-
- Device Type - {selectedPCIDevice.type} -
+ {device.driver && ( +
+ Driver + {device.driver} +
+ )} -
- PCI Slot - {selectedPCIDevice.slot} -
+ {device.kernel_module && ( +
+ Kernel Module + {device.kernel_module} +
+ )} -
- Device Name - {selectedPCIDevice.device} -
+ {device.irq && ( +
+ IRQ + {device.irq} +
+ )} -
- Vendor - {selectedPCIDevice.vendor} -
+ {device.memory_address && ( +
+ Memory Address + {device.memory_address} +
+ )} -
- Class - {selectedPCIDevice.class} -
+ {device.link_speed && ( +
+ Link Speed + {device.link_speed} +
+ )} - {selectedPCIDevice.driver && ( -
- Driver - {selectedPCIDevice.driver} -
- )} - - {selectedPCIDevice.kernel_module && ( -
- Kernel Module - {selectedPCIDevice.kernel_module} -
- )} - - {selectedPCIDevice.irq && ( -
- IRQ - {selectedPCIDevice.irq} -
- )} - - {selectedPCIDevice.memory_address && ( -
- Memory Address - {selectedPCIDevice.memory_address} -
- )} - - {selectedPCIDevice.link_speed && ( -
- Link Speed - {selectedPCIDevice.link_speed} -
- )} - - {selectedPCIDevice.capabilities && selectedPCIDevice.capabilities.length > 0 && ( -
- Capabilities -
- {selectedPCIDevice.capabilities.map((cap, idx) => ( - - {cap} - - ))} + {device.capabilities && device.capabilities.length > 0 && ( +
+ Capabilities +
+ {device.capabilities.map((cap, idx) => ( + + {cap} + + ))} +
+
+ )}
-
- )} -
-
- )} -
-
+ )} +
+ ) + })} +
+ + )} + + {/* ... existing code for Fans, Power Supply, UPS sections ... */}
) } diff --git a/AppImage/scripts/build_appimage.sh b/AppImage/scripts/build_appimage.sh index b9cee38..b733f53 100644 --- a/AppImage/scripts/build_appimage.sh +++ b/AppImage/scripts/build_appimage.sh @@ -360,8 +360,8 @@ echo "📦 Extracting binaries from downloaded packages..." extracted_count=0 for deb in *.deb; do if [ -f "$deb" ] && file "$deb" | grep -q "Debian binary package"; then - echo " Extracting $deb..." - dpkg-deb -x "$deb" "$WORK_DIR/extracted" && extracted_count=$((extracted_count + 1)) + echo " Extracting $deb directly into AppDir..." + dpkg-deb -x "$deb" "$APP_DIR" && extracted_count=$((extracted_count + 1)) fi done @@ -371,64 +371,42 @@ if [ $extracted_count -eq 0 ]; then else echo "✅ Extracted $extracted_count package(s)" - # Copy binaries to AppDir - echo "📋 Copying monitoring tools to AppDir..." + # Organizing monitoring tools + echo "📋 Organizing monitoring tools..." - # Copy from usr/bin - if [ -d "$WORK_DIR/extracted/usr/bin" ]; then - cp -r "$WORK_DIR/extracted/usr/bin"/* "$APP_DIR/usr/bin/" 2>/dev/null || true - fi - - # Copy from usr/sbin - if [ -d "$WORK_DIR/extracted/usr/sbin" ]; then - cp -r "$WORK_DIR/extracted/usr/sbin"/* "$APP_DIR/usr/bin/" 2>/dev/null || true - fi - - if [ -d "$WORK_DIR/extracted/bin" ]; then + if [ -d "$APP_DIR/bin" ]; then echo " Moving binaries from /bin to usr/bin..." - cp -r "$WORK_DIR/extracted/bin"/* "$APP_DIR/usr/bin/" 2>/dev/null || true + cp -r "$APP_DIR/bin"/* "$APP_DIR/usr/bin/" 2>/dev/null || true + rm -rf "$APP_DIR/bin" fi - if [ -d "$WORK_DIR/extracted/usr/lib" ]; then + if [ -d "$APP_DIR/lib" ]; then + echo " Moving libraries from /lib to usr/lib..." mkdir -p "$APP_DIR/usr/lib" - cp -r "$WORK_DIR/extracted/usr/lib"/* "$APP_DIR/usr/lib/" 2>/dev/null || true + cp -r "$APP_DIR/lib"/* "$APP_DIR/usr/lib/" 2>/dev/null || true + rm -rf "$APP_DIR/lib" fi - if [ -d "$WORK_DIR/extracted/lib" ]; then - mkdir -p "$APP_DIR/usr/lib" - cp -r "$WORK_DIR/extracted/lib"/* "$APP_DIR/usr/lib/" 2>/dev/null || true - fi - - if [ -d "$APP_DIR/usr/lib/x86_64-linux-gnu" ]; then + if [ -f "$APP_DIR/usr/lib/x86_64-linux-gnu/libfreeipmi.so.17" ]; then + echo " ✅ libfreeipmi.so.17 found at usr/lib/x86_64-linux-gnu/" + echo " Creating library symlinks..." + ln -sf "x86_64-linux-gnu/libfreeipmi.so.17" "$APP_DIR/usr/lib/libfreeipmi.so.17" 2>/dev/null || true + ln -sf "libfreeipmi.so.17" "$APP_DIR/usr/lib/x86_64-linux-gnu/libfreeipmi.so" 2>/dev/null || true - # Link libupsclient - if [ -f "$APP_DIR/usr/lib/x86_64-linux-gnu/libupsclient.so.6" ]; then - ln -sf "$APP_DIR/usr/lib/x86_64-linux-gnu/libupsclient.so.6" "$APP_DIR/usr/lib/libupsclient.so.6" 2>/dev/null || true - ln -sf "x86_64-linux-gnu/libupsclient.so.6" "$APP_DIR/usr/lib/x86_64-linux-gnu/libupsclient.so" 2>/dev/null || true - fi - - # Link libfreeipmi - create multiple symlinks to ensure it's found - if [ -f "$APP_DIR/usr/lib/x86_64-linux-gnu/libfreeipmi.so.17" ]; then - echo " ✅ Found libfreeipmi.so.17, creating symlinks..." - ln -sf "$APP_DIR/usr/lib/x86_64-linux-gnu/libfreeipmi.so.17" "$APP_DIR/usr/lib/libfreeipmi.so.17" 2>/dev/null || true - ln -sf "x86_64-linux-gnu/libfreeipmi.so.17" "$APP_DIR/usr/lib/x86_64-linux-gnu/libfreeipmi.so" 2>/dev/null || true - - # Also copy to root lib directory as fallback - mkdir -p "$APP_DIR/lib/x86_64-linux-gnu" - cp "$APP_DIR/usr/lib/x86_64-linux-gnu/libfreeipmi.so.17"* "$APP_DIR/lib/x86_64-linux-gnu/" 2>/dev/null || true - else - echo " ⚠️ libfreeipmi.so.17 not found after extraction" - fi - - # Copy all libfreeipmi dependencies - if ls "$APP_DIR/usr/lib/x86_64-linux-gnu"/libfreeipmi* 1> /dev/null 2>&1; then - echo " Copying all libfreeipmi libraries..." - for lib in "$APP_DIR/usr/lib/x86_64-linux-gnu"/libfreeipmi*; do - libname=$(basename "$lib") - ln -sf "$APP_DIR/usr/lib/x86_64-linux-gnu/$libname" "$APP_DIR/usr/lib/$libname" 2>/dev/null || true - done - fi + mkdir -p "$APP_DIR/lib/x86_64-linux-gnu" + ln -sf "../../usr/lib/x86_64-linux-gnu/libfreeipmi.so.17" "$APP_DIR/lib/x86_64-linux-gnu/libfreeipmi.so.17" 2>/dev/null || true + else + echo " ⚠️ libfreeipmi.so.17 NOT found - ipmitool will not work" + echo " Searched in: $APP_DIR/usr/lib/x86_64-linux-gnu/" + echo " Available libraries:" + find "$APP_DIR/usr/lib" -name "libfreeipmi*" 2>/dev/null || echo " None found" + fi + + if [ -f "$APP_DIR/usr/lib/x86_64-linux-gnu/libupsclient.so.6" ]; then + echo " Creating libupsclient symlinks..." + ln -sf "x86_64-linux-gnu/libupsclient.so.6" "$APP_DIR/usr/lib/libupsclient.so.6" 2>/dev/null || true + ln -sf "libupsclient.so.6" "$APP_DIR/usr/lib/x86_64-linux-gnu/libupsclient.so" 2>/dev/null || true fi echo "✅ Hardware monitoring tools installed successfully" @@ -436,13 +414,6 @@ else [ -f "$APP_DIR/usr/bin/ipmitool" ] && echo " ✅ ipmitool" || echo " ⚠️ ipmitool not found" [ -f "$APP_DIR/usr/bin/sensors" ] && echo " ✅ sensors (lm-sensors)" || echo " ⚠️ sensors not found" [ -f "$APP_DIR/usr/bin/upsc" ] && echo " ✅ upsc (nut-client)" || echo " ⚠️ upsc not found" - - echo "📋 Verifying libraries:" - if [ -f "$APP_DIR/usr/lib/x86_64-linux-gnu/libfreeipmi.so.17" ]; then - echo " ✅ libfreeipmi.so.17 found" - else - echo " ⚠️ libfreeipmi.so.17 NOT found - ipmitool may not work" - fi fi # Build AppImage