chore(panel): fleet-aware shell footer + drop dead vuefinder dep
All checks were successful
CI / backend-types (push) Successful in 9s
CI / frontend-build (push) Successful in 14s
CI / agent-tests (push) Successful in 49s
CI / integration (push) Successful in 22s

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:
Vantz Stockwell
2026-06-11 21:04:09 -04:00
parent 4ef5db5b0d
commit 0715492ddf
5 changed files with 41 additions and 613 deletions

View File

@@ -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 ----