appclean 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +41 -0
- package/.github/workflows/test.yml +37 -0
- package/ACTION_CHECKLIST.md +342 -0
- package/APPCLEAN_SUMMARY.md +309 -0
- package/CHANGELOG.md +205 -0
- package/CODE_OF_CONDUCT.md +49 -0
- package/CODE_REVIEW_REPORT.md +447 -0
- package/COMMUNITY_POSTS.md +307 -0
- package/CONTRIBUTING.md +121 -0
- package/DEPLOYMENT_GUIDE.md +345 -0
- package/DEPLOYMENT_STATUS.md +182 -0
- package/EXECUTIVE_REPORT.md +393 -0
- package/GITHUB_OPTIMIZATION.md +383 -0
- package/INDEX.md +165 -0
- package/LICENSE +21 -0
- package/MARKETING_SUMMARY.md +352 -0
- package/NPM_PACKAGE_OPTIMIZATION.md +281 -0
- package/NPM_PUBLISH.md +116 -0
- package/PROJECT_SUMMARY.txt +249 -0
- package/QUICKSTART.md +219 -0
- package/README.md +548 -0
- package/SECURITY.md +104 -0
- package/SETUP_GITHUB.md +237 -0
- package/TESTING_SUMMARY.md +379 -0
- package/dist/core/appUpdateChecker.d.ts +23 -0
- package/dist/core/appUpdateChecker.d.ts.map +1 -0
- package/dist/core/appUpdateChecker.js +159 -0
- package/dist/core/appUpdateChecker.js.map +1 -0
- package/dist/core/detector.d.ts +13 -0
- package/dist/core/detector.d.ts.map +1 -0
- package/dist/core/detector.js +99 -0
- package/dist/core/detector.js.map +1 -0
- package/dist/core/duplicateFileFinder.d.ts +14 -0
- package/dist/core/duplicateFileFinder.d.ts.map +1 -0
- package/dist/core/duplicateFileFinder.js +80 -0
- package/dist/core/duplicateFileFinder.js.map +1 -0
- package/dist/core/orphanedDependencyDetector.d.ts +19 -0
- package/dist/core/orphanedDependencyDetector.d.ts.map +1 -0
- package/dist/core/orphanedDependencyDetector.js +148 -0
- package/dist/core/orphanedDependencyDetector.js.map +1 -0
- package/dist/core/performanceOptimizer.d.ts +37 -0
- package/dist/core/performanceOptimizer.d.ts.map +1 -0
- package/dist/core/performanceOptimizer.js +128 -0
- package/dist/core/performanceOptimizer.js.map +1 -0
- package/dist/core/permissionHandler.d.ts +9 -0
- package/dist/core/permissionHandler.d.ts.map +1 -0
- package/dist/core/permissionHandler.js +89 -0
- package/dist/core/permissionHandler.js.map +1 -0
- package/dist/core/pluginSystem.d.ts +39 -0
- package/dist/core/pluginSystem.d.ts.map +1 -0
- package/dist/core/pluginSystem.js +120 -0
- package/dist/core/pluginSystem.js.map +1 -0
- package/dist/core/removalRecorder.d.ts +32 -0
- package/dist/core/removalRecorder.d.ts.map +1 -0
- package/dist/core/removalRecorder.js +79 -0
- package/dist/core/removalRecorder.js.map +1 -0
- package/dist/core/remover.d.ts +15 -0
- package/dist/core/remover.d.ts.map +1 -0
- package/dist/core/remover.js +225 -0
- package/dist/core/remover.js.map +1 -0
- package/dist/core/reportGenerator.d.ts +9 -0
- package/dist/core/reportGenerator.d.ts.map +1 -0
- package/dist/core/reportGenerator.js +328 -0
- package/dist/core/reportGenerator.js.map +1 -0
- package/dist/core/scheduledCleanup.d.ts +38 -0
- package/dist/core/scheduledCleanup.d.ts.map +1 -0
- package/dist/core/scheduledCleanup.js +127 -0
- package/dist/core/scheduledCleanup.js.map +1 -0
- package/dist/core/serviceFileDetector.d.ts +18 -0
- package/dist/core/serviceFileDetector.d.ts.map +1 -0
- package/dist/core/serviceFileDetector.js +136 -0
- package/dist/core/serviceFileDetector.js.map +1 -0
- package/dist/core/verificationModule.d.ts +14 -0
- package/dist/core/verificationModule.d.ts.map +1 -0
- package/dist/core/verificationModule.js +102 -0
- package/dist/core/verificationModule.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +333 -0
- package/dist/index.js.map +1 -0
- package/dist/managers/brewManager.d.ts +10 -0
- package/dist/managers/brewManager.d.ts.map +1 -0
- package/dist/managers/brewManager.js +130 -0
- package/dist/managers/brewManager.js.map +1 -0
- package/dist/managers/customManager.d.ts +8 -0
- package/dist/managers/customManager.d.ts.map +1 -0
- package/dist/managers/customManager.js +139 -0
- package/dist/managers/customManager.js.map +1 -0
- package/dist/managers/linuxManager.d.ts +10 -0
- package/dist/managers/linuxManager.d.ts.map +1 -0
- package/dist/managers/linuxManager.js +191 -0
- package/dist/managers/linuxManager.js.map +1 -0
- package/dist/managers/npmManager.d.ts +10 -0
- package/dist/managers/npmManager.d.ts.map +1 -0
- package/dist/managers/npmManager.js +119 -0
- package/dist/managers/npmManager.js.map +1 -0
- package/dist/types/index.d.ts +44 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/ui/guiServer.d.ts +10 -0
- package/dist/ui/guiServer.d.ts.map +1 -0
- package/dist/ui/guiServer.js +134 -0
- package/dist/ui/guiServer.js.map +1 -0
- package/dist/ui/menu.d.ts +6 -0
- package/dist/ui/menu.d.ts.map +1 -0
- package/dist/ui/menu.js +93 -0
- package/dist/ui/menu.js.map +1 -0
- package/dist/ui/prompts.d.ts +13 -0
- package/dist/ui/prompts.d.ts.map +1 -0
- package/dist/ui/prompts.js +161 -0
- package/dist/ui/prompts.js.map +1 -0
- package/dist/utils/filesystem.d.ts +13 -0
- package/dist/utils/filesystem.d.ts.map +1 -0
- package/dist/utils/filesystem.js +152 -0
- package/dist/utils/filesystem.js.map +1 -0
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +49 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/platform.d.ts +9 -0
- package/dist/utils/platform.d.ts.map +1 -0
- package/dist/utils/platform.js +75 -0
- package/dist/utils/platform.js.map +1 -0
- package/jest.config.js +20 -0
- package/logo.svg +60 -0
- package/package.json +55 -0
- package/setup-github.sh +125 -0
- package/src/core/appUpdateChecker.ts +220 -0
- package/src/core/detector.ts +133 -0
- package/src/core/duplicateFileFinder.ts +113 -0
- package/src/core/orphanedDependencyDetector.ts +195 -0
- package/src/core/performanceOptimizer.ts +209 -0
- package/src/core/permissionHandler.ts +121 -0
- package/src/core/pluginSystem.ts +194 -0
- package/src/core/removalRecorder.ts +146 -0
- package/src/core/remover.ts +280 -0
- package/src/core/reportGenerator.ts +354 -0
- package/src/core/scheduledCleanup.ts +204 -0
- package/src/core/serviceFileDetector.ts +181 -0
- package/src/core/verificationModule.ts +140 -0
- package/src/index.ts +449 -0
- package/src/managers/brewManager.ts +149 -0
- package/src/managers/customManager.ts +167 -0
- package/src/managers/linuxManager.ts +210 -0
- package/src/managers/npmManager.ts +137 -0
- package/src/types/index.ts +59 -0
- package/src/ui/guiServer.ts +155 -0
- package/src/ui/menu.ts +100 -0
- package/src/ui/prompts.ts +177 -0
- package/src/utils/filesystem.ts +145 -0
- package/src/utils/logger.ts +48 -0
- package/src/utils/platform.ts +75 -0
- package/tsconfig.json +20 -0
package/logo.svg
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<!-- Background Circle -->
|
|
3
|
+
<circle cx="256" cy="256" r="256" fill="#1F2937"/>
|
|
4
|
+
|
|
5
|
+
<!-- Inner Circle -->
|
|
6
|
+
<circle cx="256" cy="256" r="240" fill="#111827"/>
|
|
7
|
+
|
|
8
|
+
<!-- Gradient Definition -->
|
|
9
|
+
<defs>
|
|
10
|
+
<linearGradient id="gradientBlue" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
11
|
+
<stop offset="0%" style="stop-color:#3B82F6;stop-opacity:1" />
|
|
12
|
+
<stop offset="100%" style="stop-color:#1E40AF;stop-opacity:1" />
|
|
13
|
+
</linearGradient>
|
|
14
|
+
<linearGradient id="gradientGreen" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
15
|
+
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
|
|
16
|
+
<stop offset="100%" style="stop-color:#059669;stop-opacity:1" />
|
|
17
|
+
</linearGradient>
|
|
18
|
+
</defs>
|
|
19
|
+
|
|
20
|
+
<!-- Main Trash/Cleanup Icon -->
|
|
21
|
+
<!-- Trash can body -->
|
|
22
|
+
<path d="M 150 180 L 362 180 L 380 200 L 132 200 Z" fill="url(#gradientBlue)" opacity="0.9"/>
|
|
23
|
+
|
|
24
|
+
<!-- Trash can top/lid -->
|
|
25
|
+
<rect x="160" y="160" width="192" height="25" rx="4" fill="url(#gradientBlue)" opacity="0.7"/>
|
|
26
|
+
|
|
27
|
+
<!-- Handle -->
|
|
28
|
+
<path d="M 220 160 Q 220 140 256 140 Q 292 140 292 160" stroke="url(#gradientBlue)" stroke-width="12" fill="none" stroke-linecap="round"/>
|
|
29
|
+
|
|
30
|
+
<!-- Trash can container -->
|
|
31
|
+
<rect x="140" y="200" width="232" height="160" rx="8" fill="url(#gradientBlue)" opacity="0.8"/>
|
|
32
|
+
|
|
33
|
+
<!-- Checkmark (clean/success) - overlaid on trash -->
|
|
34
|
+
<g transform="translate(256, 280)">
|
|
35
|
+
<!-- Checkmark circle background -->
|
|
36
|
+
<circle cx="0" cy="0" r="55" fill="url(#gradientGreen)" opacity="0.95"/>
|
|
37
|
+
|
|
38
|
+
<!-- White checkmark -->
|
|
39
|
+
<path d="M -20 0 L -5 15 L 25 -20" stroke="white" stroke-width="8" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
|
|
40
|
+
</g>
|
|
41
|
+
|
|
42
|
+
<!-- Decorative elements - removed items -->
|
|
43
|
+
<!-- Floating particles representing cleanup -->
|
|
44
|
+
<circle cx="100" cy="150" r="8" fill="#3B82F6" opacity="0.6"/>
|
|
45
|
+
<circle cx="412" cy="160" r="6" fill="#10B981" opacity="0.6"/>
|
|
46
|
+
<circle cx="110" cy="350" r="7" fill="#10B981" opacity="0.5"/>
|
|
47
|
+
<circle cx="400" cy="360" r="5" fill="#3B82F6" opacity="0.5"/>
|
|
48
|
+
|
|
49
|
+
<!-- Floating lines (particles dissolving) -->
|
|
50
|
+
<line x1="95" y1="140" x2="70" y2="110" stroke="#3B82F6" stroke-width="2" opacity="0.4"/>
|
|
51
|
+
<line x1="420" y1="180" x2="450" y2="150" stroke="#10B981" stroke-width="2" opacity="0.4"/>
|
|
52
|
+
<line x1="105" y1="370" x2="75" y2="400" stroke="#10B981" stroke-width="2" opacity="0.4"/>
|
|
53
|
+
<line x1="410" y1="375" x2="445" y2="410" stroke="#3B82F6" stroke-width="2" opacity="0.4"/>
|
|
54
|
+
|
|
55
|
+
<!-- Text: APPCLEAN -->
|
|
56
|
+
<text x="256" y="440" font-family="Inter, -apple-system, system-ui, sans-serif" font-size="32" font-weight="700" text-anchor="middle" fill="white" letter-spacing="-0.5">AppClean</text>
|
|
57
|
+
|
|
58
|
+
<!-- Tagline -->
|
|
59
|
+
<text x="256" y="470" font-family="Inter, -apple-system, system-ui, sans-serif" font-size="12" text-anchor="middle" fill="#9CA3AF" letter-spacing="0.5">Clean • Safe • Smart</text>
|
|
60
|
+
</svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "appclean",
|
|
3
|
+
"version": "1.8.0",
|
|
4
|
+
"description": "A powerful CLI tool to intelligently find and safely uninstall applications from your system with all their artifacts",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"appclean": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "ts-node src/index.ts",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"test": "jest",
|
|
14
|
+
"clean": "rm -rf dist",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"uninstaller",
|
|
19
|
+
"app-management",
|
|
20
|
+
"cli",
|
|
21
|
+
"cross-platform",
|
|
22
|
+
"npm",
|
|
23
|
+
"brew",
|
|
24
|
+
"package-manager",
|
|
25
|
+
"cleanup"
|
|
26
|
+
],
|
|
27
|
+
"author": "Praveen Kothapally <dev@praveenkothapally.dev>",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"chalk": "^4.1.2",
|
|
31
|
+
"commander": "^11.1.0",
|
|
32
|
+
"inquirer": "^8.2.6",
|
|
33
|
+
"ora": "^5.4.1"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^20.10.0",
|
|
37
|
+
"@types/inquirer": "^8.2.10",
|
|
38
|
+
"typescript": "^5.3.3",
|
|
39
|
+
"ts-node": "^10.9.2",
|
|
40
|
+
"jest": "^29.7.0",
|
|
41
|
+
"@types/jest": "^29.5.11",
|
|
42
|
+
"ts-jest": "^29.1.1"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=16.0.0"
|
|
46
|
+
},
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "https://github.com/praveenkay/AppClean.git"
|
|
50
|
+
},
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/praveenkay/AppClean/issues"
|
|
53
|
+
},
|
|
54
|
+
"homepage": "https://github.com/praveenkay/AppClean#readme"
|
|
55
|
+
}
|
package/setup-github.sh
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# AppClean GitHub Setup Script
|
|
4
|
+
# This script automates the setup and deployment process
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
echo "╔════════════════════════════════════════════════════════════════╗"
|
|
9
|
+
echo "║ AppClean - GitHub Setup Automation ║"
|
|
10
|
+
echo "╚════════════════════════════════════════════════════════════════╝"
|
|
11
|
+
echo ""
|
|
12
|
+
|
|
13
|
+
# Colors
|
|
14
|
+
RED='\033[0;31m'
|
|
15
|
+
GREEN='\033[0;32m'
|
|
16
|
+
YELLOW='\033[1;33m'
|
|
17
|
+
BLUE='\033[0;34m'
|
|
18
|
+
NC='\033[0m' # No Color
|
|
19
|
+
|
|
20
|
+
# Check prerequisites
|
|
21
|
+
echo -e "${BLUE}[1/5]${NC} Checking prerequisites..."
|
|
22
|
+
if ! command -v git &> /dev/null; then
|
|
23
|
+
echo -e "${RED}❌ Git is not installed${NC}"
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
if ! command -v npm &> /dev/null; then
|
|
28
|
+
echo -e "${RED}❌ npm is not installed${NC}"
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
if ! command -v node &> /dev/null; then
|
|
33
|
+
echo -e "${RED}❌ Node.js is not installed${NC}"
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
echo -e "${GREEN}✓ Git, npm, and Node.js found${NC}"
|
|
38
|
+
echo ""
|
|
39
|
+
|
|
40
|
+
# Get GitHub username
|
|
41
|
+
echo -e "${BLUE}[2/5]${NC} Getting your GitHub username..."
|
|
42
|
+
read -p "Enter your GitHub username: " GITHUB_USERNAME
|
|
43
|
+
|
|
44
|
+
if [ -z "$GITHUB_USERNAME" ]; then
|
|
45
|
+
echo -e "${RED}❌ GitHub username is required${NC}"
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
echo -e "${GREEN}✓ GitHub username: $GITHUB_USERNAME${NC}"
|
|
50
|
+
echo ""
|
|
51
|
+
|
|
52
|
+
# Get email and name
|
|
53
|
+
echo -e "${BLUE}[3/5]${NC} Getting your information..."
|
|
54
|
+
read -p "Enter your name: " USER_NAME
|
|
55
|
+
read -p "Enter your email: " USER_EMAIL
|
|
56
|
+
|
|
57
|
+
if [ -z "$USER_NAME" ] || [ -z "$USER_EMAIL" ]; then
|
|
58
|
+
echo -e "${RED}❌ Name and email are required${NC}"
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
echo -e "${GREEN}✓ Name: $USER_NAME${NC}"
|
|
63
|
+
echo -e "${GREEN}✓ Email: $USER_EMAIL${NC}"
|
|
64
|
+
echo ""
|
|
65
|
+
|
|
66
|
+
# Configure git
|
|
67
|
+
echo -e "${BLUE}[4/5]${NC} Configuring Git..."
|
|
68
|
+
git config user.name "$USER_NAME"
|
|
69
|
+
git config user.email "$USER_EMAIL"
|
|
70
|
+
|
|
71
|
+
# Update package.json
|
|
72
|
+
sed -i '' "s|YOUR_USERNAME|$GITHUB_USERNAME|g" package.json
|
|
73
|
+
sed -i '' "s|Your Name|$USER_NAME|g" package.json
|
|
74
|
+
|
|
75
|
+
echo -e "${GREEN}✓ Git configured${NC}"
|
|
76
|
+
echo -e "${GREEN}✓ package.json updated${NC}"
|
|
77
|
+
echo ""
|
|
78
|
+
|
|
79
|
+
# Setup git remote
|
|
80
|
+
echo -e "${BLUE}[5/5]${NC} Setting up Git remote..."
|
|
81
|
+
|
|
82
|
+
git remote rm origin 2>/dev/null || true
|
|
83
|
+
git remote add origin "https://github.com/$GITHUB_USERNAME/appclean.git"
|
|
84
|
+
|
|
85
|
+
# Rename master to main
|
|
86
|
+
if git rev-parse --verify master &>/dev/null; then
|
|
87
|
+
git branch -m master main
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
echo -e "${GREEN}✓ Git remote configured${NC}"
|
|
91
|
+
echo ""
|
|
92
|
+
|
|
93
|
+
# Summary
|
|
94
|
+
echo "╔════════════════════════════════════════════════════════════════╗"
|
|
95
|
+
echo -e "${GREEN}✓ Setup Complete!${NC}"
|
|
96
|
+
echo "╚════════════════════════════════════════════════════════════════╝"
|
|
97
|
+
echo ""
|
|
98
|
+
|
|
99
|
+
echo -e "${YELLOW}Next Steps:${NC}"
|
|
100
|
+
echo ""
|
|
101
|
+
echo "1. Create GitHub repository:"
|
|
102
|
+
echo " 👉 Visit: https://github.com/new"
|
|
103
|
+
echo " - Name: appclean"
|
|
104
|
+
echo " - Description: A powerful CLI tool to intelligently find and safely uninstall applications"
|
|
105
|
+
echo " - Public"
|
|
106
|
+
echo ""
|
|
107
|
+
echo "2. Push to GitHub:"
|
|
108
|
+
echo " ${BLUE}git push -u origin main${NC}"
|
|
109
|
+
echo ""
|
|
110
|
+
echo "3. Setup npm:"
|
|
111
|
+
echo " ${BLUE}npm login${NC}"
|
|
112
|
+
echo ""
|
|
113
|
+
echo "4. Publish to npm:"
|
|
114
|
+
echo " ${BLUE}git tag v1.0.0${NC}"
|
|
115
|
+
echo " ${BLUE}git push origin v1.0.0${NC}"
|
|
116
|
+
echo " ${BLUE}npm publish${NC}"
|
|
117
|
+
echo ""
|
|
118
|
+
echo -e "${YELLOW}Repository will be at:${NC}"
|
|
119
|
+
echo " 📦 GitHub: https://github.com/$GITHUB_USERNAME/appclean"
|
|
120
|
+
echo " 📝 npm: https://npmjs.com/package/appclean"
|
|
121
|
+
echo ""
|
|
122
|
+
echo -e "${YELLOW}For detailed instructions, see:${NC}"
|
|
123
|
+
echo " 📖 SETUP_GITHUB.md"
|
|
124
|
+
echo " 📖 QUICKSTART.md"
|
|
125
|
+
echo ""
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App Update Checker
|
|
3
|
+
* Checks for available updates for installed applications
|
|
4
|
+
* v1.7.0 Feature
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { execSync } from 'child_process';
|
|
8
|
+
import { Logger } from '../utils/logger';
|
|
9
|
+
import { InstalledApp } from '../types';
|
|
10
|
+
|
|
11
|
+
export interface UpdateInfo {
|
|
12
|
+
appName: string;
|
|
13
|
+
currentVersion: string;
|
|
14
|
+
latestVersion: string;
|
|
15
|
+
hasUpdate: boolean;
|
|
16
|
+
releaseDate?: Date;
|
|
17
|
+
changelog?: string;
|
|
18
|
+
installCommand?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class AppUpdateChecker {
|
|
22
|
+
/**
|
|
23
|
+
* Check for updates for a specific app
|
|
24
|
+
*/
|
|
25
|
+
async checkForUpdate(app: InstalledApp): Promise<UpdateInfo> {
|
|
26
|
+
Logger.info(`Checking for updates: ${app.name}...`);
|
|
27
|
+
|
|
28
|
+
const info: UpdateInfo = {
|
|
29
|
+
appName: app.name,
|
|
30
|
+
currentVersion: app.version,
|
|
31
|
+
latestVersion: app.version,
|
|
32
|
+
hasUpdate: false,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
switch (app.installMethod) {
|
|
37
|
+
case 'npm':
|
|
38
|
+
await this.checkNpmUpdate(info);
|
|
39
|
+
break;
|
|
40
|
+
case 'yarn':
|
|
41
|
+
await this.checkYarnUpdate(info);
|
|
42
|
+
break;
|
|
43
|
+
case 'pnpm':
|
|
44
|
+
await this.checkPnpmUpdate(info);
|
|
45
|
+
break;
|
|
46
|
+
case 'brew':
|
|
47
|
+
await this.checkBrewUpdate(info);
|
|
48
|
+
break;
|
|
49
|
+
case 'apt':
|
|
50
|
+
await this.checkAptUpdate(info);
|
|
51
|
+
break;
|
|
52
|
+
case 'yum':
|
|
53
|
+
case 'dnf':
|
|
54
|
+
await this.checkYumUpdate(info);
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
Logger.warn(`No update checker available for ${app.installMethod}`);
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
Logger.debug(`Update check failed for ${app.name}: ${(error as Error).message}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return info;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check for updates for multiple apps
|
|
68
|
+
*/
|
|
69
|
+
async checkForUpdates(apps: InstalledApp[]): Promise<UpdateInfo[]> {
|
|
70
|
+
const updates: UpdateInfo[] = [];
|
|
71
|
+
|
|
72
|
+
for (const app of apps) {
|
|
73
|
+
const update = await this.checkForUpdate(app);
|
|
74
|
+
updates.push(update);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return updates;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check npm registry for updates
|
|
82
|
+
*/
|
|
83
|
+
private async checkNpmUpdate(info: UpdateInfo): Promise<void> {
|
|
84
|
+
try {
|
|
85
|
+
const output = execSync(`npm view ${info.appName} version 2>/dev/null`).toString().trim();
|
|
86
|
+
if (output) {
|
|
87
|
+
info.latestVersion = output;
|
|
88
|
+
info.hasUpdate = this.compareVersions(info.currentVersion, output) < 0;
|
|
89
|
+
info.installCommand = `npm install -g ${info.appName}@latest`;
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
// Package not found or network error
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check yarn registry for updates
|
|
98
|
+
*/
|
|
99
|
+
private async checkYarnUpdate(info: UpdateInfo): Promise<void> {
|
|
100
|
+
try {
|
|
101
|
+
const output = execSync(`yarn info ${info.appName} version 2>/dev/null`).toString().trim();
|
|
102
|
+
if (output) {
|
|
103
|
+
info.latestVersion = output;
|
|
104
|
+
info.hasUpdate = this.compareVersions(info.currentVersion, output) < 0;
|
|
105
|
+
info.installCommand = `yarn global upgrade ${info.appName}`;
|
|
106
|
+
}
|
|
107
|
+
} catch {
|
|
108
|
+
// Package not found or network error
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check pnpm registry for updates
|
|
114
|
+
*/
|
|
115
|
+
private async checkPnpmUpdate(info: UpdateInfo): Promise<void> {
|
|
116
|
+
try {
|
|
117
|
+
const output = execSync(`pnpm view ${info.appName} version 2>/dev/null`).toString().trim();
|
|
118
|
+
if (output) {
|
|
119
|
+
info.latestVersion = output;
|
|
120
|
+
info.hasUpdate = this.compareVersions(info.currentVersion, output) < 0;
|
|
121
|
+
info.installCommand = `pnpm add -g ${info.appName}@latest`;
|
|
122
|
+
}
|
|
123
|
+
} catch {
|
|
124
|
+
// Package not found or network error
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Check homebrew for updates
|
|
130
|
+
*/
|
|
131
|
+
private async checkBrewUpdate(info: UpdateInfo): Promise<void> {
|
|
132
|
+
try {
|
|
133
|
+
const output = execSync(`brew info ${info.appName} 2>/dev/null | grep "stable"`).toString();
|
|
134
|
+
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
135
|
+
if (match) {
|
|
136
|
+
info.latestVersion = match[1];
|
|
137
|
+
info.hasUpdate = this.compareVersions(info.currentVersion, match[1]) < 0;
|
|
138
|
+
info.installCommand = `brew upgrade ${info.appName}`;
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
// Package not found or network error
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Check apt for updates
|
|
147
|
+
*/
|
|
148
|
+
private async checkAptUpdate(info: UpdateInfo): Promise<void> {
|
|
149
|
+
try {
|
|
150
|
+
const output = execSync(`apt-cache policy ${info.appName} 2>/dev/null`).toString();
|
|
151
|
+
const match = output.match(/Candidate: (\S+)/);
|
|
152
|
+
if (match) {
|
|
153
|
+
info.latestVersion = match[1];
|
|
154
|
+
info.hasUpdate = this.compareVersions(info.currentVersion, match[1]) < 0;
|
|
155
|
+
info.installCommand = `sudo apt update && sudo apt upgrade ${info.appName}`;
|
|
156
|
+
}
|
|
157
|
+
} catch {
|
|
158
|
+
// Package not found or network error
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Check yum/dnf for updates
|
|
164
|
+
*/
|
|
165
|
+
private async checkYumUpdate(info: UpdateInfo): Promise<void> {
|
|
166
|
+
try {
|
|
167
|
+
const output = execSync(`yum info ${info.appName} 2>/dev/null`).toString();
|
|
168
|
+
const match = output.match(/Version\s+:\s+(\S+)/);
|
|
169
|
+
if (match) {
|
|
170
|
+
info.latestVersion = match[1];
|
|
171
|
+
info.hasUpdate = this.compareVersions(info.currentVersion, match[1]) < 0;
|
|
172
|
+
info.installCommand = `sudo yum update ${info.appName}`;
|
|
173
|
+
}
|
|
174
|
+
} catch {
|
|
175
|
+
// Package not found or network error
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Compare semantic versions
|
|
181
|
+
* Returns: -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
|
|
182
|
+
*/
|
|
183
|
+
private compareVersions(v1: string, v2: string): number {
|
|
184
|
+
const parts1 = v1.split('.').map((p) => parseInt(p, 10));
|
|
185
|
+
const parts2 = v2.split('.').map((p) => parseInt(p, 10));
|
|
186
|
+
|
|
187
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
188
|
+
const p1 = parts1[i] || 0;
|
|
189
|
+
const p2 = parts2[i] || 0;
|
|
190
|
+
|
|
191
|
+
if (p1 < p2) return -1;
|
|
192
|
+
if (p1 > p2) return 1;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get formatted update summary
|
|
200
|
+
*/
|
|
201
|
+
getUpdateSummary(updates: UpdateInfo[]): string {
|
|
202
|
+
const available = updates.filter((u) => u.hasUpdate);
|
|
203
|
+
|
|
204
|
+
if (available.length === 0) {
|
|
205
|
+
return 'All applications are up to date!';
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
let summary = `Found ${available.length} update(s) available:\n\n`;
|
|
209
|
+
available.forEach((update) => {
|
|
210
|
+
summary += `📦 ${update.appName}\n`;
|
|
211
|
+
summary += ` Current: ${update.currentVersion} → Latest: ${update.latestVersion}\n`;
|
|
212
|
+
if (update.installCommand) {
|
|
213
|
+
summary += ` Install: ${update.installCommand}\n`;
|
|
214
|
+
}
|
|
215
|
+
summary += '\n';
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
return summary;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { NpmManager } from '../managers/npmManager';
|
|
2
|
+
import { BrewManager } from '../managers/brewManager';
|
|
3
|
+
import { LinuxManager } from '../managers/linuxManager';
|
|
4
|
+
import { CustomManager } from '../managers/customManager';
|
|
5
|
+
import { isMacOS, isLinux } from '../utils/platform';
|
|
6
|
+
import { InstalledApp, ArtifactPath, SearchOptions } from '../types';
|
|
7
|
+
import { getFileSize } from '../utils/filesystem';
|
|
8
|
+
|
|
9
|
+
export class Detector {
|
|
10
|
+
private npmManager: NpmManager;
|
|
11
|
+
private brewManager?: BrewManager;
|
|
12
|
+
private linuxManager?: LinuxManager;
|
|
13
|
+
private customManager: CustomManager;
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
this.npmManager = new NpmManager();
|
|
17
|
+
this.customManager = new CustomManager();
|
|
18
|
+
|
|
19
|
+
if (isMacOS()) {
|
|
20
|
+
this.brewManager = new BrewManager();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (isLinux()) {
|
|
24
|
+
this.linuxManager = new LinuxManager();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async detectAllApps(): Promise<InstalledApp[]> {
|
|
29
|
+
const allApps: InstalledApp[] = [];
|
|
30
|
+
|
|
31
|
+
// npm packages
|
|
32
|
+
const npmApps = await this.npmManager.getInstalledPackages();
|
|
33
|
+
allApps.push(...npmApps);
|
|
34
|
+
|
|
35
|
+
// Homebrew packages (macOS only)
|
|
36
|
+
if (this.brewManager) {
|
|
37
|
+
const brewApps = await this.brewManager.getInstalledPackages();
|
|
38
|
+
allApps.push(...brewApps);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Linux packages
|
|
42
|
+
if (this.linuxManager) {
|
|
43
|
+
const linuxApps = await this.linuxManager.getInstalledPackages();
|
|
44
|
+
allApps.push(...linuxApps);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Custom binaries
|
|
48
|
+
const customApps = await this.customManager.findCustomInstalledApps();
|
|
49
|
+
allApps.push(...customApps);
|
|
50
|
+
|
|
51
|
+
// Remove duplicates by name
|
|
52
|
+
const seen = new Set<string>();
|
|
53
|
+
return allApps.filter((app) => {
|
|
54
|
+
if (seen.has(app.name)) return false;
|
|
55
|
+
seen.add(app.name);
|
|
56
|
+
return true;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async searchApps(options: SearchOptions): Promise<InstalledApp[]> {
|
|
61
|
+
const allApps = await this.detectAllApps();
|
|
62
|
+
|
|
63
|
+
let filtered = allApps;
|
|
64
|
+
|
|
65
|
+
// Filter by query
|
|
66
|
+
if (options.query) {
|
|
67
|
+
const query = options.query.toLowerCase();
|
|
68
|
+
filtered = filtered.filter((app) =>
|
|
69
|
+
app.name.toLowerCase().includes(query)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Filter by install method
|
|
74
|
+
if (options.installMethod) {
|
|
75
|
+
filtered = filtered.filter((app) => app.installMethod === options.installMethod);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Sort
|
|
79
|
+
switch (options.sortBy) {
|
|
80
|
+
case 'name':
|
|
81
|
+
filtered.sort((a, b) => a.name.localeCompare(b.name));
|
|
82
|
+
break;
|
|
83
|
+
case 'date':
|
|
84
|
+
filtered.sort(
|
|
85
|
+
(a, b) =>
|
|
86
|
+
(b.installedDate?.getTime() || 0) - (a.installedDate?.getTime() || 0)
|
|
87
|
+
);
|
|
88
|
+
break;
|
|
89
|
+
case 'size':
|
|
90
|
+
filtered.sort((a, b) => (b.size || 0) - (a.size || 0));
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return filtered;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async findArtifacts(appName: string, installMethod: string): Promise<ArtifactPath[]> {
|
|
98
|
+
const artifacts: ArtifactPath[] = [];
|
|
99
|
+
|
|
100
|
+
switch (installMethod) {
|
|
101
|
+
case 'npm':
|
|
102
|
+
artifacts.push(...(await this.npmManager.findArtifacts(appName)));
|
|
103
|
+
break;
|
|
104
|
+
case 'brew':
|
|
105
|
+
if (this.brewManager) {
|
|
106
|
+
artifacts.push(...(await this.brewManager.findArtifacts(appName)));
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
case 'apt':
|
|
110
|
+
case 'yum':
|
|
111
|
+
case 'dnf':
|
|
112
|
+
if (this.linuxManager) {
|
|
113
|
+
artifacts.push(...(await this.linuxManager.findArtifacts(appName)));
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
case 'custom':
|
|
117
|
+
artifacts.push(...(await this.customManager.findArtifacts(appName)));
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Calculate sizes
|
|
122
|
+
for (const artifact of artifacts) {
|
|
123
|
+
artifact.size = getFileSize(artifact.path);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return artifacts;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async estimateSpaceFreed(appName: string, installMethod: string): Promise<number> {
|
|
130
|
+
const artifacts = await this.findArtifacts(appName, installMethod);
|
|
131
|
+
return artifacts.reduce((total, artifact) => total + artifact.size, 0);
|
|
132
|
+
}
|
|
133
|
+
}
|