arkaos 3.71.0 → 3.72.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/VERSION +1 -1
- package/core/terminal/__init__.py +2 -0
- package/core/terminal/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/terminal/__pycache__/connections.cpython-313.pyc +0 -0
- package/core/terminal/__pycache__/session.cpython-313.pyc +0 -0
- package/core/terminal/connections.py +46 -0
- package/dashboard/app/layouts/default.vue +7 -0
- package/dashboard/app/pages/cognition.vue +311 -0
- package/dashboard/app/pages/settings.vue +215 -51
- package/dashboard/package.json +3 -1
- package/installer/autostart.js +178 -0
- package/installer/cli.js +7 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/__pycache__/dashboard-api.cpython-313.pyc +0 -0
- package/scripts/dashboard-api.py +178 -5
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
// PR92d v3.42.0 — primary color picker.
|
|
3
|
+
import { THEME_COLOR_OPTIONS, useThemeColor } from '~/composables/useThemeColor'
|
|
4
|
+
|
|
2
5
|
interface ProfileResponse {
|
|
3
6
|
version: string
|
|
4
7
|
name: string
|
|
@@ -22,7 +25,7 @@ const {
|
|
|
22
25
|
data: profile,
|
|
23
26
|
status: profileStatus,
|
|
24
27
|
error: profileError,
|
|
25
|
-
refresh: refreshProfile
|
|
28
|
+
refresh: refreshProfile
|
|
26
29
|
} = await fetchApi<ProfileResponse>('/api/profile')
|
|
27
30
|
|
|
28
31
|
const profileDraft = ref({
|
|
@@ -32,7 +35,7 @@ const profileDraft = ref({
|
|
|
32
35
|
market: profile.value?.market ?? '',
|
|
33
36
|
language: profile.value?.language ?? 'en',
|
|
34
37
|
vaultPath: profile.value?.vaultPath ?? '',
|
|
35
|
-
projectsDir: profile.value?.projectsDir ?? ''
|
|
38
|
+
projectsDir: profile.value?.projectsDir ?? ''
|
|
36
39
|
})
|
|
37
40
|
|
|
38
41
|
watch(profile, (p) => {
|
|
@@ -44,7 +47,7 @@ watch(profile, (p) => {
|
|
|
44
47
|
market: p.market,
|
|
45
48
|
language: p.language,
|
|
46
49
|
vaultPath: p.vaultPath,
|
|
47
|
-
projectsDir: p.projectsDir
|
|
50
|
+
projectsDir: p.projectsDir
|
|
48
51
|
}
|
|
49
52
|
}, { immediate: true })
|
|
50
53
|
|
|
@@ -68,7 +71,7 @@ async function testVault() {
|
|
|
68
71
|
if (profileDraft.value.vaultPath !== profile.value?.vaultPath) {
|
|
69
72
|
await $fetch(`${apiBase}/api/profile`, {
|
|
70
73
|
method: 'POST',
|
|
71
|
-
body: { vaultPath: profileDraft.value.vaultPath }
|
|
74
|
+
body: { vaultPath: profileDraft.value.vaultPath }
|
|
72
75
|
})
|
|
73
76
|
}
|
|
74
77
|
vaultStatus.value = await $fetch<VaultStatus>(`${apiBase}/api/settings/vault`)
|
|
@@ -76,13 +79,13 @@ async function testVault() {
|
|
|
76
79
|
title: vaultStatus.value.exists ? 'Vault reachable' : 'Vault not found',
|
|
77
80
|
description: vaultStatus.value.vault_path || 'Set a path first',
|
|
78
81
|
color: vaultStatus.value.exists ? 'success' : 'warning',
|
|
79
|
-
icon: vaultStatus.value.exists ? 'i-lucide-check-circle' : 'i-lucide-alert-circle'
|
|
82
|
+
icon: vaultStatus.value.exists ? 'i-lucide-check-circle' : 'i-lucide-alert-circle'
|
|
80
83
|
})
|
|
81
84
|
} catch (err) {
|
|
82
85
|
toast.add({
|
|
83
86
|
title: 'Test failed',
|
|
84
87
|
description: err instanceof Error ? err.message : 'unknown error',
|
|
85
|
-
color: 'error'
|
|
88
|
+
color: 'error'
|
|
86
89
|
})
|
|
87
90
|
} finally {
|
|
88
91
|
testingVault.value = false
|
|
@@ -94,19 +97,19 @@ async function saveProfile() {
|
|
|
94
97
|
try {
|
|
95
98
|
await $fetch<ProfileResponse>(`${apiBase}/api/profile`, {
|
|
96
99
|
method: 'POST',
|
|
97
|
-
body: profileDraft.value
|
|
100
|
+
body: profileDraft.value
|
|
98
101
|
})
|
|
99
102
|
await refreshProfile()
|
|
100
103
|
toast.add({
|
|
101
104
|
title: 'Profile saved',
|
|
102
105
|
description: 'Settings written to ~/.arkaos/profile.json',
|
|
103
|
-
color: 'success'
|
|
106
|
+
color: 'success'
|
|
104
107
|
})
|
|
105
108
|
} catch (err) {
|
|
106
109
|
toast.add({
|
|
107
110
|
title: 'Save failed',
|
|
108
111
|
description: err instanceof Error ? err.message : 'unknown error',
|
|
109
|
-
color: 'error'
|
|
112
|
+
color: 'error'
|
|
110
113
|
})
|
|
111
114
|
} finally {
|
|
112
115
|
savingProfile.value = false
|
|
@@ -115,7 +118,7 @@ async function saveProfile() {
|
|
|
115
118
|
|
|
116
119
|
const languageOptions = [
|
|
117
120
|
{ label: 'English', value: 'en' },
|
|
118
|
-
{ label: 'Português', value: 'pt' }
|
|
121
|
+
{ label: 'Português', value: 'pt' }
|
|
119
122
|
]
|
|
120
123
|
|
|
121
124
|
const roleOptions = [
|
|
@@ -125,16 +128,23 @@ const roleOptions = [
|
|
|
125
128
|
{ label: 'Engineer', value: 'engineer' },
|
|
126
129
|
{ label: 'Designer', value: 'designer' },
|
|
127
130
|
{ label: 'Operator', value: 'operator' },
|
|
128
|
-
{ label: 'Consultant', value: 'consultant' }
|
|
131
|
+
{ label: 'Consultant', value: 'consultant' }
|
|
129
132
|
]
|
|
130
133
|
|
|
131
134
|
// ─── API Keys (preserved from earlier) ──────────────────────────────────
|
|
132
135
|
|
|
136
|
+
interface KeyRow {
|
|
137
|
+
key: string
|
|
138
|
+
provider: string
|
|
139
|
+
configured: boolean
|
|
140
|
+
used_for?: string
|
|
141
|
+
}
|
|
142
|
+
|
|
133
143
|
const {
|
|
134
144
|
data: keysData,
|
|
135
145
|
status: keysStatus,
|
|
136
|
-
refresh: refreshKeys
|
|
137
|
-
} = fetchApi<
|
|
146
|
+
refresh: refreshKeys
|
|
147
|
+
} = fetchApi<{ keys: KeyRow[] }>('/api/keys')
|
|
138
148
|
|
|
139
149
|
const keys = computed(() => keysData.value?.keys ?? [])
|
|
140
150
|
|
|
@@ -154,13 +164,13 @@ async function saveKey() {
|
|
|
154
164
|
try {
|
|
155
165
|
await $fetch(`${apiBase}/api/keys`, {
|
|
156
166
|
method: 'POST',
|
|
157
|
-
body: { key: keyName, value: newValue.value }
|
|
167
|
+
body: { key: keyName, value: newValue.value }
|
|
158
168
|
})
|
|
159
169
|
newKey.value = ''
|
|
160
170
|
newValue.value = ''
|
|
161
171
|
customKeyName.value = ''
|
|
162
172
|
await refreshKeys()
|
|
163
|
-
} catch {}
|
|
173
|
+
} catch { /* best-effort; surfaced via list refresh */ }
|
|
164
174
|
saving.value = false
|
|
165
175
|
}
|
|
166
176
|
|
|
@@ -169,7 +179,7 @@ async function deleteKey(keyName: string) {
|
|
|
169
179
|
try {
|
|
170
180
|
await $fetch(`${apiBase}/api/keys/${keyName}`, { method: 'DELETE' })
|
|
171
181
|
await refreshKeys()
|
|
172
|
-
} catch {}
|
|
182
|
+
} catch { /* best-effort; surfaced via list refresh */ }
|
|
173
183
|
deletingKey.value = null
|
|
174
184
|
}
|
|
175
185
|
|
|
@@ -177,7 +187,7 @@ const keyOptions = [
|
|
|
177
187
|
{ label: 'OPENAI_API_KEY', value: 'OPENAI_API_KEY' },
|
|
178
188
|
{ label: 'FAL_API_KEY', value: 'FAL_API_KEY' },
|
|
179
189
|
{ label: 'GOOGLE_API_KEY', value: 'GOOGLE_API_KEY' },
|
|
180
|
-
{ label: 'Custom...', value: 'custom' }
|
|
190
|
+
{ label: 'Custom...', value: 'custom' }
|
|
181
191
|
]
|
|
182
192
|
|
|
183
193
|
// ─── PR63b v2.89.0 — MCPs / Hooks / Plugins / Theme sections ────────────
|
|
@@ -231,11 +241,8 @@ const colorMode = useColorMode()
|
|
|
231
241
|
const themeOptions = [
|
|
232
242
|
{ label: 'System (auto)', value: 'system' },
|
|
233
243
|
{ label: 'Light', value: 'light' },
|
|
234
|
-
{ label: 'Dark', value: 'dark' }
|
|
244
|
+
{ label: 'Dark', value: 'dark' }
|
|
235
245
|
]
|
|
236
|
-
|
|
237
|
-
// PR92d v3.42.0 — primary color picker.
|
|
238
|
-
import { THEME_COLOR_OPTIONS, useThemeColor } from '~/composables/useThemeColor'
|
|
239
246
|
const themeColor = useThemeColor()
|
|
240
247
|
const themeColorOptions = THEME_COLOR_OPTIONS
|
|
241
248
|
|
|
@@ -256,19 +263,52 @@ function formatInstalledAt(iso: string): string {
|
|
|
256
263
|
|
|
257
264
|
// ─── Section nav ────────────────────────────────────────────────────────
|
|
258
265
|
|
|
259
|
-
type SectionId = 'profile' | 'projects' | 'keys' | 'mcps' | 'hooks' | 'plugins' | 'theme'
|
|
260
|
-
|
|
261
|
-
const sections: { id: SectionId
|
|
262
|
-
{ id: 'profile',
|
|
263
|
-
{ id: 'projects', label: 'Projects',
|
|
264
|
-
{ id: 'keys',
|
|
265
|
-
{ id: 'mcps',
|
|
266
|
-
{ id: 'hooks',
|
|
267
|
-
{ id: 'plugins',
|
|
268
|
-
{ id: 'theme',
|
|
266
|
+
type SectionId = 'profile' | 'projects' | 'keys' | 'mcps' | 'hooks' | 'plugins' | 'theme' | 'updates'
|
|
267
|
+
|
|
268
|
+
const sections: { id: SectionId, label: string, icon: string }[] = [
|
|
269
|
+
{ id: 'profile', label: 'Profile', icon: 'i-lucide-user-circle' },
|
|
270
|
+
{ id: 'projects', label: 'Projects', icon: 'i-lucide-folders' },
|
|
271
|
+
{ id: 'keys', label: 'API Keys', icon: 'i-lucide-key' },
|
|
272
|
+
{ id: 'mcps', label: 'MCPs', icon: 'i-lucide-plug-2' },
|
|
273
|
+
{ id: 'hooks', label: 'Hooks', icon: 'i-lucide-webhook' },
|
|
274
|
+
{ id: 'plugins', label: 'Plugins', icon: 'i-lucide-puzzle' },
|
|
275
|
+
{ id: 'theme', label: 'Theme', icon: 'i-lucide-palette' },
|
|
276
|
+
{ id: 'updates', label: 'Updates', icon: 'i-lucide-download' }
|
|
269
277
|
]
|
|
270
278
|
|
|
271
279
|
const activeSection = ref<SectionId>('profile')
|
|
280
|
+
|
|
281
|
+
// ─── Updates (v3.72.0) — version check + one-click core update ──────────
|
|
282
|
+
const ver = ref<{ current: string, latest: string | null, update_available: boolean } | null>(null)
|
|
283
|
+
const checkingVer = ref(false)
|
|
284
|
+
const updating = ref(false)
|
|
285
|
+
const updateResult = ref<{ ok: boolean, output: string } | null>(null)
|
|
286
|
+
|
|
287
|
+
async function checkVersion() {
|
|
288
|
+
checkingVer.value = true
|
|
289
|
+
try {
|
|
290
|
+
ver.value = await $fetch(`${apiBase}/api/system/version`)
|
|
291
|
+
} catch {
|
|
292
|
+
ver.value = null
|
|
293
|
+
} finally {
|
|
294
|
+
checkingVer.value = false
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async function runUpdate() {
|
|
299
|
+
updating.value = true
|
|
300
|
+
updateResult.value = null
|
|
301
|
+
try {
|
|
302
|
+
updateResult.value = await $fetch(`${apiBase}/api/system/update`, { method: 'POST' })
|
|
303
|
+
} catch (e) {
|
|
304
|
+
updateResult.value = { ok: false, output: e instanceof Error ? e.message : String(e) }
|
|
305
|
+
} finally {
|
|
306
|
+
updating.value = false
|
|
307
|
+
checkVersion()
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
onMounted(checkVersion)
|
|
272
312
|
</script>
|
|
273
313
|
|
|
274
314
|
<template>
|
|
@@ -299,8 +339,8 @@ const activeSection = ref<SectionId>('profile')
|
|
|
299
339
|
<span>{{ s.label }}</span>
|
|
300
340
|
</button>
|
|
301
341
|
<p class="text-xs text-muted px-3 mt-6">
|
|
302
|
-
|
|
303
|
-
read-only diagnostics
|
|
342
|
+
8 sections. Profile + Projects edit data; Updates runs the core
|
|
343
|
+
update; everything else is read-only diagnostics.
|
|
304
344
|
</p>
|
|
305
345
|
</nav>
|
|
306
346
|
|
|
@@ -308,7 +348,9 @@ const activeSection = ref<SectionId>('profile')
|
|
|
308
348
|
<div>
|
|
309
349
|
<!-- Profile -->
|
|
310
350
|
<section v-if="activeSection === 'profile'">
|
|
311
|
-
<h2 class="text-lg font-semibold mb-1">
|
|
351
|
+
<h2 class="text-lg font-semibold mb-1">
|
|
352
|
+
Profile
|
|
353
|
+
</h2>
|
|
312
354
|
<p class="text-sm text-muted mb-6">
|
|
313
355
|
Your identity, role, and language. Stored locally at
|
|
314
356
|
<code class="font-mono text-xs">~/.arkaos/profile.json</code>.
|
|
@@ -448,7 +490,9 @@ const activeSection = ref<SectionId>('profile')
|
|
|
448
490
|
|
|
449
491
|
<!-- Projects -->
|
|
450
492
|
<section v-else-if="activeSection === 'projects'">
|
|
451
|
-
<h2 class="text-lg font-semibold mb-1">
|
|
493
|
+
<h2 class="text-lg font-semibold mb-1">
|
|
494
|
+
Project directories
|
|
495
|
+
</h2>
|
|
452
496
|
<p class="text-sm text-muted mb-6">
|
|
453
497
|
Directories the sync engine scans for projects.
|
|
454
498
|
Comma-separated absolute paths (e.g.
|
|
@@ -500,7 +544,9 @@ const activeSection = ref<SectionId>('profile')
|
|
|
500
544
|
|
|
501
545
|
<!-- API Keys -->
|
|
502
546
|
<section v-else-if="activeSection === 'keys'">
|
|
503
|
-
<h2 class="text-lg font-semibold mb-1">
|
|
547
|
+
<h2 class="text-lg font-semibold mb-1">
|
|
548
|
+
API Keys
|
|
549
|
+
</h2>
|
|
504
550
|
<p class="text-sm text-muted mb-6">
|
|
505
551
|
Configure API keys for external services. Keys are stored
|
|
506
552
|
locally at <code class="font-mono text-xs">~/.arkaos/keys.json</code>.
|
|
@@ -508,11 +554,18 @@ const activeSection = ref<SectionId>('profile')
|
|
|
508
554
|
|
|
509
555
|
<UCard class="mb-6">
|
|
510
556
|
<div class="space-y-4">
|
|
511
|
-
<p class="text-xs font-semibold text-muted uppercase tracking-wider">
|
|
557
|
+
<p class="text-xs font-semibold text-muted uppercase tracking-wider">
|
|
558
|
+
Add API Key
|
|
559
|
+
</p>
|
|
512
560
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-3 items-end">
|
|
513
561
|
<div>
|
|
514
562
|
<label class="text-xs text-muted mb-1 block">Provider</label>
|
|
515
|
-
<USelect
|
|
563
|
+
<USelect
|
|
564
|
+
v-model="newKey"
|
|
565
|
+
:items="keyOptions"
|
|
566
|
+
class="w-full"
|
|
567
|
+
placeholder="Select key..."
|
|
568
|
+
/>
|
|
516
569
|
</div>
|
|
517
570
|
<div v-if="isCustom">
|
|
518
571
|
<label class="text-xs text-muted mb-1 block">Key Name</label>
|
|
@@ -520,7 +573,12 @@ const activeSection = ref<SectionId>('profile')
|
|
|
520
573
|
</div>
|
|
521
574
|
<div :class="isCustom ? '' : 'md:col-span-1'">
|
|
522
575
|
<label class="text-xs text-muted mb-1 block">Value</label>
|
|
523
|
-
<UInput
|
|
576
|
+
<UInput
|
|
577
|
+
v-model="newValue"
|
|
578
|
+
type="password"
|
|
579
|
+
class="w-full"
|
|
580
|
+
placeholder="sk-..."
|
|
581
|
+
/>
|
|
524
582
|
</div>
|
|
525
583
|
<div>
|
|
526
584
|
<UButton
|
|
@@ -560,9 +618,17 @@ const activeSection = ref<SectionId>('profile')
|
|
|
560
618
|
variant="subtle"
|
|
561
619
|
size="xs"
|
|
562
620
|
/>
|
|
563
|
-
<UBadge
|
|
621
|
+
<UBadge
|
|
622
|
+
v-else
|
|
623
|
+
label="Not Set"
|
|
624
|
+
color="neutral"
|
|
625
|
+
variant="outline"
|
|
626
|
+
size="xs"
|
|
627
|
+
/>
|
|
564
628
|
</div>
|
|
565
|
-
<p v-if="k.used_for" class="text-xs text-muted mt-0.5">
|
|
629
|
+
<p v-if="k.used_for" class="text-xs text-muted mt-0.5">
|
|
630
|
+
{{ k.used_for }}
|
|
631
|
+
</p>
|
|
566
632
|
<p v-if="k.masked_value && k.configured" class="text-xs font-mono text-muted/60 mt-0.5">
|
|
567
633
|
{{ k.masked_value }}
|
|
568
634
|
</p>
|
|
@@ -585,7 +651,9 @@ const activeSection = ref<SectionId>('profile')
|
|
|
585
651
|
<!-- MCPs -->
|
|
586
652
|
<section v-else-if="activeSection === 'mcps'">
|
|
587
653
|
<div class="flex items-baseline justify-between mb-1">
|
|
588
|
-
<h2 class="text-lg font-semibold">
|
|
654
|
+
<h2 class="text-lg font-semibold">
|
|
655
|
+
MCPs
|
|
656
|
+
</h2>
|
|
589
657
|
<UButton
|
|
590
658
|
label="Refresh"
|
|
591
659
|
variant="ghost"
|
|
@@ -601,7 +669,9 @@ const activeSection = ref<SectionId>('profile')
|
|
|
601
669
|
</p>
|
|
602
670
|
<div v-if="!mcps.length" class="rounded-lg border border-default p-6 text-center">
|
|
603
671
|
<UIcon name="i-lucide-plug-2" class="size-10 text-muted mx-auto mb-2" />
|
|
604
|
-
<p class="text-sm text-muted">
|
|
672
|
+
<p class="text-sm text-muted">
|
|
673
|
+
No MCP servers configured.
|
|
674
|
+
</p>
|
|
605
675
|
</div>
|
|
606
676
|
<div v-else class="space-y-2">
|
|
607
677
|
<div
|
|
@@ -632,7 +702,9 @@ const activeSection = ref<SectionId>('profile')
|
|
|
632
702
|
<!-- Hooks -->
|
|
633
703
|
<section v-else-if="activeSection === 'hooks'">
|
|
634
704
|
<div class="flex items-baseline justify-between mb-1">
|
|
635
|
-
<h2 class="text-lg font-semibold">
|
|
705
|
+
<h2 class="text-lg font-semibold">
|
|
706
|
+
Hooks
|
|
707
|
+
</h2>
|
|
636
708
|
<UButton
|
|
637
709
|
label="Refresh"
|
|
638
710
|
variant="ghost"
|
|
@@ -657,7 +729,9 @@ const activeSection = ref<SectionId>('profile')
|
|
|
657
729
|
</div>
|
|
658
730
|
<div v-if="!hooks.length" class="rounded-lg border border-default p-6 text-center">
|
|
659
731
|
<UIcon name="i-lucide-webhook" class="size-10 text-muted mx-auto mb-2" />
|
|
660
|
-
<p class="text-sm text-muted">
|
|
732
|
+
<p class="text-sm text-muted">
|
|
733
|
+
No hooks wired in settings.json.
|
|
734
|
+
</p>
|
|
661
735
|
</div>
|
|
662
736
|
<div v-else class="space-y-3">
|
|
663
737
|
<div
|
|
@@ -691,7 +765,9 @@ const activeSection = ref<SectionId>('profile')
|
|
|
691
765
|
<!-- Plugins -->
|
|
692
766
|
<section v-else-if="activeSection === 'plugins'">
|
|
693
767
|
<div class="flex items-baseline justify-between mb-1">
|
|
694
|
-
<h2 class="text-lg font-semibold">
|
|
768
|
+
<h2 class="text-lg font-semibold">
|
|
769
|
+
Plugins
|
|
770
|
+
</h2>
|
|
695
771
|
<UButton
|
|
696
772
|
label="Refresh"
|
|
697
773
|
variant="ghost"
|
|
@@ -707,7 +783,9 @@ const activeSection = ref<SectionId>('profile')
|
|
|
707
783
|
</p>
|
|
708
784
|
<div v-if="!plugins.length" class="rounded-lg border border-default p-6 text-center">
|
|
709
785
|
<UIcon name="i-lucide-puzzle" class="size-10 text-muted mx-auto mb-2" />
|
|
710
|
-
<p class="text-sm text-muted">
|
|
786
|
+
<p class="text-sm text-muted">
|
|
787
|
+
No plugins installed.
|
|
788
|
+
</p>
|
|
711
789
|
<p class="text-xs text-muted mt-2">
|
|
712
790
|
Try <code class="font-mono">/plugin marketplace add andreagroferreira/arka-os</code>
|
|
713
791
|
from Claude Code.
|
|
@@ -724,8 +802,18 @@ const activeSection = ref<SectionId>('profile')
|
|
|
724
802
|
<div class="flex items-center gap-2 mb-0.5">
|
|
725
803
|
<span class="text-sm font-semibold">{{ p.name }}</span>
|
|
726
804
|
<UBadge :label="p.marketplace" variant="outline" size="xs" />
|
|
727
|
-
<UBadge
|
|
728
|
-
|
|
805
|
+
<UBadge
|
|
806
|
+
v-if="p.scope"
|
|
807
|
+
:label="p.scope"
|
|
808
|
+
variant="soft"
|
|
809
|
+
size="xs"
|
|
810
|
+
/>
|
|
811
|
+
<UBadge
|
|
812
|
+
v-if="p.version"
|
|
813
|
+
:label="`v${p.version}`"
|
|
814
|
+
variant="subtle"
|
|
815
|
+
size="xs"
|
|
816
|
+
/>
|
|
729
817
|
</div>
|
|
730
818
|
<p v-if="p.installed_at" class="text-xs text-muted">
|
|
731
819
|
Installed {{ formatInstalledAt(p.installed_at) }}
|
|
@@ -737,7 +825,9 @@ const activeSection = ref<SectionId>('profile')
|
|
|
737
825
|
|
|
738
826
|
<!-- Theme -->
|
|
739
827
|
<section v-else-if="activeSection === 'theme'">
|
|
740
|
-
<h2 class="text-lg font-semibold mb-1">
|
|
828
|
+
<h2 class="text-lg font-semibold mb-1">
|
|
829
|
+
Theme
|
|
830
|
+
</h2>
|
|
741
831
|
<p class="text-sm text-muted mb-6">
|
|
742
832
|
Light / dark / system (follows OS preference).
|
|
743
833
|
Stored locally by your browser.
|
|
@@ -779,7 +869,7 @@ const activeSection = ref<SectionId>('profile')
|
|
|
779
869
|
'bg-rose-500': opt.value === 'rose',
|
|
780
870
|
'bg-amber-500': opt.value === 'amber',
|
|
781
871
|
'bg-teal-500': opt.value === 'teal',
|
|
782
|
-
'bg-cyan-500': opt.value === 'cyan'
|
|
872
|
+
'bg-cyan-500': opt.value === 'cyan'
|
|
783
873
|
}"
|
|
784
874
|
/>
|
|
785
875
|
{{ opt.label }}
|
|
@@ -789,6 +879,80 @@ const activeSection = ref<SectionId>('profile')
|
|
|
789
879
|
</div>
|
|
790
880
|
</UCard>
|
|
791
881
|
</section>
|
|
882
|
+
|
|
883
|
+
<section v-else-if="activeSection === 'updates'">
|
|
884
|
+
<h2 class="text-lg font-semibold mb-1">
|
|
885
|
+
Updates
|
|
886
|
+
</h2>
|
|
887
|
+
<p class="text-sm text-muted mb-6">
|
|
888
|
+
Keep ArkaOS current. The button runs the core update
|
|
889
|
+
(<code class="text-xs">npx arkaos@latest update</code>); finish
|
|
890
|
+
the project sync by running <code class="text-xs">/arka update</code>
|
|
891
|
+
in Claude Code.
|
|
892
|
+
</p>
|
|
893
|
+
<UCard>
|
|
894
|
+
<div class="space-y-4">
|
|
895
|
+
<div class="flex items-center gap-3 flex-wrap">
|
|
896
|
+
<div class="text-sm">
|
|
897
|
+
Installed:
|
|
898
|
+
<UBadge :label="`v${ver?.current ?? '—'}`" variant="subtle" size="sm" />
|
|
899
|
+
</div>
|
|
900
|
+
<div class="text-sm">
|
|
901
|
+
Latest:
|
|
902
|
+
<UBadge
|
|
903
|
+
:label="ver?.latest ? `v${ver.latest}` : '—'"
|
|
904
|
+
:color="ver?.update_available ? 'warning' : 'success'"
|
|
905
|
+
variant="subtle"
|
|
906
|
+
size="sm"
|
|
907
|
+
/>
|
|
908
|
+
</div>
|
|
909
|
+
<UButton
|
|
910
|
+
size="xs"
|
|
911
|
+
variant="ghost"
|
|
912
|
+
icon="i-lucide-refresh-cw"
|
|
913
|
+
:loading="checkingVer"
|
|
914
|
+
@click="checkVersion"
|
|
915
|
+
>
|
|
916
|
+
Check
|
|
917
|
+
</UButton>
|
|
918
|
+
</div>
|
|
919
|
+
|
|
920
|
+
<div v-if="ver?.update_available" class="flex items-center gap-3">
|
|
921
|
+
<UButton
|
|
922
|
+
icon="i-lucide-download"
|
|
923
|
+
color="primary"
|
|
924
|
+
:loading="updating"
|
|
925
|
+
@click="runUpdate"
|
|
926
|
+
>
|
|
927
|
+
{{ updating ? 'Updating…' : `Update to v${ver.latest}` }}
|
|
928
|
+
</UButton>
|
|
929
|
+
<span class="text-xs text-muted">Runs the core update, then asks you to finish in Claude Code.</span>
|
|
930
|
+
</div>
|
|
931
|
+
<p v-else-if="ver && !ver.update_available" class="text-sm text-success flex items-center gap-1.5">
|
|
932
|
+
<UIcon name="i-lucide-check-circle" class="size-4" />
|
|
933
|
+
You're on the latest version.
|
|
934
|
+
</p>
|
|
935
|
+
<p v-else class="text-sm text-muted">
|
|
936
|
+
Couldn't reach the version service.
|
|
937
|
+
</p>
|
|
938
|
+
|
|
939
|
+
<div
|
|
940
|
+
v-if="updateResult"
|
|
941
|
+
class="rounded-lg border p-3 text-xs"
|
|
942
|
+
:class="updateResult.ok ? 'border-success/40 bg-success/5' : 'border-error/40 bg-error/5'"
|
|
943
|
+
>
|
|
944
|
+
<div class="flex items-center gap-1.5 font-medium mb-1">
|
|
945
|
+
<UIcon :name="updateResult.ok ? 'i-lucide-check-circle' : 'i-lucide-alert-triangle'" class="size-4" />
|
|
946
|
+
{{ updateResult.ok ? 'Core updated' : 'Update failed' }}
|
|
947
|
+
</div>
|
|
948
|
+
<p v-if="updateResult.ok" class="text-muted">
|
|
949
|
+
Now run <code>/arka update</code> in Claude Code to sync all projects (step 2).
|
|
950
|
+
</p>
|
|
951
|
+
<pre class="mt-2 whitespace-pre-wrap font-mono text-[11px] text-muted max-h-48 overflow-y-auto">{{ updateResult.output }}</pre>
|
|
952
|
+
</div>
|
|
953
|
+
</div>
|
|
954
|
+
</UCard>
|
|
955
|
+
</section>
|
|
792
956
|
</div>
|
|
793
957
|
</div>
|
|
794
958
|
</template>
|
package/dashboard/package.json
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"preview": "nuxt preview",
|
|
9
9
|
"postinstall": "nuxt prepare",
|
|
10
10
|
"lint": "eslint .",
|
|
11
|
-
"typecheck": "nuxt typecheck"
|
|
11
|
+
"typecheck": "nuxt typecheck",
|
|
12
|
+
"test:e2e": "playwright test"
|
|
12
13
|
},
|
|
13
14
|
"dependencies": {
|
|
14
15
|
"@iconify-json/lucide": "^1.2.100",
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@nuxt/eslint": "^1.15.2",
|
|
38
|
+
"@playwright/test": "^1.60.0",
|
|
37
39
|
"eslint": "^10.1.0",
|
|
38
40
|
"typescript": "^6.0.2",
|
|
39
41
|
"vue-tsc": "^3.2.6"
|