nitrostack 1.0.71 → 1.0.73

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 (253) hide show
  1. package/dist/auth/api-key.js.map +1 -1
  2. package/dist/auth/client.js.map +1 -1
  3. package/dist/auth/index.d.ts +2 -1
  4. package/dist/auth/index.d.ts.map +1 -1
  5. package/dist/auth/index.js +3 -0
  6. package/dist/auth/index.js.map +1 -1
  7. package/dist/auth/middleware.d.ts +1 -1
  8. package/dist/auth/middleware.d.ts.map +1 -1
  9. package/dist/auth/middleware.js.map +1 -1
  10. package/dist/auth/secure-secret.d.ts +136 -0
  11. package/dist/auth/secure-secret.d.ts.map +1 -0
  12. package/dist/auth/secure-secret.js +182 -0
  13. package/dist/auth/secure-secret.js.map +1 -0
  14. package/dist/auth/server-metadata.d.ts.map +1 -1
  15. package/dist/auth/server-metadata.js.map +1 -1
  16. package/dist/auth/simple-jwt.d.ts +100 -14
  17. package/dist/auth/simple-jwt.d.ts.map +1 -1
  18. package/dist/auth/simple-jwt.js +19 -9
  19. package/dist/auth/simple-jwt.js.map +1 -1
  20. package/dist/auth/token-store.js +1 -1
  21. package/dist/auth/token-store.js.map +1 -1
  22. package/dist/auth/token-validation.js +1 -1
  23. package/dist/auth/token-validation.js.map +1 -1
  24. package/dist/cli/commands/build.js +1 -1
  25. package/dist/cli/commands/build.js.map +1 -1
  26. package/dist/cli/commands/generate-types.js +12 -12
  27. package/dist/cli/commands/generate-types.js.map +1 -1
  28. package/dist/cli/commands/generate.d.ts +8 -1
  29. package/dist/cli/commands/generate.d.ts.map +1 -1
  30. package/dist/cli/commands/generate.js +13 -12
  31. package/dist/cli/commands/generate.js.map +1 -1
  32. package/dist/cli/commands/init.js +1 -1
  33. package/dist/cli/commands/init.js.map +1 -1
  34. package/dist/cli/commands/upgrade.d.ts +10 -0
  35. package/dist/cli/commands/upgrade.d.ts.map +1 -0
  36. package/dist/cli/commands/upgrade.js +221 -0
  37. package/dist/cli/commands/upgrade.js.map +1 -0
  38. package/dist/cli/index.js +7 -0
  39. package/dist/cli/index.js.map +1 -1
  40. package/dist/core/app-decorator.d.ts +4 -3
  41. package/dist/core/app-decorator.d.ts.map +1 -1
  42. package/dist/core/app-decorator.js +67 -28
  43. package/dist/core/app-decorator.js.map +1 -1
  44. package/dist/core/builders.d.ts +19 -7
  45. package/dist/core/builders.d.ts.map +1 -1
  46. package/dist/core/builders.js +15 -8
  47. package/dist/core/builders.js.map +1 -1
  48. package/dist/core/component.d.ts +8 -8
  49. package/dist/core/component.d.ts.map +1 -1
  50. package/dist/core/component.js +3 -2
  51. package/dist/core/component.js.map +1 -1
  52. package/dist/core/config-module.d.ts +11 -4
  53. package/dist/core/config-module.d.ts.map +1 -1
  54. package/dist/core/config-module.js +1 -1
  55. package/dist/core/config-module.js.map +1 -1
  56. package/dist/core/decorators/cache.decorator.d.ts +9 -9
  57. package/dist/core/decorators/cache.decorator.d.ts.map +1 -1
  58. package/dist/core/decorators/cache.decorator.js +3 -3
  59. package/dist/core/decorators/cache.decorator.js.map +1 -1
  60. package/dist/core/decorators/health-check.decorator.d.ts +3 -3
  61. package/dist/core/decorators/health-check.decorator.d.ts.map +1 -1
  62. package/dist/core/decorators/health-check.decorator.js +2 -2
  63. package/dist/core/decorators/health-check.decorator.js.map +1 -1
  64. package/dist/core/decorators/rate-limit.decorator.d.ts +5 -4
  65. package/dist/core/decorators/rate-limit.decorator.d.ts.map +1 -1
  66. package/dist/core/decorators/rate-limit.decorator.js +3 -3
  67. package/dist/core/decorators/rate-limit.decorator.js.map +1 -1
  68. package/dist/core/decorators.d.ts +47 -29
  69. package/dist/core/decorators.d.ts.map +1 -1
  70. package/dist/core/decorators.js +9 -9
  71. package/dist/core/decorators.js.map +1 -1
  72. package/dist/core/di/container.d.ts +21 -4
  73. package/dist/core/di/container.d.ts.map +1 -1
  74. package/dist/core/di/container.js +11 -7
  75. package/dist/core/di/container.js.map +1 -1
  76. package/dist/core/di/injectable.decorator.d.ts +5 -3
  77. package/dist/core/di/injectable.decorator.d.ts.map +1 -1
  78. package/dist/core/di/injectable.decorator.js.map +1 -1
  79. package/dist/core/errors.d.ts +4 -4
  80. package/dist/core/errors.d.ts.map +1 -1
  81. package/dist/core/errors.js.map +1 -1
  82. package/dist/core/events/event-emitter.d.ts +3 -3
  83. package/dist/core/events/event-emitter.d.ts.map +1 -1
  84. package/dist/core/events/event-emitter.js.map +1 -1
  85. package/dist/core/events/event.decorator.d.ts +5 -5
  86. package/dist/core/events/event.decorator.d.ts.map +1 -1
  87. package/dist/core/events/event.decorator.js +10 -6
  88. package/dist/core/events/event.decorator.js.map +1 -1
  89. package/dist/core/events/log-emitter.d.ts +7 -1
  90. package/dist/core/events/log-emitter.d.ts.map +1 -1
  91. package/dist/core/events/log-emitter.js.map +1 -1
  92. package/dist/core/filters/exception-filter.decorator.d.ts +5 -5
  93. package/dist/core/filters/exception-filter.decorator.d.ts.map +1 -1
  94. package/dist/core/filters/exception-filter.decorator.js +3 -3
  95. package/dist/core/filters/exception-filter.decorator.js.map +1 -1
  96. package/dist/core/filters/exception-filter.interface.d.ts +14 -5
  97. package/dist/core/filters/exception-filter.interface.d.ts.map +1 -1
  98. package/dist/core/guards/apikey.guard.d.ts +1 -1
  99. package/dist/core/guards/apikey.guard.d.ts.map +1 -1
  100. package/dist/core/guards/guard.interface.d.ts +1 -1
  101. package/dist/core/guards/guard.interface.d.ts.map +1 -1
  102. package/dist/core/guards/jwt.guard.d.ts +1 -1
  103. package/dist/core/guards/jwt.guard.d.ts.map +1 -1
  104. package/dist/core/guards/oauth.guard.d.ts +1 -1
  105. package/dist/core/guards/oauth.guard.d.ts.map +1 -1
  106. package/dist/core/guards/use-guards.decorator.d.ts +3 -3
  107. package/dist/core/guards/use-guards.decorator.d.ts.map +1 -1
  108. package/dist/core/guards/use-guards.decorator.js +1 -1
  109. package/dist/core/guards/use-guards.decorator.js.map +1 -1
  110. package/dist/core/index.d.ts +2 -2
  111. package/dist/core/index.d.ts.map +1 -1
  112. package/dist/core/index.js.map +1 -1
  113. package/dist/core/interceptors/interceptor.decorator.d.ts +4 -4
  114. package/dist/core/interceptors/interceptor.decorator.d.ts.map +1 -1
  115. package/dist/core/interceptors/interceptor.decorator.js +2 -2
  116. package/dist/core/interceptors/interceptor.decorator.js.map +1 -1
  117. package/dist/core/interceptors/interceptor.interface.d.ts +3 -3
  118. package/dist/core/interceptors/interceptor.interface.d.ts.map +1 -1
  119. package/dist/core/logger.d.ts.map +1 -1
  120. package/dist/core/logger.js.map +1 -1
  121. package/dist/core/middleware/middleware.decorator.d.ts +4 -4
  122. package/dist/core/middleware/middleware.decorator.d.ts.map +1 -1
  123. package/dist/core/middleware/middleware.decorator.js +2 -2
  124. package/dist/core/middleware/middleware.decorator.js.map +1 -1
  125. package/dist/core/middleware/middleware.interface.d.ts +3 -3
  126. package/dist/core/middleware/middleware.interface.d.ts.map +1 -1
  127. package/dist/core/module.d.ts +33 -14
  128. package/dist/core/module.d.ts.map +1 -1
  129. package/dist/core/module.js +11 -6
  130. package/dist/core/module.js.map +1 -1
  131. package/dist/core/oauth-module.d.ts +9 -3
  132. package/dist/core/oauth-module.d.ts.map +1 -1
  133. package/dist/core/oauth-module.js +4 -3
  134. package/dist/core/oauth-module.js.map +1 -1
  135. package/dist/core/pipes/pipe.decorator.d.ts +14 -5
  136. package/dist/core/pipes/pipe.decorator.d.ts.map +1 -1
  137. package/dist/core/pipes/pipe.decorator.js +2 -2
  138. package/dist/core/pipes/pipe.decorator.js.map +1 -1
  139. package/dist/core/pipes/pipe.interface.d.ts +9 -4
  140. package/dist/core/pipes/pipe.interface.d.ts.map +1 -1
  141. package/dist/core/prompt.d.ts +13 -4
  142. package/dist/core/prompt.d.ts.map +1 -1
  143. package/dist/core/prompt.js +2 -2
  144. package/dist/core/prompt.js.map +1 -1
  145. package/dist/core/resource.d.ts +7 -2
  146. package/dist/core/resource.d.ts.map +1 -1
  147. package/dist/core/resource.js +2 -2
  148. package/dist/core/resource.js.map +1 -1
  149. package/dist/core/server.d.ts +49 -3
  150. package/dist/core/server.d.ts.map +1 -1
  151. package/dist/core/server.js +61 -34
  152. package/dist/core/server.js.map +1 -1
  153. package/dist/core/tool.d.ts +44 -16
  154. package/dist/core/tool.d.ts.map +1 -1
  155. package/dist/core/tool.js +19 -6
  156. package/dist/core/tool.js.map +1 -1
  157. package/dist/core/transports/discovery-http-server.d.ts +7 -1
  158. package/dist/core/transports/discovery-http-server.d.ts.map +1 -1
  159. package/dist/core/transports/discovery-http-server.js.map +1 -1
  160. package/dist/core/transports/http-server.d.ts +2 -2
  161. package/dist/core/transports/http-server.d.ts.map +1 -1
  162. package/dist/core/transports/http-server.js +1 -1
  163. package/dist/core/transports/http-server.js.map +1 -1
  164. package/dist/core/transports/streamable-http.d.ts +4 -4
  165. package/dist/core/transports/streamable-http.d.ts.map +1 -1
  166. package/dist/core/transports/streamable-http.js +1 -1
  167. package/dist/core/transports/streamable-http.js.map +1 -1
  168. package/dist/core/types.d.ts +87 -15
  169. package/dist/core/types.d.ts.map +1 -1
  170. package/dist/core/widgets/widget-registry.d.ts +2 -2
  171. package/dist/core/widgets/widget-registry.d.ts.map +1 -1
  172. package/dist/core/widgets/widget-registry.js +1 -1
  173. package/dist/core/widgets/widget-registry.js.map +1 -1
  174. package/dist/testing/index.d.ts +44 -17
  175. package/dist/testing/index.d.ts.map +1 -1
  176. package/dist/testing/index.js +5 -8
  177. package/dist/testing/index.js.map +1 -1
  178. package/dist/ui-next/index.d.ts +1 -1
  179. package/dist/ui-next/index.d.ts.map +1 -1
  180. package/dist/ui-next/index.js.map +1 -1
  181. package/dist/widgets/hooks/useWidgetSDK.d.ts +5 -5
  182. package/dist/widgets/runtime/WidgetLayout.js.map +1 -1
  183. package/dist/widgets/sdk.d.ts +5 -5
  184. package/dist/widgets/sdk.d.ts.map +1 -1
  185. package/dist/widgets/sdk.js.map +1 -1
  186. package/package.json +1 -1
  187. package/src/studio/app/api/auth/fetch-metadata/route.ts +3 -2
  188. package/src/studio/app/api/auth/register-client/route.ts +3 -2
  189. package/src/studio/app/api/chat/route.ts +33 -17
  190. package/src/studio/app/api/health/checks/route.ts +5 -4
  191. package/src/studio/app/api/init/route.ts +3 -2
  192. package/src/studio/app/api/ping/route.ts +3 -2
  193. package/src/studio/app/api/prompts/[name]/route.ts +4 -3
  194. package/src/studio/app/api/prompts/route.ts +3 -2
  195. package/src/studio/app/api/resources/[...uri]/route.ts +3 -2
  196. package/src/studio/app/api/resources/route.ts +3 -2
  197. package/src/studio/app/api/roots/route.ts +3 -2
  198. package/src/studio/app/api/sampling/route.ts +3 -2
  199. package/src/studio/app/api/tools/[name]/call/route.ts +3 -2
  200. package/src/studio/app/api/tools/route.ts +4 -3
  201. package/src/studio/app/api/widget-examples/route.ts +5 -4
  202. package/src/studio/app/auth/callback/page.tsx +9 -8
  203. package/src/studio/app/chat/page.tsx +1535 -468
  204. package/src/studio/app/chat/page.tsx.backup +1046 -187
  205. package/src/studio/app/globals.css +361 -191
  206. package/src/studio/app/health/page.tsx +73 -77
  207. package/src/studio/app/layout.tsx +9 -11
  208. package/src/studio/app/logs/page.tsx +31 -32
  209. package/src/studio/app/page.tsx +136 -232
  210. package/src/studio/app/prompts/page.tsx +115 -97
  211. package/src/studio/app/resources/page.tsx +115 -124
  212. package/src/studio/app/settings/page.tsx +1083 -127
  213. package/src/studio/app/tools/page.tsx +343 -0
  214. package/src/studio/components/EnlargeModal.tsx +76 -65
  215. package/src/studio/components/LogMessage.tsx +6 -6
  216. package/src/studio/components/MarkdownRenderer.tsx +246 -349
  217. package/src/studio/components/Sidebar.tsx +165 -210
  218. package/src/studio/components/SplashScreen.tsx +109 -0
  219. package/src/studio/components/ToolCard.tsx +50 -41
  220. package/src/studio/components/VoiceOrbOverlay.tsx +475 -0
  221. package/src/studio/components/WidgetErrorBoundary.tsx +48 -0
  222. package/src/studio/components/WidgetRenderer.tsx +169 -211
  223. package/src/studio/components/ops/OpsCanvas.tsx +748 -0
  224. package/src/studio/components/ops/OpsNodeDetailPanel.tsx +150 -0
  225. package/src/studio/components/ops/OpsSummaryBar.tsx +90 -0
  226. package/src/studio/components/ops/index.ts +5 -0
  227. package/src/studio/components/ops/nodes/BaseNode.tsx +65 -0
  228. package/src/studio/components/ops/nodes/LLMCallNode.tsx +34 -0
  229. package/src/studio/components/ops/nodes/LLMResponseNode.tsx +33 -0
  230. package/src/studio/components/ops/nodes/ToolCallNode.tsx +30 -0
  231. package/src/studio/components/ops/nodes/ToolResultNode.tsx +43 -0
  232. package/src/studio/components/ops/nodes/UserPromptNode.tsx +34 -0
  233. package/src/studio/components/ops/nodes/WidgetRenderNode.tsx +23 -0
  234. package/src/studio/components/ops/nodes/index.ts +8 -0
  235. package/src/studio/components/tools/ToolsCanvas.tsx +327 -0
  236. package/src/studio/lib/api.ts +61 -42
  237. package/src/studio/lib/http-client-transport.ts +2 -2
  238. package/src/studio/lib/llm-service.ts +126 -47
  239. package/src/studio/lib/mcp-client.ts +9 -6
  240. package/src/studio/lib/ops-store.ts +427 -0
  241. package/src/studio/lib/ops-tracker.ts +416 -0
  242. package/src/studio/lib/ops-types.ts +164 -0
  243. package/src/studio/lib/store.ts +23 -11
  244. package/src/studio/lib/types.ts +228 -38
  245. package/src/studio/lib/widget-loader.ts +2 -2
  246. package/src/studio/package-lock.json +3303 -0
  247. package/src/studio/package.json +3 -1
  248. package/src/studio/public/NitroStudio Isotype Color.png +0 -0
  249. package/src/studio/tailwind.config.ts +63 -17
  250. package/templates/typescript-oauth/src/modules/flights/flights.prompts.ts +19 -22
  251. package/dist/cli/build-widgets.mjs +0 -165
  252. package/src/studio/app/auth/page.tsx +0 -560
  253. package/src/studio/app/ping/page.tsx +0 -209
