portless 0.13.0 → 0.14.0
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 +31 -2
- package/dist/{chunk-3WLVQXFE.js → chunk-OZM4AEYL.js} +22 -4
- package/dist/cli.js +786 -85
- package/dist/index.d.ts +10 -1
- package/dist/index.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -255,11 +255,16 @@ Install the proxy as an OS startup service so clean HTTPS URLs are available aft
|
|
|
255
255
|
|
|
256
256
|
```bash
|
|
257
257
|
portless service install
|
|
258
|
+
portless service install --lan
|
|
259
|
+
portless service install --wildcard
|
|
260
|
+
PORTLESS_STATE_DIR=~/.portless-lan PORTLESS_LAN=1 portless service install
|
|
258
261
|
portless service status
|
|
259
262
|
portless service uninstall
|
|
260
263
|
```
|
|
261
264
|
|
|
262
|
-
The service uses portless defaults: HTTPS on port 443 with `.localhost` names.
|
|
265
|
+
The service uses portless defaults unless install options or `PORTLESS_*` environment variables are provided: HTTPS on port 443 with `.localhost` names. `service install` accepts the proxy options you would use with `proxy start`, including `--port`, `--no-tls`, `--lan`, `--ip`, `--tld`, `--wildcard`, `--cert`, and `--key`. Use `--state-dir <path>` or `PORTLESS_STATE_DIR=<path>` to choose where service state and logs are written.
|
|
266
|
+
|
|
267
|
+
The chosen service configuration is written into launchd, systemd, or Task Scheduler and reused after reboot. `portless service status` reports the installed port, HTTPS mode, TLD, LAN mode, wildcard mode, and state directory. macOS and Linux install a root-owned service so port 443 can bind at boot. Windows installs a Task Scheduler startup task that runs as SYSTEM. Installation and removal may require administrator privileges. `portless clean` automatically removes the service.
|
|
263
268
|
|
|
264
269
|
## LAN mode
|
|
265
270
|
|
|
@@ -318,6 +323,20 @@ Set `PORTLESS_TAILSCALE=1` in your shell profile or `.env` to share every app by
|
|
|
318
323
|
|
|
319
324
|
Requires the Tailscale CLI to be installed and connected (`tailscale up`), with Tailscale HTTPS certificates enabled.
|
|
320
325
|
|
|
326
|
+
## ngrok sharing
|
|
327
|
+
|
|
328
|
+
Expose your dev server to the public internet with [ngrok](https://ngrok.com):
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
portless myapp --ngrok next dev
|
|
332
|
+
# -> https://myapp.localhost (local)
|
|
333
|
+
# -> https://abc123.ngrok.app (public)
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Set `PORTLESS_NGROK=1` in your shell profile or `.env` to enable ngrok by default when portless runs an app. `portless list` shows both local and ngrok URLs. The ngrok tunnel is cleaned up automatically when the app exits.
|
|
337
|
+
|
|
338
|
+
Requires the ngrok CLI to be installed and authenticated. If ngrok reports an authentication error, run `ngrok config add-authtoken <token>` and try again.
|
|
339
|
+
|
|
321
340
|
## Commands
|
|
322
341
|
|
|
323
342
|
```bash
|
|
@@ -349,6 +368,8 @@ portless proxy stop # Stop the proxy
|
|
|
349
368
|
|
|
350
369
|
# OS startup service
|
|
351
370
|
portless service install # Start HTTPS proxy when the OS starts
|
|
371
|
+
portless service install --lan # Start service in LAN mode
|
|
372
|
+
portless service install --wildcard # Persist wildcard routing in the service
|
|
352
373
|
portless service status # Show service and proxy status
|
|
353
374
|
portless service uninstall # Remove the startup service
|
|
354
375
|
```
|
|
@@ -366,10 +387,12 @@ portless service uninstall # Remove the startup service
|
|
|
366
387
|
--foreground Run proxy in foreground instead of daemon
|
|
367
388
|
--tld <tld> Use a custom TLD instead of .localhost (e.g. test)
|
|
368
389
|
--wildcard Allow unregistered subdomains to fall back to parent route
|
|
390
|
+
--state-dir <path> Use a custom state directory with service install
|
|
369
391
|
--script <name> Run a specific package.json script (default: dev)
|
|
370
392
|
--app-port <number> Use a fixed port for the app (skip auto-assignment)
|
|
371
393
|
--tailscale Share the app on your Tailscale network (tailnet)
|
|
372
394
|
--funnel Share the app publicly via Tailscale Funnel
|
|
395
|
+
--ngrok Share the app publicly via ngrok
|
|
373
396
|
--force Kill the existing process and take over its route
|
|
374
397
|
--name <name> Use <name> as the app name
|
|
375
398
|
```
|
|
@@ -382,11 +405,13 @@ PORTLESS_PORT=<number> Override the default proxy port
|
|
|
382
405
|
PORTLESS_APP_PORT=<number> Use a fixed port for the app (same as --app-port)
|
|
383
406
|
PORTLESS_HTTPS=0 Disable HTTPS (same as --no-tls)
|
|
384
407
|
PORTLESS_LAN=1 Enable LAN mode when set to 1 (auto-detects LAN IP)
|
|
408
|
+
PORTLESS_LAN_IP=<address> Pin a specific LAN IP for LAN mode
|
|
385
409
|
PORTLESS_TLD=<tld> Use a custom TLD (e.g. test; default: localhost)
|
|
386
410
|
PORTLESS_WILDCARD=1 Allow unregistered subdomains to fall back to parent route
|
|
387
411
|
PORTLESS_SYNC_HOSTS=0 Disable auto-sync of /etc/hosts (on by default)
|
|
388
412
|
PORTLESS_TAILSCALE=1 Share apps on your Tailscale network (same as --tailscale)
|
|
389
413
|
PORTLESS_FUNNEL=1 Share apps publicly via Tailscale Funnel (same as --funnel)
|
|
414
|
+
PORTLESS_NGROK=1 Share apps publicly via ngrok (same as --ngrok)
|
|
390
415
|
PORTLESS_STATE_DIR=<path> Override the state directory
|
|
391
416
|
|
|
392
417
|
# Injected into child processes
|
|
@@ -394,6 +419,7 @@ PORT Ephemeral port the child should listen on
|
|
|
394
419
|
HOST Usually 127.0.0.1 (omitted for Expo in LAN mode)
|
|
395
420
|
PORTLESS_URL Public URL (e.g. https://myapp.localhost)
|
|
396
421
|
PORTLESS_TAILSCALE_URL Tailscale URL of the app (when --tailscale is active)
|
|
422
|
+
PORTLESS_NGROK_URL ngrok URL of the app (when --ngrok is active)
|
|
397
423
|
NODE_EXTRA_CA_CERTS Path to the portless CA (when HTTPS is active)
|
|
398
424
|
```
|
|
399
425
|
|
|
@@ -460,6 +486,8 @@ Portless detects this misconfiguration and responds with `508 Loop Detected` alo
|
|
|
460
486
|
|
|
461
487
|
This repo is a pnpm workspace monorepo using [Turborepo](https://turbo.build). The publishable package lives in `packages/portless/`.
|
|
462
488
|
|
|
489
|
+
Use Node.js 24+ and pnpm 11 for repository development. The `.node-version` file pins the Node major for version managers.
|
|
490
|
+
|
|
463
491
|
```bash
|
|
464
492
|
pnpm install # Install all dependencies
|
|
465
493
|
pnpm build # Build all packages
|
|
@@ -472,6 +500,7 @@ pnpm format # Format all files with Prettier
|
|
|
472
500
|
|
|
473
501
|
## Requirements
|
|
474
502
|
|
|
475
|
-
- Node.js
|
|
503
|
+
- Node.js 24+
|
|
476
504
|
- macOS, Linux, or Windows
|
|
477
505
|
- Tailscale CLI (optional, for `--tailscale` and `--funnel`)
|
|
506
|
+
- ngrok CLI (optional, for `--ngrok`)
|
|
@@ -920,10 +920,28 @@ var RouteStore = class _RouteStore {
|
|
|
920
920
|
const routes = this.loadRoutes(true);
|
|
921
921
|
const route = routes.find((r) => r.hostname === hostname);
|
|
922
922
|
if (!route) return;
|
|
923
|
-
if (
|
|
924
|
-
|
|
925
|
-
route.
|
|
926
|
-
|
|
923
|
+
if ("tailscaleUrl" in fields) {
|
|
924
|
+
if (fields.tailscaleUrl === null) delete route.tailscaleUrl;
|
|
925
|
+
else if (fields.tailscaleUrl !== void 0) route.tailscaleUrl = fields.tailscaleUrl;
|
|
926
|
+
}
|
|
927
|
+
if ("tailscaleHttpsPort" in fields) {
|
|
928
|
+
if (fields.tailscaleHttpsPort === null) delete route.tailscaleHttpsPort;
|
|
929
|
+
else if (fields.tailscaleHttpsPort !== void 0)
|
|
930
|
+
route.tailscaleHttpsPort = fields.tailscaleHttpsPort;
|
|
931
|
+
}
|
|
932
|
+
if ("tailscaleFunnel" in fields) {
|
|
933
|
+
if (fields.tailscaleFunnel === null) delete route.tailscaleFunnel;
|
|
934
|
+
else if (fields.tailscaleFunnel !== void 0)
|
|
935
|
+
route.tailscaleFunnel = fields.tailscaleFunnel;
|
|
936
|
+
}
|
|
937
|
+
if ("ngrokUrl" in fields) {
|
|
938
|
+
if (fields.ngrokUrl === null) delete route.ngrokUrl;
|
|
939
|
+
else if (fields.ngrokUrl !== void 0) route.ngrokUrl = fields.ngrokUrl;
|
|
940
|
+
}
|
|
941
|
+
if ("ngrokPid" in fields) {
|
|
942
|
+
if (fields.ngrokPid === null) delete route.ngrokPid;
|
|
943
|
+
else if (fields.ngrokPid !== void 0) route.ngrokPid = fields.ngrokPid;
|
|
944
|
+
}
|
|
927
945
|
this.saveRoutes(routes);
|
|
928
946
|
} finally {
|
|
929
947
|
this.releaseLock();
|