feat: add gateway auto-start on boot and real health detection
- Auto-detect gateway connectivity on server startup, start gateway if not running - Fix /health endpoint to actually check gateway reachability instead of just CLI version - Fix MiniMax CN base_url from /anthropic to /v1 - Frontend connection status now reflects real gateway state Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+46
-4
@@ -15,12 +15,13 @@ import { fsRoutes } from './routes/filesystem'
|
|||||||
import { configRoutes } from './routes/config'
|
import { configRoutes } from './routes/config'
|
||||||
import { weixinRoutes } from './routes/weixin'
|
import { weixinRoutes } from './routes/weixin'
|
||||||
import * as hermesCli from './services/hermes-cli'
|
import * as hermesCli from './services/hermes-cli'
|
||||||
const { restartGateway } = hermesCli
|
const { restartGateway, startGateway, getVersion } = hermesCli
|
||||||
|
|
||||||
export async function bootstrap() {
|
export async function bootstrap() {
|
||||||
await mkdir(config.uploadDir, { recursive: true })
|
await mkdir(config.uploadDir, { recursive: true })
|
||||||
await mkdir(config.dataDir, { recursive: true })
|
await mkdir(config.dataDir, { recursive: true })
|
||||||
await ensureApiServerConfig()
|
await ensureApiServerConfig()
|
||||||
|
await ensureGatewayRunning()
|
||||||
|
|
||||||
const app = new Koa()
|
const app = new Koa()
|
||||||
|
|
||||||
@@ -35,12 +36,26 @@ export async function bootstrap() {
|
|||||||
app.use(configRoutes.routes())
|
app.use(configRoutes.routes())
|
||||||
app.use(weixinRoutes.routes())
|
app.use(weixinRoutes.routes())
|
||||||
|
|
||||||
// Health endpoint with version
|
// Health endpoint: check CLI version + gateway connectivity
|
||||||
app.use(async (ctx, next) => {
|
app.use(async (ctx, next) => {
|
||||||
if (ctx.path === '/health') {
|
if (ctx.path === '/health') {
|
||||||
const raw = await hermesCli.getVersion()
|
const raw = await getVersion()
|
||||||
const version = raw.split('\n')[0].replace('Hermes Agent ', '') || ''
|
const version = raw.split('\n')[0].replace('Hermes Agent ', '') || ''
|
||||||
ctx.body = { status: 'ok', platform: 'hermes-agent', version }
|
|
||||||
|
let gatewayOk = false
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${config.upstream.replace(/\/$/, '')}/health`, {
|
||||||
|
signal: AbortSignal.timeout(5000),
|
||||||
|
})
|
||||||
|
gatewayOk = res.ok
|
||||||
|
} catch { /* not reachable */ }
|
||||||
|
|
||||||
|
ctx.body = {
|
||||||
|
status: gatewayOk ? 'ok' : 'error',
|
||||||
|
platform: 'hermes-agent',
|
||||||
|
version,
|
||||||
|
gateway: gatewayOk ? 'running' : 'stopped',
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await next()
|
await next()
|
||||||
@@ -136,4 +151,31 @@ async function ensureApiServerConfig() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function ensureGatewayRunning() {
|
||||||
|
const upstream = config.upstream.replace(/\/$/, '')
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${upstream}/health`, { signal: AbortSignal.timeout(5000) })
|
||||||
|
if (res.ok) {
|
||||||
|
console.log(' ✓ Gateway is running')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Gateway not reachable
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(' ⚠ Gateway not reachable, starting...')
|
||||||
|
try {
|
||||||
|
await startGateway()
|
||||||
|
await new Promise(r => setTimeout(r, 3000))
|
||||||
|
const res = await fetch(`${upstream}/health`, { signal: AbortSignal.timeout(5000) })
|
||||||
|
if (res.ok) {
|
||||||
|
console.log(' ✓ Gateway started successfully')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log(' ✗ Gateway start attempted but still not reachable')
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(' ✗ Failed to start gateway:', err.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bootstrap()
|
bootstrap()
|
||||||
|
|||||||
@@ -182,7 +182,17 @@ export async function getVersion(): Promise<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update Hermes Agent
|
* Start Hermes gateway
|
||||||
|
*/
|
||||||
|
export async function startGateway(): Promise<string> {
|
||||||
|
const { stdout, stderr } = await execFileAsync('hermes', ['gateway', 'start'], {
|
||||||
|
timeout: 30000,
|
||||||
|
})
|
||||||
|
return stdout || stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restart Hermes gateway
|
||||||
*/
|
*/
|
||||||
export async function restartGateway(): Promise<string> {
|
export async function restartGateway(): Promise<string> {
|
||||||
const { stdout, stderr } = await execFileAsync('hermes', ['gateway', 'restart'], {
|
const { stdout, stderr } = await execFileAsync('hermes', ['gateway', 'restart'], {
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export const PROVIDER_PRESETS: ProviderPreset[] = [
|
|||||||
{
|
{
|
||||||
label: 'MiniMax (China)',
|
label: 'MiniMax (China)',
|
||||||
value: 'minimax-cn',
|
value: 'minimax-cn',
|
||||||
base_url: 'https://api.minimaxi.com/anthropic',
|
base_url: 'https://api.minimaxi.com/v1',
|
||||||
models: ['MiniMax-M2.7', 'MiniMax-M2.5', 'MiniMax-M2.1', 'MiniMax-M2'],
|
models: ['MiniMax-M2.7', 'MiniMax-M2.5', 'MiniMax-M2.1', 'MiniMax-M2'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export const PROVIDER_PRESETS: ProviderPreset[] = [
|
|||||||
{
|
{
|
||||||
label: 'MiniMax (China)',
|
label: 'MiniMax (China)',
|
||||||
value: 'minimax-cn',
|
value: 'minimax-cn',
|
||||||
base_url: 'https://api.minimaxi.com/anthropic',
|
base_url: 'https://api.minimaxi.com/v1',
|
||||||
models: ['MiniMax-M2.7', 'MiniMax-M2.5', 'MiniMax-M2.1', 'MiniMax-M2'],
|
models: ['MiniMax-M2.7', 'MiniMax-M2.5', 'MiniMax-M2.1', 'MiniMax-M2'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ export const useAppStore = defineStore('app', () => {
|
|||||||
async function checkConnection() {
|
async function checkConnection() {
|
||||||
try {
|
try {
|
||||||
const res = await checkHealth()
|
const res = await checkHealth()
|
||||||
connected.value = true
|
connected.value = res.status === 'ok'
|
||||||
if (res.version) serverVersion.value = res.version
|
if (res.version) serverVersion.value = res.version
|
||||||
} catch {
|
} catch {
|
||||||
connected.value = false
|
connected.value = false
|
||||||
|
|||||||
Reference in New Issue
Block a user