apteva 0.4.57 → 0.7.0
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/README.md +216 -54
- package/cli.js +35 -0
- package/install.js +92 -0
- package/package.json +12 -79
- package/LICENSE +0 -63
- package/bin/apteva.js +0 -196
- package/dist/ActivityPage.kxzzb4yc.js +0 -3
- package/dist/ApiDocsPage.zq998hbm.js +0 -4
- package/dist/App.55rea8mn.js +0 -61
- package/dist/App.5ywb23z4.js +0 -53
- package/dist/App.6thds120.js +0 -4
- package/dist/App.9tctxzqm.js +0 -8
- package/dist/App.a8r8ttaz.js +0 -4
- package/dist/App.agsv5bje.js +0 -4
- package/dist/App.cepapqmx.js +0 -4
- package/dist/App.dp041gb3.js +0 -221
- package/dist/App.fds72zb5.js +0 -4
- package/dist/App.fg9qj2dq.js +0 -4
- package/dist/App.ndfejbm9.js +0 -4
- package/dist/App.nxmfmq1h.js +0 -13
- package/dist/App.qdfyt8ba.js +0 -4
- package/dist/App.x2d0ygt6.js +0 -4
- package/dist/App.yt9p4nr3.js +0 -20
- package/dist/App.zn4mw16t.js +0 -1
- package/dist/ConnectionsPage.8r96ryw7.js +0 -3
- package/dist/McpPage.3cwh0gnd.js +0 -3
- package/dist/SettingsPage.ykgdh5ev.js +0 -3
- package/dist/SkillsPage.4np1s65b.js +0 -3
- package/dist/TasksPage.4g08t7p6.js +0 -3
- package/dist/TelemetryPage.72w9pwcp.js +0 -3
- package/dist/TestsPage.z4fk3r7r.js +0 -3
- package/dist/ThreadsPage.63tcajeh.js +0 -3
- package/dist/apteva-kit.css +0 -1
- package/dist/icon.png +0 -0
- package/dist/index.html +0 -16
- package/dist/styles.css +0 -1
- package/scripts/postinstall.mjs +0 -102
- package/src/auth/index.ts +0 -394
- package/src/auth/middleware.ts +0 -213
- package/src/binary.ts +0 -536
- package/src/channels/index.ts +0 -40
- package/src/channels/telegram.ts +0 -311
- package/src/crypto.ts +0 -301
- package/src/db-tests.ts +0 -174
- package/src/db.ts +0 -3133
- package/src/integrations/agentdojo.ts +0 -559
- package/src/integrations/composio.ts +0 -437
- package/src/integrations/index.ts +0 -87
- package/src/integrations/skillsmp.ts +0 -318
- package/src/mcp-client.ts +0 -605
- package/src/mcp-handler.ts +0 -394
- package/src/mcp-platform.ts +0 -2403
- package/src/openapi.ts +0 -2410
- package/src/providers.ts +0 -597
- package/src/routes/api/agent-utils.ts +0 -890
- package/src/routes/api/agents.ts +0 -916
- package/src/routes/api/api-keys.ts +0 -95
- package/src/routes/api/channels.ts +0 -182
- package/src/routes/api/helpers.ts +0 -12
- package/src/routes/api/integrations.ts +0 -639
- package/src/routes/api/mcp.ts +0 -574
- package/src/routes/api/meta-agent.ts +0 -195
- package/src/routes/api/projects.ts +0 -112
- package/src/routes/api/providers.ts +0 -424
- package/src/routes/api/skills.ts +0 -537
- package/src/routes/api/system.ts +0 -333
- package/src/routes/api/telemetry.ts +0 -203
- package/src/routes/api/tests.ts +0 -148
- package/src/routes/api/triggers.ts +0 -518
- package/src/routes/api/users.ts +0 -148
- package/src/routes/api/webhooks.ts +0 -171
- package/src/routes/api.ts +0 -53
- package/src/routes/auth.ts +0 -251
- package/src/routes/share.ts +0 -86
- package/src/routes/static.ts +0 -131
- package/src/server.ts +0 -642
- package/src/test-runner.ts +0 -598
- package/src/triggers/agentdojo.ts +0 -253
- package/src/triggers/composio.ts +0 -264
- package/src/triggers/index.ts +0 -71
- package/src/tui/AgentList.tsx +0 -145
- package/src/tui/App.tsx +0 -102
- package/src/tui/Login.tsx +0 -104
- package/src/tui/api.ts +0 -72
- package/src/tui/index.tsx +0 -7
- package/src/web/App.tsx +0 -455
- package/src/web/components/activity/ActivityPage.tsx +0 -314
- package/src/web/components/activity/index.ts +0 -1
- package/src/web/components/agents/AgentCard.tsx +0 -189
- package/src/web/components/agents/AgentPanel.tsx +0 -2244
- package/src/web/components/agents/AgentsView.tsx +0 -180
- package/src/web/components/agents/CreateAgentModal.tsx +0 -475
- package/src/web/components/agents/index.ts +0 -4
- package/src/web/components/api/ApiDocsPage.tsx +0 -842
- package/src/web/components/auth/CreateAccountStep.tsx +0 -176
- package/src/web/components/auth/LoginPage.tsx +0 -91
- package/src/web/components/auth/index.ts +0 -2
- package/src/web/components/common/Icons.tsx +0 -250
- package/src/web/components/common/LoadingSpinner.tsx +0 -44
- package/src/web/components/common/Modal.tsx +0 -199
- package/src/web/components/common/Select.tsx +0 -97
- package/src/web/components/common/index.ts +0 -20
- package/src/web/components/connections/ConnectionsPage.tsx +0 -54
- package/src/web/components/connections/IntegrationsTab.tsx +0 -170
- package/src/web/components/connections/OverviewTab.tsx +0 -137
- package/src/web/components/connections/TriggersTab.tsx +0 -1346
- package/src/web/components/dashboard/Dashboard.tsx +0 -572
- package/src/web/components/dashboard/index.ts +0 -1
- package/src/web/components/index.ts +0 -21
- package/src/web/components/layout/ErrorBanner.tsx +0 -18
- package/src/web/components/layout/Header.tsx +0 -332
- package/src/web/components/layout/Sidebar.tsx +0 -231
- package/src/web/components/layout/index.ts +0 -3
- package/src/web/components/mcp/IntegrationsPanel.tsx +0 -857
- package/src/web/components/mcp/McpPage.tsx +0 -2515
- package/src/web/components/mcp/index.ts +0 -1
- package/src/web/components/meta-agent/MetaAgent.tsx +0 -245
- package/src/web/components/onboarding/OnboardingWizard.tsx +0 -404
- package/src/web/components/onboarding/index.ts +0 -1
- package/src/web/components/settings/SettingsPage.tsx +0 -2776
- package/src/web/components/settings/index.ts +0 -1
- package/src/web/components/skills/SkillsPage.tsx +0 -1200
- package/src/web/components/tasks/TasksPage.tsx +0 -1116
- package/src/web/components/tasks/index.ts +0 -1
- package/src/web/components/telemetry/TelemetryPage.tsx +0 -1129
- package/src/web/components/tests/TestsPage.tsx +0 -594
- package/src/web/components/threads/ThreadsPage.tsx +0 -315
- package/src/web/context/AuthContext.tsx +0 -242
- package/src/web/context/ProjectContext.tsx +0 -214
- package/src/web/context/TelemetryContext.tsx +0 -299
- package/src/web/context/ThemeContext.tsx +0 -90
- package/src/web/context/UIModeContext.tsx +0 -49
- package/src/web/context/index.ts +0 -12
- package/src/web/hooks/index.ts +0 -3
- package/src/web/hooks/useAgents.ts +0 -115
- package/src/web/hooks/useOnboarding.ts +0 -20
- package/src/web/hooks/useProviders.ts +0 -75
- package/src/web/icon.png +0 -0
- package/src/web/index.html +0 -16
- package/src/web/styles.css +0 -118
- package/src/web/themes.ts +0 -162
- package/src/web/types.ts +0 -298
|
@@ -1,332 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect, useCallback, useRef } from "react";
|
|
2
|
-
import { useTelemetryContext, useAuth, useAuthHeaders, useProjects, useNotificationChange } from "../../context";
|
|
3
|
-
import { MenuIcon, ChevronDownIcon, BellIcon } from "../common/Icons";
|
|
4
|
-
import { MetaAgentButton } from "../meta-agent/MetaAgent";
|
|
5
|
-
|
|
6
|
-
interface Notification {
|
|
7
|
-
id: string;
|
|
8
|
-
agent_id: string;
|
|
9
|
-
timestamp: string;
|
|
10
|
-
category: string;
|
|
11
|
-
type: string;
|
|
12
|
-
level: string;
|
|
13
|
-
error: string | null;
|
|
14
|
-
data: Record<string, unknown> | null;
|
|
15
|
-
seen?: boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface HeaderProps {
|
|
19
|
-
onMenuClick?: () => void;
|
|
20
|
-
agents?: Array<{ id: string; name: string; projectId: string | null }>;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function Header({ onMenuClick, agents = [] }: HeaderProps) {
|
|
24
|
-
const { connected } = useTelemetryContext();
|
|
25
|
-
const authHeaders = useAuthHeaders();
|
|
26
|
-
const { projects, currentProjectId, currentProject, setCurrentProjectId, unassignedCount, projectsEnabled } = useProjects();
|
|
27
|
-
const [showProjectMenu, setShowProjectMenu] = useState(false);
|
|
28
|
-
const [showNotifications, setShowNotifications] = useState(false);
|
|
29
|
-
const [unseenCount, setUnseenCount] = useState(0);
|
|
30
|
-
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
31
|
-
const notificationChange = useNotificationChange();
|
|
32
|
-
const { events } = useTelemetryContext();
|
|
33
|
-
const { accessToken } = useAuth();
|
|
34
|
-
const fetchedOnce = useRef(false);
|
|
35
|
-
|
|
36
|
-
const agentNames = React.useMemo(() => {
|
|
37
|
-
const map: Record<string, string> = {};
|
|
38
|
-
for (const a of agents) map[a.id] = a.name;
|
|
39
|
-
return map;
|
|
40
|
-
}, [agents]);
|
|
41
|
-
|
|
42
|
-
// Set of agent IDs matching the current project filter
|
|
43
|
-
const projectAgentIds = React.useMemo(() => {
|
|
44
|
-
if (!projectsEnabled || currentProjectId === null) return null; // null = show all
|
|
45
|
-
if (currentProjectId === "unassigned") return new Set(agents.filter(a => !a.projectId).map(a => a.id));
|
|
46
|
-
return new Set(agents.filter(a => a.projectId === currentProjectId).map(a => a.id));
|
|
47
|
-
}, [agents, currentProjectId, projectsEnabled]);
|
|
48
|
-
|
|
49
|
-
// Fetch initial unseen count once
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
if (fetchedOnce.current || !accessToken) return;
|
|
52
|
-
fetchedOnce.current = true;
|
|
53
|
-
fetch("/api/notifications/count", { headers: { Authorization: `Bearer ${accessToken}` } })
|
|
54
|
-
.then(r => r.json())
|
|
55
|
-
.then(d => setUnseenCount(d.count || 0))
|
|
56
|
-
.catch(() => {});
|
|
57
|
-
}, [accessToken]);
|
|
58
|
-
|
|
59
|
-
// Bump count live from SSE (only if event matches current project)
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
if (notificationChange === 0) return;
|
|
62
|
-
const latest = events.find(e =>
|
|
63
|
-
e.level === "error" || e.category === "ERROR" || (e.category === "system" && e.type === "agent_stopped")
|
|
64
|
-
);
|
|
65
|
-
if (latest && (!projectAgentIds || projectAgentIds.has(latest.agent_id))) {
|
|
66
|
-
setUnseenCount(c => c + 1);
|
|
67
|
-
}
|
|
68
|
-
}, [notificationChange]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
69
|
-
|
|
70
|
-
// Re-count when project filter changes (from cached notifications or reset)
|
|
71
|
-
const prevProjectRef = useRef(currentProjectId);
|
|
72
|
-
useEffect(() => {
|
|
73
|
-
if (prevProjectRef.current === currentProjectId) return;
|
|
74
|
-
prevProjectRef.current = currentProjectId;
|
|
75
|
-
// Refetch count with project filter
|
|
76
|
-
if (!accessToken) return;
|
|
77
|
-
fetch("/api/notifications/count", { headers: { Authorization: `Bearer ${accessToken}` } })
|
|
78
|
-
.then(r => r.json())
|
|
79
|
-
.then(d => {
|
|
80
|
-
// API returns total unseen — client filters by project
|
|
81
|
-
if (!projectAgentIds) {
|
|
82
|
-
setUnseenCount(d.count || 0);
|
|
83
|
-
} else {
|
|
84
|
-
// We need to fetch actual notifications to filter by project
|
|
85
|
-
fetch("/api/notifications?limit=200", { headers: { Authorization: `Bearer ${accessToken}` } })
|
|
86
|
-
.then(r => r.json())
|
|
87
|
-
.then(nd => {
|
|
88
|
-
const unseen = (nd.notifications || []).filter(
|
|
89
|
-
(n: Notification) => !n.seen && projectAgentIds.has(n.agent_id)
|
|
90
|
-
);
|
|
91
|
-
setUnseenCount(unseen.length);
|
|
92
|
-
})
|
|
93
|
-
.catch(() => {});
|
|
94
|
-
}
|
|
95
|
-
})
|
|
96
|
-
.catch(() => {});
|
|
97
|
-
}, [currentProjectId, accessToken, projectAgentIds]);
|
|
98
|
-
|
|
99
|
-
const openNotifications = useCallback(async () => {
|
|
100
|
-
setShowNotifications(prev => !prev);
|
|
101
|
-
if (!showNotifications) {
|
|
102
|
-
try {
|
|
103
|
-
const headers: Record<string, string> = { "Content-Type": "application/json" };
|
|
104
|
-
if (accessToken) headers.Authorization = `Bearer ${accessToken}`;
|
|
105
|
-
const res = await fetch("/api/notifications?limit=50", { headers });
|
|
106
|
-
const data = await res.json();
|
|
107
|
-
let items: Notification[] = data.notifications || [];
|
|
108
|
-
if (projectAgentIds) items = items.filter(n => projectAgentIds.has(n.agent_id));
|
|
109
|
-
setNotifications(items);
|
|
110
|
-
// Mark all as seen
|
|
111
|
-
if (unseenCount > 0) {
|
|
112
|
-
await fetch("/api/notifications/mark-seen", {
|
|
113
|
-
method: "POST",
|
|
114
|
-
headers,
|
|
115
|
-
body: JSON.stringify({ all: true }),
|
|
116
|
-
});
|
|
117
|
-
setUnseenCount(0);
|
|
118
|
-
}
|
|
119
|
-
} catch {
|
|
120
|
-
// Ignore
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}, [showNotifications, unseenCount, accessToken, projectAgentIds]);
|
|
124
|
-
|
|
125
|
-
const handleProjectSelect = (projectId: string | null) => {
|
|
126
|
-
setCurrentProjectId(projectId);
|
|
127
|
-
setShowProjectMenu(false);
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const getProjectLabel = () => {
|
|
131
|
-
if (currentProjectId === null) return "All Projects";
|
|
132
|
-
if (currentProjectId === "unassigned") return "Unassigned";
|
|
133
|
-
return currentProject?.name || "Select Project";
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
const getProjectColor = () => {
|
|
137
|
-
if (currentProjectId === null) return "var(--color-text-muted)";
|
|
138
|
-
if (currentProjectId === "unassigned") return "var(--color-text-secondary)";
|
|
139
|
-
return currentProject?.color || "#6366f1";
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
return (
|
|
143
|
-
<header className="px-4 md:px-6 py-4 flex-shrink-0" style={{ borderBottom: "1px solid var(--color-border)" }}>
|
|
144
|
-
<div className="flex items-center justify-between">
|
|
145
|
-
<div className="flex items-center gap-3">
|
|
146
|
-
{/* Hamburger menu button - mobile only */}
|
|
147
|
-
<button
|
|
148
|
-
onClick={onMenuClick}
|
|
149
|
-
className="p-2 -ml-2 transition md:hidden"
|
|
150
|
-
style={{ color: "var(--color-text-muted)" }}
|
|
151
|
-
>
|
|
152
|
-
<MenuIcon />
|
|
153
|
-
</button>
|
|
154
|
-
<div className="flex items-center gap-2">
|
|
155
|
-
<span style={{ color: "var(--color-accent)" }}>>_</span>
|
|
156
|
-
<span className="text-xl tracking-wider">apteva</span>
|
|
157
|
-
</div>
|
|
158
|
-
|
|
159
|
-
{/* Project Selector */}
|
|
160
|
-
{projectsEnabled && projects.length > 0 && (
|
|
161
|
-
<div className="relative ml-2 md:ml-4">
|
|
162
|
-
<button
|
|
163
|
-
onClick={() => setShowProjectMenu(!showProjectMenu)}
|
|
164
|
-
className="flex items-center gap-2 px-3 py-1.5 btn transition text-sm"
|
|
165
|
-
style={{ border: "1px solid var(--color-border-light)", backgroundColor: "var(--color-surface)" }}
|
|
166
|
-
>
|
|
167
|
-
<span
|
|
168
|
-
className="w-2.5 h-2.5 rounded-full"
|
|
169
|
-
style={{ backgroundColor: getProjectColor() }}
|
|
170
|
-
/>
|
|
171
|
-
<span className="hidden sm:inline max-w-[120px] md:max-w-[180px] truncate">
|
|
172
|
-
{getProjectLabel()}
|
|
173
|
-
</span>
|
|
174
|
-
<ChevronDownIcon />
|
|
175
|
-
</button>
|
|
176
|
-
{showProjectMenu && (
|
|
177
|
-
<div className="absolute left-0 top-full mt-1 w-56 card shadow-xl z-50" style={{ backgroundColor: "var(--color-surface)" }}>
|
|
178
|
-
<div className="py-1 max-h-64 overflow-y-auto">
|
|
179
|
-
<button
|
|
180
|
-
onClick={() => handleProjectSelect(null)}
|
|
181
|
-
className="w-full px-4 py-2 text-left text-sm flex items-center gap-2 transition"
|
|
182
|
-
style={{
|
|
183
|
-
backgroundColor: currentProjectId === null ? "var(--color-surface-raised)" : "transparent",
|
|
184
|
-
color: currentProjectId === null ? "var(--color-accent)" : "var(--color-text)",
|
|
185
|
-
}}
|
|
186
|
-
>
|
|
187
|
-
<span className="w-2.5 h-2.5 rounded-full" style={{ backgroundColor: "var(--color-text-muted)" }} />
|
|
188
|
-
All Projects
|
|
189
|
-
</button>
|
|
190
|
-
{projects.map(project => (
|
|
191
|
-
<button
|
|
192
|
-
key={project.id}
|
|
193
|
-
onClick={() => handleProjectSelect(project.id)}
|
|
194
|
-
className="w-full px-4 py-2 text-left text-sm flex items-center gap-2 transition"
|
|
195
|
-
style={{
|
|
196
|
-
backgroundColor: currentProjectId === project.id ? "var(--color-surface-raised)" : "transparent",
|
|
197
|
-
color: currentProjectId === project.id ? "var(--color-accent)" : "var(--color-text)",
|
|
198
|
-
}}
|
|
199
|
-
>
|
|
200
|
-
<span
|
|
201
|
-
className="w-2.5 h-2.5 rounded-full flex-shrink-0"
|
|
202
|
-
style={{ backgroundColor: project.color }}
|
|
203
|
-
/>
|
|
204
|
-
<span className="truncate">{project.name}</span>
|
|
205
|
-
<span className="ml-auto text-xs" style={{ color: "var(--color-text-muted)" }}>{project.agentCount}</span>
|
|
206
|
-
</button>
|
|
207
|
-
))}
|
|
208
|
-
{unassignedCount > 0 && (
|
|
209
|
-
<button
|
|
210
|
-
onClick={() => handleProjectSelect("unassigned")}
|
|
211
|
-
className="w-full px-4 py-2 text-left text-sm flex items-center gap-2 transition"
|
|
212
|
-
style={{
|
|
213
|
-
backgroundColor: currentProjectId === "unassigned" ? "var(--color-surface-raised)" : "transparent",
|
|
214
|
-
color: currentProjectId === "unassigned" ? "var(--color-accent)" : "var(--color-text)",
|
|
215
|
-
}}
|
|
216
|
-
>
|
|
217
|
-
<span className="w-2.5 h-2.5 rounded-full" style={{ backgroundColor: "var(--color-text-secondary)" }} />
|
|
218
|
-
<span className="truncate">Unassigned</span>
|
|
219
|
-
<span className="ml-auto text-xs" style={{ color: "var(--color-text-muted)" }}>{unassignedCount}</span>
|
|
220
|
-
</button>
|
|
221
|
-
)}
|
|
222
|
-
</div>
|
|
223
|
-
</div>
|
|
224
|
-
)}
|
|
225
|
-
</div>
|
|
226
|
-
)}
|
|
227
|
-
</div>
|
|
228
|
-
<div className="flex items-center gap-3 md:gap-4">
|
|
229
|
-
<div className="flex items-center gap-2">
|
|
230
|
-
<span
|
|
231
|
-
className={`w-2 h-2 rounded-full ${connected ? "bg-green-400" : "bg-red-400"}`}
|
|
232
|
-
/>
|
|
233
|
-
<span className="text-xs hidden sm:inline" style={{ color: "var(--color-text-muted)" }}>
|
|
234
|
-
{connected ? "Live" : "Offline"}
|
|
235
|
-
</span>
|
|
236
|
-
</div>
|
|
237
|
-
{/* Notification Bell */}
|
|
238
|
-
<div className="relative">
|
|
239
|
-
<button
|
|
240
|
-
onClick={openNotifications}
|
|
241
|
-
className="relative p-2 transition rounded"
|
|
242
|
-
style={{ color: "var(--color-text-muted)" }}
|
|
243
|
-
>
|
|
244
|
-
<BellIcon className="w-5 h-5" />
|
|
245
|
-
{unseenCount > 0 && (
|
|
246
|
-
<span
|
|
247
|
-
className="absolute flex items-center justify-center bg-red-500 text-white font-bold rounded-full pointer-events-none"
|
|
248
|
-
style={{
|
|
249
|
-
top: 2,
|
|
250
|
-
right: 2,
|
|
251
|
-
fontSize: 9,
|
|
252
|
-
lineHeight: 1,
|
|
253
|
-
minWidth: 16,
|
|
254
|
-
height: 16,
|
|
255
|
-
padding: "0 4px",
|
|
256
|
-
}}
|
|
257
|
-
>
|
|
258
|
-
{unseenCount > 99 ? "99+" : unseenCount}
|
|
259
|
-
</span>
|
|
260
|
-
)}
|
|
261
|
-
</button>
|
|
262
|
-
{showNotifications && (
|
|
263
|
-
<>
|
|
264
|
-
<div className="fixed inset-0 z-40" onClick={() => setShowNotifications(false)} />
|
|
265
|
-
<div className="absolute right-0 top-full mt-1 w-80 card shadow-xl z-50 max-h-96 overflow-y-auto" style={{ backgroundColor: "var(--color-surface)" }}>
|
|
266
|
-
<div className="px-4 py-3 flex items-center justify-between" style={{ borderBottom: "1px solid var(--color-border-light)" }}>
|
|
267
|
-
<span className="text-sm font-medium">Notifications</span>
|
|
268
|
-
{notifications.length > 0 && (
|
|
269
|
-
<span className="text-xs" style={{ color: "var(--color-text-muted)" }}>{notifications.length} recent</span>
|
|
270
|
-
)}
|
|
271
|
-
</div>
|
|
272
|
-
{notifications.length === 0 ? (
|
|
273
|
-
<div className="px-4 py-8 text-center text-sm" style={{ color: "var(--color-text-muted)" }}>
|
|
274
|
-
No notifications
|
|
275
|
-
</div>
|
|
276
|
-
) : (
|
|
277
|
-
<div className="py-1">
|
|
278
|
-
{notifications.map(n => (
|
|
279
|
-
<div key={n.id} className="px-4 py-3 transition" style={{
|
|
280
|
-
borderBottom: "1px solid var(--color-border)",
|
|
281
|
-
backgroundColor: !n.seen ? "var(--color-bg-secondary)" : "transparent",
|
|
282
|
-
}}>
|
|
283
|
-
<div className="flex items-center gap-2 mb-1">
|
|
284
|
-
<span className={`w-2 h-2 rounded-full flex-shrink-0 ${
|
|
285
|
-
!n.seen
|
|
286
|
-
? (n.level === "error" || n.category === "ERROR" ? "bg-red-400" : "")
|
|
287
|
-
: ""
|
|
288
|
-
}`} style={{
|
|
289
|
-
backgroundColor: !n.seen
|
|
290
|
-
? (n.level === "error" || n.category === "ERROR" ? undefined : "var(--color-accent)")
|
|
291
|
-
: "var(--color-surface-raised)",
|
|
292
|
-
}} />
|
|
293
|
-
<span className="text-xs font-medium truncate" style={{ color: !n.seen ? "var(--color-text)" : "var(--color-text-muted)" }}>
|
|
294
|
-
{n.category === "system" && n.type === "agent_stopped" ? "Agent Stopped" :
|
|
295
|
-
n.category === "ERROR" ? "Error" :
|
|
296
|
-
`${n.category} / ${n.type}`}
|
|
297
|
-
</span>
|
|
298
|
-
<span className="text-[10px] ml-auto flex-shrink-0" style={{ color: "var(--color-text-faint)" }}>
|
|
299
|
-
{formatNotifTime(n.timestamp)}
|
|
300
|
-
</span>
|
|
301
|
-
</div>
|
|
302
|
-
<div className="text-xs truncate" style={{ color: !n.seen ? "var(--color-text-secondary)" : "var(--color-text-muted)" }}>
|
|
303
|
-
{n.error || (n.data as any)?.message || (n.data as any)?.error || `${n.type} event`}
|
|
304
|
-
</div>
|
|
305
|
-
<div className="text-[10px] mt-1" style={{ color: "var(--color-text-faint)" }}>
|
|
306
|
-
{agentNames[n.agent_id] || n.agent_id.slice(0, 8)}
|
|
307
|
-
</div>
|
|
308
|
-
</div>
|
|
309
|
-
))}
|
|
310
|
-
</div>
|
|
311
|
-
)}
|
|
312
|
-
</div>
|
|
313
|
-
</>
|
|
314
|
-
)}
|
|
315
|
-
</div>
|
|
316
|
-
<MetaAgentButton />
|
|
317
|
-
</div>
|
|
318
|
-
</div>
|
|
319
|
-
</header>
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
function formatNotifTime(timestamp: string): string {
|
|
324
|
-
const diff = Date.now() - new Date(timestamp).getTime();
|
|
325
|
-
const mins = Math.floor(diff / 60000);
|
|
326
|
-
if (mins < 1) return "just now";
|
|
327
|
-
if (mins < 60) return `${mins}m ago`;
|
|
328
|
-
const hours = Math.floor(mins / 60);
|
|
329
|
-
if (hours < 24) return `${hours}h ago`;
|
|
330
|
-
const days = Math.floor(hours / 24);
|
|
331
|
-
return `${days}d ago`;
|
|
332
|
-
}
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
2
|
-
import { DashboardIcon, ThreadsIcon, AgentsIcon, ActivityIcon, TasksIcon, ConnectionsIcon, McpIcon, SkillsIcon, TestsIcon, TelemetryIcon, ApiIcon, SettingsIcon, CloseIcon } from "../common/Icons";
|
|
3
|
-
import { useAuth, useUIMode } from "../../context";
|
|
4
|
-
import type { Route } from "../../types";
|
|
5
|
-
|
|
6
|
-
interface SidebarProps {
|
|
7
|
-
route: Route;
|
|
8
|
-
agentCount: number;
|
|
9
|
-
taskCount?: number;
|
|
10
|
-
onNavigate: (route: Route) => void;
|
|
11
|
-
isOpen?: boolean;
|
|
12
|
-
onClose?: () => void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function Sidebar({ route, agentCount, taskCount, onNavigate, isOpen, onClose }: SidebarProps) {
|
|
16
|
-
const { user, logout } = useAuth();
|
|
17
|
-
const { isDev, t } = useUIMode();
|
|
18
|
-
const [showUserMenu, setShowUserMenu] = useState(false);
|
|
19
|
-
|
|
20
|
-
const handleNavigate = (newRoute: Route) => {
|
|
21
|
-
onNavigate(newRoute);
|
|
22
|
-
onClose?.();
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const handleLogout = async () => {
|
|
26
|
-
await logout();
|
|
27
|
-
setShowUserMenu(false);
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<>
|
|
32
|
-
{/* Mobile overlay backdrop */}
|
|
33
|
-
{isOpen && (
|
|
34
|
-
<div
|
|
35
|
-
className="fixed inset-0 bg-black/60 z-40 md:hidden"
|
|
36
|
-
onClick={onClose}
|
|
37
|
-
/>
|
|
38
|
-
)}
|
|
39
|
-
|
|
40
|
-
{/* Sidebar - hidden on mobile unless open, always visible on md+ */}
|
|
41
|
-
<aside
|
|
42
|
-
className={`
|
|
43
|
-
fixed inset-y-0 left-0 z-50 w-64 p-4 flex flex-col transform transition-transform duration-200 ease-in-out
|
|
44
|
-
md:relative md:w-56 md:translate-x-0 md:z-auto
|
|
45
|
-
${isOpen ? "translate-x-0" : "-translate-x-full"}
|
|
46
|
-
`}
|
|
47
|
-
style={{ backgroundColor: "var(--color-bg)", borderRight: "1px solid var(--color-border)" }}
|
|
48
|
-
>
|
|
49
|
-
{/* Mobile header with close button */}
|
|
50
|
-
<div className="flex items-center justify-between mb-4 md:hidden">
|
|
51
|
-
<div className="flex items-center gap-2">
|
|
52
|
-
<span style={{ color: "var(--color-accent)" }}>>_</span>
|
|
53
|
-
<span className="text-lg tracking-wider">apteva</span>
|
|
54
|
-
</div>
|
|
55
|
-
<button
|
|
56
|
-
onClick={onClose}
|
|
57
|
-
className="p-2 transition"
|
|
58
|
-
style={{ color: "var(--color-text-muted)" }}
|
|
59
|
-
>
|
|
60
|
-
<CloseIcon />
|
|
61
|
-
</button>
|
|
62
|
-
</div>
|
|
63
|
-
|
|
64
|
-
<nav className="space-y-1 flex-1">
|
|
65
|
-
<NavButton
|
|
66
|
-
icon={<DashboardIcon />}
|
|
67
|
-
label="Dashboard"
|
|
68
|
-
active={route === "dashboard"}
|
|
69
|
-
onClick={() => handleNavigate("dashboard")}
|
|
70
|
-
/>
|
|
71
|
-
<NavButton
|
|
72
|
-
icon={<AgentsIcon />}
|
|
73
|
-
label={t("Agents", "Employees")}
|
|
74
|
-
active={route === "agents"}
|
|
75
|
-
onClick={() => handleNavigate("agents")}
|
|
76
|
-
badge={agentCount > 0 ? String(agentCount) : undefined}
|
|
77
|
-
/>
|
|
78
|
-
<NavButton
|
|
79
|
-
icon={<ThreadsIcon />}
|
|
80
|
-
label="Threads"
|
|
81
|
-
active={route === "threads"}
|
|
82
|
-
onClick={() => handleNavigate("threads")}
|
|
83
|
-
/>
|
|
84
|
-
<NavButton
|
|
85
|
-
icon={<ActivityIcon />}
|
|
86
|
-
label="Activity"
|
|
87
|
-
active={route === "activity"}
|
|
88
|
-
onClick={() => handleNavigate("activity")}
|
|
89
|
-
/>
|
|
90
|
-
<NavButton
|
|
91
|
-
icon={<TasksIcon />}
|
|
92
|
-
label="Tasks"
|
|
93
|
-
active={route === "tasks"}
|
|
94
|
-
onClick={() => handleNavigate("tasks")}
|
|
95
|
-
badge={taskCount && taskCount > 0 ? String(taskCount) : undefined}
|
|
96
|
-
/>
|
|
97
|
-
{isDev && (
|
|
98
|
-
<NavButton
|
|
99
|
-
icon={<McpIcon />}
|
|
100
|
-
label="MCP"
|
|
101
|
-
active={route === "mcp"}
|
|
102
|
-
onClick={() => handleNavigate("mcp")}
|
|
103
|
-
/>
|
|
104
|
-
)}
|
|
105
|
-
{isDev && (
|
|
106
|
-
<NavButton
|
|
107
|
-
icon={<SkillsIcon />}
|
|
108
|
-
label="Skills"
|
|
109
|
-
active={route === "skills"}
|
|
110
|
-
onClick={() => handleNavigate("skills")}
|
|
111
|
-
/>
|
|
112
|
-
)}
|
|
113
|
-
{isDev && (
|
|
114
|
-
<NavButton
|
|
115
|
-
icon={<ConnectionsIcon />}
|
|
116
|
-
label="Connections"
|
|
117
|
-
active={route === "connections"}
|
|
118
|
-
onClick={() => handleNavigate("connections")}
|
|
119
|
-
/>
|
|
120
|
-
)}
|
|
121
|
-
{isDev && (
|
|
122
|
-
<NavButton
|
|
123
|
-
icon={<TestsIcon />}
|
|
124
|
-
label="Tests"
|
|
125
|
-
active={route === "tests"}
|
|
126
|
-
onClick={() => handleNavigate("tests")}
|
|
127
|
-
/>
|
|
128
|
-
)}
|
|
129
|
-
<NavButton
|
|
130
|
-
icon={<TelemetryIcon />}
|
|
131
|
-
label="Analytics"
|
|
132
|
-
active={route === "analytics"}
|
|
133
|
-
onClick={() => handleNavigate("analytics")}
|
|
134
|
-
/>
|
|
135
|
-
{isDev && (
|
|
136
|
-
<NavButton
|
|
137
|
-
icon={<ApiIcon />}
|
|
138
|
-
label="API"
|
|
139
|
-
active={route === "api"}
|
|
140
|
-
onClick={() => handleNavigate("api")}
|
|
141
|
-
/>
|
|
142
|
-
)}
|
|
143
|
-
<NavButton
|
|
144
|
-
icon={<SettingsIcon />}
|
|
145
|
-
label="Settings"
|
|
146
|
-
active={route === "settings"}
|
|
147
|
-
onClick={() => handleNavigate("settings")}
|
|
148
|
-
/>
|
|
149
|
-
</nav>
|
|
150
|
-
|
|
151
|
-
{/* User profile - pinned to bottom */}
|
|
152
|
-
{user && (
|
|
153
|
-
<div className="relative pt-3 mt-3" style={{ borderTop: "1px solid var(--color-border)" }}>
|
|
154
|
-
<button
|
|
155
|
-
onClick={() => setShowUserMenu(!showUserMenu)}
|
|
156
|
-
className="w-full flex items-center gap-3 px-3 py-2 rounded transition"
|
|
157
|
-
style={{ color: "var(--color-text)" }}
|
|
158
|
-
onMouseEnter={e => e.currentTarget.style.backgroundColor = "var(--color-surface)"}
|
|
159
|
-
onMouseLeave={e => e.currentTarget.style.backgroundColor = "transparent"}
|
|
160
|
-
>
|
|
161
|
-
<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)" }}>
|
|
162
|
-
{user.username.charAt(0).toUpperCase()}
|
|
163
|
-
</div>
|
|
164
|
-
<div className="flex-1 min-w-0 text-left">
|
|
165
|
-
<p className="text-sm font-medium truncate">{user.username}</p>
|
|
166
|
-
<p className="text-xs" style={{ color: "var(--color-text-faint)" }}>{user.role}</p>
|
|
167
|
-
</div>
|
|
168
|
-
</button>
|
|
169
|
-
{showUserMenu && (
|
|
170
|
-
<>
|
|
171
|
-
<div className="fixed inset-0 z-40" onClick={() => setShowUserMenu(false)} />
|
|
172
|
-
<div className="absolute left-3 bottom-full mb-1 w-48 card shadow-xl z-50" style={{ backgroundColor: "var(--color-surface)" }}>
|
|
173
|
-
<button
|
|
174
|
-
onClick={handleLogout}
|
|
175
|
-
className="w-full px-4 py-2.5 text-left text-sm text-red-400 transition rounded-lg"
|
|
176
|
-
onMouseEnter={e => e.currentTarget.style.backgroundColor = "var(--color-surface-raised)"}
|
|
177
|
-
onMouseLeave={e => e.currentTarget.style.backgroundColor = "transparent"}
|
|
178
|
-
>
|
|
179
|
-
Sign out
|
|
180
|
-
</button>
|
|
181
|
-
</div>
|
|
182
|
-
</>
|
|
183
|
-
)}
|
|
184
|
-
</div>
|
|
185
|
-
)}
|
|
186
|
-
</aside>
|
|
187
|
-
</>
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
interface NavButtonProps {
|
|
192
|
-
icon: React.ReactNode;
|
|
193
|
-
label: string;
|
|
194
|
-
active: boolean;
|
|
195
|
-
onClick: () => void;
|
|
196
|
-
badge?: string;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function NavButton({ icon, label, active, onClick, badge }: NavButtonProps) {
|
|
200
|
-
return (
|
|
201
|
-
<button
|
|
202
|
-
onClick={onClick}
|
|
203
|
-
className="w-full flex items-center gap-3 px-3 py-2 font-medium transition"
|
|
204
|
-
style={{
|
|
205
|
-
borderRadius: "var(--radius-button)",
|
|
206
|
-
backgroundColor: active ? "var(--color-surface)" : "transparent",
|
|
207
|
-
color: active ? "var(--color-text)" : "var(--color-text-muted)",
|
|
208
|
-
}}
|
|
209
|
-
onMouseEnter={e => {
|
|
210
|
-
if (!active) {
|
|
211
|
-
e.currentTarget.style.backgroundColor = "var(--color-surface)";
|
|
212
|
-
e.currentTarget.style.color = "var(--color-text-secondary)";
|
|
213
|
-
}
|
|
214
|
-
}}
|
|
215
|
-
onMouseLeave={e => {
|
|
216
|
-
if (!active) {
|
|
217
|
-
e.currentTarget.style.backgroundColor = "transparent";
|
|
218
|
-
e.currentTarget.style.color = "var(--color-text-muted)";
|
|
219
|
-
}
|
|
220
|
-
}}
|
|
221
|
-
>
|
|
222
|
-
{icon}
|
|
223
|
-
{label}
|
|
224
|
-
{badge && (
|
|
225
|
-
<span className="ml-auto text-xs px-2 py-0.5 rounded-full" style={{ backgroundColor: "var(--color-surface-raised)", color: "var(--color-text-secondary)" }}>
|
|
226
|
-
{badge}
|
|
227
|
-
</span>
|
|
228
|
-
)}
|
|
229
|
-
</button>
|
|
230
|
-
);
|
|
231
|
-
}
|