arkaos 3.60.0 → 3.62.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.60.0
1
+ 3.62.0
package/core/favorites.py CHANGED
@@ -89,3 +89,36 @@ def set_favorite(kind: str, item_id: str, favorited: bool) -> dict:
89
89
  state[kind] = bucket
90
90
  _save(state)
91
91
  return {"kind": kind, "id": item_id, "favorited": favorited}
92
+
93
+
94
+ def set_many(kind: str, ids: list[str], favorited: bool) -> dict:
95
+ """PR97c v3.61.0 — bulk-set favourite state for many ids.
96
+
97
+ Returns ``{kind, favorited, applied: N, total: N}`` where applied
98
+ counts how many ids actually changed state.
99
+ """
100
+ if kind not in _VALID_KINDS:
101
+ return {"error": f"unknown kind: {kind!r}", "applied": 0, "total": 0}
102
+ if not isinstance(ids, list):
103
+ return {"error": "ids must be a list", "applied": 0, "total": 0}
104
+ state = _load()
105
+ bucket = state.setdefault(kind, [])
106
+ existing = set(bucket)
107
+ applied = 0
108
+ for item_id in ids:
109
+ if not isinstance(item_id, str) or not item_id:
110
+ continue
111
+ if favorited and item_id not in existing:
112
+ existing.add(item_id)
113
+ applied += 1
114
+ elif not favorited and item_id in existing:
115
+ existing.discard(item_id)
116
+ applied += 1
117
+ state[kind] = list(existing)
118
+ _save(state)
119
+ return {
120
+ "kind": kind,
121
+ "favorited": favorited,
122
+ "applied": applied,
123
+ "total": len([i for i in ids if isinstance(i, str) and i]),
124
+ }
@@ -43,6 +43,35 @@ const _useFavorites = () => {
43
43
  return state.value.personas.includes(id)
44
44
  }
45
45
 
46
+ // PR97c v3.61.0 — bulk set N ids in one POST.
47
+ async function setMany(
48
+ kind: 'agents' | 'personas',
49
+ ids: string[],
50
+ favorited: boolean,
51
+ ) {
52
+ try {
53
+ const res = await $fetch<{
54
+ applied?: number
55
+ total?: number
56
+ error?: string
57
+ }>(`${apiBase}/api/favorites/bulk`, {
58
+ method: 'POST',
59
+ body: { kind, ids, favorited },
60
+ })
61
+ if (res.error) throw new Error(res.error)
62
+ // Sync local state with the new server truth.
63
+ await load(true)
64
+ return res.applied ?? 0
65
+ } catch (err) {
66
+ toast.add({
67
+ title: 'Favorites bulk update failed',
68
+ description: err instanceof Error ? err.message : 'unknown error',
69
+ color: 'error',
70
+ })
71
+ return null
72
+ }
73
+ }
74
+
46
75
  async function toggle(kind: 'agents' | 'personas', id: string) {
47
76
  try {
48
77
  const res = await $fetch<{ favorited?: boolean, error?: string }>(
@@ -67,7 +96,7 @@ const _useFavorites = () => {
67
96
  }
68
97
  }
69
98
 
70
- return { state, load, isAgentFavorite, isPersonaFavorite, toggle, loaded }
99
+ return { state, load, isAgentFavorite, isPersonaFavorite, toggle, setMany, loaded }
71
100
  }
72
101
 
73
102
  export const useFavorites = createSharedComposable(_useFavorites)
@@ -177,6 +177,54 @@ async function saveYamlEditor() {
177
177
  }
178
178
  }
179
179
 
180
+ // PR97d v3.62.0 — inline edit for name / role.
181
+ type InlineField = 'name' | 'role'
182
+ const inlineField = ref<InlineField | null>(null)
183
+ const inlineDraft = ref('')
184
+ const inlineSaving = ref(false)
185
+
186
+ function startInline(field: InlineField, current: string | undefined) {
187
+ inlineField.value = field
188
+ inlineDraft.value = current ?? ''
189
+ }
190
+ function cancelInline() {
191
+ inlineField.value = null
192
+ inlineDraft.value = ''
193
+ }
194
+ async function commitInline(field: InlineField) {
195
+ if (!agent.value || inlineField.value !== field) return
196
+ const next = inlineDraft.value.trim()
197
+ const current = (field === 'name' ? agent.value.name : agent.value.role) ?? ''
198
+ if (!next || next === current) {
199
+ cancelInline()
200
+ return
201
+ }
202
+ inlineSaving.value = true
203
+ try {
204
+ const res = await $fetch<{ updated?: boolean, error?: string }>(
205
+ `${apiBase}/api/agents/${agentId}`,
206
+ { method: 'PUT', body: { [field]: next } },
207
+ )
208
+ if (res.error) throw new Error(res.error)
209
+ toast.add({
210
+ title: field === 'name' ? 'Name updated' : 'Role updated',
211
+ description: next,
212
+ color: 'success',
213
+ icon: 'i-lucide-check',
214
+ })
215
+ await refresh()
216
+ } catch (err) {
217
+ toast.add({
218
+ title: 'Save failed',
219
+ description: err instanceof Error ? err.message : 'unknown error',
220
+ color: 'error',
221
+ })
222
+ } finally {
223
+ inlineSaving.value = false
224
+ inlineField.value = null
225
+ }
226
+ }
227
+
180
228
  // PR89d v3.30.0 — download YAML.
