apteva 0.4.32 → 0.4.44
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/ActivityPage.c48n83h2.js +3 -0
- package/dist/ApiDocsPage.yzcxx5ax.js +4 -0
- package/dist/App.09yb8t0b.js +1 -0
- package/dist/App.152mbs1r.js +4 -0
- package/dist/App.3a67nx9w.js +4 -0
- package/dist/App.9epx6785.js +4 -0
- package/dist/App.d8955awp.js +4 -0
- package/dist/App.drwb57jq.js +4 -0
- package/dist/App.gssbmajb.js +4 -0
- package/dist/App.qw70pc29.js +53 -0
- package/dist/App.qzbx5wtj.js +4 -0
- package/dist/App.r5serxkt.js +8 -0
- package/dist/App.tpmp9020.js +20 -0
- package/dist/App.v2wb4d7d.js +61 -0
- package/dist/App.vxmaaj0m.js +13 -0
- package/dist/App.w4p2tda9.js +4 -0
- package/dist/App.wv2ng55q.js +221 -0
- package/dist/App.yncnrn0f.js +4 -0
- package/dist/ConnectionsPage.k6cspyqq.js +3 -0
- package/dist/McpPage.cdxm48xj.js +3 -0
- package/dist/SettingsPage.evpv7c2y.js +3 -0
- package/dist/SkillsPage.pvzp6c1a.js +3 -0
- package/dist/TasksPage.6jnvbpsy.js +3 -0
- package/dist/TelemetryPage.t7vk24zc.js +3 -0
- package/dist/TestsPage.5x6658aa.js +3 -0
- package/dist/ThreadsPage.3fvhtevh.js +3 -0
- package/dist/apteva-kit.css +1 -1
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +10 -9
- package/src/crypto.ts +4 -3
- package/src/db.ts +171 -36
- package/src/integrations/agentdojo.ts +95 -12
- package/src/integrations/index.ts +7 -0
- package/src/mcp-platform.ts +870 -142
- package/src/openapi.ts +96 -0
- package/src/providers.ts +60 -34
- package/src/routes/api/agent-utils.ts +59 -47
- package/src/routes/api/agents.ts +71 -2
- package/src/routes/api/integrations.ts +11 -5
- package/src/routes/api/mcp.ts +5 -4
- package/src/routes/api/meta-agent.ts +37 -1
- package/src/routes/api/projects.ts +3 -3
- package/src/routes/api/providers.ts +121 -30
- package/src/routes/api/skills.ts +2 -3
- package/src/routes/api/system.ts +98 -14
- package/src/routes/api/telemetry.ts +19 -1
- package/src/routes/share.ts +85 -0
- package/src/server.ts +43 -32
- package/src/triggers/agentdojo.ts +2 -2
- package/src/web/App.tsx +107 -21
- package/src/web/components/activity/ActivityPage.tsx +242 -389
- package/src/web/components/agents/AgentCard.tsx +19 -27
- package/src/web/components/agents/AgentPanel.tsx +358 -198
- package/src/web/components/agents/AgentsView.tsx +4 -4
- package/src/web/components/agents/CreateAgentModal.tsx +21 -79
- package/src/web/components/api/ApiDocsPage.tsx +66 -66
- package/src/web/components/auth/CreateAccountStep.tsx +16 -16
- package/src/web/components/auth/LoginPage.tsx +10 -10
- package/src/web/components/common/Icons.tsx +8 -0
- package/src/web/components/common/LoadingSpinner.tsx +2 -2
- package/src/web/components/common/Modal.tsx +8 -8
- package/src/web/components/common/Select.tsx +11 -10
- package/src/web/components/connections/ConnectionsPage.tsx +4 -4
- package/src/web/components/connections/IntegrationsTab.tsx +18 -18
- package/src/web/components/connections/OverviewTab.tsx +13 -13
- package/src/web/components/connections/TriggersTab.tsx +99 -99
- package/src/web/components/dashboard/Dashboard.tsx +177 -52
- package/src/web/components/index.ts +1 -1
- package/src/web/components/layout/Header.tsx +50 -34
- package/src/web/components/layout/Sidebar.tsx +41 -16
- package/src/web/components/mcp/IntegrationsPanel.tsx +160 -69
- package/src/web/components/mcp/McpPage.tsx +218 -209
- package/src/web/components/meta-agent/MetaAgent.tsx +15 -11
- package/src/web/components/onboarding/OnboardingWizard.tsx +25 -25
- package/src/web/components/settings/SettingsPage.tsx +389 -221
- package/src/web/components/skills/SkillsPage.tsx +88 -88
- package/src/web/components/tasks/TasksPage.tsx +385 -68
- package/src/web/components/telemetry/TelemetryPage.tsx +294 -39
- package/src/web/components/tests/TestsPage.tsx +50 -50
- package/src/web/components/threads/ThreadsPage.tsx +315 -0
- package/src/web/context/AuthContext.tsx +3 -3
- package/src/web/context/ProjectContext.tsx +8 -3
- package/src/web/context/TelemetryContext.tsx +24 -6
- package/src/web/context/ThemeContext.tsx +69 -0
- package/src/web/context/index.ts +3 -1
- package/src/web/styles.css +25 -7
- package/src/web/themes.ts +99 -0
- package/src/web/types.ts +4 -7
- package/dist/ActivityPage.41nbye4r.js +0 -3
- package/dist/ApiDocsPage.4smnt8m3.js +0 -4
- package/dist/App.0sbax9et.js +0 -4
- package/dist/App.0ws427h8.js +0 -4
- package/dist/App.6q6bar8b.js +0 -4
- package/dist/App.80301vdb.js +0 -4
- package/dist/App.af2wg84v.js +0 -267
- package/dist/App.ca1rz1ph.js +0 -4
- package/dist/App.ensa6z0r.js +0 -4
- package/dist/App.f8g7tych.js +0 -13
- package/dist/App.mvtqv6qc.js +0 -20
- package/dist/App.ncgc9cxy.js +0 -4
- package/dist/App.p02f4ret.js +0 -1
- package/dist/App.p0fb1pds.js +0 -4
- package/dist/App.pmaq48sj.js +0 -4
- package/dist/App.yv87t9m5.js +0 -4
- package/dist/App.zjmfm8p6.js +0 -4
- package/dist/ConnectionsPage.anb3rv9a.js +0 -3
- package/dist/McpPage.y396h6fy.js +0 -3
- package/dist/SettingsPage.p1hc60gk.js +0 -3
- package/dist/SkillsPage.yj3xdsay.js +0 -3
- package/dist/TasksPage.sjv0khtv.js +0 -3
- package/dist/TelemetryPage.2qm4w16r.js +0 -3
- package/dist/TestsPage.zzs4qfj8.js +0 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { DashboardIcon,
|
|
2
|
+
import { DashboardIcon, ThreadsIcon, AgentsIcon, ActivityIcon, TasksIcon, ConnectionsIcon, McpIcon, SkillsIcon, TestsIcon, TelemetryIcon, ApiIcon, SettingsIcon, CloseIcon } from "../common/Icons";
|
|
3
3
|
import { useAuth } from "../../context";
|
|
4
4
|
import type { Route } from "../../types";
|
|
5
5
|
|
|
@@ -39,20 +39,22 @@ export function Sidebar({ route, agentCount, taskCount, onNavigate, isOpen, onCl
|
|
|
39
39
|
{/* Sidebar - hidden on mobile unless open, always visible on md+ */}
|
|
40
40
|
<aside
|
|
41
41
|
className={`
|
|
42
|
-
fixed inset-y-0 left-0 z-50 w-64
|
|
42
|
+
fixed inset-y-0 left-0 z-50 w-64 p-4 flex flex-col transform transition-transform duration-200 ease-in-out
|
|
43
43
|
md:relative md:w-56 md:translate-x-0 md:z-auto
|
|
44
44
|
${isOpen ? "translate-x-0" : "-translate-x-full"}
|
|
45
45
|
`}
|
|
46
|
+
style={{ backgroundColor: "var(--color-bg)", borderRight: "1px solid var(--color-border)" }}
|
|
46
47
|
>
|
|
47
48
|
{/* Mobile header with close button */}
|
|
48
49
|
<div className="flex items-center justify-between mb-4 md:hidden">
|
|
49
50
|
<div className="flex items-center gap-2">
|
|
50
|
-
<span
|
|
51
|
+
<span style={{ color: "var(--color-accent)" }}>>_</span>
|
|
51
52
|
<span className="text-lg tracking-wider">apteva</span>
|
|
52
53
|
</div>
|
|
53
54
|
<button
|
|
54
55
|
onClick={onClose}
|
|
55
|
-
className="p-2
|
|
56
|
+
className="p-2 transition"
|
|
57
|
+
style={{ color: "var(--color-text-muted)" }}
|
|
56
58
|
>
|
|
57
59
|
<CloseIcon />
|
|
58
60
|
</button>
|
|
@@ -72,6 +74,12 @@ export function Sidebar({ route, agentCount, taskCount, onNavigate, isOpen, onCl
|
|
|
72
74
|
onClick={() => handleNavigate("agents")}
|
|
73
75
|
badge={agentCount > 0 ? String(agentCount) : undefined}
|
|
74
76
|
/>
|
|
77
|
+
<NavButton
|
|
78
|
+
icon={<ThreadsIcon />}
|
|
79
|
+
label="Threads"
|
|
80
|
+
active={route === "threads"}
|
|
81
|
+
onClick={() => handleNavigate("threads")}
|
|
82
|
+
/>
|
|
75
83
|
<NavButton
|
|
76
84
|
icon={<ActivityIcon />}
|
|
77
85
|
label="Activity"
|
|
@@ -131,26 +139,31 @@ export function Sidebar({ route, agentCount, taskCount, onNavigate, isOpen, onCl
|
|
|
131
139
|
|
|
132
140
|
{/* User profile - pinned to bottom */}
|
|
133
141
|
{user && (
|
|
134
|
-
<div className="relative
|
|
142
|
+
<div className="relative pt-3 mt-3" style={{ borderTop: "1px solid var(--color-border)" }}>
|
|
135
143
|
<button
|
|
136
144
|
onClick={() => setShowUserMenu(!showUserMenu)}
|
|
137
|
-
className="w-full flex items-center gap-3 px-3 py-2 rounded
|
|
145
|
+
className="w-full flex items-center gap-3 px-3 py-2 rounded transition"
|
|
146
|
+
style={{ color: "var(--color-text)" }}
|
|
147
|
+
onMouseEnter={e => e.currentTarget.style.backgroundColor = "var(--color-surface)"}
|
|
148
|
+
onMouseLeave={e => e.currentTarget.style.backgroundColor = "transparent"}
|
|
138
149
|
>
|
|
139
|
-
<div className="w-8 h-8 rounded-full
|
|
150
|
+
<div className="w-8 h-8 rounded-full flex items-center justify-center text-black font-medium text-sm flex-shrink-0" style={{ backgroundColor: "var(--color-accent)" }}>
|
|
140
151
|
{user.username.charAt(0).toUpperCase()}
|
|
141
152
|
</div>
|
|
142
153
|
<div className="flex-1 min-w-0 text-left">
|
|
143
154
|
<p className="text-sm font-medium truncate">{user.username}</p>
|
|
144
|
-
<p className="text-xs text-
|
|
155
|
+
<p className="text-xs" style={{ color: "var(--color-text-faint)" }}>{user.role}</p>
|
|
145
156
|
</div>
|
|
146
157
|
</button>
|
|
147
158
|
{showUserMenu && (
|
|
148
159
|
<>
|
|
149
160
|
<div className="fixed inset-0 z-40" onClick={() => setShowUserMenu(false)} />
|
|
150
|
-
<div className="absolute left-3 bottom-full mb-1 w-48
|
|
161
|
+
<div className="absolute left-3 bottom-full mb-1 w-48 rounded-lg shadow-xl z-50" style={{ backgroundColor: "var(--color-surface)", border: "1px solid var(--color-border-light)" }}>
|
|
151
162
|
<button
|
|
152
163
|
onClick={handleLogout}
|
|
153
|
-
className="w-full px-4 py-2.5 text-left text-sm text-red-400
|
|
164
|
+
className="w-full px-4 py-2.5 text-left text-sm text-red-400 transition rounded-lg"
|
|
165
|
+
onMouseEnter={e => e.currentTarget.style.backgroundColor = "var(--color-surface-raised)"}
|
|
166
|
+
onMouseLeave={e => e.currentTarget.style.backgroundColor = "transparent"}
|
|
154
167
|
>
|
|
155
168
|
Sign out
|
|
156
169
|
</button>
|
|
@@ -176,16 +189,28 @@ function NavButton({ icon, label, active, onClick, badge }: NavButtonProps) {
|
|
|
176
189
|
return (
|
|
177
190
|
<button
|
|
178
191
|
onClick={onClick}
|
|
179
|
-
className=
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
192
|
+
className="w-full flex items-center gap-3 px-3 py-2 rounded font-medium transition"
|
|
193
|
+
style={{
|
|
194
|
+
backgroundColor: active ? "var(--color-surface)" : "transparent",
|
|
195
|
+
color: active ? "var(--color-text)" : "var(--color-text-muted)",
|
|
196
|
+
}}
|
|
197
|
+
onMouseEnter={e => {
|
|
198
|
+
if (!active) {
|
|
199
|
+
e.currentTarget.style.backgroundColor = "var(--color-surface)";
|
|
200
|
+
e.currentTarget.style.color = "var(--color-text-secondary)";
|
|
201
|
+
}
|
|
202
|
+
}}
|
|
203
|
+
onMouseLeave={e => {
|
|
204
|
+
if (!active) {
|
|
205
|
+
e.currentTarget.style.backgroundColor = "transparent";
|
|
206
|
+
e.currentTarget.style.color = "var(--color-text-muted)";
|
|
207
|
+
}
|
|
208
|
+
}}
|
|
184
209
|
>
|
|
185
210
|
{icon}
|
|
186
211
|
{label}
|
|
187
212
|
{badge && (
|
|
188
|
-
<span className="ml-auto
|
|
213
|
+
<span className="ml-auto text-xs px-2 py-0.5 rounded-full" style={{ backgroundColor: "var(--color-surface-raised)", color: "var(--color-text-secondary)" }}>
|
|
189
214
|
{badge}
|
|
190
215
|
</span>
|
|
191
216
|
)}
|
|
@@ -10,6 +10,12 @@ interface IntegrationApp {
|
|
|
10
10
|
logo: string | null;
|
|
11
11
|
categories: string[];
|
|
12
12
|
authSchemes: string[];
|
|
13
|
+
providerSlug?: string;
|
|
14
|
+
credentialFields?: {
|
|
15
|
+
name: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
required?: boolean;
|
|
18
|
+
}[];
|
|
13
19
|
}
|
|
14
20
|
|
|
15
21
|
interface ConnectedAccount {
|
|
@@ -68,9 +74,10 @@ export function IntegrationsPanel({
|
|
|
68
74
|
const [error, setError] = useState<string | null>(null);
|
|
69
75
|
// For auth method selection (when app supports both OAuth and API Key)
|
|
70
76
|
const [authMethodModal, setAuthMethodModal] = useState<{ app: IntegrationApp } | null>(null);
|
|
71
|
-
// For API Key modal
|
|
77
|
+
// For API Key / credential modal
|
|
72
78
|
const [apiKeyModal, setApiKeyModal] = useState<{ app: IntegrationApp } | null>(null);
|
|
73
79
|
const [apiKeyInput, setApiKeyInput] = useState("");
|
|
80
|
+
const [credentialInputs, setCredentialInputs] = useState<Record<string, string>>({});
|
|
74
81
|
// For MCP config creation modal
|
|
75
82
|
const [mcpConfigModal, setMcpConfigModal] = useState<{ app: IntegrationApp } | null>(null);
|
|
76
83
|
const [mcpConfigName, setMcpConfigName] = useState("");
|
|
@@ -153,17 +160,18 @@ export function IntegrationsPanel({
|
|
|
153
160
|
}, [pendingConnection, authFetch, providerId, projectId, fetchData, onConnectionComplete]);
|
|
154
161
|
|
|
155
162
|
// Initiate connection
|
|
156
|
-
const connectApp = async (app: IntegrationApp, apiKey?: string, forceOAuth?: boolean) => {
|
|
163
|
+
const connectApp = async (app: IntegrationApp, apiKey?: string, forceOAuth?: boolean, fields?: Record<string, string>) => {
|
|
157
164
|
// If app supports multiple auth methods and user hasn't chosen, show choice
|
|
158
|
-
if (hasMultipleAuthMethods(app) && !apiKey && !forceOAuth) {
|
|
165
|
+
if (hasMultipleAuthMethods(app) && !apiKey && !fields && !forceOAuth) {
|
|
159
166
|
setAuthMethodModal({ app });
|
|
160
167
|
return;
|
|
161
168
|
}
|
|
162
169
|
|
|
163
|
-
// If app supports API key (and user didn't choose OAuth), show
|
|
164
|
-
if (supportsApiKey(app) && !apiKey && !forceOAuth) {
|
|
170
|
+
// If app supports API key (and user didn't choose OAuth), show credential modal
|
|
171
|
+
if (supportsApiKey(app) && !apiKey && !fields && !forceOAuth) {
|
|
165
172
|
setApiKeyModal({ app });
|
|
166
173
|
setApiKeyInput("");
|
|
174
|
+
setCredentialInputs({});
|
|
167
175
|
return;
|
|
168
176
|
}
|
|
169
177
|
|
|
@@ -173,7 +181,13 @@ export function IntegrationsPanel({
|
|
|
173
181
|
try {
|
|
174
182
|
// Build request body
|
|
175
183
|
const body: any = { appSlug: app.slug };
|
|
176
|
-
if (
|
|
184
|
+
if (fields && Object.keys(fields).length > 0) {
|
|
185
|
+
// Multi-field credentials
|
|
186
|
+
body.credentials = {
|
|
187
|
+
authScheme: "API_KEY",
|
|
188
|
+
fields,
|
|
189
|
+
};
|
|
190
|
+
} else if (apiKey) {
|
|
177
191
|
body.credentials = {
|
|
178
192
|
authScheme: "API_KEY",
|
|
179
193
|
apiKey,
|
|
@@ -231,11 +245,21 @@ export function IntegrationsPanel({
|
|
|
231
245
|
}
|
|
232
246
|
};
|
|
233
247
|
|
|
234
|
-
// Handle API key form submission
|
|
248
|
+
// Handle API key / credential form submission
|
|
235
249
|
const handleApiKeySubmit = (e: React.FormEvent) => {
|
|
236
250
|
e.preventDefault();
|
|
237
|
-
if (!apiKeyModal
|
|
238
|
-
|
|
251
|
+
if (!apiKeyModal) return;
|
|
252
|
+
const hasFields = apiKeyModal.app.credentialFields && apiKeyModal.app.credentialFields.length > 0;
|
|
253
|
+
if (hasFields) {
|
|
254
|
+
// Check required fields are filled
|
|
255
|
+
const requiredFields = apiKeyModal.app.credentialFields!.filter(f => f.required !== false);
|
|
256
|
+
const allFilled = requiredFields.every(f => credentialInputs[f.name]?.trim());
|
|
257
|
+
if (!allFilled) return;
|
|
258
|
+
connectApp(apiKeyModal.app, undefined, false, credentialInputs);
|
|
259
|
+
} else {
|
|
260
|
+
if (!apiKeyInput.trim()) return;
|
|
261
|
+
connectApp(apiKeyModal.app, apiKeyInput.trim());
|
|
262
|
+
}
|
|
239
263
|
};
|
|
240
264
|
|
|
241
265
|
// Disconnect (called after confirmation)
|
|
@@ -291,6 +315,20 @@ export function IntegrationsPanel({
|
|
|
291
315
|
return;
|
|
292
316
|
}
|
|
293
317
|
|
|
318
|
+
// Auto-add the server locally
|
|
319
|
+
let autoAdded = false;
|
|
320
|
+
if (data.config?.id) {
|
|
321
|
+
try {
|
|
322
|
+
const addRes = await authFetch(
|
|
323
|
+
`/api/integrations/${providerId}/configs/${data.config.id}/add${projectParam}`,
|
|
324
|
+
{ method: "POST" }
|
|
325
|
+
);
|
|
326
|
+
autoAdded = addRes.ok;
|
|
327
|
+
} catch {
|
|
328
|
+
// Non-fatal — server was still created on the provider
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
294
332
|
setMcpConfigSuccess(mcpConfigName);
|
|
295
333
|
onConnectionComplete?.();
|
|
296
334
|
} catch (e) {
|
|
@@ -311,17 +349,23 @@ export function IntegrationsPanel({
|
|
|
311
349
|
});
|
|
312
350
|
};
|
|
313
351
|
|
|
314
|
-
// Check if app is connected
|
|
315
|
-
const isConnected = (
|
|
352
|
+
// Check if app is connected (also matches via providerSlug for multi-toolkit providers)
|
|
353
|
+
const isConnected = (app: IntegrationApp) => {
|
|
316
354
|
return connectedAccounts.some(
|
|
317
|
-
(a) => a.
|
|
355
|
+
(a) => a.status === "active" && (
|
|
356
|
+
a.appId === app.slug ||
|
|
357
|
+
(app.providerSlug && a.appId === app.providerSlug)
|
|
358
|
+
)
|
|
318
359
|
);
|
|
319
360
|
};
|
|
320
361
|
|
|
321
|
-
// Get connection for app (prefer active account)
|
|
322
|
-
const getConnection = (
|
|
323
|
-
return connectedAccounts.find((a) => a.appId ===
|
|
324
|
-
|| connectedAccounts.find((a) => a.appId ===
|
|
362
|
+
// Get connection for app (prefer active account, also matches via providerSlug)
|
|
363
|
+
const getConnection = (app: IntegrationApp) => {
|
|
364
|
+
return connectedAccounts.find((a) => a.appId === app.slug && a.status === "active")
|
|
365
|
+
|| (app.providerSlug && connectedAccounts.find((a) => a.appId === app.providerSlug && a.status === "active"))
|
|
366
|
+
|| connectedAccounts.find((a) => a.appId === app.slug)
|
|
367
|
+
|| (app.providerSlug && connectedAccounts.find((a) => a.appId === app.providerSlug))
|
|
368
|
+
|| undefined;
|
|
325
369
|
};
|
|
326
370
|
|
|
327
371
|
// Filter apps
|
|
@@ -337,11 +381,11 @@ export function IntegrationsPanel({
|
|
|
337
381
|
});
|
|
338
382
|
|
|
339
383
|
// Group by connected/not connected
|
|
340
|
-
const connectedApps = filteredApps.filter((app) => isConnected(app
|
|
341
|
-
const availableApps = filteredApps.filter((app) => !isConnected(app
|
|
384
|
+
const connectedApps = filteredApps.filter((app) => isConnected(app));
|
|
385
|
+
const availableApps = filteredApps.filter((app) => !isConnected(app));
|
|
342
386
|
|
|
343
387
|
if (loading) {
|
|
344
|
-
return <div className="text-center py-8 text-[
|
|
388
|
+
return <div className="text-center py-8 text-[var(--color-text-muted)]">Loading apps...</div>;
|
|
345
389
|
}
|
|
346
390
|
|
|
347
391
|
return (
|
|
@@ -349,7 +393,7 @@ export function IntegrationsPanel({
|
|
|
349
393
|
{/* Auth Method Choice Modal */}
|
|
350
394
|
{authMethodModal && (
|
|
351
395
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
|
352
|
-
<div className="bg-[
|
|
396
|
+
<div className="bg-[var(--color-surface)] border border-[var(--color-border-light)] rounded-lg p-6 w-full max-w-md mx-4">
|
|
353
397
|
<div className="flex items-center gap-3 mb-4">
|
|
354
398
|
{authMethodModal.app.logo && (
|
|
355
399
|
<img
|
|
@@ -360,7 +404,7 @@ export function IntegrationsPanel({
|
|
|
360
404
|
)}
|
|
361
405
|
<div>
|
|
362
406
|
<h3 className="font-medium">Connect {authMethodModal.app.name}</h3>
|
|
363
|
-
<p className="text-xs text-[
|
|
407
|
+
<p className="text-xs text-[var(--color-text-muted)]">Choose how to authenticate</p>
|
|
364
408
|
</div>
|
|
365
409
|
</div>
|
|
366
410
|
<div className="space-y-3">
|
|
@@ -369,11 +413,12 @@ export function IntegrationsPanel({
|
|
|
369
413
|
setAuthMethodModal(null);
|
|
370
414
|
setApiKeyModal({ app: authMethodModal.app });
|
|
371
415
|
setApiKeyInput("");
|
|
416
|
+
setCredentialInputs({});
|
|
372
417
|
}}
|
|
373
|
-
className="w-full text-left p-3 bg-[
|
|
418
|
+
className="w-full text-left p-3 bg-[var(--color-bg)] hover:bg-[var(--color-surface-raised)] border border-[var(--color-border-light)] hover:border-[var(--color-accent)] rounded-lg transition"
|
|
374
419
|
>
|
|
375
420
|
<div className="font-medium text-sm">API Key</div>
|
|
376
|
-
<div className="text-xs text-[
|
|
421
|
+
<div className="text-xs text-[var(--color-text-muted)] mt-0.5">
|
|
377
422
|
Enter your {authMethodModal.app.name} API key directly
|
|
378
423
|
</div>
|
|
379
424
|
</button>
|
|
@@ -382,17 +427,17 @@ export function IntegrationsPanel({
|
|
|
382
427
|
setAuthMethodModal(null);
|
|
383
428
|
connectApp(authMethodModal.app, undefined, true);
|
|
384
429
|
}}
|
|
385
|
-
className="w-full text-left p-3 bg-[
|
|
430
|
+
className="w-full text-left p-3 bg-[var(--color-bg)] hover:bg-[var(--color-surface-raised)] border border-[var(--color-border-light)] hover:border-[var(--color-accent)] rounded-lg transition"
|
|
386
431
|
>
|
|
387
432
|
<div className="font-medium text-sm">OAuth</div>
|
|
388
|
-
<div className="text-xs text-[
|
|
433
|
+
<div className="text-xs text-[var(--color-text-muted)] mt-0.5">
|
|
389
434
|
Sign in with your {authMethodModal.app.name} account
|
|
390
435
|
</div>
|
|
391
436
|
</button>
|
|
392
437
|
</div>
|
|
393
438
|
<button
|
|
394
439
|
onClick={() => setAuthMethodModal(null)}
|
|
395
|
-
className="w-full text-sm text-[
|
|
440
|
+
className="w-full text-sm text-[var(--color-text-muted)] hover:text-white mt-4 py-2 transition"
|
|
396
441
|
>
|
|
397
442
|
Cancel
|
|
398
443
|
</button>
|
|
@@ -400,10 +445,10 @@ export function IntegrationsPanel({
|
|
|
400
445
|
</div>
|
|
401
446
|
)}
|
|
402
447
|
|
|
403
|
-
{/* API Key Modal */}
|
|
448
|
+
{/* API Key / Credentials Modal */}
|
|
404
449
|
{apiKeyModal && (
|
|
405
450
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
|
406
|
-
<div className="bg-[
|
|
451
|
+
<div className="bg-[var(--color-surface)] border border-[var(--color-border-light)] rounded-lg p-6 w-full max-w-md mx-4">
|
|
407
452
|
<div className="flex items-center gap-3 mb-4">
|
|
408
453
|
{apiKeyModal.app.logo && (
|
|
409
454
|
<img
|
|
@@ -414,30 +459,63 @@ export function IntegrationsPanel({
|
|
|
414
459
|
)}
|
|
415
460
|
<div>
|
|
416
461
|
<h3 className="font-medium">Connect {apiKeyModal.app.name}</h3>
|
|
417
|
-
<p className="text-xs text-[
|
|
462
|
+
<p className="text-xs text-[var(--color-text-muted)]">
|
|
463
|
+
{apiKeyModal.app.credentialFields?.length
|
|
464
|
+
? "Enter your credentials to connect"
|
|
465
|
+
: "Enter your API key to connect"}
|
|
466
|
+
</p>
|
|
418
467
|
</div>
|
|
419
468
|
</div>
|
|
420
469
|
<form onSubmit={handleApiKeySubmit}>
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
470
|
+
{apiKeyModal.app.credentialFields && apiKeyModal.app.credentialFields.length > 0 ? (
|
|
471
|
+
<div className="space-y-3 mb-4">
|
|
472
|
+
{apiKeyModal.app.credentialFields.map((field, idx) => (
|
|
473
|
+
<div key={field.name}>
|
|
474
|
+
<label className="block text-xs text-[var(--color-text-secondary)] mb-1">
|
|
475
|
+
{field.name.replace(/([A-Z])/g, " $1").replace(/[-_]/g, " ").replace(/\b\w/g, c => c.toUpperCase()).trim()}
|
|
476
|
+
{field.required !== false && <span className="text-red-400 ml-0.5">*</span>}
|
|
477
|
+
</label>
|
|
478
|
+
{field.description && (
|
|
479
|
+
<p className="text-[10px] text-[var(--color-text-faint)] mb-1">{field.description}</p>
|
|
480
|
+
)}
|
|
481
|
+
<input
|
|
482
|
+
type="password"
|
|
483
|
+
value={credentialInputs[field.name] || ""}
|
|
484
|
+
onChange={(e) => setCredentialInputs(prev => ({ ...prev, [field.name]: e.target.value }))}
|
|
485
|
+
placeholder={`Enter ${field.name}...`}
|
|
486
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded-lg px-4 py-2 focus:outline-none focus:border-[var(--color-accent)]"
|
|
487
|
+
autoFocus={idx === 0}
|
|
488
|
+
/>
|
|
489
|
+
</div>
|
|
490
|
+
))}
|
|
491
|
+
</div>
|
|
492
|
+
) : (
|
|
493
|
+
<input
|
|
494
|
+
type="password"
|
|
495
|
+
value={apiKeyInput}
|
|
496
|
+
onChange={(e) => setApiKeyInput(e.target.value)}
|
|
497
|
+
placeholder="Enter API Key..."
|
|
498
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded-lg px-4 py-2 mb-4 focus:outline-none focus:border-[var(--color-accent)]"
|
|
499
|
+
autoFocus
|
|
500
|
+
/>
|
|
501
|
+
)}
|
|
429
502
|
<div className="flex gap-2">
|
|
430
503
|
<button
|
|
431
504
|
type="button"
|
|
432
505
|
onClick={() => setApiKeyModal(null)}
|
|
433
|
-
className="flex-1 text-sm bg-[
|
|
506
|
+
className="flex-1 text-sm bg-[var(--color-surface-raised)] hover:bg-[var(--color-surface-raised)] border border-[var(--color-border-light)] px-4 py-2 rounded transition"
|
|
434
507
|
>
|
|
435
508
|
Cancel
|
|
436
509
|
</button>
|
|
437
510
|
<button
|
|
438
511
|
type="submit"
|
|
439
|
-
disabled={
|
|
440
|
-
|
|
512
|
+
disabled={
|
|
513
|
+
connecting === apiKeyModal.app.slug ||
|
|
514
|
+
(apiKeyModal.app.credentialFields?.length
|
|
515
|
+
? !apiKeyModal.app.credentialFields.filter(f => f.required !== false).every(f => credentialInputs[f.name]?.trim())
|
|
516
|
+
: !apiKeyInput.trim())
|
|
517
|
+
}
|
|
518
|
+
className="flex-1 text-sm bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] text-white px-4 py-2 rounded transition disabled:opacity-50"
|
|
441
519
|
>
|
|
442
520
|
{connecting === apiKeyModal.app.slug ? "Connecting..." : "Connect"}
|
|
443
521
|
</button>
|
|
@@ -450,7 +528,7 @@ export function IntegrationsPanel({
|
|
|
450
528
|
{/* MCP Config Creation Modal */}
|
|
451
529
|
{mcpConfigModal && (
|
|
452
530
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
|
453
|
-
<div className="bg-[
|
|
531
|
+
<div className="bg-[var(--color-surface)] border border-[var(--color-border-light)] rounded-lg p-6 w-full max-w-md mx-4">
|
|
454
532
|
{mcpConfigSuccess ? (
|
|
455
533
|
<>
|
|
456
534
|
<div className="text-center mb-4">
|
|
@@ -458,11 +536,8 @@ export function IntegrationsPanel({
|
|
|
458
536
|
<span className="text-green-400 text-2xl">✓</span>
|
|
459
537
|
</div>
|
|
460
538
|
<h3 className="font-medium text-lg">MCP Config Created!</h3>
|
|
461
|
-
<p className="text-sm text-[
|
|
462
|
-
"{mcpConfigSuccess}" has been created
|
|
463
|
-
</p>
|
|
464
|
-
<p className="text-xs text-[#666] mt-2">
|
|
465
|
-
You can now add it to your agents from the MCP Configs tab.
|
|
539
|
+
<p className="text-sm text-[var(--color-text-secondary)] mt-2">
|
|
540
|
+
"{mcpConfigSuccess}" has been created and added to your servers.
|
|
466
541
|
</p>
|
|
467
542
|
</div>
|
|
468
543
|
<button
|
|
@@ -470,7 +545,7 @@ export function IntegrationsPanel({
|
|
|
470
545
|
setMcpConfigModal(null);
|
|
471
546
|
setMcpConfigSuccess(null);
|
|
472
547
|
}}
|
|
473
|
-
className="w-full text-sm bg-[
|
|
548
|
+
className="w-full text-sm bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] text-white px-4 py-2 rounded transition"
|
|
474
549
|
>
|
|
475
550
|
Done
|
|
476
551
|
</button>
|
|
@@ -487,19 +562,19 @@ export function IntegrationsPanel({
|
|
|
487
562
|
)}
|
|
488
563
|
<div>
|
|
489
564
|
<h3 className="font-medium">Create MCP Config</h3>
|
|
490
|
-
<p className="text-xs text-[
|
|
565
|
+
<p className="text-xs text-[var(--color-text-muted)]">
|
|
491
566
|
Create an MCP config for {mcpConfigModal.app.name}
|
|
492
567
|
</p>
|
|
493
568
|
</div>
|
|
494
569
|
</div>
|
|
495
570
|
<form onSubmit={(e) => { e.preventDefault(); createMcpConfig(); }}>
|
|
496
|
-
<label className="block text-xs text-[
|
|
571
|
+
<label className="block text-xs text-[var(--color-text-secondary)] mb-1">Config Name</label>
|
|
497
572
|
<input
|
|
498
573
|
type="text"
|
|
499
574
|
value={mcpConfigName}
|
|
500
575
|
onChange={(e) => setMcpConfigName(e.target.value)}
|
|
501
576
|
placeholder="Enter config name..."
|
|
502
|
-
className="w-full bg-[
|
|
577
|
+
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded-lg px-4 py-2 mb-4 focus:outline-none focus:border-[var(--color-accent)]"
|
|
503
578
|
autoFocus
|
|
504
579
|
maxLength={30}
|
|
505
580
|
/>
|
|
@@ -507,14 +582,14 @@ export function IntegrationsPanel({
|
|
|
507
582
|
<button
|
|
508
583
|
type="button"
|
|
509
584
|
onClick={() => setMcpConfigModal(null)}
|
|
510
|
-
className="flex-1 text-sm bg-[
|
|
585
|
+
className="flex-1 text-sm bg-[var(--color-surface-raised)] hover:bg-[var(--color-surface-raised)] border border-[var(--color-border-light)] px-4 py-2 rounded transition"
|
|
511
586
|
>
|
|
512
587
|
Cancel
|
|
513
588
|
</button>
|
|
514
589
|
<button
|
|
515
590
|
type="submit"
|
|
516
591
|
disabled={!mcpConfigName.trim() || mcpConfigCreating}
|
|
517
|
-
className="flex-1 text-sm bg-[
|
|
592
|
+
className="flex-1 text-sm bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] text-white px-4 py-2 rounded transition disabled:opacity-50"
|
|
518
593
|
>
|
|
519
594
|
{mcpConfigCreating ? "Creating..." : "Create Config"}
|
|
520
595
|
</button>
|
|
@@ -529,12 +604,12 @@ export function IntegrationsPanel({
|
|
|
529
604
|
{/* Confirmation Modal */}
|
|
530
605
|
{confirmModal && (
|
|
531
606
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
|
532
|
-
<div className="bg-[
|
|
607
|
+
<div className="bg-[var(--color-surface)] border border-[var(--color-border-light)] rounded-lg p-6 w-full max-w-sm mx-4">
|
|
533
608
|
<p className="text-center mb-4">{confirmModal.message}</p>
|
|
534
609
|
<div className="flex gap-2">
|
|
535
610
|
<button
|
|
536
611
|
onClick={() => setConfirmModal(null)}
|
|
537
|
-
className="flex-1 text-sm bg-[
|
|
612
|
+
className="flex-1 text-sm bg-[var(--color-surface-raised)] hover:bg-[var(--color-surface-raised)] border border-[var(--color-border-light)] px-4 py-2 rounded transition"
|
|
538
613
|
>
|
|
539
614
|
Cancel
|
|
540
615
|
</button>
|
|
@@ -574,14 +649,14 @@ export function IntegrationsPanel({
|
|
|
574
649
|
value={search}
|
|
575
650
|
onChange={(e) => setSearch(e.target.value)}
|
|
576
651
|
placeholder="Search apps..."
|
|
577
|
-
className="w-full bg-[
|
|
652
|
+
className="w-full bg-[var(--color-surface)] border border-[var(--color-border-light)] rounded-lg px-4 py-2 focus:outline-none focus:border-[var(--color-accent)]"
|
|
578
653
|
/>
|
|
579
654
|
</div>
|
|
580
655
|
|
|
581
656
|
{/* Connected Apps */}
|
|
582
657
|
{connectedApps.length > 0 && (
|
|
583
658
|
<div>
|
|
584
|
-
<h3 className="text-sm font-medium text-[
|
|
659
|
+
<h3 className="text-sm font-medium text-[var(--color-text-secondary)] mb-3">
|
|
585
660
|
Connected ({connectedApps.length})
|
|
586
661
|
</h3>
|
|
587
662
|
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
|
@@ -589,14 +664,19 @@ export function IntegrationsPanel({
|
|
|
589
664
|
<AppCard
|
|
590
665
|
key={app.id}
|
|
591
666
|
app={app}
|
|
592
|
-
connection={getConnection(app
|
|
667
|
+
connection={getConnection(app)}
|
|
593
668
|
onConnect={() => connectApp(app)}
|
|
594
669
|
onDisconnect={() => {
|
|
595
|
-
const conn = getConnection(app
|
|
670
|
+
const conn = getConnection(app);
|
|
596
671
|
if (conn) handleDisconnect(conn);
|
|
597
672
|
}}
|
|
598
673
|
onCreateMcpConfig={hideMcpConfig ? undefined : () => openMcpConfigModal(app)}
|
|
599
674
|
onBrowseTriggers={onBrowseTriggers ? () => onBrowseTriggers(app.slug) : undefined}
|
|
675
|
+
onUpdateKey={supportsApiKey(app) ? () => {
|
|
676
|
+
setApiKeyModal({ app });
|
|
677
|
+
setApiKeyInput("");
|
|
678
|
+
setCredentialInputs({});
|
|
679
|
+
} : undefined}
|
|
600
680
|
connecting={connecting === app.slug}
|
|
601
681
|
/>
|
|
602
682
|
))}
|
|
@@ -606,11 +686,11 @@ export function IntegrationsPanel({
|
|
|
606
686
|
|
|
607
687
|
{/* Available Apps */}
|
|
608
688
|
<div>
|
|
609
|
-
<h3 className="text-sm font-medium text-[
|
|
689
|
+
<h3 className="text-sm font-medium text-[var(--color-text-secondary)] mb-3">
|
|
610
690
|
Available Apps ({availableApps.length})
|
|
611
691
|
</h3>
|
|
612
692
|
{availableApps.length === 0 ? (
|
|
613
|
-
<p className="text-[
|
|
693
|
+
<p className="text-[var(--color-text-muted)] text-sm">
|
|
614
694
|
{search ? "No apps match your search" : "No apps available"}
|
|
615
695
|
</p>
|
|
616
696
|
) : (
|
|
@@ -626,7 +706,7 @@ export function IntegrationsPanel({
|
|
|
626
706
|
</div>
|
|
627
707
|
)}
|
|
628
708
|
{availableApps.length > 50 && (
|
|
629
|
-
<p className="text-xs text-[
|
|
709
|
+
<p className="text-xs text-[var(--color-text-faint)] mt-3 text-center">
|
|
630
710
|
Showing first 50 of {availableApps.length} apps. Use search to find more.
|
|
631
711
|
</p>
|
|
632
712
|
)}
|
|
@@ -643,6 +723,7 @@ function AppCard({
|
|
|
643
723
|
onDisconnect,
|
|
644
724
|
onCreateMcpConfig,
|
|
645
725
|
onBrowseTriggers,
|
|
726
|
+
onUpdateKey,
|
|
646
727
|
connecting,
|
|
647
728
|
}: {
|
|
648
729
|
app: IntegrationApp;
|
|
@@ -651,6 +732,7 @@ function AppCard({
|
|
|
651
732
|
onDisconnect?: () => void;
|
|
652
733
|
onCreateMcpConfig?: () => void;
|
|
653
734
|
onBrowseTriggers?: () => void;
|
|
735
|
+
onUpdateKey?: () => void;
|
|
654
736
|
connecting: boolean;
|
|
655
737
|
}) {
|
|
656
738
|
const isConnected = connection?.status === "active";
|
|
@@ -660,13 +742,13 @@ function AppCard({
|
|
|
660
742
|
|
|
661
743
|
return (
|
|
662
744
|
<div
|
|
663
|
-
className={`bg-[
|
|
664
|
-
isConnected ? "border-green-500/30" : "border-[
|
|
745
|
+
className={`bg-[var(--color-surface)] border rounded-lg p-3 transition ${
|
|
746
|
+
isConnected ? "border-green-500/30" : "border-[var(--color-border)] hover:border-[var(--color-border-light)]"
|
|
665
747
|
}`}
|
|
666
748
|
>
|
|
667
749
|
<div className="flex items-start gap-3">
|
|
668
750
|
{/* Logo */}
|
|
669
|
-
<div className="w-10 h-10 rounded bg-[
|
|
751
|
+
<div className="w-10 h-10 rounded bg-[var(--color-surface-raised)] flex items-center justify-center flex-shrink-0 overflow-hidden">
|
|
670
752
|
{app.logo ? (
|
|
671
753
|
<img
|
|
672
754
|
src={app.logo}
|
|
@@ -689,7 +771,7 @@ function AppCard({
|
|
|
689
771
|
<span className="text-xs text-green-400">✓</span>
|
|
690
772
|
)}
|
|
691
773
|
{!isConnected && hasApiKey && !hasOAuth && (
|
|
692
|
-
<span className="text-[10px] bg-[
|
|
774
|
+
<span className="text-[10px] bg-[var(--color-surface-raised)] text-[var(--color-text-secondary)] px-1.5 py-0.5 rounded" title="Requires API Key">
|
|
693
775
|
API Key
|
|
694
776
|
</span>
|
|
695
777
|
)}
|
|
@@ -700,7 +782,7 @@ function AppCard({
|
|
|
700
782
|
)}
|
|
701
783
|
</div>
|
|
702
784
|
{app.description && (
|
|
703
|
-
<p className="text-xs text-[
|
|
785
|
+
<p className="text-xs text-[var(--color-text-muted)] line-clamp-2 mt-0.5">
|
|
704
786
|
{app.description}
|
|
705
787
|
</p>
|
|
706
788
|
)}
|
|
@@ -709,7 +791,7 @@ function AppCard({
|
|
|
709
791
|
{app.categories.slice(0, 2).map((cat) => (
|
|
710
792
|
<span
|
|
711
793
|
key={cat}
|
|
712
|
-
className="text-[10px] bg-[
|
|
794
|
+
className="text-[10px] bg-[var(--color-surface-raised)] text-[var(--color-text-faint)] px-1.5 py-0.5 rounded"
|
|
713
795
|
>
|
|
714
796
|
{cat}
|
|
715
797
|
</span>
|
|
@@ -739,10 +821,19 @@ function AppCard({
|
|
|
739
821
|
Browse Triggers
|
|
740
822
|
</button>
|
|
741
823
|
)}
|
|
824
|
+
{onUpdateKey && (
|
|
825
|
+
<button
|
|
826
|
+
onClick={onUpdateKey}
|
|
827
|
+
className="text-xs text-[var(--color-text-muted)] hover:text-[var(--color-accent)] transition px-2"
|
|
828
|
+
title="Update API Key"
|
|
829
|
+
>
|
|
830
|
+
Key
|
|
831
|
+
</button>
|
|
832
|
+
)}
|
|
742
833
|
{onDisconnect && (
|
|
743
834
|
<button
|
|
744
835
|
onClick={onDisconnect}
|
|
745
|
-
className="text-xs text-[
|
|
836
|
+
className="text-xs text-[var(--color-text-muted)] hover:text-red-400 transition px-2"
|
|
746
837
|
title="Disconnect"
|
|
747
838
|
>
|
|
748
839
|
×
|
|
@@ -753,7 +844,7 @@ function AppCard({
|
|
|
753
844
|
<button
|
|
754
845
|
onClick={onConnect}
|
|
755
846
|
disabled={connecting}
|
|
756
|
-
className="w-full text-xs bg-[
|
|
847
|
+
className="w-full text-xs bg-[var(--color-surface-raised)] hover:bg-[var(--color-surface-raised)] border border-[var(--color-border-light)] hover:border-[var(--color-accent)] px-3 py-1.5 rounded transition disabled:opacity-50"
|
|
757
848
|
>
|
|
758
849
|
{connecting ? "Connecting..." : (hasApiKey && !hasOAuth) ? "Enter API Key" : "Connect"}
|
|
759
850
|
</button>
|