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.
- package/dist/App.m4hg4bxq.js +218 -0
- package/dist/index.html +4 -2
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/auth/index.ts +386 -0
- package/src/auth/middleware.ts +183 -0
- package/src/binary.ts +19 -1
- package/src/db.ts +688 -45
- package/src/integrations/composio.ts +437 -0
- package/src/integrations/index.ts +80 -0
- package/src/openapi.ts +1724 -0
- package/src/routes/api.ts +1476 -118
- package/src/routes/auth.ts +242 -0
- package/src/server.ts +121 -11
- package/src/web/App.tsx +64 -19
- package/src/web/components/agents/AgentCard.tsx +24 -22
- package/src/web/components/agents/AgentPanel.tsx +810 -45
- package/src/web/components/agents/AgentsView.tsx +81 -9
- package/src/web/components/agents/CreateAgentModal.tsx +28 -1
- package/src/web/components/api/ApiDocsPage.tsx +583 -0
- package/src/web/components/auth/CreateAccountStep.tsx +176 -0
- package/src/web/components/auth/LoginPage.tsx +91 -0
- package/src/web/components/auth/index.ts +2 -0
- package/src/web/components/common/Icons.tsx +56 -0
- package/src/web/components/common/Modal.tsx +184 -1
- package/src/web/components/dashboard/Dashboard.tsx +70 -22
- package/src/web/components/index.ts +3 -0
- package/src/web/components/layout/Header.tsx +135 -18
- package/src/web/components/layout/Sidebar.tsx +87 -43
- package/src/web/components/mcp/IntegrationsPanel.tsx +743 -0
- package/src/web/components/mcp/McpPage.tsx +451 -63
- package/src/web/components/onboarding/OnboardingWizard.tsx +64 -8
- package/src/web/components/settings/SettingsPage.tsx +340 -26
- package/src/web/components/tasks/TasksPage.tsx +22 -20
- package/src/web/components/telemetry/TelemetryPage.tsx +163 -61
- package/src/web/context/AuthContext.tsx +230 -0
- package/src/web/context/ProjectContext.tsx +182 -0
- package/src/web/context/index.ts +5 -0
- package/src/web/hooks/useAgents.ts +18 -6
- package/src/web/hooks/useOnboarding.ts +20 -4
- package/src/web/hooks/useProviders.ts +15 -5
- package/src/web/icon.png +0 -0
- package/src/web/index.html +1 -1
- package/src/web/styles.css +12 -0
- package/src/web/types.ts +10 -1
- package/dist/App.3kb50qa3.js +0 -213
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { verifyAccessToken, getAuthStatus } from "./index";
|
|
2
|
+
import { UserDB, type User } from "../db";
|
|
3
|
+
|
|
4
|
+
// Extend Request type to include user
|
|
5
|
+
declare global {
|
|
6
|
+
interface Request {
|
|
7
|
+
user?: User;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Helper to create JSON response
|
|
12
|
+
function json(data: unknown, status = 200): Response {
|
|
13
|
+
return new Response(JSON.stringify(data), {
|
|
14
|
+
status,
|
|
15
|
+
headers: { "Content-Type": "application/json" },
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Public paths that don't require authentication
|
|
20
|
+
const PUBLIC_PATHS = [
|
|
21
|
+
"/api/auth/check",
|
|
22
|
+
"/api/auth/login",
|
|
23
|
+
"/api/auth/refresh",
|
|
24
|
+
"/api/health",
|
|
25
|
+
"/api/telemetry", // Agents POST telemetry here
|
|
26
|
+
"/api/telemetry/stream", // SSE doesn't support auth headers
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
// Path prefixes that don't require authentication (for agent communication)
|
|
30
|
+
const PUBLIC_PREFIXES = [
|
|
31
|
+
"/api/agents/", // Agent chat API needs to work without frontend auth
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// Paths that only work when no users exist
|
|
35
|
+
const SETUP_PATHS = [
|
|
36
|
+
"/api/onboarding/user",
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
// Admin-only paths
|
|
40
|
+
const ADMIN_PATHS = [
|
|
41
|
+
"/api/users",
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
export interface AuthContext {
|
|
45
|
+
user: User | null;
|
|
46
|
+
isAuthenticated: boolean;
|
|
47
|
+
isAdmin: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Auth middleware for protecting API routes
|
|
52
|
+
* Returns null if request should proceed, or a Response to return immediately
|
|
53
|
+
*/
|
|
54
|
+
export async function authMiddleware(req: Request, path: string): Promise<{ response?: Response; context: AuthContext }> {
|
|
55
|
+
const context: AuthContext = {
|
|
56
|
+
user: null,
|
|
57
|
+
isAuthenticated: false,
|
|
58
|
+
isAdmin: false,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Check if auth is disabled
|
|
62
|
+
if (process.env.AUTH_ENABLED === "false") {
|
|
63
|
+
return { context };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Public paths - no auth needed
|
|
67
|
+
if (PUBLIC_PATHS.some(p => path === p || path.startsWith(p + "/"))) {
|
|
68
|
+
return { context };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Public prefixes - no auth needed (for agent communication)
|
|
72
|
+
if (PUBLIC_PREFIXES.some(p => path.startsWith(p))) {
|
|
73
|
+
return { context };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Setup paths - only allowed when no users exist
|
|
77
|
+
if (SETUP_PATHS.some(p => path === p || path.startsWith(p + "/"))) {
|
|
78
|
+
const hasUsers = UserDB.hasUsers();
|
|
79
|
+
if (!hasUsers) {
|
|
80
|
+
return { context }; // Allow - no users yet
|
|
81
|
+
}
|
|
82
|
+
// Users exist - require auth for these paths
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Extract token from Authorization header
|
|
86
|
+
const authHeader = req.headers.get("Authorization");
|
|
87
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
88
|
+
return {
|
|
89
|
+
response: json({ error: "Unauthorized", code: "NO_TOKEN" }, 401),
|
|
90
|
+
context,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const token = authHeader.slice(7);
|
|
95
|
+
const payload = verifyAccessToken(token);
|
|
96
|
+
|
|
97
|
+
if (!payload) {
|
|
98
|
+
return {
|
|
99
|
+
response: json({ error: "Invalid or expired token", code: "INVALID_TOKEN" }, 401),
|
|
100
|
+
context,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Get user from database
|
|
105
|
+
const user = UserDB.findById(payload.userId);
|
|
106
|
+
if (!user) {
|
|
107
|
+
return {
|
|
108
|
+
response: json({ error: "User not found", code: "USER_NOT_FOUND" }, 401),
|
|
109
|
+
context,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Update context
|
|
114
|
+
context.user = user;
|
|
115
|
+
context.isAuthenticated = true;
|
|
116
|
+
context.isAdmin = user.role === "admin";
|
|
117
|
+
|
|
118
|
+
// Check admin-only paths
|
|
119
|
+
if (ADMIN_PATHS.some(p => path === p || path.startsWith(p + "/"))) {
|
|
120
|
+
if (user.role !== "admin") {
|
|
121
|
+
return {
|
|
122
|
+
response: json({ error: "Admin access required", code: "FORBIDDEN" }, 403),
|
|
123
|
+
context,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { context };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Helper to check if a path requires authentication
|
|
133
|
+
*/
|
|
134
|
+
export function requiresAuth(path: string): boolean {
|
|
135
|
+
if (process.env.AUTH_ENABLED === "false") {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (PUBLIC_PATHS.some(p => path === p || path.startsWith(p + "/"))) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Helper to extract token from request
|
|
148
|
+
*/
|
|
149
|
+
export function getTokenFromRequest(req: Request): string | null {
|
|
150
|
+
const authHeader = req.headers.get("Authorization");
|
|
151
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
return authHeader.slice(7);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Helper to extract refresh token from cookie
|
|
159
|
+
*/
|
|
160
|
+
export function getRefreshTokenFromCookie(req: Request): string | null {
|
|
161
|
+
const cookie = req.headers.get("Cookie");
|
|
162
|
+
if (!cookie) return null;
|
|
163
|
+
|
|
164
|
+
const match = cookie.match(/(?:^|;\s*)apteva_session=([^;]+)/);
|
|
165
|
+
return match ? match[1] : null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Create Set-Cookie header for refresh token
|
|
170
|
+
* Note: Secure flag only added when HTTPS is explicitly enabled (not just production mode)
|
|
171
|
+
* This allows Docker deployments to work on localhost without HTTPS
|
|
172
|
+
*/
|
|
173
|
+
export function createRefreshTokenCookie(token: string, maxAge: number): string {
|
|
174
|
+
const secure = process.env.HTTPS_ENABLED === "true" ? "; Secure" : "";
|
|
175
|
+
return `apteva_session=${token}; HttpOnly; SameSite=Lax; Path=/; Max-Age=${maxAge}${secure}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Create Set-Cookie header to clear refresh token
|
|
180
|
+
*/
|
|
181
|
+
export function clearRefreshTokenCookie(): string {
|
|
182
|
+
return "apteva_session=; HttpOnly; SameSite=Lax; Path=/; Max-Age=0";
|
|
183
|
+
}
|
package/src/binary.ts
CHANGED
|
@@ -286,6 +286,24 @@ export interface VersionInfo {
|
|
|
286
286
|
export interface AllVersionInfo {
|
|
287
287
|
apteva: VersionInfo;
|
|
288
288
|
agent: VersionInfo;
|
|
289
|
+
isDocker: boolean;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Detect if running in Docker
|
|
293
|
+
export function isRunningInDocker(): boolean {
|
|
294
|
+
// Check for /.dockerenv file (standard Docker indicator)
|
|
295
|
+
if (existsSync("/.dockerenv")) {
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
// Check for DATA_DIR=/data (our Docker convention)
|
|
299
|
+
if (process.env.DATA_DIR === "/data") {
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
// Check for DOCKER env var we could set
|
|
303
|
+
if (process.env.DOCKER === "true") {
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
return false;
|
|
289
307
|
}
|
|
290
308
|
|
|
291
309
|
// Initialize version file path
|
|
@@ -399,7 +417,7 @@ export async function checkForUpdates(): Promise<AllVersionInfo> {
|
|
|
399
417
|
checkForAgentUpdates(),
|
|
400
418
|
]);
|
|
401
419
|
|
|
402
|
-
return { apteva, agent };
|
|
420
|
+
return { apteva, agent, isDocker: isRunningInDocker() };
|
|
403
421
|
}
|
|
404
422
|
|
|
405
423
|
// Compare semver versions: returns positive if a > b, negative if a < b, 0 if equal
|