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,60 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// Minimal layout for public document viewing
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<div>
|
|
7
|
+
<UHeader>
|
|
8
|
+
<template #left>
|
|
9
|
+
<NuxtLink
|
|
10
|
+
to="/"
|
|
11
|
+
class="flex items-center gap-2"
|
|
12
|
+
>
|
|
13
|
+
<UIcon
|
|
14
|
+
name="i-lucide-brain"
|
|
15
|
+
class="size-6 text-primary"
|
|
16
|
+
/>
|
|
17
|
+
<span class="font-semibold">Cognova</span>
|
|
18
|
+
</NuxtLink>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<template #right>
|
|
22
|
+
<UColorModeButton />
|
|
23
|
+
</template>
|
|
24
|
+
</UHeader>
|
|
25
|
+
|
|
26
|
+
<UMain>
|
|
27
|
+
<slot />
|
|
28
|
+
</UMain>
|
|
29
|
+
<USeparator
|
|
30
|
+
icon="i-lucide-brain"
|
|
31
|
+
:ui="{ icon: 'text-primary' }"
|
|
32
|
+
/>
|
|
33
|
+
<UFooter>
|
|
34
|
+
<template #left>
|
|
35
|
+
<p class="text-muted text-sm">
|
|
36
|
+
Shared via Cognova
|
|
37
|
+
</p>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<template #right>
|
|
41
|
+
<UButton
|
|
42
|
+
icon="i-simple-icons-github"
|
|
43
|
+
to="https://github.com/Patrity/cognova"
|
|
44
|
+
target="_blank"
|
|
45
|
+
aria-label="View on GitHub"
|
|
46
|
+
color="neutral"
|
|
47
|
+
variant="ghost"
|
|
48
|
+
/>
|
|
49
|
+
<UButton
|
|
50
|
+
icon="i-simple-icons-x"
|
|
51
|
+
to="https://x.com/Patrity/"
|
|
52
|
+
target="_blank"
|
|
53
|
+
aria-label="View Twitter"
|
|
54
|
+
color="neutral"
|
|
55
|
+
variant="ghost"
|
|
56
|
+
/>
|
|
57
|
+
</template>
|
|
58
|
+
</UFooter>
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { CronAgent, CronAgentRun, AgentDetailStats, StatsPeriod, CreateAgentInput, UpdateAgentInput } from '~~/shared/types'
|
|
3
|
+
|
|
4
|
+
definePageMeta({
|
|
5
|
+
layout: 'dashboard',
|
|
6
|
+
middleware: 'auth'
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
const route = useRoute()
|
|
10
|
+
const router = useRouter()
|
|
11
|
+
const toast = useToast()
|
|
12
|
+
const { fetchAgentStats, fetchRuns, updateAgent, deleteAgent, toggleEnabled, runAgent, cancelAgent } = useAgents()
|
|
13
|
+
const { isAgentRunning } = useNotificationBus()
|
|
14
|
+
|
|
15
|
+
const agentId = computed(() => route.params.id as string)
|
|
16
|
+
|
|
17
|
+
// Fetch agent data with SSR support
|
|
18
|
+
const { data: agentData, status: agentStatus, error: agentError, refresh: refreshAgent } = await useFetch<{ data: CronAgent }>(
|
|
19
|
+
() => `/api/agents/${agentId.value}`,
|
|
20
|
+
{ key: `agent-${agentId.value}` }
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
// Computed agent from response
|
|
24
|
+
const agent = computed(() => agentData.value?.data ?? null)
|
|
25
|
+
const loading = computed(() => agentStatus.value === 'pending')
|
|
26
|
+
|
|
27
|
+
// Handle agent not found
|
|
28
|
+
if (agentError.value) {
|
|
29
|
+
throw createError({
|
|
30
|
+
statusCode: agentError.value.statusCode || 404,
|
|
31
|
+
message: agentError.value.message || 'Agent not found'
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Data state for stats and runs (client-side)
|
|
36
|
+
const stats = ref<AgentDetailStats | null>(null)
|
|
37
|
+
const runs = ref<CronAgentRun[]>([])
|
|
38
|
+
const statsLoading = ref(false)
|
|
39
|
+
const runsLoading = ref(false)
|
|
40
|
+
|
|
41
|
+
// Check if agent is running (from WebSocket OR from runs list)
|
|
42
|
+
const agentRunning = computed(() => {
|
|
43
|
+
if (!agent.value) return false
|
|
44
|
+
// Check WebSocket notification state
|
|
45
|
+
if (isAgentRunning(agent.value.id)) return true
|
|
46
|
+
// Also check if any run in the list has 'running' status
|
|
47
|
+
return runs.value.some(r => r.status === 'running')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// Period state
|
|
51
|
+
const period = ref<StatsPeriod>('7d')
|
|
52
|
+
const periodOptions = [
|
|
53
|
+
{ label: '24h', value: '24h' as StatsPeriod },
|
|
54
|
+
{ label: '7d', value: '7d' as StatsPeriod },
|
|
55
|
+
{ label: '30d', value: '30d' as StatsPeriod }
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
// UI state
|
|
59
|
+
const showEditForm = ref(false)
|
|
60
|
+
const showDeleteConfirm = ref(false)
|
|
61
|
+
const selectedRun = ref<CronAgentRun | null>(null)
|
|
62
|
+
const showRunModal = ref(false)
|
|
63
|
+
|
|
64
|
+
// Format cron expression to human readable
|
|
65
|
+
function formatSchedule(schedule: string) {
|
|
66
|
+
const parts = schedule.split(' ')
|
|
67
|
+
if (parts.length !== 5) return schedule
|
|
68
|
+
|
|
69
|
+
const min = parts[0]!
|
|
70
|
+
const hour = parts[1]!
|
|
71
|
+
const dayOfMonth = parts[2]!
|
|
72
|
+
const month = parts[3]!
|
|
73
|
+
const dayOfWeek = parts[4]!
|
|
74
|
+
|
|
75
|
+
if (min === '0' && hour === '*' && dayOfMonth === '*' && month === '*' && dayOfWeek === '*')
|
|
76
|
+
return 'Every hour'
|
|
77
|
+
if (min === '*/5' && hour === '*' && dayOfMonth === '*' && month === '*' && dayOfWeek === '*')
|
|
78
|
+
return 'Every 5 minutes'
|
|
79
|
+
if (dayOfMonth === '*' && month === '*' && dayOfWeek === '*' && hour !== '*')
|
|
80
|
+
return `Daily at ${hour}:${min.padStart(2, '0')}`
|
|
81
|
+
if (dayOfWeek === '0' && dayOfMonth === '*' && month === '*')
|
|
82
|
+
return `Weekly on Sunday at ${hour}:${min.padStart(2, '0')}`
|
|
83
|
+
|
|
84
|
+
return schedule
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Format relative time
|
|
88
|
+
function formatRelativeTime(date?: Date | string) {
|
|
89
|
+
if (!date) return 'Never'
|
|
90
|
+
const d = new Date(date)
|
|
91
|
+
const now = new Date()
|
|
92
|
+
const diff = now.getTime() - d.getTime()
|
|
93
|
+
const minutes = Math.floor(diff / 60000)
|
|
94
|
+
const hours = Math.floor(diff / 3600000)
|
|
95
|
+
const days = Math.floor(diff / 86400000)
|
|
96
|
+
|
|
97
|
+
if (minutes < 1) return 'Just now'
|
|
98
|
+
if (minutes < 60) return `${minutes}m ago`
|
|
99
|
+
if (hours < 24) return `${hours}h ago`
|
|
100
|
+
return `${days}d ago`
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Format duration
|
|
104
|
+
function formatDuration(ms?: number) {
|
|
105
|
+
if (!ms) return '-'
|
|
106
|
+
if (ms < 1000) return `${ms}ms`
|
|
107
|
+
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`
|
|
108
|
+
return `${(ms / 60000).toFixed(1)}m`
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Format date time
|
|
112
|
+
function formatDateTime(date?: Date | string) {
|
|
113
|
+
if (!date) return '-'
|
|
114
|
+
const d = new Date(date)
|
|
115
|
+
return d.toLocaleString()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Status badge color
|
|
119
|
+
function getStatusColor(status?: string) {
|
|
120
|
+
switch (status) {
|
|
121
|
+
case 'success': return 'success'
|
|
122
|
+
case 'error': return 'error'
|
|
123
|
+
case 'budget_exceeded': return 'warning'
|
|
124
|
+
case 'running': return 'info'
|
|
125
|
+
case 'cancelled': return 'neutral'
|
|
126
|
+
default: return 'neutral'
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Load stats
|
|
131
|
+
async function loadStats() {
|
|
132
|
+
statsLoading.value = true
|
|
133
|
+
try {
|
|
134
|
+
stats.value = await fetchAgentStats(agentId.value, period.value)
|
|
135
|
+
} catch {
|
|
136
|
+
// Silently fail
|
|
137
|
+
} finally {
|
|
138
|
+
statsLoading.value = false
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Load runs
|
|
143
|
+
async function loadRuns() {
|
|
144
|
+
runsLoading.value = true
|
|
145
|
+
try {
|
|
146
|
+
runs.value = await fetchRuns(agentId.value, period.value)
|
|
147
|
+
} catch {
|
|
148
|
+
// Silently fail
|
|
149
|
+
} finally {
|
|
150
|
+
runsLoading.value = false
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Handle toggle
|
|
155
|
+
async function handleToggle() {
|
|
156
|
+
if (!agent.value) return
|
|
157
|
+
try {
|
|
158
|
+
await toggleEnabled(agent.value.id)
|
|
159
|
+
await refreshAgent()
|
|
160
|
+
} catch {
|
|
161
|
+
toast.add({
|
|
162
|
+
title: 'Failed to toggle agent',
|
|
163
|
+
color: 'error',
|
|
164
|
+
icon: 'i-lucide-alert-circle'
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Handle run
|
|
170
|
+
async function handleRun() {
|
|
171
|
+
if (!agent.value) return
|
|
172
|
+
try {
|
|
173
|
+
await runAgent(agent.value.id)
|
|
174
|
+
// Refresh runs after a short delay
|
|
175
|
+
setTimeout(loadRuns, 1000)
|
|
176
|
+
} catch {
|
|
177
|
+
toast.add({
|
|
178
|
+
title: 'Failed to run agent',
|
|
179
|
+
color: 'error',
|
|
180
|
+
icon: 'i-lucide-alert-circle'
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Handle cancel
|
|
186
|
+
async function handleCancel() {
|
|
187
|
+
if (!agent.value) return
|
|
188
|
+
try {
|
|
189
|
+
await cancelAgent(agent.value.id)
|
|
190
|
+
toast.add({
|
|
191
|
+
title: 'Agent cancelled',
|
|
192
|
+
color: 'warning',
|
|
193
|
+
icon: 'i-lucide-x-circle'
|
|
194
|
+
})
|
|
195
|
+
// Refresh runs after a short delay
|
|
196
|
+
setTimeout(() => {
|
|
197
|
+
loadRuns()
|
|
198
|
+
loadStats()
|
|
199
|
+
}, 1000)
|
|
200
|
+
} catch {
|
|
201
|
+
toast.add({
|
|
202
|
+
title: 'Failed to cancel agent',
|
|
203
|
+
description: 'No running execution found',
|
|
204
|
+
color: 'error',
|
|
205
|
+
icon: 'i-lucide-alert-circle'
|
|
206
|
+
})
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Handle edit submit
|
|
211
|
+
async function handleEditSubmit(data: CreateAgentInput | UpdateAgentInput) {
|
|
212
|
+
if (!agent.value) return
|
|
213
|
+
try {
|
|
214
|
+
await updateAgent(agent.value.id, data as UpdateAgentInput)
|
|
215
|
+
toast.add({
|
|
216
|
+
title: 'Agent updated',
|
|
217
|
+
color: 'success',
|
|
218
|
+
icon: 'i-lucide-check'
|
|
219
|
+
})
|
|
220
|
+
showEditForm.value = false
|
|
221
|
+
refreshAgent()
|
|
222
|
+
loadStats()
|
|
223
|
+
} catch (e) {
|
|
224
|
+
toast.add({
|
|
225
|
+
title: 'Failed to update agent',
|
|
226
|
+
description: e instanceof Error ? e.message : 'An unexpected error occurred',
|
|
227
|
+
color: 'error',
|
|
228
|
+
icon: 'i-lucide-alert-circle'
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Handle delete
|
|
234
|
+
async function handleDelete() {
|
|
235
|
+
if (!agent.value) return
|
|
236
|
+
try {
|
|
237
|
+
await deleteAgent(agent.value.id)
|
|
238
|
+
toast.add({
|
|
239
|
+
title: 'Agent deleted',
|
|
240
|
+
color: 'success',
|
|
241
|
+
icon: 'i-lucide-check'
|
|
242
|
+
})
|
|
243
|
+
router.push('/agents')
|
|
244
|
+
} catch {
|
|
245
|
+
toast.add({
|
|
246
|
+
title: 'Failed to delete agent',
|
|
247
|
+
color: 'error',
|
|
248
|
+
icon: 'i-lucide-alert-circle'
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Open run modal
|
|
254
|
+
function openRunModal(run: CronAgentRun) {
|
|
255
|
+
selectedRun.value = run
|
|
256
|
+
showRunModal.value = true
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Truncate output for list display
|
|
260
|
+
function truncateOutput(output?: string, maxLength = 100) {
|
|
261
|
+
if (!output) return '-'
|
|
262
|
+
if (output.length <= maxLength) return output
|
|
263
|
+
return output.substring(0, maxLength) + '...'
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Watch period changes
|
|
267
|
+
watch(period, () => {
|
|
268
|
+
loadStats()
|
|
269
|
+
loadRuns()
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
// Load stats and runs on mount (agent is already fetched via useFetch)
|
|
273
|
+
onMounted(() => {
|
|
274
|
+
loadStats()
|
|
275
|
+
loadRuns()
|
|
276
|
+
})
|
|
277
|
+
</script>
|
|
278
|
+
|
|
279
|
+
<template>
|
|
280
|
+
<div class="contents">
|
|
281
|
+
<UDashboardPanel
|
|
282
|
+
id="agent-detail"
|
|
283
|
+
grow
|
|
284
|
+
>
|
|
285
|
+
<template #header>
|
|
286
|
+
<UDashboardNavbar>
|
|
287
|
+
<template #left>
|
|
288
|
+
<div class="flex items-center gap-3">
|
|
289
|
+
<UButton
|
|
290
|
+
icon="i-lucide-arrow-left"
|
|
291
|
+
variant="ghost"
|
|
292
|
+
size="sm"
|
|
293
|
+
@click="router.push('/agents')"
|
|
294
|
+
/>
|
|
295
|
+
<template v-if="agent">
|
|
296
|
+
<h1 class="font-semibold text-lg truncate">
|
|
297
|
+
{{ agent.name }}
|
|
298
|
+
</h1>
|
|
299
|
+
<UBadge
|
|
300
|
+
v-if="agentRunning"
|
|
301
|
+
color="info"
|
|
302
|
+
variant="subtle"
|
|
303
|
+
>
|
|
304
|
+
<UIcon
|
|
305
|
+
name="i-lucide-loader-2"
|
|
306
|
+
class="w-3 h-3 animate-spin mr-1"
|
|
307
|
+
/>
|
|
308
|
+
Running
|
|
309
|
+
</UBadge>
|
|
310
|
+
<UBadge
|
|
311
|
+
v-else-if="!agent.enabled"
|
|
312
|
+
color="neutral"
|
|
313
|
+
variant="subtle"
|
|
314
|
+
>
|
|
315
|
+
Disabled
|
|
316
|
+
</UBadge>
|
|
317
|
+
</template>
|
|
318
|
+
<USkeleton
|
|
319
|
+
v-else
|
|
320
|
+
class="h-6 w-32"
|
|
321
|
+
/>
|
|
322
|
+
</div>
|
|
323
|
+
</template>
|
|
324
|
+
|
|
325
|
+
<template #right>
|
|
326
|
+
<template v-if="agent">
|
|
327
|
+
<USwitch
|
|
328
|
+
:model-value="agent.enabled"
|
|
329
|
+
@update:model-value="handleToggle"
|
|
330
|
+
/>
|
|
331
|
+
<UButton
|
|
332
|
+
v-if="agentRunning"
|
|
333
|
+
icon="i-lucide-square"
|
|
334
|
+
variant="ghost"
|
|
335
|
+
color="error"
|
|
336
|
+
@click="handleCancel"
|
|
337
|
+
/>
|
|
338
|
+
<UButton
|
|
339
|
+
v-else
|
|
340
|
+
icon="i-lucide-play"
|
|
341
|
+
variant="ghost"
|
|
342
|
+
:disabled="!agent.enabled"
|
|
343
|
+
@click="handleRun"
|
|
344
|
+
/>
|
|
345
|
+
<UButton
|
|
346
|
+
icon="i-lucide-pencil"
|
|
347
|
+
variant="ghost"
|
|
348
|
+
@click="showEditForm = true"
|
|
349
|
+
/>
|
|
350
|
+
<UButton
|
|
351
|
+
icon="i-lucide-trash-2"
|
|
352
|
+
variant="ghost"
|
|
353
|
+
color="error"
|
|
354
|
+
@click="showDeleteConfirm = true"
|
|
355
|
+
/>
|
|
356
|
+
</template>
|
|
357
|
+
</template>
|
|
358
|
+
</UDashboardNavbar>
|
|
359
|
+
|
|
360
|
+
<UDashboardToolbar>
|
|
361
|
+
<template #left>
|
|
362
|
+
<UFieldGroup>
|
|
363
|
+
<UButton
|
|
364
|
+
v-for="opt in periodOptions"
|
|
365
|
+
:key="opt.value"
|
|
366
|
+
:color="period === opt.value ? 'primary' : 'neutral'"
|
|
367
|
+
:variant="period === opt.value ? 'solid' : 'ghost'"
|
|
368
|
+
size="sm"
|
|
369
|
+
@click="period = opt.value"
|
|
370
|
+
>
|
|
371
|
+
{{ opt.label }}
|
|
372
|
+
</UButton>
|
|
373
|
+
</UFieldGroup>
|
|
374
|
+
</template>
|
|
375
|
+
</UDashboardToolbar>
|
|
376
|
+
</template>
|
|
377
|
+
|
|
378
|
+
<template #body>
|
|
379
|
+
<!-- Loading state -->
|
|
380
|
+
<div
|
|
381
|
+
v-if="loading"
|
|
382
|
+
class="flex items-center justify-center h-64"
|
|
383
|
+
>
|
|
384
|
+
<UIcon
|
|
385
|
+
name="i-lucide-loader-2"
|
|
386
|
+
class="w-8 h-8 animate-spin text-neutral-400"
|
|
387
|
+
/>
|
|
388
|
+
</div>
|
|
389
|
+
|
|
390
|
+
<div
|
|
391
|
+
v-else-if="agent"
|
|
392
|
+
class="p-4 space-y-6"
|
|
393
|
+
>
|
|
394
|
+
<!-- Agent Info -->
|
|
395
|
+
<UCard>
|
|
396
|
+
<div class="space-y-3">
|
|
397
|
+
<div v-if="agent.description">
|
|
398
|
+
<p class="text-sm text-muted uppercase mb-1">
|
|
399
|
+
Description
|
|
400
|
+
</p>
|
|
401
|
+
<p>{{ agent.description }}</p>
|
|
402
|
+
</div>
|
|
403
|
+
|
|
404
|
+
<div class="flex flex-wrap gap-6">
|
|
405
|
+
<div>
|
|
406
|
+
<p class="text-sm text-muted uppercase mb-1">
|
|
407
|
+
Schedule
|
|
408
|
+
</p>
|
|
409
|
+
<p class="flex items-center gap-2">
|
|
410
|
+
<UIcon name="i-lucide-clock" />
|
|
411
|
+
{{ formatSchedule(agent.schedule) }}
|
|
412
|
+
<span class="text-sm text-muted">({{ agent.schedule }})</span>
|
|
413
|
+
</p>
|
|
414
|
+
</div>
|
|
415
|
+
|
|
416
|
+
<div v-if="agent.maxBudgetUsd">
|
|
417
|
+
<p class="text-sm text-muted uppercase mb-1">
|
|
418
|
+
Budget Limit
|
|
419
|
+
</p>
|
|
420
|
+
<p class="flex items-center gap-2">
|
|
421
|
+
<UIcon name="i-lucide-dollar-sign" />
|
|
422
|
+
${{ agent.maxBudgetUsd }} per run
|
|
423
|
+
</p>
|
|
424
|
+
</div>
|
|
425
|
+
|
|
426
|
+
<div v-if="agent.lastRunAt">
|
|
427
|
+
<p class="text-sm text-muted uppercase mb-1">
|
|
428
|
+
Last Run
|
|
429
|
+
</p>
|
|
430
|
+
<p class="flex items-center gap-2">
|
|
431
|
+
<UIcon name="i-lucide-activity" />
|
|
432
|
+
{{ formatRelativeTime(agent.lastRunAt) }}
|
|
433
|
+
</p>
|
|
434
|
+
</div>
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
437
|
+
</UCard>
|
|
438
|
+
|
|
439
|
+
<!-- Stats Cards -->
|
|
440
|
+
<AgentsAgentStatsCards
|
|
441
|
+
:stats="stats"
|
|
442
|
+
:loading="statsLoading"
|
|
443
|
+
variant="detail"
|
|
444
|
+
/>
|
|
445
|
+
|
|
446
|
+
<!-- Activity Chart -->
|
|
447
|
+
<AgentsAgentActivityChart
|
|
448
|
+
v-if="stats && stats.dailyRuns.length > 0"
|
|
449
|
+
:data="stats.dailyRuns"
|
|
450
|
+
title="Run Activity"
|
|
451
|
+
/>
|
|
452
|
+
|
|
453
|
+
<!-- Run History -->
|
|
454
|
+
<div class="space-y-4">
|
|
455
|
+
<h2 class="text-lg font-semibold">
|
|
456
|
+
Run History
|
|
457
|
+
</h2>
|
|
458
|
+
|
|
459
|
+
<!-- Loading runs -->
|
|
460
|
+
<div
|
|
461
|
+
v-if="runsLoading"
|
|
462
|
+
class="flex items-center justify-center h-32"
|
|
463
|
+
>
|
|
464
|
+
<UIcon
|
|
465
|
+
name="i-lucide-loader-2"
|
|
466
|
+
class="w-6 h-6 animate-spin text-neutral-400"
|
|
467
|
+
/>
|
|
468
|
+
</div>
|
|
469
|
+
|
|
470
|
+
<!-- Empty state -->
|
|
471
|
+
<div
|
|
472
|
+
v-else-if="runs.length === 0"
|
|
473
|
+
class="flex flex-col items-center justify-center h-32 text-neutral-500"
|
|
474
|
+
>
|
|
475
|
+
<UIcon
|
|
476
|
+
name="i-lucide-history"
|
|
477
|
+
class="w-8 h-8 mb-2"
|
|
478
|
+
/>
|
|
479
|
+
<p>No runs yet</p>
|
|
480
|
+
</div>
|
|
481
|
+
|
|
482
|
+
<!-- Run list -->
|
|
483
|
+
<div
|
|
484
|
+
v-else
|
|
485
|
+
class="space-y-2"
|
|
486
|
+
>
|
|
487
|
+
<UCard
|
|
488
|
+
v-for="run in runs"
|
|
489
|
+
:key="run.id"
|
|
490
|
+
class="cursor-pointer transition-all hover:ring-2 hover:ring-primary/50"
|
|
491
|
+
@click="openRunModal(run)"
|
|
492
|
+
>
|
|
493
|
+
<div class="flex items-start justify-between gap-4">
|
|
494
|
+
<div class="flex-1 min-w-0">
|
|
495
|
+
<div class="flex items-center gap-2 mb-1">
|
|
496
|
+
<UBadge
|
|
497
|
+
v-if="run.status === 'running'"
|
|
498
|
+
color="info"
|
|
499
|
+
variant="subtle"
|
|
500
|
+
size="sm"
|
|
501
|
+
>
|
|
502
|
+
<UIcon
|
|
503
|
+
name="i-lucide-loader-2"
|
|
504
|
+
class="w-3 h-3 animate-spin mr-1"
|
|
505
|
+
/>
|
|
506
|
+
Running
|
|
507
|
+
</UBadge>
|
|
508
|
+
<UBadge
|
|
509
|
+
v-else
|
|
510
|
+
:color="getStatusColor(run.status)"
|
|
511
|
+
size="sm"
|
|
512
|
+
>
|
|
513
|
+
{{ run.status }}
|
|
514
|
+
</UBadge>
|
|
515
|
+
<span class="text-sm text-muted">
|
|
516
|
+
{{ formatDateTime(run.startedAt) }}
|
|
517
|
+
</span>
|
|
518
|
+
</div>
|
|
519
|
+
|
|
520
|
+
<p class="text-sm text-muted truncate">
|
|
521
|
+
{{ run.error ? truncateOutput(run.error) : truncateOutput(run.output) }}
|
|
522
|
+
</p>
|
|
523
|
+
</div>
|
|
524
|
+
|
|
525
|
+
<div class="text-right text-sm text-muted shrink-0">
|
|
526
|
+
<p>{{ formatDuration(run.durationMs) }}</p>
|
|
527
|
+
<p v-if="run.costUsd">
|
|
528
|
+
${{ run.costUsd.toFixed(4) }}
|
|
529
|
+
</p>
|
|
530
|
+
</div>
|
|
531
|
+
</div>
|
|
532
|
+
</UCard>
|
|
533
|
+
</div>
|
|
534
|
+
</div>
|
|
535
|
+
</div>
|
|
536
|
+
</template>
|
|
537
|
+
</UDashboardPanel>
|
|
538
|
+
|
|
539
|
+
<!-- Edit Slideover -->
|
|
540
|
+
<USlideover v-model:open="showEditForm">
|
|
541
|
+
<template #title>
|
|
542
|
+
Edit Agent
|
|
543
|
+
</template>
|
|
544
|
+
|
|
545
|
+
<template #body>
|
|
546
|
+
<AgentsAgentForm
|
|
547
|
+
:agent="agent"
|
|
548
|
+
@submit="handleEditSubmit"
|
|
549
|
+
@cancel="showEditForm = false"
|
|
550
|
+
/>
|
|
551
|
+
</template>
|
|
552
|
+
</USlideover>
|
|
553
|
+
|
|
554
|
+
<!-- Delete Confirmation Modal -->
|
|
555
|
+
<UModal v-model:open="showDeleteConfirm">
|
|
556
|
+
<template #content>
|
|
557
|
+
<div class="p-4 space-y-4">
|
|
558
|
+
<div class="flex items-center gap-3">
|
|
559
|
+
<div class="p-2 rounded-full bg-error-500/10">
|
|
560
|
+
<UIcon
|
|
561
|
+
name="i-lucide-alert-triangle"
|
|
562
|
+
class="w-6 h-6 text-error-500"
|
|
563
|
+
/>
|
|
564
|
+
</div>
|
|
565
|
+
<div>
|
|
566
|
+
<h3 class="font-semibold">
|
|
567
|
+
Delete Agent
|
|
568
|
+
</h3>
|
|
569
|
+
<p class="text-sm text-muted">
|
|
570
|
+
Are you sure you want to delete "{{ agent?.name }}"? This action cannot be undone.
|
|
571
|
+
</p>
|
|
572
|
+
</div>
|
|
573
|
+
</div>
|
|
574
|
+
</div>
|
|
575
|
+
</template>
|
|
576
|
+
|
|
577
|
+
<template #footer>
|
|
578
|
+
<div class="flex justify-end gap-2">
|
|
579
|
+
<UButton
|
|
580
|
+
color="neutral"
|
|
581
|
+
variant="ghost"
|
|
582
|
+
@click="showDeleteConfirm = false"
|
|
583
|
+
>
|
|
584
|
+
Cancel
|
|
585
|
+
</UButton>
|
|
586
|
+
<UButton
|
|
587
|
+
color="error"
|
|
588
|
+
@click="handleDelete"
|
|
589
|
+
>
|
|
590
|
+
Delete
|
|
591
|
+
</UButton>
|
|
592
|
+
</div>
|
|
593
|
+
</template>
|
|
594
|
+
</UModal>
|
|
595
|
+
|
|
596
|
+
<!-- Run Detail Modal -->
|
|
597
|
+
<AgentsAgentRunModal
|
|
598
|
+
v-model:open="showRunModal"
|
|
599
|
+
:run="selectedRun"
|
|
600
|
+
/>
|
|
601
|
+
</div>
|
|
602
|
+
</template>
|