@xiaoxionga/buzz 2.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.
Files changed (3) hide show
  1. package/README.md +68 -0
  2. package/bin/buzz.js +321 -0
  3. package/package.json +35 -0
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # šŸ buzz
2
+
3
+ > Keep your screen awake — **zero dependencies, no admin required**.
4
+
5
+ Tired of your screen locking during long builds, meetings, or deployments? `buzz` keeps your screen awake using built-in OS tools. No native modules, no admin access, no permissions to grant.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g @xiaoxionga/buzz
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```bash
16
+ buzz # Keep awake indefinitely (Ctrl+C to stop)
17
+ buzz 20 # Keep awake for 20 minutes
18
+ buzz 2 # Keep awake for 2 hours
19
+ buzz 1.5 # Keep awake for 1.5 hours (90 min)
20
+ buzz stop # Stop any running buzz
21
+ buzz status # Check if buzz is running
22
+ buzz help # Show help
23
+ ```
24
+
25
+ ## How It Works
26
+
27
+ | Platform | Method | Install needed? | Admin needed? |
28
+ |----------|--------|:---------------:|:-------------:|
29
+ | **macOS** | `caffeinate -i` (built-in) | āŒ | āŒ |
30
+ | **Windows** | PowerShell `SendKeys F15` (built-in) | āŒ | āŒ |
31
+ | **Linux** | `xdotool` (pre-installed on most distros) | maybe | āŒ |
32
+
33
+ - **macOS**: Uses Apple's built-in `caffeinate` — not even a mouse jiggle, just cleanly blocks idle sleep.
34
+ - **Windows**: Presses F15 every 30 seconds via PowerShell. No app binds to F15, so it's completely harmless.
35
+ - **Linux**: Moves mouse 1px via `xdotool`.
36
+
37
+ ## Duration Format
38
+
39
+ | Input | Meaning |
40
+ |-------|---------|
41
+ | `buzz 30` | 30 **minutes** (numbers ≄ 10 = minutes) |
42
+ | `buzz 5` | 5 **hours** (numbers < 10 = hours) |
43
+ | `buzz 0.5` | 0.5 hours = 30 minutes |
44
+ | `buzz 1.5` | 1.5 hours = 90 minutes |
45
+
46
+ ## Why buzz?
47
+
48
+ - **Zero dependencies** — `package.json` has no `dependencies` field. Nothing to break.
49
+ - **No admin required** — perfect for locked-down corporate laptops.
50
+ - **Cross-platform** — macOS, Windows, Linux all supported out of the box.
51
+ - **Clean exit** — `buzz stop` from any terminal kills the running instance.
52
+
53
+ ## Examples
54
+
55
+ ```bash
56
+ # Weekend crunch time
57
+ buzz 3 # 3 hours of uninterrupted focus
58
+
59
+ # Quick meeting
60
+ buzz 30 # 30 minutes
61
+
62
+ # Background build running
63
+ buzz & # indefinite, then 'buzz stop' when done
64
+ ```
65
+
66
+ ## License
67
+
68
+ MIT Ā© [leonezhu](https://github.com/leonezhu)
package/bin/buzz.js ADDED
@@ -0,0 +1,321 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { exec, execSync, spawn } = require('child_process');
4
+ const os = require('os');
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ const pkg = require('../package.json');
9
+ const PLATFORM = os.platform();
10
+ const PID_FILE = path.join(os.tmpdir(), 'buzz.pid');
11
+ const JIGGLE_INTERVAL_MS = 30000; // 30s
12
+
13
+ const BEE = `
14
+ \\
15
+ \\___
16
+ (• • )
17
+ > ^ <
18
+ /| |\\
19
+ | |
20
+ [_] [_]
21
+ BUZZ!
22
+ `;
23
+
24
+ const HELP = `šŸ buzz v${pkg.version} — zero dependencies, no admin required
25
+
26
+ Keep your screen awake by simulating input.
27
+
28
+ USAGE:
29
+ buzz Keep awake indefinitely (Ctrl+C to stop)
30
+ buzz 20 Keep awake for 20 minutes
31
+ buzz 1.5 Keep awake for 1.5 hours (90 min)
32
+ buzz stop Stop any running buzz
33
+ buzz status Check if buzz is running
34
+ buzz help Show this help
35
+
36
+ PLATFORMS:
37
+ macOS Uses 'caffeinate' (built-in, zero install)
38
+ Windows Uses PowerShell SendKeys (built-in, zero install)
39
+ Linux Uses xdotool (if available)
40
+
41
+ EXAMPLES:
42
+ buzz 30 # Stay awake for 30 min
43
+ buzz 2 # Stay awake for 2 hours
44
+ buzz # Stay awake until you Ctrl+C
45
+
46
+ TIPS:
47
+ - Numbers >= 10 are treated as minutes
48
+ - Numbers < 10 are treated as hours
49
+ - No npm dependencies required!
50
+ `;
51
+
52
+ // ─── Helpers ───
53
+
54
+ function readPid() {
55
+ try { return parseInt(fs.readFileSync(PID_FILE, 'utf8').trim()); }
56
+ catch { return null; }
57
+ }
58
+
59
+ function writePid(pid) {
60
+ fs.writeFileSync(PID_FILE, String(pid));
61
+ }
62
+
63
+ function removePid() {
64
+ try { fs.unlinkSync(PID_FILE); } catch {}
65
+ }
66
+
67
+ function isRunning(pid) {
68
+ try { process.kill(pid, 0); return true; }
69
+ catch { return false; }
70
+ }
71
+
72
+ function formatDuration(ms) {
73
+ const s = Math.floor(ms / 1000);
74
+ const h = Math.floor(s / 3600);
75
+ const m = Math.floor((s % 3600) / 60);
76
+ const sec = s % 60;
77
+ if (h > 0) return `${h}h ${m}m ${sec}s`;
78
+ if (m > 0) return `${m}m ${sec}s`;
79
+ return `${sec}s`;
80
+ }
81
+
82
+ function parseDuration(arg) {
83
+ const num = parseFloat(arg);
84
+ if (isNaN(num) || num <= 0) return null;
85
+ // >= 10 → minutes, < 10 → hours
86
+ return num >= 10
87
+ ? num * 60 * 1000
88
+ : num * 60 * 60 * 1000;
89
+ }
90
+
91
+ // ─── Platform-specific jiggler ───
92
+
93
+ /**
94
+ * macOS: use built-in `caffeinate -i`
95
+ * Blocks idle sleep entirely — no mouse movement needed.
96
+ * Zero install, zero admin, zero permissions.
97
+ */
98
+ function startCaffeinate(durationMs) {
99
+ const args = ['-i']; // -i = prevent idle sleep
100
+ if (durationMs) {
101
+ args.push('-t', String(Math.ceil(durationMs / 1000)));
102
+ }
103
+ const child = spawn('caffeinate', args, { stdio: 'ignore' });
104
+ return child;
105
+ }
106
+
107
+ /**
108
+ * Windows: use PowerShell SendKeys to press F15 every 30s.
109
+ * F15 is harmless — no app binds to it.
110
+ * Zero install, zero admin.
111
+ */
112
+ function jiggleWindows() {
113
+ // Single PowerShell invocation that loops every 30s
114
+ const psScript = `
115
+ $wsh = New-Object -ComObject WScript.Shell
116
+ while ($true) {
117
+ Start-Sleep -Seconds 30
118
+ $wsh.SendKeys('{F15}')
119
+ }
120
+ `;
121
+ // This runs as a detached child
122
+ const child = spawn('powershell.exe', [
123
+ '-NoProfile', '-NonInteractive', '-Command', psScript
124
+ ], { stdio: 'ignore', detached: true });
125
+ child.unref();
126
+ return child;
127
+ }
128
+
129
+ /**
130
+ * Linux: xdotool — move mouse 1px
131
+ */
132
+ function jiggleLinux() {
133
+ exec('xdotool mousemove_relative 1 0', (err) => {
134
+ if (err) {
135
+ console.log('āš ļø xdotool not found. Install: sudo apt install xdotool');
136
+ process.exit(1);
137
+ }
138
+ setTimeout(() => {
139
+ exec('xdotool mousemove_relative -- -1 0');
140
+ }, 100);
141
+ });
142
+ }
143
+
144
+ // ─── Commands ───
145
+
146
+ function cmdStop() {
147
+ const pid = readPid();
148
+ if (!pid) {
149
+ console.log('šŸ buzz is not running.');
150
+ process.exit(0);
151
+ }
152
+ if (isRunning(pid)) {
153
+ try { process.kill(pid, 'SIGTERM'); } catch {}
154
+ setTimeout(() => {
155
+ removePid();
156
+ console.log('šŸ buzz stopped. Your screen can sleep now.');
157
+ process.exit(0);
158
+ }, 500);
159
+ } else {
160
+ removePid();
161
+ console.log('šŸ buzz was not running (cleaned up stale PID).');
162
+ process.exit(0);
163
+ }
164
+ }
165
+
166
+ function cmdStatus() {
167
+ const pid = readPid();
168
+ if (pid && isRunning(pid)) {
169
+ console.log(`šŸ buzz is running (PID: ${pid})`);
170
+ } else {
171
+ if (pid) removePid();
172
+ console.log('šŸ buzz is not running.');
173
+ }
174
+ process.exit(0);
175
+ }
176
+
177
+ function cmdHelp() {
178
+ console.log(HELP);
179
+ process.exit(0);
180
+ }
181
+
182
+ function cmdRun(durationMs) {
183
+ const existingPid = readPid();
184
+ if (existingPid && isRunning(existingPid)) {
185
+ console.log(`āš ļø buzz is already running (PID: ${existingPid}). Run 'buzz stop' first.`);
186
+ process.exit(1);
187
+ }
188
+
189
+ writePid(process.pid);
190
+
191
+ let stopped = false;
192
+ let childProc = null;
193
+ let jiggleTimer = null;
194
+
195
+ const cleanup = () => {
196
+ if (stopped) return;
197
+ stopped = true;
198
+ if (childProc) { try { childProc.kill(); } catch {} }
199
+ if (jiggleTimer) clearInterval(jiggleTimer);
200
+ removePid();
201
+ console.log('\nšŸ buzz stopped. Your screen can sleep now. Bye! šŸ‘‹');
202
+ process.exit(0);
203
+ };
204
+
205
+ process.on('SIGTERM', cleanup);
206
+ process.on('SIGINT', cleanup);
207
+ process.on('exit', () => removePid());
208
+
209
+ console.log(BEE);
210
+
211
+ const platformLabel = PLATFORM === 'darwin' ? 'macOS caffeinate'
212
+ : PLATFORM === 'win32' ? 'Windows SendKeys'
213
+ : 'Linux xdotool';
214
+
215
+ if (durationMs) {
216
+ const mins = Math.round(durationMs / 60000);
217
+ console.log(`šŸ buzz started! Keeping awake for ${mins} minutes.`);
218
+ } else {
219
+ console.log(`šŸ buzz started! Keeping awake indefinitely.`);
220
+ }
221
+ console.log(` Engine: ${platformLabel}`);
222
+ console.log(` Press Ctrl+C to stop.\n`);
223
+
224
+ const startTime = Date.now();
225
+ let tickCount = 0;
226
+
227
+ // ── macOS: just launch caffeinate and wait ──
228
+ if (PLATFORM === 'darwin') {
229
+ childProc = startCaffeinate(durationMs);
230
+
231
+ // Status updates every 5 min
232
+ jiggleTimer = setInterval(() => {
233
+ if (stopped) return;
234
+ tickCount++;
235
+ const elapsed = Date.now() - startTime;
236
+ const remaining = durationMs ? formatDuration(durationMs - elapsed) : 'āˆž';
237
+ console.log(` šŸ tick #${tickCount} | elapsed: ${formatDuration(elapsed)} | remaining: ${remaining}`);
238
+ }, 5 * 60 * 1000);
239
+
240
+ if (durationMs) {
241
+ setTimeout(() => {
242
+ console.log(`\nāœ… buzz finished! Kept awake for ${formatDuration(Date.now() - startTime)}.`);
243
+ cleanup();
244
+ }, durationMs + 500);
245
+ }
246
+ return;
247
+ }
248
+
249
+ // ── Windows: launch PowerShell jiggler ──
250
+ if (PLATFORM === 'win32') {
251
+ childProc = jiggleWindows();
252
+
253
+ jiggleTimer = setInterval(() => {
254
+ if (stopped) return;
255
+ tickCount++;
256
+ const elapsed = Date.now() - startTime;
257
+ const remaining = durationMs ? formatDuration(durationMs - elapsed) : 'āˆž';
258
+ console.log(` šŸ tick #${tickCount} | elapsed: ${formatDuration(elapsed)} | remaining: ${remaining}`);
259
+ }, 5 * 60 * 1000);
260
+
261
+ if (durationMs) {
262
+ setTimeout(() => {
263
+ console.log(`\nāœ… buzz finished! Kept awake for ${formatDuration(Date.now() - startTime)}.`);
264
+ cleanup();
265
+ }, durationMs + 500);
266
+ }
267
+ return;
268
+ }
269
+
270
+ // ── Linux: xdotool jiggle loop ──
271
+ jiggleLinux();
272
+ jiggleTimer = setInterval(() => {
273
+ if (stopped) return;
274
+ tickCount++;
275
+ jiggleLinux();
276
+ const elapsed = Date.now() - startTime;
277
+ if (durationMs && elapsed >= durationMs) {
278
+ console.log(`\nāœ… buzz finished! Kept awake for ${formatDuration(elapsed)}.`);
279
+ cleanup();
280
+ return;
281
+ }
282
+ if (tickCount % 10 === 0) {
283
+ const remaining = durationMs ? formatDuration(durationMs - elapsed) : 'āˆž';
284
+ console.log(` šŸ tick #${tickCount} | elapsed: ${formatDuration(elapsed)} | remaining: ${remaining}`);
285
+ }
286
+ }, JIGGLE_INTERVAL_MS);
287
+ }
288
+
289
+ // ─── Main ───
290
+
291
+ function main() {
292
+ const args = process.argv.slice(2);
293
+
294
+ if (args.length === 0) {
295
+ cmdRun(null);
296
+ return;
297
+ }
298
+
299
+ const cmd = args[0].toLowerCase();
300
+
301
+ switch (cmd) {
302
+ case 'help': case '--help': case '-h':
303
+ cmdHelp(); break;
304
+ case 'stop':
305
+ cmdStop(); break;
306
+ case 'status':
307
+ cmdStatus(); break;
308
+ case 'version': case '--version': case '-v':
309
+ console.log(`buzz v${pkg.version}`); process.exit(0); break;
310
+ default:
311
+ const durationMs = parseDuration(args[0]);
312
+ if (durationMs === null) {
313
+ console.log(`Unknown command: "${args[0]}"\n`);
314
+ cmdHelp();
315
+ process.exit(1);
316
+ }
317
+ cmdRun(durationMs);
318
+ }
319
+ }
320
+
321
+ main();
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@xiaoxionga/buzz",
3
+ "version": "2.0.0",
4
+ "description": "šŸ Keep your screen awake — zero dependencies, no admin required",
5
+ "main": "bin/buzz.js",
6
+ "bin": {
7
+ "buzz": "./bin/buzz.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node bin/buzz.js"
11
+ },
12
+ "keywords": [
13
+ "keep-awake",
14
+ "caffeine",
15
+ "no-sleep",
16
+ "mouse-jiggler",
17
+ "anti-idle",
18
+ "screen-awake",
19
+ "caffeinate"
20
+ ],
21
+ "author": "leonezhu",
22
+ "license": "MIT",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/leonezhu/buzz"
26
+ },
27
+ "homepage": "https://github.com/leonezhu/buzz",
28
+ "engines": {
29
+ "node": ">=12"
30
+ },
31
+ "files": [
32
+ "bin/",
33
+ "README.md"
34
+ ]
35
+ }