Skip to content

Commit 47c9982

Browse files
committed
Remove Postbox shortcut
1 parent c0afcf2 commit 47c9982

3 files changed

Lines changed: 18 additions & 185 deletions

File tree

README.md

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,6 @@ Lizardtail is a Tailscale Serve wrapper around other commands. It runs a command
66
lizardtail pnpm dev
77
```
88

9-
For Pi Postbox, use the built-in shortcut:
10-
11-
```bash
12-
lizardtail postbox
13-
```
14-
159
```text
1610
Local: http://localhost:5173
1711
@@ -24,7 +18,6 @@ Use it when your dev server is running on a remote machine and you want to open
2418
## Features
2519

2620
- Runs any command you pass it, such as `pnpm dev`, `npm run dev`, `bun run dev`, or `python -m http.server`.
27-
- Supports `lizardtail postbox` as a shortcut that launches `pi-postbox-server`, detects its actual printed local port, and exposes that port.
2821
- Streams the child command's stdout/stderr normally.
2922
- Detects common dev-server output formats, including `http://localhost:5173`, `http://127.0.0.1:3000`, `started server on 0.0.0.0:8080`, and `PORT=4321`.
3023
- Ignores timing output like `ready in 500 ms` so it does not accidentally expose port `500`.
@@ -45,7 +38,6 @@ Use it when your dev server is running on a remote machine and you want to open
4538

4639
- Node.js 20 or newer.
4740
- Tailscale installed and available as `tailscale` on `PATH`.
48-
- For `lizardtail postbox`, `pi-postbox-server` must also be installed or linked on `PATH`.
4941
- The device must be logged into Tailscale.
5042
- Tailscale Serve must be available for the device/tailnet.
5143
- For `--public`, Tailscale Funnel must be enabled for the device/tailnet.
@@ -110,7 +102,6 @@ node dist/index.js pnpm dev
110102
```bash
111103
lizardtail [options] -- <command> [args...]
112104
lizardtail [options] <command> [args...]
113-
lizardtail postbox [pi-postbox-server args...]
114105
lizardtail help [topic]
115106
lizardtail config init
116107
```
@@ -145,25 +136,16 @@ These commands are meant for both humans and coding agents: they describe usage,
145136

146137
## Examples
147138

148-
### Pi Postbox
149-
150-
```bash
151-
lizardtail postbox
152-
```
153-
154-
This starts `pi-postbox-server` with `--host 127.0.0.1` and preferred `--port 3000`, passes any extra arguments after `postbox` through to `pi-postbox-server`, waits for the server's printed listening URL, and exposes the detected actual port privately to your tailnet. If Postbox falls back because port `3000` is busy, lizardtail exposes the fallback port.
139+
### Vite / frontend dev server
155140

156141
```bash
157-
lizardtail postbox --database ~/.pi-postbox/postbox.sqlite
158-
lizardtail --public postbox
142+
lizardtail pnpm dev
159143
```
160144

161-
With this shortcut, `lizardtail --port 3333 postbox` passes `3333` as Postbox's preferred local port and still detects the actual port before exposing.
162-
163-
### Vite / frontend dev server
145+
If the frontend dev server proxies API requests to a backend, expose the frontend port so the proxied backend remains reachable from the remote browser:
164146

165147
```bash
166-
lizardtail pnpm dev
148+
lizardtail --port 5173 npm run dev
167149
```
168150

169151
If Vite is configured to bind to another host:

