kv-clearnup/electron/features/cleaner.ts

141 lines
4.3 KiB
TypeScript

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;
}
}