4b6de351bd
- Add GatewayManager for multi-profile gateway lifecycle management - Auto-detect running gateways on startup via PID + health check - Port conflict detection: check managed gateways, allocated ports, and system-level port availability (TCP bind test) - Two-phase startup: sequential port resolution, parallel process launch - Use `gateway start/restart` on normal systems, `gateway run --replace` on WSL/Docker - Wait for health check before returning start/stop responses - Add Gateways page with card-based layout showing profile status - Reorganize sidebar navigation into collapsible groups - Hide API server settings (now auto-managed by GatewayManager) - Profile switch reloads page; Ctrl+C no longer stops gateways - Remove redundant ensureApiServerConfig from index.ts and profiles.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
98 lines
2.3 KiB
TypeScript
98 lines
2.3 KiB
TypeScript
import { createRouter, createWebHashHistory } from 'vue-router'
|
|
import { hasApiKey } from '@/api/client'
|
|
|
|
const router = createRouter({
|
|
history: createWebHashHistory(),
|
|
routes: [
|
|
{
|
|
path: '/',
|
|
name: 'login',
|
|
component: () => import('@/views/LoginView.vue'),
|
|
meta: { public: true },
|
|
},
|
|
{
|
|
path: '/hermes/chat',
|
|
name: 'hermes.chat',
|
|
component: () => import('@/views/hermes/ChatView.vue'),
|
|
},
|
|
{
|
|
path: '/hermes/jobs',
|
|
name: 'hermes.jobs',
|
|
component: () => import('@/views/hermes/JobsView.vue'),
|
|
},
|
|
{
|
|
path: '/hermes/models',
|
|
name: 'hermes.models',
|
|
component: () => import('@/views/hermes/ModelsView.vue'),
|
|
},
|
|
{
|
|
path: '/hermes/profiles',
|
|
name: 'hermes.profiles',
|
|
component: () => import('@/views/hermes/ProfilesView.vue'),
|
|
},
|
|
{
|
|
path: '/hermes/logs',
|
|
name: 'hermes.logs',
|
|
component: () => import('@/views/hermes/LogsView.vue'),
|
|
},
|
|
{
|
|
path: '/hermes/usage',
|
|
name: 'hermes.usage',
|
|
component: () => import('@/views/hermes/UsageView.vue'),
|
|
},
|
|
{
|
|
path: '/hermes/skills',
|
|
name: 'hermes.skills',
|
|
component: () => import('@/views/hermes/SkillsView.vue'),
|
|
},
|
|
{
|
|
path: '/hermes/memory',
|
|
name: 'hermes.memory',
|
|
component: () => import('@/views/hermes/MemoryView.vue'),
|
|
},
|
|
{
|
|
path: '/hermes/settings',
|
|
name: 'hermes.settings',
|
|
component: () => import('@/views/hermes/SettingsView.vue'),
|
|
},
|
|
{
|
|
path: '/hermes/gateways',
|
|
name: 'hermes.gateways',
|
|
component: () => import('@/views/hermes/GatewaysView.vue'),
|
|
},
|
|
{
|
|
path: '/hermes/channels',
|
|
name: 'hermes.channels',
|
|
component: () => import('@/views/hermes/ChannelsView.vue'),
|
|
},
|
|
{
|
|
path: '/hermes/terminal',
|
|
name: 'hermes.terminal',
|
|
component: () => import('@/views/hermes/TerminalView.vue'),
|
|
},
|
|
],
|
|
})
|
|
|
|
router.beforeEach((to, _from, next) => {
|
|
// Public pages don't need auth
|
|
if (to.meta.public) {
|
|
// Already has key, skip login
|
|
if (to.name === 'login' && hasApiKey()) {
|
|
next({ path: '/hermes/chat' })
|
|
return
|
|
}
|
|
next()
|
|
return
|
|
}
|
|
|
|
// All other pages require token
|
|
if (!hasApiKey()) {
|
|
next({ name: 'login' })
|
|
return
|
|
}
|
|
|
|
next()
|
|
})
|
|
|
|
export default router
|