import fs from 'fs/promises'; import path from 'path'; import os from 'os'; // Trash Compactor: System Sanitation export async function clearCaches() { const cacheDir = path.join(os.homedir(), 'Library/Caches'); // Be careful! Cleaning everything here might break apps. // We should target specific known large caches or stale ones. // For PRD: "Empty User Caches" try { const entries = await fs.readdir(cacheDir); let freedSpace = 0; for (const entry of entries) { const fullPath = path.join(cacheDir, entry); // We might want to just delete the contents, not the folder itself, or assume app recreates it. // Safer: delete recursively. await fs.rm(fullPath, { recursive: true, force: true }); } return true; } catch (error) { console.error('Error clearing caches', error); return false; } } export async function purgePath(targetPath: string) { try { await fs.rm(targetPath, { recursive: true, force: true }); return true; } catch (e) { console.error(`Failed to purge ${targetPath}`, e); return false; } } export async function emptyTrash() { // Uses apple script to force empty trash to avoid "in use" errors if possible, // or `rm -rf ~/.Trash/*` (dangerous!). // Safe way: osascript try { const { exec } = await import('child_process'); exec(`osascript -e 'tell application "Finder" to empty trash'`); return true; } catch (e) { return false; } } export async function cleanupDocker() { try { const { exec } = await import('child_process'); const util = await import('util'); const execAsync = util.promisify(exec); // Prune everything: stopped containers, all images (dangling + unused), volumes, networks await execAsync('docker system prune -a --volumes -f'); return true; } catch (e) { console.error('Failed to cleanup docker:', e); return false; } } export async function cleanupTmp() { const tmpDir = os.tmpdir(); let success = true; try { const entries = await fs.readdir(tmpDir); for (const entry of entries) { try { // Be careful not to delete hidden system files if possible, but user asked for "complete" await fs.rm(path.join(tmpDir, entry), { recursive: true, force: true }); } catch (e) { // Ignore individual file errors (locked files) console.warn(`Skipped ${entry}`); } } } catch (e) { console.error('Failed to access tmp dir:', e); success = false; } return success; } export async function cleanupXcode() { try { const home = os.homedir(); // Remove DerivedData and iOS DeviceSupport (Aggressive!) const paths = [ path.join(home, 'Library/Developer/Xcode/DerivedData'), path.join(home, 'Library/Developer/Xcode/iOS DeviceSupport'), path.join(home, 'Library/Developer/Xcode/Archives'), path.join(home, 'Library/Caches/com.apple.dt.Xcode') ]; for (const p of paths) { try { await fs.rm(p, { recursive: true, force: true }); } catch (e) { console.warn(`Failed to clean ${p}`, e); } } return true; } catch (e) { console.error('Failed to cleanup Xcode:', e); return false; } } export async function cleanupTurnkey() { try { const home = os.homedir(); // Clean package manager caches const paths = [ path.join(home, '.npm/_cacache'), path.join(home, '.yarn/cache'), path.join(home, 'Library/pnpm/store'), // Mac default for pnpm store if not configured otherwise path.join(home, '.cache/yarn'), path.join(home, '.gradle/caches') ]; for (const p of paths) { try { await fs.rm(p, { recursive: true, force: true }); } catch (e) { console.warn(`Failed to clean ${p}`, e); } } return true; } catch (e) { console.error('Failed to cleanup package managers:', e); return false; } }