fix(shadcn): isolate Nexus CSS vars with --nx- prefix + admin password reset endpoint
CI - Build & Test / Backend (.NET) (push) Successful in 26s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 16s
CI - Build & Test / Security Check (push) Successful in 2s

This commit is contained in:
2026-06-11 10:06:53 +02:00
parent a538025049
commit b7b44494f0
59 changed files with 3267 additions and 1153 deletions
+73 -44
View File
@@ -74,6 +74,8 @@ interface DashboardAgentInfo {
model: string
isActive: boolean
currentTask: string
description?: string
tags?: string[]
}
interface DashboardOperationEntry {
@@ -105,65 +107,88 @@ interface DashboardQueueItem {
const AGENT_CATALOG: Record<string, Partial<AgentNodeData>> = {
iris: {
description: 'Koordiniert, delegiert, hält das Team tight. Die erste Anlaufstelle zwischen Boss und Maschine.',
tags: ['Orchestration', 'Delegation', 'Approval'],
description: 'Zentrale operative Führungsinstanz. Strukturiert Aufgaben, bewertet Risiken, steuert spezialisierte Agenten und eskaliert kritische Entscheidungen.',
tags: ['Orchestration', 'Delegation', 'Approval', 'Risk Management'],
color: '#8b7cf6',
icon: 'bot',
hero: true,
goal: 'Complete Mission Control v3',
goal: 'Mission Control — maximale Autonomie bei kontrolliertem Risiko',
progress: 90,
workload: 60,
workingFeed: [],
thinkingStream: [],
},
developer: {
description: 'Primärer Entwicklungsagent. Implementiert Features, behebt Bugs und schreibt Code im gesamten Stack — autonom im Rahmen seines Scopes.',
tags: ['Full-Stack', 'TypeScript', 'C#', 'Vue', '.NET', 'Builds'],
color: '#3b82f6',
icon: 'code',
goal: 'Nexus Dashboard & Dungeon System',
progress: 70,
workload: 65,
workingFeed: [],
thinkingStream: [],
},
architekt: {
description: 'Verwaltet die gesamte Server-Infrastruktur. Deployt Services, konfiguriert Docker, Nginx und Firewall. Stellt sicher, dass die Produktivumgebung stabil und sicher läuft.',
tags: ['Docker', 'Nginx', 'CI/CD', 'Firewall', 'VPS'],
color: '#eab308',
icon: 'server',
goal: 'Stabile Zero-Downtime-Deployments',
progress: 60,
workload: 45,
workingFeed: [],
thinkingStream: [],
},
researcher: {
description: 'Spezialisierter Recherche-Agent. Sucht online, prüft Quellen, analysiert Inhalte (inkl. YouTube-Videos) und übergibt strukturierte Erkenntnisse. Ausschließlich Lese- und Analyse-Rechte.',
tags: ['Research', 'Quellenprüfung', 'Analyse', 'Docs'],
color: '#22c55e',
icon: 'search',
goal: 'Verifizierte, strukturierte Recherche-Ergebnisse',
progress: 40,
workload: 30,
workingFeed: [],
thinkingStream: [],
},
reviewer: {
description: 'Code-Qualitätskontrolle. Prüft Diffs auf Bugs, Regressionen, Sicherheitslücken und Wartbarkeit. Berichtet Findings strukturiert und knapp.',
tags: ['Code Review', 'Testing', 'Security', 'Quality'],
color: '#a855f7',
icon: 'shield',
goal: 'Zero critical findings before merge',
progress: 85,
workload: 55,
workingFeed: [],
thinkingStream: [],
},
developer: {
description: 'Implements features across the stack with TypeScript, C#, and Vue.',
tags: ['Coding', 'Development', 'Builds'],
executor: {
description: 'Einziger Agent mit Host-Exec-Rechten. Führt Docker- und Shell-Befehle auf dem VPS aus — ausschließlich im Auftrag von Iris. Handelt niemals eigeninitiativ.',
tags: ['Docker', 'Shell', 'Host', 'Deployment'],
color: '#f59e0b',
icon: 'server',
goal: 'Sichere Host-Execution im Allowlist-Rahmen',
progress: 95,
workload: 20,
workingFeed: [],
thinkingStream: [],
},
// Alias: API sends "programmer" but AGENT_CATALOG uses "developer" as canonical key
programmer: {
description: 'Primärer Entwicklungsagent. Implementiert Features, behebt Bugs und schreibt Code im gesamten Stack — autonom im Rahmen seines Scopes.',
tags: ['Full-Stack', 'TypeScript', 'C#', 'Vue', '.NET', 'Builds'],
color: '#3b82f6',
icon: 'code',
goal: 'Complete Dungeon CRUD + room generation',
progress: 62,
goal: 'Nexus Dashboard & Dungeon System',
progress: 70,
workload: 65,
workingFeed: [],
thinkingStream: [],
},
devops: {
description: 'Manages Docker, deployment pipelines, and system reliability.',
tags: ['Deployment', 'Docker', 'CI/CD'],
color: '#eab308',
icon: 'server',
goal: 'Reduce build times by 40%',
progress: 45,
workload: 40,
workingFeed: [],
thinkingStream: [],
},
researcher: {
description: 'Researches APIs, patterns, and best practices. Maintains docs.',
tags: ['Research', 'Analysis', 'Docs'],
color: '#22c55e',
icon: 'search',
goal: 'Recommend real-time communication strategy',
progress: 30,
workload: 25,
workingFeed: [],
thinkingStream: [],
},
reviewer: {
description: 'Reviews pull requests, enforces standards, runs test suites.',
tags: ['Code Review', 'Testing', 'Quality'],
color: '#a855f7',
icon: 'shield',
goal: 'Zero critical findings before merge',
progress: 80,
workload: 50,
workingFeed: [],
thinkingStream: [],
},
}
function enrichAgent(api: DashboardAgentInfo): AgentNodeData {
const catalog = AGENT_CATALOG[api.id] ?? AGENT_CATALOG['developer']
const catalog = AGENT_CATALOG[api.id] ?? AGENT_CATALOG['reviewer']
return {
id: api.id,
name: api.name,
@@ -171,8 +196,8 @@ function enrichAgent(api: DashboardAgentInfo): AgentNodeData {
model: api.model,
currentTask: api.currentTask ?? 'Idle',
active: api.isActive,
description: catalog.description ?? '',
tags: catalog.tags ?? [],
description: api.description ?? catalog.description ?? '',
tags: api.tags ?? catalog.tags ?? [],
color: catalog.color ?? '#6b7385',
icon: catalog.icon ?? 'bot',
hero: catalog.hero ?? false,
@@ -214,6 +239,7 @@ const agents = ref<AgentNodeData[]>([])
const chatMessages = ref<ChatMessage[]>([])
const irisBusy = ref(false)
const irisFocus = ref('')
const busySince = ref(0)
// Operations Feed
const feedEntries = ref<FeedEntry[]>([])
@@ -330,6 +356,7 @@ async function sendChatMessage(text: string): Promise<void> {
})
irisBusy.value = true
busySince.value = Date.now()
try {
const res = await apiFetch('/api/dashboard/chat/send', {
@@ -362,6 +389,7 @@ async function sendChatMessage(text: string): Promise<void> {
})
} finally {
irisBusy.value = false
busySince.value = 0
irisFocus.value = text.trim()
}
}
@@ -457,6 +485,7 @@ export function useDashboardData() {
chatMessages,
irisBusy,
irisFocus,
busySince,
irisRuntime,
queue,
gatewayOk,
-37
View File
@@ -1,37 +0,0 @@
import { ref, onMounted, onUnmounted } from 'vue'
export function useTimer() {
const elapsed = ref(0)
let interval: ReturnType<typeof setInterval> | null = null
function start() {
if (interval !== null) return
const startTime = Date.now()
elapsed.value = 0
interval = setInterval(() => {
elapsed.value = Math.floor((Date.now() - startTime) / 1000)
}, 1000)
}
function stop() {
if (interval !== null) {
clearInterval(interval)
interval = null
}
}
function format(minutes: number, seconds: number): string {
return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`
}
const formatted = (): string => {
const m = Math.floor(elapsed.value / 60)
const s = elapsed.value % 60
return format(m, s)
}
onMounted(start)
onUnmounted(stop)
return { elapsed, formatted, start, stop }
}
+28
View File
@@ -0,0 +1,28 @@
import { ref, readonly } from 'vue'
import type { Toast } from '../types/toast'
let nextId = 1
const toasts = ref<Toast[]>([])
export function useToast() {
function add(message: string, type: Toast['type'] = 'info', durationMs = 3500) {
const id = nextId++
toasts.value.push({ id, message, type, durationMs })
if (durationMs > 0) {
setTimeout(() => remove(id), durationMs)
}
}
function remove(id: number) {
const idx = toasts.value.findIndex(t => t.id === id)
if (idx !== -1) toasts.value.splice(idx, 1)
}
return {
toasts: readonly(toasts),
success: (msg: string, durationMs?: number) => add(msg, 'success', durationMs),
error: (msg: string, durationMs?: number) => add(msg, 'error', durationMs),
info: (msg: string, durationMs?: number) => add(msg, 'info', durationMs),
remove,
}
}