File size: 3,348 Bytes
23b413b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/**
 * Hook that wraps useAgenticCatalog and provides a unified capabilities view.
 *
 * Merges catalog.tools + catalog.a2a_agents into a single CapabilityItem[]
 * list with search, status, and type filtering.
 */

import { useMemo, useState } from 'react'
import { useAgenticCatalog } from '../../agentic/useAgenticCatalog'
import type { CapabilityItem } from '../../agentic/types'
import { deriveStatus } from './ToolStatusBadge'

type Args = {
  backendUrl: string
  apiKey?: string
}

export type TypeFilter = 'all' | 'tool' | 'a2a'

export function useToolsInventory({ backendUrl, apiKey }: Args) {
  const { catalog, loading, error, refresh } = useAgenticCatalog({
    backendUrl,
    apiKey,
    enabled: true,
  })

  const [search, setSearch] = useState('')
  const [statusFilter, setStatusFilter] = useState<'all' | 'active' | 'inactive'>('all')
  const [typeFilter, setTypeFilter] = useState<TypeFilter>('all')

  /* Build unified list from both sources */
  const allItems: CapabilityItem[] = useMemo(() => {
    const toolItems: CapabilityItem[] = (catalog?.tools || [])
      .filter((t) => t.id && t.name)
      .map((t) => ({ kind: 'tool', data: t }))

    const a2aItems: CapabilityItem[] = (catalog?.a2a_agents || [])
      .filter((a) => a.id && a.name)
      .map((a) => ({ kind: 'a2a_agent', data: a }))

    return [...toolItems, ...a2aItems]
  }, [catalog?.tools, catalog?.a2a_agents])

  /* Filtered list */
  const items = useMemo(() => {
    let filtered = allItems

    // Type filter
    if (typeFilter !== 'all') {
      const kind = typeFilter === 'a2a' ? 'a2a_agent' : 'tool'
      filtered = filtered.filter((item) => item.kind === kind)
    }

    // Search filter
    if (search.trim()) {
      const q = search.toLowerCase()
      filtered = filtered.filter((item) => {
        const d = item.data
        const searchable = `${d.name} ${d.description || ''} ${d.id} ${
          item.kind === 'a2a_agent' && 'endpoint_url' in d ? d.endpoint_url || '' : ''
        }`
        return searchable.toLowerCase().includes(q)
      })
    }

    // Status filter
    if (statusFilter !== 'all') {
      filtered = filtered.filter((item) => deriveStatus(item.data.enabled) === statusFilter)
    }

    return filtered
  }, [allItems, search, statusFilter, typeFilter])

  /* Counts */
  const counts = useMemo(() => {
    const tools = (catalog?.tools || [])
    const a2a = (catalog?.a2a_agents || [])

    return {
      total: tools.length + a2a.length,
      tools: {
        total: tools.length,
        active: tools.filter((t) => t.enabled === true).length,
        inactive: tools.filter((t) => t.enabled === false).length,
      },
      a2a: {
        total: a2a.length,
        active: a2a.filter((a) => a.enabled === true).length,
        inactive: a2a.filter((a) => a.enabled === false).length,
      },
      active: tools.filter((t) => t.enabled === true).length + a2a.filter((a) => a.enabled === true).length,
      inactive: tools.filter((t) => t.enabled === false).length + a2a.filter((a) => a.enabled === false).length,
    }
  }, [catalog?.tools, catalog?.a2a_agents])

  return {
    items,
    counts,
    loading,
    error,
    refresh,
    search,
    setSearch,
    statusFilter,
    setStatusFilter,
    typeFilter,
    setTypeFilter,
    forgeHealthy: catalog?.forge?.healthy ?? null,
  }
}