killtask 1.0.0 → 1.1.2
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/index.js +90 -87
- package/package.json +26 -13
- package/readme.md +5 -5
package/index.js
CHANGED
|
@@ -1,11 +1,37 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { exec } from "child_process";
|
|
4
4
|
import os from "os";
|
|
5
|
-
import chalk from "chalk";
|
|
6
5
|
import readline from "readline";
|
|
7
6
|
|
|
8
|
-
// ───
|
|
7
|
+
// ─── ANSI ─────────────────────────────────────────────────────────────────────
|
|
8
|
+
const c = {
|
|
9
|
+
reset: "\x1b[0m",
|
|
10
|
+
bold: "\x1b[1m",
|
|
11
|
+
gray: "\x1b[90m",
|
|
12
|
+
red: "\x1b[31m",
|
|
13
|
+
green: "\x1b[32m",
|
|
14
|
+
yellow: "\x1b[33m",
|
|
15
|
+
cyan: "\x1b[36m",
|
|
16
|
+
white: "\x1b[97m",
|
|
17
|
+
magenta: "\x1b[35m",
|
|
18
|
+
bgRed: "\x1b[41m",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const clr = (code, str) => `${code}${str}${c.reset}`;
|
|
22
|
+
const gray = s => clr(c.gray, s);
|
|
23
|
+
const red = s => clr(c.red, s);
|
|
24
|
+
const green = s => clr(c.green, s);
|
|
25
|
+
const yellow = s => clr(c.yellow, s);
|
|
26
|
+
const cyan = s => clr(c.cyan, s);
|
|
27
|
+
const white = s => clr(c.white, s);
|
|
28
|
+
const magenta = s => clr(c.magenta, s);
|
|
29
|
+
const bold = s => clr(c.bold, s);
|
|
30
|
+
const boldCyan = s => `${c.bold}${c.cyan}${s}${c.reset}`;
|
|
31
|
+
const boldMagenta = s => `${c.bold}${c.magenta}${s}${c.reset}`;
|
|
32
|
+
const hitLabel = s => `${c.bgRed}${c.bold} ${s} ${c.reset}`;
|
|
33
|
+
|
|
34
|
+
// ─── Args ─────────────────────────────────────────────────────────────────────
|
|
9
35
|
const args = process.argv.slice(2);
|
|
10
36
|
const FLAGS = new Set(["--soft","--no-force","--all","--yes","-y","--verbose","-v","--help","-h"]);
|
|
11
37
|
const isForce = !args.includes("--soft") && !args.includes("--no-force");
|
|
@@ -15,7 +41,7 @@ const isVerbose = args.includes("--verbose") || args.includes("-v");
|
|
|
15
41
|
const ports = args.filter(a => !FLAGS.has(a) && !a.startsWith("-"));
|
|
16
42
|
const isWin = os.platform() === "win32";
|
|
17
43
|
|
|
18
|
-
// ─── Helpers
|
|
44
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
19
45
|
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
|
20
46
|
|
|
21
47
|
function run(cmd) {
|
|
@@ -28,14 +54,14 @@ function run(cmd) {
|
|
|
28
54
|
}
|
|
29
55
|
|
|
30
56
|
function log(...msg) {
|
|
31
|
-
if (isVerbose) console.log(
|
|
57
|
+
if (isVerbose) console.log(gray(" ·"), ...msg);
|
|
32
58
|
}
|
|
33
59
|
|
|
34
60
|
function confirm(q) {
|
|
35
61
|
if (isYes) return Promise.resolve(true);
|
|
36
62
|
return new Promise(resolve => {
|
|
37
63
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
38
|
-
rl.question(
|
|
64
|
+
rl.question(yellow(` ⚠️ ${q} [y/N] `), a => { rl.close(); resolve(a.toLowerCase() === "y"); });
|
|
39
65
|
});
|
|
40
66
|
}
|
|
41
67
|
|
|
@@ -43,54 +69,45 @@ function clearLine() {
|
|
|
43
69
|
process.stdout.write("\r" + " ".repeat(72) + "\r");
|
|
44
70
|
}
|
|
45
71
|
|
|
46
|
-
// ─── Animation
|
|
47
|
-
// Runs CONCURRENTLY with the kill — bullet travels while kill executes,
|
|
48
|
-
// explosion plays after kill resolves.
|
|
49
|
-
|
|
72
|
+
// ─── Animation ────────────────────────────────────────────────────────────────
|
|
50
73
|
const TRACK = 24;
|
|
51
|
-
const MARIO =
|
|
52
|
-
const ENEMY =
|
|
74
|
+
const MARIO = red("♜");
|
|
75
|
+
const ENEMY = `\x1b[32m♟${c.reset}`;
|
|
53
76
|
|
|
54
|
-
function
|
|
77
|
+
function drawBulletFrame(pos, enemyChar, label) {
|
|
55
78
|
let track = "";
|
|
56
79
|
for (let i = 0; i < TRACK; i++) {
|
|
57
|
-
if (i ===
|
|
58
|
-
else if (i <
|
|
59
|
-
else
|
|
80
|
+
if (i === pos) track += yellow("►");
|
|
81
|
+
else if (i < pos) track += gray("·");
|
|
82
|
+
else track += gray("─");
|
|
60
83
|
}
|
|
61
84
|
process.stdout.write(`\r ${MARIO} ${track} ${enemyChar} ${label}`);
|
|
62
85
|
}
|
|
63
86
|
|
|
64
|
-
// Animate bullet travelling — returns a promise that resolves when bullet reaches end
|
|
65
87
|
async function animateBullet() {
|
|
66
|
-
|
|
67
|
-
for (let i = 0; i < 3; i++) {
|
|
88
|
+
for (let i = 0; i < 2; i++) {
|
|
68
89
|
process.stdout.write(
|
|
69
|
-
`\r ${MARIO} ${
|
|
70
|
-
(i % 2 === 0 ?
|
|
90
|
+
`\r ${MARIO} ${gray("─".repeat(TRACK))} ${ENEMY} ` +
|
|
91
|
+
(i % 2 === 0 ? gray("aiming…") : yellow("🔫 FIRE!"))
|
|
71
92
|
);
|
|
72
|
-
await sleep(
|
|
93
|
+
await sleep(80);
|
|
73
94
|
}
|
|
74
|
-
// Bullet travels
|
|
75
95
|
for (let pos = 0; pos < TRACK; pos++) {
|
|
76
|
-
|
|
77
|
-
await sleep(
|
|
96
|
+
drawBulletFrame(pos, ENEMY, white("pew pew…"));
|
|
97
|
+
await sleep(18);
|
|
78
98
|
}
|
|
79
99
|
}
|
|
80
100
|
|
|
81
101
|
async function animateExplosion() {
|
|
82
102
|
const frames = [
|
|
83
|
-
[
|
|
84
|
-
[
|
|
85
|
-
[
|
|
86
|
-
[
|
|
87
|
-
[" ", chalk.green("✔ port killed") ],
|
|
103
|
+
[yellow("✸"), hitLabel("HIT") ],
|
|
104
|
+
[red("✺"), red("💥 BOOM!") ],
|
|
105
|
+
[yellow("✦"), red("☠ TERMINATED") ],
|
|
106
|
+
[" ", green("✔ port killed") ],
|
|
88
107
|
];
|
|
89
108
|
for (const [sym, label] of frames) {
|
|
90
|
-
process.stdout.write(
|
|
91
|
-
|
|
92
|
-
);
|
|
93
|
-
await sleep(85);
|
|
109
|
+
process.stdout.write(`\r ${MARIO} ${gray("·".repeat(TRACK))} ${sym} ${label}`);
|
|
110
|
+
await sleep(60);
|
|
94
111
|
}
|
|
95
112
|
clearLine();
|
|
96
113
|
}
|
|
@@ -99,33 +116,22 @@ async function animateBounce() {
|
|
|
99
116
|
for (let pos = TRACK - 1; pos >= 0; pos--) {
|
|
100
117
|
let track = "";
|
|
101
118
|
for (let i = 0; i < TRACK; i++) {
|
|
102
|
-
if (i === pos)
|
|
103
|
-
else if (i > pos)
|
|
104
|
-
else
|
|
119
|
+
if (i === pos) track += red("◄");
|
|
120
|
+
else if (i > pos) track += gray("·");
|
|
121
|
+
else track += gray("─");
|
|
105
122
|
}
|
|
106
|
-
process.stdout.write(
|
|
107
|
-
|
|
108
|
-
);
|
|
109
|
-
await sleep(22);
|
|
123
|
+
process.stdout.write(`\r ${MARIO} ${track} ${red("⛨")} ${red("blocked!")}`);
|
|
124
|
+
await sleep(12);
|
|
110
125
|
}
|
|
111
126
|
clearLine();
|
|
112
127
|
}
|
|
113
128
|
|
|
114
|
-
// ─── Kill + animate concurrently ─────────────────────────────────────────────
|
|
115
129
|
async function killWithAnimation(pid) {
|
|
116
|
-
// Start bullet animation and kill in parallel
|
|
117
130
|
const killPromise = killPid(pid);
|
|
118
131
|
await animateBullet();
|
|
119
|
-
|
|
120
|
-
// Wait for kill to finish (it's usually done by now)
|
|
121
132
|
const ok = await killPromise;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
await animateExplosion();
|
|
125
|
-
} else {
|
|
126
|
-
await animateBounce();
|
|
127
|
-
}
|
|
128
|
-
|
|
133
|
+
if (ok) await animateExplosion();
|
|
134
|
+
else await animateBounce();
|
|
129
135
|
return ok;
|
|
130
136
|
}
|
|
131
137
|
|
|
@@ -150,7 +156,7 @@ async function getAppLabel(pid) {
|
|
|
150
156
|
}
|
|
151
157
|
return buildLabel(name, cmdLine);
|
|
152
158
|
} catch {
|
|
153
|
-
return
|
|
159
|
+
return gray("unknown");
|
|
154
160
|
}
|
|
155
161
|
}
|
|
156
162
|
|
|
@@ -161,11 +167,11 @@ function buildLabel(name, cmdLine) {
|
|
|
161
167
|
const entry = tokens.slice(1).find(t => !t.startsWith("-")) ?? "";
|
|
162
168
|
const short = entry.split(/[/\\]/).pop();
|
|
163
169
|
if (RUNTIMES.has(bin) && short)
|
|
164
|
-
return `${
|
|
165
|
-
return
|
|
170
|
+
return `${white(bin)} ${gray("·")} ${cyan(short)}`;
|
|
171
|
+
return white(bin || "unknown");
|
|
166
172
|
}
|
|
167
173
|
|
|
168
|
-
// ─── Process Discovery
|
|
174
|
+
// ─── Process Discovery ────────────────────────────────────────────────────────
|
|
169
175
|
async function getPidsForPort(port) {
|
|
170
176
|
const pids = new Map();
|
|
171
177
|
if (isWin) {
|
|
@@ -227,7 +233,7 @@ async function killPid(pid) {
|
|
|
227
233
|
log(`PID ${pid} terminated`);
|
|
228
234
|
return true;
|
|
229
235
|
} catch (e) {
|
|
230
|
-
log(
|
|
236
|
+
log(red(e.message.split("\n").find(l => l.trim()) ?? e.message));
|
|
231
237
|
return false;
|
|
232
238
|
}
|
|
233
239
|
} else {
|
|
@@ -250,30 +256,27 @@ let nKilled = 0, nFailed = 0, nNotFound = 0;
|
|
|
250
256
|
// ─── killPort ─────────────────────────────────────────────────────────────────
|
|
251
257
|
async function killPort(port) {
|
|
252
258
|
if (!/^\d+$/.test(port) || +port < 1 || +port > 65535) {
|
|
253
|
-
console.log(
|
|
259
|
+
console.log(red(` ✗ Invalid port: ${port}`)); return;
|
|
254
260
|
}
|
|
255
261
|
|
|
256
262
|
const pids = await getPidsForPort(port);
|
|
257
263
|
|
|
258
264
|
if (pids.size === 0) {
|
|
259
|
-
console.log(` ${
|
|
265
|
+
console.log(` ${gray("○")} :${white(port)} ${gray("— nothing listening")}`);
|
|
260
266
|
nNotFound++; return;
|
|
261
267
|
}
|
|
262
268
|
|
|
263
269
|
for (const entry of pids.values()) {
|
|
264
|
-
// Single info line
|
|
265
270
|
console.log(
|
|
266
|
-
`\n ${
|
|
267
|
-
`${
|
|
271
|
+
`\n ${cyan("▸")} :${boldCyan(port)} ` +
|
|
272
|
+
`${gray(`PID ${entry.pid}`)} ${entry.appLabel}\n`
|
|
268
273
|
);
|
|
269
|
-
|
|
270
274
|
const ok = await killWithAnimation(entry.pid);
|
|
271
|
-
|
|
272
275
|
if (ok) {
|
|
273
|
-
console.log(` ${
|
|
276
|
+
console.log(` ${red("☠")} :${boldMagenta(port)} killed ${gray("←")} ${entry.appLabel}`);
|
|
274
277
|
nKilled++;
|
|
275
278
|
} else {
|
|
276
|
-
console.log(` ${
|
|
279
|
+
console.log(` ${red("✗")} :${bold(port)} could not be killed — try ${yellow("--soft")}`);
|
|
277
280
|
nFailed++;
|
|
278
281
|
}
|
|
279
282
|
console.log();
|
|
@@ -284,28 +287,28 @@ async function killPort(port) {
|
|
|
284
287
|
async function killAll() {
|
|
285
288
|
const pids = await getAllListeningPids();
|
|
286
289
|
if (pids.size === 0) {
|
|
287
|
-
console.log(
|
|
290
|
+
console.log(gray(" ○ No listening processes found.")); return;
|
|
288
291
|
}
|
|
289
292
|
|
|
290
|
-
console.log(
|
|
293
|
+
console.log(cyan(`\n ${pids.size} process(es) listening:\n`));
|
|
291
294
|
for (const { pid, appLabel, port } of pids.values())
|
|
292
|
-
console.log(` ${
|
|
295
|
+
console.log(` ${gray(`:${String(port).padEnd(6)}`)} ${gray(`PID ${String(pid).padStart(6)}`)} ${appLabel}`);
|
|
293
296
|
console.log();
|
|
294
297
|
|
|
295
298
|
const ok = await confirm(`Terminate ALL ${pids.size} processes?`);
|
|
296
|
-
if (!ok) { console.log(
|
|
299
|
+
if (!ok) { console.log(gray("\n Aborted.\n")); return; }
|
|
297
300
|
|
|
298
301
|
for (const entry of pids.values()) {
|
|
299
302
|
console.log(
|
|
300
|
-
`\n ${
|
|
301
|
-
`${
|
|
303
|
+
`\n ${cyan("▸")} :${boldCyan(entry.port)} ` +
|
|
304
|
+
`${gray(`PID ${entry.pid}`)} ${entry.appLabel}\n`
|
|
302
305
|
);
|
|
303
306
|
const killed = await killWithAnimation(entry.pid);
|
|
304
307
|
if (killed) {
|
|
305
|
-
console.log(` ${
|
|
308
|
+
console.log(` ${red("☠")} :${boldMagenta(entry.port)} killed ${gray("←")} ${entry.appLabel}`);
|
|
306
309
|
nKilled++;
|
|
307
310
|
} else {
|
|
308
|
-
console.log(` ${
|
|
311
|
+
console.log(` ${red("✗")} :${bold(entry.port)} could not be killed`);
|
|
309
312
|
nFailed++;
|
|
310
313
|
}
|
|
311
314
|
console.log();
|
|
@@ -315,20 +318,20 @@ async function killAll() {
|
|
|
315
318
|
// ─── Help ─────────────────────────────────────────────────────────────────────
|
|
316
319
|
function showHelp() {
|
|
317
320
|
console.log(`
|
|
318
|
-
${
|
|
321
|
+
${red("♜")} ${bold("killport")}
|
|
319
322
|
|
|
320
|
-
${
|
|
323
|
+
${cyan("Usage:")}
|
|
321
324
|
killport <port> [port ...]
|
|
322
325
|
killport --all
|
|
323
326
|
|
|
324
|
-
${
|
|
327
|
+
${cyan("Options:")}
|
|
325
328
|
--soft, --no-force Graceful SIGTERM before SIGKILL
|
|
326
329
|
--all Kill every listening process
|
|
327
330
|
--yes, -y Skip --all confirmation
|
|
328
331
|
--verbose, -v Show signal details
|
|
329
332
|
--help, -h This help
|
|
330
333
|
|
|
331
|
-
${
|
|
334
|
+
${cyan("Examples:")}
|
|
332
335
|
killport 3000
|
|
333
336
|
killport 3000 8080
|
|
334
337
|
killport --all -y
|
|
@@ -340,15 +343,15 @@ async function main() {
|
|
|
340
343
|
if (args.includes("--help") || args.includes("-h")) { showHelp(); process.exit(0); }
|
|
341
344
|
|
|
342
345
|
if (!isAll && ports.length === 0) {
|
|
343
|
-
console.error(
|
|
346
|
+
console.error(red("\n ❌ Provide at least one port, or use --all\n"));
|
|
344
347
|
showHelp(); process.exit(1);
|
|
345
348
|
}
|
|
346
349
|
|
|
347
350
|
const target = isAll
|
|
348
|
-
?
|
|
349
|
-
: ports.map(p =>
|
|
351
|
+
? red("all processes")
|
|
352
|
+
: ports.map(p => cyan(`:${p}`)).join(gray(" "));
|
|
350
353
|
|
|
351
|
-
console.log(`\n ${
|
|
354
|
+
console.log(`\n ${red("♜")} ${bold("killport")} ${gray("·")} ${target}\n`);
|
|
352
355
|
|
|
353
356
|
try {
|
|
354
357
|
if (isAll) {
|
|
@@ -358,15 +361,15 @@ async function main() {
|
|
|
358
361
|
}
|
|
359
362
|
|
|
360
363
|
const parts = [];
|
|
361
|
-
if (nKilled) parts.push(
|
|
362
|
-
if (nFailed) parts.push(
|
|
363
|
-
if (nNotFound) parts.push(
|
|
364
|
-
if (parts.length) console.log(" " + parts.join(
|
|
364
|
+
if (nKilled) parts.push(green(`☠ ${nKilled} killed`));
|
|
365
|
+
if (nFailed) parts.push(red(`✗ ${nFailed} failed`));
|
|
366
|
+
if (nNotFound) parts.push(gray(`○ ${nNotFound} not found`));
|
|
367
|
+
if (parts.length) console.log(" " + parts.join(gray(" · ")));
|
|
365
368
|
console.log();
|
|
366
369
|
|
|
367
370
|
} catch (err) {
|
|
368
371
|
clearLine();
|
|
369
|
-
console.error(
|
|
372
|
+
console.error(red(" ❌"), err.message);
|
|
370
373
|
process.exit(1);
|
|
371
374
|
}
|
|
372
375
|
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
|
|
2
|
+
"name": "killtask",
|
|
3
|
+
"version": "1.1.2",
|
|
4
|
+
"description": "Port killer CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"killtask": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"cli",
|
|
11
|
+
"port",
|
|
12
|
+
"kill",
|
|
13
|
+
"kill-task",
|
|
14
|
+
"task-kill",
|
|
15
|
+
"process-kill",
|
|
16
|
+
"process"
|
|
17
|
+
],
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/ksanjeeb/killport.git"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/ksanjeeb/killport#readme",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/ksanjeeb/killport/issues"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/readme.md
CHANGED