src/index.ts

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ Options:
7373
-h, --help Show this help.
7474
7575
Examples:
76-
lizardtail postbox
7776
lizardtail pnpm dev
77+
lizardtail --port 5173 npm run dev
7878
lizardtail --port 3000 npm run dev
7979
lizardtail --tailscale-port 8450 pnpm dev
8080
lizardtail --public pnpm dev
@@ -112,12 +112,16 @@ If a config file exists, its blockedPorts list replaces the built-in default lis
112112
Run a dev server command, detect its local port, and expose it through Tailscale.
113113
114114
Common usage:
115-
lizardtail postbox
116115
lizardtail pnpm dev
116+
lizardtail --port 5173 npm run dev
117117
lizardtail --port 3000 npm run dev
118118
lizardtail --tailscale-port 8450 pnpm dev
119119
lizardtail --public pnpm dev
120120
121+
For an app whose frontend dev server proxies to a backend (e.g. a Vite SPA proxying
122+
/api), expose the frontend port with --port so its proxied backend is reachable too:
123+
lizardtail --port 5173 npm run dev
124+
121125
Private vs public:
122126
Default: private tailnet-only Tailscale Serve.
123127
--public / --funnel: public internet exposure through Tailscale Funnel.
@@ -131,9 +135,6 @@ Safety:
131135
Help topics:
132136
lizardtail help config
133137
134-
Postbox shortcut:
135-
lizardtail postbox Run pi-postbox-server with local defaults, detect its actual port, and expose it.
136-
137138
Other commands:
138139
lizardtail config init Write a starter config file.
139140
`);
@@ -287,25 +288,6 @@ export function parseArgs(argv: string[]): Options {
287288
return options;
288289
}
289290

290-
function commandArgsIncludeOption(args: string[], name: string): boolean {
291-
return args.some((arg) => arg === name || arg.startsWith(`${name}=`));
292-
}
293-
294-
export function applyPostboxAlias(options: Options): Options {
295-
if (options.command[0] !== "postbox") return options;
296-
297-
const extraArgs = options.command.slice(1);
298-
const postboxArgs: string[] = [];
299-
if (!commandArgsIncludeOption(extraArgs, "--host")) postboxArgs.push("--host", options.host);
300-
if (!commandArgsIncludeOption(extraArgs, "--port")) postboxArgs.push("--port", String(options.port ?? 3000));
301-
302-
const { port: _preferredPostboxPort, ...detectionOptions } = options;
303-
return {
304-
...detectionOptions,
305-
command: ["pi-postbox-server", ...postboxArgs, ...extraArgs],
306-
};
307-
}
308-
309291
function parsePort(value: string): number {
310292
const port = Number(value);
311293
if (!Number.isInteger(port) || port < 1 || port > 65_535) {
@@ -767,9 +749,7 @@ export async function main(): Promise<void> {
767749
const argv = process.argv.slice(2);
768750
if (await handleMetaCommand(argv)) return;
769751

770-
const parsedOptions = parseArgs(argv);
771-
const postboxAlias = parsedOptions.command[0] === "postbox";
772-
const options = applyPostboxAlias(parsedOptions);
752+
const options = parseArgs(argv);
773753
const config = await loadConfig();
774754
const [command, ...args] = options.command;
775755
const tailscaleDnsName = await getTailscaleDnsName().catch(() => undefined);
@@ -899,8 +879,8 @@ export async function main(): Promise<void> {
899879
});
900880

901881
child.on("error", (error) => {
902-
if (postboxAlias && (error as NodeJS.ErrnoException).code === "ENOENT") {
903-
console.error("lizardtail: failed to start pi-postbox-server: not found on PATH. Install or link @pi-postbox/server so the pi-postbox-server binary is available.");
882+
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
883+
console.error(`lizardtail: failed to start ${command}: command not found on PATH.`);
904884
} else {
905885
console.error(`lizardtail: failed to start ${command}: ${error.message}`);
906886
}

tests/index.test.ts

Lines changed: 5 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import path from "node:path";
66
import { fileURLToPath } from "node:url";
77
import { test } from "node:test";
88

9-
import { DEFAULT_TIMEOUT_MS, applyPostboxAlias, detectLaravelViteServers, detectPortFromText, exposeWithTailscale, parseArgs, stripAnsi } from "../src/index.ts";
9+
import { DEFAULT_TIMEOUT_MS, detectLaravelViteServers, detectPortFromText, exposeWithTailscale, parseArgs, stripAnsi } from "../src/index.ts";
1010

1111
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
1212
const cliPath = path.join(repoRoot, "dist", "index.js");
@@ -45,7 +45,7 @@ test("detectPortFromText ignores invalid ports, missing ports, build durations,
4545
assert.equal(detectPortFromText("VITE v8.0.13 ready in 500 ms"), undefined);
4646
assert.equal(detectPortFromText("port 3000 is already in use"), undefined);
4747
assert.equal(detectPortFromText("Error: listen EADDRINUSE: address already in use http://127.0.0.1:3000"), undefined);
48-
assert.equal(detectPortFromText("port 3000 is already in use\npi-postbox-server listening on http://127.0.0.1:3001"), 3001);
48+
assert.equal(detectPortFromText("port 3000 is already in use\nmy-server listening on http://127.0.0.1:3001"), 3001);
4949
});
5050

5151
// Laravel's `composer run dev` commonly runs Vite and `php artisan serve` together.
@@ -102,34 +102,6 @@ test("parseArgs uses documented defaults", () => {
102102
});
103103
});
104104

105-
test("applyPostboxAlias expands lizardtail postbox to pi-postbox-server defaults and keeps detection enabled", () => {
106-
assert.deepEqual(applyPostboxAlias(parseArgs(["--port", "3333", "postbox", "--database", ":memory:"])), {
107-
command: ["pi-postbox-server", "--host", "127.0.0.1", "--port", "3333", "--database", ":memory:"],
108-
host: "127.0.0.1",
109-
timeoutMs: DEFAULT_TIMEOUT_MS,
110-
openCheck: true,
111-
public: false,
112-
});
113-
});
114-
115-
test("applyPostboxAlias preserves explicit Postbox host and port overrides", () => {
116-
assert.deepEqual(applyPostboxAlias(parseArgs(["postbox", "--port", "3333", "--host=localhost"])), {
117-
command: ["pi-postbox-server", "--port", "3333", "--host=localhost"],
118-
host: "127.0.0.1",
119-
timeoutMs: DEFAULT_TIMEOUT_MS,
120-
openCheck: true,
121-
public: false,
122-
});
123-
124-
assert.deepEqual(applyPostboxAlias(parseArgs(["postbox", "--port=4444", "--host", "localhost"])), {
125-
command: ["pi-postbox-server", "--port=4444", "--host", "localhost"],
126-
host: "127.0.0.1",
127-
timeoutMs: DEFAULT_TIMEOUT_MS,
128-
openCheck: true,
129-
public: false,
130-
});
131-
});
132-
133105
test("parseArgs supports explicit Tailscale HTTPS ports", () => {
134106
assert.deepEqual(parseArgs(["--tailscale-port", "8450", "--vite-tailscale-port", "8453", "pnpm", "dev"]), {
135107
command: ["pnpm", "dev"],
@@ -172,28 +144,10 @@ test("CLI help command prints detailed documentation", async () => {
172144
assert.match(stdout, /lizardtail config init/);
173145
});
174146

175-
test("CLI help documents the Postbox shortcut", async () => {
176-
const child = spawn(process.execPath, [cliPath, "help"], {
177-
cwd: repoRoot,
178-
stdio: ["ignore", "pipe", "pipe"],
179-
});
180-
181-
let stdout = "";
182-
child.stdout.setEncoding("utf8");
183-
child.stdout.on("data", (chunk: string) => {
184-
stdout += chunk;
185-
});
186-
187-
const exitCode = await new Promise<number | null>((resolve) => child.on("exit", resolve));
188-
189-
assert.equal(exitCode, 0);
190-
assert.match(stdout, /lizardtail postbox/);
191-
});
192-
193-
test("CLI prints an actionable error when the Postbox binary is missing", async () => {
147+
test("CLI prints an actionable error when the command is missing from PATH", async () => {
194148
const tempDir = await mkdtemp(path.join(tmpdir(), "lizardtail-test-"));
195149
try {
196-
const child = spawn(process.execPath, [cliPath, "postbox"], {
150+
const child = spawn(process.execPath, [cliPath, "definitely-not-a-real-command"], {
197151
cwd: repoRoot,
198152
env: { ...process.env, PATH: tempDir },
199153
stdio: ["ignore", "pipe", "pipe"],
@@ -208,7 +162,7 @@ test("CLI prints an actionable error when the Postbox binary is missing", async
208162
const exitCode = await new Promise<number | null>((resolve) => child.on("exit", resolve));
209163

210164
assert.equal(exitCode, 1);
211-
assert.match(stderr, /failed to start pi-postbox-server: not found on PATH/);
165+
assert.match(stderr, /failed to start definitely-not-a-real-command: command not found on PATH/);
212166
} finally {
213167
await rm(tempDir, { recursive: true, force: true });
214168
}
@@ -615,86 +569,3 @@ server.listen(0, "127.0.0.1", () => {
615569
}
616570
});
617571

618-
test("CLI postbox shortcut starts pi-postbox-server and exposes its detected port", async () => {
619-
const tempDir = await mkdtemp(path.join(tmpdir(), "lizardtail-postbox-test-"));
620-
await writeHostCommandStubs(tempDir);
621-
const tailscalePath = path.join(tempDir, "tailscale");
622-
const tailscaleLog = path.join(tempDir, "tailscale.log");
623-
const postboxPath = path.join(tempDir, "pi-postbox-server");
624-
const postboxLog = path.join(tempDir, "postbox.log");
625-
626-
await writeFile(
627-
tailscalePath,
628-
`#!/usr/bin/env bash
629-
printf '%s\n' "$*" >> "$TAILSCALE_LOG"
630-
if [ "$1" = "status" ] && [ "$2" = "--json" ]; then
631-
echo '{"Self":{"DNSName":"test-host.tailnet.ts.net.","TailscaleIPs":["100.64.0.1"]}}'
632-
exit 0
633-
fi
634-
if [ "$1" = "status" ]; then
635-
echo 'ok'
636-
exit 0
637-
fi
638-
if [ "$1" = "serve" ]; then
639-
echo 'serve ok'
640-
exit 0
641-
fi
642-
exit 1
643-
`,
644-
{ mode: 0o755 },
645-
);
646-
647-
await writeFile(
648-
postboxPath,
649-
`#!/usr/bin/env bash
650-
printf '%s\n' "$*" >> "$POSTBOX_LOG"
651-
node -e 'const http = require("http");
652-
const server = http.createServer((req, res) => res.end("ok"));
653-
server.listen(0, "127.0.0.1", () => {
654-
console.log("pi-postbox-server listening on http://127.0.0.1:" + server.address().port);
655-
setTimeout(() => server.close(), 2500);
656-
});'
657-
`,
658-
{ mode: 0o755 },
659-
);
660-
661-
try {
662-
const child = spawn(process.execPath, [cliPath, "--timeout", "5000", "postbox", "--database", ":memory:"], {
663-
cwd: repoRoot,
664-
env: {
665-
...process.env,
666-
PATH: `${tempDir}${path.delimiter}${process.env.PATH ?? ""}`,
667-
TAILSCALE_LOG: tailscaleLog,
668-
POSTBOX_LOG: postboxLog,
669-
},
670-
stdio: ["ignore", "pipe", "pipe"],
671-
});
672-
673-
let stdout = "";
674-
let stderr = "";
675-
child.stdout.setEncoding("utf8");
676-
child.stderr.setEncoding("utf8");
677-
child.stdout.on("data", (chunk: string) => {
678-
stdout += chunk;
679-
});
680-
child.stderr.on("data", (chunk: string) => {
681-
stderr += chunk;
682-
});
683-
684-
const exitCode = await new Promise<number | null>((resolve) => child.on("exit", resolve));
685-
686-
assert.equal(exitCode, 0, `stdout:\n${stdout}\nstderr:\n${stderr}`);
687-
assert.match(stdout, /pi-postbox-server listening on http:\/\/127\.0\.0\.1:\d+/);
688-
assert.match(stderr, /lizardtail: detected local server on http:\/\/127\.0\.0\.1:\d+/);
689-
assert.match(stderr, /lizardtail: serving via Tailscale: https:\/\/test-host\.tailnet\.ts\.net/);
690-
691-
const postboxCall = await readFile(postboxLog, "utf8");
692-
assert.match(postboxCall, /--host 127\.0\.0\.1 --port 3000 --database :memory:/);
693-
694-
const tailscaleCalls = await readFile(tailscaleLog, "utf8");
695-
assert.match(tailscaleCalls, /serve --bg --https 8443 http:\/\/127\.0\.0\.1:\d+/);
696-
assert.match(tailscaleCalls, /serve --https=8443 off/);
697-
} finally {
698-
await rm(tempDir, { recursive: true, force: true });
699-
}
700-
});

0 commit comments

Comments
 (0)