@@ -0,0 +1,327 @@
1
+ 'use client';
2
+
3
+ import { useCallback, useEffect, useMemo } from 'react';
4
+ import ReactFlow, {
5
+ Background,
6
+ Controls,
7
+ Edge,
8
+ Node,
9
+ ReactFlowProvider,
10
+ useNodesState,
11
+ useEdgesState,
12
+ Handle,
13
+ Position,
14
+ MarkerType,
15
+ } from 'reactflow';
16
+ import 'reactflow/dist/style.css';
17
+ import { Tool, Resource, Prompt } from '@/lib/types';
18
+ import { WrenchScrewdriverIcon, BoltIcon, SparklesIcon, CubeIcon, DocumentTextIcon, GlobeAltIcon } from '@heroicons/react/24/outline';
19
+
20
+ interface ToolsCanvasProps {
21
+ tools: Tool[];
22
+ resources?: Resource[];
23
+ prompts?: Prompt[];
24
+ onToolClick?: (tool: Tool) => void;
25
+ onResourceClick?: (resource: Resource) => void;
26
+ onPromptClick?: (prompt: Prompt) => void;
27
+ }
28
+
29
+ // Custom Agent Node (Center)
30
+ const AgentNode = ({ data }: { data: { label: string } }) => {
31
+ return (
32
+ <div className="relative group">
33
+ {/* Glow Effect */}
34
+ <div className="absolute -inset-4 bg-primary/20 rounded-full blur-xl group-hover:bg-primary/30 transition-all duration-500"></div>
35
+
36
+ <div className="relative w-32 h-32 rounded-full border-2 border-primary bg-card/90 backdrop-blur-md shadow-2xl flex flex-col items-center justify-center z-10 animate-pulse-slow">
37
+ <div className="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center mb-2">
38
+ <CubeIcon className="w-7 h-7 text-primary" />
39
+ </div>
40
+ <div className="text-[10px] font-mono text-primary uppercase tracking-widest mb-1">AGENT</div>
41
+ <div className="text-xs font-bold text-foreground text-center px-4 leading-tight">{data.label}</div>
42
+
43
+ <Handle type="source" position={Position.Top} className="!bg-primary !w-3 !h-3 !border-2 !border-background" />
44
+ <Handle type="source" position={Position.Right} className="!bg-primary !w-3 !h-3 !border-2 !border-background" />
45
+ <Handle type="source" position={Position.Bottom} className="!bg-primary !w-3 !h-3 !border-2 !border-background" />
46
+ <Handle type="source" position={Position.Left} className="!bg-primary !w-3 !h-3 !border-2 !border-background" />
47
+ </div>
48
+ </div>
49
+ );
50
+ };
51
+
52
+ // Custom Tool Node (Satellite)
53
+ const ToolNode = ({ data }: { data: { tool: Tool } }) => {
54
+ const Icon = data.tool.name.toLowerCase().includes('run') || data.tool.name.toLowerCase().includes('exec')
55
+ ? BoltIcon
56
+ : data.tool.description?.toLowerCase().includes('ai')
57
+ ? SparklesIcon
58
+ : WrenchScrewdriverIcon;
59
+
60
+ return (
61
+ <div className="w-[200px] rounded-lg border border-teal-500/20 bg-card/80 backdrop-blur-sm shadow-xl p-4 transition-all duration-300 hover:border-teal-500/60 hover:shadow-teal-500/10 group cursor-pointer">
62
+ <Handle type="target" position={Position.Left} className="!bg-muted-foreground !w-2 !h-2" />
63
+
64
+ <div className="flex items-start gap-3">
65
+ <div className="w-8 h-8 rounded-lg bg-teal-500/10 flex items-center justify-center shrink-0 group-hover:bg-teal-500/20 transition-colors">
66
+ <Icon className="w-4 h-4 text-teal-500 group-hover:text-teal-400 transition-colors" />
67
+ </div>
68
+ <div>
69
+ <div className="text-[10px] font-mono text-teal-500 uppercase tracking-wider mb-0.5">TOOL</div>
70
+ <div className="font-semibold text-foreground text-sm line-clamp-1">{data.tool.name}</div>
71
+ <div className="text-xs text-muted-foreground line-clamp-2 mt-1">{data.tool.description}</div>
72
+ </div>
73
+ </div>
74
+
75
+ {/* Argument Pills */}
76
+ {data.tool.inputSchema?.properties && Object.keys(data.tool.inputSchema.properties).length > 0 && (
77
+ <div className="flex flex-wrap gap-1 mt-3">
78
+ {Object.keys(data.tool.inputSchema.properties).slice(0, 3).map(arg => (
79
+ <span key={arg} className="px-1.5 py-0.5 rounded text-[10px] bg-muted/50 text-muted-foreground border border-border">
80
+ {arg}
81
+ </span>
82
+ ))}
83
+ {Object.keys(data.tool.inputSchema.properties).length > 3 && (
84
+ <span className="px-1.5 py-0.5 rounded text-[10px] bg-muted/50 text-muted-foreground border border-border">+{Object.keys(data.tool.inputSchema.properties).length - 3}</span>
85
+ )}
86
+ </div>
87
+ )}
88
+ </div>
89
+ );
90
+ };
91
+
92
+ // Custom Resource Node
93
+ const ResourceNode = ({ data }: { data: { resource: Resource } }) => {
94
+ return (
95
+ <div className="w-[200px] rounded-lg border border-indigo-500/20 bg-card/80 backdrop-blur-sm shadow-xl p-4 transition-all duration-300 hover:border-indigo-500/60 hover:shadow-indigo-500/10 group cursor-pointer">
96
+ <Handle type="target" position={Position.Left} className="!bg-muted-foreground !w-2 !h-2" />
97
+
98
+ <div className="flex items-start gap-3">
99
+ <div className="w-8 h-8 rounded-lg bg-indigo-500/10 flex items-center justify-center shrink-0 group-hover:bg-indigo-500/20 transition-colors">
100
+ <CubeIcon className="w-4 h-4 text-indigo-500 group-hover:text-indigo-400 transition-colors" />
101
+ </div>
102
+ <div>
103
+ <div className="text-[10px] font-mono text-indigo-500 uppercase tracking-wider mb-0.5">RESOURCE</div>
104
+ <div className="font-semibold text-foreground text-sm line-clamp-1">{data.resource.name}</div>
105
+ <div className="text-xs text-muted-foreground line-clamp-2 mt-1">{data.resource.uri}</div>
106
+ </div>
107
+ </div>
108
+ <div className="flex flex-wrap gap-1 mt-3">
109
+ <span className="px-1.5 py-0.5 rounded text-[10px] bg-indigo-500/5 text-indigo-500 border border-indigo-500/10">
110
+ {data.resource.mimeType || 'text/plain'}
111
+ </span>
112
+ </div>
113
+ </div>
114
+ );
115
+ };
116
+
117
+ // Custom Prompt Node
118
+ const PromptNode = ({ data }: { data: { prompt: Prompt } }) => {
119
+ return (
120
+ <div className="w-[200px] rounded-lg border border-amber-500/20 bg-card/80 backdrop-blur-sm shadow-xl p-4 transition-all duration-300 hover:border-amber-500/60 hover:shadow-amber-500/10 group cursor-pointer">
121
+ <Handle type="target" position={Position.Left} className="!bg-muted-foreground !w-2 !h-2" />
122
+
123
+ <div className="flex items-start gap-3">
124
+ <div className="w-8 h-8 rounded-lg bg-amber-500/10 flex items-center justify-center shrink-0 group-hover:bg-amber-500/20 transition-colors">
125
+ <DocumentTextIcon className="w-4 h-4 text-amber-500 group-hover:text-amber-400 transition-colors" />
126
+ </div>
127
+ <div>
128
+ <div className="text-[10px] font-mono text-amber-500 uppercase tracking-wider mb-0.5">PROMPT</div>
129
+ <div className="font-semibold text-foreground text-sm line-clamp-1">{data.prompt.name}</div>
130
+ <div className="text-xs text-muted-foreground line-clamp-2 mt-1">{data.prompt.description || 'No description'}</div>
131
+ </div>
132
+ </div>
133
+
134
+ {/* Arguments */}
135
+ {data.prompt.arguments && data.prompt.arguments.length > 0 && (
136
+ <div className="flex flex-wrap gap-1 mt-3">
137
+ {data.prompt.arguments.slice(0, 3).map(arg => (
138
+ <span key={arg.name} className="px-1.5 py-0.5 rounded text-[10px] bg-amber-500/5 text-amber-500 border border-amber-500/10">
139
+ {arg.name}
140
+ </span>
141
+ ))}
142
+ {data.prompt.arguments.length > 3 && (
143
+ <span className="px-1.5 py-0.5 rounded text-[10px] bg-muted/50 text-muted-foreground border border-border">+{data.prompt.arguments.length - 3}</span>
144
+ )}
145
+ </div>
146
+ )}
147
+ </div>
148
+ );
149
+ };
150
+
151
+ const nodeTypes = {
152
+ agent: AgentNode,
153
+ tool: ToolNode,
154
+ resource: ResourceNode,
155
+ prompt: PromptNode,
156
+ };
157
+
158
+ export function ToolsCanvas({ tools, resources = [], prompts = [], onToolClick, onResourceClick, onPromptClick }: ToolsCanvasProps) {
159
+ const [nodes, setNodes, onNodesChange] = useNodesState([]);
160
+ const [edges, setEdges, onEdgesChange] = useEdgesState([]);
161
+
162
+ // Calculate Layout (Categorical Zones)
163
+ useEffect(() => {
164
+ const centerX = 400;
165
+ const centerY = 300;
166
+ const zoneRadius = 350; // Distance of zone centers from main center
167
+
168
+ const newNodes: Node[] = [
169
+ {
170
+ id: 'agent-root',
171
+ type: 'agent',
172
+ position: { x: centerX - 64, y: centerY - 64 }, // Center minus half width/height
173
+ data: { label: 'NitroStack Agent' },
174
+ },
175
+ ];
176
+
177
+ const newEdges: Edge[] = [];
178
+
179
+ // Helper to place items in a grid within a zone
180
+ const placeItemsInZone = (items: (Tool | Resource | Prompt)[], type: string, startX: number, startY: number, cols: number = 3) => {
181
+ items.forEach((item, index) => {
182
+ const col = index % cols;
183
+ const row = Math.floor(index / cols);
184
+
185
+ // Grid spacing
186
+ const gapX = 240;
187
+ const gapY = 150;
188
+
189
+ const x = startX + (col * gapX);
190
+ const y = startY + (row * gapY);
191
+
192
+ let nodeId = '';
193
+ let nodeData = {};
194
+ let strokeColor = '#52525b';
195
+
196
+ if (type === 'tool') {
197
+ const tool = item as Tool;
198
+ nodeId = `tool-${tool.name}`;
199
+ nodeData = { tool };
200
+ strokeColor = '#14b8a6'; // Teal
201
+ } else if (type === 'resource') {
202
+ const resource = item as Resource;
203
+ nodeId = `resource-${resource.uri}`;
204
+ nodeData = { resource };
205
+ strokeColor = '#6366f1'; // Indigo
206
+ } else if (type === 'prompt') {
207
+ const prompt = item as Prompt;
208
+ nodeId = `prompt-${prompt.name}`;
209
+ nodeData = { prompt };
210
+ strokeColor = '#f59e0b'; // Amber
211
+ }
212
+
213
+ newNodes.push({
214
+ id: nodeId,
215
+ type: type,
216
+ position: { x, y },
217
+ data: nodeData,
218
+ });
219
+
220
+ newEdges.push({
221
+ id: `e-root-${nodeId}`,
222
+ source: 'agent-root',
223
+ target: nodeId,
224
+ type: 'default',
225
+ animated: true,
226
+ style: { stroke: strokeColor, strokeWidth: 1.5, opacity: 0.3 },
227
+ markerEnd: {
228
+ type: MarkerType.ArrowClosed,
229
+ color: strokeColor,
230
+ }
231
+ });
232
+ });
233
+ }
234
+
235
+ // 1. Prompts (Top Left)
236
+ // Center of zone is approx (-zoneRadius, -zoneRadius) relative to main center
237
+ // We'll define start point for the grid
238
+ if (prompts && prompts.length > 0) {
239
+ const startX = centerX - 600;
240
+ const startY = centerY - 400;
241
+ placeItemsInZone(prompts, 'prompt', startX, startY, 2);
242
+ }
243
+
244
+ // 2. Resources (Top Right)
245
+ if (resources && resources.length > 0) {
246
+ const startX = centerX + 200;
247
+ const startY = centerY - 400;
248
+ placeItemsInZone(resources, 'resource', startX, startY, 2);
249
+ }
250
+
251
+ // 3. Tools (Bottom)
252
+ if (tools && tools.length > 0) {
253
+ // Center the tools grid at the bottom
254
+ const cols = 4;
255
+ const gridWidth = Math.min(tools.length, cols) * 240;
256
+ const startX = centerX - (gridWidth / 2) + 100; // Adjust to center
257
+ const startY = centerY + 200;
258
+ placeItemsInZone(tools, 'tool', startX, startY, cols);
259
+ }
260
+
261
+ setNodes(newNodes);
262
+ setEdges(newEdges);
263
+ }, [tools, resources, prompts, setNodes, setEdges]); // Re-calc when any data changes
264
+
265
+ const handleNodeClick = useCallback((event: React.MouseEvent, node: Node) => {
266
+ if (node.type === 'tool' && node.data.tool && onToolClick) {
267
+ onToolClick(node.data.tool);
268
+ } else if (node.type === 'resource' && node.data.resource && onResourceClick) {
269
+ onResourceClick(node.data.resource);
270
+ } else if (node.type === 'prompt' && node.data.prompt && onPromptClick) {
271
+ onPromptClick(node.data.prompt);
272
+ }
273
+ }, [onToolClick, onResourceClick, onPromptClick]);
274
+
275
+ return (
276
+ <div className="w-full h-full min-h-[600px] bg-[#09090b] rounded-xl overflow-hidden border border-border/50 shadow-inner relative group/canvas">
277
+ <ReactFlow
278
+ nodes={nodes}
279
+ edges={edges}
280
+ onNodesChange={onNodesChange}
281
+ onEdgesChange={onEdgesChange}
282
+ nodeTypes={nodeTypes}
283
+ onNodeClick={handleNodeClick}
284
+ fitView
285
+ className="bg-[#09090b]" // Zinc-950 dark background
286
+ >
287
+ <Background
288
+ color="#27272a" // Zinc-800 dots
289
+ gap={20}
290
+ size={1.5}
291
+ variant="dots"
292
+ />
293
+ <Controls
294
+ position="bottom-right"
295
+ showInteractive={false}
296
+ className="!flex !flex-row !gap-1 !p-1 !bg-card/90 !backdrop-blur !border !border-border !rounded-lg !shadow-lg !m-4"
297
+ />
298
+ </ReactFlow>
299
+
300
+ {/* Legend / Key */}
301
+ <div className="absolute top-4 left-4 flex gap-3 pointer-events-none">
302
+ <div className="bg-card/80 backdrop-blur px-3 py-1.5 rounded-lg border border-border text-xs text-muted-foreground">
303
+ Interactive Canvas
304
+ </div>
305
+ <div className="flex gap-2">
306
+ <span className="flex items-center gap-1.5 px-2 py-1 bg-teal-500/10 border border-teal-500/20 rounded-md text-[10px] text-teal-500 uppercase tracking-wide font-medium">
307
+ <WrenchScrewdriverIcon className="w-3 h-3" /> Tools
308
+ </span>
309
+ <span className="flex items-center gap-1.5 px-2 py-1 bg-indigo-500/10 border border-indigo-500/20 rounded-md text-[10px] text-indigo-500 uppercase tracking-wide font-medium">
310
+ <CubeIcon className="w-3 h-3" /> Resources
311
+ </span>
312
+ <span className="flex items-center gap-1.5 px-2 py-1 bg-amber-500/10 border border-amber-500/20 rounded-md text-[10px] text-amber-500 uppercase tracking-wide font-medium">
313
+ <DocumentTextIcon className="w-3 h-3" /> Prompts
314
+ </span>
315
+ </div>
316
+ </div>
317
+ </div>
318
+ );
319
+ }
320
+
321
+ export default function ToolsCanvasWrapper(props: ToolsCanvasProps) {
322
+ return (
323
+ <ReactFlowProvider>
324
+ <ToolsCanvas {...props} />
325
+ </ReactFlowProvider>
326
+ )
327
+ }
@@ -1,7 +1,26 @@
1
1
  // NitroStack Studio API Client
