apteva 0.4.20 → 0.4.29
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.41nbye4r.js +3 -0
- package/dist/{ApiDocsPage.kf6bbwkk.js → ApiDocsPage.4smnt8m3.js} +2 -2
- package/dist/{App.jfx3der4.js → App.0sbax9et.js} +3 -3
- package/dist/App.0ws427h8.js +4 -0
- package/dist/App.4ehxpt48.js +4 -0
- package/dist/App.6q6bar8b.js +4 -0
- package/dist/App.ca1rz1ph.js +4 -0
- package/dist/{App.7v1w3ys9.js → App.ensa6z0r.js} +3 -3
- package/dist/{App.n4jb3c22.js → App.f8g7tych.js} +3 -3
- package/dist/App.kh7d2xj3.js +267 -0
- package/dist/App.mvtqv6qc.js +20 -0
- package/dist/{App.c90t3dxg.js → App.ncgc9cxy.js} +3 -3
- package/dist/{App.039re6cf.js → App.p0fb1pds.js} +3 -3
- package/dist/App.pmaq48sj.js +4 -0
- package/dist/{App.2yy66bnp.js → App.yv87t9m5.js} +3 -3
- package/dist/App.zjmfm8p6.js +4 -0
- package/dist/ConnectionsPage.anb3rv9a.js +3 -0
- package/dist/McpPage.y396h6fy.js +3 -0
- package/dist/SettingsPage.5k6vp396.js +3 -0
- package/dist/SkillsPage.yj3xdsay.js +3 -0
- package/dist/TasksPage.sjv0khtv.js +3 -0
- package/dist/TelemetryPage.2qm4w16r.js +3 -0
- package/dist/TestsPage.zzs4qfj8.js +3 -0
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +2 -2
- package/src/channels/telegram.ts +5 -0
- package/src/crypto.ts +13 -4
- package/src/db.ts +25 -2
- package/src/integrations/agentdojo.ts +1 -1
- package/src/providers.ts +46 -0
- package/src/routes/api/agent-utils.ts +64 -9
- package/src/routes/api/agents.ts +41 -13
- package/src/routes/api/integrations.ts +16 -6
- package/src/routes/api/mcp.ts +7 -0
- package/src/routes/api/triggers.ts +45 -5
- package/src/web/App.tsx +1 -0
- package/src/web/components/activity/ActivityPage.tsx +349 -214
- package/src/web/components/agents/AgentCard.tsx +37 -8
- package/src/web/components/agents/AgentPanel.tsx +268 -23
- package/src/web/components/connections/IntegrationsTab.tsx +57 -31
- package/src/web/components/connections/TriggersTab.tsx +336 -159
- package/src/web/components/dashboard/Dashboard.tsx +39 -7
- package/src/web/components/layout/Header.tsx +0 -34
- package/src/web/components/layout/Sidebar.tsx +43 -3
- package/src/web/components/mcp/McpPage.tsx +16 -5
- package/src/web/components/settings/SettingsPage.tsx +279 -30
- package/src/web/components/tasks/TasksPage.tsx +32 -6
- package/src/web/context/ProjectContext.tsx +5 -0
- package/src/web/context/TelemetryContext.tsx +14 -0
- package/src/web/types.ts +20 -2
- package/dist/ActivityPage.h769ek3a.js +0 -3
- package/dist/App.2jmkqm8c.js +0 -4
- package/dist/App.3515wsb4.js +0 -4
- package/dist/App.edwahsvz.js +0 -4
- package/dist/App.q3bpx15d.js +0 -20
- package/dist/App.r0a2nmqs.js +0 -267
- package/dist/App.s2yrcz15.js +0 -4
- package/dist/App.s5j82a5j.js +0 -4
- package/dist/App.tg1b94tx.js +0 -4
- package/dist/ConnectionsPage.a67fjgbf.js +0 -3
- package/dist/McpPage.d4p3xvtk.js +0 -3
- package/dist/SettingsPage.46sqpe39.js +0 -3
- package/dist/SkillsPage.j9hkqm99.js +0 -3
- package/dist/TasksPage.6pvkb7s7.js +0 -3
- package/dist/TelemetryPage.5zq9msb5.js +0 -3
- package/dist/TestsPage.24432yqt.js +0 -3
|
@@ -41,6 +41,13 @@ interface ConnectedAccount {
|
|
|
41
41
|
status: string;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
interface IntegrationApp {
|
|
45
|
+
id: string;
|
|
46
|
+
name: string;
|
|
47
|
+
slug: string;
|
|
48
|
+
logo: string | null;
|
|
49
|
+
}
|
|
50
|
+
|
|
44
51
|
interface Agent {
|
|
45
52
|
id: string;
|
|
46
53
|
name: string;
|
|
@@ -58,9 +65,9 @@ export function TriggersTab() {
|
|
|
58
65
|
const { authFetch } = useAuth();
|
|
59
66
|
const { currentProjectId } = useProjects();
|
|
60
67
|
|
|
61
|
-
// Provider selection
|
|
68
|
+
// Provider selection — only show configured providers
|
|
62
69
|
const [providers, setProviders] = useState<TriggerProviderInfo[]>([]);
|
|
63
|
-
const [selectedProvider, setSelectedProvider] = useState("
|
|
70
|
+
const [selectedProvider, setSelectedProvider] = useState("");
|
|
64
71
|
|
|
65
72
|
// Trigger instances (from selected provider)
|
|
66
73
|
const [triggers, setTriggers] = useState<TriggerInstance[]>([]);
|
|
@@ -83,17 +90,24 @@ export function TriggersTab() {
|
|
|
83
90
|
const [creating, setCreating] = useState(false);
|
|
84
91
|
const [createAgentId, setCreateAgentId] = useState(""); // For AgentDojo direct subscription flow
|
|
85
92
|
const [browseConfig, setBrowseConfig] = useState<Record<string, string>>({});
|
|
93
|
+
const [browseSelectedAccountId, setBrowseSelectedAccountId] = useState("");
|
|
86
94
|
|
|
87
|
-
// AgentDojo add subscription modal
|
|
95
|
+
// AgentDojo add subscription modal
|
|
88
96
|
const [showAddDojo, setShowAddDojo] = useState(false);
|
|
89
97
|
const [dojoTriggerTypes, setDojoTriggerTypes] = useState<TriggerType[]>([]);
|
|
90
98
|
const [dojoTypesLoading, setDojoTypesLoading] = useState(false);
|
|
91
99
|
const [dojoAccounts, setDojoAccounts] = useState<ConnectedAccount[]>([]);
|
|
100
|
+
const [dojoApps, setDojoApps] = useState<IntegrationApp[]>([]);
|
|
101
|
+
const [dojoSelectedToolkit, setDojoSelectedToolkit] = useState("");
|
|
92
102
|
const [dojoSelectedType, setDojoSelectedType] = useState<string>("");
|
|
93
103
|
const [dojoAgentId, setDojoAgentId] = useState("");
|
|
94
104
|
const [dojoCreating, setDojoCreating] = useState(false);
|
|
95
|
-
const [dojoTypeSearch, setDojoTypeSearch] = useState("");
|
|
96
105
|
const [dojoConfig, setDojoConfig] = useState<Record<string, string>>({});
|
|
106
|
+
const [dojoSelectedAccountId, setDojoSelectedAccountId] = useState("");
|
|
107
|
+
const [dojoAppDropdownOpen, setDojoAppDropdownOpen] = useState(false);
|
|
108
|
+
const [dojoAppSearch, setDojoAppSearch] = useState("");
|
|
109
|
+
const [dojoTriggerDropdownOpen, setDojoTriggerDropdownOpen] = useState(false);
|
|
110
|
+
const [dojoTriggerSearch, setDojoTriggerSearch] = useState("");
|
|
97
111
|
|
|
98
112
|
// Add subscription
|
|
99
113
|
const [showAddSub, setShowAddSub] = useState(false);
|
|
@@ -108,13 +122,21 @@ export function TriggersTab() {
|
|
|
108
122
|
|
|
109
123
|
const projectParam = currentProjectId && currentProjectId !== "unassigned" ? `?project_id=${currentProjectId}` : "";
|
|
110
124
|
|
|
111
|
-
// Fetch available providers
|
|
125
|
+
// Fetch available providers — only show ones with API keys configured
|
|
112
126
|
const fetchProviders = useCallback(async () => {
|
|
113
127
|
try {
|
|
114
128
|
const res = await authFetch(`/api/triggers/providers${projectParam}`);
|
|
115
129
|
if (res.ok) {
|
|
116
130
|
const data = await res.json();
|
|
117
|
-
|
|
131
|
+
const connected = (data.providers || []).filter((p: TriggerProviderInfo) => p.connected);
|
|
132
|
+
setProviders(connected);
|
|
133
|
+
// Auto-select first connected provider if none selected
|
|
134
|
+
if (connected.length > 0) {
|
|
135
|
+
setSelectedProvider(prev => {
|
|
136
|
+
if (!prev || !connected.find((p: TriggerProviderInfo) => p.id === prev)) return connected[0].id;
|
|
137
|
+
return prev;
|
|
138
|
+
});
|
|
139
|
+
}
|
|
118
140
|
}
|
|
119
141
|
} catch (e) {
|
|
120
142
|
console.error("Failed to fetch providers:", e);
|
|
@@ -154,10 +176,10 @@ export function TriggersTab() {
|
|
|
154
176
|
}
|
|
155
177
|
}, [authFetch, projectParam]);
|
|
156
178
|
|
|
157
|
-
// Fetch agents
|
|
179
|
+
// Fetch agents (project-scoped)
|
|
158
180
|
const fetchAgents = useCallback(async () => {
|
|
159
181
|
try {
|
|
160
|
-
const res = await authFetch(`/api/agents`);
|
|
182
|
+
const res = await authFetch(`/api/agents${projectParam}`);
|
|
161
183
|
if (res.ok) {
|
|
162
184
|
const data = await res.json();
|
|
163
185
|
setAgents(data.agents || []);
|
|
@@ -165,7 +187,7 @@ export function TriggersTab() {
|
|
|
165
187
|
} catch (e) {
|
|
166
188
|
// Ignore
|
|
167
189
|
}
|
|
168
|
-
}, [authFetch]);
|
|
190
|
+
}, [authFetch, projectParam]);
|
|
169
191
|
|
|
170
192
|
useEffect(() => {
|
|
171
193
|
fetchProviders();
|
|
@@ -214,6 +236,7 @@ export function TriggersTab() {
|
|
|
214
236
|
setSelectedAccountId("");
|
|
215
237
|
setCreateAgentId("");
|
|
216
238
|
setBrowseConfig({});
|
|
239
|
+
setBrowseSelectedAccountId("");
|
|
217
240
|
setShowCreate(true);
|
|
218
241
|
fetchConnectedAccounts();
|
|
219
242
|
};
|
|
@@ -224,65 +247,58 @@ export function TriggersTab() {
|
|
|
224
247
|
const openAddDojoSub = async () => {
|
|
225
248
|
setShowAddDojo(true);
|
|
226
249
|
setDojoSelectedType("");
|
|
250
|
+
setDojoSelectedToolkit("");
|
|
227
251
|
setDojoAgentId("");
|
|
228
|
-
setDojoTypeSearch("");
|
|
229
252
|
setDojoConfig({});
|
|
253
|
+
setDojoSelectedAccountId("");
|
|
230
254
|
|
|
231
|
-
console.log("[openAddDojoSub] Opening modal, selectedProvider:", selectedProvider);
|
|
232
|
-
console.log("[openAddDojoSub] currentProjectId:", currentProjectId, "projectParam:", projectParam);
|
|
233
|
-
console.log("[openAddDojoSub] existing dojoTriggerTypes:", dojoTriggerTypes.length, "dojoAccounts:", dojoAccounts.length);
|
|
234
|
-
|
|
235
|
-
// Fetch trigger types from agentdojo (same provider as Integrations tab uses)
|
|
236
255
|
const loadTypes = async () => {
|
|
237
|
-
if (dojoTriggerTypes.length > 0)
|
|
238
|
-
console.log("[openAddDojoSub] Already have", dojoTriggerTypes.length, "trigger types, skipping fetch");
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
256
|
+
if (dojoTriggerTypes.length > 0) return;
|
|
241
257
|
setDojoTypesLoading(true);
|
|
242
258
|
try {
|
|
243
259
|
let url = `/api/triggers/types?provider=agentdojo`;
|
|
244
260
|
if (currentProjectId && currentProjectId !== "unassigned") url += `&project_id=${currentProjectId}`;
|
|
245
|
-
console.log("[openAddDojoSub] Fetching trigger types:", url);
|
|
246
261
|
const res = await authFetch(url);
|
|
247
262
|
const data = await res.json();
|
|
248
|
-
console.log("[openAddDojoSub] Trigger types response:", res.status, "ok:", res.ok, "types count:", (data.types || []).length, "error:", data.error);
|
|
249
|
-
if (data.types && data.types.length > 0) {
|
|
250
|
-
console.log("[openAddDojoSub] Sample trigger type:", JSON.stringify(data.types[0]));
|
|
251
|
-
}
|
|
252
263
|
setDojoTriggerTypes(data.types || []);
|
|
253
264
|
} catch (e) {
|
|
254
|
-
console.error("
|
|
265
|
+
console.error("Failed to load trigger types:", e);
|
|
255
266
|
}
|
|
256
267
|
setDojoTypesLoading(false);
|
|
257
268
|
};
|
|
258
269
|
|
|
259
|
-
// Fetch connected accounts from agentdojo (same as Integrations tab)
|
|
260
270
|
const loadAccounts = async () => {
|
|
261
271
|
try {
|
|
262
272
|
const url = `/api/integrations/agentdojo/connected${projectParam}`;
|
|
263
|
-
console.log("[openAddDojoSub] Fetching connected accounts:", url);
|
|
264
273
|
const res = await authFetch(url);
|
|
265
274
|
const data = await res.json();
|
|
266
|
-
console.log("[openAddDojoSub] Connected accounts response:", res.status, "ok:", res.ok, "accounts count:", (data.accounts || []).length, "error:", data.error);
|
|
267
|
-
if (data.accounts && data.accounts.length > 0) {
|
|
268
|
-
console.log("[openAddDojoSub] Sample account:", JSON.stringify(data.accounts[0]));
|
|
269
|
-
}
|
|
270
275
|
const active = (data.accounts || []).filter((a: ConnectedAccount) => a.status === "active");
|
|
271
|
-
console.log("[openAddDojoSub] Active accounts:", active.length);
|
|
272
276
|
setDojoAccounts(active);
|
|
273
277
|
} catch (e) {
|
|
274
|
-
console.error("
|
|
278
|
+
console.error("Failed to load connected accounts:", e);
|
|
275
279
|
}
|
|
276
280
|
};
|
|
277
281
|
|
|
278
|
-
|
|
282
|
+
const loadApps = async () => {
|
|
283
|
+
if (dojoApps.length > 0) return;
|
|
284
|
+
try {
|
|
285
|
+
const url = `/api/integrations/agentdojo/apps${projectParam}`;
|
|
286
|
+
const res = await authFetch(url);
|
|
287
|
+
const data = await res.json();
|
|
288
|
+
setDojoApps((data.apps || []).map((a: any) => ({ id: a.id, name: a.name, slug: a.slug, logo: a.logo })));
|
|
289
|
+
} catch (e) {
|
|
290
|
+
console.error("Failed to load apps:", e);
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
await Promise.all([loadTypes(), loadAccounts(), loadApps()]);
|
|
279
295
|
};
|
|
280
296
|
|
|
281
297
|
// Create AgentDojo subscription from the add-subscription modal
|
|
282
298
|
const handleAddDojoSub = async () => {
|
|
283
299
|
const tt = dojoTriggerTypes.find(t => t.slug === dojoSelectedType);
|
|
284
|
-
|
|
285
|
-
|
|
300
|
+
// Use derived dojoMatchedAccount (respects user dropdown selection + auto-match fallback)
|
|
301
|
+
const matched = dojoMatchedAccount;
|
|
286
302
|
if (!tt || !dojoAgentId || !matched) return;
|
|
287
303
|
|
|
288
304
|
setDojoCreating(true);
|
|
@@ -298,9 +314,8 @@ export function TriggersTab() {
|
|
|
298
314
|
title: `${tt.name} → ${agent?.name || "Agent"}`,
|
|
299
315
|
server: tt.toolkit_slug,
|
|
300
316
|
agent_id: dojoAgentId,
|
|
301
|
-
...dojoConfig,
|
|
317
|
+
...dojoConfig,
|
|
302
318
|
};
|
|
303
|
-
console.log("[handleAddDojoSub] config:", JSON.stringify(configPayload));
|
|
304
319
|
const res = await authFetch(url, {
|
|
305
320
|
method: "POST",
|
|
306
321
|
headers: { "Content-Type": "application/json" },
|
|
@@ -507,28 +522,85 @@ export function TriggersTab() {
|
|
|
507
522
|
return t.name.toLowerCase().includes(s) || t.slug.toLowerCase().includes(s) || t.description.toLowerCase().includes(s);
|
|
508
523
|
});
|
|
509
524
|
|
|
510
|
-
//
|
|
525
|
+
// Best-effort match connected account from toolkit slug
|
|
526
|
+
// A single credential can serve multiple toolkits (e.g. "OmniKit Platform" for "OmniKit Messaging")
|
|
511
527
|
const matchAccount = (accounts: ConnectedAccount[], toolkitSlug: string): ConnectedAccount | null => {
|
|
512
528
|
if (!toolkitSlug || accounts.length === 0) return null;
|
|
513
|
-
const slug = toolkitSlug.toLowerCase();
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
a.
|
|
529
|
+
const slug = toolkitSlug.toLowerCase().replace(/[-_]/g, " ");
|
|
530
|
+
// Exact match
|
|
531
|
+
const exact = accounts.find(a =>
|
|
532
|
+
a.appId?.toLowerCase() === toolkitSlug.toLowerCase() ||
|
|
533
|
+
a.appName?.toLowerCase() === toolkitSlug.toLowerCase()
|
|
534
|
+
);
|
|
535
|
+
if (exact) return exact;
|
|
536
|
+
// Contains match
|
|
537
|
+
const contains = accounts.find(a =>
|
|
517
538
|
a.appId?.toLowerCase().includes(slug) ||
|
|
518
|
-
a.appName?.toLowerCase().includes(slug)
|
|
519
|
-
|
|
539
|
+
a.appName?.toLowerCase().replace(/[-_]/g, " ").includes(slug) ||
|
|
540
|
+
slug.includes(a.appId?.toLowerCase() || "") ||
|
|
541
|
+
slug.includes(a.appName?.toLowerCase().replace(/[-_]/g, " ") || "")
|
|
542
|
+
);
|
|
543
|
+
if (contains) return contains;
|
|
544
|
+
// Prefix match — first word overlap (e.g. "omnikit" matches "omnikit platform" and "omnikit messaging")
|
|
545
|
+
const slugWords = slug.split(/\s+/);
|
|
546
|
+
return accounts.find(a => {
|
|
547
|
+
const nameWords = (a.appName || "").toLowerCase().replace(/[-_]/g, " ").split(/\s+/);
|
|
548
|
+
return slugWords[0] && nameWords[0] && slugWords[0] === nameWords[0];
|
|
549
|
+
}) || null;
|
|
520
550
|
};
|
|
521
551
|
|
|
522
|
-
// Derived: auto-matched account for Add Subscription modal
|
|
552
|
+
// Derived: auto-matched or user-selected account for Add Subscription modal
|
|
523
553
|
const dojoSelectedTriggerType = dojoTriggerTypes.find(t => t.slug === dojoSelectedType);
|
|
524
|
-
const
|
|
554
|
+
const dojoAutoMatch = dojoSelectedTriggerType ? matchAccount(dojoAccounts, dojoSelectedTriggerType.toolkit_slug) : null;
|
|
555
|
+
const dojoMatchedAccount = dojoSelectedAccountId
|
|
556
|
+
? dojoAccounts.find(a => a.id === dojoSelectedAccountId) || dojoAutoMatch
|
|
557
|
+
: dojoAutoMatch;
|
|
558
|
+
|
|
559
|
+
// Derived: auto-matched or user-selected account for Browse Subscribe modal
|
|
560
|
+
const browseAutoMatch = selectedType && isAgentDojo ? matchAccount(connectedAccounts, selectedType.toolkit_slug) : null;
|
|
561
|
+
const browseMatchedAccount = browseSelectedAccountId
|
|
562
|
+
? connectedAccounts.find(a => a.id === browseSelectedAccountId) || browseAutoMatch
|
|
563
|
+
: browseAutoMatch;
|
|
564
|
+
|
|
565
|
+
// Derived: group trigger types by toolkit, enriched with logos from apps list
|
|
566
|
+
const dojoToolkits = React.useMemo(() => {
|
|
567
|
+
const appLogos = new Map<string, string>();
|
|
568
|
+
for (const app of dojoApps) {
|
|
569
|
+
if (app.logo) appLogos.set(app.slug, app.logo);
|
|
570
|
+
}
|
|
571
|
+
const map = new Map<string, { slug: string; name: string; logo: string | null; count: number }>();
|
|
572
|
+
for (const t of dojoTriggerTypes) {
|
|
573
|
+
const existing = map.get(t.toolkit_slug);
|
|
574
|
+
if (existing) {
|
|
575
|
+
existing.count++;
|
|
576
|
+
} else {
|
|
577
|
+
const logo = appLogos.get(t.toolkit_slug) || t.logo || null;
|
|
578
|
+
map.set(t.toolkit_slug, { slug: t.toolkit_slug, name: t.toolkit_name, logo, count: 1 });
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return Array.from(map.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
582
|
+
}, [dojoTriggerTypes, dojoApps]);
|
|
583
|
+
|
|
584
|
+
// Derived: triggers for the selected toolkit
|
|
585
|
+
const dojoToolkitTriggers = dojoSelectedToolkit
|
|
586
|
+
? dojoTriggerTypes.filter(t => t.toolkit_slug === dojoSelectedToolkit)
|
|
587
|
+
: [];
|
|
525
588
|
|
|
526
|
-
// Derived:
|
|
527
|
-
const
|
|
589
|
+
// Derived: selected toolkit info (for logo in trigger dropdown)
|
|
590
|
+
const dojoSelectedToolkitInfo = dojoToolkits.find(t => t.slug === dojoSelectedToolkit);
|
|
528
591
|
|
|
529
592
|
// Agent map for quick lookups
|
|
530
593
|
const agentMap = new Map(agents.map(a => [a.id, a]));
|
|
531
594
|
|
|
595
|
+
if (providers.length === 0 && !triggersLoading) {
|
|
596
|
+
return (
|
|
597
|
+
<div className="bg-[#111] border border-[#1a1a1a] rounded-lg p-8 text-center">
|
|
598
|
+
<p className="text-[#666]">No trigger providers configured.</p>
|
|
599
|
+
<p className="text-sm text-[#555] mt-1">Add API keys for Composio or AgentDojo in Settings to enable triggers.</p>
|
|
600
|
+
</div>
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
|
|
532
604
|
return (
|
|
533
605
|
<div className="space-y-6">
|
|
534
606
|
{/* Error */}
|
|
@@ -539,7 +611,7 @@ export function TriggersTab() {
|
|
|
539
611
|
</div>
|
|
540
612
|
)}
|
|
541
613
|
|
|
542
|
-
{/* Provider Selector */}
|
|
614
|
+
{/* Provider Selector — only show if multiple configured */}
|
|
543
615
|
{providers.length > 1 && (
|
|
544
616
|
<div className="flex items-center gap-2">
|
|
545
617
|
<span className="text-xs text-[#666]">Provider:</span>
|
|
@@ -560,9 +632,6 @@ export function TriggersTab() {
|
|
|
560
632
|
}`}
|
|
561
633
|
>
|
|
562
634
|
{p.name}
|
|
563
|
-
{!p.connected && (
|
|
564
|
-
<span className="ml-1 text-[10px] text-yellow-500" title="API key not configured">!</span>
|
|
565
|
-
)}
|
|
566
635
|
</button>
|
|
567
636
|
))}
|
|
568
637
|
</div>
|
|
@@ -1021,127 +1090,235 @@ export function TriggersTab() {
|
|
|
1021
1090
|
<div className="bg-[#111] border border-[#333] rounded-lg p-6 w-full max-w-lg mx-4">
|
|
1022
1091
|
<h3 className="font-medium mb-1">Add Subscription</h3>
|
|
1023
1092
|
<p className="text-xs text-[#666] mb-4">
|
|
1024
|
-
Select
|
|
1093
|
+
Select an app and trigger, then route it to an agent.
|
|
1025
1094
|
</p>
|
|
1026
1095
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1096
|
+
{dojoTypesLoading ? (
|
|
1097
|
+
<div className="text-center py-8 text-[#666] text-sm">Loading...</div>
|
|
1098
|
+
) : dojoTriggerTypes.length === 0 ? (
|
|
1099
|
+
<div className="text-center py-8 text-[#666] text-sm">
|
|
1100
|
+
No triggers available. Connect an app first in the Integrations tab.
|
|
1101
|
+
</div>
|
|
1102
|
+
) : (
|
|
1103
|
+
<div className="space-y-4">
|
|
1104
|
+
{/* App selector — custom dropdown with logos */}
|
|
1105
|
+
<div>
|
|
1106
|
+
<label className="block text-xs text-[#888] mb-1.5">App</label>
|
|
1107
|
+
<div className="relative">
|
|
1108
|
+
<button
|
|
1109
|
+
onClick={() => { setDojoAppDropdownOpen(!dojoAppDropdownOpen); setDojoTriggerDropdownOpen(false); setDojoAppSearch(""); }}
|
|
1110
|
+
className="w-full flex items-center gap-2 bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 text-sm text-left hover:border-[#555] transition"
|
|
1111
|
+
>
|
|
1112
|
+
{dojoSelectedToolkitInfo ? (
|
|
1113
|
+
<>
|
|
1114
|
+
{dojoSelectedToolkitInfo.logo ? (
|
|
1115
|
+
<img src={dojoSelectedToolkitInfo.logo} alt="" className="w-5 h-5 rounded object-contain flex-shrink-0" />
|
|
1116
|
+
) : (
|
|
1117
|
+
<div className="w-5 h-5 rounded bg-[#1a1a1a] flex items-center justify-center text-[10px] flex-shrink-0">
|
|
1118
|
+
{dojoSelectedToolkitInfo.name?.[0]?.toUpperCase() || "?"}
|
|
1119
|
+
</div>
|
|
1120
|
+
)}
|
|
1121
|
+
<span className="flex-1 truncate">{dojoSelectedToolkitInfo.name}</span>
|
|
1122
|
+
<span className="text-[10px] text-[#666]">{dojoSelectedToolkitInfo.count} triggers</span>
|
|
1123
|
+
</>
|
|
1124
|
+
) : (
|
|
1125
|
+
<span className="text-[#666] flex-1">Select app...</span>
|
|
1126
|
+
)}
|
|
1127
|
+
<span className="text-[#666] text-xs ml-1">▾</span>
|
|
1128
|
+
</button>
|
|
1129
|
+
{dojoAppDropdownOpen && (
|
|
1130
|
+
<>
|
|
1131
|
+
<div className="fixed inset-0 z-10" onClick={() => setDojoAppDropdownOpen(false)} />
|
|
1132
|
+
<div className="absolute left-0 right-0 top-full mt-1 bg-[#0a0a0a] border border-[#333] rounded-lg shadow-xl z-20 max-h-64 flex flex-col">
|
|
1133
|
+
<div className="p-2 border-b border-[#1a1a1a] flex-shrink-0">
|
|
1134
|
+
<input
|
|
1135
|
+
type="text"
|
|
1136
|
+
value={dojoAppSearch}
|
|
1137
|
+
onChange={(e) => setDojoAppSearch(e.target.value)}
|
|
1138
|
+
placeholder="Search apps..."
|
|
1139
|
+
className="w-full bg-[#111] border border-[#333] rounded px-2 py-1.5 text-sm focus:outline-none focus:border-[#f97316]"
|
|
1140
|
+
autoFocus
|
|
1141
|
+
/>
|
|
1142
|
+
</div>
|
|
1143
|
+
<div className="overflow-y-auto flex-1">
|
|
1144
|
+
{dojoToolkits
|
|
1145
|
+
.filter(tk => {
|
|
1146
|
+
if (!dojoAppSearch) return true;
|
|
1147
|
+
const s = dojoAppSearch.toLowerCase();
|
|
1148
|
+
return tk.name.toLowerCase().includes(s) || tk.slug.toLowerCase().includes(s);
|
|
1149
|
+
})
|
|
1150
|
+
.map(tk => (
|
|
1151
|
+
<button
|
|
1152
|
+
key={tk.slug}
|
|
1153
|
+
onClick={() => {
|
|
1154
|
+
setDojoSelectedToolkit(tk.slug);
|
|
1155
|
+
setDojoSelectedType("");
|
|
1156
|
+
setDojoConfig({});
|
|
1157
|
+
setDojoAppDropdownOpen(false);
|
|
1158
|
+
}}
|
|
1159
|
+
className={`w-full flex items-center gap-2 px-3 py-2 text-sm text-left transition hover:bg-[#1a1a1a] ${
|
|
1160
|
+
dojoSelectedToolkit === tk.slug ? "bg-[#1a1a1a] text-[#f97316]" : ""
|
|
1161
|
+
}`}
|
|
1162
|
+
>
|
|
1163
|
+
{tk.logo ? (
|
|
1164
|
+
<img src={tk.logo} alt="" className="w-5 h-5 rounded object-contain flex-shrink-0" />
|
|
1165
|
+
) : (
|
|
1166
|
+
<div className="w-5 h-5 rounded bg-[#1a1a1a] flex items-center justify-center text-[10px] flex-shrink-0">
|
|
1167
|
+
{tk.name?.[0]?.toUpperCase() || "?"}
|
|
1168
|
+
</div>
|
|
1169
|
+
)}
|
|
1170
|
+
<span className="flex-1 truncate">{tk.name}</span>
|
|
1171
|
+
<span className="text-[10px] text-[#666]">{tk.count}</span>
|
|
1172
|
+
</button>
|
|
1173
|
+
))}
|
|
1174
|
+
</div>
|
|
1175
|
+
</div>
|
|
1176
|
+
</>
|
|
1177
|
+
)}
|
|
1036
1178
|
</div>
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
<div className="
|
|
1068
|
-
|
|
1179
|
+
</div>
|
|
1180
|
+
|
|
1181
|
+
{/* Trigger selector — only shown when app is selected */}
|
|
1182
|
+
{dojoSelectedToolkit && (
|
|
1183
|
+
<div>
|
|
1184
|
+
<label className="block text-xs text-[#888] mb-1.5">Trigger</label>
|
|
1185
|
+
<div className="relative">
|
|
1186
|
+
<button
|
|
1187
|
+
onClick={() => { setDojoTriggerDropdownOpen(!dojoTriggerDropdownOpen); setDojoAppDropdownOpen(false); setDojoTriggerSearch(""); }}
|
|
1188
|
+
className="w-full flex items-center gap-2 bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 text-sm text-left hover:border-[#555] transition"
|
|
1189
|
+
>
|
|
1190
|
+
{dojoSelectedTriggerType ? (
|
|
1191
|
+
<>
|
|
1192
|
+
<span className="flex-1 truncate">{dojoSelectedTriggerType.name}</span>
|
|
1193
|
+
<span className={`text-[10px] px-1.5 py-0.5 rounded flex-shrink-0 ${
|
|
1194
|
+
dojoSelectedTriggerType.type === "webhook" ? "bg-blue-500/10 text-blue-400" : "bg-yellow-500/10 text-yellow-400"
|
|
1195
|
+
}`}>
|
|
1196
|
+
{dojoSelectedTriggerType.type}
|
|
1197
|
+
</span>
|
|
1198
|
+
</>
|
|
1199
|
+
) : (
|
|
1200
|
+
<span className="text-[#666] flex-1">Select trigger...</span>
|
|
1201
|
+
)}
|
|
1202
|
+
<span className="text-[#666] text-xs ml-1">▾</span>
|
|
1203
|
+
</button>
|
|
1204
|
+
{dojoTriggerDropdownOpen && (
|
|
1205
|
+
<>
|
|
1206
|
+
<div className="fixed inset-0 z-10" onClick={() => setDojoTriggerDropdownOpen(false)} />
|
|
1207
|
+
<div className="absolute left-0 right-0 top-full mt-1 bg-[#0a0a0a] border border-[#333] rounded-lg shadow-xl z-20 max-h-64 flex flex-col">
|
|
1208
|
+
{dojoToolkitTriggers.length > 3 && (
|
|
1209
|
+
<div className="p-2 border-b border-[#1a1a1a] flex-shrink-0">
|
|
1210
|
+
<input
|
|
1211
|
+
type="text"
|
|
1212
|
+
value={dojoTriggerSearch}
|
|
1213
|
+
onChange={(e) => setDojoTriggerSearch(e.target.value)}
|
|
1214
|
+
placeholder="Search triggers..."
|
|
1215
|
+
className="w-full bg-[#111] border border-[#333] rounded px-2 py-1.5 text-sm focus:outline-none focus:border-[#f97316]"
|
|
1216
|
+
autoFocus
|
|
1217
|
+
/>
|
|
1069
1218
|
</div>
|
|
1070
1219
|
)}
|
|
1071
|
-
<div className="flex-1
|
|
1072
|
-
|
|
1073
|
-
|
|
1220
|
+
<div className="overflow-y-auto flex-1">
|
|
1221
|
+
{dojoToolkitTriggers
|
|
1222
|
+
.filter(t => {
|
|
1223
|
+
if (!dojoTriggerSearch) return true;
|
|
1224
|
+
const s = dojoTriggerSearch.toLowerCase();
|
|
1225
|
+
return t.name.toLowerCase().includes(s) || t.slug.toLowerCase().includes(s) || t.description.toLowerCase().includes(s);
|
|
1226
|
+
})
|
|
1227
|
+
.map(t => (
|
|
1228
|
+
<button
|
|
1229
|
+
key={t.slug}
|
|
1230
|
+
onClick={() => {
|
|
1231
|
+
setDojoSelectedType(t.slug);
|
|
1232
|
+
setDojoConfig({});
|
|
1233
|
+
setDojoTriggerDropdownOpen(false);
|
|
1234
|
+
}}
|
|
1235
|
+
className={`w-full flex items-center gap-2 px-3 py-2 text-sm text-left transition hover:bg-[#1a1a1a] ${
|
|
1236
|
+
dojoSelectedType === t.slug ? "bg-[#1a1a1a] text-[#f97316]" : ""
|
|
1237
|
+
}`}
|
|
1238
|
+
>
|
|
1239
|
+
<div className="flex-1 min-w-0">
|
|
1240
|
+
<div className="truncate">{t.name}</div>
|
|
1241
|
+
<div className="text-[10px] text-[#666] truncate">{t.description}</div>
|
|
1242
|
+
</div>
|
|
1243
|
+
<span className={`text-[10px] px-1.5 py-0.5 rounded flex-shrink-0 ${
|
|
1244
|
+
t.type === "webhook" ? "bg-blue-500/10 text-blue-400" : "bg-yellow-500/10 text-yellow-400"
|
|
1245
|
+
}`}>
|
|
1246
|
+
{t.type}
|
|
1247
|
+
</span>
|
|
1248
|
+
</button>
|
|
1249
|
+
))}
|
|
1074
1250
|
</div>
|
|
1075
|
-
</
|
|
1076
|
-
|
|
1251
|
+
</div>
|
|
1252
|
+
</>
|
|
1253
|
+
)}
|
|
1077
1254
|
</div>
|
|
1078
|
-
|
|
1255
|
+
</div>
|
|
1256
|
+
)}
|
|
1257
|
+
|
|
1258
|
+
{/* Connected account — auto-matched */}
|
|
1259
|
+
{dojoSelectedType && (
|
|
1260
|
+
<div>
|
|
1261
|
+
<label className="block text-xs text-[#888] mb-1.5">Connected Account</label>
|
|
1262
|
+
{dojoMatchedAccount ? (
|
|
1263
|
+
<div className="text-xs text-green-400 bg-green-500/10 border border-green-500/20 rounded p-3">
|
|
1264
|
+
Connected: {dojoMatchedAccount.appName}
|
|
1265
|
+
</div>
|
|
1266
|
+
) : (
|
|
1267
|
+
<div className="text-xs text-yellow-400 bg-yellow-500/10 border border-yellow-500/20 rounded p-3">
|
|
1268
|
+
No connected account for {dojoSelectedTriggerType?.toolkit_name || "this app"}. Connect it first in the Integrations tab.
|
|
1269
|
+
</div>
|
|
1270
|
+
)}
|
|
1271
|
+
</div>
|
|
1079
1272
|
)}
|
|
1080
|
-
</div>
|
|
1081
1273
|
|
|
1082
|
-
|
|
1083
|
-
|
|
1274
|
+
{/* Dynamic config fields from config_schema */}
|
|
1275
|
+
{dojoSelectedTriggerType && dojoSelectedTriggerType.config_schema && Object.keys(dojoSelectedTriggerType.config_schema.properties || {}).length > 0 && (
|
|
1276
|
+
<div>
|
|
1277
|
+
<label className="block text-xs text-[#888] mb-1.5">Configuration</label>
|
|
1278
|
+
<div className="space-y-2">
|
|
1279
|
+
{Object.entries((dojoSelectedTriggerType.config_schema as any).properties || {}).map(([key, schema]: [string, any]) => {
|
|
1280
|
+
const required = ((dojoSelectedTriggerType.config_schema as any).required || []).includes(key);
|
|
1281
|
+
return (
|
|
1282
|
+
<div key={key}>
|
|
1283
|
+
<label className="block text-[11px] text-[#888] mb-1">
|
|
1284
|
+
{schema.title || key}
|
|
1285
|
+
{required && <span className="text-red-400 ml-0.5">*</span>}
|
|
1286
|
+
</label>
|
|
1287
|
+
<input
|
|
1288
|
+
type="text"
|
|
1289
|
+
value={dojoConfig[key] || ""}
|
|
1290
|
+
onChange={(e) => setDojoConfig(prev => ({ ...prev, [key]: e.target.value }))}
|
|
1291
|
+
placeholder={schema.description || `Enter ${schema.title || key}...`}
|
|
1292
|
+
className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 text-sm focus:outline-none focus:border-[#f97316]"
|
|
1293
|
+
/>
|
|
1294
|
+
</div>
|
|
1295
|
+
);
|
|
1296
|
+
})}
|
|
1297
|
+
</div>
|
|
1298
|
+
</div>
|
|
1299
|
+
)}
|
|
1300
|
+
|
|
1301
|
+
{/* Agent selection */}
|
|
1084
1302
|
<div>
|
|
1085
|
-
<label className="block text-xs text-[#888] mb-1.5">
|
|
1086
|
-
{
|
|
1087
|
-
<div className="text-xs text-
|
|
1088
|
-
|
|
1303
|
+
<label className="block text-xs text-[#888] mb-1.5">Target Agent</label>
|
|
1304
|
+
{agents.length === 0 ? (
|
|
1305
|
+
<div className="text-xs text-[#666] bg-[#0a0a0a] rounded p-3">
|
|
1306
|
+
No agents available. Create an agent first.
|
|
1089
1307
|
</div>
|
|
1090
1308
|
) : (
|
|
1091
|
-
<
|
|
1092
|
-
|
|
1093
|
-
|
|
1309
|
+
<Select
|
|
1310
|
+
value={dojoAgentId}
|
|
1311
|
+
onChange={setDojoAgentId}
|
|
1312
|
+
placeholder="Select agent..."
|
|
1313
|
+
options={agents.map(agent => ({
|
|
1314
|
+
value: agent.id,
|
|
1315
|
+
label: `${agent.name} (${agent.status})`,
|
|
1316
|
+
}))}
|
|
1317
|
+
/>
|
|
1094
1318
|
)}
|
|
1095
1319
|
</div>
|
|
1096
|
-
)}
|
|
1097
|
-
|
|
1098
|
-
{/* Dynamic config fields from config_schema */}
|
|
1099
|
-
{dojoSelectedTriggerType && dojoSelectedTriggerType.config_schema && Object.keys(dojoSelectedTriggerType.config_schema.properties || {}).length > 0 && (
|
|
1100
|
-
<div>
|
|
1101
|
-
<label className="block text-xs text-[#888] mb-1.5">Configuration</label>
|
|
1102
|
-
<div className="space-y-2">
|
|
1103
|
-
{Object.entries((dojoSelectedTriggerType.config_schema as any).properties || {}).map(([key, schema]: [string, any]) => {
|
|
1104
|
-
const required = ((dojoSelectedTriggerType.config_schema as any).required || []).includes(key);
|
|
1105
|
-
return (
|
|
1106
|
-
<div key={key}>
|
|
1107
|
-
<label className="block text-[11px] text-[#888] mb-1">
|
|
1108
|
-
{schema.title || key}
|
|
1109
|
-
{required && <span className="text-red-400 ml-0.5">*</span>}
|
|
1110
|
-
</label>
|
|
1111
|
-
<input
|
|
1112
|
-
type="text"
|
|
1113
|
-
value={dojoConfig[key] || ""}
|
|
1114
|
-
onChange={(e) => setDojoConfig(prev => ({ ...prev, [key]: e.target.value }))}
|
|
1115
|
-
placeholder={schema.description || `Enter ${schema.title || key}...`}
|
|
1116
|
-
className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 text-sm focus:outline-none focus:border-[#f97316]"
|
|
1117
|
-
/>
|
|
1118
|
-
</div>
|
|
1119
|
-
);
|
|
1120
|
-
})}
|
|
1121
|
-
</div>
|
|
1122
|
-
</div>
|
|
1123
|
-
)}
|
|
1124
|
-
|
|
1125
|
-
{/* Agent selection */}
|
|
1126
|
-
<div>
|
|
1127
|
-
<label className="block text-xs text-[#888] mb-1.5">Target Agent</label>
|
|
1128
|
-
{agents.length === 0 ? (
|
|
1129
|
-
<div className="text-xs text-[#666] bg-[#0a0a0a] rounded p-3">
|
|
1130
|
-
No agents available. Create an agent first.
|
|
1131
|
-
</div>
|
|
1132
|
-
) : (
|
|
1133
|
-
<Select
|
|
1134
|
-
value={dojoAgentId}
|
|
1135
|
-
onChange={setDojoAgentId}
|
|
1136
|
-
placeholder="Select agent..."
|
|
1137
|
-
options={agents.map(agent => ({
|
|
1138
|
-
value: agent.id,
|
|
1139
|
-
label: `${agent.name} (${agent.status})`,
|
|
1140
|
-
}))}
|
|
1141
|
-
/>
|
|
1142
|
-
)}
|
|
1143
1320
|
</div>
|
|
1144
|
-
|
|
1321
|
+
)}
|
|
1145
1322
|
|
|
1146
1323
|
<div className="flex gap-2 mt-5">
|
|
1147
1324
|
<button
|