@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 +158 -27
- package/apps/macos-widget/LocalghostWidget.entitlements +12 -0
- package/apps/macos-widget/LocalghostWidget.swift +701 -48
- 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 +8 -1
- 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 +524 -63
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +130 -5
- package/dist/index.js +761 -31
- package/dist/index.js.map +1 -1
- package/dist/tunnel-DzfLXZ8O.d.ts +126 -0
- package/dist/vite.d.ts +3 -1
- package/dist/vite.js +655 -36
- 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 +10 -4
- package/docs/macos-widget.md +68 -6
- package/package.json +6 -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,41 +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
|
|
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
|
|
156
|
+
yarn localghost trust
|
|
109
157
|
```
|
|
110
158
|
|
|
111
159
|
Reset generated setup without deleting `.localghost`:
|
|
112
160
|
|
|
113
161
|
```sh
|
|
114
|
-
yarn localghost
|
|
115
|
-
yarn localghost
|
|
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 --
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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 --
|
|
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>
|