@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.
- package/README.md +68 -0
- package/bin/buzz.js +321 -0
- 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
|
+
}
|