From 80c0a23bbdac9f17fddbb28d8f0bf9c605fc39cc Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Mon, 4 May 2026 16:05:26 +0000 Subject: [PATCH] Fix --serve: strip Vite base path, add --port/--host flags --- README.md | 12 ++++++++---- src/cli/qr-stream.ts | 32 ++++++++++++++++++++++++++++++-- src/cli/static_server.ts | 33 +++++++++++++++++++++++++++++---- 3 files changed, 67 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index bb1eb8d..96b79fa 100644 --- a/README.md +++ b/README.md @@ -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 ` | TCP port for --serve (default: 3000, also: PORT env) | +| `--host ` | Bind address for --serve (default: 0.0.0.0) | --- diff --git a/src/cli/qr-stream.ts b/src/cli/qr-stream.ts index 07b3a77..9f23822 100644 --- a/src/cli/qr-stream.ts +++ b/src/cli/qr-stream.ts @@ -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 TCP port (default: 3000, also: PORT env) + --host 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 (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 (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...'); diff --git a/src/cli/static_server.ts b/src/cli/static_server.ts index f0e3d5c..a774327 100644 --- a/src/cli/static_server.ts +++ b/src/cli/static_server.ts @@ -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;