apteva 0.4.56 → 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.
Files changed (142) hide show
  1. package/README.md +216 -54
  2. package/cli.js +35 -0
  3. package/install.js +92 -0
  4. package/package.json +12 -79
  5. package/LICENSE +0 -63
  6. package/bin/apteva.js +0 -196
  7. package/dist/ActivityPage.kxzzb4yc.js +0 -3
  8. package/dist/ApiDocsPage.zq998hbm.js +0 -4
  9. package/dist/App.55rea8mn.js +0 -61
  10. package/dist/App.5ywb23z4.js +0 -53
  11. package/dist/App.6thds120.js +0 -4
  12. package/dist/App.9tctxzqm.js +0 -8
  13. package/dist/App.a8r8ttaz.js +0 -4
  14. package/dist/App.agsv5bje.js +0 -4
  15. package/dist/App.cepapqmx.js +0 -4
  16. package/dist/App.dp041gb3.js +0 -221
  17. package/dist/App.fds72zb5.js +0 -4
  18. package/dist/App.fg9qj2dq.js +0 -4
  19. package/dist/App.ndfejbm9.js +0 -4
  20. package/dist/App.nxmfmq1h.js +0 -13
  21. package/dist/App.qdfyt8ba.js +0 -4
  22. package/dist/App.x2d0ygt6.js +0 -4
  23. package/dist/App.yt9p4nr3.js +0 -20
  24. package/dist/App.zn4mw16t.js +0 -1
  25. package/dist/ConnectionsPage.8r96ryw7.js +0 -3
  26. package/dist/McpPage.3cwh0gnd.js +0 -3
  27. package/dist/SettingsPage.ykgdh5ev.js +0 -3
  28. package/dist/SkillsPage.4np1s65b.js +0 -3
  29. package/dist/TasksPage.4g08t7p6.js +0 -3
  30. package/dist/TelemetryPage.72w9pwcp.js +0 -3
  31. package/dist/TestsPage.z4fk3r7r.js +0 -3
  32. package/dist/ThreadsPage.63tcajeh.js +0 -3
  33. package/dist/apteva-kit.css +0 -1
  34. package/dist/icon.png +0 -0
  35. package/dist/index.html +0 -16
  36. package/dist/styles.css +0 -1
  37. package/scripts/postinstall.mjs +0 -102
  38. package/src/auth/index.ts +0 -394
  39. package/src/auth/middleware.ts +0 -213
  40. package/src/binary.ts +0 -536
  41. package/src/channels/index.ts +0 -40
  42. package/src/channels/telegram.ts +0 -311
  43. package/src/crypto.ts +0 -301
  44. package/src/db-tests.ts +0 -174
  45. package/src/db.ts +0 -3133
  46. package/src/integrations/agentdojo.ts +0 -559
  47. package/src/integrations/composio.ts +0 -437
  48. package/src/integrations/index.ts +0 -87
  49. package/src/integrations/skillsmp.ts +0 -318
  50. package/src/mcp-client.ts +0 -605
  51. package/src/mcp-handler.ts +0 -394
  52. package/src/mcp-platform.ts +0 -2370
  53. package/src/openapi.ts +0 -2410
  54. package/src/providers.ts +0 -597
  55. package/src/routes/api/agent-utils.ts +0 -890
  56. package/src/routes/api/agents.ts +0 -916
  57. package/src/routes/api/api-keys.ts +0 -95
  58. package/src/routes/api/channels.ts +0 -182
  59. package/src/routes/api/helpers.ts +0 -12
  60. package/src/routes/api/integrations.ts +0 -639
  61. package/src/routes/api/mcp.ts +0 -574
  62. package/src/routes/api/meta-agent.ts +0 -195
  63. package/src/routes/api/projects.ts +0 -112
  64. package/src/routes/api/providers.ts +0 -424
  65. package/src/routes/api/skills.ts +0 -537
  66. package/src/routes/api/system.ts +0 -333
  67. package/src/routes/api/telemetry.ts +0 -203
  68. package/src/routes/api/tests.ts +0 -148
  69. package/src/routes/api/triggers.ts +0 -518
  70. package/src/routes/api/users.ts +0 -148
  71. package/src/routes/api/webhooks.ts +0 -171
  72. package/src/routes/api.ts +0 -53
  73. package/src/routes/auth.ts +0 -251
  74. package/src/routes/share.ts +0 -86
  75. package/src/routes/static.ts +0 -131
  76. package/src/server.ts +0 -642
  77. package/src/test-runner.ts +0 -598
  78. package/src/triggers/agentdojo.ts +0 -253
  79. package/src/triggers/composio.ts +0 -264
  80. package/src/triggers/index.ts +0 -71
  81. package/src/tui/AgentList.tsx +0 -145
  82. package/src/tui/App.tsx +0 -102
  83. package/src/tui/Login.tsx +0 -104
  84. package/src/tui/api.ts +0 -72
  85. package/src/tui/index.tsx +0 -7
  86. package/src/web/App.tsx +0 -455
  87. package/src/web/components/activity/ActivityPage.tsx +0 -314
  88. package/src/web/components/activity/index.ts +0 -1
  89. package/src/web/components/agents/AgentCard.tsx +0 -189
  90. package/src/web/components/agents/AgentPanel.tsx +0 -2244
  91. package/src/web/components/agents/AgentsView.tsx +0 -180
  92. package/src/web/components/agents/CreateAgentModal.tsx +0 -475
  93. package/src/web/components/agents/index.ts +0 -4
  94. package/src/web/components/api/ApiDocsPage.tsx +0 -842
  95. package/src/web/components/auth/CreateAccountStep.tsx +0 -176
  96. package/src/web/components/auth/LoginPage.tsx +0 -91
  97. package/src/web/components/auth/index.ts +0 -2
  98. package/src/web/components/common/Icons.tsx +0 -250
  99. package/src/web/components/common/LoadingSpinner.tsx +0 -44
  100. package/src/web/components/common/Modal.tsx +0 -199
  101. package/src/web/components/common/Select.tsx +0 -97
  102. package/src/web/components/common/index.ts +0 -20
  103. package/src/web/components/connections/ConnectionsPage.tsx +0 -54
  104. package/src/web/components/connections/IntegrationsTab.tsx +0 -170
  105. package/src/web/components/connections/OverviewTab.tsx +0 -137
  106. package/src/web/components/connections/TriggersTab.tsx +0 -1346
  107. package/src/web/components/dashboard/Dashboard.tsx +0 -572
  108. package/src/web/components/dashboard/index.ts +0 -1
  109. package/src/web/components/index.ts +0 -21
  110. package/src/web/components/layout/ErrorBanner.tsx +0 -18
  111. package/src/web/components/layout/Header.tsx +0 -332
  112. package/src/web/components/layout/Sidebar.tsx +0 -231
  113. package/src/web/components/layout/index.ts +0 -3
  114. package/src/web/components/mcp/IntegrationsPanel.tsx +0 -857
  115. package/src/web/components/mcp/McpPage.tsx +0 -2515
  116. package/src/web/components/mcp/index.ts +0 -1
  117. package/src/web/components/meta-agent/MetaAgent.tsx +0 -245
  118. package/src/web/components/onboarding/OnboardingWizard.tsx +0 -404
  119. package/src/web/components/onboarding/index.ts +0 -1
  120. package/src/web/components/settings/SettingsPage.tsx +0 -2776
  121. package/src/web/components/settings/index.ts +0 -1
  122. package/src/web/components/skills/SkillsPage.tsx +0 -1200
  123. package/src/web/components/tasks/TasksPage.tsx +0 -1116
  124. package/src/web/components/tasks/index.ts +0 -1
  125. package/src/web/components/telemetry/TelemetryPage.tsx +0 -1129
  126. package/src/web/components/tests/TestsPage.tsx +0 -594
  127. package/src/web/components/threads/ThreadsPage.tsx +0 -315
  128. package/src/web/context/AuthContext.tsx +0 -242
  129. package/src/web/context/ProjectContext.tsx +0 -214
  130. package/src/web/context/TelemetryContext.tsx +0 -299
  131. package/src/web/context/ThemeContext.tsx +0 -90
  132. package/src/web/context/UIModeContext.tsx +0 -49
  133. package/src/web/context/index.ts +0 -12
  134. package/src/web/hooks/index.ts +0 -3
  135. package/src/web/hooks/useAgents.ts +0 -115
  136. package/src/web/hooks/useOnboarding.ts +0 -20
  137. package/src/web/hooks/useProviders.ts +0 -75
  138. package/src/web/icon.png +0 -0
  139. package/src/web/index.html +0 -16
  140. package/src/web/styles.css +0 -118
  141. package/src/web/themes.ts +0 -162
  142. package/src/web/types.ts +0 -298
