@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.
- package/AGENTS.md +1090 -0
- package/README.md +607 -0
- package/build.mjs +92 -0
- package/dist/di.js +8 -0
- package/dist/di.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandFooter.js +80 -0
- package/dist/frontend/components/CommandPalette/CommandFooter.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandHeader.js +53 -0
- package/dist/frontend/components/CommandPalette/CommandHeader.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandInput.js +29 -0
- package/dist/frontend/components/CommandPalette/CommandInput.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandItem.js +92 -0
- package/dist/frontend/components/CommandPalette/CommandItem.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandPalette.js +244 -0
- package/dist/frontend/components/CommandPalette/CommandPalette.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandPaletteProvider.js +42 -0
- package/dist/frontend/components/CommandPalette/CommandPaletteProvider.js.map +7 -0
- package/dist/frontend/components/CommandPalette/CommandPaletteWrapper.js +18 -0
- package/dist/frontend/components/CommandPalette/CommandPaletteWrapper.js.map +7 -0
- package/dist/frontend/components/CommandPalette/DebugPanel.js +215 -0
- package/dist/frontend/components/CommandPalette/DebugPanel.js.map +7 -0
- package/dist/frontend/components/CommandPalette/MessageBubble.js +64 -0
- package/dist/frontend/components/CommandPalette/MessageBubble.js.map +7 -0
- package/dist/frontend/components/CommandPalette/ToolCallConfirmation.js +91 -0
- package/dist/frontend/components/CommandPalette/ToolCallConfirmation.js.map +7 -0
- package/dist/frontend/components/CommandPalette/ToolCallDisplay.js +47 -0
- package/dist/frontend/components/CommandPalette/ToolCallDisplay.js.map +7 -0
- package/dist/frontend/components/CommandPalette/ToolChatPage.js +74 -0
- package/dist/frontend/components/CommandPalette/ToolChatPage.js.map +7 -0
- package/dist/frontend/components/CommandPalette/index.js +28 -0
- package/dist/frontend/components/CommandPalette/index.js.map +7 -0
- package/dist/frontend/constants.js +41 -0
- package/dist/frontend/constants.js.map +7 -0
- package/dist/frontend/hooks/index.js +13 -0
- package/dist/frontend/hooks/index.js.map +7 -0
- package/dist/frontend/hooks/useCommandPalette.js +1094 -0
- package/dist/frontend/hooks/useCommandPalette.js.map +7 -0
- package/dist/frontend/hooks/useMcpTools.js +66 -0
- package/dist/frontend/hooks/useMcpTools.js.map +7 -0
- package/dist/frontend/hooks/usePageContext.js +48 -0
- package/dist/frontend/hooks/usePageContext.js.map +7 -0
- package/dist/frontend/hooks/useRecentActions.js +56 -0
- package/dist/frontend/hooks/useRecentActions.js.map +7 -0
- package/dist/frontend/hooks/useRecentTools.js +55 -0
- package/dist/frontend/hooks/useRecentTools.js.map +7 -0
- package/dist/frontend/index.js +35 -0
- package/dist/frontend/index.js.map +7 -0
- package/dist/frontend/types.js +1 -0
- package/dist/frontend/types.js.map +7 -0
- package/dist/frontend/utils/index.js +7 -0
- package/dist/frontend/utils/index.js.map +7 -0
- package/dist/frontend/utils/toolMatcher.js +95 -0
- package/dist/frontend/utils/toolMatcher.js.map +7 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +7 -0
- package/dist/modules/ai_assistant/acl.js +14 -0
- package/dist/modules/ai_assistant/acl.js.map +7 -0
- package/dist/modules/ai_assistant/api/chat/route.js +152 -0
- package/dist/modules/ai_assistant/api/chat/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/health/route.js +27 -0
- package/dist/modules/ai_assistant/api/health/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/route/route.js +123 -0
- package/dist/modules/ai_assistant/api/route/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/settings/route.js +60 -0
- package/dist/modules/ai_assistant/api/settings/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/tools/execute/route.js +58 -0
- package/dist/modules/ai_assistant/api/tools/execute/route.js.map +7 -0
- package/dist/modules/ai_assistant/api/tools/route.js +48 -0
- package/dist/modules/ai_assistant/api/tools/route.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.js +10 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.js.map +7 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.meta.js +28 -0
- package/dist/modules/ai_assistant/backend/config/ai-assistant/page.meta.js.map +7 -0
- package/dist/modules/ai_assistant/cli.js +192 -0
- package/dist/modules/ai_assistant/cli.js.map +7 -0
- package/dist/modules/ai_assistant/di.js +11 -0
- package/dist/modules/ai_assistant/di.js.map +7 -0
- package/dist/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.js +257 -0
- package/dist/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.js.map +7 -0
- package/dist/modules/ai_assistant/index.js +13 -0
- package/dist/modules/ai_assistant/index.js.map +7 -0
- package/dist/modules/ai_assistant/lib/ai-sdk.js +13 -0
- package/dist/modules/ai_assistant/lib/ai-sdk.js.map +7 -0
- package/dist/modules/ai_assistant/lib/api-discovery-tools.js +249 -0
- package/dist/modules/ai_assistant/lib/api-discovery-tools.js.map +7 -0
- package/dist/modules/ai_assistant/lib/api-endpoint-index-config.js +177 -0
- package/dist/modules/ai_assistant/lib/api-endpoint-index-config.js.map +7 -0
- package/dist/modules/ai_assistant/lib/api-endpoint-index.js +210 -0
- package/dist/modules/ai_assistant/lib/api-endpoint-index.js.map +7 -0
- package/dist/modules/ai_assistant/lib/auth.js +87 -0
- package/dist/modules/ai_assistant/lib/auth.js.map +7 -0
- package/dist/modules/ai_assistant/lib/chat-config.js +117 -0
- package/dist/modules/ai_assistant/lib/chat-config.js.map +7 -0
- package/dist/modules/ai_assistant/lib/client-factory.js +60 -0
- package/dist/modules/ai_assistant/lib/client-factory.js.map +7 -0
- package/dist/modules/ai_assistant/lib/http-server.js +367 -0
- package/dist/modules/ai_assistant/lib/http-server.js.map +7 -0
- package/dist/modules/ai_assistant/lib/in-process-client.js +126 -0
- package/dist/modules/ai_assistant/lib/in-process-client.js.map +7 -0
- package/dist/modules/ai_assistant/lib/mcp-client.js +146 -0
- package/dist/modules/ai_assistant/lib/mcp-client.js.map +7 -0
- package/dist/modules/ai_assistant/lib/mcp-dev-server.js +283 -0
- package/dist/modules/ai_assistant/lib/mcp-dev-server.js.map +7 -0
- package/dist/modules/ai_assistant/lib/mcp-server-config.js +160 -0
- package/dist/modules/ai_assistant/lib/mcp-server-config.js.map +7 -0
- package/dist/modules/ai_assistant/lib/mcp-server.js +156 -0
- package/dist/modules/ai_assistant/lib/mcp-server.js.map +7 -0
- package/dist/modules/ai_assistant/lib/mcp-tool-adapter.js +44 -0
- package/dist/modules/ai_assistant/lib/mcp-tool-adapter.js.map +7 -0
- package/dist/modules/ai_assistant/lib/opencode-client.js +247 -0
- package/dist/modules/ai_assistant/lib/opencode-client.js.map +7 -0
- package/dist/modules/ai_assistant/lib/opencode-handlers.js +398 -0
- package/dist/modules/ai_assistant/lib/opencode-handlers.js.map +7 -0
- package/dist/modules/ai_assistant/lib/schema-utils.js +94 -0
- package/dist/modules/ai_assistant/lib/schema-utils.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-executor.js +55 -0
- package/dist/modules/ai_assistant/lib/tool-executor.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-index-config.js +125 -0
- package/dist/modules/ai_assistant/lib/tool-index-config.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-loader.js +88 -0
- package/dist/modules/ai_assistant/lib/tool-loader.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-registry.js +65 -0
- package/dist/modules/ai_assistant/lib/tool-registry.js.map +7 -0
- package/dist/modules/ai_assistant/lib/tool-search.js +192 -0
- package/dist/modules/ai_assistant/lib/tool-search.js.map +7 -0
- package/dist/modules/ai_assistant/lib/types.js +1 -0
- package/dist/modules/ai_assistant/lib/types.js.map +7 -0
- package/package.json +108 -0
- package/src/di.ts +11 -0
- package/src/frontend/components/CommandPalette/CommandFooter.tsx +113 -0
- package/src/frontend/components/CommandPalette/CommandHeader.tsx +76 -0
- package/src/frontend/components/CommandPalette/CommandInput.tsx +50 -0
- package/src/frontend/components/CommandPalette/CommandItem.tsx +111 -0
- package/src/frontend/components/CommandPalette/CommandPalette.tsx +276 -0
- package/src/frontend/components/CommandPalette/CommandPaletteProvider.tsx +60 -0
- package/src/frontend/components/CommandPalette/CommandPaletteWrapper.tsx +21 -0
- package/src/frontend/components/CommandPalette/DebugPanel.tsx +257 -0
- package/src/frontend/components/CommandPalette/MessageBubble.tsx +73 -0
- package/src/frontend/components/CommandPalette/ToolCallConfirmation.tsx +130 -0
- package/src/frontend/components/CommandPalette/ToolCallDisplay.tsx +57 -0
- package/src/frontend/components/CommandPalette/ToolChatPage.tsx +125 -0
- package/src/frontend/components/CommandPalette/index.ts +14 -0
- package/src/frontend/constants.ts +35 -0
- package/src/frontend/hooks/index.ts +5 -0
- package/src/frontend/hooks/useCommandPalette.ts +1389 -0
- package/src/frontend/hooks/useMcpTools.ts +73 -0
- package/src/frontend/hooks/usePageContext.ts +61 -0
- package/src/frontend/hooks/useRecentActions.ts +64 -0
- package/src/frontend/hooks/useRecentTools.ts +69 -0
- package/src/frontend/index.ts +39 -0
- package/src/frontend/types.ts +260 -0
- package/src/frontend/utils/index.ts +1 -0
- package/src/frontend/utils/toolMatcher.ts +127 -0
- package/src/index.ts +92 -0
- package/src/modules/ai_assistant/acl.ts +10 -0
- package/src/modules/ai_assistant/api/chat/route.ts +213 -0
- package/src/modules/ai_assistant/api/health/route.ts +30 -0
- package/src/modules/ai_assistant/api/route/route.ts +149 -0
- package/src/modules/ai_assistant/api/settings/route.ts +73 -0
- package/src/modules/ai_assistant/api/tools/execute/route.ts +71 -0
- package/src/modules/ai_assistant/api/tools/route.ts +57 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/page.meta.ts +26 -0
- package/src/modules/ai_assistant/backend/config/ai-assistant/page.tsx +12 -0
- package/src/modules/ai_assistant/cli.ts +233 -0
- package/src/modules/ai_assistant/di.ts +9 -0
- package/src/modules/ai_assistant/frontend/components/AiAssistantSettingsPageClient.tsx +418 -0
- package/src/modules/ai_assistant/index.ts +11 -0
- package/src/modules/ai_assistant/lib/ai-sdk.ts +5 -0
- package/src/modules/ai_assistant/lib/api-discovery-tools.ts +334 -0
- package/src/modules/ai_assistant/lib/api-endpoint-index-config.ts +243 -0
- package/src/modules/ai_assistant/lib/api-endpoint-index.ts +381 -0
- package/src/modules/ai_assistant/lib/auth.ts +185 -0
- package/src/modules/ai_assistant/lib/chat-config.ts +152 -0
- package/src/modules/ai_assistant/lib/client-factory.ts +130 -0
- package/src/modules/ai_assistant/lib/http-server.ts +498 -0
- package/src/modules/ai_assistant/lib/in-process-client.ts +205 -0
- package/src/modules/ai_assistant/lib/mcp-client.ts +221 -0
- package/src/modules/ai_assistant/lib/mcp-dev-server.ts +373 -0
- package/src/modules/ai_assistant/lib/mcp-server-config.ts +287 -0
- package/src/modules/ai_assistant/lib/mcp-server.ts +214 -0
- package/src/modules/ai_assistant/lib/mcp-tool-adapter.ts +76 -0
- package/src/modules/ai_assistant/lib/opencode-client.ts +426 -0
- package/src/modules/ai_assistant/lib/opencode-handlers.ts +676 -0
- package/src/modules/ai_assistant/lib/schema-utils.ts +142 -0
- package/src/modules/ai_assistant/lib/tool-executor.ts +71 -0
- package/src/modules/ai_assistant/lib/tool-index-config.ts +178 -0
- package/src/modules/ai_assistant/lib/tool-loader.ts +149 -0
- package/src/modules/ai_assistant/lib/tool-registry.ts +114 -0
- package/src/modules/ai_assistant/lib/tool-search.ts +308 -0
- package/src/modules/ai_assistant/lib/types.ts +147 -0
- package/test-schema.ts +37 -0
- package/tsconfig.json +10 -0
- package/watch.mjs +6 -0
package/README.md
ADDED
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
# AI Assistant Module
|
|
2
|
+
|
|
3
|
+
AI-powered assistance capabilities for Open Mercato, featuring MCP (Model Context Protocol) server integration and OpenCode as the AI backend.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **MCP Server** - HTTP server exposing platform tools via Model Context Protocol
|
|
8
|
+
- **OpenCode Integration** - AI backend for natural language processing and tool execution
|
|
9
|
+
- **API Discovery Tools** - Meta-tools for dynamic API access (`api_discover`, `api_execute`, `api_schema`)
|
|
10
|
+
- **Two-Tier Authentication** - Server-level API key + user-level session tokens
|
|
11
|
+
- **Session Management** - Ephemeral API keys with configurable TTL for secure tool execution
|
|
12
|
+
|
|
13
|
+
## Architecture Overview
|
|
14
|
+
|
|
15
|
+
```mermaid
|
|
16
|
+
graph TB
|
|
17
|
+
subgraph "Web Application"
|
|
18
|
+
ChatAPI["/api/chat (SSE)"]
|
|
19
|
+
SessionMgr[Session Token Manager]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
subgraph "OpenCode Server :4096"
|
|
23
|
+
OC[OpenCode Agent]
|
|
24
|
+
SSE[SSE /event endpoint]
|
|
25
|
+
Sessions[Session Manager]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
subgraph "MCP HTTP Server :3001"
|
|
29
|
+
MCPEndpoint["/mcp endpoint"]
|
|
30
|
+
ServerAuth[Server Auth<br/>x-api-key]
|
|
31
|
+
UserAuth[User Auth<br/>_sessionToken]
|
|
32
|
+
Tools[Tool Registry]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
subgraph "Open Mercato Platform"
|
|
36
|
+
Customers[Customers]
|
|
37
|
+
Sales[Sales]
|
|
38
|
+
Catalog[Catalog]
|
|
39
|
+
Search[Search]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
ChatAPI -->|"Create session token"| SessionMgr
|
|
43
|
+
ChatAPI -->|"HTTP POST"| OC
|
|
44
|
+
ChatAPI <-->|"SSE Subscribe"| SSE
|
|
45
|
+
OC -->|"MCP Protocol"| MCPEndpoint
|
|
46
|
+
MCPEndpoint --> ServerAuth
|
|
47
|
+
ServerAuth --> UserAuth
|
|
48
|
+
UserAuth --> Tools
|
|
49
|
+
Tools --> Customers
|
|
50
|
+
Tools --> Sales
|
|
51
|
+
Tools --> Catalog
|
|
52
|
+
Tools --> Search
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Message Flow
|
|
56
|
+
|
|
57
|
+
```mermaid
|
|
58
|
+
sequenceDiagram
|
|
59
|
+
participant User
|
|
60
|
+
participant API as /api/chat
|
|
61
|
+
participant DB as Database
|
|
62
|
+
participant OC as OpenCode :4096
|
|
63
|
+
participant MCP as MCP Server :3001
|
|
64
|
+
|
|
65
|
+
User->>API: POST {messages, sessionId?}
|
|
66
|
+
|
|
67
|
+
alt New Chat Session
|
|
68
|
+
API->>DB: createSessionApiKey(userId, roles)
|
|
69
|
+
DB-->>API: sessionToken (sess_xxx)
|
|
70
|
+
API->>API: Inject token into message
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
API-->>User: SSE: {type: 'thinking'}
|
|
74
|
+
API->>OC: POST /session/{id}/message
|
|
75
|
+
|
|
76
|
+
loop Tool Execution
|
|
77
|
+
OC->>MCP: Tool call with _sessionToken
|
|
78
|
+
MCP->>MCP: Validate x-api-key (Tier 1)
|
|
79
|
+
MCP->>DB: findApiKeyBySessionToken()
|
|
80
|
+
DB-->>MCP: User's roles & permissions
|
|
81
|
+
MCP->>MCP: Check tool permissions
|
|
82
|
+
MCP-->>OC: Tool result
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
OC-->>API: SSE: message.part.updated
|
|
86
|
+
API-->>User: SSE: {type: 'text', content: '...'}
|
|
87
|
+
OC-->>API: SSE: session.status = idle
|
|
88
|
+
API-->>User: SSE: {type: 'done', sessionId}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Two-Tier Authentication
|
|
92
|
+
|
|
93
|
+
```mermaid
|
|
94
|
+
graph TB
|
|
95
|
+
subgraph "Tier 1: Server Authentication"
|
|
96
|
+
Request[Incoming Request]
|
|
97
|
+
CheckKey{x-api-key header?}
|
|
98
|
+
ValidateKey[Compare with<br/>MCP_SERVER_API_KEY]
|
|
99
|
+
RejectServer[401 Unauthorized]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
subgraph "Tier 2: User Authentication"
|
|
103
|
+
ExtractToken[Extract _sessionToken<br/>from tool args]
|
|
104
|
+
LookupToken[findApiKeyBySessionToken]
|
|
105
|
+
LoadACL[rbacService.loadAcl]
|
|
106
|
+
CheckPerms{Has required<br/>features?}
|
|
107
|
+
RejectUser[403 Insufficient Permissions]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
subgraph "Execution"
|
|
111
|
+
Execute[Execute Tool<br/>with User Context]
|
|
112
|
+
Result[Return Result]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
Request --> CheckKey
|
|
116
|
+
CheckKey -->|No| RejectServer
|
|
117
|
+
CheckKey -->|Yes| ValidateKey
|
|
118
|
+
ValidateKey -->|Invalid| RejectServer
|
|
119
|
+
ValidateKey -->|Valid| ExtractToken
|
|
120
|
+
ExtractToken --> LookupToken
|
|
121
|
+
LookupToken -->|Not found/expired| RejectUser
|
|
122
|
+
LookupToken -->|Found| LoadACL
|
|
123
|
+
LoadACL --> CheckPerms
|
|
124
|
+
CheckPerms -->|No| RejectUser
|
|
125
|
+
CheckPerms -->|Yes| Execute
|
|
126
|
+
Execute --> Result
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Session Token Flow
|
|
130
|
+
|
|
131
|
+
```mermaid
|
|
132
|
+
sequenceDiagram
|
|
133
|
+
participant User
|
|
134
|
+
participant API as /api/chat
|
|
135
|
+
participant DB as api_keys table
|
|
136
|
+
participant OC as OpenCode
|
|
137
|
+
participant MCP as MCP Server
|
|
138
|
+
|
|
139
|
+
Note over User,MCP: New Chat Session
|
|
140
|
+
User->>API: POST /api/chat (first message)
|
|
141
|
+
API->>API: Get user's role IDs from JWT
|
|
142
|
+
API->>DB: createSessionApiKey({<br/>sessionToken,<br/>userId,<br/>userRoles,<br/>ttlMinutes: 120<br/>})
|
|
143
|
+
DB-->>API: Ephemeral key created
|
|
144
|
+
API->>OC: Message with session token
|
|
145
|
+
|
|
146
|
+
Note over OC,MCP: Tool Execution
|
|
147
|
+
OC->>MCP: Tool call with _sessionToken: "sess_xxx"
|
|
148
|
+
MCP->>MCP: Validate MCP_SERVER_API_KEY
|
|
149
|
+
MCP->>DB: findApiKeyBySessionToken()
|
|
150
|
+
DB-->>MCP: Session key with user's roles
|
|
151
|
+
MCP->>MCP: Load ACL from session key
|
|
152
|
+
MCP->>MCP: Check tool permissions
|
|
153
|
+
MCP-->>OC: Tool result
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## API Discovery Tools
|
|
157
|
+
|
|
158
|
+
```mermaid
|
|
159
|
+
flowchart LR
|
|
160
|
+
subgraph "User Query"
|
|
161
|
+
Q[User: "Find customers in New York"]
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
subgraph "AI Processing"
|
|
165
|
+
A1[api_discover<br/>'customers search']
|
|
166
|
+
A2[api_schema<br/>'/api/v1/customers']
|
|
167
|
+
A3[api_execute<br/>GET /api/v1/customers]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
subgraph "Results"
|
|
171
|
+
R[15 customers found]
|
|
172
|
+
Response[AI: "I found 15 customers<br/>in New York..."]
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
Q --> A1
|
|
176
|
+
A1 -->|"Found endpoints"| A2
|
|
177
|
+
A2 -->|"Got schema"| A3
|
|
178
|
+
A3 --> R
|
|
179
|
+
R --> Response
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
| Tool | Description |
|
|
183
|
+
|------|-------------|
|
|
184
|
+
| `api_discover` | Search for APIs by keyword, module, or HTTP method |
|
|
185
|
+
| `api_schema` | Get detailed schema for a specific endpoint |
|
|
186
|
+
| `api_execute` | Execute an API call with parameters |
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Quick Start
|
|
191
|
+
|
|
192
|
+
### 1. Configure Environment Variables
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
# MCP Server Authentication (required for production server)
|
|
196
|
+
MCP_SERVER_API_KEY=your-secure-server-key-here
|
|
197
|
+
|
|
198
|
+
# OpenCode URL (default: http://localhost:4096)
|
|
199
|
+
OPENCODE_URL=http://localhost:4096
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
> **Note:** The `MCP_SERVER_API_KEY` must also be configured in OpenCode's `opencode.json` as the `x-api-key` header.
|
|
203
|
+
|
|
204
|
+
### 2. Start the Stack
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
# Start MCP server (choose one)
|
|
208
|
+
yarn mcp:dev # Development mode - API key auth only
|
|
209
|
+
yarn mcp:serve # Production mode - API key + session tokens
|
|
210
|
+
|
|
211
|
+
# Start OpenCode container
|
|
212
|
+
docker start opencode-mvp
|
|
213
|
+
|
|
214
|
+
# Verify connectivity
|
|
215
|
+
curl http://localhost:3001/health # MCP health
|
|
216
|
+
curl http://localhost:4096/global/health # OpenCode health
|
|
217
|
+
curl http://localhost:4096/mcp # MCP connection status
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## MCP Server Modes
|
|
223
|
+
|
|
224
|
+
The module provides two MCP HTTP server modes for different use cases:
|
|
225
|
+
|
|
226
|
+
### Development Server (`yarn mcp:dev`)
|
|
227
|
+
|
|
228
|
+
A simplified HTTP server for local development. Authenticates once at startup using an API key.
|
|
229
|
+
|
|
230
|
+
**Features:**
|
|
231
|
+
- HTTP transport on port 3001 (configurable via `MCP_DEV_PORT`)
|
|
232
|
+
- Direct API key authentication (no session tokens needed)
|
|
233
|
+
- Tools filtered by API key permissions at startup
|
|
234
|
+
- Ideal for local testing and development
|
|
235
|
+
|
|
236
|
+
**Configuration (`.mcp.json`):**
|
|
237
|
+
```json
|
|
238
|
+
{
|
|
239
|
+
"mcpServers": {
|
|
240
|
+
"open-mercato": {
|
|
241
|
+
"type": "http",
|
|
242
|
+
"url": "http://localhost:3001/mcp",
|
|
243
|
+
"headers": {
|
|
244
|
+
"x-api-key": "omk_your_api_key_here"
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Running:**
|
|
252
|
+
```bash
|
|
253
|
+
# API key from .mcp.json headers.x-api-key
|
|
254
|
+
yarn mcp:dev
|
|
255
|
+
|
|
256
|
+
# Or from environment variable
|
|
257
|
+
OPEN_MERCATO_API_KEY=omk_xxx yarn mcp:dev
|
|
258
|
+
|
|
259
|
+
# Custom port
|
|
260
|
+
MCP_DEV_PORT=3002 yarn mcp:dev
|
|
261
|
+
|
|
262
|
+
# Enable debug logging
|
|
263
|
+
MCP_DEBUG=true yarn mcp:dev
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**Endpoints:**
|
|
267
|
+
- `POST http://localhost:3001/mcp` - MCP protocol endpoint
|
|
268
|
+
- `GET http://localhost:3001/health` - Health check
|
|
269
|
+
|
|
270
|
+
### Production Server (`yarn mcp:serve`)
|
|
271
|
+
|
|
272
|
+
A stateless HTTP server for production use. Requires two-tier authentication.
|
|
273
|
+
|
|
274
|
+
**Features:**
|
|
275
|
+
- HTTP transport on port 3001
|
|
276
|
+
- Two-tier authentication:
|
|
277
|
+
1. Server-level: `x-api-key` header validated against `MCP_SERVER_API_KEY`
|
|
278
|
+
2. User-level: `_sessionToken` parameter in each tool call
|
|
279
|
+
- Per-request permission checks based on user's session
|
|
280
|
+
- Ephemeral session tokens with 120-minute TTL
|
|
281
|
+
|
|
282
|
+
**Environment Variables:**
|
|
283
|
+
```bash
|
|
284
|
+
MCP_SERVER_API_KEY=your-secure-server-key-here
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**How it works:**
|
|
288
|
+
1. Chat API creates a session token for the logged-in user
|
|
289
|
+
2. OpenCode connects with static `MCP_SERVER_API_KEY`
|
|
290
|
+
3. Each tool call includes `_sessionToken` in arguments
|
|
291
|
+
4. MCP server resolves user permissions from session token
|
|
292
|
+
5. Tool executes with user's ACL context
|
|
293
|
+
|
|
294
|
+
### Comparison
|
|
295
|
+
|
|
296
|
+
| Feature | Dev (`mcp:dev`) | Production (`mcp:serve`) |
|
|
297
|
+
|---------|-----------------|-------------------------|
|
|
298
|
+
| Authentication | API key only | API key + session tokens |
|
|
299
|
+
| Permission Check | Once at startup | Per tool call |
|
|
300
|
+
| Session Tokens | Not required | Required (`_sessionToken`) |
|
|
301
|
+
| Use Case | Local development | Web AI chat interface |
|
|
302
|
+
| User Context | From API key | From session token |
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## OpenCode Integration
|
|
307
|
+
|
|
308
|
+
The AI Assistant uses **OpenCode** as the AI agent backend. OpenCode is a Go-based headless AI agent that connects to our MCP server for tool access.
|
|
309
|
+
|
|
310
|
+
### OpenCode SSE Events
|
|
311
|
+
|
|
312
|
+
```mermaid
|
|
313
|
+
graph LR
|
|
314
|
+
subgraph "Session Events"
|
|
315
|
+
SE1[session.status: busy]
|
|
316
|
+
SE2[session.status: idle]
|
|
317
|
+
SE3[session.status: waiting]
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
subgraph "Message Events"
|
|
321
|
+
ME1[message.updated]
|
|
322
|
+
ME2[message.part.updated]
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
subgraph "Question Events"
|
|
326
|
+
QE1[question.asked]
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
subgraph "Handlers"
|
|
330
|
+
H1[Emit 'thinking']
|
|
331
|
+
H2[Emit 'text' / 'tool-call']
|
|
332
|
+
H3[Emit 'question']
|
|
333
|
+
H4[Emit 'done']
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
SE1 --> H1
|
|
337
|
+
SE2 --> H4
|
|
338
|
+
SE3 --> H3
|
|
339
|
+
ME2 --> H2
|
|
340
|
+
QE1 --> H3
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### OpenCode Configuration
|
|
344
|
+
|
|
345
|
+
```json
|
|
346
|
+
{
|
|
347
|
+
"provider": "anthropic",
|
|
348
|
+
"model": "claude-sonnet-4-20250514",
|
|
349
|
+
"mcp": {
|
|
350
|
+
"open-mercato": {
|
|
351
|
+
"type": "sse",
|
|
352
|
+
"url": "http://host.docker.internal:3001/mcp",
|
|
353
|
+
"headers": {
|
|
354
|
+
"x-api-key": "your-secure-server-key-here"
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### OpenCode API Reference
|
|
362
|
+
|
|
363
|
+
| Endpoint | Method | Purpose |
|
|
364
|
+
|----------|--------|---------|
|
|
365
|
+
| `/event` | GET | SSE event stream |
|
|
366
|
+
| `/session` | POST | Create new session |
|
|
367
|
+
| `/session/{id}` | GET | Get session |
|
|
368
|
+
| `/session/{id}/message` | POST | Send message |
|
|
369
|
+
| `/question` | GET | List pending questions |
|
|
370
|
+
| `/question/{id}/reply` | POST | Answer question |
|
|
371
|
+
| `/global/health` | GET | Health check |
|
|
372
|
+
| `/mcp` | GET | MCP connection status |
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## API Key Entity Extension
|
|
377
|
+
|
|
378
|
+
The `api_keys` table includes session-specific fields:
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
@Entity({ tableName: 'api_keys' })
|
|
382
|
+
export class ApiKey {
|
|
383
|
+
// ... existing fields ...
|
|
384
|
+
|
|
385
|
+
@Property({ name: 'session_token', type: 'text', nullable: true })
|
|
386
|
+
sessionToken?: string | null
|
|
387
|
+
|
|
388
|
+
@Property({ name: 'session_user_id', type: 'uuid', nullable: true })
|
|
389
|
+
sessionUserId?: string | null
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Session Key Service Functions
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
// Generate a unique session token
|
|
397
|
+
generateSessionToken(): string // Returns: "sess_xxxxxxxx..."
|
|
398
|
+
|
|
399
|
+
// Create an ephemeral API key for a chat session
|
|
400
|
+
createSessionApiKey(em, {
|
|
401
|
+
sessionToken: string,
|
|
402
|
+
userId: string,
|
|
403
|
+
userRoles: string[], // Role IDs (not names)
|
|
404
|
+
tenantId?: string | null,
|
|
405
|
+
organizationId?: string | null,
|
|
406
|
+
ttlMinutes?: number // Default: 120
|
|
407
|
+
}): Promise<{ keyId, secret, sessionToken }>
|
|
408
|
+
|
|
409
|
+
// Look up a session key
|
|
410
|
+
findApiKeyBySessionToken(em, sessionToken): Promise<ApiKey | null>
|
|
411
|
+
|
|
412
|
+
// Delete a session key
|
|
413
|
+
deleteSessionApiKey(em, sessionToken): Promise<void>
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## API Routes
|
|
419
|
+
|
|
420
|
+
| Route | Method | Description |
|
|
421
|
+
|-------|--------|-------------|
|
|
422
|
+
| `/api/chat` | POST | Streaming chat with AI (SSE) |
|
|
423
|
+
| `/api/tools` | GET | List all available tools |
|
|
424
|
+
| `/api/tools/execute` | POST | Execute a specific tool |
|
|
425
|
+
| `/api/settings` | GET/POST | AI provider configuration |
|
|
426
|
+
| `/api/mcp-servers` | GET/POST | External MCP server management |
|
|
427
|
+
| `/api/mcp-servers/[id]` | GET/PUT/DELETE | Single MCP server operations |
|
|
428
|
+
|
|
429
|
+
### Chat API Request/Response
|
|
430
|
+
|
|
431
|
+
**Request**:
|
|
432
|
+
```typescript
|
|
433
|
+
{
|
|
434
|
+
messages: Array<{ role: 'user' | 'assistant'; content: string }>
|
|
435
|
+
sessionId?: string // Optional, for continuing conversation
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
**SSE Events**:
|
|
440
|
+
```typescript
|
|
441
|
+
type ChatSSEEvent =
|
|
442
|
+
| { type: 'thinking' }
|
|
443
|
+
| { type: 'text'; content: string }
|
|
444
|
+
| { type: 'tool-call'; id: string; toolName: string; args: unknown }
|
|
445
|
+
| { type: 'tool-result'; id: string; toolName: string; result: unknown }
|
|
446
|
+
| { type: 'question'; question: OpenCodeQuestion }
|
|
447
|
+
| { type: 'metadata'; model?: string; provider?: string; tokens?: { input: number; output: number } }
|
|
448
|
+
| { type: 'done'; sessionId?: string }
|
|
449
|
+
| { type: 'error'; error: string }
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## Directory Structure
|
|
455
|
+
|
|
456
|
+
```
|
|
457
|
+
packages/ai-assistant/
|
|
458
|
+
├── src/
|
|
459
|
+
│ ├── index.ts # Package exports
|
|
460
|
+
│ ├── di.ts # Dependency injection setup
|
|
461
|
+
│ ├── types.ts # Shared TypeScript types
|
|
462
|
+
│ │
|
|
463
|
+
│ ├── modules/ai_assistant/
|
|
464
|
+
│ │ ├── index.ts # Module exports
|
|
465
|
+
│ │ ├── acl.ts # Permission definitions
|
|
466
|
+
│ │ ├── cli.ts # CLI commands (mcp:serve, mcp:dev)
|
|
467
|
+
│ │ ├── di.ts # Module DI container
|
|
468
|
+
│ │ │
|
|
469
|
+
│ │ ├── lib/
|
|
470
|
+
│ │ │ ├── opencode-client.ts # OpenCode server client
|
|
471
|
+
│ │ │ ├── opencode-handlers.ts # Request handlers for OpenCode
|
|
472
|
+
│ │ │ ├── api-discovery-tools.ts # api_discover, api_execute, api_schema
|
|
473
|
+
│ │ │ ├── api-endpoint-index.ts # OpenAPI endpoint indexing
|
|
474
|
+
│ │ │ ├── http-server.ts # MCP HTTP server implementation
|
|
475
|
+
│ │ │ ├── mcp-server.ts # MCP stdio server implementation
|
|
476
|
+
│ │ │ ├── tool-registry.ts # Global tool registration
|
|
477
|
+
│ │ │ ├── tool-executor.ts # Tool execution logic
|
|
478
|
+
│ │ │ ├── tool-loader.ts # Discovers tools from modules
|
|
479
|
+
│ │ │ ├── mcp-tool-adapter.ts # Converts MCP tools to AI SDK format
|
|
480
|
+
│ │ │ └── types.ts # Module-specific types
|
|
481
|
+
│ │ │
|
|
482
|
+
│ │ ├── api/chat/
|
|
483
|
+
│ │ │ └── route.ts # POST /api/chat handler
|
|
484
|
+
│ │ │
|
|
485
|
+
│ │ └── frontend/components/
|
|
486
|
+
│ │ ├── AiAssistantSettingsPageClient.tsx
|
|
487
|
+
│ │ └── McpServersSection.tsx
|
|
488
|
+
│ │
|
|
489
|
+
│ └── frontend/
|
|
490
|
+
│ ├── index.ts # Frontend exports
|
|
491
|
+
│ ├── types.ts # Frontend TypeScript types
|
|
492
|
+
│ └── hooks/
|
|
493
|
+
│ └── useMcpTools.ts # Tool fetching and execution
|
|
494
|
+
│
|
|
495
|
+
├── AGENTS.md # Technical guide for AI agents
|
|
496
|
+
└── README.md # This file
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Registering Tools
|
|
502
|
+
|
|
503
|
+
```mermaid
|
|
504
|
+
flowchart LR
|
|
505
|
+
subgraph Modules
|
|
506
|
+
M1[customers/ai-tools.ts]
|
|
507
|
+
M2[sales/ai-tools.ts]
|
|
508
|
+
M3[search/ai-tools.ts]
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
subgraph Registration
|
|
512
|
+
R[registerMcpTool]
|
|
513
|
+
TR[Tool Registry]
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
subgraph Runtime
|
|
517
|
+
MCP[MCP Server]
|
|
518
|
+
ACL[ACL Check]
|
|
519
|
+
Exec[Execute Handler]
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
M1 -->|registerMcpTool| R
|
|
523
|
+
M2 -->|registerMcpTool| R
|
|
524
|
+
M3 -->|registerMcpTool| R
|
|
525
|
+
R --> TR
|
|
526
|
+
TR --> MCP
|
|
527
|
+
MCP --> ACL
|
|
528
|
+
ACL --> Exec
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
Register tools via `registerMcpTool()`:
|
|
532
|
+
|
|
533
|
+
```typescript
|
|
534
|
+
import { registerMcpTool } from '@open-mercato/ai-assistant/tools'
|
|
535
|
+
import { z } from 'zod'
|
|
536
|
+
|
|
537
|
+
registerMcpTool({
|
|
538
|
+
name: 'my_module_action',
|
|
539
|
+
description: 'Description of what this tool does',
|
|
540
|
+
inputSchema: z.object({
|
|
541
|
+
param1: z.string().describe('Description of param1'),
|
|
542
|
+
param2: z.number().optional(),
|
|
543
|
+
}),
|
|
544
|
+
requiredFeatures: ['my_module.action'],
|
|
545
|
+
handler: async (input, ctx) => {
|
|
546
|
+
const service = ctx.container.resolve('myService')
|
|
547
|
+
return { success: true, data: result }
|
|
548
|
+
}
|
|
549
|
+
}, { moduleId: 'my_module' })
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
## Permissions (ACL)
|
|
555
|
+
|
|
556
|
+
| Feature ID | Description |
|
|
557
|
+
|------------|-------------|
|
|
558
|
+
| `ai_assistant.view` | View AI Assistant |
|
|
559
|
+
| `ai_assistant.settings.manage` | Manage AI settings |
|
|
560
|
+
| `ai_assistant.mcp.serve` | Start MCP Server |
|
|
561
|
+
| `ai_assistant.tools.list` | List MCP Tools |
|
|
562
|
+
| `ai_assistant.mcp_servers.view` | View MCP server configs |
|
|
563
|
+
| `ai_assistant.mcp_servers.manage` | Manage MCP server configs |
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
## Troubleshooting
|
|
568
|
+
|
|
569
|
+
| Symptom | Likely Cause | Fix |
|
|
570
|
+
|---------|--------------|-----|
|
|
571
|
+
| "Agent is working..." forever | OpenCode not responding | Check `curl http://localhost:4096/global/health` |
|
|
572
|
+
| "MCP connection failed" | MCP server not running | Start with `yarn mcp:serve` or `yarn mcp:dev` |
|
|
573
|
+
| Empty response | OpenCode not connected to MCP | Check `curl http://localhost:4096/mcp` |
|
|
574
|
+
| "Unauthorized" error | Missing/invalid API key | Check x-api-key in opencode.json matches MCP_SERVER_API_KEY |
|
|
575
|
+
| "Session expired" errors | Session token TTL exceeded | Start new chat session (creates new 120-min token) |
|
|
576
|
+
| Tools fail with UNAUTHORIZED | Missing _sessionToken | Verify AI is passing token in tool args |
|
|
577
|
+
| "Insufficient permissions" | User lacks required features | Check user's role assignments |
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## Technical Notes
|
|
582
|
+
|
|
583
|
+
### Zod 4 Schema Handling
|
|
584
|
+
|
|
585
|
+
The module includes handling for Zod 4 schemas with the Vercel AI SDK. See [AGENTS.md](./AGENTS.md) for implementation details.
|
|
586
|
+
|
|
587
|
+
### Docker Configuration
|
|
588
|
+
|
|
589
|
+
```yaml
|
|
590
|
+
# docker-compose.yml
|
|
591
|
+
services:
|
|
592
|
+
opencode:
|
|
593
|
+
build: ./docker/opencode
|
|
594
|
+
container_name: opencode-mvp
|
|
595
|
+
ports:
|
|
596
|
+
- "4096:4096"
|
|
597
|
+
environment:
|
|
598
|
+
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
|
599
|
+
volumes:
|
|
600
|
+
- ./docker/opencode/opencode.json:/root/.opencode/opencode.json
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
---
|
|
604
|
+
|
|
605
|
+
## License
|
|
606
|
+
|
|
607
|
+
Proprietary - Open Mercato
|
package/build.mjs
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as esbuild from 'esbuild'
|
|
2
|
+
import { glob } from 'glob'
|
|
3
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, copyFileSync } from 'node:fs'
|
|
4
|
+
import { dirname, join, relative } from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
8
|
+
|
|
9
|
+
const entryPoints = await glob(join(__dirname, 'src/**/*.{ts,tsx}'), {
|
|
10
|
+
ignore: ['**/__tests__/**', '**/*.test.ts', '**/*.test.tsx']
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
// Plugin to add .js extension to relative imports
|
|
14
|
+
const addJsExtension = {
|
|
15
|
+
name: 'add-js-extension',
|
|
16
|
+
setup(build) {
|
|
17
|
+
build.onEnd(async (result) => {
|
|
18
|
+
if (result.errors.length > 0) return
|
|
19
|
+
const outputFiles = await glob(join(__dirname, 'dist/**/*.js'))
|
|
20
|
+
for (const file of outputFiles) {
|
|
21
|
+
const fileDir = dirname(file)
|
|
22
|
+
let content = readFileSync(file, 'utf-8')
|
|
23
|
+
// Add .js to relative imports that don't have an extension
|
|
24
|
+
content = content.replace(
|
|
25
|
+
/from\s+["'](\.[^"']+)["']/g,
|
|
26
|
+
(match, path) => {
|
|
27
|
+
if (path.endsWith('.js') || path.endsWith('.json')) return match
|
|
28
|
+
// Check if it's a directory with index.js
|
|
29
|
+
const resolvedPath = join(fileDir, path)
|
|
30
|
+
if (existsSync(resolvedPath) && existsSync(join(resolvedPath, 'index.js'))) {
|
|
31
|
+
return `from "${path}/index.js"`
|
|
32
|
+
}
|
|
33
|
+
return `from "${path}.js"`
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
content = content.replace(
|
|
37
|
+
/import\s*\(\s*["'](\.[^"']+)["']\s*\)/g,
|
|
38
|
+
(match, path) => {
|
|
39
|
+
if (path.endsWith('.js') || path.endsWith('.json')) return match
|
|
40
|
+
// Check if it's a directory with index.js
|
|
41
|
+
const resolvedPath = join(fileDir, path)
|
|
42
|
+
if (existsSync(resolvedPath) && existsSync(join(resolvedPath, 'index.js'))) {
|
|
43
|
+
return `import("${path}/index.js")`
|
|
44
|
+
}
|
|
45
|
+
return `import("${path}.js")`
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
// Handle side-effect imports: import "./path" (no from clause)
|
|
49
|
+
content = content.replace(
|
|
50
|
+
/import\s+["'](\.[^"']+)["'];/g,
|
|
51
|
+
(match, path) => {
|
|
52
|
+
if (path.endsWith('.js') || path.endsWith('.json')) return match
|
|
53
|
+
// Check if it's a directory with index.js
|
|
54
|
+
const resolvedPath = join(fileDir, path)
|
|
55
|
+
if (existsSync(resolvedPath) && existsSync(join(resolvedPath, 'index.js'))) {
|
|
56
|
+
return `import "${path}/index.js";`
|
|
57
|
+
}
|
|
58
|
+
return `import "${path}.js";`
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
writeFileSync(file, content)
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const outdir = join(__dirname, 'dist')
|
|
68
|
+
|
|
69
|
+
await esbuild.build({
|
|
70
|
+
entryPoints,
|
|
71
|
+
outdir,
|
|
72
|
+
outbase: join(__dirname, 'src'),
|
|
73
|
+
format: 'esm',
|
|
74
|
+
platform: 'node',
|
|
75
|
+
target: 'node18',
|
|
76
|
+
sourcemap: true,
|
|
77
|
+
jsx: 'automatic',
|
|
78
|
+
plugins: [addJsExtension],
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// Copy JSON files from src to dist
|
|
82
|
+
const jsonFiles = await glob(join(__dirname, 'src/**/*.json'), {
|
|
83
|
+
ignore: ['**/node_modules/**']
|
|
84
|
+
})
|
|
85
|
+
for (const jsonFile of jsonFiles) {
|
|
86
|
+
const relativePath = relative(join(__dirname, 'src'), jsonFile)
|
|
87
|
+
const destPath = join(outdir, relativePath)
|
|
88
|
+
mkdirSync(dirname(destPath), { recursive: true })
|
|
89
|
+
copyFileSync(jsonFile, destPath)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log('ai-assistant built successfully')
|