chore(panel): fleet-aware shell footer + drop dead vuefinder dep
COA-B cleanup: - Sidebar agent-health footer now reads the fleet store (host count / online count / per-host status + last heartbeat) instead of the single legacy server.connection row, which disagreed with the multi-host fleet. Removed the legacy useServerStore dependency from the shell. - Removed the unused 'vuefinder' dependency (replaced by the native file manager): dep + main.ts plugin/CSS registration. Main JS chunk 588kB -> 165kB. Recon reclassified the 'dead cmd.server v1' item: it is the LIVE license-level command path (module config applies, plugin install, schedules, legacy start/stop) served only by the Go agent — a Rust-agent parity gap, not dead code. Left intact. Build-green (vue-tsc) + boots clean in-browser (0 console errors). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,6 @@
|
||||
import { ref, computed, watch, onMounted } from 'vue'
|
||||
import { RouterView, useRoute, useRouter } from 'vue-router'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useServerStore } from '@/stores/server'
|
||||
import { useFleetStore } from '@/stores/fleet'
|
||||
import { useThemeGame, syncActiveGameFromFleet } from '@/composables/useThemeGame'
|
||||
import { useGameProfile } from '@/config/gameProfiles'
|
||||
@@ -31,7 +30,6 @@ import type { ActiveGame } from '@/composables/useThemeGame'
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
const server = useServerStore()
|
||||
const fleet = useFleetStore()
|
||||
const { theme, activeGame, setActiveGame, toggleTheme } = useThemeGame()
|
||||
|
||||
@@ -106,32 +104,43 @@ function hasVisibleItems(section: NavSection): boolean {
|
||||
return section.items.some(canShowNavItem)
|
||||
}
|
||||
|
||||
// ---- Agent health ----
|
||||
const hasAgent = computed(() => server.connection !== null)
|
||||
// ---- Agent health — driven by the fleet, not the legacy single connection ----
|
||||
// Real hosts only (the fleet response appends a synthetic '__unassigned__'
|
||||
// bucket for orphaned instances; summary counts already exclude it).
|
||||
const realHosts = computed(() => fleet.hosts.filter((h) => h.id !== '__unassigned__'))
|
||||
const hostCount = computed(() => fleet.summary.host_count)
|
||||
const onlineHosts = computed(() => fleet.summary.online_host_count)
|
||||
const instanceCount = computed(() => fleet.summary.instance_count)
|
||||
|
||||
const hasAgent = computed(() => hostCount.value > 0)
|
||||
|
||||
const agentTone = computed(() => {
|
||||
const cs = server.connection?.connection_status
|
||||
if (cs === 'connected') return 'online' as const
|
||||
if (cs === 'degraded') return 'warn' as const
|
||||
return 'offline' as const
|
||||
if (onlineHosts.value === 0) return 'offline' as const
|
||||
if (onlineHosts.value < hostCount.value) return 'warn' as const
|
||||
return 'online' as const
|
||||
})
|
||||
const agentLabel = computed(() => {
|
||||
const cs = server.connection?.connection_status
|
||||
if (cs === 'connected') return 'Healthy'
|
||||
if (cs === 'degraded') return 'Degraded'
|
||||
return 'Offline'
|
||||
if (onlineHosts.value === 0) return 'Offline'
|
||||
if (onlineHosts.value < hostCount.value) return 'Degraded'
|
||||
return 'Healthy'
|
||||
})
|
||||
const agentName = computed(() => server.connection?.server_ip ?? 'Host agent')
|
||||
// One host → its hostname; multiple → fleet count.
|
||||
const agentName = computed(() =>
|
||||
hostCount.value === 1 ? (realHosts.value[0]?.hostname ?? 'Host agent') : `${hostCount.value} hosts`,
|
||||
)
|
||||
|
||||
const agentMetaLine = computed(() => {
|
||||
const cs = server.connection?.connection_status
|
||||
let line = cs === 'connected' ? 'Connected' : server.connection?.companion_last_seen
|
||||
? `Last seen ${safeDate(server.connection.companion_last_seen)}`
|
||||
: 'Awaiting first heartbeat'
|
||||
if (server.stats) {
|
||||
line += ` · ${server.stats.player_count}/${server.stats.max_players} players`
|
||||
const insts = `${instanceCount.value} ${instanceCount.value === 1 ? 'instance' : 'instances'}`
|
||||
if (hostCount.value === 1) {
|
||||
const h = realHosts.value[0]
|
||||
const state = h?.status === 'connected'
|
||||
? 'Connected'
|
||||
: h?.last_heartbeat_at
|
||||
? `Last seen ${safeDate(h.last_heartbeat_at)}`
|
||||
: 'Awaiting first heartbeat'
|
||||
return `${state} · ${insts}`
|
||||
}
|
||||
return line
|
||||
return `${onlineHosts.value}/${hostCount.value} online · ${insts}`
|
||||
})
|
||||
|
||||
// ---- Topbar ----
|
||||
|
||||
@@ -2,12 +2,10 @@ import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||
|
||||
import { VueFinderPlugin } from 'vuefinder'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import { initThemeGame } from './composables/useThemeGame'
|
||||
import './style.css'
|
||||
import 'vuefinder/dist/vuefinder.css'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
@@ -16,7 +14,6 @@ pinia.use(piniaPluginPersistedstate)
|
||||
|
||||
app.use(pinia)
|
||||
app.use(router)
|
||||
app.use(VueFinderPlugin)
|
||||
|
||||
// Apply the design-system theming contract (data-theme/data-game on <html>).
|
||||
initThemeGame()
|
||||
|
||||
Reference in New Issue
Block a user