nitrostack 1.0.70 → 1.0.72

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 (35) hide show
  1. package/package.json +1 -1
  2. package/src/studio/app/api/chat/route.ts +33 -15
  3. package/src/studio/app/auth/callback/page.tsx +6 -6
  4. package/src/studio/app/chat/page.tsx +1124 -415
  5. package/src/studio/app/chat/page.tsx.backup +1046 -187
  6. package/src/studio/app/globals.css +361 -191
  7. package/src/studio/app/health/page.tsx +72 -76
  8. package/src/studio/app/layout.tsx +9 -11
  9. package/src/studio/app/logs/page.tsx +29 -30
  10. package/src/studio/app/page.tsx +134 -230
  11. package/src/studio/app/prompts/page.tsx +115 -97
  12. package/src/studio/app/resources/page.tsx +115 -124
  13. package/src/studio/app/settings/page.tsx +1080 -125
  14. package/src/studio/app/tools/page.tsx +343 -0
  15. package/src/studio/components/EnlargeModal.tsx +76 -65
  16. package/src/studio/components/LogMessage.tsx +5 -5
  17. package/src/studio/components/MarkdownRenderer.tsx +4 -4
  18. package/src/studio/components/Sidebar.tsx +150 -210
  19. package/src/studio/components/SplashScreen.tsx +109 -0
  20. package/src/studio/components/ToolCard.tsx +50 -41
  21. package/src/studio/components/VoiceOrbOverlay.tsx +469 -0
  22. package/src/studio/components/WidgetRenderer.tsx +8 -3
  23. package/src/studio/components/tools/ToolsCanvas.tsx +327 -0
  24. package/src/studio/lib/llm-service.ts +104 -1
  25. package/src/studio/lib/store.ts +36 -21
  26. package/src/studio/lib/types.ts +1 -1
  27. package/src/studio/package-lock.json +3303 -0
  28. package/src/studio/package.json +3 -1
  29. package/src/studio/public/NitroStudio Isotype Color.png +0 -0
  30. package/src/studio/tailwind.config.ts +63 -17
  31. package/templates/typescript-starter/package-lock.json +4112 -0
  32. package/templates/typescript-starter/package.json +2 -3
  33. package/templates/typescript-starter/src/modules/calculator/calculator.tools.ts +100 -5
  34. package/src/studio/app/auth/page.tsx +0 -560
  35. package/src/studio/app/ping/page.tsx +0 -209
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nitrostack",
3
- "version": "1.0.70",
3
+ "version": "1.0.72",
4
4
  "description": "NitroStack - Build powerful MCP servers with TypeScript",
5
5
  "type": "module",
6
6
  "main": "dist/core/index.js",
@@ -14,8 +14,8 @@ export async function POST(request: NextRequest) {
14
14
  mcpApiKey?: string; // MCP server API key
15
15
  };
16
16
 
