@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.
- package/README.md +168 -127
- package/bin/commands/file/rename/README.md +225 -0
- package/bin/commands/file/rename/rename.js +41 -3
- package/bin/commands/git/README.md +129 -0
- package/bin/commands/hello/README.md +35 -0
- package/bin/commands/help/README.md +55 -0
- package/bin/commands/nvm/README.md +90 -0
- package/bin/commands/process/kill-port/README.md +49 -0
- package/bin/commands/process/kill-port/kill-port.js +184 -0
- package/bin/commands/process/list-port/README.md +58 -0
- package/bin/commands/process/list-port/list-port.js +164 -0
- package/bin/commands/ssh/README.md +80 -0
- package/bin/index.js +13 -16
- package/package.json +5 -6
|
@@ -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
|
|
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
|
|
277
|
-
const isHelpInvocation = tsArgs.some((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
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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
|
|
305
|
-
const command =
|
|
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.
|
|
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.
|
|
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-
|
|
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
|
-
"
|
|
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": {
|