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.
Files changed (142) hide show
  1. package/README.md +216 -54
  2. package/cli.js +35 -0
  3. package/install.js +92 -0
  4. package/package.json +12 -79
  5. package/LICENSE +0 -63
  6. package/bin/apteva.js +0 -196
  7. package/dist/ActivityPage.kxzzb4yc.js +0 -3
  8. package/dist/ApiDocsPage.zq998hbm.js +0 -4
  9. package/dist/App.55rea8mn.js +0 -61
  10. package/dist/App.5ywb23z4.js +0 -53
  11. package/dist/App.6thds120.js +0 -4
  12. package/dist/App.9tctxzqm.js +0 -8
  13. package/dist/App.a8r8ttaz.js +0 -4
  14. package/dist/App.agsv5bje.js +0 -4
  15. package/dist/App.cepapqmx.js +0 -4
  16. package/dist/App.dp041gb3.js +0 -221
  17. package/dist/App.fds72zb5.js +0 -4
  18. package/dist/App.fg9qj2dq.js +0 -4
  19. package/dist/App.ndfejbm9.js +0 -4
  20. package/dist/App.nxmfmq1h.js +0 -13
  21. package/dist/App.qdfyt8ba.js +0 -4
  22. package/dist/App.x2d0ygt6.js +0 -4
  23. package/dist/App.yt9p4nr3.js +0 -20
  24. package/dist/App.zn4mw16t.js +0 -1
  25. package/dist/ConnectionsPage.8r96ryw7.js +0 -3
  26. package/dist/McpPage.3cwh0gnd.js +0 -3
  27. package/dist/SettingsPage.ykgdh5ev.js +0 -3
  28. package/dist/SkillsPage.4np1s65b.js +0 -3
  29. package/dist/TasksPage.4g08t7p6.js +0 -3
  30. package/dist/TelemetryPage.72w9pwcp.js +0 -3
  31. package/dist/TestsPage.z4fk3r7r.js +0 -3
  32. package/dist/ThreadsPage.63tcajeh.js +0 -3
  33. package/dist/apteva-kit.css +0 -1
  34. package/dist/icon.png +0 -0
  35. package/dist/index.html +0 -16
  36. package/dist/styles.css +0 -1
  37. package/scripts/postinstall.mjs +0 -102
  38. package/src/auth/index.ts +0 -394
  39. package/src/auth/middleware.ts +0 -213
  40. package/src/binary.ts +0 -536
  41. package/src/channels/index.ts +0 -40
  42. package/src/channels/telegram.ts +0 -311
  43. package/src/crypto.ts +0 -301
  44. package/src/db-tests.ts +0 -174
  45. package/src/db.ts +0 -3133
  46. package/src/integrations/agentdojo.ts +0 -559
  47. package/src/integrations/composio.ts +0 -437
  48. package/src/integrations/index.ts +0 -87
  49. package/src/integrations/skillsmp.ts +0 -318
  50. package/src/mcp-client.ts +0 -605
  51. package/src/mcp-handler.ts +0 -394
  52. package/src/mcp-platform.ts +0 -2403
  53. package/src/openapi.ts +0 -2410
  54. package/src/providers.ts +0 -597
  55. package/src/routes/api/agent-utils.ts +0 -890
  56. package/src/routes/api/agents.ts +0 -916
  57. package/src/routes/api/api-keys.ts +0 -95
  58. package/src/routes/api/channels.ts +0 -182
  59. package/src/routes/api/helpers.ts +0 -12
  60. package/src/routes/api/integrations.ts +0 -639
  61. package/src/routes/api/mcp.ts +0 -574
  62. package/src/routes/api/meta-agent.ts +0 -195
  63. package/src/routes/api/projects.ts +0 -112
  64. package/src/routes/api/providers.ts +0 -424
  65. package/src/routes/api/skills.ts +0 -537
  66. package/src/routes/api/system.ts +0 -333
  67. package/src/routes/api/telemetry.ts +0 -203
  68. package/src/routes/api/tests.ts +0 -148
  69. package/src/routes/api/triggers.ts +0 -518
  70. package/src/routes/api/users.ts +0 -148
  71. package/src/routes/api/webhooks.ts +0 -171
  72. package/src/routes/api.ts +0 -53
  73. package/src/routes/auth.ts +0 -251
  74. package/src/routes/share.ts +0 -86
  75. package/src/routes/static.ts +0 -131
  76. package/src/server.ts +0 -642
  77. package/src/test-runner.ts +0 -598
  78. package/src/triggers/agentdojo.ts +0 -253
  79. package/src/triggers/composio.ts +0 -264
  80. package/src/triggers/index.ts +0 -71
  81. package/src/tui/AgentList.tsx +0 -145
  82. package/src/tui/App.tsx +0 -102
  83. package/src/tui/Login.tsx +0 -104
  84. package/src/tui/api.ts +0 -72
  85. package/src/tui/index.tsx +0 -7
  86. package/src/web/App.tsx +0 -455
  87. package/src/web/components/activity/ActivityPage.tsx +0 -314
  88. package/src/web/components/activity/index.ts +0 -1
  89. package/src/web/components/agents/AgentCard.tsx +0 -189
  90. package/src/web/components/agents/AgentPanel.tsx +0 -2244
  91. package/src/web/components/agents/AgentsView.tsx +0 -180
  92. package/src/web/components/agents/CreateAgentModal.tsx +0 -475
  93. package/src/web/components/agents/index.ts +0 -4
  94. package/src/web/components/api/ApiDocsPage.tsx +0 -842
  95. package/src/web/components/auth/CreateAccountStep.tsx +0 -176
  96. package/src/web/components/auth/LoginPage.tsx +0 -91
  97. package/src/web/components/auth/index.ts +0 -2
  98. package/src/web/components/common/Icons.tsx +0 -250
  99. package/src/web/components/common/LoadingSpinner.tsx +0 -44
  100. package/src/web/components/common/Modal.tsx +0 -199
  101. package/src/web/components/common/Select.tsx +0 -97
  102. package/src/web/components/common/index.ts +0 -20
  103. package/src/web/components/connections/ConnectionsPage.tsx +0 -54
  104. package/src/web/components/connections/IntegrationsTab.tsx +0 -170
  105. package/src/web/components/connections/OverviewTab.tsx +0 -137
  106. package/src/web/components/connections/TriggersTab.tsx +0 -1346
  107. package/src/web/components/dashboard/Dashboard.tsx +0 -572
  108. package/src/web/components/dashboard/index.ts +0 -1
  109. package/src/web/components/index.ts +0 -21
  110. package/src/web/components/layout/ErrorBanner.tsx +0 -18
  111. package/src/web/components/layout/Header.tsx +0 -332
  112. package/src/web/components/layout/Sidebar.tsx +0 -231
  113. package/src/web/components/layout/index.ts +0 -3
  114. package/src/web/components/mcp/IntegrationsPanel.tsx +0 -857
  115. package/src/web/components/mcp/McpPage.tsx +0 -2515
  116. package/src/web/components/mcp/index.ts +0 -1
  117. package/src/web/components/meta-agent/MetaAgent.tsx +0 -245
  118. package/src/web/components/onboarding/OnboardingWizard.tsx +0 -404
  119. package/src/web/components/onboarding/index.ts +0 -1
  120. package/src/web/components/settings/SettingsPage.tsx +0 -2776
  121. package/src/web/components/settings/index.ts +0 -1
  122. package/src/web/components/skills/SkillsPage.tsx +0 -1200
  123. package/src/web/components/tasks/TasksPage.tsx +0 -1116
  124. package/src/web/components/tasks/index.ts +0 -1
  125. package/src/web/components/telemetry/TelemetryPage.tsx +0 -1129
  126. package/src/web/components/tests/TestsPage.tsx +0 -594
  127. package/src/web/components/threads/ThreadsPage.tsx +0 -315
  128. package/src/web/context/AuthContext.tsx +0 -242
  129. package/src/web/context/ProjectContext.tsx +0 -214
  130. package/src/web/context/TelemetryContext.tsx +0 -299
  131. package/src/web/context/ThemeContext.tsx +0 -90
  132. package/src/web/context/UIModeContext.tsx +0 -49
  133. package/src/web/context/index.ts +0 -12
  134. package/src/web/hooks/index.ts +0 -3
  135. package/src/web/hooks/useAgents.ts +0 -115
  136. package/src/web/hooks/useOnboarding.ts +0 -20
  137. package/src/web/hooks/useProviders.ts +0 -75
  138. package/src/web/icon.png +0 -0
  139. package/src/web/index.html +0 -16
  140. package/src/web/styles.css +0 -118
  141. package/src/web/themes.ts +0 -162
  142. 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">&gt;_</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";