apteva 0.1.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 (47) hide show
  1. package/LICENSE +63 -0
  2. package/README.md +84 -0
  3. package/bin/agent-linux-amd64 +0 -0
  4. package/bin/apteva.js +144 -0
  5. package/dist/App.g02zmbqf.js +213 -0
  6. package/dist/App.g02zmbqf.js.map +37 -0
  7. package/dist/App.mq6jqare.js +1 -0
  8. package/dist/apteva-kit.css +1 -0
  9. package/dist/index.html +14 -0
  10. package/dist/styles.css +1 -0
  11. package/package.json +65 -0
  12. package/src/binary.ts +116 -0
  13. package/src/crypto.ts +152 -0
  14. package/src/db.ts +446 -0
  15. package/src/providers.ts +255 -0
  16. package/src/routes/api.ts +380 -0
  17. package/src/routes/static.ts +47 -0
  18. package/src/server.ts +134 -0
  19. package/src/web/App.tsx +218 -0
  20. package/src/web/components/agents/AgentCard.tsx +71 -0
  21. package/src/web/components/agents/AgentsView.tsx +69 -0
  22. package/src/web/components/agents/ChatPanel.tsx +63 -0
  23. package/src/web/components/agents/CreateAgentModal.tsx +128 -0
  24. package/src/web/components/agents/index.ts +4 -0
  25. package/src/web/components/common/Icons.tsx +61 -0
  26. package/src/web/components/common/LoadingSpinner.tsx +44 -0
  27. package/src/web/components/common/Modal.tsx +16 -0
  28. package/src/web/components/common/Select.tsx +96 -0
  29. package/src/web/components/common/index.ts +4 -0
  30. package/src/web/components/dashboard/Dashboard.tsx +136 -0
  31. package/src/web/components/dashboard/index.ts +1 -0
  32. package/src/web/components/index.ts +11 -0
  33. package/src/web/components/layout/ErrorBanner.tsx +18 -0
  34. package/src/web/components/layout/Header.tsx +26 -0
  35. package/src/web/components/layout/Sidebar.tsx +66 -0
  36. package/src/web/components/layout/index.ts +3 -0
  37. package/src/web/components/onboarding/OnboardingWizard.tsx +344 -0
  38. package/src/web/components/onboarding/index.ts +1 -0
  39. package/src/web/components/settings/SettingsPage.tsx +285 -0
  40. package/src/web/components/settings/index.ts +1 -0
  41. package/src/web/hooks/index.ts +3 -0
  42. package/src/web/hooks/useAgents.ts +62 -0
  43. package/src/web/hooks/useOnboarding.ts +25 -0
  44. package/src/web/hooks/useProviders.ts +65 -0
  45. package/src/web/index.html +21 -0
  46. package/src/web/styles.css +23 -0
  47. package/src/web/types.ts +43 -0
