@harbinger-ai/harbinger 0.1.1 → 0.1.3
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/chat/actions.js +416 -0
- package/lib/chat/components/agents-page.js +545 -0
- package/lib/chat/components/agents-page.jsx +571 -0
- package/lib/chat/components/app-sidebar.js +33 -1
- package/lib/chat/components/app-sidebar.jsx +37 -1
- package/lib/chat/components/findings-page.js +164 -103
- package/lib/chat/components/findings-page.jsx +156 -101
- package/lib/chat/components/icons.js +62 -0
- package/lib/chat/components/icons.jsx +62 -0
- package/lib/chat/components/index.js +3 -0
- package/lib/chat/components/mcp-page.js +383 -55
- package/lib/chat/components/mcp-page.jsx +404 -101
- package/lib/chat/components/mission-control.js +490 -0
- package/lib/chat/components/mission-control.jsx +618 -0
- package/lib/chat/components/registry-page.js +267 -133
- package/lib/chat/components/registry-page.jsx +299 -138
- 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 +337 -0
- package/lib/chat/components/settings-providers-page.jsx +410 -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 +269 -200
- package/lib/chat/components/targets-page.jsx +181 -111
- package/lib/mcp/actions.js +120 -0
- package/lib/mcp/registry.js +164 -0
- package/package.json +1 -1
|
@@ -0,0 +1,545 @@
|
|
|
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 } from "./icons.js";
|
|
6
|
+
import { getAgentProfilesWithStatus, getAgentProfile, updateAgentFile, createAgent, createAgentJob } from "../actions.js";
|
|
7
|
+
function AgentCard({ agent, onViewProfile, onAssignTask, index }) {
|
|
8
|
+
const codename = agent.codename || agent.name || agent.id;
|
|
9
|
+
const initial = codename.charAt(0).toUpperCase();
|
|
10
|
+
const isActive = agent.status === "active";
|
|
11
|
+
return /* @__PURE__ */ jsx(
|
|
12
|
+
motion.div,
|
|
13
|
+
{
|
|
14
|
+
initial: { opacity: 0, y: 8 },
|
|
15
|
+
animate: { opacity: 1, y: 0 },
|
|
16
|
+
transition: { duration: 0.25, delay: index * 0.04 },
|
|
17
|
+
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]"}`,
|
|
18
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 p-4", children: [
|
|
19
|
+
/* @__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 }) }),
|
|
20
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
21
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
22
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm font-mono font-semibold text-foreground", children: [
|
|
23
|
+
"@",
|
|
24
|
+
codename.toUpperCase()
|
|
25
|
+
] }),
|
|
26
|
+
/* @__PURE__ */ jsx("div", { className: `w-2 h-2 rounded-full shrink-0 ${isActive ? "bg-green-500 animate-pulse" : "bg-muted-foreground/40"}` }),
|
|
27
|
+
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: [
|
|
28
|
+
agent.activeJobs,
|
|
29
|
+
" job",
|
|
30
|
+
agent.activeJobs !== 1 ? "s" : "",
|
|
31
|
+
" running"
|
|
32
|
+
] })
|
|
33
|
+
] }),
|
|
34
|
+
agent.role && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5 font-mono truncate", children: agent.role }),
|
|
35
|
+
agent.specialization && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground/70 mt-0.5 font-mono truncate", children: agent.specialization })
|
|
36
|
+
] }),
|
|
37
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
38
|
+
/* @__PURE__ */ jsx(
|
|
39
|
+
"button",
|
|
40
|
+
{
|
|
41
|
+
onClick: () => onViewProfile(agent.id),
|
|
42
|
+
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",
|
|
43
|
+
children: "View"
|
|
44
|
+
}
|
|
45
|
+
),
|
|
46
|
+
/* @__PURE__ */ jsx(
|
|
47
|
+
"button",
|
|
48
|
+
{
|
|
49
|
+
onClick: () => onAssignTask(agent),
|
|
50
|
+
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",
|
|
51
|
+
children: "Assign Task"
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
] })
|
|
55
|
+
] })
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
function AgentProfilePanel({ agentId, onClose }) {
|
|
60
|
+
const [profile, setProfile] = useState(null);
|
|
61
|
+
const [loading, setLoading] = useState(true);
|
|
62
|
+
const [editingFile, setEditingFile] = useState(null);
|
|
63
|
+
const [editContent, setEditContent] = useState("");
|
|
64
|
+
const [saving, setSaving] = useState(false);
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
setLoading(true);
|
|
67
|
+
getAgentProfile(agentId).then((p) => {
|
|
68
|
+
setProfile(p);
|
|
69
|
+
setLoading(false);
|
|
70
|
+
});
|
|
71
|
+
}, [agentId]);
|
|
72
|
+
async function handleSave() {
|
|
73
|
+
if (!editingFile) return;
|
|
74
|
+
setSaving(true);
|
|
75
|
+
await updateAgentFile(agentId, editingFile, editContent);
|
|
76
|
+
const updated = await getAgentProfile(agentId);
|
|
77
|
+
setProfile(updated);
|
|
78
|
+
setEditingFile(null);
|
|
79
|
+
setSaving(false);
|
|
80
|
+
}
|
|
81
|
+
function startEdit(filename, content) {
|
|
82
|
+
setEditingFile(filename);
|
|
83
|
+
setEditContent(content || "");
|
|
84
|
+
}
|
|
85
|
+
if (loading) {
|
|
86
|
+
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)) }) });
|
|
87
|
+
}
|
|
88
|
+
if (!profile) return null;
|
|
89
|
+
const codename = profile.codename || profile.name || profile.id;
|
|
90
|
+
const files = [
|
|
91
|
+
{ name: "SOUL.md", content: profile.soul, label: "Soul" },
|
|
92
|
+
{ name: "SKILLS.md", content: profile.skills, label: "Skills" },
|
|
93
|
+
{ name: "TOOLS.md", content: profile.tools, label: "Tools" },
|
|
94
|
+
{ name: "HEARTBEAT.md", content: profile.heartbeat, label: "Heartbeat" }
|
|
95
|
+
];
|
|
96
|
+
return /* @__PURE__ */ jsxs(
|
|
97
|
+
motion.div,
|
|
98
|
+
{
|
|
99
|
+
initial: { opacity: 0, y: 12 },
|
|
100
|
+
animate: { opacity: 1, y: 0 },
|
|
101
|
+
exit: { opacity: 0, y: 12 },
|
|
102
|
+
className: "rounded-lg border border-[--cyan]/20 bg-[--card] overflow-hidden",
|
|
103
|
+
children: [
|
|
104
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-4 border-b border-white/[0.06]", children: [
|
|
105
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
106
|
+
/* @__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() }) }),
|
|
107
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
108
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm font-mono font-semibold", children: [
|
|
109
|
+
"@",
|
|
110
|
+
codename.toUpperCase()
|
|
111
|
+
] }),
|
|
112
|
+
profile.role && /* @__PURE__ */ jsx("p", { className: "text-[10px] font-mono text-muted-foreground", children: profile.role })
|
|
113
|
+
] })
|
|
114
|
+
] }),
|
|
115
|
+
/* @__PURE__ */ jsx("button", { onClick: onClose, className: "text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ jsx(XIcon, { size: 16 }) })
|
|
116
|
+
] }),
|
|
117
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4 border-b border-white/[0.06]", children: [
|
|
118
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] font-medium text-[--cyan] uppercase tracking-wider", children: "Identity" }),
|
|
119
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2 mt-2", children: [
|
|
120
|
+
profile.name && /* @__PURE__ */ jsxs("div", { children: [
|
|
121
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-muted-foreground uppercase", children: "Name" }),
|
|
122
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-mono", children: profile.name })
|
|
123
|
+
] }),
|
|
124
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
125
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-muted-foreground uppercase", children: "Codename" }),
|
|
126
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-mono", children: codename })
|
|
127
|
+
] }),
|
|
128
|
+
profile.role && /* @__PURE__ */ jsxs("div", { children: [
|
|
129
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-muted-foreground uppercase", children: "Role" }),
|
|
130
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-mono", children: profile.role })
|
|
131
|
+
] }),
|
|
132
|
+
profile.specialization && /* @__PURE__ */ jsxs("div", { children: [
|
|
133
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-muted-foreground uppercase", children: "Specialization" }),
|
|
134
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-mono", children: profile.specialization })
|
|
135
|
+
] })
|
|
136
|
+
] })
|
|
137
|
+
] }),
|
|
138
|
+
profile.config && /* @__PURE__ */ jsxs("div", { className: "p-4 border-b border-white/[0.06]", children: [
|
|
139
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] font-medium text-[--cyan] uppercase tracking-wider", children: "Config" }),
|
|
140
|
+
/* @__PURE__ */ jsx("pre", { className: "mt-2 text-[11px] bg-black/30 rounded-md p-2.5 font-mono overflow-auto max-h-24 text-foreground/80 border border-white/[0.04]", children: JSON.stringify(profile.config, null, 2) })
|
|
141
|
+
] }),
|
|
142
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4", children: [
|
|
143
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] font-medium text-[--cyan] uppercase tracking-wider", children: "Files" }),
|
|
144
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 mt-2", children: files.map((f) => /* @__PURE__ */ jsxs("div", { children: [
|
|
145
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
146
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-muted-foreground", children: f.name }),
|
|
147
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
148
|
+
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" }),
|
|
149
|
+
/* @__PURE__ */ jsx(
|
|
150
|
+
"button",
|
|
151
|
+
{
|
|
152
|
+
onClick: () => editingFile === f.name ? setEditingFile(null) : startEdit(f.name, f.content),
|
|
153
|
+
className: "text-muted-foreground hover:text-[--cyan] transition-colors p-0.5",
|
|
154
|
+
children: /* @__PURE__ */ jsx(PencilIcon, { size: 10 })
|
|
155
|
+
}
|
|
156
|
+
)
|
|
157
|
+
] })
|
|
158
|
+
] }),
|
|
159
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: editingFile === f.name && /* @__PURE__ */ jsxs(
|
|
160
|
+
motion.div,
|
|
161
|
+
{
|
|
162
|
+
initial: { height: 0, opacity: 0 },
|
|
163
|
+
animate: { height: "auto", opacity: 1 },
|
|
164
|
+
exit: { height: 0, opacity: 0 },
|
|
165
|
+
className: "overflow-hidden",
|
|
166
|
+
children: [
|
|
167
|
+
/* @__PURE__ */ jsx(
|
|
168
|
+
"textarea",
|
|
169
|
+
{
|
|
170
|
+
value: editContent,
|
|
171
|
+
onChange: (e) => setEditContent(e.target.value),
|
|
172
|
+
rows: 8,
|
|
173
|
+
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"
|
|
174
|
+
}
|
|
175
|
+
),
|
|
176
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 mt-1", children: [
|
|
177
|
+
/* @__PURE__ */ jsxs(
|
|
178
|
+
"button",
|
|
179
|
+
{
|
|
180
|
+
onClick: handleSave,
|
|
181
|
+
disabled: saving,
|
|
182
|
+
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",
|
|
183
|
+
children: [
|
|
184
|
+
saving ? /* @__PURE__ */ jsx(SpinnerIcon, { size: 10 }) : /* @__PURE__ */ jsx(CheckIcon, { size: 10 }),
|
|
185
|
+
" Save"
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
),
|
|
189
|
+
/* @__PURE__ */ jsx(
|
|
190
|
+
"button",
|
|
191
|
+
{
|
|
192
|
+
onClick: () => setEditingFile(null),
|
|
193
|
+
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",
|
|
194
|
+
children: "Cancel"
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
] })
|
|
198
|
+
]
|
|
199
|
+
}
|
|
200
|
+
) })
|
|
201
|
+
] }, f.name)) })
|
|
202
|
+
] })
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
function AssignTaskDialog({ agent, onClose }) {
|
|
208
|
+
const [prompt, setPrompt] = useState("");
|
|
209
|
+
const [submitting, setSubmitting] = useState(false);
|
|
210
|
+
const [result, setResult] = useState(null);
|
|
211
|
+
async function handleSubmit() {
|
|
212
|
+
if (!prompt) return;
|
|
213
|
+
setSubmitting(true);
|
|
214
|
+
const res = await createAgentJob(agent.id, prompt);
|
|
215
|
+
setResult(res);
|
|
216
|
+
setSubmitting(false);
|
|
217
|
+
}
|
|
218
|
+
const codename = agent.codename || agent.name || agent.id;
|
|
219
|
+
return /* @__PURE__ */ jsxs(
|
|
220
|
+
motion.div,
|
|
221
|
+
{
|
|
222
|
+
initial: { opacity: 0, scale: 0.95 },
|
|
223
|
+
animate: { opacity: 1, scale: 1 },
|
|
224
|
+
exit: { opacity: 0, scale: 0.95 },
|
|
225
|
+
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
226
|
+
children: [
|
|
227
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/60", onClick: onClose }),
|
|
228
|
+
/* @__PURE__ */ jsxs("div", { className: "relative w-full max-w-lg rounded-lg border border-[--cyan]/20 bg-[--card] shadow-2xl", children: [
|
|
229
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-4 border-b border-white/[0.06]", children: [
|
|
230
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
231
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
232
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#ff5f57]" }),
|
|
233
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#febc2e]" }),
|
|
234
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#28c840]" })
|
|
235
|
+
] }),
|
|
236
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono text-[10px] font-medium text-[--cyan] uppercase tracking-wider ml-1", children: [
|
|
237
|
+
"Assign Task to @",
|
|
238
|
+
codename.toUpperCase()
|
|
239
|
+
] })
|
|
240
|
+
] }),
|
|
241
|
+
/* @__PURE__ */ jsx("button", { onClick: onClose, className: "text-muted-foreground hover:text-foreground", children: /* @__PURE__ */ jsx(XIcon, { size: 14 }) })
|
|
242
|
+
] }),
|
|
243
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4", children: [
|
|
244
|
+
/* @__PURE__ */ jsx(
|
|
245
|
+
"textarea",
|
|
246
|
+
{
|
|
247
|
+
value: prompt,
|
|
248
|
+
onChange: (e) => setPrompt(e.target.value),
|
|
249
|
+
placeholder: "Describe the task for this agent...",
|
|
250
|
+
rows: 4,
|
|
251
|
+
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",
|
|
252
|
+
autoFocus: true
|
|
253
|
+
}
|
|
254
|
+
),
|
|
255
|
+
result && /* @__PURE__ */ jsx("p", { className: `text-xs font-mono mt-2 ${result.error ? "text-[--destructive]" : "text-green-500"}`, children: result.error || "Job created successfully" }),
|
|
256
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 mt-3", children: [
|
|
257
|
+
/* @__PURE__ */ jsx(
|
|
258
|
+
"button",
|
|
259
|
+
{
|
|
260
|
+
onClick: onClose,
|
|
261
|
+
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",
|
|
262
|
+
children: "Cancel"
|
|
263
|
+
}
|
|
264
|
+
),
|
|
265
|
+
/* @__PURE__ */ jsx(
|
|
266
|
+
"button",
|
|
267
|
+
{
|
|
268
|
+
onClick: handleSubmit,
|
|
269
|
+
disabled: submitting || !prompt,
|
|
270
|
+
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",
|
|
271
|
+
children: submitting ? /* @__PURE__ */ jsx(SpinnerIcon, { size: 12 }) : "Create Job"
|
|
272
|
+
}
|
|
273
|
+
)
|
|
274
|
+
] })
|
|
275
|
+
] })
|
|
276
|
+
] })
|
|
277
|
+
]
|
|
278
|
+
}
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
function CreateAgentForm({ onCreated, onClose }) {
|
|
282
|
+
const [name, setName] = useState("");
|
|
283
|
+
const [codename, setCodename] = useState("");
|
|
284
|
+
const [role, setRole] = useState("");
|
|
285
|
+
const [specialization, setSpecialization] = useState("");
|
|
286
|
+
const [soul, setSoul] = useState("");
|
|
287
|
+
const [creating, setCreating] = useState(false);
|
|
288
|
+
const [result, setResult] = useState(null);
|
|
289
|
+
async function handleCreate() {
|
|
290
|
+
if (!name && !codename) return;
|
|
291
|
+
setCreating(true);
|
|
292
|
+
const res = await createAgent({ name, codename, role, specialization, soul });
|
|
293
|
+
setResult(res);
|
|
294
|
+
if (!res.error) {
|
|
295
|
+
onCreated();
|
|
296
|
+
setName("");
|
|
297
|
+
setCodename("");
|
|
298
|
+
setRole("");
|
|
299
|
+
setSpecialization("");
|
|
300
|
+
setSoul("");
|
|
301
|
+
}
|
|
302
|
+
setCreating(false);
|
|
303
|
+
}
|
|
304
|
+
return /* @__PURE__ */ jsx(
|
|
305
|
+
motion.div,
|
|
306
|
+
{
|
|
307
|
+
initial: { opacity: 0, height: 0 },
|
|
308
|
+
animate: { opacity: 1, height: "auto" },
|
|
309
|
+
exit: { opacity: 0, height: 0 },
|
|
310
|
+
className: "overflow-hidden",
|
|
311
|
+
children: /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-[--cyan]/20 bg-[--card] p-5 mt-4", children: [
|
|
312
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
|
|
313
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
314
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
315
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#ff5f57]" }),
|
|
316
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#febc2e]" }),
|
|
317
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#28c840]" })
|
|
318
|
+
] }),
|
|
319
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] font-medium text-[--cyan] uppercase tracking-wider ml-1", children: "Create New Agent" })
|
|
320
|
+
] }),
|
|
321
|
+
/* @__PURE__ */ jsx("button", { onClick: onClose, className: "text-muted-foreground hover:text-foreground", children: /* @__PURE__ */ jsx(XIcon, { size: 14 }) })
|
|
322
|
+
] }),
|
|
323
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3 mb-3", children: [
|
|
324
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
325
|
+
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-mono font-medium text-muted-foreground uppercase tracking-wider mb-1", children: "Name" }),
|
|
326
|
+
/* @__PURE__ */ jsx(
|
|
327
|
+
"input",
|
|
328
|
+
{
|
|
329
|
+
value: name,
|
|
330
|
+
onChange: (e) => setName(e.target.value),
|
|
331
|
+
placeholder: "Reaper",
|
|
332
|
+
className: "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"
|
|
333
|
+
}
|
|
334
|
+
)
|
|
335
|
+
] }),
|
|
336
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
337
|
+
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-mono font-medium text-muted-foreground uppercase tracking-wider mb-1", children: "Codename" }),
|
|
338
|
+
/* @__PURE__ */ jsx(
|
|
339
|
+
"input",
|
|
340
|
+
{
|
|
341
|
+
value: codename,
|
|
342
|
+
onChange: (e) => setCodename(e.target.value),
|
|
343
|
+
placeholder: "REAPER",
|
|
344
|
+
className: "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"
|
|
345
|
+
}
|
|
346
|
+
)
|
|
347
|
+
] }),
|
|
348
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
349
|
+
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-mono font-medium text-muted-foreground uppercase tracking-wider mb-1", children: "Role" }),
|
|
350
|
+
/* @__PURE__ */ jsx(
|
|
351
|
+
"input",
|
|
352
|
+
{
|
|
353
|
+
value: role,
|
|
354
|
+
onChange: (e) => setRole(e.target.value),
|
|
355
|
+
placeholder: "Autonomous Recon Specialist",
|
|
356
|
+
className: "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"
|
|
357
|
+
}
|
|
358
|
+
)
|
|
359
|
+
] }),
|
|
360
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
361
|
+
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-mono font-medium text-muted-foreground uppercase tracking-wider mb-1", children: "Specialization" }),
|
|
362
|
+
/* @__PURE__ */ jsx(
|
|
363
|
+
"input",
|
|
364
|
+
{
|
|
365
|
+
value: specialization,
|
|
366
|
+
onChange: (e) => setSpecialization(e.target.value),
|
|
367
|
+
placeholder: "Bug Bounty Recon",
|
|
368
|
+
className: "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"
|
|
369
|
+
}
|
|
370
|
+
)
|
|
371
|
+
] })
|
|
372
|
+
] }),
|
|
373
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3", children: [
|
|
374
|
+
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-mono font-medium text-muted-foreground uppercase tracking-wider mb-1", children: "SOUL.md (optional)" }),
|
|
375
|
+
/* @__PURE__ */ jsx(
|
|
376
|
+
"textarea",
|
|
377
|
+
{
|
|
378
|
+
value: soul,
|
|
379
|
+
onChange: (e) => setSoul(e.target.value),
|
|
380
|
+
rows: 4,
|
|
381
|
+
placeholder: "Agent personality and system prompt...",
|
|
382
|
+
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"
|
|
383
|
+
}
|
|
384
|
+
)
|
|
385
|
+
] }),
|
|
386
|
+
result && /* @__PURE__ */ jsx("p", { className: `text-xs font-mono mb-2 ${result.error ? "text-[--destructive]" : "text-green-500"}`, children: result.error || `Agent created: ${result.id}` }),
|
|
387
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
388
|
+
/* @__PURE__ */ jsxs(
|
|
389
|
+
"button",
|
|
390
|
+
{
|
|
391
|
+
onClick: handleCreate,
|
|
392
|
+
disabled: creating || !name && !codename,
|
|
393
|
+
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",
|
|
394
|
+
children: [
|
|
395
|
+
creating ? /* @__PURE__ */ jsx(SpinnerIcon, { size: 12 }) : /* @__PURE__ */ jsx(PlusIcon, { size: 12 }),
|
|
396
|
+
" Create Agent"
|
|
397
|
+
]
|
|
398
|
+
}
|
|
399
|
+
),
|
|
400
|
+
/* @__PURE__ */ jsx(
|
|
401
|
+
"button",
|
|
402
|
+
{
|
|
403
|
+
onClick: onClose,
|
|
404
|
+
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",
|
|
405
|
+
children: "Cancel"
|
|
406
|
+
}
|
|
407
|
+
)
|
|
408
|
+
] })
|
|
409
|
+
] })
|
|
410
|
+
}
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
function AgentsPage() {
|
|
414
|
+
const [agents, setAgents] = useState([]);
|
|
415
|
+
const [loading, setLoading] = useState(true);
|
|
416
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
417
|
+
const [viewingProfile, setViewingProfile] = useState(null);
|
|
418
|
+
const [assigningTask, setAssigningTask] = useState(null);
|
|
419
|
+
const [showCreate, setShowCreate] = useState(false);
|
|
420
|
+
const [search, setSearch] = useState("");
|
|
421
|
+
async function load() {
|
|
422
|
+
try {
|
|
423
|
+
const a = await getAgentProfilesWithStatus();
|
|
424
|
+
setAgents(a);
|
|
425
|
+
} catch {
|
|
426
|
+
}
|
|
427
|
+
setLoading(false);
|
|
428
|
+
setRefreshing(false);
|
|
429
|
+
}
|
|
430
|
+
useEffect(() => {
|
|
431
|
+
load();
|
|
432
|
+
}, []);
|
|
433
|
+
const activeCount = agents.filter((a) => a.status === "active").length;
|
|
434
|
+
const filtered = agents.filter((a) => {
|
|
435
|
+
if (!search) return true;
|
|
436
|
+
const q = search.toLowerCase();
|
|
437
|
+
const codename = (a.codename || a.name || a.id || "").toLowerCase();
|
|
438
|
+
const role = (a.role || "").toLowerCase();
|
|
439
|
+
return codename.includes(q) || role.includes(q);
|
|
440
|
+
});
|
|
441
|
+
if (loading) {
|
|
442
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 p-6", children: [
|
|
443
|
+
/* @__PURE__ */ jsx("div", { className: "h-8 w-48 animate-pulse rounded-lg bg-white/[0.04]" }),
|
|
444
|
+
/* @__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)) })
|
|
445
|
+
] });
|
|
446
|
+
}
|
|
447
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
448
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-6", children: [
|
|
449
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
450
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-mono font-semibold text-[--cyan] text-glow-cyan", children: "Agents" }),
|
|
451
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground mt-1 font-mono", children: "Manage agent profiles, roles, and assignments" })
|
|
452
|
+
] }),
|
|
453
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
454
|
+
/* @__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: [
|
|
455
|
+
agents.length,
|
|
456
|
+
" agents"
|
|
457
|
+
] }),
|
|
458
|
+
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: [
|
|
459
|
+
/* @__PURE__ */ jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-green-500 animate-pulse" }),
|
|
460
|
+
activeCount,
|
|
461
|
+
" active"
|
|
462
|
+
] }),
|
|
463
|
+
/* @__PURE__ */ jsxs(
|
|
464
|
+
"button",
|
|
465
|
+
{
|
|
466
|
+
onClick: () => setShowCreate(!showCreate),
|
|
467
|
+
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",
|
|
468
|
+
children: [
|
|
469
|
+
/* @__PURE__ */ jsx(PlusIcon, { size: 12 }),
|
|
470
|
+
" New Agent"
|
|
471
|
+
]
|
|
472
|
+
}
|
|
473
|
+
),
|
|
474
|
+
/* @__PURE__ */ jsx(
|
|
475
|
+
"button",
|
|
476
|
+
{
|
|
477
|
+
onClick: () => {
|
|
478
|
+
setRefreshing(true);
|
|
479
|
+
load();
|
|
480
|
+
},
|
|
481
|
+
disabled: refreshing,
|
|
482
|
+
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",
|
|
483
|
+
children: refreshing ? /* @__PURE__ */ jsx(SpinnerIcon, { size: 14 }) : /* @__PURE__ */ jsx(RefreshIcon, { size: 14 })
|
|
484
|
+
}
|
|
485
|
+
)
|
|
486
|
+
] })
|
|
487
|
+
] }),
|
|
488
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-3 mb-6", children: [
|
|
489
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center p-4 rounded-lg border border-white/[0.06] bg-[--card]", children: [
|
|
490
|
+
/* @__PURE__ */ jsx("span", { className: "text-2xl font-semibold text-[--cyan] font-mono", children: agents.length }),
|
|
491
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] text-muted-foreground uppercase tracking-wider mt-1", children: "Agents" })
|
|
492
|
+
] }),
|
|
493
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center p-4 rounded-lg border border-white/[0.06] bg-[--card]", children: [
|
|
494
|
+
/* @__PURE__ */ jsx("span", { className: "text-2xl font-semibold text-green-500 font-mono", children: activeCount }),
|
|
495
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] text-muted-foreground uppercase tracking-wider mt-1", children: "Active" })
|
|
496
|
+
] }),
|
|
497
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center p-4 rounded-lg border border-white/[0.06] bg-[--card]", children: [
|
|
498
|
+
/* @__PURE__ */ jsx("span", { className: "text-2xl font-semibold font-mono text-muted-foreground", children: agents.length - activeCount }),
|
|
499
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] text-muted-foreground uppercase tracking-wider mt-1", children: "Idle" })
|
|
500
|
+
] })
|
|
501
|
+
] }),
|
|
502
|
+
agents.length > 0 && /* @__PURE__ */ jsxs("div", { className: "relative mb-4", children: [
|
|
503
|
+
/* @__PURE__ */ jsx("div", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsx(SearchIcon, { size: 14 }) }),
|
|
504
|
+
/* @__PURE__ */ jsx(
|
|
505
|
+
"input",
|
|
506
|
+
{
|
|
507
|
+
placeholder: "Search agents...",
|
|
508
|
+
value: search,
|
|
509
|
+
onChange: (e) => setSearch(e.target.value),
|
|
510
|
+
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"
|
|
511
|
+
}
|
|
512
|
+
)
|
|
513
|
+
] }),
|
|
514
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: showCreate && /* @__PURE__ */ jsx(CreateAgentForm, { onCreated: () => {
|
|
515
|
+
load();
|
|
516
|
+
setShowCreate(false);
|
|
517
|
+
}, onClose: () => setShowCreate(false) }) }),
|
|
518
|
+
/* @__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: [
|
|
519
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-full bg-[--cyan]/10 p-4 mb-4", children: /* @__PURE__ */ jsx(UsersIcon, { size: 24, className: "text-[--cyan]" }) }),
|
|
520
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono font-medium mb-1", children: "No agents configured" }),
|
|
521
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[11px] text-muted-foreground font-mono max-w-sm", children: [
|
|
522
|
+
"Create agent profiles in the ",
|
|
523
|
+
/* @__PURE__ */ jsx("span", { className: "text-[--cyan]", children: "agents/" }),
|
|
524
|
+
' directory or click "New Agent" above.'
|
|
525
|
+
] })
|
|
526
|
+
] }) : filtered.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center py-12 text-center", children: [
|
|
527
|
+
/* @__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 }) }),
|
|
528
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono text-muted-foreground", children: "No agents match your search." })
|
|
529
|
+
] }) : filtered.map((agent, i) => /* @__PURE__ */ jsx(
|
|
530
|
+
AgentCard,
|
|
531
|
+
{
|
|
532
|
+
agent,
|
|
533
|
+
onViewProfile: setViewingProfile,
|
|
534
|
+
onAssignTask: setAssigningTask,
|
|
535
|
+
index: i
|
|
536
|
+
},
|
|
537
|
+
agent.id
|
|
538
|
+
)) }),
|
|
539
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: viewingProfile && /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(AgentProfilePanel, { agentId: viewingProfile, onClose: () => setViewingProfile(null) }) }) }),
|
|
540
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: assigningTask && /* @__PURE__ */ jsx(AssignTaskDialog, { agent: assigningTask, onClose: () => setAssigningTask(null) }) })
|
|
541
|
+
] });
|
|
542
|
+
}
|
|
543
|
+
export {
|
|
544
|
+
AgentsPage
|
|
545
|
+
};
|