@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,45 @@
|
|
|
1
|
+
# Credentials - NEVER commit these
|
|
2
|
+
|
|
3
|
+
.env
|
|
4
|
+
.env.local
|
|
5
|
+
*.pem
|
|
6
|
+
*.key
|
|
7
|
+
|
|
8
|
+
.claude/*
|
|
9
|
+
!.claude/skills
|
|
10
|
+
|
|
11
|
+
# Pi system prompt (generated at runtime from SOUL.md)
|
|
12
|
+
.pi/SYSTEM.md
|
|
13
|
+
|
|
14
|
+
# Skills dependencies (installed at runtime in Docker for correct arch)
|
|
15
|
+
skills/*/node_modules/
|
|
16
|
+
|
|
17
|
+
# Node
|
|
18
|
+
node_modules/
|
|
19
|
+
|
|
20
|
+
# Next.js
|
|
21
|
+
.next/
|
|
22
|
+
.next-new/
|
|
23
|
+
.next-old/
|
|
24
|
+
out/
|
|
25
|
+
|
|
26
|
+
# OS files
|
|
27
|
+
.DS_Store
|
|
28
|
+
Thumbs.db
|
|
29
|
+
|
|
30
|
+
# IDE
|
|
31
|
+
.idea/
|
|
32
|
+
.vscode/
|
|
33
|
+
*.swp
|
|
34
|
+
*.swo
|
|
35
|
+
|
|
36
|
+
# Logs
|
|
37
|
+
*.log
|
|
38
|
+
|
|
39
|
+
# Temporary files
|
|
40
|
+
tmp/
|
|
41
|
+
temp/
|
|
42
|
+
.tmp/
|
|
43
|
+
|
|
44
|
+
# Data (SQLite memory, etc.)
|
|
45
|
+
data/
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Env Sanitizer Extension - Protects credentials from AI agent access
|
|
3
|
+
*
|
|
4
|
+
* Uses Pi's spawnHook to filter sensitive env vars from bash subprocess calls
|
|
5
|
+
* while keeping them available in the main process for:
|
|
6
|
+
* - Anthropic SDK (needs ANTHROPIC_API_KEY at init)
|
|
7
|
+
* - GitHub CLI (needs GH_TOKEN)
|
|
8
|
+
* - Other extensions that may need credentials
|
|
9
|
+
*
|
|
10
|
+
* Dynamically filters all keys defined in the SECRETS JSON env var.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
14
|
+
import { createBashTool } from "@mariozechner/pi-coding-agent";
|
|
15
|
+
|
|
16
|
+
// Parse SECRETS JSON to get list of keys to filter
|
|
17
|
+
function getSecretKeys(): string[] {
|
|
18
|
+
const keys: string[] = [];
|
|
19
|
+
if (process.env.SECRETS) {
|
|
20
|
+
try {
|
|
21
|
+
const secrets = JSON.parse(process.env.SECRETS);
|
|
22
|
+
keys.push(...Object.keys(secrets));
|
|
23
|
+
} catch {
|
|
24
|
+
// Invalid JSON, ignore
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Always filter SECRETS itself
|
|
28
|
+
keys.push("SECRETS");
|
|
29
|
+
return [...new Set(keys)]; // Dedupe
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default function (pi: ExtensionAPI) {
|
|
33
|
+
const secretKeys = getSecretKeys();
|
|
34
|
+
|
|
35
|
+
// Override bash tool with filtered environment for subprocesses
|
|
36
|
+
const bashTool = createBashTool(process.cwd(), {
|
|
37
|
+
spawnHook: ({ command, cwd, env }) => {
|
|
38
|
+
// Filter all secret keys from subprocess environment
|
|
39
|
+
const filteredEnv = { ...env };
|
|
40
|
+
for (const key of secretKeys) {
|
|
41
|
+
delete filteredEnv[key];
|
|
42
|
+
}
|
|
43
|
+
return { command, cwd, env: filteredEnv };
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
pi.registerTool(bashTool);
|
|
48
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Templates Directory — Scaffolding Only
|
|
2
|
+
|
|
3
|
+
This directory contains files that get copied into user projects when they run `npx thepopebot init`. It is **not** where event handler logic, API routes, or core features live.
|
|
4
|
+
|
|
5
|
+
## Rules
|
|
6
|
+
|
|
7
|
+
- **NEVER** add event handler code, API route handlers, or core logic here. All of that belongs in the NPM package (`api/`, `lib/`, `config/`, `bin/`).
|
|
8
|
+
- Templates exist solely to scaffold a new user's project folder with thin wiring and user-editable configuration.
|
|
9
|
+
- Files here may be modified to fix wiring, update configuration defaults, or adjust scaffolding — but never to implement features.
|
|
10
|
+
|
|
11
|
+
## What belongs here
|
|
12
|
+
|
|
13
|
+
- **Next.js wiring**: `next.config.mjs`, `instrumentation.js`, catch-all route, middleware — thin re-exports from `thepopebot/*`
|
|
14
|
+
- **User-editable config**: `config/SOUL.md`, `config/EVENT_HANDLER.md`, `config/CRONS.json`, `config/TRIGGERS.json`, etc.
|
|
15
|
+
- **GitHub Actions workflows**: `.github/workflows/`
|
|
16
|
+
- **Docker files**: `docker/`, `docker-compose.yml`
|
|
17
|
+
- **UI page shells**: `app/` pages that import components from the package
|
|
18
|
+
- **Client components**: `app/components/` for components that must live in the user's project (e.g., `login-form.jsx`, `setup-form.jsx`) because they depend on user-side packages like `next-auth/react`
|
|
19
|
+
|
|
20
|
+
## What does NOT belong here
|
|
21
|
+
|
|
22
|
+
- Route handlers with business logic
|
|
23
|
+
- Library code (`lib/`)
|
|
24
|
+
- Database operations
|
|
25
|
+
- LLM/AI integrations
|
|
26
|
+
- Tool implementations
|
|
27
|
+
- Anything that should be shared across all users via `npm update thepopebot`
|
|
28
|
+
|
|
29
|
+
If you're adding a feature to the event handler, put it in the package. Templates just wire into it.
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# My Agent
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This is an autonomous AI agent powered by [thepopebot](https://github.com/stephengpope/thepopebot). It uses a **two-layer architecture**:
|
|
6
|
+
|
|
7
|
+
1. **Event Handler** — A Next.js server that orchestrates everything: web UI, Telegram chat, cron scheduling, webhook triggers, and job creation.
|
|
8
|
+
2. **Docker Agent** — A container that runs the Pi coding agent for autonomous task execution. Each job gets its own branch, container, and PR.
|
|
9
|
+
|
|
10
|
+
All core logic lives in the `thepopebot` npm package. This project is a scaffolded shell — thin Next.js wiring, user-editable configuration, GitHub Actions workflows, and Docker files.
|
|
11
|
+
|
|
12
|
+
## Directory Structure
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
project-root/
|
|
16
|
+
├── CLAUDE.md # This file (project documentation)
|
|
17
|
+
├── next.config.mjs # Next.js config (wraps withThepopebot())
|
|
18
|
+
├── instrumentation.js # Server startup hook (re-exports from package)
|
|
19
|
+
├── middleware.js # Auth middleware (re-exports from package)
|
|
20
|
+
├── .env # API keys and tokens (gitignored)
|
|
21
|
+
├── package.json
|
|
22
|
+
│
|
|
23
|
+
├── app/ # Next.js app directory (page shells + client components)
|
|
24
|
+
│ ├── api/[...thepopebot]/route.js # Catch-all API route (re-exports from package)
|
|
25
|
+
│ ├── stream/chat/route.js # Chat streaming endpoint (session auth)
|
|
26
|
+
│ └── components/ # Client-side components
|
|
27
|
+
│
|
|
28
|
+
├── config/ # Agent configuration (user-editable)
|
|
29
|
+
│ ├── SOUL.md # Personality, identity, and values
|
|
30
|
+
│ ├── EVENT_HANDLER.md # Event handler LLM system prompt
|
|
31
|
+
│ ├── AGENT.md # Agent runtime environment docs
|
|
32
|
+
│ ├── JOB_SUMMARY.md # Prompt for summarizing completed jobs
|
|
33
|
+
│ ├── HEARTBEAT.md # Self-monitoring / heartbeat behavior
|
|
34
|
+
│ ├── SKILL_BUILDING_GUIDE.md # Guide for building agent skills
|
|
35
|
+
│ ├── CRONS.json # Scheduled job definitions
|
|
36
|
+
│ └── TRIGGERS.json # Webhook trigger definitions
|
|
37
|
+
│
|
|
38
|
+
├── .github/workflows/ # GitHub Actions
|
|
39
|
+
├── docker/ # Docker files (job agent + event handler)
|
|
40
|
+
├── skills/ # All available agent skills
|
|
41
|
+
│ └── active/ # Symlinks to active skills (shared by Pi + Claude Code)
|
|
42
|
+
├── .pi/skills → skills/active # Pi reads skills from here
|
|
43
|
+
├── .claude/skills → skills/active # Claude Code reads skills from here
|
|
44
|
+
├── cron/ # Scripts for command-type cron actions
|
|
45
|
+
├── triggers/ # Scripts for command-type trigger actions
|
|
46
|
+
├── logs/ # Per-job output (logs/<JOB_ID>/job.md + session .jsonl)
|
|
47
|
+
└── data/ # SQLite database (data/thepopebot.sqlite)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Two-Layer Architecture
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
54
|
+
│ │
|
|
55
|
+
│ ┌──────────────────┐ ┌──────────────────┐ │
|
|
56
|
+
│ │ Event Handler │ ──1──► │ GitHub │ │
|
|
57
|
+
│ │ (creates job) │ │ (job/* branch) │ │
|
|
58
|
+
│ └────────▲─────────┘ └────────┬─────────┘ │
|
|
59
|
+
│ │ │ │
|
|
60
|
+
│ │ 2 (triggers run-job.yml) │
|
|
61
|
+
│ │ │ │
|
|
62
|
+
│ │ ▼ │
|
|
63
|
+
│ │ ┌──────────────────┐ │
|
|
64
|
+
│ │ │ Docker Agent │ │
|
|
65
|
+
│ │ │ (runs Pi, PRs) │ │
|
|
66
|
+
│ │ └────────┬─────────┘ │
|
|
67
|
+
│ │ │ │
|
|
68
|
+
│ │ 3 (creates PR) │
|
|
69
|
+
│ │ │ │
|
|
70
|
+
│ │ ▼ │
|
|
71
|
+
│ │ ┌──────────────────┐ │
|
|
72
|
+
│ │ │ GitHub │ │
|
|
73
|
+
│ │ │ (PR opened) │ │
|
|
74
|
+
│ │ └────────┬─────────┘ │
|
|
75
|
+
│ │ │ │
|
|
76
|
+
│ │ 4a (auto-merge.yml) │
|
|
77
|
+
│ │ 4b (notify-pr-complete.yml) │
|
|
78
|
+
│ │ │ │
|
|
79
|
+
│ 5 (notification → web UI │ │
|
|
80
|
+
│ and Telegram) │ │
|
|
81
|
+
│ └────────────────────────────┘ │
|
|
82
|
+
│ │
|
|
83
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Event Handler** (this Next.js server): Receives requests (web UI, Telegram, webhooks, cron timers), creates jobs by pushing a `job/<uuid>` branch to GitHub, and manages the web interface.
|
|
87
|
+
|
|
88
|
+
**Docker Agent**: A container spun up by GitHub Actions (`run-job.yml`) that clones the job branch, runs the Pi coding agent with the job prompt, commits results, and opens a PR.
|
|
89
|
+
|
|
90
|
+
## Job Lifecycle
|
|
91
|
+
|
|
92
|
+
1. **Job created** — Event handler calls `createJob()` (via chat, cron, trigger, or API)
|
|
93
|
+
2. **Branch pushed** — A `job/<uuid>` branch is created with `logs/<uuid>/job.md` containing the task prompt
|
|
94
|
+
3. **Workflow triggers** — `run-job.yml` fires on `job/*` branch creation
|
|
95
|
+
4. **Container runs** — Docker agent clones the branch, builds `SYSTEM.md` from `config/SOUL.md` + `config/AGENT.md`, runs Pi with the job prompt, and logs the session to `logs/<uuid>/`
|
|
96
|
+
5. **PR created** — Agent commits results and opens a pull request
|
|
97
|
+
6. **Auto-merge** — `auto-merge.yml` squash-merges the PR if all changed files fall within `ALLOWED_PATHS` prefixes (default: `/logs`)
|
|
98
|
+
7. **Notification** — `notify-pr-complete.yml` sends job results back to the event handler, which creates a notification in the web UI and sends a Telegram message
|
|
99
|
+
|
|
100
|
+
## Action Types
|
|
101
|
+
|
|
102
|
+
Both cron jobs and webhook triggers use the same dispatch system. Every action has a `type` field:
|
|
103
|
+
|
|
104
|
+
| | `agent` (default) | `command` | `webhook` |
|
|
105
|
+
|---|---|---|---|
|
|
106
|
+
| **Uses LLM** | Yes — spins up Pi in Docker | No | No |
|
|
107
|
+
| **Runtime** | Minutes to hours | Milliseconds to seconds | Milliseconds to seconds |
|
|
108
|
+
| **Cost** | LLM API calls + GitHub Actions | Free (runs on event handler) | Free (runs on event handler) |
|
|
109
|
+
| **Use case** | Tasks that need to think, reason, write code | Shell scripts, file operations | Call external APIs, forward webhooks |
|
|
110
|
+
|
|
111
|
+
If the task needs to *think*, use `agent`. If it just needs to *do*, use `command`. If it needs to *call an external service*, use `webhook`.
|
|
112
|
+
|
|
113
|
+
### Agent action
|
|
114
|
+
```json
|
|
115
|
+
{ "type": "agent", "job": "Analyze the logs and write a summary report" }
|
|
116
|
+
```
|
|
117
|
+
Creates a Docker Agent job. The `job` string is passed as-is to the LLM as its task prompt.
|
|
118
|
+
|
|
119
|
+
### Command action
|
|
120
|
+
```json
|
|
121
|
+
{ "type": "command", "command": "node cleanup.js --older-than 7d" }
|
|
122
|
+
```
|
|
123
|
+
Runs a shell command on the event handler. Working directory: `cron/` for crons, `triggers/` for triggers.
|
|
124
|
+
|
|
125
|
+
### Webhook action
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"type": "webhook",
|
|
129
|
+
"url": "https://api.example.com/notify",
|
|
130
|
+
"method": "POST",
|
|
131
|
+
"headers": { "Authorization": "Bearer token" },
|
|
132
|
+
"vars": { "source": "my-agent" }
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
Makes an HTTP request. `GET` skips the body. `POST` (default) sends `{ ...vars }` or `{ ...vars, data: <incoming payload> }` when triggered by a webhook.
|
|
136
|
+
|
|
137
|
+
## Cron Jobs
|
|
138
|
+
|
|
139
|
+
Defined in `config/CRONS.json`, loaded at server startup by `node-cron`.
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
[
|
|
143
|
+
{
|
|
144
|
+
"name": "Daily Check",
|
|
145
|
+
"schedule": "0 9 * * *",
|
|
146
|
+
"type": "agent",
|
|
147
|
+
"job": "Review recent activity and summarize findings",
|
|
148
|
+
"enabled": true
|
|
149
|
+
}
|
|
150
|
+
]
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
| Field | Required | Description |
|
|
154
|
+
|-------|----------|-------------|
|
|
155
|
+
| `name` | Yes | Display name |
|
|
156
|
+
| `schedule` | Yes | Cron expression (e.g., `0 9 * * *` = daily at 9am) |
|
|
157
|
+
| `type` | No | `agent` (default), `command`, or `webhook` |
|
|
158
|
+
| `job` | For agent | Task prompt passed to the LLM |
|
|
159
|
+
| `command` | For command | Shell command (runs in `cron/` directory) |
|
|
160
|
+
| `url` | For webhook | Target URL |
|
|
161
|
+
| `method` | For webhook | `GET` or `POST` (default: `POST`) |
|
|
162
|
+
| `headers` | For webhook | Custom request headers |
|
|
163
|
+
| `vars` | For webhook | Key-value pairs merged into request body |
|
|
164
|
+
| `enabled` | No | Set `false` to disable (default: `true`) |
|
|
165
|
+
| `llm_provider` | No | Override LLM provider for this cron (agent type only) |
|
|
166
|
+
| `llm_model` | No | Override LLM model for this cron (agent type only) |
|
|
167
|
+
|
|
168
|
+
## Webhook Triggers
|
|
169
|
+
|
|
170
|
+
Defined in `config/TRIGGERS.json`, loaded at server startup. Triggers fire on POST requests to watched paths (after auth, before route handler, fire-and-forget).
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
[
|
|
174
|
+
{
|
|
175
|
+
"name": "GitHub Push",
|
|
176
|
+
"watch_path": "/webhook/github-push",
|
|
177
|
+
"enabled": true,
|
|
178
|
+
"actions": [
|
|
179
|
+
{
|
|
180
|
+
"type": "agent",
|
|
181
|
+
"job": "Review the push to {{body.ref}}: {{body.head_commit.message}}"
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
}
|
|
185
|
+
]
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
| Field | Required | Description |
|
|
189
|
+
|-------|----------|-------------|
|
|
190
|
+
| `name` | Yes | Display name |
|
|
191
|
+
| `watch_path` | Yes | URL path to watch (e.g., `/webhook/github-push`) |
|
|
192
|
+
| `actions` | Yes | Array of actions to fire (same fields as cron actions) |
|
|
193
|
+
| `enabled` | No | Set `false` to disable (default: `true`) |
|
|
194
|
+
|
|
195
|
+
**Template tokens** for `job` and `command` strings:
|
|
196
|
+
|
|
197
|
+
| Token | Resolves to |
|
|
198
|
+
|-------|-------------|
|
|
199
|
+
| `{{body}}` | Entire request body as JSON |
|
|
200
|
+
| `{{body.field}}` | Nested field from request body |
|
|
201
|
+
| `{{query}}` | All query parameters as JSON |
|
|
202
|
+
| `{{query.field}}` | Specific query parameter |
|
|
203
|
+
| `{{headers}}` | All request headers as JSON |
|
|
204
|
+
| `{{headers.field}}` | Specific request header |
|
|
205
|
+
|
|
206
|
+
## API Endpoints
|
|
207
|
+
|
|
208
|
+
All API routes are under `/api/`, handled by the catch-all route.
|
|
209
|
+
|
|
210
|
+
| Endpoint | Method | Auth | Purpose |
|
|
211
|
+
|----------|--------|------|---------|
|
|
212
|
+
| `/api/create-job` | POST | `x-api-key` | Create a new autonomous agent job |
|
|
213
|
+
| `/api/telegram/webhook` | POST | `TELEGRAM_WEBHOOK_SECRET` | Telegram bot webhook |
|
|
214
|
+
| `/api/telegram/register` | POST | `x-api-key` | Register Telegram webhook URL |
|
|
215
|
+
| `/api/github/webhook` | POST | `GH_WEBHOOK_SECRET` | Receive notifications from GitHub Actions |
|
|
216
|
+
| `/api/jobs/status` | GET | `x-api-key` | Check status of running/queued jobs |
|
|
217
|
+
| `/api/ping` | GET | Public | Health check |
|
|
218
|
+
|
|
219
|
+
**`x-api-key`**: Database-backed API keys generated through the web UI (Settings > Secrets). Keys are SHA-256 hashed, verified with timing-safe comparison. Format: `tpb_` prefix + 64 hex characters.
|
|
220
|
+
|
|
221
|
+
## Web Interface
|
|
222
|
+
|
|
223
|
+
Accessible after login at `APP_URL`. Routes: `/` (chat), `/chats` (history), `/chat/[chatId]` (resume chat), `/settings/crons`, `/settings/triggers`, `/settings/secrets` (API keys), `/swarm` (job monitor), `/notifications`, `/login` (auth / first-time admin setup).
|
|
224
|
+
|
|
225
|
+
## Authentication
|
|
226
|
+
|
|
227
|
+
NextAuth v5 with Credentials provider (email/password), JWT in httpOnly cookies. First visit creates admin account. Browser UI uses Server Actions with `requireAuth()`. API routes use `x-api-key` header. Chat streaming uses a dedicated route at `/stream/chat` with `auth()` session check.
|
|
228
|
+
|
|
229
|
+
## Database
|
|
230
|
+
|
|
231
|
+
SQLite via Drizzle ORM at `data/thepopebot.sqlite`. Auto-initialized and auto-migrated on server startup. Tables: `users`, `chats`, `messages`, `notifications`, `subscriptions`, `settings` (key-value store, also stores API keys). Column naming: camelCase in JS → snake_case in SQL.
|
|
232
|
+
|
|
233
|
+
## GitHub Actions Workflows
|
|
234
|
+
|
|
235
|
+
| Workflow | Trigger | Purpose |
|
|
236
|
+
|----------|---------|---------|
|
|
237
|
+
| `run-job.yml` | `job/*` branch created | Runs the Docker agent container |
|
|
238
|
+
| `rebuild-event-handler.yml` | Push to `main` | Rebuilds server (fast path or Docker restart) |
|
|
239
|
+
| `upgrade-event-handler.yml` | Manual `workflow_dispatch` | Creates PR to upgrade thepopebot package |
|
|
240
|
+
| `build-image.yml` | `docker/job-pi-coding-agent/**` changes | Builds Pi coding agent Docker image to GHCR |
|
|
241
|
+
| `auto-merge.yml` | Job PR opened | Squash-merges if changes are within `ALLOWED_PATHS` |
|
|
242
|
+
| `notify-pr-complete.yml` | After `auto-merge.yml` | Sends job completion notification |
|
|
243
|
+
| `notify-job-failed.yml` | `run-job.yml` fails | Sends failure notification |
|
|
244
|
+
|
|
245
|
+
## GitHub Secrets & Variables
|
|
246
|
+
|
|
247
|
+
### Secrets (prefix-based naming)
|
|
248
|
+
|
|
249
|
+
| Prefix | Purpose | Visible to LLM? | Example |
|
|
250
|
+
|--------|---------|------------------|---------|
|
|
251
|
+
| `AGENT_` | Protected credentials for Docker agent | No (filtered by env-sanitizer) | `AGENT_GH_TOKEN`, `AGENT_ANTHROPIC_API_KEY` |
|
|
252
|
+
| `AGENT_LLM_` | LLM-accessible credentials for Docker agent | Yes | `AGENT_LLM_BRAVE_API_KEY` |
|
|
253
|
+
| *(none)* | Workflow-only secrets (never passed to container) | N/A | `GH_WEBHOOK_SECRET` |
|
|
254
|
+
|
|
255
|
+
`AGENT_*` secrets are collected into a `SECRETS` JSON object by `run-job.yml` (prefix stripped) and exported as env vars in the container. `AGENT_LLM_*` go into `LLM_SECRETS` and are not filtered from the LLM's bash environment.
|
|
256
|
+
|
|
257
|
+
### Repository Variables
|
|
258
|
+
|
|
259
|
+
| Variable | Description | Default |
|
|
260
|
+
|----------|-------------|---------|
|
|
261
|
+
| `APP_URL` | Public URL for the event handler | Required |
|
|
262
|
+
| `AUTO_MERGE` | Set to `"false"` to disable auto-merge | Enabled |
|
|
263
|
+
| `ALLOWED_PATHS` | Comma-separated path prefixes for auto-merge | `/logs` |
|
|
264
|
+
| `JOB_IMAGE_URL` | Docker image for job agent (GHCR URLs trigger auto-builds) | Default thepopebot image |
|
|
265
|
+
| `EVENT_HANDLER_IMAGE_URL` | Docker image for event handler | Default thepopebot image |
|
|
266
|
+
| `RUNS_ON` | GitHub Actions runner label | `ubuntu-latest` |
|
|
267
|
+
| `LLM_PROVIDER` | LLM provider for Docker agent | `anthropic` |
|
|
268
|
+
| `LLM_MODEL` | LLM model name for Docker agent | Provider default |
|
|
269
|
+
|
|
270
|
+
## Environment Variables
|
|
271
|
+
|
|
272
|
+
| Variable | Description | Required |
|
|
273
|
+
|----------|-------------|----------|
|
|
274
|
+
| `APP_URL` | Public URL for webhooks and Telegram | Yes |
|
|
275
|
+
| `AUTH_SECRET` | NextAuth session encryption (auto-generated) | Yes |
|
|
276
|
+
| `GH_TOKEN` | GitHub PAT for creating branches/files | Yes |
|
|
277
|
+
| `GH_OWNER` | GitHub repository owner | Yes |
|
|
278
|
+
| `GH_REPO` | GitHub repository name | Yes |
|
|
279
|
+
| `TELEGRAM_BOT_TOKEN` | Telegram bot token | For Telegram |
|
|
280
|
+
| `TELEGRAM_WEBHOOK_SECRET` | Telegram webhook validation secret | No |
|
|
281
|
+
| `TELEGRAM_CHAT_ID` | Default chat ID for notifications | For Telegram |
|
|
282
|
+
| `GH_WEBHOOK_SECRET` | GitHub Actions webhook auth | For notifications |
|
|
283
|
+
| `LLM_PROVIDER` | `anthropic`, `openai`, `google`, or `custom` | No (default: `anthropic`) |
|
|
284
|
+
| `LLM_MODEL` | Model name override | No |
|
|
285
|
+
| `LLM_MAX_TOKENS` | Max tokens for LLM responses | No (default: 4096) |
|
|
286
|
+
| `ANTHROPIC_API_KEY` | Anthropic API key | For anthropic provider |
|
|
287
|
+
| `OPENAI_API_KEY` | OpenAI API key / Whisper | For openai provider |
|
|
288
|
+
| `OPENAI_BASE_URL` | Custom OpenAI-compatible base URL | For custom provider |
|
|
289
|
+
| `GOOGLE_API_KEY` | Google API key | For google provider |
|
|
290
|
+
| `CUSTOM_API_KEY` | Custom provider API key | For custom provider |
|
|
291
|
+
| `DATABASE_PATH` | Override SQLite DB location | No |
|
|
292
|
+
|
|
293
|
+
## Customization
|
|
294
|
+
|
|
295
|
+
User-editable config files in `config/`: `SOUL.md` (personality), `EVENT_HANDLER.md` (LLM system prompt), `AGENT.md` (runtime docs), `JOB_SUMMARY.md` (job summaries), `HEARTBEAT.md` (self-monitoring), `SKILL_BUILDING_GUIDE.md` (skill guide), `CRONS.json` (scheduled jobs), `TRIGGERS.json` (webhook triggers).
|
|
296
|
+
|
|
297
|
+
Skills in `skills/` are activated by symlinking into `skills/active/`. Both `.pi/skills` and `.claude/skills` point to `skills/active/`. Scripts for command-type actions go in `cron/` and `triggers/`.
|
|
298
|
+
|
|
299
|
+
### Markdown includes and variables
|
|
300
|
+
|
|
301
|
+
Config markdown files support includes and built-in variables (processed by the package's `render-md.js`):
|
|
302
|
+
|
|
303
|
+
| Syntax | Description |
|
|
304
|
+
|--------|-------------|
|
|
305
|
+
| `{{ filepath.md }}` | Include another file (relative to project root, recursive with circular detection) |
|
|
306
|
+
| `{{datetime}}` | Current ISO timestamp |
|
|
307
|
+
| `{{skills}}` | Dynamic bullet list of active skill descriptions from `skills/active/*/SKILL.md` frontmatter — never hardcode skill names, this is resolved at runtime |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { GET, POST } from 'thepopebot/api';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { GET, POST } from 'thepopebot/auth';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { auth } from 'thepopebot/auth';
|
|
2
|
+
import { ChatPage } from 'thepopebot/chat';
|
|
3
|
+
|
|
4
|
+
export default async function ChatRoute({ params }) {
|
|
5
|
+
const { chatId } = await params;
|
|
6
|
+
const session = await auth();
|
|
7
|
+
return <ChatPage session={session} needsSetup={false} chatId={chatId} />;
|
|
8
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function AsciiLogo() {
|
|
2
|
+
return (
|
|
3
|
+
<pre className="text-foreground text-[clamp(0.45rem,1.5vw,0.85rem)] leading-snug text-left mb-8 select-none">{` _____ _ ____ ____ _
|
|
4
|
+
|_ _| |__ ___| _ \\ ___ _ __ ___| __ ) ___ | |_
|
|
5
|
+
| | | '_ \\ / _ \\ |_) / _ \\| '_ \\ / _ \\ _ \\ / _ \\| __|
|
|
6
|
+
| | | | | | __/ __/ (_) | |_) | __/ |_) | (_) | |_
|
|
7
|
+
|_| |_| |_|\\___|_| \\___/| .__/ \\___|____/ \\___/ \\__|
|
|
8
|
+
|_|`}</pre>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { signIn } from 'next-auth/react';
|
|
5
|
+
import { useRouter, useSearchParams } from 'next/navigation';
|
|
6
|
+
import { Button } from './ui/button';
|
|
7
|
+
import { Input } from './ui/input';
|
|
8
|
+
import { Label } from './ui/label';
|
|
9
|
+
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from './ui/card';
|
|
10
|
+
|
|
11
|
+
export function LoginForm() {
|
|
12
|
+
const router = useRouter();
|
|
13
|
+
const searchParams = useSearchParams();
|
|
14
|
+
const justCreated = searchParams.get('created') === '1';
|
|
15
|
+
const [email, setEmail] = useState('');
|
|
16
|
+
const [password, setPassword] = useState('');
|
|
17
|
+
const [error, setError] = useState('');
|
|
18
|
+
const [loading, setLoading] = useState(false);
|
|
19
|
+
|
|
20
|
+
async function handleSubmit(e) {
|
|
21
|
+
e.preventDefault();
|
|
22
|
+
setError('');
|
|
23
|
+
setLoading(true);
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const result = await signIn('credentials', {
|
|
27
|
+
email,
|
|
28
|
+
password,
|
|
29
|
+
redirect: false,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (result?.error) {
|
|
33
|
+
setError('Invalid email or password.');
|
|
34
|
+
} else {
|
|
35
|
+
router.push('/');
|
|
36
|
+
}
|
|
37
|
+
} catch {
|
|
38
|
+
setError('Something went wrong. Please try again.');
|
|
39
|
+
} finally {
|
|
40
|
+
setLoading(false);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div className="w-full max-w-sm space-y-3">
|
|
46
|
+
{justCreated && (
|
|
47
|
+
<div className="rounded-lg border border-green-500/30 bg-green-500/5 p-4">
|
|
48
|
+
<p className="text-sm font-medium text-green-600 dark:text-green-400">
|
|
49
|
+
Account created. Sign in with your new credentials.
|
|
50
|
+
</p>
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
<Card className="w-full max-w-sm">
|
|
54
|
+
<CardHeader>
|
|
55
|
+
<CardTitle>Sign In</CardTitle>
|
|
56
|
+
<CardDescription>Log in to your agent dashboard.</CardDescription>
|
|
57
|
+
</CardHeader>
|
|
58
|
+
<CardContent>
|
|
59
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
60
|
+
<div className="space-y-2">
|
|
61
|
+
<Label htmlFor="email">Email</Label>
|
|
62
|
+
<Input
|
|
63
|
+
id="email"
|
|
64
|
+
type="email"
|
|
65
|
+
placeholder="admin@example.com"
|
|
66
|
+
value={email}
|
|
67
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
68
|
+
required
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
<div className="space-y-2">
|
|
72
|
+
<Label htmlFor="password">Password</Label>
|
|
73
|
+
<Input
|
|
74
|
+
id="password"
|
|
75
|
+
type="password"
|
|
76
|
+
value={password}
|
|
77
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
78
|
+
required
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
{error && (
|
|
82
|
+
<p className="text-sm text-destructive">{error}</p>
|
|
83
|
+
)}
|
|
84
|
+
<Button type="submit" className="w-full" disabled={loading}>
|
|
85
|
+
{loading ? 'Signing in...' : 'Sign In'}
|
|
86
|
+
</Button>
|
|
87
|
+
</form>
|
|
88
|
+
</CardContent>
|
|
89
|
+
</Card>
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { useRouter } from 'next/navigation';
|
|
5
|
+
import { Button } from './ui/button';
|
|
6
|
+
import { Input } from './ui/input';
|
|
7
|
+
import { Label } from './ui/label';
|
|
8
|
+
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from './ui/card';
|
|
9
|
+
import { setupAdmin } from 'thepopebot/auth/actions';
|
|
10
|
+
|
|
11
|
+
export function SetupForm() {
|
|
12
|
+
const router = useRouter();
|
|
13
|
+
const [email, setEmail] = useState('');
|
|
14
|
+
const [password, setPassword] = useState('');
|
|
15
|
+
const [error, setError] = useState('');
|
|
16
|
+
const [loading, setLoading] = useState(false);
|
|
17
|
+
|
|
18
|
+
async function handleSubmit(e) {
|
|
19
|
+
e.preventDefault();
|
|
20
|
+
setError('');
|
|
21
|
+
setLoading(true);
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const result = await setupAdmin(email, password);
|
|
25
|
+
|
|
26
|
+
if (result.error) {
|
|
27
|
+
setError(result.error);
|
|
28
|
+
setLoading(false);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Redirect to login with success indicator — admin must authenticate through the normal flow
|
|
33
|
+
router.push('/login?created=1');
|
|
34
|
+
} catch {
|
|
35
|
+
setError('Something went wrong. Please try again.');
|
|
36
|
+
} finally {
|
|
37
|
+
setLoading(false);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Card className="w-full max-w-sm">
|
|
43
|
+
<CardHeader>
|
|
44
|
+
<CardTitle>Create Admin Account</CardTitle>
|
|
45
|
+
<CardDescription>Set up your first admin account to get started.</CardDescription>
|
|
46
|
+
</CardHeader>
|
|
47
|
+
<CardContent>
|
|
48
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
49
|
+
<div className="space-y-2">
|
|
50
|
+
<Label htmlFor="email">Email</Label>
|
|
51
|
+
<Input
|
|
52
|
+
id="email"
|
|
53
|
+
type="email"
|
|
54
|
+
placeholder="admin@example.com"
|
|
55
|
+
value={email}
|
|
56
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
57
|
+
required
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
<div className="space-y-2">
|
|
61
|
+
<Label htmlFor="password">Password</Label>
|
|
62
|
+
<Input
|
|
63
|
+
id="password"
|
|
64
|
+
type="password"
|
|
65
|
+
placeholder="Min 8 characters"
|
|
66
|
+
value={password}
|
|
67
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
68
|
+
required
|
|
69
|
+
minLength={8}
|
|
70
|
+
/>
|
|
71
|
+
</div>
|
|
72
|
+
{error && (
|
|
73
|
+
<p className="text-sm text-destructive">{error}</p>
|
|
74
|
+
)}
|
|
75
|
+
<Button type="submit" className="w-full" disabled={loading}>
|
|
76
|
+
{loading ? 'Creating...' : 'Create Account'}
|
|
77
|
+
</Button>
|
|
78
|
+
</form>
|
|
79
|
+
</CardContent>
|
|
80
|
+
</Card>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { ThemeProvider as NextThemesProvider } from 'next-themes';
|
|
4
|
+
|
|
5
|
+
export function ThemeProvider({ children }) {
|
|
6
|
+
return (
|
|
7
|
+
<NextThemesProvider attribute="class" defaultTheme="system" enableSystem>
|
|
8
|
+
{children}
|
|
9
|
+
</NextThemesProvider>
|
|
10
|
+
);
|
|
11
|
+
}
|