apteva 0.2.3 → 0.2.5

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 (36) hide show
  1. package/dist/App.ggy88vnx.js +213 -0
  2. package/dist/index.html +1 -1
  3. package/dist/styles.css +1 -1
  4. package/package.json +6 -6
  5. package/src/binary.ts +271 -1
  6. package/src/crypto.ts +53 -0
  7. package/src/db.ts +492 -3
  8. package/src/mcp-client.ts +599 -0
  9. package/src/providers.ts +31 -0
  10. package/src/routes/api.ts +786 -63
  11. package/src/server.ts +122 -5
  12. package/src/web/App.tsx +36 -1
  13. package/src/web/components/agents/AgentCard.tsx +22 -1
  14. package/src/web/components/agents/AgentPanel.tsx +381 -0
  15. package/src/web/components/agents/AgentsView.tsx +27 -10
  16. package/src/web/components/agents/CreateAgentModal.tsx +7 -7
  17. package/src/web/components/agents/index.ts +1 -1
  18. package/src/web/components/common/Icons.tsx +8 -0
  19. package/src/web/components/common/Modal.tsx +2 -2
  20. package/src/web/components/common/Select.tsx +1 -1
  21. package/src/web/components/common/index.ts +1 -0
  22. package/src/web/components/dashboard/Dashboard.tsx +74 -25
  23. package/src/web/components/index.ts +5 -2
  24. package/src/web/components/layout/Sidebar.tsx +22 -2
  25. package/src/web/components/mcp/McpPage.tsx +1144 -0
  26. package/src/web/components/mcp/index.ts +1 -0
  27. package/src/web/components/onboarding/OnboardingWizard.tsx +5 -1
  28. package/src/web/components/settings/SettingsPage.tsx +312 -82
  29. package/src/web/components/tasks/TasksPage.tsx +129 -0
  30. package/src/web/components/tasks/index.ts +1 -0
  31. package/src/web/components/telemetry/TelemetryPage.tsx +316 -0
  32. package/src/web/hooks/useAgents.ts +23 -0
  33. package/src/web/styles.css +18 -0
  34. package/src/web/types.ts +75 -1
  35. package/dist/App.wfhmfhx7.js +0 -213
  36. package/src/web/components/agents/ChatPanel.tsx +0 -63
