apteva 0.4.57 → 0.7.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.
- package/README.md +216 -54
- package/cli.js +35 -0
- package/install.js +92 -0
- package/package.json +12 -79
- package/LICENSE +0 -63
- package/bin/apteva.js +0 -196
- package/dist/ActivityPage.kxzzb4yc.js +0 -3
- package/dist/ApiDocsPage.zq998hbm.js +0 -4
- package/dist/App.55rea8mn.js +0 -61
- package/dist/App.5ywb23z4.js +0 -53
- package/dist/App.6thds120.js +0 -4
- package/dist/App.9tctxzqm.js +0 -8
- package/dist/App.a8r8ttaz.js +0 -4
- package/dist/App.agsv5bje.js +0 -4
- package/dist/App.cepapqmx.js +0 -4
- package/dist/App.dp041gb3.js +0 -221
- package/dist/App.fds72zb5.js +0 -4
- package/dist/App.fg9qj2dq.js +0 -4
- package/dist/App.ndfejbm9.js +0 -4
- package/dist/App.nxmfmq1h.js +0 -13
- package/dist/App.qdfyt8ba.js +0 -4
- package/dist/App.x2d0ygt6.js +0 -4
- package/dist/App.yt9p4nr3.js +0 -20
- package/dist/App.zn4mw16t.js +0 -1
- package/dist/ConnectionsPage.8r96ryw7.js +0 -3
- package/dist/McpPage.3cwh0gnd.js +0 -3
- package/dist/SettingsPage.ykgdh5ev.js +0 -3
- package/dist/SkillsPage.4np1s65b.js +0 -3
- package/dist/TasksPage.4g08t7p6.js +0 -3
- package/dist/TelemetryPage.72w9pwcp.js +0 -3
- package/dist/TestsPage.z4fk3r7r.js +0 -3
- package/dist/ThreadsPage.63tcajeh.js +0 -3
- package/dist/apteva-kit.css +0 -1
- package/dist/icon.png +0 -0
- package/dist/index.html +0 -16
- package/dist/styles.css +0 -1
- package/scripts/postinstall.mjs +0 -102
- package/src/auth/index.ts +0 -394
- package/src/auth/middleware.ts +0 -213
- package/src/binary.ts +0 -536
- package/src/channels/index.ts +0 -40
- package/src/channels/telegram.ts +0 -311
- package/src/crypto.ts +0 -301
- package/src/db-tests.ts +0 -174
- package/src/db.ts +0 -3133
- package/src/integrations/agentdojo.ts +0 -559
- package/src/integrations/composio.ts +0 -437
- package/src/integrations/index.ts +0 -87
- package/src/integrations/skillsmp.ts +0 -318
- package/src/mcp-client.ts +0 -605
- package/src/mcp-handler.ts +0 -394
- package/src/mcp-platform.ts +0 -2403
- package/src/openapi.ts +0 -2410
- package/src/providers.ts +0 -597
- package/src/routes/api/agent-utils.ts +0 -890
- package/src/routes/api/agents.ts +0 -916
- package/src/routes/api/api-keys.ts +0 -95
- package/src/routes/api/channels.ts +0 -182
- package/src/routes/api/helpers.ts +0 -12
- package/src/routes/api/integrations.ts +0 -639
- package/src/routes/api/mcp.ts +0 -574
- package/src/routes/api/meta-agent.ts +0 -195
- package/src/routes/api/projects.ts +0 -112
- package/src/routes/api/providers.ts +0 -424
- package/src/routes/api/skills.ts +0 -537
- package/src/routes/api/system.ts +0 -333
- package/src/routes/api/telemetry.ts +0 -203
- package/src/routes/api/tests.ts +0 -148
- package/src/routes/api/triggers.ts +0 -518
- package/src/routes/api/users.ts +0 -148
- package/src/routes/api/webhooks.ts +0 -171
- package/src/routes/api.ts +0 -53
- package/src/routes/auth.ts +0 -251
- package/src/routes/share.ts +0 -86
- package/src/routes/static.ts +0 -131
- package/src/server.ts +0 -642
- package/src/test-runner.ts +0 -598
- package/src/triggers/agentdojo.ts +0 -253
- package/src/triggers/composio.ts +0 -264
- package/src/triggers/index.ts +0 -71
- package/src/tui/AgentList.tsx +0 -145
- package/src/tui/App.tsx +0 -102
- package/src/tui/Login.tsx +0 -104
- package/src/tui/api.ts +0 -72
- package/src/tui/index.tsx +0 -7
- package/src/web/App.tsx +0 -455
- package/src/web/components/activity/ActivityPage.tsx +0 -314
- package/src/web/components/activity/index.ts +0 -1
- package/src/web/components/agents/AgentCard.tsx +0 -189
- package/src/web/components/agents/AgentPanel.tsx +0 -2244
- package/src/web/components/agents/AgentsView.tsx +0 -180
- package/src/web/components/agents/CreateAgentModal.tsx +0 -475
- package/src/web/components/agents/index.ts +0 -4
- package/src/web/components/api/ApiDocsPage.tsx +0 -842
- package/src/web/components/auth/CreateAccountStep.tsx +0 -176
- package/src/web/components/auth/LoginPage.tsx +0 -91
- package/src/web/components/auth/index.ts +0 -2
- package/src/web/components/common/Icons.tsx +0 -250
- package/src/web/components/common/LoadingSpinner.tsx +0 -44
- package/src/web/components/common/Modal.tsx +0 -199
- package/src/web/components/common/Select.tsx +0 -97
- package/src/web/components/common/index.ts +0 -20
- package/src/web/components/connections/ConnectionsPage.tsx +0 -54
- package/src/web/components/connections/IntegrationsTab.tsx +0 -170
- package/src/web/components/connections/OverviewTab.tsx +0 -137
- package/src/web/components/connections/TriggersTab.tsx +0 -1346
- package/src/web/components/dashboard/Dashboard.tsx +0 -572
- package/src/web/components/dashboard/index.ts +0 -1
- package/src/web/components/index.ts +0 -21
- package/src/web/components/layout/ErrorBanner.tsx +0 -18
- package/src/web/components/layout/Header.tsx +0 -332
- package/src/web/components/layout/Sidebar.tsx +0 -231
- package/src/web/components/layout/index.ts +0 -3
- package/src/web/components/mcp/IntegrationsPanel.tsx +0 -857
- package/src/web/components/mcp/McpPage.tsx +0 -2515
- package/src/web/components/mcp/index.ts +0 -1
- package/src/web/components/meta-agent/MetaAgent.tsx +0 -245
- package/src/web/components/onboarding/OnboardingWizard.tsx +0 -404
- package/src/web/components/onboarding/index.ts +0 -1
- package/src/web/components/settings/SettingsPage.tsx +0 -2776
- package/src/web/components/settings/index.ts +0 -1
- package/src/web/components/skills/SkillsPage.tsx +0 -1200
- package/src/web/components/tasks/TasksPage.tsx +0 -1116
- package/src/web/components/tasks/index.ts +0 -1
- package/src/web/components/telemetry/TelemetryPage.tsx +0 -1129
- package/src/web/components/tests/TestsPage.tsx +0 -594
- package/src/web/components/threads/ThreadsPage.tsx +0 -315
- package/src/web/context/AuthContext.tsx +0 -242
- package/src/web/context/ProjectContext.tsx +0 -214
- package/src/web/context/TelemetryContext.tsx +0 -299
- package/src/web/context/ThemeContext.tsx +0 -90
- package/src/web/context/UIModeContext.tsx +0 -49
- package/src/web/context/index.ts +0 -12
- package/src/web/hooks/index.ts +0 -3
- package/src/web/hooks/useAgents.ts +0 -115
- package/src/web/hooks/useOnboarding.ts +0 -20
- package/src/web/hooks/useProviders.ts +0 -75
- package/src/web/icon.png +0 -0
- package/src/web/index.html +0 -16
- package/src/web/styles.css +0 -118
- package/src/web/themes.ts +0 -162
- package/src/web/types.ts +0 -298
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { McpPage } from "./McpPage";
|
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect, useMemo, useCallback, createContext, useContext, type ReactNode } from "react";
|
|
2
|
-
import { Chat } from "@apteva/apteva-kit";
|
|
3
|
-
import { useAuth, useProjects, useTriggerRefresh, useTheme, useUIMode } from "../../context";
|
|
4
|
-
|
|
5
|
-
interface MetaAgentStatus {
|
|
6
|
-
enabled: boolean;
|
|
7
|
-
available?: boolean;
|
|
8
|
-
reason?: string;
|
|
9
|
-
agent?: {
|
|
10
|
-
id: string;
|
|
11
|
-
name: string;
|
|
12
|
-
status: "stopped" | "running";
|
|
13
|
-
port: number | null;
|
|
14
|
-
provider: string;
|
|
15
|
-
model: string;
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface MetaAgentContextValue {
|
|
20
|
-
status: MetaAgentStatus | null;
|
|
21
|
-
isOpen: boolean;
|
|
22
|
-
isStarting: boolean;
|
|
23
|
-
error: string | null;
|
|
24
|
-
isAvailable: boolean;
|
|
25
|
-
isRunning: boolean;
|
|
26
|
-
agent: MetaAgentStatus["agent"] | undefined;
|
|
27
|
-
toggle: () => void;
|
|
28
|
-
close: () => void;
|
|
29
|
-
startAgent: () => Promise<void>;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const MetaAgentContext = createContext<MetaAgentContextValue | null>(null);
|
|
33
|
-
|
|
34
|
-
export function useMetaAgent() {
|
|
35
|
-
return useContext(MetaAgentContext);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function MetaAgentProvider({ children }: { children: ReactNode }) {
|
|
39
|
-
const { authFetch, isAuthenticated, isLoading: authLoading } = useAuth();
|
|
40
|
-
const [status, setStatus] = useState<MetaAgentStatus | null>(null);
|
|
41
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
42
|
-
const [isStarting, setIsStarting] = useState(false);
|
|
43
|
-
const [error, setError] = useState<string | null>(null);
|
|
44
|
-
|
|
45
|
-
// Fetch meta agent status
|
|
46
|
-
const fetchStatus = async () => {
|
|
47
|
-
try {
|
|
48
|
-
const res = await authFetch("/api/meta-agent/status");
|
|
49
|
-
const data = await res.json();
|
|
50
|
-
setStatus(data);
|
|
51
|
-
} catch (e) {
|
|
52
|
-
console.error("[MetaAgent] Failed to fetch status:", e);
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
// Only fetch when authenticated
|
|
58
|
-
if (!authLoading && isAuthenticated) {
|
|
59
|
-
fetchStatus();
|
|
60
|
-
}
|
|
61
|
-
}, [authFetch, isAuthenticated, authLoading]);
|
|
62
|
-
|
|
63
|
-
// Start the meta agent
|
|
64
|
-
const startAgent = async () => {
|
|
65
|
-
setIsStarting(true);
|
|
66
|
-
setError(null);
|
|
67
|
-
try {
|
|
68
|
-
const res = await authFetch("/api/meta-agent/start", { method: "POST" });
|
|
69
|
-
const data = await res.json();
|
|
70
|
-
if (!res.ok) {
|
|
71
|
-
setError(data.error || "Failed to start assistant");
|
|
72
|
-
} else {
|
|
73
|
-
await fetchStatus();
|
|
74
|
-
}
|
|
75
|
-
} catch (e) {
|
|
76
|
-
setError("Failed to start assistant");
|
|
77
|
-
}
|
|
78
|
-
setIsStarting(false);
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const isAvailable = !!(status?.enabled && status?.available);
|
|
82
|
-
const agent = status?.agent;
|
|
83
|
-
const isRunning = !!(agent?.status === "running" && agent?.port);
|
|
84
|
-
|
|
85
|
-
const value: MetaAgentContextValue = {
|
|
86
|
-
status,
|
|
87
|
-
isOpen,
|
|
88
|
-
isStarting,
|
|
89
|
-
error,
|
|
90
|
-
isAvailable,
|
|
91
|
-
isRunning,
|
|
92
|
-
agent,
|
|
93
|
-
toggle: () => setIsOpen(!isOpen),
|
|
94
|
-
close: () => setIsOpen(false),
|
|
95
|
-
startAgent,
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
return (
|
|
99
|
-
<MetaAgentContext.Provider value={value}>
|
|
100
|
-
{children}
|
|
101
|
-
</MetaAgentContext.Provider>
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Header button component - to be used in Header.tsx
|
|
106
|
-
export function MetaAgentButton() {
|
|
107
|
-
const ctx = useMetaAgent();
|
|
108
|
-
if (!ctx?.isAvailable) return null;
|
|
109
|
-
|
|
110
|
-
return (
|
|
111
|
-
<button
|
|
112
|
-
onClick={ctx.toggle}
|
|
113
|
-
className={`hidden md:flex items-center gap-2 px-3 py-2 rounded transition ${
|
|
114
|
-
ctx.isOpen
|
|
115
|
-
? "bg-[var(--color-accent)] text-white"
|
|
116
|
-
: "bg-[var(--color-bg-secondary)] hover:bg-[var(--color-surface-raised)] text-[var(--color-text-secondary)] hover:text-white"
|
|
117
|
-
}`}
|
|
118
|
-
title="Apteva Assistant"
|
|
119
|
-
>
|
|
120
|
-
<AssistantIcon className="w-5 h-5" />
|
|
121
|
-
<span className="text-sm">Assistant</span>
|
|
122
|
-
{ctx.isRunning && (
|
|
123
|
-
<span className="w-2 h-2 rounded-full bg-green-400" />
|
|
124
|
-
)}
|
|
125
|
-
</button>
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Chat panel component - renders as a right-side drawer
|
|
130
|
-
export function MetaAgentPanel() {
|
|
131
|
-
const { theme } = useTheme();
|
|
132
|
-
const { t } = useUIMode();
|
|
133
|
-
const ctx = useMetaAgent();
|
|
134
|
-
const { currentProjectId, currentProject } = useProjects();
|
|
135
|
-
const triggerRefresh = useTriggerRefresh();
|
|
136
|
-
if (!ctx?.isAvailable || !ctx.isOpen) return null;
|
|
137
|
-
|
|
138
|
-
const { agent, isRunning, error, isStarting, startAgent, close } = ctx;
|
|
139
|
-
|
|
140
|
-
// Build context string for the meta agent
|
|
141
|
-
const chatContext = useMemo(() => {
|
|
142
|
-
const parts: string[] = [];
|
|
143
|
-
if (currentProject) {
|
|
144
|
-
parts.push(`Current project: "${currentProject.name}" (id: ${currentProject.id})`);
|
|
145
|
-
} else if (currentProjectId === "unassigned") {
|
|
146
|
-
parts.push("Viewing: unassigned agents (no project)");
|
|
147
|
-
} else {
|
|
148
|
-
parts.push("Viewing: All Projects");
|
|
149
|
-
}
|
|
150
|
-
return parts.join("\n");
|
|
151
|
-
}, [currentProjectId, currentProject]);
|
|
152
|
-
|
|
153
|
-
return (
|
|
154
|
-
<>
|
|
155
|
-
{/* Backdrop */}
|
|
156
|
-
<div
|
|
157
|
-
className="hidden md:block fixed inset-0 bg-black/20 z-40"
|
|
158
|
-
onClick={close}
|
|
159
|
-
/>
|
|
160
|
-
|
|
161
|
-
{/* Drawer Panel */}
|
|
162
|
-
<div className="hidden md:flex fixed top-0 right-0 h-full w-[480px] lg:w-[540px] bg-[var(--color-bg)] border-l border-[var(--color-border)] shadow-2xl z-50 flex-col">
|
|
163
|
-
{/* Header */}
|
|
164
|
-
<div className="flex items-center justify-between px-4 py-3 border-b border-[var(--color-border)] bg-[var(--color-surface)]">
|
|
165
|
-
<div className="flex items-center gap-2">
|
|
166
|
-
<div className={`w-2 h-2 rounded-full ${isRunning ? "bg-green-400" : "bg-[var(--color-scrollbar)]"}`} />
|
|
167
|
-
<span className="font-medium text-sm">Apteva Assistant</span>
|
|
168
|
-
</div>
|
|
169
|
-
<button
|
|
170
|
-
onClick={close}
|
|
171
|
-
className="text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)] transition"
|
|
172
|
-
>
|
|
173
|
-
<CloseIcon />
|
|
174
|
-
</button>
|
|
175
|
-
</div>
|
|
176
|
-
|
|
177
|
-
{/* Content */}
|
|
178
|
-
<div className="flex-1 min-h-0 flex flex-col">
|
|
179
|
-
{isRunning ? (
|
|
180
|
-
<Chat
|
|
181
|
-
agentId="default"
|
|
182
|
-
apiUrl={`/api/agents/${agent!.id}`}
|
|
183
|
-
placeholder="Ask me anything about Apteva..."
|
|
184
|
-
variant="terminal"
|
|
185
|
-
theme={theme.id as "light" | "dark"}
|
|
186
|
-
showHeader={false}
|
|
187
|
-
context={chatContext}
|
|
188
|
-
onToolResult={triggerRefresh}
|
|
189
|
-
/>
|
|
190
|
-
) : (
|
|
191
|
-
<div className="flex-1 flex flex-col items-center justify-center p-6 text-center">
|
|
192
|
-
<AssistantIcon className="w-12 h-12 text-[var(--color-border-light)] mb-4" />
|
|
193
|
-
<h3 className="font-medium mb-2">Apteva Assistant</h3>
|
|
194
|
-
<p className="text-sm text-[var(--color-text-muted)] mb-6">
|
|
195
|
-
{t(
|
|
196
|
-
"I can help you navigate Apteva, create agents, set up MCP servers, and more.",
|
|
197
|
-
"I can help you hire employees, assign them work, and manage your team."
|
|
198
|
-
)}
|
|
199
|
-
</p>
|
|
200
|
-
{error && (
|
|
201
|
-
<p className="text-sm text-red-400 mb-4">{error}</p>
|
|
202
|
-
)}
|
|
203
|
-
<button
|
|
204
|
-
onClick={startAgent}
|
|
205
|
-
disabled={isStarting}
|
|
206
|
-
className="bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] disabled:opacity-50 text-white px-6 py-2 rounded font-medium transition"
|
|
207
|
-
>
|
|
208
|
-
{isStarting ? "Starting..." : "Start Assistant"}
|
|
209
|
-
</button>
|
|
210
|
-
</div>
|
|
211
|
-
)}
|
|
212
|
-
</div>
|
|
213
|
-
</div>
|
|
214
|
-
</>
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function AssistantIcon({ className = "w-6 h-6" }: { className?: string }) {
|
|
219
|
-
return (
|
|
220
|
-
<svg className={className} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
221
|
-
<path
|
|
222
|
-
strokeLinecap="round"
|
|
223
|
-
strokeLinejoin="round"
|
|
224
|
-
strokeWidth={2}
|
|
225
|
-
d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"
|
|
226
|
-
/>
|
|
227
|
-
</svg>
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function CloseIcon() {
|
|
232
|
-
return (
|
|
233
|
-
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
234
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
235
|
-
</svg>
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function MinimizeIcon() {
|
|
240
|
-
return (
|
|
241
|
-
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
242
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
|
243
|
-
</svg>
|
|
244
|
-
);
|
|
245
|
-
}
|
|
@@ -1,404 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from "react";
|
|
2
|
-
import { CheckIcon } from "../common/Icons";
|
|
3
|
-
import { CreateAccountStep } from "../auth";
|
|
4
|
-
import type { Provider } from "../../types";
|
|
5
|
-
|
|
6
|
-
interface OnboardingWizardProps {
|
|
7
|
-
onComplete: () => void;
|
|
8
|
-
needsAccount?: boolean; // Whether to show account creation step
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function OnboardingWizard({ onComplete, needsAccount = false }: OnboardingWizardProps) {
|
|
12
|
-
// Step 0 = account creation (if needed), Step 1 = add keys, Step 2 = complete
|
|
13
|
-
const [step, setStep] = useState(needsAccount ? 0 : 1);
|
|
14
|
-
const [providers, setProviders] = useState<Provider[]>([]);
|
|
15
|
-
const [selectedProvider, setSelectedProvider] = useState<string | null>(null);
|
|
16
|
-
const [apiKey, setApiKey] = useState("");
|
|
17
|
-
const [saving, setSaving] = useState(false);
|
|
18
|
-
const [testing, setTesting] = useState(false);
|
|
19
|
-
const [error, setError] = useState<string | null>(null);
|
|
20
|
-
const [success, setSuccess] = useState<string | null>(null);
|
|
21
|
-
const [accountCreated, setAccountCreated] = useState(false);
|
|
22
|
-
|
|
23
|
-
// Get auth token from session storage (set during account creation)
|
|
24
|
-
const getAuthHeaders = (): Record<string, string> => {
|
|
25
|
-
const token = sessionStorage.getItem("accessToken");
|
|
26
|
-
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
// Don't fetch providers until after account is created (if needed)
|
|
31
|
-
if (needsAccount && !accountCreated && step === 0) return;
|
|
32
|
-
|
|
33
|
-
fetch("/api/providers", { headers: getAuthHeaders() })
|
|
34
|
-
.then(res => res.json())
|
|
35
|
-
.then(data => {
|
|
36
|
-
// Only show LLM providers in onboarding, not integrations
|
|
37
|
-
const llmProviders = (data.providers || []).filter((p: Provider) => p.type === "llm");
|
|
38
|
-
setProviders(llmProviders);
|
|
39
|
-
});
|
|
40
|
-
}, [accountCreated, step, needsAccount]);
|
|
41
|
-
|
|
42
|
-
const configuredProviders = providers.filter(p => p.hasKey);
|
|
43
|
-
|
|
44
|
-
const saveKey = async () => {
|
|
45
|
-
if (!selectedProvider || !apiKey) return;
|
|
46
|
-
setSaving(true);
|
|
47
|
-
setError(null);
|
|
48
|
-
setSuccess(null);
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
setTesting(true);
|
|
52
|
-
const testRes = await fetch(`/api/keys/${selectedProvider}/test`, {
|
|
53
|
-
method: "POST",
|
|
54
|
-
headers: { "Content-Type": "application/json", ...getAuthHeaders() },
|
|
55
|
-
body: JSON.stringify({ key: apiKey }),
|
|
56
|
-
});
|
|
57
|
-
const testData = await testRes.json();
|
|
58
|
-
setTesting(false);
|
|
59
|
-
|
|
60
|
-
if (!testData.valid) {
|
|
61
|
-
setError(testData.error || "API key is invalid");
|
|
62
|
-
setSaving(false);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const saveRes = await fetch(`/api/keys/${selectedProvider}`, {
|
|
67
|
-
method: "POST",
|
|
68
|
-
headers: { "Content-Type": "application/json", ...getAuthHeaders() },
|
|
69
|
-
body: JSON.stringify({ key: apiKey }),
|
|
70
|
-
});
|
|
71
|
-
const saveData = await saveRes.json();
|
|
72
|
-
|
|
73
|
-
if (!saveRes.ok) {
|
|
74
|
-
setError(saveData.error || "Failed to save key");
|
|
75
|
-
} else {
|
|
76
|
-
setSuccess("API key saved successfully!");
|
|
77
|
-
setApiKey("");
|
|
78
|
-
const res = await fetch("/api/providers", { headers: getAuthHeaders() });
|
|
79
|
-
const data = await res.json();
|
|
80
|
-
setProviders(data.providers || []);
|
|
81
|
-
setSelectedProvider(null);
|
|
82
|
-
}
|
|
83
|
-
} catch (e) {
|
|
84
|
-
setError("Failed to save key");
|
|
85
|
-
}
|
|
86
|
-
setSaving(false);
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const completeOnboarding = async () => {
|
|
90
|
-
// Create a default project for the user
|
|
91
|
-
try {
|
|
92
|
-
const projectRes = await fetch("/api/projects", {
|
|
93
|
-
method: "POST",
|
|
94
|
-
headers: { "Content-Type": "application/json", ...getAuthHeaders() },
|
|
95
|
-
body: JSON.stringify({
|
|
96
|
-
name: "My Project",
|
|
97
|
-
description: "Default project for organizing agents",
|
|
98
|
-
color: "#f97316", // Orange - matches brand color
|
|
99
|
-
}),
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
if (projectRes.ok) {
|
|
103
|
-
const data = await projectRes.json();
|
|
104
|
-
// Set this project as the current project in localStorage
|
|
105
|
-
if (data.project?.id) {
|
|
106
|
-
localStorage.setItem("apteva_current_project", data.project.id);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
} catch (e) {
|
|
110
|
-
// Don't block onboarding if project creation fails
|
|
111
|
-
console.error("Failed to create default project:", e);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
await fetch("/api/onboarding/complete", { method: "POST", headers: getAuthHeaders() });
|
|
115
|
-
onComplete();
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
const handleAccountCreated = () => {
|
|
119
|
-
setAccountCreated(true);
|
|
120
|
-
setStep(1);
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
// Calculate total steps and current progress
|
|
124
|
-
const totalSteps = needsAccount ? 3 : 2;
|
|
125
|
-
const currentStep = needsAccount ? step : step - 1;
|
|
126
|
-
|
|
127
|
-
return (
|
|
128
|
-
<div className="min-h-screen bg-[var(--color-bg)] text-[var(--color-text)] flex items-center justify-center p-8">
|
|
129
|
-
<div className="w-full max-w-2xl">
|
|
130
|
-
{/* Logo */}
|
|
131
|
-
<div className="text-center mb-8">
|
|
132
|
-
<div className="flex items-center justify-center gap-2 mb-2">
|
|
133
|
-
<span className="text-[var(--color-accent)] text-3xl">>_</span>
|
|
134
|
-
<span className="text-3xl tracking-wider">apteva</span>
|
|
135
|
-
</div>
|
|
136
|
-
<p className="text-[var(--color-text-muted)]">Run AI agents locally</p>
|
|
137
|
-
</div>
|
|
138
|
-
|
|
139
|
-
{/* Progress */}
|
|
140
|
-
<div className="flex items-center justify-center gap-2 mb-8">
|
|
141
|
-
{needsAccount && (
|
|
142
|
-
<>
|
|
143
|
-
<div className={`w-3 h-3 rounded-full ${step >= 0 ? 'bg-[var(--color-accent)]' : 'bg-[var(--color-surface-raised)]'}`} />
|
|
144
|
-
<div className={`w-16 h-0.5 ${step >= 1 ? 'bg-[var(--color-accent)]' : 'bg-[var(--color-surface-raised)]'}`} />
|
|
145
|
-
</>
|
|
146
|
-
)}
|
|
147
|
-
<div className={`w-3 h-3 rounded-full ${step >= 1 ? 'bg-[var(--color-accent)]' : 'bg-[var(--color-surface-raised)]'}`} />
|
|
148
|
-
<div className={`w-16 h-0.5 ${step >= 2 ? 'bg-[var(--color-accent)]' : 'bg-[var(--color-surface-raised)]'}`} />
|
|
149
|
-
<div className={`w-3 h-3 rounded-full ${step >= 2 ? 'bg-[var(--color-accent)]' : 'bg-[var(--color-surface-raised)]'}`} />
|
|
150
|
-
</div>
|
|
151
|
-
|
|
152
|
-
<div className="bg-[var(--color-surface)] card p-8">
|
|
153
|
-
{step === 0 && needsAccount && (
|
|
154
|
-
<CreateAccountStep onComplete={handleAccountCreated} />
|
|
155
|
-
)}
|
|
156
|
-
|
|
157
|
-
{step === 1 && (
|
|
158
|
-
<Step1AddKeys
|
|
159
|
-
providers={providers}
|
|
160
|
-
configuredProviders={configuredProviders}
|
|
161
|
-
selectedProvider={selectedProvider}
|
|
162
|
-
apiKey={apiKey}
|
|
163
|
-
saving={saving}
|
|
164
|
-
testing={testing}
|
|
165
|
-
error={error}
|
|
166
|
-
success={success}
|
|
167
|
-
onSelectProvider={setSelectedProvider}
|
|
168
|
-
onApiKeyChange={setApiKey}
|
|
169
|
-
onSaveKey={saveKey}
|
|
170
|
-
onContinue={() => setStep(2)}
|
|
171
|
-
/>
|
|
172
|
-
)}
|
|
173
|
-
|
|
174
|
-
{step === 2 && (
|
|
175
|
-
<Step2Complete
|
|
176
|
-
configuredProviders={configuredProviders}
|
|
177
|
-
onAddMore={() => setStep(1)}
|
|
178
|
-
onComplete={completeOnboarding}
|
|
179
|
-
/>
|
|
180
|
-
)}
|
|
181
|
-
</div>
|
|
182
|
-
</div>
|
|
183
|
-
</div>
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
interface Step1Props {
|
|
188
|
-
providers: Provider[];
|
|
189
|
-
configuredProviders: Provider[];
|
|
190
|
-
selectedProvider: string | null;
|
|
191
|
-
apiKey: string;
|
|
192
|
-
saving: boolean;
|
|
193
|
-
testing: boolean;
|
|
194
|
-
error: string | null;
|
|
195
|
-
success: string | null;
|
|
196
|
-
onSelectProvider: (id: string | null) => void;
|
|
197
|
-
onApiKeyChange: (key: string) => void;
|
|
198
|
-
onSaveKey: () => void;
|
|
199
|
-
onContinue: () => void;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function Step1AddKeys({
|
|
203
|
-
providers,
|
|
204
|
-
configuredProviders,
|
|
205
|
-
selectedProvider,
|
|
206
|
-
apiKey,
|
|
207
|
-
saving,
|
|
208
|
-
testing,
|
|
209
|
-
error,
|
|
210
|
-
success,
|
|
211
|
-
onSelectProvider,
|
|
212
|
-
onApiKeyChange,
|
|
213
|
-
onSaveKey,
|
|
214
|
-
onContinue,
|
|
215
|
-
}: Step1Props) {
|
|
216
|
-
const selectedProviderData = providers.find(p => p.id === selectedProvider);
|
|
217
|
-
|
|
218
|
-
// When a provider is selected, show focused view with just that provider
|
|
219
|
-
if (selectedProvider && selectedProviderData) {
|
|
220
|
-
return (
|
|
221
|
-
<>
|
|
222
|
-
<h2 className="text-2xl font-semibold mb-2">Add {selectedProviderData.name} Key</h2>
|
|
223
|
-
<p className="text-[var(--color-text-muted)] mb-6">
|
|
224
|
-
Enter your API key below. It will be encrypted and stored locally.
|
|
225
|
-
</p>
|
|
226
|
-
|
|
227
|
-
<div className="mb-6">
|
|
228
|
-
<div className="p-4 rounded border border-[var(--color-accent)] bg-[var(--color-accent-5)] mb-4">
|
|
229
|
-
<div className="flex items-center justify-between">
|
|
230
|
-
<div>
|
|
231
|
-
<p className="font-medium">{selectedProviderData.name}</p>
|
|
232
|
-
<p className="text-sm text-[var(--color-text-muted)]">
|
|
233
|
-
{selectedProviderData.models.length} models available
|
|
234
|
-
</p>
|
|
235
|
-
</div>
|
|
236
|
-
<a
|
|
237
|
-
href={selectedProviderData.docsUrl}
|
|
238
|
-
target="_blank"
|
|
239
|
-
rel="noopener noreferrer"
|
|
240
|
-
className="text-sm text-[#3b82f6] hover:underline"
|
|
241
|
-
>
|
|
242
|
-
Get API Key
|
|
243
|
-
</a>
|
|
244
|
-
</div>
|
|
245
|
-
</div>
|
|
246
|
-
|
|
247
|
-
<div className="space-y-3">
|
|
248
|
-
<input
|
|
249
|
-
type="password"
|
|
250
|
-
value={apiKey}
|
|
251
|
-
onChange={e => onApiKeyChange(e.target.value)}
|
|
252
|
-
placeholder="Enter your API key..."
|
|
253
|
-
autoFocus
|
|
254
|
-
className="w-full bg-[var(--color-bg)] border border-[var(--color-border-light)] rounded px-4 py-3 focus:outline-none focus:border-[var(--color-accent)] text-lg"
|
|
255
|
-
/>
|
|
256
|
-
{error && <p className="text-red-400 text-sm">{error}</p>}
|
|
257
|
-
{success && <p className="text-green-400 text-sm">{success}</p>}
|
|
258
|
-
</div>
|
|
259
|
-
</div>
|
|
260
|
-
|
|
261
|
-
<div className="flex gap-3">
|
|
262
|
-
<button
|
|
263
|
-
onClick={() => {
|
|
264
|
-
onSelectProvider(null);
|
|
265
|
-
onApiKeyChange("");
|
|
266
|
-
}}
|
|
267
|
-
className="flex-1 border border-[var(--color-border-light)] hover:border-[var(--color-text-muted)] px-4 py-3 rounded font-medium transition"
|
|
268
|
-
>
|
|
269
|
-
Back
|
|
270
|
-
</button>
|
|
271
|
-
<button
|
|
272
|
-
onClick={onSaveKey}
|
|
273
|
-
disabled={!apiKey || saving}
|
|
274
|
-
className="flex-1 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] disabled:opacity-50 disabled:cursor-not-allowed text-black px-4 py-3 rounded font-medium transition"
|
|
275
|
-
>
|
|
276
|
-
{testing ? "Testing..." : saving ? "Saving..." : "Save API Key"}
|
|
277
|
-
</button>
|
|
278
|
-
</div>
|
|
279
|
-
</>
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Default view: show all providers
|
|
284
|
-
return (
|
|
285
|
-
<>
|
|
286
|
-
<h2 className="text-2xl font-semibold mb-2">Welcome to apteva</h2>
|
|
287
|
-
<p className="text-[var(--color-text-muted)] mb-6">
|
|
288
|
-
To get started, you'll need to add at least one AI provider API key.
|
|
289
|
-
Your keys are encrypted and stored locally.
|
|
290
|
-
</p>
|
|
291
|
-
|
|
292
|
-
<div className="space-y-3 mb-6">
|
|
293
|
-
{providers.map(provider => (
|
|
294
|
-
<ProviderCard
|
|
295
|
-
key={provider.id}
|
|
296
|
-
provider={provider}
|
|
297
|
-
selected={false}
|
|
298
|
-
onSelect={() => !provider.hasKey && onSelectProvider(provider.id)}
|
|
299
|
-
/>
|
|
300
|
-
))}
|
|
301
|
-
</div>
|
|
302
|
-
|
|
303
|
-
<button
|
|
304
|
-
onClick={onContinue}
|
|
305
|
-
disabled={configuredProviders.length === 0}
|
|
306
|
-
className="w-full bg-[var(--color-surface-raised)] hover:bg-[var(--color-surface-raised)] disabled:opacity-50 disabled:cursor-not-allowed px-4 py-3 rounded font-medium transition"
|
|
307
|
-
>
|
|
308
|
-
{configuredProviders.length === 0
|
|
309
|
-
? "Add at least one API key to continue"
|
|
310
|
-
: `Continue with ${configuredProviders.length} provider${configuredProviders.length > 1 ? 's' : ''}`
|
|
311
|
-
}
|
|
312
|
-
</button>
|
|
313
|
-
</>
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
interface ProviderCardProps {
|
|
318
|
-
provider: Provider;
|
|
319
|
-
selected: boolean;
|
|
320
|
-
onSelect: () => void;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
function ProviderCard({ provider, selected, onSelect }: ProviderCardProps) {
|
|
324
|
-
return (
|
|
325
|
-
<div
|
|
326
|
-
onClick={onSelect}
|
|
327
|
-
className={`p-4 rounded border transition cursor-pointer ${
|
|
328
|
-
provider.hasKey
|
|
329
|
-
? 'border-green-500/30 bg-green-500/5'
|
|
330
|
-
: selected
|
|
331
|
-
? 'border-[var(--color-accent)] bg-[var(--color-accent-5)]'
|
|
332
|
-
: 'border-[var(--color-border-light)] hover:border-[var(--color-border-light)]'
|
|
333
|
-
}`}
|
|
334
|
-
>
|
|
335
|
-
<div className="flex items-center justify-between">
|
|
336
|
-
<div>
|
|
337
|
-
<p className="font-medium">{provider.name}</p>
|
|
338
|
-
<p className="text-sm text-[var(--color-text-muted)]">
|
|
339
|
-
{provider.models.length} models available
|
|
340
|
-
</p>
|
|
341
|
-
</div>
|
|
342
|
-
{provider.hasKey ? (
|
|
343
|
-
<span className="text-green-400 text-sm flex items-center gap-1">
|
|
344
|
-
<CheckIcon />
|
|
345
|
-
Configured ({provider.keyHint})
|
|
346
|
-
</span>
|
|
347
|
-
) : (
|
|
348
|
-
<a
|
|
349
|
-
href={provider.docsUrl}
|
|
350
|
-
target="_blank"
|
|
351
|
-
rel="noopener noreferrer"
|
|
352
|
-
onClick={e => e.stopPropagation()}
|
|
353
|
-
className="text-sm text-[#3b82f6] hover:underline"
|
|
354
|
-
>
|
|
355
|
-
Get API Key
|
|
356
|
-
</a>
|
|
357
|
-
)}
|
|
358
|
-
</div>
|
|
359
|
-
</div>
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
interface Step2Props {
|
|
364
|
-
configuredProviders: Provider[];
|
|
365
|
-
onAddMore: () => void;
|
|
366
|
-
onComplete: () => void;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
function Step2Complete({ configuredProviders, onAddMore, onComplete }: Step2Props) {
|
|
370
|
-
return (
|
|
371
|
-
<>
|
|
372
|
-
<h2 className="text-2xl font-semibold mb-2">You're all set!</h2>
|
|
373
|
-
<p className="text-[var(--color-text-muted)] mb-6">
|
|
374
|
-
You've configured {configuredProviders.length} provider{configuredProviders.length > 1 ? 's' : ''}.
|
|
375
|
-
You can add more providers later in Settings.
|
|
376
|
-
</p>
|
|
377
|
-
|
|
378
|
-
<div className="space-y-2 mb-6">
|
|
379
|
-
{configuredProviders.map(provider => (
|
|
380
|
-
<div key={provider.id} className="flex items-center gap-3 p-3 bg-[var(--color-bg)] rounded">
|
|
381
|
-
<CheckIcon className="w-5 h-5 text-green-400" />
|
|
382
|
-
<span>{provider.name}</span>
|
|
383
|
-
<span className="text-[var(--color-text-muted)] text-sm">({provider.keyHint})</span>
|
|
384
|
-
</div>
|
|
385
|
-
))}
|
|
386
|
-
</div>
|
|
387
|
-
|
|
388
|
-
<div className="flex gap-3">
|
|
389
|
-
<button
|
|
390
|
-
onClick={onAddMore}
|
|
391
|
-
className="flex-1 border border-[var(--color-border-light)] hover:border-[var(--color-accent)] px-4 py-3 rounded font-medium transition"
|
|
392
|
-
>
|
|
393
|
-
Add More
|
|
394
|
-
</button>
|
|
395
|
-
<button
|
|
396
|
-
onClick={onComplete}
|
|
397
|
-
className="flex-1 bg-[var(--color-accent)] hover:bg-[var(--color-accent-hover)] text-black px-4 py-3 rounded font-medium transition"
|
|
398
|
-
>
|
|
399
|
-
Start Using apteva
|
|
400
|
-
</button>
|
|
401
|
-
</div>
|
|
402
|
-
</>
|
|
403
|
-
);
|
|
404
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { OnboardingWizard } from "./OnboardingWizard";
|