feat: add Termux/proot environment compatibility (#457)
- Add init system detection (systemd/launchd/windows-service/other) - Automatically use "gateway run" mode for environments without service managers (WSL/Docker/Termux/proot) - Add safeNetworkInterfaces() wrapper to handle uv_interface_addresses permission errors in proot - Prevents ERR_SYSTEM_ERROR (errno 13) when os.networkInterfaces() fails in restricted environments Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,18 @@ process.on('unhandledRejection', (reason) => {
|
|||||||
let server: any = null
|
let server: any = null
|
||||||
let chatRunServer: any = null
|
let chatRunServer: any = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全获取网络接口信息(兼容 Termux/proot 环境)
|
||||||
|
* 在 proot 环境中 os.networkInterfaces() 会抛出权限错误(errno 13)
|
||||||
|
*/
|
||||||
|
function safeNetworkInterfaces() {
|
||||||
|
try {
|
||||||
|
return os.networkInterfaces()
|
||||||
|
} catch {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function bootstrap() {
|
export async function bootstrap() {
|
||||||
console.log(`hermes-web-ui v${APP_VERSION} starting...`)
|
console.log(`hermes-web-ui v${APP_VERSION} starting...`)
|
||||||
await mkdir(config.uploadDir, { recursive: true })
|
await mkdir(config.uploadDir, { recursive: true })
|
||||||
@@ -123,7 +135,7 @@ export async function bootstrap() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
server.on('listening', () => {
|
server.on('listening', () => {
|
||||||
const interfaces = os.networkInterfaces()
|
const interfaces = safeNetworkInterfaces()
|
||||||
const localIp = Object.values(interfaces).flat().find(i => i?.family === 'IPv4' && !i?.internal)?.address || 'localhost'
|
const localIp = Object.values(interfaces).flat().find(i => i?.family === 'IPv4' && !i?.internal)?.address || 'localhost'
|
||||||
console.log(`Server: http://localhost:${config.port} (LAN: http://${localIp}:${config.port})`)
|
console.log(`Server: http://localhost:${config.port} (LAN: http://${localIp}:${config.port})`)
|
||||||
console.log(`Upstream: ${config.upstream}`)
|
console.log(`Upstream: ${config.upstream}`)
|
||||||
|
|||||||
@@ -49,10 +49,49 @@ const execFileAsync = promisify(execFile)
|
|||||||
const HERMES_BASE = resolve(homedir(), '.hermes')
|
const HERMES_BASE = resolve(homedir(), '.hermes')
|
||||||
const HERMES_BIN = process.env.HERMES_BIN?.trim() || 'hermes'
|
const HERMES_BIN = process.env.HERMES_BIN?.trim() || 'hermes'
|
||||||
|
|
||||||
// WSL / Docker 没有 systemd 或 launchd,需要用 "gateway run" 代替 "gateway start"
|
/**
|
||||||
const isWsl = existsSync('/proc/version') && readFileSync('/proc/version', 'utf-8').toLowerCase().includes('microsoft')
|
* 检测系统的 init 系统(服务管理器)
|
||||||
const isDocker = existsSync('/.dockerenv')
|
* - macOS → launchd
|
||||||
const needsRunMode = isWsl || isDocker
|
* - Windows → windows-service
|
||||||
|
* - Linux → systemd / sysvinit / other
|
||||||
|
*
|
||||||
|
* 没有 systemd/launchd/windows-service 的环境需要用 "gateway run" 代替 "gateway start"
|
||||||
|
* (适用于 WSL/Docker/Termux/proot 等无服务管理器的环境)
|
||||||
|
*/
|
||||||
|
function detectInitSystem(): string {
|
||||||
|
const platform = process.platform
|
||||||
|
|
||||||
|
// macOS → launchd
|
||||||
|
if (platform === 'darwin') {
|
||||||
|
return 'launchd'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows → Service Manager
|
||||||
|
if (platform === 'win32') {
|
||||||
|
return 'windows-service'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linux 才检查 /proc
|
||||||
|
if (platform === 'linux') {
|
||||||
|
try {
|
||||||
|
const comm = readFileSync('/proc/1/comm', 'utf-8').trim()
|
||||||
|
|
||||||
|
if (comm === 'systemd') return 'systemd'
|
||||||
|
if (comm === 'init') return 'sysvinit'
|
||||||
|
|
||||||
|
return 'other'
|
||||||
|
} catch {
|
||||||
|
return 'unknown'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'unknown'
|
||||||
|
}
|
||||||
|
|
||||||
|
const initSystem = detectInitSystem()
|
||||||
|
const needsRunMode = !['systemd', 'launchd', 'windows-service'].includes(initSystem)
|
||||||
|
// 启动时输出 init 系统检测结果(方便调试)
|
||||||
|
logger.debug('Detected init system: %s (needsRunMode: %s)', initSystem, needsRunMode)
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// 类型定义
|
// 类型定义
|
||||||
|
|||||||
Reference in New Issue
Block a user