@@ -0,0 +1,316 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import { Select } from "../common/Select";
3
+
4
+ interface TelemetryStats {
5
+ total_events: number;
6
+ total_llm_calls: number;
7
+ total_tool_calls: number;
8
+ total_errors: number;
9
+ total_input_tokens: number;
10
+ total_output_tokens: number;
11
+ }
12
+
13
+ interface TelemetryEvent {
14
+ id: string;
15
+ agent_id: string;
16
+ timestamp: string;
17
+ category: string;
18
+ type: string;
19
+ level: string;
20
+ trace_id: string | null;
21
+ thread_id: string | null;
22
+ data: Record<string, unknown> | null;
23
+ duration_ms: number | null;
24
+ error: string | null;
25
+ }
26
+
27
+ interface UsageByAgent {
28
+ agent_id: string;
29
+ input_tokens: number;
30
+ output_tokens: number;
31
+ llm_calls: number;
32
+ tool_calls: number;
33
+ errors: number;
34
+ }
35
+
36
+ export function TelemetryPage() {
37
+ const [stats, setStats] = useState<TelemetryStats | null>(null);
38
+ const [events, setEvents] = useState<TelemetryEvent[]>([]);
39
+ const [usage, setUsage] = useState<UsageByAgent[]>([]);
40
+ const [loading, setLoading] = useState(true);
41
+ const [filter, setFilter] = useState({
42
+ category: "",
43
+ level: "",
44
+ agent_id: "",
45
+ });
46
+ const [agents, setAgents] = useState<Array<{ id: string; name: string }>>([]);
47
+ const [expandedEvent, setExpandedEvent] = useState<string | null>(null);
48
+
49
+ // Fetch agents for dropdown
50
+ useEffect(() => {
51
+ const fetchAgents = async () => {
52
+ try {
53
+ const res = await fetch("/api/agents");
54
+ const data = await res.json();
55
+ setAgents(data.agents || []);
56
+ } catch (e) {
57
+ console.error("Failed to fetch agents:", e);
58
+ }
59
+ };
60
+ fetchAgents();
61
+ }, []);
62
+
63
+ // Fetch telemetry data
64
+ const fetchData = async () => {
65
+ setLoading(true);
66
+ try {
67
+ // Fetch stats
68
+ const statsRes = await fetch("/api/telemetry/stats");
69
+ const statsData = await statsRes.json();
70
+ setStats(statsData.stats);
71
+
72
+ // Fetch events with filters
73
+ const params = new URLSearchParams();
74
+ if (filter.category) params.set("category", filter.category);
75
+ if (filter.level) params.set("level", filter.level);
76
+ if (filter.agent_id) params.set("agent_id", filter.agent_id);
77
+ params.set("limit", "50");
78
+
79
+ const eventsRes = await fetch(`/api/telemetry/events?${params}`);
80
+ const eventsData = await eventsRes.json();
81
+ setEvents(eventsData.events || []);
82
+
83
+ // Fetch usage by agent
84
+ const usageRes = await fetch("/api/telemetry/usage?group_by=agent");
85
+ const usageData = await usageRes.json();
86
+ setUsage(usageData.usage || []);
87
+ } catch (e) {
88
+ console.error("Failed to fetch telemetry:", e);
89
+ }
90
+ setLoading(false);
91
+ };
92
+
93
+ useEffect(() => {
94
+ fetchData();
95
+ // Auto-refresh every 30 seconds
96
+ const interval = setInterval(fetchData, 30000);
97
+ return () => clearInterval(interval);
98
+ }, [filter]);
99
+
100
+ const getAgentName = (agentId: string) => {
101
+ const agent = agents.find(a => a.id === agentId);
102
+ return agent?.name || agentId;
103
+ };
104
+
105
+ const formatNumber = (n: number) => {
106
+ if (n >= 1000000) return (n / 1000000).toFixed(1) + "M";
107
+ if (n >= 1000) return (n / 1000).toFixed(1) + "K";
108
+ return n.toString();
109
+ };
110
+
111
+ const levelColors: Record<string, string> = {
112
+ debug: "text-[#555]",
113
+ info: "text-blue-400",
114
+ warn: "text-yellow-400",
115
+ error: "text-red-400",
116
+ };
117
+
118
+ const categoryColors: Record<string, string> = {
119
+ LLM: "bg-purple-500/20 text-purple-400 border-purple-500/30",
120
+ TOOL: "bg-blue-500/20 text-blue-400 border-blue-500/30",
121
+ CHAT: "bg-green-500/20 text-green-400 border-green-500/30",
122
+ ERROR: "bg-red-500/20 text-red-400 border-red-500/30",
123
+ SYSTEM: "bg-gray-500/20 text-gray-400 border-gray-500/30",
124
+ TASK: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30",
125
+ MEMORY: "bg-cyan-500/20 text-cyan-400 border-cyan-500/30",
126
+ MCP: "bg-orange-500/20 text-orange-400 border-orange-500/30",
127
+ };
128
+
129
+ const agentOptions = [
130
+ { value: "", label: "All Agents" },
131
+ ...agents.map(a => ({ value: a.id, label: a.name })),
132
+ ];
133
+
134
+ const categoryOptions = [
135
+ { value: "", label: "All Categories" },
136
+ { value: "LLM", label: "LLM" },
137
+ { value: "TOOL", label: "Tool" },
138
+ { value: "CHAT", label: "Chat" },
139
+ { value: "TASK", label: "Task" },
140
+ { value: "MEMORY", label: "Memory" },
141
+ { value: "MCP", label: "MCP" },
142
+ { value: "SYSTEM", label: "System" },
143
+ { value: "ERROR", label: "Error" },
144
+ ];
145
+
146
+ const levelOptions = [
147
+ { value: "", label: "All Levels" },
148
+ { value: "debug", label: "Debug" },
149
+ { value: "info", label: "Info" },
150
+ { value: "warn", label: "Warn" },
151
+ { value: "error", label: "Error" },
152
+ ];
153
+
154
+ return (
155
+ <div className="flex-1 overflow-auto p-6">
156
+ <div className="max-w-6xl">
157
+ {/* Header */}
158
+ <div className="mb-6">
159
+ <h1 className="text-2xl font-semibold mb-1">Telemetry</h1>
160
+ <p className="text-[#666]">
161
+ Monitor agent activity, token usage, and errors.
162
+ </p>
163
+ </div>
164
+
165
+ {/* Stats Cards */}
166
+ {stats && (
167
+ <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4 mb-6">
168
+ <StatCard label="Events" value={formatNumber(stats.total_events)} />
169
+ <StatCard label="LLM Calls" value={formatNumber(stats.total_llm_calls)} />
170
+ <StatCard label="Tool Calls" value={formatNumber(stats.total_tool_calls)} />
171
+ <StatCard label="Errors" value={formatNumber(stats.total_errors)} color="red" />
172
+ <StatCard label="Input Tokens" value={formatNumber(stats.total_input_tokens)} />
173
+ <StatCard label="Output Tokens" value={formatNumber(stats.total_output_tokens)} />
174
+ </div>
175
+ )}
176
+
177
+ {/* Usage by Agent */}
178
+ {usage.length > 0 && (
179
+ <div className="mb-6">
180
+ <h2 className="text-lg font-medium mb-3">Usage by Agent</h2>
181
+ <div className="bg-[#111] border border-[#1a1a1a] rounded-lg overflow-hidden">
182
+ <table className="w-full text-sm">
183
+ <thead>
184
+ <tr className="border-b border-[#1a1a1a] text-[#666]">
185
+ <th className="text-left p-3">Agent</th>
186
+ <th className="text-right p-3">LLM Calls</th>
187
+ <th className="text-right p-3">Tool Calls</th>
188
+ <th className="text-right p-3">Input Tokens</th>
189
+ <th className="text-right p-3">Output Tokens</th>
190
+ <th className="text-right p-3">Errors</th>
191
+ </tr>
192
+ </thead>
193
+ <tbody>
194
+ {usage.map((u) => (
195
+ <tr key={u.agent_id} className="border-b border-[#1a1a1a] last:border-0">
196
+ <td className="p-3 font-medium">{getAgentName(u.agent_id)}</td>
197
+ <td className="p-3 text-right text-[#888]">{formatNumber(u.llm_calls)}</td>
198
+ <td className="p-3 text-right text-[#888]">{formatNumber(u.tool_calls)}</td>
199
+ <td className="p-3 text-right text-[#888]">{formatNumber(u.input_tokens)}</td>
200
+ <td className="p-3 text-right text-[#888]">{formatNumber(u.output_tokens)}</td>
201
+ <td className="p-3 text-right">
202
+ {u.errors > 0 ? (
203
+ <span className="text-red-400">{u.errors}</span>
204
+ ) : (
205
+ <span className="text-[#444]">0</span>
206
+ )}
207
+ </td>
208
+ </tr>
209
+ ))}
210
+ </tbody>
211
+ </table>
212
+ </div>
213
+ </div>
214
+ )}
215
+
216
+ {/* Filters */}
217
+ <div className="flex items-center gap-3 mb-4">
218
+ <div className="w-56">
219
+ <Select
220
+ value={filter.agent_id}
221
+ options={agentOptions}
222
+ onChange={(value) => setFilter({ ...filter, agent_id: value })}
223
+ placeholder="All Agents"
224
+ />
225
+ </div>
226
+ <div className="w-48">
227
+ <Select
228
+ value={filter.category}
229
+ options={categoryOptions}
230
+ onChange={(value) => setFilter({ ...filter, category: value })}
231
+ placeholder="All Categories"
232
+ />
233
+ </div>
234
+ <div className="w-40">
235
+ <Select
236
+ value={filter.level}
237
+ options={levelOptions}
238
+ onChange={(value) => setFilter({ ...filter, level: value })}
239
+ placeholder="All Levels"
240
+ />
241
+ </div>
242
+ <button
243
+ onClick={fetchData}
244
+ className="px-3 py-2 bg-[#1a1a1a] hover:bg-[#222] border border-[#333] rounded text-sm transition"
245
+ >
246
+ Refresh
247
+ </button>
248
+ </div>
249
+
250
+ {/* Events List */}
251
+ <div className="bg-[#111] border border-[#1a1a1a] rounded-lg">
252
+ <div className="p-3 border-b border-[#1a1a1a]">
253
+ <h2 className="font-medium">Recent Events</h2>
254
+ </div>
255
+
256
+ {loading && events.length === 0 ? (
257
+ <div className="p-8 text-center text-[#666]">Loading...</div>
258
+ ) : events.length === 0 ? (
259
+ <div className="p-8 text-center text-[#666]">
260
+ No telemetry events yet. Events will appear here once agents start sending data.
261
+ </div>
262
+ ) : (
263
+ <div className="divide-y divide-[#1a1a1a]">
264
+ {events.map((event) => (
265
+ <div
266
+ key={event.id}
267
+ className="p-3 hover:bg-[#0a0a0a] transition cursor-pointer"
268
+ onClick={() => setExpandedEvent(expandedEvent === event.id ? null : event.id)}
269
+ >
270
+ <div className="flex items-start gap-3">
271
+ <span className={`px-2 py-0.5 rounded text-xs border ${categoryColors[event.category] || "bg-[#222] text-[#888] border-[#333]"}`}>
272
+ {event.category}
273
+ </span>
274
+ <div className="flex-1 min-w-0">
275
+ <div className="flex items-center gap-2">
276
+ <span className="font-medium text-sm">{event.type}</span>
277
+ <span className={`text-xs ${levelColors[event.level] || "text-[#666]"}`}>
278
+ {event.level}
279
+ </span>
280
+ {event.duration_ms && (
281
+ <span className="text-xs text-[#555]">{event.duration_ms}ms</span>
282
+ )}
283
+ </div>
284
+ <div className="text-xs text-[#555] mt-1">
285
+ {getAgentName(event.agent_id)} · {new Date(event.timestamp).toLocaleString()}
286
+ </div>
287
+ {event.error && (
288
+ <div className="text-xs text-red-400 mt-1 font-mono">{event.error}</div>
289
+ )}
290
+ {expandedEvent === event.id && event.data && Object.keys(event.data).length > 0 && (
291
+ <pre className="text-xs text-[#666] mt-2 p-2 bg-[#0a0a0a] rounded overflow-x-auto">
292
+ {JSON.stringify(event.data, null, 2)}
293
+ </pre>
294
+ )}
295
+ </div>
296
+ </div>
297
+ </div>
298
+ ))}
299
+ </div>
300
+ )}
301
+ </div>
302
+ </div>
303
+ </div>
304
+ );
305
+ }
306
+
307
+ function StatCard({ label, value, color }: { label: string; value: string; color?: string }) {
308
+ return (
309
+ <div className="bg-[#111] border border-[#1a1a1a] rounded-lg p-4">
310
+ <div className="text-[#666] text-xs mb-1">{label}</div>
311
+ <div className={`text-2xl font-semibold ${color === "red" ? "text-red-400" : ""}`}>
312
+ {value}
313
+ </div>
314
+ </div>
315
+ );
316
+ }
@@ -24,6 +24,7 @@ export function useAgents(enabled: boolean) {
24
24
  provider: string;
25
25
  systemPrompt: string;
26
26
  features: AgentFeatures;
27
+ mcpServers?: string[];
27
28
  }) => {
28
29
  await fetch("/api/agents", {
29
30
  method: "POST",
@@ -38,6 +39,27 @@ export function useAgents(enabled: boolean) {
38
39
  await fetchAgents();
39
40
  };
40
41
 
42
+ const updateAgent = async (id: string, updates: {
43
+ name?: string;
44
+ model?: string;
45
+ provider?: string;
46
+ systemPrompt?: string;
47
+ features?: AgentFeatures;
48
+ mcpServers?: string[];
49
+ }): Promise<{ error?: string }> => {
50
+ const res = await fetch(`/api/agents/${id}`, {
51
+ method: "PUT",
52
+ headers: { "Content-Type": "application/json" },
53
+ body: JSON.stringify(updates),
54
+ });
55
+ const data = await res.json();
56
+ await fetchAgents();
57
+ if (!res.ok && data.error) {
58
+ return { error: data.error };
59
+ }
60
+ return {};
61
+ };
62
+
41
63
  const toggleAgent = async (agent: Agent): Promise<{ error?: string }> => {
42
64
  const action = agent.status === "running" ? "stop" : "start";
43
65
  const res = await fetch(`/api/agents/${agent.id}/${action}`, { method: "POST" });
@@ -57,6 +79,7 @@ export function useAgents(enabled: boolean) {
57
79
  runningCount,
58
80
  fetchAgents,
59
81
  createAgent,
82
+ updateAgent,
60
83
  deleteAgent,
61
84
  toggleAgent,
62
85
  };
@@ -10,6 +10,15 @@ html, body {
10
10
  -moz-osx-font-smoothing: grayscale;
11
11
  }
12
12
 
13
+ /* Hide scrollbars globally but allow scrolling */
14
+ * {
15
+ -ms-overflow-style: none;
16
+ scrollbar-width: none;
17
+ }
18
+ *::-webkit-scrollbar {
19
+ display: none;
20
+ }
21
+
13
22
  ::selection {
14
23
  background-color: #f97316;
15
24
  color: #0a0a0a;
@@ -21,3 +30,12 @@ html, body {
21
30
  -webkit-box-orient: vertical;
22
31
  overflow: hidden;
23
32
  }
33
+
34
+ /* Hide scrollbar but allow scrolling */
35
+ .scrollbar-hide {
36
+ -ms-overflow-style: none;
37
+ scrollbar-width: none;
38
+ }
39
+ .scrollbar-hide::-webkit-scrollbar {
40
+ display: none;
41
+ }
package/src/web/types.ts CHANGED
@@ -18,6 +18,14 @@ export const DEFAULT_FEATURES: AgentFeatures = {
18
18
  realtime: false,
19
19
  };
20
20
 
21
+ export interface McpServerSummary {
22
+ id: string;
23
+ name: string;
24
+ type: string;
25
+ status: "stopped" | "running";
26
+ port: number | null;
27
+ }
28
+
21
29
  export interface Agent {
22
30
  id: string;
23
31
  name: string;
@@ -27,9 +35,37 @@ export interface Agent {
27
35
  status: "stopped" | "running";
28
36
  port?: number;
29
37
  features: AgentFeatures;
38
+ mcpServers: string[]; // Array of MCP server IDs
39
+ mcpServerDetails?: McpServerSummary[]; // Full details included from API
30
40
  createdAt: string;
31
41
  }
32
42
 
43
+ export interface McpServer {
44
+ id: string;
45
+ name: string;
46
+ type: "npm" | "github" | "http" | "custom";
47
+ package: string | null;
48
+ command: string | null;
49
+ port: number | null;
50
+ status: "stopped" | "running";
51
+ }
52
+
53
+ export interface McpTool {
54
+ name: string;
55
+ description?: string;
56
+ inputSchema: Record<string, unknown>;
57
+ }
58
+
59
+ export interface McpToolCallResult {
60
+ content: Array<{
61
+ type: "text" | "image" | "resource";
62
+ text?: string;
63
+ data?: string;
64
+ mimeType?: string;
65
+ }>;
66
+ isError?: boolean;
67
+ }
68
+
33
69
  export interface ProviderModel {
34
70
  value: string;
35
71
  label: string;
@@ -39,11 +75,14 @@ export interface ProviderModel {
39
75
  export interface Provider {
40
76
  id: string;
41
77
  name: string;
78
+ type: "llm" | "integration";
42
79
  docsUrl: string;
80
+ description?: string;
43
81
  models: ProviderModel[];
44
82
  hasKey: boolean;
45
83
  keyHint: string | null;
46
84
  isValid: boolean | null;
85
+ configured?: boolean; // for backwards compatibility
47
86
  }
48
87
 
49
88
  export interface OnboardingStatus {
@@ -52,7 +91,41 @@ export interface OnboardingStatus {
52
91
  has_any_keys: boolean;
53
92
  }
54
93
 
55
- export type Route = "dashboard" | "agents" | "settings";
94
+ export type Route = "dashboard" | "agents" | "tasks" | "mcp" | "telemetry" | "settings";
95
+
96
+ export interface Task {
97
+ id: string;
98
+ title: string;
99
+ description?: string;
100
+ type: "once" | "recurring";
101
+ status: "pending" | "running" | "completed" | "failed" | "cancelled";
102
+ priority: number;
103
+ source: "local" | "delegated";
104
+ created_at: string;
105
+ execute_at?: string;
106
+ executed_at?: string;
107
+ recurrence?: string;
108
+ next_run?: string;
109
+ result?: any;
110
+ agentId: string;
111
+ agentName: string;
112
+ }
113
+
114
+ export interface DashboardStats {
115
+ agents: {
116
+ total: number;
117
+ running: number;
118
+ };
119
+ tasks: {
120
+ total: number;
121
+ pending: number;
122
+ running: number;
123
+ completed: number;
124
+ };
125
+ providers: {
126
+ configured: number;
127
+ };
128
+ }
56
129
 
57
130
  export interface NewAgentForm {
58
131
  name: string;
@@ -60,4 +133,5 @@ export interface NewAgentForm {
60
133
  provider: string;
61
134
  systemPrompt: string;
62
135
  features: AgentFeatures;
136
+ mcpServers: string[];
63
137
  }