@open-mercato/ai-assistant 0.4.2-canary-c02407ff85

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 (193) hide show
  1. package/AGENTS.md +1090 -0
  2. package/README.md +607 -0
  3. package/build.mjs +92 -0
  4. package/dist/di.js +8 -0
  5. package/dist/di.js.map +7 -0
  6. package/dist/frontend/components/CommandPalette/CommandFooter.js +80 -0
  7. package/dist/frontend/components/CommandPalette/CommandFooter.js.map +7 -0
  8. package/dist/frontend/components/CommandPalette/CommandHeader.js +53 -0
  9. package/dist/frontend/components/CommandPalette/CommandHeader.js.map +7 -0
  10. package/dist/frontend/components/CommandPalette/CommandInput.js +29 -0
  11. package/dist/frontend/components/CommandPalette/CommandInput.js.map +7 -0
  12. package/dist/frontend/components/CommandPalette/CommandItem.js +92 -0
  13. package/dist/frontend/components/CommandPalette/CommandItem.js.map +7 -0
  14. package/dist/frontend/components/CommandPalette/CommandPalette.js +244 -0
  15. package/dist/frontend/components/CommandPalette/CommandPalette.js.map +7 -0
  16. package/dist/frontend/components/CommandPalette/CommandPaletteProvider.js +42 -0
  17. package/dist/frontend/components/CommandPalette/CommandPaletteProvider.js.map +7 -0
  18. package/dist/frontend/components/CommandPalette/CommandPaletteWrapper.js +18 -0
  19. package/dist/frontend/components/CommandPalette/CommandPaletteWrapper.js.map +7 -0
  20. package/dist/frontend/components/CommandPalette/DebugPanel.js +215 -0
  21. package/dist/frontend/components/CommandPalette/DebugPanel.js.map +7 -0
  22. package/dist/frontend/components/CommandPalette/MessageBubble.js +64 -0
  23. package/dist/frontend/components/CommandPalette/MessageBubble.js.map +7 -0
  24. package/dist/frontend/components/CommandPalette/ToolCallConfirmation.js +91 -0
  25. package/dist/frontend/components/CommandPalette/ToolCallConfirmation.js.map +7 -0
  26. package/dist/frontend/components/CommandPalette/ToolCallDisplay.js +47 -0
  27. package/dist/frontend/components/CommandPalette/ToolCallDisplay.js.map +7 -0
  28. package/dist/frontend/components/CommandPalette/ToolChatPage.js +74 -0
  29. package/dist/frontend/components/CommandPalette/ToolChatPage.js.map +7 -0
  30. package/dist/frontend/components/CommandPalette/index.js +28 -0
  31. package/dist/frontend/components/CommandPalette/index.js.map +7 -0
  32. package/dist/frontend/constants.js +41 -0
  33. package/dist/frontend/constants.js.map +7 -0
  34. package/dist/frontend/hooks/index.js +13 -0
  35. package/dist/frontend/hooks/index.js.map +7 -0
  36. package/dist/frontend/hooks/useCommandPalette.js +1094 -0
  37. package/dist/frontend/hooks/useCommandPalette.js.map +7 -0
  38. package/dist/frontend/hooks/useMcpTools.js +66 -0
  39. package/dist/frontend/hooks/useMcpTools.js.map +7 -0
  40. package/dist/frontend/hooks/usePageContext.js +48 -0
  41. package/dist/frontend/hooks/usePageContext.js.map +7 -0
  42. package/dist/frontend/hooks/useRecentActions.js +56 -0
  43. package/dist/frontend/hooks/useRecentActions.js.map +7 -0
  44. package/dist/frontend/hooks/useRecentTools.js +55 -0
  45. package/dist/frontend/hooks/useRecentTools.js.map +7 -0
  46. package/dist/frontend/index.js +35 -0
  47. package/dist/frontend/index.js.map +7 -0
  48. package/dist/frontend/types.js +1 -0
  49. package/dist/frontend/types.js.map +7 -0
  50. package/dist/frontend/utils/index.js +7 -0
  51. package/dist/frontend/utils/index.js.map +7 -0
  52. package/dist/frontend/utils/toolMatcher.js +95 -0
  53. package/dist/frontend/utils/toolMatcher.js.map +7 -0
  54. package/dist/index.js +57 -0
  55. package/dist/index.js.map +7 -0
  56. package/dist/modules/ai_assistant/acl.js +14 -0
  57. package/dist/modules/ai_assistant/acl.js.map +7 -0
  58. package/dist/modules/ai_assistant/api/chat/route.js +152 -0
  59. package/dist/modules/ai_assistant/api/chat/route.js.map +7 -0
  60. package/dist/modules/ai_assistant/api/health/route.js +27 -0
  61. package/dist/modules/ai_assistant/api/health/route.js.map +7 -0
  62. package/dist/modules/ai_assistant/api/route/route.js +123 -0
  63. package/dist/modules/ai_assistant/api/route/route.js.map +7 -0
  64. package/dist/modules/ai_assistant/api/settings/route.js +60 -0
  65. package/dist/modules/ai_assistant/api/settings/route.js.map +7 -0
  66. package/dist/modules/ai_assistant/api/tools/execute/route.js +58 -0
  67. package/dist/modules/ai_assistant/api/tools/execute/route.js.map +7 -0
  68. package/dist/modules/ai_assistant/api/tools/route.js +48 -0
  69. package/dist/modules/ai_assistant/api/tools/route.js.map +7 -0
  70. package/dist/modules/ai_assistant/backend/config/ai-assistant/page.js +10 -0
  71. package/dist/modules/ai_assistant/backend/config/ai-assistant/page.js.map +7 -0
  72. package/dist/modules/ai_assistant/backend/config/ai-assistant/page.meta.js +28 -0
  73. package/dist/modules/ai_assistant/backend/config/ai-assistant/page.meta.js.map +7 -0
  74. package/dist/modules/ai_assistant/cli.js +192 -0
  75. package/dist/modules/ai_assistant/cli.js.map +7 -0
  76. package/dist/modules/ai_assistant/di.js +11 -0
  77. package/dist/modules/ai_assistant/di.js.map +7 -0
  78. package/dist/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.js +257 -0
  79. package/dist/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.js.map +7 -0
  80. package/dist/modules/ai_assistant/index.js +13 -0
  81. package/dist/modules/ai_assistant/index.js.map +7 -0
  82. package/dist/modules/ai_assistant/lib/ai-sdk.js +13 -0
  83. package/dist/modules/ai_assistant/lib/ai-sdk.js.map +7 -0
  84. package/dist/modules/ai_assistant/lib/api-discovery-tools.js +249 -0
  85. package/dist/modules/ai_assistant/lib/api-discovery-tools.js.map +7 -0
  86. package/dist/modules/ai_assistant/lib/api-endpoint-index-config.js +177 -0
  87. package/dist/modules/ai_assistant/lib/api-endpoint-index-config.js.map +7 -0
  88. package/dist/modules/ai_assistant/lib/api-endpoint-index.js +210 -0
  89. package/dist/modules/ai_assistant/lib/api-endpoint-index.js.map +7 -0
  90. package/dist/modules/ai_assistant/lib/auth.js +87 -0
  91. package/dist/modules/ai_assistant/lib/auth.js.map +7 -0
  92. package/dist/modules/ai_assistant/lib/chat-config.js +117 -0
  93. package/dist/modules/ai_assistant/lib/chat-config.js.map +7 -0
  94. package/dist/modules/ai_assistant/lib/client-factory.js +60 -0
  95. package/dist/modules/ai_assistant/lib/client-factory.js.map +7 -0
  96. package/dist/modules/ai_assistant/lib/http-server.js +367 -0
  97. package/dist/modules/ai_assistant/lib/http-server.js.map +7 -0
  98. package/dist/modules/ai_assistant/lib/in-process-client.js +126 -0
  99. package/dist/modules/ai_assistant/lib/in-process-client.js.map +7 -0
  100. package/dist/modules/ai_assistant/lib/mcp-client.js +146 -0
  101. package/dist/modules/ai_assistant/lib/mcp-client.js.map +7 -0
  102. package/dist/modules/ai_assistant/lib/mcp-dev-server.js +283 -0
  103. package/dist/modules/ai_assistant/lib/mcp-dev-server.js.map +7 -0
  104. package/dist/modules/ai_assistant/lib/mcp-server-config.js +160 -0
  105. package/dist/modules/ai_assistant/lib/mcp-server-config.js.map +7 -0
  106. package/dist/modules/ai_assistant/lib/mcp-server.js +156 -0
  107. package/dist/modules/ai_assistant/lib/mcp-server.js.map +7 -0
  108. package/dist/modules/ai_assistant/lib/mcp-tool-adapter.js +44 -0
  109. package/dist/modules/ai_assistant/lib/mcp-tool-adapter.js.map +7 -0
  110. package/dist/modules/ai_assistant/lib/opencode-client.js +247 -0
  111. package/dist/modules/ai_assistant/lib/opencode-client.js.map +7 -0
  112. package/dist/modules/ai_assistant/lib/opencode-handlers.js +398 -0
  113. package/dist/modules/ai_assistant/lib/opencode-handlers.js.map +7 -0
  114. package/dist/modules/ai_assistant/lib/schema-utils.js +94 -0
  115. package/dist/modules/ai_assistant/lib/schema-utils.js.map +7 -0
  116. package/dist/modules/ai_assistant/lib/tool-executor.js +55 -0
  117. package/dist/modules/ai_assistant/lib/tool-executor.js.map +7 -0
  118. package/dist/modules/ai_assistant/lib/tool-index-config.js +125 -0
  119. package/dist/modules/ai_assistant/lib/tool-index-config.js.map +7 -0
  120. package/dist/modules/ai_assistant/lib/tool-loader.js +88 -0
  121. package/dist/modules/ai_assistant/lib/tool-loader.js.map +7 -0
  122. package/dist/modules/ai_assistant/lib/tool-registry.js +65 -0
  123. package/dist/modules/ai_assistant/lib/tool-registry.js.map +7 -0
  124. package/dist/modules/ai_assistant/lib/tool-search.js +192 -0
  125. package/dist/modules/ai_assistant/lib/tool-search.js.map +7 -0
  126. package/dist/modules/ai_assistant/lib/types.js +1 -0
  127. package/dist/modules/ai_assistant/lib/types.js.map +7 -0
  128. package/package.json +108 -0
  129. package/src/di.ts +11 -0
  130. package/src/frontend/components/CommandPalette/CommandFooter.tsx +113 -0
  131. package/src/frontend/components/CommandPalette/CommandHeader.tsx +76 -0
  132. package/src/frontend/components/CommandPalette/CommandInput.tsx +50 -0
  133. package/src/frontend/components/CommandPalette/CommandItem.tsx +111 -0
  134. package/src/frontend/components/CommandPalette/CommandPalette.tsx +276 -0
  135. package/src/frontend/components/CommandPalette/CommandPaletteProvider.tsx +60 -0
  136. package/src/frontend/components/CommandPalette/CommandPaletteWrapper.tsx +21 -0
  137. package/src/frontend/components/CommandPalette/DebugPanel.tsx +257 -0
  138. package/src/frontend/components/CommandPalette/MessageBubble.tsx +73 -0
  139. package/src/frontend/components/CommandPalette/ToolCallConfirmation.tsx +130 -0
  140. package/src/frontend/components/CommandPalette/ToolCallDisplay.tsx +57 -0
  141. package/src/frontend/components/CommandPalette/ToolChatPage.tsx +125 -0
  142. package/src/frontend/components/CommandPalette/index.ts +14 -0
  143. package/src/frontend/constants.ts +35 -0
  144. package/src/frontend/hooks/index.ts +5 -0
  145. package/src/frontend/hooks/useCommandPalette.ts +1389 -0
  146. package/src/frontend/hooks/useMcpTools.ts +73 -0
  147. package/src/frontend/hooks/usePageContext.ts +61 -0
  148. package/src/frontend/hooks/useRecentActions.ts +64 -0
  149. package/src/frontend/hooks/useRecentTools.ts +69 -0
  150. package/src/frontend/index.ts +39 -0
  151. package/src/frontend/types.ts +260 -0
  152. package/src/frontend/utils/index.ts +1 -0
  153. package/src/frontend/utils/toolMatcher.ts +127 -0
  154. package/src/index.ts +92 -0
  155. package/src/modules/ai_assistant/acl.ts +10 -0
  156. package/src/modules/ai_assistant/api/chat/route.ts +213 -0
  157. package/src/modules/ai_assistant/api/health/route.ts +30 -0
  158. package/src/modules/ai_assistant/api/route/route.ts +149 -0
  159. package/src/modules/ai_assistant/api/settings/route.ts +73 -0
  160. package/src/modules/ai_assistant/api/tools/execute/route.ts +71 -0
  161. package/src/modules/ai_assistant/api/tools/route.ts +57 -0
  162. package/src/modules/ai_assistant/backend/config/ai-assistant/page.meta.ts +26 -0
  163. package/src/modules/ai_assistant/backend/config/ai-assistant/page.tsx +12 -0
  164. package/src/modules/ai_assistant/cli.ts +233 -0
  165. package/src/modules/ai_assistant/di.ts +9 -0
  166. package/src/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.tsx +418 -0
  167. package/src/modules/ai_assistant/index.ts +11 -0
  168. package/src/modules/ai_assistant/lib/ai-sdk.ts +5 -0
  169. package/src/modules/ai_assistant/lib/api-discovery-tools.ts +334 -0
  170. package/src/modules/ai_assistant/lib/api-endpoint-index-config.ts +243 -0
  171. package/src/modules/ai_assistant/lib/api-endpoint-index.ts +381 -0
  172. package/src/modules/ai_assistant/lib/auth.ts +185 -0
  173. package/src/modules/ai_assistant/lib/chat-config.ts +152 -0
  174. package/src/modules/ai_assistant/lib/client-factory.ts +130 -0
  175. package/src/modules/ai_assistant/lib/http-server.ts +498 -0
  176. package/src/modules/ai_assistant/lib/in-process-client.ts +205 -0
  177. package/src/modules/ai_assistant/lib/mcp-client.ts +221 -0
  178. package/src/modules/ai_assistant/lib/mcp-dev-server.ts +373 -0
  179. package/src/modules/ai_assistant/lib/mcp-server-config.ts +287 -0
  180. package/src/modules/ai_assistant/lib/mcp-server.ts +214 -0
  181. package/src/modules/ai_assistant/lib/mcp-tool-adapter.ts +76 -0
  182. package/src/modules/ai_assistant/lib/opencode-client.ts +426 -0
  183. package/src/modules/ai_assistant/lib/opencode-handlers.ts +676 -0
  184. package/src/modules/ai_assistant/lib/schema-utils.ts +142 -0
  185. package/src/modules/ai_assistant/lib/tool-executor.ts +71 -0
  186. package/src/modules/ai_assistant/lib/tool-index-config.ts +178 -0
  187. package/src/modules/ai_assistant/lib/tool-loader.ts +149 -0
  188. package/src/modules/ai_assistant/lib/tool-registry.ts +114 -0
  189. package/src/modules/ai_assistant/lib/tool-search.ts +308 -0
  190. package/src/modules/ai_assistant/lib/types.ts +147 -0
  191. package/test-schema.ts +37 -0
  192. package/tsconfig.json +10 -0
  193. package/watch.mjs +6 -0
