arkaos 3.38.0 → 3.40.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
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.40.0
|
|
@@ -69,9 +69,13 @@ async function refreshAll() {
|
|
|
69
69
|
await Promise.all([refresh(), refreshActivity()])
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
72
|
+
// PR92b v3.40.0 — initial values come from ?q=...&dept=...&tier=...
|
|
73
|
+
// so /agents links can deep-link to a filtered view.
|
|
74
|
+
const route = useRoute()
|
|
75
|
+
const router = useRouter()
|
|
76
|
+
const search = ref(String(route.query.q ?? ''))
|
|
77
|
+
const departmentFilter = ref(String(route.query.dept ?? 'all'))
|
|
78
|
+
const tierFilter = ref(String(route.query.tier ?? 'all'))
|
|
75
79
|
const page = ref(1)
|
|
76
80
|
const pageSize = 15
|
|
77
81
|
|
|
@@ -92,8 +96,13 @@ const tierOptions = [
|
|
|
92
96
|
]
|
|
93
97
|
|
|
94
98
|
// PR87a v3.19.0 — DNA filters (DISC primary + MBTI group).
|
|
95
|
-
|
|
96
|
-
const
|
|
99
|
+
// PR92b v3.40.0 — seed from URL query.
|
|
100
|
+
const discFilter = ref<'all' | 'D' | 'I' | 'S' | 'C'>(
|
|
101
|
+
(route.query.disc as any) ?? 'all',
|
|
102
|
+
)
|
|
103
|
+
const mbtiGroupFilter = ref<'all' | 'analysts' | 'diplomats' | 'sentinels' | 'explorers'>(
|
|
104
|
+
(route.query.mbti as any) ?? 'all',
|
|
105
|
+
)
|
|
97
106
|
|
|
98
107
|
const discOptions = [
|
|
99
108
|
{ label: 'All DISC', value: 'all' },
|
|
@@ -164,6 +173,23 @@ const paginatedAgents = computed(() => {
|
|
|
164
173
|
|
|
165
174
|
const totalPages = computed(() => Math.max(1, Math.ceil(totalFiltered.value / pageSize)))
|
|
166
175
|
|
|
176
|
+
// PR92b v3.40.0 — push filter state to URL so deep-links survive reload
|
|
177
|
+
// and the browser back/forward buttons work as expected.
|
|
178
|
+
watch(
|
|
179
|
+
[search, departmentFilter, tierFilter, discFilter, mbtiGroupFilter, favoritesOnly],
|
|
180
|
+
() => {
|
|
181
|
+
const query: Record<string, string> = {}
|
|
182
|
+
if (search.value.trim()) query.q = search.value.trim()
|
|
183
|
+
if (departmentFilter.value !== 'all') query.dept = departmentFilter.value
|
|
184
|
+
if (tierFilter.value !== 'all') query.tier = tierFilter.value
|
|
185
|
+
if (discFilter.value !== 'all') query.disc = discFilter.value
|
|
186
|
+
if (mbtiGroupFilter.value !== 'all') query.mbti = mbtiGroupFilter.value
|
|
187
|
+
if (favoritesOnly.value) query.fav = '1'
|
|
188
|
+
router.replace({ query })
|
|
189
|
+
},
|
|
190
|
+
{ flush: 'post' },
|
|
191
|
+
)
|
|
192
|
+
|
|
167
193
|
watch([search, departmentFilter, tierFilter, discFilter, mbtiGroupFilter], () => {
|
|
168
194
|
page.value = 1
|
|
169
195
|
})
|
|
@@ -200,9 +226,10 @@ function goToAgent(id: string) {
|
|
|
200
226
|
}
|
|
201
227
|
|
|
202
228
|
// PR86a v3.15.0 — favorites.
|
|
229
|
+
// PR92b v3.40.0 — favoritesOnly persists in URL (`?fav=1`).
|
|
203
230
|
const favs = useFavorites()
|
|
204
231
|
await favs.load()
|
|
205
|
-
const favoritesOnly = ref(
|
|
232
|
+
const favoritesOnly = ref(route.query.fav === '1')
|
|
206
233
|
|
|
207
234
|
// PR83b v3.4.0 — bulk selection + delete.
|
|
208
235
|
// PR84b v3.8.0 — bulk move department.
|
|
@@ -142,6 +142,40 @@ const favs = useFavorites()
|
|
|
142
142
|
await favs.load()
|
|
143
143
|
const favoritesOnly = ref(false)
|
|
144
144
|
|
|
145
|
+
// PR92a v3.39.0 — bulk export every persona as a zip.
|
|
146
|
+
const exportingZip = ref(false)
|
|
147
|
+
async function exportAllAsZip() {
|
|
148
|
+
exportingZip.value = true
|
|
149
|
+
try {
|
|
150
|
+
const blob = await $fetch<Blob>(
|
|
151
|
+
`${apiBase}/api/personas/export-all.zip`,
|
|
152
|
+
{ responseType: 'blob' },
|
|
153
|
+
)
|
|
154
|
+
const url = URL.createObjectURL(blob)
|
|
155
|
+
const a = document.createElement('a')
|
|
156
|
+
a.href = url
|
|
157
|
+
a.download = 'arkaos-personas.zip'
|
|
158
|
+
document.body.appendChild(a)
|
|
159
|
+
a.click()
|
|
160
|
+
a.remove()
|
|
161
|
+
URL.revokeObjectURL(url)
|
|
162
|
+
toast.add({
|
|
163
|
+
title: 'ZIP downloaded',
|
|
164
|
+
description: 'arkaos-personas.zip',
|
|
165
|
+
color: 'success',
|
|
166
|
+
icon: 'i-lucide-archive',
|
|
167
|
+
})
|
|
168
|
+
} catch (err) {
|
|
169
|
+
toast.add({
|
|
170
|
+
title: 'Export failed',
|
|
171
|
+
description: err instanceof Error ? err.message : 'unknown error',
|
|
172
|
+
color: 'error',
|
|
173
|
+
})
|
|
174
|
+
} finally {
|
|
175
|
+
exportingZip.value = false
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
145
179
|
// PR87b v3.20.0 — import .md persona files.
|
|
146
180
|
// PR91b v3.36.0 — extended with URL import.
|
|
147
181
|
const importInput = ref<HTMLInputElement | null>(null)
|
|
@@ -354,6 +388,14 @@ async function undoTrashIds(ids: string[]) {
|
|
|
354
388
|
/>
|
|
355
389
|
</template>
|
|
356
390
|
<template #right>
|
|
391
|
+
<UButton
|
|
392
|
+
label="Export ZIP"
|
|
393
|
+
icon="i-lucide-archive"
|
|
394
|
+
variant="ghost"
|
|
395
|
+
size="sm"
|
|
396
|
+
:loading="exportingZip"
|
|
397
|
+
@click="exportAllAsZip"
|
|
398
|
+
/>
|
|
357
399
|
<UDropdownMenu
|
|
358
400
|
:items="[
|
|
359
401
|
{ label: 'Pick .md files…', icon: 'i-lucide-file-up', onSelect: triggerImport },
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
Binary file
|
package/scripts/dashboard-api.py
CHANGED
|
@@ -322,6 +322,68 @@ def agent_activity_strip(agent_id: str, period: str = "month"):
|
|
|
322
322
|
}
|
|
323
323
|
|
|
324
324
|
|
|
325
|
+
@app.get("/api/personas/export-all.zip")
|
|
326
|
+
def personas_export_all():
|
|
327
|
+
"""PR92a v3.39.0 — stream every persona as Markdown inside a ZIP.
|
|
328
|
+
|
|
329
|
+
Iterates `PersonaManager.list_all()` plus any Obsidian-vault entries
|
|
330
|
+
surfaced via `persona_detail`, renders each through
|
|
331
|
+
`ObsidianPersonaStore._render`, and zips them. Filename uses the
|
|
332
|
+
persona name (sanitised), falling back to id.
|
|
333
|
+
"""
|
|
334
|
+
mgr = _get_persona_manager()
|
|
335
|
+
if not mgr:
|
|
336
|
+
return {"error": "Persona manager unavailable"}
|
|
337
|
+
try:
|
|
338
|
+
items = list(mgr.list_all() or [])
|
|
339
|
+
except Exception as exc: # noqa: BLE001
|
|
340
|
+
return {"error": f"list failed: {exc}"}
|
|
341
|
+
|
|
342
|
+
from core.personas.obsidian_store import ObsidianPersonaStore
|
|
343
|
+
|
|
344
|
+
import io
|
|
345
|
+
import zipfile
|
|
346
|
+
|
|
347
|
+
buffer = io.BytesIO()
|
|
348
|
+
seen: set[str] = set()
|
|
349
|
+
written = 0
|
|
350
|
+
with zipfile.ZipFile(buffer, mode="w", compression=zipfile.ZIP_DEFLATED) as zf:
|
|
351
|
+
for p in items:
|
|
352
|
+
persona = p if hasattr(p, "model_dump") else None
|
|
353
|
+
if persona is None:
|
|
354
|
+
continue
|
|
355
|
+
slug = _zip_persona_slug(persona.name or persona.id)
|
|
356
|
+
if slug in seen:
|
|
357
|
+
slug = f"{slug}-{persona.id[:6]}"
|
|
358
|
+
seen.add(slug)
|
|
359
|
+
try:
|
|
360
|
+
body = ObsidianPersonaStore._render(persona)
|
|
361
|
+
except Exception: # noqa: BLE001
|
|
362
|
+
continue
|
|
363
|
+
zf.writestr(f"{slug}.md", body)
|
|
364
|
+
written += 1
|
|
365
|
+
|
|
366
|
+
if written == 0:
|
|
367
|
+
return {"error": "no personas to export"}
|
|
368
|
+
|
|
369
|
+
from fastapi import Response
|
|
370
|
+
return Response(
|
|
371
|
+
content=buffer.getvalue(),
|
|
372
|
+
media_type="application/zip",
|
|
373
|
+
headers={
|
|
374
|
+
"Content-Disposition": 'attachment; filename="arkaos-personas.zip"',
|
|
375
|
+
},
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _zip_persona_slug(name: str) -> str:
|
|
380
|
+
"""Sanitise a name for use as a zip member filename."""
|
|
381
|
+
import re
|
|
382
|
+
cleaned = re.sub(r"[^A-Za-z0-9._-]+", "-", str(name or ""))
|
|
383
|
+
cleaned = cleaned.strip("-") or "persona"
|
|
384
|
+
return cleaned[:80]
|
|
385
|
+
|
|
386
|
+
|
|
325
387
|
@app.get("/api/personas/{persona_id}/markdown")
|
|
326
388
|
def persona_download_markdown(persona_id: str):
|
|
327
389
|
"""PR90a v3.31.0 — return the persona as a Markdown file.
|