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,583 @@
1
+ import { useState, useEffect } from "react";
2
+ import { useAuth } from "../../context";
3
+
4
+ interface OpenApiPath {
5
+ [method: string]: {
6
+ tags?: string[];
7
+ summary?: string;
8
+ description?: string;
9
+ parameters?: Array<{
10
+ name: string;
11
+ in: string;
12
+ required?: boolean;
13
+ schema?: { type: string };
14
+ description?: string;
15
+ }>;
16
+ requestBody?: {
17
+ required?: boolean;
18
+ content?: {
19
+ [mediaType: string]: {
20
+ schema?: any;
21
+ };
22
+ };
23
+ };
24
+ responses?: {
25
+ [code: string]: {
26
+ description?: string;
27
+ content?: any;
28
+ };
29
+ };
30
+ };
31
+ }
32
+
33
+ interface OpenApiSpec {
34
+ info: {
35
+ title: string;
36
+ description: string;
37
+ version: string;
38
+ };
39
+ tags?: Array<{ name: string; description: string }>;
40
+ paths: { [path: string]: OpenApiPath };
41
+ components?: {
42
+ schemas?: { [name: string]: any };
43
+ };
44
+ }
45
+
46
+ const METHOD_COLORS: Record<string, string> = {
47
+ get: "#61affe",
48
+ post: "#49cc90",
49
+ put: "#fca130",
50
+ delete: "#f93e3e",
51
+ patch: "#50e3c2",
52
+ };
53
+
54
+ export function ApiDocsPage() {
55
+ const { authFetch } = useAuth();
56
+ const [spec, setSpec] = useState<OpenApiSpec | null>(null);
57
+ const [loading, setLoading] = useState(true);
58
+ const [expandedPaths, setExpandedPaths] = useState<Set<string>>(new Set());
59
+ const [selectedTag, setSelectedTag] = useState<string | null>(null);
60
+ const [copied, setCopied] = useState(false);
61
+
62
+ useEffect(() => {
63
+ loadSpec();
64
+ }, []);
65
+
66
+ async function copyToClipboard() {
67
+ if (!spec) return;
68
+ try {
69
+ await navigator.clipboard.writeText(JSON.stringify(spec, null, 2));
70
+ setCopied(true);
71
+ setTimeout(() => setCopied(false), 2000);
72
+ } catch (err) {
73
+ console.error("Failed to copy:", err);
74
+ }
75
+ }
76
+
77
+ function downloadJson() {
78
+ if (!spec) return;
79
+ const blob = new Blob([JSON.stringify(spec, null, 2)], { type: "application/json" });
80
+ const url = URL.createObjectURL(blob);
81
+ const a = document.createElement("a");
82
+ a.href = url;
83
+ a.download = "apteva-openapi.json";
84
+ a.click();
85
+ URL.revokeObjectURL(url);
86
+ }
87
+
88
+ async function loadSpec() {
89
+ try {
90
+ const res = await authFetch("/api/openapi");
91
+ if (res.ok) {
92
+ const data = await res.json();
93
+ setSpec(data);
94
+ }
95
+ } catch (err) {
96
+ console.error("Failed to load OpenAPI spec:", err);
97
+ } finally {
98
+ setLoading(false);
99
+ }
100
+ }
101
+
102
+ function togglePath(pathKey: string) {
103
+ setExpandedPaths((prev) => {
104
+ const next = new Set(prev);
105
+ if (next.has(pathKey)) {
106
+ next.delete(pathKey);
107
+ } else {
108
+ next.add(pathKey);
109
+ }
110
+ return next;
111
+ });
112
+ }
113
+
114
+ function getSchemaPreview(schema: any, depth = 0): string {
115
+ if (!schema) return "{}";
116
+ if (depth > 2) return "...";
117
+
118
+ if (schema.$ref) {
119
+ const refName = schema.$ref.split("/").pop();
120
+ return refName || "Object";
121
+ }
122
+
123
+ if (schema.type === "array") {
124
+ const itemType = getSchemaPreview(schema.items, depth + 1);
125
+ return `${itemType}[]`;
126
+ }
127
+
128
+ if (schema.type === "object" && schema.properties) {
129
+ const props = Object.entries(schema.properties)
130
+ .slice(0, 3)
131
+ .map(([k, v]: [string, any]) => `${k}: ${v.type || "any"}`)
132
+ .join(", ");
133
+ const more = Object.keys(schema.properties).length > 3 ? ", ..." : "";
134
+ return `{ ${props}${more} }`;
135
+ }
136
+
137
+ return schema.type || "any";
138
+ }
139
+
140
+ if (loading) {
141
+ return (
142
+ <div style={{ padding: 24 }}>
143
+ <p style={{ color: "#888" }}>Loading API documentation...</p>
144
+ </div>
145
+ );
146
+ }
147
+
148
+ if (!spec) {
149
+ return (
150
+ <div style={{ padding: 24 }}>
151
+ <p style={{ color: "#f66" }}>Failed to load API documentation</p>
152
+ </div>
153
+ );
154
+ }
155
+
156
+ const tags = spec.tags || [];
157
+ const paths = Object.entries(spec.paths);
158
+
159
+ // Extract schema names referenced by a method
160
+ function getReferencedSchemas(method: any): Set<string> {
161
+ const refs = new Set<string>();
162
+
163
+ function extractRefs(obj: any) {
164
+ if (!obj) return;
165
+ if (typeof obj === "object") {
166
+ if (obj.$ref) {
167
+ const name = obj.$ref.split("/").pop();
168
+ if (name) refs.add(name);
169
+ }
170
+ for (const value of Object.values(obj)) {
171
+ extractRefs(value);
172
+ }
173
+ }
174
+ }
175
+
176
+ extractRefs(method.requestBody);
177
+ extractRefs(method.responses);
178
+ return refs;
179
+ }
180
+
181
+ // Get all schemas referenced by filtered endpoints
182
+ function getFilteredSchemas(): string[] {
183
+ if (!selectedTag || !spec.components?.schemas) {
184
+ return Object.keys(spec.components?.schemas || {});
185
+ }
186
+
187
+ const usedSchemas = new Set<string>();
188
+
189
+ for (const [_, methods] of filteredPaths) {
190
+ for (const [method, details] of Object.entries(methods)) {
191
+ if (!["get", "post", "put", "delete", "patch"].includes(method)) continue;
192
+ if (details.tags?.includes(selectedTag)) {
193
+ const refs = getReferencedSchemas(details);
194
+ refs.forEach(r => usedSchemas.add(r));
195
+ }
196
+ }
197
+ }
198
+
199
+ return Array.from(usedSchemas);
200
+ }
201
+
202
+ // Filter paths by selected tag
203
+ const filteredPaths = selectedTag
204
+ ? paths.filter(([_, methods]) =>
205
+ Object.values(methods).some((m) => m.tags?.includes(selectedTag))
206
+ )
207
+ : paths;
208
+
209
+ return (
210
+ <div style={{ padding: 24, maxWidth: 1000, height: "100%", overflowY: "auto" }}>
211
+ <div style={{ marginBottom: 24 }}>
212
+ <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 8 }}>
213
+ <h1 style={{ fontSize: 24, fontWeight: 600 }}>
214
+ {spec.info.title}
215
+ </h1>
216
+ <div style={{ display: "flex", gap: 8 }}>
217
+ <button
218
+ onClick={copyToClipboard}
219
+ style={{
220
+ padding: "8px 16px",
221
+ borderRadius: 4,
222
+ border: "1px solid #333",
223
+ background: copied ? "#49cc90" : "#1a1a2e",
224
+ color: copied ? "#000" : "#fff",
225
+ cursor: "pointer",
226
+ fontSize: 12,
227
+ fontFamily: "inherit",
228
+ }}
229
+ >
230
+ {copied ? "Copied!" : "Copy JSON"}
231
+ </button>
232
+ <button
233
+ onClick={downloadJson}
234
+ style={{
235
+ padding: "8px 16px",
236
+ borderRadius: 4,
237
+ border: "1px solid #333",
238
+ background: "#1a1a2e",
239
+ color: "#fff",
240
+ cursor: "pointer",
241
+ fontSize: 12,
242
+ fontFamily: "inherit",
243
+ }}
244
+ >
245
+ Download
246
+ </button>
247
+ </div>
248
+ </div>
249
+ <p style={{ color: "#888", marginBottom: 8 }}>
250
+ {spec.info.description.split("\n")[0]}
251
+ </p>
252
+ <p style={{ color: "#666", fontSize: 12 }}>Version: {spec.info.version}</p>
253
+ </div>
254
+
255
+ {/* Base URL */}
256
+ <div
257
+ style={{
258
+ background: "#1a1a2e",
259
+ padding: 12,
260
+ borderRadius: 6,
261
+ marginBottom: 24,
262
+ fontFamily: "monospace",
263
+ }}
264
+ >
265
+ <span style={{ color: "#888" }}>Base URL: </span>
266
+ <span style={{ color: "#61affe" }}>
267
+ {window.location.origin}/api
268
+ </span>
269
+ </div>
270
+
271
+ {/* Tag filters */}
272
+ <div style={{ marginBottom: 24, display: "flex", flexWrap: "wrap", gap: 8 }}>
273
+ <button
274
+ onClick={() => setSelectedTag(null)}
275
+ style={{
276
+ padding: "6px 12px",
277
+ borderRadius: 4,
278
+ border: "1px solid #333",
279
+ background: selectedTag === null ? "#333" : "transparent",
280
+ color: selectedTag === null ? "#fff" : "#888",
281
+ cursor: "pointer",
282
+ fontSize: 12,
283
+ }}
284
+ >
285
+ All
286
+ </button>
287
+ {tags.map((tag) => (
288
+ <button
289
+ key={tag.name}
290
+ onClick={() => setSelectedTag(tag.name)}
291
+ style={{
292
+ padding: "6px 12px",
293
+ borderRadius: 4,
294
+ border: "1px solid #333",
295
+ background: selectedTag === tag.name ? "#333" : "transparent",
296
+ color: selectedTag === tag.name ? "#fff" : "#888",
297
+ cursor: "pointer",
298
+ fontSize: 12,
299
+ }}
300
+ title={tag.description}
301
+ >
302
+ {tag.name}
303
+ </button>
304
+ ))}
305
+ </div>
306
+
307
+ {/* Endpoints */}
308
+ <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
309
+ {filteredPaths.map(([path, methods]) =>
310
+ Object.entries(methods)
311
+ .filter(([method]) => ["get", "post", "put", "delete", "patch"].includes(method))
312
+ .map(([method, details]) => {
313
+ const pathKey = `${method}:${path}`;
314
+ const isExpanded = expandedPaths.has(pathKey);
315
+ const methodUpper = method.toUpperCase();
316
+ const color = METHOD_COLORS[method] || "#888";
317
+
318
+ return (
319
+ <div
320
+ key={pathKey}
321
+ style={{
322
+ border: "1px solid #333",
323
+ borderRadius: 6,
324
+ overflow: "hidden",
325
+ }}
326
+ >
327
+ {/* Header */}
328
+ <div
329
+ onClick={() => togglePath(pathKey)}
330
+ style={{
331
+ display: "flex",
332
+ alignItems: "center",
333
+ gap: 12,
334
+ padding: "12px 16px",
335
+ background: isExpanded ? "#1a1a2e" : "transparent",
336
+ cursor: "pointer",
337
+ }}
338
+ >
339
+ <span
340
+ style={{
341
+ background: color,
342
+ color: "#000",
343
+ padding: "4px 8px",
344
+ borderRadius: 4,
345
+ fontSize: 11,
346
+ fontWeight: 600,
347
+ minWidth: 60,
348
+ textAlign: "center",
349
+ }}
350
+ >
351
+ {methodUpper}
352
+ </span>
353
+ <span style={{ fontFamily: "monospace", color: "#fff" }}>
354
+ {path}
355
+ </span>
356
+ <span style={{ color: "#888", flex: 1, fontSize: 13 }}>
357
+ {details.summary}
358
+ </span>
359
+ <span style={{ color: "#666", fontSize: 12 }}>
360
+ {isExpanded ? "[-]" : "[+]"}
361
+ </span>
362
+ </div>
363
+
364
+ {/* Expanded details */}
365
+ {isExpanded && (
366
+ <div
367
+ style={{
368
+ padding: 16,
369
+ background: "#0d0d1a",
370
+ borderTop: "1px solid #333",
371
+ }}
372
+ >
373
+ {details.description && (
374
+ <p style={{ color: "#888", marginBottom: 16, fontSize: 13 }}>
375
+ {details.description}
376
+ </p>
377
+ )}
378
+
379
+ {/* Parameters */}
380
+ {details.parameters && details.parameters.length > 0 && (
381
+ <div style={{ marginBottom: 16 }}>
382
+ <h4 style={{ fontSize: 13, color: "#888", marginBottom: 8 }}>
383
+ Parameters
384
+ </h4>
385
+ <div
386
+ style={{
387
+ background: "#1a1a2e",
388
+ borderRadius: 4,
389
+ padding: 12,
390
+ }}
391
+ >
392
+ {details.parameters.map((param) => (
393
+ <div
394
+ key={param.name}
395
+ style={{
396
+ display: "flex",
397
+ gap: 12,
398
+ marginBottom: 8,
399
+ fontSize: 12,
400
+ }}
401
+ >
402
+ <span style={{ color: "#61affe", minWidth: 100 }}>
403
+ {param.name}
404
+ {param.required && (
405
+ <span style={{ color: "#f66" }}>*</span>
406
+ )}
407
+ </span>
408
+ <span style={{ color: "#666" }}>({param.in})</span>
409
+ <span style={{ color: "#888" }}>
410
+ {param.schema?.type || "string"}
411
+ </span>
412
+ {param.description && (
413
+ <span style={{ color: "#666" }}>
414
+ - {param.description}
415
+ </span>
416
+ )}
417
+ </div>
418
+ ))}
419
+ </div>
420
+ </div>
421
+ )}
422
+
423
+ {/* Request Body */}
424
+ {details.requestBody && (
425
+ <div style={{ marginBottom: 16 }}>
426
+ <h4 style={{ fontSize: 13, color: "#888", marginBottom: 8 }}>
427
+ Request Body
428
+ {details.requestBody.required && (
429
+ <span style={{ color: "#f66" }}> (required)</span>
430
+ )}
431
+ </h4>
432
+ <div
433
+ style={{
434
+ background: "#1a1a2e",
435
+ borderRadius: 4,
436
+ padding: 12,
437
+ fontFamily: "monospace",
438
+ fontSize: 12,
439
+ color: "#49cc90",
440
+ }}
441
+ >
442
+ {Object.entries(details.requestBody.content || {}).map(
443
+ ([mediaType, content]) => (
444
+ <div key={mediaType}>
445
+ <span style={{ color: "#666" }}>{mediaType}: </span>
446
+ {getSchemaPreview(content.schema)}
447
+ </div>
448
+ )
449
+ )}
450
+ </div>
451
+ </div>
452
+ )}
453
+
454
+ {/* Responses */}
455
+ {details.responses && (
456
+ <div>
457
+ <h4 style={{ fontSize: 13, color: "#888", marginBottom: 8 }}>
458
+ Responses
459
+ </h4>
460
+ <div
461
+ style={{
462
+ background: "#1a1a2e",
463
+ borderRadius: 4,
464
+ padding: 12,
465
+ }}
466
+ >
467
+ {Object.entries(details.responses).map(([code, resp]) => {
468
+ const respContent = resp.content?.["application/json"]?.schema;
469
+ const schemaRef = respContent?.$ref?.split("/").pop();
470
+ const schemaType = respContent?.type;
471
+ const schemaItems = respContent?.items?.$ref?.split("/").pop();
472
+
473
+ return (
474
+ <div
475
+ key={code}
476
+ style={{
477
+ marginBottom: 12,
478
+ fontSize: 12,
479
+ }}
480
+ >
481
+ <div style={{ display: "flex", gap: 12, marginBottom: 4 }}>
482
+ <span
483
+ style={{
484
+ color: code.startsWith("2") ? "#49cc90" : "#f66",
485
+ minWidth: 40,
486
+ }}
487
+ >
488
+ {code}
489
+ </span>
490
+ <span style={{ color: "#888" }}>{resp.description}</span>
491
+ </div>
492
+ {respContent && (
493
+ <div
494
+ style={{
495
+ marginLeft: 52,
496
+ padding: "8px 12px",
497
+ background: "#0d0d1a",
498
+ borderRadius: 4,
499
+ fontFamily: "monospace",
500
+ }}
501
+ >
502
+ {schemaRef ? (
503
+ <span style={{ color: "#61affe" }}>{schemaRef}</span>
504
+ ) : schemaType === "array" && schemaItems ? (
505
+ <span style={{ color: "#61affe" }}>{schemaItems}[]</span>
506
+ ) : schemaType === "array" ? (
507
+ <span style={{ color: "#888" }}>array</span>
508
+ ) : (
509
+ <span style={{ color: "#888" }}>{getSchemaPreview(respContent)}</span>
510
+ )}
511
+ </div>
512
+ )}
513
+ </div>
514
+ );
515
+ })}
516
+ </div>
517
+ </div>
518
+ )}
519
+ </div>
520
+ )}
521
+ </div>
522
+ );
523
+ })
524
+ )}
525
+ </div>
526
+
527
+ {/* Schemas section */}
528
+ {spec.components?.schemas && getFilteredSchemas().length > 0 && (
529
+ <div style={{ marginTop: 32 }}>
530
+ <h2 style={{ fontSize: 18, fontWeight: 600, marginBottom: 16 }}>
531
+ Schemas {selectedTag && <span style={{ color: "#666", fontSize: 14 }}>({selectedTag})</span>}
532
+ </h2>
533
+ <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
534
+ {getFilteredSchemas().map((name) => {
535
+ const schema = spec.components!.schemas![name];
536
+ if (!schema) return null;
537
+ return (
538
+ <div
539
+ key={name}
540
+ style={{
541
+ border: "1px solid #333",
542
+ borderRadius: 6,
543
+ padding: 12,
544
+ }}
545
+ >
546
+ <h3 style={{ fontSize: 14, color: "#61affe", marginBottom: 8 }}>
547
+ {name}
548
+ </h3>
549
+ {schema.properties && (
550
+ <div style={{ fontSize: 12 }}>
551
+ {Object.entries(schema.properties).map(([prop, propSchema]: [string, any]) => (
552
+ <div
553
+ key={prop}
554
+ style={{
555
+ display: "flex",
556
+ gap: 8,
557
+ marginBottom: 4,
558
+ fontFamily: "monospace",
559
+ }}
560
+ >
561
+ <span style={{ color: "#fff", minWidth: 120 }}>{prop}</span>
562
+ <span style={{ color: "#888" }}>
563
+ {propSchema.type || (propSchema.$ref ? propSchema.$ref.split("/").pop() : "any")}
564
+ {propSchema.nullable && " | null"}
565
+ </span>
566
+ {propSchema.enum && (
567
+ <span style={{ color: "#666" }}>
568
+ [{propSchema.enum.join(" | ")}]
569
+ </span>
570
+ )}
571
+ </div>
572
+ ))}
573
+ </div>
574
+ )}
575
+ </div>
576
+ );
577
+ })}
578
+ </div>
579
+ </div>
580
+ )}
581
+ </div>
582
+ );
583
+ }