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: 'Unknown', detail: 'Awaiting connection…' }, models: [], metrics: { activeAgents: 0, queuedTasks: 0, successRate: 0, incidents: 0 }, projects: [], tasks: [], activity: [], } const fallbackRouting: RoutingTarget[] = [] 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 { 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 }, }, })