apteva 0.4.57 → 0.7.1
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 +15 -76
- 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,314 +0,0 @@
|
|
|
1
|
-
import React, { useState, useMemo, useEffect, useCallback, useRef } from "react";
|
|
2
|
-
import { useAuth, useProjects, useTelemetryContext } from "../../context";
|
|
3
|
-
import type { TelemetryEvent } from "../../context";
|
|
4
|
-
import type { Agent, Route } from "../../types";
|
|
5
|
-
import { Select } from "../common/Select";
|
|
6
|
-
|
|
7
|
-
interface ActivityPageProps {
|
|
8
|
-
agents: Agent[];
|
|
9
|
-
loading: boolean;
|
|
10
|
-
onNavigate?: (route: Route) => void;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Event types we show in the timeline (skip noisy internal ones)
|
|
14
|
-
const VISIBLE_TYPES = new Set([
|
|
15
|
-
"thread_activity",
|
|
16
|
-
"agent_started",
|
|
17
|
-
"agent_stopped",
|
|
18
|
-
"agent_error",
|
|
19
|
-
"task_created",
|
|
20
|
-
"task_updated",
|
|
21
|
-
"task_deleted",
|
|
22
|
-
"task_executed",
|
|
23
|
-
"llm_request",
|
|
24
|
-
"tool_invocation",
|
|
25
|
-
"mcp_request",
|
|
26
|
-
"mcp_tool_execution",
|
|
27
|
-
]);
|
|
28
|
-
|
|
29
|
-
// Category colors for the timeline dot
|
|
30
|
-
const CATEGORY_COLORS: Record<string, string> = {
|
|
31
|
-
CHAT: "bg-green-400",
|
|
32
|
-
LLM: "bg-purple-400",
|
|
33
|
-
TOOL: "bg-blue-400",
|
|
34
|
-
TASK: "bg-yellow-400",
|
|
35
|
-
MEMORY: "bg-cyan-400",
|
|
36
|
-
MCP: "bg-orange-400",
|
|
37
|
-
SYSTEM: "bg-gray-400",
|
|
38
|
-
ERROR: "bg-red-400",
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
function describeEvent(evt: TelemetryEvent, agentName: string): string {
|
|
42
|
-
const data = evt.data || {};
|
|
43
|
-
switch (evt.type) {
|
|
44
|
-
case "thread_activity":
|
|
45
|
-
return (data.activity as string) || "Working...";
|
|
46
|
-
case "agent_started":
|
|
47
|
-
return "Agent started";
|
|
48
|
-
case "agent_stopped":
|
|
49
|
-
return data.reason ? `Agent stopped (${data.reason})` : "Agent stopped";
|
|
50
|
-
case "agent_error":
|
|
51
|
-
return evt.error || "Agent error";
|
|
52
|
-
case "llm_request":
|
|
53
|
-
return "Thinking...";
|
|
54
|
-
case "tool_invocation": {
|
|
55
|
-
const toolRaw = (data.tool_name || "") as string;
|
|
56
|
-
if (!toolRaw) return "Using tools";
|
|
57
|
-
const toolFormatted = toolRaw.replace(/[-_]/g, " ").replace(/\b\w/g, c => c.toUpperCase());
|
|
58
|
-
return `Tool: ${toolFormatted}`;
|
|
59
|
-
}
|
|
60
|
-
case "task_created":
|
|
61
|
-
return data.title ? `Task created: ${data.title}` : "Task created";
|
|
62
|
-
case "task_updated": {
|
|
63
|
-
const status = data.status as string | undefined;
|
|
64
|
-
const title = data.title as string | undefined;
|
|
65
|
-
const statusLabel = status ? status.charAt(0).toUpperCase() + status.slice(1) : null;
|
|
66
|
-
if (title && statusLabel) return `Task ${statusLabel}: ${title}`;
|
|
67
|
-
if (statusLabel) return `Task ${statusLabel}`;
|
|
68
|
-
if (title) return `Task updated: ${title}`;
|
|
69
|
-
return "Task updated";
|
|
70
|
-
}
|
|
71
|
-
case "task_deleted":
|
|
72
|
-
return "Task deleted";
|
|
73
|
-
case "task_executed": {
|
|
74
|
-
const title = data.title as string | undefined;
|
|
75
|
-
return title ? `Task started: ${title}` : "Task started";
|
|
76
|
-
}
|
|
77
|
-
case "memory_stored":
|
|
78
|
-
return "Memory stored";
|
|
79
|
-
case "memory_retrieved":
|
|
80
|
-
return "Memory retrieved";
|
|
81
|
-
case "mcp_request":
|
|
82
|
-
return data.server ? `MCP request to ${data.server}` : "MCP request";
|
|
83
|
-
case "mcp_tool_execution": {
|
|
84
|
-
const rawName = (data.tool_name || data.tool || "") as string;
|
|
85
|
-
if (!rawName) return "MCP tool call";
|
|
86
|
-
// "Server__tool-name-here" -> take part after __, format dashes/underscores to spaces, title case
|
|
87
|
-
const toolPart = rawName.includes("__") ? rawName.split("__").slice(1).join("__") : rawName;
|
|
88
|
-
const formatted = toolPart.replace(/[-_]/g, " ").replace(/\b\w/g, c => c.toUpperCase());
|
|
89
|
-
return `MCP: ${formatted}`;
|
|
90
|
-
}
|
|
91
|
-
default:
|
|
92
|
-
return evt.type.replace(/_/g, " ");
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function ActivityPage({ agents, loading, onNavigate }: ActivityPageProps) {
|
|
97
|
-
const { authFetch } = useAuth();
|
|
98
|
-
const { currentProjectId } = useProjects();
|
|
99
|
-
const { events: realtimeEvents, statusChangeCounter } = useTelemetryContext();
|
|
100
|
-
const [historicalEvents, setHistoricalEvents] = useState<TelemetryEvent[]>([]);
|
|
101
|
-
const [filterAgentId, setFilterAgentId] = useState<string>("");
|
|
102
|
-
const [seenIds] = useState(() => new Set<string>());
|
|
103
|
-
const hasLoadedHistory = useRef(false);
|
|
104
|
-
|
|
105
|
-
const filteredAgents = useMemo(() => {
|
|
106
|
-
if (currentProjectId === null) return agents;
|
|
107
|
-
if (currentProjectId === "unassigned") return agents.filter(a => !a.projectId);
|
|
108
|
-
return agents.filter(a => a.projectId === currentProjectId);
|
|
109
|
-
}, [agents, currentProjectId]);
|
|
110
|
-
|
|
111
|
-
const agentIds = useMemo(() => new Set(filteredAgents.map(a => a.id)), [filteredAgents]);
|
|
112
|
-
const agentNameMap = useMemo(() => {
|
|
113
|
-
const map = new Map<string, string>();
|
|
114
|
-
filteredAgents.forEach(a => map.set(a.id, a.name));
|
|
115
|
-
return map;
|
|
116
|
-
}, [filteredAgents]);
|
|
117
|
-
|
|
118
|
-
// Fetch historical events
|
|
119
|
-
const fetchHistory = useCallback(async () => {
|
|
120
|
-
const projectParam = currentProjectId ? `&project_id=${encodeURIComponent(currentProjectId)}` : "";
|
|
121
|
-
try {
|
|
122
|
-
const res = await authFetch(`/api/telemetry/events?limit=200${projectParam}`);
|
|
123
|
-
if (res.ok) {
|
|
124
|
-
const data = await res.json();
|
|
125
|
-
setHistoricalEvents(data.events || []);
|
|
126
|
-
}
|
|
127
|
-
} catch {}
|
|
128
|
-
}, [authFetch, currentProjectId]);
|
|
129
|
-
|
|
130
|
-
useEffect(() => {
|
|
131
|
-
fetchHistory();
|
|
132
|
-
}, [fetchHistory, statusChangeCounter]);
|
|
133
|
-
|
|
134
|
-
// Mark historical events as seen so they don't animate
|
|
135
|
-
useEffect(() => {
|
|
136
|
-
if (historicalEvents.length > 0 && !hasLoadedHistory.current) {
|
|
137
|
-
for (const evt of historicalEvents) {
|
|
138
|
-
seenIds.add(evt.id);
|
|
139
|
-
}
|
|
140
|
-
// Also mark any realtime events already present at mount
|
|
141
|
-
for (const evt of realtimeEvents) {
|
|
142
|
-
seenIds.add(evt.id);
|
|
143
|
-
}
|
|
144
|
-
hasLoadedHistory.current = true;
|
|
145
|
-
}
|
|
146
|
-
}, [historicalEvents, realtimeEvents, seenIds]);
|
|
147
|
-
|
|
148
|
-
// Merge realtime + historical, filter, sort
|
|
149
|
-
const timeline = useMemo(() => {
|
|
150
|
-
const seen = new Set<string>();
|
|
151
|
-
const merged: TelemetryEvent[] = [];
|
|
152
|
-
|
|
153
|
-
const shouldShow = (evt: TelemetryEvent) => {
|
|
154
|
-
if (!VISIBLE_TYPES.has(evt.type) || !agentIds.has(evt.agent_id) || evt.data?.parent_id) return false;
|
|
155
|
-
// MCP tool: only show completed calls (with duration), skip the start event
|
|
156
|
-
if (evt.type === "mcp_tool_execution" && !evt.duration_ms) return false;
|
|
157
|
-
return true;
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
for (const evt of realtimeEvents) {
|
|
161
|
-
if (!seen.has(evt.id) && shouldShow(evt)) {
|
|
162
|
-
merged.push(evt);
|
|
163
|
-
seen.add(evt.id);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
for (const evt of historicalEvents) {
|
|
167
|
-
if (!seen.has(evt.id) && shouldShow(evt)) {
|
|
168
|
-
merged.push(evt);
|
|
169
|
-
seen.add(evt.id);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
let filtered = merged;
|
|
174
|
-
if (filterAgentId) {
|
|
175
|
-
filtered = filtered.filter(e => e.agent_id === filterAgentId);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
filtered.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
179
|
-
return filtered.slice(0, 200);
|
|
180
|
-
}, [realtimeEvents, historicalEvents, agentIds, filterAgentId]);
|
|
181
|
-
|
|
182
|
-
// Group by date for section headers
|
|
183
|
-
const groupedTimeline = useMemo(() => {
|
|
184
|
-
const groups: { label: string; events: TelemetryEvent[] }[] = [];
|
|
185
|
-
let currentLabel = "";
|
|
186
|
-
const today = new Date().toDateString();
|
|
187
|
-
const yesterday = new Date(Date.now() - 86400000).toDateString();
|
|
188
|
-
|
|
189
|
-
for (const evt of timeline) {
|
|
190
|
-
const dateStr = new Date(evt.timestamp).toDateString();
|
|
191
|
-
const label = dateStr === today ? "Today" : dateStr === yesterday ? "Yesterday" : dateStr;
|
|
192
|
-
if (label !== currentLabel) {
|
|
193
|
-
currentLabel = label;
|
|
194
|
-
groups.push({ label, events: [] });
|
|
195
|
-
}
|
|
196
|
-
groups[groups.length - 1].events.push(evt);
|
|
197
|
-
}
|
|
198
|
-
return groups;
|
|
199
|
-
}, [timeline]);
|
|
200
|
-
|
|
201
|
-
if (loading) {
|
|
202
|
-
return <div className="flex-1 flex items-center justify-center text-[var(--color-text-muted)]">Loading...</div>;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const runningCount = filteredAgents.filter(a => a.status === "running").length;
|
|
206
|
-
|
|
207
|
-
return (
|
|
208
|
-
<div className="flex-1 flex flex-col overflow-hidden">
|
|
209
|
-
{/* Header */}
|
|
210
|
-
<div className="px-6 pt-6 pb-4 shrink-0">
|
|
211
|
-
<div className="flex items-center justify-between gap-4">
|
|
212
|
-
<div>
|
|
213
|
-
<h1 className="text-xl font-semibold">Activity</h1>
|
|
214
|
-
<p className="text-sm text-[var(--color-text-muted)] mt-0.5">
|
|
215
|
-
{runningCount} of {filteredAgents.length} agents running
|
|
216
|
-
</p>
|
|
217
|
-
</div>
|
|
218
|
-
<div className="w-48">
|
|
219
|
-
<Select
|
|
220
|
-
value={filterAgentId}
|
|
221
|
-
onChange={setFilterAgentId}
|
|
222
|
-
placeholder="All agents"
|
|
223
|
-
options={[
|
|
224
|
-
{ value: "", label: "All agents" },
|
|
225
|
-
...filteredAgents.map(a => ({ value: a.id, label: a.name })),
|
|
226
|
-
]}
|
|
227
|
-
/>
|
|
228
|
-
</div>
|
|
229
|
-
</div>
|
|
230
|
-
</div>
|
|
231
|
-
|
|
232
|
-
{/* Timeline */}
|
|
233
|
-
<div className="flex-1 overflow-auto px-6 pb-6">
|
|
234
|
-
{timeline.length === 0 ? (
|
|
235
|
-
<div className="flex flex-col items-center justify-center py-20 text-[var(--color-text-faint)]">
|
|
236
|
-
<p className="text-lg mb-2">No activity yet</p>
|
|
237
|
-
<p className="text-sm">Agent activity will appear here in real-time.</p>
|
|
238
|
-
</div>
|
|
239
|
-
) : (
|
|
240
|
-
<div className="max-w-2xl">
|
|
241
|
-
{groupedTimeline.map(group => (
|
|
242
|
-
<div key={group.label}>
|
|
243
|
-
{/* Date header */}
|
|
244
|
-
<div className="sticky top-0 z-10 bg-[var(--color-bg)] backdrop-blur-sm py-2 mb-1">
|
|
245
|
-
<span className="text-xs font-semibold text-[var(--color-text-muted)] uppercase tracking-wider">
|
|
246
|
-
{group.label}
|
|
247
|
-
</span>
|
|
248
|
-
</div>
|
|
249
|
-
|
|
250
|
-
{/* Events */}
|
|
251
|
-
<div className="relative ml-3 border-l border-[var(--color-border)]">
|
|
252
|
-
{group.events.map(evt => {
|
|
253
|
-
const isNew = !seenIds.has(evt.id);
|
|
254
|
-
if (isNew) seenIds.add(evt.id); // mark seen after first render
|
|
255
|
-
const agentName = agentNameMap.get(evt.agent_id) || evt.agent_id.slice(0, 8);
|
|
256
|
-
const dotColor = evt.level === "error"
|
|
257
|
-
? "bg-red-400"
|
|
258
|
-
: CATEGORY_COLORS[evt.category] || "bg-[var(--color-text-faint)]";
|
|
259
|
-
|
|
260
|
-
return (
|
|
261
|
-
<div
|
|
262
|
-
key={evt.id}
|
|
263
|
-
className={`relative pl-6 pr-3 py-2.5 hover:bg-[var(--color-surface-hover)] transition-colors ${
|
|
264
|
-
isNew ? "animate-slideIn" : ""
|
|
265
|
-
}`}
|
|
266
|
-
>
|
|
267
|
-
{/* Timeline dot */}
|
|
268
|
-
<span className={`absolute left-[-4.5px] top-[14px] w-[9px] h-[9px] rounded-full ${dotColor} ring-2 ring-[#0a0a0a]`} />
|
|
269
|
-
|
|
270
|
-
<div className="flex items-start justify-between gap-3">
|
|
271
|
-
<div className="flex-1 min-w-0">
|
|
272
|
-
<p className={`text-sm ${evt.level === "error" ? "text-red-400" : ""}`}>
|
|
273
|
-
{describeEvent(evt, agentName)}
|
|
274
|
-
</p>
|
|
275
|
-
<div className="flex items-center gap-2 text-[11px] text-[var(--color-text-faint)] mt-0.5">
|
|
276
|
-
<span className="text-[var(--color-text-secondary)] font-medium">{agentName}</span>
|
|
277
|
-
<span className="text-[var(--color-border-light)]">·</span>
|
|
278
|
-
<span className="text-[var(--color-text-faint)]">{evt.category}</span>
|
|
279
|
-
{evt.duration_ms != null && evt.duration_ms > 0 && (
|
|
280
|
-
<>
|
|
281
|
-
<span className="text-[var(--color-border-light)]">·</span>
|
|
282
|
-
<span>{evt.duration_ms < 1000 ? `${evt.duration_ms}ms` : `${(evt.duration_ms / 1000).toFixed(1)}s`}</span>
|
|
283
|
-
</>
|
|
284
|
-
)}
|
|
285
|
-
</div>
|
|
286
|
-
</div>
|
|
287
|
-
<span className="text-[11px] text-[var(--color-text-faint)] shrink-0 pt-0.5">
|
|
288
|
-
{timeAgo(evt.timestamp)}
|
|
289
|
-
</span>
|
|
290
|
-
</div>
|
|
291
|
-
</div>
|
|
292
|
-
);
|
|
293
|
-
})}
|
|
294
|
-
</div>
|
|
295
|
-
</div>
|
|
296
|
-
))}
|
|
297
|
-
</div>
|
|
298
|
-
)}
|
|
299
|
-
</div>
|
|
300
|
-
</div>
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function timeAgo(timestamp: string): string {
|
|
305
|
-
const seconds = Math.floor((Date.now() - new Date(timestamp).getTime()) / 1000);
|
|
306
|
-
if (seconds < 5) return "just now";
|
|
307
|
-
if (seconds < 60) return `${seconds}s ago`;
|
|
308
|
-
const minutes = Math.floor(seconds / 60);
|
|
309
|
-
if (minutes < 60) return `${minutes}m ago`;
|
|
310
|
-
const hours = Math.floor(minutes / 60);
|
|
311
|
-
if (hours < 24) return `${hours}h ago`;
|
|
312
|
-
const days = Math.floor(hours / 24);
|
|
313
|
-
return `${days}d ago`;
|
|
314
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { ActivityPage } from "./ActivityPage";
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { MemoryIcon, TasksIcon, VisionIcon, OperatorIcon, McpIcon, RealtimeIcon, FilesIcon, MultiAgentIcon, SkillsIcon, ActivityIcon } from "../common/Icons";
|
|
3
|
-
import { useAgentActivity, useProjects, useUIMode } from "../../context";
|
|
4
|
-
import type { Agent, AgentFeatures } from "../../types";
|
|
5
|
-
|
|
6
|
-
interface AgentCardProps {
|
|
7
|
-
agent: Agent;
|
|
8
|
-
selected: boolean;
|
|
9
|
-
onSelect: () => void;
|
|
10
|
-
onToggle: (e?: React.MouseEvent) => void;
|
|
11
|
-
showProject?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const FEATURE_ICONS: { key: keyof AgentFeatures; icon: React.ComponentType<{ className?: string }>; label: string }[] = [
|
|
15
|
-
{ key: "memory", icon: MemoryIcon, label: "Memory" },
|
|
16
|
-
{ key: "tasks", icon: TasksIcon, label: "Tasks" },
|
|
17
|
-
{ key: "files", icon: FilesIcon, label: "Files" },
|
|
18
|
-
{ key: "vision", icon: VisionIcon, label: "Vision" },
|
|
19
|
-
{ key: "operator", icon: OperatorIcon, label: "Operator" },
|
|
20
|
-
{ key: "mcp", icon: McpIcon, label: "MCP" },
|
|
21
|
-
{ key: "realtime", icon: RealtimeIcon, label: "Realtime" },
|
|
22
|
-
{ key: "agents", icon: MultiAgentIcon, label: "Multi-Agent" },
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
export const AgentCard = React.memo(function AgentCard({ agent, selected, onSelect, onToggle, showProject }: AgentCardProps) {
|
|
26
|
-
const enabledFeatures = FEATURE_ICONS.filter(f => agent.features?.[f.key]);
|
|
27
|
-
const mcpServers = agent.mcpServerDetails || [];
|
|
28
|
-
const skills = agent.skillDetails || [];
|
|
29
|
-
const { isActive, label: activityLabel } = useAgentActivity(agent.id);
|
|
30
|
-
const { projects } = useProjects();
|
|
31
|
-
const { isDev } = useUIMode();
|
|
32
|
-
const project = agent.projectId ? projects.find(p => p.id === agent.projectId) : null;
|
|
33
|
-
const subscriptions = agent.subscriptions || [];
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<div
|
|
37
|
-
onClick={onSelect}
|
|
38
|
-
className={`bg-[var(--color-surface)] card p-5 transition cursor-pointer flex flex-col h-full ${
|
|
39
|
-
selected
|
|
40
|
-
? '!border-[var(--color-accent)]'
|
|
41
|
-
: 'hover:border-[var(--color-border-light)]'
|
|
42
|
-
}`}
|
|
43
|
-
>
|
|
44
|
-
<div className="flex items-start justify-between mb-3">
|
|
45
|
-
<div>
|
|
46
|
-
<h3 className="font-semibold text-lg">{agent.name}</h3>
|
|
47
|
-
{isDev && (
|
|
48
|
-
<p className="text-sm text-[var(--color-text-muted)]">
|
|
49
|
-
{agent.provider} / {agent.model}
|
|
50
|
-
{agent.port && <span className="text-[var(--color-text-faint)]"> · :{agent.port}</span>}
|
|
51
|
-
</p>
|
|
52
|
-
)}
|
|
53
|
-
{showProject && project && (
|
|
54
|
-
<p className="text-sm text-[var(--color-text-muted)] flex items-center gap-1.5 mt-1">
|
|
55
|
-
<span className="w-2 h-2 rounded-full" style={{ backgroundColor: project.color }} />
|
|
56
|
-
{project.name}
|
|
57
|
-
</p>
|
|
58
|
-
)}
|
|
59
|
-
</div>
|
|
60
|
-
<StatusBadge status={agent.status} isActive={isActive && agent.status === "running"} activityLabel={activityLabel} />
|
|
61
|
-
</div>
|
|
62
|
-
|
|
63
|
-
{isDev && enabledFeatures.length > 0 && (
|
|
64
|
-
<div className="flex flex-wrap gap-1.5 mb-3">
|
|
65
|
-
{enabledFeatures.map(({ key, icon: Icon, label }) => (
|
|
66
|
-
<span
|
|
67
|
-
key={key}
|
|
68
|
-
className="inline-flex items-center gap-1 px-2 py-0.5 bg-[var(--color-accent-10)] text-[var(--color-accent-70)] text-xs"
|
|
69
|
-
style={{ borderRadius: "var(--radius-badge)" }}
|
|
70
|
-
title={label}
|
|
71
|
-
>
|
|
72
|
-
<Icon className="w-3 h-3" />
|
|
73
|
-
{label}
|
|
74
|
-
</span>
|
|
75
|
-
))}
|
|
76
|
-
</div>
|
|
77
|
-
)}
|
|
78
|
-
|
|
79
|
-
{/* MCP Servers */}
|
|
80
|
-
{isDev && mcpServers.length > 0 && (
|
|
81
|
-
<div className="flex flex-wrap gap-1.5 mb-3">
|
|
82
|
-
{mcpServers.map((server) => {
|
|
83
|
-
// HTTP/remote servers are always available
|
|
84
|
-
const isAvailable = (server.type === "http" && server.url) || server.status === "running";
|
|
85
|
-
return (
|
|
86
|
-
<span
|
|
87
|
-
key={server.id}
|
|
88
|
-
className={`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs ${
|
|
89
|
-
isAvailable
|
|
90
|
-
? "bg-green-500/10 text-green-400"
|
|
91
|
-
: "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]"
|
|
92
|
-
}`}
|
|
93
|
-
title={`MCP: ${server.name} (${isAvailable ? "available" : server.status})`}
|
|
94
|
-
>
|
|
95
|
-
<McpIcon className="w-3 h-3" />
|
|
96
|
-
{server.name}
|
|
97
|
-
</span>
|
|
98
|
-
);
|
|
99
|
-
})}
|
|
100
|
-
</div>
|
|
101
|
-
)}
|
|
102
|
-
|
|
103
|
-
{/* Skills */}
|
|
104
|
-
{isDev && skills.length > 0 && (
|
|
105
|
-
<div className="flex flex-wrap gap-1.5 mb-3">
|
|
106
|
-
{skills.map((skill) => (
|
|
107
|
-
<span
|
|
108
|
-
key={skill.id}
|
|
109
|
-
className={`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs ${
|
|
110
|
-
skill.enabled
|
|
111
|
-
? "bg-purple-500/10 text-purple-400"
|
|
112
|
-
: "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]"
|
|
113
|
-
}`}
|
|
114
|
-
title={`Skill: ${skill.name} v${skill.version}`}
|
|
115
|
-
>
|
|
116
|
-
<SkillsIcon className="w-3 h-3" />
|
|
117
|
-
{skill.name}
|
|
118
|
-
</span>
|
|
119
|
-
))}
|
|
120
|
-
</div>
|
|
121
|
-
)}
|
|
122
|
-
|
|
123
|
-
{/* Subscriptions (triggers listening to) */}
|
|
124
|
-
{isDev && subscriptions.length > 0 && (
|
|
125
|
-
<div className="flex flex-wrap gap-1.5 mb-3">
|
|
126
|
-
{subscriptions.map((sub) => (
|
|
127
|
-
<span
|
|
128
|
-
key={sub.id}
|
|
129
|
-
className={`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs ${
|
|
130
|
-
sub.enabled
|
|
131
|
-
? "bg-cyan-500/10 text-cyan-400"
|
|
132
|
-
: "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]"
|
|
133
|
-
}`}
|
|
134
|
-
title={`Trigger: ${sub.trigger_slug.replace(/_/g, " ")}`}
|
|
135
|
-
>
|
|
136
|
-
<ActivityIcon className="w-3 h-3" />
|
|
137
|
-
{sub.trigger_slug.replace(/_/g, " ")}
|
|
138
|
-
</span>
|
|
139
|
-
))}
|
|
140
|
-
</div>
|
|
141
|
-
)}
|
|
142
|
-
|
|
143
|
-
<p className="text-sm text-[var(--color-text-muted)] line-clamp-2 mb-4 flex-1">
|
|
144
|
-
{agent.systemPrompt}
|
|
145
|
-
</p>
|
|
146
|
-
|
|
147
|
-
<button
|
|
148
|
-
onClick={onToggle}
|
|
149
|
-
disabled={agent.status === "starting" || agent.status === "stopping"}
|
|
150
|
-
className={`w-full px-3 py-1.5 btn text-sm font-medium transition mt-auto ${
|
|
151
|
-
agent.status === "starting" || agent.status === "stopping"
|
|
152
|
-
? "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)] cursor-wait"
|
|
153
|
-
: agent.status === "running"
|
|
154
|
-
? "bg-red-500/15 text-red-500 hover:bg-red-500/25 border border-red-500/20"
|
|
155
|
-
: "bg-[#3b82f6]/20 text-[#3b82f6] hover:bg-[#3b82f6]/30"
|
|
156
|
-
}`}
|
|
157
|
-
>
|
|
158
|
-
{agent.status === "starting" ? "Starting..." : agent.status === "stopping" ? "Stopping..." : agent.status === "running" ? "Stop" : "Start"}
|
|
159
|
-
</button>
|
|
160
|
-
</div>
|
|
161
|
-
);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
function StatusBadge({ status, isActive, activityLabel }: { status: Agent["status"]; isActive?: boolean; activityLabel?: string }) {
|
|
165
|
-
if (status === "running" && isActive && activityLabel) {
|
|
166
|
-
return (
|
|
167
|
-
<span className="px-2 py-1 text-xs font-medium bg-green-500/20 text-green-400 animate-pulse" style={{ borderRadius: "var(--radius-badge)" }}>
|
|
168
|
-
{activityLabel}
|
|
169
|
-
</span>
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const isTransitioning = status === "starting" || status === "stopping";
|
|
174
|
-
|
|
175
|
-
return (
|
|
176
|
-
<span
|
|
177
|
-
className={`px-2 py-1 text-xs font-medium ${
|
|
178
|
-
isTransitioning
|
|
179
|
-
? "bg-yellow-500/20 text-yellow-400 animate-pulse"
|
|
180
|
-
: status === "running"
|
|
181
|
-
? "bg-[#3b82f6]/20 text-[#3b82f6]"
|
|
182
|
-
: "bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]"
|
|
183
|
-
}`}
|
|
184
|
-
style={{ borderRadius: "var(--radius-badge)" }}
|
|
185
|
-
>
|
|
186
|
-
{status}
|
|
187
|
-
</span>
|
|
188
|
-
);
|
|
189
|
-
}
|