cursor-recursive-rag 0.2.0-alpha.1

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.
Files changed (144) hide show
  1. package/README.md +354 -0
  2. package/bin/cursor-rag.js +18 -0
  3. package/dist/adapters/embeddings/index.d.ts +5 -0
  4. package/dist/adapters/embeddings/index.d.ts.map +1 -0
  5. package/dist/adapters/embeddings/index.js +16 -0
  6. package/dist/adapters/embeddings/index.js.map +1 -0
  7. package/dist/adapters/embeddings/ollama.d.ts +11 -0
  8. package/dist/adapters/embeddings/ollama.d.ts.map +1 -0
  9. package/dist/adapters/embeddings/ollama.js +30 -0
  10. package/dist/adapters/embeddings/ollama.js.map +1 -0
  11. package/dist/adapters/embeddings/openai.d.ts +11 -0
  12. package/dist/adapters/embeddings/openai.d.ts.map +1 -0
  13. package/dist/adapters/embeddings/openai.js +28 -0
  14. package/dist/adapters/embeddings/openai.js.map +1 -0
  15. package/dist/adapters/embeddings/types.d.ts +6 -0
  16. package/dist/adapters/embeddings/types.d.ts.map +1 -0
  17. package/dist/adapters/embeddings/types.js +2 -0
  18. package/dist/adapters/embeddings/types.js.map +1 -0
  19. package/dist/adapters/embeddings/xenova.d.ts +11 -0
  20. package/dist/adapters/embeddings/xenova.d.ts.map +1 -0
  21. package/dist/adapters/embeddings/xenova.js +26 -0
  22. package/dist/adapters/embeddings/xenova.js.map +1 -0
  23. package/dist/adapters/vector/chroma.d.ts +23 -0
  24. package/dist/adapters/vector/chroma.d.ts.map +1 -0
  25. package/dist/adapters/vector/chroma.js +91 -0
  26. package/dist/adapters/vector/chroma.js.map +1 -0
  27. package/dist/adapters/vector/index.d.ts +5 -0
  28. package/dist/adapters/vector/index.d.ts.map +1 -0
  29. package/dist/adapters/vector/index.js +22 -0
  30. package/dist/adapters/vector/index.js.map +1 -0
  31. package/dist/adapters/vector/memory.d.ts +22 -0
  32. package/dist/adapters/vector/memory.d.ts.map +1 -0
  33. package/dist/adapters/vector/memory.js +105 -0
  34. package/dist/adapters/vector/memory.js.map +1 -0
  35. package/dist/adapters/vector/qdrant.d.ts +14 -0
  36. package/dist/adapters/vector/qdrant.d.ts.map +1 -0
  37. package/dist/adapters/vector/qdrant.js +120 -0
  38. package/dist/adapters/vector/qdrant.js.map +1 -0
  39. package/dist/adapters/vector/redis.d.ts +31 -0
  40. package/dist/adapters/vector/redis.d.ts.map +1 -0
  41. package/dist/adapters/vector/redis.js +164 -0
  42. package/dist/adapters/vector/redis.js.map +1 -0
  43. package/dist/adapters/vector/vectorize.d.ts +10 -0
  44. package/dist/adapters/vector/vectorize.d.ts.map +1 -0
  45. package/dist/adapters/vector/vectorize.js +22 -0
  46. package/dist/adapters/vector/vectorize.js.map +1 -0
  47. package/dist/cli/commands/dashboard.d.ts +3 -0
  48. package/dist/cli/commands/dashboard.d.ts.map +1 -0
  49. package/dist/cli/commands/dashboard.js +23 -0
  50. package/dist/cli/commands/dashboard.js.map +1 -0
  51. package/dist/cli/commands/ingest.d.ts +3 -0
  52. package/dist/cli/commands/ingest.d.ts.map +1 -0
  53. package/dist/cli/commands/ingest.js +133 -0
  54. package/dist/cli/commands/ingest.js.map +1 -0
  55. package/dist/cli/commands/search.d.ts +3 -0
  56. package/dist/cli/commands/search.d.ts.map +1 -0
  57. package/dist/cli/commands/search.js +39 -0
  58. package/dist/cli/commands/search.js.map +1 -0
  59. package/dist/cli/commands/setup.d.ts +3 -0
  60. package/dist/cli/commands/setup.d.ts.map +1 -0
  61. package/dist/cli/commands/setup.js +311 -0
  62. package/dist/cli/commands/setup.js.map +1 -0
  63. package/dist/cli/commands/status.d.ts +3 -0
  64. package/dist/cli/commands/status.d.ts.map +1 -0
  65. package/dist/cli/commands/status.js +39 -0
  66. package/dist/cli/commands/status.js.map +1 -0
  67. package/dist/cli/index.d.ts +3 -0
  68. package/dist/cli/index.d.ts.map +1 -0
  69. package/dist/cli/index.js +34 -0
  70. package/dist/cli/index.js.map +1 -0
  71. package/dist/dashboard/public/index.html +819 -0
  72. package/dist/dashboard/server.d.ts +10 -0
  73. package/dist/dashboard/server.d.ts.map +1 -0
  74. package/dist/dashboard/server.js +397 -0
  75. package/dist/dashboard/server.js.map +1 -0
  76. package/dist/integrations/index.d.ts +3 -0
  77. package/dist/integrations/index.d.ts.map +1 -0
  78. package/dist/integrations/index.js +3 -0
  79. package/dist/integrations/index.js.map +1 -0
  80. package/dist/integrations/mcp-gateway.d.ts +91 -0
  81. package/dist/integrations/mcp-gateway.d.ts.map +1 -0
  82. package/dist/integrations/mcp-gateway.js +128 -0
  83. package/dist/integrations/mcp-gateway.js.map +1 -0
  84. package/dist/integrations/openskills.d.ts +67 -0
  85. package/dist/integrations/openskills.d.ts.map +1 -0
  86. package/dist/integrations/openskills.js +223 -0
  87. package/dist/integrations/openskills.js.map +1 -0
  88. package/dist/proxy/index.d.ts +29 -0
  89. package/dist/proxy/index.d.ts.map +1 -0
  90. package/dist/proxy/index.js +124 -0
  91. package/dist/proxy/index.js.map +1 -0
  92. package/dist/server/index.d.ts +2 -0
  93. package/dist/server/index.d.ts.map +1 -0
  94. package/dist/server/index.js +43 -0
  95. package/dist/server/index.js.map +1 -0
  96. package/dist/server/tools/crawl.d.ts +15 -0
  97. package/dist/server/tools/crawl.d.ts.map +1 -0
  98. package/dist/server/tools/crawl.js +97 -0
  99. package/dist/server/tools/crawl.js.map +1 -0
  100. package/dist/server/tools/gateway.d.ts +27 -0
  101. package/dist/server/tools/gateway.d.ts.map +1 -0
  102. package/dist/server/tools/gateway.js +58 -0
  103. package/dist/server/tools/gateway.js.map +1 -0
  104. package/dist/server/tools/index.d.ts +10 -0
  105. package/dist/server/tools/index.d.ts.map +1 -0
  106. package/dist/server/tools/index.js +302 -0
  107. package/dist/server/tools/index.js.map +1 -0
  108. package/dist/server/tools/ingest.d.ts +15 -0
  109. package/dist/server/tools/ingest.d.ts.map +1 -0
  110. package/dist/server/tools/ingest.js +84 -0
  111. package/dist/server/tools/ingest.js.map +1 -0
  112. package/dist/server/tools/list-sources.d.ts +9 -0
  113. package/dist/server/tools/list-sources.d.ts.map +1 -0
  114. package/dist/server/tools/list-sources.js +63 -0
  115. package/dist/server/tools/list-sources.js.map +1 -0
  116. package/dist/server/tools/recursive-query.d.ts +16 -0
  117. package/dist/server/tools/recursive-query.d.ts.map +1 -0
  118. package/dist/server/tools/recursive-query.js +65 -0
  119. package/dist/server/tools/recursive-query.js.map +1 -0
  120. package/dist/server/tools/search.d.ts +15 -0
  121. package/dist/server/tools/search.d.ts.map +1 -0
  122. package/dist/server/tools/search.js +20 -0
  123. package/dist/server/tools/search.js.map +1 -0
  124. package/dist/server/tools/skills.d.ts +30 -0
  125. package/dist/server/tools/skills.d.ts.map +1 -0
  126. package/dist/server/tools/skills.js +85 -0
  127. package/dist/server/tools/skills.js.map +1 -0
  128. package/dist/services/chunker.d.ts +3 -0
  129. package/dist/services/chunker.d.ts.map +1 -0
  130. package/dist/services/chunker.js +75 -0
  131. package/dist/services/chunker.js.map +1 -0
  132. package/dist/services/config.d.ts +8 -0
  133. package/dist/services/config.d.ts.map +1 -0
  134. package/dist/services/config.js +66 -0
  135. package/dist/services/config.js.map +1 -0
  136. package/dist/services/query-decomposer.d.ts +44 -0
  137. package/dist/services/query-decomposer.d.ts.map +1 -0
  138. package/dist/services/query-decomposer.js +79 -0
  139. package/dist/services/query-decomposer.js.map +1 -0
  140. package/dist/types/index.d.ts +93 -0
  141. package/dist/types/index.d.ts.map +1 -0
  142. package/dist/types/index.js +2 -0
  143. package/dist/types/index.js.map +1 -0
  144. package/package.json +56 -0
