Fix --serve: strip Vite base path, add --port/--host flags

This commit is contained in:
Hermes Agent
2026-05-04 16:05:26 +00:00
parent 6d4a01dcd7
commit 80c0a23bbd
3 changed files with 67 additions and 10 deletions
+8 -4
View File
@@ -49,10 +49,12 @@ The terminal clears, enters an alternate screen buffer, and displays the QR fram
### Start the web app preview server
```bash
qr-stream --serve
qr-stream --serve start web app preview server
qr-stream --serve --port 8080 custom port
qr-stream --serve --host 127.0.0.1 localhost only
```
Serves the built web UI on `http://localhost:3000` (default). Change the port with the `PORT` environment variable:
Serves the built web UI. Defaults to port 3000 and binds `0.0.0.0`. Change the port with the `--port` flag or `PORT` environment variable:
```bash
PORT=8080 qr-stream --serve
@@ -64,8 +66,10 @@ The server resolves the `dist/` directory automatically, so it works from both t
| Flag | Description |
|------|-------------|
| `-h`, `--help` | Show usage information |
| `-s`, `--serve` | Start the web preview server |
| `-h`, `--help` | Show usage information |
| `-s`, `--serve` | Start the web preview server |
| `--port <n>` | TCP port for --serve (default: 3000, also: PORT env) |
| `--host <ip>` | Bind address for --serve (default: 0.0.0.0) |
---
+30 -2
View File
@@ -32,6 +32,9 @@ import {
} from './terminal_raster';
import { startServer } from './static_server';
/** Must match the `base` in vite.config.ts — assets are baked with this prefix. */
const VITE_BASE = '/hermes-web-demos/qr-transfer/';
const FPS_MS = 100;
// ─────────────────────────────────────────────────────────────────────────────────
@@ -48,6 +51,10 @@ Usage:
bunx qr-stream [file] via bunx
qr-stream --serve start web app preview server
Server flags (with --serve):
--port <n> TCP port (default: 3000, also: PORT env)
--host <ip> Bind address (default: 0.0.0.0)
Controls:
q, Q quit
Ctrl-C quit
@@ -163,8 +170,29 @@ function main() {
}
if (args.includes('--serve') || args.includes('-s')) {
const port = Number(process.env.PORT) || 3000;
const server = startServer(port);
// Parse --port <n> (default 3000)
let port = 3000;
const portIdx = args.indexOf('--port');
if (portIdx !== -1 && portIdx + 1 < args.length) {
port = Number(args[portIdx + 1]);
if (isNaN(port) || port < 1 || port > 65535) {
console.error('Error: --port must be a number between 1 and 65535');
process.exit(1);
}
}
// Also support PORT env var (overridable by --port)
if (process.env.PORT && args.indexOf('--port') === -1) {
port = Number(process.env.PORT);
}
// Parse --host <ip> (default 0.0.0.0)
let host: string | undefined;
const hostIdx = args.indexOf('--host');
if (hostIdx !== -1 && hostIdx + 1 < args.length) {
host = args[hostIdx + 1];
}
const server = startServer(port, host, VITE_BASE);
function shutdown() {
console.log('\nShutting down server...');
+29 -4
View File
@@ -45,8 +45,24 @@ function findWebRoot(): string {
);
}
export function startServer(port: number): Server {
/**
* Create a static HTTP server for the built web app.
*
* @param port TCP port to listen on
* @param host Host address to bind to (default: '0.0.0.0')
* @param base URL path prefix baked into the assets (e.g. '/my-subpath/'), or empty for root
*/
export function startServer(
port: number,
host?: string,
base?: string,
): Server {
const root = findWebRoot();
const basePath = base || '';
// Normalise: ensure base starts with / and does not end with /
const normalisedBase = basePath
? '/' + basePath.replace(/^\/+|\/+$/g, '')
: '';
const server = createServer((req, res) => {
let pathname = req.url ?? '/';
@@ -54,8 +70,16 @@ export function startServer(port: number): Server {
const qIdx = pathname.indexOf('?');
if (qIdx !== -1) pathname = pathname.slice(0, qIdx);
// Strip base prefix if present (so /base/assets/foo.js → /assets/foo.js)
let resolved = pathname;
if (normalisedBase && pathname.startsWith(normalisedBase + '/')) {
resolved = pathname.slice(normalisedBase.length);
} else if (normalisedBase === pathname) {
resolved = '/';
}
// Security: prevent directory traversal
const safePath = pathname.replace(/\.{2,}/g, '');
const safePath = resolved.replace(/\.{2,}/g, '');
let filePath = join(root, safePath);
if (!existsSync(filePath) || !filePath.startsWith(root)) {
@@ -92,8 +116,9 @@ export function startServer(port: number): Server {
res.end(content);
});
server.listen(port, () => {
console.log(`QR Stream web app serving at http://localhost:${port}`);
server.listen(port, host ?? '0.0.0.0', () => {
const addr = host ?? '0.0.0.0';
console.log(`QR Stream web app serving at http://${addr}:${port}`);
});
return server;