cognova 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +58 -0
- package/Claude/CLAUDE.md +92 -0
- package/Claude/hooks/lib/__init__.py +1 -0
- package/Claude/hooks/lib/hook_client.py +207 -0
- package/Claude/hooks/log-event.py +97 -0
- package/Claude/hooks/pre-compact.py +46 -0
- package/Claude/hooks/session-end.py +26 -0
- package/Claude/hooks/session-start.py +35 -0
- package/Claude/hooks/stop-extract.py +40 -0
- package/Claude/rules/frontmatter.md +54 -0
- package/Claude/rules/markdown.md +43 -0
- package/Claude/rules/note-organization.md +33 -0
- package/Claude/settings.json +54 -0
- package/Claude/skills/README.md +136 -0
- package/Claude/skills/_lib/__init__.py +1 -0
- package/Claude/skills/_lib/api.py +164 -0
- package/Claude/skills/_lib/output.py +95 -0
- package/Claude/skills/environment/SKILL.md +73 -0
- package/Claude/skills/environment/environment.py +239 -0
- package/Claude/skills/memory/SKILL.md +153 -0
- package/Claude/skills/memory/memory.py +270 -0
- package/Claude/skills/project/SKILL.md +105 -0
- package/Claude/skills/project/project.py +203 -0
- package/Claude/skills/skill-creator/SKILL.md +261 -0
- package/Claude/skills/task/SKILL.md +135 -0
- package/Claude/skills/task/task.py +310 -0
- package/LICENSE +21 -0
- package/README.md +176 -0
- package/app/app.config.ts +8 -0
- package/app/app.vue +39 -0
- package/app/assets/css/main.css +10 -0
- package/app/components/AppLogo.vue +40 -0
- package/app/components/AssistantPanel.client.vue +518 -0
- package/app/components/ConfirmModal.vue +84 -0
- package/app/components/TemplateMenu.vue +49 -0
- package/app/components/agents/AgentActivityChart.client.vue +105 -0
- package/app/components/agents/AgentActivityChart.server.vue +25 -0
- package/app/components/agents/AgentForm.vue +304 -0
- package/app/components/agents/AgentRunModal.vue +154 -0
- package/app/components/agents/AgentStatsCards.vue +98 -0
- package/app/components/chat/ChatInput.vue +85 -0
- package/app/components/chat/ConversationList.vue +78 -0
- package/app/components/chat/MessageBubble.vue +81 -0
- package/app/components/chat/StreamingMessage.vue +36 -0
- package/app/components/chat/ToolCallBlock.vue +77 -0
- package/app/components/editor/CodeEditor.client.vue +212 -0
- package/app/components/editor/CodeEditorFallback.vue +12 -0
- package/app/components/editor/DocumentEditor.vue +326 -0
- package/app/components/editor/DocumentMetadata.vue +140 -0
- package/app/components/editor/MarkdownEditor.vue +146 -0
- package/app/components/files/FileTree.vue +436 -0
- package/app/components/hooks/HookActivityChart.client.vue +117 -0
- package/app/components/hooks/HookActivityChart.server.vue +25 -0
- package/app/components/hooks/HookStatsCards.vue +63 -0
- package/app/components/hooks/RecentEventsTable.vue +123 -0
- package/app/components/hooks/ToolBreakdownTable.vue +72 -0
- package/app/components/search/DashboardSearch.vue +122 -0
- package/app/components/tasks/ProjectSelect.vue +35 -0
- package/app/components/tasks/TaskCard.vue +182 -0
- package/app/components/tasks/TaskDetail.vue +160 -0
- package/app/components/tasks/TaskForm.vue +280 -0
- package/app/components/tasks/TaskList.vue +69 -0
- package/app/components/view/ViewToc.vue +85 -0
- package/app/composables/useAgents.ts +153 -0
- package/app/composables/useAuth.ts +73 -0
- package/app/composables/useChat.ts +298 -0
- package/app/composables/useDocument.ts +141 -0
- package/app/composables/useEditor.ts +100 -0
- package/app/composables/useFileTree.ts +220 -0
- package/app/composables/useHookEvents.ts +68 -0
- package/app/composables/useMemories.ts +83 -0
- package/app/composables/useNotificationBus.ts +154 -0
- package/app/composables/usePreferences.ts +131 -0
- package/app/composables/useProjects.ts +97 -0
- package/app/composables/useSearch.ts +52 -0
- package/app/composables/useTasks.ts +201 -0
- package/app/composables/useTerminal.ts +135 -0
- package/app/layouts/auth.vue +20 -0
- package/app/layouts/dashboard.vue +186 -0
- package/app/layouts/view.vue +60 -0
- package/app/middleware/auth.ts +9 -0
- package/app/pages/agents/[id].vue +602 -0
- package/app/pages/agents/index.vue +412 -0
- package/app/pages/chat.vue +146 -0
- package/app/pages/dashboard.vue +80 -0
- package/app/pages/docs.vue +131 -0
- package/app/pages/hooks.vue +163 -0
- package/app/pages/index.vue +249 -0
- package/app/pages/login.vue +60 -0
- package/app/pages/memories.vue +282 -0
- package/app/pages/settings.vue +625 -0
- package/app/pages/tasks.vue +312 -0
- package/app/pages/view/[uuid].vue +376 -0
- package/dist/cli/index.js +2711 -0
- package/drizzle.config.ts +10 -0
- package/nuxt.config.ts +98 -0
- package/package.json +107 -0
- package/server/api/agents/[id]/cancel.post.ts +27 -0
- package/server/api/agents/[id]/run.post.ts +34 -0
- package/server/api/agents/[id]/runs.get.ts +45 -0
- package/server/api/agents/[id]/stats.get.ts +94 -0
- package/server/api/agents/[id].delete.ts +29 -0
- package/server/api/agents/[id].get.ts +25 -0
- package/server/api/agents/[id].patch.ts +55 -0
- package/server/api/agents/index.get.ts +15 -0
- package/server/api/agents/index.post.ts +48 -0
- package/server/api/agents/stats.get.ts +86 -0
- package/server/api/auth/[...all].ts +5 -0
- package/server/api/conversations/[id].delete.ts +16 -0
- package/server/api/conversations/[id].get.ts +34 -0
- package/server/api/conversations/index.get.ts +17 -0
- package/server/api/documents/[id]/index.delete.ts +47 -0
- package/server/api/documents/[id]/index.put.ts +102 -0
- package/server/api/documents/[id]/public.get.ts +60 -0
- package/server/api/documents/[id]/restore.post.ts +65 -0
- package/server/api/documents/by-path.post.ts +168 -0
- package/server/api/documents/index.get.ts +48 -0
- package/server/api/fs/delete.post.ts +41 -0
- package/server/api/fs/list.get.ts +99 -0
- package/server/api/fs/mkdir.post.ts +44 -0
- package/server/api/fs/move.post.ts +68 -0
- package/server/api/fs/read.post.ts +48 -0
- package/server/api/fs/rename.post.ts +55 -0
- package/server/api/fs/write.post.ts +51 -0
- package/server/api/health.get.ts +40 -0
- package/server/api/home.get.ts +26 -0
- package/server/api/hooks/events/index.get.ts +56 -0
- package/server/api/hooks/events/index.post.ts +36 -0
- package/server/api/hooks/stats.get.ts +99 -0
- package/server/api/memory/[id].delete.ts +26 -0
- package/server/api/memory/context.get.ts +83 -0
- package/server/api/memory/extract.post.ts +42 -0
- package/server/api/memory/search.get.ts +70 -0
- package/server/api/memory/store.post.ts +31 -0
- package/server/api/projects/[id]/index.delete.ts +40 -0
- package/server/api/projects/[id]/index.get.ts +25 -0
- package/server/api/projects/[id]/index.put.ts +50 -0
- package/server/api/projects/index.get.ts +20 -0
- package/server/api/projects/index.post.ts +34 -0
- package/server/api/secrets/[key].delete.ts +31 -0
- package/server/api/secrets/[key].get.ts +30 -0
- package/server/api/secrets/[key].put.ts +52 -0
- package/server/api/secrets/index.get.ts +20 -0
- package/server/api/secrets/index.post.ts +58 -0
- package/server/api/tasks/[id]/index.delete.ts +46 -0
- package/server/api/tasks/[id]/index.get.ts +24 -0
- package/server/api/tasks/[id]/index.put.ts +70 -0
- package/server/api/tasks/[id]/restore.post.ts +49 -0
- package/server/api/tasks/index.get.ts +53 -0
- package/server/api/tasks/index.post.ts +47 -0
- package/server/api/tasks/tags.get.ts +21 -0
- package/server/api/user/email.patch.ts +56 -0
- package/server/db/index.ts +76 -0
- package/server/db/migrate.ts +41 -0
- package/server/db/schema.ts +345 -0
- package/server/db/seed.ts +46 -0
- package/server/db/types.ts +28 -0
- package/server/drizzle/migrations/0000_brown_george_stacy.sql +34 -0
- package/server/drizzle/migrations/0001_stormy_pyro.sql +16 -0
- package/server/drizzle/migrations/0002_clean_colossus.sql +50 -0
- package/server/drizzle/migrations/0003_fine_joystick.sql +12 -0
- package/server/drizzle/migrations/0004_tan_groot.sql +26 -0
- package/server/drizzle/migrations/0005_cloudy_lilith.sql +33 -0
- package/server/drizzle/migrations/0006_ordinary_retro_girl.sql +13 -0
- package/server/drizzle/migrations/0007_flowery_venus.sql +15 -0
- package/server/drizzle/migrations/0008_talented_zombie.sql +13 -0
- package/server/drizzle/migrations/0009_gray_shen.sql +15 -0
- package/server/drizzle/migrations/meta/0000_snapshot.json +230 -0
- package/server/drizzle/migrations/meta/0001_snapshot.json +306 -0
- package/server/drizzle/migrations/meta/0002_snapshot.json +615 -0
- package/server/drizzle/migrations/meta/0003_snapshot.json +730 -0
- package/server/drizzle/migrations/meta/0004_snapshot.json +916 -0
- package/server/drizzle/migrations/meta/0005_snapshot.json +1127 -0
- package/server/drizzle/migrations/meta/0006_snapshot.json +1213 -0
- package/server/drizzle/migrations/meta/0007_snapshot.json +1307 -0
- package/server/drizzle/migrations/meta/0008_snapshot.json +1390 -0
- package/server/drizzle/migrations/meta/0009_snapshot.json +1487 -0
- package/server/drizzle/migrations/meta/_journal.json +76 -0
- package/server/middleware/auth.ts +79 -0
- package/server/plugins/00.env-validate.ts +38 -0
- package/server/plugins/01.api-token.ts +31 -0
- package/server/plugins/02.database.ts +54 -0
- package/server/plugins/03.file-watcher.ts +65 -0
- package/server/plugins/04.cron-agents.ts +26 -0
- package/server/routes/_ws/chat.ts +252 -0
- package/server/routes/notifications.ts +47 -0
- package/server/routes/terminal.ts +98 -0
- package/server/services/agent-executor.ts +218 -0
- package/server/services/cron-scheduler.ts +78 -0
- package/server/services/memory-extractor.ts +120 -0
- package/server/utils/agent-cleanup.ts +91 -0
- package/server/utils/agent-registry.ts +95 -0
- package/server/utils/auth.ts +33 -0
- package/server/utils/chat-session-manager.ts +59 -0
- package/server/utils/crypto.ts +40 -0
- package/server/utils/db-guard.ts +12 -0
- package/server/utils/db-state.ts +63 -0
- package/server/utils/document-sync.ts +207 -0
- package/server/utils/frontmatter.ts +84 -0
- package/server/utils/notification-bus.ts +60 -0
- package/server/utils/path-validator.ts +55 -0
- package/server/utils/pty-manager.ts +130 -0
- package/shared/types/index.ts +604 -0
- package/shared/utils/language-detection.ts +87 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { CronAgent, CreateAgentInput, UpdateAgentInput, StatsPeriod, AgentGlobalStats } from '~~/shared/types'
|
|
3
|
+
|
|
4
|
+
definePageMeta({
|
|
5
|
+
layout: 'dashboard',
|
|
6
|
+
middleware: 'auth'
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
const toast = useToast()
|
|
10
|
+
const router = useRouter()
|
|
11
|
+
const { agents, loading, fetchAgents, createAgent, updateAgent, toggleEnabled, runAgent, cancelAgent, fetchGlobalStats } = useAgents()
|
|
12
|
+
const { isAgentRunning } = useNotificationBus()
|
|
13
|
+
|
|
14
|
+
// Stats state with persisted period preference
|
|
15
|
+
const { agentStatsPeriod } = usePreferences()
|
|
16
|
+
const period = ref<StatsPeriod>(agentStatsPeriod.value)
|
|
17
|
+
const stats = ref<AgentGlobalStats | null>(null)
|
|
18
|
+
const statsLoading = ref(false)
|
|
19
|
+
|
|
20
|
+
// Check if agent is running (from WebSocket OR from stats)
|
|
21
|
+
function checkAgentRunning(agentId: string): boolean {
|
|
22
|
+
// Check WebSocket notification state
|
|
23
|
+
if (isAgentRunning(agentId)) return true
|
|
24
|
+
// Also check stats for running agent IDs
|
|
25
|
+
return stats.value?.runningAgentIds?.includes(agentId) ?? false
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Slideover state
|
|
29
|
+
const showForm = ref(false)
|
|
30
|
+
const editingAgent = ref<CronAgent | null>(null)
|
|
31
|
+
|
|
32
|
+
// Period options
|
|
33
|
+
const periodOptions = [
|
|
34
|
+
{ label: '24h', value: '24h' as StatsPeriod },
|
|
35
|
+
{ label: '7d', value: '7d' as StatsPeriod },
|
|
36
|
+
{ label: '30d', value: '30d' as StatsPeriod }
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
// Format cron expression to human readable
|
|
40
|
+
function formatSchedule(schedule: string) {
|
|
41
|
+
const parts = schedule.split(' ')
|
|
42
|
+
if (parts.length !== 5) return schedule
|
|
43
|
+
|
|
44
|
+
const min = parts[0]!
|
|
45
|
+
const hour = parts[1]!
|
|
46
|
+
const dayOfMonth = parts[2]!
|
|
47
|
+
const month = parts[3]!
|
|
48
|
+
const dayOfWeek = parts[4]!
|
|
49
|
+
|
|
50
|
+
if (min === '0' && hour === '*' && dayOfMonth === '*' && month === '*' && dayOfWeek === '*')
|
|
51
|
+
return 'Every hour'
|
|
52
|
+
if (min === '*/5' && hour === '*' && dayOfMonth === '*' && month === '*' && dayOfWeek === '*')
|
|
53
|
+
return 'Every 5 minutes'
|
|
54
|
+
if (dayOfMonth === '*' && month === '*' && dayOfWeek === '*' && hour !== '*')
|
|
55
|
+
return `Daily at ${hour}:${min.padStart(2, '0')}`
|
|
56
|
+
if (dayOfWeek === '0' && dayOfMonth === '*' && month === '*')
|
|
57
|
+
return `Weekly on Sunday at ${hour}:${min.padStart(2, '0')}`
|
|
58
|
+
|
|
59
|
+
return schedule
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Format relative time
|
|
63
|
+
function formatRelativeTime(date?: Date | string) {
|
|
64
|
+
if (!date) return 'Never'
|
|
65
|
+
const d = new Date(date)
|
|
66
|
+
const now = new Date()
|
|
67
|
+
const diff = now.getTime() - d.getTime()
|
|
68
|
+
const minutes = Math.floor(diff / 60000)
|
|
69
|
+
const hours = Math.floor(diff / 3600000)
|
|
70
|
+
const days = Math.floor(diff / 86400000)
|
|
71
|
+
|
|
72
|
+
if (minutes < 1) return 'Just now'
|
|
73
|
+
if (minutes < 60) return `${minutes}m ago`
|
|
74
|
+
if (hours < 24) return `${hours}h ago`
|
|
75
|
+
return `${days}d ago`
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Open form for new agent
|
|
79
|
+
function openNewAgentForm() {
|
|
80
|
+
editingAgent.value = null
|
|
81
|
+
showForm.value = true
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Open form for editing
|
|
85
|
+
function openEditForm(agent: CronAgent, event: Event) {
|
|
86
|
+
event.stopPropagation()
|
|
87
|
+
editingAgent.value = agent
|
|
88
|
+
showForm.value = true
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Navigate to agent detail
|
|
92
|
+
function navigateToAgent(agent: CronAgent) {
|
|
93
|
+
router.push(`/agents/${agent.id}`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Handle form submission
|
|
97
|
+
async function handleSubmit(data: CreateAgentInput | UpdateAgentInput) {
|
|
98
|
+
try {
|
|
99
|
+
if (editingAgent.value) {
|
|
100
|
+
await updateAgent(editingAgent.value.id, data as UpdateAgentInput)
|
|
101
|
+
toast.add({
|
|
102
|
+
title: 'Agent updated',
|
|
103
|
+
color: 'success',
|
|
104
|
+
icon: 'i-lucide-check'
|
|
105
|
+
})
|
|
106
|
+
} else {
|
|
107
|
+
await createAgent(data as CreateAgentInput)
|
|
108
|
+
toast.add({
|
|
109
|
+
title: 'Agent created',
|
|
110
|
+
color: 'success',
|
|
111
|
+
icon: 'i-lucide-check'
|
|
112
|
+
})
|
|
113
|
+
// Refresh stats after creating
|
|
114
|
+
loadStats()
|
|
115
|
+
}
|
|
116
|
+
showForm.value = false
|
|
117
|
+
editingAgent.value = null
|
|
118
|
+
} catch (e) {
|
|
119
|
+
toast.add({
|
|
120
|
+
title: 'Failed to save agent',
|
|
121
|
+
description: e instanceof Error ? e.message : 'An unexpected error occurred',
|
|
122
|
+
color: 'error',
|
|
123
|
+
icon: 'i-lucide-alert-circle'
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Handle toggle enabled
|
|
129
|
+
async function handleToggle(id: string) {
|
|
130
|
+
try {
|
|
131
|
+
await toggleEnabled(id)
|
|
132
|
+
} catch {
|
|
133
|
+
toast.add({
|
|
134
|
+
title: 'Failed to toggle agent',
|
|
135
|
+
color: 'error',
|
|
136
|
+
icon: 'i-lucide-alert-circle'
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Handle run agent
|
|
142
|
+
async function handleRun(id: string, event: Event) {
|
|
143
|
+
event.stopPropagation()
|
|
144
|
+
try {
|
|
145
|
+
await runAgent(id)
|
|
146
|
+
} catch {
|
|
147
|
+
toast.add({
|
|
148
|
+
title: 'Failed to run agent',
|
|
149
|
+
color: 'error',
|
|
150
|
+
icon: 'i-lucide-alert-circle'
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Handle cancel agent
|
|
156
|
+
async function handleCancel(id: string, event: Event) {
|
|
157
|
+
event.stopPropagation()
|
|
158
|
+
try {
|
|
159
|
+
await cancelAgent(id)
|
|
160
|
+
toast.add({
|
|
161
|
+
title: 'Agent cancelled',
|
|
162
|
+
color: 'warning',
|
|
163
|
+
icon: 'i-lucide-x-circle'
|
|
164
|
+
})
|
|
165
|
+
} catch {
|
|
166
|
+
toast.add({
|
|
167
|
+
title: 'Failed to cancel agent',
|
|
168
|
+
color: 'error',
|
|
169
|
+
icon: 'i-lucide-alert-circle'
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Status badge color
|
|
175
|
+
function getStatusColor(status?: string) {
|
|
176
|
+
switch (status) {
|
|
177
|
+
case 'success': return 'success'
|
|
178
|
+
case 'error': return 'error'
|
|
179
|
+
case 'budget_exceeded': return 'warning'
|
|
180
|
+
case 'running': return 'info'
|
|
181
|
+
case 'cancelled': return 'neutral'
|
|
182
|
+
default: return 'neutral'
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Load stats
|
|
187
|
+
async function loadStats() {
|
|
188
|
+
statsLoading.value = true
|
|
189
|
+
try {
|
|
190
|
+
stats.value = await fetchGlobalStats(period.value)
|
|
191
|
+
} catch {
|
|
192
|
+
// Silently fail stats loading
|
|
193
|
+
} finally {
|
|
194
|
+
statsLoading.value = false
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Watch period changes and persist preference
|
|
199
|
+
watch(period, (value) => {
|
|
200
|
+
agentStatsPeriod.value = value
|
|
201
|
+
loadStats()
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
// Load data on mount
|
|
205
|
+
onMounted(async () => {
|
|
206
|
+
await fetchAgents()
|
|
207
|
+
loadStats()
|
|
208
|
+
})
|
|
209
|
+
</script>
|
|
210
|
+
|
|
211
|
+
<template>
|
|
212
|
+
<div class="contents">
|
|
213
|
+
<UDashboardPanel
|
|
214
|
+
id="agents"
|
|
215
|
+
grow
|
|
216
|
+
>
|
|
217
|
+
<template #header>
|
|
218
|
+
<UDashboardNavbar title="Scheduled Agents">
|
|
219
|
+
<template #right>
|
|
220
|
+
<UFieldGroup>
|
|
221
|
+
<UButton
|
|
222
|
+
v-for="opt in periodOptions"
|
|
223
|
+
:key="opt.value"
|
|
224
|
+
:color="period === opt.value ? 'primary' : 'neutral'"
|
|
225
|
+
:variant="period === opt.value ? 'solid' : 'ghost'"
|
|
226
|
+
size="sm"
|
|
227
|
+
@click="period = opt.value"
|
|
228
|
+
>
|
|
229
|
+
{{ opt.label }}
|
|
230
|
+
</UButton>
|
|
231
|
+
</UFieldGroup>
|
|
232
|
+
<UButton
|
|
233
|
+
icon="i-lucide-plus"
|
|
234
|
+
label="New Agent"
|
|
235
|
+
@click="openNewAgentForm"
|
|
236
|
+
/>
|
|
237
|
+
</template>
|
|
238
|
+
</UDashboardNavbar>
|
|
239
|
+
</template>
|
|
240
|
+
|
|
241
|
+
<template #body>
|
|
242
|
+
<div class="p-4 space-y-6">
|
|
243
|
+
<!-- Stats Cards -->
|
|
244
|
+
<AgentsAgentStatsCards
|
|
245
|
+
:stats="stats"
|
|
246
|
+
:loading="statsLoading"
|
|
247
|
+
variant="global"
|
|
248
|
+
/>
|
|
249
|
+
|
|
250
|
+
<!-- Activity Chart -->
|
|
251
|
+
<AgentsAgentActivityChart
|
|
252
|
+
v-if="stats && stats.dailyRuns.length > 0"
|
|
253
|
+
:data="stats.dailyRuns"
|
|
254
|
+
title="Run Activity"
|
|
255
|
+
/>
|
|
256
|
+
|
|
257
|
+
<!-- Loading state -->
|
|
258
|
+
<div
|
|
259
|
+
v-if="loading"
|
|
260
|
+
class="flex items-center justify-center h-64"
|
|
261
|
+
>
|
|
262
|
+
<UIcon
|
|
263
|
+
name="i-lucide-loader-2"
|
|
264
|
+
class="w-8 h-8 animate-spin text-neutral-400"
|
|
265
|
+
/>
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
<!-- Empty state -->
|
|
269
|
+
<div
|
|
270
|
+
v-else-if="agents.length === 0"
|
|
271
|
+
class="flex flex-col items-center justify-center h-64 text-neutral-500"
|
|
272
|
+
>
|
|
273
|
+
<UIcon
|
|
274
|
+
name="i-lucide-bot"
|
|
275
|
+
class="w-12 h-12 mb-4"
|
|
276
|
+
/>
|
|
277
|
+
<p class="text-lg font-medium">
|
|
278
|
+
No agents yet
|
|
279
|
+
</p>
|
|
280
|
+
<p class="text-sm">
|
|
281
|
+
Create your first scheduled agent to automate tasks
|
|
282
|
+
</p>
|
|
283
|
+
<UButton
|
|
284
|
+
class="mt-4"
|
|
285
|
+
icon="i-lucide-plus"
|
|
286
|
+
label="Create Agent"
|
|
287
|
+
@click="openNewAgentForm"
|
|
288
|
+
/>
|
|
289
|
+
</div>
|
|
290
|
+
|
|
291
|
+
<!-- Agent Cards -->
|
|
292
|
+
<div
|
|
293
|
+
v-else
|
|
294
|
+
class="space-y-4"
|
|
295
|
+
>
|
|
296
|
+
<h2 class="text-lg font-semibold">
|
|
297
|
+
Agents
|
|
298
|
+
</h2>
|
|
299
|
+
<UCard
|
|
300
|
+
v-for="agent in agents"
|
|
301
|
+
:key="agent.id"
|
|
302
|
+
class="cursor-pointer transition-all hover:ring-2 hover:ring-primary/50"
|
|
303
|
+
@click="navigateToAgent(agent)"
|
|
304
|
+
>
|
|
305
|
+
<div class="flex items-start justify-between gap-4">
|
|
306
|
+
<div class="flex-1 min-w-0">
|
|
307
|
+
<div class="flex items-center gap-2">
|
|
308
|
+
<h3 class="font-medium truncate">
|
|
309
|
+
{{ agent.name }}
|
|
310
|
+
</h3>
|
|
311
|
+
<UBadge
|
|
312
|
+
v-if="checkAgentRunning(agent.id)"
|
|
313
|
+
color="info"
|
|
314
|
+
variant="subtle"
|
|
315
|
+
size="sm"
|
|
316
|
+
>
|
|
317
|
+
<UIcon
|
|
318
|
+
name="i-lucide-loader-2"
|
|
319
|
+
class="w-3 h-3 animate-spin mr-1"
|
|
320
|
+
/>
|
|
321
|
+
Running
|
|
322
|
+
</UBadge>
|
|
323
|
+
<UBadge
|
|
324
|
+
v-else-if="agent.lastStatus"
|
|
325
|
+
:color="getStatusColor(agent.lastStatus)"
|
|
326
|
+
size="sm"
|
|
327
|
+
>
|
|
328
|
+
{{ agent.lastStatus }}
|
|
329
|
+
</UBadge>
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
<p
|
|
333
|
+
v-if="agent.description"
|
|
334
|
+
class="text-sm text-muted truncate mt-1"
|
|
335
|
+
>
|
|
336
|
+
{{ agent.description }}
|
|
337
|
+
</p>
|
|
338
|
+
|
|
339
|
+
<div class="flex items-center gap-4 mt-2 text-sm text-muted">
|
|
340
|
+
<span class="flex items-center gap-1">
|
|
341
|
+
<UIcon name="i-lucide-clock" />
|
|
342
|
+
{{ formatSchedule(agent.schedule) }}
|
|
343
|
+
</span>
|
|
344
|
+
<span
|
|
345
|
+
v-if="agent.lastRunAt"
|
|
346
|
+
class="flex items-center gap-1"
|
|
347
|
+
>
|
|
348
|
+
<UIcon name="i-lucide-activity" />
|
|
349
|
+
{{ formatRelativeTime(agent.lastRunAt) }}
|
|
350
|
+
</span>
|
|
351
|
+
<span
|
|
352
|
+
v-if="agent.maxBudgetUsd"
|
|
353
|
+
class="flex items-center gap-1"
|
|
354
|
+
>
|
|
355
|
+
<UIcon name="i-lucide-dollar-sign" />
|
|
356
|
+
${{ agent.maxBudgetUsd }} limit
|
|
357
|
+
</span>
|
|
358
|
+
</div>
|
|
359
|
+
</div>
|
|
360
|
+
|
|
361
|
+
<div class="flex items-center gap-2">
|
|
362
|
+
<USwitch
|
|
363
|
+
:model-value="agent.enabled"
|
|
364
|
+
@click.stop
|
|
365
|
+
@update:model-value="handleToggle(agent.id)"
|
|
366
|
+
/>
|
|
367
|
+
<UButton
|
|
368
|
+
v-if="checkAgentRunning(agent.id)"
|
|
369
|
+
icon="i-lucide-square"
|
|
370
|
+
variant="ghost"
|
|
371
|
+
size="sm"
|
|
372
|
+
color="error"
|
|
373
|
+
@click.stop="handleCancel(agent.id, $event)"
|
|
374
|
+
/>
|
|
375
|
+
<UButton
|
|
376
|
+
v-else
|
|
377
|
+
icon="i-lucide-play"
|
|
378
|
+
variant="ghost"
|
|
379
|
+
size="sm"
|
|
380
|
+
:disabled="!agent.enabled"
|
|
381
|
+
@click.stop="handleRun(agent.id, $event)"
|
|
382
|
+
/>
|
|
383
|
+
<UButton
|
|
384
|
+
icon="i-lucide-pencil"
|
|
385
|
+
variant="ghost"
|
|
386
|
+
size="sm"
|
|
387
|
+
@click.stop="openEditForm(agent, $event)"
|
|
388
|
+
/>
|
|
389
|
+
</div>
|
|
390
|
+
</div>
|
|
391
|
+
</UCard>
|
|
392
|
+
</div>
|
|
393
|
+
</div>
|
|
394
|
+
</template>
|
|
395
|
+
</UDashboardPanel>
|
|
396
|
+
|
|
397
|
+
<!-- Agent Form Slideover -->
|
|
398
|
+
<USlideover v-model:open="showForm">
|
|
399
|
+
<template #title>
|
|
400
|
+
{{ editingAgent ? 'Edit Agent' : 'New Agent' }}
|
|
401
|
+
</template>
|
|
402
|
+
|
|
403
|
+
<template #body>
|
|
404
|
+
<AgentsAgentForm
|
|
405
|
+
:agent="editingAgent"
|
|
406
|
+
@submit="handleSubmit"
|
|
407
|
+
@cancel="showForm = false"
|
|
408
|
+
/>
|
|
409
|
+
</template>
|
|
410
|
+
</USlideover>
|
|
411
|
+
</div>
|
|
412
|
+
</template>
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
definePageMeta({
|
|
3
|
+
layout: 'dashboard',
|
|
4
|
+
middleware: 'auth'
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
connectionStatus,
|
|
9
|
+
sessionStatus,
|
|
10
|
+
activeConversationId,
|
|
11
|
+
messages,
|
|
12
|
+
conversations,
|
|
13
|
+
streamingText,
|
|
14
|
+
streamingToolCalls,
|
|
15
|
+
connect,
|
|
16
|
+
sendMessage,
|
|
17
|
+
interrupt,
|
|
18
|
+
startNewConversation,
|
|
19
|
+
loadConversation,
|
|
20
|
+
loadConversations,
|
|
21
|
+
deleteConversation
|
|
22
|
+
} = useChat()
|
|
23
|
+
|
|
24
|
+
const messagesContainer = ref<HTMLElement | null>(null)
|
|
25
|
+
|
|
26
|
+
onMounted(() => {
|
|
27
|
+
connect()
|
|
28
|
+
loadConversations()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
function handleSend(message: string) {
|
|
32
|
+
sendMessage(message)
|
|
33
|
+
nextTick(scrollToBottom)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function scrollToBottom() {
|
|
37
|
+
if (messagesContainer.value)
|
|
38
|
+
messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Auto-scroll during streaming
|
|
42
|
+
watch(streamingText, () => nextTick(scrollToBottom))
|
|
43
|
+
|
|
44
|
+
const conversationTitle = computed(() => {
|
|
45
|
+
if (!activeConversationId.value) return 'New Chat'
|
|
46
|
+
const conv = conversations.value.find(c => c.id === activeConversationId.value)
|
|
47
|
+
return conv?.title || 'Chat'
|
|
48
|
+
})
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<template>
|
|
52
|
+
<div class="flex flex-1 min-w-0">
|
|
53
|
+
<UDashboardPanel
|
|
54
|
+
id="chat-sidebar"
|
|
55
|
+
:default-size="20"
|
|
56
|
+
:min-size="15"
|
|
57
|
+
:max-size="30"
|
|
58
|
+
collapsible
|
|
59
|
+
resizable
|
|
60
|
+
class="hidden lg:flex"
|
|
61
|
+
>
|
|
62
|
+
<ChatConversationList
|
|
63
|
+
:conversations="conversations"
|
|
64
|
+
:active-id="activeConversationId"
|
|
65
|
+
@select="loadConversation"
|
|
66
|
+
@delete="deleteConversation"
|
|
67
|
+
@new="startNewConversation"
|
|
68
|
+
/>
|
|
69
|
+
</UDashboardPanel>
|
|
70
|
+
|
|
71
|
+
<UDashboardPanel
|
|
72
|
+
id="chat-main"
|
|
73
|
+
grow
|
|
74
|
+
>
|
|
75
|
+
<template #header>
|
|
76
|
+
<UDashboardNavbar :title="conversationTitle">
|
|
77
|
+
<template #right>
|
|
78
|
+
<UBadge
|
|
79
|
+
v-if="sessionStatus === 'streaming'"
|
|
80
|
+
color="warning"
|
|
81
|
+
variant="subtle"
|
|
82
|
+
>
|
|
83
|
+
Streaming
|
|
84
|
+
</UBadge>
|
|
85
|
+
<UButton
|
|
86
|
+
icon="i-lucide-plus"
|
|
87
|
+
variant="ghost"
|
|
88
|
+
color="neutral"
|
|
89
|
+
class="lg:hidden"
|
|
90
|
+
@click="startNewConversation"
|
|
91
|
+
/>
|
|
92
|
+
</template>
|
|
93
|
+
</UDashboardNavbar>
|
|
94
|
+
</template>
|
|
95
|
+
|
|
96
|
+
<template #default>
|
|
97
|
+
<div class="flex flex-col h-full">
|
|
98
|
+
<!-- Messages area -->
|
|
99
|
+
<div
|
|
100
|
+
ref="messagesContainer"
|
|
101
|
+
class="flex-1 overflow-y-auto p-4 space-y-4"
|
|
102
|
+
>
|
|
103
|
+
<!-- Empty state -->
|
|
104
|
+
<div
|
|
105
|
+
v-if="messages.length === 0 && sessionStatus !== 'streaming'"
|
|
106
|
+
class="flex flex-col items-center justify-center h-full text-dimmed"
|
|
107
|
+
>
|
|
108
|
+
<UIcon
|
|
109
|
+
name="i-lucide-message-square"
|
|
110
|
+
class="size-12 mb-4"
|
|
111
|
+
/>
|
|
112
|
+
<p class="text-lg font-medium">
|
|
113
|
+
Start a conversation
|
|
114
|
+
</p>
|
|
115
|
+
<p class="text-sm mt-1">
|
|
116
|
+
Send a message to start chatting with Claude
|
|
117
|
+
</p>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<!-- Message list -->
|
|
121
|
+
<ChatMessageBubble
|
|
122
|
+
v-for="msg in messages"
|
|
123
|
+
:key="msg.id"
|
|
124
|
+
:message="msg"
|
|
125
|
+
/>
|
|
126
|
+
|
|
127
|
+
<!-- Streaming indicator -->
|
|
128
|
+
<ChatStreamingMessage
|
|
129
|
+
v-if="sessionStatus === 'streaming'"
|
|
130
|
+
:text="streamingText"
|
|
131
|
+
:tool-calls="streamingToolCalls"
|
|
132
|
+
/>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<!-- Input -->
|
|
136
|
+
<ChatInput
|
|
137
|
+
:session-status="sessionStatus"
|
|
138
|
+
:connection-status="connectionStatus"
|
|
139
|
+
@send="handleSend"
|
|
140
|
+
@interrupt="interrupt"
|
|
141
|
+
/>
|
|
142
|
+
</div>
|
|
143
|
+
</template>
|
|
144
|
+
</UDashboardPanel>
|
|
145
|
+
</div>
|
|
146
|
+
</template>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
definePageMeta({
|
|
3
|
+
layout: 'dashboard',
|
|
4
|
+
middleware: 'auth'
|
|
5
|
+
})
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<UDashboardPanel
|
|
10
|
+
id="dashboard"
|
|
11
|
+
grow
|
|
12
|
+
>
|
|
13
|
+
<UDashboardNavbar title="Dashboard">
|
|
14
|
+
<template #actions>
|
|
15
|
+
<UColorModeButton />
|
|
16
|
+
</template>
|
|
17
|
+
</UDashboardNavbar>
|
|
18
|
+
|
|
19
|
+
<div class="p-6 overflow-auto">
|
|
20
|
+
<div class="space-y-6">
|
|
21
|
+
<div>
|
|
22
|
+
<h1 class="text-2xl font-bold">
|
|
23
|
+
Welcome to Cognova
|
|
24
|
+
</h1>
|
|
25
|
+
<p class="text-muted mt-1">
|
|
26
|
+
Your personal knowledge management system.
|
|
27
|
+
</p>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
31
|
+
<UCard>
|
|
32
|
+
<template #header>
|
|
33
|
+
<div class="flex items-center gap-2">
|
|
34
|
+
<UIcon
|
|
35
|
+
name="i-lucide-check-square"
|
|
36
|
+
class="size-5 text-primary"
|
|
37
|
+
/>
|
|
38
|
+
<span class="font-semibold">Tasks Due Today</span>
|
|
39
|
+
</div>
|
|
40
|
+
</template>
|
|
41
|
+
<p class="text-muted text-sm">
|
|
42
|
+
No tasks due today.
|
|
43
|
+
</p>
|
|
44
|
+
</UCard>
|
|
45
|
+
|
|
46
|
+
<UCard>
|
|
47
|
+
<template #header>
|
|
48
|
+
<div class="flex items-center gap-2">
|
|
49
|
+
<UIcon
|
|
50
|
+
name="i-lucide-file-text"
|
|
51
|
+
class="size-5 text-primary"
|
|
52
|
+
/>
|
|
53
|
+
<span class="font-semibold">Recent Notes</span>
|
|
54
|
+
</div>
|
|
55
|
+
</template>
|
|
56
|
+
<p class="text-muted text-sm">
|
|
57
|
+
No recent notes.
|
|
58
|
+
</p>
|
|
59
|
+
</UCard>
|
|
60
|
+
|
|
61
|
+
<UCard>
|
|
62
|
+
<template #header>
|
|
63
|
+
<div class="flex items-center gap-2">
|
|
64
|
+
<UIcon
|
|
65
|
+
name="i-lucide-inbox"
|
|
66
|
+
class="size-5 text-primary"
|
|
67
|
+
/>
|
|
68
|
+
<span class="font-semibold">Quick Capture</span>
|
|
69
|
+
</div>
|
|
70
|
+
</template>
|
|
71
|
+
<UInput
|
|
72
|
+
placeholder="What's on your mind?"
|
|
73
|
+
class="mt-2"
|
|
74
|
+
/>
|
|
75
|
+
</UCard>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</UDashboardPanel>
|
|
80
|
+
</template>
|