gigaclaw 1.4.0
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/LICENSE +26 -0
- package/README.md +237 -0
- package/api/CLAUDE.md +19 -0
- package/api/index.js +265 -0
- package/bin/cli.js +823 -0
- package/bin/local.sh +85 -0
- package/bin/postinstall.js +63 -0
- package/config/index.js +26 -0
- package/config/instrumentation.js +62 -0
- package/drizzle/0000_initial.sql +52 -0
- package/drizzle/0001_nostalgic_sersi.sql +11 -0
- package/drizzle/0002_black_daimon_hellstrom.sql +19 -0
- package/drizzle/0003_rename_code_workspaces.sql +5 -0
- package/drizzle/meta/0000_snapshot.json +321 -0
- package/drizzle/meta/0001_snapshot.json +390 -0
- package/drizzle/meta/0002_snapshot.json +411 -0
- package/drizzle/meta/0003_snapshot.json +419 -0
- package/drizzle/meta/_journal.json +34 -0
- package/lib/actions.js +44 -0
- package/lib/ai/agent.js +86 -0
- package/lib/ai/index.js +342 -0
- package/lib/ai/model.js +180 -0
- package/lib/ai/tools.js +269 -0
- package/lib/ai/web-search.js +42 -0
- package/lib/auth/actions.js +28 -0
- package/lib/auth/config.js +27 -0
- package/lib/auth/edge-config.js +27 -0
- package/lib/auth/index.js +27 -0
- package/lib/auth/middleware.js +62 -0
- package/lib/channels/base.js +56 -0
- package/lib/channels/index.js +15 -0
- package/lib/channels/telegram.js +148 -0
- package/lib/chat/actions.js +579 -0
- package/lib/chat/api.js +140 -0
- package/lib/chat/components/app-sidebar.js +213 -0
- package/lib/chat/components/app-sidebar.jsx +279 -0
- package/lib/chat/components/chat-header.js +192 -0
- package/lib/chat/components/chat-header.jsx +223 -0
- package/lib/chat/components/chat-input.js +236 -0
- package/lib/chat/components/chat-input.jsx +249 -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 +99 -0
- package/lib/chat/components/chat-page.jsx +121 -0
- package/lib/chat/components/chat.js +153 -0
- package/lib/chat/components/chat.jsx +199 -0
- package/lib/chat/components/chats-page.js +367 -0
- package/lib/chat/components/chats-page.jsx +394 -0
- package/lib/chat/components/code-mode-toggle.js +132 -0
- package/lib/chat/components/code-mode-toggle.jsx +163 -0
- package/lib/chat/components/crons-page.js +172 -0
- package/lib/chat/components/crons-page.jsx +244 -0
- package/lib/chat/components/greeting.js +11 -0
- package/lib/chat/components/greeting.jsx +16 -0
- package/lib/chat/components/icons.js +805 -0
- package/lib/chat/components/icons.jsx +751 -0
- package/lib/chat/components/index.js +20 -0
- package/lib/chat/components/message.js +363 -0
- package/lib/chat/components/message.jsx +422 -0
- package/lib/chat/components/messages.js +65 -0
- package/lib/chat/components/messages.jsx +74 -0
- package/lib/chat/components/notifications-page.js +56 -0
- package/lib/chat/components/notifications-page.jsx +87 -0
- package/lib/chat/components/page-layout.js +21 -0
- package/lib/chat/components/page-layout.jsx +28 -0
- package/lib/chat/components/pull-requests-page.js +103 -0
- package/lib/chat/components/pull-requests-page.jsx +113 -0
- package/lib/chat/components/settings-layout.js +39 -0
- package/lib/chat/components/settings-layout.jsx +53 -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 +138 -0
- package/lib/chat/components/sidebar-history-item.jsx +119 -0
- package/lib/chat/components/sidebar-history.js +167 -0
- package/lib/chat/components/sidebar-history.jsx +220 -0
- package/lib/chat/components/sidebar-user-nav.js +61 -0
- package/lib/chat/components/sidebar-user-nav.jsx +77 -0
- package/lib/chat/components/swarm-page.js +157 -0
- package/lib/chat/components/swarm-page.jsx +210 -0
- package/lib/chat/components/tool-call.js +89 -0
- package/lib/chat/components/tool-call.jsx +107 -0
- package/lib/chat/components/triggers-page.js +153 -0
- package/lib/chat/components/triggers-page.jsx +221 -0
- package/lib/chat/components/ui/combobox.js +98 -0
- package/lib/chat/components/ui/combobox.jsx +114 -0
- package/lib/chat/components/ui/confirm-dialog.js +53 -0
- package/lib/chat/components/ui/confirm-dialog.jsx +57 -0
- package/lib/chat/components/ui/dropdown-menu.js +194 -0
- package/lib/chat/components/ui/dropdown-menu.jsx +215 -0
- package/lib/chat/components/ui/rename-dialog.js +78 -0
- package/lib/chat/components/ui/rename-dialog.jsx +74 -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 +228 -0
- package/lib/chat/components/ui/sidebar.jsx +246 -0
- package/lib/chat/components/ui/tooltip.js +56 -0
- package/lib/chat/components/ui/tooltip.jsx +66 -0
- package/lib/chat/components/upgrade-dialog.js +151 -0
- package/lib/chat/components/upgrade-dialog.jsx +170 -0
- package/lib/chat/utils.js +11 -0
- package/lib/code/actions.js +153 -0
- package/lib/code/code-page.js +22 -0
- package/lib/code/code-page.jsx +25 -0
- package/lib/code/index.js +1 -0
- package/lib/code/terminal-view.js +201 -0
- package/lib/code/terminal-view.jsx +224 -0
- package/lib/code/ws-proxy.js +80 -0
- package/lib/cron.js +246 -0
- package/lib/db/api-keys.js +163 -0
- package/lib/db/chats.js +168 -0
- package/lib/db/code-workspaces.js +110 -0
- package/lib/db/index.js +52 -0
- package/lib/db/notifications.js +99 -0
- package/lib/db/schema.js +66 -0
- package/lib/db/update-check.js +96 -0
- package/lib/db/users.js +89 -0
- package/lib/paths.js +42 -0
- package/lib/tools/create-job.js +97 -0
- package/lib/tools/docker.js +146 -0
- package/lib/tools/github.js +271 -0
- package/lib/tools/openai.js +35 -0
- package/lib/tools/telegram.js +292 -0
- package/lib/triggers.js +104 -0
- package/lib/utils/render-md.js +111 -0
- package/package.json +118 -0
- package/setup/lib/auth.mjs +81 -0
- package/setup/lib/env.mjs +21 -0
- package/setup/lib/fs-utils.mjs +20 -0
- package/setup/lib/github.mjs +149 -0
- package/setup/lib/prerequisites.mjs +155 -0
- package/setup/lib/prompts.mjs +267 -0
- package/setup/lib/providers.mjs +105 -0
- package/setup/lib/sync.mjs +125 -0
- package/setup/lib/targets.mjs +45 -0
- package/setup/lib/telegram-verify.mjs +63 -0
- package/setup/lib/telegram.mjs +76 -0
- package/setup/setup-cloud.mjs +833 -0
- package/setup/setup-local.mjs +377 -0
- package/setup/setup-telegram.mjs +265 -0
- package/setup/setup.mjs +87 -0
- package/templates/.dockerignore +5 -0
- package/templates/.env.example +104 -0
- package/templates/.github/workflows/auto-merge.yml +117 -0
- package/templates/.github/workflows/notify-job-failed.yml +64 -0
- package/templates/.github/workflows/notify-pr-complete.yml +119 -0
- package/templates/.github/workflows/rebuild-event-handler.yml +121 -0
- package/templates/.github/workflows/run-job.yml +89 -0
- package/templates/.github/workflows/upgrade-event-handler.yml +62 -0
- package/templates/.gitignore.template +45 -0
- package/templates/.pi/extensions/env-sanitizer/index.ts +48 -0
- package/templates/.pi/extensions/env-sanitizer/package.json +5 -0
- package/templates/CLAUDE.md +29 -0
- package/templates/CLAUDE.md.template +308 -0
- package/templates/app/api/[...gigaclaw]/route.js +1 -0
- package/templates/app/api/auth/[...nextauth]/route.js +1 -0
- package/templates/app/chat/[chatId]/page.js +9 -0
- package/templates/app/chats/page.js +7 -0
- package/templates/app/code/[codeWorkspaceId]/page.js +9 -0
- package/templates/app/components/ascii-logo.jsx +12 -0
- package/templates/app/components/login-form.jsx +92 -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 +5 -0
- package/templates/app/globals.css +90 -0
- package/templates/app/layout.js +33 -0
- package/templates/app/login/page.js +15 -0
- package/templates/app/notifications/page.js +7 -0
- package/templates/app/page.js +7 -0
- package/templates/app/pull-requests/page.js +7 -0
- package/templates/app/settings/crons/page.js +5 -0
- 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/settings/triggers/page.js +5 -0
- package/templates/app/stream/chat/route.js +1 -0
- package/templates/app/swarm/page.js +7 -0
- package/templates/app/triggers/page.js +5 -0
- package/templates/config/CODE_PLANNING.md +14 -0
- package/templates/config/CRONS.json +56 -0
- package/templates/config/HEARTBEAT.md +3 -0
- package/templates/config/JOB_AGENT.md +30 -0
- package/templates/config/JOB_PLANNING.md +240 -0
- package/templates/config/JOB_SUMMARY.md +130 -0
- package/templates/config/SKILL_BUILDING_GUIDE.md +96 -0
- package/templates/config/SOUL.md +48 -0
- package/templates/config/TRIGGERS.json +58 -0
- package/templates/config/WEB_SEARCH_AVAILABLE.md +5 -0
- package/templates/config/WEB_SEARCH_UNAVAILABLE.md +3 -0
- package/templates/docker/claude-code-job/Dockerfile +34 -0
- package/templates/docker/claude-code-job/entrypoint.sh +149 -0
- package/templates/docker/claude-code-workspace/.tmux.conf +5 -0
- package/templates/docker/claude-code-workspace/Dockerfile +61 -0
- package/templates/docker/claude-code-workspace/entrypoint.sh +51 -0
- package/templates/docker/event-handler/Dockerfile +20 -0
- package/templates/docker/event-handler/ecosystem.config.cjs +7 -0
- package/templates/docker/pi-coding-agent-job/Dockerfile +51 -0
- package/templates/docker/pi-coding-agent-job/entrypoint.sh +164 -0
- package/templates/docker-compose.local.yml +78 -0
- package/templates/docker-compose.yml +64 -0
- package/templates/instrumentation.js +6 -0
- package/templates/middleware.js +23 -0
- package/templates/next.config.mjs +3 -0
- package/templates/postcss.config.mjs +5 -0
- package/templates/public/favicon.ico +0 -0
- package/templates/server.js +25 -0
- package/templates/skills/LICENSE +21 -0
- package/templates/skills/README.md +119 -0
- package/templates/skills/brave-search/SKILL.md +79 -0
- package/templates/skills/brave-search/content.js +86 -0
- package/templates/skills/brave-search/package-lock.json +621 -0
- package/templates/skills/brave-search/package.json +14 -0
- package/templates/skills/brave-search/search.js +199 -0
- package/templates/skills/browser-tools/SKILL.md +196 -0
- package/templates/skills/browser-tools/browser-content.js +103 -0
- package/templates/skills/browser-tools/browser-cookies.js +35 -0
- package/templates/skills/browser-tools/browser-eval.js +53 -0
- package/templates/skills/browser-tools/browser-hn-scraper.js +108 -0
- package/templates/skills/browser-tools/browser-nav.js +44 -0
- package/templates/skills/browser-tools/browser-pick.js +162 -0
- package/templates/skills/browser-tools/browser-screenshot.js +34 -0
- package/templates/skills/browser-tools/browser-start.js +87 -0
- package/templates/skills/browser-tools/package-lock.json +2556 -0
- package/templates/skills/browser-tools/package.json +19 -0
- package/templates/skills/google-docs/SKILL.md +23 -0
- package/templates/skills/google-docs/create.sh +69 -0
- package/templates/skills/google-drive/SKILL.md +47 -0
- package/templates/skills/google-drive/delete.sh +47 -0
- package/templates/skills/google-drive/download.sh +50 -0
- package/templates/skills/google-drive/list.sh +41 -0
- package/templates/skills/google-drive/upload.sh +76 -0
- package/templates/skills/kie-ai/SKILL.md +38 -0
- package/templates/skills/kie-ai/generate-image.sh +77 -0
- package/templates/skills/kie-ai/generate-video.sh +69 -0
- package/templates/skills/llm-secrets/SKILL.md +34 -0
- package/templates/skills/llm-secrets/llm-secrets.js +33 -0
- package/templates/skills/modify-self/SKILL.md +12 -0
- package/templates/skills/youtube-transcript/SKILL.md +41 -0
- package/templates/skills/youtube-transcript/package-lock.json +24 -0
- package/templates/skills/youtube-transcript/package.json +8 -0
- package/templates/skills/youtube-transcript/transcript.js +84 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
4
|
+
import { PageLayout } from './page-layout.js';
|
|
5
|
+
import { SpinnerIcon, RefreshIcon } from './icons.js';
|
|
6
|
+
import { getSwarmStatus } from '../actions.js';
|
|
7
|
+
|
|
8
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
9
|
+
// Utilities
|
|
10
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
function formatDuration(seconds) {
|
|
13
|
+
if (seconds < 60) return `${seconds}s`;
|
|
14
|
+
const minutes = Math.floor(seconds / 60);
|
|
15
|
+
const secs = seconds % 60;
|
|
16
|
+
if (minutes < 60) return `${minutes}m ${secs}s`;
|
|
17
|
+
const hours = Math.floor(minutes / 60);
|
|
18
|
+
const mins = minutes % 60;
|
|
19
|
+
return `${hours}h ${mins}m`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function timeAgo(timestamp) {
|
|
23
|
+
const seconds = Math.floor((Date.now() - new Date(timestamp).getTime()) / 1000);
|
|
24
|
+
if (seconds < 60) return 'just now';
|
|
25
|
+
const minutes = Math.floor(seconds / 60);
|
|
26
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
27
|
+
const hours = Math.floor(minutes / 60);
|
|
28
|
+
if (hours < 24) return `${hours}h ago`;
|
|
29
|
+
const days = Math.floor(hours / 24);
|
|
30
|
+
if (days < 30) return `${days}d ago`;
|
|
31
|
+
const months = Math.floor(days / 30);
|
|
32
|
+
return `${months}mo ago`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function LoadingSkeleton() {
|
|
36
|
+
return (
|
|
37
|
+
<div className="flex flex-col gap-4">
|
|
38
|
+
{[...Array(5)].map((_, i) => (
|
|
39
|
+
<div key={i} className="h-14 animate-pulse rounded-md bg-border/50" />
|
|
40
|
+
))}
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
46
|
+
// Workflow List
|
|
47
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
const conclusionBadgeStyles = {
|
|
50
|
+
success: 'bg-green-500/10 text-green-500',
|
|
51
|
+
failure: 'bg-red-500/10 text-red-500',
|
|
52
|
+
cancelled: 'bg-yellow-500/10 text-yellow-500',
|
|
53
|
+
skipped: 'bg-muted text-muted-foreground',
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function SwarmWorkflowList({ runs }) {
|
|
57
|
+
if (!runs || runs.length === 0) {
|
|
58
|
+
return (
|
|
59
|
+
<div className="text-sm text-muted-foreground py-4 text-center">
|
|
60
|
+
No workflow runs.
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div className="flex flex-col divide-y divide-border">
|
|
67
|
+
{runs.map((run) => {
|
|
68
|
+
const isActive = run.status === 'in_progress' || run.status === 'queued';
|
|
69
|
+
const isRunning = run.status === 'in_progress';
|
|
70
|
+
const isQueued = run.status === 'queued';
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<a
|
|
74
|
+
key={run.run_id}
|
|
75
|
+
href={run.html_url}
|
|
76
|
+
target="_blank"
|
|
77
|
+
rel="noopener noreferrer"
|
|
78
|
+
className="flex items-center gap-3 py-3 px-2 -mx-2 rounded-md hover:bg-accent transition-colors no-underline text-inherit"
|
|
79
|
+
>
|
|
80
|
+
{/* Status indicator */}
|
|
81
|
+
{isRunning && (
|
|
82
|
+
<span className="inline-block h-2.5 w-2.5 shrink-0 rounded-full bg-green-500 animate-pulse" />
|
|
83
|
+
)}
|
|
84
|
+
{isQueued && (
|
|
85
|
+
<span className="inline-block h-2.5 w-2.5 shrink-0 rounded-full bg-yellow-500" />
|
|
86
|
+
)}
|
|
87
|
+
{!isActive && (
|
|
88
|
+
<span
|
|
89
|
+
className={`inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium uppercase shrink-0 ${
|
|
90
|
+
conclusionBadgeStyles[run.conclusion] || 'bg-muted text-muted-foreground'
|
|
91
|
+
}`}
|
|
92
|
+
>
|
|
93
|
+
{run.conclusion || 'unknown'}
|
|
94
|
+
</span>
|
|
95
|
+
)}
|
|
96
|
+
|
|
97
|
+
{/* Workflow name */}
|
|
98
|
+
<span className="text-sm font-medium truncate">
|
|
99
|
+
{run.workflow_name || run.branch}
|
|
100
|
+
</span>
|
|
101
|
+
|
|
102
|
+
{/* Duration or time ago */}
|
|
103
|
+
<span className="text-xs text-muted-foreground shrink-0">
|
|
104
|
+
{isActive
|
|
105
|
+
? formatDuration(run.duration_seconds)
|
|
106
|
+
: timeAgo(run.updated_at || run.started_at)}
|
|
107
|
+
</span>
|
|
108
|
+
|
|
109
|
+
{/* Spacer */}
|
|
110
|
+
<div className="flex-1" />
|
|
111
|
+
|
|
112
|
+
{/* Link label */}
|
|
113
|
+
<span className="text-xs text-blue-500 shrink-0">
|
|
114
|
+
View
|
|
115
|
+
</span>
|
|
116
|
+
</a>
|
|
117
|
+
);
|
|
118
|
+
})}
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
124
|
+
// Main Page
|
|
125
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
export function SwarmPage({ session }) {
|
|
128
|
+
const [runs, setRuns] = useState([]);
|
|
129
|
+
const [hasMore, setHasMore] = useState(false);
|
|
130
|
+
const [page, setPage] = useState(1);
|
|
131
|
+
const [loading, setLoading] = useState(true);
|
|
132
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
133
|
+
|
|
134
|
+
const fetchPage = useCallback(async (p) => {
|
|
135
|
+
try {
|
|
136
|
+
const data = await getSwarmStatus(p);
|
|
137
|
+
setRuns(data.runs || []);
|
|
138
|
+
setHasMore(data.hasMore || false);
|
|
139
|
+
setPage(p);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
console.error('Failed to fetch swarm status:', err);
|
|
142
|
+
} finally {
|
|
143
|
+
setLoading(false);
|
|
144
|
+
setRefreshing(false);
|
|
145
|
+
}
|
|
146
|
+
}, []);
|
|
147
|
+
|
|
148
|
+
// Initial load
|
|
149
|
+
useEffect(() => { fetchPage(1); }, [fetchPage]);
|
|
150
|
+
|
|
151
|
+
// Auto-refresh current page every 10s
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
const interval = setInterval(() => fetchPage(page), 10000);
|
|
154
|
+
return () => clearInterval(interval);
|
|
155
|
+
}, [fetchPage, page]);
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<PageLayout session={session}>
|
|
159
|
+
{/* Header */}
|
|
160
|
+
<div className="flex items-center justify-between mb-6">
|
|
161
|
+
<h1 className="text-2xl font-semibold">Swarm</h1>
|
|
162
|
+
{!loading && (
|
|
163
|
+
<button
|
|
164
|
+
onClick={() => { setRefreshing(true); fetchPage(1); }}
|
|
165
|
+
disabled={refreshing}
|
|
166
|
+
className="inline-flex items-center gap-1.5 rounded-md px-2.5 py-1.5 text-xs font-medium border border-border bg-background text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:pointer-events-none"
|
|
167
|
+
>
|
|
168
|
+
{refreshing ? (
|
|
169
|
+
<>
|
|
170
|
+
<SpinnerIcon size={14} />
|
|
171
|
+
Refreshing...
|
|
172
|
+
</>
|
|
173
|
+
) : (
|
|
174
|
+
<>
|
|
175
|
+
<RefreshIcon size={14} />
|
|
176
|
+
Refresh
|
|
177
|
+
</>
|
|
178
|
+
)}
|
|
179
|
+
</button>
|
|
180
|
+
)}
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
{loading ? (
|
|
184
|
+
<LoadingSkeleton />
|
|
185
|
+
) : (
|
|
186
|
+
<div>
|
|
187
|
+
<SwarmWorkflowList runs={runs} />
|
|
188
|
+
{/* Pagination */}
|
|
189
|
+
<div className="flex items-center justify-between mt-4 pt-4 border-t border-border">
|
|
190
|
+
<button
|
|
191
|
+
onClick={() => { setRefreshing(true); fetchPage(page - 1); }}
|
|
192
|
+
disabled={page <= 1 || refreshing}
|
|
193
|
+
className="inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium border border-border bg-background text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:pointer-events-none"
|
|
194
|
+
>
|
|
195
|
+
Previous
|
|
196
|
+
</button>
|
|
197
|
+
<span className="text-sm text-muted-foreground">Page {page}</span>
|
|
198
|
+
<button
|
|
199
|
+
onClick={() => { setRefreshing(true); fetchPage(page + 1); }}
|
|
200
|
+
disabled={!hasMore || refreshing}
|
|
201
|
+
className="inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium border border-border bg-background text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:pointer-events-none"
|
|
202
|
+
>
|
|
203
|
+
Next
|
|
204
|
+
</button>
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
)}
|
|
208
|
+
</PageLayout>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { WrenchIcon, SpinnerIcon, CheckIcon, XIcon, ChevronDownIcon } from "./icons.js";
|
|
5
|
+
import { cn } from "../utils.js";
|
|
6
|
+
const TOOL_DISPLAY_NAMES = {
|
|
7
|
+
create_job: "Create Job",
|
|
8
|
+
get_job_status: "Check Job Status",
|
|
9
|
+
get_system_technical_specs: "Read Tech Docs",
|
|
10
|
+
get_skill_building_guide: "Read Skill Docs",
|
|
11
|
+
get_skill_details: "Get Skill",
|
|
12
|
+
start_coding: "Start Coding",
|
|
13
|
+
get_repository_details: "Get Repository Details"
|
|
14
|
+
};
|
|
15
|
+
function getToolDisplayName(toolName) {
|
|
16
|
+
return TOOL_DISPLAY_NAMES[toolName] || toolName.replace(/_/g, " ");
|
|
17
|
+
}
|
|
18
|
+
function formatContent(content) {
|
|
19
|
+
if (content == null) return null;
|
|
20
|
+
if (typeof content === "string") {
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(content);
|
|
23
|
+
return JSON.stringify(parsed, null, 2);
|
|
24
|
+
} catch {
|
|
25
|
+
return content;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return JSON.stringify(content, null, 2);
|
|
29
|
+
}
|
|
30
|
+
function ToolCall({ part }) {
|
|
31
|
+
const [expanded, setExpanded] = useState(false);
|
|
32
|
+
const toolName = part.toolName || (part.type?.startsWith("tool-") ? part.type.slice(5) : "tool");
|
|
33
|
+
const displayName = getToolDisplayName(toolName);
|
|
34
|
+
const state = part.state || "input-available";
|
|
35
|
+
const isRunning = state === "input-streaming" || state === "input-available";
|
|
36
|
+
const isDone = state === "output-available";
|
|
37
|
+
const isError = state === "output-error";
|
|
38
|
+
return /* @__PURE__ */ jsxs("div", { className: "my-1 rounded-lg border border-border bg-background", children: [
|
|
39
|
+
/* @__PURE__ */ jsxs(
|
|
40
|
+
"button",
|
|
41
|
+
{
|
|
42
|
+
onClick: () => setExpanded(!expanded),
|
|
43
|
+
className: "flex w-full items-center gap-2 px-3 py-2 text-left text-sm hover:bg-muted/50 rounded-lg",
|
|
44
|
+
children: [
|
|
45
|
+
/* @__PURE__ */ jsx(WrenchIcon, { size: 14, className: "text-muted-foreground shrink-0" }),
|
|
46
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: displayName }),
|
|
47
|
+
/* @__PURE__ */ jsxs("span", { className: "ml-auto flex items-center gap-1.5 text-xs text-muted-foreground", children: [
|
|
48
|
+
isRunning && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
49
|
+
/* @__PURE__ */ jsx(SpinnerIcon, { size: 12 }),
|
|
50
|
+
/* @__PURE__ */ jsx("span", { children: "Running..." })
|
|
51
|
+
] }),
|
|
52
|
+
isDone && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
53
|
+
/* @__PURE__ */ jsx(CheckIcon, { size: 12, className: "text-green-500" }),
|
|
54
|
+
/* @__PURE__ */ jsx("span", { children: "Done" })
|
|
55
|
+
] }),
|
|
56
|
+
isError && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
57
|
+
/* @__PURE__ */ jsx(XIcon, { size: 12, className: "text-red-500" }),
|
|
58
|
+
/* @__PURE__ */ jsx("span", { children: "Error" })
|
|
59
|
+
] })
|
|
60
|
+
] }),
|
|
61
|
+
/* @__PURE__ */ jsx(
|
|
62
|
+
ChevronDownIcon,
|
|
63
|
+
{
|
|
64
|
+
size: 14,
|
|
65
|
+
className: cn(
|
|
66
|
+
"text-muted-foreground transition-transform shrink-0",
|
|
67
|
+
expanded && "rotate-180"
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
),
|
|
74
|
+
expanded && /* @__PURE__ */ jsxs("div", { className: "border-t border-border px-3 py-2 text-xs", children: [
|
|
75
|
+
part.input != null && /* @__PURE__ */ jsxs("div", { className: "mb-2", children: [
|
|
76
|
+
/* @__PURE__ */ jsx("div", { className: "font-medium text-muted-foreground mb-1", children: "Input" }),
|
|
77
|
+
/* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap break-all rounded bg-muted p-2 text-foreground overflow-x-auto", children: formatContent(part.input) })
|
|
78
|
+
] }),
|
|
79
|
+
part.output != null && /* @__PURE__ */ jsxs("div", { children: [
|
|
80
|
+
/* @__PURE__ */ jsx("div", { className: "font-medium text-muted-foreground mb-1", children: "Output" }),
|
|
81
|
+
/* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap break-all rounded bg-muted p-2 text-foreground overflow-x-auto max-h-64 overflow-y-auto", children: formatContent(part.output) })
|
|
82
|
+
] }),
|
|
83
|
+
part.input == null && part.output == null && /* @__PURE__ */ jsx("div", { className: "text-muted-foreground italic", children: "Waiting for data..." })
|
|
84
|
+
] })
|
|
85
|
+
] });
|
|
86
|
+
}
|
|
87
|
+
export {
|
|
88
|
+
ToolCall
|
|
89
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { WrenchIcon, SpinnerIcon, CheckIcon, XIcon, ChevronDownIcon } from './icons.js';
|
|
5
|
+
import { cn } from '../utils.js';
|
|
6
|
+
|
|
7
|
+
const TOOL_DISPLAY_NAMES = {
|
|
8
|
+
create_job: 'Create Job',
|
|
9
|
+
get_job_status: 'Check Job Status',
|
|
10
|
+
get_system_technical_specs: 'Read Tech Docs',
|
|
11
|
+
get_skill_building_guide: 'Read Skill Docs',
|
|
12
|
+
get_skill_details: 'Get Skill',
|
|
13
|
+
start_coding: 'Start Coding',
|
|
14
|
+
get_repository_details: 'Get Repository Details',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function getToolDisplayName(toolName) {
|
|
18
|
+
return TOOL_DISPLAY_NAMES[toolName] || toolName.replace(/_/g, ' ');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function formatContent(content) {
|
|
22
|
+
if (content == null) return null;
|
|
23
|
+
if (typeof content === 'string') {
|
|
24
|
+
try {
|
|
25
|
+
const parsed = JSON.parse(content);
|
|
26
|
+
return JSON.stringify(parsed, null, 2);
|
|
27
|
+
} catch {
|
|
28
|
+
return content;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return JSON.stringify(content, null, 2);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function ToolCall({ part }) {
|
|
35
|
+
const [expanded, setExpanded] = useState(false);
|
|
36
|
+
|
|
37
|
+
const toolName = part.toolName || (part.type?.startsWith('tool-') ? part.type.slice(5) : 'tool');
|
|
38
|
+
const displayName = getToolDisplayName(toolName);
|
|
39
|
+
const state = part.state || 'input-available';
|
|
40
|
+
|
|
41
|
+
const isRunning = state === 'input-streaming' || state === 'input-available';
|
|
42
|
+
const isDone = state === 'output-available';
|
|
43
|
+
const isError = state === 'output-error';
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div className="my-1 rounded-lg border border-border bg-background">
|
|
47
|
+
<button
|
|
48
|
+
onClick={() => setExpanded(!expanded)}
|
|
49
|
+
className="flex w-full items-center gap-2 px-3 py-2 text-left text-sm hover:bg-muted/50 rounded-lg"
|
|
50
|
+
>
|
|
51
|
+
<WrenchIcon size={14} className="text-muted-foreground shrink-0" />
|
|
52
|
+
<span className="font-medium text-foreground">{displayName}</span>
|
|
53
|
+
<span className="ml-auto flex items-center gap-1.5 text-xs text-muted-foreground">
|
|
54
|
+
{isRunning && (
|
|
55
|
+
<>
|
|
56
|
+
<SpinnerIcon size={12} />
|
|
57
|
+
<span>Running...</span>
|
|
58
|
+
</>
|
|
59
|
+
)}
|
|
60
|
+
{isDone && (
|
|
61
|
+
<>
|
|
62
|
+
<CheckIcon size={12} className="text-green-500" />
|
|
63
|
+
<span>Done</span>
|
|
64
|
+
</>
|
|
65
|
+
)}
|
|
66
|
+
{isError && (
|
|
67
|
+
<>
|
|
68
|
+
<XIcon size={12} className="text-red-500" />
|
|
69
|
+
<span>Error</span>
|
|
70
|
+
</>
|
|
71
|
+
)}
|
|
72
|
+
</span>
|
|
73
|
+
<ChevronDownIcon
|
|
74
|
+
size={14}
|
|
75
|
+
className={cn(
|
|
76
|
+
'text-muted-foreground transition-transform shrink-0',
|
|
77
|
+
expanded && 'rotate-180'
|
|
78
|
+
)}
|
|
79
|
+
/>
|
|
80
|
+
</button>
|
|
81
|
+
|
|
82
|
+
{expanded && (
|
|
83
|
+
<div className="border-t border-border px-3 py-2 text-xs">
|
|
84
|
+
{part.input != null && (
|
|
85
|
+
<div className="mb-2">
|
|
86
|
+
<div className="font-medium text-muted-foreground mb-1">Input</div>
|
|
87
|
+
<pre className="whitespace-pre-wrap break-all rounded bg-muted p-2 text-foreground overflow-x-auto">
|
|
88
|
+
{formatContent(part.input)}
|
|
89
|
+
</pre>
|
|
90
|
+
</div>
|
|
91
|
+
)}
|
|
92
|
+
{part.output != null && (
|
|
93
|
+
<div>
|
|
94
|
+
<div className="font-medium text-muted-foreground mb-1">Output</div>
|
|
95
|
+
<pre className="whitespace-pre-wrap break-all rounded bg-muted p-2 text-foreground overflow-x-auto max-h-64 overflow-y-auto">
|
|
96
|
+
{formatContent(part.output)}
|
|
97
|
+
</pre>
|
|
98
|
+
</div>
|
|
99
|
+
)}
|
|
100
|
+
{part.input == null && part.output == null && (
|
|
101
|
+
<div className="text-muted-foreground italic">Waiting for data...</div>
|
|
102
|
+
)}
|
|
103
|
+
</div>
|
|
104
|
+
)}
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
|
+
import { ZapIcon, ChevronDownIcon } from "./icons.js";
|
|
5
|
+
import { getSwarmConfig } from "../actions.js";
|
|
6
|
+
const typeBadgeStyles = {
|
|
7
|
+
agent: "bg-purple-500/10 text-purple-500",
|
|
8
|
+
command: "bg-blue-500/10 text-blue-500",
|
|
9
|
+
webhook: "bg-orange-500/10 text-orange-500"
|
|
10
|
+
};
|
|
11
|
+
const typeOrder = { agent: 0, command: 1, webhook: 2 };
|
|
12
|
+
function sortByType(items) {
|
|
13
|
+
return [...items].sort((a, b) => {
|
|
14
|
+
const actions_a = a.actions || [];
|
|
15
|
+
const actions_b = b.actions || [];
|
|
16
|
+
const ta = typeOrder[actions_a[0]?.type || "agent"] ?? 99;
|
|
17
|
+
const tb = typeOrder[actions_b[0]?.type || "agent"] ?? 99;
|
|
18
|
+
return ta - tb;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function GroupHeader({ label, count }) {
|
|
22
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 pt-2 pb-1", children: [
|
|
23
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide", children: label }),
|
|
24
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
25
|
+
"(",
|
|
26
|
+
count,
|
|
27
|
+
")"
|
|
28
|
+
] })
|
|
29
|
+
] });
|
|
30
|
+
}
|
|
31
|
+
function ActionCard({ action, index }) {
|
|
32
|
+
const type = action.type || "agent";
|
|
33
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-md border bg-background p-3", children: [
|
|
34
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
35
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground font-medium", children: [
|
|
36
|
+
"Action ",
|
|
37
|
+
index + 1
|
|
38
|
+
] }),
|
|
39
|
+
/* @__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 })
|
|
40
|
+
] }),
|
|
41
|
+
type === "agent" && action.job && /* @__PURE__ */ jsxs("div", { children: [
|
|
42
|
+
/* @__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 }),
|
|
43
|
+
(action.llm_provider || action.llm_model) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
|
|
44
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "LLM:" }),
|
|
45
|
+
/* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full bg-purple-500/10 text-purple-500 px-2 py-0.5 text-[10px] font-medium", children: [action.llm_provider, action.llm_model].filter(Boolean).join(" / ") })
|
|
46
|
+
] })
|
|
47
|
+
] }),
|
|
48
|
+
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 }),
|
|
49
|
+
type === "webhook" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
50
|
+
/* @__PURE__ */ jsxs("pre", { className: "text-xs bg-muted rounded-md p-3 whitespace-pre-wrap break-words font-mono overflow-auto", children: [
|
|
51
|
+
action.method && action.method !== "POST" ? `${action.method} ` : "",
|
|
52
|
+
action.url
|
|
53
|
+
] }),
|
|
54
|
+
action.vars && Object.keys(action.vars).length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
55
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Variables" }),
|
|
56
|
+
/* @__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) })
|
|
57
|
+
] })
|
|
58
|
+
] })
|
|
59
|
+
] });
|
|
60
|
+
}
|
|
61
|
+
function TriggerCard({ trigger }) {
|
|
62
|
+
const [expanded, setExpanded] = useState(false);
|
|
63
|
+
const disabled = trigger.enabled === false;
|
|
64
|
+
const actions = trigger.actions || [];
|
|
65
|
+
const actionTypes = actions.map((a) => a.type || "agent").filter((v, i, arr) => arr.indexOf(v) === i);
|
|
66
|
+
return /* @__PURE__ */ jsxs(
|
|
67
|
+
"div",
|
|
68
|
+
{
|
|
69
|
+
className: `rounded-lg border bg-card transition-opacity ${disabled ? "opacity-60" : ""}`,
|
|
70
|
+
children: [
|
|
71
|
+
/* @__PURE__ */ jsxs(
|
|
72
|
+
"button",
|
|
73
|
+
{
|
|
74
|
+
onClick: () => setExpanded(!expanded),
|
|
75
|
+
className: "flex items-center gap-3 w-full text-left p-4 hover:bg-accent/50 rounded-lg",
|
|
76
|
+
children: [
|
|
77
|
+
/* @__PURE__ */ jsx("div", { className: "shrink-0 rounded-md bg-muted p-2", children: /* @__PURE__ */ jsx(ZapIcon, { size: 16 }) }),
|
|
78
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
79
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: trigger.name }),
|
|
80
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground mt-0.5", children: [
|
|
81
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono", children: trigger.watch_path }),
|
|
82
|
+
/* @__PURE__ */ jsx("span", { className: "mx-1.5 text-border", children: "|" }),
|
|
83
|
+
actions.length,
|
|
84
|
+
" action",
|
|
85
|
+
actions.length !== 1 ? "s" : "",
|
|
86
|
+
actionTypes.length > 0 && /* @__PURE__ */ jsxs("span", { className: "ml-1", children: [
|
|
87
|
+
"(",
|
|
88
|
+
actionTypes.join(", "),
|
|
89
|
+
")"
|
|
90
|
+
] })
|
|
91
|
+
] })
|
|
92
|
+
] }),
|
|
93
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
94
|
+
/* @__PURE__ */ jsx(
|
|
95
|
+
"span",
|
|
96
|
+
{
|
|
97
|
+
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"}`,
|
|
98
|
+
children: disabled ? "disabled" : "enabled"
|
|
99
|
+
}
|
|
100
|
+
),
|
|
101
|
+
/* @__PURE__ */ jsx("span", { className: `transition-transform ${expanded ? "rotate-180" : ""}`, children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: 14 }) })
|
|
102
|
+
] })
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
),
|
|
106
|
+
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)) })
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
function TriggersPage() {
|
|
112
|
+
const [triggers, setTriggers] = useState([]);
|
|
113
|
+
const [loading, setLoading] = useState(true);
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
getSwarmConfig().then((data) => {
|
|
116
|
+
if (data?.triggers) setTriggers(data.triggers);
|
|
117
|
+
}).catch(() => {
|
|
118
|
+
}).finally(() => setLoading(false));
|
|
119
|
+
}, []);
|
|
120
|
+
const enabled = sortByType(triggers.filter((t) => t.enabled !== false));
|
|
121
|
+
const disabled = sortByType(triggers.filter((t) => t.enabled === false));
|
|
122
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
123
|
+
!loading && /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground mb-4", children: [
|
|
124
|
+
triggers.length,
|
|
125
|
+
" trigger",
|
|
126
|
+
triggers.length !== 1 ? "s" : "",
|
|
127
|
+
" configured, ",
|
|
128
|
+
enabled.length,
|
|
129
|
+
" enabled"
|
|
130
|
+
] }),
|
|
131
|
+
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: [
|
|
132
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-full bg-muted p-4 mb-4", children: /* @__PURE__ */ jsx(ZapIcon, { size: 24 }) }),
|
|
133
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium mb-1", children: "No triggers configured" }),
|
|
134
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground max-w-sm", children: [
|
|
135
|
+
"Add webhook triggers by editing ",
|
|
136
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono", children: "config/TRIGGERS.json" }),
|
|
137
|
+
" in your project."
|
|
138
|
+
] })
|
|
139
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
140
|
+
enabled.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
141
|
+
/* @__PURE__ */ jsx(GroupHeader, { label: "Enabled", count: enabled.length }),
|
|
142
|
+
enabled.map((trigger, i) => /* @__PURE__ */ jsx(TriggerCard, { trigger }, `enabled-${i}`))
|
|
143
|
+
] }),
|
|
144
|
+
disabled.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
145
|
+
/* @__PURE__ */ jsx(GroupHeader, { label: "Disabled", count: disabled.length }),
|
|
146
|
+
disabled.map((trigger, i) => /* @__PURE__ */ jsx(TriggerCard, { trigger }, `disabled-${i}`))
|
|
147
|
+
] })
|
|
148
|
+
] })
|
|
149
|
+
] });
|
|
150
|
+
}
|
|
151
|
+
export {
|
|
152
|
+
TriggersPage
|
|
153
|
+
};
|