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.
Files changed (67) hide show
  1. package/dist/ActivityPage.41nbye4r.js +3 -0
  2. package/dist/{ApiDocsPage.kf6bbwkk.js → ApiDocsPage.4smnt8m3.js} +2 -2
  3. package/dist/{App.jfx3der4.js → App.0sbax9et.js} +3 -3
  4. package/dist/App.0ws427h8.js +4 -0
  5. package/dist/App.4ehxpt48.js +4 -0
  6. package/dist/App.6q6bar8b.js +4 -0
  7. package/dist/App.ca1rz1ph.js +4 -0
  8. package/dist/{App.7v1w3ys9.js → App.ensa6z0r.js} +3 -3
  9. package/dist/{App.n4jb3c22.js → App.f8g7tych.js} +3 -3
  10. package/dist/App.kh7d2xj3.js +267 -0
  11. package/dist/App.mvtqv6qc.js +20 -0
  12. package/dist/{App.c90t3dxg.js → App.ncgc9cxy.js} +3 -3
  13. package/dist/{App.039re6cf.js → App.p0fb1pds.js} +3 -3
  14. package/dist/App.pmaq48sj.js +4 -0
  15. package/dist/{App.2yy66bnp.js → App.yv87t9m5.js} +3 -3
  16. package/dist/App.zjmfm8p6.js +4 -0
  17. package/dist/ConnectionsPage.anb3rv9a.js +3 -0
  18. package/dist/McpPage.y396h6fy.js +3 -0
  19. package/dist/SettingsPage.5k6vp396.js +3 -0
  20. package/dist/SkillsPage.yj3xdsay.js +3 -0
  21. package/dist/TasksPage.sjv0khtv.js +3 -0
  22. package/dist/TelemetryPage.2qm4w16r.js +3 -0
  23. package/dist/TestsPage.zzs4qfj8.js +3 -0
  24. package/dist/index.html +1 -1
  25. package/dist/styles.css +1 -1
  26. package/package.json +2 -2
  27. package/src/channels/telegram.ts +5 -0
  28. package/src/crypto.ts +13 -4
  29. package/src/db.ts +25 -2
  30. package/src/integrations/agentdojo.ts +1 -1
  31. package/src/providers.ts +46 -0
  32. package/src/routes/api/agent-utils.ts +64 -9
  33. package/src/routes/api/agents.ts +41 -13
  34. package/src/routes/api/integrations.ts +16 -6
  35. package/src/routes/api/mcp.ts +7 -0
  36. package/src/routes/api/triggers.ts +45 -5
  37. package/src/web/App.tsx +1 -0
  38. package/src/web/components/activity/ActivityPage.tsx +349 -214
  39. package/src/web/components/agents/AgentCard.tsx +37 -8
  40. package/src/web/components/agents/AgentPanel.tsx +268 -23
  41. package/src/web/components/connections/IntegrationsTab.tsx +57 -31
  42. package/src/web/components/connections/TriggersTab.tsx +336 -159
  43. package/src/web/components/dashboard/Dashboard.tsx +39 -7
  44. package/src/web/components/layout/Header.tsx +0 -34
  45. package/src/web/components/layout/Sidebar.tsx +43 -3
  46. package/src/web/components/mcp/McpPage.tsx +16 -5
  47. package/src/web/components/settings/SettingsPage.tsx +279 -30
  48. package/src/web/components/tasks/TasksPage.tsx +32 -6
  49. package/src/web/context/ProjectContext.tsx +5 -0
  50. package/src/web/context/TelemetryContext.tsx +14 -0
  51. package/src/web/types.ts +20 -2
  52. package/dist/ActivityPage.h769ek3a.js +0 -3
  53. package/dist/App.2jmkqm8c.js +0 -4
  54. package/dist/App.3515wsb4.js +0 -4
  55. package/dist/App.edwahsvz.js +0 -4
  56. package/dist/App.q3bpx15d.js +0 -20
  57. package/dist/App.r0a2nmqs.js +0 -267
  58. package/dist/App.s2yrcz15.js +0 -4
  59. package/dist/App.s5j82a5j.js +0 -4
  60. package/dist/App.tg1b94tx.js +0 -4
  61. package/dist/ConnectionsPage.a67fjgbf.js +0 -3
  62. package/dist/McpPage.d4p3xvtk.js +0 -3
  63. package/dist/SettingsPage.46sqpe39.js +0 -3
  64. package/dist/SkillsPage.j9hkqm99.js +0 -3
  65. package/dist/TasksPage.6pvkb7s7.js +0 -3
  66. package/dist/TelemetryPage.5zq9msb5.js +0 -3
  67. package/dist/TestsPage.24432yqt.js +0 -3
