@harbinger-ai/harbinger 0.1.2 → 0.1.4
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/lib/bounty/actions.js +45 -0
- package/lib/chat/actions.js +715 -0
- package/lib/chat/components/agents-page.js +918 -0
- package/lib/chat/components/agents-page.jsx +869 -0
- package/lib/chat/components/app-sidebar.js +17 -1
- package/lib/chat/components/app-sidebar.jsx +19 -1
- package/lib/chat/components/icons.js +145 -0
- package/lib/chat/components/icons.jsx +171 -0
- package/lib/chat/components/index.js +2 -0
- package/lib/chat/components/mcp-page.js +383 -55
- package/lib/chat/components/mcp-page.jsx +404 -101
- package/lib/chat/components/page-layout.js +41 -2
- package/lib/chat/components/page-layout.jsx +40 -2
- package/lib/chat/components/settings-layout.js +3 -2
- package/lib/chat/components/settings-layout.jsx +2 -1
- package/lib/chat/components/settings-providers-page.js +872 -0
- package/lib/chat/components/settings-providers-page.jsx +917 -0
- package/lib/chat/components/settings-secrets-page.js +91 -66
- package/lib/chat/components/settings-secrets-page.jsx +83 -72
- package/lib/chat/components/targets-page.js +554 -96
- package/lib/chat/components/targets-page.jsx +464 -114
- package/lib/mcp/actions.js +120 -0
- package/lib/mcp/registry.js +164 -0
- package/package.json +1 -1
|
@@ -0,0 +1,918 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
|
+
import { motion, AnimatePresence } from "framer-motion";
|
|
5
|
+
import { UsersIcon, RefreshIcon, SpinnerIcon, ChevronDownIcon, PlusIcon, PencilIcon, CheckIcon, XIcon, SearchIcon, CpuIcon, PackageIcon, DatabaseIcon, FolderIcon, ClockIcon, SlidersIcon, TagIcon, HashIcon, PlayIcon } from "./icons.js";
|
|
6
|
+
import { getAgentProfilesWithStatus, getAgentProfile, updateAgentFile, createAgent, createAgentJob, getAvailableMcpTools, getLlmProviders } from "../actions.js";
|
|
7
|
+
const PROVIDER_MODELS = {
|
|
8
|
+
anthropic: ["claude-sonnet-4-20250514", "claude-opus-4-20250514", "claude-haiku-4-5-20251001"],
|
|
9
|
+
openai: ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "o1", "o1-mini", "o3-mini"],
|
|
10
|
+
google: ["gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.0-flash"],
|
|
11
|
+
custom: []
|
|
12
|
+
};
|
|
13
|
+
const ROLE_OPTIONS = ["Recon", "Exploitation", "Reporting", "Infrastructure", "Research", "Analysis", "Development", "Custom"];
|
|
14
|
+
const SPEC_OPTIONS = ["Web", "API", "Mobile", "Cloud", "Network", "Blockchain", "IoT", "Social Engineering", "Custom"];
|
|
15
|
+
const RUN_MODES = ["manual", "scheduled", "continuous", "event-triggered"];
|
|
16
|
+
const SOUL_TEMPLATES = {
|
|
17
|
+
recon: `# {{CODENAME}}
|
|
18
|
+
|
|
19
|
+
You are {{NAME}}, an autonomous reconnaissance specialist.
|
|
20
|
+
|
|
21
|
+
## Mission
|
|
22
|
+
Discover and enumerate all attack surface for the assigned target.
|
|
23
|
+
|
|
24
|
+
## Approach
|
|
25
|
+
- Start with passive reconnaissance
|
|
26
|
+
- Enumerate subdomains, ports, services
|
|
27
|
+
- Identify technologies and frameworks
|
|
28
|
+
- Map the application architecture
|
|
29
|
+
- Document all findings systematically
|
|
30
|
+
|
|
31
|
+
## Personality
|
|
32
|
+
- Methodical and thorough
|
|
33
|
+
- Never skip steps
|
|
34
|
+
- Always verify findings`,
|
|
35
|
+
exploit: `# {{CODENAME}}
|
|
36
|
+
|
|
37
|
+
You are {{NAME}}, an autonomous exploitation specialist.
|
|
38
|
+
|
|
39
|
+
## Mission
|
|
40
|
+
Identify and validate security vulnerabilities in assigned targets.
|
|
41
|
+
|
|
42
|
+
## Approach
|
|
43
|
+
- Analyze attack surface from recon data
|
|
44
|
+
- Test for common vulnerability classes (OWASP Top 10)
|
|
45
|
+
- Develop and validate proof-of-concept exploits
|
|
46
|
+
- Assess impact and severity
|
|
47
|
+
- Document reproduction steps clearly
|
|
48
|
+
|
|
49
|
+
## Personality
|
|
50
|
+
- Creative and persistent
|
|
51
|
+
- Ethical \u2014 never cause damage
|
|
52
|
+
- Report accurately, no exaggeration`,
|
|
53
|
+
report: `# {{CODENAME}}
|
|
54
|
+
|
|
55
|
+
You are {{NAME}}, an autonomous report writer.
|
|
56
|
+
|
|
57
|
+
## Mission
|
|
58
|
+
Create clear, actionable security reports from findings data.
|
|
59
|
+
|
|
60
|
+
## Approach
|
|
61
|
+
- Gather all findings and evidence
|
|
62
|
+
- Assess severity using CVSS
|
|
63
|
+
- Write clear reproduction steps
|
|
64
|
+
- Suggest remediation
|
|
65
|
+
- Format for platform submission
|
|
66
|
+
|
|
67
|
+
## Personality
|
|
68
|
+
- Precise and concise
|
|
69
|
+
- Technical but accessible
|
|
70
|
+
- Focus on impact`,
|
|
71
|
+
custom: `# {{CODENAME}}
|
|
72
|
+
|
|
73
|
+
You are {{NAME}}, a specialized AI agent.
|
|
74
|
+
|
|
75
|
+
## Role
|
|
76
|
+
{{ROLE}}
|
|
77
|
+
|
|
78
|
+
## Specialization
|
|
79
|
+
{{SPEC}}
|
|
80
|
+
|
|
81
|
+
## Mission
|
|
82
|
+
Describe the agent's primary objective here.
|
|
83
|
+
|
|
84
|
+
## Approach
|
|
85
|
+
- Step 1
|
|
86
|
+
- Step 2
|
|
87
|
+
- Step 3`
|
|
88
|
+
};
|
|
89
|
+
function AgentCard({ agent, onViewProfile, onAssignTask, index }) {
|
|
90
|
+
const codename = agent.codename || agent.name || agent.id;
|
|
91
|
+
const initial = codename.charAt(0).toUpperCase();
|
|
92
|
+
const isActive = agent.status === "active";
|
|
93
|
+
return /* @__PURE__ */ jsx(
|
|
94
|
+
motion.div,
|
|
95
|
+
{
|
|
96
|
+
initial: { opacity: 0, y: 8 },
|
|
97
|
+
animate: { opacity: 1, y: 0 },
|
|
98
|
+
transition: { duration: 0.25, delay: index * 0.04 },
|
|
99
|
+
className: `rounded-lg border bg-[--card] transition-all hover:border-[--cyan]/20 ${isActive ? "border-green-500/20 shadow-[0_0_15px_oklch(0.7_0.17_145/8%)]" : "border-white/[0.06]"}`,
|
|
100
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 p-4", children: [
|
|
101
|
+
/* @__PURE__ */ jsx("div", { className: "shrink-0 w-12 h-12 rounded-lg bg-[--cyan]/10 border border-[--cyan]/20 flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "text-xl font-mono font-bold text-[--cyan]", children: initial }) }),
|
|
102
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
103
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
104
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm font-mono font-semibold text-foreground", children: [
|
|
105
|
+
"@",
|
|
106
|
+
codename.toUpperCase()
|
|
107
|
+
] }),
|
|
108
|
+
/* @__PURE__ */ jsx("div", { className: `w-2 h-2 rounded-full shrink-0 ${isActive ? "bg-green-500 animate-pulse" : "bg-muted-foreground/40"}` }),
|
|
109
|
+
isActive && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-green-500/10 text-green-500 border border-green-500/20 px-2 py-0.5 text-[9px] font-mono font-medium", children: [
|
|
110
|
+
agent.activeJobs,
|
|
111
|
+
" job",
|
|
112
|
+
agent.activeJobs !== 1 ? "s" : "",
|
|
113
|
+
" running"
|
|
114
|
+
] })
|
|
115
|
+
] }),
|
|
116
|
+
agent.role && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5 font-mono truncate", children: agent.role }),
|
|
117
|
+
agent.specialization && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground/70 mt-0.5 font-mono truncate", children: agent.specialization })
|
|
118
|
+
] }),
|
|
119
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
120
|
+
/* @__PURE__ */ jsx(
|
|
121
|
+
"button",
|
|
122
|
+
{
|
|
123
|
+
onClick: () => onViewProfile(agent.id),
|
|
124
|
+
className: "inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-xs font-mono font-medium border border-white/[0.06] hover:bg-white/[0.04] hover:border-[--cyan]/30 hover:text-[--cyan] transition-colors",
|
|
125
|
+
children: "View"
|
|
126
|
+
}
|
|
127
|
+
),
|
|
128
|
+
/* @__PURE__ */ jsx(
|
|
129
|
+
"button",
|
|
130
|
+
{
|
|
131
|
+
onClick: () => onAssignTask(agent),
|
|
132
|
+
className: "inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-xs font-mono font-medium bg-[--cyan]/10 text-[--cyan] border border-[--cyan]/20 hover:bg-[--cyan] hover:text-[--primary-foreground] transition-colors",
|
|
133
|
+
children: "Assign Task"
|
|
134
|
+
}
|
|
135
|
+
)
|
|
136
|
+
] })
|
|
137
|
+
] })
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
function AgentProfilePanel({ agentId, onClose }) {
|
|
142
|
+
const [profile, setProfile] = useState(null);
|
|
143
|
+
const [loading, setLoading] = useState(true);
|
|
144
|
+
const [editingFile, setEditingFile] = useState(null);
|
|
145
|
+
const [editContent, setEditContent] = useState("");
|
|
146
|
+
const [saving, setSaving] = useState(false);
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
setLoading(true);
|
|
149
|
+
getAgentProfile(agentId).then((p) => {
|
|
150
|
+
setProfile(p);
|
|
151
|
+
setLoading(false);
|
|
152
|
+
});
|
|
153
|
+
}, [agentId]);
|
|
154
|
+
async function handleSave() {
|
|
155
|
+
if (!editingFile) return;
|
|
156
|
+
setSaving(true);
|
|
157
|
+
await updateAgentFile(agentId, editingFile, editContent);
|
|
158
|
+
const updated = await getAgentProfile(agentId);
|
|
159
|
+
setProfile(updated);
|
|
160
|
+
setEditingFile(null);
|
|
161
|
+
setSaving(false);
|
|
162
|
+
}
|
|
163
|
+
function startEdit(filename, content) {
|
|
164
|
+
setEditingFile(filename);
|
|
165
|
+
setEditContent(content || "");
|
|
166
|
+
}
|
|
167
|
+
if (loading) {
|
|
168
|
+
return /* @__PURE__ */ jsx(motion.div, { initial: { opacity: 0, x: 20 }, animate: { opacity: 1, x: 0 }, className: "rounded-lg border border-[--cyan]/20 bg-[--card] p-5", children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: [...Array(3)].map((_, i) => /* @__PURE__ */ jsx("div", { className: "h-12 animate-shimmer rounded-md border border-white/[0.06]" }, i)) }) });
|
|
169
|
+
}
|
|
170
|
+
if (!profile) return null;
|
|
171
|
+
const codename = profile.codename || profile.name || profile.id;
|
|
172
|
+
const files = [
|
|
173
|
+
{ name: "SOUL.md", content: profile.soul, label: "Soul" },
|
|
174
|
+
{ name: "SKILLS.md", content: profile.skills, label: "Skills" },
|
|
175
|
+
{ name: "TOOLS.md", content: profile.tools, label: "Tools" },
|
|
176
|
+
{ name: "HEARTBEAT.md", content: profile.heartbeat, label: "Heartbeat" },
|
|
177
|
+
{ name: "IDENTITY.md", content: null, label: "Identity" }
|
|
178
|
+
];
|
|
179
|
+
return /* @__PURE__ */ jsxs(
|
|
180
|
+
motion.div,
|
|
181
|
+
{
|
|
182
|
+
initial: { opacity: 0, y: 12 },
|
|
183
|
+
animate: { opacity: 1, y: 0 },
|
|
184
|
+
exit: { opacity: 0, y: 12 },
|
|
185
|
+
className: "rounded-lg border border-[--cyan]/20 bg-[--card] overflow-hidden",
|
|
186
|
+
children: [
|
|
187
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-4 border-b border-white/[0.06]", children: [
|
|
188
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
189
|
+
/* @__PURE__ */ jsx("div", { className: "w-10 h-10 rounded-lg bg-[--cyan]/10 border border-[--cyan]/20 flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "text-lg font-mono font-bold text-[--cyan]", children: codename.charAt(0).toUpperCase() }) }),
|
|
190
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
191
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm font-mono font-semibold", children: [
|
|
192
|
+
"@",
|
|
193
|
+
codename.toUpperCase()
|
|
194
|
+
] }),
|
|
195
|
+
profile.role && /* @__PURE__ */ jsx("p", { className: "text-[10px] font-mono text-muted-foreground", children: profile.role })
|
|
196
|
+
] })
|
|
197
|
+
] }),
|
|
198
|
+
/* @__PURE__ */ jsx("button", { onClick: onClose, className: "text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ jsx(XIcon, { size: 16 }) })
|
|
199
|
+
] }),
|
|
200
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4 border-b border-white/[0.06]", children: [
|
|
201
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] font-medium text-[--cyan] uppercase tracking-wider", children: "Identity" }),
|
|
202
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2 mt-2", children: [
|
|
203
|
+
profile.name && /* @__PURE__ */ jsxs("div", { children: [
|
|
204
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-muted-foreground uppercase", children: "Name" }),
|
|
205
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-mono", children: profile.name })
|
|
206
|
+
] }),
|
|
207
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
208
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-muted-foreground uppercase", children: "Codename" }),
|
|
209
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-mono", children: codename })
|
|
210
|
+
] }),
|
|
211
|
+
profile.role && /* @__PURE__ */ jsxs("div", { children: [
|
|
212
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-muted-foreground uppercase", children: "Role" }),
|
|
213
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-mono", children: profile.role })
|
|
214
|
+
] }),
|
|
215
|
+
profile.specialization && /* @__PURE__ */ jsxs("div", { children: [
|
|
216
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-muted-foreground uppercase", children: "Specialization" }),
|
|
217
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-mono", children: profile.specialization })
|
|
218
|
+
] })
|
|
219
|
+
] })
|
|
220
|
+
] }),
|
|
221
|
+
profile.config && /* @__PURE__ */ jsxs("div", { className: "p-4 border-b border-white/[0.06]", children: [
|
|
222
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] font-medium text-[--cyan] uppercase tracking-wider", children: "Config" }),
|
|
223
|
+
/* @__PURE__ */ jsx("pre", { className: "mt-2 text-[11px] bg-black/30 rounded-md p-2.5 font-mono overflow-auto max-h-32 text-foreground/80 border border-white/[0.04] scrollbar-thin", children: JSON.stringify(profile.config, null, 2) })
|
|
224
|
+
] }),
|
|
225
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4", children: [
|
|
226
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] font-medium text-[--cyan] uppercase tracking-wider", children: "Files" }),
|
|
227
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 mt-2", children: files.map((f) => /* @__PURE__ */ jsxs("div", { children: [
|
|
228
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
229
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-muted-foreground", children: f.name }),
|
|
230
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
231
|
+
f.content ? /* @__PURE__ */ jsx("span", { className: "text-[9px] font-mono text-green-500", children: "exists" }) : /* @__PURE__ */ jsx("span", { className: "text-[9px] font-mono text-muted-foreground/50", children: "empty" }),
|
|
232
|
+
/* @__PURE__ */ jsx(
|
|
233
|
+
"button",
|
|
234
|
+
{
|
|
235
|
+
onClick: () => editingFile === f.name ? setEditingFile(null) : startEdit(f.name, f.content),
|
|
236
|
+
className: "text-muted-foreground hover:text-[--cyan] transition-colors p-0.5",
|
|
237
|
+
children: /* @__PURE__ */ jsx(PencilIcon, { size: 10 })
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
] })
|
|
241
|
+
] }),
|
|
242
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: editingFile === f.name && /* @__PURE__ */ jsxs(motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, className: "overflow-hidden", children: [
|
|
243
|
+
/* @__PURE__ */ jsx(
|
|
244
|
+
"textarea",
|
|
245
|
+
{
|
|
246
|
+
value: editContent,
|
|
247
|
+
onChange: (e) => setEditContent(e.target.value),
|
|
248
|
+
rows: 8,
|
|
249
|
+
className: "w-full mt-1 text-[11px] border border-white/[0.06] rounded-md p-2.5 bg-black/20 font-mono text-foreground/80 focus:outline-none focus:border-[--cyan]/40 focus:ring-1 focus:ring-[--cyan]/20 transition-colors resize-y"
|
|
250
|
+
}
|
|
251
|
+
),
|
|
252
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 mt-1", children: [
|
|
253
|
+
/* @__PURE__ */ jsxs(
|
|
254
|
+
"button",
|
|
255
|
+
{
|
|
256
|
+
onClick: handleSave,
|
|
257
|
+
disabled: saving,
|
|
258
|
+
className: "inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-[10px] font-mono font-medium bg-[--cyan]/10 text-[--cyan] border border-[--cyan]/20 hover:bg-[--cyan] hover:text-[--primary-foreground] transition-colors disabled:opacity-50",
|
|
259
|
+
children: [
|
|
260
|
+
saving ? /* @__PURE__ */ jsx(SpinnerIcon, { size: 10 }) : /* @__PURE__ */ jsx(CheckIcon, { size: 10 }),
|
|
261
|
+
" Save"
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
),
|
|
265
|
+
/* @__PURE__ */ jsx(
|
|
266
|
+
"button",
|
|
267
|
+
{
|
|
268
|
+
onClick: () => setEditingFile(null),
|
|
269
|
+
className: "inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-[10px] font-mono font-medium border border-white/[0.06] hover:bg-white/[0.04] transition-colors text-muted-foreground",
|
|
270
|
+
children: "Cancel"
|
|
271
|
+
}
|
|
272
|
+
)
|
|
273
|
+
] })
|
|
274
|
+
] }) })
|
|
275
|
+
] }, f.name)) })
|
|
276
|
+
] })
|
|
277
|
+
]
|
|
278
|
+
}
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
function AssignTaskDialog({ agent, onClose }) {
|
|
282
|
+
const [prompt, setPrompt] = useState("");
|
|
283
|
+
const [submitting, setSubmitting] = useState(false);
|
|
284
|
+
const [result, setResult] = useState(null);
|
|
285
|
+
async function handleSubmit() {
|
|
286
|
+
if (!prompt) return;
|
|
287
|
+
setSubmitting(true);
|
|
288
|
+
const res = await createAgentJob(agent.id, prompt);
|
|
289
|
+
setResult(res);
|
|
290
|
+
setSubmitting(false);
|
|
291
|
+
}
|
|
292
|
+
const codename = agent.codename || agent.name || agent.id;
|
|
293
|
+
return /* @__PURE__ */ jsxs(
|
|
294
|
+
motion.div,
|
|
295
|
+
{
|
|
296
|
+
initial: { opacity: 0, scale: 0.95 },
|
|
297
|
+
animate: { opacity: 1, scale: 1 },
|
|
298
|
+
exit: { opacity: 0, scale: 0.95 },
|
|
299
|
+
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
300
|
+
children: [
|
|
301
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/60", onClick: onClose }),
|
|
302
|
+
/* @__PURE__ */ jsxs("div", { className: "relative w-full max-w-lg rounded-lg border border-[--cyan]/20 bg-[--card] shadow-2xl", children: [
|
|
303
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-4 border-b border-white/[0.06]", children: [
|
|
304
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
305
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
306
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#ff5f57]" }),
|
|
307
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#febc2e]" }),
|
|
308
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#28c840]" })
|
|
309
|
+
] }),
|
|
310
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono text-[10px] font-medium text-[--cyan] uppercase tracking-wider ml-1", children: [
|
|
311
|
+
"Assign Task to @",
|
|
312
|
+
codename.toUpperCase()
|
|
313
|
+
] })
|
|
314
|
+
] }),
|
|
315
|
+
/* @__PURE__ */ jsx("button", { onClick: onClose, className: "text-muted-foreground hover:text-foreground", children: /* @__PURE__ */ jsx(XIcon, { size: 14 }) })
|
|
316
|
+
] }),
|
|
317
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4", children: [
|
|
318
|
+
/* @__PURE__ */ jsx(
|
|
319
|
+
"textarea",
|
|
320
|
+
{
|
|
321
|
+
value: prompt,
|
|
322
|
+
onChange: (e) => setPrompt(e.target.value),
|
|
323
|
+
placeholder: "Describe the task for this agent...",
|
|
324
|
+
rows: 4,
|
|
325
|
+
className: "w-full text-sm border border-white/[0.06] rounded-md p-3 bg-black/20 font-mono text-foreground/80 placeholder:text-muted-foreground/50 focus:outline-none focus:border-[--cyan]/40 focus:ring-1 focus:ring-[--cyan]/20 transition-colors resize-y",
|
|
326
|
+
autoFocus: true
|
|
327
|
+
}
|
|
328
|
+
),
|
|
329
|
+
result && /* @__PURE__ */ jsx("p", { className: `text-xs font-mono mt-2 ${result.error ? "text-[--destructive]" : "text-green-500"}`, children: result.error || "Job created successfully" }),
|
|
330
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 mt-3", children: [
|
|
331
|
+
/* @__PURE__ */ jsx("button", { onClick: onClose, className: "inline-flex items-center gap-1.5 rounded-md px-4 py-2 text-xs font-mono font-medium border border-white/[0.06] hover:bg-white/[0.04] transition-colors text-muted-foreground", children: "Cancel" }),
|
|
332
|
+
/* @__PURE__ */ jsx(
|
|
333
|
+
"button",
|
|
334
|
+
{
|
|
335
|
+
onClick: handleSubmit,
|
|
336
|
+
disabled: submitting || !prompt,
|
|
337
|
+
className: "inline-flex items-center gap-1.5 rounded-md px-4 py-2 text-xs font-mono font-medium bg-[--cyan]/10 text-[--cyan] border border-[--cyan]/20 hover:bg-[--cyan] hover:text-[--primary-foreground] transition-colors disabled:opacity-50",
|
|
338
|
+
children: submitting ? /* @__PURE__ */ jsx(SpinnerIcon, { size: 12 }) : "Create Job"
|
|
339
|
+
}
|
|
340
|
+
)
|
|
341
|
+
] })
|
|
342
|
+
] })
|
|
343
|
+
] })
|
|
344
|
+
]
|
|
345
|
+
}
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
function TabBtn({ active, onClick, icon: Icon, label }) {
|
|
349
|
+
return /* @__PURE__ */ jsxs(
|
|
350
|
+
"button",
|
|
351
|
+
{
|
|
352
|
+
onClick,
|
|
353
|
+
className: `inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-mono font-medium rounded-md border transition-colors ${active ? "bg-[--cyan]/10 text-[--cyan] border-[--cyan]/20" : "border-white/[0.06] text-muted-foreground hover:text-foreground hover:bg-white/[0.04]"}`,
|
|
354
|
+
children: [
|
|
355
|
+
Icon && /* @__PURE__ */ jsx(Icon, { size: 12 }),
|
|
356
|
+
label
|
|
357
|
+
]
|
|
358
|
+
}
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
function FormField({ label, children }) {
|
|
362
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
363
|
+
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-mono font-medium text-muted-foreground uppercase tracking-wider mb-1", children: label }),
|
|
364
|
+
children
|
|
365
|
+
] });
|
|
366
|
+
}
|
|
367
|
+
const inputClass = "w-full text-sm border border-white/[0.06] rounded-md px-3 py-2 bg-black/20 font-mono text-foreground/80 placeholder:text-muted-foreground/50 focus:outline-none focus:border-[--cyan]/40 focus:ring-1 focus:ring-[--cyan]/20 transition-colors";
|
|
368
|
+
const selectClass = "w-full text-sm border border-white/[0.06] rounded-md px-3 py-2 bg-black/20 font-mono focus:outline-none focus:border-[--cyan]/40 transition-colors";
|
|
369
|
+
const textareaClass = "w-full text-sm border border-white/[0.06] rounded-md p-3 bg-black/20 font-mono text-foreground/80 placeholder:text-muted-foreground/50 focus:outline-none focus:border-[--cyan]/40 focus:ring-1 focus:ring-[--cyan]/20 transition-colors resize-y";
|
|
370
|
+
function CreateAgentForm({ onCreated, onClose }) {
|
|
371
|
+
const [tab, setTab] = useState("basic");
|
|
372
|
+
const [creating, setCreating] = useState(false);
|
|
373
|
+
const [testing, setTesting] = useState(false);
|
|
374
|
+
const [testResult, setTestResult] = useState(null);
|
|
375
|
+
const [result, setResult] = useState(null);
|
|
376
|
+
const [mcpTools, setMcpTools] = useState([]);
|
|
377
|
+
const [loadingTools, setLoadingTools] = useState(false);
|
|
378
|
+
const [name, setName] = useState("");
|
|
379
|
+
const [codename, setCodename] = useState("");
|
|
380
|
+
const [role, setRole] = useState("");
|
|
381
|
+
const [specialization, setSpecialization] = useState("");
|
|
382
|
+
const [description, setDescription] = useState("");
|
|
383
|
+
const [goal, setGoal] = useState("");
|
|
384
|
+
const [tags, setTags] = useState([]);
|
|
385
|
+
const [tagInput, setTagInput] = useState("");
|
|
386
|
+
const [llmProvider, setLlmProvider] = useState("");
|
|
387
|
+
const [llmModel, setLlmModel] = useState("");
|
|
388
|
+
const [temperature, setTemperature] = useState(0.7);
|
|
389
|
+
const [maxTokens, setMaxTokens] = useState(4096);
|
|
390
|
+
const [soulTemplate, setSoulTemplate] = useState("custom");
|
|
391
|
+
const [soul, setSoul] = useState("");
|
|
392
|
+
const [skills, setSkills] = useState("");
|
|
393
|
+
const [heartbeat, setHeartbeat] = useState("");
|
|
394
|
+
const [selectedTools, setSelectedTools] = useState([]);
|
|
395
|
+
const [runMode, setRunMode] = useState("manual");
|
|
396
|
+
const [schedule, setSchedule] = useState("");
|
|
397
|
+
const [maxConcurrent, setMaxConcurrent] = useState(1);
|
|
398
|
+
const [timeout, setTimeout_] = useState(0);
|
|
399
|
+
const [cpu, setCpu] = useState("1");
|
|
400
|
+
const [memory, setMemory] = useState("1GB");
|
|
401
|
+
const [disk, setDisk] = useState("5GB");
|
|
402
|
+
const [network, setNetwork] = useState("full");
|
|
403
|
+
const [workDir, setWorkDir] = useState("/workspace");
|
|
404
|
+
const [allowedPaths, setAllowedPaths] = useState("");
|
|
405
|
+
const [envVars, setEnvVars] = useState([]);
|
|
406
|
+
const [swarm, setSwarm] = useState("");
|
|
407
|
+
const [supervisor, setSupervisor] = useState("");
|
|
408
|
+
useEffect(() => {
|
|
409
|
+
if (name && !codename) {
|
|
410
|
+
setCodename(name.toUpperCase().replace(/[^A-Z0-9]/g, "_").replace(/_+/g, "_"));
|
|
411
|
+
}
|
|
412
|
+
}, [name]);
|
|
413
|
+
useEffect(() => {
|
|
414
|
+
if (tab === "tools" && mcpTools.length === 0 && !loadingTools) {
|
|
415
|
+
setLoadingTools(true);
|
|
416
|
+
getAvailableMcpTools().then((t) => {
|
|
417
|
+
setMcpTools(t);
|
|
418
|
+
setLoadingTools(false);
|
|
419
|
+
}).catch(() => setLoadingTools(false));
|
|
420
|
+
}
|
|
421
|
+
}, [tab]);
|
|
422
|
+
function applySoulTemplate(templateKey) {
|
|
423
|
+
setSoulTemplate(templateKey);
|
|
424
|
+
let content = SOUL_TEMPLATES[templateKey] || SOUL_TEMPLATES.custom;
|
|
425
|
+
content = content.replace(/\{\{CODENAME\}\}/g, codename || "AGENT");
|
|
426
|
+
content = content.replace(/\{\{NAME\}\}/g, name || "Agent");
|
|
427
|
+
content = content.replace(/\{\{ROLE\}\}/g, role || "General Agent");
|
|
428
|
+
content = content.replace(/\{\{SPEC\}\}/g, specialization || "General");
|
|
429
|
+
setSoul(content);
|
|
430
|
+
}
|
|
431
|
+
function addTag() {
|
|
432
|
+
const t = tagInput.trim().toLowerCase();
|
|
433
|
+
if (t && !tags.includes(t)) setTags([...tags, t]);
|
|
434
|
+
setTagInput("");
|
|
435
|
+
}
|
|
436
|
+
function addEnvVar() {
|
|
437
|
+
setEnvVars([...envVars, { key: "", value: "" }]);
|
|
438
|
+
}
|
|
439
|
+
async function handleTest() {
|
|
440
|
+
setTesting(true);
|
|
441
|
+
setTestResult(null);
|
|
442
|
+
const issues = [];
|
|
443
|
+
if (!name && !codename) issues.push("Name or codename is required");
|
|
444
|
+
if (llmProvider && !llmModel) issues.push("LLM model not selected");
|
|
445
|
+
if (runMode === "scheduled" && !schedule) issues.push("Schedule expression is required for scheduled mode");
|
|
446
|
+
setTestResult(issues.length === 0 ? { success: true } : { issues });
|
|
447
|
+
setTesting(false);
|
|
448
|
+
}
|
|
449
|
+
async function handleCreate() {
|
|
450
|
+
if (!name && !codename) return;
|
|
451
|
+
setCreating(true);
|
|
452
|
+
const identity = {
|
|
453
|
+
name,
|
|
454
|
+
codename,
|
|
455
|
+
role,
|
|
456
|
+
specialization,
|
|
457
|
+
description,
|
|
458
|
+
goal,
|
|
459
|
+
tags,
|
|
460
|
+
soul: soul || void 0,
|
|
461
|
+
skills: skills || void 0,
|
|
462
|
+
heartbeat: heartbeat || void 0,
|
|
463
|
+
llm: llmProvider ? { provider: llmProvider, model: llmModel, temperature, maxTokens } : void 0,
|
|
464
|
+
scheduling: runMode !== "manual" ? { mode: runMode, schedule, maxConcurrent, timeout: timeout || void 0 } : void 0,
|
|
465
|
+
resources: { cpu, memory, disk, network },
|
|
466
|
+
mcpTools: selectedTools.length > 0 ? selectedTools : void 0,
|
|
467
|
+
envVars: envVars.reduce((acc, v) => {
|
|
468
|
+
if (v.key) acc[v.key] = v.value;
|
|
469
|
+
return acc;
|
|
470
|
+
}, {}),
|
|
471
|
+
team: swarm || supervisor ? { swarm: swarm || void 0, supervisor: supervisor || void 0 } : void 0
|
|
472
|
+
};
|
|
473
|
+
const res = await createAgent(identity);
|
|
474
|
+
setResult(res);
|
|
475
|
+
if (!res.error) onCreated();
|
|
476
|
+
setCreating(false);
|
|
477
|
+
}
|
|
478
|
+
const TABS = [
|
|
479
|
+
{ id: "basic", label: "Basic", icon: UsersIcon },
|
|
480
|
+
{ id: "llm", label: "LLM", icon: CpuIcon },
|
|
481
|
+
{ id: "soul", label: "Soul", icon: PencilIcon },
|
|
482
|
+
{ id: "tools", label: "Tools", icon: PackageIcon },
|
|
483
|
+
{ id: "schedule", label: "Schedule", icon: ClockIcon },
|
|
484
|
+
{ id: "resources", label: "Resources", icon: SlidersIcon },
|
|
485
|
+
{ id: "files", label: "Files", icon: FolderIcon },
|
|
486
|
+
{ id: "env", label: "Env", icon: HashIcon },
|
|
487
|
+
{ id: "team", label: "Team", icon: UsersIcon }
|
|
488
|
+
];
|
|
489
|
+
return /* @__PURE__ */ jsx(motion.div, { initial: { opacity: 0, height: 0 }, animate: { opacity: 1, height: "auto" }, exit: { opacity: 0, height: 0 }, className: "overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-[--cyan]/20 bg-[--card] mt-4 overflow-hidden", children: [
|
|
490
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-4 border-b border-white/[0.06]", children: [
|
|
491
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
492
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
493
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#ff5f57]" }),
|
|
494
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#febc2e]" }),
|
|
495
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#28c840]" })
|
|
496
|
+
] }),
|
|
497
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] font-medium text-[--cyan] uppercase tracking-wider ml-1", children: "Create New Agent" })
|
|
498
|
+
] }),
|
|
499
|
+
/* @__PURE__ */ jsx("button", { onClick: onClose, className: "text-muted-foreground hover:text-foreground", children: /* @__PURE__ */ jsx(XIcon, { size: 14 }) })
|
|
500
|
+
] }),
|
|
501
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5 p-4 pb-0 overflow-x-auto scrollbar-thin", children: TABS.map((t) => /* @__PURE__ */ jsx(TabBtn, { active: tab === t.id, onClick: () => setTab(t.id), icon: t.icon, label: t.label }, t.id)) }),
|
|
502
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4", children: [
|
|
503
|
+
tab === "basic" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
504
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3", children: [
|
|
505
|
+
/* @__PURE__ */ jsx(FormField, { label: "Name", children: /* @__PURE__ */ jsx("input", { value: name, onChange: (e) => setName(e.target.value), placeholder: "Reaper", className: inputClass }) }),
|
|
506
|
+
/* @__PURE__ */ jsx(FormField, { label: "Codename (auto-generated)", children: /* @__PURE__ */ jsx("input", { value: codename, onChange: (e) => setCodename(e.target.value), placeholder: "REAPER", className: inputClass }) }),
|
|
507
|
+
/* @__PURE__ */ jsx(FormField, { label: "Role", children: /* @__PURE__ */ jsxs("select", { value: role, onChange: (e) => setRole(e.target.value), className: selectClass, children: [
|
|
508
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Select role..." }),
|
|
509
|
+
ROLE_OPTIONS.map((r) => /* @__PURE__ */ jsx("option", { value: r, children: r }, r))
|
|
510
|
+
] }) }),
|
|
511
|
+
/* @__PURE__ */ jsx(FormField, { label: "Specialization", children: /* @__PURE__ */ jsxs("select", { value: specialization, onChange: (e) => setSpecialization(e.target.value), className: selectClass, children: [
|
|
512
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Select specialization..." }),
|
|
513
|
+
SPEC_OPTIONS.map((s) => /* @__PURE__ */ jsx("option", { value: s, children: s }, s))
|
|
514
|
+
] }) })
|
|
515
|
+
] }),
|
|
516
|
+
/* @__PURE__ */ jsx(FormField, { label: "Description", children: /* @__PURE__ */ jsx("input", { value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Brief purpose statement", className: inputClass }) }),
|
|
517
|
+
/* @__PURE__ */ jsx(FormField, { label: "Goal", children: /* @__PURE__ */ jsx("textarea", { value: goal, onChange: (e) => setGoal(e.target.value), placeholder: "Specific mission objective...", rows: 2, className: textareaClass }) }),
|
|
518
|
+
/* @__PURE__ */ jsxs(FormField, { label: "Tags", children: [
|
|
519
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5 mb-2", children: tags.map((t) => /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-[--cyan]/10 text-[--cyan] border border-[--cyan]/20 px-2 py-0.5 text-[10px] font-mono", children: [
|
|
520
|
+
t,
|
|
521
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setTags(tags.filter((x) => x !== t)), className: "hover:text-[--destructive]", children: /* @__PURE__ */ jsx(XIcon, { size: 8 }) })
|
|
522
|
+
] }, t)) }),
|
|
523
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
524
|
+
/* @__PURE__ */ jsx(
|
|
525
|
+
"input",
|
|
526
|
+
{
|
|
527
|
+
value: tagInput,
|
|
528
|
+
onChange: (e) => setTagInput(e.target.value),
|
|
529
|
+
onKeyDown: (e) => e.key === "Enter" && (e.preventDefault(), addTag()),
|
|
530
|
+
placeholder: "Add tag...",
|
|
531
|
+
className: inputClass
|
|
532
|
+
}
|
|
533
|
+
),
|
|
534
|
+
/* @__PURE__ */ jsx("button", { onClick: addTag, className: "shrink-0 inline-flex items-center gap-1 rounded-md px-3 py-2 text-xs font-mono font-medium border border-white/[0.06] hover:bg-white/[0.04] transition-colors", children: /* @__PURE__ */ jsx(PlusIcon, { size: 12 }) })
|
|
535
|
+
] })
|
|
536
|
+
] })
|
|
537
|
+
] }),
|
|
538
|
+
tab === "llm" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
539
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3", children: [
|
|
540
|
+
/* @__PURE__ */ jsx(FormField, { label: "Provider", children: /* @__PURE__ */ jsxs("select", { value: llmProvider, onChange: (e) => {
|
|
541
|
+
setLlmProvider(e.target.value);
|
|
542
|
+
setLlmModel("");
|
|
543
|
+
}, className: selectClass, children: [
|
|
544
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Default (inherit)" }),
|
|
545
|
+
/* @__PURE__ */ jsx("option", { value: "anthropic", children: "Anthropic" }),
|
|
546
|
+
/* @__PURE__ */ jsx("option", { value: "openai", children: "OpenAI" }),
|
|
547
|
+
/* @__PURE__ */ jsx("option", { value: "google", children: "Google" }),
|
|
548
|
+
/* @__PURE__ */ jsx("option", { value: "custom", children: "Custom" })
|
|
549
|
+
] }) }),
|
|
550
|
+
/* @__PURE__ */ jsxs(FormField, { label: "Model", children: [
|
|
551
|
+
/* @__PURE__ */ jsxs("select", { value: llmModel, onChange: (e) => setLlmModel(e.target.value), className: selectClass, disabled: !llmProvider, children: [
|
|
552
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Select model..." }),
|
|
553
|
+
(PROVIDER_MODELS[llmProvider] || []).map((m) => /* @__PURE__ */ jsx("option", { value: m, children: m }, m))
|
|
554
|
+
] }),
|
|
555
|
+
llmProvider === "custom" && /* @__PURE__ */ jsx("input", { value: llmModel, onChange: (e) => setLlmModel(e.target.value), placeholder: "model-name", className: `${inputClass} mt-2` })
|
|
556
|
+
] })
|
|
557
|
+
] }),
|
|
558
|
+
/* @__PURE__ */ jsxs(FormField, { label: `Temperature: ${temperature}`, children: [
|
|
559
|
+
/* @__PURE__ */ jsx(
|
|
560
|
+
"input",
|
|
561
|
+
{
|
|
562
|
+
type: "range",
|
|
563
|
+
min: "0",
|
|
564
|
+
max: "1",
|
|
565
|
+
step: "0.05",
|
|
566
|
+
value: temperature,
|
|
567
|
+
onChange: (e) => setTemperature(parseFloat(e.target.value)),
|
|
568
|
+
className: "w-full accent-[--cyan]"
|
|
569
|
+
}
|
|
570
|
+
),
|
|
571
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-[9px] font-mono text-muted-foreground mt-1", children: [
|
|
572
|
+
/* @__PURE__ */ jsx("span", { children: "Precise (0)" }),
|
|
573
|
+
/* @__PURE__ */ jsx("span", { children: "Creative (1)" })
|
|
574
|
+
] })
|
|
575
|
+
] }),
|
|
576
|
+
/* @__PURE__ */ jsx(FormField, { label: "Max Tokens", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
577
|
+
/* @__PURE__ */ jsx("input", { type: "number", value: maxTokens, onChange: (e) => setMaxTokens(parseInt(e.target.value) || 4096), className: inputClass }),
|
|
578
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-1", children: [4096, 8192, 16384].map((v) => /* @__PURE__ */ jsx(
|
|
579
|
+
"button",
|
|
580
|
+
{
|
|
581
|
+
onClick: () => setMaxTokens(v),
|
|
582
|
+
className: `px-2 py-1 text-[10px] font-mono rounded border transition-colors ${maxTokens === v ? "bg-[--cyan]/10 text-[--cyan] border-[--cyan]/20" : "border-white/[0.06] text-muted-foreground hover:text-foreground"}`,
|
|
583
|
+
children: v >= 1e3 ? `${v / 1e3}k` : v
|
|
584
|
+
},
|
|
585
|
+
v
|
|
586
|
+
)) })
|
|
587
|
+
] }) })
|
|
588
|
+
] }),
|
|
589
|
+
tab === "soul" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
590
|
+
/* @__PURE__ */ jsx(FormField, { label: "Template", children: /* @__PURE__ */ jsx("div", { className: "flex gap-2 mb-2", children: Object.entries({ recon: "Recon Agent", exploit: "Exploit Dev", report: "Report Writer", custom: "Custom" }).map(([k, label]) => /* @__PURE__ */ jsx(
|
|
591
|
+
"button",
|
|
592
|
+
{
|
|
593
|
+
onClick: () => applySoulTemplate(k),
|
|
594
|
+
className: `px-3 py-1.5 text-xs font-mono rounded-md border transition-colors ${soulTemplate === k ? "bg-[--cyan]/10 text-[--cyan] border-[--cyan]/20" : "border-white/[0.06] text-muted-foreground hover:text-foreground hover:bg-white/[0.04]"}`,
|
|
595
|
+
children: label
|
|
596
|
+
},
|
|
597
|
+
k
|
|
598
|
+
)) }) }),
|
|
599
|
+
/* @__PURE__ */ jsx(FormField, { label: "SOUL.md", children: /* @__PURE__ */ jsx(
|
|
600
|
+
"textarea",
|
|
601
|
+
{
|
|
602
|
+
value: soul,
|
|
603
|
+
onChange: (e) => setSoul(e.target.value),
|
|
604
|
+
rows: 10,
|
|
605
|
+
placeholder: "Agent personality and system prompt...",
|
|
606
|
+
className: textareaClass
|
|
607
|
+
}
|
|
608
|
+
) }),
|
|
609
|
+
/* @__PURE__ */ jsx(FormField, { label: "SKILLS.md (optional)", children: /* @__PURE__ */ jsx(
|
|
610
|
+
"textarea",
|
|
611
|
+
{
|
|
612
|
+
value: skills,
|
|
613
|
+
onChange: (e) => setSkills(e.target.value),
|
|
614
|
+
rows: 4,
|
|
615
|
+
placeholder: "Define agent skills and capabilities...",
|
|
616
|
+
className: textareaClass
|
|
617
|
+
}
|
|
618
|
+
) }),
|
|
619
|
+
/* @__PURE__ */ jsx(FormField, { label: "HEARTBEAT.md (optional)", children: /* @__PURE__ */ jsx(
|
|
620
|
+
"textarea",
|
|
621
|
+
{
|
|
622
|
+
value: heartbeat,
|
|
623
|
+
onChange: (e) => setHeartbeat(e.target.value),
|
|
624
|
+
rows: 3,
|
|
625
|
+
placeholder: "Self-monitoring behavior...",
|
|
626
|
+
className: textareaClass
|
|
627
|
+
}
|
|
628
|
+
) })
|
|
629
|
+
] }),
|
|
630
|
+
tab === "tools" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
631
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-mono text-muted-foreground", children: "Select MCP tools this agent can use." }),
|
|
632
|
+
loadingTools ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 py-4", children: [
|
|
633
|
+
/* @__PURE__ */ jsx(SpinnerIcon, { size: 14 }),
|
|
634
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-mono text-muted-foreground", children: "Loading tools..." })
|
|
635
|
+
] }) : mcpTools.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-xs font-mono text-muted-foreground py-4", children: "No MCP tools configured. Add tools in Settings > MCP." }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-2 max-h-[300px] overflow-y-auto scrollbar-thin", children: mcpTools.map((tool) => {
|
|
636
|
+
const checked = selectedTools.includes(tool.name);
|
|
637
|
+
return /* @__PURE__ */ jsxs(
|
|
638
|
+
"label",
|
|
639
|
+
{
|
|
640
|
+
className: `flex items-start gap-2.5 p-2.5 rounded-lg border cursor-pointer transition-colors ${checked ? "border-[--cyan]/30 bg-[--cyan]/5" : "border-white/[0.06] bg-[--card] hover:border-white/[0.12]"}`,
|
|
641
|
+
children: [
|
|
642
|
+
/* @__PURE__ */ jsx(
|
|
643
|
+
"input",
|
|
644
|
+
{
|
|
645
|
+
type: "checkbox",
|
|
646
|
+
checked,
|
|
647
|
+
onChange: (e) => setSelectedTools(e.target.checked ? [...selectedTools, tool.name] : selectedTools.filter((t) => t !== tool.name)),
|
|
648
|
+
className: "mt-0.5 accent-[--cyan]"
|
|
649
|
+
}
|
|
650
|
+
),
|
|
651
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
652
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-mono font-medium truncate", children: tool.name }),
|
|
653
|
+
tool.description && /* @__PURE__ */ jsx("p", { className: "text-[10px] font-mono text-muted-foreground truncate mt-0.5", children: tool.description })
|
|
654
|
+
] })
|
|
655
|
+
]
|
|
656
|
+
},
|
|
657
|
+
tool.name
|
|
658
|
+
);
|
|
659
|
+
}) }),
|
|
660
|
+
selectedTools.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1.5", children: [
|
|
661
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-muted-foreground", children: "Selected:" }),
|
|
662
|
+
selectedTools.map((t) => /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-[--cyan]/10 text-[--cyan] border border-[--cyan]/20 px-2 py-0.5 text-[9px] font-mono", children: [
|
|
663
|
+
t,
|
|
664
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setSelectedTools(selectedTools.filter((x) => x !== t)), children: /* @__PURE__ */ jsx(XIcon, { size: 8 }) })
|
|
665
|
+
] }, t))
|
|
666
|
+
] })
|
|
667
|
+
] }),
|
|
668
|
+
tab === "schedule" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
669
|
+
/* @__PURE__ */ jsx(FormField, { label: "Run Mode", children: /* @__PURE__ */ jsx("select", { value: runMode, onChange: (e) => setRunMode(e.target.value), className: selectClass, children: RUN_MODES.map((m) => /* @__PURE__ */ jsx("option", { value: m, children: m }, m)) }) }),
|
|
670
|
+
runMode === "scheduled" && /* @__PURE__ */ jsxs(FormField, { label: "Cron Schedule", children: [
|
|
671
|
+
/* @__PURE__ */ jsx("input", { value: schedule, onChange: (e) => setSchedule(e.target.value), placeholder: "0 */6 * * *", className: inputClass }),
|
|
672
|
+
/* @__PURE__ */ jsx("p", { className: "text-[9px] font-mono text-muted-foreground mt-1", children: "Standard cron expression (min hour dom month dow)" })
|
|
673
|
+
] }),
|
|
674
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
675
|
+
/* @__PURE__ */ jsx(FormField, { label: "Max Concurrent Jobs", children: /* @__PURE__ */ jsx("select", { value: maxConcurrent, onChange: (e) => setMaxConcurrent(parseInt(e.target.value)), className: selectClass, children: [1, 2, 5, 10].map((v) => /* @__PURE__ */ jsx("option", { value: v, children: v }, v)) }) }),
|
|
676
|
+
/* @__PURE__ */ jsx(FormField, { label: "Timeout (seconds, 0=none)", children: /* @__PURE__ */ jsx("input", { type: "number", value: timeout, onChange: (e) => setTimeout_(parseInt(e.target.value) || 0), className: inputClass, min: "0" }) })
|
|
677
|
+
] })
|
|
678
|
+
] }),
|
|
679
|
+
tab === "resources" && /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
680
|
+
/* @__PURE__ */ jsx(FormField, { label: "CPU Cores", children: /* @__PURE__ */ jsx("select", { value: cpu, onChange: (e) => setCpu(e.target.value), className: selectClass, children: ["0.5", "1", "2", "4"].map((v) => /* @__PURE__ */ jsxs("option", { value: v, children: [
|
|
681
|
+
v,
|
|
682
|
+
" core",
|
|
683
|
+
v !== "1" ? "s" : ""
|
|
684
|
+
] }, v)) }) }),
|
|
685
|
+
/* @__PURE__ */ jsx(FormField, { label: "Memory", children: /* @__PURE__ */ jsx("select", { value: memory, onChange: (e) => setMemory(e.target.value), className: selectClass, children: ["512MB", "1GB", "2GB", "4GB", "8GB"].map((v) => /* @__PURE__ */ jsx("option", { value: v, children: v }, v)) }) }),
|
|
686
|
+
/* @__PURE__ */ jsx(FormField, { label: "Disk", children: /* @__PURE__ */ jsx("select", { value: disk, onChange: (e) => setDisk(e.target.value), className: selectClass, children: ["1GB", "5GB", "10GB", "Unlimited"].map((v) => /* @__PURE__ */ jsx("option", { value: v, children: v }, v)) }) }),
|
|
687
|
+
/* @__PURE__ */ jsx(FormField, { label: "Network", children: /* @__PURE__ */ jsxs("select", { value: network, onChange: (e) => setNetwork(e.target.value), className: selectClass, children: [
|
|
688
|
+
/* @__PURE__ */ jsx("option", { value: "isolated", children: "Isolated" }),
|
|
689
|
+
/* @__PURE__ */ jsx("option", { value: "internal", children: "Internal only" }),
|
|
690
|
+
/* @__PURE__ */ jsx("option", { value: "full", children: "Full internet" })
|
|
691
|
+
] }) })
|
|
692
|
+
] }) }),
|
|
693
|
+
tab === "files" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
694
|
+
/* @__PURE__ */ jsx(FormField, { label: "Working Directory", children: /* @__PURE__ */ jsx("input", { value: workDir, onChange: (e) => setWorkDir(e.target.value), className: inputClass }) }),
|
|
695
|
+
/* @__PURE__ */ jsx(FormField, { label: "Allowed Paths (comma-separated)", children: /* @__PURE__ */ jsx("input", { value: allowedPaths, onChange: (e) => setAllowedPaths(e.target.value), placeholder: "/workspace, /tmp", className: inputClass }) })
|
|
696
|
+
] }),
|
|
697
|
+
tab === "env" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
698
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-mono text-muted-foreground", children: "Agent-specific environment variables." }),
|
|
699
|
+
envVars.map((v, i) => /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
700
|
+
/* @__PURE__ */ jsx(
|
|
701
|
+
"input",
|
|
702
|
+
{
|
|
703
|
+
value: v.key,
|
|
704
|
+
onChange: (e) => {
|
|
705
|
+
const nv = [...envVars];
|
|
706
|
+
nv[i] = { ...nv[i], key: e.target.value };
|
|
707
|
+
setEnvVars(nv);
|
|
708
|
+
},
|
|
709
|
+
placeholder: "KEY",
|
|
710
|
+
className: inputClass
|
|
711
|
+
}
|
|
712
|
+
),
|
|
713
|
+
/* @__PURE__ */ jsx(
|
|
714
|
+
"input",
|
|
715
|
+
{
|
|
716
|
+
value: v.value,
|
|
717
|
+
onChange: (e) => {
|
|
718
|
+
const nv = [...envVars];
|
|
719
|
+
nv[i] = { ...nv[i], value: e.target.value };
|
|
720
|
+
setEnvVars(nv);
|
|
721
|
+
},
|
|
722
|
+
placeholder: "value",
|
|
723
|
+
className: inputClass
|
|
724
|
+
}
|
|
725
|
+
),
|
|
726
|
+
/* @__PURE__ */ jsx(
|
|
727
|
+
"button",
|
|
728
|
+
{
|
|
729
|
+
onClick: () => setEnvVars(envVars.filter((_, j) => j !== i)),
|
|
730
|
+
className: "shrink-0 p-2 text-muted-foreground hover:text-[--destructive] transition-colors",
|
|
731
|
+
children: /* @__PURE__ */ jsx(TrashIcon, { size: 12 })
|
|
732
|
+
}
|
|
733
|
+
)
|
|
734
|
+
] }, i)),
|
|
735
|
+
/* @__PURE__ */ jsxs(
|
|
736
|
+
"button",
|
|
737
|
+
{
|
|
738
|
+
onClick: addEnvVar,
|
|
739
|
+
className: "inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-mono font-medium border border-dashed border-white/[0.12] hover:bg-white/[0.04] transition-colors text-muted-foreground hover:text-foreground self-start",
|
|
740
|
+
children: [
|
|
741
|
+
/* @__PURE__ */ jsx(PlusIcon, { size: 12 }),
|
|
742
|
+
" Add Variable"
|
|
743
|
+
]
|
|
744
|
+
}
|
|
745
|
+
)
|
|
746
|
+
] }),
|
|
747
|
+
tab === "team" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
748
|
+
/* @__PURE__ */ jsx(FormField, { label: "Part of Swarm", children: /* @__PURE__ */ jsxs("select", { value: swarm, onChange: (e) => setSwarm(e.target.value), className: selectClass, children: [
|
|
749
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "None" }),
|
|
750
|
+
/* @__PURE__ */ jsx("option", { value: "recon-team", children: "Recon Team" }),
|
|
751
|
+
/* @__PURE__ */ jsx("option", { value: "exploit-team", children: "Exploit Team" }),
|
|
752
|
+
/* @__PURE__ */ jsx("option", { value: "full-swarm", children: "Full Swarm" })
|
|
753
|
+
] }) }),
|
|
754
|
+
/* @__PURE__ */ jsx(FormField, { label: "Supervisor Agent", children: /* @__PURE__ */ jsx("input", { value: supervisor, onChange: (e) => setSupervisor(e.target.value), placeholder: "@OVERSEER", className: inputClass }) })
|
|
755
|
+
] }),
|
|
756
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-4 pt-4 border-t border-white/[0.06]", children: [
|
|
757
|
+
/* @__PURE__ */ jsxs(
|
|
758
|
+
"button",
|
|
759
|
+
{
|
|
760
|
+
onClick: handleCreate,
|
|
761
|
+
disabled: creating || !name && !codename,
|
|
762
|
+
className: "inline-flex items-center gap-1.5 rounded-md px-4 py-2 text-xs font-mono font-medium bg-[--cyan]/10 text-[--cyan] border border-[--cyan]/20 hover:bg-[--cyan] hover:text-[--primary-foreground] transition-colors disabled:opacity-50",
|
|
763
|
+
children: [
|
|
764
|
+
creating ? /* @__PURE__ */ jsx(SpinnerIcon, { size: 12 }) : /* @__PURE__ */ jsx(PlusIcon, { size: 12 }),
|
|
765
|
+
" Create Agent"
|
|
766
|
+
]
|
|
767
|
+
}
|
|
768
|
+
),
|
|
769
|
+
/* @__PURE__ */ jsxs(
|
|
770
|
+
"button",
|
|
771
|
+
{
|
|
772
|
+
onClick: handleTest,
|
|
773
|
+
disabled: testing,
|
|
774
|
+
className: "inline-flex items-center gap-1.5 rounded-md px-3 py-2 text-xs font-mono font-medium border border-white/[0.06] hover:bg-white/[0.04] transition-colors text-muted-foreground hover:text-foreground disabled:opacity-50",
|
|
775
|
+
children: [
|
|
776
|
+
testing ? /* @__PURE__ */ jsx(SpinnerIcon, { size: 12 }) : /* @__PURE__ */ jsx(PlayIcon, { size: 12 }),
|
|
777
|
+
" Validate"
|
|
778
|
+
]
|
|
779
|
+
}
|
|
780
|
+
),
|
|
781
|
+
/* @__PURE__ */ jsx(
|
|
782
|
+
"button",
|
|
783
|
+
{
|
|
784
|
+
onClick: onClose,
|
|
785
|
+
className: "inline-flex items-center gap-1.5 rounded-md px-4 py-2 text-xs font-mono font-medium border border-white/[0.06] hover:bg-white/[0.04] transition-colors text-muted-foreground",
|
|
786
|
+
children: "Cancel"
|
|
787
|
+
}
|
|
788
|
+
),
|
|
789
|
+
result && /* @__PURE__ */ jsx("p", { className: `text-xs font-mono ml-2 ${result.error ? "text-[--destructive]" : "text-green-500"}`, children: result.error || `Agent created: ${result.id}` }),
|
|
790
|
+
testResult && /* @__PURE__ */ jsx("p", { className: `text-xs font-mono ml-2 ${testResult.success ? "text-green-500" : "text-yellow-500"}`, children: testResult.success ? "Configuration valid" : testResult.issues?.join(", ") })
|
|
791
|
+
] })
|
|
792
|
+
] })
|
|
793
|
+
] }) });
|
|
794
|
+
}
|
|
795
|
+
function AgentsPage() {
|
|
796
|
+
const [agents, setAgents] = useState([]);
|
|
797
|
+
const [loading, setLoading] = useState(true);
|
|
798
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
799
|
+
const [viewingProfile, setViewingProfile] = useState(null);
|
|
800
|
+
const [assigningTask, setAssigningTask] = useState(null);
|
|
801
|
+
const [showCreate, setShowCreate] = useState(false);
|
|
802
|
+
const [search, setSearch] = useState("");
|
|
803
|
+
async function load() {
|
|
804
|
+
try {
|
|
805
|
+
const a = await getAgentProfilesWithStatus();
|
|
806
|
+
setAgents(a);
|
|
807
|
+
} catch {
|
|
808
|
+
}
|
|
809
|
+
setLoading(false);
|
|
810
|
+
setRefreshing(false);
|
|
811
|
+
}
|
|
812
|
+
useEffect(() => {
|
|
813
|
+
load();
|
|
814
|
+
}, []);
|
|
815
|
+
const activeCount = agents.filter((a) => a.status === "active").length;
|
|
816
|
+
const filtered = agents.filter((a) => {
|
|
817
|
+
if (!search) return true;
|
|
818
|
+
const q = search.toLowerCase();
|
|
819
|
+
const codename = (a.codename || a.name || a.id || "").toLowerCase();
|
|
820
|
+
const role = (a.role || "").toLowerCase();
|
|
821
|
+
return codename.includes(q) || role.includes(q);
|
|
822
|
+
});
|
|
823
|
+
if (loading) {
|
|
824
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
825
|
+
/* @__PURE__ */ jsx("div", { className: "h-8 w-48 animate-pulse rounded-lg bg-white/[0.04]" }),
|
|
826
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: [...Array(4)].map((_, i) => /* @__PURE__ */ jsx("div", { className: "h-20 animate-pulse rounded-lg bg-white/[0.04] border border-white/[0.06]" }, i)) })
|
|
827
|
+
] });
|
|
828
|
+
}
|
|
829
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
830
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-6", children: [
|
|
831
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
832
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-mono font-semibold text-[--cyan] text-glow-cyan", children: "Agents" }),
|
|
833
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground mt-1 font-mono", children: "Manage agent profiles, roles, and assignments" })
|
|
834
|
+
] }),
|
|
835
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
836
|
+
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 text-[10px] font-mono font-medium bg-[--cyan]/10 text-[--cyan] border border-[--cyan]/20", children: [
|
|
837
|
+
agents.length,
|
|
838
|
+
" agents"
|
|
839
|
+
] }),
|
|
840
|
+
activeCount > 0 && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 text-[10px] font-mono font-medium bg-green-500/10 text-green-500 border border-green-500/20", children: [
|
|
841
|
+
/* @__PURE__ */ jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-green-500 animate-pulse" }),
|
|
842
|
+
activeCount,
|
|
843
|
+
" active"
|
|
844
|
+
] }),
|
|
845
|
+
/* @__PURE__ */ jsxs(
|
|
846
|
+
"button",
|
|
847
|
+
{
|
|
848
|
+
onClick: () => setShowCreate(!showCreate),
|
|
849
|
+
className: "inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-mono font-medium bg-[--cyan]/10 text-[--cyan] border border-[--cyan]/20 hover:bg-[--cyan] hover:text-[--primary-foreground] transition-colors",
|
|
850
|
+
children: [
|
|
851
|
+
/* @__PURE__ */ jsx(PlusIcon, { size: 12 }),
|
|
852
|
+
" New Agent"
|
|
853
|
+
]
|
|
854
|
+
}
|
|
855
|
+
),
|
|
856
|
+
/* @__PURE__ */ jsx(
|
|
857
|
+
"button",
|
|
858
|
+
{
|
|
859
|
+
onClick: () => {
|
|
860
|
+
setRefreshing(true);
|
|
861
|
+
load();
|
|
862
|
+
},
|
|
863
|
+
disabled: refreshing,
|
|
864
|
+
className: "inline-flex items-center gap-1.5 rounded-md px-2.5 py-1.5 text-xs font-mono font-medium border border-white/[0.06] text-muted-foreground hover:text-foreground hover:bg-white/[0.04] disabled:opacity-50 transition-colors",
|
|
865
|
+
children: refreshing ? /* @__PURE__ */ jsx(SpinnerIcon, { size: 14 }) : /* @__PURE__ */ jsx(RefreshIcon, { size: 14 })
|
|
866
|
+
}
|
|
867
|
+
)
|
|
868
|
+
] })
|
|
869
|
+
] }),
|
|
870
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-3 mb-6", children: [
|
|
871
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center p-4 rounded-lg border border-white/[0.06] bg-[--card]", children: [
|
|
872
|
+
/* @__PURE__ */ jsx("span", { className: "text-2xl font-semibold text-[--cyan] font-mono", children: agents.length }),
|
|
873
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] text-muted-foreground uppercase tracking-wider mt-1", children: "Agents" })
|
|
874
|
+
] }),
|
|
875
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center p-4 rounded-lg border border-white/[0.06] bg-[--card]", children: [
|
|
876
|
+
/* @__PURE__ */ jsx("span", { className: "text-2xl font-semibold text-green-500 font-mono", children: activeCount }),
|
|
877
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] text-muted-foreground uppercase tracking-wider mt-1", children: "Active" })
|
|
878
|
+
] }),
|
|
879
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center p-4 rounded-lg border border-white/[0.06] bg-[--card]", children: [
|
|
880
|
+
/* @__PURE__ */ jsx("span", { className: "text-2xl font-semibold font-mono text-muted-foreground", children: agents.length - activeCount }),
|
|
881
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] text-muted-foreground uppercase tracking-wider mt-1", children: "Idle" })
|
|
882
|
+
] })
|
|
883
|
+
] }),
|
|
884
|
+
agents.length > 0 && /* @__PURE__ */ jsxs("div", { className: "relative mb-4", children: [
|
|
885
|
+
/* @__PURE__ */ jsx("div", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsx(SearchIcon, { size: 14 }) }),
|
|
886
|
+
/* @__PURE__ */ jsx(
|
|
887
|
+
"input",
|
|
888
|
+
{
|
|
889
|
+
placeholder: "Search agents...",
|
|
890
|
+
value: search,
|
|
891
|
+
onChange: (e) => setSearch(e.target.value),
|
|
892
|
+
className: "w-full text-sm border border-white/[0.06] rounded-md pl-9 pr-3 py-2 bg-black/20 font-mono placeholder:text-muted-foreground/50 focus:outline-none focus:border-[--cyan]/40 focus:ring-1 focus:ring-[--cyan]/20 transition-colors"
|
|
893
|
+
}
|
|
894
|
+
)
|
|
895
|
+
] }),
|
|
896
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: showCreate && /* @__PURE__ */ jsx(CreateAgentForm, { onCreated: () => {
|
|
897
|
+
load();
|
|
898
|
+
setShowCreate(false);
|
|
899
|
+
}, onClose: () => setShowCreate(false) }) }),
|
|
900
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3 mt-4", children: filtered.length === 0 && agents.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-center rounded-lg border border-white/[0.06] bg-[--card]", children: [
|
|
901
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-full bg-[--cyan]/10 p-4 mb-4", children: /* @__PURE__ */ jsx(UsersIcon, { size: 24, className: "text-[--cyan]" }) }),
|
|
902
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono font-medium mb-1", children: "No agents configured" }),
|
|
903
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[11px] text-muted-foreground font-mono max-w-sm", children: [
|
|
904
|
+
"Create agent profiles in the ",
|
|
905
|
+
/* @__PURE__ */ jsx("span", { className: "text-[--cyan]", children: "agents/" }),
|
|
906
|
+
" directory or click \u201CNew Agent\u201D above."
|
|
907
|
+
] })
|
|
908
|
+
] }) : filtered.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center py-12 text-center", children: [
|
|
909
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-full bg-white/[0.04] border border-white/[0.06] p-4 mb-4", children: /* @__PURE__ */ jsx(SearchIcon, { size: 24 }) }),
|
|
910
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono text-muted-foreground", children: "No agents match your search." })
|
|
911
|
+
] }) : filtered.map((agent, i) => /* @__PURE__ */ jsx(AgentCard, { agent, onViewProfile: setViewingProfile, onAssignTask: setAssigningTask, index: i }, agent.id)) }),
|
|
912
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: viewingProfile && /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(AgentProfilePanel, { agentId: viewingProfile, onClose: () => setViewingProfile(null) }) }) }),
|
|
913
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: assigningTask && /* @__PURE__ */ jsx(AssignTaskDialog, { agent: assigningTask, onClose: () => setAssigningTask(null) }) })
|
|
914
|
+
] });
|
|
915
|
+
}
|
|
916
|
+
export {
|
|
917
|
+
AgentsPage
|
|
918
|
+
};
|