@harbinger-ai/harbinger 0.1.0 → 0.1.2
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/bin/cli.js +40 -38
- package/bin/local.sh +2 -2
- package/bin/postinstall.js +8 -8
- package/config/index.js +6 -3
- package/config/instrumentation.js +11 -11
- package/lib/chat/actions.js +19 -0
- package/lib/chat/components/app-sidebar.js +17 -1
- package/lib/chat/components/app-sidebar.jsx +19 -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 +22 -0
- package/lib/chat/components/icons.jsx +20 -0
- package/lib/chat/components/index.js +1 -0
- 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/sidebar-user-nav.js +1 -1
- package/lib/chat/components/sidebar-user-nav.jsx +1 -1
- package/lib/chat/components/targets-page.js +269 -200
- package/lib/chat/components/targets-page.jsx +181 -111
- package/lib/chat/components/upgrade-dialog.js +2 -2
- package/lib/chat/components/upgrade-dialog.jsx +2 -2
- package/lib/cron.js +11 -7
- package/lib/db/index.js +6 -1
- package/lib/mcp/actions.js +1 -1
- package/lib/mcp/handler.js +2 -2
- package/lib/mcp/server.js +1 -1
- package/lib/paths.js +1 -1
- package/package.json +1 -1
- package/templates/.env.example +4 -4
- package/templates/.github/workflows/rebuild-event-handler.yml +20 -20
- package/templates/.github/workflows/run-job.yml +6 -6
- package/templates/.github/workflows/upgrade-event-handler.yml +12 -12
- package/templates/CLAUDE.md +3 -3
- package/templates/CLAUDE.md.template +9 -9
- package/templates/app/api/[...thepopebot]/route.js +1 -1
- package/templates/app/api/auth/[...nextauth]/route.js +1 -1
- package/templates/app/chat/[chatId]/page.js +2 -2
- package/templates/app/chats/page.js +2 -2
- package/templates/app/components/setup-form.jsx +1 -1
- package/templates/app/findings/page.js +2 -2
- package/templates/app/globals.css +1 -1
- package/templates/app/layout.js +1 -1
- package/templates/app/login/page.js +1 -1
- package/templates/app/notifications/page.js +2 -2
- package/templates/app/page.js +2 -2
- package/templates/app/settings/crons/page.js +1 -1
- package/templates/app/settings/layout.js +2 -2
- package/templates/app/settings/mcp/page.js +1 -1
- package/templates/app/settings/secrets/page.js +1 -1
- package/templates/app/settings/triggers/page.js +1 -1
- package/templates/app/stream/chat/route.js +1 -1
- package/templates/app/swarm/page.js +2 -2
- package/templates/app/targets/page.js +2 -2
- package/templates/app/toolbox/page.js +2 -2
- package/templates/config/AGENT.md +2 -2
- package/templates/config/EVENT_HANDLER.md +3 -3
- package/templates/config/SKILL_BUILDING_GUIDE.md +1 -1
- package/templates/config/SOUL.md +1 -1
- package/templates/docker/event-handler/Dockerfile +1 -1
- package/templates/docker-compose.yml +2 -2
- package/templates/instrumentation.js +1 -1
- package/templates/middleware.js +1 -1
- package/templates/next.config.mjs +2 -2
|
@@ -1,80 +1,122 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect } from "react";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { motion, AnimatePresence } from "framer-motion";
|
|
5
|
+
import { PackageIcon, SearchIcon, SpinnerIcon, CheckIcon, ChevronDownIcon, DownloadIcon, GlobeIcon } from "./icons.js";
|
|
6
|
+
import { getCatalog, getInstalledTools, installTool, uninstallTool, toggleTool, installFromGithub, getContainers, spawnContainer, stopContainer } from "../../registry/actions.js";
|
|
6
7
|
const CATEGORY_COLORS = {
|
|
7
|
-
recon: "bg-blue-500/10 text-blue-500",
|
|
8
|
-
scanning: "bg-cyan-500/10 text-cyan-500",
|
|
9
|
-
web: "bg-orange-500/10 text-orange-500",
|
|
10
|
-
osint: "bg-green-500/10 text-green-500",
|
|
11
|
-
cloud: "bg-purple-500/10 text-purple-500",
|
|
12
|
-
credential: "bg-red-500/10 text-red-500",
|
|
13
|
-
exploitation: "bg-red-600/10 text-red-600",
|
|
14
|
-
binary: "bg-gray-500/10 text-gray-500",
|
|
15
|
-
forensics: "bg-yellow-500/10 text-yellow-500",
|
|
16
|
-
automation: "bg-indigo-500/10 text-indigo-500",
|
|
17
|
-
custom: "bg-
|
|
8
|
+
recon: { bg: "bg-blue-500/10", text: "text-blue-500", border: "border-blue-500/20" },
|
|
9
|
+
scanning: { bg: "bg-cyan-500/10", text: "text-cyan-500", border: "border-cyan-500/20" },
|
|
10
|
+
web: { bg: "bg-orange-500/10", text: "text-orange-500", border: "border-orange-500/20" },
|
|
11
|
+
osint: { bg: "bg-green-500/10", text: "text-green-500", border: "border-green-500/20" },
|
|
12
|
+
cloud: { bg: "bg-purple-500/10", text: "text-purple-500", border: "border-purple-500/20" },
|
|
13
|
+
credential: { bg: "bg-red-500/10", text: "text-red-500", border: "border-red-500/20" },
|
|
14
|
+
exploitation: { bg: "bg-red-600/10", text: "text-red-600", border: "border-red-600/20" },
|
|
15
|
+
binary: { bg: "bg-gray-500/10", text: "text-gray-500", border: "border-gray-500/20" },
|
|
16
|
+
forensics: { bg: "bg-yellow-500/10", text: "text-yellow-500", border: "border-yellow-500/20" },
|
|
17
|
+
automation: { bg: "bg-indigo-500/10", text: "text-indigo-500", border: "border-indigo-500/20" },
|
|
18
|
+
custom: { bg: "bg-white/5", text: "text-muted-foreground", border: "border-white/10" }
|
|
18
19
|
};
|
|
19
|
-
function
|
|
20
|
-
return
|
|
21
|
-
/* @__PURE__ */ jsx("div", { className: "shrink-0 rounded-md bg-muted p-2", children: /* @__PURE__ */ jsx(PackageIcon, { size: 14 }) }),
|
|
22
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
23
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: tool.name }),
|
|
24
|
-
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground mt-0.5 line-clamp-1", children: tool.description })
|
|
25
|
-
] }),
|
|
26
|
-
/* @__PURE__ */ jsx("span", { className: `shrink-0 inline-flex rounded-full px-2 py-0.5 text-[10px] font-medium ${CATEGORY_COLORS[tool.category] || CATEGORY_COLORS.custom}`, children: tool.category }),
|
|
27
|
-
installed ? /* @__PURE__ */ jsxs("span", { className: "shrink-0 inline-flex items-center gap-1 rounded-full bg-green-500/10 text-green-500 px-2 py-0.5 text-[10px] font-medium", children: [
|
|
28
|
-
/* @__PURE__ */ jsx(CheckIcon, { size: 10 }),
|
|
29
|
-
" installed"
|
|
30
|
-
] }) : /* @__PURE__ */ jsxs(
|
|
31
|
-
"button",
|
|
32
|
-
{
|
|
33
|
-
onClick: () => onInstall(tool.id),
|
|
34
|
-
disabled: installing === tool.id,
|
|
35
|
-
className: "shrink-0 inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium border hover:bg-accent/50 transition-colors disabled:opacity-50",
|
|
36
|
-
children: [
|
|
37
|
-
installing === tool.id ? /* @__PURE__ */ jsx(SpinnerIcon, { size: 12 }) : /* @__PURE__ */ jsx(DownloadIcon, { size: 12 }),
|
|
38
|
-
"Install"
|
|
39
|
-
]
|
|
40
|
-
}
|
|
41
|
-
)
|
|
42
|
-
] });
|
|
20
|
+
function getCatStyle(cat) {
|
|
21
|
+
return CATEGORY_COLORS[cat] || CATEGORY_COLORS.custom;
|
|
43
22
|
}
|
|
44
|
-
function
|
|
23
|
+
function CatalogToolCard({ tool, installed, onInstall, installing, index }) {
|
|
24
|
+
const cat = getCatStyle(tool.category);
|
|
25
|
+
return /* @__PURE__ */ jsx(
|
|
26
|
+
motion.div,
|
|
27
|
+
{
|
|
28
|
+
initial: { opacity: 0, y: 8 },
|
|
29
|
+
animate: { opacity: 1, y: 0 },
|
|
30
|
+
transition: { duration: 0.25, delay: index * 0.02 },
|
|
31
|
+
className: "group rounded-lg border border-white/[0.06] bg-[--card] hover:border-[--cyan]/20 transition-colors overflow-hidden",
|
|
32
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 p-3", children: [
|
|
33
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
|
|
34
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#ff5f57]" }),
|
|
35
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#febc2e]" }),
|
|
36
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#28c840]" })
|
|
37
|
+
] }),
|
|
38
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
39
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono font-medium truncate", children: tool.name }),
|
|
40
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground mt-0.5 line-clamp-1 font-mono", children: tool.description })
|
|
41
|
+
] }),
|
|
42
|
+
/* @__PURE__ */ jsx("span", { className: `shrink-0 inline-flex rounded-full px-2 py-0.5 text-[10px] font-mono font-medium border ${cat.bg} ${cat.text} ${cat.border}`, children: tool.category }),
|
|
43
|
+
installed ? /* @__PURE__ */ jsxs("span", { className: "shrink-0 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-[10px] font-mono font-medium", children: [
|
|
44
|
+
/* @__PURE__ */ jsx(CheckIcon, { size: 10 }),
|
|
45
|
+
" installed"
|
|
46
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
47
|
+
"button",
|
|
48
|
+
{
|
|
49
|
+
onClick: () => onInstall(tool.id),
|
|
50
|
+
disabled: installing === tool.id,
|
|
51
|
+
className: "shrink-0 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 disabled:opacity-50",
|
|
52
|
+
children: [
|
|
53
|
+
installing === tool.id ? /* @__PURE__ */ jsx(SpinnerIcon, { size: 12 }) : /* @__PURE__ */ jsx(DownloadIcon, { size: 12 }),
|
|
54
|
+
"Install"
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
] })
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
function InstalledToolCard({ tool, onUninstall, onToggle, onSpawn, index }) {
|
|
45
63
|
const [expanded, setExpanded] = useState(false);
|
|
46
64
|
const disabled = !tool.enabled;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
const cat = getCatStyle(tool.category);
|
|
66
|
+
return /* @__PURE__ */ jsxs(
|
|
67
|
+
motion.div,
|
|
68
|
+
{
|
|
69
|
+
initial: { opacity: 0, y: 8 },
|
|
70
|
+
animate: { opacity: 1, y: 0 },
|
|
71
|
+
transition: { duration: 0.25, delay: index * 0.03 },
|
|
72
|
+
className: `rounded-lg border border-white/[0.06] bg-[--card] transition-all ${disabled ? "opacity-50" : "hover:border-[--cyan]/20"}`,
|
|
73
|
+
children: [
|
|
74
|
+
/* @__PURE__ */ jsxs("button", { onClick: () => setExpanded(!expanded), className: "flex items-center gap-3 w-full text-left p-3 hover:bg-white/[0.02] rounded-lg transition-colors", children: [
|
|
75
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
|
|
76
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#ff5f57]" }),
|
|
77
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#febc2e]" }),
|
|
78
|
+
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-[#28c840]" })
|
|
79
|
+
] }),
|
|
80
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
81
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono font-medium truncate", children: tool.name }),
|
|
82
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground mt-0.5 truncate font-mono", children: tool.description })
|
|
83
|
+
] }),
|
|
84
|
+
/* @__PURE__ */ jsx("span", { className: `shrink-0 inline-flex rounded-full px-2 py-0.5 text-[10px] font-mono font-medium border ${cat.bg} ${cat.text} ${cat.border}`, children: tool.category }),
|
|
85
|
+
/* @__PURE__ */ jsx("span", { className: `shrink-0 transition-transform ${expanded ? "rotate-180" : ""}`, children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: 14 }) })
|
|
86
|
+
] }),
|
|
87
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: expanded && /* @__PURE__ */ jsx(
|
|
88
|
+
motion.div,
|
|
89
|
+
{
|
|
90
|
+
initial: { height: 0, opacity: 0 },
|
|
91
|
+
animate: { height: "auto", opacity: 1 },
|
|
92
|
+
exit: { height: 0, opacity: 0 },
|
|
93
|
+
transition: { duration: 0.2 },
|
|
94
|
+
className: "overflow-hidden",
|
|
95
|
+
children: /* @__PURE__ */ jsxs("div", { className: "border-t border-white/[0.06] px-4 py-3 flex flex-col gap-2", children: [
|
|
96
|
+
tool.dockerImage && /* @__PURE__ */ jsxs("div", { className: "flex gap-2 text-xs items-baseline", children: [
|
|
97
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground font-mono text-[10px] uppercase tracking-wider w-14 shrink-0", children: "Docker" }),
|
|
98
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-foreground/80", children: tool.dockerImage })
|
|
99
|
+
] }),
|
|
100
|
+
tool.installCmd && /* @__PURE__ */ jsxs("div", { className: "flex gap-2 text-xs items-baseline", children: [
|
|
101
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground font-mono text-[10px] uppercase tracking-wider w-14 shrink-0", children: "Install" }),
|
|
102
|
+
/* @__PURE__ */ jsx("code", { className: "font-mono bg-black/30 px-2 py-0.5 rounded text-[11px] text-foreground/80 border border-white/[0.04] break-all", children: tool.installCmd })
|
|
103
|
+
] }),
|
|
104
|
+
tool.sourceUrl && /* @__PURE__ */ jsxs("div", { className: "flex gap-2 text-xs items-baseline", children: [
|
|
105
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground font-mono text-[10px] uppercase tracking-wider w-14 shrink-0", children: "Source" }),
|
|
106
|
+
/* @__PURE__ */ jsx("a", { href: tool.sourceUrl, target: "_blank", rel: "noopener", className: "text-[--cyan] hover:underline truncate font-mono", children: tool.sourceUrl })
|
|
107
|
+
] }),
|
|
108
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
|
|
109
|
+
tool.dockerImage && /* @__PURE__ */ jsx("button", { onClick: () => onSpawn(tool.id), 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", children: "Spawn" }),
|
|
110
|
+
/* @__PURE__ */ jsx("button", { onClick: () => onToggle(tool.id), 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] transition-colors", children: tool.enabled ? "Disable" : "Enable" }),
|
|
111
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
112
|
+
/* @__PURE__ */ jsx("button", { onClick: () => onUninstall(tool.id), className: "text-xs font-mono text-muted-foreground hover:text-[--destructive] transition-colors", children: "Uninstall" })
|
|
113
|
+
] })
|
|
114
|
+
] })
|
|
115
|
+
}
|
|
116
|
+
) })
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
);
|
|
78
120
|
}
|
|
79
121
|
function GithubInstaller({ onInstall }) {
|
|
80
122
|
const [url, setUrl] = useState("");
|
|
@@ -89,17 +131,36 @@ function GithubInstaller({ onInstall }) {
|
|
|
89
131
|
setLoading(false);
|
|
90
132
|
if (!res.error) setUrl("");
|
|
91
133
|
}
|
|
92
|
-
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4 mb-4", children: [
|
|
93
|
-
/* @__PURE__ */
|
|
94
|
-
|
|
134
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-white/[0.06] bg-[--card] p-4 mb-4", children: [
|
|
135
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
136
|
+
/* @__PURE__ */ jsx(GlobeIcon, { size: 14 }),
|
|
137
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono font-medium text-[--cyan]", children: "Install from GitHub" })
|
|
138
|
+
] }),
|
|
139
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground mb-3 font-mono", children: "Paste a GitHub repository URL to add any tool to your registry." }),
|
|
95
140
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
96
|
-
/* @__PURE__ */ jsx(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
141
|
+
/* @__PURE__ */ jsx(
|
|
142
|
+
"input",
|
|
143
|
+
{
|
|
144
|
+
placeholder: "https://github.com/owner/repo",
|
|
145
|
+
value: url,
|
|
146
|
+
onChange: (e) => setUrl(e.target.value),
|
|
147
|
+
className: "flex-1 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"
|
|
148
|
+
}
|
|
149
|
+
),
|
|
150
|
+
/* @__PURE__ */ jsxs(
|
|
151
|
+
"button",
|
|
152
|
+
{
|
|
153
|
+
onClick: handleInstall,
|
|
154
|
+
disabled: loading || !url,
|
|
155
|
+
className: "inline-flex items-center gap-1.5 rounded-md px-4 py-2 text-xs font-mono font-medium bg-[--cyan] text-[--primary-foreground] hover:opacity-90 disabled:opacity-50 transition-opacity",
|
|
156
|
+
children: [
|
|
157
|
+
loading ? /* @__PURE__ */ jsx(SpinnerIcon, { size: 12 }) : /* @__PURE__ */ jsx(DownloadIcon, { size: 12 }),
|
|
158
|
+
" Install"
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
)
|
|
101
162
|
] }),
|
|
102
|
-
result && /* @__PURE__ */ jsx("p", { className: `text-xs mt-2 ${result.error ? "text-destructive" : "text-green-500"}`, children: result.error || `Installed ${result.name}` })
|
|
163
|
+
result && /* @__PURE__ */ jsx("p", { className: `text-xs font-mono mt-2 ${result.error ? "text-[--destructive]" : "text-green-500"}`, children: result.error || `Installed ${result.name}` })
|
|
103
164
|
] });
|
|
104
165
|
}
|
|
105
166
|
function RegistryPage() {
|
|
@@ -122,6 +183,7 @@ function RegistryPage() {
|
|
|
122
183
|
load();
|
|
123
184
|
}, []);
|
|
124
185
|
const installedSlugs = new Set(installed.map((t) => t.slug));
|
|
186
|
+
const runningContainers = containers.filter((c) => c.status === "running").length;
|
|
125
187
|
async function handleInstall(catalogId) {
|
|
126
188
|
setInstalling(catalogId);
|
|
127
189
|
await installTool(catalogId);
|
|
@@ -154,67 +216,139 @@ function RegistryPage() {
|
|
|
154
216
|
if (search && !t.name.toLowerCase().includes(search.toLowerCase()) && !t.description.toLowerCase().includes(search.toLowerCase())) return false;
|
|
155
217
|
return true;
|
|
156
218
|
});
|
|
157
|
-
if (loading)
|
|
219
|
+
if (loading) {
|
|
220
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: [...Array(5)].map((_, i) => /* @__PURE__ */ jsx("div", { className: "h-14 animate-pulse rounded-lg bg-white/[0.04] border border-white/[0.06]" }, i)) });
|
|
221
|
+
}
|
|
222
|
+
const tabs = [
|
|
223
|
+
{ id: "catalog", label: "CATALOG", count: catalog.tools.length },
|
|
224
|
+
{ id: "installed", label: "INSTALLED", count: installed.length },
|
|
225
|
+
{ id: "containers", label: "CONTAINERS", count: runningContainers }
|
|
226
|
+
];
|
|
158
227
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
159
|
-
/* @__PURE__ */
|
|
160
|
-
/* @__PURE__ */
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
"
|
|
166
|
-
] })
|
|
167
|
-
] }) }),
|
|
168
|
-
/* @__PURE__ */ jsx("div", { className: "flex gap-1 border-b border-border mb-4", children: [
|
|
169
|
-
{ id: "catalog", label: `Catalog (${catalog.tools.length})` },
|
|
170
|
-
{ id: "installed", label: `Installed (${installed.length})` },
|
|
171
|
-
{ id: "containers", label: `Containers (${containers.filter((c) => c.status === "running").length})` }
|
|
172
|
-
].map((t) => /* @__PURE__ */ jsx("button", { onClick: () => setTab(t.id), className: `px-3 py-2 text-sm font-medium border-b-2 transition-colors ${tab === t.id ? "border-foreground text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"}`, children: t.label }, t.id)) }),
|
|
173
|
-
tab === "catalog" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
174
|
-
/* @__PURE__ */ jsx("div", { className: "flex flex-col sm:flex-row gap-3 mb-4", children: /* @__PURE__ */ jsxs("div", { className: "relative flex-1", children: [
|
|
175
|
-
/* @__PURE__ */ jsx(SearchIcon, { size: 14 }),
|
|
176
|
-
/* @__PURE__ */ jsx("input", { placeholder: "Search tools...", value: search, onChange: (e) => setSearch(e.target.value), className: "w-full text-sm border rounded-md pl-3 pr-3 py-2 bg-background" })
|
|
177
|
-
] }) }),
|
|
178
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-1 mb-4 overflow-x-auto pb-1", children: [
|
|
179
|
-
/* @__PURE__ */ jsxs("button", { onClick: () => setActiveCategory("all"), className: `shrink-0 px-3 py-1 rounded-full text-xs font-medium transition-colors ${activeCategory === "all" ? "bg-foreground text-background" : "bg-muted text-muted-foreground hover:text-foreground"}`, children: [
|
|
180
|
-
"All (",
|
|
228
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-6", children: [
|
|
229
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
230
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-mono font-semibold text-[--cyan] text-glow-cyan", children: "Toolbox" }),
|
|
231
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground mt-1 font-mono", children: "Security tool registry and container management" })
|
|
232
|
+
] }),
|
|
233
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
234
|
+
/* @__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: [
|
|
181
235
|
catalog.tools.length,
|
|
182
|
-
"
|
|
236
|
+
" tools"
|
|
183
237
|
] }),
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
"
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
filteredCatalog.map((t) => /* @__PURE__ */ jsx(CatalogToolCard, { tool: t, installed: installedSlugs.has(t.id), onInstall: handleInstall, installing }, t.id)),
|
|
194
|
-
filteredCatalog.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-center text-sm text-muted-foreground py-8", children: "No tools match your search." })
|
|
238
|
+
/* @__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: [
|
|
239
|
+
installed.length,
|
|
240
|
+
" installed"
|
|
241
|
+
] }),
|
|
242
|
+
runningContainers > 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-orange-500/10 text-orange-500 border border-orange-500/20", children: [
|
|
243
|
+
/* @__PURE__ */ jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-orange-500 animate-pulse" }),
|
|
244
|
+
runningContainers,
|
|
245
|
+
" running"
|
|
246
|
+
] })
|
|
195
247
|
] })
|
|
196
248
|
] }),
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
249
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-1 border-b border-white/[0.06] mb-4", children: tabs.map((t) => /* @__PURE__ */ jsxs(
|
|
250
|
+
"button",
|
|
251
|
+
{
|
|
252
|
+
onClick: () => setTab(t.id),
|
|
253
|
+
className: `px-4 py-2.5 text-[11px] font-mono font-medium uppercase tracking-wider border-b-2 transition-colors ${tab === t.id ? "border-[--cyan] text-[--cyan]" : "border-transparent text-muted-foreground hover:text-foreground"}`,
|
|
254
|
+
children: [
|
|
255
|
+
t.label,
|
|
256
|
+
" ",
|
|
257
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
258
|
+
"(",
|
|
259
|
+
t.count,
|
|
260
|
+
")"
|
|
261
|
+
] })
|
|
262
|
+
]
|
|
263
|
+
},
|
|
264
|
+
t.id
|
|
265
|
+
)) }),
|
|
266
|
+
/* @__PURE__ */ jsxs(AnimatePresence, { mode: "wait", children: [
|
|
267
|
+
tab === "catalog" && /* @__PURE__ */ jsxs(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, children: [
|
|
268
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col sm:flex-row gap-3 mb-4", children: /* @__PURE__ */ jsxs("div", { className: "relative flex-1", children: [
|
|
269
|
+
/* @__PURE__ */ jsx("div", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsx(SearchIcon, { size: 14 }) }),
|
|
270
|
+
/* @__PURE__ */ jsx(
|
|
271
|
+
"input",
|
|
272
|
+
{
|
|
273
|
+
placeholder: "Search tools...",
|
|
274
|
+
value: search,
|
|
275
|
+
onChange: (e) => setSearch(e.target.value),
|
|
276
|
+
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"
|
|
277
|
+
}
|
|
278
|
+
)
|
|
279
|
+
] }) }),
|
|
280
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 mb-4 overflow-x-auto pb-1 scrollbar-thin", children: [
|
|
281
|
+
/* @__PURE__ */ jsxs(
|
|
282
|
+
"button",
|
|
283
|
+
{
|
|
284
|
+
onClick: () => setActiveCategory("all"),
|
|
285
|
+
className: `shrink-0 px-3 py-1 rounded-full text-[10px] font-mono font-medium border transition-colors ${activeCategory === "all" ? "bg-[--cyan]/10 text-[--cyan] border-[--cyan]/20" : "border-white/[0.06] text-muted-foreground hover:text-foreground hover:border-white/[0.12]"}`,
|
|
286
|
+
children: [
|
|
287
|
+
"All (",
|
|
288
|
+
catalog.tools.length,
|
|
289
|
+
")"
|
|
290
|
+
]
|
|
291
|
+
}
|
|
292
|
+
),
|
|
293
|
+
catalog.categories.map((c) => {
|
|
294
|
+
const cs = getCatStyle(c.id);
|
|
295
|
+
const isActive = activeCategory === c.id;
|
|
296
|
+
return /* @__PURE__ */ jsxs(
|
|
297
|
+
"button",
|
|
298
|
+
{
|
|
299
|
+
onClick: () => setActiveCategory(c.id),
|
|
300
|
+
className: `shrink-0 px-3 py-1 rounded-full text-[10px] font-mono font-medium border transition-colors ${isActive ? `${cs.bg} ${cs.text} ${cs.border}` : "border-white/[0.06] text-muted-foreground hover:text-foreground hover:border-white/[0.12]"}`,
|
|
301
|
+
children: [
|
|
302
|
+
c.name,
|
|
303
|
+
" (",
|
|
304
|
+
catalog.tools.filter((t) => t.category === c.id).length,
|
|
305
|
+
")"
|
|
306
|
+
]
|
|
307
|
+
},
|
|
308
|
+
c.id
|
|
309
|
+
);
|
|
310
|
+
})
|
|
311
|
+
] }),
|
|
312
|
+
/* @__PURE__ */ jsx(GithubInstaller, { onInstall: handleGithubInstall }),
|
|
313
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
314
|
+
filteredCatalog.map((t, i) => /* @__PURE__ */ jsx(CatalogToolCard, { tool: t, installed: installedSlugs.has(t.id), onInstall: handleInstall, installing, index: i }, t.id)),
|
|
315
|
+
filteredCatalog.length === 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center py-12 text-center", children: [
|
|
316
|
+
/* @__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 }) }),
|
|
317
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono text-muted-foreground", children: "No tools match your search." })
|
|
318
|
+
] })
|
|
214
319
|
] })
|
|
215
|
-
] }),
|
|
216
|
-
|
|
217
|
-
|
|
320
|
+
] }, "catalog"),
|
|
321
|
+
tab === "installed" && /* @__PURE__ */ jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, children: installed.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [
|
|
322
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-full bg-white/[0.04] border border-white/[0.06] p-4 mb-4", children: /* @__PURE__ */ jsx(PackageIcon, { size: 24 }) }),
|
|
323
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono font-medium mb-1", children: "No tools installed" }),
|
|
324
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground font-mono", children: "Browse the catalog to install security tools for your agents." })
|
|
325
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: installed.map((t, i) => /* @__PURE__ */ jsx(InstalledToolCard, { tool: t, onUninstall: handleUninstall, onToggle: handleToggle, onSpawn: handleSpawn, index: i }, t.id)) }) }, "installed"),
|
|
326
|
+
tab === "containers" && /* @__PURE__ */ jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, children: containers.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [
|
|
327
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-full bg-white/[0.04] border border-white/[0.06] p-4 mb-4", children: /* @__PURE__ */ jsx(PackageIcon, { size: 24 }) }),
|
|
328
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono font-medium mb-1", children: "No containers running" }),
|
|
329
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground font-mono", children: "Spawn containers from installed tools to give agents terminal access." })
|
|
330
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: containers.map((c, i) => /* @__PURE__ */ jsxs(
|
|
331
|
+
motion.div,
|
|
332
|
+
{
|
|
333
|
+
initial: { opacity: 0, y: 8 },
|
|
334
|
+
animate: { opacity: 1, y: 0 },
|
|
335
|
+
transition: { duration: 0.25, delay: i * 0.03 },
|
|
336
|
+
className: "flex items-center gap-3 p-3 rounded-lg border border-white/[0.06] bg-[--card] hover:border-[--cyan]/20 transition-colors",
|
|
337
|
+
children: [
|
|
338
|
+
/* @__PURE__ */ jsx("div", { className: `shrink-0 w-2.5 h-2.5 rounded-full ${c.status === "running" ? "bg-green-500 animate-pulse" : c.status === "stopped" ? "bg-muted-foreground" : "bg-red-500"}` }),
|
|
339
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
340
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-mono font-medium truncate", children: c.imageName }),
|
|
341
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-0.5", children: [
|
|
342
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-muted-foreground", children: c.containerId?.slice(0, 12) }),
|
|
343
|
+
/* @__PURE__ */ jsx("span", { className: `inline-flex rounded-full px-1.5 py-0.5 text-[9px] font-mono font-medium ${c.status === "running" ? "bg-green-500/10 text-green-500 border border-green-500/20" : "bg-white/5 text-muted-foreground border border-white/10"}`, children: c.status })
|
|
344
|
+
] })
|
|
345
|
+
] }),
|
|
346
|
+
c.status === "running" && /* @__PURE__ */ jsx("button", { onClick: () => handleStop(c.id), className: "text-xs font-mono text-muted-foreground hover:text-[--destructive] transition-colors", children: "Stop" })
|
|
347
|
+
]
|
|
348
|
+
},
|
|
349
|
+
c.id
|
|
350
|
+
)) }) }, "containers")
|
|
351
|
+
] })
|
|
218
352
|
] });
|
|
219
353
|
}
|
|
220
354
|
export {
|