diff --git a/CLAUDE.md b/CLAUDE.md index 40664601..48e1b9f5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -166,6 +166,7 @@ Use `resolveModelString()` from `@automaker/model-resolver` to convert model ali ## Environment Variables - `ANTHROPIC_API_KEY` - Anthropic API key (or use Claude Code CLI auth) +- `HOST` - Host to bind server to (default: 0.0.0.0) - `PORT` - Server port (default: 3008) - `DATA_DIR` - Data storage directory (default: ./data) - `ALLOWED_ROOT_DIRECTORY` - Restrict file operations to specific directory diff --git a/apps/server/.env.example b/apps/server/.env.example index 68b28395..6ac27145 100644 --- a/apps/server/.env.example +++ b/apps/server/.env.example @@ -44,6 +44,11 @@ CORS_ORIGIN=http://localhost:3007 # OPTIONAL - Server # ============================================ +# Host to bind the server to (default: 0.0.0.0) +# Use 0.0.0.0 to listen on all interfaces (recommended for Docker/remote access) +# Use 127.0.0.1 or localhost to restrict to local connections only +HOST=0.0.0.0 + # Port to run the server on PORT=3008 diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index e6f9d0d2..c6e440c8 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -88,6 +88,7 @@ import { getEventHistoryService } from './services/event-history-service.js'; dotenv.config(); const PORT = parseInt(process.env.PORT || '3008', 10); +const HOST = process.env.HOST || '0.0.0.0'; const DATA_DIR = process.env.DATA_DIR || './data'; const ENABLE_REQUEST_LOGGING_DEFAULT = process.env.ENABLE_REQUEST_LOGGING !== 'false'; // Default to true @@ -609,22 +610,24 @@ terminalWss.on('connection', (ws: WebSocket, req: import('http').IncomingMessage }); // Start server with error handling for port conflicts -const startServer = (port: number) => { - server.listen(port, () => { +const startServer = (port: number, host: string) => { + server.listen(port, host, () => { const terminalStatus = isTerminalEnabled() ? isTerminalPasswordRequired() ? 'enabled (password protected)' : 'enabled' : 'disabled'; const portStr = port.toString().padEnd(4); + const hostDisplay = host === '0.0.0.0' ? 'localhost' : host; logger.info(` ╔═══════════════════════════════════════════════════════╗ ║ Automaker Backend Server ║ ╠═══════════════════════════════════════════════════════╣ -║ HTTP API: http://localhost:${portStr} ║ -║ WebSocket: ws://localhost:${portStr}/api/events ║ -║ Terminal: ws://localhost:${portStr}/api/terminal/ws ║ -║ Health: http://localhost:${portStr}/api/health ║ +║ Listening: ${host}:${port}${' '.repeat(Math.max(0, 34 - host.length - port.toString().length))}║ +║ HTTP API: http://${hostDisplay}:${portStr} ║ +║ WebSocket: ws://${hostDisplay}:${portStr}/api/events ║ +║ Terminal: ws://${hostDisplay}:${portStr}/api/terminal/ws ║ +║ Health: http://${hostDisplay}:${portStr}/api/health ║ ║ Terminal: ${terminalStatus.padEnd(37)}║ ╚═══════════════════════════════════════════════════════╝ `); @@ -658,7 +661,7 @@ const startServer = (port: number) => { }); }; -startServer(PORT); +startServer(PORT, HOST); // Global error handlers to prevent crashes from uncaught errors process.on('unhandledRejection', (reason: unknown, _promise: Promise) => { diff --git a/apps/ui/vite.config.mts b/apps/ui/vite.config.mts index e40bce49..71a70cda 100644 --- a/apps/ui/vite.config.mts +++ b/apps/ui/vite.config.mts @@ -65,6 +65,7 @@ export default defineConfig(({ command }) => { }, }, server: { + host: process.env.HOST || '0.0.0.0', port: parseInt(process.env.TEST_PORT || '3007', 10), }, build: { diff --git a/package-lock.json b/package-lock.json index dd96e672..fc03ffe8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ }, "apps/server": { "name": "@automaker/server", - "version": "0.10.0", + "version": "0.11.0", "license": "SEE LICENSE IN LICENSE", "dependencies": { "@anthropic-ai/claude-agent-sdk": "0.1.76", @@ -80,7 +80,7 @@ }, "apps/ui": { "name": "@automaker/ui", - "version": "0.10.0", + "version": "0.11.0", "hasInstallScript": true, "license": "SEE LICENSE IN LICENSE", "dependencies": { @@ -8956,9 +8956,9 @@ } }, "node_modules/diff": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", - "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", + "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -10811,9 +10811,9 @@ } }, "node_modules/hono": { - "version": "4.11.3", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.3.tgz", - "integrity": "sha512-PmQi306+M/ct/m5s66Hrg+adPnkD5jiO6IjA7WhWw0gSBSo1EcRegwuI1deZ+wd5pzCGynCcn2DprnE4/yEV4w==", + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.4.tgz", + "integrity": "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==", "license": "MIT", "peer": true, "engines": {