@@ -0,0 +1,819 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" class="dark">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Cursor RAG Dashboard</title>
7
+ <script src="https://cdn.tailwindcss.com?plugins=forms"></script>
8
+ <script>
9
+ tailwind.config = {
10
+ darkMode: 'class',
11
+ theme: {
12
+ extend: {
13
+ colors: {
14
+ primary: {
15
+ 50: '#f0f9ff',
16
+ 100: '#e0f2fe',
17
+ 200: '#bae6fd',
18
+ 300: '#7dd3fc',
19
+ 400: '#38bdf8',
20
+ 500: '#0ea5e9',
21
+ 600: '#0284c7',
22
+ 700: '#0369a1',
23
+ 800: '#075985',
24
+ 900: '#0c4a6e',
25
+ 950: '#082f49',
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }
31
+ </script>
32
+ <style>
33
+ @keyframes pulse-dot {
34
+ 0%, 100% { opacity: 1; }
35
+ 50% { opacity: 0.5; }
36
+ }
37
+ .pulse-dot { animation: pulse-dot 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; }
38
+ </style>
39
+ </head>
40
+ <body class="bg-gray-950 text-gray-100 min-h-screen">
41
+ <div id="app" class="flex">
42
+ <!-- Sidebar -->
43
+ <aside class="w-64 bg-gray-900 border-r border-gray-800 min-h-screen p-4 fixed">
44
+ <div class="flex items-center gap-3 mb-8">
45
+ <div class="w-10 h-10 bg-primary-600 rounded-lg flex items-center justify-center">
46
+ <svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
47
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/>
48
+ </svg>
49
+ </div>
50
+ <div>
51
+ <h1 class="font-bold text-lg">Cursor RAG</h1>
52
+ <p class="text-xs text-gray-500">Dashboard</p>
53
+ </div>
54
+ </div>
55
+
56
+ <nav class="space-y-1">
57
+ <a href="#" onclick="showTab('overview')" id="nav-overview" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg bg-gray-800 text-white">
58
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
59
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"/>
60
+ </svg>
61
+ Overview
62
+ </a>
63
+ <a href="#" onclick="showTab('search')" id="nav-search" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg text-gray-400 hover:bg-gray-800 hover:text-white transition-colors">
64
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
65
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
66
+ </svg>
67
+ Search
68
+ </a>
69
+ <a href="#" onclick="showTab('gateway')" id="nav-gateway" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg text-gray-400 hover:bg-gray-800 hover:text-white transition-colors">
70
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
71
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
72
+ </svg>
73
+ MCP Gateway
74
+ </a>
75
+ <a href="#" onclick="showTab('skills')" id="nav-skills" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg text-gray-400 hover:bg-gray-800 hover:text-white transition-colors">
76
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
77
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/>
78
+ </svg>
79
+ OpenSkills
80
+ </a>
81
+ <a href="#" onclick="showTab('activity')" id="nav-activity" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg text-gray-400 hover:bg-gray-800 hover:text-white transition-colors">
82
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
83
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
84
+ </svg>
85
+ Activity
86
+ </a>
87
+ <a href="#" onclick="showTab('settings')" id="nav-settings" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg text-gray-400 hover:bg-gray-800 hover:text-white transition-colors">
88
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
89
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
90
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
91
+ </svg>
92
+ Settings
93
+ </a>
94
+ </nav>
95
+
96
+ <div class="absolute bottom-4 left-4 right-4">
97
+ <div class="bg-gray-800 rounded-lg p-3">
98
+ <div class="flex items-center gap-2 mb-2">
99
+ <div id="status-dot" class="w-2 h-2 bg-green-500 rounded-full pulse-dot"></div>
100
+ <span class="text-sm text-gray-400">Server Status</span>
101
+ </div>
102
+ <p id="status-text" class="text-xs text-gray-500">Connected to MCP</p>
103
+ </div>
104
+ </div>
105
+ </aside>
106
+
107
+ <!-- Main Content -->
108
+ <main class="ml-64 flex-1 p-8">
109
+ <!-- Overview Tab -->
110
+ <div id="tab-overview" class="tab-content">
111
+ <h2 class="text-2xl font-bold mb-6">Overview</h2>
112
+
113
+ <!-- Stats Cards -->
114
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
115
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-5">
116
+ <div class="flex items-center justify-between mb-3">
117
+ <span class="text-gray-400 text-sm">RAG Chunks</span>
118
+ <svg class="w-5 h-5 text-primary-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
119
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4"/>
120
+ </svg>
121
+ </div>
122
+ <p class="text-3xl font-bold" id="stat-chunks">-</p>
123
+ </div>
124
+
125
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-5">
126
+ <div class="flex items-center justify-between mb-3">
127
+ <span class="text-gray-400 text-sm">Gateway Tools</span>
128
+ <svg class="w-5 h-5 text-purple-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
129
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
130
+ </svg>
131
+ </div>
132
+ <p class="text-3xl font-bold" id="stat-gateway-tools">-</p>
133
+ </div>
134
+
135
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-5">
136
+ <div class="flex items-center justify-between mb-3">
137
+ <span class="text-gray-400 text-sm">OpenSkills</span>
138
+ <svg class="w-5 h-5 text-yellow-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
139
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/>
140
+ </svg>
141
+ </div>
142
+ <p class="text-3xl font-bold" id="stat-skills">-</p>
143
+ </div>
144
+
145
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-5">
146
+ <div class="flex items-center justify-between mb-3">
147
+ <span class="text-gray-400 text-sm">Gateway Backends</span>
148
+ <svg class="w-5 h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
149
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2"/>
150
+ </svg>
151
+ </div>
152
+ <p class="text-3xl font-bold" id="stat-backends">-</p>
153
+ </div>
154
+ </div>
155
+
156
+ <!-- Integration Status -->
157
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-8">
158
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-6">
159
+ <h3 class="text-lg font-semibold mb-4 flex items-center gap-2">
160
+ <span id="gateway-status-icon" class="w-3 h-3 bg-gray-500 rounded-full"></span>
161
+ MCP Gateway
162
+ </h3>
163
+ <div id="gateway-status-content" class="text-sm text-gray-400">
164
+ Checking connection...
165
+ </div>
166
+ </div>
167
+
168
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-6">
169
+ <h3 class="text-lg font-semibold mb-4 flex items-center gap-2">
170
+ <span id="skills-status-icon" class="w-3 h-3 bg-gray-500 rounded-full"></span>
171
+ OpenSkills
172
+ </h3>
173
+ <div id="skills-status-content" class="text-sm text-gray-400">
174
+ Checking configuration...
175
+ </div>
176
+ </div>
177
+ </div>
178
+
179
+ <!-- Quick Actions -->
180
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-6 mb-8">
181
+ <h3 class="text-lg font-semibold mb-4">Quick Actions</h3>
182
+ <div class="flex flex-wrap gap-3">
183
+ <button onclick="showTab('search')" class="px-4 py-2 bg-primary-600 hover:bg-primary-700 rounded-lg transition-colors flex items-center gap-2">
184
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
185
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
186
+ </svg>
187
+ Search Knowledge Base
188
+ </button>
189
+ <button onclick="showTab('gateway')" class="px-4 py-2 bg-purple-600 hover:bg-purple-700 rounded-lg transition-colors flex items-center gap-2">
190
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
191
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
192
+ </svg>
193
+ Browse Gateway Tools
194
+ </button>
195
+ <button onclick="showTab('skills')" class="px-4 py-2 bg-yellow-600 hover:bg-yellow-700 rounded-lg transition-colors flex items-center gap-2">
196
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
197
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/>
198
+ </svg>
199
+ Browse Skills
200
+ </button>
201
+ <button onclick="refreshAll()" class="px-4 py-2 bg-gray-800 hover:bg-gray-700 rounded-lg transition-colors flex items-center gap-2">
202
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
203
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
204
+ </svg>
205
+ Refresh All
206
+ </button>
207
+ </div>
208
+ </div>
209
+
210
+ <!-- Recent Activity Preview -->
211
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-6">
212
+ <div class="flex items-center justify-between mb-4">
213
+ <h3 class="text-lg font-semibold">Recent Activity</h3>
214
+ <a href="#" onclick="showTab('activity')" class="text-primary-500 text-sm hover:underline">View all</a>
215
+ </div>
216
+ <div id="recent-activity" class="space-y-3">
217
+ <p class="text-gray-500 text-sm">Loading...</p>
218
+ </div>
219
+ </div>
220
+ </div>
221
+
222
+ <!-- Search Tab -->
223
+ <div id="tab-search" class="tab-content hidden">
224
+ <h2 class="text-2xl font-bold mb-6">Search Knowledge Base</h2>
225
+
226
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-6 mb-6">
227
+ <div class="flex gap-4">
228
+ <input type="text" id="search-input" placeholder="Search ingested documents..."
229
+ class="flex-1 bg-gray-800 border border-gray-700 rounded-lg px-4 py-3 focus:outline-none focus:border-primary-500 focus:ring-1 focus:ring-primary-500">
230
+ <button onclick="performSearch()" class="px-6 py-3 bg-primary-600 hover:bg-primary-700 rounded-lg transition-colors flex items-center gap-2">
231
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
232
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
233
+ </svg>
234
+ Search
235
+ </button>
236
+ </div>
237
+ <p class="text-xs text-gray-500 mt-2">Searches documents ingested via cursor-rag ingest command</p>
238
+ </div>
239
+
240
+ <div id="search-results" class="space-y-4">
241
+ <p class="text-gray-500 text-center py-8">Enter a query to search the knowledge base</p>
242
+ </div>
243
+ </div>
244
+
245
+ <!-- MCP Gateway Tab -->
246
+ <div id="tab-gateway" class="tab-content hidden">
247
+ <h2 class="text-2xl font-bold mb-6">MCP Gateway Tools</h2>
248
+
249
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-6 mb-6">
250
+ <div class="flex gap-4">
251
+ <input type="text" id="gateway-search-input" placeholder="Search tools (e.g., linear, github, filesystem)..."
252
+ class="flex-1 bg-gray-800 border border-gray-700 rounded-lg px-4 py-3 focus:outline-none focus:border-primary-500 focus:ring-1 focus:ring-primary-500">
253
+ <button onclick="searchGatewayTools()" class="px-6 py-3 bg-purple-600 hover:bg-purple-700 rounded-lg transition-colors flex items-center gap-2">
254
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
255
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
256
+ </svg>
257
+ Search
258
+ </button>
259
+ </div>
260
+ </div>
261
+
262
+ <!-- Backend Filter -->
263
+ <div class="mb-6">
264
+ <div class="flex flex-wrap gap-2" id="backend-filters">
265
+ <button onclick="filterBackend('')" class="backend-filter px-3 py-1 bg-gray-800 hover:bg-gray-700 rounded-full text-sm transition-colors active" data-backend="">
266
+ All
267
+ </button>
268
+ </div>
269
+ </div>
270
+
271
+ <div id="gateway-tools-list" class="space-y-4">
272
+ <p class="text-gray-500 text-center py-8">Loading gateway tools...</p>
273
+ </div>
274
+ </div>
275
+
276
+ <!-- OpenSkills Tab -->
277
+ <div id="tab-skills" class="tab-content hidden">
278
+ <h2 class="text-2xl font-bold mb-6">OpenSkills</h2>
279
+
280
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-6 mb-6">
281
+ <div class="flex gap-4">
282
+ <input type="text" id="skills-search-input" placeholder="Search skills..."
283
+ class="flex-1 bg-gray-800 border border-gray-700 rounded-lg px-4 py-3 focus:outline-none focus:border-primary-500 focus:ring-1 focus:ring-primary-500">
284
+ <button onclick="searchSkills()" class="px-6 py-3 bg-yellow-600 hover:bg-yellow-700 rounded-lg transition-colors flex items-center gap-2">
285
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
286
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
287
+ </svg>
288
+ Search
289
+ </button>
290
+ </div>
291
+ </div>
292
+
293
+ <div id="skills-list" class="space-y-4">
294
+ <p class="text-gray-500 text-center py-8">Loading skills...</p>
295
+ </div>
296
+ </div>
297
+
298
+ <!-- Activity Tab -->
299
+ <div id="tab-activity" class="tab-content hidden">
300
+ <h2 class="text-2xl font-bold mb-6">Activity Log</h2>
301
+
302
+ <div class="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden">
303
+ <div id="activity-log" class="divide-y divide-gray-800">
304
+ <p class="text-gray-500 text-center py-8">Loading activity...</p>
305
+ </div>
306
+ </div>
307
+ </div>
308
+
309
+ <!-- Settings Tab -->
310
+ <div id="tab-settings" class="tab-content hidden">
311
+ <h2 class="text-2xl font-bold mb-6">Settings</h2>
312
+
313
+ <div class="space-y-6">
314
+ <!-- Vector Store Settings -->
315
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-6">
316
+ <h3 class="text-lg font-semibold mb-4">Vector Store</h3>
317
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
318
+ <div>
319
+ <label class="block text-sm text-gray-400 mb-2">Type</label>
320
+ <select id="config-vectorstore" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2">
321
+ <option value="memory">Memory (File-based)</option>
322
+ <option value="chroma">ChromaDB (Server)</option>
323
+ <option value="qdrant">Qdrant</option>
324
+ <option value="vectorize">Cloudflare Vectorize</option>
325
+ </select>
326
+ </div>
327
+ </div>
328
+ </div>
329
+
330
+ <!-- Embeddings Settings -->
331
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-6">
332
+ <h3 class="text-lg font-semibold mb-4">Embeddings</h3>
333
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
334
+ <div>
335
+ <label class="block text-sm text-gray-400 mb-2">Model</label>
336
+ <select id="config-embeddings" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2">
337
+ <option value="xenova">Xenova (Local)</option>
338
+ <option value="openai">OpenAI</option>
339
+ <option value="ollama">Ollama</option>
340
+ </select>
341
+ </div>
342
+ </div>
343
+ </div>
344
+
345
+ <!-- MCP Gateway Settings -->
346
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-6">
347
+ <h3 class="text-lg font-semibold mb-4">MCP Gateway</h3>
348
+ <div class="space-y-4">
349
+ <div class="flex items-center gap-3">
350
+ <input type="checkbox" id="config-gateway-enabled" class="w-4 h-4 rounded bg-gray-800 border-gray-700 text-primary-600 focus:ring-primary-500">
351
+ <label for="config-gateway-enabled" class="text-sm">Enable MCP Gateway integration</label>
352
+ </div>
353
+ <div>
354
+ <label class="block text-sm text-gray-400 mb-2">Gateway URL</label>
355
+ <input type="text" id="config-gateway-url" placeholder="http://localhost:3010" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2">
356
+ </div>
357
+ </div>
358
+ </div>
359
+
360
+ <!-- OpenSkills Settings -->
361
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-6">
362
+ <h3 class="text-lg font-semibold mb-4">OpenSkills</h3>
363
+ <div class="space-y-4">
364
+ <div class="flex items-center gap-3">
365
+ <input type="checkbox" id="config-skills-enabled" class="w-4 h-4 rounded bg-gray-800 border-gray-700 text-primary-600 focus:ring-primary-500">
366
+ <label for="config-skills-enabled" class="text-sm">Enable OpenSkills integration</label>
367
+ </div>
368
+ <div class="flex items-center gap-3">
369
+ <input type="checkbox" id="config-skills-autoingest" class="w-4 h-4 rounded bg-gray-800 border-gray-700 text-primary-600 focus:ring-primary-500">
370
+ <label for="config-skills-autoingest" class="text-sm">Auto-ingest skills into RAG</label>
371
+ </div>
372
+ </div>
373
+ </div>
374
+
375
+ <!-- Proxy Settings -->
376
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-6">
377
+ <h3 class="text-lg font-semibold mb-4">Proxy Configuration</h3>
378
+ <div class="space-y-4">
379
+ <div class="flex items-center gap-3">
380
+ <input type="checkbox" id="config-proxy-enabled" class="w-4 h-4 rounded bg-gray-800 border-gray-700 text-primary-600 focus:ring-primary-500">
381
+ <label for="config-proxy-enabled" class="text-sm">Enable rotating proxy</label>
382
+ </div>
383
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
384
+ <div>
385
+ <label class="block text-sm text-gray-400 mb-2">Driver</label>
386
+ <select id="config-proxy-driver" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2">
387
+ <option value="none">None</option>
388
+ <option value="packetstream">PacketStream</option>
389
+ <option value="smartproxy">SmartProxy</option>
390
+ </select>
391
+ </div>
392
+ <div>
393
+ <label class="block text-sm text-gray-400 mb-2">Host</label>
394
+ <input type="text" id="config-proxy-host" placeholder="proxy.packetstream.io" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2">
395
+ </div>
396
+ </div>
397
+ </div>
398
+ </div>
399
+
400
+ <!-- Save Button -->
401
+ <div class="flex justify-end">
402
+ <button onclick="saveSettings()" class="px-6 py-3 bg-primary-600 hover:bg-primary-700 rounded-lg transition-colors flex items-center gap-2">
403
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
404
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
405
+ </svg>
406
+ Save Settings
407
+ </button>
408
+ </div>
409
+ </div>
410
+ </div>
411
+ </main>
412
+ </div>
413
+
414
+ <script>
415
+ const API_BASE = '';
416
+ let gatewayUrl = 'http://localhost:3010';
417
+ let allGatewayTools = [];
418
+ let currentBackendFilter = '';
419
+
420
+ // Tab Navigation
421
+ function showTab(tabId) {
422
+ document.querySelectorAll('.tab-content').forEach(el => el.classList.add('hidden'));
423
+ document.querySelectorAll('.nav-link').forEach(el => {
424
+ el.classList.remove('bg-gray-800', 'text-white');
425
+ el.classList.add('text-gray-400');
426
+ });
427
+
428
+ document.getElementById(`tab-${tabId}`).classList.remove('hidden');
429
+ const navLink = document.getElementById(`nav-${tabId}`);
430
+ if (navLink) {
431
+ navLink.classList.add('bg-gray-800', 'text-white');
432
+ navLink.classList.remove('text-gray-400');
433
+ }
434
+
435
+ // Load data for specific tabs
436
+ if (tabId === 'gateway') loadGatewayTools();
437
+ if (tabId === 'skills') loadSkills();
438
+ }
439
+
440
+ // Fetch Stats
441
+ async function fetchStats() {
442
+ try {
443
+ const res = await fetch(`${API_BASE}/api/stats`);
444
+ const stats = await res.json();
445
+
446
+ document.getElementById('stat-chunks').textContent = stats.totalChunks.toLocaleString();
447
+
448
+ // Update gateway URL from config
449
+ if (stats.mcpGatewayUrl) {
450
+ gatewayUrl = stats.mcpGatewayUrl;
451
+ }
452
+ } catch (e) {
453
+ console.error('Failed to fetch stats:', e);
454
+ }
455
+ }
456
+
457
+ // Fetch Gateway Health (via proxy to avoid CORS)
458
+ async function fetchGatewayHealth() {
459
+ try {
460
+ const res = await fetch(`${API_BASE}/api/gateway/health`);
461
+ const health = await res.json();
462
+
463
+ document.getElementById('stat-gateway-tools').textContent = health.tools?.enabled || health.tools || 0;
464
+ document.getElementById('stat-backends').textContent = `${health.backends?.connected || 0}/${health.backends?.total || 0}`;
465
+
466
+ const icon = document.getElementById('gateway-status-icon');
467
+ const content = document.getElementById('gateway-status-content');
468
+
469
+ if (health.status === 'ok') {
470
+ icon.className = 'w-3 h-3 bg-green-500 rounded-full';
471
+ content.innerHTML = `
472
+ <p>Connected to <strong>${gatewayUrl}</strong></p>
473
+ <p class="mt-1">${health.tools?.enabled || health.tools} tools available across ${health.backends?.connected} backends</p>
474
+ `;
475
+ } else {
476
+ icon.className = 'w-3 h-3 bg-yellow-500 rounded-full';
477
+ content.textContent = 'Gateway partially available';
478
+ }
479
+ } catch (e) {
480
+ document.getElementById('stat-gateway-tools').textContent = '-';
481
+ document.getElementById('stat-backends').textContent = '-';
482
+
483
+ const icon = document.getElementById('gateway-status-icon');
484
+ const content = document.getElementById('gateway-status-content');
485
+ icon.className = 'w-3 h-3 bg-red-500 rounded-full';
486
+ content.innerHTML = `<p class="text-red-400">Not connected</p><p class="text-xs mt-1">Start gateway: cd mcp-gateway && npm start</p>`;
487
+ }
488
+ }
489
+
490
+ // Fetch Skills
491
+ async function fetchSkillsCount() {
492
+ try {
493
+ const res = await fetch(`${API_BASE}/api/skills`);
494
+ const data = await res.json();
495
+ document.getElementById('stat-skills').textContent = data.skills?.length || 0;
496
+
497
+ const icon = document.getElementById('skills-status-icon');
498
+ const content = document.getElementById('skills-status-content');
499
+
500
+ if (data.skills && data.skills.length > 0) {
501
+ icon.className = 'w-3 h-3 bg-green-500 rounded-full';
502
+ content.innerHTML = `<p>${data.skills.length} skills available</p>`;
503
+ } else {
504
+ icon.className = 'w-3 h-3 bg-yellow-500 rounded-full';
505
+ content.innerHTML = `<p>No skills found</p><p class="text-xs mt-1">Install with: npx openskills install anthropics/skills</p>`;
506
+ }
507
+ } catch (e) {
508
+ document.getElementById('stat-skills').textContent = '-';
509
+ const icon = document.getElementById('skills-status-icon');
510
+ const content = document.getElementById('skills-status-content');
511
+ icon.className = 'w-3 h-3 bg-gray-500 rounded-full';
512
+ content.textContent = 'OpenSkills integration disabled';
513
+ }
514
+ }
515
+
516
+ // Load Gateway Tools (via proxy to avoid CORS)
517
+ async function loadGatewayTools() {
518
+ const listEl = document.getElementById('gateway-tools-list');
519
+ listEl.innerHTML = '<p class="text-gray-500 text-center py-8">Loading tools from MCP Gateway...</p>';
520
+
521
+ try {
522
+ const res = await fetch(`${API_BASE}/api/gateway/api/code/tools/search?detailLevel=name_description&limit=200`);
523
+ const data = await res.json();
524
+
525
+ allGatewayTools = data.tools || data || [];
526
+
527
+ // Build backend filters
528
+ const backends = [...new Set(allGatewayTools.map(t => t.backend || 'unknown'))];
529
+ const filtersEl = document.getElementById('backend-filters');
530
+ filtersEl.innerHTML = `
531
+ <button onclick="filterBackend('')" class="backend-filter px-3 py-1 bg-primary-600 rounded-full text-sm transition-colors" data-backend="">
532
+ All (${allGatewayTools.length})
533
+ </button>
534
+ ${backends.map(b => `
535
+ <button onclick="filterBackend('${b}')" class="backend-filter px-3 py-1 bg-gray-800 hover:bg-gray-700 rounded-full text-sm transition-colors" data-backend="${b}">
536
+ ${b} (${allGatewayTools.filter(t => t.backend === b).length})
537
+ </button>
538
+ `).join('')}
539
+ `;
540
+
541
+ renderGatewayTools(allGatewayTools);
542
+ } catch (e) {
543
+ listEl.innerHTML = `<p class="text-red-500 text-center py-8">Failed to load gateway tools. Is MCP Gateway running at ${gatewayUrl}?</p>`;
544
+ }
545
+ }
546
+
547
+ function filterBackend(backend) {
548
+ currentBackendFilter = backend;
549
+
550
+ // Update button styles
551
+ document.querySelectorAll('.backend-filter').forEach(btn => {
552
+ if (btn.dataset.backend === backend) {
553
+ btn.classList.remove('bg-gray-800');
554
+ btn.classList.add('bg-primary-600');
555
+ } else {
556
+ btn.classList.remove('bg-primary-600');
557
+ btn.classList.add('bg-gray-800');
558
+ }
559
+ });
560
+
561
+ const filtered = backend ? allGatewayTools.filter(t => t.backend === backend) : allGatewayTools;
562
+ renderGatewayTools(filtered);
563
+ }
564
+
565
+ function renderGatewayTools(tools) {
566
+ const listEl = document.getElementById('gateway-tools-list');
567
+
568
+ if (tools.length === 0) {
569
+ listEl.innerHTML = '<p class="text-gray-500 text-center py-8">No tools found</p>';
570
+ return;
571
+ }
572
+
573
+ listEl.innerHTML = tools.map(tool => `
574
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-5">
575
+ <div class="flex items-start justify-between mb-2">
576
+ <h4 class="font-mono text-primary-400">${tool.name}</h4>
577
+ <span class="text-xs px-2 py-1 bg-gray-800 rounded-full">${tool.backend || 'unknown'}</span>
578
+ </div>
579
+ <p class="text-sm text-gray-400">${tool.description || 'No description'}</p>
580
+ </div>
581
+ `).join('');
582
+ }
583
+
584
+ function searchGatewayTools() {
585
+ const query = document.getElementById('gateway-search-input').value.toLowerCase().trim();
586
+ if (!query) {
587
+ filterBackend(currentBackendFilter);
588
+ return;
589
+ }
590
+
591
+ const filtered = allGatewayTools.filter(t =>
592
+ t.name.toLowerCase().includes(query) ||
593
+ (t.description && t.description.toLowerCase().includes(query)) ||
594
+ (t.backend && t.backend.toLowerCase().includes(query))
595
+ );
596
+
597
+ renderGatewayTools(filtered);
598
+ }
599
+
600
+ // Load Skills
601
+ async function loadSkills() {
602
+ const listEl = document.getElementById('skills-list');
603
+ listEl.innerHTML = '<p class="text-gray-500 text-center py-8">Loading OpenSkills...</p>';
604
+
605
+ try {
606
+ const res = await fetch(`${API_BASE}/api/skills`);
607
+ const data = await res.json();
608
+
609
+ const skills = data.skills || [];
610
+
611
+ if (skills.length === 0) {
612
+ listEl.innerHTML = `
613
+ <div class="text-center py-8">
614
+ <p class="text-gray-500 mb-4">No skills installed</p>
615
+ <p class="text-sm text-gray-600">Install skills with:</p>
616
+ <code class="text-primary-400 text-sm">npx openskills install anthropics/skills</code>
617
+ </div>
618
+ `;
619
+ return;
620
+ }
621
+
622
+ listEl.innerHTML = skills.map(skill => `
623
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-5">
624
+ <div class="flex items-start justify-between mb-2">
625
+ <h4 class="font-semibold text-yellow-400">${skill.name}</h4>
626
+ <span class="text-xs px-2 py-1 bg-gray-800 rounded-full">${skill.location || 'local'}</span>
627
+ </div>
628
+ <p class="text-sm text-gray-400">${skill.description || 'No description'}</p>
629
+ </div>
630
+ `).join('');
631
+ } catch (e) {
632
+ listEl.innerHTML = `<p class="text-red-500 text-center py-8">Failed to load skills: ${e.message}</p>`;
633
+ }
634
+ }
635
+
636
+ function searchSkills() {
637
+ const query = document.getElementById('skills-search-input').value.toLowerCase().trim();
638
+ // For now, just reload and filter client-side
639
+ loadSkills();
640
+ }
641
+
642
+ function refreshAll() {
643
+ fetchStats();
644
+ fetchGatewayHealth();
645
+ fetchSkillsCount();
646
+ fetchActivity();
647
+ }
648
+
649
+ // Fetch Activity
650
+ async function fetchActivity() {
651
+ try {
652
+ const res = await fetch(`${API_BASE}/api/activity`);
653
+ const activity = await res.json();
654
+
655
+ const recentEl = document.getElementById('recent-activity');
656
+ const fullEl = document.getElementById('activity-log');
657
+
658
+ if (activity.length === 0) {
659
+ recentEl.innerHTML = '<p class="text-gray-500 text-sm">No recent activity</p>';
660
+ fullEl.innerHTML = '<p class="text-gray-500 text-center py-8">No activity recorded yet</p>';
661
+ return;
662
+ }
663
+
664
+ const renderActivity = (items) => items.map(item => `
665
+ <div class="flex items-start gap-3 p-4">
666
+ <div class="w-2 h-2 mt-2 rounded-full ${item.type === 'error' ? 'bg-red-500' : item.type === 'search' ? 'bg-blue-500' : 'bg-green-500'}"></div>
667
+ <div class="flex-1">
668
+ <p class="text-sm">${item.message}</p>
669
+ <p class="text-xs text-gray-500 mt-1">${new Date(item.timestamp).toLocaleString()}</p>
670
+ </div>
671
+ </div>
672
+ `).join('');
673
+
674
+ recentEl.innerHTML = renderActivity(activity.slice(0, 5));
675
+ fullEl.innerHTML = renderActivity(activity);
676
+ } catch (e) {
677
+ console.error('Failed to fetch activity:', e);
678
+ }
679
+ }
680
+
681
+ // Fetch Config
682
+ async function fetchConfig() {
683
+ try {
684
+ const res = await fetch(`${API_BASE}/api/config`);
685
+ const config = await res.json();
686
+
687
+ if (config.error) return;
688
+
689
+ document.getElementById('config-vectorstore').value = config.vectorStore || 'memory';
690
+ document.getElementById('config-embeddings').value = config.embeddings || 'xenova';
691
+
692
+ if (config.mcpGateway) {
693
+ document.getElementById('config-gateway-enabled').checked = config.mcpGateway.enabled;
694
+ document.getElementById('config-gateway-url').value = config.mcpGateway.url || 'http://localhost:3010';
695
+ if (config.mcpGateway.url) gatewayUrl = config.mcpGateway.url;
696
+ }
697
+
698
+ if (config.openSkills) {
699
+ document.getElementById('config-skills-enabled').checked = config.openSkills.enabled;
700
+ document.getElementById('config-skills-autoingest').checked = config.openSkills.autoIngestSkills;
701
+ }
702
+
703
+ if (config.proxy) {
704
+ document.getElementById('config-proxy-enabled').checked = config.proxy.enabled;
705
+ document.getElementById('config-proxy-driver').value = config.proxy.driver || 'none';
706
+ document.getElementById('config-proxy-host').value = config.proxy.host || '';
707
+ }
708
+ } catch (e) {
709
+ console.error('Failed to fetch config:', e);
710
+ }
711
+ }
712
+
713
+ // Save Settings
714
+ async function saveSettings() {
715
+ const updates = {
716
+ vectorStore: document.getElementById('config-vectorstore').value,
717
+ embeddings: document.getElementById('config-embeddings').value,
718
+ mcpGateway: {
719
+ enabled: document.getElementById('config-gateway-enabled').checked,
720
+ url: document.getElementById('config-gateway-url').value || 'http://localhost:3010'
721
+ },
722
+ openSkills: {
723
+ enabled: document.getElementById('config-skills-enabled').checked,
724
+ autoIngestSkills: document.getElementById('config-skills-autoingest').checked
725
+ },
726
+ proxy: {
727
+ enabled: document.getElementById('config-proxy-enabled').checked,
728
+ driver: document.getElementById('config-proxy-driver').value,
729
+ host: document.getElementById('config-proxy-host').value || undefined
730
+ }
731
+ };
732
+
733
+ try {
734
+ const res = await fetch(`${API_BASE}/api/config`, {
735
+ method: 'POST',
736
+ headers: { 'Content-Type': 'application/json' },
737
+ body: JSON.stringify(updates)
738
+ });
739
+
740
+ if (res.ok) {
741
+ alert('Settings saved successfully!');
742
+ refreshAll();
743
+ } else {
744
+ alert('Failed to save settings');
745
+ }
746
+ } catch (e) {
747
+ alert('Failed to save settings: ' + e.message);
748
+ }
749
+ }
750
+
751
+ // Search Knowledge Base
752
+ async function performSearch() {
753
+ const query = document.getElementById('search-input').value.trim();
754
+ if (!query) return;
755
+
756
+ const resultsEl = document.getElementById('search-results');
757
+ resultsEl.innerHTML = '<p class="text-gray-500 text-center py-8">Searching...</p>';
758
+
759
+ try {
760
+ const res = await fetch(`${API_BASE}/api/search`, {
761
+ method: 'POST',
762
+ headers: { 'Content-Type': 'application/json' },
763
+ body: JSON.stringify({ query, topK: 10 })
764
+ });
765
+
766
+ const results = await res.json();
767
+
768
+ if (results.error) {
769
+ resultsEl.innerHTML = `<p class="text-red-500 text-center py-8">${results.error}</p>`;
770
+ return;
771
+ }
772
+
773
+ if (results.length === 0) {
774
+ resultsEl.innerHTML = '<p class="text-gray-500 text-center py-8">No results found in knowledge base</p>';
775
+ return;
776
+ }
777
+
778
+ resultsEl.innerHTML = results.map((result, i) => `
779
+ <div class="bg-gray-900 border border-gray-800 rounded-xl p-5">
780
+ <div class="flex items-center justify-between mb-3">
781
+ <span class="text-sm text-gray-400">Score: ${result.score.toFixed(4)}</span>
782
+ <span class="text-xs text-gray-500">${result.metadata?.source || 'Unknown source'}</span>
783
+ </div>
784
+ <p class="text-sm text-gray-300 whitespace-pre-wrap">${result.content.substring(0, 500)}${result.content.length > 500 ? '...' : ''}</p>
785
+ </div>
786
+ `).join('');
787
+ } catch (e) {
788
+ resultsEl.innerHTML = `<p class="text-red-500 text-center py-8">Search failed: ${e.message}</p>`;
789
+ }
790
+ }
791
+
792
+ // Enter key handlers
793
+ document.getElementById('search-input').addEventListener('keypress', (e) => {
794
+ if (e.key === 'Enter') performSearch();
795
+ });
796
+ document.getElementById('gateway-search-input').addEventListener('keypress', (e) => {
797
+ if (e.key === 'Enter') searchGatewayTools();
798
+ });
799
+ document.getElementById('skills-search-input').addEventListener('keypress', (e) => {
800
+ if (e.key === 'Enter') searchSkills();
801
+ });
802
+
803
+ // Initial load
804
+ fetchConfig().then(() => {
805
+ fetchStats();
806
+ fetchGatewayHealth();
807
+ fetchSkillsCount();
808
+ fetchActivity();
809
+ });
810
+
811
+ // Refresh every 30 seconds
812
+ setInterval(() => {
813
+ fetchStats();
814
+ fetchGatewayHealth();
815
+ fetchActivity();
816
+ }, 30000);
817
+ </script>
818
+ </body>
819
+ </html>