Initial commit: Nexus Mission Control Platform
- ASP.NET Core 10 Backend (JWT Auth, Agent config API) - Vue 3 Frontend (Dashboard, Team, Agents, Config Editor) - PostgreSQL Database - Docker Compose setup - Mission Control Dashboard redesign
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import type { AgentInfo, OperationsSnapshot, RoutingTarget } from '../types'
|
||||
import { apiFetch } from '../services/api'
|
||||
|
||||
const fallback: OperationsSnapshot = {
|
||||
generatedAt: new Date().toISOString(),
|
||||
runtime: { runtime: 'OpenClaw', status: 'Online', detail: 'Gateway responding' },
|
||||
models: [
|
||||
{ provider: 'OpenClaw', model: 'deepseek/deepseek-v4-flash', status: 'Online', isLocal: false, detail: 'Programmer agent' },
|
||||
{ provider: 'OpenClaw', model: 'deepseek/deepseek-v4-pro', status: 'Online', isLocal: false, detail: 'Reviewer agent' },
|
||||
{ provider: 'OpenClaw', model: 'openai/gpt-5.3-chat-latest', status: 'Online', isLocal: false, detail: 'Iris orchestrator' },
|
||||
],
|
||||
metrics: { activeAgents: 3, queuedTasks: 7, successRate: 98.4, incidents: 0 },
|
||||
projects: [
|
||||
{ id: 'nexus', name: 'Nexus', status: 'Active', progress: 18 },
|
||||
{ id: 'openclaw', name: 'OpenClaw Runtime', status: 'Online', progress: 100 },
|
||||
{ id: 'infra', name: 'Noveria Infrastructure', status: 'Stable', progress: 74 },
|
||||
],
|
||||
tasks: [
|
||||
{ id: 'preview-foundation', title: 'Nexus foundation', state: 'In progress', priority: 'Critical', updatedAt: new Date().toISOString() },
|
||||
{ id: 'preview-runtime', title: 'Connect OpenClaw adapter', state: 'In progress', priority: 'High', updatedAt: new Date().toISOString() },
|
||||
{ id: 'preview-routing', title: 'Configure model routing', state: 'In progress', priority: 'High', updatedAt: new Date().toISOString() },
|
||||
{ id: 'preview-auth', title: 'Owner authentication', state: 'Done', priority: 'Critical', updatedAt: new Date().toISOString() },
|
||||
],
|
||||
activity: [
|
||||
{ type: 'runtime', message: 'OpenClaw runtime health checked', at: new Date().toISOString() },
|
||||
{ type: 'deploy', message: 'Nexus foundation initialized', at: new Date(Date.now() - 720000).toISOString() },
|
||||
{ type: 'deploy', message: 'Model routing configured for DeepSeek agents', at: new Date(Date.now() - 1140000).toISOString() },
|
||||
],
|
||||
}
|
||||
|
||||
const fallbackRouting: RoutingTarget[] = [
|
||||
{ priority: 1, provider: 'OpenClaw', model: 'deepseek/deepseek-v4-flash', purpose: 'Programmer agent', status: 'Online', detail: 'Routed through OpenClaw' },
|
||||
{ priority: 2, provider: 'OpenClaw', model: 'deepseek/deepseek-v4-pro', purpose: 'Reviewer agent', status: 'Online', detail: 'Routed through OpenClaw' },
|
||||
{ priority: 3, provider: 'OpenClaw', model: 'openai/gpt-5.3-chat-latest', purpose: 'Iris orchestrator', status: 'Online', detail: 'Routed through OpenClaw' },
|
||||
]
|
||||
|
||||
export const useOperationsStore = defineStore('operations', {
|
||||
state: () => ({
|
||||
snapshot: fallback,
|
||||
routing: fallbackRouting,
|
||||
loading: false,
|
||||
connected: false,
|
||||
}),
|
||||
actions: {
|
||||
async createProject(name: string) {
|
||||
const response = await apiFetch('/api/v1/projects', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name }),
|
||||
})
|
||||
if (!response.ok) throw new Error('Project could not be created')
|
||||
const project = await response.json()
|
||||
this.snapshot.projects.unshift({
|
||||
id: project.id,
|
||||
name: project.name,
|
||||
status: project.status,
|
||||
progress: project.progress,
|
||||
})
|
||||
},
|
||||
async createTask(title: string, priority: string) {
|
||||
const response = await apiFetch('/api/v1/tasks', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ title, priority }),
|
||||
})
|
||||
if (!response.ok) throw new Error('Task could not be created')
|
||||
const task = await response.json()
|
||||
this.snapshot.tasks.unshift(task)
|
||||
this.snapshot.metrics.queuedTasks += 1
|
||||
},
|
||||
async updateTaskState(id: string, state: string) {
|
||||
const response = await apiFetch(`/api/v1/tasks/${id}/state`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ state }),
|
||||
})
|
||||
if (!response.ok) throw new Error('Task state could not be updated')
|
||||
const updatedTask = await response.json()
|
||||
const index = this.snapshot.tasks.findIndex(task => task.id === id)
|
||||
if (index !== -1) this.snapshot.tasks[index] = updatedTask
|
||||
this.snapshot.metrics.queuedTasks = this.snapshot.tasks.filter(task => task.state !== 'Done').length
|
||||
this.snapshot.metrics.incidents = this.snapshot.tasks.filter(task => task.state === 'Blocked').length
|
||||
const completed = this.snapshot.tasks.filter(task => task.state === 'Done').length
|
||||
this.snapshot.metrics.successRate = this.snapshot.tasks.length
|
||||
? Math.round((completed * 1000) / this.snapshot.tasks.length) / 10
|
||||
: 100
|
||||
},
|
||||
async updateTask(id: string, data: { title?: string; priority?: string; projectId?: string | null }) {
|
||||
const response = await apiFetch(`/api/v1/tasks/${id}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
if (!response.ok) throw new Error('Task could not be updated')
|
||||
const updatedTask = await response.json()
|
||||
const index = this.snapshot.tasks.findIndex(task => task.id === id)
|
||||
if (index !== -1) this.snapshot.tasks[index] = updatedTask
|
||||
},
|
||||
async updateProject(id: string, data: { name?: string; description?: string; status?: string }) {
|
||||
const response = await apiFetch(`/api/v1/projects/${id}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
if (!response.ok) throw new Error('Project could not be updated')
|
||||
const updatedProject = await response.json()
|
||||
const index = this.snapshot.projects.findIndex(p => p.id === id)
|
||||
if (index !== -1) this.snapshot.projects[index] = {
|
||||
id: updatedProject.id,
|
||||
name: updatedProject.name,
|
||||
status: updatedProject.status,
|
||||
progress: updatedProject.progress,
|
||||
}
|
||||
},
|
||||
async deleteTask(id: string) {
|
||||
const response = await apiFetch(`/api/v1/tasks/${id}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
if (response.status === 403) {
|
||||
const err = await response.json().catch(() => ({ detail: 'Task cannot be deleted in its current state.' }))
|
||||
throw new Error(err.detail || 'Task cannot be deleted in its current state.')
|
||||
}
|
||||
if (!response.ok) throw new Error('Task could not be deleted')
|
||||
this.snapshot.tasks = this.snapshot.tasks.filter(t => t.id !== id)
|
||||
this.snapshot.metrics.queuedTasks = this.snapshot.tasks.filter(t => t.state !== 'Done').length
|
||||
const completed = this.snapshot.tasks.filter(t => t.state === 'Done').length
|
||||
this.snapshot.metrics.successRate = this.snapshot.tasks.length
|
||||
? Math.round((completed * 1000) / this.snapshot.tasks.length) / 10
|
||||
: 100
|
||||
},
|
||||
async deleteProject(id: string) {
|
||||
const response = await apiFetch(`/api/v1/projects/${id}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
if (!response.ok) throw new Error('Project could not be deleted')
|
||||
this.snapshot.projects = this.snapshot.projects.filter(p => p.id !== id)
|
||||
},
|
||||
async refresh() {
|
||||
this.loading = true
|
||||
try {
|
||||
const [snapshotResponse, routingResponse] = await Promise.all([
|
||||
apiFetch('/api/v1/operations/snapshot'),
|
||||
apiFetch('/api/v1/routing'),
|
||||
])
|
||||
if (!snapshotResponse.ok || !routingResponse.ok) throw new Error('Nexus API unavailable')
|
||||
this.snapshot = await snapshotResponse.json()
|
||||
this.routing = await routingResponse.json()
|
||||
this.connected = true
|
||||
} catch {
|
||||
this.connected = false
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
async fetchAgents(): Promise<AgentInfo[]> {
|
||||
try {
|
||||
const response = await apiFetch('/api/v1/agents')
|
||||
if (!response.ok) throw new Error('Failed to fetch agents')
|
||||
return await response.json()
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
},
|
||||
async approveTask(id: string) {
|
||||
const response = await apiFetch(`/api/v1/tasks/${id}/approve`, {
|
||||
method: 'POST',
|
||||
})
|
||||
if (!response.ok) throw new Error('Task could not be approved')
|
||||
const index = this.snapshot.tasks.findIndex(task => task.id === id)
|
||||
if (index !== -1) {
|
||||
this.snapshot.tasks.splice(index, 1)
|
||||
}
|
||||
this.snapshot.metrics.queuedTasks = this.snapshot.tasks.filter(task => task.state !== 'Done').length
|
||||
this.snapshot.metrics.incidents = this.snapshot.tasks.filter(task => task.state === 'Blocked').length
|
||||
const completed = this.snapshot.tasks.filter(task => task.state === 'Done').length
|
||||
this.snapshot.metrics.successRate = this.snapshot.tasks.length
|
||||
? Math.round((completed * 1000) / this.snapshot.tasks.length) / 10
|
||||
: 100
|
||||
},
|
||||
async rejectTask(id: string) {
|
||||
const response = await apiFetch(`/api/v1/tasks/${id}/reject`, {
|
||||
method: 'POST',
|
||||
})
|
||||
if (!response.ok) throw new Error('Task could not be rejected')
|
||||
const index = this.snapshot.tasks.findIndex(task => task.id === id)
|
||||
if (index !== -1) {
|
||||
this.snapshot.tasks[index] = { ...this.snapshot.tasks[index], state: 'Backlog' }
|
||||
}
|
||||
this.snapshot.metrics.queuedTasks = this.snapshot.tasks.filter(task => task.state !== 'Done').length
|
||||
this.snapshot.metrics.incidents = this.snapshot.tasks.filter(task => task.state === 'Blocked').length
|
||||
const completed = this.snapshot.tasks.filter(task => task.state === 'Done').length
|
||||
this.snapshot.metrics.successRate = this.snapshot.tasks.length
|
||||
? Math.round((completed * 1000) / this.snapshot.tasks.length) / 10
|
||||
: 100
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user