aris-mac-cleaner 3.0.0 → 3.5.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/README.md +196 -63
- package/bin/cli.js +1588 -359
- package/package.json +28 -4
package/bin/cli.js
CHANGED
|
@@ -1,54 +1,154 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// ARIS MAC CLEANER v3.5 PREMIUM
|
|
5
|
+
// por Salvador Reis
|
|
6
|
+
// ============================================================================
|
|
7
7
|
|
|
8
|
-
const { execSync, spawnSync } = require('child_process');
|
|
8
|
+
const { execSync, spawnSync, spawn } = require('child_process');
|
|
9
9
|
const os = require('os');
|
|
10
10
|
const fs = require('fs');
|
|
11
11
|
const path = require('path');
|
|
12
12
|
const readline = require('readline');
|
|
13
13
|
const crypto = require('crypto');
|
|
14
|
+
const https = require('https');
|
|
15
|
+
|
|
16
|
+
// Premium dependencies (with fallbacks)
|
|
17
|
+
let chalk, gradient, ora, cliProgress, asciichart, notifier, boxen, figures;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
chalk = require('chalk');
|
|
21
|
+
} catch (e) {
|
|
22
|
+
chalk = {
|
|
23
|
+
cyan: s => `\x1b[36m${s}\x1b[0m`,
|
|
24
|
+
green: s => `\x1b[32m${s}\x1b[0m`,
|
|
25
|
+
red: s => `\x1b[31m${s}\x1b[0m`,
|
|
26
|
+
yellow: s => `\x1b[33m${s}\x1b[0m`,
|
|
27
|
+
white: s => `\x1b[37m${s}\x1b[0m`,
|
|
28
|
+
gray: s => `\x1b[90m${s}\x1b[0m`,
|
|
29
|
+
dim: s => `\x1b[2m${s}\x1b[0m`,
|
|
30
|
+
bold: s => `\x1b[1m${s}\x1b[0m`,
|
|
31
|
+
magenta: s => `\x1b[35m${s}\x1b[0m`,
|
|
32
|
+
blue: s => `\x1b[34m${s}\x1b[0m`,
|
|
33
|
+
bgCyan: { black: s => `\x1b[46m\x1b[30m${s}\x1b[0m` },
|
|
34
|
+
bgGreen: { black: s => `\x1b[42m\x1b[30m${s}\x1b[0m` },
|
|
35
|
+
bgRed: { white: s => `\x1b[41m\x1b[37m${s}\x1b[0m` },
|
|
36
|
+
bgYellow: { black: s => `\x1b[43m\x1b[30m${s}\x1b[0m` }
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
gradient = require('gradient-string');
|
|
42
|
+
} catch (e) {
|
|
43
|
+
gradient = {
|
|
44
|
+
pastel: { multiline: s => s },
|
|
45
|
+
cristal: { multiline: s => s },
|
|
46
|
+
rainbow: { multiline: s => s },
|
|
47
|
+
vice: { multiline: s => s },
|
|
48
|
+
mind: { multiline: s => s }
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
ora = require('ora');
|
|
54
|
+
} catch (e) {
|
|
55
|
+
ora = (text) => ({
|
|
56
|
+
start: () => ({ succeed: () => {}, fail: () => {}, stop: () => {}, text: '' }),
|
|
57
|
+
succeed: () => {},
|
|
58
|
+
fail: () => {},
|
|
59
|
+
stop: () => {}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
cliProgress = require('cli-progress');
|
|
65
|
+
} catch (e) {
|
|
66
|
+
cliProgress = {
|
|
67
|
+
SingleBar: class {
|
|
68
|
+
constructor() {}
|
|
69
|
+
start() {}
|
|
70
|
+
update() {}
|
|
71
|
+
stop() {}
|
|
72
|
+
increment() {}
|
|
73
|
+
},
|
|
74
|
+
Presets: { shades_classic: {} }
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
asciichart = require('asciichart');
|
|
80
|
+
} catch (e) {
|
|
81
|
+
asciichart = { plot: (data) => data.join(', ') };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
notifier = require('node-notifier');
|
|
86
|
+
} catch (e) {
|
|
87
|
+
notifier = { notify: () => {} };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
boxen = require('boxen');
|
|
92
|
+
} catch (e) {
|
|
93
|
+
boxen = (text) => text;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
figures = require('figures');
|
|
98
|
+
} catch (e) {
|
|
99
|
+
figures = {
|
|
100
|
+
tick: '\u2713',
|
|
101
|
+
cross: '\u2717',
|
|
102
|
+
bullet: '\u2022',
|
|
103
|
+
arrowRight: '\u2192',
|
|
104
|
+
warning: '\u26A0',
|
|
105
|
+
info: '\u2139',
|
|
106
|
+
star: '\u2605',
|
|
107
|
+
heart: '\u2665',
|
|
108
|
+
pointer: '\u25B6',
|
|
109
|
+
line: '\u2500',
|
|
110
|
+
radioOn: '\u25C9',
|
|
111
|
+
radioOff: '\u25EF',
|
|
112
|
+
checkboxOn: '\u2612',
|
|
113
|
+
checkboxOff: '\u2610'
|
|
114
|
+
};
|
|
115
|
+
}
|
|
14
116
|
|
|
15
|
-
|
|
117
|
+
// ============================================================================
|
|
118
|
+
// CONSTANTS
|
|
119
|
+
// ============================================================================
|
|
120
|
+
|
|
121
|
+
const VERSION = '3.5.0';
|
|
122
|
+
const PACKAGE_NAME = 'aris-mac-cleaner';
|
|
16
123
|
const HOME = os.homedir();
|
|
17
124
|
const CONFIG_DIR = path.join(HOME, '.aris-mac-cleaner');
|
|
18
125
|
const HISTORY_FILE = path.join(CONFIG_DIR, 'history.log');
|
|
19
126
|
const EXCLUSIONS_FILE = path.join(CONFIG_DIR, 'exclusions.conf');
|
|
20
127
|
const CUSTOM_PATHS_FILE = path.join(CONFIG_DIR, 'custom-paths.conf');
|
|
21
128
|
const LANGUAGE_FILE = path.join(CONFIG_DIR, 'language');
|
|
129
|
+
const BACKUP_DIR = path.join(CONFIG_DIR, 'backups');
|
|
130
|
+
const REPORTS_DIR = path.join(CONFIG_DIR, 'reports');
|
|
131
|
+
const SCHEDULE_FILE = path.join(CONFIG_DIR, 'schedule.plist');
|
|
132
|
+
const BENCHMARK_FILE = path.join(CONFIG_DIR, 'benchmark.log');
|
|
133
|
+
const USAGE_FILE = path.join(CONFIG_DIR, 'usage.json');
|
|
134
|
+
|
|
135
|
+
// Ensure directories exist
|
|
136
|
+
[CONFIG_DIR, BACKUP_DIR, REPORTS_DIR].forEach(dir => {
|
|
137
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// ============================================================================
|
|
141
|
+
// GLOBAL STATE
|
|
142
|
+
// ============================================================================
|
|
143
|
+
|
|
144
|
+
let DRY_RUN = false;
|
|
145
|
+
let SECURE_DELETE = false;
|
|
146
|
+
let systemStats = {};
|
|
147
|
+
let lastCleanupResults = null;
|
|
22
148
|
|
|
23
|
-
//
|
|
24
|
-
if (!fs.existsSync(CONFIG_DIR)) {
|
|
25
|
-
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
29
|
-
// COLORS AND SYMBOLS
|
|
30
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
31
|
-
|
|
32
|
-
const c = {
|
|
33
|
-
reset: '\x1b[0m',
|
|
34
|
-
red: '\x1b[0;31m',
|
|
35
|
-
green: '\x1b[0;32m',
|
|
36
|
-
yellow: '\x1b[1;33m',
|
|
37
|
-
blue: '\x1b[0;34m',
|
|
38
|
-
cyan: '\x1b[0;36m',
|
|
39
|
-
white: '\x1b[1;37m',
|
|
40
|
-
dim: '\x1b[2m',
|
|
41
|
-
bold: '\x1b[1m'
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const CHECK = '✓';
|
|
45
|
-
const CROSS = '✗';
|
|
46
|
-
const ARROW = '→';
|
|
47
|
-
const BULLET = '•';
|
|
48
|
-
|
|
49
|
-
// ══════════════════════════════════════════════════════════════════════════════
|
|
149
|
+
// ============================================================================
|
|
50
150
|
// MULTI-LANGUAGE
|
|
51
|
-
//
|
|
151
|
+
// ============================================================================
|
|
52
152
|
|
|
53
153
|
let LANG_CODE = 'pt';
|
|
54
154
|
if (fs.existsSync(LANGUAGE_FILE)) {
|
|
@@ -58,18 +158,24 @@ if (fs.existsSync(LANGUAGE_FILE)) {
|
|
|
58
158
|
const MSG = LANG_CODE === 'pt' ? {
|
|
59
159
|
WELCOME: 'Bem-vindo ao Aris Mac Cleaner',
|
|
60
160
|
MENU_TITLE: 'MENU PRINCIPAL',
|
|
61
|
-
OPT_QUICK: 'Limpeza
|
|
62
|
-
OPT_LARGE: '
|
|
63
|
-
OPT_DUPLICATES: '
|
|
64
|
-
OPT_APPS: 'Desinstalar
|
|
65
|
-
OPT_STARTUP: '
|
|
161
|
+
OPT_QUICK: 'Limpeza Rapida',
|
|
162
|
+
OPT_LARGE: 'Ficheiros Grandes',
|
|
163
|
+
OPT_DUPLICATES: 'Duplicados',
|
|
164
|
+
OPT_APPS: 'Desinstalar Apps',
|
|
165
|
+
OPT_STARTUP: 'Apps de Arranque',
|
|
66
166
|
OPT_BROWSERS: 'Limpar Browsers',
|
|
67
|
-
OPT_PROCESSES: '
|
|
68
|
-
OPT_STATS: '
|
|
69
|
-
OPT_SETTINGS: '
|
|
167
|
+
OPT_PROCESSES: 'Processos Pesados',
|
|
168
|
+
OPT_STATS: 'Estatisticas',
|
|
169
|
+
OPT_SETTINGS: 'Configuracoes',
|
|
170
|
+
OPT_DISK: 'Analisador Disco',
|
|
171
|
+
OPT_PRIVACY: 'Limpeza Privacidade',
|
|
172
|
+
OPT_MEMORY: 'Otimizar Memoria',
|
|
173
|
+
OPT_BENCHMARK: 'Benchmark',
|
|
174
|
+
OPT_SCHEDULER: 'Agendar Limpeza',
|
|
175
|
+
OPT_EXPORT: 'Exportar Relatorio',
|
|
70
176
|
OPT_EXIT: 'Sair',
|
|
71
177
|
CHOOSE: 'Escolha',
|
|
72
|
-
INVALID: '
|
|
178
|
+
INVALID: 'Opcao invalida',
|
|
73
179
|
PRESS_ENTER: 'Pressiona ENTER para continuar',
|
|
74
180
|
SCANNING: 'A analisar...',
|
|
75
181
|
FOUND: 'Encontrado',
|
|
@@ -79,13 +185,13 @@ const MSG = LANG_CODE === 'pt' ? {
|
|
|
79
185
|
CANCEL: 'Cancelar',
|
|
80
186
|
CONFIRM: 'Confirmar',
|
|
81
187
|
FREED: 'Libertado',
|
|
82
|
-
AVAILABLE: '
|
|
188
|
+
AVAILABLE: 'Disponivel',
|
|
83
189
|
HEALTH_SCORE: 'Health Score',
|
|
84
190
|
EXCELLENT: 'Excelente',
|
|
85
191
|
GOOD: 'Bom',
|
|
86
|
-
ATTENTION: '
|
|
87
|
-
CRITICAL: '
|
|
88
|
-
DIAGNOSE: '
|
|
192
|
+
ATTENTION: 'Atencao',
|
|
193
|
+
CRITICAL: 'Critico',
|
|
194
|
+
DIAGNOSE: 'DIAGNOSTICO DO SISTEMA',
|
|
89
195
|
DISK: 'Disco',
|
|
90
196
|
RAM: 'RAM',
|
|
91
197
|
SWAP: 'Swap',
|
|
@@ -97,9 +203,9 @@ const MSG = LANG_CODE === 'pt' ? {
|
|
|
97
203
|
ALL: 'todos',
|
|
98
204
|
NONE: 'nenhum',
|
|
99
205
|
BACK: 'Voltar',
|
|
100
|
-
FINAL_REPORT: '
|
|
206
|
+
FINAL_REPORT: 'RELATORIO FINAL',
|
|
101
207
|
READY: 'Pronto para trabalhar!',
|
|
102
|
-
RESTART_REC: '
|
|
208
|
+
RESTART_REC: 'Recomendacao: Reinicia o Mac para limpar memoria',
|
|
103
209
|
NO_ITEMS: 'Nada encontrado',
|
|
104
210
|
CLEANING: 'A limpar...',
|
|
105
211
|
CATEGORIES: 'CATEGORIAS',
|
|
@@ -107,8 +213,8 @@ const MSG = LANG_CODE === 'pt' ? {
|
|
|
107
213
|
DEV_TOOLS: 'Ferramentas de Dev',
|
|
108
214
|
BROWSERS_CAT: 'Browsers',
|
|
109
215
|
DOWNLOADS_CAT: 'Downloads',
|
|
110
|
-
APPS_CAT: '
|
|
111
|
-
PROCESS_MEM: 'Por
|
|
216
|
+
APPS_CAT: 'Aplicacoes',
|
|
217
|
+
PROCESS_MEM: 'Por Memoria',
|
|
112
218
|
PROCESS_CPU: 'Por CPU',
|
|
113
219
|
KILL: 'Matar',
|
|
114
220
|
STARTUP_ITEMS: 'Items de Arranque',
|
|
@@ -117,52 +223,74 @@ const MSG = LANG_CODE === 'pt' ? {
|
|
|
117
223
|
DISABLED: 'Desativado',
|
|
118
224
|
TOGGLE: 'Alternar',
|
|
119
225
|
DISABLE_ALL: 'Desativar todos',
|
|
120
|
-
THIS_MONTH: 'Este
|
|
226
|
+
THIS_MONTH: 'Este mes',
|
|
121
227
|
CLEANINGS: 'Limpezas',
|
|
122
|
-
AVG_SCORE: 'Score
|
|
123
|
-
HISTORY: '
|
|
124
|
-
EXCLUSIONS: 'Gerir
|
|
228
|
+
AVG_SCORE: 'Score medio',
|
|
229
|
+
HISTORY: 'Historico',
|
|
230
|
+
EXCLUSIONS: 'Gerir Exclusoes',
|
|
125
231
|
CUSTOM_PATHS: 'Caminhos Personalizados',
|
|
126
232
|
CHANGE_LANG: 'Mudar Idioma',
|
|
127
|
-
RESET_DEFAULTS: 'Repor
|
|
128
|
-
PREVIEW_MODE: 'Modo Preview (
|
|
233
|
+
RESET_DEFAULTS: 'Repor Predefinicoes',
|
|
234
|
+
PREVIEW_MODE: 'Modo Preview (nao apaga nada)',
|
|
129
235
|
LARGE_FILES: 'FICHEIROS GRANDES',
|
|
130
236
|
DUPLICATES: 'FICHEIROS DUPLICADOS',
|
|
131
|
-
UNINSTALL: 'DESINSTALAR
|
|
237
|
+
UNINSTALL: 'DESINSTALAR APLICACOES',
|
|
132
238
|
APP_SIZE: 'app',
|
|
133
239
|
DATA_SIZE: 'dados',
|
|
134
|
-
CURRENT_EXCLUSIONS: '
|
|
240
|
+
CURRENT_EXCLUSIONS: 'Exclusoes atuais',
|
|
135
241
|
CURRENT_PATHS: 'Caminhos personalizados atuais',
|
|
136
242
|
ENTER_PATH: 'Introduz caminho (c=limpar, n=voltar)',
|
|
137
243
|
PATH: 'Caminho',
|
|
138
244
|
CLEARED: 'Limpo',
|
|
139
245
|
ADDED: 'Adicionado',
|
|
140
246
|
LANG_CHANGED_EN: 'Language changed to English',
|
|
141
|
-
LANG_CHANGED_PT: 'Idioma alterado para
|
|
142
|
-
SETTINGS_RESET: '
|
|
247
|
+
LANG_CHANGED_PT: 'Idioma alterado para Portugues',
|
|
248
|
+
SETTINGS_RESET: 'Configuracoes repostas',
|
|
143
249
|
REMOVED: 'Removido',
|
|
144
250
|
NEED_SUDO: 'Precisa sudo para remover app. Executa:',
|
|
145
251
|
DELETING: 'A apagar',
|
|
146
252
|
KILLED: 'Morto',
|
|
147
253
|
FAILED_KILL: 'Falhou ao matar',
|
|
148
|
-
NO_HISTORY: 'Sem
|
|
254
|
+
NO_HISTORY: 'Sem historico. Faz algumas limpezas primeiro!',
|
|
149
255
|
FEATURE_PROGRESS: 'Funcionalidade em progresso...',
|
|
150
256
|
GROUP: 'Grupo',
|
|
151
257
|
EACH: 'cada',
|
|
152
258
|
IN_DUPLICATES: 'em duplicados',
|
|
153
|
-
HASHING: 'A calcular hashes...'
|
|
259
|
+
HASHING: 'A calcular hashes...',
|
|
260
|
+
DRY_RUN_MODE: 'MODO DRY-RUN (nada sera apagado)',
|
|
261
|
+
SECURE_DELETE_MODE: 'MODO SEGURO (overwrite antes de apagar)',
|
|
262
|
+
BACKUP_CREATED: 'Backup criado',
|
|
263
|
+
SCHEDULE_CREATED: 'Agendamento criado',
|
|
264
|
+
SCHEDULE_REMOVED: 'Agendamento removido',
|
|
265
|
+
REPORT_EXPORTED: 'Relatorio exportado',
|
|
266
|
+
UPDATE_AVAILABLE: 'Atualizacao disponivel',
|
|
267
|
+
UP_TO_DATE: 'Ja tens a versao mais recente',
|
|
268
|
+
MEMORY_FREED: 'Memoria libertada',
|
|
269
|
+
PRIVACY_CLEANED: 'Privacidade limpa',
|
|
270
|
+
BENCHMARK_COMPLETE: 'Benchmark completo',
|
|
271
|
+
RECOMMENDATIONS: 'RECOMENDACOES',
|
|
272
|
+
DISK_ANALYSIS: 'ANALISE DE DISCO',
|
|
273
|
+
SCHEDULED_DAILY: 'Agendado diariamente',
|
|
274
|
+
SCHEDULED_WEEKLY: 'Agendado semanalmente',
|
|
275
|
+
CLEANUP_COMPLETE: 'Limpeza completa!'
|
|
154
276
|
} : {
|
|
155
277
|
WELCOME: 'Welcome to Aris Mac Cleaner',
|
|
156
278
|
MENU_TITLE: 'MAIN MENU',
|
|
157
|
-
OPT_QUICK: 'Quick Clean
|
|
158
|
-
OPT_LARGE: '
|
|
159
|
-
OPT_DUPLICATES: '
|
|
160
|
-
OPT_APPS: 'Uninstall
|
|
161
|
-
OPT_STARTUP: '
|
|
279
|
+
OPT_QUICK: 'Quick Clean',
|
|
280
|
+
OPT_LARGE: 'Large Files',
|
|
281
|
+
OPT_DUPLICATES: 'Duplicates',
|
|
282
|
+
OPT_APPS: 'Uninstall Apps',
|
|
283
|
+
OPT_STARTUP: 'Startup Apps',
|
|
162
284
|
OPT_BROWSERS: 'Clean Browsers',
|
|
163
|
-
OPT_PROCESSES: '
|
|
164
|
-
OPT_STATS: '
|
|
285
|
+
OPT_PROCESSES: 'Heavy Processes',
|
|
286
|
+
OPT_STATS: 'Statistics',
|
|
165
287
|
OPT_SETTINGS: 'Settings',
|
|
288
|
+
OPT_DISK: 'Disk Analyzer',
|
|
289
|
+
OPT_PRIVACY: 'Privacy Sweep',
|
|
290
|
+
OPT_MEMORY: 'Memory Optimizer',
|
|
291
|
+
OPT_BENCHMARK: 'Benchmark',
|
|
292
|
+
OPT_SCHEDULER: 'Schedule Cleanup',
|
|
293
|
+
OPT_EXPORT: 'Export Report',
|
|
166
294
|
OPT_EXIT: 'Exit',
|
|
167
295
|
CHOOSE: 'Choose',
|
|
168
296
|
INVALID: 'Invalid option',
|
|
@@ -234,7 +362,7 @@ const MSG = LANG_CODE === 'pt' ? {
|
|
|
234
362
|
CLEARED: 'Cleared',
|
|
235
363
|
ADDED: 'Added',
|
|
236
364
|
LANG_CHANGED_EN: 'Language changed to English',
|
|
237
|
-
LANG_CHANGED_PT: 'Idioma alterado para
|
|
365
|
+
LANG_CHANGED_PT: 'Idioma alterado para Portugues',
|
|
238
366
|
SETTINGS_RESET: 'Settings reset to defaults',
|
|
239
367
|
REMOVED: 'Removed',
|
|
240
368
|
NEED_SUDO: 'Need sudo to remove app. Run:',
|
|
@@ -246,12 +374,82 @@ const MSG = LANG_CODE === 'pt' ? {
|
|
|
246
374
|
GROUP: 'Group',
|
|
247
375
|
EACH: 'each',
|
|
248
376
|
IN_DUPLICATES: 'in duplicates',
|
|
249
|
-
HASHING: 'Calculating hashes...'
|
|
377
|
+
HASHING: 'Calculating hashes...',
|
|
378
|
+
DRY_RUN_MODE: 'DRY-RUN MODE (nothing will be deleted)',
|
|
379
|
+
SECURE_DELETE_MODE: 'SECURE MODE (overwrite before delete)',
|
|
380
|
+
BACKUP_CREATED: 'Backup created',
|
|
381
|
+
SCHEDULE_CREATED: 'Schedule created',
|
|
382
|
+
SCHEDULE_REMOVED: 'Schedule removed',
|
|
383
|
+
REPORT_EXPORTED: 'Report exported',
|
|
384
|
+
UPDATE_AVAILABLE: 'Update available',
|
|
385
|
+
UP_TO_DATE: 'You have the latest version',
|
|
386
|
+
MEMORY_FREED: 'Memory freed',
|
|
387
|
+
PRIVACY_CLEANED: 'Privacy cleaned',
|
|
388
|
+
BENCHMARK_COMPLETE: 'Benchmark complete',
|
|
389
|
+
RECOMMENDATIONS: 'RECOMMENDATIONS',
|
|
390
|
+
DISK_ANALYSIS: 'DISK ANALYSIS',
|
|
391
|
+
SCHEDULED_DAILY: 'Scheduled daily',
|
|
392
|
+
SCHEDULED_WEEKLY: 'Scheduled weekly',
|
|
393
|
+
CLEANUP_COMPLETE: 'Cleanup complete!'
|
|
250
394
|
};
|
|
251
395
|
|
|
252
|
-
//
|
|
396
|
+
// ============================================================================
|
|
397
|
+
// ANIMATED ASCII LOGO
|
|
398
|
+
// ============================================================================
|
|
399
|
+
|
|
400
|
+
const ASCII_LOGO = `
|
|
401
|
+
_ ____ ___ ____
|
|
402
|
+
/ \\ | _ \\|_ _/ ___|
|
|
403
|
+
/ _ \\ | |_) || |\\___ \\
|
|
404
|
+
/ ___ \\| _ < | | ___) |
|
|
405
|
+
/_/ \\_\\_| \\_\\___|____/
|
|
406
|
+
|
|
407
|
+
MAC CLEANER v${VERSION} PREMIUM
|
|
408
|
+
`;
|
|
409
|
+
|
|
410
|
+
const ASCII_LOGO_FRAMES = [
|
|
411
|
+
`
|
|
412
|
+
_
|
|
413
|
+
/ \\
|
|
414
|
+
/ _ \\
|
|
415
|
+
/ ___ \\
|
|
416
|
+
/_/ \\_\\
|
|
417
|
+
`,
|
|
418
|
+
`
|
|
419
|
+
_ ____
|
|
420
|
+
/ \\ | _ \\
|
|
421
|
+
/ _ \\ | |_) |
|
|
422
|
+
/ ___ \\| _ <
|
|
423
|
+
/_/ \\_\\_| \\_\\
|
|
424
|
+
`,
|
|
425
|
+
`
|
|
426
|
+
_ ____ ___ ____
|
|
427
|
+
/ \\ | _ \\|_ _/ ___|
|
|
428
|
+
/ _ \\ | |_) || |\\___ \\
|
|
429
|
+
/ ___ \\| _ < | | ___) |
|
|
430
|
+
/_/ \\_\\_| \\_\\___|____/
|
|
431
|
+
`,
|
|
432
|
+
ASCII_LOGO
|
|
433
|
+
];
|
|
434
|
+
|
|
435
|
+
async function animateLogo() {
|
|
436
|
+
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
|
437
|
+
|
|
438
|
+
for (const frame of ASCII_LOGO_FRAMES) {
|
|
439
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
440
|
+
console.log(gradient.cristal.multiline(frame));
|
|
441
|
+
await sleep(150);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Final sparkle effect
|
|
445
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
446
|
+
console.log(gradient.pastel.multiline(ASCII_LOGO));
|
|
447
|
+
await sleep(300);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// ============================================================================
|
|
253
451
|
// UTILITY FUNCTIONS
|
|
254
|
-
//
|
|
452
|
+
// ============================================================================
|
|
255
453
|
|
|
256
454
|
function exec(cmd) {
|
|
257
455
|
try {
|
|
@@ -294,6 +492,28 @@ function clearScreen() {
|
|
|
294
492
|
process.stdout.write('\x1b[2J\x1b[H');
|
|
295
493
|
}
|
|
296
494
|
|
|
495
|
+
function prompt(question) {
|
|
496
|
+
return new Promise((resolve) => {
|
|
497
|
+
const rl = readline.createInterface({
|
|
498
|
+
input: process.stdin,
|
|
499
|
+
output: process.stdout
|
|
500
|
+
});
|
|
501
|
+
rl.question(question, (answer) => {
|
|
502
|
+
rl.close();
|
|
503
|
+
resolve(answer.trim());
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
async function pressEnter() {
|
|
509
|
+
console.log();
|
|
510
|
+
await prompt(` ${chalk.dim(MSG.PRESS_ENTER + '...')} `);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// ============================================================================
|
|
514
|
+
// PREMIUM HEADER WITH GRADIENT
|
|
515
|
+
// ============================================================================
|
|
516
|
+
|
|
297
517
|
function header() {
|
|
298
518
|
clearScreen();
|
|
299
519
|
const date = new Date().toLocaleDateString('en-GB', {
|
|
@@ -301,44 +521,189 @@ function header() {
|
|
|
301
521
|
});
|
|
302
522
|
const time = new Date().toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' });
|
|
303
523
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
524
|
+
const titleBox = `
|
|
525
|
+
${chalk.cyan('+')}${chalk.cyan('-'.repeat(68))}${chalk.cyan('+')}
|
|
526
|
+
${chalk.cyan('|')}${chalk.white(' ARIS MAC CLEANER v' + VERSION + ' PREMIUM ')}${chalk.cyan('|')}
|
|
527
|
+
${chalk.cyan('|')}${chalk.gray(' ' + date + ' ' + figures.bullet + ' ' + time + ' ')}${chalk.cyan('|')}
|
|
528
|
+
${chalk.cyan('+')}${chalk.cyan('-'.repeat(68))}${chalk.cyan('+')}
|
|
529
|
+
`;
|
|
530
|
+
|
|
531
|
+
console.log(gradient.pastel.multiline(titleBox));
|
|
532
|
+
|
|
533
|
+
// Show mode indicators
|
|
534
|
+
if (DRY_RUN) {
|
|
535
|
+
console.log(chalk.bgYellow.black(` ${figures.warning} ${MSG.DRY_RUN_MODE} `));
|
|
536
|
+
console.log();
|
|
537
|
+
}
|
|
538
|
+
if (SECURE_DELETE) {
|
|
539
|
+
console.log(chalk.bgRed.white(` ${figures.warning} ${MSG.SECURE_DELETE_MODE} `));
|
|
540
|
+
console.log();
|
|
541
|
+
}
|
|
310
542
|
}
|
|
311
543
|
|
|
312
544
|
function sectionHeader(title) {
|
|
313
|
-
console.log(
|
|
314
|
-
console.log(
|
|
315
|
-
console.log(
|
|
545
|
+
console.log(chalk.white('+' + '-'.repeat(65) + '+'));
|
|
546
|
+
console.log(chalk.white('|') + ' ' + chalk.cyan(figures.pointer + ' ' + title));
|
|
547
|
+
console.log(chalk.white('+' + '-'.repeat(65) + '+'));
|
|
316
548
|
console.log();
|
|
317
549
|
}
|
|
318
550
|
|
|
319
|
-
|
|
551
|
+
// ============================================================================
|
|
552
|
+
// AUTO-UPDATE CHECKER
|
|
553
|
+
// ============================================================================
|
|
554
|
+
|
|
555
|
+
async function checkForUpdates() {
|
|
320
556
|
return new Promise((resolve) => {
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
557
|
+
const req = https.get(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, { timeout: 5000 }, (res) => {
|
|
558
|
+
let data = '';
|
|
559
|
+
res.on('data', chunk => data += chunk);
|
|
560
|
+
res.on('end', () => {
|
|
561
|
+
try {
|
|
562
|
+
const json = JSON.parse(data);
|
|
563
|
+
const latestVersion = json.version;
|
|
564
|
+
if (latestVersion && latestVersion !== VERSION) {
|
|
565
|
+
resolve({ hasUpdate: true, version: latestVersion });
|
|
566
|
+
} else {
|
|
567
|
+
resolve({ hasUpdate: false });
|
|
568
|
+
}
|
|
569
|
+
} catch (e) {
|
|
570
|
+
resolve({ hasUpdate: false });
|
|
571
|
+
}
|
|
572
|
+
});
|
|
324
573
|
});
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
574
|
+
|
|
575
|
+
req.on('error', () => resolve({ hasUpdate: false }));
|
|
576
|
+
req.on('timeout', () => {
|
|
577
|
+
req.destroy();
|
|
578
|
+
resolve({ hasUpdate: false });
|
|
328
579
|
});
|
|
329
580
|
});
|
|
330
581
|
}
|
|
331
582
|
|
|
332
|
-
async function
|
|
333
|
-
|
|
334
|
-
|
|
583
|
+
async function showUpdateNotification() {
|
|
584
|
+
const update = await checkForUpdates();
|
|
585
|
+
if (update.hasUpdate) {
|
|
586
|
+
console.log();
|
|
587
|
+
console.log(chalk.bgGreen.black(` ${figures.star} ${MSG.UPDATE_AVAILABLE}: v${update.version} `));
|
|
588
|
+
console.log(chalk.dim(` npm update -g ${PACKAGE_NAME}`));
|
|
589
|
+
console.log();
|
|
590
|
+
}
|
|
335
591
|
}
|
|
336
592
|
|
|
337
|
-
//
|
|
338
|
-
// SYSTEM
|
|
339
|
-
//
|
|
593
|
+
// ============================================================================
|
|
594
|
+
// BACKUP SYSTEM
|
|
595
|
+
// ============================================================================
|
|
596
|
+
|
|
597
|
+
function createBackup(items) {
|
|
598
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
599
|
+
const backupFile = path.join(BACKUP_DIR, `backup-${timestamp}.json`);
|
|
600
|
+
|
|
601
|
+
const backupData = {
|
|
602
|
+
timestamp: new Date().toISOString(),
|
|
603
|
+
version: VERSION,
|
|
604
|
+
items: items.map(item => ({
|
|
605
|
+
path: item.path,
|
|
606
|
+
name: item.name,
|
|
607
|
+
size: item.size,
|
|
608
|
+
category: item.category
|
|
609
|
+
}))
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
fs.writeFileSync(backupFile, JSON.stringify(backupData, null, 2));
|
|
613
|
+
return backupFile;
|
|
614
|
+
}
|
|
340
615
|
|
|
341
|
-
|
|
616
|
+
function listBackups() {
|
|
617
|
+
if (!fs.existsSync(BACKUP_DIR)) return [];
|
|
618
|
+
return fs.readdirSync(BACKUP_DIR)
|
|
619
|
+
.filter(f => f.endsWith('.json'))
|
|
620
|
+
.sort()
|
|
621
|
+
.reverse()
|
|
622
|
+
.slice(0, 10);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// ============================================================================
|
|
626
|
+
// SECURE DELETE
|
|
627
|
+
// ============================================================================
|
|
628
|
+
|
|
629
|
+
function secureDelete(filePath) {
|
|
630
|
+
if (!fs.existsSync(filePath)) return;
|
|
631
|
+
|
|
632
|
+
const stats = fs.statSync(filePath);
|
|
633
|
+
|
|
634
|
+
if (stats.isDirectory()) {
|
|
635
|
+
// For directories, just use rm -rf (secure delete of dirs is complex)
|
|
636
|
+
fs.rmSync(filePath, { recursive: true, force: true });
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Overwrite file content 3 times before deletion
|
|
641
|
+
const size = stats.size;
|
|
642
|
+
const fd = fs.openSync(filePath, 'w');
|
|
643
|
+
|
|
644
|
+
// Pass 1: zeros
|
|
645
|
+
const zeros = Buffer.alloc(Math.min(size, 1024 * 1024), 0);
|
|
646
|
+
let written = 0;
|
|
647
|
+
while (written < size) {
|
|
648
|
+
fs.writeSync(fd, zeros, 0, Math.min(zeros.length, size - written));
|
|
649
|
+
written += zeros.length;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Pass 2: ones
|
|
653
|
+
fs.lseekSync ? fs.lseekSync(fd, 0, 0) : null;
|
|
654
|
+
const ones = Buffer.alloc(Math.min(size, 1024 * 1024), 0xFF);
|
|
655
|
+
written = 0;
|
|
656
|
+
while (written < size) {
|
|
657
|
+
fs.writeSync(fd, ones, 0, Math.min(ones.length, size - written));
|
|
658
|
+
written += ones.length;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Pass 3: random
|
|
662
|
+
const random = crypto.randomBytes(Math.min(size, 1024 * 1024));
|
|
663
|
+
written = 0;
|
|
664
|
+
while (written < size) {
|
|
665
|
+
fs.writeSync(fd, random, 0, Math.min(random.length, size - written));
|
|
666
|
+
written += random.length;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
fs.closeSync(fd);
|
|
670
|
+
fs.unlinkSync(filePath);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
function deleteItem(itemPath) {
|
|
674
|
+
if (DRY_RUN) {
|
|
675
|
+
console.log(chalk.yellow(` ${figures.info} [DRY-RUN] Would delete: ${itemPath}`));
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
try {
|
|
680
|
+
if (SECURE_DELETE) {
|
|
681
|
+
secureDelete(itemPath);
|
|
682
|
+
} else {
|
|
683
|
+
fs.rmSync(itemPath, { recursive: true, force: true });
|
|
684
|
+
}
|
|
685
|
+
return true;
|
|
686
|
+
} catch (e) {
|
|
687
|
+
return false;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// ============================================================================
|
|
692
|
+
// PROGRESS BAR
|
|
693
|
+
// ============================================================================
|
|
694
|
+
|
|
695
|
+
function createProgressBar(total, format) {
|
|
696
|
+
return new cliProgress.SingleBar({
|
|
697
|
+
format: format || ' {bar} | {percentage}% | {value}/{total}',
|
|
698
|
+
barCompleteChar: '\u2588',
|
|
699
|
+
barIncompleteChar: '\u2591',
|
|
700
|
+
hideCursor: true
|
|
701
|
+
}, cliProgress.Presets.shades_classic);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// ============================================================================
|
|
705
|
+
// SYSTEM DIAGNOSTICS
|
|
706
|
+
// ============================================================================
|
|
342
707
|
|
|
343
708
|
function getSystemStats() {
|
|
344
709
|
// Disk
|
|
@@ -350,11 +715,11 @@ function getSystemStats() {
|
|
|
350
715
|
systemStats.diskFreeBlocks = parseInt(diskRaw[3]) || 0;
|
|
351
716
|
|
|
352
717
|
if (systemStats.diskPercent < 50) {
|
|
353
|
-
systemStats.diskStatus = `${
|
|
718
|
+
systemStats.diskStatus = chalk.green(`${figures.tick} ${MSG.EXCELLENT}`);
|
|
354
719
|
} else if (systemStats.diskPercent < 80) {
|
|
355
|
-
systemStats.diskStatus = `${
|
|
720
|
+
systemStats.diskStatus = chalk.yellow(`${figures.warning} ${MSG.ATTENTION}`);
|
|
356
721
|
} else {
|
|
357
|
-
systemStats.diskStatus = `${
|
|
722
|
+
systemStats.diskStatus = chalk.red(`${figures.cross} ${MSG.CRITICAL}`);
|
|
358
723
|
}
|
|
359
724
|
|
|
360
725
|
// RAM
|
|
@@ -362,11 +727,11 @@ function getSystemStats() {
|
|
|
362
727
|
systemStats.ramUsedPercent = 100 - ramFreePercent;
|
|
363
728
|
|
|
364
729
|
if (systemStats.ramUsedPercent < 70) {
|
|
365
|
-
systemStats.ramStatus = `${
|
|
730
|
+
systemStats.ramStatus = chalk.green(`${figures.tick} OK`);
|
|
366
731
|
} else if (systemStats.ramUsedPercent < 90) {
|
|
367
|
-
systemStats.ramStatus = `${
|
|
732
|
+
systemStats.ramStatus = chalk.yellow(`${figures.warning} ${MSG.ATTENTION}`);
|
|
368
733
|
} else {
|
|
369
|
-
systemStats.ramStatus = `${
|
|
734
|
+
systemStats.ramStatus = chalk.red(`${figures.cross} ${MSG.CRITICAL}`);
|
|
370
735
|
}
|
|
371
736
|
|
|
372
737
|
// Swap
|
|
@@ -374,11 +739,11 @@ function getSystemStats() {
|
|
|
374
739
|
systemStats.swapUsed = parseInt(swapStr) || 0;
|
|
375
740
|
|
|
376
741
|
if (systemStats.swapUsed < 2000) {
|
|
377
|
-
systemStats.swapStatus = `${
|
|
742
|
+
systemStats.swapStatus = chalk.green(`${figures.tick} OK`);
|
|
378
743
|
} else if (systemStats.swapUsed < 8000) {
|
|
379
|
-
systemStats.swapStatus = `${
|
|
744
|
+
systemStats.swapStatus = chalk.yellow(`${figures.warning} High`);
|
|
380
745
|
} else {
|
|
381
|
-
systemStats.swapStatus = `${
|
|
746
|
+
systemStats.swapStatus = chalk.red(`${figures.cross} ${MSG.CRITICAL}`);
|
|
382
747
|
}
|
|
383
748
|
|
|
384
749
|
// CPU
|
|
@@ -386,20 +751,20 @@ function getSystemStats() {
|
|
|
386
751
|
systemStats.cpuUsed = Math.round(100 - cpuIdle);
|
|
387
752
|
|
|
388
753
|
if (systemStats.cpuUsed < 50) {
|
|
389
|
-
systemStats.cpuStatus = `${
|
|
754
|
+
systemStats.cpuStatus = chalk.green(`${figures.tick} OK`);
|
|
390
755
|
} else if (systemStats.cpuUsed < 80) {
|
|
391
|
-
systemStats.cpuStatus = `${
|
|
756
|
+
systemStats.cpuStatus = chalk.yellow(`${figures.warning} ${MSG.ATTENTION}`);
|
|
392
757
|
} else {
|
|
393
|
-
systemStats.cpuStatus = `${
|
|
758
|
+
systemStats.cpuStatus = chalk.red(`${figures.cross} ${MSG.CRITICAL}`);
|
|
394
759
|
}
|
|
395
760
|
}
|
|
396
761
|
|
|
397
762
|
function showDiagnostics() {
|
|
398
763
|
sectionHeader(MSG.DIAGNOSE);
|
|
399
|
-
console.log(` ${
|
|
400
|
-
console.log(` ${
|
|
401
|
-
console.log(` ${
|
|
402
|
-
console.log(` ${
|
|
764
|
+
console.log(` ${figures.bullet} ${chalk.white(MSG.DISK)} ${systemStats.diskFree}GB ${MSG.FREE_OF} ${systemStats.diskTotal}GB ${systemStats.diskStatus}`);
|
|
765
|
+
console.log(` ${figures.bullet} ${chalk.white(MSG.RAM)} ${systemStats.ramUsedPercent}% ${MSG.IN_USE} ${systemStats.ramStatus}`);
|
|
766
|
+
console.log(` ${figures.bullet} ${chalk.white(MSG.SWAP)} ${systemStats.swapUsed}MB ${MSG.IN_USE} ${systemStats.swapStatus}`);
|
|
767
|
+
console.log(` ${figures.bullet} ${chalk.white(MSG.CPU)} ${systemStats.cpuUsed}% ${MSG.IN_USE} ${systemStats.cpuStatus}`);
|
|
403
768
|
console.log();
|
|
404
769
|
}
|
|
405
770
|
|
|
@@ -412,52 +777,834 @@ function calculateHealthScore() {
|
|
|
412
777
|
if (systemStats.swapUsed > 8000) score -= 20;
|
|
413
778
|
else if (systemStats.swapUsed > 4000) score -= 10;
|
|
414
779
|
|
|
415
|
-
let color =
|
|
780
|
+
let color = chalk.green;
|
|
416
781
|
let text = MSG.EXCELLENT;
|
|
417
|
-
if (score < 60) { color =
|
|
418
|
-
else if (score < 80) { color =
|
|
782
|
+
if (score < 60) { color = chalk.red; text = MSG.ATTENTION; }
|
|
783
|
+
else if (score < 80) { color = chalk.yellow; text = MSG.GOOD; }
|
|
419
784
|
|
|
420
785
|
return { score, color, text };
|
|
421
786
|
}
|
|
422
787
|
|
|
788
|
+
// ============================================================================
|
|
789
|
+
// SOUND NOTIFICATION
|
|
790
|
+
// ============================================================================
|
|
791
|
+
|
|
792
|
+
function playNotificationSound() {
|
|
793
|
+
// Use macOS native sound
|
|
794
|
+
exec('afplay /System/Library/Sounds/Glass.aiff &');
|
|
795
|
+
|
|
796
|
+
// Also show system notification
|
|
797
|
+
notifier.notify({
|
|
798
|
+
title: 'Aris Mac Cleaner',
|
|
799
|
+
message: MSG.CLEANUP_COMPLETE,
|
|
800
|
+
sound: true,
|
|
801
|
+
icon: path.join(__dirname, 'icon.png')
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// ============================================================================
|
|
806
|
+
// SMART RECOMMENDATIONS
|
|
807
|
+
// ============================================================================
|
|
808
|
+
|
|
809
|
+
function getSmartRecommendations() {
|
|
810
|
+
const recommendations = [];
|
|
811
|
+
|
|
812
|
+
// Check disk usage
|
|
813
|
+
if (systemStats.diskPercent > 80) {
|
|
814
|
+
recommendations.push({
|
|
815
|
+
priority: 'high',
|
|
816
|
+
icon: figures.warning,
|
|
817
|
+
text: LANG_CODE === 'pt'
|
|
818
|
+
? 'Disco quase cheio! Considera apagar ficheiros grandes ou desinstalar apps.'
|
|
819
|
+
: 'Disk almost full! Consider deleting large files or uninstalling apps.'
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// Check RAM
|
|
824
|
+
if (systemStats.ramUsedPercent > 85) {
|
|
825
|
+
recommendations.push({
|
|
826
|
+
priority: 'high',
|
|
827
|
+
icon: figures.warning,
|
|
828
|
+
text: LANG_CODE === 'pt'
|
|
829
|
+
? 'Memoria RAM muito usada. Usa o otimizador de memoria ou mata processos pesados.'
|
|
830
|
+
: 'RAM heavily used. Use memory optimizer or kill heavy processes.'
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// Check swap
|
|
835
|
+
if (systemStats.swapUsed > 5000) {
|
|
836
|
+
recommendations.push({
|
|
837
|
+
priority: 'medium',
|
|
838
|
+
icon: figures.info,
|
|
839
|
+
text: LANG_CODE === 'pt'
|
|
840
|
+
? 'Swap elevado. Considera reiniciar o Mac para limpar.'
|
|
841
|
+
: 'High swap usage. Consider restarting Mac to clear.'
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// Check history for patterns
|
|
846
|
+
if (fs.existsSync(HISTORY_FILE)) {
|
|
847
|
+
const history = fs.readFileSync(HISTORY_FILE, 'utf8').split('\n').filter(l => l.trim());
|
|
848
|
+
const lastWeek = history.filter(l => {
|
|
849
|
+
const date = new Date(l.split(' | ')[0]);
|
|
850
|
+
return (Date.now() - date.getTime()) < 7 * 24 * 60 * 60 * 1000;
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
if (lastWeek.length === 0) {
|
|
854
|
+
recommendations.push({
|
|
855
|
+
priority: 'low',
|
|
856
|
+
icon: figures.star,
|
|
857
|
+
text: LANG_CODE === 'pt'
|
|
858
|
+
? 'Nao fizeste limpeza esta semana. Considera fazer uma limpeza rapida.'
|
|
859
|
+
: 'No cleanup this week. Consider running a quick clean.'
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// Check for large caches
|
|
865
|
+
const chromeCache = getSize('~/Library/Caches/Google');
|
|
866
|
+
if (chromeCache > 500) {
|
|
867
|
+
recommendations.push({
|
|
868
|
+
priority: 'medium',
|
|
869
|
+
icon: figures.bullet,
|
|
870
|
+
text: LANG_CODE === 'pt'
|
|
871
|
+
? `Cache do Chrome com ${formatSize(chromeCache)}. Limpa os browsers.`
|
|
872
|
+
: `Chrome cache at ${formatSize(chromeCache)}. Clean browsers.`
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// Check Xcode
|
|
877
|
+
const xcodeData = getSize('~/Library/Developer/Xcode/DerivedData');
|
|
878
|
+
if (xcodeData > 1000) {
|
|
879
|
+
recommendations.push({
|
|
880
|
+
priority: 'medium',
|
|
881
|
+
icon: figures.bullet,
|
|
882
|
+
text: LANG_CODE === 'pt'
|
|
883
|
+
? `Xcode DerivedData com ${formatSize(xcodeData)}. Pode ser limpo.`
|
|
884
|
+
: `Xcode DerivedData at ${formatSize(xcodeData)}. Can be cleaned.`
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
return recommendations;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function showRecommendations() {
|
|
892
|
+
const recs = getSmartRecommendations();
|
|
893
|
+
|
|
894
|
+
if (recs.length === 0) return;
|
|
895
|
+
|
|
896
|
+
sectionHeader(MSG.RECOMMENDATIONS);
|
|
897
|
+
|
|
898
|
+
recs.forEach(rec => {
|
|
899
|
+
const color = rec.priority === 'high' ? chalk.red :
|
|
900
|
+
rec.priority === 'medium' ? chalk.yellow : chalk.cyan;
|
|
901
|
+
console.log(` ${color(rec.icon)} ${rec.text}`);
|
|
902
|
+
});
|
|
903
|
+
console.log();
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// ============================================================================
|
|
907
|
+
// FINAL REPORT
|
|
908
|
+
// ============================================================================
|
|
909
|
+
|
|
423
910
|
function showFinalReport(freed) {
|
|
424
911
|
const diskFreeAfter = exec("df -h / | tail -1").split(/\s+/)[3];
|
|
425
912
|
const health = calculateHealthScore();
|
|
426
913
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
914
|
+
const reportBox = `
|
|
915
|
+
${chalk.cyan('+')}${chalk.cyan('='.repeat(68))}${chalk.cyan('+')}
|
|
916
|
+
${chalk.cyan('|')}${chalk.white(' ' + MSG.FINAL_REPORT + ' ')}${chalk.cyan('|')}
|
|
917
|
+
${chalk.cyan('+')}${chalk.cyan('='.repeat(68))}${chalk.cyan('+')}
|
|
918
|
+
${chalk.cyan('|')} ${chalk.cyan('|')}
|
|
919
|
+
${chalk.cyan('|')} ${chalk.white(MSG.DISK)} ${chalk.cyan('|')}
|
|
920
|
+
${chalk.cyan('|')} ${figures.arrowRight} ${MSG.AVAILABLE}: ${chalk.green(diskFreeAfter)} ${chalk.cyan('|')}
|
|
921
|
+
${freed > 0 ? `${chalk.cyan('|')} ${figures.arrowRight} ${MSG.FREED}: ${chalk.green('+' + formatSize(freed))} ${chalk.cyan('|')}` : ''}
|
|
922
|
+
${chalk.cyan('|')} ${chalk.cyan('|')}
|
|
923
|
+
${chalk.cyan('|')} ${chalk.white(MSG.HEALTH_SCORE)} ${chalk.cyan('|')}
|
|
924
|
+
${chalk.cyan('|')} ${figures.arrowRight} ${health.color(health.score + '/100 - ' + health.text)} ${chalk.cyan('|')}
|
|
925
|
+
${chalk.cyan('|')} ${chalk.cyan('|')}
|
|
926
|
+
${chalk.cyan('+')}${chalk.cyan('='.repeat(68))}${chalk.cyan('+')}
|
|
927
|
+
`;
|
|
928
|
+
|
|
929
|
+
console.log(gradient.pastel.multiline(reportBox));
|
|
442
930
|
|
|
443
931
|
if (systemStats.swapUsed > 5000 || systemStats.ramUsedPercent > 85) {
|
|
444
|
-
console.log(`${
|
|
932
|
+
console.log(chalk.yellow(`${figures.warning} ${MSG.RESTART_REC}`));
|
|
445
933
|
console.log();
|
|
446
934
|
}
|
|
447
935
|
|
|
448
|
-
console.log(`${
|
|
936
|
+
console.log(chalk.green(`${figures.tick} ${MSG.READY}`));
|
|
937
|
+
|
|
938
|
+
// Store results for export
|
|
939
|
+
lastCleanupResults = {
|
|
940
|
+
timestamp: new Date().toISOString(),
|
|
941
|
+
freed: freed,
|
|
942
|
+
diskFree: diskFreeAfter,
|
|
943
|
+
healthScore: health.score,
|
|
944
|
+
healthText: health.text
|
|
945
|
+
};
|
|
449
946
|
|
|
450
947
|
logCleanup(freed, health.score);
|
|
948
|
+
|
|
949
|
+
// Play notification sound
|
|
950
|
+
playNotificationSound();
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// ============================================================================
|
|
954
|
+
// DISK ANALYZER
|
|
955
|
+
// ============================================================================
|
|
956
|
+
|
|
957
|
+
async function diskAnalyzer() {
|
|
958
|
+
header();
|
|
959
|
+
sectionHeader(MSG.DISK_ANALYSIS);
|
|
960
|
+
|
|
961
|
+
const spinner = ora(MSG.SCANNING).start();
|
|
962
|
+
|
|
963
|
+
// Analyze key directories
|
|
964
|
+
const dirs = [
|
|
965
|
+
{ path: HOME, name: 'Home' },
|
|
966
|
+
{ path: path.join(HOME, 'Library'), name: 'Library' },
|
|
967
|
+
{ path: path.join(HOME, 'Documents'), name: 'Documents' },
|
|
968
|
+
{ path: path.join(HOME, 'Downloads'), name: 'Downloads' },
|
|
969
|
+
{ path: path.join(HOME, 'Desktop'), name: 'Desktop' },
|
|
970
|
+
{ path: path.join(HOME, 'Movies'), name: 'Movies' },
|
|
971
|
+
{ path: path.join(HOME, 'Music'), name: 'Music' },
|
|
972
|
+
{ path: path.join(HOME, 'Pictures'), name: 'Pictures' },
|
|
973
|
+
{ path: '/Applications', name: 'Applications' }
|
|
974
|
+
];
|
|
975
|
+
|
|
976
|
+
const sizes = [];
|
|
977
|
+
|
|
978
|
+
for (const dir of dirs) {
|
|
979
|
+
if (fs.existsSync(dir.path)) {
|
|
980
|
+
dir.size = getSize(dir.path);
|
|
981
|
+
sizes.push(dir);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// Sort by size
|
|
986
|
+
sizes.sort((a, b) => b.size - a.size);
|
|
987
|
+
|
|
988
|
+
spinner.stop();
|
|
989
|
+
|
|
990
|
+
// Calculate max for bar scaling
|
|
991
|
+
const maxSize = Math.max(...sizes.map(s => s.size));
|
|
992
|
+
const barWidth = 40;
|
|
993
|
+
|
|
994
|
+
console.log(chalk.white(' Directory Usage:'));
|
|
995
|
+
console.log();
|
|
996
|
+
|
|
997
|
+
sizes.forEach((dir, i) => {
|
|
998
|
+
const barLength = Math.round((dir.size / maxSize) * barWidth);
|
|
999
|
+
const bar = chalk.cyan('\u2588'.repeat(barLength)) + chalk.gray('\u2591'.repeat(barWidth - barLength));
|
|
1000
|
+
const sizeStr = formatSize(dir.size).padStart(10);
|
|
1001
|
+
|
|
1002
|
+
console.log(` ${chalk.white(dir.name.padEnd(15))} ${bar} ${chalk.green(sizeStr)}`);
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
console.log();
|
|
1006
|
+
|
|
1007
|
+
// Show Library breakdown
|
|
1008
|
+
console.log(chalk.white(' Library Breakdown:'));
|
|
1009
|
+
console.log();
|
|
1010
|
+
|
|
1011
|
+
const libraryDirs = [
|
|
1012
|
+
{ path: path.join(HOME, 'Library/Caches'), name: 'Caches' },
|
|
1013
|
+
{ path: path.join(HOME, 'Library/Application Support'), name: 'App Support' },
|
|
1014
|
+
{ path: path.join(HOME, 'Library/Containers'), name: 'Containers' },
|
|
1015
|
+
{ path: path.join(HOME, 'Library/Developer'), name: 'Developer' },
|
|
1016
|
+
{ path: path.join(HOME, 'Library/Logs'), name: 'Logs' }
|
|
1017
|
+
];
|
|
1018
|
+
|
|
1019
|
+
for (const dir of libraryDirs) {
|
|
1020
|
+
if (fs.existsSync(dir.path)) {
|
|
1021
|
+
const size = getSize(dir.path);
|
|
1022
|
+
const sizeStr = formatSize(size).padStart(10);
|
|
1023
|
+
console.log(` ${figures.bullet} ${chalk.dim(dir.name.padEnd(20))} ${chalk.yellow(sizeStr)}`);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
await pressEnter();
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// ============================================================================
|
|
1031
|
+
// PRIVACY SWEEP
|
|
1032
|
+
// ============================================================================
|
|
1033
|
+
|
|
1034
|
+
async function privacySweep() {
|
|
1035
|
+
header();
|
|
1036
|
+
sectionHeader(MSG.OPT_PRIVACY);
|
|
1037
|
+
|
|
1038
|
+
const items = [];
|
|
1039
|
+
|
|
1040
|
+
// Browser data
|
|
1041
|
+
const browserPaths = [
|
|
1042
|
+
{ path: '~/Library/Cookies', name: 'System Cookies' },
|
|
1043
|
+
{ path: '~/Library/Application Support/Google/Chrome/Default/Cookies', name: 'Chrome Cookies' },
|
|
1044
|
+
{ path: '~/Library/Application Support/Google/Chrome/Default/History', name: 'Chrome History' },
|
|
1045
|
+
{ path: '~/Library/Application Support/Google/Chrome/Default/Login Data', name: 'Chrome Saved Logins' },
|
|
1046
|
+
{ path: '~/Library/Safari/History.db', name: 'Safari History' },
|
|
1047
|
+
{ path: '~/Library/Safari/Downloads.plist', name: 'Safari Downloads' },
|
|
1048
|
+
{ path: '~/Library/Safari/LocalStorage', name: 'Safari Local Storage' },
|
|
1049
|
+
{ path: '~/Library/Application Support/Firefox/Profiles', name: 'Firefox Profile Data' },
|
|
1050
|
+
{ path: '~/.recently-used', name: 'Recent Files' },
|
|
1051
|
+
{ path: '~/Library/Application Support/com.apple.sharedfilelist', name: 'Recent Items' },
|
|
1052
|
+
{ path: '~/Library/Preferences/com.apple.recentitems.plist', name: 'Recent Items Plist' }
|
|
1053
|
+
];
|
|
1054
|
+
|
|
1055
|
+
console.log(chalk.white(' Privacy-sensitive items found:'));
|
|
1056
|
+
console.log();
|
|
1057
|
+
|
|
1058
|
+
browserPaths.forEach((item, i) => {
|
|
1059
|
+
const fullPath = item.path.replace(/^~/, HOME);
|
|
1060
|
+
if (fs.existsSync(fullPath)) {
|
|
1061
|
+
const size = getSize(item.path);
|
|
1062
|
+
items.push({ ...item, fullPath, size });
|
|
1063
|
+
console.log(` ${chalk.cyan('[' + String(i + 1).padStart(2) + ']')} ${item.name.padEnd(30)} ${chalk.dim(formatSize(size))}`);
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
if (items.length === 0) {
|
|
1068
|
+
console.log(` ${chalk.green(figures.tick)} ${MSG.NO_ITEMS}`);
|
|
1069
|
+
await pressEnter();
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
console.log();
|
|
1074
|
+
console.log(chalk.dim(` ${MSG.SELECT_DELETE} [a=${MSG.ALL}/n=${MSG.CANCEL}]:`));
|
|
1075
|
+
const choice = await prompt(` ${chalk.white(MSG.CHOOSE + ':')} `);
|
|
1076
|
+
|
|
1077
|
+
if (choice.toLowerCase() === 'n' || choice === '') {
|
|
1078
|
+
await pressEnter();
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
let cleaned = 0;
|
|
1083
|
+
|
|
1084
|
+
if (choice.toLowerCase() === 'a') {
|
|
1085
|
+
const spinner = ora(MSG.CLEANING).start();
|
|
1086
|
+
|
|
1087
|
+
for (const item of items) {
|
|
1088
|
+
if (deleteItem(item.fullPath)) {
|
|
1089
|
+
cleaned += item.size;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
spinner.succeed(chalk.green(`${MSG.PRIVACY_CLEANED}: ${formatSize(cleaned)}`));
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
await pressEnter();
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// ============================================================================
|
|
1100
|
+
// MEMORY OPTIMIZER
|
|
1101
|
+
// ============================================================================
|
|
1102
|
+
|
|
1103
|
+
async function memoryOptimizer() {
|
|
1104
|
+
header();
|
|
1105
|
+
sectionHeader(MSG.OPT_MEMORY);
|
|
1106
|
+
|
|
1107
|
+
console.log(chalk.white(' Current Memory Status:'));
|
|
1108
|
+
console.log();
|
|
1109
|
+
console.log(` ${figures.bullet} RAM: ${systemStats.ramUsedPercent}% ${MSG.IN_USE}`);
|
|
1110
|
+
console.log(` ${figures.bullet} Swap: ${systemStats.swapUsed} MB ${MSG.IN_USE}`);
|
|
1111
|
+
console.log();
|
|
1112
|
+
|
|
1113
|
+
console.log(chalk.yellow(` ${figures.warning} This will run 'sudo purge' to free inactive memory.`));
|
|
1114
|
+
console.log(chalk.dim(' You may need to enter your password.'));
|
|
1115
|
+
console.log();
|
|
1116
|
+
|
|
1117
|
+
const confirm = await prompt(` ${chalk.white('Continue? [y/n]:')} `);
|
|
1118
|
+
|
|
1119
|
+
if (confirm.toLowerCase() !== 'y') {
|
|
1120
|
+
await pressEnter();
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
if (DRY_RUN) {
|
|
1125
|
+
console.log(chalk.yellow(` ${figures.info} [DRY-RUN] Would run: sudo purge`));
|
|
1126
|
+
} else {
|
|
1127
|
+
const spinner = ora('Freeing memory...').start();
|
|
1128
|
+
|
|
1129
|
+
try {
|
|
1130
|
+
execSync('sudo purge', { stdio: 'inherit' });
|
|
1131
|
+
spinner.succeed(chalk.green(MSG.MEMORY_FREED));
|
|
1132
|
+
} catch (e) {
|
|
1133
|
+
spinner.fail(chalk.red('Failed to free memory'));
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
// Refresh stats
|
|
1138
|
+
getSystemStats();
|
|
1139
|
+
console.log();
|
|
1140
|
+
console.log(chalk.white(' Updated Memory Status:'));
|
|
1141
|
+
console.log(` ${figures.bullet} RAM: ${systemStats.ramUsedPercent}% ${MSG.IN_USE}`);
|
|
1142
|
+
console.log(` ${figures.bullet} Swap: ${systemStats.swapUsed} MB ${MSG.IN_USE}`);
|
|
1143
|
+
|
|
1144
|
+
await pressEnter();
|
|
451
1145
|
}
|
|
452
1146
|
|
|
453
|
-
//
|
|
454
|
-
//
|
|
455
|
-
//
|
|
1147
|
+
// ============================================================================
|
|
1148
|
+
// BENCHMARK MODE
|
|
1149
|
+
// ============================================================================
|
|
1150
|
+
|
|
1151
|
+
async function benchmarkMode() {
|
|
1152
|
+
header();
|
|
1153
|
+
sectionHeader(MSG.OPT_BENCHMARK);
|
|
1154
|
+
|
|
1155
|
+
console.log(chalk.white(' Running system benchmark...'));
|
|
1156
|
+
console.log();
|
|
1157
|
+
|
|
1158
|
+
const results = {
|
|
1159
|
+
timestamp: new Date().toISOString(),
|
|
1160
|
+
tests: []
|
|
1161
|
+
};
|
|
1162
|
+
|
|
1163
|
+
// Disk read speed
|
|
1164
|
+
const spinner1 = ora('Testing disk read speed...').start();
|
|
1165
|
+
const diskStart = Date.now();
|
|
1166
|
+
exec('dd if=/dev/zero of=/tmp/benchmark_test bs=1m count=100 2>/dev/null');
|
|
1167
|
+
const diskWrite = Date.now() - diskStart;
|
|
1168
|
+
|
|
1169
|
+
const diskReadStart = Date.now();
|
|
1170
|
+
exec('dd if=/tmp/benchmark_test of=/dev/null bs=1m 2>/dev/null');
|
|
1171
|
+
const diskRead = Date.now() - diskReadStart;
|
|
1172
|
+
|
|
1173
|
+
exec('rm /tmp/benchmark_test');
|
|
1174
|
+
spinner1.succeed(`Disk: Write ${Math.round(100000 / diskWrite)} MB/s, Read ${Math.round(100000 / diskRead)} MB/s`);
|
|
1175
|
+
|
|
1176
|
+
results.tests.push({
|
|
1177
|
+
name: 'Disk Write',
|
|
1178
|
+
value: Math.round(100000 / diskWrite),
|
|
1179
|
+
unit: 'MB/s'
|
|
1180
|
+
});
|
|
1181
|
+
results.tests.push({
|
|
1182
|
+
name: 'Disk Read',
|
|
1183
|
+
value: Math.round(100000 / diskRead),
|
|
1184
|
+
unit: 'MB/s'
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
// CPU benchmark
|
|
1188
|
+
const spinner2 = ora('Testing CPU performance...').start();
|
|
1189
|
+
const cpuStart = Date.now();
|
|
1190
|
+
let cpuTest = 0;
|
|
1191
|
+
for (let i = 0; i < 10000000; i++) {
|
|
1192
|
+
cpuTest += Math.sqrt(i);
|
|
1193
|
+
}
|
|
1194
|
+
const cpuTime = Date.now() - cpuStart;
|
|
1195
|
+
spinner2.succeed(`CPU: ${cpuTime}ms for 10M sqrt operations`);
|
|
1196
|
+
|
|
1197
|
+
results.tests.push({
|
|
1198
|
+
name: 'CPU (10M sqrt)',
|
|
1199
|
+
value: cpuTime,
|
|
1200
|
+
unit: 'ms'
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1203
|
+
// Memory benchmark
|
|
1204
|
+
const spinner3 = ora('Testing memory allocation...').start();
|
|
1205
|
+
const memStart = Date.now();
|
|
1206
|
+
const testArrays = [];
|
|
1207
|
+
for (let i = 0; i < 100; i++) {
|
|
1208
|
+
testArrays.push(new Array(1000000).fill(Math.random()));
|
|
1209
|
+
}
|
|
1210
|
+
const memTime = Date.now() - memStart;
|
|
1211
|
+
spinner3.succeed(`Memory: ${memTime}ms to allocate 100MB`);
|
|
1212
|
+
|
|
1213
|
+
results.tests.push({
|
|
1214
|
+
name: 'Memory (100MB alloc)',
|
|
1215
|
+
value: memTime,
|
|
1216
|
+
unit: 'ms'
|
|
1217
|
+
});
|
|
1218
|
+
|
|
1219
|
+
// Save results
|
|
1220
|
+
const benchmarkLine = `${results.timestamp} | Disk: W${Math.round(100000 / diskWrite)}MB/s R${Math.round(100000 / diskRead)}MB/s | CPU: ${cpuTime}ms | Mem: ${memTime}ms\n`;
|
|
1221
|
+
fs.appendFileSync(BENCHMARK_FILE, benchmarkLine);
|
|
1222
|
+
|
|
1223
|
+
console.log();
|
|
1224
|
+
console.log(chalk.green(`${figures.tick} ${MSG.BENCHMARK_COMPLETE}`));
|
|
1225
|
+
|
|
1226
|
+
// Show history if available
|
|
1227
|
+
if (fs.existsSync(BENCHMARK_FILE)) {
|
|
1228
|
+
const history = fs.readFileSync(BENCHMARK_FILE, 'utf8').split('\n').filter(l => l.trim());
|
|
1229
|
+
if (history.length > 1) {
|
|
1230
|
+
console.log();
|
|
1231
|
+
console.log(chalk.white(' Recent benchmarks:'));
|
|
1232
|
+
history.slice(-5).forEach(line => {
|
|
1233
|
+
console.log(chalk.dim(` ${line}`));
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
await pressEnter();
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// ============================================================================
|
|
1242
|
+
// SCHEDULER (LAUNCHD)
|
|
1243
|
+
// ============================================================================
|
|
1244
|
+
|
|
1245
|
+
async function scheduler() {
|
|
1246
|
+
header();
|
|
1247
|
+
sectionHeader(MSG.OPT_SCHEDULER);
|
|
1248
|
+
|
|
1249
|
+
const plistPath = path.join(HOME, 'Library/LaunchAgents/com.aris.mac-cleaner.plist');
|
|
1250
|
+
const isScheduled = fs.existsSync(plistPath);
|
|
1251
|
+
|
|
1252
|
+
console.log(chalk.white(' Cleanup Scheduler'));
|
|
1253
|
+
console.log();
|
|
1254
|
+
|
|
1255
|
+
if (isScheduled) {
|
|
1256
|
+
console.log(chalk.green(` ${figures.tick} Scheduled cleanup is ACTIVE`));
|
|
1257
|
+
console.log();
|
|
1258
|
+
console.log(` ${chalk.cyan('[1]')} Remove schedule`);
|
|
1259
|
+
console.log(` ${chalk.cyan('[2]')} View current schedule`);
|
|
1260
|
+
} else {
|
|
1261
|
+
console.log(chalk.yellow(` ${figures.info} No scheduled cleanup configured`));
|
|
1262
|
+
console.log();
|
|
1263
|
+
console.log(` ${chalk.cyan('[1]')} Schedule daily cleanup (midnight)`);
|
|
1264
|
+
console.log(` ${chalk.cyan('[2]')} Schedule weekly cleanup (Sunday midnight)`);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
console.log();
|
|
1268
|
+
console.log(` ${chalk.cyan('[0]')} ${MSG.BACK}`);
|
|
1269
|
+
console.log();
|
|
1270
|
+
|
|
1271
|
+
const choice = await prompt(` ${chalk.white(MSG.CHOOSE + ':')} `);
|
|
1272
|
+
|
|
1273
|
+
if (isScheduled) {
|
|
1274
|
+
if (choice === '1') {
|
|
1275
|
+
// Remove schedule
|
|
1276
|
+
try {
|
|
1277
|
+
exec(`launchctl unload "${plistPath}" 2>/dev/null`);
|
|
1278
|
+
fs.unlinkSync(plistPath);
|
|
1279
|
+
console.log(chalk.green(` ${figures.tick} ${MSG.SCHEDULE_REMOVED}`));
|
|
1280
|
+
} catch (e) {
|
|
1281
|
+
console.log(chalk.red(` ${figures.cross} Failed to remove schedule`));
|
|
1282
|
+
}
|
|
1283
|
+
} else if (choice === '2') {
|
|
1284
|
+
// View schedule
|
|
1285
|
+
const content = fs.readFileSync(plistPath, 'utf8');
|
|
1286
|
+
console.log();
|
|
1287
|
+
console.log(chalk.dim(content));
|
|
1288
|
+
}
|
|
1289
|
+
} else {
|
|
1290
|
+
if (choice === '1' || choice === '2') {
|
|
1291
|
+
const isDaily = choice === '1';
|
|
1292
|
+
|
|
1293
|
+
const plistContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1294
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
1295
|
+
<plist version="1.0">
|
|
1296
|
+
<dict>
|
|
1297
|
+
<key>Label</key>
|
|
1298
|
+
<string>com.aris.mac-cleaner</string>
|
|
1299
|
+
<key>ProgramArguments</key>
|
|
1300
|
+
<array>
|
|
1301
|
+
<string>${process.execPath}</string>
|
|
1302
|
+
<string>${__filename}</string>
|
|
1303
|
+
<string>--quick</string>
|
|
1304
|
+
</array>
|
|
1305
|
+
<key>StartCalendarInterval</key>
|
|
1306
|
+
<dict>
|
|
1307
|
+
<key>Hour</key>
|
|
1308
|
+
<integer>0</integer>
|
|
1309
|
+
<key>Minute</key>
|
|
1310
|
+
<integer>0</integer>
|
|
1311
|
+
${isDaily ? '' : '<key>Weekday</key>\n <integer>0</integer>'}
|
|
1312
|
+
</dict>
|
|
1313
|
+
<key>StandardOutPath</key>
|
|
1314
|
+
<string>${path.join(CONFIG_DIR, 'scheduled-cleanup.log')}</string>
|
|
1315
|
+
<key>StandardErrorPath</key>
|
|
1316
|
+
<string>${path.join(CONFIG_DIR, 'scheduled-cleanup-error.log')}</string>
|
|
1317
|
+
</dict>
|
|
1318
|
+
</plist>`;
|
|
1319
|
+
|
|
1320
|
+
try {
|
|
1321
|
+
fs.writeFileSync(plistPath, plistContent);
|
|
1322
|
+
exec(`launchctl load "${plistPath}"`);
|
|
1323
|
+
console.log(chalk.green(` ${figures.tick} ${isDaily ? MSG.SCHEDULED_DAILY : MSG.SCHEDULED_WEEKLY}`));
|
|
1324
|
+
} catch (e) {
|
|
1325
|
+
console.log(chalk.red(` ${figures.cross} Failed to create schedule`));
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
await pressEnter();
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// ============================================================================
|
|
1334
|
+
// EXPORT HTML REPORT
|
|
1335
|
+
// ============================================================================
|
|
1336
|
+
|
|
1337
|
+
async function exportHtmlReport() {
|
|
1338
|
+
header();
|
|
1339
|
+
sectionHeader(MSG.OPT_EXPORT);
|
|
1340
|
+
|
|
1341
|
+
getSystemStats();
|
|
1342
|
+
const health = calculateHealthScore();
|
|
1343
|
+
const recs = getSmartRecommendations();
|
|
1344
|
+
|
|
1345
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
1346
|
+
const reportPath = path.join(REPORTS_DIR, `report-${timestamp}.html`);
|
|
1347
|
+
|
|
1348
|
+
// Read history
|
|
1349
|
+
let historyData = [];
|
|
1350
|
+
if (fs.existsSync(HISTORY_FILE)) {
|
|
1351
|
+
historyData = fs.readFileSync(HISTORY_FILE, 'utf8').split('\n').filter(l => l.trim()).slice(-30);
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
const html = `<!DOCTYPE html>
|
|
1355
|
+
<html lang="en">
|
|
1356
|
+
<head>
|
|
1357
|
+
<meta charset="UTF-8">
|
|
1358
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1359
|
+
<title>Aris Mac Cleaner Report - ${new Date().toLocaleDateString()}</title>
|
|
1360
|
+
<style>
|
|
1361
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
1362
|
+
body {
|
|
1363
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
1364
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
1365
|
+
color: #fff;
|
|
1366
|
+
min-height: 100vh;
|
|
1367
|
+
padding: 40px;
|
|
1368
|
+
}
|
|
1369
|
+
.container { max-width: 900px; margin: 0 auto; }
|
|
1370
|
+
.header {
|
|
1371
|
+
text-align: center;
|
|
1372
|
+
margin-bottom: 40px;
|
|
1373
|
+
padding: 30px;
|
|
1374
|
+
background: rgba(255,255,255,0.1);
|
|
1375
|
+
border-radius: 20px;
|
|
1376
|
+
backdrop-filter: blur(10px);
|
|
1377
|
+
}
|
|
1378
|
+
.header h1 {
|
|
1379
|
+
font-size: 2.5em;
|
|
1380
|
+
background: linear-gradient(90deg, #00d2ff, #3a7bd5);
|
|
1381
|
+
-webkit-background-clip: text;
|
|
1382
|
+
-webkit-text-fill-color: transparent;
|
|
1383
|
+
}
|
|
1384
|
+
.header p { color: #888; margin-top: 10px; }
|
|
1385
|
+
.card {
|
|
1386
|
+
background: rgba(255,255,255,0.05);
|
|
1387
|
+
border-radius: 15px;
|
|
1388
|
+
padding: 25px;
|
|
1389
|
+
margin-bottom: 20px;
|
|
1390
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
1391
|
+
}
|
|
1392
|
+
.card h2 {
|
|
1393
|
+
color: #00d2ff;
|
|
1394
|
+
margin-bottom: 20px;
|
|
1395
|
+
font-size: 1.3em;
|
|
1396
|
+
}
|
|
1397
|
+
.stat-row {
|
|
1398
|
+
display: flex;
|
|
1399
|
+
justify-content: space-between;
|
|
1400
|
+
padding: 12px 0;
|
|
1401
|
+
border-bottom: 1px solid rgba(255,255,255,0.05);
|
|
1402
|
+
}
|
|
1403
|
+
.stat-row:last-child { border-bottom: none; }
|
|
1404
|
+
.stat-label { color: #888; }
|
|
1405
|
+
.stat-value { font-weight: bold; }
|
|
1406
|
+
.health-score {
|
|
1407
|
+
text-align: center;
|
|
1408
|
+
padding: 40px;
|
|
1409
|
+
}
|
|
1410
|
+
.health-score .score {
|
|
1411
|
+
font-size: 4em;
|
|
1412
|
+
font-weight: bold;
|
|
1413
|
+
background: linear-gradient(90deg, ${health.score >= 80 ? '#00ff87, #60efff' : health.score >= 60 ? '#ffcc00, #ff6600' : '#ff0000, #ff6666'});
|
|
1414
|
+
-webkit-background-clip: text;
|
|
1415
|
+
-webkit-text-fill-color: transparent;
|
|
1416
|
+
}
|
|
1417
|
+
.health-score .label {
|
|
1418
|
+
color: #888;
|
|
1419
|
+
font-size: 1.2em;
|
|
1420
|
+
margin-top: 10px;
|
|
1421
|
+
}
|
|
1422
|
+
.recommendation {
|
|
1423
|
+
padding: 15px;
|
|
1424
|
+
margin: 10px 0;
|
|
1425
|
+
border-radius: 10px;
|
|
1426
|
+
background: rgba(255,204,0,0.1);
|
|
1427
|
+
border-left: 3px solid #ffcc00;
|
|
1428
|
+
}
|
|
1429
|
+
.recommendation.high {
|
|
1430
|
+
background: rgba(255,0,0,0.1);
|
|
1431
|
+
border-left-color: #ff4444;
|
|
1432
|
+
}
|
|
1433
|
+
.history-item {
|
|
1434
|
+
font-family: monospace;
|
|
1435
|
+
font-size: 0.9em;
|
|
1436
|
+
padding: 8px;
|
|
1437
|
+
color: #888;
|
|
1438
|
+
}
|
|
1439
|
+
.footer {
|
|
1440
|
+
text-align: center;
|
|
1441
|
+
margin-top: 40px;
|
|
1442
|
+
color: #666;
|
|
1443
|
+
font-size: 0.9em;
|
|
1444
|
+
}
|
|
1445
|
+
</style>
|
|
1446
|
+
</head>
|
|
1447
|
+
<body>
|
|
1448
|
+
<div class="container">
|
|
1449
|
+
<div class="header">
|
|
1450
|
+
<h1>ARIS MAC CLEANER</h1>
|
|
1451
|
+
<p>System Report - ${new Date().toLocaleString()}</p>
|
|
1452
|
+
</div>
|
|
1453
|
+
|
|
1454
|
+
<div class="card health-score">
|
|
1455
|
+
<div class="score">${health.score}/100</div>
|
|
1456
|
+
<div class="label">${MSG.HEALTH_SCORE} - ${health.text}</div>
|
|
1457
|
+
</div>
|
|
1458
|
+
|
|
1459
|
+
<div class="card">
|
|
1460
|
+
<h2>${MSG.DIAGNOSE}</h2>
|
|
1461
|
+
<div class="stat-row">
|
|
1462
|
+
<span class="stat-label">${MSG.DISK}</span>
|
|
1463
|
+
<span class="stat-value">${systemStats.diskFree}GB ${MSG.FREE_OF} ${systemStats.diskTotal}GB (${systemStats.diskPercent}% ${MSG.IN_USE})</span>
|
|
1464
|
+
</div>
|
|
1465
|
+
<div class="stat-row">
|
|
1466
|
+
<span class="stat-label">${MSG.RAM}</span>
|
|
1467
|
+
<span class="stat-value">${systemStats.ramUsedPercent}% ${MSG.IN_USE}</span>
|
|
1468
|
+
</div>
|
|
1469
|
+
<div class="stat-row">
|
|
1470
|
+
<span class="stat-label">${MSG.SWAP}</span>
|
|
1471
|
+
<span class="stat-value">${systemStats.swapUsed} MB ${MSG.IN_USE}</span>
|
|
1472
|
+
</div>
|
|
1473
|
+
<div class="stat-row">
|
|
1474
|
+
<span class="stat-label">${MSG.CPU}</span>
|
|
1475
|
+
<span class="stat-value">${systemStats.cpuUsed}% ${MSG.IN_USE}</span>
|
|
1476
|
+
</div>
|
|
1477
|
+
</div>
|
|
1478
|
+
|
|
1479
|
+
${recs.length > 0 ? `
|
|
1480
|
+
<div class="card">
|
|
1481
|
+
<h2>${MSG.RECOMMENDATIONS}</h2>
|
|
1482
|
+
${recs.map(r => `<div class="recommendation ${r.priority}">${r.text}</div>`).join('')}
|
|
1483
|
+
</div>
|
|
1484
|
+
` : ''}
|
|
1485
|
+
|
|
1486
|
+
${historyData.length > 0 ? `
|
|
1487
|
+
<div class="card">
|
|
1488
|
+
<h2>${MSG.HISTORY}</h2>
|
|
1489
|
+
${historyData.map(h => `<div class="history-item">${h}</div>`).join('')}
|
|
1490
|
+
</div>
|
|
1491
|
+
` : ''}
|
|
1492
|
+
|
|
1493
|
+
${lastCleanupResults ? `
|
|
1494
|
+
<div class="card">
|
|
1495
|
+
<h2>Last Cleanup</h2>
|
|
1496
|
+
<div class="stat-row">
|
|
1497
|
+
<span class="stat-label">${MSG.FREED}</span>
|
|
1498
|
+
<span class="stat-value">${formatSize(lastCleanupResults.freed)}</span>
|
|
1499
|
+
</div>
|
|
1500
|
+
</div>
|
|
1501
|
+
` : ''}
|
|
1502
|
+
|
|
1503
|
+
<div class="footer">
|
|
1504
|
+
<p>Generated by Aris Mac Cleaner v${VERSION}</p>
|
|
1505
|
+
<p>por Salvador Reis</p>
|
|
1506
|
+
</div>
|
|
1507
|
+
</div>
|
|
1508
|
+
</body>
|
|
1509
|
+
</html>`;
|
|
1510
|
+
|
|
1511
|
+
fs.writeFileSync(reportPath, html);
|
|
1512
|
+
|
|
1513
|
+
console.log(chalk.green(` ${figures.tick} ${MSG.REPORT_EXPORTED}`));
|
|
1514
|
+
console.log(chalk.dim(` ${reportPath}`));
|
|
1515
|
+
console.log();
|
|
1516
|
+
|
|
1517
|
+
const open = await prompt(` Open in browser? [y/n]: `);
|
|
1518
|
+
if (open.toLowerCase() === 'y') {
|
|
1519
|
+
exec(`open "${reportPath}"`);
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
await pressEnter();
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
// ============================================================================
|
|
1526
|
+
// STATISTICS WITH ASCII CHART
|
|
1527
|
+
// ============================================================================
|
|
1528
|
+
|
|
1529
|
+
async function showStatistics() {
|
|
1530
|
+
header();
|
|
1531
|
+
sectionHeader(MSG.OPT_STATS);
|
|
1532
|
+
|
|
1533
|
+
if (!fs.existsSync(HISTORY_FILE)) {
|
|
1534
|
+
console.log(` ${chalk.dim(MSG.NO_HISTORY)}`);
|
|
1535
|
+
await pressEnter();
|
|
1536
|
+
return;
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
const history = fs.readFileSync(HISTORY_FILE, 'utf8').split('\n').filter(l => l.trim());
|
|
1540
|
+
const currentMonth = new Date().toISOString().slice(0, 7);
|
|
1541
|
+
|
|
1542
|
+
const monthLines = history.filter(l => l.startsWith(currentMonth));
|
|
1543
|
+
const monthCleanups = monthLines.length;
|
|
1544
|
+
|
|
1545
|
+
let monthFreed = 0;
|
|
1546
|
+
let monthScoreSum = 0;
|
|
1547
|
+
const freedValues = [];
|
|
1548
|
+
|
|
1549
|
+
monthLines.forEach(line => {
|
|
1550
|
+
const freedMatch = line.match(/(\d+)\s*MB/);
|
|
1551
|
+
const scoreMatch = line.match(/Score:\s*(\d+)/);
|
|
1552
|
+
if (freedMatch) {
|
|
1553
|
+
const freed = parseInt(freedMatch[1]);
|
|
1554
|
+
monthFreed += freed;
|
|
1555
|
+
freedValues.push(freed);
|
|
1556
|
+
}
|
|
1557
|
+
if (scoreMatch) monthScoreSum += parseInt(scoreMatch[1]);
|
|
1558
|
+
});
|
|
1559
|
+
|
|
1560
|
+
const monthAvg = monthCleanups > 0 ? Math.round(monthScoreSum / monthCleanups) : 0;
|
|
1561
|
+
|
|
1562
|
+
console.log(chalk.white(` ${MSG.THIS_MONTH}:`));
|
|
1563
|
+
console.log(` ${figures.bullet} ${MSG.CLEANINGS}: ${chalk.cyan(monthCleanups)}`);
|
|
1564
|
+
console.log(` ${figures.bullet} ${MSG.TOTAL} ${MSG.FREED}: ${chalk.green(formatSize(monthFreed))}`);
|
|
1565
|
+
console.log(` ${figures.bullet} ${MSG.AVG_SCORE}: ${chalk.yellow(monthAvg + '/100')}`);
|
|
1566
|
+
console.log();
|
|
1567
|
+
|
|
1568
|
+
// Show ASCII chart if we have enough data
|
|
1569
|
+
if (freedValues.length >= 3) {
|
|
1570
|
+
console.log(chalk.white(' Space Freed (MB) - Last cleanups:'));
|
|
1571
|
+
console.log();
|
|
1572
|
+
|
|
1573
|
+
try {
|
|
1574
|
+
const chartData = freedValues.slice(-15);
|
|
1575
|
+
const chart = asciichart.plot(chartData, {
|
|
1576
|
+
height: 8,
|
|
1577
|
+
colors: [asciichart.cyan],
|
|
1578
|
+
format: (x) => String(Math.round(x)).padStart(6)
|
|
1579
|
+
});
|
|
1580
|
+
chart.split('\n').forEach(line => console.log(' ' + line));
|
|
1581
|
+
} catch (e) {
|
|
1582
|
+
// Fallback if asciichart fails
|
|
1583
|
+
freedValues.slice(-10).forEach((v, i) => {
|
|
1584
|
+
const bar = chalk.cyan('\u2588'.repeat(Math.min(Math.round(v / 50), 40)));
|
|
1585
|
+
console.log(` ${bar} ${v} MB`);
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
console.log();
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
console.log(chalk.white(` ${MSG.HISTORY}:`));
|
|
1592
|
+
history.slice(-10).forEach(line => {
|
|
1593
|
+
console.log(chalk.dim(` ${line}`));
|
|
1594
|
+
});
|
|
1595
|
+
|
|
1596
|
+
await pressEnter();
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
// ============================================================================
|
|
1600
|
+
// QUICK CLEAN (ENHANCED)
|
|
1601
|
+
// ============================================================================
|
|
456
1602
|
|
|
457
1603
|
async function quickClean() {
|
|
458
1604
|
header();
|
|
459
1605
|
getSystemStats();
|
|
460
1606
|
showDiagnostics();
|
|
1607
|
+
showRecommendations();
|
|
461
1608
|
sectionHeader(MSG.CATEGORIES);
|
|
462
1609
|
|
|
463
1610
|
const items = [];
|
|
@@ -474,6 +1621,8 @@ async function quickClean() {
|
|
|
474
1621
|
}
|
|
475
1622
|
}
|
|
476
1623
|
|
|
1624
|
+
const spinner = ora(MSG.SCANNING).start();
|
|
1625
|
+
|
|
477
1626
|
// System Caches
|
|
478
1627
|
addItem('~/Library/Caches/Google', 'Google/Chrome Cache', 'system');
|
|
479
1628
|
addItem('~/Library/Caches/com.spotify.client', 'Spotify Cache', 'system');
|
|
@@ -545,8 +1694,10 @@ async function quickClean() {
|
|
|
545
1694
|
});
|
|
546
1695
|
}
|
|
547
1696
|
|
|
1697
|
+
spinner.stop();
|
|
1698
|
+
|
|
548
1699
|
if (items.length === 0) {
|
|
549
|
-
console.log(` ${
|
|
1700
|
+
console.log(` ${chalk.green(figures.tick)} ${MSG.NO_ITEMS}`);
|
|
550
1701
|
await pressEnter();
|
|
551
1702
|
return;
|
|
552
1703
|
}
|
|
@@ -557,11 +1708,11 @@ async function quickClean() {
|
|
|
557
1708
|
if (catItems.length === 0) return;
|
|
558
1709
|
|
|
559
1710
|
const catSize = catItems.reduce((sum, i) => sum + i.size, 0);
|
|
560
|
-
console.log(` ${
|
|
1711
|
+
console.log(` ${chalk.white(' ' + catName)} ${chalk.dim('(' + formatSize(catSize) + ')')}`);
|
|
561
1712
|
|
|
562
1713
|
catItems.forEach(item => {
|
|
563
1714
|
const idx = items.indexOf(item) + 1;
|
|
564
|
-
console.log(` ${
|
|
1715
|
+
console.log(` ${chalk.cyan('[' + String(idx).padStart(2) + ']')} ${item.name.padEnd(35)} ${chalk.dim(String(item.size).padStart(6) + ' MB')}`);
|
|
565
1716
|
});
|
|
566
1717
|
console.log();
|
|
567
1718
|
}
|
|
@@ -573,18 +1724,18 @@ async function quickClean() {
|
|
|
573
1724
|
showCategory('apps', MSG.APPS_CAT);
|
|
574
1725
|
showCategory('custom', 'Custom');
|
|
575
1726
|
|
|
576
|
-
console.log(` ${
|
|
1727
|
+
console.log(` ${figures.arrowRight} ${chalk.white(MSG.TOTAL + ':')} ${chalk.green(formatSize(totalSize))}`);
|
|
577
1728
|
console.log();
|
|
578
1729
|
|
|
579
1730
|
// Selection
|
|
580
1731
|
sectionHeader(MSG.SELECT_DELETE);
|
|
581
|
-
console.log(`
|
|
1732
|
+
console.log(chalk.dim(` a = ${MSG.ALL} | n = ${MSG.CANCEL} | 1,3,5 | 1-5 | 1-5,8,10`));
|
|
582
1733
|
console.log();
|
|
583
1734
|
|
|
584
|
-
const choice = await prompt(` ${
|
|
1735
|
+
const choice = await prompt(` ${chalk.white(MSG.CHOOSE + ':')} `);
|
|
585
1736
|
|
|
586
1737
|
if (choice.toLowerCase() === 'n' || choice === '') {
|
|
587
|
-
console.log(` ${
|
|
1738
|
+
console.log(` ${chalk.yellow(MSG.CANCEL)}`);
|
|
588
1739
|
await pressEnter();
|
|
589
1740
|
return;
|
|
590
1741
|
}
|
|
@@ -608,42 +1759,68 @@ async function quickClean() {
|
|
|
608
1759
|
});
|
|
609
1760
|
}
|
|
610
1761
|
|
|
611
|
-
//
|
|
1762
|
+
// Create backup
|
|
1763
|
+
const selectedItems = items.filter(i => i.selected);
|
|
1764
|
+
if (selectedItems.length > 0 && !DRY_RUN) {
|
|
1765
|
+
const backupFile = createBackup(selectedItems);
|
|
1766
|
+
console.log(chalk.dim(` ${figures.info} ${MSG.BACKUP_CREATED}: ${path.basename(backupFile)}`));
|
|
1767
|
+
console.log();
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
// Clean with progress bar
|
|
612
1771
|
console.log();
|
|
613
1772
|
sectionHeader(MSG.CLEANING);
|
|
614
1773
|
|
|
1774
|
+
const progressBar = createProgressBar(selectedItems.length);
|
|
1775
|
+
progressBar.start(selectedItems.length, 0);
|
|
1776
|
+
|
|
615
1777
|
let cleanedSize = 0;
|
|
1778
|
+
const claudeVersionsPathLocal = path.join(HOME, '.local/share/claude/versions');
|
|
616
1779
|
|
|
617
|
-
for (
|
|
1780
|
+
for (let i = 0; i < items.length; i++) {
|
|
1781
|
+
const item = items[i];
|
|
618
1782
|
if (!item.selected) continue;
|
|
619
1783
|
|
|
620
1784
|
if (item.path === 'CLAUDE_VERSIONS') {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
1785
|
+
if (!DRY_RUN) {
|
|
1786
|
+
const versions = fs.readdirSync(claudeVersionsPathLocal).sort().reverse();
|
|
1787
|
+
versions.slice(1).forEach(v => {
|
|
1788
|
+
try {
|
|
1789
|
+
deleteItem(path.join(claudeVersionsPathLocal, v));
|
|
1790
|
+
} catch (e) {}
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
628
1793
|
cleanedSize += item.size;
|
|
629
1794
|
} else if (item.path === 'OLD_DOWNLOADS') {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
1795
|
+
if (!DRY_RUN) {
|
|
1796
|
+
exec('find ~/Downloads -name "*.dmg" -mtime +7 -delete 2>/dev/null');
|
|
1797
|
+
exec('find ~/Downloads -name "*.zip" -mtime +30 -delete 2>/dev/null');
|
|
1798
|
+
}
|
|
633
1799
|
cleanedSize += item.size;
|
|
634
1800
|
} else if (fs.existsSync(item.path)) {
|
|
635
|
-
|
|
636
|
-
fs.rmSync(item.path, { recursive: true, force: true });
|
|
637
|
-
console.log(` ${c.green}${CHECK}${c.reset} ${item.name}`);
|
|
1801
|
+
if (deleteItem(item.path)) {
|
|
638
1802
|
cleanedSize += item.size;
|
|
639
|
-
}
|
|
1803
|
+
}
|
|
640
1804
|
}
|
|
1805
|
+
|
|
1806
|
+
progressBar.increment();
|
|
641
1807
|
}
|
|
642
1808
|
|
|
1809
|
+
progressBar.stop();
|
|
1810
|
+
|
|
643
1811
|
// npm cache
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
1812
|
+
if (!DRY_RUN) {
|
|
1813
|
+
try {
|
|
1814
|
+
execSync('npm cache clean --force 2>/dev/null', { stdio: 'ignore' });
|
|
1815
|
+
} catch (e) {}
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
console.log();
|
|
1819
|
+
|
|
1820
|
+
// Show cleaned items
|
|
1821
|
+
selectedItems.forEach(item => {
|
|
1822
|
+
console.log(` ${chalk.green(figures.tick)} ${item.name}`);
|
|
1823
|
+
});
|
|
647
1824
|
|
|
648
1825
|
console.log();
|
|
649
1826
|
getSystemStats();
|
|
@@ -651,16 +1828,15 @@ async function quickClean() {
|
|
|
651
1828
|
await pressEnter();
|
|
652
1829
|
}
|
|
653
1830
|
|
|
654
|
-
//
|
|
655
|
-
//
|
|
656
|
-
//
|
|
1831
|
+
// ============================================================================
|
|
1832
|
+
// FIND LARGE FILES
|
|
1833
|
+
// ============================================================================
|
|
657
1834
|
|
|
658
1835
|
async function findLargeFiles() {
|
|
659
1836
|
header();
|
|
660
1837
|
sectionHeader(`${MSG.LARGE_FILES} (>100MB)`);
|
|
661
1838
|
|
|
662
|
-
|
|
663
|
-
console.log();
|
|
1839
|
+
const spinner = ora(MSG.SCANNING).start();
|
|
664
1840
|
|
|
665
1841
|
const files = [];
|
|
666
1842
|
|
|
@@ -681,8 +1857,10 @@ async function findLargeFiles() {
|
|
|
681
1857
|
files.push({ path: file, size });
|
|
682
1858
|
});
|
|
683
1859
|
|
|
1860
|
+
spinner.stop();
|
|
1861
|
+
|
|
684
1862
|
if (files.length === 0) {
|
|
685
|
-
console.log(` ${
|
|
1863
|
+
console.log(` ${chalk.green(figures.tick)} ${MSG.NO_ITEMS}`);
|
|
686
1864
|
await pressEnter();
|
|
687
1865
|
return;
|
|
688
1866
|
}
|
|
@@ -697,15 +1875,15 @@ async function findLargeFiles() {
|
|
|
697
1875
|
if (displayFile.length > 50) {
|
|
698
1876
|
displayFile = '...' + displayFile.slice(-47);
|
|
699
1877
|
}
|
|
700
|
-
console.log(` ${
|
|
1878
|
+
console.log(` ${chalk.cyan('[' + String(i + 1).padStart(2) + ']')} ${chalk.dim(String(file.size).padStart(6) + ' MB')} ${displayFile}`);
|
|
701
1879
|
});
|
|
702
1880
|
|
|
703
1881
|
console.log();
|
|
704
|
-
console.log(` ${
|
|
1882
|
+
console.log(` ${figures.arrowRight} ${chalk.white(MSG.TOTAL + ':')} ${chalk.green(formatSize(totalLarge))} ${chalk.dim('(' + files.length + ' files)')}`);
|
|
705
1883
|
console.log();
|
|
706
1884
|
|
|
707
|
-
console.log(` ${
|
|
708
|
-
const choice = await prompt(` ${
|
|
1885
|
+
console.log(chalk.dim(` ${MSG.SELECT_DELETE} [numbers/a/n]:`));
|
|
1886
|
+
const choice = await prompt(` ${chalk.white(MSG.CHOOSE + ':')} `);
|
|
709
1887
|
|
|
710
1888
|
if (choice.toLowerCase() === 'n' || choice === '') {
|
|
711
1889
|
await pressEnter();
|
|
@@ -738,28 +1916,28 @@ async function findLargeFiles() {
|
|
|
738
1916
|
for (let i = 0; i < files.length; i++) {
|
|
739
1917
|
if (!selected[i]) continue;
|
|
740
1918
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
console.log(` ${c.green}${CHECK}${c.reset} Deleted: ${path.basename(files[i].path)}`);
|
|
1919
|
+
if (deleteItem(files[i].path)) {
|
|
1920
|
+
console.log(` ${chalk.green(figures.tick)} Deleted: ${path.basename(files[i].path)}`);
|
|
744
1921
|
deletedSize += files[i].size;
|
|
745
|
-
}
|
|
1922
|
+
}
|
|
746
1923
|
}
|
|
747
1924
|
|
|
748
1925
|
console.log();
|
|
749
|
-
console.log(` ${
|
|
1926
|
+
console.log(` ${chalk.green(MSG.FREED + ': ' + formatSize(deletedSize))}`);
|
|
1927
|
+
|
|
1928
|
+
playNotificationSound();
|
|
750
1929
|
await pressEnter();
|
|
751
1930
|
}
|
|
752
1931
|
|
|
753
|
-
//
|
|
754
|
-
//
|
|
755
|
-
//
|
|
1932
|
+
// ============================================================================
|
|
1933
|
+
// FIND DUPLICATES
|
|
1934
|
+
// ============================================================================
|
|
756
1935
|
|
|
757
1936
|
async function findDuplicates() {
|
|
758
1937
|
header();
|
|
759
1938
|
sectionHeader(MSG.DUPLICATES);
|
|
760
1939
|
|
|
761
|
-
|
|
762
|
-
console.log();
|
|
1940
|
+
const spinner = ora(`${MSG.SCANNING} (this may take a while)...`).start();
|
|
763
1941
|
|
|
764
1942
|
// Find files > 1MB, group by size
|
|
765
1943
|
const sizeMap = new Map();
|
|
@@ -776,7 +1954,7 @@ async function findDuplicates() {
|
|
|
776
1954
|
} catch (e) {}
|
|
777
1955
|
});
|
|
778
1956
|
|
|
779
|
-
|
|
1957
|
+
spinner.text = MSG.HASHING;
|
|
780
1958
|
|
|
781
1959
|
// Find duplicates by hash for files with same size
|
|
782
1960
|
const hashGroups = new Map();
|
|
@@ -795,6 +1973,8 @@ async function findDuplicates() {
|
|
|
795
1973
|
}
|
|
796
1974
|
}
|
|
797
1975
|
|
|
1976
|
+
spinner.stop();
|
|
1977
|
+
|
|
798
1978
|
// Display duplicate groups
|
|
799
1979
|
header();
|
|
800
1980
|
sectionHeader(MSG.DUPLICATES);
|
|
@@ -811,12 +1991,12 @@ async function findDuplicates() {
|
|
|
811
1991
|
const dupSpace = (fileList.length - 1) * fileSize;
|
|
812
1992
|
totalDupSize += dupSpace;
|
|
813
1993
|
|
|
814
|
-
console.log(` ${
|
|
1994
|
+
console.log(` ${chalk.white(MSG.GROUP + ' ' + groupNum)} ${chalk.dim('(' + fileSize + ' MB ' + MSG.EACH + ')')}`);
|
|
815
1995
|
|
|
816
1996
|
fileList.forEach((file, idx) => {
|
|
817
1997
|
let display = file.replace(HOME, '~');
|
|
818
1998
|
if (display.length > 50) display = '...' + display.slice(-47);
|
|
819
|
-
console.log(` ${
|
|
1999
|
+
console.log(` ${chalk.cyan('[' + (idx + 1) + ']')} ${display}`);
|
|
820
2000
|
allDups.push(file);
|
|
821
2001
|
});
|
|
822
2002
|
console.log();
|
|
@@ -825,29 +2005,29 @@ async function findDuplicates() {
|
|
|
825
2005
|
}
|
|
826
2006
|
|
|
827
2007
|
if (groupNum === 0) {
|
|
828
|
-
console.log(` ${
|
|
2008
|
+
console.log(` ${chalk.green(figures.tick)} ${MSG.NO_ITEMS}`);
|
|
829
2009
|
await pressEnter();
|
|
830
2010
|
return;
|
|
831
2011
|
}
|
|
832
2012
|
|
|
833
|
-
console.log(` ${
|
|
2013
|
+
console.log(` ${figures.arrowRight} ${chalk.white(MSG.TOTAL + ':')} ${chalk.green(formatSize(totalDupSize))} ${MSG.IN_DUPLICATES}`);
|
|
834
2014
|
console.log();
|
|
835
|
-
console.log(` ${
|
|
836
|
-
const choice = await prompt(` ${
|
|
2015
|
+
console.log(chalk.dim(` ${MSG.SELECT_KEEP}`));
|
|
2016
|
+
const choice = await prompt(` ${chalk.white(MSG.CHOOSE + ':')} `);
|
|
837
2017
|
|
|
838
|
-
console.log(` ${
|
|
2018
|
+
console.log(` ${chalk.yellow(MSG.FEATURE_PROGRESS)}`);
|
|
839
2019
|
await pressEnter();
|
|
840
2020
|
}
|
|
841
2021
|
|
|
842
|
-
//
|
|
843
|
-
//
|
|
844
|
-
//
|
|
2022
|
+
// ============================================================================
|
|
2023
|
+
// UNINSTALL APPS
|
|
2024
|
+
// ============================================================================
|
|
845
2025
|
|
|
846
2026
|
async function uninstallApps() {
|
|
847
2027
|
header();
|
|
848
2028
|
sectionHeader(MSG.UNINSTALL);
|
|
849
2029
|
|
|
850
|
-
|
|
2030
|
+
const spinner = ora(MSG.SCANNING).start();
|
|
851
2031
|
|
|
852
2032
|
const apps = [];
|
|
853
2033
|
|
|
@@ -893,22 +2073,24 @@ async function uninstallApps() {
|
|
|
893
2073
|
if (apps.length >= 25) break;
|
|
894
2074
|
}
|
|
895
2075
|
|
|
2076
|
+
spinner.stop();
|
|
2077
|
+
|
|
896
2078
|
header();
|
|
897
2079
|
sectionHeader(MSG.UNINSTALL);
|
|
898
2080
|
|
|
899
2081
|
if (apps.length === 0) {
|
|
900
|
-
console.log(` ${
|
|
2082
|
+
console.log(` ${chalk.green(figures.tick)} ${MSG.NO_ITEMS}`);
|
|
901
2083
|
await pressEnter();
|
|
902
2084
|
return;
|
|
903
2085
|
}
|
|
904
2086
|
|
|
905
2087
|
apps.forEach((app, i) => {
|
|
906
|
-
console.log(` ${
|
|
2088
|
+
console.log(` ${chalk.cyan('[' + String(i + 1).padStart(2) + ']')} ${app.name.padEnd(25)} ${chalk.dim('(' + String(app.appSize).padStart(3) + ' MB ' + MSG.APP_SIZE + ' + ' + String(app.dataSize).padStart(3) + ' MB ' + MSG.DATA_SIZE + ')')}`);
|
|
907
2089
|
});
|
|
908
2090
|
|
|
909
2091
|
console.log();
|
|
910
|
-
console.log(` ${
|
|
911
|
-
const choice = await prompt(` ${
|
|
2092
|
+
console.log(chalk.dim(` ${MSG.SELECT_DELETE} [numbers/n]:`));
|
|
2093
|
+
const choice = await prompt(` ${chalk.white(MSG.CHOOSE + ':')} `);
|
|
912
2094
|
|
|
913
2095
|
if (choice.toLowerCase() === 'n' || choice === '') {
|
|
914
2096
|
await pressEnter();
|
|
@@ -921,10 +2103,10 @@ async function uninstallApps() {
|
|
|
921
2103
|
if (idx < 0 || idx >= apps.length) continue;
|
|
922
2104
|
|
|
923
2105
|
const app = apps[idx];
|
|
924
|
-
console.log(` ${
|
|
2106
|
+
console.log(` ${chalk.yellow(MSG.DELETING + ' ' + app.name + '...')}`);
|
|
925
2107
|
|
|
926
2108
|
// Remove app data
|
|
927
|
-
if (app.bundleId) {
|
|
2109
|
+
if (app.bundleId && !DRY_RUN) {
|
|
928
2110
|
const dataPaths = [
|
|
929
2111
|
path.join(HOME, 'Library/Application Support', app.name),
|
|
930
2112
|
path.join(HOME, 'Library/Application Support', app.bundleId),
|
|
@@ -938,33 +2120,32 @@ async function uninstallApps() {
|
|
|
938
2120
|
|
|
939
2121
|
dataPaths.forEach(dp => {
|
|
940
2122
|
try {
|
|
941
|
-
if (fs.existsSync(dp))
|
|
2123
|
+
if (fs.existsSync(dp)) deleteItem(dp);
|
|
942
2124
|
} catch (e) {}
|
|
943
2125
|
});
|
|
944
2126
|
}
|
|
945
2127
|
|
|
946
2128
|
// Remove app
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
console.log(` ${
|
|
952
|
-
console.log(` ${c.dim}sudo rm -rf "${app.path}"${c.reset}`);
|
|
2129
|
+
if (deleteItem(app.path)) {
|
|
2130
|
+
console.log(` ${chalk.green(figures.tick)} ${app.name} ${MSG.REMOVED}`);
|
|
2131
|
+
} else {
|
|
2132
|
+
console.log(` ${chalk.red(MSG.NEED_SUDO)}`);
|
|
2133
|
+
console.log(` ${chalk.dim('sudo rm -rf "' + app.path + '"')}`);
|
|
953
2134
|
}
|
|
954
2135
|
}
|
|
955
2136
|
|
|
956
2137
|
await pressEnter();
|
|
957
2138
|
}
|
|
958
2139
|
|
|
959
|
-
//
|
|
960
|
-
//
|
|
961
|
-
//
|
|
2140
|
+
// ============================================================================
|
|
2141
|
+
// MANAGE STARTUP
|
|
2142
|
+
// ============================================================================
|
|
962
2143
|
|
|
963
2144
|
async function manageStartup() {
|
|
964
2145
|
header();
|
|
965
2146
|
sectionHeader(MSG.STARTUP_ITEMS);
|
|
966
2147
|
|
|
967
|
-
console.log(` ${
|
|
2148
|
+
console.log(` ${chalk.white(MSG.STARTUP_ITEMS + ':')}`);
|
|
968
2149
|
|
|
969
2150
|
// Get login items via AppleScript
|
|
970
2151
|
const loginItems = exec(`osascript -e 'tell application "System Events" to get the name of every login item' 2>/dev/null`);
|
|
@@ -972,14 +2153,14 @@ async function manageStartup() {
|
|
|
972
2153
|
if (loginItems) {
|
|
973
2154
|
const items = loginItems.split(', ').filter(i => i.trim());
|
|
974
2155
|
items.forEach((item, idx) => {
|
|
975
|
-
console.log(` ${
|
|
2156
|
+
console.log(` ${chalk.cyan('[' + (idx + 1) + ']')} ${chalk.green(figures.tick)} ${item} ${chalk.dim('(' + MSG.ENABLED + ')')}`);
|
|
976
2157
|
});
|
|
977
2158
|
} else {
|
|
978
|
-
console.log(
|
|
2159
|
+
console.log(chalk.dim(' No login items found'));
|
|
979
2160
|
}
|
|
980
2161
|
|
|
981
2162
|
console.log();
|
|
982
|
-
console.log(` ${
|
|
2163
|
+
console.log(` ${chalk.white(MSG.LAUNCH_AGENTS + ':')}`);
|
|
983
2164
|
|
|
984
2165
|
// User Launch Agents
|
|
985
2166
|
const launchAgentsDir = path.join(HOME, 'Library/LaunchAgents');
|
|
@@ -993,29 +2174,29 @@ async function manageStartup() {
|
|
|
993
2174
|
const loaded = exec(`launchctl list 2>/dev/null | grep "${name}"`);
|
|
994
2175
|
|
|
995
2176
|
const status = loaded ?
|
|
996
|
-
`${
|
|
997
|
-
`${
|
|
2177
|
+
`${chalk.green(figures.tick)} ${MSG.ENABLED}` :
|
|
2178
|
+
`${chalk.red(figures.cross)} ${MSG.DISABLED}`;
|
|
998
2179
|
|
|
999
|
-
console.log(` ${
|
|
2180
|
+
console.log(` ${chalk.cyan('[' + idx + ']')} ${status} ${chalk.dim(name)}`);
|
|
1000
2181
|
idx++;
|
|
1001
2182
|
}
|
|
1002
2183
|
}
|
|
1003
2184
|
|
|
1004
2185
|
console.log();
|
|
1005
|
-
console.log(
|
|
1006
|
-
const choice = await prompt(` ${
|
|
2186
|
+
console.log(chalk.dim(" Enter number to toggle, 'd' to disable all, or 'n' to go back"));
|
|
2187
|
+
const choice = await prompt(` ${chalk.white(MSG.CHOOSE + ':')} `);
|
|
1007
2188
|
|
|
1008
2189
|
if (choice.toLowerCase() === 'd') {
|
|
1009
2190
|
exec(`osascript -e 'tell application "System Events" to delete every login item' 2>/dev/null`);
|
|
1010
|
-
console.log(` ${
|
|
2191
|
+
console.log(` ${chalk.green(figures.tick)} All login items disabled`);
|
|
1011
2192
|
}
|
|
1012
2193
|
|
|
1013
2194
|
await pressEnter();
|
|
1014
2195
|
}
|
|
1015
2196
|
|
|
1016
|
-
//
|
|
1017
|
-
//
|
|
1018
|
-
//
|
|
2197
|
+
// ============================================================================
|
|
2198
|
+
// CLEAN BROWSERS
|
|
2199
|
+
// ============================================================================
|
|
1019
2200
|
|
|
1020
2201
|
async function cleanBrowsers() {
|
|
1021
2202
|
header();
|
|
@@ -1033,37 +2214,37 @@ async function cleanBrowsers() {
|
|
|
1033
2214
|
}
|
|
1034
2215
|
}
|
|
1035
2216
|
|
|
1036
|
-
console.log(` ${
|
|
2217
|
+
console.log(` ${chalk.white('Chrome:')}`);
|
|
1037
2218
|
addBrowserItem('~/Library/Caches/Google/Chrome', 'Chrome Cache');
|
|
1038
2219
|
addBrowserItem('~/Library/Application Support/Google/Chrome/Default/Service Worker', 'Chrome Service Workers');
|
|
1039
2220
|
addBrowserItem('~/Library/Application Support/Google/Chrome/Default/GPUCache', 'Chrome GPU Cache');
|
|
1040
2221
|
|
|
1041
|
-
console.log(` ${
|
|
2222
|
+
console.log(` ${chalk.white('Safari:')}`);
|
|
1042
2223
|
addBrowserItem('~/Library/Caches/com.apple.Safari', 'Safari Cache');
|
|
1043
2224
|
addBrowserItem('~/Library/Safari/LocalStorage', 'Safari Local Storage');
|
|
1044
2225
|
|
|
1045
|
-
console.log(` ${
|
|
2226
|
+
console.log(` ${chalk.white('Firefox:')}`);
|
|
1046
2227
|
const ffProfile = exec('find ~/Library/Application\\ Support/Firefox/Profiles -maxdepth 1 -name "*.default*" 2>/dev/null | head -1');
|
|
1047
2228
|
if (ffProfile) {
|
|
1048
2229
|
addBrowserItem(`${ffProfile}/cache2`, 'Firefox Cache');
|
|
1049
2230
|
}
|
|
1050
2231
|
|
|
1051
2232
|
items.forEach((item, i) => {
|
|
1052
|
-
console.log(` ${
|
|
2233
|
+
console.log(` ${chalk.cyan('[' + String(i + 1).padStart(2) + ']')} ${item.name.padEnd(30)} ${chalk.dim(String(item.size).padStart(6) + ' MB')}`);
|
|
1053
2234
|
});
|
|
1054
2235
|
|
|
1055
2236
|
if (items.length === 0) {
|
|
1056
|
-
console.log(` ${
|
|
2237
|
+
console.log(` ${chalk.green(figures.tick)} ${MSG.NO_ITEMS}`);
|
|
1057
2238
|
await pressEnter();
|
|
1058
2239
|
return;
|
|
1059
2240
|
}
|
|
1060
2241
|
|
|
1061
2242
|
console.log();
|
|
1062
|
-
console.log(` ${
|
|
2243
|
+
console.log(` ${figures.arrowRight} ${chalk.white(MSG.TOTAL + ':')} ${chalk.green(formatSize(totalBrowser))}`);
|
|
1063
2244
|
console.log();
|
|
1064
2245
|
|
|
1065
|
-
console.log(` ${
|
|
1066
|
-
const choice = await prompt(` ${
|
|
2246
|
+
console.log(chalk.dim(` ${MSG.SELECT_DELETE} [numbers/a/n]:`));
|
|
2247
|
+
const choice = await prompt(` ${chalk.white(MSG.CHOOSE + ':')} `);
|
|
1067
2248
|
|
|
1068
2249
|
if (choice.toLowerCase() === 'n' || choice === '') {
|
|
1069
2250
|
await pressEnter();
|
|
@@ -1085,27 +2266,28 @@ async function cleanBrowsers() {
|
|
|
1085
2266
|
|
|
1086
2267
|
for (const item of items) {
|
|
1087
2268
|
if (!item.selected) continue;
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
console.log(` ${c.green}${CHECK}${c.reset} ${item.name}`);
|
|
2269
|
+
if (deleteItem(item.path)) {
|
|
2270
|
+
console.log(` ${chalk.green(figures.tick)} ${item.name}`);
|
|
1091
2271
|
cleaned += item.size;
|
|
1092
|
-
}
|
|
2272
|
+
}
|
|
1093
2273
|
}
|
|
1094
2274
|
|
|
1095
2275
|
console.log();
|
|
1096
|
-
console.log(` ${
|
|
2276
|
+
console.log(` ${chalk.green(MSG.FREED + ': ' + formatSize(cleaned))}`);
|
|
2277
|
+
|
|
2278
|
+
playNotificationSound();
|
|
1097
2279
|
await pressEnter();
|
|
1098
2280
|
}
|
|
1099
2281
|
|
|
1100
|
-
//
|
|
1101
|
-
//
|
|
1102
|
-
//
|
|
2282
|
+
// ============================================================================
|
|
2283
|
+
// KILL PROCESSES
|
|
2284
|
+
// ============================================================================
|
|
1103
2285
|
|
|
1104
2286
|
async function killProcesses() {
|
|
1105
2287
|
header();
|
|
1106
2288
|
sectionHeader(MSG.PROCESS_MEM);
|
|
1107
2289
|
|
|
1108
|
-
console.log(` ${
|
|
2290
|
+
console.log(` ${chalk.white(MSG.PROCESS_MEM + ':')}`);
|
|
1109
2291
|
|
|
1110
2292
|
const procs = [];
|
|
1111
2293
|
const totalMemBytes = parseInt(exec("sysctl hw.memsize 2>/dev/null | awk '{print $2}'")) || 8589934592;
|
|
@@ -1130,20 +2312,20 @@ async function killProcesses() {
|
|
|
1130
2312
|
|
|
1131
2313
|
procs.push({ pid, name, memMB });
|
|
1132
2314
|
|
|
1133
|
-
console.log(` ${
|
|
2315
|
+
console.log(` ${chalk.cyan('[' + String(procs.length).padStart(2) + ']')} ${name.padEnd(30)} ${chalk.red(memMB + ' MB RAM')}`);
|
|
1134
2316
|
|
|
1135
2317
|
if (procs.length >= 10) break;
|
|
1136
2318
|
}
|
|
1137
2319
|
|
|
1138
2320
|
if (procs.length === 0) {
|
|
1139
|
-
console.log(` ${
|
|
2321
|
+
console.log(` ${chalk.green(figures.tick)} ${MSG.NO_ITEMS}`);
|
|
1140
2322
|
await pressEnter();
|
|
1141
2323
|
return;
|
|
1142
2324
|
}
|
|
1143
2325
|
|
|
1144
2326
|
console.log();
|
|
1145
|
-
console.log(` ${
|
|
1146
|
-
const choice = await prompt(` ${
|
|
2327
|
+
console.log(chalk.dim(` ${MSG.KILL} process [number/n]:`));
|
|
2328
|
+
const choice = await prompt(` ${chalk.white(MSG.CHOOSE + ':')} `);
|
|
1147
2329
|
|
|
1148
2330
|
if (choice.toLowerCase() === 'n' || choice === '') {
|
|
1149
2331
|
await pressEnter();
|
|
@@ -1153,66 +2335,25 @@ async function killProcesses() {
|
|
|
1153
2335
|
const idx = parseInt(choice) - 1;
|
|
1154
2336
|
if (idx >= 0 && idx < procs.length) {
|
|
1155
2337
|
const proc = procs[idx];
|
|
1156
|
-
try {
|
|
1157
|
-
process.kill(parseInt(proc.pid), 'SIGKILL');
|
|
1158
|
-
console.log(` ${c.green}${CHECK}${c.reset} ${MSG.KILLED}: ${proc.name} (PID: ${proc.pid})`);
|
|
1159
|
-
} catch (e) {
|
|
1160
|
-
console.log(` ${c.red}${CROSS}${c.reset} ${MSG.FAILED_KILL} ${proc.name}`);
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
await pressEnter();
|
|
1165
|
-
}
|
|
1166
2338
|
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
await pressEnter();
|
|
1178
|
-
return;
|
|
2339
|
+
if (DRY_RUN) {
|
|
2340
|
+
console.log(chalk.yellow(` ${figures.info} [DRY-RUN] Would kill: ${proc.name} (PID: ${proc.pid})`));
|
|
2341
|
+
} else {
|
|
2342
|
+
try {
|
|
2343
|
+
process.kill(parseInt(proc.pid), 'SIGKILL');
|
|
2344
|
+
console.log(` ${chalk.green(figures.tick)} ${MSG.KILLED}: ${proc.name} (PID: ${proc.pid})`);
|
|
2345
|
+
} catch (e) {
|
|
2346
|
+
console.log(` ${chalk.red(figures.cross)} ${MSG.FAILED_KILL} ${proc.name}`);
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
1179
2349
|
}
|
|
1180
2350
|
|
|
1181
|
-
const history = fs.readFileSync(HISTORY_FILE, 'utf8').split('\n').filter(l => l.trim());
|
|
1182
|
-
const currentMonth = new Date().toISOString().slice(0, 7);
|
|
1183
|
-
|
|
1184
|
-
const monthLines = history.filter(l => l.startsWith(currentMonth));
|
|
1185
|
-
const monthCleanups = monthLines.length;
|
|
1186
|
-
|
|
1187
|
-
let monthFreed = 0;
|
|
1188
|
-
let monthScoreSum = 0;
|
|
1189
|
-
|
|
1190
|
-
monthLines.forEach(line => {
|
|
1191
|
-
const freedMatch = line.match(/(\d+)\s*MB/);
|
|
1192
|
-
const scoreMatch = line.match(/Score:\s*(\d+)/);
|
|
1193
|
-
if (freedMatch) monthFreed += parseInt(freedMatch[1]);
|
|
1194
|
-
if (scoreMatch) monthScoreSum += parseInt(scoreMatch[1]);
|
|
1195
|
-
});
|
|
1196
|
-
|
|
1197
|
-
const monthAvg = monthCleanups > 0 ? Math.round(monthScoreSum / monthCleanups) : 0;
|
|
1198
|
-
|
|
1199
|
-
console.log(` ${c.white}${MSG.THIS_MONTH}:${c.reset}`);
|
|
1200
|
-
console.log(` ${BULLET} ${MSG.CLEANINGS}: ${monthCleanups}`);
|
|
1201
|
-
console.log(` ${BULLET} ${MSG.TOTAL} ${MSG.FREED}: ${formatSize(monthFreed)}`);
|
|
1202
|
-
console.log(` ${BULLET} ${MSG.AVG_SCORE}: ${monthAvg}/100`);
|
|
1203
|
-
console.log();
|
|
1204
|
-
|
|
1205
|
-
console.log(` ${c.white}${MSG.HISTORY}:${c.reset}`);
|
|
1206
|
-
history.slice(-10).forEach(line => {
|
|
1207
|
-
console.log(` ${c.dim}${line}${c.reset}`);
|
|
1208
|
-
});
|
|
1209
|
-
|
|
1210
2351
|
await pressEnter();
|
|
1211
2352
|
}
|
|
1212
2353
|
|
|
1213
|
-
//
|
|
1214
|
-
//
|
|
1215
|
-
//
|
|
2354
|
+
// ============================================================================
|
|
2355
|
+
// SETTINGS
|
|
2356
|
+
// ============================================================================
|
|
1216
2357
|
|
|
1217
2358
|
async function showSettings() {
|
|
1218
2359
|
while (true) {
|
|
@@ -1221,15 +2362,18 @@ async function showSettings() {
|
|
|
1221
2362
|
|
|
1222
2363
|
const currentLang = LANG_CODE === 'en' ? 'EN' : 'PT';
|
|
1223
2364
|
|
|
1224
|
-
console.log(` ${
|
|
1225
|
-
console.log(` ${
|
|
1226
|
-
console.log(` ${
|
|
1227
|
-
console.log(` ${
|
|
2365
|
+
console.log(` ${chalk.cyan('[1]')} ${MSG.EXCLUSIONS}`);
|
|
2366
|
+
console.log(` ${chalk.cyan('[2]')} ${MSG.CUSTOM_PATHS}`);
|
|
2367
|
+
console.log(` ${chalk.cyan('[3]')} ${MSG.CHANGE_LANG} (current: ${currentLang})`);
|
|
2368
|
+
console.log(` ${chalk.cyan('[4]')} ${MSG.RESET_DEFAULTS}`);
|
|
2369
|
+
console.log(` ${chalk.cyan('[5]')} ${DRY_RUN ? chalk.green(figures.tick) : figures.radioOff} Dry-Run Mode`);
|
|
2370
|
+
console.log(` ${chalk.cyan('[6]')} ${SECURE_DELETE ? chalk.green(figures.tick) : figures.radioOff} Secure Delete Mode`);
|
|
2371
|
+
console.log(` ${chalk.cyan('[7]')} View Backups`);
|
|
1228
2372
|
console.log();
|
|
1229
|
-
console.log(` ${
|
|
2373
|
+
console.log(` ${chalk.cyan('[0]')} ${MSG.BACK}`);
|
|
1230
2374
|
console.log();
|
|
1231
2375
|
|
|
1232
|
-
const choice = await prompt(` ${
|
|
2376
|
+
const choice = await prompt(` ${chalk.white(MSG.CHOOSE + ':')} `);
|
|
1233
2377
|
|
|
1234
2378
|
switch (choice) {
|
|
1235
2379
|
case '1':
|
|
@@ -1237,24 +2381,24 @@ async function showSettings() {
|
|
|
1237
2381
|
header();
|
|
1238
2382
|
sectionHeader(MSG.EXCLUSIONS);
|
|
1239
2383
|
|
|
1240
|
-
console.log(` ${
|
|
2384
|
+
console.log(` ${chalk.white(MSG.CURRENT_EXCLUSIONS + ':')}`);
|
|
1241
2385
|
if (fs.existsSync(EXCLUSIONS_FILE)) {
|
|
1242
2386
|
const exclusions = fs.readFileSync(EXCLUSIONS_FILE, 'utf8').split('\n').filter(l => l.trim());
|
|
1243
2387
|
exclusions.forEach((ex, i) => console.log(` ${i + 1}. ${ex}`));
|
|
1244
2388
|
} else {
|
|
1245
|
-
console.log(
|
|
2389
|
+
console.log(chalk.dim(' None'));
|
|
1246
2390
|
}
|
|
1247
2391
|
console.log();
|
|
1248
|
-
console.log(` ${
|
|
2392
|
+
console.log(chalk.dim(` ${MSG.ENTER_PATH}:`));
|
|
1249
2393
|
|
|
1250
|
-
const newExclusion = await prompt(` ${
|
|
2394
|
+
const newExclusion = await prompt(` ${chalk.white(MSG.PATH + ':')} `);
|
|
1251
2395
|
|
|
1252
2396
|
if (newExclusion === 'c') {
|
|
1253
2397
|
if (fs.existsSync(EXCLUSIONS_FILE)) fs.unlinkSync(EXCLUSIONS_FILE);
|
|
1254
|
-
console.log(` ${
|
|
2398
|
+
console.log(` ${chalk.green(figures.tick)} Exclusions ${MSG.CLEARED}`);
|
|
1255
2399
|
} else if (newExclusion !== 'n' && newExclusion) {
|
|
1256
2400
|
fs.appendFileSync(EXCLUSIONS_FILE, newExclusion + '\n');
|
|
1257
|
-
console.log(` ${
|
|
2401
|
+
console.log(` ${chalk.green(figures.tick)} ${MSG.ADDED}: ${newExclusion}`);
|
|
1258
2402
|
}
|
|
1259
2403
|
await pressEnter();
|
|
1260
2404
|
break;
|
|
@@ -1264,24 +2408,24 @@ async function showSettings() {
|
|
|
1264
2408
|
header();
|
|
1265
2409
|
sectionHeader(MSG.CUSTOM_PATHS);
|
|
1266
2410
|
|
|
1267
|
-
console.log(` ${
|
|
2411
|
+
console.log(` ${chalk.white(MSG.CURRENT_PATHS + ':')}`);
|
|
1268
2412
|
if (fs.existsSync(CUSTOM_PATHS_FILE)) {
|
|
1269
2413
|
const paths = fs.readFileSync(CUSTOM_PATHS_FILE, 'utf8').split('\n').filter(l => l.trim() && !l.startsWith('#'));
|
|
1270
2414
|
paths.forEach((p, i) => console.log(` ${i + 1}. ${p}`));
|
|
1271
2415
|
} else {
|
|
1272
|
-
console.log(
|
|
2416
|
+
console.log(chalk.dim(' None'));
|
|
1273
2417
|
}
|
|
1274
2418
|
console.log();
|
|
1275
|
-
console.log(` ${
|
|
2419
|
+
console.log(chalk.dim(` ${MSG.ENTER_PATH}:`));
|
|
1276
2420
|
|
|
1277
|
-
const newPath = await prompt(` ${
|
|
2421
|
+
const newPath = await prompt(` ${chalk.white(MSG.PATH + ':')} `);
|
|
1278
2422
|
|
|
1279
2423
|
if (newPath === 'c') {
|
|
1280
2424
|
if (fs.existsSync(CUSTOM_PATHS_FILE)) fs.unlinkSync(CUSTOM_PATHS_FILE);
|
|
1281
|
-
console.log(` ${
|
|
2425
|
+
console.log(` ${chalk.green(figures.tick)} Custom paths ${MSG.CLEARED}`);
|
|
1282
2426
|
} else if (newPath !== 'n' && newPath) {
|
|
1283
2427
|
fs.appendFileSync(CUSTOM_PATHS_FILE, newPath + '\n');
|
|
1284
|
-
console.log(` ${
|
|
2428
|
+
console.log(` ${chalk.green(figures.tick)} ${MSG.ADDED}: ${newPath}`);
|
|
1285
2429
|
}
|
|
1286
2430
|
await pressEnter();
|
|
1287
2431
|
break;
|
|
@@ -1290,10 +2434,9 @@ async function showSettings() {
|
|
|
1290
2434
|
// Change language
|
|
1291
2435
|
const newLang = LANG_CODE === 'pt' ? 'en' : 'pt';
|
|
1292
2436
|
fs.writeFileSync(LANGUAGE_FILE, newLang);
|
|
1293
|
-
console.log(` ${
|
|
2437
|
+
console.log(` ${chalk.green(figures.tick)} ${newLang === 'en' ? MSG.LANG_CHANGED_EN : MSG.LANG_CHANGED_PT}`);
|
|
1294
2438
|
await pressEnter();
|
|
1295
2439
|
// Restart to apply language change
|
|
1296
|
-
const { spawn } = require('child_process');
|
|
1297
2440
|
spawn(process.argv[0], process.argv.slice(1), { stdio: 'inherit' });
|
|
1298
2441
|
process.exit(0);
|
|
1299
2442
|
break;
|
|
@@ -1303,14 +2446,41 @@ async function showSettings() {
|
|
|
1303
2446
|
[EXCLUSIONS_FILE, CUSTOM_PATHS_FILE, LANGUAGE_FILE].forEach(f => {
|
|
1304
2447
|
if (fs.existsSync(f)) fs.unlinkSync(f);
|
|
1305
2448
|
});
|
|
1306
|
-
console.log(` ${
|
|
2449
|
+
console.log(` ${chalk.green(figures.tick)} ${MSG.SETTINGS_RESET}`);
|
|
1307
2450
|
await pressEnter();
|
|
1308
2451
|
// Restart to apply changes
|
|
1309
|
-
|
|
1310
|
-
spawn2(process.argv[0], process.argv.slice(1), { stdio: 'inherit' });
|
|
2452
|
+
spawn(process.argv[0], process.argv.slice(1), { stdio: 'inherit' });
|
|
1311
2453
|
process.exit(0);
|
|
1312
2454
|
break;
|
|
1313
2455
|
|
|
2456
|
+
case '5':
|
|
2457
|
+
DRY_RUN = !DRY_RUN;
|
|
2458
|
+
console.log(` ${chalk.green(figures.tick)} Dry-Run Mode: ${DRY_RUN ? 'ON' : 'OFF'}`);
|
|
2459
|
+
await pressEnter();
|
|
2460
|
+
break;
|
|
2461
|
+
|
|
2462
|
+
case '6':
|
|
2463
|
+
SECURE_DELETE = !SECURE_DELETE;
|
|
2464
|
+
console.log(` ${chalk.green(figures.tick)} Secure Delete Mode: ${SECURE_DELETE ? 'ON' : 'OFF'}`);
|
|
2465
|
+
await pressEnter();
|
|
2466
|
+
break;
|
|
2467
|
+
|
|
2468
|
+
case '7':
|
|
2469
|
+
// View backups
|
|
2470
|
+
header();
|
|
2471
|
+
sectionHeader('Backups');
|
|
2472
|
+
|
|
2473
|
+
const backups = listBackups();
|
|
2474
|
+
if (backups.length === 0) {
|
|
2475
|
+
console.log(chalk.dim(' No backups found'));
|
|
2476
|
+
} else {
|
|
2477
|
+
backups.forEach((b, i) => {
|
|
2478
|
+
console.log(` ${chalk.cyan('[' + (i + 1) + ']')} ${b}`);
|
|
2479
|
+
});
|
|
2480
|
+
}
|
|
2481
|
+
await pressEnter();
|
|
2482
|
+
break;
|
|
2483
|
+
|
|
1314
2484
|
case '0':
|
|
1315
2485
|
return;
|
|
1316
2486
|
|
|
@@ -1320,56 +2490,84 @@ async function showSettings() {
|
|
|
1320
2490
|
}
|
|
1321
2491
|
}
|
|
1322
2492
|
|
|
1323
|
-
//
|
|
2493
|
+
// ============================================================================
|
|
1324
2494
|
// MAIN MENU
|
|
1325
|
-
//
|
|
2495
|
+
// ============================================================================
|
|
1326
2496
|
|
|
1327
2497
|
async function mainMenu() {
|
|
1328
2498
|
while (true) {
|
|
1329
2499
|
header();
|
|
1330
2500
|
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
console.log(
|
|
1336
|
-
console.log(` ${
|
|
1337
|
-
console.log(` ${
|
|
1338
|
-
console.log(` ${
|
|
1339
|
-
console.log(` ${
|
|
2501
|
+
// Show update notification
|
|
2502
|
+
await showUpdateNotification();
|
|
2503
|
+
|
|
2504
|
+
// Main options
|
|
2505
|
+
console.log(chalk.white(' CLEANING'));
|
|
2506
|
+
console.log(` ${chalk.cyan('[1]')} ${MSG.OPT_QUICK}`);
|
|
2507
|
+
console.log(` ${chalk.cyan('[2]')} ${MSG.OPT_LARGE}`);
|
|
2508
|
+
console.log(` ${chalk.cyan('[3]')} ${MSG.OPT_DUPLICATES}`);
|
|
2509
|
+
console.log(` ${chalk.cyan('[4]')} ${MSG.OPT_APPS}`);
|
|
2510
|
+
console.log(` ${chalk.cyan('[5]')} ${MSG.OPT_BROWSERS}`);
|
|
2511
|
+
console.log(` ${chalk.cyan('[6]')} ${MSG.OPT_PRIVACY}`);
|
|
2512
|
+
console.log();
|
|
2513
|
+
|
|
2514
|
+
console.log(chalk.white(' SYSTEM'));
|
|
2515
|
+
console.log(` ${chalk.cyan('[7]')} ${MSG.OPT_STARTUP}`);
|
|
2516
|
+
console.log(` ${chalk.cyan('[8]')} ${MSG.OPT_PROCESSES}`);
|
|
2517
|
+
console.log(` ${chalk.cyan('[9]')} ${MSG.OPT_MEMORY}`);
|
|
2518
|
+
console.log(` ${chalk.cyan('[10]')} ${MSG.OPT_DISK}`);
|
|
2519
|
+
console.log();
|
|
2520
|
+
|
|
2521
|
+
console.log(chalk.white(' TOOLS'));
|
|
2522
|
+
console.log(` ${chalk.cyan('[11]')} ${MSG.OPT_BENCHMARK}`);
|
|
2523
|
+
console.log(` ${chalk.cyan('[12]')} ${MSG.OPT_SCHEDULER}`);
|
|
2524
|
+
console.log(` ${chalk.cyan('[13]')} ${MSG.OPT_EXPORT}`);
|
|
2525
|
+
console.log(` ${chalk.cyan('[14]')} ${MSG.OPT_STATS}`);
|
|
2526
|
+
console.log(` ${chalk.cyan('[15]')} ${MSG.OPT_SETTINGS}`);
|
|
1340
2527
|
console.log();
|
|
1341
|
-
|
|
2528
|
+
|
|
2529
|
+
console.log(` ${chalk.cyan('[0]')} ${MSG.OPT_EXIT}`);
|
|
1342
2530
|
console.log();
|
|
1343
2531
|
|
|
1344
|
-
const choice = await prompt(` ${
|
|
2532
|
+
const choice = await prompt(` ${chalk.white(MSG.CHOOSE + ':')} `);
|
|
1345
2533
|
|
|
1346
2534
|
switch (choice) {
|
|
1347
2535
|
case '1': await quickClean(); break;
|
|
1348
2536
|
case '2': await findLargeFiles(); break;
|
|
1349
2537
|
case '3': await findDuplicates(); break;
|
|
1350
2538
|
case '4': await uninstallApps(); break;
|
|
1351
|
-
case '5': await
|
|
1352
|
-
case '6': await
|
|
1353
|
-
case '7': await
|
|
1354
|
-
case '8': await
|
|
1355
|
-
case '9': await
|
|
2539
|
+
case '5': await cleanBrowsers(); break;
|
|
2540
|
+
case '6': await privacySweep(); break;
|
|
2541
|
+
case '7': await manageStartup(); break;
|
|
2542
|
+
case '8': await killProcesses(); break;
|
|
2543
|
+
case '9': await memoryOptimizer(); break;
|
|
2544
|
+
case '10': await diskAnalyzer(); break;
|
|
2545
|
+
case '11': await benchmarkMode(); break;
|
|
2546
|
+
case '12': await scheduler(); break;
|
|
2547
|
+
case '13': await exportHtmlReport(); break;
|
|
2548
|
+
case '14': await showStatistics(); break;
|
|
2549
|
+
case '15': await showSettings(); break;
|
|
1356
2550
|
case '0':
|
|
1357
2551
|
clearScreen();
|
|
1358
2552
|
console.log();
|
|
1359
|
-
console.log(
|
|
2553
|
+
console.log(gradient.pastel.multiline(`
|
|
2554
|
+
Thank you for using Aris Mac Cleaner!
|
|
2555
|
+
|
|
2556
|
+
por Salvador Reis ${figures.bullet} github.com/salvadorreis
|
|
2557
|
+
`));
|
|
1360
2558
|
console.log();
|
|
1361
2559
|
process.exit(0);
|
|
1362
2560
|
break;
|
|
1363
2561
|
default:
|
|
1364
|
-
console.log(` ${
|
|
2562
|
+
console.log(` ${chalk.red(MSG.INVALID)}`);
|
|
1365
2563
|
await new Promise(r => setTimeout(r, 1000));
|
|
1366
2564
|
}
|
|
1367
2565
|
}
|
|
1368
2566
|
}
|
|
1369
2567
|
|
|
1370
|
-
//
|
|
2568
|
+
// ============================================================================
|
|
1371
2569
|
// MAIN
|
|
1372
|
-
//
|
|
2570
|
+
// ============================================================================
|
|
1373
2571
|
|
|
1374
2572
|
// Check if macOS
|
|
1375
2573
|
if (os.platform() !== 'darwin') {
|
|
@@ -1380,32 +2578,63 @@ if (os.platform() !== 'darwin') {
|
|
|
1380
2578
|
// Command line arguments
|
|
1381
2579
|
const args = process.argv.slice(2);
|
|
1382
2580
|
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
2581
|
+
// Parse flags
|
|
2582
|
+
if (args.includes('--dry-run')) {
|
|
2583
|
+
DRY_RUN = true;
|
|
1386
2584
|
}
|
|
1387
2585
|
|
|
1388
|
-
if (args.includes('--
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
} else if (args.includes('--version') || args.includes('-v')) {
|
|
2586
|
+
if (args.includes('--secure')) {
|
|
2587
|
+
SECURE_DELETE = true;
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
1394
2591
|
console.log(`Aris Mac Cleaner v${VERSION}`);
|
|
1395
2592
|
process.exit(0);
|
|
1396
|
-
}
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
1397
2596
|
console.log(`
|
|
1398
|
-
|
|
2597
|
+
${gradient.pastel.multiline(ASCII_LOGO)}
|
|
1399
2598
|
|
|
1400
2599
|
Usage:
|
|
1401
|
-
aris-mac-cleaner
|
|
1402
|
-
aris-mac-cleaner -q
|
|
1403
|
-
aris-mac-cleaner -
|
|
1404
|
-
aris-mac-cleaner
|
|
2600
|
+
aris-mac-cleaner Start interactive menu
|
|
2601
|
+
aris-mac-cleaner -q Quick clean without menu
|
|
2602
|
+
aris-mac-cleaner --dry-run Preview mode (nothing deleted)
|
|
2603
|
+
aris-mac-cleaner --secure Secure delete (overwrite before delete)
|
|
2604
|
+
aris-mac-cleaner -v Show version
|
|
2605
|
+
aris-mac-cleaner -h Show this help
|
|
1405
2606
|
|
|
1406
2607
|
Config directory: ~/.aris-mac-cleaner/
|
|
2608
|
+
|
|
2609
|
+
Premium Features:
|
|
2610
|
+
- Animated logo with gradient colors
|
|
2611
|
+
- Progress bars and spinners
|
|
2612
|
+
- Auto-update checker
|
|
2613
|
+
- Backup before clean
|
|
2614
|
+
- Cleanup history with ASCII charts
|
|
2615
|
+
- Disk analyzer with visual tree
|
|
2616
|
+
- Smart recommendations
|
|
2617
|
+
- HTML report export
|
|
2618
|
+
- Benchmark mode
|
|
2619
|
+
- Privacy sweep
|
|
2620
|
+
- Memory optimizer
|
|
2621
|
+
- Scheduled cleanups (launchd)
|
|
2622
|
+
- Sound notifications
|
|
2623
|
+
- Dry-run and secure delete modes
|
|
1407
2624
|
`);
|
|
1408
2625
|
process.exit(0);
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
if (args.includes('--quick') || args.includes('-q')) {
|
|
2629
|
+
(async () => {
|
|
2630
|
+
getSystemStats();
|
|
2631
|
+
await quickClean();
|
|
2632
|
+
})();
|
|
1409
2633
|
} else {
|
|
1410
|
-
|
|
2634
|
+
// Show animated logo and start menu
|
|
2635
|
+
(async () => {
|
|
2636
|
+
await animateLogo();
|
|
2637
|
+
getSystemStats();
|
|
2638
|
+
mainMenu();
|
|
2639
|
+
})();
|
|
1411
2640
|
}
|