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.
Files changed (3) hide show
  1. package/index.js +86 -82
  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,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 = 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
88
  for (let i = 0; i < 3; 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
93
  await sleep(180);
73
94
  }
74
- // Bullet travels
75
95
  for (let pos = 0; pos < TRACK; pos++) {
76
- drawFrame(pos, ENEMY, chalk.white("pew pew…"));
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
- [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
+ [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) track += chalk.red("◄");
103
- else if (i > pos) track += chalk.gray("·");
104
- else track += chalk.gray("─");
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
- if (ok) {
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 chalk.gray("unknown");
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 `${chalk.white(bin)} ${chalk.gray("·")} ${chalk.cyan(short)}`;
165
- return chalk.white(bin || "unknown");
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(chalk.red(e.message.split("\n").find(l => l.trim()) ?? e.message));
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(chalk.red(` ✗ Invalid port: ${port}`)); return;
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(` ${chalk.gray("○")} :${chalk.white(port)} ${chalk.gray("— nothing listening")}`);
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 ${chalk.cyan("▸")} :${chalk.bold.cyan(port)} ` +
267
- `${chalk.gray(`PID ${entry.pid}`)} ${entry.appLabel}\n`
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(` ${chalk.red("☠")} :${chalk.bold.magenta(port)} killed ${chalk.gray("←")} ${entry.appLabel}`);
277
+ console.log(` ${red("☠")} :${boldMagenta(port)} killed ${gray("←")} ${entry.appLabel}`);
274
278
  nKilled++;
275
279
  } else {
276
- console.log(` ${chalk.red("✗")} :${chalk.bold(port)} could not be killed — try ${chalk.yellow("--soft")}`);
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(chalk.gray(" ○ No listening processes found.")); return;
291
+ console.log(gray(" ○ No listening processes found.")); return;
288
292
  }
289
293
 
290
- console.log(chalk.cyan(`\n ${pids.size} process(es) listening:\n`));
294
+ console.log(cyan(`\n ${pids.size} process(es) listening:\n`));
291
295
  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}`);
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(chalk.gray("\n Aborted.\n")); return; }
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 ${chalk.cyan("▸")} :${chalk.bold.cyan(entry.port)} ` +
301
- `${chalk.gray(`PID ${entry.pid}`)} ${entry.appLabel}\n`
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(` ${chalk.red("☠")} :${chalk.bold.magenta(entry.port)} killed ${chalk.gray("←")} ${entry.appLabel}`);
309
+ console.log(` ${red("☠")} :${boldMagenta(entry.port)} killed ${gray("←")} ${entry.appLabel}`);
306
310
  nKilled++;
307
311
  } else {
308
- console.log(` ${chalk.red("✗")} :${chalk.bold(entry.port)} could not be killed`);
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
- ${chalk.red("♜")} ${chalk.bold("killport")}
322
+ ${red("♜")} ${bold("killport")}
319
323
 
320
- ${chalk.cyan("Usage:")}
324
+ ${cyan("Usage:")}
321
325
  killport <port> [port ...]
322
326
  killport --all
323
327
 
324
- ${chalk.cyan("Options:")}
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
- ${chalk.cyan("Examples:")}
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(chalk.red("\n ❌ Provide at least one port, or use --all\n"));
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
- ? chalk.red("all processes")
349
- : ports.map(p => chalk.cyan(`:${p}`)).join(chalk.gray(" "));
352
+ ? red("all processes")
353
+ : ports.map(p => cyan(`:${p}`)).join(gray(" "));
350
354
 
351
- console.log(`\n ${chalk.red("♜")} ${chalk.bold("killport")} ${chalk.gray("·")} ${target}\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(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(" · ")));
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(chalk.red(" ❌"), err.message);
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
- "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.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
@@ -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