From 68ff0588091af75c6f2fb2ae49442e34b7b11f60 Mon Sep 17 00:00:00 2001 From: mrwuliu Date: Fri, 13 Mar 2026 12:51:12 +0800 Subject: [PATCH 1/3] feat: add configurable port and switch to IPv4 by default - Add PTY_WEB_PORT env var for fixed port (0 = random) - Change default hostname from ::1 (IPv6) to 127.0.0.1 (IPv4) - Update README with new environment variable --- README.md | 3 ++- src/web/server/server.ts | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f91e0a4..06b7ff9 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,8 @@ This eliminates the need for polling—perfect for long-running processes like b | Variable | Default | Description | | ---------------------- | ---------- | -------------------------------------------------- | | `PTY_MAX_BUFFER_LINES` | `50000` | Maximum lines to keep in output buffer per session | -| `PTY_WEB_HOSTNAME` | `::1` | Hostname for the web server to bind to | +| `PTY_WEB_HOSTNAME` | `127.0.0.1`| Hostname for the web server to bind to (IPv4) | +| `PTY_WEB_PORT` | `0` (random) | Port for the web server (0 = random port) | ### Permissions diff --git a/src/web/server/server.ts b/src/web/server/server.ts index 4391751..a50beca 100644 --- a/src/web/server/server.ts +++ b/src/web/server/server.ts @@ -41,8 +41,8 @@ export class PTYServer implements Disposable { private startWebServer(): Server { return Bun.serve({ - port: 0, - hostname: process.env.PTY_WEB_HOSTNAME ?? '::1', + port: process.env.PTY_WEB_PORT ? parseInt(process.env.PTY_WEB_PORT, 10) : 0, + hostname: process.env.PTY_WEB_HOSTNAME ?? '127.0.0.1', routes: { ...this.staticRoutes, From 5f2e5fef82bcff2d4ae1cde47a42a353b48ba23e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B1=9F=E6=A1=91?= <90854541+mr-wuliu@users.noreply.github.com> Date: Sat, 4 Apr 2026 03:47:31 +0800 Subject: [PATCH 2/3] test: update host expectations for IPv4 default --- test/e2e/fixtures.ts | 6 ++++-- test/web-server.test.ts | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test/e2e/fixtures.ts b/test/e2e/fixtures.ts index b3dffbe..a9467ec 100644 --- a/test/e2e/fixtures.ts +++ b/test/e2e/fixtures.ts @@ -90,12 +90,14 @@ export const test = base.extend({ const serverURL = serverURLText.trim() // Parse URL to extract port number - const urlMatch = serverURL.match(/http:\/\/\[::1\]:(\d+)/) + const urlMatch = serverURL.match(/http:\/\/(?:127\.0\.0\.1|\[::1\]):(\d+)/) if (!urlMatch || !urlMatch[1]) { throw new Error(`Invalid port file format: ${serverURL}`) } const port = parseInt(urlMatch[1], 10) - const baseURL = `http://[::1]:${port}` + const baseURL = serverURL.includes('[::1]') + ? `http://[::1]:${port}` + : `http://127.0.0.1:${port}` await waitForServer(baseURL, 15000) diff --git a/test/web-server.test.ts b/test/web-server.test.ts index 24dd4c3..49ce544 100644 --- a/test/web-server.test.ts +++ b/test/web-server.test.ts @@ -13,7 +13,7 @@ describe('Web Server', () => { it('should start server successfully', async () => { await using server = await PTYServer.createServer() const url = server.server.url - expect(url.hostname).toBe('[::1]') + expect(url.hostname).toBe('127.0.0.1') expect(url.protocol).toBe('http:') expect(url.port).not.toBe(0) expect(url.port).not.toBe(8080) // Default port should be avoided From a43426933e3fdad4b31608cd92d4fed39eec2a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B1=9F=E6=A1=91?= <90854541+mr-wuliu@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:16:53 +0800 Subject: [PATCH 3/3] fix(web): default PTY web host back to IPv6 loopback --- README.md | 2 +- src/web/server/server.ts | 2 +- test/npm-pack-integration.test.ts | 8 +++++--- test/web-server.test.ts | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 06b7ff9..2a71094 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,7 @@ This eliminates the need for polling—perfect for long-running processes like b | Variable | Default | Description | | ---------------------- | ---------- | -------------------------------------------------- | | `PTY_MAX_BUFFER_LINES` | `50000` | Maximum lines to keep in output buffer per session | -| `PTY_WEB_HOSTNAME` | `127.0.0.1`| Hostname for the web server to bind to (IPv4) | +| `PTY_WEB_HOSTNAME` | `::1` | Hostname for the web server to bind to (IPv6 loopback by default) | | `PTY_WEB_PORT` | `0` (random) | Port for the web server (0 = random port) | ### Permissions diff --git a/src/web/server/server.ts b/src/web/server/server.ts index a50beca..c8e94ed 100644 --- a/src/web/server/server.ts +++ b/src/web/server/server.ts @@ -42,7 +42,7 @@ export class PTYServer implements Disposable { private startWebServer(): Server { return Bun.serve({ port: process.env.PTY_WEB_PORT ? parseInt(process.env.PTY_WEB_PORT, 10) : 0, - hostname: process.env.PTY_WEB_HOSTNAME ?? '127.0.0.1', + hostname: process.env.PTY_WEB_HOSTNAME ?? '::1', routes: { ...this.staticRoutes, diff --git a/test/npm-pack-integration.test.ts b/test/npm-pack-integration.test.ts index 4482506..72ce0b2 100644 --- a/test/npm-pack-integration.test.ts +++ b/test/npm-pack-integration.test.ts @@ -32,6 +32,7 @@ describe('npm pack integration', () => { let tempDir: string let packFile: string | null = null let serverProcess: ReturnType | null = null + let baseURL = '' afterEach(async () => { // Cleanup server process @@ -137,12 +138,13 @@ describe('npm pack integration', () => { const port = await waitWithRetry() expect(port).not.toBe(0) + baseURL = `http://[::1]:${port}` // Wait for server to be ready let retries = 20 // 10 seconds while (retries > 0) { try { - const response = await fetch(`http://localhost:${port}/api/sessions`) + const response = await fetch(`${baseURL}/api/sessions`) if (response.ok) break } catch (error) { if (!(error instanceof DOMException) || error.name !== 'AbortError') { @@ -155,12 +157,12 @@ describe('npm pack integration', () => { expect(retries).toBeGreaterThan(0) // Server should be ready // 5) Fetch assets - const assetResponse = await fetch(`http://localhost:${port}/assets/${assetName}`) + const assetResponse = await fetch(`${baseURL}/assets/${assetName}`) expect(assetResponse.status).toBe(200) // Could add more specific checks here, like content-type or specific assets // 6) Fetch index.html and verify it's the built version - const indexResponse = await fetch(`http://localhost:${port}/`) + const indexResponse = await fetch(`${baseURL}/`) expect(indexResponse.status).toBe(200) const indexContent = await indexResponse.text() expect(indexContent).not.toContain('main.tsx') // Fails if raw HTML is served diff --git a/test/web-server.test.ts b/test/web-server.test.ts index 49ce544..24dd4c3 100644 --- a/test/web-server.test.ts +++ b/test/web-server.test.ts @@ -13,7 +13,7 @@ describe('Web Server', () => { it('should start server successfully', async () => { await using server = await PTYServer.createServer() const url = server.server.url - expect(url.hostname).toBe('127.0.0.1') + expect(url.hostname).toBe('[::1]') expect(url.protocol).toBe('http:') expect(url.port).not.toBe(0) expect(url.port).not.toBe(8080) // Default port should be avoided