killtask 1.0.0 → 1.1.1
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 +86 -82
- 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,53 +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
|
-
// Aim flash
|
|
67
88
|
for (let i = 0; i < 3; 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
93
|
await sleep(180);
|
|
73
94
|
}
|
|
74
|
-
// Bullet travels
|
|
75
95
|
for (let pos = 0; pos < TRACK; pos++) {
|
|
76
|
-
|
|
96
|
+
drawBulletFrame(pos, ENEMY, white("pew pew…"));
|
|
77
97
|
await sleep(36);
|
|
78
98
|
}
|
|
79
99
|
}
|
|
80
100
|
|
|
81
101
|
async function animateExplosion() {
|
|
82
102
|
const frames = [
|
|
83
|
-
[
|
|
84
|
-
[
|
|
85
|
-
[
|
|
86
|
-
[
|
|
87
|
-
[" ",
|
|
103
|
+
[yellow("✸"), hitLabel("HIT") ],
|
|
104
|
+
[red("✺"), red("💥 BOOM!") ],
|
|
105
|
+
[yellow("✦"), red("☠ TERMINATED") ],
|
|
106
|
+
[white("·"), green("✔ port killed") ],
|
|
107
|
+
[" ", green("✔ port killed") ],
|
|
88
108
|
];
|
|
89
109
|
for (const [sym, label] of frames) {
|
|
90
|
-
process.stdout.write(
|
|
91
|
-
`\r ${MARIO} ${chalk.gray("·".repeat(TRACK))} ${sym} ${label}`
|
|
92
|
-
);
|
|
110
|
+
process.stdout.write(`\r ${MARIO} ${gray("·".repeat(TRACK))} ${sym} ${label}`);
|
|
93
111
|
await sleep(85);
|
|
94
112
|
}
|
|
95
113
|
clearLine();
|
|
@@ -99,33 +117,22 @@ async function animateBounce() {
|
|
|
99
117
|
for (let pos = TRACK - 1; pos >= 0; pos--) {
|
|
100
118
|
let track = "";
|
|
101
119
|
for (let i = 0; i < TRACK; i++) {
|
|
102
|
-
if (i === pos)
|
|
103
|
-
else if (i > pos)
|
|
104
|
-
else
|
|
120
|
+
if (i === pos) track += red("◄");
|
|
121
|
+
else if (i > pos) track += gray("·");
|
|
122
|
+
else track += gray("─");
|
|
105
123
|
}
|
|
106
|
-
process.stdout.write(
|
|
107
|
-
`\r ${MARIO} ${track} ${chalk.red("⛨")} ${chalk.red("blocked!")}`
|
|
108
|
-
);
|
|
124
|
+
process.stdout.write(`\r ${MARIO} ${track} ${red("⛨")} ${red("blocked!")}`);
|
|
109
125
|
await sleep(22);
|
|
110
126
|
}
|
|
111
127
|
clearLine();
|
|
112
128
|
}
|
|
113
129
|
|
|
114
|
-
// ─── Kill + animate concurrently ─────────────────────────────────────────────
|
|
115
130
|
async function killWithAnimation(pid) {
|
|
116
|
-
// Start bullet animation and kill in parallel
|
|
117
131
|
const killPromise = killPid(pid);
|
|
118
132
|
await animateBullet();
|
|
119
|
-
|
|
120
|
-
// Wait for kill to finish (it's usually done by now)
|
|
121
133
|
const ok = await killPromise;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
await animateExplosion();
|
|
125
|
-
} else {
|
|
126
|
-
await animateBounce();
|
|
127
|
-
}
|
|
128
|
-
|
|
134
|
+
if (ok) await animateExplosion();
|
|
135
|
+
else await animateBounce();
|
|
129
136
|
return ok;
|
|
130
137
|
}
|
|
131
138
|
|
|
@@ -150,7 +157,7 @@ async function getAppLabel(pid) {
|
|
|
150
157
|
}
|
|
151
158
|
return buildLabel(name, cmdLine);
|
|
152
159
|
} catch {
|
|
153
|
-
return
|
|
160
|
+
return gray("unknown");
|
|
154
161
|
}
|
|
155
162
|
}
|
|
156
163
|
|
|
@@ -161,11 +168,11 @@ function buildLabel(name, cmdLine) {
|
|
|
161
168
|
const entry = tokens.slice(1).find(t => !t.startsWith("-")) ?? "";
|
|
162
169
|
const short = entry.split(/[/\\]/).pop();
|
|
163
170
|
if (RUNTIMES.has(bin) && short)
|
|
164
|
-
return `${
|
|
165
|
-
return
|
|
171
|
+
return `${white(bin)} ${gray("·")} ${cyan(short)}`;
|
|
172
|
+
return white(bin || "unknown");
|
|
166
173
|
}
|
|
167
174
|
|
|
168
|
-
// ─── Process Discovery
|
|
175
|
+
// ─── Process Discovery ────────────────────────────────────────────────────────
|
|
169
176
|
async function getPidsForPort(port) {
|
|
170
177
|
const pids = new Map();
|
|
171
178
|
if (isWin) {
|
|
@@ -227,7 +234,7 @@ async function killPid(pid) {
|
|
|
227
234
|
log(`PID ${pid} terminated`);
|
|
228
235
|
return true;
|
|
229
236
|
} catch (e) {
|
|
230
|
-
log(
|
|
237
|
+
log(red(e.message.split("\n").find(l => l.trim()) ?? e.message));
|
|
231
238
|
return false;
|
|
232
239
|
}
|
|
233
240
|
} else {
|
|
@@ -250,30 +257,27 @@ let nKilled = 0, nFailed = 0, nNotFound = 0;
|
|
|
250
257
|
// ─── killPort ─────────────────────────────────────────────────────────────────
|
|
251
258
|
async function killPort(port) {
|
|
252
259
|
if (!/^\d+$/.test(port) || +port < 1 || +port > 65535) {
|
|
253
|
-
console.log(
|
|
260
|
+
console.log(red(` ✗ Invalid port: ${port}`)); return;
|
|
254
261
|
}
|
|
255
262
|
|
|
256
263
|
const pids = await getPidsForPort(port);
|
|
257
264
|
|
|
258
265
|
if (pids.size === 0) {
|
|
259
|
-
console.log(` ${
|
|
266
|
+
console.log(` ${gray("○")} :${white(port)} ${gray("— nothing listening")}`);
|
|
260
267
|
nNotFound++; return;
|
|
261
268
|
}
|
|
262
269
|
|
|
263
270
|
for (const entry of pids.values()) {
|
|
264
|
-
// Single info line
|
|
265
271
|
console.log(
|
|
266
|
-
`\n ${
|
|
267
|
-
`${
|
|
272
|
+
`\n ${cyan("▸")} :${boldCyan(port)} ` +
|
|
273
|
+
`${gray(`PID ${entry.pid}`)} ${entry.appLabel}\n`
|
|
268
274
|
);
|
|
269
|
-
|
|
270
275
|
const ok = await killWithAnimation(entry.pid);
|
|
271
|
-
|
|
272
276
|
if (ok) {
|
|
273
|
-
console.log(` ${
|
|
277
|
+
console.log(` ${red("☠")} :${boldMagenta(port)} killed ${gray("←")} ${entry.appLabel}`);
|
|
274
278
|
nKilled++;
|
|
275
279
|
} else {
|
|
276
|
-
console.log(` ${
|
|
280
|
+
console.log(` ${red("✗")} :${bold(port)} could not be killed — try ${yellow("--soft")}`);
|
|
277
281
|
nFailed++;
|
|
278
282
|
}
|
|
279
283
|
console.log();
|
|
@@ -284,28 +288,28 @@ async function killPort(port) {
|
|
|
284
288
|
async function killAll() {
|
|
285
289
|
const pids = await getAllListeningPids();
|
|
286
290
|
if (pids.size === 0) {
|
|
287
|
-
console.log(
|
|
291
|
+
console.log(gray(" ○ No listening processes found.")); return;
|
|
288
292
|
}
|
|
289
293
|
|
|
290
|
-
console.log(
|
|
294
|
+
console.log(cyan(`\n ${pids.size} process(es) listening:\n`));
|
|
291
295
|
for (const { pid, appLabel, port } of pids.values())
|
|
292
|
-
console.log(` ${
|
|
296
|
+
console.log(` ${gray(`:${String(port).padEnd(6)}`)} ${gray(`PID ${String(pid).padStart(6)}`)} ${appLabel}`);
|
|
293
297
|
console.log();
|
|
294
298
|
|
|
295
299
|
const ok = await confirm(`Terminate ALL ${pids.size} processes?`);
|
|
296
|
-
if (!ok) { console.log(
|
|
300
|
+
if (!ok) { console.log(gray("\n Aborted.\n")); return; }
|
|
297
301
|
|
|
298
302
|
for (const entry of pids.values()) {
|
|
299
303
|
console.log(
|
|
300
|
-
`\n ${
|
|
301
|
-
`${
|
|
304
|
+
`\n ${cyan("▸")} :${boldCyan(entry.port)} ` +
|
|
305
|
+
`${gray(`PID ${entry.pid}`)} ${entry.appLabel}\n`
|
|
302
306
|
);
|
|
303
307
|
const killed = await killWithAnimation(entry.pid);
|
|
304
308
|
if (killed) {
|
|
305
|
-
console.log(` ${
|
|
309
|
+
console.log(` ${red("☠")} :${boldMagenta(entry.port)} killed ${gray("←")} ${entry.appLabel}`);
|
|
306
310
|
nKilled++;
|
|
307
311
|
} else {
|
|
308
|
-
console.log(` ${
|
|
312
|
+
console.log(` ${red("✗")} :${bold(entry.port)} could not be killed`);
|
|
309
313
|
nFailed++;
|
|
310
314
|
}
|
|
311
315
|
console.log();
|
|
@@ -315,20 +319,20 @@ async function killAll() {
|
|
|
315
319
|
// ─── Help ─────────────────────────────────────────────────────────────────────
|
|
316
320
|
function showHelp() {
|
|
317
321
|
console.log(`
|
|
318
|
-
${
|
|
322
|
+
${red("♜")} ${bold("killport")}
|
|
319
323
|
|
|
320
|
-
${
|
|
324
|
+
${cyan("Usage:")}
|
|
321
325
|
killport <port> [port ...]
|
|
322
326
|
killport --all
|
|
323
327
|
|
|
324
|
-
${
|
|
328
|
+
${cyan("Options:")}
|
|
325
329
|
--soft, --no-force Graceful SIGTERM before SIGKILL
|
|
326
330
|
--all Kill every listening process
|
|
327
331
|
--yes, -y Skip --all confirmation
|
|
328
332
|
--verbose, -v Show signal details
|
|
329
333
|
--help, -h This help
|
|
330
334
|
|
|
331
|
-
${
|
|
335
|
+
${cyan("Examples:")}
|
|
332
336
|
killport 3000
|
|
333
337
|
killport 3000 8080
|
|
334
338
|
killport --all -y
|
|
@@ -340,15 +344,15 @@ async function main() {
|
|
|
340
344
|
if (args.includes("--help") || args.includes("-h")) { showHelp(); process.exit(0); }
|
|
341
345
|
|
|
342
346
|
if (!isAll && ports.length === 0) {
|
|
343
|
-
console.error(
|
|
347
|
+
console.error(red("\n ❌ Provide at least one port, or use --all\n"));
|
|
344
348
|
showHelp(); process.exit(1);
|
|
345
349
|
}
|
|
346
350
|
|
|
347
351
|
const target = isAll
|
|
348
|
-
?
|
|
349
|
-
: ports.map(p =>
|
|
352
|
+
? red("all processes")
|
|
353
|
+
: ports.map(p => cyan(`:${p}`)).join(gray(" "));
|
|
350
354
|
|
|
351
|
-
console.log(`\n ${
|
|
355
|
+
console.log(`\n ${red("♜")} ${bold("killport")} ${gray("·")} ${target}\n`);
|
|
352
356
|
|
|
353
357
|
try {
|
|
354
358
|
if (isAll) {
|
|
@@ -358,15 +362,15 @@ async function main() {
|
|
|
358
362
|
}
|
|
359
363
|
|
|
360
364
|
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(
|
|
365
|
+
if (nKilled) parts.push(green(`☠ ${nKilled} killed`));
|
|
366
|
+
if (nFailed) parts.push(red(`✗ ${nFailed} failed`));
|
|
367
|
+
if (nNotFound) parts.push(gray(`○ ${nNotFound} not found`));
|
|
368
|
+
if (parts.length) console.log(" " + parts.join(gray(" · ")));
|
|
365
369
|
console.log();
|
|
366
370
|
|
|
367
371
|
} catch (err) {
|
|
368
372
|
clearLine();
|
|
369
|
-
console.error(
|
|
373
|
+
console.error(red(" ❌"), err.message);
|
|
370
374
|
process.exit(1);
|
|
371
375
|
}
|
|
372
376
|
}
|
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.1",
|
|
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