portless 0.2.0 → 0.2.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 (2) hide show
  1. package/dist/cli.js +45 -4
  2. package/package.json +1 -1
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
- console.log(chalk.yellow("Proxy is not running."));
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)) {
@@ -374,7 +415,7 @@ ${chalk.bold("Skip portless:")}
374
415
  process.exit(0);
375
416
  }
376
417
  if (args[0] === "--version" || args[0] === "-v") {
377
- console.log("0.2.0");
418
+ console.log("0.2.1");
378
419
  process.exit(0);
379
420
  }
380
421
  if (args[0] === "list") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "portless",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
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",