apteva 0.2.11 → 0.3.6
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.mvbdnw89.js +227 -0
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/auth/index.ts +11 -3
- package/src/auth/middleware.ts +1 -0
- package/src/crypto.ts +4 -0
- package/src/db.ts +437 -14
- package/src/integrations/skillsmp.ts +318 -0
- package/src/providers.ts +21 -0
- package/src/routes/api.ts +836 -16
- package/src/server.ts +58 -7
- package/src/web/App.tsx +24 -8
- package/src/web/components/agents/AgentCard.tsx +36 -11
- package/src/web/components/agents/AgentPanel.tsx +333 -24
- package/src/web/components/agents/AgentsView.tsx +1 -1
- package/src/web/components/agents/CreateAgentModal.tsx +169 -23
- package/src/web/components/common/Icons.tsx +8 -0
- package/src/web/components/common/index.ts +1 -0
- package/src/web/components/index.ts +1 -0
- package/src/web/components/layout/Header.tsx +4 -2
- package/src/web/components/layout/Sidebar.tsx +7 -1
- package/src/web/components/mcp/McpPage.tsx +602 -19
- package/src/web/components/meta-agent/MetaAgent.tsx +222 -0
- package/src/web/components/settings/SettingsPage.tsx +212 -150
- package/src/web/components/skills/SkillsPage.tsx +871 -0
- package/src/web/context/AuthContext.tsx +5 -0
- package/src/web/context/ProjectContext.tsx +26 -4
- package/src/web/types.ts +48 -3
- package/dist/App.44ge5b89.js +0 -218
|
@@ -4,7 +4,8 @@ import { CloseIcon, MemoryIcon, TasksIcon, VisionIcon, OperatorIcon, McpIcon, Re
|
|
|
4
4
|
import { Select } from "../common/Select";
|
|
5
5
|
import { useConfirm } from "../common/Modal";
|
|
6
6
|
import { useAuth } from "../../context";
|
|
7
|
-
import type { Agent, Provider, AgentFeatures, McpServer } from "../../types";
|
|
7
|
+
import type { Agent, Provider, AgentFeatures, McpServer, SkillSummary, AgentMode, MultiAgentConfig } from "../../types";
|
|
8
|
+
import { getMultiAgentConfig } from "../../types";
|
|
8
9
|
|
|
9
10
|
type Tab = "chat" | "threads" | "tasks" | "memory" | "files" | "settings";
|
|
10
11
|
|
|
@@ -885,25 +886,41 @@ function FilesTab({ agent }: { agent: Agent }) {
|
|
|
885
886
|
);
|
|
886
887
|
}
|
|
887
888
|
|
|
889
|
+
interface AvailableSkill {
|
|
890
|
+
id: string;
|
|
891
|
+
name: string;
|
|
892
|
+
description: string;
|
|
893
|
+
version: string;
|
|
894
|
+
enabled: boolean;
|
|
895
|
+
project_id: string | null;
|
|
896
|
+
}
|
|
897
|
+
|
|
888
898
|
function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
889
899
|
agent: Agent;
|
|
890
900
|
providers: Provider[];
|
|
891
901
|
onUpdateAgent: (updates: Partial<Agent>) => Promise<{ error?: string }>;
|
|
892
902
|
onDeleteAgent: () => void;
|
|
893
903
|
}) {
|
|
894
|
-
const { authFetch } = useAuth();
|
|
904
|
+
const { authFetch, isDev } = useAuth();
|
|
895
905
|
const [form, setForm] = useState({
|
|
896
906
|
name: agent.name,
|
|
897
907
|
provider: agent.provider,
|
|
898
908
|
model: agent.model,
|
|
899
909
|
systemPrompt: agent.systemPrompt,
|
|
900
|
-
features: {
|
|
910
|
+
features: {
|
|
911
|
+
...agent.features,
|
|
912
|
+
builtinTools: agent.features.builtinTools || { webSearch: false, webFetch: false },
|
|
913
|
+
},
|
|
901
914
|
mcpServers: [...(agent.mcpServers || [])],
|
|
915
|
+
skills: [...(agent.skills || [])],
|
|
902
916
|
});
|
|
903
917
|
const [saving, setSaving] = useState(false);
|
|
904
918
|
const [confirmDelete, setConfirmDelete] = useState(false);
|
|
905
919
|
const [message, setMessage] = useState<{ type: "success" | "error"; text: string } | null>(null);
|
|
906
920
|
const [availableMcpServers, setAvailableMcpServers] = useState<McpServer[]>([]);
|
|
921
|
+
const [availableSkills, setAvailableSkills] = useState<AvailableSkill[]>([]);
|
|
922
|
+
const [apiKey, setApiKey] = useState<string | null>(null);
|
|
923
|
+
const [showApiKey, setShowApiKey] = useState(false);
|
|
907
924
|
|
|
908
925
|
// Fetch available MCP servers
|
|
909
926
|
useEffect(() => {
|
|
@@ -919,6 +936,37 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
919
936
|
fetchMcpServers();
|
|
920
937
|
}, [authFetch]);
|
|
921
938
|
|
|
939
|
+
// Fetch API key (dev mode only)
|
|
940
|
+
useEffect(() => {
|
|
941
|
+
if (!isDev) return;
|
|
942
|
+
const fetchApiKey = async () => {
|
|
943
|
+
try {
|
|
944
|
+
const res = await authFetch(`/api/agents/${agent.id}/api-key`);
|
|
945
|
+
if (res.ok) {
|
|
946
|
+
const data = await res.json();
|
|
947
|
+
setApiKey(data.apiKey);
|
|
948
|
+
}
|
|
949
|
+
} catch (e) {
|
|
950
|
+
// Ignore - not critical
|
|
951
|
+
}
|
|
952
|
+
};
|
|
953
|
+
fetchApiKey();
|
|
954
|
+
}, [agent.id, isDev, authFetch]);
|
|
955
|
+
|
|
956
|
+
// Fetch available skills
|
|
957
|
+
useEffect(() => {
|
|
958
|
+
const fetchSkills = async () => {
|
|
959
|
+
try {
|
|
960
|
+
const res = await authFetch("/api/skills");
|
|
961
|
+
const data = await res.json();
|
|
962
|
+
setAvailableSkills(data.skills || []);
|
|
963
|
+
} catch (e) {
|
|
964
|
+
console.error("Failed to fetch skills:", e);
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
fetchSkills();
|
|
968
|
+
}, [authFetch]);
|
|
969
|
+
|
|
922
970
|
// Reset form when agent changes
|
|
923
971
|
useEffect(() => {
|
|
924
972
|
setForm({
|
|
@@ -926,8 +974,12 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
926
974
|
provider: agent.provider,
|
|
927
975
|
model: agent.model,
|
|
928
976
|
systemPrompt: agent.systemPrompt,
|
|
929
|
-
features: {
|
|
977
|
+
features: {
|
|
978
|
+
...agent.features,
|
|
979
|
+
builtinTools: agent.features.builtinTools || { webSearch: false, webFetch: false },
|
|
980
|
+
},
|
|
930
981
|
mcpServers: [...(agent.mcpServers || [])],
|
|
982
|
+
skills: [...(agent.skills || [])],
|
|
931
983
|
});
|
|
932
984
|
setMessage(null);
|
|
933
985
|
}, [agent.id]);
|
|
@@ -951,10 +1003,63 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
951
1003
|
};
|
|
952
1004
|
|
|
953
1005
|
const toggleFeature = (key: keyof AgentFeatures) => {
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1006
|
+
if (key === "agents") {
|
|
1007
|
+
// Special handling for agents feature - convert to MultiAgentConfig
|
|
1008
|
+
const current = prev => {
|
|
1009
|
+
const agentConfig = getMultiAgentConfig(prev.features, agent.projectId);
|
|
1010
|
+
return agentConfig.enabled;
|
|
1011
|
+
};
|
|
1012
|
+
setForm(prev => {
|
|
1013
|
+
const isEnabled = typeof prev.features.agents === "boolean"
|
|
1014
|
+
? prev.features.agents
|
|
1015
|
+
: (prev.features.agents as MultiAgentConfig)?.enabled ?? false;
|
|
1016
|
+
if (isEnabled) {
|
|
1017
|
+
// Turning off - set to false
|
|
1018
|
+
return { ...prev, features: { ...prev.features, agents: false } };
|
|
1019
|
+
} else {
|
|
1020
|
+
// Turning on - set to config with defaults
|
|
1021
|
+
return {
|
|
1022
|
+
...prev,
|
|
1023
|
+
features: {
|
|
1024
|
+
...prev.features,
|
|
1025
|
+
agents: { enabled: true, mode: "worker" as AgentMode, group: agent.projectId || undefined },
|
|
1026
|
+
},
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
});
|
|
1030
|
+
} else {
|
|
1031
|
+
setForm(prev => ({
|
|
1032
|
+
...prev,
|
|
1033
|
+
features: { ...prev.features, [key]: !prev.features[key] },
|
|
1034
|
+
}));
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
|
|
1038
|
+
// Set multi-agent mode
|
|
1039
|
+
const setAgentMode = (mode: AgentMode) => {
|
|
1040
|
+
setForm(prev => {
|
|
1041
|
+
const currentConfig = getMultiAgentConfig(prev.features, agent.projectId);
|
|
1042
|
+
return {
|
|
1043
|
+
...prev,
|
|
1044
|
+
features: {
|
|
1045
|
+
...prev.features,
|
|
1046
|
+
agents: { ...currentConfig, enabled: true, mode },
|
|
1047
|
+
},
|
|
1048
|
+
};
|
|
1049
|
+
});
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1052
|
+
// Helper to check if agents feature is enabled
|
|
1053
|
+
const isAgentsEnabled = () => {
|
|
1054
|
+
const agentsVal = form.features.agents;
|
|
1055
|
+
if (typeof agentsVal === "boolean") return agentsVal;
|
|
1056
|
+
return (agentsVal as MultiAgentConfig)?.enabled ?? false;
|
|
1057
|
+
};
|
|
1058
|
+
|
|
1059
|
+
// Get current agent mode
|
|
1060
|
+
const getAgentMode = (): AgentMode => {
|
|
1061
|
+
const config = getMultiAgentConfig(form.features, agent.projectId);
|
|
1062
|
+
return config.mode || "worker";
|
|
958
1063
|
};
|
|
959
1064
|
|
|
960
1065
|
const toggleMcpServer = (serverId: string) => {
|
|
@@ -966,6 +1071,15 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
966
1071
|
}));
|
|
967
1072
|
};
|
|
968
1073
|
|
|
1074
|
+
const toggleSkill = (skillId: string) => {
|
|
1075
|
+
setForm(prev => ({
|
|
1076
|
+
...prev,
|
|
1077
|
+
skills: prev.skills.includes(skillId)
|
|
1078
|
+
? prev.skills.filter(id => id !== skillId)
|
|
1079
|
+
: [...prev.skills, skillId],
|
|
1080
|
+
}));
|
|
1081
|
+
};
|
|
1082
|
+
|
|
969
1083
|
const handleSave = async () => {
|
|
970
1084
|
setSaving(true);
|
|
971
1085
|
setMessage(null);
|
|
@@ -985,7 +1099,8 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
985
1099
|
form.model !== agent.model ||
|
|
986
1100
|
form.systemPrompt !== agent.systemPrompt ||
|
|
987
1101
|
JSON.stringify(form.features) !== JSON.stringify(agent.features) ||
|
|
988
|
-
JSON.stringify(form.mcpServers.sort()) !== JSON.stringify((agent.mcpServers || []).sort())
|
|
1102
|
+
JSON.stringify(form.mcpServers.sort()) !== JSON.stringify((agent.mcpServers || []).sort()) ||
|
|
1103
|
+
JSON.stringify(form.skills.sort()) !== JSON.stringify((agent.skills || []).sort());
|
|
989
1104
|
|
|
990
1105
|
return (
|
|
991
1106
|
<div className="flex-1 overflow-auto p-4">
|
|
@@ -1025,28 +1140,130 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1025
1140
|
|
|
1026
1141
|
<FormField label="Features">
|
|
1027
1142
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
|
1028
|
-
{FEATURE_CONFIG.map(({ key, label, description, icon: Icon }) =>
|
|
1143
|
+
{FEATURE_CONFIG.map(({ key, label, description, icon: Icon }) => {
|
|
1144
|
+
// For agents feature, check the enabled property of the config
|
|
1145
|
+
const isEnabled = key === "agents" ? isAgentsEnabled() : !!form.features[key];
|
|
1146
|
+
return (
|
|
1147
|
+
<button
|
|
1148
|
+
key={key}
|
|
1149
|
+
type="button"
|
|
1150
|
+
onClick={() => toggleFeature(key)}
|
|
1151
|
+
className={`flex items-center gap-3 p-3 rounded border text-left transition ${
|
|
1152
|
+
isEnabled
|
|
1153
|
+
? "border-[#f97316] bg-[#f97316]/10"
|
|
1154
|
+
: "border-[#222] hover:border-[#333]"
|
|
1155
|
+
}`}
|
|
1156
|
+
>
|
|
1157
|
+
<Icon className={`w-5 h-5 flex-shrink-0 ${isEnabled ? "text-[#f97316]" : "text-[#666]"}`} />
|
|
1158
|
+
<div className="flex-1 min-w-0">
|
|
1159
|
+
<div className={`text-sm font-medium ${isEnabled ? "text-[#f97316]" : ""}`}>
|
|
1160
|
+
{label}
|
|
1161
|
+
</div>
|
|
1162
|
+
<div className="text-xs text-[#666]">{description}</div>
|
|
1163
|
+
</div>
|
|
1164
|
+
</button>
|
|
1165
|
+
);
|
|
1166
|
+
})}
|
|
1167
|
+
</div>
|
|
1168
|
+
</FormField>
|
|
1169
|
+
|
|
1170
|
+
{/* Multi-Agent Mode Selection - shown when agents is enabled */}
|
|
1171
|
+
{isAgentsEnabled() && (
|
|
1172
|
+
<FormField label="Multi-Agent Mode">
|
|
1173
|
+
<div className="flex gap-2">
|
|
1029
1174
|
<button
|
|
1030
|
-
key={key}
|
|
1031
1175
|
type="button"
|
|
1032
|
-
onClick={() =>
|
|
1033
|
-
className={`flex
|
|
1034
|
-
|
|
1176
|
+
onClick={() => setAgentMode("coordinator")}
|
|
1177
|
+
className={`flex-1 p-3 rounded border text-left transition ${
|
|
1178
|
+
getAgentMode() === "coordinator"
|
|
1035
1179
|
? "border-[#f97316] bg-[#f97316]/10"
|
|
1036
1180
|
: "border-[#222] hover:border-[#333]"
|
|
1037
1181
|
}`}
|
|
1038
1182
|
>
|
|
1039
|
-
<
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1183
|
+
<div className={`text-sm font-medium ${getAgentMode() === "coordinator" ? "text-[#f97316]" : ""}`}>
|
|
1184
|
+
Coordinator
|
|
1185
|
+
</div>
|
|
1186
|
+
<div className="text-xs text-[#666]">Orchestrates and delegates to other agents</div>
|
|
1187
|
+
</button>
|
|
1188
|
+
<button
|
|
1189
|
+
type="button"
|
|
1190
|
+
onClick={() => setAgentMode("worker")}
|
|
1191
|
+
className={`flex-1 p-3 rounded border text-left transition ${
|
|
1192
|
+
getAgentMode() === "worker"
|
|
1193
|
+
? "border-[#f97316] bg-[#f97316]/10"
|
|
1194
|
+
: "border-[#222] hover:border-[#333]"
|
|
1195
|
+
}`}
|
|
1196
|
+
>
|
|
1197
|
+
<div className={`text-sm font-medium ${getAgentMode() === "worker" ? "text-[#f97316]" : ""}`}>
|
|
1198
|
+
Worker
|
|
1045
1199
|
</div>
|
|
1200
|
+
<div className="text-xs text-[#666]">Receives tasks from coordinators</div>
|
|
1046
1201
|
</button>
|
|
1047
|
-
|
|
1202
|
+
</div>
|
|
1203
|
+
{agent.projectId && (
|
|
1204
|
+
<p className="text-xs text-[#555] mt-2">
|
|
1205
|
+
Group: Using project as agent group
|
|
1206
|
+
</p>
|
|
1207
|
+
)}
|
|
1208
|
+
</FormField>
|
|
1209
|
+
)}
|
|
1210
|
+
|
|
1211
|
+
{/* Agent Built-in Tools - Anthropic only */}
|
|
1212
|
+
{form.provider === "anthropic" && (
|
|
1213
|
+
<FormField label="Agent Built-in Tools">
|
|
1214
|
+
<div className="flex flex-wrap gap-2">
|
|
1215
|
+
<button
|
|
1216
|
+
type="button"
|
|
1217
|
+
onClick={() => setForm(prev => ({
|
|
1218
|
+
...prev,
|
|
1219
|
+
features: {
|
|
1220
|
+
...prev.features,
|
|
1221
|
+
builtinTools: {
|
|
1222
|
+
...prev.features.builtinTools,
|
|
1223
|
+
webSearch: !prev.features.builtinTools?.webSearch,
|
|
1224
|
+
},
|
|
1225
|
+
},
|
|
1226
|
+
}))}
|
|
1227
|
+
className={`flex items-center gap-2 px-3 py-2 rounded border transition ${
|
|
1228
|
+
form.features.builtinTools?.webSearch
|
|
1229
|
+
? "border-[#f97316] bg-[#f97316]/10 text-[#f97316]"
|
|
1230
|
+
: "border-[#222] hover:border-[#333] text-[#888]"
|
|
1231
|
+
}`}
|
|
1232
|
+
>
|
|
1233
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
1234
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
1235
|
+
</svg>
|
|
1236
|
+
<span className="text-sm">Web Search</span>
|
|
1237
|
+
</button>
|
|
1238
|
+
<button
|
|
1239
|
+
type="button"
|
|
1240
|
+
onClick={() => setForm(prev => ({
|
|
1241
|
+
...prev,
|
|
1242
|
+
features: {
|
|
1243
|
+
...prev.features,
|
|
1244
|
+
builtinTools: {
|
|
1245
|
+
...prev.features.builtinTools,
|
|
1246
|
+
webFetch: !prev.features.builtinTools?.webFetch,
|
|
1247
|
+
},
|
|
1248
|
+
},
|
|
1249
|
+
}))}
|
|
1250
|
+
className={`flex items-center gap-2 px-3 py-2 rounded border transition ${
|
|
1251
|
+
form.features.builtinTools?.webFetch
|
|
1252
|
+
? "border-[#f97316] bg-[#f97316]/10 text-[#f97316]"
|
|
1253
|
+
: "border-[#222] hover:border-[#333] text-[#888]"
|
|
1254
|
+
}`}
|
|
1255
|
+
>
|
|
1256
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
1257
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
|
|
1258
|
+
</svg>
|
|
1259
|
+
<span className="text-sm">Web Fetch</span>
|
|
1260
|
+
</button>
|
|
1048
1261
|
</div>
|
|
1262
|
+
<p className="text-xs text-[#555] mt-2">
|
|
1263
|
+
Provider-native tools for real-time web access
|
|
1264
|
+
</p>
|
|
1049
1265
|
</FormField>
|
|
1266
|
+
)}
|
|
1050
1267
|
|
|
1051
1268
|
{/* MCP Server Selection - shown when MCP is enabled */}
|
|
1052
1269
|
{form.features.mcp && (
|
|
@@ -1057,7 +1274,9 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1057
1274
|
</p>
|
|
1058
1275
|
) : (
|
|
1059
1276
|
<div className="space-y-2">
|
|
1060
|
-
{availableMcpServers
|
|
1277
|
+
{availableMcpServers
|
|
1278
|
+
.filter(server => server.project_id === null || server.project_id === agent.projectId)
|
|
1279
|
+
.map(server => {
|
|
1061
1280
|
const isRemote = server.type === "http" && server.url;
|
|
1062
1281
|
const isAvailable = isRemote || server.status === "running";
|
|
1063
1282
|
const serverInfo = isRemote
|
|
@@ -1078,8 +1297,13 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1078
1297
|
isAvailable ? "bg-green-400" : "bg-[#444]"
|
|
1079
1298
|
}`} />
|
|
1080
1299
|
<div className="flex-1 min-w-0">
|
|
1081
|
-
<div className=
|
|
1082
|
-
{server.
|
|
1300
|
+
<div className="flex items-center gap-2">
|
|
1301
|
+
<span className={`text-sm font-medium ${form.mcpServers.includes(server.id) ? "text-[#f97316]" : ""}`}>
|
|
1302
|
+
{server.name}
|
|
1303
|
+
</span>
|
|
1304
|
+
{server.project_id === null && (
|
|
1305
|
+
<span className="text-[10px] text-[#666] bg-[#1a1a1a] px-1.5 py-0.5 rounded">Global</span>
|
|
1306
|
+
)}
|
|
1083
1307
|
</div>
|
|
1084
1308
|
<div className="text-xs text-[#666]">{serverInfo}</div>
|
|
1085
1309
|
</div>
|
|
@@ -1101,6 +1325,50 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1101
1325
|
</FormField>
|
|
1102
1326
|
)}
|
|
1103
1327
|
|
|
1328
|
+
{/* Skills Selection */}
|
|
1329
|
+
<FormField label="Skills">
|
|
1330
|
+
{availableSkills.length === 0 ? (
|
|
1331
|
+
<p className="text-sm text-[#666]">
|
|
1332
|
+
No skills configured. Add skills in the Skills page first.
|
|
1333
|
+
</p>
|
|
1334
|
+
) : (
|
|
1335
|
+
<div className="space-y-2">
|
|
1336
|
+
{availableSkills
|
|
1337
|
+
.filter(s => s.enabled && (s.project_id === null || s.project_id === agent.projectId))
|
|
1338
|
+
.map(skill => (
|
|
1339
|
+
<button
|
|
1340
|
+
key={skill.id}
|
|
1341
|
+
type="button"
|
|
1342
|
+
onClick={() => toggleSkill(skill.id)}
|
|
1343
|
+
className={`w-full flex items-center gap-3 p-3 rounded border text-left transition ${
|
|
1344
|
+
form.skills.includes(skill.id)
|
|
1345
|
+
? "border-[#f97316] bg-[#f97316]/10"
|
|
1346
|
+
: "border-[#222] hover:border-[#333]"
|
|
1347
|
+
}`}
|
|
1348
|
+
>
|
|
1349
|
+
<div className="flex-1 min-w-0">
|
|
1350
|
+
<div className="flex items-center gap-2">
|
|
1351
|
+
<span className={`text-sm font-medium ${form.skills.includes(skill.id) ? "text-[#f97316]" : ""}`}>
|
|
1352
|
+
{skill.name}
|
|
1353
|
+
</span>
|
|
1354
|
+
{skill.project_id === null && (
|
|
1355
|
+
<span className="text-[10px] text-[#666] bg-[#1a1a1a] px-1.5 py-0.5 rounded">Global</span>
|
|
1356
|
+
)}
|
|
1357
|
+
</div>
|
|
1358
|
+
<div className="text-xs text-[#666]">{skill.description}</div>
|
|
1359
|
+
</div>
|
|
1360
|
+
<div className="text-xs px-2 py-0.5 rounded bg-[#222] text-[#666]">
|
|
1361
|
+
v{skill.version}
|
|
1362
|
+
</div>
|
|
1363
|
+
</button>
|
|
1364
|
+
))}
|
|
1365
|
+
<p className="text-xs text-[#666] mt-2">
|
|
1366
|
+
Skills provide reusable instructions for the agent.
|
|
1367
|
+
</p>
|
|
1368
|
+
</div>
|
|
1369
|
+
)}
|
|
1370
|
+
</FormField>
|
|
1371
|
+
|
|
1104
1372
|
{message && (
|
|
1105
1373
|
<div className={`text-sm px-3 py-2 rounded ${
|
|
1106
1374
|
message.type === "success"
|
|
@@ -1125,6 +1393,47 @@ function SettingsTab({ agent, providers, onUpdateAgent, onDeleteAgent }: {
|
|
|
1125
1393
|
</p>
|
|
1126
1394
|
)}
|
|
1127
1395
|
|
|
1396
|
+
{/* Developer Info (dev mode only) */}
|
|
1397
|
+
{isDev && apiKey && (
|
|
1398
|
+
<div className="mt-8 pt-6 border-t border-[#222]">
|
|
1399
|
+
<p className="text-sm text-[#666] mb-3">Developer Info</p>
|
|
1400
|
+
<div className="space-y-2">
|
|
1401
|
+
<div className="flex items-center justify-between">
|
|
1402
|
+
<span className="text-xs text-[#666]">Agent ID</span>
|
|
1403
|
+
<code className="text-xs bg-[#1a1a1a] px-2 py-1 rounded text-[#888]">{agent.id}</code>
|
|
1404
|
+
</div>
|
|
1405
|
+
<div className="flex items-center justify-between">
|
|
1406
|
+
<span className="text-xs text-[#666]">Port</span>
|
|
1407
|
+
<code className="text-xs bg-[#1a1a1a] px-2 py-1 rounded text-[#888]">{agent.port || "N/A"}</code>
|
|
1408
|
+
</div>
|
|
1409
|
+
<div className="flex flex-col gap-1">
|
|
1410
|
+
<div className="flex items-center justify-between">
|
|
1411
|
+
<span className="text-xs text-[#666]">API Key</span>
|
|
1412
|
+
<button
|
|
1413
|
+
onClick={() => setShowApiKey(!showApiKey)}
|
|
1414
|
+
className="text-xs text-[#f97316] hover:text-[#fb923c]"
|
|
1415
|
+
>
|
|
1416
|
+
{showApiKey ? "Hide" : "Show"}
|
|
1417
|
+
</button>
|
|
1418
|
+
</div>
|
|
1419
|
+
{showApiKey && (
|
|
1420
|
+
<code className="text-xs bg-[#1a1a1a] px-2 py-1 rounded text-[#888] break-all">
|
|
1421
|
+
{apiKey}
|
|
1422
|
+
</code>
|
|
1423
|
+
)}
|
|
1424
|
+
</div>
|
|
1425
|
+
{agent.status === "running" && agent.port && (
|
|
1426
|
+
<div className="flex flex-col gap-1 mt-2">
|
|
1427
|
+
<span className="text-xs text-[#666]">Test with curl</span>
|
|
1428
|
+
<code className="text-xs bg-[#1a1a1a] px-2 py-1.5 rounded text-[#666] break-all">
|
|
1429
|
+
curl -H "X-API-Key: {showApiKey ? apiKey : "***"}" http://localhost:{agent.port}/config
|
|
1430
|
+
</code>
|
|
1431
|
+
</div>
|
|
1432
|
+
)}
|
|
1433
|
+
</div>
|
|
1434
|
+
</div>
|
|
1435
|
+
)}
|
|
1436
|
+
|
|
1128
1437
|
{/* Danger Zone */}
|
|
1129
1438
|
<div className="mt-8 pt-6 border-t border-[#222]">
|
|
1130
1439
|
<p className="text-sm text-[#666] mb-3">Danger Zone</p>
|
|
@@ -90,7 +90,7 @@ export function AgentsView({
|
|
|
90
90
|
) : filteredAgents.length === 0 ? (
|
|
91
91
|
<EmptyState onNewAgent={onNewAgent} canCreateAgent={canCreateAgent} hasProjectFilter={currentProjectId !== null} />
|
|
92
92
|
) : (
|
|
93
|
-
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
|
93
|
+
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3 auto-rows-fr">
|
|
94
94
|
{filteredAgents.map((agent) => (
|
|
95
95
|
<AgentCard
|
|
96
96
|
key={agent.id}
|