@@ -0,0 +1,96 @@
1
+ import React, { useState, useRef, useEffect } from "react";
2
+
3
+ interface SelectOption {
4
+ value: string;
5
+ label: string;
6
+ recommended?: boolean;
7
+ }
8
+
9
+ interface SelectProps {
10
+ value: string;
11
+ options: SelectOption[];
12
+ onChange: (value: string) => void;
13
+ placeholder?: string;
14
+ }
15
+
16
+ export function Select({ value, options, onChange, placeholder = "Select..." }: SelectProps) {
17
+ const [isOpen, setIsOpen] = useState(false);
18
+ const ref = useRef<HTMLDivElement>(null);
19
+
20
+ const selectedOption = options.find(o => o.value === value);
21
+
22
+ useEffect(() => {
23
+ function handleClickOutside(event: MouseEvent) {
24
+ if (ref.current && !ref.current.contains(event.target as Node)) {
25
+ setIsOpen(false);
26
+ }
27
+ }
28
+ document.addEventListener("mousedown", handleClickOutside);
29
+ return () => document.removeEventListener("mousedown", handleClickOutside);
30
+ }, []);
31
+
32
+ return (
33
+ <div ref={ref} className="relative">
34
+ <button
35
+ type="button"
36
+ onClick={() => setIsOpen(!isOpen)}
37
+ className="w-full bg-[#0a0a0a] border border-[#222] rounded px-3 py-2 text-left flex items-center justify-between focus:outline-none focus:border-[#f97316] text-[#e0e0e0] hover:border-[#333] transition"
38
+ >
39
+ <span className={selectedOption ? "text-[#e0e0e0]" : "text-[#666]"}>
40
+ {selectedOption ? (
41
+ <>
42
+ {selectedOption.label}
43
+ {selectedOption.recommended && (
44
+ <span className="text-[#f97316] text-xs ml-2">(Recommended)</span>
45
+ )}
46
+ </>
47
+ ) : placeholder}
48
+ </span>
49
+ <ChevronIcon isOpen={isOpen} />
50
+ </button>
51
+
52
+ {isOpen && (
53
+ <div className="absolute z-50 w-full mt-1 bg-[#111] border border-[#222] rounded shadow-lg max-h-60 overflow-auto">
54
+ {options.map((option) => (
55
+ <button
56
+ key={option.value}
57
+ type="button"
58
+ onClick={() => {
59
+ onChange(option.value);
60
+ setIsOpen(false);
61
+ }}
62
+ className={`w-full px-3 py-2 text-left flex items-center justify-between hover:bg-[#1a1a1a] transition ${
63
+ option.value === value ? "bg-[#1a1a1a] text-[#f97316]" : "text-[#e0e0e0]"
64
+ }`}
65
+ >
66
+ <span>
67
+ {option.label}
68
+ {option.recommended && (
69
+ <span className="text-[#f97316] text-xs ml-2">(Recommended)</span>
70
+ )}
71
+ </span>
72
+ {option.value === value && (
73
+ <svg className="w-4 h-4 text-[#f97316]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
74
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
75
+ </svg>
76
+ )}
77
+ </button>
78
+ ))}
79
+ </div>
80
+ )}
81
+ </div>
82
+ );
83
+ }
84
+
85
+ function ChevronIcon({ isOpen }: { isOpen: boolean }) {
86
+ return (
87
+ <svg
88
+ className={`w-4 h-4 text-[#666] transition-transform ${isOpen ? "rotate-180" : ""}`}
89
+ fill="none"
90
+ stroke="currentColor"
91
+ viewBox="0 0 24 24"
92
+ >
93
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
94
+ </svg>
95
+ );
96
+ }
@@ -0,0 +1,4 @@
1
+ export { LoadingSpinner } from "./LoadingSpinner";
2
+ export { Modal } from "./Modal";
3
+ export { Select } from "./Select";
4
+ export { CheckIcon, CloseIcon, DashboardIcon, AgentsIcon, SettingsIcon } from "./Icons";
@@ -0,0 +1,136 @@
1
+ import React from "react";
2
+ import type { Agent, Provider, Route } from "../../types";
3
+
4
+ interface DashboardProps {
5
+ agents: Agent[];
6
+ loading: boolean;
7
+ runningCount: number;
8
+ configuredProviders: Provider[];
9
+ onNavigate: (route: Route) => void;
10
+ onSelectAgent: (agent: Agent) => void;
11
+ }
12
+
13
+ export function Dashboard({
14
+ agents,
15
+ loading,
16
+ runningCount,
17
+ configuredProviders,
18
+ onNavigate,
19
+ onSelectAgent,
20
+ }: DashboardProps) {
21
+ return (
22
+ <div className="flex-1 overflow-auto p-6">
23
+ {/* Stats Cards */}
24
+ <div className="grid grid-cols-3 gap-4 mb-6">
25
+ <StatCard label="Total Agents" value={agents.length} />
26
+ <StatCard label="Running" value={runningCount} color="text-[#3b82f6]" />
27
+ <StatCard label="Providers" value={configuredProviders.length} color="text-[#f97316]" />
28
+ </div>
29
+
30
+ <div className="grid grid-cols-2 gap-6">
31
+ {/* Agents List */}
32
+ <DashboardCard
33
+ title="Agents"
34
+ actionLabel="View All"
35
+ onAction={() => onNavigate("agents")}
36
+ >
37
+ {loading ? (
38
+ <div className="p-4 text-center text-[#666]">Loading...</div>
39
+ ) : agents.length === 0 ? (
40
+ <div className="p-4 text-center text-[#666]">No agents yet</div>
41
+ ) : (
42
+ <div className="divide-y divide-[#1a1a1a]">
43
+ {agents.slice(0, 5).map((agent) => (
44
+ <div
45
+ key={agent.id}
46
+ onClick={() => onSelectAgent(agent)}
47
+ className="px-4 py-3 hover:bg-[#1a1a1a] cursor-pointer flex items-center justify-between"
48
+ >
49
+ <div>
50
+ <p className="font-medium">{agent.name}</p>
51
+ <p className="text-sm text-[#666]">{agent.provider}</p>
52
+ </div>
53
+ <span
54
+ className={`w-2 h-2 rounded-full ${
55
+ agent.status === "running" ? "bg-[#3b82f6]" : "bg-[#444]"
56
+ }`}
57
+ />
58
+ </div>
59
+ ))}
60
+ </div>
61
+ )}
62
+ </DashboardCard>
63
+
64
+ {/* Configured Providers */}
65
+ <DashboardCard
66
+ title="Providers"
67
+ actionLabel="Manage"
68
+ onAction={() => onNavigate("settings")}
69
+ >
70
+ {configuredProviders.length === 0 ? (
71
+ <div className="p-4 text-center text-[#666]">
72
+ <p>No providers configured</p>
73
+ <button
74
+ onClick={() => onNavigate("settings")}
75
+ className="text-[#f97316] hover:underline mt-1"
76
+ >
77
+ Add API Key
78
+ </button>
79
+ </div>
80
+ ) : (
81
+ <div className="divide-y divide-[#1a1a1a]">
82
+ {configuredProviders.map((provider) => (
83
+ <div key={provider.id} className="px-4 py-3 flex items-center justify-between">
84
+ <div>
85
+ <p className="font-medium">{provider.name}</p>
86
+ <p className="text-sm text-[#666]">{provider.models.length} models</p>
87
+ </div>
88
+ <span className="text-green-400 text-sm">{provider.keyHint}</span>
89
+ </div>
90
+ ))}
91
+ </div>
92
+ )}
93
+ </DashboardCard>
94
+ </div>
95
+ </div>
96
+ );
97
+ }
98
+
99
+ interface StatCardProps {
100
+ label: string;
101
+ value: number;
102
+ color?: string;
103
+ }
104
+
105
+ function StatCard({ label, value, color }: StatCardProps) {
106
+ return (
107
+ <div className="bg-[#111] rounded p-4 border border-[#1a1a1a]">
108
+ <p className="text-sm text-[#666] mb-1">{label}</p>
109
+ <p className={`text-2xl font-semibold ${color || ''}`}>{value}</p>
110
+ </div>
111
+ );
112
+ }
113
+
114
+ interface DashboardCardProps {
115
+ title: string;
116
+ actionLabel: string;
117
+ onAction: () => void;
118
+ children: React.ReactNode;
119
+ }
120
+
121
+ function DashboardCard({ title, actionLabel, onAction, children }: DashboardCardProps) {
122
+ return (
123
+ <div className="bg-[#111] rounded border border-[#1a1a1a] overflow-hidden">
124
+ <div className="px-4 py-3 border-b border-[#1a1a1a] flex items-center justify-between">
125
+ <h3 className="font-semibold">{title}</h3>
126
+ <button
127
+ onClick={onAction}
128
+ className="text-sm text-[#3b82f6] hover:text-[#60a5fa]"
129
+ >
130
+ {actionLabel}
131
+ </button>
132
+ </div>
133
+ {children}
134
+ </div>
135
+ );
136
+ }
@@ -0,0 +1 @@
1
+ export { Dashboard } from "./Dashboard";
@@ -0,0 +1,11 @@
1
+ // Common components
2
+ export { LoadingSpinner, Modal, Select, CheckIcon, CloseIcon, DashboardIcon, AgentsIcon, SettingsIcon } from "./common";
3
+
4
+ // Layout components
5
+ export { Header, Sidebar, ErrorBanner } from "./layout";
6
+
7
+ // Feature components
8
+ export { OnboardingWizard } from "./onboarding";
9
+ export { SettingsPage } from "./settings";
10
+ export { AgentCard, CreateAgentModal, ChatPanel, AgentsView } from "./agents";
11
+ export { Dashboard } from "./dashboard";
@@ -0,0 +1,18 @@
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
+ }
@@ -0,0 +1,26 @@
1
+ import React from "react";
2
+
3
+ interface HeaderProps {
4
+ onNewAgent: () => void;
5
+ canCreateAgent: boolean;
6
+ }
7
+
8
+ export function Header({ onNewAgent, canCreateAgent }: HeaderProps) {
9
+ return (
10
+ <header className="border-b border-[#1a1a1a] px-6 py-4 flex-shrink-0">
11
+ <div className="flex items-center justify-between">
12
+ <div className="flex items-center gap-2">
13
+ <span className="text-[#f97316]">&gt;_</span>
14
+ <span className="text-xl tracking-wider">apteva</span>
15
+ </div>
16
+ <button
17
+ onClick={onNewAgent}
18
+ disabled={!canCreateAgent}
19
+ className="bg-[#f97316] hover:bg-[#fb923c] disabled:opacity-50 disabled:cursor-not-allowed text-black px-4 py-2 rounded font-medium transition"
20
+ >
21
+ + New Agent
22
+ </button>
23
+ </div>
24
+ </header>
25
+ );
26
+ }
@@ -0,0 +1,66 @@
1
+ import React from "react";
2
+ import { DashboardIcon, AgentsIcon, SettingsIcon } from "../common/Icons";
3
+ import type { Route } from "../../types";
4
+
5
+ interface SidebarProps {
6
+ route: Route;
7
+ agentCount: number;
8
+ onNavigate: (route: Route) => void;
9
+ }
10
+
11
+ export function Sidebar({ route, agentCount, onNavigate }: SidebarProps) {
12
+ return (
13
+ <aside className="w-56 border-r border-[#1a1a1a] flex-shrink-0 p-4">
14
+ <nav className="space-y-1">
15
+ <NavButton
16
+ icon={<DashboardIcon />}
17
+ label="Dashboard"
18
+ active={route === "dashboard"}
19
+ onClick={() => onNavigate("dashboard")}
20
+ />
21
+ <NavButton
22
+ icon={<AgentsIcon />}
23
+ label="Agents"
24
+ active={route === "agents"}
25
+ onClick={() => onNavigate("agents")}
26
+ badge={agentCount > 0 ? String(agentCount) : undefined}
27
+ />
28
+ <NavButton
29
+ icon={<SettingsIcon />}
30
+ label="Settings"
31
+ active={route === "settings"}
32
+ onClick={() => onNavigate("settings")}
33
+ />
34
+ </nav>
35
+ </aside>
36
+ );
37
+ }
38
+
39
+ interface NavButtonProps {
40
+ icon: React.ReactNode;
41
+ label: string;
42
+ active: boolean;
43
+ onClick: () => void;
44
+ badge?: string;
45
+ }
46
+
47
+ function NavButton({ icon, label, active, onClick, badge }: NavButtonProps) {
48
+ return (
49
+ <button
50
+ onClick={onClick}
51
+ className={`w-full flex items-center gap-3 px-3 py-2 rounded font-medium transition ${
52
+ active
53
+ ? "bg-[#111] text-[#e0e0e0]"
54
+ : "text-[#666] hover:bg-[#111] hover:text-[#888]"
55
+ }`}
56
+ >
57
+ {icon}
58
+ {label}
59
+ {badge && (
60
+ <span className="ml-auto bg-[#333] text-[#888] text-xs px-2 py-0.5 rounded-full">
61
+ {badge}
62
+ </span>
63
+ )}
64
+ </button>
65
+ );
66
+ }
@@ -0,0 +1,3 @@
1
+ export { Header } from "./Header";
2
+ export { Sidebar } from "./Sidebar";
3
+ export { ErrorBanner } from "./ErrorBanner";