@hamedb89/localghost 0.1.8 → 0.1.9

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 CHANGED
@@ -9,9 +9,9 @@ Buh. Friendly local hostnames for app repos.
9
9
  [![CI](https://github.com/hamedb89/localghost/actions/workflows/ci.yml/badge.svg)](https://github.com/hamedb89/localghost/actions/workflows/ci.yml)
10
10
  [![GitHub Pages](https://github.com/hamedb89/localghost/actions/workflows/pages.yml/badge.svg)](https://github.com/hamedb89/localghost/actions/workflows/pages.yml)
11
11
  [![Publish npm](https://github.com/hamedb89/localghost/actions/workflows/publish-npm.yml/badge.svg)](https://github.com/hamedb89/localghost/actions/workflows/publish-npm.yml)
12
- [![npm version](https://img.shields.io/badge/npm-v0.1.8-CB3837?logo=npm)](https://www.npmjs.com/package/@hamedb89/localghost)
12
+ [![npm version](https://img.shields.io/badge/npm-v0.1.9-CB3837?logo=npm)](https://www.npmjs.com/package/@hamedb89/localghost)
13
13
 
14
- Localghost is a tiny Node.js CLI for friendly local domains in app repos. It gives each project one small contract for `.localhost` hostnames, Caddy reverse proxies, Vite `allowedHosts`, and the system hosts file, so developers can open `http://app.localhost/` instead of remembering which localhost port belongs to which process.
14
+ Localghost is a tiny Node.js CLI for friendly local domains in app repos. Add it as a dev dependency, run `yarn dev`, and use `http://app.localhost/` instead of remembering which localhost port belongs to which process.
15
15
 
16
16
  [Website](https://hamedb89.github.io/localghost/) · [npm](https://www.npmjs.com/package/@hamedb89/localghost) · [GitHub](https://github.com/hamedb89/localghost)
17
17
 
@@ -31,6 +31,7 @@ Localghost is a tiny Node.js CLI for friendly local domains in app repos. It giv
31
31
 
32
32
  - CI runs typecheck, build, site build, and npm package dry-run on Node 20 and 22.
33
33
  - GitHub Pages is deployed by Actions from the checked-in `site/` and `assets/` sources.
34
+ - Preview the exact Pages artifact locally with `npm run site:serve`, then open `http://127.0.0.1:4173/`.
34
35
  - npm publish is guarded by `prepublishOnly` and the release workflow publishes with npm provenance.
35
36
  - Runtime dependencies are intentionally small: `commander` for the CLI and `execa` for process execution. Vite is an optional peer dependency for the Vite plugin.
36
37
  - No postinstall scripts, hidden Homebrew installs, or broad hosts-file rewrites.
@@ -40,21 +41,62 @@ Localghost is a tiny Node.js CLI for friendly local domains in app repos. It giv
40
41
  <img src="./assets/localghost-app-icon.png" alt="Localghost app icon" width="180">
41
42
  </p>
42
43
 
43
- ## Install
44
+ ## Start Here
44
45
 
45
46
  ```sh
46
47
  yarn add -D @hamedb89/localghost
47
48
  ```
48
49
 
49
- ## Quick Start
50
+ That is the entrypoint you are optimizing for: install the dev dependency, keep using the dev command your team already knows, and let Localghost handle the local-domain setup around it.
50
51
 
51
- Create the project config and optional package scripts:
52
+ For Vite apps, add the plugin once:
53
+
54
+ ```ts
55
+ import { defineConfig } from "vite";
56
+ import { localGhostPlugin } from "@hamedb89/localghost/vite";
57
+
58
+ export default defineConfig({
59
+ plugins: [localGhostPlugin()]
60
+ });
61
+ ```
62
+
63
+ Now your daily command is just:
64
+
65
+ ```sh
66
+ yarn dev
67
+ ```
68
+
69
+ And you are ready.
70
+
71
+ On the first interactive `yarn dev`, Localghost can create `.localghost`, explain the `/etc/hosts` change, write the local Caddyfile, and then print the browser-facing URL:
72
+
73
+ ```txt
74
+ localghost
75
+ local: http://app.localhost/
76
+ also: http://www.app.localhost/
77
+ target: http://127.0.0.1:5173/
78
+ ```
79
+
80
+ If your project does not use Vite, or you want one command that starts Caddy and then your app process, wrap your raw dev script:
81
+
82
+ ```json
83
+ {
84
+ "scripts": {
85
+ "dev": "localghost run -- yarn dev:raw",
86
+ "dev:raw": "next dev"
87
+ }
88
+ }
89
+ ```
90
+
91
+ Then the daily command stays the same:
52
92
 
53
93
  ```sh
54
- yarn localghost init --write-scripts
94
+ yarn dev
55
95
  ```
56
96
 
57
- This creates `.localghost`:
97
+ ## Configuration
98
+
99
+ Most apps only need a `.localghost` file when they want explicit hostnames or multiple services:
58
100
 
59
101
  ```txt
60
102
  # Buh. Friendly names for local services.
@@ -64,6 +106,12 @@ www.app.localhost 5173
64
106
  api.app.localhost 8787
65
107
  ```
66
108
 
109
+ If `.localghost` is missing, the Vite plugin can prompt to create it during `yarn dev`. You can also create it directly:
110
+
111
+ ```sh
112
+ yarn localghost init
113
+ ```
114
+
67
115
  Check the machine:
68
116
 
69
117
  ```sh
@@ -78,41 +126,41 @@ Run: brew install caddy
78
126
  Localghost will not install it for you. No surprise spells.
79
127
  ```
80
128
 
81
- First time on a machine:
129
+ Prepare or repair the machine setup directly:
82
130
 
83
131
  ```sh
84
- yarn localghost:setup
132
+ yarn localghost setup
85
133
  ```
86
134
 
87
135
  Check that the hosts block and Caddyfile are ready:
88
136
 
89
137
  ```sh
90
- yarn localghost:ready
138
+ yarn localghost status --ready
91
139
  ```
92
140
 
93
- Daily proxy:
141
+ Run only the proxy when your app server is started separately:
94
142
 
95
143
  ```sh
96
- yarn localghost:proxy
144
+ yarn localghost dev
97
145
  ```
98
146
 
99
147
  Use HTTPS only when you explicitly want Caddy local certificates:
100
148
 
101
149
  ```sh
102
- yarn localghost:proxy:https
150
+ yarn localghost dev --https
103
151
  ```
104
152
 
105
153
  Trust Caddy's local HTTPS CA when you want browsers to stop showing local certificate warnings:
106
154
 
107
155
  ```sh
108
- yarn localghost:trust
156
+ yarn localghost trust
109
157
  ```
110
158
 
111
159
  Reset generated setup without deleting `.localghost`:
112
160
 
113
161
  ```sh
114
- yarn localghost:reset
115
- yarn localghost:setup
162
+ yarn localghost reset
163
+ yarn localghost setup
116
164
  ```
117
165
 
118
166
  Prefer `.localhost` names. `.local` is supported, but Localghost warns because `.local` can collide with mDNS/Bonjour.
@@ -173,7 +221,6 @@ A full app might compose them with its own servers:
173
221
  {
174
222
  "scripts": {
175
223
  "dev": "localghost run -- yarn dev:raw",
176
- "dev:dynamic": "localghost run --dynamic-port -- vite",
177
224
  "dev:raw": "vite"
178
225
  }
179
226
  }
@@ -184,7 +231,7 @@ In Turborepo, let Localghost wrap the dev runner and keep dev uncached:
184
231
  ```json
185
232
  {
186
233
  "scripts": {
187
- "dev": "localghost run --dynamic-port -- yarn dev:raw",
234
+ "dev": "localghost run -- yarn dev:raw",
188
235
  "dev:raw": "turbo dev"
189
236
  }
190
237
  }
@@ -200,18 +247,15 @@ Then keep persistent dev tasks uncached:
200
247
  }
201
248
  ```
202
249
 
203
- `localghost run` resolves one shared Localghost context, starts Caddy, handles the optional HTTPS trust prompt, then starts the child command. That keeps Localghost setup/proxy output before Vite's ready log. It passes the selected port to the child command through `LOCALGHOST_PORT` and `VITE_PORT`, and stops Caddy when the child exits. With `--dynamic-port`, Localghost starts at the configured port, checks `127.0.0.1:<port>`, and walks upward until it finds a free port.
250
+ `localghost run` resolves one shared Localghost context, starts Caddy, handles the optional HTTPS trust prompt, then starts the child command. That keeps Localghost setup/proxy output before Vite's ready log. It passes the selected port to the child command through `LOCALGHOST_PORT` and `VITE_PORT`, and stops Caddy when the child exits. Dynamic ports are on by default: Localghost starts at the configured port, checks `127.0.0.1:<port>`, and walks upward until it finds a free port. Use `--dynamic-port=no` when you want strict fixed-port behavior.
204
251
 
205
- When settings need to be shared by the CLI wrapper and the Vite plugin, put them in `localghost.config.mjs`:
252
+ Most repos do not need `localghost.config.mjs`. Localghost derives `project` from `package.json`, defaults to port `5173`, keeps HTTPS off by default, enables dynamic ports by default, and adds `www.` aliases by default. Add `localghost.config.mjs` only when you want to override those defaults:
206
253
 
207
254
  ```js
208
255
  import { defineLocalghostConfig } from "@hamedb89/localghost";
209
256
 
210
257
  export default defineLocalghostConfig({
211
- project: "app",
212
- port: 5173,
213
- https: true,
214
- dynamicPort: true
258
+ https: true
215
259
  });
216
260
  ```
217
261
 
@@ -228,6 +272,93 @@ Then the daily script can stay small:
228
272
 
229
273
  `www.` aliases are enabled by default. A `.localghost` entry like `app.localhost 5173` also sets up `www.app.localhost` unless `wwwAlias: false` is set in `localghost.config.mjs`.
230
274
 
275
+ `ghostTunnel` is the production opt-in for a wildcard product entrypoint on top of your deployed Vite app. The default namespace is `<route>-<project>-<owner>.ghost.<domain>`, and omitted domains are shown as `*` in logs:
276
+
277
+ ```js
278
+ import { defineLocalghostConfig } from "@hamedb89/localghost";
279
+
280
+ export default defineLocalghostConfig({
281
+ ghostTunnel: {
282
+ domains: "socialworkouts.app",
283
+ mode: "manual"
284
+ }
285
+ });
286
+ ```
287
+
288
+ With `ghostTunnel: { domains }`, local route and Vite startup logs use local defaults for `route`, `project`, and `owner`, then fill the configured domain:
289
+
290
+ ```txt
291
+ localghost ghost tunnel
292
+ mode: manual
293
+ expected: https://app-decision-layer-hamed.ghost.socialworkouts.app/
294
+ ```
295
+
296
+ Without `domains`, the expected URL stays wildcarded:
297
+
298
+ ```txt
299
+ localghost ghost tunnel
300
+ mode: manual
301
+ expected: https://app-decision-layer-hamed.ghost.*/
302
+ ```
303
+
304
+ `ghostTunnel: "manual"` and `ghostTunnel: "public"` are shorthand modes. `manual` is the default; use `enabled: false` to keep domains/config in the file without exposing the tunnel surface.
305
+
306
+ Use object form to override defaults or provide a concrete preview URL:
307
+
308
+ ```js
309
+ export default defineLocalghostConfig({
310
+ ghostTunnel: {
311
+ domains: "socialworkouts.app",
312
+ preview: {
313
+ route: "plan",
314
+ project: "summer-base",
315
+ owner: "hamed"
316
+ }
317
+ }
318
+ });
319
+ ```
320
+
321
+ Production apps can read that flag without requiring the local `.localghost` file, then construct and validate tunnel URLs:
322
+
323
+ ```ts
324
+ import {
325
+ assertSecureGhostTunnelRequest,
326
+ constructGhostTunnelUrl,
327
+ readLocalghostProjectConfig
328
+ } from "@hamedb89/localghost";
329
+
330
+ const { config } = await readLocalghostProjectConfig();
331
+ const url = constructGhostTunnelUrl({
332
+ domain: "socialworkouts.app",
333
+ route: "plan",
334
+ project: "summer-base",
335
+ owner: "hamed",
336
+ ghostTunnel: config.ghostTunnel
337
+ });
338
+
339
+ const route = assertSecureGhostTunnelRequest({
340
+ host: request.headers.get("host") ?? "",
341
+ domain: "socialworkouts.app",
342
+ protocol: "https",
343
+ authenticated: Boolean(session),
344
+ ghostTunnel: config.ghostTunnel
345
+ });
346
+ ```
347
+
348
+ That constructs `https://plan-summer-base-hamed.ghost.socialworkouts.app/`, validates the same host shape, requires HTTPS by default, and requires the app to confirm auth by default. See [Ghost Tunnel](./docs/ghost-tunnel.md) for the production DNS and routing flow.
349
+
350
+ When `ghostTunnel.preview` is configured, local route and Vite startup logs include the concrete URL as `expected: https://plan-summer-base-hamed.ghost.socialworkouts.app/`. In an interactive Vite terminal, press `g` to show the Ghost Tunnel configuration and open a numbered concrete URL. Wildcard `*` URLs are shown for clarity, but the menu only opens configured concrete domains.
351
+
352
+ Relay guardrails are private-by-default: public requests never choose the local target, route registration requires a matching local-agent bearer token, signed route claims are exact/scoped/expiring, and default targets are limited to `localhost`, `127.0.0.1`, and `::1` with dangerous ports blocked. The package exports relay helpers for registration, target validation, header stripping, log redaction, limits, and safe offline responses.
353
+
354
+ Local security checks:
355
+
356
+ ```sh
357
+ npm test
358
+ npm run test:cli
359
+ npm run test:coverage
360
+ ```
361
+
231
362
  `localghost dev` and `localghost run` also register their active sessions in a user-local activity file. Use `localghost ps` to see the Localghost apps currently running on the machine:
232
363
 
233
364
  ```txt
@@ -244,7 +375,7 @@ Pass `--json` when another helper, such as a menu bar app, needs to poll the sam
244
375
 
245
376
  ## macOS Widget
246
377
 
247
- Localghost includes a tiny native macOS menu-bar widget in `apps/macos-widget`. It shows `LG n` in the top bar, where `n` is the number of active Localghost-managed apps, and its menu lists each project, route, target port, and listening state.
378
+ Localghost includes a tiny native macOS widget in `apps/macos-widget`. One widget tracks all active Localghost sessions across the machine. It shows `LG n` in the top bar and opens a small floating desktop panel with the Localghost route list, target ports, and listening state.
248
379
 
249
380
  Build it from source:
250
381
 
@@ -271,7 +402,7 @@ export default defineConfig({
271
402
  });
272
403
  ```
273
404
 
274
- The plugin binds Vite to `127.0.0.1` by default, prints the selected Localghost domain, generates an explicit `server.allowedHosts` list from the selected config file, and does not set `allowedHosts: true`. It runs only during local `vite serve`; production/build mode no-ops. When `dynamicPort` is enabled in plugin options or `localghost.config.mjs`, the plugin uses the configured port when available and otherwise moves to the next free port before Vite starts.
405
+ The plugin binds Vite to `127.0.0.1` by default, prints the selected Localghost domain, generates an explicit `server.allowedHosts` list from the selected config file, and does not set `allowedHosts: true`. It runs only during local `vite serve`; production/build mode no-ops. Dynamic ports are enabled by default, so the plugin uses the configured port when available and otherwise moves to the next free port before Vite starts. Set `dynamicPort: false` when strict fixed-port behavior matters.
275
406
 
276
407
  If `.localghost` is missing and Vite is running in an interactive terminal, the plugin asks whether to create one, prompts for the primary domain and optional extra domains, and then asks whether to run setup. Before touching `/etc/hosts`, it explains why macOS may ask for your password and confirms that only Localghost's managed block is changed.
277
408
 
@@ -310,7 +441,7 @@ localghost update
310
441
  localghost --no-update-check doctor
311
442
  localghost run -- vite
312
443
  localghost run --trust -- vite
313
- localghost run --dynamic-port -- turbo dev
444
+ localghost run --dynamic-port=no -- vite
314
445
  localghost dev --config-pattern '^\.localghost\.'
315
446
  localghost dev --https
316
447
  localghost print
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>com.apple.security.app-sandbox</key>
6
+ <true/>
7
+ <key>com.apple.security.application-groups</key>
8
+ <array>
9
+ <string>group.app.localghost</string>
10
+ </array>
11
+ </dict>
12
+ </plist>