@@ -1,572 +0,0 @@
1
- import React, { useState, useEffect, useCallback, useMemo, useRef } from "react";
2
- import { useAgentActivity, useAuth, useProjects, useTelemetryContext, useUIMode } from "../../context";
3
- import { useTelemetry } from "../../context/TelemetryContext";
4
- import type { TelemetryEvent } from "../../context";
5
- import type { Agent, Provider, Route, DashboardStats, Task } from "../../types";
6
- import { CloseIcon } from "../common/Icons";
7
-
8
- interface DashboardProps {
9
- agents: Agent[];
10
- loading: boolean;
11
- runningCount: number;
12
- configuredProviders: Provider[];
13
- onNavigate: (route: Route) => void;
14
- onSelectAgent: (agent: Agent) => void;
15
- }
16
-
17
- export function Dashboard({
18
- agents,
19
- loading,
20
- runningCount,
21
- configuredProviders,
22
- onNavigate,
23
- onSelectAgent,
24
- }: DashboardProps) {
25
- const { authFetch } = useAuth();
26
- const { currentProjectId } = useProjects();
27
- const { isDev, t } = useUIMode();
28
- const { events: realtimeEvents, statusChangeCounter } = useTelemetryContext();
29
- const { events: taskTelemetryEvents } = useTelemetry({ category: "TASK" });
30
- const lastProcessedTaskEventRef = useRef<string | null>(null);
31
- const [stats, setStats] = useState<DashboardStats | null>(null);
32
- const [recentTasks, setRecentTasks] = useState<Task[]>([]);
33
- const [historicalActivities, setHistoricalActivities] = useState<TelemetryEvent[]>([]);
34
- const [quickMessageAgent, setQuickMessageAgent] = useState<Agent | null>(null);
35
-
36
- // Filter agents by current project
37
- const filteredAgents = useMemo(() => {
38
- if (!currentProjectId) return agents; // "All Projects"
39
- if (currentProjectId === "unassigned") return agents.filter(a => !a.projectId);
40
- return agents.filter(a => a.projectId === currentProjectId);
41
- }, [agents, currentProjectId]);
42
-
43
- const filteredRunningCount = useMemo(() => {
44
- return filteredAgents.filter(a => a.status === "running").length;
45
- }, [filteredAgents]);
46
-
47
- // Get agent IDs for filtering tasks
48
- const projectAgentIds = useMemo(() => {
49
- return new Set(filteredAgents.map(a => a.id));
50
- }, [filteredAgents]);
51
-
52
- const fetchDashboardData = useCallback(async () => {
53
- try {
54
- const projectParam = currentProjectId ? `project_id=${encodeURIComponent(currentProjectId)}` : "";
55
- const [dashRes, tasksRes, activityRes] = await Promise.all([
56
- authFetch(`/api/dashboard${projectParam ? `?${projectParam}` : ""}`),
57
- authFetch(`/api/tasks?status=all${projectParam ? `&${projectParam}` : ""}`),
58
- authFetch(`/api/telemetry/events?type=thread_activity&limit=20${projectParam ? `&${projectParam}` : ""}`),
59
- ]);
60
-
61
- if (dashRes.ok) {
62
- const data = await dashRes.json();
63
- setStats(data);
64
- }
65
-
66
- if (tasksRes.ok) {
67
- const data = await tasksRes.json();
68
- setRecentTasks(data.tasks || []);
69
- }
70
-
71
- if (activityRes.ok) {
72
- const data = await activityRes.json();
73
- setHistoricalActivities(data.events || []);
74
- }
75
- } catch (e) {
76
- console.error("Failed to fetch dashboard data:", e);
77
- }
78
- }, [authFetch, currentProjectId]);
79
-
80
- useEffect(() => {
81
- fetchDashboardData();
82
- }, [fetchDashboardData, statusChangeCounter]);
83
-
84
- // Real-time task updates from telemetry
85
- useEffect(() => {
86
- if (!taskTelemetryEvents.length) return;
87
- const latestEvent = taskTelemetryEvents[0];
88
- if (!latestEvent || latestEvent.id === lastProcessedTaskEventRef.current) return;
89
- if (latestEvent.type === "task_created" || latestEvent.type === "task_updated" || latestEvent.type === "task_deleted") {
90
- lastProcessedTaskEventRef.current = latestEvent.id;
91
- fetchDashboardData();
92
- }
93
- }, [taskTelemetryEvents, fetchDashboardData]);
94
-
95
- // Filter tasks by project agents and sort by next execution (soonest first)
96
- const filteredTasks = useMemo(() => {
97
- let list = currentProjectId
98
- ? recentTasks.filter(t => projectAgentIds.has(t.agentId))
99
- : recentTasks;
100
- return sortTasksByNextExecution(list);
101
- }, [recentTasks, currentProjectId, projectAgentIds]);
102
-
103
- // Calculate task stats from filtered tasks
104
- const taskStats = useMemo(() => {
105
- if (!currentProjectId) {
106
- return stats?.tasks || { total: 0, pending: 0, running: 0, completed: 0 };
107
- }
108
- // When filtering by project, calculate from filtered tasks
109
- const total = filteredTasks.length;
110
- const pending = filteredTasks.filter(t => t.status === "pending").length;
111
- const running = filteredTasks.filter(t => t.status === "running").length;
112
- const completed = filteredTasks.filter(t => t.status === "completed").length;
113
- return { total, pending, running, completed };
114
- }, [stats, currentProjectId, filteredTasks]);
115
-
116
- // Merge real-time + historical thread_activity events, deduplicate
117
- const activities = useMemo(() => {
118
- const realtimeActivities = realtimeEvents.filter(e => e.type === "thread_activity" && !e.data?.parent_id);
119
- const seen = new Set(realtimeActivities.map(e => e.id));
120
- const merged = [...realtimeActivities];
121
- for (const evt of historicalActivities) {
122
- if (!seen.has(evt.id) && !evt.data?.parent_id) {
123
- merged.push(evt);
124
- seen.add(evt.id);
125
- }
126
- }
127
- // Filter by project
128
- let filtered = merged;
129
- if (currentProjectId) {
130
- filtered = merged.filter(e => projectAgentIds.has(e.agent_id));
131
- }
132
- // Sort newest first
133
- filtered.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
134
- return filtered.slice(0, 12);
135
- }, [realtimeEvents, historicalActivities, currentProjectId, projectAgentIds]);
136
-
137
- // Build agent name lookup
138
- const agentNameMap = useMemo(() => {
139
- const map = new Map<string, string>();
140
- for (const a of agents) {
141
- map.set(a.id, a.name);
142
- }
143
- return map;
144
- }, [agents]);
145
-
146
- return (
147
- <div className="flex-1 overflow-auto p-6">
148
- {/* Stats Cards */}
149
- <div className={`grid grid-cols-2 ${isDev ? 'sm:grid-cols-4' : 'sm:grid-cols-3'} gap-4 mb-6`}>
150
- <StatCard label={t("Agents", "Employees")} value={filteredAgents.length} subValue={`${filteredRunningCount} running`} />
151
- <StatCard label="Tasks" value={taskStats.total} subValue={`${taskStats.pending} pending`} />
152
- <StatCard label="Completed" value={taskStats.completed} color="text-green-400" />
153
- {isDev && <StatCard label="Providers" value={configuredProviders.length} color="text-[var(--color-accent)]" />}
154
- </div>
155
-
156
- <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
157
- {/* Agents List */}
158
- <DashboardCard
159
- title={t("Agents", "Employees")}
160
- actionLabel="View All"
161
- onAction={() => onNavigate("agents")}
162
- >
163
- {loading ? (
164
- <div className="p-4 text-center text-[var(--color-text-muted)]">Loading...</div>
165
- ) : filteredAgents.length === 0 ? (
166
- <div className="p-4 text-center text-[var(--color-text-muted)]">{t("No agents yet", "No employees yet")}</div>
167
- ) : (
168
- <div className="divide-y divide-[var(--color-border)]">
169
- {filteredAgents.slice(0, 5).map((agent) => (
170
- <AgentListItem
171
- key={agent.id}
172
- agent={agent}
173
- onSelect={() => onSelectAgent(agent)}
174
- onMessage={agent.status === "running" ? () => setQuickMessageAgent(agent) : undefined}
175
- showProject={!currentProjectId}
176
- />
177
- ))}
178
- </div>
179
- )}
180
- </DashboardCard>
181
-
182
- {/* Activity Feed */}
183
- <DashboardCard
184
- title="Activity"
185
- actionLabel="Analytics"
186
- onAction={() => onNavigate("analytics")}
187
- >
188
- {activities.length === 0 ? (
189
- <div className="p-4 text-center text-[var(--color-text-muted)]">
190
- <p>No activity yet</p>
191
- <p className="text-sm text-[var(--color-text-faint)] mt-1">Agent activity will appear here in real-time</p>
192
- </div>
193
- ) : (
194
- <div className="divide-y divide-[var(--color-border)]">
195
- {activities.map((evt) => (
196
- <ActivityItem
197
- key={evt.id}
198
- activity={(evt.data?.activity as string) || "Working..."}
199
- agentName={agentNameMap.get(evt.agent_id) || evt.agent_id}
200
- timestamp={evt.timestamp}
201
- />
202
- ))}
203
- </div>
204
- )}
205
- </DashboardCard>
206
-
207
- {/* Tasks */}
208
- <DashboardCard
209
- title="Tasks"
210
- actionLabel="View All"
211
- onAction={() => onNavigate("tasks")}
212
- >
213
- {filteredTasks.length === 0 ? (
214
- <div className="p-4 text-center text-[var(--color-text-muted)]">
215
- <p>No tasks yet</p>
216
- <p className="text-sm text-[var(--color-text-faint)] mt-1">Tasks will appear when agents create them</p>
217
- </div>
218
- ) : (
219
- <div className="divide-y divide-[var(--color-border)]">
220
- {filteredTasks.slice(0, 5).map((task) => (
221
- <div
222
- key={`${task.agentId}-${task.id}`}
223
- className="px-4 py-3 flex items-center justify-between"
224
- >
225
- <div className="flex-1 min-w-0">
226
- <p className="font-medium truncate">{task.title}</p>
227
- <p className="text-sm text-[var(--color-text-muted)]">
228
- {task.agentName}
229
- {task.recurrence && (
230
- <span className="ml-1 text-[var(--color-text-faint)]">· {formatCronShort(task.recurrence)}</span>
231
- )}
232
- {task.next_run && (
233
- <span className="ml-1 text-[var(--color-accent)]">· {formatRelativeShort(task.next_run)}</span>
234
- )}
235
- {!task.next_run && task.execute_at && (
236
- <span className="ml-1 text-[var(--color-accent)]">· {formatRelativeShort(task.execute_at)}</span>
237
- )}
238
- </p>
239
- </div>
240
- <TaskStatusBadge status={task.status} />
241
- </div>
242
- ))}
243
- </div>
244
- )}
245
- </DashboardCard>
246
- </div>
247
-
248
- {/* Quick Message Modal */}
249
- {quickMessageAgent && (
250
- <QuickMessageModal
251
- agent={quickMessageAgent}
252
- onClose={() => setQuickMessageAgent(null)}
253
- />
254
- )}
255
- </div>
256
- );
257
- }
258
-
259
- interface StatCardProps {
260
- label: string;
261
- value: number;
262
- subValue?: string;
263
- color?: string;
264
- }
265
-
266
- function StatCard({ label, value, subValue, color }: StatCardProps) {
267
- return (
268
- <div className="bg-[var(--color-surface)] rounded p-4 border border-[var(--color-border)]">
269
- <p className="text-sm text-[var(--color-text-muted)] mb-1">{label}</p>
270
- <p className={`text-2xl font-semibold ${color || ''}`}>{value}</p>
271
- {subValue && <p className="text-xs text-[var(--color-text-faint)] mt-1">{subValue}</p>}
272
- </div>
273
- );
274
- }
275
-
276
- interface DashboardCardProps {
277
- title: string;
278
- actionLabel: string;
279
- onAction: () => void;
280
- children: React.ReactNode;
281
- }
282
-
283
- function DashboardCard({ title, actionLabel, onAction, children }: DashboardCardProps) {
284
- return (
285
- <div className="bg-[var(--color-surface)] rounded border border-[var(--color-border)] overflow-hidden">
286
- <div className="px-4 py-3 border-b border-[var(--color-border)] flex items-center justify-between">
287
- <h3 className="font-semibold">{title}</h3>
288
- <button
289
- onClick={onAction}
290
- className="text-sm text-[#3b82f6] hover:text-[#60a5fa]"
291
- >
292
- {actionLabel}
293
- </button>
294
- </div>
295
- {children}
296
- </div>
297
- );
298
- }
299
-
300
- function AgentListItem({ agent, onSelect, onMessage, showProject }: { agent: Agent; onSelect: () => void; onMessage?: () => void; showProject?: boolean }) {
301
- const { isActive, label } = useAgentActivity(agent.id);
302
- const { projects } = useProjects();
303
- const { isDev } = useUIMode();
304
- const project = agent.projectId ? projects.find(p => p.id === agent.projectId) : null;
305
-
306
- return (
307
- <div
308
- onClick={onSelect}
309
- className="px-4 py-3 hover:bg-[var(--color-surface-raised)] cursor-pointer flex items-center justify-between group"
310
- >
311
- <div className="flex items-center gap-3 flex-1 min-w-0">
312
- <span
313
- className={`w-2 h-2 rounded-full flex-shrink-0 ${
314
- agent.status === "running"
315
- ? isActive
316
- ? "bg-green-400 animate-pulse"
317
- : "bg-[#3b82f6]"
318
- : "bg-[var(--color-scrollbar)]"
319
- }`}
320
- />
321
- <div className="flex-1 min-w-0">
322
- <p className="font-medium truncate">{agent.name}</p>
323
- <div className="flex items-center gap-2 text-sm text-[var(--color-text-muted)]">
324
- {isActive && label ? (
325
- <span className="text-green-400 truncate">{label}</span>
326
- ) : (
327
- <span>{isDev ? `${agent.provider} · ` : ""}{agent.status === "running" ? "idle" : "stopped"}</span>
328
- )}
329
- {showProject && project && (
330
- <>
331
- <span className="text-[var(--color-text-faint)]">·</span>
332
- <span className="flex items-center gap-1">
333
- <span className="w-2 h-2 rounded-full" style={{ backgroundColor: project.color }} />
334
- {project.name}
335
- </span>
336
- </>
337
- )}
338
- </div>
339
- </div>
340
- </div>
341
- {onMessage && (
342
- <button
343
- onClick={(e) => { e.stopPropagation(); onMessage(); }}
344
- className="opacity-0 group-hover:opacity-100 transition px-2 py-1 text-xs text-[var(--color-accent)] hover:bg-[var(--color-accent-10)] rounded"
345
- title="Send message"
346
- >
347
- <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
348
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
349
- </svg>
350
- </button>
351
- )}
352
- </div>
353
- );
354
- }
355
-
356
- function timeAgo(timestamp: string): string {
357
- const seconds = Math.floor((Date.now() - new Date(timestamp).getTime()) / 1000);
358
- if (seconds < 5) return "just now";
359
- if (seconds < 60) return `${seconds}s ago`;
360
- const minutes = Math.floor(seconds / 60);
361
- if (minutes < 60) return `${minutes}m ago`;
362
- const hours = Math.floor(minutes / 60);
363
- if (hours < 24) return `${hours}h ago`;
364
- const days = Math.floor(hours / 24);
365
- return `${days}d ago`;
366
- }
367
-
368
- function ActivityItem({ activity, agentName, timestamp }: { activity: string; agentName: string; timestamp: string }) {
369
- return (
370
- <div className="px-4 py-3">
371
- <p className="text-sm truncate">{activity}</p>
372
- <div className="flex items-center gap-2 text-xs text-[var(--color-text-faint)] mt-1">
373
- <span className="text-[var(--color-text-muted)]">{agentName}</span>
374
- <span className="text-[var(--color-text-faint)]">&middot;</span>
375
- <span>{timeAgo(timestamp)}</span>
376
- </div>
377
- </div>
378
- );
379
- }
380
-
381
- function TaskStatusBadge({ status }: { status: Task["status"] }) {
382
- const colors: Record<string, string> = {
383
- pending: "bg-yellow-500/20 text-yellow-400",
384
- running: "bg-blue-500/20 text-blue-400",
385
- completed: "bg-green-500/20 text-green-400",
386
- failed: "bg-red-500/20 text-red-400",
387
- cancelled: "bg-gray-500/20 text-gray-400",
388
- };
389
-
390
- return (
391
- <span className={`px-2 py-0.5 rounded text-xs font-medium ${colors[status] || colors.pending}`}>
392
- {status}
393
- </span>
394
- );
395
- }
396
-
397
- // --- Quick Message Modal ---
398
-
399
- function QuickMessageModal({ agent, onClose }: { agent: Agent; onClose: () => void }) {
400
- const { authFetch } = useAuth();
401
- const [message, setMessage] = useState("");
402
- const [sending, setSending] = useState(false);
403
- const [sent, setSent] = useState(false);
404
- const inputRef = useRef<HTMLInputElement>(null);
405
-
406
- useEffect(() => {
407
- inputRef.current?.focus();
408
- }, []);
409
-
410
- const handleSend = async () => {
411
- if (!message.trim() || sending) return;
412
- setSending(true);
413
- try {
414
- const res = await authFetch(`/api/agents/${agent.id}/chat`, {
415
- method: "POST",
416
- headers: { "Content-Type": "application/json" },
417
- body: JSON.stringify({ message: message.trim(), agent_id: agent.id }),
418
- });
419
- if (res.ok) {
420
- setSent(true);
421
- setTimeout(onClose, 1200);
422
- }
423
- } catch {
424
- // ignore
425
- } finally {
426
- setSending(false);
427
- }
428
- };
429
-
430
- return (
431
- <div className="fixed inset-0 z-50 flex items-center justify-center">
432
- <div className="absolute inset-0 bg-black/60" onClick={onClose} />
433
- <div className="relative bg-[var(--color-surface)] border border-[var(--color-border-light)] rounded-xl shadow-2xl w-full max-w-md mx-4 p-5">
434
- <div className="flex items-center justify-between mb-4">
435
- <div className="flex items-center gap-3">
436
- <span className="w-2.5 h-2.5 rounded-full bg-green-400 animate-pulse" />
437
- <h3 className="font-medium">{agent.name}</h3>
438
- </div>
439
- <button onClick={onClose} className="text-[var(--color-text-muted)] hover:text-[var(--color-text)] transition">
440
- <CloseIcon />
441
- </button>
442
- </div>
443
-
444
- {sent ? (
445
- <div className="py-6 text-center">
446
- <p className="text-green-400 font-medium">Message sent</p>
447
- <p className="text-sm text-[var(--color-text-faint)] mt-1">The agent will process your message</p>
448
- </div>
449
- ) : (
450
- <div className="flex gap-2">
451
- <input
452
- ref={inputRef}
453
- type="text"
454
- value={message}
455
- onChange={e => setMessage(e.target.value)}
456
- onKeyDown={e => e.key === "Enter" && handleSend()}
457
- placeholder={`Message ${agent.name}...`}
458
- disabled={sending}
459
- className="flex-1 bg-[var(--color-bg)] border border-[var(--color-border-light)] btn px-3 py-2.5 text-sm focus:outline-none focus:border-[var(--color-accent)] placeholder-[#444] disabled:opacity-50"
460
- />
461
- <button
462
- onClick={handleSend}
463
- disabled={sending || !message.trim()}
464
- className="px-4 py-2.5 bg-[var(--color-accent)] text-black btn text-sm font-medium hover:bg-[var(--color-accent-hover)] transition disabled:opacity-30"
465
- >
466
- {sending ? "..." : "Send"}
467
- </button>
468
- </div>
469
- )}
470
- </div>
471
- </div>
472
- );
473
- }
474
-
475
- // --- Task sorting helper ---
476
-
477
- function statusPriority(task: Task): number {
478
- if (task.status === "running") return 0;
479
- if (task.status === "pending") return 1;
480
- if (task.status === "completed") return 2;
481
- if (task.status === "failed") return 3;
482
- return 4; // cancelled etc
483
- }
484
-
485
- function sortTasksByNextExecution(tasks: Task[]): Task[] {
486
- return [...tasks].sort((a, b) => {
487
- const aPri = statusPriority(a);
488
- const bPri = statusPriority(b);
489
- if (aPri !== bPri) return aPri - bPri;
490
- // Within running/pending: soonest next execution first
491
- if (aPri <= 1) {
492
- const aTime = a.next_run || a.execute_at || null;
493
- const bTime = b.next_run || b.execute_at || null;
494
- const aTs = aTime ? new Date(aTime).getTime() : Infinity;
495
- const bTs = bTime ? new Date(bTime).getTime() : Infinity;
496
- return aTs - bTs;
497
- }
498
- // Within completed/failed: most recent first
499
- const aDate = a.completed_at || a.executed_at || a.created_at;
500
- const bDate = b.completed_at || b.executed_at || b.created_at;
501
- return new Date(bDate).getTime() - new Date(aDate).getTime();
502
- });
503
- }
504
-
505
- // --- Schedule formatting helpers (compact versions for dashboard) ---
506
-
507
- const DASH_DAY_NAMES = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
508
-
509
- function formatCronShort(cron: string): string {
510
- try {
511
- const parts = cron.trim().split(/\s+/);
512
- if (parts.length !== 5) return cron;
513
- const [minute, hour, dayOfMonth, month, dayOfWeek] = parts;
514
-
515
- if (minute.startsWith("*/") && hour === "*" && dayOfMonth === "*" && month === "*" && dayOfWeek === "*") {
516
- const n = parseInt(minute.slice(2));
517
- return n === 1 ? "Every min" : `Every ${n}min`;
518
- }
519
- if (minute !== "*" && !minute.includes("/") && hour === "*" && dayOfMonth === "*" && month === "*" && dayOfWeek === "*") {
520
- return "Hourly";
521
- }
522
- if (hour.startsWith("*/") && dayOfMonth === "*" && month === "*" && dayOfWeek === "*") {
523
- const n = parseInt(hour.slice(2));
524
- return n === 1 ? "Hourly" : `Every ${n}h`;
525
- }
526
-
527
- const formatTime = (h: string, m: string): string => {
528
- const hr = parseInt(h);
529
- const mn = parseInt(m);
530
- if (isNaN(hr)) return "";
531
- const ampm = hr >= 12 ? "PM" : "AM";
532
- const h12 = hr === 0 ? 12 : hr > 12 ? hr - 12 : hr;
533
- return `${h12}:${mn.toString().padStart(2, "0")} ${ampm}`;
534
- };
535
-
536
- if (hour !== "*" && !hour.includes("/") && dayOfMonth === "*" && month === "*") {
537
- const timeStr = formatTime(hour, minute);
538
- if (dayOfWeek === "*") return `Daily ${timeStr}`;
539
- const days = dayOfWeek.split(",").map(d => DASH_DAY_NAMES[parseInt(d.trim())] || d);
540
- if (days.length === 1) return `${days[0]} ${timeStr}`;
541
- return `${days.join(" & ")} ${timeStr}`;
542
- }
543
- return cron;
544
- } catch {
545
- return cron;
546
- }
547
- }
548
-
549
- function formatRelativeShort(dateStr: string): string {
550
- const date = new Date(dateStr);
551
- const now = new Date();
552
- const diffMs = date.getTime() - now.getTime();
553
- const isFuture = diffMs > 0;
554
- const absDiffMs = Math.abs(diffMs);
555
- const minutes = Math.floor(absDiffMs / 60000);
556
- const hours = Math.floor(absDiffMs / 3600000);
557
-
558
- const timeStr = date.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
559
-
560
- const isToday = date.toDateString() === now.toDateString();
561
- const tomorrow = new Date(now);
562
- tomorrow.setDate(tomorrow.getDate() + 1);
563
- const isTomorrow = date.toDateString() === tomorrow.toDateString();
564
-
565
- if (isToday) {
566
- if (minutes < 1) return "now";
567
- if (minutes < 60) return isFuture ? `in ${minutes}m` : `${minutes}m ago`;
568
- return isFuture ? `in ${hours}h` : `${hours}h ago`;
569
- }
570
- if (isTomorrow) return `Tomorrow ${timeStr}`;
571
- return `${DASH_DAY_NAMES[date.getDay()]} ${timeStr}`;
572
- }
@@ -1 +0,0 @@
1
- export { Dashboard } from "./Dashboard";
@@ -1,21 +0,0 @@
1
- // Common components
2
- export { LoadingSpinner, Modal, Select, CheckIcon, CloseIcon, DashboardIcon, AgentsIcon, SettingsIcon, TasksIcon } from "./common";
3
-
4
- // Layout components
5
- export { Header, Sidebar, ErrorBanner } from "./layout";
6
-
7
- // Auth components
8
- export { LoginPage, CreateAccountStep } from "./auth";
9
-
10
- // Feature components
11
- export { OnboardingWizard } from "./onboarding";
12
- export { SettingsPage } from "./settings";
13
- export { AgentCard, CreateAgentModal, AgentPanel, AgentsView } from "./agents";
14
- export { Dashboard } from "./dashboard";
15
- export { ThreadsPage } from "./threads/ThreadsPage";
16
- export { TasksPage } from "./tasks";
17
- export { McpPage } from "./mcp";
18
- export { SkillsPage } from "./skills/SkillsPage";
19
- export { TestsPage } from "./tests/TestsPage";
20
- export { TelemetryPage } from "./telemetry/TelemetryPage";
21
- export { ConnectionsPage } from "./connections/ConnectionsPage";
@@ -1,18 +0,0 @@
1
- import React from "react";
2
- import { CloseIcon } from "../common/Icons";
3
-
4
- interface ErrorBannerProps {
5
- message: string;
6
- onDismiss: () => void;
7
- }
8
-
9
- export function ErrorBanner({ message, onDismiss }: ErrorBannerProps) {
10
- return (
11
- <div className="bg-red-500/10 border-b border-red-500/30 px-6 py-3 text-red-400 text-sm flex items-center justify-between">
12
- <span>{message}</span>
13
- <button onClick={onDismiss} className="hover:text-red-300">
14
- <CloseIcon className="w-4 h-4" />
15
- </button>
16
- </div>
17
- );
18
- }