apteva 0.2.7 → 0.2.9

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 (46) hide show
  1. package/dist/App.m4hg4bxq.js +218 -0
  2. package/dist/index.html +4 -2
  3. package/dist/styles.css +1 -1
  4. package/package.json +1 -1
  5. package/src/auth/index.ts +386 -0
  6. package/src/auth/middleware.ts +183 -0
  7. package/src/binary.ts +19 -1
  8. package/src/db.ts +688 -45
  9. package/src/integrations/composio.ts +437 -0
  10. package/src/integrations/index.ts +80 -0
  11. package/src/openapi.ts +1724 -0
  12. package/src/routes/api.ts +1476 -118
  13. package/src/routes/auth.ts +242 -0
  14. package/src/server.ts +121 -11
  15. package/src/web/App.tsx +64 -19
  16. package/src/web/components/agents/AgentCard.tsx +24 -22
  17. package/src/web/components/agents/AgentPanel.tsx +810 -45
  18. package/src/web/components/agents/AgentsView.tsx +81 -9
  19. package/src/web/components/agents/CreateAgentModal.tsx +28 -1
  20. package/src/web/components/api/ApiDocsPage.tsx +583 -0
  21. package/src/web/components/auth/CreateAccountStep.tsx +176 -0
  22. package/src/web/components/auth/LoginPage.tsx +91 -0
  23. package/src/web/components/auth/index.ts +2 -0
  24. package/src/web/components/common/Icons.tsx +56 -0
  25. package/src/web/components/common/Modal.tsx +184 -1
  26. package/src/web/components/dashboard/Dashboard.tsx +70 -22
  27. package/src/web/components/index.ts +3 -0
  28. package/src/web/components/layout/Header.tsx +135 -18
  29. package/src/web/components/layout/Sidebar.tsx +87 -43
  30. package/src/web/components/mcp/IntegrationsPanel.tsx +743 -0
  31. package/src/web/components/mcp/McpPage.tsx +451 -63
  32. package/src/web/components/onboarding/OnboardingWizard.tsx +64 -8
  33. package/src/web/components/settings/SettingsPage.tsx +340 -26
  34. package/src/web/components/tasks/TasksPage.tsx +22 -20
  35. package/src/web/components/telemetry/TelemetryPage.tsx +163 -61
  36. package/src/web/context/AuthContext.tsx +230 -0
  37. package/src/web/context/ProjectContext.tsx +182 -0
  38. package/src/web/context/index.ts +5 -0
  39. package/src/web/hooks/useAgents.ts +18 -6
  40. package/src/web/hooks/useOnboarding.ts +20 -4
  41. package/src/web/hooks/useProviders.ts +15 -5
  42. package/src/web/icon.png +0 -0
  43. package/src/web/index.html +1 -1
  44. package/src/web/styles.css +12 -0
  45. package/src/web/types.ts +10 -1
  46. package/dist/App.3kb50qa3.js +0 -213
