mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-13 18:07:29 +00:00
Compare commits
11 Commits
main
...
fix/networ
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b53eb6874 | ||
|
|
22f2b92215 | ||
|
|
03269a33f5 | ||
|
|
e644f5cb7d | ||
|
|
f41383f3a6 | ||
|
|
ea844b110c | ||
|
|
1995a411d2 | ||
|
|
468ab599be | ||
|
|
f3b25bbbab | ||
|
|
0049ff51ee | ||
|
|
6fa552f157 |
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
# Backend server port (Express API + WebSocket server)
|
# Backend server port (Express API + WebSocket server)
|
||||||
#API server
|
#API server
|
||||||
PORT=3001
|
SERVER_PORT=3001
|
||||||
#Frontend port
|
#Frontend port
|
||||||
VITE_PORT=5173
|
VITE_PORT=5173
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
npx @siteboon/claude-code-ui
|
npx @siteboon/claude-code-ui
|
||||||
```
|
```
|
||||||
|
|
||||||
サーバーが起動し、`http://localhost:3001`(または設定した PORT)でアクセスできます。
|
サーバーが起動し、`http://localhost:3001`(または設定した SERVER_PORT)でアクセスできます。
|
||||||
|
|
||||||
**再起動**: サーバーを停止した後、同じ `npx` コマンドを再度実行するだけです
|
**再起動**: サーバーを停止した後、同じ `npx` コマンドを再度実行するだけです
|
||||||
### グローバルインストール(定期的に使用する場合)
|
### グローバルインストール(定期的に使用する場合)
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
npx @siteboon/claude-code-ui
|
npx @siteboon/claude-code-ui
|
||||||
```
|
```
|
||||||
|
|
||||||
서버가 시작되면 `http://localhost:3001` (또는 설정한 PORT)에서 접근할 수 있습니다.
|
서버가 시작되면 `http://localhost:3001` (또는 설정한 SERVER_PORT)에서 접근할 수 있습니다.
|
||||||
|
|
||||||
**재시작**: 서버를 중지한 후 동일한 `npx` 명령을 다시 실행하면 됩니다
|
**재시작**: 서버를 중지한 후 동일한 `npx` 명령을 다시 실행하면 됩니다
|
||||||
### 전역 설치 (정기적 사용 시)
|
### 전역 설치 (정기적 사용 시)
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
npx @siteboon/claude-code-ui
|
npx @siteboon/claude-code-ui
|
||||||
```
|
```
|
||||||
|
|
||||||
服务器将启动并可通过 `http://localhost:3001`(或您配置的 PORT)访问。
|
服务器将启动并可通过 `http://localhost:3001`(或您配置的 SERVER_PORT)访问。
|
||||||
|
|
||||||
**重启**: 停止服务器后只需再次运行相同的 `npx` 命令
|
**重启**: 停止服务器后只需再次运行相同的 `npx` 命令
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "concurrently --kill-others \"npm run server\" \"npm run client\"",
|
"dev": "concurrently --kill-others \"npm run server\" \"npm run client\"",
|
||||||
"server": "node server/index.js",
|
"server": "node server/index.js",
|
||||||
"client": "vite --host",
|
"client": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"typecheck": "tsc --noEmit -p tsconfig.json",
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ function showStatus() {
|
|||||||
|
|
||||||
// Environment variables
|
// Environment variables
|
||||||
console.log(`\n${c.info('[INFO]')} Configuration:`);
|
console.log(`\n${c.info('[INFO]')} Configuration:`);
|
||||||
console.log(` PORT: ${c.bright(process.env.PORT || '3001')} ${c.dim(process.env.PORT ? '' : '(default)')}`);
|
console.log(` SERVER_PORT: ${c.bright(process.env.SERVER_PORT || process.env.PORT || '3001')} ${c.dim(process.env.SERVER_PORT || process.env.PORT ? '' : '(default)')}`);
|
||||||
console.log(` DATABASE_PATH: ${c.dim(process.env.DATABASE_PATH || '(using default location)')}`);
|
console.log(` DATABASE_PATH: ${c.dim(process.env.DATABASE_PATH || '(using default location)')}`);
|
||||||
console.log(` CLAUDE_CLI_PATH: ${c.dim(process.env.CLAUDE_CLI_PATH || 'claude (default)')}`);
|
console.log(` CLAUDE_CLI_PATH: ${c.dim(process.env.CLAUDE_CLI_PATH || 'claude (default)')}`);
|
||||||
console.log(` CONTEXT_WINDOW: ${c.dim(process.env.CONTEXT_WINDOW || '160000 (default)')}`);
|
console.log(` CONTEXT_WINDOW: ${c.dim(process.env.CONTEXT_WINDOW || '160000 (default)')}`);
|
||||||
@@ -134,7 +134,7 @@ function showStatus() {
|
|||||||
console.log(` ${c.dim('>')} Use ${c.bright('cloudcli --port 8080')} to run on a custom port`);
|
console.log(` ${c.dim('>')} Use ${c.bright('cloudcli --port 8080')} to run on a custom port`);
|
||||||
console.log(` ${c.dim('>')} Use ${c.bright('cloudcli --database-path /path/to/db')} for custom database`);
|
console.log(` ${c.dim('>')} Use ${c.bright('cloudcli --database-path /path/to/db')} for custom database`);
|
||||||
console.log(` ${c.dim('>')} Run ${c.bright('cloudcli help')} for all options`);
|
console.log(` ${c.dim('>')} Run ${c.bright('cloudcli help')} for all options`);
|
||||||
console.log(` ${c.dim('>')} Access the UI at http://localhost:${process.env.PORT || '3001'}\n`);
|
console.log(` ${c.dim('>')} Access the UI at http://localhost:${process.env.SERVER_PORT || process.env.PORT || '3001'}\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show help
|
// Show help
|
||||||
@@ -169,7 +169,8 @@ Examples:
|
|||||||
$ cloudcli status # Show configuration
|
$ cloudcli status # Show configuration
|
||||||
|
|
||||||
Environment Variables:
|
Environment Variables:
|
||||||
PORT Set server port (default: 3001)
|
SERVER_PORT Set server port (default: 3001)
|
||||||
|
PORT Set server port (default: 3001) (LEGACY)
|
||||||
DATABASE_PATH Set custom database location
|
DATABASE_PATH Set custom database location
|
||||||
CLAUDE_CLI_PATH Set custom Claude CLI path
|
CLAUDE_CLI_PATH Set custom Claude CLI path
|
||||||
CONTEXT_WINDOW Set context window size (default: 160000)
|
CONTEXT_WINDOW Set context window size (default: 160000)
|
||||||
@@ -260,9 +261,9 @@ function parseArgs(args) {
|
|||||||
const arg = args[i];
|
const arg = args[i];
|
||||||
|
|
||||||
if (arg === '--port' || arg === '-p') {
|
if (arg === '--port' || arg === '-p') {
|
||||||
parsed.options.port = args[++i];
|
parsed.options.serverPort = args[++i];
|
||||||
} else if (arg.startsWith('--port=')) {
|
} else if (arg.startsWith('--port=')) {
|
||||||
parsed.options.port = arg.split('=')[1];
|
parsed.options.serverPort = arg.split('=')[1];
|
||||||
} else if (arg === '--database-path') {
|
} else if (arg === '--database-path') {
|
||||||
parsed.options.databasePath = args[++i];
|
parsed.options.databasePath = args[++i];
|
||||||
} else if (arg.startsWith('--database-path=')) {
|
} else if (arg.startsWith('--database-path=')) {
|
||||||
@@ -285,8 +286,10 @@ async function main() {
|
|||||||
const { command, options } = parseArgs(args);
|
const { command, options } = parseArgs(args);
|
||||||
|
|
||||||
// Apply CLI options to environment variables
|
// Apply CLI options to environment variables
|
||||||
if (options.port) {
|
if (options.serverPort) {
|
||||||
process.env.PORT = options.port;
|
process.env.SERVER_PORT = options.serverPort;
|
||||||
|
} else if (!process.env.SERVER_PORT && process.env.PORT) {
|
||||||
|
process.env.SERVER_PORT = process.env.PORT;
|
||||||
}
|
}
|
||||||
if (options.databasePath) {
|
if (options.databasePath) {
|
||||||
process.env.DATABASE_PATH = options.databasePath;
|
process.env.DATABASE_PATH = options.databasePath;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const c = {
|
|||||||
dim: (text) => `${colors.dim}${text}${colors.reset}`,
|
dim: (text) => `${colors.dim}${text}${colors.reset}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('PORT from env:', process.env.PORT);
|
console.log('SERVER_PORT from env:', process.env.SERVER_PORT);
|
||||||
|
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { WebSocketServer, WebSocket } from 'ws';
|
import { WebSocketServer, WebSocket } from 'ws';
|
||||||
@@ -70,6 +70,7 @@ import { initializeDatabase, sessionNamesDb, applyCustomSessionNames } from './d
|
|||||||
import { configureWebPush } from './services/vapid-keys.js';
|
import { configureWebPush } from './services/vapid-keys.js';
|
||||||
import { validateApiKey, authenticateToken, authenticateWebSocket } from './middleware/auth.js';
|
import { validateApiKey, authenticateToken, authenticateWebSocket } from './middleware/auth.js';
|
||||||
import { IS_PLATFORM } from './constants/config.js';
|
import { IS_PLATFORM } from './constants/config.js';
|
||||||
|
import { getConnectableHost } from '../shared/networkHosts.js';
|
||||||
|
|
||||||
const VALID_PROVIDERS = ['claude', 'codex', 'cursor', 'gemini'];
|
const VALID_PROVIDERS = ['claude', 'codex', 'cursor', 'gemini'];
|
||||||
|
|
||||||
@@ -2403,7 +2404,8 @@ app.get('*', (req, res) => {
|
|||||||
res.sendFile(indexPath);
|
res.sendFile(indexPath);
|
||||||
} else {
|
} else {
|
||||||
// In development, redirect to Vite dev server only if dist doesn't exist
|
// In development, redirect to Vite dev server only if dist doesn't exist
|
||||||
res.redirect(`http://localhost:${process.env.VITE_PORT || 5173}`);
|
const redirectHost = getConnectableHost(req.hostname);
|
||||||
|
res.redirect(`${req.protocol}://${redirectHost}:${VITE_PORT}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2491,10 +2493,10 @@ async function getFileTree(dirPath, maxDepth = 3, currentDepth = 0, showHidden =
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const PORT = process.env.PORT || 3001;
|
const SERVER_PORT = process.env.SERVER_PORT || 3001;
|
||||||
const HOST = process.env.HOST || '0.0.0.0';
|
const HOST = process.env.HOST || '0.0.0.0';
|
||||||
// Show localhost in URL when binding to all interfaces (0.0.0.0 isn't a connectable address)
|
const DISPLAY_HOST = getConnectableHost(HOST);
|
||||||
const DISPLAY_HOST = HOST === '0.0.0.0' ? 'localhost' : HOST;
|
const VITE_PORT = process.env.VITE_PORT || 5173;
|
||||||
|
|
||||||
// Initialize database and start server
|
// Initialize database and start server
|
||||||
async function startServer() {
|
async function startServer() {
|
||||||
@@ -2511,13 +2513,15 @@ async function startServer() {
|
|||||||
|
|
||||||
// Log Claude implementation mode
|
// Log Claude implementation mode
|
||||||
console.log(`${c.info('[INFO]')} Using Claude Agents SDK for Claude integration`);
|
console.log(`${c.info('[INFO]')} Using Claude Agents SDK for Claude integration`);
|
||||||
console.log(`${c.info('[INFO]')} Running in ${c.bright(isProduction ? 'PRODUCTION' : 'DEVELOPMENT')} mode`);
|
console.log('');
|
||||||
|
|
||||||
if (!isProduction) {
|
if (isProduction) {
|
||||||
console.log(`${c.warn('[WARN]')} Note: Requests will be proxied to Vite dev server at ${c.dim('http://localhost:' + (process.env.VITE_PORT || 5173))}`);
|
console.log(`${c.info('[INFO]')} To run in production mode, go to http://${DISPLAY_HOST}:${SERVER_PORT}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
server.listen(PORT, HOST, async () => {
|
console.log(`${c.info('[INFO]')} To run in development mode with hot-module replacement, go to http://${DISPLAY_HOST}:${VITE_PORT}`);
|
||||||
|
|
||||||
|
server.listen(SERVER_PORT, HOST, async () => {
|
||||||
const appInstallPath = path.join(__dirname, '..');
|
const appInstallPath = path.join(__dirname, '..');
|
||||||
|
|
||||||
console.log('');
|
console.log('');
|
||||||
@@ -2525,7 +2529,7 @@ async function startServer() {
|
|||||||
console.log(` ${c.bright('Claude Code UI Server - Ready')}`);
|
console.log(` ${c.bright('Claude Code UI Server - Ready')}`);
|
||||||
console.log(c.dim('═'.repeat(63)));
|
console.log(c.dim('═'.repeat(63)));
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log(`${c.info('[INFO]')} Server URL: ${c.bright('http://' + DISPLAY_HOST + ':' + PORT)}`);
|
console.log(`${c.info('[INFO]')} Server URL: ${c.bright('http://' + DISPLAY_HOST + ':' + SERVER_PORT)}`);
|
||||||
console.log(`${c.info('[INFO]')} Installed at: ${c.dim(appInstallPath)}`);
|
console.log(`${c.info('[INFO]')} Installed at: ${c.dim(appInstallPath)}`);
|
||||||
console.log(`${c.tip('[TIP]')} Run "cloudcli status" for full configuration details`);
|
console.log(`${c.tip('[TIP]')} Run "cloudcli status" for full configuration details`);
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|||||||
@@ -529,7 +529,7 @@ router.get('/next/:projectName', async (req, res) => {
|
|||||||
|
|
||||||
// Fallback to loading tasks and finding next one locally
|
// Fallback to loading tasks and finding next one locally
|
||||||
// Use localhost to bypass proxy for internal server-to-server calls
|
// Use localhost to bypass proxy for internal server-to-server calls
|
||||||
const tasksResponse = await fetch(`http://localhost:${process.env.PORT || 3001}/api/taskmaster/tasks/${encodeURIComponent(projectName)}`, {
|
const tasksResponse = await fetch(`http://localhost:${process.env.SERVER_PORT || process.env.PORT || '3001'}/api/taskmaster/tasks/${encodeURIComponent(projectName)}`, {
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': req.headers.authorization
|
'Authorization': req.headers.authorization
|
||||||
}
|
}
|
||||||
|
|||||||
22
shared/networkHosts.js
Normal file
22
shared/networkHosts.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
export function isWildcardHost(host) {
|
||||||
|
return host === '0.0.0.0' || host === '::';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isLoopbackHost(host) {
|
||||||
|
return host === 'localhost' || host === '127.0.0.1' || host === '::1' || host === '[::1]';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeLoopbackHost(host) {
|
||||||
|
if (!host) {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
return isLoopbackHost(host) ? 'localhost' : host;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use localhost for connectable loopback and wildcard addresses in browser-facing URLs.
|
||||||
|
export function getConnectableHost(host) {
|
||||||
|
if (!host) {
|
||||||
|
return 'localhost';
|
||||||
|
}
|
||||||
|
return isWildcardHost(host) || isLoopbackHost(host) ? 'localhost' : host;
|
||||||
|
}
|
||||||
@@ -1,15 +1,21 @@
|
|||||||
import { defineConfig, loadEnv } from 'vite'
|
import { defineConfig, loadEnv } from 'vite'
|
||||||
import react from '@vitejs/plugin-react'
|
import react from '@vitejs/plugin-react'
|
||||||
|
import { getConnectableHost, normalizeLoopbackHost } from './shared/networkHosts.js'
|
||||||
|
|
||||||
export default defineConfig(({ command, mode }) => {
|
export default defineConfig(({ mode }) => {
|
||||||
// Load env file based on `mode` in the current working directory.
|
// Load env file based on `mode` in the current working directory.
|
||||||
const env = loadEnv(mode, process.cwd(), '')
|
const env = loadEnv(mode, process.cwd(), '')
|
||||||
|
|
||||||
const host = env.HOST || '0.0.0.0'
|
const configuredHost = env.HOST || '0.0.0.0'
|
||||||
// When binding to all interfaces (0.0.0.0), proxy should connect to localhost
|
// if the host is not a loopback address, it should be used directly.
|
||||||
// Otherwise, proxy to the specific host the backend is bound to
|
// This allows the vite server to EXPOSE all interfaces when the host
|
||||||
const proxyHost = host === '0.0.0.0' ? 'localhost' : host
|
// is set to '0.0.0.0' or '::', while still using 'localhost' for browser
|
||||||
const port = env.PORT || 3001
|
// URLs and proxy targets.
|
||||||
|
const host = normalizeLoopbackHost(configuredHost)
|
||||||
|
|
||||||
|
const proxyHost = getConnectableHost(configuredHost)
|
||||||
|
// TODO: Remove support for legacy PORT variables in all locations in a future major release, leaving only SERVER_PORT.
|
||||||
|
const serverPort = env.SERVER_PORT || env.PORT || 3001
|
||||||
|
|
||||||
return {
|
return {
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
@@ -17,13 +23,13 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
host,
|
host,
|
||||||
port: parseInt(env.VITE_PORT) || 5173,
|
port: parseInt(env.VITE_PORT) || 5173,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': `http://${proxyHost}:${port}`,
|
'/api': `http://${proxyHost}:${serverPort}`,
|
||||||
'/ws': {
|
'/ws': {
|
||||||
target: `ws://${proxyHost}:${port}`,
|
target: `ws://${proxyHost}:${serverPort}`,
|
||||||
ws: true
|
ws: true
|
||||||
},
|
},
|
||||||
'/shell': {
|
'/shell': {
|
||||||
target: `ws://${proxyHost}:${port}`,
|
target: `ws://${proxyHost}:${serverPort}`,
|
||||||
ws: true
|
ws: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user