181
229
  const downloadingYaml = ref(false)
182
230
  async function downloadYaml() {
@@ -416,6 +416,22 @@ async function bulkDelete() {
416
416
  await refreshAll()
417
417
  }
418
418
 
419
+ // PR97c v3.61.0 — bulk star / unstar selected agents.
420
+ async function bulkStar(favorited: boolean) {
421
+ if (selected.value.size === 0) return
422
+ const ids = Array.from(selected.value)
423
+ const applied = await favs.setMany('agents', ids, favorited)
424
+ if (applied === null) return
425
+ toast.add({
426
+ title: favorited
427
+ ? `Starred ${applied} agent${applied === 1 ? '' : 's'}`
428
+ : `Unstarred ${applied} agent${applied === 1 ? '' : 's'}`,
429
+ description: applied < ids.length ? `${ids.length - applied} already in state` : undefined,
430
+ color: 'success',
431
+ icon: favorited ? 'i-lucide-star' : 'i-lucide-star-off',
432
+ })
433
+ }
434
+
419
435
  async function undoTrashIds(ids: string[]) {
420
436
  const results = await Promise.allSettled(
421
437
  ids.map((tid) =>
@@ -636,6 +652,22 @@ async function undoTrashIds(ids: string[]) {
636
652
  @click="clearSelection"
637
653
  />
638
654
  <div class="h-5 w-px bg-default" />
655
+ <UButton
656
+ icon="i-lucide-star"
657
+ size="sm"
658
+ variant="soft"
659
+ color="warning"
660
+ aria-label="Star selected"
661
+ @click="bulkStar(true)"
662
+ />
663
+ <UButton
664
+ icon="i-lucide-star-off"
665
+ size="sm"
666
+ variant="ghost"
667
+ color="neutral"
668
+ aria-label="Unstar selected"
669
+ @click="bulkStar(false)"
670
+ />
639
671
  <UButton
640
672
  label="Compare"
641
673
  icon="i-lucide-columns-2"
@@ -418,6 +418,22 @@ async function bulkDelete() {
418
418
  await refreshAll()
419
419
  }
420
420
 
421
+ // PR97c v3.61.0 — bulk star / unstar selected personas.
422
+ async function bulkStar(favorited: boolean) {
423
+ if (selected.value.size === 0) return
424
+ const ids = Array.from(selected.value)
425
+ const applied = await favs.setMany('personas', ids, favorited)
426
+ if (applied === null) return
427
+ toast.add({
428
+ title: favorited
429
+ ? `Starred ${applied} persona${applied === 1 ? '' : 's'}`
430
+ : `Unstarred ${applied} persona${applied === 1 ? '' : 's'}`,
431
+ description: applied < ids.length ? `${ids.length - applied} already in state` : undefined,
432
+ color: 'success',
433
+ icon: favorited ? 'i-lucide-star' : 'i-lucide-star-off',
434
+ })
435
+ }
436
+
421
437
  async function undoTrashIds(ids: string[]) {
422
438
  const results = await Promise.allSettled(
423
439
  ids.map((tid) =>
@@ -721,6 +737,22 @@ async function undoTrashIds(ids: string[]) {
721
737
  @click="clearSelection"
722
738
  />
723
739
  <div class="h-5 w-px bg-default" />
740
+ <UButton
741
+ icon="i-lucide-star"
742
+ size="sm"
743
+ variant="soft"
744
+ color="warning"
745
+ aria-label="Star selected"
746
+ @click="bulkStar(true)"
747
+ />
748
+ <UButton
749
+ icon="i-lucide-star-off"
750
+ size="sm"
751
+ variant="ghost"
752
+ color="neutral"
753
+ aria-label="Unstar selected"
754
+ @click="bulkStar(false)"
755
+ />
724
756
  <UButton
725
757
  label="Export ZIP"
726
758
  icon="i-lucide-archive"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkaos",
3
- "version": "3.60.0",
3
+ "version": "3.62.0",
4
4
  "description": "The Operating System for AI Agent Teams",
5
5
  "type": "module",
6
6
  "bin": {
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "arkaos-core"
3
- version = "3.60.0"
3
+ version = "3.62.0"
4
4
  description = "Core engine for ArkaOS — The Operating System for AI Agent Teams"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -2607,6 +2607,22 @@ def favorites_toggle(kind: str, item_id: str):
2607
2607
  return _fav.toggle(kind, item_id)
2608
2608
 
2609
2609
 
2610
+ @app.post("/api/favorites/bulk")
2611
+ def favorites_bulk(body: dict):
2612
+ """PR97c v3.61.0 — bulk star/unstar many ids in one shot.
2613
+
2614
+ Body: ``{"kind": "agents"|"personas", "ids": [...], "favorited": bool}``
2615
+ Returns ``{kind, favorited, applied, total}``.
2616
+ """
2617
+ if not isinstance(body, dict):
2618
+ return {"error": "body must be an object"}
2619
+ kind = (body.get("kind") or "").strip()
2620
+ ids = body.get("ids") or []
2621
+ favorited = bool(body.get("favorited"))
2622
+ from core import favorites as _fav
2623
+ return _fav.set_many(kind, ids if isinstance(ids, list) else [], favorited)
2624
+
2625
+
2610
2626
  # --- Global search (PR85d v3.14.0) ---
2611
2627
 
2612
2628
  @app.get("/api/search")