@@ -0,0 +1,182 @@
1
+ import React, { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from "react";
2
+ import { useAuth } from "./AuthContext";
3
+
4
+ export interface Project {
5
+ id: string;
6
+ name: string;
7
+ description: string | null;
8
+ color: string;
9
+ agentCount: number;
10
+ createdAt: string;
11
+ updatedAt: string;
12
+ }
13
+
14
+ interface ProjectContextValue {
15
+ projects: Project[];
16
+ currentProjectId: string | null; // null = "All Projects", "unassigned" = unassigned agents
17
+ currentProject: Project | null;
18
+ isLoading: boolean;
19
+ error: string | null;
20
+ unassignedCount: number;
21
+ setCurrentProjectId: (id: string | null) => void;
22
+ createProject: (data: { name: string; description?: string; color?: string }) => Promise<Project | null>;
23
+ updateProject: (id: string, data: { name?: string; description?: string; color?: string }) => Promise<Project | null>;
24
+ deleteProject: (id: string) => Promise<boolean>;
25
+ refreshProjects: () => Promise<void>;
26
+ }
27
+
28
+ const ProjectContext = createContext<ProjectContextValue | null>(null);
29
+
30
+ export function useProjects(): ProjectContextValue {
31
+ const context = useContext(ProjectContext);
32
+ if (!context) {
33
+ throw new Error("useProjects must be used within a ProjectProvider");
34
+ }
35
+ return context;
36
+ }
37
+
38
+ interface ProjectProviderProps {
39
+ children: ReactNode;
40
+ }
41
+
42
+ const STORAGE_KEY = "apteva_current_project";
43
+
44
+ export function ProjectProvider({ children }: ProjectProviderProps) {
45
+ const { authFetch, isAuthenticated, isLoading: authLoading } = useAuth();
46
+ const [projects, setProjects] = useState<Project[]>([]);
47
+ const [currentProjectId, setCurrentProjectIdState] = useState<string | null>(() => {
48
+ // Load from localStorage
49
+ if (typeof window !== "undefined") {
50
+ return localStorage.getItem(STORAGE_KEY);
51
+ }
52
+ return null;
53
+ });
54
+ const [isLoading, setIsLoading] = useState(true);
55
+ const [error, setError] = useState<string | null>(null);
56
+ const [unassignedCount, setUnassignedCount] = useState(0);
57
+
58
+ const setCurrentProjectId = useCallback((id: string | null) => {
59
+ setCurrentProjectIdState(id);
60
+ if (typeof window !== "undefined") {
61
+ if (id === null) {
62
+ localStorage.removeItem(STORAGE_KEY);
63
+ } else {
64
+ localStorage.setItem(STORAGE_KEY, id);
65
+ }
66
+ }
67
+ }, []);
68
+
69
+ const currentProject = projects.find(p => p.id === currentProjectId) || null;
70
+
71
+ const refreshProjects = useCallback(async () => {
72
+ if (!isAuthenticated && !authLoading) {
73
+ setProjects([]);
74
+ setIsLoading(false);
75
+ return;
76
+ }
77
+
78
+ try {
79
+ setError(null);
80
+ const res = await authFetch("/api/projects");
81
+ if (!res.ok) {
82
+ throw new Error("Failed to fetch projects");
83
+ }
84
+ const data = await res.json();
85
+ setProjects(data.projects || []);
86
+ setUnassignedCount(data.unassignedCount || 0);
87
+
88
+ // If current project no longer exists, reset to all
89
+ if (currentProjectId && currentProjectId !== "unassigned" && !data.projects.find((p: Project) => p.id === currentProjectId)) {
90
+ setCurrentProjectId(null);
91
+ }
92
+ } catch (e) {
93
+ console.error("Failed to fetch projects:", e);
94
+ setError("Failed to load projects");
95
+ } finally {
96
+ setIsLoading(false);
97
+ }
98
+ }, [authFetch, isAuthenticated, authLoading, currentProjectId, setCurrentProjectId]);
99
+
100
+ const createProject = useCallback(async (data: { name: string; description?: string; color?: string }): Promise<Project | null> => {
101
+ try {
102
+ const res = await authFetch("/api/projects", {
103
+ method: "POST",
104
+ headers: { "Content-Type": "application/json" },
105
+ body: JSON.stringify(data),
106
+ });
107
+ if (!res.ok) {
108
+ const err = await res.json();
109
+ throw new Error(err.error || "Failed to create project");
110
+ }
111
+ const result = await res.json();
112
+ await refreshProjects();
113
+ return result.project;
114
+ } catch (e) {
115
+ console.error("Failed to create project:", e);
116
+ return null;
117
+ }
118
+ }, [authFetch, refreshProjects]);
119
+
120
+ const updateProject = useCallback(async (id: string, data: { name?: string; description?: string; color?: string }): Promise<Project | null> => {
121
+ try {
122
+ const res = await authFetch(`/api/projects/${id}`, {
123
+ method: "PUT",
124
+ headers: { "Content-Type": "application/json" },
125
+ body: JSON.stringify(data),
126
+ });
127
+ if (!res.ok) {
128
+ const err = await res.json();
129
+ throw new Error(err.error || "Failed to update project");
130
+ }
131
+ const result = await res.json();
132
+ await refreshProjects();
133
+ return result.project;
134
+ } catch (e) {
135
+ console.error("Failed to update project:", e);
136
+ return null;
137
+ }
138
+ }, [authFetch, refreshProjects]);
139
+
140
+ const deleteProject = useCallback(async (id: string): Promise<boolean> => {
141
+ try {
142
+ const res = await authFetch(`/api/projects/${id}`, {
143
+ method: "DELETE",
144
+ });
145
+ if (!res.ok) {
146
+ const err = await res.json();
147
+ throw new Error(err.error || "Failed to delete project");
148
+ }
149
+ if (currentProjectId === id) {
150
+ setCurrentProjectId(null);
151
+ }
152
+ await refreshProjects();
153
+ return true;
154
+ } catch (e) {
155
+ console.error("Failed to delete project:", e);
156
+ return false;
157
+ }
158
+ }, [authFetch, currentProjectId, setCurrentProjectId, refreshProjects]);
159
+
160
+ // Fetch projects when authenticated
161
+ useEffect(() => {
162
+ if (!authLoading) {
163
+ refreshProjects();
164
+ }
165
+ }, [authLoading, refreshProjects]);
166
+
167
+ const value: ProjectContextValue = {
168
+ projects,
169
+ currentProjectId,
170
+ currentProject,
171
+ isLoading,
172
+ error,
173
+ unassignedCount,
174
+ setCurrentProjectId,
175
+ createProject,
176
+ updateProject,
177
+ deleteProject,
178
+ refreshProjects,
179
+ };
180
+
181
+ return <ProjectContext.Provider value={value}>{children}</ProjectContext.Provider>;
182
+ }
@@ -1,2 +1,7 @@
1
1
  export { TelemetryProvider, useTelemetryContext, useTelemetry, useAgentActivity } from "./TelemetryContext";
2
2
  export type { TelemetryEvent } from "./TelemetryContext";
3
+
4
+ export { AuthProvider, useAuth, useAuthHeaders } from "./AuthContext";
5
+
6
+ export { ProjectProvider, useProjects } from "./ProjectContext";
7
+ export type { Project } from "./ProjectContext";
@@ -1,16 +1,26 @@
1
1
  import { useState, useEffect, useCallback } from "react";
2
2
  import type { Agent, AgentFeatures } from "../types";
3
+ import { useAuth } from "../context";
3
4
 
4
5
  export function useAgents(enabled: boolean) {
6
+ const { accessToken } = useAuth();
5
7
  const [agents, setAgents] = useState<Agent[]>([]);
6
8
  const [loading, setLoading] = useState(true);
7
9
 
10
+ const getHeaders = useCallback((): Record<string, string> => {
11
+ const headers: Record<string, string> = { "Content-Type": "application/json" };
12
+ if (accessToken) {
13
+ headers.Authorization = `Bearer ${accessToken}`;
14
+ }
15
+ return headers;
16
+ }, [accessToken]);
17
+
8
18
  const fetchAgents = useCallback(async () => {
9
- const res = await fetch("/api/agents");
19
+ const res = await fetch("/api/agents", { headers: getHeaders() });
10
20
  const data = await res.json();
11
21
  setAgents(data.agents || []);
12
22
  setLoading(false);
13
- }, []);
23
+ }, [getHeaders]);
14
24
 