17
- console.log('Received chat request:', {
18
- provider,
17
+ console.log('Received chat request:', {
18
+ provider,
19
19
  messagesCount: messages?.length,
20
20
  messages: JSON.stringify(messages),
21
21
  hasApiKey: !!apiKey,
@@ -30,7 +30,7 @@ export async function POST(request: NextRequest) {
30
30
  { status: 400 }
31
31
  );
32
32
  }
33
-
33
+
34
34
  if (messages.length === 0) {
35
35
  return NextResponse.json(
36
36
  { error: 'Messages array is empty' },
@@ -46,13 +46,13 @@ export async function POST(request: NextRequest) {
46
46
  description: tool.description || '',
47
47
  inputSchema: tool.inputSchema || {},
48
48
  })) || [];
49
-
49
+
50
50
  // Add synthetic tools for prompts and resources
51
51
  const promptsList = await client.listPrompts().catch(() => ({ prompts: [] }));
52
52
  const resourcesList = await client.listResources().catch(() => ({ resources: [] }));
53
-
53
+
54
54
  const syntheticTools = [];
55
-
55
+
56
56
  // Add a tool to list available prompts
57
57
  if (promptsList.prompts && promptsList.prompts.length > 0) {
58
58
  syntheticTools.push({
@@ -63,7 +63,7 @@ export async function POST(request: NextRequest) {
63
63
  properties: {},
64
64
  },
65
65
  });
66
-
66
+
67
67
  // Add a tool to execute prompts
68
68
  syntheticTools.push({
69
69
  name: 'execute_prompt',
@@ -84,7 +84,7 @@ export async function POST(request: NextRequest) {
84
84
  },
85
85
  });
86
86
  }
87
-
87
+
88
88
  // Add a tool to list available resources
89
89
  if (resourcesList.resources && resourcesList.resources.length > 0) {
90
90
  syntheticTools.push({
@@ -95,7 +95,7 @@ export async function POST(request: NextRequest) {
95
95
  properties: {},
96
96
  },
97
97
  });
98
-
98
+
99
99
  // Add a tool to read resources
100
100
  syntheticTools.push({
101
101
  name: 'read_resource',
@@ -112,7 +112,9 @@ export async function POST(request: NextRequest) {
112
112
  },
113
113
  });
114
114
  }
115
-
115
+
116
+
117
+
116
118
  const tools = [...mcpTools, ...syntheticTools];
117
119
 
118
120
  // Call LLM
@@ -126,7 +128,7 @@ export async function POST(request: NextRequest) {
126
128
  try {
127
129
  let result: any;
128
130
  let toolContent = '';
129
-
131
+
130
132
  // Handle synthetic tools
131
133
  if (toolCall.name === 'list_prompts') {
132
134
  const prompts = await client.listPrompts();
@@ -157,7 +159,7 @@ export async function POST(request: NextRequest) {
157
159
  } else if (toolCall.name === 'read_resource') {
158
160
  const resource = await client.readResource(toolCall.arguments.uri);
159
161
  // Extract the actual content from the resource
160
- let resourceContent = resource;
162
+ let resourceContent: any = resource;
161
163
  if (resource.contents && Array.isArray(resource.contents)) {
162
164
  // If it's an array of contents, extract text from each
163
165
  resourceContent = resource.contents.map((c: any) => {
@@ -172,21 +174,37 @@ export async function POST(request: NextRequest) {
172
174
  // Regular MCP tool execution
173
175
  // Inject auth tokens into tool arguments if available
174
176
  const toolArgs = { ...toolCall.arguments };
177
+
178
+ // Check if we have a file in the last user message and if this is a convert_temperature tool
179
+ if (toolCall.name === 'convert_temperature') {
180
+ const lastUserMessage = messages.slice().reverse().find(m => m.role === 'user');
181
+ if (lastUserMessage && lastUserMessage.file) {
182
+ // Inject file content if not already provided by LLM (or overwrite it to be safe)
183
+ console.log('📂 Injecting file content into convert_temperature tool call');
184
+ toolArgs.file_content = lastUserMessage.file.data;
185
+ toolArgs.file_type = lastUserMessage.file.type;
186
+
187
+ if (!toolArgs.file_name) {
188
+ toolArgs.file_name = lastUserMessage.file.name;
189
+ }
190
+ }
191
+ }
192
+
175
193
  if (jwtToken || mcpApiKey) {
176
194
  toolArgs._meta = {
177
195
  ...(toolArgs._meta || {}),
178
196
  };
179
-
197
+
180
198
  if (jwtToken) {
181
199
  toolArgs._meta._jwt = jwtToken;
182
200
  toolArgs._meta.authorization = `Bearer ${jwtToken}`;
183
201
  }
184
-
202
+
185
203
  if (mcpApiKey) {
186
204
  toolArgs._meta.apiKey = mcpApiKey;
187
205
  toolArgs._meta['x-api-key'] = mcpApiKey;
188
206
  }
189
-
207
+
190
208
  console.log(`🔐 Executing tool "${toolCall.name}" with auth:`, {
191
209
  hasJwt: !!jwtToken,
192
210
  hasMcpApiKey: !!mcpApiKey,
@@ -3,7 +3,7 @@
3
3
  import { useEffect, useState, Suspense } from 'react';
4
4
  import { useRouter, useSearchParams } from 'next/navigation';
5
5
  import { useStudioStore } from '@/lib/store';
6
- import { Loader2, CheckCircle2, XCircle } from 'lucide-react';
6
+ import { ArrowPathIcon, CheckCircleIcon, XCircleIcon } from '@heroicons/react/24/outline';
7
7
 
8
8
  function OAuthCallback() {
9
9
  const router = useRouter();
@@ -95,7 +95,7 @@ function OAuthCallback() {
95
95
  // Store access token as JWT token
96
96
  if (tokens.access_token) {
97
97
  setJwtToken(tokens.access_token);
98
-
98
+
99
99
  // Clean up session storage
100
100
  sessionStorage.removeItem('oauth_code_verifier');
101
101
  sessionStorage.removeItem('oauth_state');
@@ -126,7 +126,7 @@ function OAuthCallback() {
126
126
  <div className="card p-8 max-w-md w-full text-center">
127
127
  {status === 'processing' && (
128
128
  <>
129
- <Loader2 className="w-16 h-16 text-primary mx-auto mb-4 animate-spin" />
129
+ <ArrowPathIcon className="w-16 h-16 text-primary mx-auto mb-4 animate-spin" />
130
130
  <h1 className="text-2xl font-bold text-foreground mb-2">Processing...</h1>
131
131
  <p className="text-muted-foreground">{message}</p>
132
132
  </>
@@ -134,7 +134,7 @@ function OAuthCallback() {
134
134
 
135
135
  {status === 'success' && (
136
136
  <>
137
- <CheckCircle2 className="w-16 h-16 text-emerald-500 mx-auto mb-4" />
137
+ <CheckCircleIcon className="w-16 h-16 text-emerald-500 mx-auto mb-4" />
138
138
  <h1 className="text-2xl font-bold text-foreground mb-2">Success!</h1>
139
139
  <p className="text-muted-foreground">{message}</p>
140
140
  </>
@@ -142,7 +142,7 @@ function OAuthCallback() {
142
142
 
143
143
  {status === 'error' && (
144
144
  <>
145
- <XCircle className="w-16 h-16 text-red-500 mx-auto mb-4" />
145
+ <XCircleIcon className="w-16 h-16 text-red-500 mx-auto mb-4" />
146
146
  <h1 className="text-2xl font-bold text-foreground mb-2">Authorization Failed</h1>
147
147
  <p className="text-muted-foreground mb-4">{message}</p>
148
148
  <button
@@ -163,7 +163,7 @@ export default function AuthCallbackPage() {
163
163
  <Suspense fallback={
164
164
  <div className="fixed inset-0 flex items-center justify-center bg-background">
165
165
  <div className="text-center">
166
- <Loader2 className="w-8 h-8 text-primary animate-spin mx-auto mb-4" />
166
+ <ArrowPathIcon className="w-8 h-8 text-primary animate-spin mx-auto mb-4" />
167
167
  <p className="text-muted-foreground">Loading...</p>
168
168
  </div>
169
169
  </div>