@harbinger-ai/harbinger 0.1.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 +21 -0
- package/README.md +406 -0
- package/agents/README.md +76 -0
- package/agents/_template/CONFIG.yaml +7 -0
- package/agents/_template/HEARTBEAT.md +59 -0
- package/agents/_template/IDENTITY.md +4 -0
- package/agents/_template/SKILLS.md +1 -0
- package/agents/_template/SOUL.md +25 -0
- package/agents/_template/TOOLS.md +3 -0
- package/agents/binary-reverser/CONFIG.yaml +21 -0
- package/agents/binary-reverser/HEARTBEAT.md +65 -0
- package/agents/binary-reverser/IDENTITY.md +1 -0
- package/agents/binary-reverser/SKILLS.md +1 -0
- package/agents/binary-reverser/SOUL.md +23 -0
- package/agents/binary-reverser/TOOLS.md +99 -0
- package/agents/browser-agent/CONFIG.yaml +20 -0
- package/agents/browser-agent/HEARTBEAT.md +79 -0
- package/agents/browser-agent/IDENTITY.md +5 -0
- package/agents/browser-agent/SKILLS.md +86 -0
- package/agents/browser-agent/SOUL.md +23 -0
- package/agents/browser-agent/TOOLS.md +186 -0
- package/agents/cloud-infiltrator/CONFIG.yaml +22 -0
- package/agents/cloud-infiltrator/HEARTBEAT.md +78 -0
- package/agents/cloud-infiltrator/IDENTITY.md +1 -0
- package/agents/cloud-infiltrator/SKILLS.md +1 -0
- package/agents/cloud-infiltrator/SOUL.md +23 -0
- package/agents/cloud-infiltrator/TOOLS.md +68 -0
- package/agents/coding-assistant/CONFIG.yaml +22 -0
- package/agents/coding-assistant/HEARTBEAT.md +57 -0
- package/agents/coding-assistant/IDENTITY.md +5 -0
- package/agents/coding-assistant/SKILLS.md +69 -0
- package/agents/coding-assistant/SOUL.md +60 -0
- package/agents/coding-assistant/TOOLS.md +168 -0
- package/agents/learning-agent/CONFIG.yaml +21 -0
- package/agents/learning-agent/HEARTBEAT.md +63 -0
- package/agents/learning-agent/IDENTITY.md +5 -0
- package/agents/learning-agent/SKILLS.md +86 -0
- package/agents/learning-agent/SOUL.md +77 -0
- package/agents/learning-agent/TOOLS.md +145 -0
- package/agents/maintainer/CONFIG.yaml +31 -0
- package/agents/maintainer/HEARTBEAT.md +28 -0
- package/agents/maintainer/IDENTITY.md +33 -0
- package/agents/maintainer/SKILLS.md +24 -0
- package/agents/maintainer/SOUL.md +61 -0
- package/agents/maintainer/TOOLS.md +29 -0
- package/agents/maintainer/lib/engine.js +279 -0
- package/agents/maintainer/lib/safe-fixer.js +183 -0
- package/agents/morning-brief/CONFIG.yaml +22 -0
- package/agents/morning-brief/HEARTBEAT.md +60 -0
- package/agents/morning-brief/IDENTITY.md +5 -0
- package/agents/morning-brief/SKILLS.md +56 -0
- package/agents/morning-brief/SOUL.md +64 -0
- package/agents/morning-brief/TOOLS.md +112 -0
- package/agents/osint-detective/CONFIG.yaml +24 -0
- package/agents/osint-detective/HEARTBEAT.md +66 -0
- package/agents/osint-detective/IDENTITY.md +1 -0
- package/agents/osint-detective/SKILLS.md +1 -0
- package/agents/osint-detective/SOUL.md +23 -0
- package/agents/osint-detective/TOOLS.md +81 -0
- package/agents/recon-scout/CONFIG.yaml +22 -0
- package/agents/recon-scout/HEARTBEAT.md +79 -0
- package/agents/recon-scout/IDENTITY.md +1 -0
- package/agents/recon-scout/SKILLS.md +1 -0
- package/agents/recon-scout/SOUL.md +23 -0
- package/agents/recon-scout/TOOLS.md +93 -0
- package/agents/report-writer/CONFIG.yaml +21 -0
- package/agents/report-writer/HEARTBEAT.md +63 -0
- package/agents/report-writer/IDENTITY.md +1 -0
- package/agents/report-writer/SKILLS.md +1 -0
- package/agents/report-writer/SOUL.md +23 -0
- package/agents/report-writer/TOOLS.md +69 -0
- package/agents/shared/README.md +13 -0
- package/agents/web-hacker/CONFIG.yaml +24 -0
- package/agents/web-hacker/HEARTBEAT.md +78 -0
- package/agents/web-hacker/IDENTITY.md +1 -0
- package/agents/web-hacker/SKILLS.md +1 -0
- package/agents/web-hacker/SOUL.md +23 -0
- package/agents/web-hacker/TOOLS.md +86 -0
- package/api/CLAUDE.md +19 -0
- package/api/index.js +274 -0
- package/bin/cli.js +620 -0
- package/bin/local.sh +31 -0
- package/bin/postinstall.js +63 -0
- package/config/index.js +24 -0
- package/config/instrumentation.js +93 -0
- package/drizzle/0000_initial.sql +52 -0
- package/drizzle/0001_bounty_and_registry.sql +82 -0
- package/drizzle/0002_sync_columns.sql +7 -0
- package/drizzle/0003_graceful_bloodscream.sql +86 -0
- package/drizzle/meta/0000_snapshot.json +321 -0
- package/drizzle/meta/0003_snapshot.json +878 -0
- package/drizzle/meta/_journal.json +34 -0
- package/drizzle/relations.ts +3 -0
- package/drizzle/schema.ts +145 -0
- package/lib/actions.js +47 -0
- package/lib/agents.js +166 -0
- package/lib/ai/agent.js +96 -0
- package/lib/ai/autonomous-engine.js +261 -0
- package/lib/ai/index.js +359 -0
- package/lib/ai/model-router.js +254 -0
- package/lib/ai/model.js +73 -0
- package/lib/ai/tools.js +84 -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 +53 -0
- package/lib/bounty/actions.js +119 -0
- package/lib/bounty/findings.js +64 -0
- package/lib/bounty/programs.js +34 -0
- package/lib/bounty/sync-targets.js +267 -0
- package/lib/bounty/targets.js +33 -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 +288 -0
- package/lib/chat/api.js +135 -0
- package/lib/chat/components/app-sidebar.js +237 -0
- package/lib/chat/components/app-sidebar.jsx +289 -0
- package/lib/chat/components/chat-header.js +27 -0
- package/lib/chat/components/chat-header.jsx +37 -0
- package/lib/chat/components/chat-input.js +230 -0
- package/lib/chat/components/chat-input.jsx +228 -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 +81 -0
- package/lib/chat/components/chat-page.jsx +100 -0
- package/lib/chat/components/chat.js +150 -0
- package/lib/chat/components/chat.jsx +182 -0
- package/lib/chat/components/chats-page.js +302 -0
- package/lib/chat/components/chats-page.jsx +330 -0
- package/lib/chat/components/crons-page.js +172 -0
- package/lib/chat/components/crons-page.jsx +244 -0
- package/lib/chat/components/enhanced-tool-call.js +103 -0
- package/lib/chat/components/enhanced-tool-call.jsx +139 -0
- package/lib/chat/components/findings-page.js +175 -0
- package/lib/chat/components/findings-page.jsx +214 -0
- package/lib/chat/components/greeting.js +22 -0
- package/lib/chat/components/greeting.jsx +26 -0
- package/lib/chat/components/icons.js +777 -0
- package/lib/chat/components/icons.jsx +741 -0
- package/lib/chat/components/index.js +26 -0
- package/lib/chat/components/mcp-page.js +260 -0
- package/lib/chat/components/mcp-page.jsx +355 -0
- package/lib/chat/components/message.js +289 -0
- package/lib/chat/components/message.jsx +315 -0
- package/lib/chat/components/messages.js +66 -0
- package/lib/chat/components/messages.jsx +77 -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/registry-page.js +222 -0
- package/lib/chat/components/registry-page.jsx +255 -0
- package/lib/chat/components/settings-layout.js +40 -0
- package/lib/chat/components/settings-layout.jsx +54 -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 +132 -0
- package/lib/chat/components/sidebar-history-item.jsx +113 -0
- package/lib/chat/components/sidebar-history.js +115 -0
- package/lib/chat/components/sidebar-history.jsx +157 -0
- package/lib/chat/components/sidebar-user-nav.js +63 -0
- package/lib/chat/components/sidebar-user-nav.jsx +73 -0
- package/lib/chat/components/status-bar.js +39 -0
- package/lib/chat/components/status-bar.jsx +51 -0
- package/lib/chat/components/swarm-page.js +157 -0
- package/lib/chat/components/swarm-page.jsx +210 -0
- package/lib/chat/components/targets-page.js +376 -0
- package/lib/chat/components/targets-page.jsx +389 -0
- package/lib/chat/components/tool-call.js +86 -0
- package/lib/chat/components/tool-call.jsx +104 -0
- package/lib/chat/components/tool-panel.js +107 -0
- package/lib/chat/components/tool-panel.jsx +145 -0
- package/lib/chat/components/triggers-page.js +153 -0
- package/lib/chat/components/triggers-page.jsx +221 -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 +98 -0
- package/lib/chat/components/ui/dropdown-menu.jsx +116 -0
- package/lib/chat/components/ui/rename-dialog.js +74 -0
- package/lib/chat/components/ui/rename-dialog.jsx +72 -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/components/upgrade-dialog.js +151 -0
- package/lib/chat/components/upgrade-dialog.jsx +170 -0
- package/lib/chat/utils.js +11 -0
- package/lib/cron.js +246 -0
- package/lib/db/api-keys.js +163 -0
- package/lib/db/chats.js +145 -0
- package/lib/db/index.js +52 -0
- package/lib/db/notifications.js +99 -0
- package/lib/db/schema.js +145 -0
- package/lib/db/update-check.js +96 -0
- package/lib/db/users.js +89 -0
- package/lib/mcp/actions.js +104 -0
- package/lib/mcp/client.js +79 -0
- package/lib/mcp/handler.js +57 -0
- package/lib/mcp/server.js +165 -0
- package/lib/paths.js +46 -0
- package/lib/registry/actions.js +164 -0
- package/lib/registry/catalog.js +137 -0
- package/lib/registry/tools.js +71 -0
- package/lib/tools/create-job.js +99 -0
- package/lib/tools/github.js +217 -0
- package/lib/tools/openai.js +35 -0
- package/lib/tools/telegram.js +292 -0
- package/lib/triggers.js +118 -0
- package/lib/utils/render-md.js +102 -0
- package/package.json +103 -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 +48 -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-telegram.mjs +264 -0
- package/setup/setup.mjs +842 -0
- package/templates/.dockerignore +5 -0
- package/templates/.env.example +63 -0
- package/templates/.github/workflows/auto-merge.yml +117 -0
- package/templates/.github/workflows/build-image.yml +36 -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 +307 -0
- package/templates/app/api/[...thepopebot]/route.js +1 -0
- package/templates/app/api/auth/[...nextauth]/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 +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/findings/page.js +7 -0
- package/templates/app/globals.css +90 -0
- package/templates/app/layout.js +19 -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/settings/crons/page.js +5 -0
- package/templates/app/settings/layout.js +7 -0
- package/templates/app/settings/mcp/page.js +5 -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/targets/page.js +7 -0
- package/templates/app/toolbox/page.js +7 -0
- package/templates/app/triggers/page.js +5 -0
- package/templates/config/AGENT.md +34 -0
- package/templates/config/CRONS.json +56 -0
- package/templates/config/EVENT_HANDLER.md +224 -0
- package/templates/config/HEARTBEAT.md +3 -0
- package/templates/config/JOB_SUMMARY.md +130 -0
- package/templates/config/MCP_SERVERS.json +1 -0
- package/templates/config/SKILL_BUILDING_GUIDE.md +90 -0
- package/templates/config/SOUL.md +17 -0
- package/templates/config/TRIGGERS.json +58 -0
- package/templates/docker/event-handler/Dockerfile +20 -0
- package/templates/docker/event-handler/ecosystem.config.cjs +8 -0
- package/templates/docker/job-claude-code/Dockerfile +34 -0
- package/templates/docker/job-claude-code/entrypoint.sh +139 -0
- package/templates/docker/job-pi-coding-agent/Dockerfile +44 -0
- package/templates/docker/job-pi-coding-agent/entrypoint.sh +163 -0
- package/templates/docker-compose.yml +63 -0
- package/templates/instrumentation.js +6 -0
- package/templates/middleware.js +1 -0
- package/templates/next.config.mjs +3 -0
- package/templates/postcss.config.mjs +5 -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/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
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect, useCallback } from "react";
|
|
4
|
+
import { ArrowUpCircleIcon, SpinnerIcon, CheckIcon, XIcon, RefreshIcon } from "./icons.js";
|
|
5
|
+
import { triggerUpgrade } from "../actions.js";
|
|
6
|
+
function UpgradeDialog({ open, onClose, version, updateAvailable, changelog }) {
|
|
7
|
+
const [upgrading, setUpgrading] = useState(false);
|
|
8
|
+
const [result, setResult] = useState(null);
|
|
9
|
+
const handleClose = useCallback(() => onClose(), [onClose]);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (!open) return;
|
|
12
|
+
const handleEsc = (e) => {
|
|
13
|
+
if (e.key === "Escape") handleClose();
|
|
14
|
+
};
|
|
15
|
+
document.addEventListener("keydown", handleEsc);
|
|
16
|
+
return () => document.removeEventListener("keydown", handleEsc);
|
|
17
|
+
}, [open, handleClose]);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!open) {
|
|
20
|
+
setUpgrading(false);
|
|
21
|
+
setResult(null);
|
|
22
|
+
}
|
|
23
|
+
}, [open]);
|
|
24
|
+
if (!open) return null;
|
|
25
|
+
const handleUpgrade = async () => {
|
|
26
|
+
setUpgrading(true);
|
|
27
|
+
setResult(null);
|
|
28
|
+
try {
|
|
29
|
+
await triggerUpgrade();
|
|
30
|
+
setResult("success");
|
|
31
|
+
} catch {
|
|
32
|
+
setResult("error");
|
|
33
|
+
} finally {
|
|
34
|
+
setUpgrading(false);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
38
|
+
/* @__PURE__ */ jsx("div", { className: "fixed inset-0 bg-black/50", onClick: handleClose }),
|
|
39
|
+
/* @__PURE__ */ jsx("div", { className: "relative z-50 w-full max-w-md rounded-lg border border-border bg-background p-6 shadow-lg", onClick: (e) => e.stopPropagation(), children: result === "success" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
40
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
|
|
41
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: "Upgrade Initiated" }),
|
|
42
|
+
/* @__PURE__ */ jsx("button", { onClick: handleClose, className: "text-muted-foreground hover:text-foreground", children: /* @__PURE__ */ jsx(XIcon, { size: 16 }) })
|
|
43
|
+
] }),
|
|
44
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
|
|
45
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-10 w-10 rounded-full bg-emerald-500/10 shrink-0", children: /* @__PURE__ */ jsx(CheckIcon, { size: 20, className: "text-emerald-500" }) }),
|
|
46
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm font-medium", children: [
|
|
47
|
+
"Upgrading to ",
|
|
48
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono text-emerald-500", children: [
|
|
49
|
+
"v",
|
|
50
|
+
updateAvailable
|
|
51
|
+
] })
|
|
52
|
+
] })
|
|
53
|
+
] }),
|
|
54
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3 text-sm text-muted-foreground", children: [
|
|
55
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
56
|
+
"The upgrade process has been triggered and could take",
|
|
57
|
+
" ",
|
|
58
|
+
/* @__PURE__ */ jsx("strong", { className: "text-foreground", children: "5\u201310 minutes" }),
|
|
59
|
+
" to complete."
|
|
60
|
+
] }),
|
|
61
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
62
|
+
"You can monitor progress from the",
|
|
63
|
+
" ",
|
|
64
|
+
/* @__PURE__ */ jsx("a", { href: "/swarm", className: "text-emerald-500 hover:underline font-medium", children: "Swarm" }),
|
|
65
|
+
" page."
|
|
66
|
+
] }),
|
|
67
|
+
/* @__PURE__ */ jsx("p", { children: "The site may be briefly unresponsive for a few seconds when the server restarts." }),
|
|
68
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
69
|
+
"Some files (prompts, crons, triggers) won't be auto-updated to avoid breaking your bot.",
|
|
70
|
+
" ",
|
|
71
|
+
/* @__PURE__ */ jsx(
|
|
72
|
+
"a",
|
|
73
|
+
{
|
|
74
|
+
href: "https://github.com/stephengpope/thepopebot?tab=readme-ov-file#understanding-init",
|
|
75
|
+
target: "_blank",
|
|
76
|
+
rel: "noopener noreferrer",
|
|
77
|
+
className: "text-emerald-500 hover:underline",
|
|
78
|
+
children: "Learn more"
|
|
79
|
+
}
|
|
80
|
+
),
|
|
81
|
+
"."
|
|
82
|
+
] })
|
|
83
|
+
] }),
|
|
84
|
+
/* @__PURE__ */ jsx("div", { className: "mt-4 rounded-md border border-border bg-muted/30 p-3", children: /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
85
|
+
"If you hit unrecoverable errors, see the",
|
|
86
|
+
" ",
|
|
87
|
+
/* @__PURE__ */ jsx(
|
|
88
|
+
"a",
|
|
89
|
+
{
|
|
90
|
+
href: "https://github.com/stephengpope/thepopebot?tab=readme-ov-file#manual-updating",
|
|
91
|
+
target: "_blank",
|
|
92
|
+
rel: "noopener noreferrer",
|
|
93
|
+
className: "text-emerald-500 hover:underline",
|
|
94
|
+
children: "manual update guide"
|
|
95
|
+
}
|
|
96
|
+
),
|
|
97
|
+
"."
|
|
98
|
+
] }) })
|
|
99
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
100
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
|
|
101
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: "Upgrade Available" }),
|
|
102
|
+
/* @__PURE__ */ jsx("button", { onClick: handleClose, className: "text-muted-foreground hover:text-foreground", children: /* @__PURE__ */ jsx(XIcon, { size: 16 }) })
|
|
103
|
+
] }),
|
|
104
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
|
|
105
|
+
/* @__PURE__ */ jsx(ArrowUpCircleIcon, { size: 24 }),
|
|
106
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
107
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Installed version" }),
|
|
108
|
+
/* @__PURE__ */ jsxs("p", { className: "text-lg font-mono font-semibold", children: [
|
|
109
|
+
"v",
|
|
110
|
+
version
|
|
111
|
+
] })
|
|
112
|
+
] })
|
|
113
|
+
] }),
|
|
114
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-md border border-emerald-500/30 bg-emerald-500/5 p-3 mb-4", children: /* @__PURE__ */ jsxs("p", { className: "text-sm font-medium", children: [
|
|
115
|
+
"Version ",
|
|
116
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono text-emerald-500", children: [
|
|
117
|
+
"v",
|
|
118
|
+
updateAvailable
|
|
119
|
+
] }),
|
|
120
|
+
" is available"
|
|
121
|
+
] }) }),
|
|
122
|
+
changelog && /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
|
|
123
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium mb-2", children: "What's new" }),
|
|
124
|
+
/* @__PURE__ */ jsx("div", { className: "max-h-48 overflow-y-auto rounded-md border border-border bg-muted/30 p-3 text-sm text-muted-foreground whitespace-pre-wrap", children: changelog })
|
|
125
|
+
] }),
|
|
126
|
+
/* @__PURE__ */ jsx(
|
|
127
|
+
"button",
|
|
128
|
+
{
|
|
129
|
+
onClick: handleUpgrade,
|
|
130
|
+
disabled: upgrading,
|
|
131
|
+
className: "w-full inline-flex items-center justify-center gap-2 rounded-md px-4 py-2.5 text-sm font-medium bg-emerald-500 text-white hover:bg-emerald-600 disabled:opacity-50 disabled:pointer-events-none",
|
|
132
|
+
children: upgrading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
133
|
+
/* @__PURE__ */ jsx(SpinnerIcon, { size: 16 }),
|
|
134
|
+
"Triggering upgrade..."
|
|
135
|
+
] }) : result === "error" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
136
|
+
/* @__PURE__ */ jsx(RefreshIcon, { size: 16 }),
|
|
137
|
+
"Retry"
|
|
138
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
139
|
+
/* @__PURE__ */ jsx(ArrowUpCircleIcon, { size: 16 }),
|
|
140
|
+
"Upgrade to v",
|
|
141
|
+
updateAvailable
|
|
142
|
+
] })
|
|
143
|
+
}
|
|
144
|
+
),
|
|
145
|
+
result === "error" && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-400 mt-3", children: "Failed to trigger the upgrade workflow. Check that your GitHub token has workflow permissions." })
|
|
146
|
+
] }) })
|
|
147
|
+
] });
|
|
148
|
+
}
|
|
149
|
+
export {
|
|
150
|
+
UpgradeDialog
|
|
151
|
+
};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
4
|
+
import { ArrowUpCircleIcon, SpinnerIcon, CheckIcon, XIcon, RefreshIcon } from './icons.js';
|
|
5
|
+
import { triggerUpgrade } from '../actions.js';
|
|
6
|
+
|
|
7
|
+
export function UpgradeDialog({ open, onClose, version, updateAvailable, changelog }) {
|
|
8
|
+
const [upgrading, setUpgrading] = useState(false);
|
|
9
|
+
const [result, setResult] = useState(null);
|
|
10
|
+
|
|
11
|
+
const handleClose = useCallback(() => onClose(), [onClose]);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!open) return;
|
|
15
|
+
const handleEsc = (e) => {
|
|
16
|
+
if (e.key === 'Escape') handleClose();
|
|
17
|
+
};
|
|
18
|
+
document.addEventListener('keydown', handleEsc);
|
|
19
|
+
return () => document.removeEventListener('keydown', handleEsc);
|
|
20
|
+
}, [open, handleClose]);
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!open) {
|
|
24
|
+
setUpgrading(false);
|
|
25
|
+
setResult(null);
|
|
26
|
+
}
|
|
27
|
+
}, [open]);
|
|
28
|
+
|
|
29
|
+
if (!open) return null;
|
|
30
|
+
|
|
31
|
+
const handleUpgrade = async () => {
|
|
32
|
+
setUpgrading(true);
|
|
33
|
+
setResult(null);
|
|
34
|
+
try {
|
|
35
|
+
await triggerUpgrade();
|
|
36
|
+
setResult('success');
|
|
37
|
+
} catch {
|
|
38
|
+
setResult('error');
|
|
39
|
+
} finally {
|
|
40
|
+
setUpgrading(false);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
|
46
|
+
<div className="fixed inset-0 bg-black/50" onClick={handleClose} />
|
|
47
|
+
<div className="relative z-50 w-full max-w-md rounded-lg border border-border bg-background p-6 shadow-lg" onClick={(e) => e.stopPropagation()}>
|
|
48
|
+
{result === 'success' ? (
|
|
49
|
+
<>
|
|
50
|
+
<div className="flex items-center justify-between mb-4">
|
|
51
|
+
<h3 className="text-lg font-semibold">Upgrade Initiated</h3>
|
|
52
|
+
<button onClick={handleClose} className="text-muted-foreground hover:text-foreground">
|
|
53
|
+
<XIcon size={16} />
|
|
54
|
+
</button>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div className="flex items-center gap-3 mb-4">
|
|
58
|
+
<div className="flex items-center justify-center h-10 w-10 rounded-full bg-emerald-500/10 shrink-0">
|
|
59
|
+
<CheckIcon size={20} className="text-emerald-500" />
|
|
60
|
+
</div>
|
|
61
|
+
<p className="text-sm font-medium">
|
|
62
|
+
Upgrading to <span className="font-mono text-emerald-500">v{updateAvailable}</span>
|
|
63
|
+
</p>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div className="space-y-3 text-sm text-muted-foreground">
|
|
67
|
+
<p>
|
|
68
|
+
The upgrade process has been triggered and could take{' '}
|
|
69
|
+
<strong className="text-foreground">5–10 minutes</strong> to complete.
|
|
70
|
+
</p>
|
|
71
|
+
<p>
|
|
72
|
+
You can monitor progress from the{' '}
|
|
73
|
+
<a href="/swarm" className="text-emerald-500 hover:underline font-medium">Swarm</a> page.
|
|
74
|
+
</p>
|
|
75
|
+
<p>
|
|
76
|
+
The site may be briefly unresponsive for a few seconds when the server restarts.
|
|
77
|
+
</p>
|
|
78
|
+
<p>
|
|
79
|
+
Some files (prompts, crons, triggers) won't be auto-updated to avoid breaking your bot.{' '}
|
|
80
|
+
<a
|
|
81
|
+
href="https://github.com/stephengpope/thepopebot?tab=readme-ov-file#understanding-init"
|
|
82
|
+
target="_blank"
|
|
83
|
+
rel="noopener noreferrer"
|
|
84
|
+
className="text-emerald-500 hover:underline"
|
|
85
|
+
>
|
|
86
|
+
Learn more
|
|
87
|
+
</a>.
|
|
88
|
+
</p>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div className="mt-4 rounded-md border border-border bg-muted/30 p-3">
|
|
92
|
+
<p className="text-xs text-muted-foreground">
|
|
93
|
+
If you hit unrecoverable errors, see the{' '}
|
|
94
|
+
<a
|
|
95
|
+
href="https://github.com/stephengpope/thepopebot?tab=readme-ov-file#manual-updating"
|
|
96
|
+
target="_blank"
|
|
97
|
+
rel="noopener noreferrer"
|
|
98
|
+
className="text-emerald-500 hover:underline"
|
|
99
|
+
>
|
|
100
|
+
manual update guide
|
|
101
|
+
</a>.
|
|
102
|
+
</p>
|
|
103
|
+
</div>
|
|
104
|
+
</>
|
|
105
|
+
) : (
|
|
106
|
+
<>
|
|
107
|
+
<div className="flex items-center justify-between mb-4">
|
|
108
|
+
<h3 className="text-lg font-semibold">Upgrade Available</h3>
|
|
109
|
+
<button onClick={handleClose} className="text-muted-foreground hover:text-foreground">
|
|
110
|
+
<XIcon size={16} />
|
|
111
|
+
</button>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<div className="flex items-center gap-3 mb-4">
|
|
115
|
+
<ArrowUpCircleIcon size={24} />
|
|
116
|
+
<div>
|
|
117
|
+
<p className="text-sm text-muted-foreground">Installed version</p>
|
|
118
|
+
<p className="text-lg font-mono font-semibold">v{version}</p>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div className="rounded-md border border-emerald-500/30 bg-emerald-500/5 p-3 mb-4">
|
|
123
|
+
<p className="text-sm font-medium">
|
|
124
|
+
Version <span className="font-mono text-emerald-500">v{updateAvailable}</span> is available
|
|
125
|
+
</p>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
{changelog && (
|
|
129
|
+
<div className="mb-4">
|
|
130
|
+
<p className="text-sm font-medium mb-2">What's new</p>
|
|
131
|
+
<div className="max-h-48 overflow-y-auto rounded-md border border-border bg-muted/30 p-3 text-sm text-muted-foreground whitespace-pre-wrap">
|
|
132
|
+
{changelog}
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
)}
|
|
136
|
+
|
|
137
|
+
<button
|
|
138
|
+
onClick={handleUpgrade}
|
|
139
|
+
disabled={upgrading}
|
|
140
|
+
className="w-full inline-flex items-center justify-center gap-2 rounded-md px-4 py-2.5 text-sm font-medium bg-emerald-500 text-white hover:bg-emerald-600 disabled:opacity-50 disabled:pointer-events-none"
|
|
141
|
+
>
|
|
142
|
+
{upgrading ? (
|
|
143
|
+
<>
|
|
144
|
+
<SpinnerIcon size={16} />
|
|
145
|
+
Triggering upgrade...
|
|
146
|
+
</>
|
|
147
|
+
) : result === 'error' ? (
|
|
148
|
+
<>
|
|
149
|
+
<RefreshIcon size={16} />
|
|
150
|
+
Retry
|
|
151
|
+
</>
|
|
152
|
+
) : (
|
|
153
|
+
<>
|
|
154
|
+
<ArrowUpCircleIcon size={16} />
|
|
155
|
+
Upgrade to v{updateAvailable}
|
|
156
|
+
</>
|
|
157
|
+
)}
|
|
158
|
+
</button>
|
|
159
|
+
|
|
160
|
+
{result === 'error' && (
|
|
161
|
+
<p className="text-xs text-red-400 mt-3">
|
|
162
|
+
Failed to trigger the upgrade workflow. Check that your GitHub token has workflow permissions.
|
|
163
|
+
</p>
|
|
164
|
+
)}
|
|
165
|
+
</>
|
|
166
|
+
)}
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { clsx } from 'clsx';
|
|
2
|
+
import { twMerge } from 'tailwind-merge';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Merge Tailwind CSS classes with conflict resolution.
|
|
6
|
+
* @param {...any} inputs - Class name inputs
|
|
7
|
+
* @returns {string}
|
|
8
|
+
*/
|
|
9
|
+
export function cn(...inputs) {
|
|
10
|
+
return twMerge(clsx(inputs));
|
|
11
|
+
}
|
package/lib/cron.js
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import cron from 'node-cron';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { cronsFile, cronDir } from './paths.js';
|
|
5
|
+
import { executeAction } from './actions.js';
|
|
6
|
+
|
|
7
|
+
function getInstalledVersion() {
|
|
8
|
+
const pkgPath = path.join(process.cwd(), 'node_modules', 'thepopebot', 'package.json');
|
|
9
|
+
return JSON.parse(fs.readFileSync(pkgPath, 'utf8')).version;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// In-memory flag for available update (read by sidebar, written by cron)
|
|
13
|
+
let _updateAvailable = null;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get the in-memory update-available version (or null).
|
|
17
|
+
* @returns {string|null}
|
|
18
|
+
*/
|
|
19
|
+
function getUpdateAvailable() {
|
|
20
|
+
return _updateAvailable;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Set the in-memory update-available version.
|
|
25
|
+
* @param {string|null} v
|
|
26
|
+
*/
|
|
27
|
+
function setUpdateAvailable(v) {
|
|
28
|
+
_updateAvailable = v;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Compare two semver strings numerically.
|
|
33
|
+
* @param {string} candidate - e.g. "1.2.40"
|
|
34
|
+
* @param {string} baseline - e.g. "1.2.39"
|
|
35
|
+
* @returns {boolean} true if candidate > baseline
|
|
36
|
+
*/
|
|
37
|
+
function isVersionNewer(candidate, baseline) {
|
|
38
|
+
// Pre-release candidate is never "newer" for upgrade purposes
|
|
39
|
+
if (candidate.includes('-')) return false;
|
|
40
|
+
|
|
41
|
+
const a = candidate.split('.').map(Number);
|
|
42
|
+
const b = baseline.replace(/-.*$/, '').split('.').map(Number);
|
|
43
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
44
|
+
const av = a[i] || 0;
|
|
45
|
+
const bv = b[i] || 0;
|
|
46
|
+
if (av > bv) return true;
|
|
47
|
+
if (av < bv) return false;
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if a version string is a pre-release (contains '-').
|
|
54
|
+
* @param {string} v
|
|
55
|
+
* @returns {boolean}
|
|
56
|
+
*/
|
|
57
|
+
function isPrerelease(v) {
|
|
58
|
+
return v.includes('-');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Compare two semver strings (including pre-release).
|
|
63
|
+
* Returns positive if a > b, negative if a < b, 0 if equal.
|
|
64
|
+
* Ordering: 1.2.71-beta.0 < 1.2.71-beta.1 < 1.2.71 (stable) < 1.2.72-beta.0
|
|
65
|
+
* @param {string} a
|
|
66
|
+
* @param {string} b
|
|
67
|
+
* @returns {number}
|
|
68
|
+
*/
|
|
69
|
+
function compareVersions(a, b) {
|
|
70
|
+
const [aCore, aPre] = a.split('-');
|
|
71
|
+
const [bCore, bPre] = b.split('-');
|
|
72
|
+
|
|
73
|
+
const aParts = aCore.split('.').map(Number);
|
|
74
|
+
const bParts = bCore.split('.').map(Number);
|
|
75
|
+
|
|
76
|
+
// Compare major.minor.patch
|
|
77
|
+
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
78
|
+
const av = aParts[i] || 0;
|
|
79
|
+
const bv = bParts[i] || 0;
|
|
80
|
+
if (av !== bv) return av - bv;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Same core version: stable beats pre-release
|
|
84
|
+
if (!aPre && bPre) return 1; // a is stable, b is pre-release
|
|
85
|
+
if (aPre && !bPre) return -1; // a is pre-release, b is stable
|
|
86
|
+
if (!aPre && !bPre) return 0; // both stable, same core
|
|
87
|
+
|
|
88
|
+
// Both pre-release with same core: compare pre-release number
|
|
89
|
+
const aNum = parseInt(aPre.split('.').pop(), 10) || 0;
|
|
90
|
+
const bNum = parseInt(bPre.split('.').pop(), 10) || 0;
|
|
91
|
+
return aNum - bNum;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Fetch release notes from GitHub for the target version.
|
|
96
|
+
* @param {string} target - Target upgrade version
|
|
97
|
+
*/
|
|
98
|
+
async function fetchAndStoreReleaseNotes(target) {
|
|
99
|
+
try {
|
|
100
|
+
const ghRes = await fetch(
|
|
101
|
+
`https://api.github.com/repos/stephengpope/thepopebot/releases/tags/v${target}`
|
|
102
|
+
);
|
|
103
|
+
if (!ghRes.ok) return;
|
|
104
|
+
const release = await ghRes.json();
|
|
105
|
+
if (release.body) {
|
|
106
|
+
const { setReleaseNotes } = await import('./db/update-check.js');
|
|
107
|
+
setReleaseNotes(release.body);
|
|
108
|
+
}
|
|
109
|
+
} catch {}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check npm registry for a newer version of thepopebot.
|
|
114
|
+
*/
|
|
115
|
+
async function runVersionCheck() {
|
|
116
|
+
try {
|
|
117
|
+
const installed = getInstalledVersion();
|
|
118
|
+
|
|
119
|
+
if (isPrerelease(installed)) {
|
|
120
|
+
// Beta path: check both stable and beta dist-tags
|
|
121
|
+
const results = await Promise.allSettled([
|
|
122
|
+
fetch('https://registry.npmjs.org/thepopebot/latest'),
|
|
123
|
+
fetch('https://registry.npmjs.org/thepopebot/beta'),
|
|
124
|
+
]);
|
|
125
|
+
|
|
126
|
+
const candidates = [];
|
|
127
|
+
for (const result of results) {
|
|
128
|
+
if (result.status !== 'fulfilled') continue;
|
|
129
|
+
const res = result.value;
|
|
130
|
+
if (!res.ok) continue;
|
|
131
|
+
const data = await res.json();
|
|
132
|
+
if (data.version && compareVersions(data.version, installed) > 0) {
|
|
133
|
+
candidates.push(data.version);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (candidates.length > 0) {
|
|
138
|
+
// Pick the best candidate (highest version)
|
|
139
|
+
candidates.sort(compareVersions);
|
|
140
|
+
const best = candidates[candidates.length - 1];
|
|
141
|
+
console.log(`[version check] update available: ${installed} → ${best}`);
|
|
142
|
+
setUpdateAvailable(best);
|
|
143
|
+
const { setAvailableVersion } = await import('./db/update-check.js');
|
|
144
|
+
setAvailableVersion(best);
|
|
145
|
+
await fetchAndStoreReleaseNotes(best);
|
|
146
|
+
} else {
|
|
147
|
+
setUpdateAvailable(null);
|
|
148
|
+
const { clearAvailableVersion, clearReleaseNotes } = await import('./db/update-check.js');
|
|
149
|
+
clearAvailableVersion();
|
|
150
|
+
clearReleaseNotes();
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
// Stable path: existing logic, untouched
|
|
154
|
+
const res = await fetch('https://registry.npmjs.org/thepopebot/latest');
|
|
155
|
+
if (!res.ok) {
|
|
156
|
+
console.warn(`[version check] npm registry returned ${res.status}`);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const data = await res.json();
|
|
160
|
+
const latest = data.version;
|
|
161
|
+
|
|
162
|
+
if (isVersionNewer(latest, installed)) {
|
|
163
|
+
console.log(`[version check] update available: ${installed} → ${latest}`);
|
|
164
|
+
setUpdateAvailable(latest);
|
|
165
|
+
// Persist to DB
|
|
166
|
+
const { setAvailableVersion } = await import('./db/update-check.js');
|
|
167
|
+
setAvailableVersion(latest);
|
|
168
|
+
await fetchAndStoreReleaseNotes(latest);
|
|
169
|
+
} else {
|
|
170
|
+
setUpdateAvailable(null);
|
|
171
|
+
// Clear DB
|
|
172
|
+
const { clearAvailableVersion, clearReleaseNotes } = await import('./db/update-check.js');
|
|
173
|
+
clearAvailableVersion();
|
|
174
|
+
clearReleaseNotes();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} catch (err) {
|
|
178
|
+
console.warn(`[version check] failed: ${err.message}`);
|
|
179
|
+
// Leave existing flag untouched on error
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Start built-in crons (version check). Called from instrumentation.
|
|
185
|
+
*/
|
|
186
|
+
function startBuiltinCrons() {
|
|
187
|
+
// Schedule hourly
|
|
188
|
+
cron.schedule('0 * * * *', runVersionCheck);
|
|
189
|
+
// Run once immediately
|
|
190
|
+
runVersionCheck();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Load and schedule crons from CRONS.json
|
|
195
|
+
* @returns {Array} - Array of scheduled cron tasks
|
|
196
|
+
*/
|
|
197
|
+
function loadCrons() {
|
|
198
|
+
const cronFile = cronsFile;
|
|
199
|
+
|
|
200
|
+
console.log('\n--- Cron Jobs ---');
|
|
201
|
+
|
|
202
|
+
if (!fs.existsSync(cronFile)) {
|
|
203
|
+
console.log('No CRONS.json found');
|
|
204
|
+
console.log('-----------------\n');
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const crons = JSON.parse(fs.readFileSync(cronFile, 'utf8'));
|
|
209
|
+
const tasks = [];
|
|
210
|
+
|
|
211
|
+
for (const cronEntry of crons) {
|
|
212
|
+
const { name, schedule, type = 'agent', enabled } = cronEntry;
|
|
213
|
+
if (enabled === false) continue;
|
|
214
|
+
|
|
215
|
+
if (!cron.validate(schedule)) {
|
|
216
|
+
console.error(`Invalid schedule for "${name}": ${schedule}`);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const task = cron.schedule(schedule, async () => {
|
|
221
|
+
try {
|
|
222
|
+
const result = await executeAction(cronEntry, { cwd: cronDir });
|
|
223
|
+
console.log(`[CRON] ${name}: ${result || 'ran'}`);
|
|
224
|
+
console.log(`[CRON] ${name}: completed!`);
|
|
225
|
+
} catch (err) {
|
|
226
|
+
console.error(`[CRON] ${name}: error - ${err.message}`);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
tasks.push({ name, schedule, type, task });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (tasks.length === 0) {
|
|
234
|
+
console.log('No active cron jobs');
|
|
235
|
+
} else {
|
|
236
|
+
for (const { name, schedule, type } of tasks) {
|
|
237
|
+
console.log(` ${name}: ${schedule} (${type})`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
console.log('-----------------\n');
|
|
242
|
+
|
|
243
|
+
return tasks;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export { loadCrons, startBuiltinCrons, getUpdateAvailable, setUpdateAvailable, getInstalledVersion, isPrerelease };
|