@@ -59,7 +59,7 @@ export function Dashboard({
59
59
 
60
60
  if (tasksRes.ok) {
61
61
  const data = await tasksRes.json();
62
- setRecentTasks((data.tasks || []).slice(0, 5));
62
+ setRecentTasks(data.tasks || []);
63
63
  }
64
64
 
65
65
  if (activityRes.ok) {
@@ -75,10 +75,12 @@ export function Dashboard({
75
75
  fetchDashboardData();
76
76
  }, [fetchDashboardData, statusChangeCounter]);
77
77
 
78
- // Filter tasks by project agents
78
+ // Filter tasks by project agents and sort by next execution (soonest first)
79
79
  const filteredTasks = useMemo(() => {
80
- if (!currentProjectId) return recentTasks;
81
- return recentTasks.filter(t => projectAgentIds.has(t.agentId));
80
+ let list = currentProjectId
81
+ ? recentTasks.filter(t => projectAgentIds.has(t.agentId))
82
+ : recentTasks;
83
+ return sortTasksByNextExecution(list);
82
84
  }, [recentTasks, currentProjectId, projectAgentIds]);
83
85
 
84
86
  // Calculate task stats from filtered tasks
@@ -179,9 +181,9 @@ export function Dashboard({
179
181
  )}
180
182
  </DashboardCard>
181
183
 
182
- {/* Recent Tasks */}
184
+ {/* Tasks */}
183
185
  <DashboardCard
184
- title="Recent Tasks"
186
+ title="Tasks"
185
187
  actionLabel="View All"
186
188
  onAction={() => onNavigate("tasks")}
187
189
  >
@@ -192,7 +194,7 @@ export function Dashboard({
192
194
  </div>
193
195
  ) : (
194
196
  <div className="divide-y divide-[#1a1a1a]">
195
- {filteredTasks.map((task) => (
197
+ {filteredTasks.slice(0, 5).map((task) => (
196
198
  <div
197
199
  key={`${task.agentId}-${task.id}`}
198
200
  className="px-4 py-3 flex items-center justify-between"
@@ -343,6 +345,36 @@ function TaskStatusBadge({ status }: { status: Task["status"] }) {
343
345
  );
344
346
  }
345
347
 
348
+ // --- Task sorting helper ---
349
+
350
+ function statusPriority(task: Task): number {
351
+ if (task.status === "running") return 0;
352
+ if (task.status === "pending") return 1;
353
+ if (task.status === "completed") return 2;
354
+ if (task.status === "failed") return 3;
355
+ return 4; // cancelled etc
356
+ }
357
+
358
+ function sortTasksByNextExecution(tasks: Task[]): Task[] {
359
+ return [...tasks].sort((a, b) => {
360
+ const aPri = statusPriority(a);
361
+ const bPri = statusPriority(b);
362
+ if (aPri !== bPri) return aPri - bPri;
363
+ // Within running/pending: soonest next execution first
364
+ if (aPri <= 1) {
365
+ const aTime = a.next_run || a.execute_at || null;
366
+ const bTime = b.next_run || b.execute_at || null;
367
+ const aTs = aTime ? new Date(aTime).getTime() : Infinity;
368
+ const bTs = bTime ? new Date(bTime).getTime() : Infinity;
369
+ return aTs - bTs;
370
+ }
371
+ // Within completed/failed: most recent first
372
+ const aDate = a.completed_at || a.executed_at || a.created_at;
373
+ const bDate = b.completed_at || b.executed_at || b.created_at;
374
+ return new Date(bDate).getTime() - new Date(aDate).getTime();
375
+ });
376
+ }
377
+
346
378
  // --- Schedule formatting helpers (compact versions for dashboard) ---
347
379
 
348
380
  const DASH_DAY_NAMES = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
@@ -22,10 +22,8 @@ interface HeaderProps {
22
22
 
23
23
  export function Header({ onMenuClick, agents = [] }: HeaderProps) {
24
24
  const { connected } = useTelemetryContext();
25
- const { user, logout } = useAuth();
26
25
  const authHeaders = useAuthHeaders();
27
26
  const { projects, currentProjectId, currentProject, setCurrentProjectId, unassignedCount, projectsEnabled } = useProjects();
28
- const [showUserMenu, setShowUserMenu] = useState(false);
29
27
  const [showProjectMenu, setShowProjectMenu] = useState(false);
30
28
  const [showNotifications, setShowNotifications] = useState(false);
31
29
  const [unseenCount, setUnseenCount] = useState(0);
@@ -124,11 +122,6 @@ export function Header({ onMenuClick, agents = [] }: HeaderProps) {
124
122
  }
125
123
  }, [showNotifications, unseenCount, accessToken, projectAgentIds]);
126
124
 
127
- const handleLogout = async () => {
128
- await logout();
129
- setShowUserMenu(false);
130
- };
131
-
132
125
  const handleProjectSelect = (projectId: string | null) => {
133
126
  setCurrentProjectId(projectId);
134
127
  setShowProjectMenu(false);
@@ -305,33 +298,6 @@ export function Header({ onMenuClick, agents = [] }: HeaderProps) {
305
298
  )}
306
299
  </div>
307
300
  <MetaAgentButton />
308
- {user && (
309
- <div className="relative">
310
- <button
311
- onClick={() => setShowUserMenu(!showUserMenu)}
312
- className="flex items-center gap-2 px-2 md:px-3 py-2 rounded hover:bg-[#1a1a1a] transition"
313
- >
314
- <div className="w-8 h-8 rounded-full bg-[#f97316] flex items-center justify-center text-black font-medium text-sm">
315
- {user.username.charAt(0).toUpperCase()}
316
- </div>
317
- <span className="text-sm text-[#888] hidden sm:block">{user.username}</span>
318
- </button>
319
- {showUserMenu && (
320
- <div className="absolute right-0 top-full mt-1 w-48 bg-[#111] border border-[#222] rounded-lg shadow-xl z-50">
321
- <div className="px-4 py-3 border-b border-[#222]">
322
- <p className="text-sm font-medium">{user.username}</p>
323
- <p className="text-xs text-[#f97316] mt-1">{user.role}</p>
324
- </div>
325
- <button
326
- onClick={handleLogout}
327
- className="w-full px-4 py-2 text-left text-sm text-red-400 hover:bg-[#1a1a1a] transition"
328
- >
329
- Sign out
330
- </button>
331
- </div>
332
- )}
333
- </div>
334
- )}
335
301
  </div>
336
302
  </div>
337
303
  </header>
@@ -1,5 +1,6 @@
1
- import React from "react";
1
+ import React, { useState } from "react";
2
2
  import { DashboardIcon, ActivityIcon, AgentsIcon, TasksIcon, ConnectionsIcon, McpIcon, SkillsIcon, TestsIcon, TelemetryIcon, ApiIcon, SettingsIcon, CloseIcon } from "../common/Icons";
3
+ import { useAuth } from "../../context";
3
4
  import type { Route } from "../../types";
4
5
 
5
6
  interface SidebarProps {
@@ -12,11 +13,19 @@ interface SidebarProps {
12
13
  }
13
14
 
14
15
  export function Sidebar({ route, agentCount, taskCount, onNavigate, isOpen, onClose }: SidebarProps) {
16
+ const { user, logout } = useAuth();
17
+ const [showUserMenu, setShowUserMenu] = useState(false);
18
+
15
19
  const handleNavigate = (newRoute: Route) => {
16
20
  onNavigate(newRoute);
17
21
  onClose?.();
18
22
  };
19
23
 
24
+ const handleLogout = async () => {
25
+ await logout();
26
+ setShowUserMenu(false);
27
+ };
28
+
20
29
  return (
21
30
  <>
22
31
  {/* Mobile overlay backdrop */}
@@ -30,7 +39,7 @@ export function Sidebar({ route, agentCount, taskCount, onNavigate, isOpen, onCl
30
39
  {/* Sidebar - hidden on mobile unless open, always visible on md+ */}
31
40
  <aside
32
41
  className={`
33
- fixed inset-y-0 left-0 z-50 w-64 bg-[#0a0a0a] border-r border-[#1a1a1a] p-4 transform transition-transform duration-200 ease-in-out
42
+ fixed inset-y-0 left-0 z-50 w-64 bg-[#0a0a0a] border-r border-[#1a1a1a] p-4 flex flex-col transform transition-transform duration-200 ease-in-out
34
43
  md:relative md:w-56 md:translate-x-0 md:z-auto
35
44
  ${isOpen ? "translate-x-0" : "-translate-x-full"}
36
45
  `}
@@ -49,7 +58,7 @@ export function Sidebar({ route, agentCount, taskCount, onNavigate, isOpen, onCl
49
58
  </button>
50
59
  </div>
51
60
 
52
- <nav className="space-y-1">
61
+ <nav className="space-y-1 flex-1">
53
62
  <NavButton
54
63
  icon={<DashboardIcon />}
55
64
  label="Dashboard"
@@ -119,6 +128,37 @@ export function Sidebar({ route, agentCount, taskCount, onNavigate, isOpen, onCl
119
128
  onClick={() => handleNavigate("settings")}
120
129
  />
121
130
  </nav>
131
+
132
+ {/* User profile - pinned to bottom */}
133
+ {user && (
134
+ <div className="relative border-t border-[#1a1a1a] pt-3 mt-3">
135
+ <button
136
+ onClick={() => setShowUserMenu(!showUserMenu)}
137
+ className="w-full flex items-center gap-3 px-3 py-2 rounded hover:bg-[#111] transition"
138
+ >
139
+ <div className="w-8 h-8 rounded-full bg-[#f97316] flex items-center justify-center text-black font-medium text-sm flex-shrink-0">
140
+ {user.username.charAt(0).toUpperCase()}
141
+ </div>
142
+ <div className="flex-1 min-w-0 text-left">
143
+ <p className="text-sm font-medium truncate">{user.username}</p>
144
+ <p className="text-xs text-[#555]">{user.role}</p>
145
+ </div>
146
+ </button>
147
+ {showUserMenu && (
148
+ <>
149
+ <div className="fixed inset-0 z-40" onClick={() => setShowUserMenu(false)} />
150
+ <div className="absolute left-3 bottom-full mb-1 w-48 bg-[#111] border border-[#222] rounded-lg shadow-xl z-50">
151
+ <button
152
+ onClick={handleLogout}
153
+ className="w-full px-4 py-2.5 text-left text-sm text-red-400 hover:bg-[#1a1a1a] transition rounded-lg"
154
+ >
155
+ Sign out
156
+ </button>
157
+ </div>
158
+ </>
159
+ )}
160
+ </div>
161
+ )}
122
162
  </aside>
123
163
  </>
124
164
  );
@@ -1366,6 +1366,7 @@ function AgentDojoContent({
1366
1366
  const serversUrl = projectId && projectId !== "unassigned"
1367
1367
  ? `/api/mcp/servers?project=${encodeURIComponent(projectId)}`
1368
1368
  : "/api/mcp/servers";
1369
+ console.log(`[AgentDojo:fetchConfigs] projectId=${projectId} serversUrl=${serversUrl}`);
1369
1370
  const [configsRes, serversRes] = await Promise.all([
1370
1371
  authFetch(`/api/integrations/agentdojo/configs${projectParam}`),
1371
1372
  authFetch(serversUrl),
@@ -1373,18 +1374,24 @@ function AgentDojoContent({
1373
1374
  const configsData = await configsRes.json();
1374
1375
  const serversData = await serversRes.json();
1375
1376
 
1377
+ console.log(`[AgentDojo:fetchConfigs] configs=${(configsData.configs || []).length} servers=${(serversData.servers || []).length}`);
1376
1378
  setConfigs(configsData.configs || []);
1377
1379
 
1378
1380
  // Track which configs are already added as local servers
1381
+ const agentdojoServers = (serversData.servers || []).filter((s: any) => s.source === "agentdojo");
1382
+ console.log(`[AgentDojo:fetchConfigs] agentdojo servers found: ${agentdojoServers.length}`);
1383
+ for (const s of agentdojoServers) {
1384
+ const match = s.url?.match(/\/mcp\/([^/?]+)/);
1385
+ console.log(`[AgentDojo:fetchConfigs] server: id=${s.id} name=${s.name} project_id=${s.project_id} url=${s.url?.substring(0, 80)} extracted=${match ? match[1] : s.name}`);
1386
+ }
1379
1387
  const agentdojoServerIds = new Set(
1380
- (serversData.servers || [])
1381
- .filter((s: any) => s.source === "agentdojo")
1382
- .map((s: any) => {
1388
+ agentdojoServers.map((s: any) => {
1383
1389
  // Extract config ID from URL or match by name
1384
1390
  const match = s.url?.match(/\/mcp\/([^/?]+)/);
1385
1391
  return match ? match[1] : s.name;
1386
1392
  })
1387
1393
  );
1394
+ console.log(`[AgentDojo:fetchConfigs] addedServers set:`, [...agentdojoServerIds]);
1388
1395
  setAddedServers(agentdojoServerIds);
1389
1396
  } catch (e) {
1390
1397
  console.error("Failed to fetch AgentDojo configs:", e);
@@ -1396,15 +1403,19 @@ function AgentDojoContent({
1396
1403
  setAddingConfig(configId);
1397
1404
  try {
1398
1405
  const projectParam = projectId && projectId !== "unassigned" ? `?project_id=${projectId}` : "";
1406
+ console.log(`[AgentDojo:addConfig] configId=${configId} projectParam=${projectParam}`);
1399
1407
  const res = await authFetch(`/api/integrations/agentdojo/configs/${configId}/add${projectParam}`, {
1400
1408
  method: "POST",
1401
1409
  });
1410
+ const data = await res.json();
1411
+ console.log(`[AgentDojo:addConfig] response status=${res.status} ok=${res.ok} message=${data.message} server.id=${data.server?.id} server.project_id=${data.server?.project_id}`);
1402
1412
  if (res.ok) {
1403
1413
  const config = configs.find(c => c.id === configId);
1404
- setAddedServers(prev => new Set([...prev, config?.slug || configId]));
1414
+ const addKey = config?.slug || configId;
1415
+ console.log(`[AgentDojo:addConfig] marking as added: key=${addKey} config.slug=${config?.slug} config.id=${config?.id} config.name=${config?.name}`);
1416
+ setAddedServers(prev => new Set([...prev, addKey]));
1405
1417
  onServerAdded?.();
1406
1418
  } else {
1407
- const data = await res.json();
1408
1419
  await alert(data.error || "Failed to add config", { title: "Error", variant: "error" });
1409
1420
  }
1410
1421
  } catch (e) {