2
2
 
3
+ import type {
4
+ JsonValue,
5
+ ChatMessage,
6
+ PKCEParams,
7
+ ClientRegistration,
8
+ } from './types';
9
+
3
10
  const API_BASE = typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3000';
4
11
 
12
+ /**
13
+ * Client registration metadata for OAuth
14
+ */
15
+ interface ClientRegistrationMetadata {
16
+ client_name?: string;
17
+ redirect_uris: string[];
18
+ token_endpoint_auth_method?: string;
19
+ grant_types?: string[];
20
+ response_types?: string[];
21
+ [key: string]: JsonValue | undefined;
22
+ }
23
+
5
24
  export class StudioAPI {
6
25
  private baseUrl: string;
7
26
  private initialized: boolean = false;
@@ -11,7 +30,7 @@ export class StudioAPI {
11
30
  }
12
31
 
13
32
  // Initialize MCP connection (call once on app start)
14
- async initialize() {
33
+ async initialize(): Promise<void> {
15
34
  if (this.initialized) return;
16
35
  try {
17
36
  await fetch(`${this.baseUrl}/api/init`, { method: 'POST' });
@@ -22,111 +41,111 @@ export class StudioAPI {
22
41
  }
23
42
 
24
43
  // Connection
25
- async checkConnection() {
44
+ async checkConnection(): Promise<JsonValue> {
26
45
  const response = await fetch(`${this.baseUrl}/api/health`);
27
- return response.json();
46
+ return response.json() as Promise<JsonValue>;
28
47
  }
29
48
 
30
49
  // Tools
31
- async listTools() {
50
+ async listTools(): Promise<JsonValue> {
32
51
  const response = await fetch(`${this.baseUrl}/api/tools`);
33
- return response.json();
52
+ return response.json() as Promise<JsonValue>;
34
53
  }
35
54
 
36
- async getTools() {
55
+ async getTools(): Promise<JsonValue> {
37
56
  return this.listTools();
38
57
  }
39
58
 
40
- async callTool(name: string, args: any, jwtToken?: string, apiKey?: string) {
59
+ async callTool(name: string, args: JsonValue, jwtToken?: string, apiKey?: string): Promise<JsonValue> {
41
60
  const response = await fetch(`${this.baseUrl}/api/tools/${name}/call`, {
42
61
  method: 'POST',
43
62
  headers: { 'Content-Type': 'application/json' },
44
63
  body: JSON.stringify({ args, jwtToken, apiKey }),
45
64
  });
46
- return response.json();
65
+ return response.json() as Promise<JsonValue>;
47
66
  }
48
67
 
49
68
  // Resources
50
- async getResources() {
69
+ async getResources(): Promise<JsonValue> {
51
70
  const response = await fetch(`${this.baseUrl}/api/resources`);
52
- return response.json();
71
+ return response.json() as Promise<JsonValue>;
53
72
  }
54
73
 
55
- async getResource(uri: string) {
74
+ async getResource(uri: string): Promise<JsonValue> {
56
75
  const response = await fetch(`${this.baseUrl}/api/resources/${encodeURIComponent(uri)}`);
57
- return response.json();
76
+ return response.json() as Promise<JsonValue>;
58
77
  }
59
78
 
60
79
  // Prompts
61
- async getPrompts() {
80
+ async getPrompts(): Promise<JsonValue> {
62
81
  const response = await fetch(`${this.baseUrl}/api/prompts`);
63
- return response.json();
82
+ return response.json() as Promise<JsonValue>;
64
83
  }
65
84
 
66
- async executePrompt(name: string, args: any) {
85
+ async executePrompt(name: string, args: Record<string, string>): Promise<JsonValue> {
67
86
  const response = await fetch(`${this.baseUrl}/api/prompts/${name}`, {
68
87
  method: 'POST',
69
88
  headers: { 'Content-Type': 'application/json' },
70
89
  body: JSON.stringify(args),
71
90
  });
72
- return response.json();
91
+ return response.json() as Promise<JsonValue>;
73
92
  }
74
93
 
75
94
  // Ping
76
- async ping() {
95
+ async ping(): Promise<JsonValue> {
77
96
  const response = await fetch(`${this.baseUrl}/api/ping`);
78
- return response.json();
97
+ return response.json() as Promise<JsonValue>;
79
98
  }
80
99
 
81
100
  // Sampling
82
- async sample(params: { prompt: string; maxTokens: number; model?: string }) {
101
+ async sample(params: { prompt: string; maxTokens: number; model?: string }): Promise<JsonValue> {
83
102
  const response = await fetch(`${this.baseUrl}/api/sampling`, {
84
103
  method: 'POST',
85
104
  headers: { 'Content-Type': 'application/json' },
86
105
  body: JSON.stringify(params),
87
106
  });
88
- return response.json();
107
+ return response.json() as Promise<JsonValue>;
89
108
  }
90
109
 
91
110
  // Roots
92
- async getRoots() {
111
+ async getRoots(): Promise<JsonValue> {
93
112
  const response = await fetch(`${this.baseUrl}/api/roots`);
94
- return response.json();
113
+ return response.json() as Promise<JsonValue>;
95
114
  }
96
115
 
97
116
  // Chat
98
117
  async chat(params: {
99
118
  provider: string;
100
- messages: any[];
119
+ messages: ChatMessage[];
101
120
  apiKey: string; // LLM API key (OpenAI/Gemini)
102
121
  jwtToken?: string; // MCP server JWT token
103
122
  mcpApiKey?: string; // MCP server API key
104
- }) {
123
+ }): Promise<JsonValue> {
105
124
  const response = await fetch(`${this.baseUrl}/api/chat`, {
106
125
  method: 'POST',
107
126
  headers: { 'Content-Type': 'application/json' },
108
127
  body: JSON.stringify(params),
109
128
  });
110
- return response.json();
129
+ return response.json() as Promise<JsonValue>;
111
130
  }
112
131
 
113
132
  // Auth (OAuth 2.1)
114
- async discoverAuth(url: string, type: 'resource' | 'auth-server') {
133
+ async discoverAuth(url: string, type: 'resource' | 'auth-server'): Promise<JsonValue> {
115
134
  const response = await fetch(`${this.baseUrl}/api/auth/fetch-metadata`, {
116
135
  method: 'POST',
117
136
  headers: { 'Content-Type': 'application/json' },
118
137
  body: JSON.stringify({ url, type }),
119
138
  });
120
- return response.json();
139
+ return response.json() as Promise<JsonValue>;
121
140
  }
122
141
 
123
- async registerClient(endpoint: string, metadata: any) {
142
+ async registerClient(endpoint: string, metadata: ClientRegistrationMetadata): Promise<ClientRegistration> {
124
143
  const response = await fetch(`${this.baseUrl}/api/auth/register-client`, {
125
144
  method: 'POST',
126
145
  headers: { 'Content-Type': 'application/json' },
127
146
  body: JSON.stringify({ endpoint, metadata }),
128
147
  });
129
- return response.json();
148
+ return response.json() as Promise<ClientRegistration>;
130
149
  }
131
150
 
132
151
  async startOAuthFlow(params: {
@@ -135,30 +154,30 @@ export class StudioAPI {
135
154
  redirectUri: string;
136
155
  scope: string;
137
156
  resource: string;
138
- }) {
157
+ }): Promise<JsonValue> {
139
158
  const response = await fetch(`${this.baseUrl}/api/auth/start-flow`, {
140
159
  method: 'POST',
141
160
  headers: { 'Content-Type': 'application/json' },
142
161
  body: JSON.stringify(params),
143
162
  });
144
- return response.json();
163
+ return response.json() as Promise<JsonValue>;
145
164
  }
146
165
 
147
166
  async exchangeToken(params: {
148
167
  code: string;
149
- pkce: any;
168
+ pkce: PKCEParams;
150
169
  tokenEndpoint: string;
151
170
  clientId: string;
152
171
  clientSecret?: string;
153
172
  redirectUri: string;
154
173
  resource: string;
155
- }) {
174
+ }): Promise<JsonValue> {
156
175
  const response = await fetch(`${this.baseUrl}/api/auth/exchange-token`, {
157
176
  method: 'POST',
158
177
  headers: { 'Content-Type': 'application/json' },
159
178
  body: JSON.stringify(params),
160
179
  });
161
- return response.json();
180
+ return response.json() as Promise<JsonValue>;
162
181
  }
163
182
 
164
183
  async refreshToken(params: {
@@ -167,13 +186,13 @@ export class StudioAPI {
167
186
  clientId: string;
168
187
  clientSecret?: string;
169
188
  resource: string;
170
- }) {
189
+ }): Promise<JsonValue> {
171
190
  const response = await fetch(`${this.baseUrl}/api/auth/refresh-token`, {
172
191
  method: 'POST',
173
192
  headers: { 'Content-Type': 'application/json' },
174
193
  body: JSON.stringify(params),
175
194
  });
176
- return response.json();
195
+ return response.json() as Promise<JsonValue>;
177
196
  }
178
197
 
179
198
  async revokeToken(params: {
@@ -181,25 +200,25 @@ export class StudioAPI {
181
200
  revocationEndpoint: string;
182
201
  clientId: string;
183
202
  clientSecret?: string;
184
- }) {
203
+ }): Promise<JsonValue> {
185
204
  const response = await fetch(`${this.baseUrl}/api/auth/revoke-token`, {
186
205
  method: 'POST',
187
206
  headers: { 'Content-Type': 'application/json' },
188
207
  body: JSON.stringify(params),
189
208
  });
190
- return response.json();
209
+ return response.json() as Promise<JsonValue>;
191
210
  }
192
211
 
193
212
  // Health
194
- async getHealth() {
213
+ async getHealth(): Promise<JsonValue> {
195
214
  const response = await fetch(`${this.baseUrl}/api/health/checks`);
196
- return response.json();
215
+ return response.json() as Promise<JsonValue>;
197
216
  }
198
217
 
199
218
  // Widget Examples
200
- async getWidgetExamples() {
219
+ async getWidgetExamples(): Promise<JsonValue> {
201
220
  const response = await fetch(`${this.baseUrl}/api/widget-examples`);
202
- return response.json();
221
+ return response.json() as Promise<JsonValue>;
203
222
  }
204
223
  }
205
224
 
@@ -11,7 +11,7 @@ import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
11
11
  import { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
12
12
 
13
13
  // EventSource type that works in both browser and Node.js
14
- type EventSourceType = typeof EventSource extends { prototype: infer T } ? T : any;
14
+ type EventSourceType = typeof EventSource extends { prototype: infer T } ? T : EventSource;
15
15
 
16
16
  export interface HttpClientTransportOptions {
17
17
  /**
@@ -47,7 +47,7 @@ export class HttpClientTransport implements Transport {
47
47
  private closeHandler?: () => void;
48
48
  private errorHandler?: (error: Error) => void;
49
49
  private isConnected = false;
50
- private EventSourceImpl: any;
50
+ private EventSourceImpl: typeof EventSource;
51
51
 
52
52
  constructor(options: HttpClientTransportOptions) {
53
53
  this.baseUrl = options.baseUrl.replace(/\/$/, ''); // Remove trailing slash