nitrostack 1.0.0 → 1.0.2

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 (164) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/cli/index.js +4 -1
  3. package/dist/cli/index.js.map +1 -1
  4. package/package.json +1 -1
  5. package/src/studio/README.md +140 -0
  6. package/src/studio/app/api/auth/fetch-metadata/route.ts +71 -0
  7. package/src/studio/app/api/auth/register-client/route.ts +67 -0
  8. package/src/studio/app/api/chat/route.ts +123 -0
  9. package/src/studio/app/api/health/checks/route.ts +42 -0
  10. package/src/studio/app/api/health/route.ts +13 -0
  11. package/src/studio/app/api/init/route.ts +85 -0
  12. package/src/studio/app/api/ping/route.ts +13 -0
  13. package/src/studio/app/api/prompts/[name]/route.ts +21 -0
  14. package/src/studio/app/api/prompts/route.ts +13 -0
  15. package/src/studio/app/api/resources/[...uri]/route.ts +18 -0
  16. package/src/studio/app/api/resources/route.ts +13 -0
  17. package/src/studio/app/api/roots/route.ts +13 -0
  18. package/src/studio/app/api/sampling/route.ts +14 -0
  19. package/src/studio/app/api/tools/[name]/call/route.ts +41 -0
  20. package/src/studio/app/api/tools/route.ts +23 -0
  21. package/src/studio/app/api/widget-examples/route.ts +44 -0
  22. package/src/studio/app/auth/callback/page.tsx +160 -0
  23. package/src/studio/app/auth/page.tsx +543 -0
  24. package/src/studio/app/chat/page.tsx +530 -0
  25. package/src/studio/app/chat/page.tsx.backup +390 -0
  26. package/src/studio/app/globals.css +410 -0
  27. package/src/studio/app/health/page.tsx +177 -0
  28. package/src/studio/app/layout.tsx +48 -0
  29. package/src/studio/app/page.tsx +337 -0
  30. package/src/studio/app/page.tsx.backup +346 -0
  31. package/src/studio/app/ping/page.tsx +204 -0
  32. package/src/studio/app/prompts/page.tsx +228 -0
  33. package/src/studio/app/resources/page.tsx +313 -0
  34. package/src/studio/components/EnlargeModal.tsx +116 -0
  35. package/src/studio/components/Sidebar.tsx +133 -0
  36. package/src/studio/components/ToolCard.tsx +108 -0
  37. package/src/studio/components/WidgetRenderer.tsx +99 -0
  38. package/src/studio/lib/api.ts +207 -0
  39. package/src/studio/lib/llm-service.ts +361 -0
  40. package/src/studio/lib/mcp-client.ts +168 -0
  41. package/src/studio/lib/store.ts +192 -0
  42. package/src/studio/lib/theme-provider.tsx +50 -0
  43. package/src/studio/lib/types.ts +107 -0
  44. package/src/studio/lib/widget-loader.ts +90 -0
  45. package/src/studio/middleware.ts +27 -0
  46. package/src/studio/next.config.js +16 -0
  47. package/src/studio/package-lock.json +2696 -0
  48. package/src/studio/package.json +34 -0
  49. package/src/studio/postcss.config.mjs +10 -0
  50. package/src/studio/tailwind.config.ts +67 -0
  51. package/src/studio/tsconfig.json +41 -0
  52. package/templates/typescript-auth/.env.example +23 -0
  53. package/templates/typescript-auth/src/app.module.ts +103 -0
  54. package/templates/typescript-auth/src/db/database.ts +163 -0
  55. package/templates/typescript-auth/src/db/seed.ts +374 -0
  56. package/templates/typescript-auth/src/db/setup.ts +87 -0
  57. package/templates/typescript-auth/src/events/analytics.service.ts +52 -0
  58. package/templates/typescript-auth/src/events/notification.service.ts +40 -0
  59. package/templates/typescript-auth/src/filters/global-exception.filter.ts +28 -0
  60. package/templates/typescript-auth/src/guards/README.md +75 -0
  61. package/templates/typescript-auth/src/guards/jwt.guard.ts +105 -0
  62. package/templates/typescript-auth/src/health/database.health.ts +41 -0
  63. package/templates/typescript-auth/src/index.ts +26 -0
  64. package/templates/typescript-auth/src/interceptors/transform.interceptor.ts +24 -0
  65. package/templates/typescript-auth/src/middleware/logging.middleware.ts +42 -0
  66. package/templates/typescript-auth/src/modules/addresses/addresses.module.ts +16 -0
  67. package/templates/typescript-auth/src/modules/addresses/addresses.prompts.ts +114 -0
  68. package/templates/typescript-auth/src/modules/addresses/addresses.resources.ts +40 -0
  69. package/templates/typescript-auth/src/modules/addresses/addresses.tools.ts +241 -0
  70. package/templates/typescript-auth/src/modules/auth/auth.module.ts +16 -0
  71. package/templates/typescript-auth/src/modules/auth/auth.prompts.ts +147 -0
  72. package/templates/typescript-auth/src/modules/auth/auth.resources.ts +84 -0
  73. package/templates/typescript-auth/src/modules/auth/auth.tools.ts +139 -0
  74. package/templates/typescript-auth/src/modules/cart/cart.module.ts +16 -0
  75. package/templates/typescript-auth/src/modules/cart/cart.prompts.ts +95 -0
  76. package/templates/typescript-auth/src/modules/cart/cart.resources.ts +44 -0
  77. package/templates/typescript-auth/src/modules/cart/cart.tools.ts +281 -0
  78. package/templates/typescript-auth/src/modules/orders/orders.module.ts +16 -0
  79. package/templates/typescript-auth/src/modules/orders/orders.prompts.ts +88 -0
  80. package/templates/typescript-auth/src/modules/orders/orders.resources.ts +48 -0
  81. package/templates/typescript-auth/src/modules/orders/orders.tools.ts +281 -0
  82. package/templates/typescript-auth/src/modules/products/products.module.ts +16 -0
  83. package/templates/typescript-auth/src/modules/products/products.prompts.ts +146 -0
  84. package/templates/typescript-auth/src/modules/products/products.resources.ts +98 -0
  85. package/templates/typescript-auth/src/modules/products/products.tools.ts +266 -0
  86. package/templates/typescript-auth/src/pipes/validation.pipe.ts +42 -0
  87. package/templates/typescript-auth/src/services/database.service.ts +90 -0
  88. package/templates/typescript-auth/src/widgets/app/add-to-cart/page.tsx +122 -0
  89. package/templates/typescript-auth/src/widgets/app/address-added/page.tsx +116 -0
  90. package/templates/typescript-auth/src/widgets/app/address-deleted/page.tsx +105 -0
  91. package/templates/typescript-auth/src/widgets/app/address-list/page.tsx +139 -0
  92. package/templates/typescript-auth/src/widgets/app/address-updated/page.tsx +153 -0
  93. package/templates/typescript-auth/src/widgets/app/cart-cleared/page.tsx +86 -0
  94. package/templates/typescript-auth/src/widgets/app/cart-updated/page.tsx +116 -0
  95. package/templates/typescript-auth/src/widgets/app/categories/page.tsx +134 -0
  96. package/templates/typescript-auth/src/widgets/app/layout.tsx +21 -0
  97. package/templates/typescript-auth/src/widgets/app/login-result/page.tsx +129 -0
  98. package/templates/typescript-auth/src/widgets/app/order-confirmation/page.tsx +206 -0
  99. package/templates/typescript-auth/src/widgets/app/order-details/page.tsx +225 -0
  100. package/templates/typescript-auth/src/widgets/app/order-history/page.tsx +218 -0
  101. package/templates/typescript-auth/src/widgets/app/product-card/page.tsx +121 -0
  102. package/templates/typescript-auth/src/widgets/app/products-grid/page.tsx +173 -0
  103. package/templates/typescript-auth/src/widgets/app/shopping-cart/page.tsx +187 -0
  104. package/templates/typescript-auth/src/widgets/app/whoami/page.tsx +165 -0
  105. package/templates/typescript-auth/src/widgets/next.config.js +38 -0
  106. package/templates/typescript-auth/src/widgets/package.json +18 -0
  107. package/templates/typescript-auth/src/widgets/styles/ecommerce.ts +169 -0
  108. package/templates/typescript-auth/src/widgets/tsconfig.json +28 -0
  109. package/templates/typescript-auth/src/widgets/types/tool-data.ts +141 -0
  110. package/templates/typescript-auth/src/widgets/widget-manifest.json +464 -0
  111. package/templates/typescript-auth/tsconfig.json +27 -0
  112. package/templates/typescript-auth-api-key/.env +15 -0
  113. package/templates/typescript-auth-api-key/.env.example +4 -0
  114. package/templates/typescript-auth-api-key/src/app.module.ts +38 -0
  115. package/templates/typescript-auth-api-key/src/guards/apikey.guard.ts +47 -0
  116. package/templates/typescript-auth-api-key/src/guards/multi-auth.guard.ts +157 -0
  117. package/templates/typescript-auth-api-key/src/health/system.health.ts +55 -0
  118. package/templates/typescript-auth-api-key/src/index.ts +47 -0
  119. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.module.ts +12 -0
  120. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.prompts.ts +73 -0
  121. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.resources.ts +60 -0
  122. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.tools.ts +71 -0
  123. package/templates/typescript-auth-api-key/src/modules/demo/demo.module.ts +18 -0
  124. package/templates/typescript-auth-api-key/src/modules/demo/demo.tools.ts +155 -0
  125. package/templates/typescript-auth-api-key/src/modules/demo/multi-auth.tools.ts +123 -0
  126. package/templates/typescript-auth-api-key/src/widgets/app/calculator-operations/page.tsx +133 -0
  127. package/templates/typescript-auth-api-key/src/widgets/app/calculator-result/page.tsx +134 -0
  128. package/templates/typescript-auth-api-key/src/widgets/app/layout.tsx +14 -0
  129. package/templates/typescript-auth-api-key/src/widgets/next.config.js +37 -0
  130. package/templates/typescript-auth-api-key/src/widgets/package.json +24 -0
  131. package/templates/typescript-auth-api-key/src/widgets/tsconfig.json +28 -0
  132. package/templates/typescript-auth-api-key/src/widgets/widget-manifest.json +48 -0
  133. package/templates/typescript-auth-api-key/tsconfig.json +23 -0
  134. package/templates/typescript-oauth/.env.example +91 -0
  135. package/templates/typescript-oauth/src/app.module.ts +89 -0
  136. package/templates/typescript-oauth/src/guards/oauth.guard.ts +127 -0
  137. package/templates/typescript-oauth/src/index.ts +74 -0
  138. package/templates/typescript-oauth/src/modules/demo/demo.module.ts +16 -0
  139. package/templates/typescript-oauth/src/modules/demo/demo.tools.ts +190 -0
  140. package/templates/typescript-oauth/src/widgets/app/calculator-operations/page.tsx +133 -0
  141. package/templates/typescript-oauth/src/widgets/app/calculator-result/page.tsx +134 -0
  142. package/templates/typescript-oauth/src/widgets/app/layout.tsx +14 -0
  143. package/templates/typescript-oauth/src/widgets/next.config.js +37 -0
  144. package/templates/typescript-oauth/src/widgets/package.json +24 -0
  145. package/templates/typescript-oauth/src/widgets/tsconfig.json +28 -0
  146. package/templates/typescript-oauth/src/widgets/widget-manifest.json +48 -0
  147. package/templates/typescript-oauth/tsconfig.json +23 -0
  148. package/templates/typescript-starter/.env.example +4 -0
  149. package/templates/typescript-starter/src/app.module.ts +34 -0
  150. package/templates/typescript-starter/src/health/system.health.ts +55 -0
  151. package/templates/typescript-starter/src/index.ts +27 -0
  152. package/templates/typescript-starter/src/modules/calculator/calculator.module.ts +12 -0
  153. package/templates/typescript-starter/src/modules/calculator/calculator.prompts.ts +73 -0
  154. package/templates/typescript-starter/src/modules/calculator/calculator.resources.ts +60 -0
  155. package/templates/typescript-starter/src/modules/calculator/calculator.tools.ts +71 -0
  156. package/templates/typescript-starter/src/widgets/app/calculator-operations/page.tsx +133 -0
  157. package/templates/typescript-starter/src/widgets/app/calculator-result/page.tsx +134 -0
  158. package/templates/typescript-starter/src/widgets/app/layout.tsx +14 -0
  159. package/templates/typescript-starter/src/widgets/next.config.js +37 -0
  160. package/templates/typescript-starter/src/widgets/package.json +24 -0
  161. package/templates/typescript-starter/src/widgets/tsconfig.json +28 -0
  162. package/templates/typescript-starter/src/widgets/widget-manifest.json +48 -0
  163. package/templates/typescript-starter/tsconfig.json +23 -0
  164. package/LICENSE_URLS_UPDATE_COMPLETE.md +0 -388
