thepopebot 1.1.2 → 1.2.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/README.md +1 -1
- package/api/index.js +72 -165
- package/bin/cli.js +36 -6
- package/bin/local.sh +31 -0
- package/bin/postinstall.js +6 -2
- package/config/index.js +2 -11
- package/config/instrumentation.js +17 -5
- package/lib/actions.js +7 -6
- package/lib/ai/agent.js +36 -0
- package/lib/ai/index.js +274 -0
- package/lib/ai/model.js +67 -0
- package/lib/ai/tools.js +49 -0
- package/lib/auth/actions.js +28 -0
- package/lib/auth/config.js +45 -0
- package/lib/auth/index.js +27 -0
- package/lib/auth/middleware.js +30 -0
- package/lib/channels/base.js +56 -0
- package/lib/channels/index.js +15 -0
- package/lib/channels/telegram.js +146 -0
- package/lib/chat/actions.js +239 -0
- package/lib/chat/api.js +103 -0
- package/lib/chat/components/app-sidebar.js +161 -0
- package/lib/chat/components/app-sidebar.jsx +214 -0
- package/lib/chat/components/chat-header.js +9 -0
- package/lib/chat/components/chat-header.jsx +14 -0
- package/lib/chat/components/chat-input.js +230 -0
- package/lib/chat/components/chat-input.jsx +232 -0
- package/lib/chat/components/chat-nav-context.js +11 -0
- package/lib/chat/components/chat-nav-context.jsx +11 -0
- package/lib/chat/components/chat-page.js +70 -0
- package/lib/chat/components/chat-page.jsx +89 -0
- package/lib/chat/components/chat.js +78 -0
- package/lib/chat/components/chat.jsx +91 -0
- package/lib/chat/components/chats-page.js +170 -0
- package/lib/chat/components/chats-page.jsx +203 -0
- package/lib/chat/components/crons-page.js +144 -0
- package/lib/chat/components/crons-page.jsx +204 -0
- package/lib/chat/components/greeting.js +11 -0
- package/lib/chat/components/greeting.jsx +14 -0
- package/lib/chat/components/icons.js +518 -0
- package/lib/chat/components/icons.jsx +482 -0
- package/lib/chat/components/index.js +19 -0
- package/lib/chat/components/message.js +66 -0
- package/lib/chat/components/message.jsx +92 -0
- package/lib/chat/components/messages.js +63 -0
- package/lib/chat/components/messages.jsx +72 -0
- package/lib/chat/components/notifications-page.js +54 -0
- package/lib/chat/components/notifications-page.jsx +83 -0
- package/lib/chat/components/page-layout.js +21 -0
- package/lib/chat/components/page-layout.jsx +28 -0
- package/lib/chat/components/settings-layout.js +37 -0
- package/lib/chat/components/settings-layout.jsx +51 -0
- package/lib/chat/components/settings-secrets-page.js +216 -0
- package/lib/chat/components/settings-secrets-page.jsx +264 -0
- package/lib/chat/components/sidebar-history-item.js +54 -0
- package/lib/chat/components/sidebar-history-item.jsx +50 -0
- package/lib/chat/components/sidebar-history.js +92 -0
- package/lib/chat/components/sidebar-history.jsx +132 -0
- package/lib/chat/components/sidebar-user-nav.js +59 -0
- package/lib/chat/components/sidebar-user-nav.jsx +69 -0
- package/lib/chat/components/swarm-page.js +250 -0
- package/lib/chat/components/swarm-page.jsx +356 -0
- package/lib/chat/components/triggers-page.js +121 -0
- package/lib/chat/components/triggers-page.jsx +177 -0
- package/lib/chat/components/ui/dropdown-menu.js +98 -0
- package/lib/chat/components/ui/dropdown-menu.jsx +116 -0
- package/lib/chat/components/ui/scroll-area.js +13 -0
- package/lib/chat/components/ui/scroll-area.jsx +17 -0
- package/lib/chat/components/ui/separator.js +21 -0
- package/lib/chat/components/ui/separator.jsx +18 -0
- package/lib/chat/components/ui/sheet.js +75 -0
- package/lib/chat/components/ui/sheet.jsx +95 -0
- package/lib/chat/components/ui/sidebar.js +227 -0
- package/lib/chat/components/ui/sidebar.jsx +245 -0
- package/lib/chat/components/ui/tooltip.js +56 -0
- package/lib/chat/components/ui/tooltip.jsx +66 -0
- package/lib/chat/utils.js +11 -0
- package/lib/cron.js +7 -8
- package/lib/db/api-keys.js +160 -0
- package/lib/db/chats.js +129 -0
- package/lib/db/index.js +106 -0
- package/lib/db/notifications.js +99 -0
- package/lib/db/schema.js +51 -0
- package/lib/db/users.js +89 -0
- package/lib/paths.js +23 -17
- package/lib/tools/create-job.js +3 -3
- package/lib/tools/github.js +145 -1
- package/lib/tools/openai.js +1 -1
- package/lib/tools/telegram.js +4 -3
- package/lib/triggers.js +6 -7
- package/lib/utils/render-md.js +6 -6
- package/package.json +43 -6
- package/setup/lib/auth.mjs +22 -9
- package/setup/lib/prerequisites.mjs +10 -3
- package/setup/lib/telegram-verify.mjs +3 -16
- package/setup/setup-telegram.mjs +31 -62
- package/setup/setup.mjs +58 -98
- package/templates/.dockerignore +5 -0
- package/templates/.env.example +18 -2
- package/templates/.github/workflows/auto-merge.yml +1 -1
- package/templates/.github/workflows/build-image.yml +6 -4
- package/templates/.github/workflows/notify-job-failed.yml +2 -2
- package/templates/.github/workflows/notify-pr-complete.yml +2 -2
- package/templates/.github/workflows/run-job.yml +24 -10
- package/templates/CLAUDE.md +5 -3
- package/templates/app/api/auth/[...nextauth]/route.js +1 -0
- package/templates/app/api/chat/route.js +1 -0
- package/templates/app/chat/[chatId]/page.js +8 -0
- package/templates/app/chats/page.js +7 -0
- package/templates/app/components/ascii-logo.jsx +10 -0
- package/templates/app/components/login-form.jsx +81 -0
- package/templates/app/components/setup-form.jsx +82 -0
- package/templates/app/components/theme-provider.jsx +11 -0
- package/templates/app/components/theme-toggle.jsx +38 -0
- package/templates/app/components/ui/button.jsx +21 -0
- package/templates/app/components/ui/card.jsx +23 -0
- package/templates/app/components/ui/input.jsx +10 -0
- package/templates/app/components/ui/label.jsx +10 -0
- package/templates/app/crons/page.js +7 -0
- package/templates/app/globals.css +66 -0
- package/templates/app/layout.js +9 -2
- package/templates/app/login/page.js +15 -0
- package/templates/app/notifications/page.js +7 -0
- package/templates/app/page.js +6 -30
- package/templates/app/settings/layout.js +7 -0
- package/templates/app/settings/page.js +5 -0
- package/templates/app/settings/secrets/page.js +5 -0
- package/templates/app/swarm/page.js +7 -0
- package/templates/app/triggers/page.js +7 -0
- package/templates/config/CRONS.json +2 -2
- package/templates/config/TRIGGERS.json +2 -2
- package/templates/docker/event_handler/Dockerfile +19 -0
- package/templates/docker/{entrypoint.sh → job/entrypoint.sh} +4 -4
- package/templates/docker/runner/Dockerfile +38 -0
- package/templates/docker/runner/entrypoint.sh +41 -0
- package/templates/docker-compose.yml +52 -0
- package/templates/instrumentation.js +6 -1
- package/templates/middleware.js +1 -0
- package/templates/postcss.config.mjs +5 -0
- package/lib/claude/conversation.js +0 -76
- package/lib/claude/index.js +0 -142
- package/lib/claude/tools.js +0 -54
- /package/templates/docker/{Dockerfile → job/Dockerfile} +0 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
|
+
import { PageLayout } from "./page-layout.js";
|
|
5
|
+
import { ZapIcon, ChevronDownIcon } from "./icons.js";
|
|
6
|
+
import { getSwarmConfig } from "../actions.js";
|
|
7
|
+
const typeBadgeStyles = {
|
|
8
|
+
agent: "bg-purple-500/10 text-purple-500",
|
|
9
|
+
command: "bg-blue-500/10 text-blue-500",
|
|
10
|
+
webhook: "bg-orange-500/10 text-orange-500"
|
|
11
|
+
};
|
|
12
|
+
function ActionCard({ action, index }) {
|
|
13
|
+
const type = action.type || "agent";
|
|
14
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-md border bg-background p-3", children: [
|
|
15
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
16
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground font-medium", children: [
|
|
17
|
+
"Action ",
|
|
18
|
+
index + 1
|
|
19
|
+
] }),
|
|
20
|
+
/* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium ${typeBadgeStyles[type] || typeBadgeStyles.agent}`, children: type })
|
|
21
|
+
] }),
|
|
22
|
+
type === "agent" && action.job && /* @__PURE__ */ jsx("pre", { className: "text-xs bg-muted rounded-md p-3 whitespace-pre-wrap break-words font-mono overflow-auto max-h-48", children: action.job }),
|
|
23
|
+
type === "command" && action.command && /* @__PURE__ */ jsx("pre", { className: "text-xs bg-muted rounded-md p-3 whitespace-pre-wrap break-words font-mono overflow-auto max-h-48", children: action.command }),
|
|
24
|
+
type === "webhook" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
25
|
+
/* @__PURE__ */ jsxs("pre", { className: "text-xs bg-muted rounded-md p-3 whitespace-pre-wrap break-words font-mono overflow-auto", children: [
|
|
26
|
+
action.method && action.method !== "POST" ? `${action.method} ` : "",
|
|
27
|
+
action.url
|
|
28
|
+
] }),
|
|
29
|
+
action.vars && Object.keys(action.vars).length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
30
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Variables" }),
|
|
31
|
+
/* @__PURE__ */ jsx("pre", { className: "text-xs bg-muted rounded-md p-3 whitespace-pre-wrap break-words font-mono overflow-auto max-h-48", children: JSON.stringify(action.vars, null, 2) })
|
|
32
|
+
] })
|
|
33
|
+
] })
|
|
34
|
+
] });
|
|
35
|
+
}
|
|
36
|
+
function TriggerCard({ trigger }) {
|
|
37
|
+
const [expanded, setExpanded] = useState(false);
|
|
38
|
+
const disabled = trigger.enabled === false;
|
|
39
|
+
const actions = trigger.actions || [];
|
|
40
|
+
const actionTypes = actions.map((a) => a.type || "agent").filter((v, i, arr) => arr.indexOf(v) === i);
|
|
41
|
+
return /* @__PURE__ */ jsxs(
|
|
42
|
+
"div",
|
|
43
|
+
{
|
|
44
|
+
className: `rounded-lg border bg-card transition-opacity ${disabled ? "opacity-60" : ""}`,
|
|
45
|
+
children: [
|
|
46
|
+
/* @__PURE__ */ jsxs(
|
|
47
|
+
"button",
|
|
48
|
+
{
|
|
49
|
+
onClick: () => setExpanded(!expanded),
|
|
50
|
+
className: "flex items-center gap-3 w-full text-left p-4 hover:bg-accent/50 rounded-lg",
|
|
51
|
+
children: [
|
|
52
|
+
/* @__PURE__ */ jsx("div", { className: "shrink-0 rounded-md bg-muted p-2", children: /* @__PURE__ */ jsx(ZapIcon, { size: 16 }) }),
|
|
53
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
54
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: trigger.name }),
|
|
55
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground mt-0.5", children: [
|
|
56
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono", children: trigger.watch_path }),
|
|
57
|
+
/* @__PURE__ */ jsx("span", { className: "mx-1.5 text-border", children: "|" }),
|
|
58
|
+
actions.length,
|
|
59
|
+
" action",
|
|
60
|
+
actions.length !== 1 ? "s" : "",
|
|
61
|
+
actionTypes.length > 0 && /* @__PURE__ */ jsxs("span", { className: "ml-1", children: [
|
|
62
|
+
"(",
|
|
63
|
+
actionTypes.join(", "),
|
|
64
|
+
")"
|
|
65
|
+
] })
|
|
66
|
+
] })
|
|
67
|
+
] }),
|
|
68
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
69
|
+
/* @__PURE__ */ jsx(
|
|
70
|
+
"span",
|
|
71
|
+
{
|
|
72
|
+
className: `inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium ${disabled ? "bg-muted text-muted-foreground" : "bg-green-500/10 text-green-500"}`,
|
|
73
|
+
children: disabled ? "disabled" : "enabled"
|
|
74
|
+
}
|
|
75
|
+
),
|
|
76
|
+
/* @__PURE__ */ jsx("span", { className: `transition-transform ${expanded ? "rotate-180" : ""}`, children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: 14 }) })
|
|
77
|
+
] })
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
),
|
|
81
|
+
expanded && /* @__PURE__ */ jsx("div", { className: "border-t px-4 py-3 flex flex-col gap-2", children: actions.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "No actions defined." }) : actions.map((action, i) => /* @__PURE__ */ jsx(ActionCard, { action, index: i }, i)) })
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
function TriggersPage({ session }) {
|
|
87
|
+
const [triggers, setTriggers] = useState([]);
|
|
88
|
+
const [loading, setLoading] = useState(true);
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
getSwarmConfig().then((data) => {
|
|
91
|
+
if (data?.triggers) setTriggers(data.triggers);
|
|
92
|
+
}).catch(() => {
|
|
93
|
+
}).finally(() => setLoading(false));
|
|
94
|
+
}, []);
|
|
95
|
+
const enabledCount = triggers.filter((t) => t.enabled !== false).length;
|
|
96
|
+
return /* @__PURE__ */ jsxs(PageLayout, { session, children: [
|
|
97
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-6", children: [
|
|
98
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold", children: "Triggers" }),
|
|
99
|
+
!loading && /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground mt-1", children: [
|
|
100
|
+
triggers.length,
|
|
101
|
+
" trigger",
|
|
102
|
+
triggers.length !== 1 ? "s" : "",
|
|
103
|
+
" configured, ",
|
|
104
|
+
enabledCount,
|
|
105
|
+
" enabled"
|
|
106
|
+
] })
|
|
107
|
+
] }),
|
|
108
|
+
loading ? /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: [...Array(3)].map((_, i) => /* @__PURE__ */ jsx("div", { className: "h-20 animate-pulse rounded-lg bg-border/50" }, i)) }) : triggers.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [
|
|
109
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-full bg-muted p-4 mb-4", children: /* @__PURE__ */ jsx(ZapIcon, { size: 24 }) }),
|
|
110
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium mb-1", children: "No triggers configured" }),
|
|
111
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground max-w-sm", children: [
|
|
112
|
+
"Add webhook triggers by editing ",
|
|
113
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono", children: "config/TRIGGERS.json" }),
|
|
114
|
+
" in your project."
|
|
115
|
+
] })
|
|
116
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: triggers.map((trigger, i) => /* @__PURE__ */ jsx(TriggerCard, { trigger }, i)) })
|
|
117
|
+
] });
|
|
118
|
+
}
|
|
119
|
+
export {
|
|
120
|
+
TriggersPage
|
|
121
|
+
};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect } from 'react';
|
|
4
|
+
import { PageLayout } from './page-layout.js';
|
|
5
|
+
import { ZapIcon, ChevronDownIcon } from './icons.js';
|
|
6
|
+
import { getSwarmConfig } from '../actions.js';
|
|
7
|
+
|
|
8
|
+
const typeBadgeStyles = {
|
|
9
|
+
agent: 'bg-purple-500/10 text-purple-500',
|
|
10
|
+
command: 'bg-blue-500/10 text-blue-500',
|
|
11
|
+
webhook: 'bg-orange-500/10 text-orange-500',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
15
|
+
// Action Card (nested inside trigger)
|
|
16
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
function ActionCard({ action, index }) {
|
|
19
|
+
const type = action.type || 'agent';
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="rounded-md border bg-background p-3">
|
|
23
|
+
<div className="flex items-center gap-2 mb-2">
|
|
24
|
+
<span className="text-xs text-muted-foreground font-medium">Action {index + 1}</span>
|
|
25
|
+
<span className={`inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium ${typeBadgeStyles[type] || typeBadgeStyles.agent}`}>
|
|
26
|
+
{type}
|
|
27
|
+
</span>
|
|
28
|
+
</div>
|
|
29
|
+
{type === 'agent' && action.job && (
|
|
30
|
+
<pre className="text-xs bg-muted rounded-md p-3 whitespace-pre-wrap break-words font-mono overflow-auto max-h-48">
|
|
31
|
+
{action.job}
|
|
32
|
+
</pre>
|
|
33
|
+
)}
|
|
34
|
+
{type === 'command' && action.command && (
|
|
35
|
+
<pre className="text-xs bg-muted rounded-md p-3 whitespace-pre-wrap break-words font-mono overflow-auto max-h-48">
|
|
36
|
+
{action.command}
|
|
37
|
+
</pre>
|
|
38
|
+
)}
|
|
39
|
+
{type === 'webhook' && (
|
|
40
|
+
<div className="flex flex-col gap-2">
|
|
41
|
+
<pre className="text-xs bg-muted rounded-md p-3 whitespace-pre-wrap break-words font-mono overflow-auto">
|
|
42
|
+
{action.method && action.method !== 'POST' ? `${action.method} ` : ''}{action.url}
|
|
43
|
+
</pre>
|
|
44
|
+
{action.vars && Object.keys(action.vars).length > 0 && (
|
|
45
|
+
<div>
|
|
46
|
+
<p className="text-xs font-medium text-muted-foreground mb-1">Variables</p>
|
|
47
|
+
<pre className="text-xs bg-muted rounded-md p-3 whitespace-pre-wrap break-words font-mono overflow-auto max-h-48">
|
|
48
|
+
{JSON.stringify(action.vars, null, 2)}
|
|
49
|
+
</pre>
|
|
50
|
+
</div>
|
|
51
|
+
)}
|
|
52
|
+
</div>
|
|
53
|
+
)}
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
59
|
+
// Trigger Card
|
|
60
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
function TriggerCard({ trigger }) {
|
|
63
|
+
const [expanded, setExpanded] = useState(false);
|
|
64
|
+
const disabled = trigger.enabled === false;
|
|
65
|
+
const actions = trigger.actions || [];
|
|
66
|
+
const actionTypes = actions
|
|
67
|
+
.map((a) => a.type || 'agent')
|
|
68
|
+
.filter((v, i, arr) => arr.indexOf(v) === i);
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<div
|
|
72
|
+
className={`rounded-lg border bg-card transition-opacity ${disabled ? 'opacity-60' : ''}`}
|
|
73
|
+
>
|
|
74
|
+
<button
|
|
75
|
+
onClick={() => setExpanded(!expanded)}
|
|
76
|
+
className="flex items-center gap-3 w-full text-left p-4 hover:bg-accent/50 rounded-lg"
|
|
77
|
+
>
|
|
78
|
+
<div className="shrink-0 rounded-md bg-muted p-2">
|
|
79
|
+
<ZapIcon size={16} />
|
|
80
|
+
</div>
|
|
81
|
+
<div className="flex-1 min-w-0">
|
|
82
|
+
<p className="text-sm font-medium truncate">{trigger.name}</p>
|
|
83
|
+
<p className="text-xs text-muted-foreground mt-0.5">
|
|
84
|
+
<span className="font-mono">{trigger.watch_path}</span>
|
|
85
|
+
<span className="mx-1.5 text-border">|</span>
|
|
86
|
+
{actions.length} action{actions.length !== 1 ? 's' : ''}
|
|
87
|
+
{actionTypes.length > 0 && (
|
|
88
|
+
<span className="ml-1">({actionTypes.join(', ')})</span>
|
|
89
|
+
)}
|
|
90
|
+
</p>
|
|
91
|
+
</div>
|
|
92
|
+
<div className="flex items-center gap-2 shrink-0">
|
|
93
|
+
<span
|
|
94
|
+
className={`inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium ${
|
|
95
|
+
disabled ? 'bg-muted text-muted-foreground' : 'bg-green-500/10 text-green-500'
|
|
96
|
+
}`}
|
|
97
|
+
>
|
|
98
|
+
{disabled ? 'disabled' : 'enabled'}
|
|
99
|
+
</span>
|
|
100
|
+
<span className={`transition-transform ${expanded ? 'rotate-180' : ''}`}>
|
|
101
|
+
<ChevronDownIcon size={14} />
|
|
102
|
+
</span>
|
|
103
|
+
</div>
|
|
104
|
+
</button>
|
|
105
|
+
|
|
106
|
+
{expanded && (
|
|
107
|
+
<div className="border-t px-4 py-3 flex flex-col gap-2">
|
|
108
|
+
{actions.length === 0 ? (
|
|
109
|
+
<p className="text-xs text-muted-foreground">No actions defined.</p>
|
|
110
|
+
) : (
|
|
111
|
+
actions.map((action, i) => (
|
|
112
|
+
<ActionCard key={i} action={action} index={i} />
|
|
113
|
+
))
|
|
114
|
+
)}
|
|
115
|
+
</div>
|
|
116
|
+
)}
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
122
|
+
// Main Page
|
|
123
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
export function TriggersPage({ session }) {
|
|
126
|
+
const [triggers, setTriggers] = useState([]);
|
|
127
|
+
const [loading, setLoading] = useState(true);
|
|
128
|
+
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
getSwarmConfig()
|
|
131
|
+
.then((data) => {
|
|
132
|
+
if (data?.triggers) setTriggers(data.triggers);
|
|
133
|
+
})
|
|
134
|
+
.catch(() => {})
|
|
135
|
+
.finally(() => setLoading(false));
|
|
136
|
+
}, []);
|
|
137
|
+
|
|
138
|
+
const enabledCount = triggers.filter((t) => t.enabled !== false).length;
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<PageLayout session={session}>
|
|
142
|
+
{/* Header */}
|
|
143
|
+
<div className="mb-6">
|
|
144
|
+
<h1 className="text-2xl font-semibold">Triggers</h1>
|
|
145
|
+
{!loading && (
|
|
146
|
+
<p className="text-sm text-muted-foreground mt-1">
|
|
147
|
+
{triggers.length} trigger{triggers.length !== 1 ? 's' : ''} configured, {enabledCount} enabled
|
|
148
|
+
</p>
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
{loading ? (
|
|
153
|
+
<div className="flex flex-col gap-3">
|
|
154
|
+
{[...Array(3)].map((_, i) => (
|
|
155
|
+
<div key={i} className="h-20 animate-pulse rounded-lg bg-border/50" />
|
|
156
|
+
))}
|
|
157
|
+
</div>
|
|
158
|
+
) : triggers.length === 0 ? (
|
|
159
|
+
<div className="flex flex-col items-center justify-center py-16 text-center">
|
|
160
|
+
<div className="rounded-full bg-muted p-4 mb-4">
|
|
161
|
+
<ZapIcon size={24} />
|
|
162
|
+
</div>
|
|
163
|
+
<p className="text-sm font-medium mb-1">No triggers configured</p>
|
|
164
|
+
<p className="text-xs text-muted-foreground max-w-sm">
|
|
165
|
+
Add webhook triggers by editing <span className="font-mono">config/TRIGGERS.json</span> in your project.
|
|
166
|
+
</p>
|
|
167
|
+
</div>
|
|
168
|
+
) : (
|
|
169
|
+
<div className="flex flex-col gap-3">
|
|
170
|
+
{triggers.map((trigger, i) => (
|
|
171
|
+
<TriggerCard key={i} trigger={trigger} />
|
|
172
|
+
))}
|
|
173
|
+
</div>
|
|
174
|
+
)}
|
|
175
|
+
</PageLayout>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useState, useEffect, useRef } from "react";
|
|
4
|
+
import { cn } from "../../utils.js";
|
|
5
|
+
const DropdownContext = createContext({ open: false, onOpenChange: () => {
|
|
6
|
+
} });
|
|
7
|
+
function DropdownMenu({ children, open: controlledOpen, onOpenChange: controlledOnOpenChange }) {
|
|
8
|
+
const [internalOpen, setInternalOpen] = useState(false);
|
|
9
|
+
const open = controlledOpen !== void 0 ? controlledOpen : internalOpen;
|
|
10
|
+
const onOpenChange = controlledOnOpenChange || setInternalOpen;
|
|
11
|
+
return /* @__PURE__ */ jsx(DropdownContext.Provider, { value: { open, onOpenChange }, children: /* @__PURE__ */ jsx("div", { className: "relative inline-block", children }) });
|
|
12
|
+
}
|
|
13
|
+
function DropdownMenuTrigger({ children, asChild, ...props }) {
|
|
14
|
+
const { open, onOpenChange } = useContext(DropdownContext);
|
|
15
|
+
const handleClick = (e) => {
|
|
16
|
+
e.stopPropagation();
|
|
17
|
+
onOpenChange(!open);
|
|
18
|
+
};
|
|
19
|
+
if (asChild && children) {
|
|
20
|
+
return /* @__PURE__ */ jsx("span", { onClick: handleClick, ...props, children });
|
|
21
|
+
}
|
|
22
|
+
return /* @__PURE__ */ jsx("button", { onClick: handleClick, ...props, children });
|
|
23
|
+
}
|
|
24
|
+
function DropdownMenuContent({ children, className, align = "start", side = "bottom", sideOffset = 4, ...props }) {
|
|
25
|
+
const { open, onOpenChange } = useContext(DropdownContext);
|
|
26
|
+
const ref = useRef(null);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (!open) return;
|
|
29
|
+
const handleClickOutside = (e) => {
|
|
30
|
+
if (ref.current && !ref.current.contains(e.target)) {
|
|
31
|
+
onOpenChange(false);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const handleEsc = (e) => {
|
|
35
|
+
if (e.key === "Escape") onOpenChange(false);
|
|
36
|
+
};
|
|
37
|
+
setTimeout(() => document.addEventListener("click", handleClickOutside), 0);
|
|
38
|
+
document.addEventListener("keydown", handleEsc);
|
|
39
|
+
return () => {
|
|
40
|
+
document.removeEventListener("click", handleClickOutside);
|
|
41
|
+
document.removeEventListener("keydown", handleEsc);
|
|
42
|
+
};
|
|
43
|
+
}, [open, onOpenChange]);
|
|
44
|
+
if (!open) return null;
|
|
45
|
+
return /* @__PURE__ */ jsx(
|
|
46
|
+
"div",
|
|
47
|
+
{
|
|
48
|
+
ref,
|
|
49
|
+
className: cn(
|
|
50
|
+
"absolute z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-muted p-1 text-foreground shadow-md",
|
|
51
|
+
side === "bottom" && `top-full mt-1`,
|
|
52
|
+
side === "top" && `bottom-full mb-1`,
|
|
53
|
+
align === "end" && "right-0",
|
|
54
|
+
align === "start" && "left-0",
|
|
55
|
+
className
|
|
56
|
+
),
|
|
57
|
+
...props,
|
|
58
|
+
children
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
function DropdownMenuItem({ children, className, onClick, ...props }) {
|
|
63
|
+
const { onOpenChange } = useContext(DropdownContext);
|
|
64
|
+
return /* @__PURE__ */ jsx(
|
|
65
|
+
"div",
|
|
66
|
+
{
|
|
67
|
+
role: "menuitem",
|
|
68
|
+
className: cn(
|
|
69
|
+
"relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-background focus:bg-background",
|
|
70
|
+
className
|
|
71
|
+
),
|
|
72
|
+
onClick: (e) => {
|
|
73
|
+
onClick?.(e);
|
|
74
|
+
onOpenChange(false);
|
|
75
|
+
},
|
|
76
|
+
...props,
|
|
77
|
+
children
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
function DropdownMenuSeparator({ className }) {
|
|
82
|
+
return /* @__PURE__ */ jsx("div", { className: cn("-mx-1 my-1 h-px bg-border", className) });
|
|
83
|
+
}
|
|
84
|
+
function DropdownMenuLabel({ children, className }) {
|
|
85
|
+
return /* @__PURE__ */ jsx("div", { className: cn("px-2 py-1.5 text-sm font-semibold", className), children });
|
|
86
|
+
}
|
|
87
|
+
function DropdownMenuGroup({ children }) {
|
|
88
|
+
return /* @__PURE__ */ jsx("div", { children });
|
|
89
|
+
}
|
|
90
|
+
export {
|
|
91
|
+
DropdownMenu,
|
|
92
|
+
DropdownMenuContent,
|
|
93
|
+
DropdownMenuGroup,
|
|
94
|
+
DropdownMenuItem,
|
|
95
|
+
DropdownMenuLabel,
|
|
96
|
+
DropdownMenuSeparator,
|
|
97
|
+
DropdownMenuTrigger
|
|
98
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext, useState, useEffect, useRef } from 'react';
|
|
4
|
+
import { cn } from '../../utils.js';
|
|
5
|
+
|
|
6
|
+
const DropdownContext = createContext({ open: false, onOpenChange: () => {} });
|
|
7
|
+
|
|
8
|
+
export function DropdownMenu({ children, open: controlledOpen, onOpenChange: controlledOnOpenChange }) {
|
|
9
|
+
const [internalOpen, setInternalOpen] = useState(false);
|
|
10
|
+
const open = controlledOpen !== undefined ? controlledOpen : internalOpen;
|
|
11
|
+
const onOpenChange = controlledOnOpenChange || setInternalOpen;
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<DropdownContext.Provider value={{ open, onOpenChange }}>
|
|
15
|
+
<div className="relative inline-block">{children}</div>
|
|
16
|
+
</DropdownContext.Provider>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function DropdownMenuTrigger({ children, asChild, ...props }) {
|
|
21
|
+
const { open, onOpenChange } = useContext(DropdownContext);
|
|
22
|
+
const handleClick = (e) => {
|
|
23
|
+
e.stopPropagation();
|
|
24
|
+
onOpenChange(!open);
|
|
25
|
+
};
|
|
26
|
+
if (asChild && children) {
|
|
27
|
+
return (
|
|
28
|
+
<span onClick={handleClick} {...props}>
|
|
29
|
+
{children}
|
|
30
|
+
</span>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return (
|
|
34
|
+
<button onClick={handleClick} {...props}>
|
|
35
|
+
{children}
|
|
36
|
+
</button>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function DropdownMenuContent({ children, className, align = 'start', side = 'bottom', sideOffset = 4, ...props }) {
|
|
41
|
+
const { open, onOpenChange } = useContext(DropdownContext);
|
|
42
|
+
const ref = useRef(null);
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!open) return;
|
|
46
|
+
const handleClickOutside = (e) => {
|
|
47
|
+
if (ref.current && !ref.current.contains(e.target)) {
|
|
48
|
+
onOpenChange(false);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const handleEsc = (e) => {
|
|
52
|
+
if (e.key === 'Escape') onOpenChange(false);
|
|
53
|
+
};
|
|
54
|
+
setTimeout(() => document.addEventListener('click', handleClickOutside), 0);
|
|
55
|
+
document.addEventListener('keydown', handleEsc);
|
|
56
|
+
return () => {
|
|
57
|
+
document.removeEventListener('click', handleClickOutside);
|
|
58
|
+
document.removeEventListener('keydown', handleEsc);
|
|
59
|
+
};
|
|
60
|
+
}, [open, onOpenChange]);
|
|
61
|
+
|
|
62
|
+
if (!open) return null;
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div
|
|
66
|
+
ref={ref}
|
|
67
|
+
className={cn(
|
|
68
|
+
'absolute z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-muted p-1 text-foreground shadow-md',
|
|
69
|
+
side === 'bottom' && `top-full mt-1`,
|
|
70
|
+
side === 'top' && `bottom-full mb-1`,
|
|
71
|
+
align === 'end' && 'right-0',
|
|
72
|
+
align === 'start' && 'left-0',
|
|
73
|
+
className
|
|
74
|
+
)}
|
|
75
|
+
{...props}
|
|
76
|
+
>
|
|
77
|
+
{children}
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function DropdownMenuItem({ children, className, onClick, ...props }) {
|
|
83
|
+
const { onOpenChange } = useContext(DropdownContext);
|
|
84
|
+
return (
|
|
85
|
+
<div
|
|
86
|
+
role="menuitem"
|
|
87
|
+
className={cn(
|
|
88
|
+
'relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-background focus:bg-background',
|
|
89
|
+
className
|
|
90
|
+
)}
|
|
91
|
+
onClick={(e) => {
|
|
92
|
+
onClick?.(e);
|
|
93
|
+
onOpenChange(false);
|
|
94
|
+
}}
|
|
95
|
+
{...props}
|
|
96
|
+
>
|
|
97
|
+
{children}
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function DropdownMenuSeparator({ className }) {
|
|
103
|
+
return <div className={cn('-mx-1 my-1 h-px bg-border', className)} />;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function DropdownMenuLabel({ children, className }) {
|
|
107
|
+
return (
|
|
108
|
+
<div className={cn('px-2 py-1.5 text-sm font-semibold', className)}>
|
|
109
|
+
{children}
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function DropdownMenuGroup({ children }) {
|
|
115
|
+
return <div>{children}</div>;
|
|
116
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from "../../utils.js";
|
|
4
|
+
function ScrollArea({ children, className, ...props }) {
|
|
5
|
+
return /* @__PURE__ */ jsx("div", { className: cn("relative overflow-hidden", className), ...props, children: /* @__PURE__ */ jsx("div", { className: "h-full w-full overflow-y-auto scrollbar-thin", children }) });
|
|
6
|
+
}
|
|
7
|
+
function ScrollBar() {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
ScrollArea,
|
|
12
|
+
ScrollBar
|
|
13
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../utils.js';
|
|
4
|
+
|
|
5
|
+
export function ScrollArea({ children, className, ...props }) {
|
|
6
|
+
return (
|
|
7
|
+
<div className={cn('relative overflow-hidden', className)} {...props}>
|
|
8
|
+
<div className="h-full w-full overflow-y-auto scrollbar-thin">
|
|
9
|
+
{children}
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function ScrollBar() {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from "../../utils.js";
|
|
4
|
+
function Separator({ className, orientation = "horizontal", ...props }) {
|
|
5
|
+
return /* @__PURE__ */ jsx(
|
|
6
|
+
"div",
|
|
7
|
+
{
|
|
8
|
+
role: "separator",
|
|
9
|
+
"aria-orientation": orientation,
|
|
10
|
+
className: cn(
|
|
11
|
+
"shrink-0 bg-border",
|
|
12
|
+
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
|
13
|
+
className
|
|
14
|
+
),
|
|
15
|
+
...props
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
Separator
|
|
21
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../utils.js';
|
|
4
|
+
|
|
5
|
+
export function Separator({ className, orientation = 'horizontal', ...props }) {
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
role="separator"
|
|
9
|
+
aria-orientation={orientation}
|
|
10
|
+
className={cn(
|
|
11
|
+
'shrink-0 bg-border',
|
|
12
|
+
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
|
|
13
|
+
className
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useEffect } from "react";
|
|
4
|
+
import { cn } from "../../utils.js";
|
|
5
|
+
const SheetContext = createContext({ open: false, onOpenChange: () => {
|
|
6
|
+
} });
|
|
7
|
+
function Sheet({ children, open, onOpenChange }) {
|
|
8
|
+
return /* @__PURE__ */ jsx(SheetContext.Provider, { value: { open, onOpenChange }, children });
|
|
9
|
+
}
|
|
10
|
+
function SheetTrigger({ children, asChild, ...props }) {
|
|
11
|
+
const { onOpenChange } = useContext(SheetContext);
|
|
12
|
+
if (asChild && children) {
|
|
13
|
+
return /* @__PURE__ */ jsx("span", { onClick: () => onOpenChange(true), ...props, children });
|
|
14
|
+
}
|
|
15
|
+
return /* @__PURE__ */ jsx("button", { onClick: () => onOpenChange(true), ...props, children });
|
|
16
|
+
}
|
|
17
|
+
function SheetContent({ children, className, side = "left", ...props }) {
|
|
18
|
+
const { open, onOpenChange } = useContext(SheetContext);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (!open) return;
|
|
21
|
+
const handleEsc = (e) => {
|
|
22
|
+
if (e.key === "Escape") onOpenChange(false);
|
|
23
|
+
};
|
|
24
|
+
document.addEventListener("keydown", handleEsc);
|
|
25
|
+
return () => document.removeEventListener("keydown", handleEsc);
|
|
26
|
+
}, [open, onOpenChange]);
|
|
27
|
+
if (!open) return null;
|
|
28
|
+
return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50", children: [
|
|
29
|
+
/* @__PURE__ */ jsx(
|
|
30
|
+
"div",
|
|
31
|
+
{
|
|
32
|
+
className: "fixed inset-0 bg-black/50",
|
|
33
|
+
onClick: () => onOpenChange(false)
|
|
34
|
+
}
|
|
35
|
+
),
|
|
36
|
+
/* @__PURE__ */ jsx(
|
|
37
|
+
"div",
|
|
38
|
+
{
|
|
39
|
+
className: cn(
|
|
40
|
+
"fixed z-50 bg-background shadow-lg transition-transform",
|
|
41
|
+
side === "left" && "inset-y-0 left-0 w-3/4 max-w-sm border-r border-border",
|
|
42
|
+
side === "right" && "inset-y-0 right-0 w-3/4 max-w-sm border-l border-border",
|
|
43
|
+
className
|
|
44
|
+
),
|
|
45
|
+
...props,
|
|
46
|
+
children
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
] });
|
|
50
|
+
}
|
|
51
|
+
function SheetHeader({ children, className }) {
|
|
52
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-2 p-4", className), children });
|
|
53
|
+
}
|
|
54
|
+
function SheetTitle({ children, className }) {
|
|
55
|
+
return /* @__PURE__ */ jsx("h2", { className: cn("text-lg font-semibold text-foreground", className), children });
|
|
56
|
+
}
|
|
57
|
+
function SheetDescription({ children, className }) {
|
|
58
|
+
return /* @__PURE__ */ jsx("p", { className: cn("text-sm text-muted-foreground", className), children });
|
|
59
|
+
}
|
|
60
|
+
function SheetClose({ children, asChild, ...props }) {
|
|
61
|
+
const { onOpenChange } = useContext(SheetContext);
|
|
62
|
+
if (asChild && children) {
|
|
63
|
+
return /* @__PURE__ */ jsx("span", { onClick: () => onOpenChange(false), ...props, children });
|
|
64
|
+
}
|
|
65
|
+
return /* @__PURE__ */ jsx("button", { onClick: () => onOpenChange(false), ...props, children });
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
Sheet,
|
|
69
|
+
SheetClose,
|
|
70
|
+
SheetContent,
|
|
71
|
+
SheetDescription,
|
|
72
|
+
SheetHeader,
|
|
73
|
+
SheetTitle,
|
|
74
|
+
SheetTrigger
|
|
75
|
+
};
|