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,625 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
interface Secret {
|
|
3
|
+
id: string
|
|
4
|
+
key: string
|
|
5
|
+
description: string | null
|
|
6
|
+
createdAt: string
|
|
7
|
+
updatedAt: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
definePageMeta({
|
|
11
|
+
layout: 'dashboard',
|
|
12
|
+
middleware: 'auth'
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const { user, updateProfile, changeEmail, changePassword } = useAuth()
|
|
16
|
+
const toast = useToast()
|
|
17
|
+
|
|
18
|
+
// Profile form state
|
|
19
|
+
const profileState = reactive({
|
|
20
|
+
name: ''
|
|
21
|
+
})
|
|
22
|
+
const profileLoading = ref(false)
|
|
23
|
+
|
|
24
|
+
// Email form state
|
|
25
|
+
const emailState = reactive({
|
|
26
|
+
newEmail: ''
|
|
27
|
+
})
|
|
28
|
+
const emailLoading = ref(false)
|
|
29
|
+
|
|
30
|
+
// Password form state
|
|
31
|
+
const passwordState = reactive({
|
|
32
|
+
currentPassword: '',
|
|
33
|
+
newPassword: '',
|
|
34
|
+
confirmPassword: ''
|
|
35
|
+
})
|
|
36
|
+
const passwordLoading = ref(false)
|
|
37
|
+
|
|
38
|
+
// Secrets state
|
|
39
|
+
const secretsData = ref<Secret[]>([])
|
|
40
|
+
const secretsLoading = ref(false)
|
|
41
|
+
const secretModal = ref(false)
|
|
42
|
+
const secretDeleteConfirm = ref(false)
|
|
43
|
+
const secretToDelete = ref<Secret | null>(null)
|
|
44
|
+
const secretSaving = ref(false)
|
|
45
|
+
const editingSecret = ref<Secret | null>(null)
|
|
46
|
+
|
|
47
|
+
const secretForm = reactive({
|
|
48
|
+
key: '',
|
|
49
|
+
value: '',
|
|
50
|
+
description: ''
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// Initialize forms with current user data
|
|
54
|
+
watch(() => user.value, (u) => {
|
|
55
|
+
if (u) {
|
|
56
|
+
profileState.name = u.name || ''
|
|
57
|
+
emailState.newEmail = u.email || ''
|
|
58
|
+
}
|
|
59
|
+
}, { immediate: true })
|
|
60
|
+
|
|
61
|
+
// Fetch secrets
|
|
62
|
+
async function fetchSecrets() {
|
|
63
|
+
secretsLoading.value = true
|
|
64
|
+
try {
|
|
65
|
+
const { data } = await $fetch<{ data: Secret[] }>('/api/secrets')
|
|
66
|
+
secretsData.value = data
|
|
67
|
+
} catch {
|
|
68
|
+
toast.add({
|
|
69
|
+
title: 'Failed to load secrets',
|
|
70
|
+
color: 'error'
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
secretsLoading.value = false
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Tab items
|
|
77
|
+
const tabs = [
|
|
78
|
+
{ label: 'Account', icon: 'i-lucide-user', value: 'account', slot: 'account' },
|
|
79
|
+
{ label: 'Secrets', icon: 'i-lucide-key', value: 'secrets', slot: 'secrets' },
|
|
80
|
+
{ label: 'App', icon: 'i-lucide-settings', value: 'app', slot: 'app' }
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
// Secrets table columns
|
|
84
|
+
const secretColumns = [
|
|
85
|
+
{ accessorKey: 'key', header: 'Key' },
|
|
86
|
+
{ accessorKey: 'description', header: 'Description' },
|
|
87
|
+
{ accessorKey: 'updatedAt', header: 'Last Updated' },
|
|
88
|
+
{ accessorKey: 'actions', header: '' }
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
function openCreateSecret() {
|
|
92
|
+
editingSecret.value = null
|
|
93
|
+
secretForm.key = ''
|
|
94
|
+
secretForm.value = ''
|
|
95
|
+
secretForm.description = ''
|
|
96
|
+
secretModal.value = true
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function openEditSecret(secret: Secret) {
|
|
100
|
+
editingSecret.value = secret
|
|
101
|
+
secretForm.key = secret.key
|
|
102
|
+
secretForm.value = ''
|
|
103
|
+
secretForm.description = secret.description || ''
|
|
104
|
+
secretModal.value = true
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function confirmDeleteSecret(secret: Secret) {
|
|
108
|
+
secretToDelete.value = secret
|
|
109
|
+
secretDeleteConfirm.value = true
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function handleSecretSubmit() {
|
|
113
|
+
if (!secretForm.key || (!editingSecret.value && !secretForm.value)) {
|
|
114
|
+
toast.add({
|
|
115
|
+
title: 'Missing fields',
|
|
116
|
+
description: editingSecret.value ? 'Key is required.' : 'Key and value are required.',
|
|
117
|
+
color: 'error'
|
|
118
|
+
})
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
secretSaving.value = true
|
|
123
|
+
try {
|
|
124
|
+
if (editingSecret.value) {
|
|
125
|
+
await $fetch(`/api/secrets/${editingSecret.value.key}`, {
|
|
126
|
+
method: 'PUT',
|
|
127
|
+
body: {
|
|
128
|
+
value: secretForm.value || undefined,
|
|
129
|
+
description: secretForm.description || undefined
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
toast.add({
|
|
133
|
+
title: 'Secret updated',
|
|
134
|
+
color: 'success'
|
|
135
|
+
})
|
|
136
|
+
} else {
|
|
137
|
+
await $fetch('/api/secrets', {
|
|
138
|
+
method: 'POST',
|
|
139
|
+
body: {
|
|
140
|
+
key: secretForm.key,
|
|
141
|
+
value: secretForm.value,
|
|
142
|
+
description: secretForm.description || undefined
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
toast.add({
|
|
146
|
+
title: 'Secret created',
|
|
147
|
+
color: 'success'
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
secretModal.value = false
|
|
151
|
+
await fetchSecrets()
|
|
152
|
+
} catch (err: unknown) {
|
|
153
|
+
const error = err as { data?: { message?: string } }
|
|
154
|
+
toast.add({
|
|
155
|
+
title: editingSecret.value ? 'Failed to update secret' : 'Failed to create secret',
|
|
156
|
+
description: error.data?.message || 'An error occurred',
|
|
157
|
+
color: 'error'
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
secretSaving.value = false
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function handleDeleteSecret() {
|
|
164
|
+
if (!secretToDelete.value) return
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
await $fetch(`/api/secrets/${secretToDelete.value.key}`, {
|
|
168
|
+
method: 'DELETE'
|
|
169
|
+
})
|
|
170
|
+
toast.add({
|
|
171
|
+
title: 'Secret deleted',
|
|
172
|
+
color: 'success'
|
|
173
|
+
})
|
|
174
|
+
secretDeleteConfirm.value = false
|
|
175
|
+
secretToDelete.value = null
|
|
176
|
+
await fetchSecrets()
|
|
177
|
+
} catch {
|
|
178
|
+
toast.add({
|
|
179
|
+
title: 'Failed to delete secret',
|
|
180
|
+
color: 'error'
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function formatDate(dateStr: string) {
|
|
186
|
+
return new Date(dateStr).toLocaleDateString('en-US', {
|
|
187
|
+
month: 'short',
|
|
188
|
+
day: 'numeric',
|
|
189
|
+
year: 'numeric'
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Load secrets when component mounts
|
|
194
|
+
onMounted(() => {
|
|
195
|
+
fetchSecrets()
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
// Form handlers
|
|
199
|
+
async function handleProfileSubmit() {
|
|
200
|
+
profileLoading.value = true
|
|
201
|
+
const result = await updateProfile({
|
|
202
|
+
name: profileState.name
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
if (result.error) {
|
|
206
|
+
toast.add({
|
|
207
|
+
title: 'Update failed',
|
|
208
|
+
description: result.error.message,
|
|
209
|
+
color: 'error'
|
|
210
|
+
})
|
|
211
|
+
} else {
|
|
212
|
+
toast.add({
|
|
213
|
+
title: 'Profile updated',
|
|
214
|
+
description: 'Your name has been updated successfully.',
|
|
215
|
+
color: 'success'
|
|
216
|
+
})
|
|
217
|
+
}
|
|
218
|
+
profileLoading.value = false
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function handleEmailSubmit() {
|
|
222
|
+
if (!emailState.newEmail) {
|
|
223
|
+
toast.add({
|
|
224
|
+
title: 'Email required',
|
|
225
|
+
description: 'Please enter a new email address.',
|
|
226
|
+
color: 'error'
|
|
227
|
+
})
|
|
228
|
+
return
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
emailLoading.value = true
|
|
232
|
+
const result = await changeEmail(emailState.newEmail)
|
|
233
|
+
|
|
234
|
+
if (result.error) {
|
|
235
|
+
toast.add({
|
|
236
|
+
title: 'Email change failed',
|
|
237
|
+
description: result.error.message,
|
|
238
|
+
color: 'error'
|
|
239
|
+
})
|
|
240
|
+
} else {
|
|
241
|
+
toast.add({
|
|
242
|
+
title: 'Email updated',
|
|
243
|
+
description: 'Your email has been changed successfully.',
|
|
244
|
+
color: 'success'
|
|
245
|
+
})
|
|
246
|
+
}
|
|
247
|
+
emailLoading.value = false
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function handlePasswordSubmit() {
|
|
251
|
+
// Client-side validation
|
|
252
|
+
if (passwordState.newPassword !== passwordState.confirmPassword) {
|
|
253
|
+
toast.add({
|
|
254
|
+
title: 'Passwords do not match',
|
|
255
|
+
description: 'Please make sure your new passwords match.',
|
|
256
|
+
color: 'error'
|
|
257
|
+
})
|
|
258
|
+
return
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (!passwordState.currentPassword || !passwordState.newPassword) {
|
|
262
|
+
toast.add({
|
|
263
|
+
title: 'Missing fields',
|
|
264
|
+
description: 'Please fill in all password fields.',
|
|
265
|
+
color: 'error'
|
|
266
|
+
})
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
passwordLoading.value = true
|
|
271
|
+
const result = await changePassword({
|
|
272
|
+
currentPassword: passwordState.currentPassword,
|
|
273
|
+
newPassword: passwordState.newPassword
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
if (result.error) {
|
|
277
|
+
toast.add({
|
|
278
|
+
title: 'Password change failed',
|
|
279
|
+
description: result.error.message,
|
|
280
|
+
color: 'error'
|
|
281
|
+
})
|
|
282
|
+
} else {
|
|
283
|
+
toast.add({
|
|
284
|
+
title: 'Password changed',
|
|
285
|
+
description: 'Your password has been changed successfully.',
|
|
286
|
+
color: 'success'
|
|
287
|
+
})
|
|
288
|
+
// Clear form
|
|
289
|
+
passwordState.currentPassword = ''
|
|
290
|
+
passwordState.newPassword = ''
|
|
291
|
+
passwordState.confirmPassword = ''
|
|
292
|
+
}
|
|
293
|
+
passwordLoading.value = false
|
|
294
|
+
}
|
|
295
|
+
</script>
|
|
296
|
+
|
|
297
|
+
<template>
|
|
298
|
+
<UDashboardPanel
|
|
299
|
+
id="settings"
|
|
300
|
+
grow
|
|
301
|
+
>
|
|
302
|
+
<UDashboardNavbar title="Settings">
|
|
303
|
+
<template #right>
|
|
304
|
+
<UColorModeButton />
|
|
305
|
+
</template>
|
|
306
|
+
</UDashboardNavbar>
|
|
307
|
+
|
|
308
|
+
<div class="p-6">
|
|
309
|
+
<ClientOnly>
|
|
310
|
+
<UTabs
|
|
311
|
+
:items="tabs"
|
|
312
|
+
default-value="account"
|
|
313
|
+
class="w-full mx-auto"
|
|
314
|
+
:ui="{ list: 'max-w-xl' }"
|
|
315
|
+
>
|
|
316
|
+
<!-- Account Tab -->
|
|
317
|
+
<template #account>
|
|
318
|
+
<div class="space-y-8 max-w-2xl mx-auto py-6">
|
|
319
|
+
<!-- Profile Section -->
|
|
320
|
+
<div>
|
|
321
|
+
<h3 class="text-lg font-semibold mb-4">
|
|
322
|
+
Profile
|
|
323
|
+
</h3>
|
|
324
|
+
<UForm
|
|
325
|
+
:state="profileState"
|
|
326
|
+
class="space-y-4"
|
|
327
|
+
@submit="handleProfileSubmit"
|
|
328
|
+
>
|
|
329
|
+
<UFormField
|
|
330
|
+
label="Name"
|
|
331
|
+
name="name"
|
|
332
|
+
>
|
|
333
|
+
<UInput
|
|
334
|
+
v-model="profileState.name"
|
|
335
|
+
placeholder="Your name"
|
|
336
|
+
class="w-full"
|
|
337
|
+
/>
|
|
338
|
+
</UFormField>
|
|
339
|
+
<UButton
|
|
340
|
+
type="submit"
|
|
341
|
+
:loading="profileLoading"
|
|
342
|
+
>
|
|
343
|
+
Save Name
|
|
344
|
+
</UButton>
|
|
345
|
+
</UForm>
|
|
346
|
+
</div>
|
|
347
|
+
|
|
348
|
+
<USeparator />
|
|
349
|
+
|
|
350
|
+
<!-- Email Section -->
|
|
351
|
+
<div>
|
|
352
|
+
<h3 class="text-lg font-semibold mb-4">
|
|
353
|
+
Email Address
|
|
354
|
+
</h3>
|
|
355
|
+
<UForm
|
|
356
|
+
:state="emailState"
|
|
357
|
+
class="space-y-4"
|
|
358
|
+
@submit="handleEmailSubmit"
|
|
359
|
+
>
|
|
360
|
+
<UFormField
|
|
361
|
+
label="Email"
|
|
362
|
+
name="newEmail"
|
|
363
|
+
>
|
|
364
|
+
<UInput
|
|
365
|
+
v-model="emailState.newEmail"
|
|
366
|
+
type="email"
|
|
367
|
+
placeholder="your@email.com"
|
|
368
|
+
class="w-full"
|
|
369
|
+
/>
|
|
370
|
+
</UFormField>
|
|
371
|
+
<UButton
|
|
372
|
+
type="submit"
|
|
373
|
+
:loading="emailLoading"
|
|
374
|
+
>
|
|
375
|
+
Change Email
|
|
376
|
+
</UButton>
|
|
377
|
+
</UForm>
|
|
378
|
+
</div>
|
|
379
|
+
|
|
380
|
+
<USeparator />
|
|
381
|
+
|
|
382
|
+
<!-- Password Section -->
|
|
383
|
+
<div>
|
|
384
|
+
<h3 class="text-lg font-semibold mb-4">
|
|
385
|
+
Change Password
|
|
386
|
+
</h3>
|
|
387
|
+
<UForm
|
|
388
|
+
:state="passwordState"
|
|
389
|
+
class="space-y-4"
|
|
390
|
+
@submit="handlePasswordSubmit"
|
|
391
|
+
>
|
|
392
|
+
<UFormField
|
|
393
|
+
label="Current Password"
|
|
394
|
+
name="currentPassword"
|
|
395
|
+
>
|
|
396
|
+
<UInput
|
|
397
|
+
v-model="passwordState.currentPassword"
|
|
398
|
+
type="password"
|
|
399
|
+
class="w-full"
|
|
400
|
+
/>
|
|
401
|
+
</UFormField>
|
|
402
|
+
<UFormField
|
|
403
|
+
label="New Password"
|
|
404
|
+
name="newPassword"
|
|
405
|
+
>
|
|
406
|
+
<UInput
|
|
407
|
+
v-model="passwordState.newPassword"
|
|
408
|
+
type="password"
|
|
409
|
+
class="w-full"
|
|
410
|
+
/>
|
|
411
|
+
</UFormField>
|
|
412
|
+
<UFormField
|
|
413
|
+
label="Confirm New Password"
|
|
414
|
+
name="confirmPassword"
|
|
415
|
+
>
|
|
416
|
+
<UInput
|
|
417
|
+
v-model="passwordState.confirmPassword"
|
|
418
|
+
type="password"
|
|
419
|
+
class="w-full"
|
|
420
|
+
/>
|
|
421
|
+
</UFormField>
|
|
422
|
+
<UButton
|
|
423
|
+
type="submit"
|
|
424
|
+
:loading="passwordLoading"
|
|
425
|
+
>
|
|
426
|
+
Change Password
|
|
427
|
+
</UButton>
|
|
428
|
+
</UForm>
|
|
429
|
+
</div>
|
|
430
|
+
</div>
|
|
431
|
+
</template>
|
|
432
|
+
|
|
433
|
+
<!-- Secrets Tab -->
|
|
434
|
+
<template #secrets>
|
|
435
|
+
<div class="py-6">
|
|
436
|
+
<div class="flex items-center justify-between mb-6">
|
|
437
|
+
<div>
|
|
438
|
+
<h3 class="text-lg font-semibold">
|
|
439
|
+
Secrets
|
|
440
|
+
</h3>
|
|
441
|
+
<p class="text-sm text-dimmed">
|
|
442
|
+
Encrypted key-value store for skills and integrations.
|
|
443
|
+
</p>
|
|
444
|
+
</div>
|
|
445
|
+
<UButton
|
|
446
|
+
icon="i-lucide-plus"
|
|
447
|
+
@click="openCreateSecret"
|
|
448
|
+
>
|
|
449
|
+
Add Secret
|
|
450
|
+
</UButton>
|
|
451
|
+
</div>
|
|
452
|
+
|
|
453
|
+
<UTable
|
|
454
|
+
:columns="secretColumns"
|
|
455
|
+
:data="secretsData"
|
|
456
|
+
:loading="secretsLoading"
|
|
457
|
+
>
|
|
458
|
+
<template #key-cell="{ row }">
|
|
459
|
+
<code class="text-sm bg-elevated px-2 py-0.5 rounded">{{ row.original.key }}</code>
|
|
460
|
+
</template>
|
|
461
|
+
<template #description-cell="{ row }">
|
|
462
|
+
<span class="text-dimmed">{{ row.original.description || '—' }}</span>
|
|
463
|
+
</template>
|
|
464
|
+
<template #updatedAt-cell="{ row }">
|
|
465
|
+
<span class="text-dimmed text-sm">{{ formatDate(row.original.updatedAt) }}</span>
|
|
466
|
+
</template>
|
|
467
|
+
<template #actions-cell="{ row }">
|
|
468
|
+
<div class="flex gap-2 justify-end">
|
|
469
|
+
<UButton
|
|
470
|
+
variant="ghost"
|
|
471
|
+
icon="i-lucide-pencil"
|
|
472
|
+
size="xs"
|
|
473
|
+
@click="openEditSecret(row.original)"
|
|
474
|
+
/>
|
|
475
|
+
<UButton
|
|
476
|
+
variant="ghost"
|
|
477
|
+
color="error"
|
|
478
|
+
icon="i-lucide-trash-2"
|
|
479
|
+
size="xs"
|
|
480
|
+
@click="confirmDeleteSecret(row.original)"
|
|
481
|
+
/>
|
|
482
|
+
</div>
|
|
483
|
+
</template>
|
|
484
|
+
<template #empty>
|
|
485
|
+
<div class="py-12 text-center">
|
|
486
|
+
<UIcon
|
|
487
|
+
name="i-lucide-key"
|
|
488
|
+
class="size-12 mx-auto mb-4 text-dimmed"
|
|
489
|
+
/>
|
|
490
|
+
<p class="text-dimmed">
|
|
491
|
+
No secrets yet. Add one to get started.
|
|
492
|
+
</p>
|
|
493
|
+
</div>
|
|
494
|
+
</template>
|
|
495
|
+
</UTable>
|
|
496
|
+
</div>
|
|
497
|
+
</template>
|
|
498
|
+
|
|
499
|
+
<!-- App Tab -->
|
|
500
|
+
<template #app>
|
|
501
|
+
<div class="py-12 text-center">
|
|
502
|
+
<UIcon
|
|
503
|
+
name="i-lucide-settings"
|
|
504
|
+
class="size-16 mx-auto mb-4 text-dimmed"
|
|
505
|
+
/>
|
|
506
|
+
<h3 class="text-lg font-semibold mb-2">
|
|
507
|
+
App Settings
|
|
508
|
+
</h3>
|
|
509
|
+
<p class="text-dimmed">
|
|
510
|
+
Coming soon...
|
|
511
|
+
</p>
|
|
512
|
+
</div>
|
|
513
|
+
</template>
|
|
514
|
+
</UTabs>
|
|
515
|
+
|
|
516
|
+
<template #fallback>
|
|
517
|
+
<div class="space-y-8 max-w-xl mx-auto py-6">
|
|
518
|
+
<USkeleton class="h-10 w-full" />
|
|
519
|
+
<div class="space-y-4">
|
|
520
|
+
<USkeleton class="h-5 w-20" />
|
|
521
|
+
<USkeleton class="h-10 w-full" />
|
|
522
|
+
<USkeleton class="h-10 w-28" />
|
|
523
|
+
</div>
|
|
524
|
+
</div>
|
|
525
|
+
</template>
|
|
526
|
+
</ClientOnly>
|
|
527
|
+
</div>
|
|
528
|
+
|
|
529
|
+
<!-- Secret Create/Edit Modal -->
|
|
530
|
+
<UModal v-model:open="secretModal">
|
|
531
|
+
<template #header>
|
|
532
|
+
<h3 class="text-lg font-semibold">
|
|
533
|
+
{{ editingSecret ? 'Edit Secret' : 'Add Secret' }}
|
|
534
|
+
</h3>
|
|
535
|
+
</template>
|
|
536
|
+
<template #body>
|
|
537
|
+
<UForm
|
|
538
|
+
:state="secretForm"
|
|
539
|
+
class="space-y-4"
|
|
540
|
+
@submit="handleSecretSubmit"
|
|
541
|
+
>
|
|
542
|
+
<UFormField
|
|
543
|
+
label="Key"
|
|
544
|
+
name="key"
|
|
545
|
+
:hint="editingSecret ? '' : 'SCREAMING_SNAKE_CASE'"
|
|
546
|
+
>
|
|
547
|
+
<UInput
|
|
548
|
+
v-model="secretForm.key"
|
|
549
|
+
:disabled="!!editingSecret"
|
|
550
|
+
placeholder="MY_API_KEY"
|
|
551
|
+
class="w-full font-mono"
|
|
552
|
+
/>
|
|
553
|
+
</UFormField>
|
|
554
|
+
<UFormField
|
|
555
|
+
label="Value"
|
|
556
|
+
name="value"
|
|
557
|
+
:hint="editingSecret ? 'Leave empty to keep current value' : ''"
|
|
558
|
+
>
|
|
559
|
+
<UInput
|
|
560
|
+
v-model="secretForm.value"
|
|
561
|
+
type="password"
|
|
562
|
+
:placeholder="editingSecret ? '••••••••' : 'Secret value'"
|
|
563
|
+
class="w-full"
|
|
564
|
+
/>
|
|
565
|
+
</UFormField>
|
|
566
|
+
<UFormField
|
|
567
|
+
label="Description"
|
|
568
|
+
name="description"
|
|
569
|
+
>
|
|
570
|
+
<UInput
|
|
571
|
+
v-model="secretForm.description"
|
|
572
|
+
placeholder="Optional description"
|
|
573
|
+
class="w-full"
|
|
574
|
+
/>
|
|
575
|
+
</UFormField>
|
|
576
|
+
<div class="flex justify-end gap-2 pt-4">
|
|
577
|
+
<UButton
|
|
578
|
+
variant="ghost"
|
|
579
|
+
@click="secretModal = false"
|
|
580
|
+
>
|
|
581
|
+
Cancel
|
|
582
|
+
</UButton>
|
|
583
|
+
<UButton
|
|
584
|
+
type="submit"
|
|
585
|
+
:loading="secretSaving"
|
|
586
|
+
>
|
|
587
|
+
{{ editingSecret ? 'Update' : 'Create' }}
|
|
588
|
+
</UButton>
|
|
589
|
+
</div>
|
|
590
|
+
</UForm>
|
|
591
|
+
</template>
|
|
592
|
+
</UModal>
|
|
593
|
+
|
|
594
|
+
<!-- Delete Confirmation Modal -->
|
|
595
|
+
<UModal v-model:open="secretDeleteConfirm">
|
|
596
|
+
<template #header>
|
|
597
|
+
<h3 class="text-lg font-semibold">
|
|
598
|
+
Delete Secret
|
|
599
|
+
</h3>
|
|
600
|
+
</template>
|
|
601
|
+
<template #body>
|
|
602
|
+
<p class="mb-4">
|
|
603
|
+
Are you sure you want to delete <code class="bg-elevated px-2 py-0.5 rounded">{{ secretToDelete?.key }}</code>?
|
|
604
|
+
</p>
|
|
605
|
+
<p class="text-sm text-dimmed">
|
|
606
|
+
This action cannot be undone. Any skills using this secret will stop working.
|
|
607
|
+
</p>
|
|
608
|
+
<div class="flex justify-end gap-2 pt-6">
|
|
609
|
+
<UButton
|
|
610
|
+
variant="ghost"
|
|
611
|
+
@click="secretDeleteConfirm = false"
|
|
612
|
+
>
|
|
613
|
+
Cancel
|
|
614
|
+
</UButton>
|
|
615
|
+
<UButton
|
|
616
|
+
color="error"
|
|
617
|
+
@click="handleDeleteSecret"
|
|
618
|
+
>
|
|
619
|
+
Delete
|
|
620
|
+
</UButton>
|
|
621
|
+
</div>
|
|
622
|
+
</template>
|
|
623
|
+
</UModal>
|
|
624
|
+
</UDashboardPanel>
|
|
625
|
+
</template>
|