@hamedb89/localghost 0.1.6 → 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 +202 -22
- package/apps/macos-widget/LocalghostWidget.entitlements +12 -0
- package/apps/macos-widget/LocalghostWidget.swift +871 -0
- package/apps/macos-widget/Resources/localghost-logo-source.png +0 -0
- package/apps/macos-widget/Resources/localghost-widget-ui-reference.png +0 -0
- package/apps/macos-widget/Shared/LocalghostWidgetSnapshot.swift +50 -0
- package/apps/macos-widget/WidgetExtension/LocalghostDesktopWidget.swift +176 -0
- package/apps/macos-widget/WidgetExtension/LocalghostDesktopWidgetExtension.entitlements +12 -0
- package/apps/macos-widget/build.sh +60 -0
- package/apps/macos-widget/project.yml +40 -0
- package/assets/ghost-tunnel-app-icon.png +0 -0
- package/assets/ghost-tunnel-portal.png +0 -0
- package/assets/ghost-tunnel-wordmark.png +0 -0
- package/dist/cli.js +731 -91
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +138 -5
- package/dist/index.js +833 -37
- package/dist/index.js.map +1 -1
- package/dist/tunnel-DzfLXZ8O.d.ts +126 -0
- package/dist/vite.d.ts +5 -1
- package/dist/vite.js +717 -43
- package/dist/vite.js.map +1 -1
- package/docs/flows.md +19 -0
- package/docs/ghost-tunnel.md +249 -0
- package/docs/github.md +6 -6
- package/docs/localghost.1.md +28 -7
- package/docs/macos-widget.md +108 -0
- package/package.json +8 -2
- package/dist/config-Cde1Bich.d.ts +0 -31
package/README.md
CHANGED
|
@@ -9,9 +9,9 @@ Buh. Friendly local hostnames for app repos.
|
|
|
9
9
|
[](https://github.com/hamedb89/localghost/actions/workflows/ci.yml)
|
|
10
10
|
[](https://github.com/hamedb89/localghost/actions/workflows/pages.yml)
|
|
11
11
|
[](https://github.com/hamedb89/localghost/actions/workflows/publish-npm.yml)
|
|
12
|
-
[](https://www.npmjs.com/package/@hamedb89/localghost)
|
|
13
13
|
|
|
14
|
-
Localghost is a tiny Node.js CLI for friendly local domains in app repos.
|
|
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
|
-
##
|
|
44
|
+
## Start Here
|
|
44
45
|
|
|
45
46
|
```sh
|
|
46
47
|
yarn add -D @hamedb89/localghost
|
|
47
48
|
```
|
|
48
49
|
|
|
49
|
-
|
|
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
|
-
|
|
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
|
|
94
|
+
yarn dev
|
|
55
95
|
```
|
|
56
96
|
|
|
57
|
-
|
|
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,35 +126,41 @@ Run: brew install caddy
|
|
|
78
126
|
Localghost will not install it for you. No surprise spells.
|
|
79
127
|
```
|
|
80
128
|
|
|
81
|
-
|
|
129
|
+
Prepare or repair the machine setup directly:
|
|
82
130
|
|
|
83
131
|
```sh
|
|
84
|
-
yarn localghost
|
|
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
|
|
138
|
+
yarn localghost status --ready
|
|
91
139
|
```
|
|
92
140
|
|
|
93
|
-
|
|
141
|
+
Run only the proxy when your app server is started separately:
|
|
94
142
|
|
|
95
143
|
```sh
|
|
96
|
-
yarn localghost
|
|
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
|
|
150
|
+
yarn localghost dev --https
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Trust Caddy's local HTTPS CA when you want browsers to stop showing local certificate warnings:
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
yarn localghost trust
|
|
103
157
|
```
|
|
104
158
|
|
|
105
159
|
Reset generated setup without deleting `.localghost`:
|
|
106
160
|
|
|
107
161
|
```sh
|
|
108
|
-
yarn localghost
|
|
109
|
-
yarn localghost
|
|
162
|
+
yarn localghost reset
|
|
163
|
+
yarn localghost setup
|
|
110
164
|
```
|
|
111
165
|
|
|
112
166
|
Prefer `.localhost` names. `.local` is supported, but Localghost warns because `.local` can collide with mDNS/Bonjour.
|
|
@@ -146,6 +200,7 @@ The Vite plugin accepts the same shape through `fileName`, `configFiles`, or `co
|
|
|
146
200
|
"localghost:proxy:https": "localghost dev --https",
|
|
147
201
|
"localghost:run": "localghost run --",
|
|
148
202
|
"localghost:ready": "localghost status --ready",
|
|
203
|
+
"localghost:trust": "localghost trust",
|
|
149
204
|
"localghost:ps": "localghost ps",
|
|
150
205
|
"localghost:print": "localghost print",
|
|
151
206
|
"localghost:routes": "localghost routes",
|
|
@@ -165,8 +220,7 @@ A full app might compose them with its own servers:
|
|
|
165
220
|
```json
|
|
166
221
|
{
|
|
167
222
|
"scripts": {
|
|
168
|
-
"dev": "localghost run --
|
|
169
|
-
"dev:dynamic": "localghost run --dynamic-port -- vite",
|
|
223
|
+
"dev": "localghost run -- yarn dev:raw",
|
|
170
224
|
"dev:raw": "vite"
|
|
171
225
|
}
|
|
172
226
|
}
|
|
@@ -177,7 +231,7 @@ In Turborepo, let Localghost wrap the dev runner and keep dev uncached:
|
|
|
177
231
|
```json
|
|
178
232
|
{
|
|
179
233
|
"scripts": {
|
|
180
|
-
"dev": "localghost run --
|
|
234
|
+
"dev": "localghost run -- yarn dev:raw",
|
|
181
235
|
"dev:raw": "turbo dev"
|
|
182
236
|
}
|
|
183
237
|
}
|
|
@@ -193,7 +247,117 @@ Then keep persistent dev tasks uncached:
|
|
|
193
247
|
}
|
|
194
248
|
```
|
|
195
249
|
|
|
196
|
-
`localghost run` resolves one shared Localghost context, starts Caddy, passes the selected port to the child command through `LOCALGHOST_PORT` and `VITE_PORT`, and stops Caddy when the child exits.
|
|
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.
|
|
251
|
+
|
|
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:
|
|
253
|
+
|
|
254
|
+
```js
|
|
255
|
+
import { defineLocalghostConfig } from "@hamedb89/localghost";
|
|
256
|
+
|
|
257
|
+
export default defineLocalghostConfig({
|
|
258
|
+
https: true
|
|
259
|
+
});
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Then the daily script can stay small:
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"scripts": {
|
|
267
|
+
"dev": "localghost run -- yarn dev:raw",
|
|
268
|
+
"dev:raw": "vite"
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
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`.
|
|
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
|
+
```
|
|
197
361
|
|
|
198
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:
|
|
199
363
|
|
|
@@ -209,6 +373,19 @@ app run: vite
|
|
|
209
373
|
|
|
210
374
|
Pass `--json` when another helper, such as a menu bar app, needs to poll the same state.
|
|
211
375
|
|
|
376
|
+
## macOS Widget
|
|
377
|
+
|
|
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.
|
|
379
|
+
|
|
380
|
+
Build it from source:
|
|
381
|
+
|
|
382
|
+
```sh
|
|
383
|
+
npm run build
|
|
384
|
+
npm run macos:widget:build
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
The app bundle is written to `dist/LocalghostWidget.app`. See [docs/macos-widget.md](./docs/macos-widget.md) for local development notes.
|
|
388
|
+
|
|
212
389
|
## Vite
|
|
213
390
|
|
|
214
391
|
```ts
|
|
@@ -219,14 +396,13 @@ export default defineConfig({
|
|
|
219
396
|
plugins: [
|
|
220
397
|
localGhostPlugin({
|
|
221
398
|
port: 5173,
|
|
222
|
-
dynamicPort: true,
|
|
223
399
|
configFiles: [".localghost.private", ".localghost"]
|
|
224
400
|
})
|
|
225
401
|
]
|
|
226
402
|
});
|
|
227
403
|
```
|
|
228
404
|
|
|
229
|
-
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.
|
|
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.
|
|
230
406
|
|
|
231
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.
|
|
232
408
|
|
|
@@ -253,6 +429,7 @@ localghost setup
|
|
|
253
429
|
localghost setup --project app
|
|
254
430
|
localghost setup --config .localghost.preview
|
|
255
431
|
localghost setup --https
|
|
432
|
+
localghost trust
|
|
256
433
|
localghost status
|
|
257
434
|
localghost status --ready
|
|
258
435
|
localghost ps
|
|
@@ -263,7 +440,8 @@ localghost teardown --remove-caddyfile
|
|
|
263
440
|
localghost update
|
|
264
441
|
localghost --no-update-check doctor
|
|
265
442
|
localghost run -- vite
|
|
266
|
-
localghost run --
|
|
443
|
+
localghost run --trust -- vite
|
|
444
|
+
localghost run --dynamic-port=no -- vite
|
|
267
445
|
localghost dev --config-pattern '^\.localghost\.'
|
|
268
446
|
localghost dev --https
|
|
269
447
|
localghost print
|
|
@@ -273,6 +451,8 @@ Localghost checks npm for newer releases after successful commands. The check ha
|
|
|
273
451
|
|
|
274
452
|
`setup`, `dev`, and `teardown` refuse to run in production-like environments such as `NODE_ENV=production`, `VERCEL_ENV=production`, or `LOCALGHOST_ENV=production`.
|
|
275
453
|
|
|
454
|
+
When HTTPS is enabled, `localghost dev` and `localghost run` ask once whether to trust Caddy's local HTTPS CA. If you accept, macOS may ask for your password so Caddy can add its local CA to Keychain. Localghost records the result in `ops/local/localghost-state.json`; use `localghost trust` or `localghost run --trust -- ...` when you want to rerun the trust step intentionally.
|
|
455
|
+
|
|
276
456
|
`setup` writes only a managed block in the system hosts file:
|
|
277
457
|
|
|
278
458
|
```txt
|
|
@@ -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>
|