feat: Profile health score system (scoring engine, API, frontend)
Health scoring engine with 4 dimensions: Gateway stability (40%), config
completeness (25%), skill consistency (20%), and Cron success rate (15%).
Grades A(80-100) B(60-79) C(40-59) D(0-39) with detailed deduction items
and fix suggestions per dimension.
API endpoints:
- GET /health/scores — list all profiles with grade filter + sort
- GET /health/scores/{profile} — detail with deductions + suggestions
- POST /health/compare — multi-profile config/skill comparison
- POST /health/calculate — manual recalculation (owner only)
Frontend HealthPage: grade distribution cards, score table with expandable
detail rows, batch compare view with diff highlighting, grade/select/sort
filters, and owner-only recalculation.
Data sources use placeholders (TODO: hermes CLI / file reads) but the full
scoring logic is implemented end-to-end.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import client from './client'
|
||||
|
||||
/* ── types ── */
|
||||
|
||||
export interface ScoreRow {
|
||||
rank: number
|
||||
name: string
|
||||
total_score: number
|
||||
grade: string
|
||||
gateway_score: number
|
||||
config_score: number
|
||||
skill_score: number
|
||||
cron_score: number
|
||||
deduct_summary: string[]
|
||||
}
|
||||
|
||||
export interface ScoreListResponse {
|
||||
total: number
|
||||
distribution: { A: number; B: number; C: number; D: number }
|
||||
scores: ScoreRow[]
|
||||
}
|
||||
|
||||
export interface DimensionDetail {
|
||||
name: string
|
||||
weight: string
|
||||
score: number
|
||||
max_score: number
|
||||
deductions: string[]
|
||||
suggestions: string[]
|
||||
}
|
||||
|
||||
export interface ProfileScoreDetail {
|
||||
profile_name: string
|
||||
total_score: number
|
||||
grade: string
|
||||
calculated_at: string
|
||||
dimensions: DimensionDetail[]
|
||||
}
|
||||
|
||||
export interface CompareRow {
|
||||
field: string
|
||||
category: string
|
||||
has_diff: boolean
|
||||
[profileName: string]: unknown
|
||||
}
|
||||
|
||||
export interface CompareResponse {
|
||||
profiles: string[]
|
||||
rows: CompareRow[]
|
||||
}
|
||||
|
||||
export interface CalculateResponse {
|
||||
message: string
|
||||
count: number
|
||||
}
|
||||
|
||||
/* ── API functions ── */
|
||||
|
||||
export const getHealthScores = (params?: { grade?: string; sort?: string }) =>
|
||||
client.get<ScoreListResponse>('/health/scores', { params })
|
||||
|
||||
export const getProfileScore = (profile: string) =>
|
||||
client.get<ProfileScoreDetail>(`/health/scores/${encodeURIComponent(profile)}`)
|
||||
|
||||
export const compareProfiles = (profile_names: string[]) =>
|
||||
client.post<CompareResponse>('/health/compare', { profile_names })
|
||||
|
||||
export const recalculateScores = () =>
|
||||
client.post<CalculateResponse>('/health/calculate')
|
||||
Reference in New Issue
Block a user