apteva 0.4.4 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/App.y11xqt9m.js +227 -0
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/db.ts +93 -19
- package/src/integrations/agentdojo.ts +350 -0
- package/src/openapi.ts +195 -0
- package/src/providers.ts +78 -7
- package/src/routes/api/agent-utils.ts +638 -0
- package/src/routes/api/agents.ts +743 -0
- package/src/routes/api/helpers.ts +12 -0
- package/src/routes/api/integrations.ts +608 -0
- package/src/routes/api/mcp.ts +377 -0
- package/src/routes/api/meta-agent.ts +145 -0
- package/src/routes/api/projects.ts +95 -0
- package/src/routes/api/providers.ts +269 -0
- package/src/routes/api/skills.ts +538 -0
- package/src/routes/api/system.ts +215 -0
- package/src/routes/api/telemetry.ts +142 -0
- package/src/routes/api/users.ts +148 -0
- package/src/routes/api.ts +32 -3477
- package/src/server.ts +1 -1
- package/src/web/components/api/ApiDocsPage.tsx +259 -0
- package/src/web/components/mcp/IntegrationsPanel.tsx +15 -8
- package/src/web/components/mcp/McpPage.tsx +458 -174
- package/src/web/components/settings/SettingsPage.tsx +275 -36
- package/src/web/components/skills/SkillsPage.tsx +330 -1
- package/src/web/components/tasks/TasksPage.tsx +187 -58
- package/src/web/context/TelemetryContext.tsx +14 -1
- package/src/web/hooks/useAgents.ts +9 -0
- package/src/web/types.ts +22 -4
- package/dist/App.mbp9atpm.js +0 -227
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
2
|
import { CheckIcon, CloseIcon, PlusIcon } from "../common/Icons";
|
|
3
3
|
import { Modal, useConfirm } from "../common/Modal";
|
|
4
|
+
import { Select } from "../common/Select";
|
|
4
5
|
import { useProjects, useAuth, type Project } from "../../context";
|
|
5
6
|
import type { Provider } from "../../types";
|
|
6
7
|
|
|
@@ -91,6 +92,7 @@ function SettingsNavItem({
|
|
|
91
92
|
|
|
92
93
|
function ProvidersSettings() {
|
|
93
94
|
const { authFetch } = useAuth();
|
|
95
|
+
const { projects, projectsEnabled } = useProjects();
|
|
94
96
|
const [providers, setProviders] = useState<Provider[]>([]);
|
|
95
97
|
const [selectedProvider, setSelectedProvider] = useState<string | null>(null);
|
|
96
98
|
const [apiKey, setApiKey] = useState("");
|
|
@@ -213,7 +215,7 @@ function ProvidersSettings() {
|
|
|
213
215
|
</p>
|
|
214
216
|
</div>
|
|
215
217
|
|
|
216
|
-
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3
|
|
218
|
+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
217
219
|
{llmProviders.map(provider => (
|
|
218
220
|
<ProviderKeyCard
|
|
219
221
|
key={provider.id}
|
|
@@ -251,7 +253,7 @@ function ProvidersSettings() {
|
|
|
251
253
|
</p>
|
|
252
254
|
</div>
|
|
253
255
|
|
|
254
|
-
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3
|
|
256
|
+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
255
257
|
{integrations.map(provider => (
|
|
256
258
|
<IntegrationKeyCard
|
|
257
259
|
key={provider.id}
|
|
@@ -275,6 +277,9 @@ function ProvidersSettings() {
|
|
|
275
277
|
onApiKeyChange={setApiKey}
|
|
276
278
|
onSave={saveKey}
|
|
277
279
|
onDelete={() => deleteKey(provider.id)}
|
|
280
|
+
projectsEnabled={projectsEnabled}
|
|
281
|
+
projects={projects}
|
|
282
|
+
onRefresh={fetchProviders}
|
|
278
283
|
/>
|
|
279
284
|
))}
|
|
280
285
|
</div>
|
|
@@ -932,6 +937,22 @@ function ProviderKeyCard({
|
|
|
932
937
|
);
|
|
933
938
|
}
|
|
934
939
|
|
|
940
|
+
interface IntegrationKey {
|
|
941
|
+
id: string;
|
|
942
|
+
provider_id: string;
|
|
943
|
+
key_hint: string;
|
|
944
|
+
is_valid: boolean;
|
|
945
|
+
project_id: string | null;
|
|
946
|
+
name: string | null;
|
|
947
|
+
created_at: string;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
interface IntegrationKeyCardProps extends ProviderKeyCardProps {
|
|
951
|
+
projectsEnabled: boolean;
|
|
952
|
+
projects: Array<{ id: string; name: string; color: string }>;
|
|
953
|
+
onRefresh: () => void;
|
|
954
|
+
}
|
|
955
|
+
|
|
935
956
|
function IntegrationKeyCard({
|
|
936
957
|
provider,
|
|
937
958
|
isEditing,
|
|
@@ -945,20 +966,195 @@ function IntegrationKeyCard({
|
|
|
945
966
|
onApiKeyChange,
|
|
946
967
|
onSave,
|
|
947
968
|
onDelete,
|
|
948
|
-
|
|
969
|
+
projectsEnabled,
|
|
970
|
+
projects,
|
|
971
|
+
onRefresh,
|
|
972
|
+
}: IntegrationKeyCardProps) {
|
|
973
|
+
const { authFetch } = useAuth();
|
|
974
|
+
const [keys, setKeys] = useState<IntegrationKey[]>([]);
|
|
975
|
+
const [selectedProjectId, setSelectedProjectId] = useState<string>("");
|
|
976
|
+
const [expanded, setExpanded] = useState(false);
|
|
977
|
+
const { confirm, ConfirmDialog } = useConfirm();
|
|
978
|
+
|
|
979
|
+
// Fetch all keys for this provider
|
|
980
|
+
const fetchKeys = async () => {
|
|
981
|
+
try {
|
|
982
|
+
const res = await authFetch(`/api/keys/${provider.id}`);
|
|
983
|
+
const data = await res.json();
|
|
984
|
+
setKeys(data.keys || []);
|
|
985
|
+
} catch (e) {
|
|
986
|
+
console.error("Failed to fetch keys:", e);
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
|
|
990
|
+
useEffect(() => {
|
|
991
|
+
if (projectsEnabled) {
|
|
992
|
+
fetchKeys();
|
|
993
|
+
}
|
|
994
|
+
}, [provider.id, projectsEnabled]);
|
|
995
|
+
|
|
996
|
+
const handleSaveWithProject = async () => {
|
|
997
|
+
if (!apiKey) return;
|
|
998
|
+
|
|
999
|
+
try {
|
|
1000
|
+
const res = await authFetch(`/api/keys/${provider.id}`, {
|
|
1001
|
+
method: "POST",
|
|
1002
|
+
headers: { "Content-Type": "application/json" },
|
|
1003
|
+
body: JSON.stringify({
|
|
1004
|
+
key: apiKey,
|
|
1005
|
+
project_id: selectedProjectId || null,
|
|
1006
|
+
}),
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
if (res.ok) {
|
|
1010
|
+
onApiKeyChange("");
|
|
1011
|
+
setSelectedProjectId("");
|
|
1012
|
+
onCancelEdit();
|
|
1013
|
+
fetchKeys();
|
|
1014
|
+
onRefresh();
|
|
1015
|
+
}
|
|
1016
|
+
} catch (e) {
|
|
1017
|
+
console.error("Failed to save key:", e);
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
|
|
1021
|
+
const handleDeleteKey = async (keyId: string, keyName: string | null) => {
|
|
1022
|
+
const confirmed = await confirm(
|
|
1023
|
+
`Are you sure you want to remove this API key${keyName ? ` (${keyName})` : ""}?`,
|
|
1024
|
+
{ confirmText: "Remove", title: "Remove API Key" }
|
|
1025
|
+
);
|
|
1026
|
+
if (!confirmed) return;
|
|
1027
|
+
|
|
1028
|
+
try {
|
|
1029
|
+
await authFetch(`/api/keys/by-id/${keyId}`, { method: "DELETE" });
|
|
1030
|
+
fetchKeys();
|
|
1031
|
+
onRefresh();
|
|
1032
|
+
} catch (e) {
|
|
1033
|
+
console.error("Failed to delete key:", e);
|
|
1034
|
+
}
|
|
1035
|
+
};
|
|
1036
|
+
|
|
1037
|
+
const globalKey = keys.find(k => !k.project_id);
|
|
1038
|
+
const projectKeys = keys.filter(k => k.project_id);
|
|
1039
|
+
const getProjectName = (projectId: string) => projects.find(p => p.id === projectId)?.name || "Unknown";
|
|
1040
|
+
const getProjectColor = (projectId: string) => projects.find(p => p.id === projectId)?.color || "#666";
|
|
1041
|
+
|
|
1042
|
+
// Simple view when projects not enabled
|
|
1043
|
+
if (!projectsEnabled) {
|
|
1044
|
+
return (
|
|
1045
|
+
<div className={`bg-[#111] border rounded-lg p-4 ${
|
|
1046
|
+
provider.hasKey ? 'border-[#f97316]/20' : 'border-[#1a1a1a]'
|
|
1047
|
+
}`}>
|
|
1048
|
+
<div className="flex items-center justify-between mb-2">
|
|
1049
|
+
<div>
|
|
1050
|
+
<h3 className="font-medium">{provider.name}</h3>
|
|
1051
|
+
<p className="text-sm text-[#666]">{provider.description || "MCP integration"}</p>
|
|
1052
|
+
</div>
|
|
1053
|
+
{provider.hasKey ? (
|
|
1054
|
+
<span className="text-[#f97316] text-xs flex items-center gap-1 bg-[#f97316]/10 px-2 py-1 rounded">
|
|
1055
|
+
<CheckIcon className="w-3 h-3" />
|
|
1056
|
+
{provider.keyHint}
|
|
1057
|
+
</span>
|
|
1058
|
+
) : (
|
|
1059
|
+
<span className="text-[#666] text-xs bg-[#1a1a1a] px-2 py-1 rounded">
|
|
1060
|
+
Not configured
|
|
1061
|
+
</span>
|
|
1062
|
+
)}
|
|
1063
|
+
</div>
|
|
1064
|
+
|
|
1065
|
+
<div className="mt-3 pt-3 border-t border-[#1a1a1a]">
|
|
1066
|
+
{isEditing ? (
|
|
1067
|
+
<div className="space-y-3">
|
|
1068
|
+
<input
|
|
1069
|
+
type="password"
|
|
1070
|
+
value={apiKey}
|
|
1071
|
+
onChange={e => onApiKeyChange(e.target.value)}
|
|
1072
|
+
placeholder={provider.hasKey ? "Enter new API key..." : "Enter API key..."}
|
|
1073
|
+
autoFocus
|
|
1074
|
+
className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316]"
|
|
1075
|
+
/>
|
|
1076
|
+
{error && <p className="text-red-400 text-sm">{error}</p>}
|
|
1077
|
+
{success && <p className="text-green-400 text-sm">{success}</p>}
|
|
1078
|
+
<div className="flex gap-2">
|
|
1079
|
+
<button
|
|
1080
|
+
onClick={onCancelEdit}
|
|
1081
|
+
className="flex-1 px-3 py-1.5 border border-[#333] rounded text-sm hover:border-[#666]"
|
|
1082
|
+
>
|
|
1083
|
+
Cancel
|
|
1084
|
+
</button>
|
|
1085
|
+
<button
|
|
1086
|
+
onClick={onSave}
|
|
1087
|
+
disabled={!apiKey || saving}
|
|
1088
|
+
className="flex-1 px-3 py-1.5 bg-[#f97316] text-black rounded text-sm font-medium disabled:opacity-50"
|
|
1089
|
+
>
|
|
1090
|
+
{testing ? "Validating..." : saving ? "Saving..." : "Save"}
|
|
1091
|
+
</button>
|
|
1092
|
+
</div>
|
|
1093
|
+
</div>
|
|
1094
|
+
) : provider.hasKey ? (
|
|
1095
|
+
<div className="flex items-center justify-between">
|
|
1096
|
+
<a
|
|
1097
|
+
href={provider.docsUrl}
|
|
1098
|
+
target="_blank"
|
|
1099
|
+
rel="noopener noreferrer"
|
|
1100
|
+
className="text-sm text-[#3b82f6] hover:underline"
|
|
1101
|
+
>
|
|
1102
|
+
View docs
|
|
1103
|
+
</a>
|
|
1104
|
+
<div className="flex items-center gap-3">
|
|
1105
|
+
<button
|
|
1106
|
+
onClick={onStartEdit}
|
|
1107
|
+
className="text-sm text-[#888] hover:text-[#e0e0e0]"
|
|
1108
|
+
>
|
|
1109
|
+
Update key
|
|
1110
|
+
</button>
|
|
1111
|
+
<button
|
|
1112
|
+
onClick={onDelete}
|
|
1113
|
+
className="text-red-400 hover:text-red-300 text-sm"
|
|
1114
|
+
>
|
|
1115
|
+
Remove
|
|
1116
|
+
</button>
|
|
1117
|
+
</div>
|
|
1118
|
+
</div>
|
|
1119
|
+
) : (
|
|
1120
|
+
<div className="flex items-center justify-between">
|
|
1121
|
+
<a
|
|
1122
|
+
href={provider.docsUrl}
|
|
1123
|
+
target="_blank"
|
|
1124
|
+
rel="noopener noreferrer"
|
|
1125
|
+
className="text-sm text-[#3b82f6] hover:underline"
|
|
1126
|
+
>
|
|
1127
|
+
Get API key
|
|
1128
|
+
</a>
|
|
1129
|
+
<button
|
|
1130
|
+
onClick={onStartEdit}
|
|
1131
|
+
className="text-sm text-[#f97316] hover:text-[#fb923c]"
|
|
1132
|
+
>
|
|
1133
|
+
+ Add key
|
|
1134
|
+
</button>
|
|
1135
|
+
</div>
|
|
1136
|
+
)}
|
|
1137
|
+
</div>
|
|
1138
|
+
</div>
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
// Enhanced view with project support
|
|
949
1143
|
return (
|
|
1144
|
+
<>
|
|
1145
|
+
{ConfirmDialog}
|
|
950
1146
|
<div className={`bg-[#111] border rounded-lg p-4 ${
|
|
951
|
-
|
|
1147
|
+
keys.length > 0 ? 'border-[#f97316]/20' : 'border-[#1a1a1a]'
|
|
952
1148
|
}`}>
|
|
953
1149
|
<div className="flex items-center justify-between mb-2">
|
|
954
1150
|
<div>
|
|
955
1151
|
<h3 className="font-medium">{provider.name}</h3>
|
|
956
1152
|
<p className="text-sm text-[#666]">{provider.description || "MCP integration"}</p>
|
|
957
1153
|
</div>
|
|
958
|
-
{
|
|
1154
|
+
{keys.length > 0 ? (
|
|
959
1155
|
<span className="text-[#f97316] text-xs flex items-center gap-1 bg-[#f97316]/10 px-2 py-1 rounded">
|
|
960
1156
|
<CheckIcon className="w-3 h-3" />
|
|
961
|
-
{
|
|
1157
|
+
{keys.length} key{keys.length !== 1 ? "s" : ""}
|
|
962
1158
|
</span>
|
|
963
1159
|
) : (
|
|
964
1160
|
<span className="text-[#666] text-xs bg-[#1a1a1a] px-2 py-1 rounded">
|
|
@@ -967,6 +1163,58 @@ function IntegrationKeyCard({
|
|
|
967
1163
|
)}
|
|
968
1164
|
</div>
|
|
969
1165
|
|
|
1166
|
+
{/* Keys List */}
|
|
1167
|
+
{keys.length > 0 && (
|
|
1168
|
+
<div className="mt-3 space-y-2">
|
|
1169
|
+
{/* Global Key */}
|
|
1170
|
+
{globalKey && (
|
|
1171
|
+
<div className="flex items-center justify-between text-sm bg-[#0a0a0a] rounded px-3 py-2">
|
|
1172
|
+
<div className="flex items-center gap-2">
|
|
1173
|
+
<span className="text-[#888]">Global</span>
|
|
1174
|
+
<span className="text-[#555]">·</span>
|
|
1175
|
+
<span className="text-[#666] font-mono text-xs">{globalKey.key_hint}</span>
|
|
1176
|
+
</div>
|
|
1177
|
+
<button
|
|
1178
|
+
onClick={() => handleDeleteKey(globalKey.id, "Global")}
|
|
1179
|
+
className="text-red-400 hover:text-red-300 text-xs"
|
|
1180
|
+
>
|
|
1181
|
+
Remove
|
|
1182
|
+
</button>
|
|
1183
|
+
</div>
|
|
1184
|
+
)}
|
|
1185
|
+
|
|
1186
|
+
{/* Project Keys - show first 2, expand for more */}
|
|
1187
|
+
{projectKeys.slice(0, expanded ? undefined : 2).map(key => (
|
|
1188
|
+
<div key={key.id} className="flex items-center justify-between text-sm bg-[#0a0a0a] rounded px-3 py-2">
|
|
1189
|
+
<div className="flex items-center gap-2 min-w-0">
|
|
1190
|
+
<span
|
|
1191
|
+
className="w-2 h-2 rounded-full flex-shrink-0"
|
|
1192
|
+
style={{ backgroundColor: getProjectColor(key.project_id!) }}
|
|
1193
|
+
/>
|
|
1194
|
+
<span className="text-[#888] truncate">{key.name || getProjectName(key.project_id!)}</span>
|
|
1195
|
+
<span className="text-[#555]">·</span>
|
|
1196
|
+
<span className="text-[#666] font-mono text-xs">{key.key_hint}</span>
|
|
1197
|
+
</div>
|
|
1198
|
+
<button
|
|
1199
|
+
onClick={() => handleDeleteKey(key.id, key.name || getProjectName(key.project_id!))}
|
|
1200
|
+
className="text-red-400 hover:text-red-300 text-xs flex-shrink-0 ml-2"
|
|
1201
|
+
>
|
|
1202
|
+
Remove
|
|
1203
|
+
</button>
|
|
1204
|
+
</div>
|
|
1205
|
+
))}
|
|
1206
|
+
|
|
1207
|
+
{projectKeys.length > 2 && !expanded && (
|
|
1208
|
+
<button
|
|
1209
|
+
onClick={() => setExpanded(true)}
|
|
1210
|
+
className="text-xs text-[#666] hover:text-[#888] w-full text-center py-1"
|
|
1211
|
+
>
|
|
1212
|
+
Show {projectKeys.length - 2} more...
|
|
1213
|
+
</button>
|
|
1214
|
+
)}
|
|
1215
|
+
</div>
|
|
1216
|
+
)}
|
|
1217
|
+
|
|
970
1218
|
<div className="mt-3 pt-3 border-t border-[#1a1a1a]">
|
|
971
1219
|
{isEditing ? (
|
|
972
1220
|
<div className="space-y-3">
|
|
@@ -974,50 +1222,40 @@ function IntegrationKeyCard({
|
|
|
974
1222
|
type="password"
|
|
975
1223
|
value={apiKey}
|
|
976
1224
|
onChange={e => onApiKeyChange(e.target.value)}
|
|
977
|
-
placeholder=
|
|
1225
|
+
placeholder="Enter API key..."
|
|
978
1226
|
autoFocus
|
|
979
1227
|
className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316]"
|
|
980
1228
|
/>
|
|
1229
|
+
|
|
1230
|
+
<Select
|
|
1231
|
+
value={selectedProjectId}
|
|
1232
|
+
onChange={setSelectedProjectId}
|
|
1233
|
+
placeholder="Global (all projects)"
|
|
1234
|
+
options={[
|
|
1235
|
+
{ value: "", label: "Global (all projects)" },
|
|
1236
|
+
...projects.map(p => ({ value: p.id, label: p.name }))
|
|
1237
|
+
]}
|
|
1238
|
+
/>
|
|
1239
|
+
|
|
981
1240
|
{error && <p className="text-red-400 text-sm">{error}</p>}
|
|
982
1241
|
{success && <p className="text-green-400 text-sm">{success}</p>}
|
|
1242
|
+
|
|
983
1243
|
<div className="flex gap-2">
|
|
984
1244
|
<button
|
|
985
|
-
onClick={
|
|
1245
|
+
onClick={() => {
|
|
1246
|
+
onCancelEdit();
|
|
1247
|
+
setSelectedProjectId("");
|
|
1248
|
+
}}
|
|
986
1249
|
className="flex-1 px-3 py-1.5 border border-[#333] rounded text-sm hover:border-[#666]"
|
|
987
1250
|
>
|
|
988
1251
|
Cancel
|
|
989
1252
|
</button>
|
|
990
1253
|
<button
|
|
991
|
-
onClick={
|
|
1254
|
+
onClick={handleSaveWithProject}
|
|
992
1255
|
disabled={!apiKey || saving}
|
|
993
1256
|
className="flex-1 px-3 py-1.5 bg-[#f97316] text-black rounded text-sm font-medium disabled:opacity-50"
|
|
994
1257
|
>
|
|
995
|
-
{
|
|
996
|
-
</button>
|
|
997
|
-
</div>
|
|
998
|
-
</div>
|
|
999
|
-
) : provider.hasKey ? (
|
|
1000
|
-
<div className="flex items-center justify-between">
|
|
1001
|
-
<a
|
|
1002
|
-
href={provider.docsUrl}
|
|
1003
|
-
target="_blank"
|
|
1004
|
-
rel="noopener noreferrer"
|
|
1005
|
-
className="text-sm text-[#3b82f6] hover:underline"
|
|
1006
|
-
>
|
|
1007
|
-
View docs
|
|
1008
|
-
</a>
|
|
1009
|
-
<div className="flex items-center gap-3">
|
|
1010
|
-
<button
|
|
1011
|
-
onClick={onStartEdit}
|
|
1012
|
-
className="text-sm text-[#888] hover:text-[#e0e0e0]"
|
|
1013
|
-
>
|
|
1014
|
-
Update key
|
|
1015
|
-
</button>
|
|
1016
|
-
<button
|
|
1017
|
-
onClick={onDelete}
|
|
1018
|
-
className="text-red-400 hover:text-red-300 text-sm"
|
|
1019
|
-
>
|
|
1020
|
-
Remove
|
|
1258
|
+
{saving ? "Saving..." : "Save"}
|
|
1021
1259
|
</button>
|
|
1022
1260
|
</div>
|
|
1023
1261
|
</div>
|
|
@@ -1029,7 +1267,7 @@ function IntegrationKeyCard({
|
|
|
1029
1267
|
rel="noopener noreferrer"
|
|
1030
1268
|
className="text-sm text-[#3b82f6] hover:underline"
|
|
1031
1269
|
>
|
|
1032
|
-
Get API key
|
|
1270
|
+
{keys.length > 0 ? "View docs" : "Get API key"}
|
|
1033
1271
|
</a>
|
|
1034
1272
|
<button
|
|
1035
1273
|
onClick={onStartEdit}
|
|
@@ -1041,6 +1279,7 @@ function IntegrationKeyCard({
|
|
|
1041
1279
|
)}
|
|
1042
1280
|
</div>
|
|
1043
1281
|
</div>
|
|
1282
|
+
</>
|
|
1044
1283
|
);
|
|
1045
1284
|
}
|
|
1046
1285
|
|