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.
Files changed (3) hide show
  1. package/index.js +90 -87
  2. package/package.json +26 -13
  3. 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
- // ─── Args ────────────────────────────────────────────────────────────────────
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(chalk.gray(" ·"), ...msg);
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(chalk.yellow(` ⚠️ ${q} [y/N] `), a => { rl.close(); resolve(a.toLowerCase() === "y"); });
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 = chalk.red("♜");
52
- const ENEMY = chalk.green("♟");
74
+ const MARIO = red("♜");
75
+ const ENEMY = `\x1b[32m♟${c.reset}`;
53
76
 
54
- function drawFrame(bulletPos, enemyChar, label) {
77
+ function drawBulletFrame(pos, enemyChar, label) {
55
78
  let track = "";
56
79
  for (let i = 0; i < TRACK; i++) {
57
- if (i === bulletPos) track += chalk.yellowBright("►");
58
- else if (i < bulletPos) track += chalk.gray("·");
59
- else track += chalk.gray("─");
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
- for (let i = 0; i < 3; i++) {
88
+ for (let i = 0; i < 2; i++) {
68
89
  process.stdout.write(
69
- `\r ${MARIO} ${chalk.gray("─".repeat(TRACK))} ${ENEMY} ` +
70
- (i % 2 === 0 ? chalk.gray("aiming…") : chalk.yellowBright("🔫 FIRE!"))
90
+ `\r ${MARIO} ${gray("─".repeat(TRACK))} ${ENEMY} ` +
91
+ (i % 2 === 0 ? gray("aiming…") : yellow("🔫 FIRE!"))
71
92
  );
72
- await sleep(180);
93
+ await sleep(80);
73
94
  }
74
- // Bullet travels
75
95
  for (let pos = 0; pos < TRACK; pos++) {
76
- drawFrame(pos, ENEMY, chalk.white("pew pew…"));
77
- await sleep(36);
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
- [chalk.yellowBright("✸"), chalk.bgRed.bold(" HIT ") ],
84
- [chalk.red("✺"), chalk.red("💥 BOOM!") ],
85
- [chalk.yellowBright("✦"), chalk.red("☠ TERMINATED") ],
86
- [chalk.white("·"), chalk.green("✔ port killed") ],
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
- `\r ${MARIO} ${chalk.gray("·".repeat(TRACK))} ${sym} ${label}`
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) track += chalk.red("◄");
103
- else if (i > pos) track += chalk.gray("·");
104
- else track += chalk.gray("─");
119
+ if (i === pos) track += red("◄");
120
+ else if (i > pos) track += gray("·");
121
+ else track += gray("─");
105
122
  }
106
- process.stdout.write(
107
- `\r ${MARIO} ${track} ${chalk.red("⛨")} ${chalk.red("blocked!")}`
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
- if (ok) {
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 chalk.gray("unknown");
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 `${chalk.white(bin)} ${chalk.gray("·")} ${chalk.cyan(short)}`;
165
- return chalk.white(bin || "unknown");
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(chalk.red(e.message.split("\n").find(l => l.trim()) ?? e.message));
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(chalk.red(` ✗ Invalid port: ${port}`)); return;
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(` ${chalk.gray("○")} :${chalk.white(port)} ${chalk.gray("— nothing listening")}`);
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 ${chalk.cyan("▸")} :${chalk.bold.cyan(port)} ` +
267
- `${chalk.gray(`PID ${entry.pid}`)} ${entry.appLabel}\n`
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(` ${chalk.red("☠")} :${chalk.bold.magenta(port)} killed ${chalk.gray("←")} ${entry.appLabel}`);
276
+ console.log(` ${red("☠")} :${boldMagenta(port)} killed ${gray("←")} ${entry.appLabel}`);
274
277
  nKilled++;
275
278
  } else {
276
- console.log(` ${chalk.red("✗")} :${chalk.bold(port)} could not be killed — try ${chalk.yellow("--soft")}`);
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(chalk.gray(" ○ No listening processes found.")); return;
290
+ console.log(gray(" ○ No listening processes found.")); return;
288
291
  }
289
292
 
290
- console.log(chalk.cyan(`\n ${pids.size} process(es) listening:\n`));
293
+ console.log(cyan(`\n ${pids.size} process(es) listening:\n`));
291
294
  for (const { pid, appLabel, port } of pids.values())
292
- console.log(` ${chalk.gray(`:${String(port).padEnd(6)}`)} ${chalk.gray(`PID ${String(pid).padStart(6)}`)} ${appLabel}`);
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(chalk.gray("\n Aborted.\n")); return; }
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 ${chalk.cyan("▸")} :${chalk.bold.cyan(entry.port)} ` +
301
- `${chalk.gray(`PID ${entry.pid}`)} ${entry.appLabel}\n`
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(` ${chalk.red("☠")} :${chalk.bold.magenta(entry.port)} killed ${chalk.gray("←")} ${entry.appLabel}`);
308
+ console.log(` ${red("☠")} :${boldMagenta(entry.port)} killed ${gray("←")} ${entry.appLabel}`);
306
309
  nKilled++;
307
310
  } else {
308
- console.log(` ${chalk.red("✗")} :${chalk.bold(entry.port)} could not be killed`);
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
- ${chalk.red("♜")} ${chalk.bold("killport")}
321
+ ${red("♜")} ${bold("killport")}
319
322
 
320
- ${chalk.cyan("Usage:")}
323
+ ${cyan("Usage:")}
321
324
  killport <port> [port ...]
322
325
  killport --all
323
326
 
324
- ${chalk.cyan("Options:")}
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
- ${chalk.cyan("Examples:")}
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(chalk.red("\n ❌ Provide at least one port, or use --all\n"));
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
- ? chalk.red("all processes")
349
- : ports.map(p => chalk.cyan(`:${p}`)).join(chalk.gray(" "));
351
+ ? red("all processes")
352
+ : ports.map(p => cyan(`:${p}`)).join(gray(" "));
350
353
 
351
- console.log(`\n ${chalk.red("♜")} ${chalk.bold("killport")} ${chalk.gray("·")} ${target}\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(chalk.green(`☠ ${nKilled} killed`));
362
- if (nFailed) parts.push(chalk.red(`✗ ${nFailed} failed`));
363
- if (nNotFound) parts.push(chalk.gray(`○ ${nNotFound} not found`));
364
- if (parts.length) console.log(" " + parts.join(chalk.gray(" · ")));
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(chalk.red(" ❌"), err.message);
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
- "name": "killtask",
3
- "version": "1.0.0",
4
- "description": "Port killer CLI",
5
- "type": "module",
6
- "bin": {
7
- "killtask": "./index.js"
8
- },
9
- "dependencies": {
10
- "chalk": "^5.3.0"
11
- },
12
- "keywords": ["cli", "port", "kill", "kill-task","task-kill","process-kill","process"],
13
- "license": "MIT"
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
@@ -1,9 +1,9 @@
1
- # killport
1
+ # killtask
2
2
  CLI to kill ports.
3
3
 
4
4
  ## Usage
5
5
 
6
- killport 8080
7
- killport 8080 3000
8
- killport --all
9
- killport 8080 --force
6
+ npx killtask 8080
7
+ npx killtask 8080 3000
8
+ npx killtask --all
9
+ npx killtask 8080 --force