15
25
  useEffect(() => {
16
26
  if (enabled) {
@@ -25,17 +35,18 @@ export function useAgents(enabled: boolean) {
25
35
  systemPrompt: string;
26
36
  features: AgentFeatures;
27
37
  mcpServers?: string[];
38
+ projectId?: string | null;
28
39
  }) => {
29
40
  await fetch("/api/agents", {
30
41
  method: "POST",
31
- headers: { "Content-Type": "application/json" },
42
+ headers: getHeaders(),
32
43
  body: JSON.stringify(agent),
33
44
  });
34
45
  await fetchAgents();
35
46
  };
36
47
 
37
48
  const deleteAgent = async (id: string) => {
38
- await fetch(`/api/agents/${id}`, { method: "DELETE" });
49
+ await fetch(`/api/agents/${id}`, { method: "DELETE", headers: getHeaders() });
39
50
  await fetchAgents();
40
51
  };
41
52
 
@@ -46,10 +57,11 @@ export function useAgents(enabled: boolean) {
46
57
  systemPrompt?: string;
47
58
  features?: AgentFeatures;
48
59
  mcpServers?: string[];
60
+ projectId?: string | null;
49
61
  }): Promise<{ error?: string }> => {
50
62
  const res = await fetch(`/api/agents/${id}`, {
51
63
  method: "PUT",
52
- headers: { "Content-Type": "application/json" },
64
+ headers: getHeaders(),
53
65
  body: JSON.stringify(updates),
54
66
  });
55
67
  const data = await res.json();
@@ -62,7 +74,7 @@ export function useAgents(enabled: boolean) {
62
74
 
63
75
  const toggleAgent = async (agent: Agent): Promise<{ error?: string }> => {
64
76
  const action = agent.status === "running" ? "stop" : "start";
65
- const res = await fetch(`/api/agents/${agent.id}/${action}`, { method: "POST" });
77
+ const res = await fetch(`/api/agents/${agent.id}/${action}`, { method: "POST", headers: getHeaders() });
66
78
  const data = await res.json();
67
79
  await fetchAgents();
68
80
  if (!res.ok && data.error) {
@@ -1,21 +1,37 @@
1
1
  import { useState, useEffect, useCallback } from "react";
2
+ import { useAuth } from "../context";
2
3
 
3
4
  export function useOnboarding() {
5
+ const { accessToken, isAuthenticated } = useAuth();
4
6
  const [isComplete, setIsComplete] = useState<boolean | null>(null);
5
7
 
8
+ const getHeaders = useCallback((): Record<string, string> => {
9
+ const headers: Record<string, string> = {};
10
+ if (accessToken) {
11
+ headers.Authorization = `Bearer ${accessToken}`;
12
+ }
13
+ return headers;
14
+ }, [accessToken]);
15
+
6
16
  useEffect(() => {
7
- fetch("/api/onboarding/status")
17
+ // Only check onboarding status when authenticated
18
+ if (!isAuthenticated) {
19
+ setIsComplete(null);
20
+ return;
21
+ }
22
+
23
+ fetch("/api/onboarding/status", { headers: getHeaders() })
8
24
  .then(res => res.json())
9
25
  .then(data => {
10
26
  setIsComplete(data.completed || data.has_any_keys);
11
27
  })
12
28
  .catch(() => setIsComplete(true)); // Fallback to showing app
13
- }, []);
29
+ }, [isAuthenticated, getHeaders]);
14
30
 
15
31
  const complete = useCallback(async () => {
16
- await fetch("/api/onboarding/complete", { method: "POST" });
32
+ await fetch("/api/onboarding/complete", { method: "POST", headers: getHeaders() });
17
33
  setIsComplete(true);
18
- }, []);
34
+ }, [getHeaders]);
19
35
 
20
36
  return {
21
37
  isComplete,
@@ -1,14 +1,24 @@
1
1
  import { useState, useEffect, useCallback } from "react";
2
2
  import type { Provider } from "../types";
3
+ import { useAuth } from "../context";
3
4
 
4
5
  export function useProviders(enabled: boolean) {
6
+ const { accessToken } = useAuth();
5
7
  const [providers, setProviders] = useState<Provider[]>([]);
6
8
 
9
+ const getHeaders = useCallback((): Record<string, string> => {
10
+ const headers: Record<string, string> = { "Content-Type": "application/json" };
11
+ if (accessToken) {
12
+ headers.Authorization = `Bearer ${accessToken}`;
13
+ }
14
+ return headers;
15
+ }, [accessToken]);
16
+
7
17
  const fetchProviders = useCallback(async () => {
8
- const res = await fetch("/api/providers");
18
+ const res = await fetch("/api/providers", { headers: getHeaders() });
9
19
  const data = await res.json();
10
20
  setProviders(data.providers || []);
11
- }, []);
21
+ }, [getHeaders]);
12
22
 
13
23
  useEffect(() => {
14
24
  if (enabled) {
@@ -25,7 +35,7 @@ export function useProviders(enabled: boolean) {
25
35
  // First test the key
26
36
  const testRes = await fetch(`/api/keys/${providerId}/test`, {
27
37
  method: "POST",
28
- headers: { "Content-Type": "application/json" },
38
+ headers: getHeaders(),
29
39
  body: JSON.stringify({ key: apiKey }),
30
40
  });
31
41
  const testData = await testRes.json();
@@ -37,7 +47,7 @@ export function useProviders(enabled: boolean) {
37
47
  // Save the key
38
48
  const saveRes = await fetch(`/api/keys/${providerId}`, {
39
49
  method: "POST",
40
- headers: { "Content-Type": "application/json" },
50
+ headers: getHeaders(),
41
51
  body: JSON.stringify({ key: apiKey }),
42
52
  });
43
53
 
@@ -51,7 +61,7 @@ export function useProviders(enabled: boolean) {
51
61
  };
52
62
 
53
63
  const deleteKey = async (providerId: string) => {
54
- await fetch(`/api/keys/${providerId}`, { method: "DELETE" });
64
+ await fetch(`/api/keys/${providerId}`, { method: "DELETE", headers: getHeaders() });
55
65
  await fetchProviders();
56
66
  };
57
67
 
Binary file
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>apteva</title>
6
+ <title>Apteva</title>
7
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
9
  <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
@@ -39,3 +39,15 @@ html, body {
39
39
  .scrollbar-hide::-webkit-scrollbar {
40
40
  display: none;
41
41
  }
42
+
43
+ /* Telemetry event slide-in animation */
44
+ @keyframes slideIn {
45
+ from {
46
+ opacity: 0;
47
+ transform: translateY(-8px);
48
+ }
49
+ to {
50
+ opacity: 1;
51
+ transform: translateY(0);
52
+ }
53
+ }
package/src/web/types.ts CHANGED
@@ -7,6 +7,8 @@ export interface AgentFeatures {
7
7
  operator: boolean;
8
8
  mcp: boolean;
9
9
  realtime: boolean;
10
+ files: boolean;
11
+ agents: boolean;
10
12
  }
11
13
 
12
14
  export const DEFAULT_FEATURES: AgentFeatures = {
@@ -16,6 +18,8 @@ export const DEFAULT_FEATURES: AgentFeatures = {
16
18
  operator: false,
17
19
  mcp: false,
18
20
  realtime: false,
21
+ files: false,
22
+ agents: false,
19
23
  };
20
24
 
21
25
  export interface McpServerSummary {
@@ -37,6 +41,7 @@ export interface Agent {
37
41
  features: AgentFeatures;
38
42
  mcpServers: string[]; // Array of MCP server IDs
39
43
  mcpServerDetails?: McpServerSummary[]; // Full details included from API
44
+ projectId: string | null; // Optional project grouping
40
45
  createdAt: string;
41
46
  }
42
47
 
@@ -46,8 +51,11 @@ export interface McpServer {
46
51
  type: "npm" | "github" | "http" | "custom";
47
52
  package: string | null;
48
53
  command: string | null;
54
+ url: string | null;
55
+ headers: Record<string, string>;
49
56
  port: number | null;
50
57
  status: "stopped" | "running";
58
+ source: string | null; // "composio", "smithery", or null for local
51
59
  }
52
60
 
53
61
  export interface McpTool {
@@ -91,7 +99,7 @@ export interface OnboardingStatus {
91
99
  has_any_keys: boolean;
92
100
  }
93
101
 
94
- export type Route = "dashboard" | "agents" | "tasks" | "mcp" | "telemetry" | "settings";
102
+ export type Route = "dashboard" | "agents" | "tasks" | "mcp" | "telemetry" | "settings" | "api";
95
103
 
96
104
  export interface Task {
97
105
  id: string;
@@ -134,4 +142,5 @@ export interface NewAgentForm {
134
142
  systemPrompt: string;
135
143
  features: AgentFeatures;
136
144
  mcpServers: string[];
145
+ projectId?: string | null;
137
146
  }