@@ -0,0 +1,390 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import { useStudioStore } from '@/lib/store';
5
+ import { api } from '@/lib/api';
6
+ import { WidgetRenderer } from '@/components/WidgetRenderer';
7
+ import type { ChatMessage, Tool, ToolCall } from '@/lib/types';
8
+
9
+ export default function ChatPage() {
10
+ const {
11
+ chatMessages,
12
+ addChatMessage,
13
+ clearChat,
14
+ currentProvider,
15
+ setCurrentProvider,
16
+ currentImage,
17
+ setCurrentImage,
18
+ jwtToken,
19
+ tools,
20
+ setTools,
21
+ } = useStudioStore();
22
+
23
+ const [inputValue, setInputValue] = useState('');
24
+ const [loading, setLoading] = useState(false);
25
+ const [showSettings, setShowSettings] = useState(false);
26
+ const messagesEndRef = useRef<HTMLDivElement>(null);
27
+ const fileInputRef = useRef<HTMLInputElement>(null);
28
+
29
+ useEffect(() => {
30
+ loadTools();
31
+ }, []);
32
+
33
+ useEffect(() => {
34
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
35
+ }, [chatMessages]);
36
+
37
+ const loadTools = async () => {
38
+ try {
39
+ const data = await api.getTools();
40
+ setTools(data.tools || []);
41
+ } catch (error) {
42
+ console.error('Failed to load tools:', error);
43
+ }
44
+ };
45
+
46
+ const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
47
+ const file = e.target.files?.[0];
48
+ if (!file) return;
49
+
50
+ if (file.size > 20 * 1024 * 1024) {
51
+ alert('Image too large (max 20MB)');
52
+ return;
53
+ }
54
+
55
+ const reader = new FileReader();
56
+ reader.onload = (event) => {
57
+ setCurrentImage({
58
+ data: event.target?.result as string,
59
+ type: file.type,
60
+ name: file.name,
61
+ });
62
+ };
63
+ reader.readAsDataURL(file);
64
+ };
65
+
66
+ const handleSend = async () => {
67
+ if (!inputValue.trim() && !currentImage) return;
68
+
69
+ const apiKey = localStorage.getItem(`${currentProvider}_api_key`);
70
+ if (!apiKey) {
71
+ setShowSettings(true);
72
+ alert('Please set your API key first');
73
+ return;
74
+ }
75
+
76
+ const userMessage: ChatMessage = {
77
+ role: 'user',
78
+ content: inputValue,
79
+ };
80
+
81
+ if (currentImage) {
82
+ userMessage.image = currentImage;
83
+ }
84
+
85
+ addChatMessage(userMessage);
86
+ setInputValue('');
87
+ setCurrentImage(null);
88
+ setLoading(true);
89
+
90
+ try {
91
+ const response = await api.chat({
92
+ provider: currentProvider,
93
+ messages: [...chatMessages, userMessage],
94
+ apiKey,
95
+ jwtToken: jwtToken || undefined,
96
+ });
97
+
98
+ if (response.message) {
99
+ addChatMessage(response.message);
100
+ }
101
+
102
+ // Handle tool calls
103
+ if (response.toolCalls && response.toolResults) {
104
+ // Check for login token
105
+ for (let i = 0; i < response.toolCalls.length; i++) {
106
+ const toolCall = response.toolCalls[i];
107
+ const toolResult = response.toolResults[i];
108
+
109
+ if (toolCall.name === 'login' && toolResult.content) {
110
+ try {
111
+ const parsed = JSON.parse(toolResult.content);
112
+ if (parsed.token) {
113
+ useStudioStore.getState().setJwtToken(parsed.token);
114
+ }
115
+ } catch (e) {
116
+ // Ignore
117
+ }
118
+ }
119
+ }
120
+
121
+ // Add tool results
122
+ for (const result of response.toolResults) {
123
+ addChatMessage(result);
124
+ }
125
+
126
+ // Continue conversation
127
+ await continueChatWithToolResults(apiKey);
128
+ }
129
+ } catch (error) {
130
+ console.error('Chat error:', error);
131
+ addChatMessage({
132
+ role: 'assistant',
133
+ content: 'Sorry, I encountered an error. Please try again.',
134
+ });
135
+ } finally {
136
+ setLoading(false);
137
+ }
138
+ };
139
+
140
+ const continueChatWithToolResults = async (apiKey: string) => {
141
+ try {
142
+ const response = await api.chat({
143
+ provider: currentProvider,
144
+ messages: chatMessages,
145
+ apiKey,
146
+ jwtToken: jwtToken || undefined,
147
+ });
148
+
149
+ if (response.message) {
150
+ addChatMessage(response.message);
151
+ }
152
+
153
+ // Recursive tool calls
154
+ if (response.toolCalls && response.toolResults) {
155
+ for (const result of response.toolResults) {
156
+ addChatMessage(result);
157
+ }
158
+ await continueChatWithToolResults(apiKey);
159
+ }
160
+ } catch (error) {
161
+ console.error('Continuation error:', error);
162
+ }
163
+ };
164
+
165
+ const saveApiKey = (provider: 'openai' | 'gemini') => {
166
+ const input = document.getElementById(`${provider}-api-key`) as HTMLInputElement;
167
+ const key = input?.value.trim();
168
+
169
+ if (!key || key === '••••••••') {
170
+ alert('Please enter a valid API key');
171
+ return;
172
+ }
173
+
174
+ localStorage.setItem(`${provider}_api_key`, key);
175
+ input.value = '••••••••';
176
+ alert(`${provider === 'openai' ? 'OpenAI' : 'Gemini'} API key saved`);
177
+ };
178
+
179
+ return (
180
+ <div className="h-screen flex flex-col bg-dark-bg">
181
+ {/* Header */}
182
+ <div className="border-b border-dark-border p-4 flex items-center justify-between">
183
+ <div>
184
+ <h1 className="text-2xl font-bold">🤖 AI Chat</h1>
185
+ <p className="text-text-secondary text-sm">Chat with tools integration</p>
186
+ </div>
187
+
188
+ <div className="flex items-center gap-2">
189
+ <select
190
+ value={currentProvider}
191
+ onChange={(e) => setCurrentProvider(e.target.value as 'openai' | 'gemini')}
192
+ className="input w-32"
193
+ >
194
+ <option value="openai">OpenAI</option>
195
+ <option value="gemini">Gemini</option>
196
+ </select>
197
+ <button onClick={() => setShowSettings(!showSettings)} className="btn btn-secondary">
198
+ ⚙️
199
+ </button>
200
+ <button onClick={clearChat} className="btn btn-secondary">
201
+ 🗑️
202
+ </button>
203
+ </div>
204
+ </div>
205
+
206
+ {/* Settings Panel */}
207
+ {showSettings && (
208
+ <div className="border-b border-dark-border p-4 bg-dark-surface">
209
+ <h3 className="font-medium mb-3">API Keys</h3>
210
+ <div className="grid grid-cols-2 gap-4">
211
+ <div>
212
+ <label className="text-sm mb-2 block">OpenAI API Key</label>
213
+ <div className="flex gap-2">
214
+ <input id="openai-api-key" type="password" className="input flex-1" placeholder="sk-..." />
215
+ <button onClick={() => saveApiKey('openai')} className="btn btn-primary">
216
+ Save
217
+ </button>
218
+ </div>
219
+ </div>
220
+ <div>
221
+ <label className="text-sm mb-2 block">Gemini API Key</label>
222
+ <div className="flex gap-2">
223
+ <input id="gemini-api-key" type="password" className="input flex-1" placeholder="AIza..." />
224
+ <button onClick={() => saveApiKey('gemini')} className="btn btn-primary">
225
+ Save
226
+ </button>
227
+ </div>
228
+ </div>
229
+ </div>
230
+ </div>
231
+ )}
232
+
233
+ {/* Messages */}
234
+ <div className="flex-1 overflow-y-auto p-4 space-y-4">
235
+ {chatMessages.map((msg, idx) => (
236
+ <ChatMessageComponent key={idx} message={msg} tools={tools} />
237
+ ))}
238
+ {loading && (
239
+ <div className="flex items-center gap-2 text-text-secondary">
240
+ <div className="animate-pulse">Thinking...</div>
241
+ </div>
242
+ )}
243
+ <div ref={messagesEndRef} />
244
+ </div>
245
+
246
+ {/* Input */}
247
+ <div className="border-t border-dark-border p-4">
248
+ {currentImage && (
249
+ <div className="mb-2 relative inline-block">
250
+ <img
251
+ src={currentImage.data}
252
+ alt="Upload preview"
253
+ className="h-20 rounded-lg border border-dark-border"
254
+ />
255
+ <button
256
+ onClick={() => setCurrentImage(null)}
257
+ className="absolute -top-2 -right-2 bg-error text-white rounded-full w-6 h-6 flex items-center justify-center text-xs"
258
+ >
259
+
260
+ </button>
261
+ </div>
262
+ )}
263
+ <div className="flex gap-2">
264
+ <input
265
+ type="file"
266
+ ref={fileInputRef}
267
+ onChange={handleImageUpload}
268
+ accept="image/*"
269
+ className="hidden"
270
+ />
271
+ <button
272
+ onClick={() => fileInputRef.current?.click()}
273
+ className="btn btn-secondary"
274
+ >
275
+ 🖼️
276
+ </button>
277
+ <textarea
278
+ value={inputValue}
279
+ onChange={(e) => setInputValue(e.target.value)}
280
+ onKeyDown={(e) => {
281
+ if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
282
+ handleSend();
283
+ }
284
+ }}
285
+ placeholder="Type a message... (Cmd/Ctrl + Enter to send)"
286
+ className="input flex-1 resize-none"
287
+ rows={2}
288
+ />
289
+ <button onClick={handleSend} className="btn btn-primary px-6" disabled={loading}>
290
+ Send
291
+ </button>
292
+ </div>
293
+ </div>
294
+ </div>
295
+ );
296
+ }
297
+
298
+ function ChatMessageComponent({ message, tools }: { message: ChatMessage; tools: Tool[] }) {
299
+ if (message.role === 'tool') return null; // Don't render tool messages directly
300
+
301
+ const isUser = message.role === 'user';
302
+
303
+ return (
304
+ <div
305
+ className={`flex ${isUser ? 'justify-end' : 'justify-start'} animate-fade-in`}
306
+ >
307
+ <div className={`max-w-[70%] ${isUser ? 'bg-primary-600' : 'bg-dark-surface'} rounded-2xl p-4 border border-dark-border`}>
308
+ {message.image && (
309
+ <img
310
+ src={message.image.data}
311
+ alt={message.image.name}
312
+ className="rounded-lg mb-2 max-w-full"
313
+ />
314
+ )}
315
+ <p className="text-sm whitespace-pre-wrap">{message.content}</p>
316
+
317
+ {/* Tool Calls */}
318
+ {message.toolCalls && message.toolCalls.length > 0 && (
319
+ <div className="mt-3 space-y-2">
320
+ {message.toolCalls.map((toolCall) => (
321
+ <ToolCallComponent key={toolCall.id} toolCall={toolCall} tools={tools} />
322
+ ))}
323
+ </div>
324
+ )}
325
+ </div>
326
+ </div>
327
+ );
328
+ }
329
+
330
+ function ToolCallComponent({ toolCall, tools }: { toolCall: ToolCall; tools: Tool[] }) {
331
+ const tool = tools.find((t) => t.name === toolCall.name);
332
+
333
+ // Get widget URI from multiple possible sources
334
+ const componentUri =
335
+ tool?.widget?.route ||
336
+ tool?.outputTemplate ||
337
+ tool?._meta?.['openai/outputTemplate'] ||
338
+ tool?._meta?.['ui/template'];
339
+
340
+ // Get result data from toolCall and unwrap if needed
341
+ let widgetData = toolCall.result || toolCall.arguments;
342
+
343
+ // Unwrap if response was wrapped by TransformInterceptor
344
+ // Check if it has the interceptor's structure: { success, data, metadata }
345
+ if (widgetData && typeof widgetData === 'object' &&
346
+ widgetData.success !== undefined && widgetData.data !== undefined) {
347
+ widgetData = widgetData.data; // Return the unwrapped data
348
+ }
349
+
350
+ console.log('ToolCallComponent:', {
351
+ toolName: toolCall.name,
352
+ componentUri,
353
+ hasData: !!widgetData,
354
+ tool
355
+ });
356
+
357
+ return (
358
+ <div className="rounded-lg p-3 border" style={{
359
+ backgroundColor: 'hsl(var(--muted))',
360
+ borderColor: 'hsl(var(--border))'
361
+ }}>
362
+ <div className="flex items-center gap-2 mb-2">
363
+ <span>🔧</span>
364
+ <span className="font-medium text-sm">{toolCall.name}</span>
365
+ </div>
366
+
367
+ {/* Arguments */}
368
+ <details className="text-xs" style={{ color: 'hsl(var(--muted-foreground))' }}>
369
+ <summary className="cursor-pointer">Arguments</summary>
370
+ <pre className="mt-2 p-2 rounded overflow-auto" style={{
371
+ backgroundColor: 'hsl(var(--background))'
372
+ }}>
373
+ {JSON.stringify(toolCall.arguments, null, 2)}
374
+ </pre>
375
+ </details>
376
+
377
+ {/* Widget if available */}
378
+ {componentUri && widgetData && (
379
+ <div className="mt-3 rounded-lg overflow-hidden border" style={{
380
+ height: '320px',
381
+ borderColor: 'hsl(var(--border))',
382
+ backgroundColor: 'hsl(var(--background))'
383
+ }}>
384
+ <WidgetRenderer uri={componentUri} data={widgetData} />
385
+ </div>
386
+ )}
387
+ </div>
388
+ );
389
+ }
390
+