portless 0.2.0 → 0.2.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.
- package/dist/cli.js +59 -7
- package/package.json +18 -15
package/dist/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ import chalk from "chalk";
|
|
|
11
11
|
import * as fs from "fs";
|
|
12
12
|
import * as path from "path";
|
|
13
13
|
import * as readline from "readline";
|
|
14
|
-
import { spawn, spawnSync } from "child_process";
|
|
14
|
+
import { execSync, spawn, spawnSync } from "child_process";
|
|
15
15
|
|
|
16
16
|
// src/cli-utils.ts
|
|
17
17
|
import * as net from "net";
|
|
@@ -85,6 +85,18 @@ function readProxyPort() {
|
|
|
85
85
|
return DEFAULT_PROXY_PORT;
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
+
function findPidOnPort(port) {
|
|
89
|
+
try {
|
|
90
|
+
const output = execSync(`lsof -ti tcp:${port} -sTCP:LISTEN`, {
|
|
91
|
+
encoding: "utf-8",
|
|
92
|
+
timeout: 5e3
|
|
93
|
+
});
|
|
94
|
+
const pid = parseInt(output.trim().split("\n")[0], 10);
|
|
95
|
+
return isNaN(pid) ? null : pid;
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
88
100
|
async function waitForProxy(proxyPort, maxAttempts = 20, intervalMs = 250) {
|
|
89
101
|
const port = proxyPort ?? readProxyPort();
|
|
90
102
|
for (let i = 0; i < maxAttempts; i++) {
|
|
@@ -199,11 +211,40 @@ function startProxyServer(proxyPort) {
|
|
|
199
211
|
}
|
|
200
212
|
async function stopProxy() {
|
|
201
213
|
const pidPath = store.pidPath;
|
|
214
|
+
const proxyPort = readProxyPort();
|
|
202
215
|
if (!fs.existsSync(pidPath)) {
|
|
203
|
-
|
|
216
|
+
if (await isProxyRunning(proxyPort)) {
|
|
217
|
+
console.log(chalk.yellow(`PID file is missing but port ${proxyPort} is still in use.`));
|
|
218
|
+
const pid = findPidOnPort(proxyPort);
|
|
219
|
+
if (pid !== null) {
|
|
220
|
+
try {
|
|
221
|
+
process.kill(pid, "SIGTERM");
|
|
222
|
+
try {
|
|
223
|
+
fs.unlinkSync(PROXY_PORT_PATH);
|
|
224
|
+
} catch {
|
|
225
|
+
}
|
|
226
|
+
console.log(chalk.green(`Killed process ${pid}. Proxy stopped.`));
|
|
227
|
+
} catch (err) {
|
|
228
|
+
if (isErrnoException(err) && err.code === "EPERM") {
|
|
229
|
+
console.error(chalk.red("Permission denied. The proxy runs as root."));
|
|
230
|
+
console.log(chalk.blue("Use: sudo portless proxy stop"));
|
|
231
|
+
} else {
|
|
232
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
233
|
+
console.error(chalk.red("Failed to stop proxy:"), message);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} else if (process.getuid?.() !== 0) {
|
|
237
|
+
console.error(chalk.red("Permission denied. The proxy likely runs as root."));
|
|
238
|
+
console.log(chalk.blue("Use: sudo portless proxy stop"));
|
|
239
|
+
} else {
|
|
240
|
+
console.error(chalk.red(`Could not identify the process on port ${proxyPort}.`));
|
|
241
|
+
console.log(chalk.blue(`Try: sudo kill "$(lsof -ti tcp:${proxyPort})"`));
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
console.log(chalk.yellow("Proxy is not running."));
|
|
245
|
+
}
|
|
204
246
|
return;
|
|
205
247
|
}
|
|
206
|
-
const proxyPort = readProxyPort();
|
|
207
248
|
try {
|
|
208
249
|
const pid = parseInt(fs.readFileSync(pidPath, "utf-8"), 10);
|
|
209
250
|
if (isNaN(pid)) {
|
|
@@ -326,6 +367,13 @@ portless
|
|
|
326
367
|
}
|
|
327
368
|
async function main() {
|
|
328
369
|
const args = process.argv.slice(2);
|
|
370
|
+
const isNpx = process.env.npm_command === "exec" && !process.env.npm_lifecycle_event;
|
|
371
|
+
const isPnpmDlx = !!process.env.PNPM_SCRIPT_SRC_DIR && !process.env.npm_lifecycle_event;
|
|
372
|
+
if (isNpx || isPnpmDlx) {
|
|
373
|
+
console.error(chalk.red("Error: portless should not be run via npx or pnpm dlx."));
|
|
374
|
+
console.log(chalk.blue("Install globally: npm install -g portless"));
|
|
375
|
+
process.exit(1);
|
|
376
|
+
}
|
|
329
377
|
const skipPortless = process.env.PORTLESS === "0" || process.env.PORTLESS === "skip";
|
|
330
378
|
if (skipPortless && args.length >= 2 && args[0] !== "proxy") {
|
|
331
379
|
spawnCommand(args.slice(1));
|
|
@@ -338,6 +386,10 @@ ${chalk.bold("portless")} - Replace port numbers with stable, named .localhost U
|
|
|
338
386
|
Eliminates port conflicts, memorizing port numbers, and cookie/storage
|
|
339
387
|
clashes by giving each dev server a stable .localhost URL.
|
|
340
388
|
|
|
389
|
+
${chalk.bold("Install:")}
|
|
390
|
+
${chalk.cyan("npm install -g portless")}
|
|
391
|
+
Do NOT add portless as a project dependency.
|
|
392
|
+
|
|
341
393
|
${chalk.bold("Usage:")}
|
|
342
394
|
${chalk.cyan("sudo portless proxy")} Start the proxy (run once, keep open)
|
|
343
395
|
${chalk.cyan("sudo portless proxy --port 8080")} Start the proxy on a custom port
|
|
@@ -346,9 +398,9 @@ ${chalk.bold("Usage:")}
|
|
|
346
398
|
${chalk.cyan("portless list")} Show active routes
|
|
347
399
|
|
|
348
400
|
${chalk.bold("Examples:")}
|
|
349
|
-
sudo portless proxy
|
|
350
|
-
portless myapp next dev
|
|
351
|
-
portless api.myapp pnpm start
|
|
401
|
+
sudo portless proxy # Start proxy in terminal 1
|
|
402
|
+
portless myapp next dev # Terminal 2 -> http://myapp.localhost
|
|
403
|
+
portless api.myapp pnpm start # Terminal 3 -> http://api.myapp.localhost
|
|
352
404
|
|
|
353
405
|
${chalk.bold("In package.json:")}
|
|
354
406
|
{
|
|
@@ -374,7 +426,7 @@ ${chalk.bold("Skip portless:")}
|
|
|
374
426
|
process.exit(0);
|
|
375
427
|
}
|
|
376
428
|
if (args[0] === "--version" || args[0] === "-v") {
|
|
377
|
-
console.log("0.2.
|
|
429
|
+
console.log("0.2.2");
|
|
378
430
|
process.exit(0);
|
|
379
431
|
}
|
|
380
432
|
if (args[0] === "list") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "portless",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Replace port numbers with stable, named .localhost URLs. For humans and agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"dist"
|
|
19
19
|
],
|
|
20
|
+
"packageManager": "pnpm@9.15.4",
|
|
20
21
|
"engines": {
|
|
21
22
|
"node": ">=20"
|
|
22
23
|
},
|
|
@@ -24,6 +25,21 @@
|
|
|
24
25
|
"darwin",
|
|
25
26
|
"linux"
|
|
26
27
|
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsup",
|
|
30
|
+
"dev": "tsup --watch",
|
|
31
|
+
"format": "prettier --write .",
|
|
32
|
+
"format:check": "prettier --check .",
|
|
33
|
+
"lint": "eslint src/",
|
|
34
|
+
"lint:fix": "eslint src/ --fix",
|
|
35
|
+
"prepublishOnly": "pnpm build",
|
|
36
|
+
"pretest": "pnpm build",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"test:coverage": "vitest run --coverage",
|
|
39
|
+
"test:watch": "vitest",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"prepare": "husky"
|
|
42
|
+
},
|
|
27
43
|
"keywords": [
|
|
28
44
|
"local",
|
|
29
45
|
"development",
|
|
@@ -65,18 +81,5 @@
|
|
|
65
81
|
"prettier --write"
|
|
66
82
|
],
|
|
67
83
|
"*.{json,md,yml,yaml}": "prettier --write"
|
|
68
|
-
},
|
|
69
|
-
"scripts": {
|
|
70
|
-
"build": "tsup",
|
|
71
|
-
"dev": "tsup --watch",
|
|
72
|
-
"format": "prettier --write .",
|
|
73
|
-
"format:check": "prettier --check .",
|
|
74
|
-
"lint": "eslint src/",
|
|
75
|
-
"lint:fix": "eslint src/ --fix",
|
|
76
|
-
"pretest": "pnpm build",
|
|
77
|
-
"test": "vitest run",
|
|
78
|
-
"test:coverage": "vitest run --coverage",
|
|
79
|
-
"test:watch": "vitest",
|
|
80
|
-
"typecheck": "tsc --noEmit"
|
|
81
84
|
}
|
|
82
|
-
}
|
|
85
|
+
}
|