@tertium/hlpr 0.5.8 → 0.6.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.
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env bun
2
+ // @bun
3
+
4
+ // src/commands/process/list-port/list-port.ts
5
+ import { execSync } from "child_process";
6
+ import { platform } from "os";
7
+ function getProcessesOnWindowsPort(port) {
8
+ try {
9
+ const output = execSync(`netstat -ano | findstr :${port}`, { encoding: "utf-8" });
10
+ const processes = [];
11
+ const lines = output.split(`
12
+ `);
13
+ const pids = new Set;
14
+ for (const line of lines) {
15
+ const match = line.match(/\s+(\d+)\s*$/);
16
+ if (match) {
17
+ const pid = parseInt(match[1], 10);
18
+ if (pid > 0 && !pids.has(pid)) {
19
+ pids.add(pid);
20
+ try {
21
+ const taskOutput = execSync(`tasklist /FI "PID eq ${pid}" /NH /FO CSV`, { encoding: "utf-8" }).trim();
22
+ const parts = taskOutput.split('","');
23
+ const command = parts[0].replace(/^"|"$/g, "");
24
+ processes.push({
25
+ pid,
26
+ state: "LISTENING",
27
+ command
28
+ });
29
+ } catch {
30
+ processes.push({
31
+ pid,
32
+ state: "LISTENING"
33
+ });
34
+ }
35
+ }
36
+ }
37
+ }
38
+ return processes;
39
+ } catch (error) {
40
+ return [];
41
+ }
42
+ }
43
+ function getProcessesOnLinuxPort(port) {
44
+ try {
45
+ const output = execSync(`lsof -i :${port} -n -P`, { encoding: "utf-8" });
46
+ const processes = [];
47
+ const lines = output.split(`
48
+ `);
49
+ const pids = new Set;
50
+ for (let i = 1;i < lines.length; i++) {
51
+ const parts = lines[i].trim().split(/\s+/);
52
+ if (parts.length > 2) {
53
+ const command = parts[0];
54
+ const pid = parseInt(parts[1], 10);
55
+ const state = parts[7] || "LISTENING";
56
+ if (!isNaN(pid) && pid > 0 && !pids.has(pid)) {
57
+ pids.add(pid);
58
+ processes.push({
59
+ pid,
60
+ state,
61
+ command
62
+ });
63
+ }
64
+ }
65
+ }
66
+ return processes;
67
+ } catch {
68
+ try {
69
+ const output = execSync(`netstat -tulnp 2>/dev/null | grep :${port}`, { encoding: "utf-8" });
70
+ const processes = [];
71
+ const lines = output.split(`
72
+ `);
73
+ const pids = new Set;
74
+ for (const line of lines) {
75
+ const match = line.match(/(\d+)\/(.+)/);
76
+ if (match) {
77
+ const pid = parseInt(match[1], 10);
78
+ const command = match[2];
79
+ if (!pids.has(pid)) {
80
+ pids.add(pid);
81
+ processes.push({
82
+ pid,
83
+ state: "LISTENING",
84
+ command
85
+ });
86
+ }
87
+ }
88
+ }
89
+ return processes;
90
+ } catch {
91
+ return [];
92
+ }
93
+ }
94
+ }
95
+ function formatTable(processes) {
96
+ if (processes.length === 0) {
97
+ return "No processes found";
98
+ }
99
+ const pidWidth = 10;
100
+ const stateWidth = 12;
101
+ const commandWidth = 50;
102
+ const header = `${"PID".padEnd(pidWidth)} ${"STATE".padEnd(stateWidth)} COMMAND`;
103
+ const separator = "\u2500".repeat(pidWidth + stateWidth + commandWidth + 2);
104
+ const rows = processes.map((p) => {
105
+ const command = p.command || "unknown";
106
+ const truncated = command.length > commandWidth ? command.substring(0, commandWidth - 3) + "..." : command;
107
+ return `${p.pid.toString().padEnd(pidWidth)} ${p.state.padEnd(stateWidth)} ${truncated}`;
108
+ });
109
+ return `${header}
110
+ ${separator}
111
+ ${rows.join(`
112
+ `)}`;
113
+ }
114
+ async function listPort(port, options = {}) {
115
+ const { verbose = false } = options;
116
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
117
+ throw new Error(`Invalid port number: ${port}. Port must be between 1 and 65535.`);
118
+ }
119
+ const os = platform();
120
+ if (verbose)
121
+ console.log(`Searching for processes on port ${port} (${os})...
122
+ `);
123
+ let processes = [];
124
+ if (os === "win32") {
125
+ processes = getProcessesOnWindowsPort(port);
126
+ } else {
127
+ processes = getProcessesOnLinuxPort(port);
128
+ }
129
+ return processes;
130
+ }
131
+ if (import.meta.url.endsWith(process.argv[1]?.replace(/\\/g, "/"))) {
132
+ const args = process.argv.slice(2);
133
+ if (args.includes("--help") || args.includes("-h") || args.length === 0) {
134
+ console.log("Usage: list-port <port> [options]");
135
+ console.log("List process(es) running on a specified port (Windows & Linux support)");
136
+ console.log();
137
+ console.log("Arguments:");
138
+ console.log(" port Port number (1-65535)");
139
+ console.log();
140
+ console.log("Options:");
141
+ console.log(" -v, --verbose Show verbose output");
142
+ console.log(" -h, --help Show this help message");
143
+ console.log();
144
+ console.log("Examples:");
145
+ console.log(" hlpr process list-port 3000");
146
+ console.log(" hlpr process list-port 8080 --verbose");
147
+ console.log(" hlpr process list-port 5432 -v");
148
+ process.exit(0);
149
+ }
150
+ const port = parseInt(args[0], 10);
151
+ const verbose = args.includes("-v") || args.includes("--verbose");
152
+ listPort(port, { verbose }).then((processes) => {
153
+ console.log(formatTable(processes));
154
+ process.exit(processes.length === 0 ? 1 : 0);
155
+ }).catch((error) => {
156
+ console.error("Error:", error.message);
157
+ process.exit(1);
158
+ });
159
+ }
160
+ var list_port_default = { listPort };
161
+ export {
162
+ listPort,
163
+ list_port_default as default
164
+ };
@@ -0,0 +1,80 @@
1
+ # SSH Commands
2
+
3
+ SSH configuration and setup utilities.
4
+
5
+ ## Platform Support
6
+
7
+ - ✅ **Linux** - Fully supported
8
+ - ✅ **macOS** - Fully supported
9
+ - ✅ **Windows** - Requires Git Bash or WSL
10
+ - Git Bash (included with Git for Windows) provides Unix-like `~/.ssh` directory
11
+ - Native Windows SSH uses `%USERPROFILE%\.ssh` instead
12
+
13
+ ## init-dir
14
+
15
+ Initialize SSH directory with proper permissions.
16
+
17
+ ### Usage
18
+
19
+ ```bash
20
+ hlpr ssh init-dir
21
+ ```
22
+
23
+ ### What it does
24
+
25
+ Creates the `~/.ssh` directory and essential SSH configuration files with correct permissions:
26
+
27
+ 1. Creates `~/.ssh` directory (if it doesn't exist)
28
+ 2. Creates `~/.ssh/known_hosts` file
29
+ 3. Creates `~/.ssh/config` file
30
+ 4. Sets directory permissions to 700 (rwx------)
31
+ 5. Sets file permissions to 644 (rw-r--r--)
32
+
33
+ ### Permissions explained
34
+
35
+ - `~/.ssh/` → 700 (only owner can read/write/execute)
36
+ - `~/.ssh/known_hosts` → 644 (owner can write, others can read)
37
+ - `~/.ssh/config` → 644 (owner can write, others can read)
38
+
39
+ These permissions are required by SSH for security. If permissions are incorrect, SSH will refuse to use the files.
40
+
41
+ ### When to use
42
+
43
+ - Setting up SSH on a new system
44
+ - Fixing SSH permission issues
45
+ - After accidentally deleting SSH configuration files
46
+
47
+ ### Example
48
+
49
+ ```bash
50
+ $ hlpr ssh init-dir
51
+ # Creates ~/.ssh/ with proper structure and permissions
52
+ ```
53
+
54
+ ### Verify
55
+
56
+ ```bash
57
+ ls -la ~/.ssh/
58
+ # Should show:
59
+ # drwx------ ~/.ssh/
60
+ # -rw-r--r-- ~/.ssh/config
61
+ # -rw-r--r-- ~/.ssh/known_hosts
62
+ ```
63
+
64
+ ### Note
65
+
66
+ This command will fail if `~/.ssh` already exists. If you need to fix permissions on an existing directory, you can:
67
+
68
+ ```bash
69
+ chmod 700 ~/.ssh
70
+ chmod 644 ~/.ssh/known_hosts
71
+ chmod 644 ~/.ssh/config
72
+ ```
73
+
74
+ ### Next steps
75
+
76
+ After initializing the SSH directory, you typically:
77
+
78
+ 1. Generate SSH keys: `ssh-keygen -t ed25519 -C "your_email@example.com"`
79
+ 2. Add SSH config entries to `~/.ssh/config`
80
+ 3. Add public key to remote servers or services (GitHub, GitLab, etc.)
package/bin/index.js CHANGED
@@ -6,7 +6,6 @@ import { exec } from "node:child_process";
6
6
  import * as path from "node:path";
7
7
  import * as fs from "node:fs";
8
8
  import * as readline from "node:readline";
9
- import * as os from "node:os";
10
9
  import { fileURLToPath } from "node:url";
11
10
  async function getVersion() {
12
11
  try {
@@ -20,13 +19,6 @@ async function getVersion() {
20
19
  return "0.0.0";
21
20
  }
22
21
  }
23
- function detectShell() {
24
- const isWindows = os.platform() === "win32";
25
- if (isWindows) {
26
- return "bash";
27
- }
28
- return "bash";
29
- }
30
22
  var __filename2 = fileURLToPath(import.meta.url);
31
23
  var __dirname2 = path.dirname(__filename2);
32
24
  var scriptDir = path.join(__dirname2, "..", "src");
@@ -245,7 +237,7 @@ async function main() {
245
237
  }
246
238
  }
247
239
  if (!isTypeScriptCommand && restArgs.length > 0) {
248
- const scriptName = restArgs.join("");
240
+ const scriptName = restArgs[0];
249
241
  let scriptPathCandidate = path.join(scriptDir, "commands", category, `${scriptName}.sh`);
250
242
  if (!fs.existsSync(scriptPathCandidate)) {
251
243
  scriptPathCandidate = path.join(__dirname2, "..", "bin", "commands", category, `${scriptName}.sh`);
@@ -273,8 +265,8 @@ async function main() {
273
265
  const finalCommand = `node "${scriptPath}" ${tsArgs.join(" ")}`;
274
266
  console.log(`Executing command: ${finalCommand}`);
275
267
  const success2 = await executeCommand(finalCommand, {});
276
- const helpFlags = ["-h", "--help", "help", "/h", "/help", "/?"];
277
- const isHelpInvocation = tsArgs.some((arg) => helpFlags.includes(arg));
268
+ const helpFlags2 = ["-h", "--help", "help", "/h", "/help", "/?"];
269
+ const isHelpInvocation = tsArgs.some((arg) => helpFlags2.includes(arg));
278
270
  if (!success2 && !forceFlag && !isHelpInvocation) {
279
271
  console.error("Command failed, stopping execution.");
280
272
  process.exit(1);
@@ -284,6 +276,7 @@ async function main() {
284
276
  return;
285
277
  }
286
278
  const scriptContent = await readFile(scriptPath, "utf-8");
279
+ const scriptArgs = restArgs.slice(1);
287
280
  const variableRegex = /{{([^}]+)}}/g;
288
281
  const variables = {};
289
282
  const uniqueVars = new Set;
@@ -291,9 +284,13 @@ async function main() {
291
284
  while ((match = variableRegex.exec(scriptContent)) !== null) {
292
285
  uniqueVars.add(match[1]);
293
286
  }
294
- for (const varName of uniqueVars) {
295
- const value = await prompt(`Enter ${varName}: `);
296
- variables[varName] = value;
287
+ const helpFlags = ["-h", "--help", "help", "/h", "/help", "/?"];
288
+ const isHelpRequested = scriptArgs.some((arg) => helpFlags.includes(arg));
289
+ if (!isHelpRequested) {
290
+ for (const varName of uniqueVars) {
291
+ const value = await prompt(`Enter ${varName}: `);
292
+ variables[varName] = value;
293
+ }
297
294
  }
298
295
  let processedScript = scriptContent;
299
296
  for (const [key, value] of Object.entries(variables)) {
@@ -301,8 +298,8 @@ async function main() {
301
298
  }
302
299
  const tempScriptPath = path.join(path.dirname(scriptPath), `_temp_${path.basename(scriptPath)}`);
303
300
  fs.writeFileSync(tempScriptPath, processedScript);
304
- const shell = detectShell();
305
- const command = shell === "powershell" ? `powershell -File "${tempScriptPath}"` : `bash "${tempScriptPath}"`;
301
+ const argsStr = scriptArgs.map((arg) => `"${arg}"`).join(" ");
302
+ const command = `bash "${tempScriptPath}" ${argsStr}`;
306
303
  const success = await executeCommand(command, {});
307
304
  fs.unlinkSync(tempScriptPath);
308
305
  if (!success && !forceFlag) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tertium/hlpr",
3
- "version": "0.5.8",
3
+ "version": "0.6.1",
4
4
  "description": "Windows and *Nix utility for typical programming activity",
5
5
  "author": "Vitalii Balabanov",
6
6
  "email": "tertiumnon@gmail.com",
@@ -8,22 +8,21 @@
8
8
  "type": "module",
9
9
  "scripts": {
10
10
  "build:main": "bun build ./src/index.ts --outfile ./bin/index.js --target node",
11
- "build:commands": "bun build-commands.js",
11
+ "build:commands": "bun scripts/build-commands/build-commands.ts",
12
12
  "build": "bun run build:main && bun run build:commands",
13
13
  "test": "bun test",
14
- "test:e2e": "bun scripts/test-rename-e2e.cjs",
14
+ "test:e2e": "bun scripts/test-rename/test-rename.ts",
15
15
  "prepublishOnly": "bun run build",
16
- "release:minor": "bun node_modules/@tertium/js/scripts/release.js minor",
17
16
  "release:patch": "bun node_modules/@tertium/js/scripts/release.js patch",
17
+ "release:minor": "bun node_modules/@tertium/js/scripts/release.js minor",
18
18
  "release:major": "bun node_modules/@tertium/js/scripts/release.js major",
19
- "prepare": "husky"
19
+ "setup:hooks": "git config core.hooksPath .githooks"
20
20
  },
21
21
  "dependencies": {
22
22
  "@tertium/js": "^1.4.7"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^22.13.10",
26
- "husky": "^9.1.7",
27
26
  "typescript": "^5.8.3"
28
27
  },
29
28
  "bin": {