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
package/nuxt.config.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
2
|
+
export default defineNuxtConfig({
|
|
3
|
+
modules: [
|
|
4
|
+
'@nuxt/eslint',
|
|
5
|
+
'@nuxt/ui',
|
|
6
|
+
'@nuxtjs/mdc'
|
|
7
|
+
],
|
|
8
|
+
|
|
9
|
+
devtools: {
|
|
10
|
+
enabled: true
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
css: ['~/assets/css/main.css'],
|
|
14
|
+
|
|
15
|
+
mdc: {
|
|
16
|
+
highlight: {
|
|
17
|
+
theme: {
|
|
18
|
+
default: 'github-light',
|
|
19
|
+
dark: 'github-dark'
|
|
20
|
+
},
|
|
21
|
+
langs: [
|
|
22
|
+
'typescript',
|
|
23
|
+
'javascript',
|
|
24
|
+
'ts',
|
|
25
|
+
'js',
|
|
26
|
+
'bash',
|
|
27
|
+
'shell',
|
|
28
|
+
'vue',
|
|
29
|
+
'html',
|
|
30
|
+
'css',
|
|
31
|
+
'scss',
|
|
32
|
+
'json',
|
|
33
|
+
'yaml',
|
|
34
|
+
'toml',
|
|
35
|
+
'xml',
|
|
36
|
+
'markdown',
|
|
37
|
+
'md',
|
|
38
|
+
'sql',
|
|
39
|
+
'graphql',
|
|
40
|
+
'python',
|
|
41
|
+
'go',
|
|
42
|
+
'rust',
|
|
43
|
+
'c',
|
|
44
|
+
'cpp',
|
|
45
|
+
'cs',
|
|
46
|
+
'java',
|
|
47
|
+
'dockerfile',
|
|
48
|
+
'diff',
|
|
49
|
+
'kotlin'
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
runtimeConfig: {
|
|
55
|
+
databaseUrl: process.env.DATABASE_URL || ''
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
alias: {
|
|
59
|
+
'@shared': '~/../../shared'
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
routeRules: {},
|
|
63
|
+
|
|
64
|
+
compatibilityDate: '2025-01-15',
|
|
65
|
+
|
|
66
|
+
// Enable WebSocket support for terminal
|
|
67
|
+
nitro: {
|
|
68
|
+
experimental: {
|
|
69
|
+
websocket: true
|
|
70
|
+
},
|
|
71
|
+
// node-pty is a native module that can't be bundled
|
|
72
|
+
externals: {
|
|
73
|
+
external: ['node-pty']
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
// Required for UEditor to avoid prosemirror plugin conflicts
|
|
78
|
+
vite: {
|
|
79
|
+
optimizeDeps: {
|
|
80
|
+
include: [
|
|
81
|
+
'@nuxt/ui > prosemirror-state',
|
|
82
|
+
'@nuxt/ui > prosemirror-transform',
|
|
83
|
+
'@nuxt/ui > prosemirror-model',
|
|
84
|
+
'@nuxt/ui > prosemirror-view',
|
|
85
|
+
'@nuxt/ui > prosemirror-gapcursor'
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
eslint: {
|
|
91
|
+
config: {
|
|
92
|
+
stylistic: {
|
|
93
|
+
commaDangle: 'never',
|
|
94
|
+
braceStyle: '1tbs'
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cognova",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Personal knowledge management system with Claude Code integration",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/Patrity/cognova.git"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"cognova": "./dist/cli/index.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist/cli",
|
|
15
|
+
"app",
|
|
16
|
+
"shared",
|
|
17
|
+
"server",
|
|
18
|
+
"Claude",
|
|
19
|
+
"nuxt.config.ts",
|
|
20
|
+
"tsconfig.json",
|
|
21
|
+
"drizzle.config.ts",
|
|
22
|
+
".env.example"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"cli:build": "pnpm --filter cognova-cli build",
|
|
26
|
+
"cli:dev": "pnpm --filter cognova-cli dev",
|
|
27
|
+
"build": "nuxt build",
|
|
28
|
+
"dev": "nuxt dev",
|
|
29
|
+
"preview": "nuxt preview",
|
|
30
|
+
"prepare": "nuxt prepare",
|
|
31
|
+
"lint": "eslint .",
|
|
32
|
+
"typecheck": "nuxt typecheck",
|
|
33
|
+
"db:generate": "drizzle-kit generate",
|
|
34
|
+
"db:migrate": "drizzle-kit migrate",
|
|
35
|
+
"db:push": "drizzle-kit push",
|
|
36
|
+
"db:studio": "drizzle-kit studio",
|
|
37
|
+
"db:up": "docker compose --profile local up -d db",
|
|
38
|
+
"db:down": "docker compose --profile local down",
|
|
39
|
+
"docker:up": "./scripts/docker-up.sh -d",
|
|
40
|
+
"docker:down": "docker compose --profile local down",
|
|
41
|
+
"auth:create-admin": "tsx scripts/create-admin.ts",
|
|
42
|
+
"auth:reset-pw": "tsx scripts/reset-password.ts"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.23",
|
|
46
|
+
"@anthropic-ai/sdk": "^0.72.1",
|
|
47
|
+
"@codemirror/lang-cpp": "^6.0.3",
|
|
48
|
+
"@codemirror/lang-css": "^6.3.1",
|
|
49
|
+
"@codemirror/lang-html": "^6.4.11",
|
|
50
|
+
"@codemirror/lang-java": "^6.0.2",
|
|
51
|
+
"@codemirror/lang-javascript": "^6.2.4",
|
|
52
|
+
"@codemirror/lang-json": "^6.0.2",
|
|
53
|
+
"@codemirror/lang-markdown": "^6.5.0",
|
|
54
|
+
"@codemirror/lang-python": "^6.2.1",
|
|
55
|
+
"@codemirror/lang-rust": "^6.0.2",
|
|
56
|
+
"@codemirror/lang-sql": "^6.10.0",
|
|
57
|
+
"@codemirror/lang-vue": "^0.1.3",
|
|
58
|
+
"@codemirror/lang-xml": "^6.1.0",
|
|
59
|
+
"@codemirror/lang-yaml": "^6.1.2",
|
|
60
|
+
"@codemirror/language": "^6.12.1",
|
|
61
|
+
"@codemirror/legacy-modes": "^6.5.2",
|
|
62
|
+
"@codemirror/state": "^6.5.4",
|
|
63
|
+
"@codemirror/theme-one-dark": "^6.1.3",
|
|
64
|
+
"@codemirror/view": "^6.39.11",
|
|
65
|
+
"@iconify-json/lucide": "^1.2.82",
|
|
66
|
+
"@iconify-json/simple-icons": "^1.2.63",
|
|
67
|
+
"@nuxt/ui": "^4.3.0",
|
|
68
|
+
"@nuxtjs/mdc": "^0.20.0",
|
|
69
|
+
"@unovis/ts": "^1.6.2",
|
|
70
|
+
"@unovis/vue": "^1.6.2",
|
|
71
|
+
"@vueuse/core": "^14.1.0",
|
|
72
|
+
"@vueuse/integrations": "^14.1.0",
|
|
73
|
+
"@xterm/addon-fit": "^0.11.0",
|
|
74
|
+
"@xterm/addon-web-links": "^0.12.0",
|
|
75
|
+
"@xterm/xterm": "^6.0.0",
|
|
76
|
+
"better-auth": "^1.4.17",
|
|
77
|
+
"chokidar": "^5.0.0",
|
|
78
|
+
"codemirror": "^6.0.2",
|
|
79
|
+
"cron": "^4.4.0",
|
|
80
|
+
"date-fns": "^4.1.0",
|
|
81
|
+
"dotenv": "^17.2.3",
|
|
82
|
+
"drizzle-orm": "^0.45.1",
|
|
83
|
+
"gray-matter": "^4.0.3",
|
|
84
|
+
"node-pty": "^1.1.0",
|
|
85
|
+
"nuxt": "^4.2.2",
|
|
86
|
+
"postgres": "^3.4.8",
|
|
87
|
+
"shiki": "^3.21.0",
|
|
88
|
+
"sortablejs": "^1.15.6",
|
|
89
|
+
"tailwindcss": "^4.0.0"
|
|
90
|
+
},
|
|
91
|
+
"devDependencies": {
|
|
92
|
+
"@nuxt/eslint": "^1.12.1",
|
|
93
|
+
"@types/node": "^25.0.8",
|
|
94
|
+
"@types/sortablejs": "^1.15.9",
|
|
95
|
+
"drizzle-kit": "^0.31.8",
|
|
96
|
+
"eslint": "^9.39.2",
|
|
97
|
+
"tsx": "^4.21.0",
|
|
98
|
+
"typescript": "^5.9.3",
|
|
99
|
+
"vue-tsc": "^3.2.1"
|
|
100
|
+
},
|
|
101
|
+
"packageManager": "pnpm@10.26.1",
|
|
102
|
+
"pnpm": {
|
|
103
|
+
"onlyBuiltDependencies": [
|
|
104
|
+
"node-pty"
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { agentRegistry } from '~~/server/utils/agent-registry'
|
|
2
|
+
import { requireDb } from '~~/server/utils/db-guard'
|
|
3
|
+
|
|
4
|
+
export default defineEventHandler(async (event) => {
|
|
5
|
+
requireDb(event)
|
|
6
|
+
|
|
7
|
+
const id = getRouterParam(event, 'id')
|
|
8
|
+
if (!id) {
|
|
9
|
+
throw createError({ statusCode: 400, message: 'Agent ID is required' })
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Cancel all running executions for this agent
|
|
13
|
+
const cancelledCount = agentRegistry.cancelByAgentId(id)
|
|
14
|
+
|
|
15
|
+
if (cancelledCount === 0) {
|
|
16
|
+
throw createError({
|
|
17
|
+
statusCode: 404,
|
|
18
|
+
message: 'No running executions found for this agent'
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
success: true,
|
|
24
|
+
message: `Cancelled ${cancelledCount} running execution(s)`,
|
|
25
|
+
cancelledCount
|
|
26
|
+
}
|
|
27
|
+
})
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { eq } from 'drizzle-orm'
|
|
2
|
+
import { getDb, schema } from '~~/server/db'
|
|
3
|
+
import { requireDb } from '~~/server/utils/db-guard'
|
|
4
|
+
import { executeAgent } from '~~/server/services/agent-executor'
|
|
5
|
+
|
|
6
|
+
export default defineEventHandler(async (event) => {
|
|
7
|
+
requireDb(event)
|
|
8
|
+
|
|
9
|
+
const id = getRouterParam(event, 'id')
|
|
10
|
+
if (!id) {
|
|
11
|
+
throw createError({ statusCode: 400, message: 'Agent ID is required' })
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const db = getDb()
|
|
15
|
+
|
|
16
|
+
const agent = await db.query.cronAgents.findFirst({
|
|
17
|
+
where: eq(schema.cronAgents.id, id)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
if (!agent) {
|
|
21
|
+
throw createError({ statusCode: 404, message: 'Agent not found' })
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Execute asynchronously (don't wait for completion)
|
|
25
|
+
executeAgent({
|
|
26
|
+
id: agent.id,
|
|
27
|
+
name: agent.name,
|
|
28
|
+
prompt: agent.prompt,
|
|
29
|
+
maxTurns: agent.maxTurns ?? 50,
|
|
30
|
+
maxBudgetUsd: agent.maxBudgetUsd ?? undefined
|
|
31
|
+
}).catch(err => console.error(`[agent] Manual run failed for ${agent.name}:`, err))
|
|
32
|
+
|
|
33
|
+
return { message: 'Agent started' }
|
|
34
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { eq, desc, and, gte } from 'drizzle-orm'
|
|
2
|
+
import { getDb, schema } from '~~/server/db'
|
|
3
|
+
import { requireDb } from '~~/server/utils/db-guard'
|
|
4
|
+
import type { StatsPeriod } from '~~/shared/types'
|
|
5
|
+
|
|
6
|
+
function getPeriodInterval(period: StatsPeriod): Date {
|
|
7
|
+
const now = new Date()
|
|
8
|
+
switch (period) {
|
|
9
|
+
case '24h':
|
|
10
|
+
return new Date(now.getTime() - 24 * 60 * 60 * 1000)
|
|
11
|
+
case '7d':
|
|
12
|
+
return new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
|
|
13
|
+
case '30d':
|
|
14
|
+
return new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default defineEventHandler(async (event) => {
|
|
19
|
+
requireDb(event)
|
|
20
|
+
|
|
21
|
+
const id = getRouterParam(event, 'id')
|
|
22
|
+
if (!id) {
|
|
23
|
+
throw createError({ statusCode: 400, message: 'Agent ID is required' })
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const query = getQuery(event)
|
|
27
|
+
const limit = parseInt(query.limit as string) || 100
|
|
28
|
+
const period = query.period as StatsPeriod | undefined
|
|
29
|
+
|
|
30
|
+
const db = getDb()
|
|
31
|
+
|
|
32
|
+
// Build where clause
|
|
33
|
+
const whereConditions = [eq(schema.cronAgentRuns.agentId, id)]
|
|
34
|
+
if (period) {
|
|
35
|
+
whereConditions.push(gte(schema.cronAgentRuns.startedAt, getPeriodInterval(period)))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const runs = await db.query.cronAgentRuns.findMany({
|
|
39
|
+
where: and(...whereConditions),
|
|
40
|
+
orderBy: [desc(schema.cronAgentRuns.startedAt)],
|
|
41
|
+
limit
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return { data: runs }
|
|
45
|
+
})
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { eq, and, gte } from 'drizzle-orm'
|
|
2
|
+
import { getDb, schema } from '~~/server/db'
|
|
3
|
+
import { requireDb } from '~~/server/utils/db-guard'
|
|
4
|
+
import type { StatsPeriod, AgentDetailStats, DailyRunData } from '~~/shared/types'
|
|
5
|
+
|
|
6
|
+
function getPeriodInterval(period: StatsPeriod): Date {
|
|
7
|
+
const now = new Date()
|
|
8
|
+
switch (period) {
|
|
9
|
+
case '24h':
|
|
10
|
+
return new Date(now.getTime() - 24 * 60 * 60 * 1000)
|
|
11
|
+
case '7d':
|
|
12
|
+
return new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
|
|
13
|
+
case '30d':
|
|
14
|
+
return new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default defineEventHandler(async (event) => {
|
|
19
|
+
requireDb(event)
|
|
20
|
+
|
|
21
|
+
const id = getRouterParam(event, 'id')
|
|
22
|
+
if (!id) {
|
|
23
|
+
throw createError({ statusCode: 400, message: 'Agent ID is required' })
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const query = getQuery(event)
|
|
27
|
+
const period = (query.period as StatsPeriod) || '7d'
|
|
28
|
+
const periodStart = getPeriodInterval(period)
|
|
29
|
+
|
|
30
|
+
const db = getDb()
|
|
31
|
+
|
|
32
|
+
// Get all runs for this agent in period
|
|
33
|
+
const runs = await db.query.cronAgentRuns.findMany({
|
|
34
|
+
where: and(
|
|
35
|
+
eq(schema.cronAgentRuns.agentId, id),
|
|
36
|
+
gte(schema.cronAgentRuns.startedAt, periodStart)
|
|
37
|
+
)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const totalRuns = runs.length
|
|
41
|
+
const successfulRuns = runs.filter(r => r.status === 'success').length
|
|
42
|
+
const successRate = totalRuns > 0 ? Math.round((successfulRuns / totalRuns) * 100) : 0
|
|
43
|
+
const totalCostUsd = runs.reduce((sum, r) => sum + (r.costUsd || 0), 0)
|
|
44
|
+
|
|
45
|
+
// Calculate average duration (only completed runs)
|
|
46
|
+
const completedRuns = runs.filter(r => r.durationMs != null)
|
|
47
|
+
const avgDurationMs = completedRuns.length > 0
|
|
48
|
+
? Math.round(completedRuns.reduce((sum, r) => sum + (r.durationMs || 0), 0) / completedRuns.length)
|
|
49
|
+
: 0
|
|
50
|
+
|
|
51
|
+
// Get last run time
|
|
52
|
+
const lastRun = runs.length > 0
|
|
53
|
+
? runs.reduce((latest, r) => r.startedAt > latest.startedAt ? r : latest)
|
|
54
|
+
: null
|
|
55
|
+
const lastRunAt = lastRun?.startedAt.toISOString() || null
|
|
56
|
+
|
|
57
|
+
// Get daily breakdown for chart
|
|
58
|
+
const dailyRunsMap = new Map<string, DailyRunData>()
|
|
59
|
+
|
|
60
|
+
for (const run of runs) {
|
|
61
|
+
const date = run.startedAt.toISOString().split('T')[0]!
|
|
62
|
+
const existing = dailyRunsMap.get(date) || {
|
|
63
|
+
date,
|
|
64
|
+
success: 0,
|
|
65
|
+
error: 0,
|
|
66
|
+
total: 0,
|
|
67
|
+
costUsd: 0
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
existing.total++
|
|
71
|
+
if (run.status === 'success') {
|
|
72
|
+
existing.success++
|
|
73
|
+
} else if (run.status === 'error' || run.status === 'budget_exceeded' || run.status === 'cancelled') {
|
|
74
|
+
existing.error++
|
|
75
|
+
}
|
|
76
|
+
existing.costUsd += run.costUsd || 0
|
|
77
|
+
|
|
78
|
+
dailyRunsMap.set(date, existing)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Sort by date
|
|
82
|
+
const dailyRuns = Array.from(dailyRunsMap.values()).sort((a, b) => a.date.localeCompare(b.date))
|
|
83
|
+
|
|
84
|
+
const stats: AgentDetailStats = {
|
|
85
|
+
totalRuns,
|
|
86
|
+
successRate,
|
|
87
|
+
avgDurationMs,
|
|
88
|
+
totalCostUsd,
|
|
89
|
+
lastRunAt,
|
|
90
|
+
dailyRuns
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { data: stats }
|
|
94
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { eq } from 'drizzle-orm'
|
|
2
|
+
import { getDb, schema } from '~~/server/db'
|
|
3
|
+
import { requireDb } from '~~/server/utils/db-guard'
|
|
4
|
+
import { unscheduleAgent } from '~~/server/services/cron-scheduler'
|
|
5
|
+
|
|
6
|
+
export default defineEventHandler(async (event) => {
|
|
7
|
+
requireDb(event)
|
|
8
|
+
|
|
9
|
+
const id = getRouterParam(event, 'id')
|
|
10
|
+
if (!id) {
|
|
11
|
+
throw createError({ statusCode: 400, message: 'Agent ID is required' })
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const db = getDb()
|
|
15
|
+
|
|
16
|
+
// Unschedule before deleting
|
|
17
|
+
unscheduleAgent(id)
|
|
18
|
+
|
|
19
|
+
// Delete agent (runs will cascade delete)
|
|
20
|
+
const [deleted] = await db.delete(schema.cronAgents)
|
|
21
|
+
.where(eq(schema.cronAgents.id, id))
|
|
22
|
+
.returning()
|
|
23
|
+
|
|
24
|
+
if (!deleted) {
|
|
25
|
+
throw createError({ statusCode: 404, message: 'Agent not found' })
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return { data: deleted }
|
|
29
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { eq } from 'drizzle-orm'
|
|
2
|
+
import { getDb, schema } from '~~/server/db'
|
|
3
|
+
import { requireDb } from '~~/server/utils/db-guard'
|
|
4
|
+
|
|
5
|
+
export default defineEventHandler(async (event) => {
|
|
6
|
+
requireDb(event)
|
|
7
|
+
|
|
8
|
+
const id = getRouterParam(event, 'id')
|
|
9
|
+
if (!id) {
|
|
10
|
+
throw createError({ statusCode: 400, message: 'Agent ID is required' })
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const db = getDb()
|
|
14
|
+
|
|
15
|
+
const agent = await db.query.cronAgents.findFirst({
|
|
16
|
+
where: eq(schema.cronAgents.id, id),
|
|
17
|
+
with: { creator: true }
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
if (!agent) {
|
|
21
|
+
throw createError({ statusCode: 404, message: 'Agent not found' })
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return { data: agent }
|
|
25
|
+
})
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { eq } from 'drizzle-orm'
|
|
2
|
+
import { getDb, schema } from '~~/server/db'
|
|
3
|
+
import { requireDb } from '~~/server/utils/db-guard'
|
|
4
|
+
import { scheduleAgent, unscheduleAgent } from '~~/server/services/cron-scheduler'
|
|
5
|
+
|
|
6
|
+
interface UpdateAgentBody {
|
|
7
|
+
name?: string
|
|
8
|
+
description?: string
|
|
9
|
+
schedule?: string
|
|
10
|
+
prompt?: string
|
|
11
|
+
enabled?: boolean
|
|
12
|
+
maxTurns?: number
|
|
13
|
+
maxBudgetUsd?: number | null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default defineEventHandler(async (event) => {
|
|
17
|
+
requireDb(event)
|
|
18
|
+
|
|
19
|
+
const id = getRouterParam(event, 'id')
|
|
20
|
+
if (!id) {
|
|
21
|
+
throw createError({ statusCode: 400, message: 'Agent ID is required' })
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const body = await readBody<UpdateAgentBody>(event)
|
|
25
|
+
const db = getDb()
|
|
26
|
+
|
|
27
|
+
// Get existing agent
|
|
28
|
+
const existing = await db.query.cronAgents.findFirst({
|
|
29
|
+
where: eq(schema.cronAgents.id, id)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
if (!existing) {
|
|
33
|
+
throw createError({ statusCode: 404, message: 'Agent not found' })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Update agent
|
|
37
|
+
const [agent] = await db.update(schema.cronAgents)
|
|
38
|
+
.set({
|
|
39
|
+
...body,
|
|
40
|
+
updatedAt: new Date()
|
|
41
|
+
})
|
|
42
|
+
.where(eq(schema.cronAgents.id, id))
|
|
43
|
+
.returning()
|
|
44
|
+
|
|
45
|
+
// Reschedule if schedule or enabled changed
|
|
46
|
+
if (body.schedule !== undefined || body.enabled !== undefined) {
|
|
47
|
+
if (agent!.enabled) {
|
|
48
|
+
scheduleAgent(agent!)
|
|
49
|
+
} else {
|
|
50
|
+
unscheduleAgent(agent!.id)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { data: agent }
|
|
55
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getDb } from '~~/server/db'
|
|
2
|
+
import { requireDb } from '~~/server/utils/db-guard'
|
|
3
|
+
|
|
4
|
+
export default defineEventHandler(async (event) => {
|
|
5
|
+
requireDb(event)
|
|
6
|
+
|
|
7
|
+
const db = getDb()
|
|
8
|
+
|
|
9
|
+
const agents = await db.query.cronAgents.findMany({
|
|
10
|
+
with: { creator: true },
|
|
11
|
+
orderBy: (agents, { asc }) => [asc(agents.name)]
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
return { data: agents }
|
|
15
|
+
})
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { getDb, schema } from '~~/server/db'
|
|
2
|
+
import { requireDb } from '~~/server/utils/db-guard'
|
|
3
|
+
import { scheduleAgent } from '~~/server/services/cron-scheduler'
|
|
4
|
+
|
|
5
|
+
interface CreateAgentBody {
|
|
6
|
+
name: string
|
|
7
|
+
description?: string
|
|
8
|
+
schedule: string
|
|
9
|
+
prompt: string
|
|
10
|
+
enabled?: boolean
|
|
11
|
+
maxTurns?: number
|
|
12
|
+
maxBudgetUsd?: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default defineEventHandler(async (event) => {
|
|
16
|
+
requireDb(event)
|
|
17
|
+
|
|
18
|
+
const body = await readBody<CreateAgentBody>(event)
|
|
19
|
+
|
|
20
|
+
if (!body.name || !body.schedule || !body.prompt) {
|
|
21
|
+
throw createError({
|
|
22
|
+
statusCode: 400,
|
|
23
|
+
message: 'Name, schedule, and prompt are required'
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const db = getDb()
|
|
28
|
+
|
|
29
|
+
const [agent] = await db.insert(schema.cronAgents)
|
|
30
|
+
.values({
|
|
31
|
+
name: body.name,
|
|
32
|
+
description: body.description,
|
|
33
|
+
schedule: body.schedule,
|
|
34
|
+
prompt: body.prompt,
|
|
35
|
+
enabled: body.enabled ?? true,
|
|
36
|
+
maxTurns: body.maxTurns ?? 50,
|
|
37
|
+
maxBudgetUsd: body.maxBudgetUsd,
|
|
38
|
+
createdBy: event.context.user?.id
|
|
39
|
+
})
|
|
40
|
+
.returning()
|
|
41
|
+
|
|
42
|
+
// Schedule if enabled
|
|
43
|
+
if (agent!.enabled) {
|
|
44
|
+
scheduleAgent(agent!)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { data: agent }
|
|
48
|
+
})
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { gte } from 'drizzle-orm'
|
|
2
|
+
import { getDb, schema } from '~~/server/db'
|
|
3
|
+
import { requireDb } from '~~/server/utils/db-guard'
|
|
4
|
+
import type { StatsPeriod, AgentGlobalStats, DailyRunData } from '~~/shared/types'
|
|
5
|
+
|
|
6
|
+
function getPeriodInterval(period: StatsPeriod): Date {
|
|
7
|
+
const now = new Date()
|
|
8
|
+
switch (period) {
|
|
9
|
+
case '24h':
|
|
10
|
+
return new Date(now.getTime() - 24 * 60 * 60 * 1000)
|
|
11
|
+
case '7d':
|
|
12
|
+
return new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
|
|
13
|
+
case '30d':
|
|
14
|
+
return new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default defineEventHandler(async (event) => {
|
|
19
|
+
requireDb(event)
|
|
20
|
+
|
|
21
|
+
const query = getQuery(event)
|
|
22
|
+
const period = (query.period as StatsPeriod) || '7d'
|
|
23
|
+
const periodStart = getPeriodInterval(period)
|
|
24
|
+
|
|
25
|
+
const db = getDb()
|
|
26
|
+
|
|
27
|
+
// Get agent counts
|
|
28
|
+
const agents = await db.query.cronAgents.findMany()
|
|
29
|
+
const totalAgents = agents.length
|
|
30
|
+
const activeAgents = agents.filter(a => a.enabled).length
|
|
31
|
+
|
|
32
|
+
// Get runs in period with aggregations
|
|
33
|
+
const runsInPeriod = await db.query.cronAgentRuns.findMany({
|
|
34
|
+
where: gte(schema.cronAgentRuns.startedAt, periodStart)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const totalRuns = runsInPeriod.length
|
|
38
|
+
const successfulRuns = runsInPeriod.filter(r => r.status === 'success').length
|
|
39
|
+
const successRate = totalRuns > 0 ? Math.round((successfulRuns / totalRuns) * 100) : 0
|
|
40
|
+
const totalCostUsd = runsInPeriod.reduce((sum, r) => sum + (r.costUsd || 0), 0)
|
|
41
|
+
|
|
42
|
+
// Get running agent IDs
|
|
43
|
+
const runningAgentIds = runsInPeriod
|
|
44
|
+
.filter(r => r.status === 'running')
|
|
45
|
+
.map(r => r.agentId)
|
|
46
|
+
.filter((id, index, arr) => arr.indexOf(id) === index) // unique
|
|
47
|
+
|
|
48
|
+
// Get daily breakdown for chart
|
|
49
|
+
const dailyRunsMap = new Map<string, DailyRunData>()
|
|
50
|
+
|
|
51
|
+
for (const run of runsInPeriod) {
|
|
52
|
+
const date = run.startedAt.toISOString().split('T')[0]!
|
|
53
|
+
const existing = dailyRunsMap.get(date) || {
|
|
54
|
+
date,
|
|
55
|
+
success: 0,
|
|
56
|
+
error: 0,
|
|
57
|
+
total: 0,
|
|
58
|
+
costUsd: 0
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
existing.total++
|
|
62
|
+
if (run.status === 'success') {
|
|
63
|
+
existing.success++
|
|
64
|
+
} else if (run.status === 'error' || run.status === 'budget_exceeded' || run.status === 'cancelled') {
|
|
65
|
+
existing.error++
|
|
66
|
+
}
|
|
67
|
+
existing.costUsd += run.costUsd || 0
|
|
68
|
+
|
|
69
|
+
dailyRunsMap.set(date, existing)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Sort by date
|
|
73
|
+
const dailyRuns = Array.from(dailyRunsMap.values()).sort((a, b) => a.date.localeCompare(b.date))
|
|
74
|
+
|
|
75
|
+
const stats: AgentGlobalStats = {
|
|
76
|
+
totalAgents,
|
|
77
|
+
activeAgents,
|
|
78
|
+
runsInPeriod: totalRuns,
|
|
79
|
+
successRate,
|
|
80
|
+
totalCostUsd,
|
|
81
|
+
runningAgentIds,
|
|
82
|
+
dailyRuns
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { data: stats }
|
|
86
|
+
})
|