@tukuyomil032/broom 1.0.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/LICENSE +21 -0
- package/README.md +554 -0
- package/dist/commands/analyze.js +371 -0
- package/dist/commands/backup.js +257 -0
- package/dist/commands/clean.js +255 -0
- package/dist/commands/completion.js +714 -0
- package/dist/commands/config.js +474 -0
- package/dist/commands/doctor.js +280 -0
- package/dist/commands/duplicates.js +325 -0
- package/dist/commands/help.js +34 -0
- package/dist/commands/index.js +22 -0
- package/dist/commands/installer.js +266 -0
- package/dist/commands/optimize.js +270 -0
- package/dist/commands/purge.js +271 -0
- package/dist/commands/remove.js +184 -0
- package/dist/commands/reports.js +173 -0
- package/dist/commands/schedule.js +249 -0
- package/dist/commands/status.js +468 -0
- package/dist/commands/touchid.js +230 -0
- package/dist/commands/uninstall.js +336 -0
- package/dist/commands/update.js +182 -0
- package/dist/commands/watch.js +258 -0
- package/dist/index.js +131 -0
- package/dist/scanners/base.js +21 -0
- package/dist/scanners/browser-cache.js +111 -0
- package/dist/scanners/dev-cache.js +64 -0
- package/dist/scanners/docker.js +96 -0
- package/dist/scanners/downloads.js +66 -0
- package/dist/scanners/homebrew.js +82 -0
- package/dist/scanners/index.js +126 -0
- package/dist/scanners/installer.js +87 -0
- package/dist/scanners/ios-backups.js +82 -0
- package/dist/scanners/node-modules.js +75 -0
- package/dist/scanners/temp-files.js +65 -0
- package/dist/scanners/trash.js +90 -0
- package/dist/scanners/user-cache.js +62 -0
- package/dist/scanners/user-logs.js +53 -0
- package/dist/scanners/xcode.js +124 -0
- package/dist/types/index.js +23 -0
- package/dist/ui/index.js +5 -0
- package/dist/ui/monitors.js +345 -0
- package/dist/ui/output.js +304 -0
- package/dist/ui/prompts.js +270 -0
- package/dist/utils/config.js +133 -0
- package/dist/utils/debug.js +119 -0
- package/dist/utils/fs.js +283 -0
- package/dist/utils/help.js +265 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/paths.js +142 -0
- package/dist/utils/report.js +404 -0
- package/package.json +87 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schedule command - Automated cleanup scheduling
|
|
3
|
+
*/
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import { enhanceCommandHelp } from '../utils/help.js';
|
|
7
|
+
import { expandPath, exists } from '../utils/fs.js';
|
|
8
|
+
import { printHeader, success, error, warning } from '../ui/output.js';
|
|
9
|
+
import { writeFile, readFile, unlink } from 'fs/promises';
|
|
10
|
+
import { execSync } from 'child_process';
|
|
11
|
+
const LAUNCH_AGENT_PATH = expandPath('~/Library/LaunchAgents/com.broom.cleanup.plist');
|
|
12
|
+
/**
|
|
13
|
+
* Days of week mapping
|
|
14
|
+
*/
|
|
15
|
+
const WEEKDAYS = {
|
|
16
|
+
sunday: 0,
|
|
17
|
+
monday: 1,
|
|
18
|
+
tuesday: 2,
|
|
19
|
+
wednesday: 3,
|
|
20
|
+
thursday: 4,
|
|
21
|
+
friday: 5,
|
|
22
|
+
saturday: 6,
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Generate LaunchAgent plist content
|
|
26
|
+
*/
|
|
27
|
+
function generateLaunchAgent(config) {
|
|
28
|
+
const [hour, minute] = config.time.split(':').map(Number);
|
|
29
|
+
const broomPath = execSync('which broom').toString().trim() || '/usr/local/bin/broom';
|
|
30
|
+
const args = ['<string>clean</string>', '<string>--yes</string>', '<string>--safe</string>'];
|
|
31
|
+
if (config.scanners && config.scanners.length > 0) {
|
|
32
|
+
args.push(`<string>--scanners</string>`, `<string>${config.scanners.join(',')}</string>`);
|
|
33
|
+
}
|
|
34
|
+
let calendarInterval = '';
|
|
35
|
+
if (config.interval === 'daily') {
|
|
36
|
+
calendarInterval = `
|
|
37
|
+
<key>StartCalendarInterval</key>
|
|
38
|
+
<dict>
|
|
39
|
+
<key>Hour</key>
|
|
40
|
+
<integer>${hour}</integer>
|
|
41
|
+
<key>Minute</key>
|
|
42
|
+
<integer>${minute}</integer>
|
|
43
|
+
</dict>`;
|
|
44
|
+
}
|
|
45
|
+
else if (config.interval === 'weekly' && config.weekday !== undefined) {
|
|
46
|
+
calendarInterval = `
|
|
47
|
+
<key>StartCalendarInterval</key>
|
|
48
|
+
<dict>
|
|
49
|
+
<key>Weekday</key>
|
|
50
|
+
<integer>${config.weekday}</integer>
|
|
51
|
+
<key>Hour</key>
|
|
52
|
+
<integer>${hour}</integer>
|
|
53
|
+
<key>Minute</key>
|
|
54
|
+
<integer>${minute}</integer>
|
|
55
|
+
</dict>`;
|
|
56
|
+
}
|
|
57
|
+
else if (config.interval === 'monthly') {
|
|
58
|
+
calendarInterval = `
|
|
59
|
+
<key>StartCalendarInterval</key>
|
|
60
|
+
<dict>
|
|
61
|
+
<key>Day</key>
|
|
62
|
+
<integer>1</integer>
|
|
63
|
+
<key>Hour</key>
|
|
64
|
+
<integer>${hour}</integer>
|
|
65
|
+
<key>Minute</key>
|
|
66
|
+
<integer>${minute}</integer>
|
|
67
|
+
</dict>`;
|
|
68
|
+
}
|
|
69
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
70
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
71
|
+
<plist version="1.0">
|
|
72
|
+
<dict>
|
|
73
|
+
<key>Label</key>
|
|
74
|
+
<string>com.broom.cleanup</string>
|
|
75
|
+
|
|
76
|
+
<key>ProgramArguments</key>
|
|
77
|
+
<array>
|
|
78
|
+
<string>${broomPath}</string>
|
|
79
|
+
${args.join('\n ')}
|
|
80
|
+
</array>
|
|
81
|
+
|
|
82
|
+
${calendarInterval}
|
|
83
|
+
|
|
84
|
+
<key>StandardOutPath</key>
|
|
85
|
+
<string>${expandPath('~/.config/broom/schedule.log')}</string>
|
|
86
|
+
|
|
87
|
+
<key>StandardErrorPath</key>
|
|
88
|
+
<string>${expandPath('~/.config/broom/schedule-error.log')}</string>
|
|
89
|
+
</dict>
|
|
90
|
+
</plist>`;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Install schedule
|
|
94
|
+
*/
|
|
95
|
+
async function installSchedule(options) {
|
|
96
|
+
const time = options.time || '09:00';
|
|
97
|
+
let interval = 'weekly';
|
|
98
|
+
let weekday;
|
|
99
|
+
if (options.daily) {
|
|
100
|
+
interval = 'daily';
|
|
101
|
+
}
|
|
102
|
+
else if (options.monthly) {
|
|
103
|
+
interval = 'monthly';
|
|
104
|
+
}
|
|
105
|
+
else if (options.weekly) {
|
|
106
|
+
interval = 'weekly';
|
|
107
|
+
const dayStr = (options.day || 'monday').toLowerCase();
|
|
108
|
+
weekday = WEEKDAYS[dayStr];
|
|
109
|
+
if (weekday === undefined) {
|
|
110
|
+
error(`Invalid day: ${options.day}. Use: ${Object.keys(WEEKDAYS).join(', ')}`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const scanners = options.scanners?.split(',');
|
|
115
|
+
printHeader('⏰ Schedule Cleanup');
|
|
116
|
+
console.log(chalk.bold('Configuration:'));
|
|
117
|
+
console.log(` Interval: ${chalk.cyan(interval)}`);
|
|
118
|
+
if (interval === 'weekly' && weekday !== undefined) {
|
|
119
|
+
const dayName = Object.keys(WEEKDAYS).find((k) => WEEKDAYS[k] === weekday);
|
|
120
|
+
console.log(` Day: ${chalk.cyan(dayName)}`);
|
|
121
|
+
}
|
|
122
|
+
console.log(` Time: ${chalk.cyan(time)}`);
|
|
123
|
+
if (scanners) {
|
|
124
|
+
console.log(` Scanners: ${chalk.cyan(scanners.join(', '))}`);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
console.log(` Scanners: ${chalk.cyan('all safe scanners')}`);
|
|
128
|
+
}
|
|
129
|
+
console.log();
|
|
130
|
+
// Check if already scheduled
|
|
131
|
+
if (exists(LAUNCH_AGENT_PATH)) {
|
|
132
|
+
warning('A schedule already exists. Remove it first with --remove');
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// Generate plist
|
|
136
|
+
const plist = generateLaunchAgent({ interval, time, weekday, scanners });
|
|
137
|
+
// Write plist
|
|
138
|
+
await writeFile(LAUNCH_AGENT_PATH, plist, 'utf-8');
|
|
139
|
+
// Load with launchctl
|
|
140
|
+
try {
|
|
141
|
+
execSync(`launchctl load "${LAUNCH_AGENT_PATH}"`);
|
|
142
|
+
success('Schedule installed successfully!');
|
|
143
|
+
console.log();
|
|
144
|
+
console.log(chalk.dim('Cleanup will run automatically according to the schedule.'));
|
|
145
|
+
console.log(chalk.dim(`Logs: ~/.config/broom/schedule.log`));
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
error(`Failed to load schedule: ${err}`);
|
|
149
|
+
await unlink(LAUNCH_AGENT_PATH);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Remove schedule
|
|
154
|
+
*/
|
|
155
|
+
async function removeSchedule() {
|
|
156
|
+
printHeader('⏰ Remove Schedule');
|
|
157
|
+
if (!exists(LAUNCH_AGENT_PATH)) {
|
|
158
|
+
warning('No schedule found');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
// Unload from launchctl
|
|
163
|
+
execSync(`launchctl unload "${LAUNCH_AGENT_PATH}"`);
|
|
164
|
+
// Remove plist
|
|
165
|
+
await unlink(LAUNCH_AGENT_PATH);
|
|
166
|
+
success('Schedule removed successfully');
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
error(`Failed to remove schedule: ${err}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* List schedules
|
|
174
|
+
*/
|
|
175
|
+
async function listSchedule() {
|
|
176
|
+
printHeader('⏰ Scheduled Cleanups');
|
|
177
|
+
if (!exists(LAUNCH_AGENT_PATH)) {
|
|
178
|
+
warning('No schedule configured');
|
|
179
|
+
console.log();
|
|
180
|
+
console.log(chalk.dim('Use "broom schedule --weekly --time 09:00" to create a schedule'));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
const content = await readFile(LAUNCH_AGENT_PATH, 'utf-8');
|
|
185
|
+
// Parse basic info from plist
|
|
186
|
+
const timeMatch = content.match(/<key>Hour<\/key>\s*<integer>(\d+)<\/integer>/);
|
|
187
|
+
const minuteMatch = content.match(/<key>Minute<\/key>\s*<integer>(\d+)<\/integer>/);
|
|
188
|
+
const weekdayMatch = content.match(/<key>Weekday<\/key>\s*<integer>(\d+)<\/integer>/);
|
|
189
|
+
const dayMatch = content.match(/<key>Day<\/key>\s*<integer>(\d+)<\/integer>/);
|
|
190
|
+
console.log(chalk.bold('Active Schedule:'));
|
|
191
|
+
if (dayMatch) {
|
|
192
|
+
console.log(` Interval: ${chalk.cyan('Monthly')}`);
|
|
193
|
+
console.log(` Day: ${chalk.cyan('1st of month')}`);
|
|
194
|
+
}
|
|
195
|
+
else if (weekdayMatch) {
|
|
196
|
+
const weekday = parseInt(weekdayMatch[1]);
|
|
197
|
+
const dayName = Object.keys(WEEKDAYS).find((k) => WEEKDAYS[k] === weekday) || 'Unknown';
|
|
198
|
+
console.log(` Interval: ${chalk.cyan('Weekly')}`);
|
|
199
|
+
console.log(` Day: ${chalk.cyan(dayName)}`);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
console.log(` Interval: ${chalk.cyan('Daily')}`);
|
|
203
|
+
}
|
|
204
|
+
if (timeMatch && minuteMatch) {
|
|
205
|
+
const hour = timeMatch[1].padStart(2, '0');
|
|
206
|
+
const minute = minuteMatch[1].padStart(2, '0');
|
|
207
|
+
console.log(` Time: ${chalk.cyan(`${hour}:${minute}`)}`);
|
|
208
|
+
}
|
|
209
|
+
console.log();
|
|
210
|
+
console.log(chalk.dim(`Config: ${LAUNCH_AGENT_PATH}`));
|
|
211
|
+
console.log(chalk.dim('Use "broom schedule --remove" to uninstall'));
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
error(`Failed to read schedule: ${err}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Execute schedule command
|
|
219
|
+
*/
|
|
220
|
+
export async function scheduleCommand(options) {
|
|
221
|
+
if (options.list || (!options.daily && !options.weekly && !options.monthly && !options.remove)) {
|
|
222
|
+
await listSchedule();
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (options.remove) {
|
|
226
|
+
await removeSchedule();
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
await installSchedule(options);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Create schedule command
|
|
233
|
+
*/
|
|
234
|
+
export function createScheduleCommand() {
|
|
235
|
+
const cmd = new Command('schedule')
|
|
236
|
+
.description('Schedule automated cleanups')
|
|
237
|
+
.option('-l, --list', 'Show current schedule')
|
|
238
|
+
.option('-r, --remove', 'Remove scheduled cleanup')
|
|
239
|
+
.option('--daily', 'Schedule daily cleanup')
|
|
240
|
+
.option('--weekly', 'Schedule weekly cleanup')
|
|
241
|
+
.option('--monthly', 'Schedule monthly cleanup')
|
|
242
|
+
.option('--day <day>', 'Day of week for weekly (default: monday)')
|
|
243
|
+
.option('--time <time>', 'Time to run (HH:MM format, default: 09:00)')
|
|
244
|
+
.option('--scanners <list>', 'Comma-separated list of scanners to run')
|
|
245
|
+
.action(async (options) => {
|
|
246
|
+
await scheduleCommand(options);
|
|
247
|
+
});
|
|
248
|
+
return enhanceCommandHelp(cmd);
|
|
249
|
+
}
|