@@ -0,0 +1,418 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { useState } from 'react'
5
+ import { useQuery } from '@tanstack/react-query'
6
+ import { Bot, Loader2, CheckCircle2, XCircle, ChevronDown, ChevronRight, Server, Wrench, AlertTriangle } from 'lucide-react'
7
+ import { Button } from '@open-mercato/ui/primitives/button'
8
+ import {
9
+ CommandPaletteProvider,
10
+ CommandPalette,
11
+ useCommandPaletteContext,
12
+ } from '../../../../frontend'
13
+
14
+ // OpenCode health response type
15
+ type OpenCodeHealthResponse = {
16
+ status: 'ok' | 'error'
17
+ opencode?: {
18
+ healthy: boolean
19
+ version: string
20
+ }
21
+ mcp?: Record<string, { status: string; error?: string }>
22
+ search?: {
23
+ available: boolean
24
+ driver: string | null
25
+ }
26
+ url: string
27
+ message?: string
28
+ }
29
+
30
+ // Provider config type from settings API
31
+ type ProviderConfig = {
32
+ id: string
33
+ name: string
34
+ model: string
35
+ defaultModel: string
36
+ envKey: string
37
+ configured: boolean
38
+ }
39
+
40
+ type SettingsResponse = {
41
+ provider: ProviderConfig
42
+ availableProviders: ProviderConfig[]
43
+ }
44
+
45
+ // Tool info type
46
+ type ToolInfo = {
47
+ name: string
48
+ description: string
49
+ module: string
50
+ inputSchema: Record<string, unknown>
51
+ }
52
+
53
+ // API fetch functions
54
+ async function fetchHealth(): Promise<OpenCodeHealthResponse> {
55
+ const res = await fetch('/api/ai_assistant/health')
56
+ if (!res.ok) throw new Error('Failed to fetch health')
57
+ return res.json()
58
+ }
59
+
60
+ async function fetchSettings(): Promise<SettingsResponse> {
61
+ const res = await fetch('/api/ai_assistant/settings')
62
+ if (!res.ok) throw new Error('Failed to fetch settings')
63
+ return res.json()
64
+ }
65
+
66
+ async function fetchTools(): Promise<{ tools: ToolInfo[] }> {
67
+ const res = await fetch('/api/ai_assistant/tools')
68
+ if (!res.ok) throw new Error('Failed to fetch tools')
69
+ return res.json()
70
+ }
71
+
72
+ function AiAssistantSettingsContent() {
73
+ const [toolsExpanded, setToolsExpanded] = useState(false)
74
+ const { setIsOpen } = useCommandPaletteContext()
75
+
76
+ // Health query - polls every 10 seconds
77
+ const healthQuery = useQuery({
78
+ queryKey: ['ai-assistant', 'health'],
79
+ queryFn: fetchHealth,
80
+ refetchInterval: 10000,
81
+ staleTime: 5000,
82
+ })
83
+
84
+ // Settings query - no polling needed (static config)
85
+ const settingsQuery = useQuery({
86
+ queryKey: ['ai-assistant', 'settings'],
87
+ queryFn: fetchSettings,
88
+ staleTime: 60000,
89
+ })
90
+
91
+ // Tools query - no polling needed
92
+ const toolsQuery = useQuery({
93
+ queryKey: ['ai-assistant', 'tools'],
94
+ queryFn: fetchTools,
95
+ staleTime: 60000,
96
+ })
97
+
98
+ // Open AI Assistant palette
99
+ const openAiAssistant = () => {
100
+ setIsOpen(true)
101
+ }
102
+
103
+ const isLoading = healthQuery.isLoading || settingsQuery.isLoading || toolsQuery.isLoading
104
+
105
+ if (isLoading) {
106
+ return (
107
+ <div className="flex items-center gap-2 text-muted-foreground py-8">
108
+ <Loader2 className="h-4 w-4 animate-spin" />
109
+ Loading settings...
110
+ </div>
111
+ )
112
+ }
113
+
114
+ const health = healthQuery.data
115
+ const settings = settingsQuery.data
116
+ const tools = toolsQuery.data?.tools || []
117
+
118
+ // Group tools by module
119
+ const toolsByModule = tools.reduce<Record<string, ToolInfo[]>>((acc, tool) => {
120
+ const module = tool.module || 'other'
121
+ if (!acc[module]) acc[module] = []
122
+ acc[module].push(tool)
123
+ return acc
124
+ }, {})
125
+
126
+ const provider = settings?.provider
127
+
128
+ return (
129
+ <div className="flex flex-col gap-6">
130
+ {/* Header */}
131
+ <div className="space-y-1">
132
+ <h1 className="text-2xl font-bold flex items-center gap-2">
133
+ <Bot className="h-6 w-6" />
134
+ AI Assistant Settings
135
+ </h1>
136
+ <p className="text-muted-foreground">
137
+ Configure and monitor the AI assistant
138
+ </p>
139
+ </div>
140
+
141
+ {/* Test AI Assistant Section */}
142
+ <div className="rounded-lg border bg-card p-6">
143
+ <div className="flex items-start justify-between">
144
+ <div className="space-y-1">
145
+ <h2 className="text-lg font-semibold flex items-center gap-2">
146
+ <Bot className="h-5 w-5" />
147
+ Test AI Assistant
148
+ </h2>
149
+ <p className="text-sm text-muted-foreground">
150
+ Click the button to open the AI Assistant command palette.
151
+ </p>
152
+ </div>
153
+ <Button onClick={openAiAssistant} size="lg" className="gap-2">
154
+ <Bot className="h-4 w-4" />
155
+ Open AI Assistant
156
+ </Button>
157
+ </div>
158
+ </div>
159
+
160
+ {/* Configuration Section */}
161
+ <div className="rounded-lg border bg-card p-6">
162
+ <h2 className="text-sm font-semibold mb-4 flex items-center gap-2">
163
+ <Server className="h-4 w-4" />
164
+ Configuration
165
+ </h2>
166
+ <div className="bg-muted/50 rounded-md p-4 space-y-2">
167
+ <div className="flex items-center gap-2 text-sm">
168
+ <span className="text-muted-foreground">Provider:</span>
169
+ <span className="font-medium">{provider?.name || 'Anthropic'}</span>
170
+ {provider?.configured ? (
171
+ <span className="flex items-center gap-1 text-emerald-600 dark:text-emerald-400 text-xs">
172
+ <CheckCircle2 className="h-3 w-3" />
173
+ Configured
174
+ </span>
175
+ ) : (
176
+ <span className="flex items-center gap-1 text-amber-600 dark:text-amber-400 text-xs">
177
+ <XCircle className="h-3 w-3" />
178
+ Not configured
179
+ </span>
180
+ )}
181
+ </div>
182
+ <div className="flex items-center gap-2 text-sm">
183
+ <span className="text-muted-foreground">Model:</span>
184
+ <code className="font-mono text-xs bg-background px-1.5 py-0.5 rounded">{provider?.model || 'claude-haiku-4-5-20251001'}</code>
185
+ </div>
186
+ <div className="flex items-center gap-2 text-sm">
187
+ <span className="text-muted-foreground">Required:</span>
188
+ <span>Set <code className="font-mono text-xs bg-background px-1.5 py-0.5 rounded">{provider?.envKey || 'OPENCODE_ANTHROPIC_API_KEY'}</code> in .env</span>
189
+ </div>
190
+ </div>
191
+
192
+ {/* Available Providers */}
193
+ {settings?.availableProviders && settings.availableProviders.length > 1 && (
194
+ <div className="mt-4">
195
+ <p className="text-xs text-muted-foreground mb-2">Available Providers:</p>
196
+ <div className="flex flex-wrap gap-2">
197
+ {settings.availableProviders.map((p) => (
198
+ <div
199
+ key={p.id}
200
+ className={`px-2 py-1 rounded text-xs ${
201
+ p.id === provider?.id
202
+ ? 'bg-primary text-primary-foreground'
203
+ : p.configured
204
+ ? 'bg-emerald-100 text-emerald-800 dark:bg-emerald-900/30 dark:text-emerald-400'
205
+ : 'bg-muted text-muted-foreground'
206
+ }`}
207
+ >
208
+ {p.name}
209
+ {p.id === provider?.id && ' (active)'}
210
+ {p.id !== provider?.id && p.configured && ' (ready)'}
211
+ </div>
212
+ ))}
213
+ </div>
214
+ </div>
215
+ )}
216
+
217
+ <p className="text-xs text-muted-foreground mt-3">
218
+ Set <code className="font-mono text-[10px] bg-muted px-1 rounded">OPENCODE_PROVIDER</code> in .env to change provider (anthropic, openai, google).
219
+ </p>
220
+ </div>
221
+
222
+ {/* Requirements Section */}
223
+ <div className="rounded-lg border bg-card p-6">
224
+ <h2 className="text-sm font-semibold mb-4 flex items-center gap-2">
225
+ <AlertTriangle className="h-4 w-4" />
226
+ Requirements
227
+ </h2>
228
+ <div className="bg-muted/50 rounded-md p-4 space-y-3">
229
+ <div className="flex items-center gap-2 text-sm">
230
+ <span className="text-muted-foreground">Full-Text Search:</span>
231
+ {health?.search?.available ? (
232
+ <span className="flex items-center gap-1 text-emerald-600 dark:text-emerald-400">
233
+ <CheckCircle2 className="h-3 w-3" />
234
+ Meilisearch connected
235
+ </span>
236
+ ) : (
237
+ <span className="flex items-center gap-1 text-amber-600 dark:text-amber-400">
238
+ <XCircle className="h-3 w-3" />
239
+ Not available
240
+ </span>
241
+ )}
242
+ </div>
243
+ <p className="text-xs text-muted-foreground">
244
+ A full-text search driver (Meilisearch) is required for API endpoint discovery.
245
+ Endpoints are indexed automatically when the MCP server starts.
246
+ </p>
247
+ </div>
248
+ </div>
249
+
250
+ {/* OpenCode Connection Section */}
251
+ <div className="rounded-lg border bg-card p-6">
252
+ <div className="flex items-center justify-between mb-4">
253
+ <h2 className="text-sm font-semibold flex items-center gap-2">
254
+ <Server className="h-4 w-4" />
255
+ OpenCode Connection
256
+ </h2>
257
+ {healthQuery.isFetching && !healthQuery.isLoading && (
258
+ <Loader2 className="h-3 w-3 animate-spin text-muted-foreground" />
259
+ )}
260
+ </div>
261
+ <div className="grid gap-4 sm:grid-cols-2">
262
+ {/* OpenCode Server Status */}
263
+ <div className={`p-4 rounded-lg border-2 ${
264
+ health?.status === 'ok' && health.opencode?.healthy
265
+ ? 'border-emerald-500/50 bg-emerald-50/50 dark:bg-emerald-900/10'
266
+ : 'border-destructive/50 bg-destructive/5'
267
+ }`}>
268
+ <div className="flex items-start justify-between">
269
+ <div>
270
+ <p className="text-sm font-medium">OpenCode Server</p>
271
+ <p className="text-xs text-muted-foreground mt-1">
272
+ {health?.status === 'ok' && health.opencode?.healthy ? (
273
+ <span className="flex items-center gap-1 text-emerald-600 dark:text-emerald-400">
274
+ <CheckCircle2 className="h-3 w-3" />
275
+ Connected
276
+ </span>
277
+ ) : (
278
+ <span className="flex items-center gap-1 text-destructive">
279
+ <XCircle className="h-3 w-3" />
280
+ {health?.message || 'Disconnected'}
281
+ </span>
282
+ )}
283
+ </p>
284
+ {health?.opencode?.version && (
285
+ <p className="text-xs text-muted-foreground mt-1">
286
+ Version: {health.opencode.version}
287
+ </p>
288
+ )}
289
+ <p className="text-xs text-muted-foreground mt-1">
290
+ {health?.url || 'http://localhost:4096'}
291
+ </p>
292
+ </div>
293
+ {health?.status === 'ok' && health.opencode?.healthy && (
294
+ <div className="flex h-5 w-5 items-center justify-center rounded-full bg-emerald-100 text-emerald-600 dark:bg-emerald-900/40 dark:text-emerald-400">
295
+ <svg className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
296
+ <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
297
+ </svg>
298
+ </div>
299
+ )}
300
+ </div>
301
+ </div>
302
+
303
+ {/* MCP Server Status */}
304
+ {health?.mcp && Object.entries(health.mcp).map(([name, mcpStatus]) => (
305
+ <div
306
+ key={name}
307
+ className={`p-4 rounded-lg border-2 ${
308
+ mcpStatus.status === 'connected'
309
+ ? 'border-emerald-500/50 bg-emerald-50/50 dark:bg-emerald-900/10'
310
+ : mcpStatus.status === 'connecting'
311
+ ? 'border-amber-500/50 bg-amber-50/50 dark:bg-amber-900/10'
312
+ : 'border-destructive/50 bg-destructive/5'
313
+ }`}
314
+ >
315
+ <div className="flex items-start justify-between">
316
+ <div>
317
+ <p className="text-sm font-medium">MCP Server</p>
318
+ <p className="text-xs text-muted-foreground mt-1">
319
+ {mcpStatus.status === 'connected' ? (
320
+ <span className="flex items-center gap-1 text-emerald-600 dark:text-emerald-400">
321
+ <CheckCircle2 className="h-3 w-3" />
322
+ Connected
323
+ </span>
324
+ ) : mcpStatus.status === 'connecting' ? (
325
+ <span className="flex items-center gap-1 text-amber-600 dark:text-amber-400">
326
+ <Loader2 className="h-3 w-3 animate-spin" />
327
+ Connecting...
328
+ </span>
329
+ ) : (
330
+ <span className="flex items-center gap-1 text-destructive">
331
+ <XCircle className="h-3 w-3" />
332
+ {mcpStatus.error || 'Failed'}
333
+ </span>
334
+ )}
335
+ </p>
336
+ <p className="text-xs text-muted-foreground mt-1">{name}</p>
337
+ <p className="text-xs text-muted-foreground mt-1">localhost:3001</p>
338
+ </div>
339
+ {mcpStatus.status === 'connected' && (
340
+ <div className="flex h-5 w-5 items-center justify-center rounded-full bg-emerald-100 text-emerald-600 dark:bg-emerald-900/40 dark:text-emerald-400">
341
+ <svg className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
342
+ <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
343
+ </svg>
344
+ </div>
345
+ )}
346
+ </div>
347
+ </div>
348
+ ))}
349
+
350
+ {/* Show placeholder if no MCP info */}
351
+ {(!health?.mcp || Object.keys(health.mcp).length === 0) && (
352
+ <div className="p-4 rounded-lg border-2 border-border bg-muted/20">
353
+ <div>
354
+ <p className="text-sm font-medium">MCP Server</p>
355
+ <p className="text-xs text-muted-foreground mt-1">
356
+ <span className="flex items-center gap-1">
357
+ <XCircle className="h-3 w-3" />
358
+ Not connected
359
+ </span>
360
+ </p>
361
+ <p className="text-xs text-muted-foreground mt-1">localhost:3001</p>
362
+ </div>
363
+ </div>
364
+ )}
365
+ </div>
366
+ </div>
367
+
368
+ {/* MCP Tools Section */}
369
+ <div className="rounded-lg border bg-card p-6">
370
+ <button
371
+ onClick={() => setToolsExpanded(!toolsExpanded)}
372
+ className="w-full flex items-center justify-between text-left"
373
+ >
374
+ <h2 className="text-sm font-semibold flex items-center gap-2">
375
+ <Wrench className="h-4 w-4" />
376
+ MCP Tools ({tools.length} tools)
377
+ </h2>
378
+ {toolsExpanded ? (
379
+ <ChevronDown className="h-4 w-4 text-muted-foreground" />
380
+ ) : (
381
+ <ChevronRight className="h-4 w-4 text-muted-foreground" />
382
+ )}
383
+ </button>
384
+
385
+ {toolsExpanded && (
386
+ <div className="mt-4 space-y-4">
387
+ {Object.entries(toolsByModule).map(([module, moduleTools]) => (
388
+ <div key={module} className="space-y-2">
389
+ <h3 className="text-xs font-medium text-muted-foreground uppercase tracking-wider">
390
+ {module}
391
+ </h3>
392
+ <div className="space-y-1">
393
+ {moduleTools.map((tool) => (
394
+ <div key={tool.name} className="pl-2 border-l-2 border-muted py-1">
395
+ <p className="text-sm font-medium">{tool.name}</p>
396
+ <p className="text-xs text-muted-foreground">{tool.description}</p>
397
+ </div>
398
+ ))}
399
+ </div>
400
+ </div>
401
+ ))}
402
+ </div>
403
+ )}
404
+ </div>
405
+ </div>
406
+ )
407
+ }
408
+
409
+ export function AiAssistantSettingsPageClient() {
410
+ return (
411
+ <CommandPaletteProvider tenantId="" organizationId={null}>
412
+ <AiAssistantSettingsContent />
413
+ <CommandPalette />
414
+ </CommandPaletteProvider>
415
+ )
416
+ }
417
+
418
+ export default AiAssistantSettingsPageClient
@@ -0,0 +1,11 @@
1
+ import type { ModuleInfo } from '@open-mercato/shared/modules/registry'
2
+
3
+ export const metadata: ModuleInfo = {
4
+ name: 'ai_assistant',
5
+ title: 'AI Assistant',
6
+ version: '0.1.0',
7
+ description: 'MCP server for AI assistant integration with multi-tenant support.',
8
+ author: 'FreightTech Team',
9
+ }
10
+
11
+ export { features } from './acl'
@@ -0,0 +1,5 @@
1
+ // Re-export AI SDK functions from the ai-assistant package
2
+ export { streamText, generateObject, stepCountIs } from 'ai'
3
+ export { createOpenAI } from '@ai-sdk/openai'
4
+ export { createAnthropic } from '@ai-sdk/anthropic'
5
+ export { createGoogleGenerativeAI } from '@ai-sdk/google'