arkaos 3.28.0 → 3.30.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/runtime/__pycache__/llm_provider.cpython-313.pyc +0 -0
- package/dashboard/app/pages/agents/[id].vue +43 -0
- package/dashboard/app/pages/settings.vue +94 -1
- 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 +62 -0
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.30.0
|
|
Binary file
|
|
@@ -105,6 +105,41 @@ function markedHtml(src: string): string {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
// PR89d v3.30.0 — download YAML.
|
|
109
|
+
const downloadingYaml = ref(false)
|
|
110
|
+
async function downloadYaml() {
|
|
111
|
+
if (!agent.value) return
|
|
112
|
+
downloadingYaml.value = true
|
|
113
|
+
try {
|
|
114
|
+
const blob = await $fetch<Blob>(
|
|
115
|
+
`${apiBase}/api/agents/${agentId}/yaml`,
|
|
116
|
+
{ responseType: 'blob' },
|
|
117
|
+
)
|
|
118
|
+
const url = URL.createObjectURL(blob)
|
|
119
|
+
const a = document.createElement('a')
|
|
120
|
+
a.href = url
|
|
121
|
+
a.download = `${agentId}.yaml`
|
|
122
|
+
document.body.appendChild(a)
|
|
123
|
+
a.click()
|
|
124
|
+
a.remove()
|
|
125
|
+
URL.revokeObjectURL(url)
|
|
126
|
+
toast.add({
|
|
127
|
+
title: 'YAML downloaded',
|
|
128
|
+
description: `${agentId}.yaml`,
|
|
129
|
+
color: 'success',
|
|
130
|
+
icon: 'i-lucide-download',
|
|
131
|
+
})
|
|
132
|
+
} catch (err) {
|
|
133
|
+
toast.add({
|
|
134
|
+
title: 'Download failed',
|
|
135
|
+
description: err instanceof Error ? err.message : 'unknown error',
|
|
136
|
+
color: 'error',
|
|
137
|
+
})
|
|
138
|
+
} finally {
|
|
139
|
+
downloadingYaml.value = false
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
108
143
|
// PR86c v3.17.0 — export to Obsidian.
|
|
109
144
|
const exporting = ref(false)
|
|
110
145
|
async function exportToVault() {
|
|
@@ -359,6 +394,14 @@ function formatTokens(n: number): string {
|
|
|
359
394
|
:loading="exporting"
|
|
360
395
|
@click="exportToVault"
|
|
361
396
|
/>
|
|
397
|
+
<UButton
|
|
398
|
+
label="YAML"
|
|
399
|
+
icon="i-lucide-download"
|
|
400
|
+
variant="ghost"
|
|
401
|
+
size="sm"
|
|
402
|
+
:loading="downloadingYaml"
|
|
403
|
+
@click="downloadYaml"
|
|
404
|
+
/>
|
|
362
405
|
<UButton
|
|
363
406
|
label="Edit"
|
|
364
407
|
icon="i-lucide-pencil"
|
|
@@ -50,6 +50,45 @@ watch(profile, (p) => {
|
|
|
50
50
|
|
|
51
51
|
const savingProfile = ref(false)
|
|
52
52
|
|
|
53
|
+
// PR89c v3.29.0 — vault connection test.
|
|
54
|
+
interface VaultStatus {
|
|
55
|
+
configured: boolean
|
|
56
|
+
vault_path: string
|
|
57
|
+
exists: boolean
|
|
58
|
+
personas: { dir: string, count: number }
|
|
59
|
+
agents: { dir: string, count: number }
|
|
60
|
+
}
|
|
61
|
+
const vaultStatus = ref<VaultStatus | null>(null)
|
|
62
|
+
const testingVault = ref(false)
|
|
63
|
+
|
|
64
|
+
async function testVault() {
|
|
65
|
+
testingVault.value = true
|
|
66
|
+
try {
|
|
67
|
+
// Save first so the backend reads the current value
|
|
68
|
+
if (profileDraft.value.vaultPath !== profile.value?.vaultPath) {
|
|
69
|
+
await $fetch(`${apiBase}/api/profile`, {
|
|
70
|
+
method: 'POST',
|
|
71
|
+
body: { vaultPath: profileDraft.value.vaultPath },
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
vaultStatus.value = await $fetch<VaultStatus>(`${apiBase}/api/settings/vault`)
|
|
75
|
+
toast.add({
|
|
76
|
+
title: vaultStatus.value.exists ? 'Vault reachable' : 'Vault not found',
|
|
77
|
+
description: vaultStatus.value.vault_path || 'Set a path first',
|
|
78
|
+
color: vaultStatus.value.exists ? 'success' : 'warning',
|
|
79
|
+
icon: vaultStatus.value.exists ? 'i-lucide-check-circle' : 'i-lucide-alert-circle',
|
|
80
|
+
})
|
|
81
|
+
} catch (err) {
|
|
82
|
+
toast.add({
|
|
83
|
+
title: 'Test failed',
|
|
84
|
+
description: err instanceof Error ? err.message : 'unknown error',
|
|
85
|
+
color: 'error',
|
|
86
|
+
})
|
|
87
|
+
} finally {
|
|
88
|
+
testingVault.value = false
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
53
92
|
async function saveProfile() {
|
|
54
93
|
savingProfile.value = true
|
|
55
94
|
try {
|
|
@@ -326,8 +365,18 @@ const activeSection = ref<SectionId>('profile')
|
|
|
326
365
|
|
|
327
366
|
<UFormField
|
|
328
367
|
label="Vault path"
|
|
329
|
-
help="Where your Obsidian vault lives. Used by the KB-first hook."
|
|
368
|
+
help="Where your Obsidian vault lives. Used by the KB-first hook + Persona/Agent exporters."
|
|
330
369
|
>
|
|
370
|
+
<template #hint>
|
|
371
|
+
<UButton
|
|
372
|
+
label="Test connection"
|
|
373
|
+
icon="i-lucide-plug-zap"
|
|
374
|
+
size="xs"
|
|
375
|
+
variant="soft"
|
|
376
|
+
:loading="testingVault"
|
|
377
|
+
@click="testVault"
|
|
378
|
+
/>
|
|
379
|
+
</template>
|
|
331
380
|
<UInput
|
|
332
381
|
v-model="profileDraft.vaultPath"
|
|
333
382
|
placeholder="/Users/you/Documents/Vault"
|
|
@@ -335,6 +384,50 @@ const activeSection = ref<SectionId>('profile')
|
|
|
335
384
|
/>
|
|
336
385
|
</UFormField>
|
|
337
386
|
|
|
387
|
+
<!-- PR89c v3.29.0 — vault connection test result -->
|
|
388
|
+
<div
|
|
389
|
+
v-if="vaultStatus"
|
|
390
|
+
class="rounded-lg border p-3 text-xs space-y-1"
|
|
391
|
+
:class="
|
|
392
|
+
vaultStatus.exists
|
|
393
|
+
? 'border-emerald-500/40 bg-emerald-500/5'
|
|
394
|
+
: 'border-yellow-500/40 bg-yellow-500/5'
|
|
395
|
+
"
|
|
396
|
+
>
|
|
397
|
+
<div class="flex items-center gap-2 font-semibold">
|
|
398
|
+
<UIcon
|
|
399
|
+
:name="vaultStatus.exists ? 'i-lucide-check-circle' : 'i-lucide-alert-circle'"
|
|
400
|
+
:class="vaultStatus.exists ? 'text-emerald-500 size-4' : 'text-yellow-500 size-4'"
|
|
401
|
+
/>
|
|
402
|
+
<span v-if="!vaultStatus.configured">Vault not configured</span>
|
|
403
|
+
<span v-else-if="!vaultStatus.exists">Path does not exist</span>
|
|
404
|
+
<span v-else>Vault reachable</span>
|
|
405
|
+
</div>
|
|
406
|
+
<p v-if="vaultStatus.exists" class="text-muted font-mono">
|
|
407
|
+
{{ vaultStatus.vault_path }}
|
|
408
|
+
</p>
|
|
409
|
+
<ul v-if="vaultStatus.exists" class="space-y-0.5 pt-1">
|
|
410
|
+
<li class="flex items-center gap-2">
|
|
411
|
+
<UIcon name="i-lucide-folder" class="size-3 text-muted" />
|
|
412
|
+
<span class="font-mono">Personas/</span>
|
|
413
|
+
<UBadge
|
|
414
|
+
:label="`${vaultStatus.personas.count} files`"
|
|
415
|
+
variant="subtle"
|
|
416
|
+
size="xs"
|
|
417
|
+
/>
|
|
418
|
+
</li>
|
|
419
|
+
<li class="flex items-center gap-2">
|
|
420
|
+
<UIcon name="i-lucide-folder" class="size-3 text-muted" />
|
|
421
|
+
<span class="font-mono">Agents/</span>
|
|
422
|
+
<UBadge
|
|
423
|
+
:label="`${vaultStatus.agents.count} files`"
|
|
424
|
+
variant="subtle"
|
|
425
|
+
size="xs"
|
|
426
|
+
/>
|
|
427
|
+
</li>
|
|
428
|
+
</ul>
|
|
429
|
+
</div>
|
|
430
|
+
|
|
338
431
|
<div class="flex justify-end pt-2">
|
|
339
432
|
<UButton
|
|
340
433
|
label="Save profile"
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
Binary file
|
package/scripts/dashboard-api.py
CHANGED
|
@@ -322,6 +322,30 @@ def agent_activity_strip(agent_id: str, period: str = "month"):
|
|
|
322
322
|
}
|
|
323
323
|
|
|
324
324
|
|
|
325
|
+
@app.get("/api/agents/{agent_id}/yaml")
|
|
326
|
+
def agent_download_yaml(agent_id: str):
|
|
327
|
+
"""PR89d v3.30.0 — return the raw YAML for the agent.
|
|
328
|
+
|
|
329
|
+
Responds with ``application/x-yaml`` and an attachment Content-
|
|
330
|
+
Disposition so browsers prompt a Save As. Refuses unknown agents.
|
|
331
|
+
"""
|
|
332
|
+
yaml_file = _resolve_agent_yaml(agent_id)
|
|
333
|
+
if yaml_file is None:
|
|
334
|
+
return {"error": "Agent not found"}
|
|
335
|
+
try:
|
|
336
|
+
content = yaml_file.read_text(encoding="utf-8")
|
|
337
|
+
except OSError as exc:
|
|
338
|
+
return {"error": f"read failed: {exc}"}
|
|
339
|
+
from fastapi import Response
|
|
340
|
+
return Response(
|
|
341
|
+
content=content,
|
|
342
|
+
media_type="application/x-yaml",
|
|
343
|
+
headers={
|
|
344
|
+
"Content-Disposition": f'attachment; filename="{yaml_file.name}"',
|
|
345
|
+
},
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
|
|
325
349
|
@app.get("/api/agents/{agent_id}/history")
|
|
326
350
|
def agent_history(agent_id: str, limit: int = 20):
|
|
327
351
|
"""PR88d v3.26.0 — combined history for an agent.
|
|
@@ -2500,6 +2524,44 @@ def settings_plugins():
|
|
|
2500
2524
|
|
|
2501
2525
|
# --- Profile (PR63 v2.81.0) ---
|
|
2502
2526
|
|
|
2527
|
+
@app.get("/api/settings/vault")
|
|
2528
|
+
def settings_vault():
|
|
2529
|
+
"""PR89c v3.29.0 — vault path connection test.
|
|
2530
|
+
|
|
2531
|
+
Reads ``profile.vaultPath`` and reports back whether the path
|
|
2532
|
+
exists, whether ``Personas/`` and ``Agents/`` subdirs are present,
|
|
2533
|
+
and how many ``*.md`` files each contains.
|
|
2534
|
+
|
|
2535
|
+
Returns ``{configured, vault_path, exists, personas: {dir, count},
|
|
2536
|
+
agents: {dir, count}}``.
|
|
2537
|
+
"""
|
|
2538
|
+
from core.profile import ProfileManager
|
|
2539
|
+
profile = ProfileManager().read()
|
|
2540
|
+
configured = bool(profile.vaultPath)
|
|
2541
|
+
vault = Path(profile.vaultPath).expanduser() if configured else None
|
|
2542
|
+
exists = bool(vault and vault.exists() and vault.is_dir())
|
|
2543
|
+
|
|
2544
|
+
def _subdir_info(name: str) -> dict:
|
|
2545
|
+
if not exists:
|
|
2546
|
+
return {"dir": "", "count": 0}
|
|
2547
|
+
sub = vault / name
|
|
2548
|
+
if not sub.exists():
|
|
2549
|
+
return {"dir": str(sub), "count": 0}
|
|
2550
|
+
try:
|
|
2551
|
+
count = sum(1 for _ in sub.glob("*.md"))
|
|
2552
|
+
except OSError:
|
|
2553
|
+
count = 0
|
|
2554
|
+
return {"dir": str(sub), "count": count}
|
|
2555
|
+
|
|
2556
|
+
return {
|
|
2557
|
+
"configured": configured,
|
|
2558
|
+
"vault_path": str(vault) if vault else "",
|
|
2559
|
+
"exists": exists,
|
|
2560
|
+
"personas": _subdir_info("Personas"),
|
|
2561
|
+
"agents": _subdir_info("Agents"),
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
|
|
2503
2565
|
@app.get("/api/profile")
|
|
2504
2566
|
def profile_get():
|
|
2505
2567
|
"""Return the operator profile from ~/.arkaos/profile.json.
|