portless 0.11.1 → 0.13.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 +54 -1
- package/dist/{chunk-RRH2JUIU.js → chunk-3WLVQXFE.js} +24 -1
- package/dist/cli.js +1024 -86
- package/dist/index.d.ts +8 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -249,6 +249,18 @@ portless trust
|
|
|
249
249
|
|
|
250
250
|
On Linux, `portless trust` supports Debian/Ubuntu, Arch, Fedora/RHEL/CentOS, and openSUSE (via `update-ca-certificates` or `update-ca-trust`). On Windows, it uses `certutil` to add the CA to the system trust store.
|
|
251
251
|
|
|
252
|
+
## Start at OS startup
|
|
253
|
+
|
|
254
|
+
Install the proxy as an OS startup service so clean HTTPS URLs are available after reboot without starting the proxy from a terminal:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
portless service install
|
|
258
|
+
portless service status
|
|
259
|
+
portless service uninstall
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The service uses portless defaults: HTTPS on port 443 with `.localhost` names. 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
|
+
|
|
252
264
|
## LAN mode
|
|
253
265
|
|
|
254
266
|
```bash
|
|
@@ -276,6 +288,36 @@ LAN mode depends on the system mDNS tools that portless already spawns: macOS sh
|
|
|
276
288
|
|
|
277
289
|
- **Expo / React Native**: portless always injects `--port`. React Native also gets `--host 127.0.0.1`. Expo gets `--host localhost` outside LAN mode, but in LAN mode portless leaves Metro on its default LAN host behavior instead of forcing `--host` or `HOST`.
|
|
278
290
|
|
|
291
|
+
## Tailscale sharing
|
|
292
|
+
|
|
293
|
+
Share your dev server with teammates on your [Tailscale](https://tailscale.com) network:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
portless myapp --tailscale next dev
|
|
297
|
+
# -> https://myapp.localhost (local)
|
|
298
|
+
# -> https://devbox.yourteam.ts.net (tailnet)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Each `--tailscale` app is root-mounted on its own Tailscale HTTPS port, so no framework `basePath` configuration is needed. The first app gets port 443, subsequent apps get 8443, 8444, etc.
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
portless myapp --tailscale next dev # -> https://devbox.ts.net
|
|
305
|
+
portless api --tailscale pnpm start # -> https://devbox.ts.net:8443
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Use `--funnel` to expose your dev server to the public internet via [Tailscale Funnel](https://tailscale.com/kb/1223/funnel/):
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
portless myapp --funnel next dev
|
|
312
|
+
# -> https://devbox.yourteam.ts.net (public)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Tailscale HTTPS certificates must be enabled before `--tailscale` or `--funnel` can register HTTPS URLs. Funnel must also be enabled for the tailnet and node before `--funnel` can register the public URL. If either setting is missing, portless exits before starting the child process.
|
|
316
|
+
|
|
317
|
+
Set `PORTLESS_TAILSCALE=1` in your shell profile or `.env` to share every app by default. `portless list` shows both local and tailnet URLs. Tailscale serve registrations are cleaned up automatically when the app exits.
|
|
318
|
+
|
|
319
|
+
Requires the Tailscale CLI to be installed and connected (`tailscale up`), with Tailscale HTTPS certificates enabled.
|
|
320
|
+
|
|
279
321
|
## Commands
|
|
280
322
|
|
|
281
323
|
```bash
|
|
@@ -304,6 +346,11 @@ portless proxy start -p 1355 # Start on a custom port (no sudo)
|
|
|
304
346
|
portless proxy start --foreground # Start in foreground (for debugging)
|
|
305
347
|
portless proxy start --wildcard # Allow unregistered subdomains to fall back to parent
|
|
306
348
|
portless proxy stop # Stop the proxy
|
|
349
|
+
|
|
350
|
+
# OS startup service
|
|
351
|
+
portless service install # Start HTTPS proxy when the OS starts
|
|
352
|
+
portless service status # Show service and proxy status
|
|
353
|
+
portless service uninstall # Remove the startup service
|
|
307
354
|
```
|
|
308
355
|
|
|
309
356
|
### Options
|
|
@@ -321,6 +368,8 @@ portless proxy stop # Stop the proxy
|
|
|
321
368
|
--wildcard Allow unregistered subdomains to fall back to parent route
|
|
322
369
|
--script <name> Run a specific package.json script (default: dev)
|
|
323
370
|
--app-port <number> Use a fixed port for the app (skip auto-assignment)
|
|
371
|
+
--tailscale Share the app on your Tailscale network (tailnet)
|
|
372
|
+
--funnel Share the app publicly via Tailscale Funnel
|
|
324
373
|
--force Kill the existing process and take over its route
|
|
325
374
|
--name <name> Use <name> as the app name
|
|
326
375
|
```
|
|
@@ -336,16 +385,19 @@ PORTLESS_LAN=1 Enable LAN mode when set to 1 (auto-detects LAN
|
|
|
336
385
|
PORTLESS_TLD=<tld> Use a custom TLD (e.g. test; default: localhost)
|
|
337
386
|
PORTLESS_WILDCARD=1 Allow unregistered subdomains to fall back to parent route
|
|
338
387
|
PORTLESS_SYNC_HOSTS=0 Disable auto-sync of /etc/hosts (on by default)
|
|
388
|
+
PORTLESS_TAILSCALE=1 Share apps on your Tailscale network (same as --tailscale)
|
|
389
|
+
PORTLESS_FUNNEL=1 Share apps publicly via Tailscale Funnel (same as --funnel)
|
|
339
390
|
PORTLESS_STATE_DIR=<path> Override the state directory
|
|
340
391
|
|
|
341
392
|
# Injected into child processes
|
|
342
393
|
PORT Ephemeral port the child should listen on
|
|
343
394
|
HOST Usually 127.0.0.1 (omitted for Expo in LAN mode)
|
|
344
395
|
PORTLESS_URL Public URL (e.g. https://myapp.localhost)
|
|
396
|
+
PORTLESS_TAILSCALE_URL Tailscale URL of the app (when --tailscale is active)
|
|
345
397
|
NODE_EXTRA_CA_CERTS Path to the portless CA (when HTTPS is active)
|
|
346
398
|
```
|
|
347
399
|
|
|
348
|
-
> **Reserved names:** `run`, `get`, `alias`, `hosts`, `list`, `trust`, `clean`, `prune`, and `
|
|
400
|
+
> **Reserved names:** `run`, `get`, `alias`, `hosts`, `list`, `trust`, `clean`, `prune`, `proxy`, and `service` are subcommands and cannot be used as app names directly. Use `portless run <cmd>` to infer the name from your project, or `portless --name <name> <cmd>` to force any name including reserved ones.
|
|
349
401
|
|
|
350
402
|
## Uninstall / reset
|
|
351
403
|
|
|
@@ -422,3 +474,4 @@ pnpm format # Format all files with Prettier
|
|
|
422
474
|
|
|
423
475
|
- Node.js 20+
|
|
424
476
|
- macOS, Linux, or Windows
|
|
477
|
+
- Tailscale CLI (optional, for `--tailscale` and `--funnel`)
|
|
@@ -846,7 +846,8 @@ var RouteStore = class _RouteStore {
|
|
|
846
846
|
}
|
|
847
847
|
}
|
|
848
848
|
const filtered = routes.filter((r) => r.hostname !== hostname);
|
|
849
|
-
|
|
849
|
+
const entry = { hostname, port, pid };
|
|
850
|
+
filtered.push(entry);
|
|
850
851
|
this.saveRoutes(filtered);
|
|
851
852
|
} finally {
|
|
852
853
|
this.releaseLock();
|
|
@@ -906,6 +907,28 @@ var RouteStore = class _RouteStore {
|
|
|
906
907
|
this.releaseLock();
|
|
907
908
|
}
|
|
908
909
|
}
|
|
910
|
+
/**
|
|
911
|
+
* Update metadata on an existing route entry. Only provided fields are
|
|
912
|
+
* merged; the route must already exist (matched by hostname).
|
|
913
|
+
*/
|
|
914
|
+
updateRoute(hostname, fields) {
|
|
915
|
+
this.ensureDir();
|
|
916
|
+
if (!this.acquireLock()) {
|
|
917
|
+
throw new Error("Failed to acquire route lock");
|
|
918
|
+
}
|
|
919
|
+
try {
|
|
920
|
+
const routes = this.loadRoutes(true);
|
|
921
|
+
const route = routes.find((r) => r.hostname === hostname);
|
|
922
|
+
if (!route) return;
|
|
923
|
+
if (fields.tailscaleUrl !== void 0) route.tailscaleUrl = fields.tailscaleUrl;
|
|
924
|
+
if (fields.tailscaleHttpsPort !== void 0)
|
|
925
|
+
route.tailscaleHttpsPort = fields.tailscaleHttpsPort;
|
|
926
|
+
if (fields.tailscaleFunnel !== void 0) route.tailscaleFunnel = fields.tailscaleFunnel;
|
|
927
|
+
this.saveRoutes(routes);
|
|
928
|
+
} finally {
|
|
929
|
+
this.releaseLock();
|
|
930
|
+
}
|
|
931
|
+
}
|
|
909
932
|
removeRoute(hostname) {
|
|
910
933
|
this.ensureDir();
|
|
911
934
|
if (!this.acquireLock()) {
|