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.
- package/dist/auth/api-key.js.map +1 -1
- package/dist/auth/client.js.map +1 -1
- package/dist/auth/index.d.ts +2 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +3 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/middleware.d.ts +1 -1
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js.map +1 -1
- package/dist/auth/secure-secret.d.ts +136 -0
- package/dist/auth/secure-secret.d.ts.map +1 -0
- package/dist/auth/secure-secret.js +182 -0
- package/dist/auth/secure-secret.js.map +1 -0
- package/dist/auth/server-metadata.d.ts.map +1 -1
- package/dist/auth/server-metadata.js.map +1 -1
- package/dist/auth/simple-jwt.d.ts +100 -14
- package/dist/auth/simple-jwt.d.ts.map +1 -1
- package/dist/auth/simple-jwt.js +19 -9
- package/dist/auth/simple-jwt.js.map +1 -1
- package/dist/auth/token-store.js +1 -1
- package/dist/auth/token-store.js.map +1 -1
- package/dist/auth/token-validation.js +1 -1
- package/dist/auth/token-validation.js.map +1 -1
- package/dist/cli/commands/build.js +1 -1
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/generate-types.js +12 -12
- package/dist/cli/commands/generate-types.js.map +1 -1
- package/dist/cli/commands/generate.d.ts +8 -1
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +13 -12
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/init.js +1 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/upgrade.d.ts +10 -0
- package/dist/cli/commands/upgrade.d.ts.map +1 -0
- package/dist/cli/commands/upgrade.js +221 -0
- package/dist/cli/commands/upgrade.js.map +1 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/app-decorator.d.ts +4 -3
- package/dist/core/app-decorator.d.ts.map +1 -1
- package/dist/core/app-decorator.js +67 -28
- package/dist/core/app-decorator.js.map +1 -1
- package/dist/core/builders.d.ts +19 -7
- package/dist/core/builders.d.ts.map +1 -1
- package/dist/core/builders.js +15 -8
- package/dist/core/builders.js.map +1 -1
- package/dist/core/component.d.ts +8 -8
- package/dist/core/component.d.ts.map +1 -1
- package/dist/core/component.js +3 -2
- package/dist/core/component.js.map +1 -1
- package/dist/core/config-module.d.ts +11 -4
- package/dist/core/config-module.d.ts.map +1 -1
- package/dist/core/config-module.js +1 -1
- package/dist/core/config-module.js.map +1 -1
- package/dist/core/decorators/cache.decorator.d.ts +9 -9
- package/dist/core/decorators/cache.decorator.d.ts.map +1 -1
- package/dist/core/decorators/cache.decorator.js +3 -3
- package/dist/core/decorators/cache.decorator.js.map +1 -1
- package/dist/core/decorators/health-check.decorator.d.ts +3 -3
- package/dist/core/decorators/health-check.decorator.d.ts.map +1 -1
- package/dist/core/decorators/health-check.decorator.js +2 -2
- package/dist/core/decorators/health-check.decorator.js.map +1 -1
- package/dist/core/decorators/rate-limit.decorator.d.ts +5 -4
- package/dist/core/decorators/rate-limit.decorator.d.ts.map +1 -1
- package/dist/core/decorators/rate-limit.decorator.js +3 -3
- package/dist/core/decorators/rate-limit.decorator.js.map +1 -1
- package/dist/core/decorators.d.ts +47 -29
- package/dist/core/decorators.d.ts.map +1 -1
- package/dist/core/decorators.js +9 -9
- package/dist/core/decorators.js.map +1 -1
- package/dist/core/di/container.d.ts +21 -4
- package/dist/core/di/container.d.ts.map +1 -1
- package/dist/core/di/container.js +11 -7
- package/dist/core/di/container.js.map +1 -1
- package/dist/core/di/injectable.decorator.d.ts +5 -3
- package/dist/core/di/injectable.decorator.d.ts.map +1 -1
- package/dist/core/di/injectable.decorator.js.map +1 -1
- package/dist/core/errors.d.ts +4 -4
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js.map +1 -1
- package/dist/core/events/event-emitter.d.ts +3 -3
- package/dist/core/events/event-emitter.d.ts.map +1 -1
- package/dist/core/events/event-emitter.js.map +1 -1
- package/dist/core/events/event.decorator.d.ts +5 -5
- package/dist/core/events/event.decorator.d.ts.map +1 -1
- package/dist/core/events/event.decorator.js +10 -6
- package/dist/core/events/event.decorator.js.map +1 -1
- package/dist/core/events/log-emitter.d.ts +7 -1
- package/dist/core/events/log-emitter.d.ts.map +1 -1
- package/dist/core/events/log-emitter.js.map +1 -1
- package/dist/core/filters/exception-filter.decorator.d.ts +5 -5
- package/dist/core/filters/exception-filter.decorator.d.ts.map +1 -1
- package/dist/core/filters/exception-filter.decorator.js +3 -3
- package/dist/core/filters/exception-filter.decorator.js.map +1 -1
- package/dist/core/filters/exception-filter.interface.d.ts +14 -5
- package/dist/core/filters/exception-filter.interface.d.ts.map +1 -1
- package/dist/core/guards/apikey.guard.d.ts +1 -1
- package/dist/core/guards/apikey.guard.d.ts.map +1 -1
- package/dist/core/guards/guard.interface.d.ts +1 -1
- package/dist/core/guards/guard.interface.d.ts.map +1 -1
- package/dist/core/guards/jwt.guard.d.ts +1 -1
- package/dist/core/guards/jwt.guard.d.ts.map +1 -1
- package/dist/core/guards/oauth.guard.d.ts +1 -1
- package/dist/core/guards/oauth.guard.d.ts.map +1 -1
- package/dist/core/guards/use-guards.decorator.d.ts +3 -3
- package/dist/core/guards/use-guards.decorator.d.ts.map +1 -1
- package/dist/core/guards/use-guards.decorator.js +1 -1
- package/dist/core/guards/use-guards.decorator.js.map +1 -1
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/interceptors/interceptor.decorator.d.ts +4 -4
- package/dist/core/interceptors/interceptor.decorator.d.ts.map +1 -1
- package/dist/core/interceptors/interceptor.decorator.js +2 -2
- package/dist/core/interceptors/interceptor.decorator.js.map +1 -1
- package/dist/core/interceptors/interceptor.interface.d.ts +3 -3
- package/dist/core/interceptors/interceptor.interface.d.ts.map +1 -1
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js.map +1 -1
- package/dist/core/middleware/middleware.decorator.d.ts +4 -4
- package/dist/core/middleware/middleware.decorator.d.ts.map +1 -1
- package/dist/core/middleware/middleware.decorator.js +2 -2
- package/dist/core/middleware/middleware.decorator.js.map +1 -1
- package/dist/core/middleware/middleware.interface.d.ts +3 -3
- package/dist/core/middleware/middleware.interface.d.ts.map +1 -1
- package/dist/core/module.d.ts +33 -14
- package/dist/core/module.d.ts.map +1 -1
- package/dist/core/module.js +11 -6
- package/dist/core/module.js.map +1 -1
- package/dist/core/oauth-module.d.ts +9 -3
- package/dist/core/oauth-module.d.ts.map +1 -1
- package/dist/core/oauth-module.js +4 -3
- package/dist/core/oauth-module.js.map +1 -1
- package/dist/core/pipes/pipe.decorator.d.ts +14 -5
- package/dist/core/pipes/pipe.decorator.d.ts.map +1 -1
- package/dist/core/pipes/pipe.decorator.js +2 -2
- package/dist/core/pipes/pipe.decorator.js.map +1 -1
- package/dist/core/pipes/pipe.interface.d.ts +9 -4
- package/dist/core/pipes/pipe.interface.d.ts.map +1 -1
- package/dist/core/prompt.d.ts +13 -4
- package/dist/core/prompt.d.ts.map +1 -1
- package/dist/core/prompt.js +2 -2
- package/dist/core/prompt.js.map +1 -1
- package/dist/core/resource.d.ts +7 -2
- package/dist/core/resource.d.ts.map +1 -1
- package/dist/core/resource.js +2 -2
- package/dist/core/resource.js.map +1 -1
- package/dist/core/server.d.ts +49 -3
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +61 -34
- package/dist/core/server.js.map +1 -1
- package/dist/core/tool.d.ts +44 -16
- package/dist/core/tool.d.ts.map +1 -1
- package/dist/core/tool.js +19 -6
- package/dist/core/tool.js.map +1 -1
- package/dist/core/transports/discovery-http-server.d.ts +7 -1
- package/dist/core/transports/discovery-http-server.d.ts.map +1 -1
- package/dist/core/transports/discovery-http-server.js.map +1 -1
- package/dist/core/transports/http-server.d.ts +2 -2
- package/dist/core/transports/http-server.d.ts.map +1 -1
- package/dist/core/transports/http-server.js +1 -1
- package/dist/core/transports/http-server.js.map +1 -1
- package/dist/core/transports/streamable-http.d.ts +4 -4
- package/dist/core/transports/streamable-http.d.ts.map +1 -1
- package/dist/core/transports/streamable-http.js +1 -1
- package/dist/core/transports/streamable-http.js.map +1 -1
- package/dist/core/types.d.ts +87 -15
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/widgets/widget-registry.d.ts +2 -2
- package/dist/core/widgets/widget-registry.d.ts.map +1 -1
- package/dist/core/widgets/widget-registry.js +1 -1
- package/dist/core/widgets/widget-registry.js.map +1 -1
- package/dist/testing/index.d.ts +44 -17
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +5 -8
- package/dist/testing/index.js.map +1 -1
- package/dist/ui-next/index.d.ts +1 -1
- package/dist/ui-next/index.d.ts.map +1 -1
- package/dist/ui-next/index.js.map +1 -1
- package/dist/widgets/hooks/useWidgetSDK.d.ts +5 -5
- package/dist/widgets/runtime/WidgetLayout.js.map +1 -1
- package/dist/widgets/sdk.d.ts +5 -5
- package/dist/widgets/sdk.d.ts.map +1 -1
- package/dist/widgets/sdk.js.map +1 -1
- package/package.json +1 -1
- package/src/studio/app/api/auth/fetch-metadata/route.ts +3 -2
- package/src/studio/app/api/auth/register-client/route.ts +3 -2
- package/src/studio/app/api/chat/route.ts +33 -17
- package/src/studio/app/api/health/checks/route.ts +5 -4
- package/src/studio/app/api/init/route.ts +3 -2
- package/src/studio/app/api/ping/route.ts +3 -2
- package/src/studio/app/api/prompts/[name]/route.ts +4 -3
- package/src/studio/app/api/prompts/route.ts +3 -2
- package/src/studio/app/api/resources/[...uri]/route.ts +3 -2
- package/src/studio/app/api/resources/route.ts +3 -2
- package/src/studio/app/api/roots/route.ts +3 -2
- package/src/studio/app/api/sampling/route.ts +3 -2
- package/src/studio/app/api/tools/[name]/call/route.ts +3 -2
- package/src/studio/app/api/tools/route.ts +4 -3
- package/src/studio/app/api/widget-examples/route.ts +5 -4
- package/src/studio/app/auth/callback/page.tsx +9 -8
- package/src/studio/app/chat/page.tsx +1535 -468
- package/src/studio/app/chat/page.tsx.backup +1046 -187
- package/src/studio/app/globals.css +361 -191
- package/src/studio/app/health/page.tsx +73 -77
- package/src/studio/app/layout.tsx +9 -11
- package/src/studio/app/logs/page.tsx +31 -32
- package/src/studio/app/page.tsx +136 -232
- package/src/studio/app/prompts/page.tsx +115 -97
- package/src/studio/app/resources/page.tsx +115 -124
- package/src/studio/app/settings/page.tsx +1083 -127
- package/src/studio/app/tools/page.tsx +343 -0
- package/src/studio/components/EnlargeModal.tsx +76 -65
- package/src/studio/components/LogMessage.tsx +6 -6
- package/src/studio/components/MarkdownRenderer.tsx +246 -349
- package/src/studio/components/Sidebar.tsx +165 -210
- package/src/studio/components/SplashScreen.tsx +109 -0
- package/src/studio/components/ToolCard.tsx +50 -41
- package/src/studio/components/VoiceOrbOverlay.tsx +475 -0
- package/src/studio/components/WidgetErrorBoundary.tsx +48 -0
- package/src/studio/components/WidgetRenderer.tsx +169 -211
- package/src/studio/components/ops/OpsCanvas.tsx +748 -0
- package/src/studio/components/ops/OpsNodeDetailPanel.tsx +150 -0
- package/src/studio/components/ops/OpsSummaryBar.tsx +90 -0
- package/src/studio/components/ops/index.ts +5 -0
- package/src/studio/components/ops/nodes/BaseNode.tsx +65 -0
- package/src/studio/components/ops/nodes/LLMCallNode.tsx +34 -0
- package/src/studio/components/ops/nodes/LLMResponseNode.tsx +33 -0
- package/src/studio/components/ops/nodes/ToolCallNode.tsx +30 -0
- package/src/studio/components/ops/nodes/ToolResultNode.tsx +43 -0
- package/src/studio/components/ops/nodes/UserPromptNode.tsx +34 -0
- package/src/studio/components/ops/nodes/WidgetRenderNode.tsx +23 -0
- package/src/studio/components/ops/nodes/index.ts +8 -0
- package/src/studio/components/tools/ToolsCanvas.tsx +327 -0
- package/src/studio/lib/api.ts +61 -42
- package/src/studio/lib/http-client-transport.ts +2 -2
- package/src/studio/lib/llm-service.ts +126 -47
- package/src/studio/lib/mcp-client.ts +9 -6
- package/src/studio/lib/ops-store.ts +427 -0
- package/src/studio/lib/ops-tracker.ts +416 -0
- package/src/studio/lib/ops-types.ts +164 -0
- package/src/studio/lib/store.ts +23 -11
- package/src/studio/lib/types.ts +228 -38
- package/src/studio/lib/widget-loader.ts +2 -2
- package/src/studio/package-lock.json +3303 -0
- package/src/studio/package.json +3 -1
- package/src/studio/public/NitroStudio Isotype Color.png +0 -0
- package/src/studio/tailwind.config.ts +63 -17
- package/templates/typescript-oauth/src/modules/flights/flights.prompts.ts +19 -22
- package/dist/cli/build-widgets.mjs +0 -165
- package/src/studio/app/auth/page.tsx +0 -560
- package/src/studio/app/ping/page.tsx +0 -209
package/src/studio/app/page.tsx
CHANGED
|
@@ -3,53 +3,68 @@
|
|
|
3
3
|
import { useEffect, useState } from 'react';
|
|
4
4
|
import { useStudioStore } from '@/lib/store';
|
|
5
5
|
import { api } from '@/lib/api';
|
|
6
|
-
import { ToolCard } from '@/components/ToolCard';
|
|
7
|
-
import { WidgetRenderer } from '@/components/WidgetRenderer';
|
|
8
6
|
import type { Tool } from '@/lib/types';
|
|
9
|
-
import
|
|
7
|
+
import dynamic from 'next/dynamic';
|
|
8
|
+
import { ArrowPathIcon, WrenchScrewdriverIcon, XMarkIcon, PlayIcon, ChevronDownIcon } from '@heroicons/react/24/outline';
|
|
9
|
+
import { WidgetRenderer } from '@/components/WidgetRenderer';
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
// Dynamic import for ToolsCanvas
|
|
13
|
+
const ToolsCanvas = dynamic(() => import('@/components/tools/ToolsCanvas').then(mod => mod.default), {
|
|
14
|
+
ssr: false,
|
|
15
|
+
loading: () => <div className="w-full h-[600px] bg-muted/10 animate-pulse rounded-xl" />
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export default function AppPage() {
|
|
19
|
+
const {
|
|
20
|
+
tools, setTools,
|
|
21
|
+
resources, setResources,
|
|
22
|
+
prompts, setPrompts,
|
|
23
|
+
loading, setLoading,
|
|
24
|
+
connection, setConnection,
|
|
25
|
+
jwtToken, apiKey, oauthState
|
|
26
|
+
} = useStudioStore();
|
|
10
27
|
|
|
11
|
-
export default function ToolsPage() {
|
|
12
|
-
const { tools, setTools, loading, setLoading, connection, setConnection, jwtToken, apiKey, oauthState } = useStudioStore();
|
|
13
|
-
const [searchQuery, setSearchQuery] = useState('');
|
|
14
28
|
const [selectedTool, setSelectedTool] = useState<Tool | null>(null);
|
|
15
|
-
const [toolArgs, setToolArgs] = useState<Record<string,
|
|
16
|
-
const [toolResult, setToolResult] = useState<
|
|
29
|
+
const [toolArgs, setToolArgs] = useState<Record<string, unknown>>({});
|
|
30
|
+
const [toolResult, setToolResult] = useState<unknown>(null);
|
|
17
31
|
const [executingTool, setExecutingTool] = useState(false);
|
|
18
|
-
|
|
19
|
-
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
// Get effective token
|
|
20
36
|
const effectiveToken = jwtToken || oauthState?.currentToken;
|
|
21
37
|
|
|
22
|
-
// Initialize MCP, load tools and check connection on mount
|
|
23
38
|
useEffect(() => {
|
|
24
39
|
const init = async () => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
40
|
+
if (tools.length === 0 || resources.length === 0 || prompts.length === 0) {
|
|
41
|
+
await loadData();
|
|
42
|
+
}
|
|
28
43
|
};
|
|
29
44
|
init();
|
|
30
45
|
}, []);
|
|
31
46
|
|
|
32
|
-
const
|
|
33
|
-
try {
|
|
34
|
-
const health = await api.checkConnection();
|
|
35
|
-
setConnection({
|
|
36
|
-
connected: health.connected,
|
|
37
|
-
status: health.connected ? 'connected' : 'disconnected',
|
|
38
|
-
});
|
|
39
|
-
} catch (error) {
|
|
40
|
-
setConnection({ connected: false, status: 'disconnected' });
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const loadTools = async () => {
|
|
47
|
+
const loadData = async () => {
|
|
45
48
|
setLoading('tools', true);
|
|
49
|
+
setLoading('resources', true);
|
|
50
|
+
setLoading('prompts', true);
|
|
51
|
+
|
|
46
52
|
try {
|
|
47
|
-
const
|
|
48
|
-
|
|
53
|
+
const [toolsData, resourcesData, promptsData] = await Promise.all([
|
|
54
|
+
api.getTools().catch(() => ({ tools: [] })),
|
|
55
|
+
api.getResources().catch(() => ({ resources: [] })),
|
|
56
|
+
api.getPrompts().catch(() => ({ prompts: [] }))
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
setTools(toolsData.tools || []);
|
|
60
|
+
setResources(resourcesData.resources || []);
|
|
61
|
+
setPrompts(promptsData.prompts || []);
|
|
49
62
|
} catch (error) {
|
|
50
|
-
console.error('Failed to load
|
|
63
|
+
console.error('Failed to load data:', error);
|
|
51
64
|
} finally {
|
|
52
65
|
setLoading('tools', false);
|
|
66
|
+
setLoading('resources', false);
|
|
67
|
+
setLoading('prompts', false);
|
|
53
68
|
}
|
|
54
69
|
};
|
|
55
70
|
|
|
@@ -59,6 +74,15 @@ export default function ToolsPage() {
|
|
|
59
74
|
setToolResult(null);
|
|
60
75
|
};
|
|
61
76
|
|
|
77
|
+
const handleResourceClick = (resource: { name: string; uri: string }) => {
|
|
78
|
+
// For now just console log, or we could open a details modal
|
|
79
|
+
console.log('Resource clicked:', resource);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const handlePromptClick = (prompt: { name: string }) => {
|
|
83
|
+
console.log('Prompt clicked:', prompt);
|
|
84
|
+
};
|
|
85
|
+
|
|
62
86
|
const handleSubmitTool = async (e: React.FormEvent) => {
|
|
63
87
|
e.preventDefault();
|
|
64
88
|
if (!selectedTool) return;
|
|
@@ -67,26 +91,21 @@ export default function ToolsPage() {
|
|
|
67
91
|
setToolResult(null);
|
|
68
92
|
|
|
69
93
|
try {
|
|
70
|
-
// Pass JWT token and API key if available (check both jwtToken and OAuth token)
|
|
71
94
|
const result = await api.callTool(selectedTool.name, toolArgs, effectiveToken || undefined, apiKey || undefined);
|
|
72
95
|
setToolResult(result);
|
|
73
96
|
|
|
74
|
-
// Extract JWT token from ANY tool response (not just 'login')
|
|
75
|
-
// Check if result contains a token field at any level
|
|
76
97
|
if (result.content) {
|
|
77
98
|
try {
|
|
78
99
|
const content = result.content[0]?.text;
|
|
79
100
|
if (content) {
|
|
80
101
|
const parsed = JSON.parse(content);
|
|
81
|
-
// Check for token in multiple possible locations
|
|
82
102
|
const token = parsed.token || parsed.access_token || parsed.jwt || parsed.data?.token;
|
|
83
103
|
if (token) {
|
|
84
|
-
console.log('🔐 Token received from tool, saving to global state');
|
|
85
104
|
useStudioStore.getState().setJwtToken(token);
|
|
86
105
|
}
|
|
87
106
|
}
|
|
88
107
|
} catch (e) {
|
|
89
|
-
// Ignore
|
|
108
|
+
// Ignore
|
|
90
109
|
}
|
|
91
110
|
}
|
|
92
111
|
} catch (error) {
|
|
@@ -97,247 +116,131 @@ export default function ToolsPage() {
|
|
|
97
116
|
}
|
|
98
117
|
};
|
|
99
118
|
|
|
100
|
-
|
|
101
|
-
tool.name.toLowerCase().includes(searchQuery.toLowerCase())
|
|
102
|
-
);
|
|
119
|
+
|
|
103
120
|
|
|
104
121
|
return (
|
|
105
122
|
<>
|
|
106
123
|
<div className="fixed inset-0 flex flex-col bg-background" style={{ left: 'var(--sidebar-width, 15rem)' }}>
|
|
107
|
-
{/*
|
|
108
|
-
<div className="sticky top-0 z-10 border-b border-border/50 px-6 py-
|
|
124
|
+
{/* Minimal Professional Header */}
|
|
125
|
+
<div className="sticky top-0 z-10 border-b border-border/50 px-6 py-4 flex items-center justify-between bg-card/50 backdrop-blur-sm">
|
|
126
|
+
<div className="flex items-center gap-6">
|
|
127
|
+
<h1 className="text-lg font-semibold text-foreground">App Canvas</h1>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
109
130
|
<div className="flex items-center gap-3">
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
</div>
|
|
131
|
+
{/* Run App Button Removed */}
|
|
132
|
+
|
|
133
|
+
<button onClick={loadData} className="btn btn-ghost text-muted-foreground hover:text-foreground text-sm px-3 py-2 gap-2">
|
|
134
|
+
<ArrowPathIcon className="h-4 w-4" />
|
|
135
|
+
</button>
|
|
116
136
|
</div>
|
|
117
|
-
<button onClick={loadTools} className="btn btn-primary text-sm px-4 py-2 gap-2">
|
|
118
|
-
<RefreshCw className="w-4 h-4" />
|
|
119
|
-
Refresh
|
|
120
|
-
</button>
|
|
121
137
|
</div>
|
|
122
138
|
|
|
123
|
-
{/* Content -
|
|
124
|
-
<div className="flex-1 overflow-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
<input
|
|
129
|
-
type="text"
|
|
130
|
-
placeholder="Search tools..."
|
|
131
|
-
value={searchQuery}
|
|
132
|
-
onChange={(e) => setSearchQuery(e.target.value)}
|
|
133
|
-
className="input w-full"
|
|
134
|
-
/>
|
|
139
|
+
{/* Content - Canvas View */}
|
|
140
|
+
<div className="flex-1 relative overflow-hidden bg-[#09090b]">
|
|
141
|
+
{loading.tools || loading.resources || loading.prompts ? (
|
|
142
|
+
<div className="w-full h-full flex items-center justify-center">
|
|
143
|
+
<div className="w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin"></div>
|
|
135
144
|
</div>
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
<AlertCircle className="empty-state-icon" />
|
|
147
|
-
<p className="empty-state-title">
|
|
148
|
-
{searchQuery ? 'No tools found matching your search' : 'No tools available'}
|
|
149
|
-
</p>
|
|
150
|
-
<p className="empty-state-description">
|
|
151
|
-
{searchQuery ? 'Try a different search term' : 'No MCP tools have been registered'}
|
|
152
|
-
</p>
|
|
153
|
-
</div>
|
|
154
|
-
) : (
|
|
155
|
-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
156
|
-
{filteredTools.map((tool) => (
|
|
157
|
-
<ToolCard key={tool.name} tool={tool} onExecute={handleExecuteTool} />
|
|
158
|
-
))}
|
|
159
|
-
</div>
|
|
160
|
-
)}
|
|
161
|
-
</div>
|
|
145
|
+
) : (
|
|
146
|
+
<ToolsCanvas
|
|
147
|
+
tools={tools}
|
|
148
|
+
resources={resources}
|
|
149
|
+
prompts={prompts}
|
|
150
|
+
onToolClick={handleExecuteTool}
|
|
151
|
+
onResourceClick={handleResourceClick}
|
|
152
|
+
onPromptClick={handlePromptClick}
|
|
153
|
+
/>
|
|
154
|
+
)}
|
|
162
155
|
</div>
|
|
163
156
|
</div>
|
|
164
157
|
|
|
165
|
-
{/*
|
|
158
|
+
{/* Run App Modal - Removed */}
|
|
159
|
+
|
|
160
|
+
{/* Tool Executor Side Drawer - Reused for consistency */}
|
|
166
161
|
{selectedTool && (
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
<div className="flex items-center gap-3">
|
|
178
|
-
<div className="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center">
|
|
179
|
-
<Wrench className="w-5 h-5 text-primary" />
|
|
180
|
-
</div>
|
|
181
|
-
<h2 className="text-xl font-bold text-foreground">{selectedTool.name}</h2>
|
|
162
|
+
<div className="fixed inset-0 z-50 flex justify-end bg-background/80 backdrop-blur-sm animate-fade-in" onClick={() => setSelectedTool(null)}>
|
|
163
|
+
<div className="relative w-full max-w-2xl h-full bg-card border-l border-border shadow-2xl overflow-hidden animate-slide-in-right flex flex-col" onClick={(e) => e.stopPropagation()}>
|
|
164
|
+
<div className="flex items-center justify-between px-6 py-4 border-b border-border bg-card">
|
|
165
|
+
<div className="flex items-center gap-3">
|
|
166
|
+
<div className="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center">
|
|
167
|
+
<WrenchScrewdriverIcon className="w-5 h-5 text-primary" />
|
|
168
|
+
</div>
|
|
169
|
+
<div>
|
|
170
|
+
<h2 className="text-lg font-bold text-foreground">{selectedTool.name}</h2>
|
|
171
|
+
<p className="text-xs text-muted-foreground">Tool Executor</p>
|
|
182
172
|
</div>
|
|
183
|
-
<button
|
|
184
|
-
onClick={() => setSelectedTool(null)}
|
|
185
|
-
className="btn btn-ghost w-10 h-10 p-0"
|
|
186
|
-
>
|
|
187
|
-
<X className="w-5 h-5" />
|
|
188
|
-
</button>
|
|
189
173
|
</div>
|
|
174
|
+
<button onClick={() => setSelectedTool(null)} className="btn btn-ghost w-8 h-8 p-0 flex items-center justify-center rounded-full hover:bg-muted" aria-label="Close">
|
|
175
|
+
<XMarkIcon className="w-5 h-5" />
|
|
176
|
+
</button>
|
|
177
|
+
</div>
|
|
190
178
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
179
|
+
<div className="flex-1 overflow-y-auto bg-muted/5 p-6">
|
|
180
|
+
<div className="bg-card p-4 rounded-xl border border-border mb-6">
|
|
181
|
+
<p className="text-sm text-foreground leading-relaxed">{selectedTool.description || 'No description available'}</p>
|
|
182
|
+
</div>
|
|
194
183
|
|
|
195
|
-
<form onSubmit={handleSubmitTool}>
|
|
196
|
-
{/* Generate form inputs from schema */}
|
|
184
|
+
<form onSubmit={handleSubmitTool} className="space-y-6">
|
|
197
185
|
{selectedTool.inputSchema?.properties && typeof selectedTool.inputSchema.properties === 'object' && Object.keys(selectedTool.inputSchema.properties).length > 0 ? (
|
|
198
|
-
|
|
186
|
+
<div className="space-y-4">
|
|
199
187
|
{Object.entries(selectedTool.inputSchema.properties).map(([key, prop]: [string, any]) => {
|
|
200
188
|
const isRequired = selectedTool.inputSchema?.required?.includes(key);
|
|
201
|
-
|
|
202
|
-
// Handle different input types
|
|
203
189
|
if (prop.enum) {
|
|
204
|
-
// Enum/Select field
|
|
205
190
|
return (
|
|
206
|
-
<div key={key}
|
|
207
|
-
<label className="block text-sm font-medium text-foreground mb-2">
|
|
208
|
-
|
|
209
|
-
{isRequired && <span className="text-destructive ml-1">*</span>}
|
|
210
|
-
</label>
|
|
211
|
-
<select
|
|
212
|
-
className="input"
|
|
213
|
-
value={toolArgs[key] || prop.default || ''}
|
|
214
|
-
onChange={(e) => setToolArgs({ ...toolArgs, [key]: e.target.value })}
|
|
215
|
-
required={isRequired}
|
|
216
|
-
>
|
|
191
|
+
<div key={key}>
|
|
192
|
+
<label className="block text-sm font-medium text-foreground mb-2">{prop.title || key}{isRequired && <span className="text-destructive ml-1">*</span>}</label>
|
|
193
|
+
<select className="input w-full" value={toolArgs[key] || prop.default || ''} onChange={(e) => setToolArgs({ ...toolArgs, [key]: e.target.value })} required={isRequired}>
|
|
217
194
|
<option value="">Select...</option>
|
|
218
|
-
{prop.enum.map((val
|
|
219
|
-
<option key={val} value={val}>
|
|
220
|
-
{val}
|
|
221
|
-
</option>
|
|
222
|
-
))}
|
|
195
|
+
{prop.enum.map((val) => <option key={String(val)} value={String(val)}>{String(val)}</option>)}
|
|
223
196
|
</select>
|
|
224
|
-
{prop.description && (
|
|
225
|
-
<p className="text-xs text-muted-foreground mt-1">{prop.description}</p>
|
|
226
|
-
)}
|
|
227
197
|
</div>
|
|
228
198
|
);
|
|
229
199
|
} else if (prop.type === 'boolean') {
|
|
230
|
-
// Checkbox field
|
|
231
200
|
return (
|
|
232
|
-
<div key={key}
|
|
233
|
-
<label className="flex items-center gap-2 cursor-pointer">
|
|
234
|
-
<input
|
|
235
|
-
|
|
236
|
-
className="w-4 h-4"
|
|
237
|
-
checked={toolArgs[key] || false}
|
|
238
|
-
onChange={(e) => setToolArgs({ ...toolArgs, [key]: e.target.checked })}
|
|
239
|
-
/>
|
|
240
|
-
<span className="text-sm font-medium text-foreground">
|
|
241
|
-
{prop.title || key}
|
|
242
|
-
{isRequired && <span className="text-destructive ml-1">*</span>}
|
|
243
|
-
</span>
|
|
201
|
+
<div key={key}>
|
|
202
|
+
<label className="flex items-center gap-2 cursor-pointer p-3 bg-muted/30 rounded-lg border border-border">
|
|
203
|
+
<input type="checkbox" className="checkbox checkbox-primary w-4 h-4" checked={toolArgs[key] || false} onChange={(e) => setToolArgs({ ...toolArgs, [key]: e.target.checked })} />
|
|
204
|
+
<span className="text-sm font-medium text-foreground">{prop.title || key}</span>
|
|
244
205
|
</label>
|
|
245
|
-
{prop.description && (
|
|
246
|
-
<p className="text-xs text-muted-foreground mt-1 ml-6">{prop.description}</p>
|
|
247
|
-
)}
|
|
248
206
|
</div>
|
|
249
207
|
);
|
|
250
208
|
} else {
|
|
251
|
-
// Text/Number field
|
|
252
209
|
return (
|
|
253
|
-
<div key={key}
|
|
254
|
-
<label className="block text-sm font-medium text-foreground mb-2">
|
|
255
|
-
|
|
256
|
-
{isRequired && <span className="text-destructive ml-1">*</span>}
|
|
257
|
-
</label>
|
|
258
|
-
<input
|
|
259
|
-
type={prop.type === 'number' || prop.type === 'integer' ? 'number' : 'text'}
|
|
260
|
-
className="input"
|
|
261
|
-
value={toolArgs[key] || prop.default || ''}
|
|
262
|
-
onChange={(e) => {
|
|
263
|
-
const value = prop.type === 'number' || prop.type === 'integer'
|
|
264
|
-
? (e.target.value ? Number(e.target.value) : '')
|
|
265
|
-
: e.target.value;
|
|
266
|
-
setToolArgs({ ...toolArgs, [key]: value });
|
|
267
|
-
}}
|
|
268
|
-
required={isRequired}
|
|
269
|
-
placeholder={prop.description}
|
|
270
|
-
min={prop.minimum}
|
|
271
|
-
max={prop.maximum}
|
|
272
|
-
/>
|
|
273
|
-
{prop.description && (
|
|
274
|
-
<p className="text-xs text-muted-foreground mt-1">{prop.description}</p>
|
|
275
|
-
)}
|
|
210
|
+
<div key={key}>
|
|
211
|
+
<label className="block text-sm font-medium text-foreground mb-2">{prop.title || key}{isRequired && <span className="text-destructive ml-1">*</span>}</label>
|
|
212
|
+
<input type={prop.type === 'number' || prop.type === 'integer' ? 'number' : 'text'} className="input w-full" value={toolArgs[key] || prop.default || ''} onChange={(e) => setToolArgs({ ...toolArgs, [key]: prop.type === 'number' || prop.type === 'integer' ? Number(e.target.value) : e.target.value })} required={isRequired} placeholder={prop.description} />
|
|
276
213
|
</div>
|
|
277
214
|
);
|
|
278
215
|
}
|
|
279
216
|
})}
|
|
280
|
-
</>
|
|
281
|
-
) : (
|
|
282
|
-
<div className="mb-4 p-4 bg-muted/30 rounded-lg border border-border">
|
|
283
|
-
<p className="text-sm text-muted-foreground">No input required</p>
|
|
284
217
|
</div>
|
|
218
|
+
) : (
|
|
219
|
+
<div className="p-4 bg-muted/30 rounded-lg border border-border border-dashed text-center"><p className="text-sm text-muted-foreground">No arguments required</p></div>
|
|
285
220
|
)}
|
|
286
221
|
|
|
287
|
-
<
|
|
288
|
-
type="submit"
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
>
|
|
292
|
-
<Play className="w-4 h-4" />
|
|
293
|
-
{executingTool ? 'Executing...' : 'Execute Tool'}
|
|
294
|
-
</button>
|
|
222
|
+
<div className="sticky bottom-0 -mx-6 -mb-6 p-6 bg-card border-t border-border mt-auto">
|
|
223
|
+
<button type="submit" className="btn btn-primary w-full gap-2 py-3" disabled={executingTool}>
|
|
224
|
+
<PlayIcon className="w-4 h-4" />{executingTool ? 'Executing...' : 'Execute Tool'}
|
|
225
|
+
</button>
|
|
226
|
+
</div>
|
|
295
227
|
</form>
|
|
296
228
|
|
|
297
|
-
{/* Result */}
|
|
298
229
|
{toolResult && (
|
|
299
|
-
<div className="mt-
|
|
300
|
-
<div>
|
|
301
|
-
<
|
|
302
|
-
<pre className="bg-muted/30 p-4 rounded-lg text-sm overflow-auto max-h-64 text-foreground font-mono border border-border">
|
|
303
|
-
{JSON.stringify(toolResult, null, 2)}
|
|
304
|
-
</pre>
|
|
230
|
+
<div className="mt-8 pt-8 border-t border-border">
|
|
231
|
+
<div className="bg-muted/30 rounded-xl border border-border overflow-hidden mb-6">
|
|
232
|
+
<pre className="p-4 text-xs font-mono text-foreground overflow-auto max-h-96">{JSON.stringify(toolResult, null, 2)}</pre>
|
|
305
233
|
</div>
|
|
306
|
-
|
|
307
|
-
{/* Widget UI Rendering */}
|
|
308
234
|
{(() => {
|
|
309
|
-
|
|
310
|
-
const widgetUri =
|
|
311
|
-
selectedTool.widget?.route ||
|
|
312
|
-
selectedTool.outputTemplate ||
|
|
313
|
-
selectedTool._meta?.['ui/template'] ||
|
|
314
|
-
selectedTool._meta?.['openai/outputTemplate'];
|
|
315
|
-
|
|
235
|
+
const widgetUri = selectedTool.widget?.route || selectedTool.outputTemplate || selectedTool._meta?.['ui/template'] || selectedTool._meta?.['openai/outputTemplate'];
|
|
316
236
|
return widgetUri && toolResult ? (
|
|
317
|
-
<div>
|
|
318
|
-
<
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if (toolResult.content?.[0]?.text) {
|
|
325
|
-
try {
|
|
326
|
-
const parsed = JSON.parse(toolResult.content[0].text);
|
|
327
|
-
// Unwrap if response was wrapped by TransformInterceptor
|
|
328
|
-
if (parsed.success !== undefined && parsed.data !== undefined) {
|
|
329
|
-
return parsed.data;
|
|
330
|
-
}
|
|
331
|
-
return parsed;
|
|
332
|
-
} catch {
|
|
333
|
-
return { message: toolResult.content[0].text };
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
return toolResult;
|
|
337
|
-
})()}
|
|
338
|
-
className="w-full h-full"
|
|
339
|
-
/>
|
|
340
|
-
</div>
|
|
237
|
+
<div className="border border-border rounded-xl overflow-hidden bg-background shadow-sm">
|
|
238
|
+
<WidgetRenderer uri={widgetUri} data={(() => {
|
|
239
|
+
if (toolResult.content?.[0]?.text) {
|
|
240
|
+
try { const parsed = JSON.parse(toolResult.content[0].text); return (parsed.success !== undefined && parsed.data !== undefined) ? parsed.data : parsed; } catch { return { message: toolResult.content[0].text }; }
|
|
241
|
+
}
|
|
242
|
+
return toolResult;
|
|
243
|
+
})()} className="w-full widget-in-chat widget-expanded" />
|
|
341
244
|
</div>
|
|
342
245
|
) : null;
|
|
343
246
|
})()}
|
|
@@ -345,7 +248,8 @@ export default function ToolsPage() {
|
|
|
345
248
|
)}
|
|
346
249
|
</div>
|
|
347
250
|
</div>
|
|
348
|
-
|
|
251
|
+
</div>
|
|
252
|
+
)}
|
|
349
253
|
</>
|
|
350
254
|
);
|
|
351
255
|
}
|