@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.
Files changed (317) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +406 -0
  3. package/agents/README.md +76 -0
  4. package/agents/_template/CONFIG.yaml +7 -0
  5. package/agents/_template/HEARTBEAT.md +59 -0
  6. package/agents/_template/IDENTITY.md +4 -0
  7. package/agents/_template/SKILLS.md +1 -0
  8. package/agents/_template/SOUL.md +25 -0
  9. package/agents/_template/TOOLS.md +3 -0
  10. package/agents/binary-reverser/CONFIG.yaml +21 -0
  11. package/agents/binary-reverser/HEARTBEAT.md +65 -0
  12. package/agents/binary-reverser/IDENTITY.md +1 -0
  13. package/agents/binary-reverser/SKILLS.md +1 -0
  14. package/agents/binary-reverser/SOUL.md +23 -0
  15. package/agents/binary-reverser/TOOLS.md +99 -0
  16. package/agents/browser-agent/CONFIG.yaml +20 -0
  17. package/agents/browser-agent/HEARTBEAT.md +79 -0
  18. package/agents/browser-agent/IDENTITY.md +5 -0
  19. package/agents/browser-agent/SKILLS.md +86 -0
  20. package/agents/browser-agent/SOUL.md +23 -0
  21. package/agents/browser-agent/TOOLS.md +186 -0
  22. package/agents/cloud-infiltrator/CONFIG.yaml +22 -0
  23. package/agents/cloud-infiltrator/HEARTBEAT.md +78 -0
  24. package/agents/cloud-infiltrator/IDENTITY.md +1 -0
  25. package/agents/cloud-infiltrator/SKILLS.md +1 -0
  26. package/agents/cloud-infiltrator/SOUL.md +23 -0
  27. package/agents/cloud-infiltrator/TOOLS.md +68 -0
  28. package/agents/coding-assistant/CONFIG.yaml +22 -0
  29. package/agents/coding-assistant/HEARTBEAT.md +57 -0
  30. package/agents/coding-assistant/IDENTITY.md +5 -0
  31. package/agents/coding-assistant/SKILLS.md +69 -0
  32. package/agents/coding-assistant/SOUL.md +60 -0
  33. package/agents/coding-assistant/TOOLS.md +168 -0
  34. package/agents/learning-agent/CONFIG.yaml +21 -0
  35. package/agents/learning-agent/HEARTBEAT.md +63 -0
  36. package/agents/learning-agent/IDENTITY.md +5 -0
  37. package/agents/learning-agent/SKILLS.md +86 -0
  38. package/agents/learning-agent/SOUL.md +77 -0
  39. package/agents/learning-agent/TOOLS.md +145 -0
  40. package/agents/maintainer/CONFIG.yaml +31 -0
  41. package/agents/maintainer/HEARTBEAT.md +28 -0
  42. package/agents/maintainer/IDENTITY.md +33 -0
  43. package/agents/maintainer/SKILLS.md +24 -0
  44. package/agents/maintainer/SOUL.md +61 -0
  45. package/agents/maintainer/TOOLS.md +29 -0
  46. package/agents/maintainer/lib/engine.js +279 -0
  47. package/agents/maintainer/lib/safe-fixer.js +183 -0
  48. package/agents/morning-brief/CONFIG.yaml +22 -0
  49. package/agents/morning-brief/HEARTBEAT.md +60 -0
  50. package/agents/morning-brief/IDENTITY.md +5 -0
  51. package/agents/morning-brief/SKILLS.md +56 -0
  52. package/agents/morning-brief/SOUL.md +64 -0
  53. package/agents/morning-brief/TOOLS.md +112 -0
  54. package/agents/osint-detective/CONFIG.yaml +24 -0
  55. package/agents/osint-detective/HEARTBEAT.md +66 -0
  56. package/agents/osint-detective/IDENTITY.md +1 -0
  57. package/agents/osint-detective/SKILLS.md +1 -0
  58. package/agents/osint-detective/SOUL.md +23 -0
  59. package/agents/osint-detective/TOOLS.md +81 -0
  60. package/agents/recon-scout/CONFIG.yaml +22 -0
  61. package/agents/recon-scout/HEARTBEAT.md +79 -0
  62. package/agents/recon-scout/IDENTITY.md +1 -0
  63. package/agents/recon-scout/SKILLS.md +1 -0
  64. package/agents/recon-scout/SOUL.md +23 -0
  65. package/agents/recon-scout/TOOLS.md +93 -0
  66. package/agents/report-writer/CONFIG.yaml +21 -0
  67. package/agents/report-writer/HEARTBEAT.md +63 -0
  68. package/agents/report-writer/IDENTITY.md +1 -0
  69. package/agents/report-writer/SKILLS.md +1 -0
  70. package/agents/report-writer/SOUL.md +23 -0
  71. package/agents/report-writer/TOOLS.md +69 -0
  72. package/agents/shared/README.md +13 -0
  73. package/agents/web-hacker/CONFIG.yaml +24 -0
  74. package/agents/web-hacker/HEARTBEAT.md +78 -0
  75. package/agents/web-hacker/IDENTITY.md +1 -0
  76. package/agents/web-hacker/SKILLS.md +1 -0
  77. package/agents/web-hacker/SOUL.md +23 -0
  78. package/agents/web-hacker/TOOLS.md +86 -0
  79. package/api/CLAUDE.md +19 -0
  80. package/api/index.js +274 -0
  81. package/bin/cli.js +620 -0
  82. package/bin/local.sh +31 -0
  83. package/bin/postinstall.js +63 -0
  84. package/config/index.js +24 -0
  85. package/config/instrumentation.js +93 -0
  86. package/drizzle/0000_initial.sql +52 -0
  87. package/drizzle/0001_bounty_and_registry.sql +82 -0
  88. package/drizzle/0002_sync_columns.sql +7 -0
  89. package/drizzle/0003_graceful_bloodscream.sql +86 -0
  90. package/drizzle/meta/0000_snapshot.json +321 -0
  91. package/drizzle/meta/0003_snapshot.json +878 -0
  92. package/drizzle/meta/_journal.json +34 -0
  93. package/drizzle/relations.ts +3 -0
  94. package/drizzle/schema.ts +145 -0
  95. package/lib/actions.js +47 -0
  96. package/lib/agents.js +166 -0
  97. package/lib/ai/agent.js +96 -0
  98. package/lib/ai/autonomous-engine.js +261 -0
  99. package/lib/ai/index.js +359 -0
  100. package/lib/ai/model-router.js +254 -0
  101. package/lib/ai/model.js +73 -0
  102. package/lib/ai/tools.js +84 -0
  103. package/lib/auth/actions.js +28 -0
  104. package/lib/auth/config.js +27 -0
  105. package/lib/auth/edge-config.js +27 -0
  106. package/lib/auth/index.js +27 -0
  107. package/lib/auth/middleware.js +53 -0
  108. package/lib/bounty/actions.js +119 -0
  109. package/lib/bounty/findings.js +64 -0
  110. package/lib/bounty/programs.js +34 -0
  111. package/lib/bounty/sync-targets.js +267 -0
  112. package/lib/bounty/targets.js +33 -0
  113. package/lib/channels/base.js +56 -0
  114. package/lib/channels/index.js +15 -0
  115. package/lib/channels/telegram.js +148 -0
  116. package/lib/chat/actions.js +288 -0
  117. package/lib/chat/api.js +135 -0
  118. package/lib/chat/components/app-sidebar.js +237 -0
  119. package/lib/chat/components/app-sidebar.jsx +289 -0
  120. package/lib/chat/components/chat-header.js +27 -0
  121. package/lib/chat/components/chat-header.jsx +37 -0
  122. package/lib/chat/components/chat-input.js +230 -0
  123. package/lib/chat/components/chat-input.jsx +228 -0
  124. package/lib/chat/components/chat-nav-context.js +11 -0
  125. package/lib/chat/components/chat-nav-context.jsx +11 -0
  126. package/lib/chat/components/chat-page.js +81 -0
  127. package/lib/chat/components/chat-page.jsx +100 -0
  128. package/lib/chat/components/chat.js +150 -0
  129. package/lib/chat/components/chat.jsx +182 -0
  130. package/lib/chat/components/chats-page.js +302 -0
  131. package/lib/chat/components/chats-page.jsx +330 -0
  132. package/lib/chat/components/crons-page.js +172 -0
  133. package/lib/chat/components/crons-page.jsx +244 -0
  134. package/lib/chat/components/enhanced-tool-call.js +103 -0
  135. package/lib/chat/components/enhanced-tool-call.jsx +139 -0
  136. package/lib/chat/components/findings-page.js +175 -0
  137. package/lib/chat/components/findings-page.jsx +214 -0
  138. package/lib/chat/components/greeting.js +22 -0
  139. package/lib/chat/components/greeting.jsx +26 -0
  140. package/lib/chat/components/icons.js +777 -0
  141. package/lib/chat/components/icons.jsx +741 -0
  142. package/lib/chat/components/index.js +26 -0
  143. package/lib/chat/components/mcp-page.js +260 -0
  144. package/lib/chat/components/mcp-page.jsx +355 -0
  145. package/lib/chat/components/message.js +289 -0
  146. package/lib/chat/components/message.jsx +315 -0
  147. package/lib/chat/components/messages.js +66 -0
  148. package/lib/chat/components/messages.jsx +77 -0
  149. package/lib/chat/components/notifications-page.js +56 -0
  150. package/lib/chat/components/notifications-page.jsx +87 -0
  151. package/lib/chat/components/page-layout.js +21 -0
  152. package/lib/chat/components/page-layout.jsx +28 -0
  153. package/lib/chat/components/registry-page.js +222 -0
  154. package/lib/chat/components/registry-page.jsx +255 -0
  155. package/lib/chat/components/settings-layout.js +40 -0
  156. package/lib/chat/components/settings-layout.jsx +54 -0
  157. package/lib/chat/components/settings-secrets-page.js +216 -0
  158. package/lib/chat/components/settings-secrets-page.jsx +264 -0
  159. package/lib/chat/components/sidebar-history-item.js +132 -0
  160. package/lib/chat/components/sidebar-history-item.jsx +113 -0
  161. package/lib/chat/components/sidebar-history.js +115 -0
  162. package/lib/chat/components/sidebar-history.jsx +157 -0
  163. package/lib/chat/components/sidebar-user-nav.js +63 -0
  164. package/lib/chat/components/sidebar-user-nav.jsx +73 -0
  165. package/lib/chat/components/status-bar.js +39 -0
  166. package/lib/chat/components/status-bar.jsx +51 -0
  167. package/lib/chat/components/swarm-page.js +157 -0
  168. package/lib/chat/components/swarm-page.jsx +210 -0
  169. package/lib/chat/components/targets-page.js +376 -0
  170. package/lib/chat/components/targets-page.jsx +389 -0
  171. package/lib/chat/components/tool-call.js +86 -0
  172. package/lib/chat/components/tool-call.jsx +104 -0
  173. package/lib/chat/components/tool-panel.js +107 -0
  174. package/lib/chat/components/tool-panel.jsx +145 -0
  175. package/lib/chat/components/triggers-page.js +153 -0
  176. package/lib/chat/components/triggers-page.jsx +221 -0
  177. package/lib/chat/components/ui/confirm-dialog.js +53 -0
  178. package/lib/chat/components/ui/confirm-dialog.jsx +57 -0
  179. package/lib/chat/components/ui/dropdown-menu.js +98 -0
  180. package/lib/chat/components/ui/dropdown-menu.jsx +116 -0
  181. package/lib/chat/components/ui/rename-dialog.js +74 -0
  182. package/lib/chat/components/ui/rename-dialog.jsx +72 -0
  183. package/lib/chat/components/ui/scroll-area.js +13 -0
  184. package/lib/chat/components/ui/scroll-area.jsx +17 -0
  185. package/lib/chat/components/ui/separator.js +21 -0
  186. package/lib/chat/components/ui/separator.jsx +18 -0
  187. package/lib/chat/components/ui/sheet.js +75 -0
  188. package/lib/chat/components/ui/sheet.jsx +95 -0
  189. package/lib/chat/components/ui/sidebar.js +227 -0
  190. package/lib/chat/components/ui/sidebar.jsx +245 -0
  191. package/lib/chat/components/ui/tooltip.js +56 -0
  192. package/lib/chat/components/ui/tooltip.jsx +66 -0
  193. package/lib/chat/components/upgrade-dialog.js +151 -0
  194. package/lib/chat/components/upgrade-dialog.jsx +170 -0
  195. package/lib/chat/utils.js +11 -0
  196. package/lib/cron.js +246 -0
  197. package/lib/db/api-keys.js +163 -0
  198. package/lib/db/chats.js +145 -0
  199. package/lib/db/index.js +52 -0
  200. package/lib/db/notifications.js +99 -0
  201. package/lib/db/schema.js +145 -0
  202. package/lib/db/update-check.js +96 -0
  203. package/lib/db/users.js +89 -0
  204. package/lib/mcp/actions.js +104 -0
  205. package/lib/mcp/client.js +79 -0
  206. package/lib/mcp/handler.js +57 -0
  207. package/lib/mcp/server.js +165 -0
  208. package/lib/paths.js +46 -0
  209. package/lib/registry/actions.js +164 -0
  210. package/lib/registry/catalog.js +137 -0
  211. package/lib/registry/tools.js +71 -0
  212. package/lib/tools/create-job.js +99 -0
  213. package/lib/tools/github.js +217 -0
  214. package/lib/tools/openai.js +35 -0
  215. package/lib/tools/telegram.js +292 -0
  216. package/lib/triggers.js +118 -0
  217. package/lib/utils/render-md.js +102 -0
  218. package/package.json +103 -0
  219. package/setup/lib/auth.mjs +81 -0
  220. package/setup/lib/env.mjs +21 -0
  221. package/setup/lib/fs-utils.mjs +20 -0
  222. package/setup/lib/github.mjs +149 -0
  223. package/setup/lib/prerequisites.mjs +155 -0
  224. package/setup/lib/prompts.mjs +267 -0
  225. package/setup/lib/providers.mjs +48 -0
  226. package/setup/lib/sync.mjs +125 -0
  227. package/setup/lib/targets.mjs +45 -0
  228. package/setup/lib/telegram-verify.mjs +63 -0
  229. package/setup/lib/telegram.mjs +76 -0
  230. package/setup/setup-telegram.mjs +264 -0
  231. package/setup/setup.mjs +842 -0
  232. package/templates/.dockerignore +5 -0
  233. package/templates/.env.example +63 -0
  234. package/templates/.github/workflows/auto-merge.yml +117 -0
  235. package/templates/.github/workflows/build-image.yml +36 -0
  236. package/templates/.github/workflows/notify-job-failed.yml +64 -0
  237. package/templates/.github/workflows/notify-pr-complete.yml +119 -0
  238. package/templates/.github/workflows/rebuild-event-handler.yml +121 -0
  239. package/templates/.github/workflows/run-job.yml +89 -0
  240. package/templates/.github/workflows/upgrade-event-handler.yml +62 -0
  241. package/templates/.gitignore.template +45 -0
  242. package/templates/.pi/extensions/env-sanitizer/index.ts +48 -0
  243. package/templates/.pi/extensions/env-sanitizer/package.json +5 -0
  244. package/templates/CLAUDE.md +29 -0
  245. package/templates/CLAUDE.md.template +307 -0
  246. package/templates/app/api/[...thepopebot]/route.js +1 -0
  247. package/templates/app/api/auth/[...nextauth]/route.js +1 -0
  248. package/templates/app/chat/[chatId]/page.js +8 -0
  249. package/templates/app/chats/page.js +7 -0
  250. package/templates/app/components/ascii-logo.jsx +10 -0
  251. package/templates/app/components/login-form.jsx +92 -0
  252. package/templates/app/components/setup-form.jsx +82 -0
  253. package/templates/app/components/theme-provider.jsx +11 -0
  254. package/templates/app/components/theme-toggle.jsx +38 -0
  255. package/templates/app/components/ui/button.jsx +21 -0
  256. package/templates/app/components/ui/card.jsx +23 -0
  257. package/templates/app/components/ui/input.jsx +10 -0
  258. package/templates/app/components/ui/label.jsx +10 -0
  259. package/templates/app/crons/page.js +5 -0
  260. package/templates/app/findings/page.js +7 -0
  261. package/templates/app/globals.css +90 -0
  262. package/templates/app/layout.js +19 -0
  263. package/templates/app/login/page.js +15 -0
  264. package/templates/app/notifications/page.js +7 -0
  265. package/templates/app/page.js +7 -0
  266. package/templates/app/settings/crons/page.js +5 -0
  267. package/templates/app/settings/layout.js +7 -0
  268. package/templates/app/settings/mcp/page.js +5 -0
  269. package/templates/app/settings/page.js +5 -0
  270. package/templates/app/settings/secrets/page.js +5 -0
  271. package/templates/app/settings/triggers/page.js +5 -0
  272. package/templates/app/stream/chat/route.js +1 -0
  273. package/templates/app/swarm/page.js +7 -0
  274. package/templates/app/targets/page.js +7 -0
  275. package/templates/app/toolbox/page.js +7 -0
  276. package/templates/app/triggers/page.js +5 -0
  277. package/templates/config/AGENT.md +34 -0
  278. package/templates/config/CRONS.json +56 -0
  279. package/templates/config/EVENT_HANDLER.md +224 -0
  280. package/templates/config/HEARTBEAT.md +3 -0
  281. package/templates/config/JOB_SUMMARY.md +130 -0
  282. package/templates/config/MCP_SERVERS.json +1 -0
  283. package/templates/config/SKILL_BUILDING_GUIDE.md +90 -0
  284. package/templates/config/SOUL.md +17 -0
  285. package/templates/config/TRIGGERS.json +58 -0
  286. package/templates/docker/event-handler/Dockerfile +20 -0
  287. package/templates/docker/event-handler/ecosystem.config.cjs +8 -0
  288. package/templates/docker/job-claude-code/Dockerfile +34 -0
  289. package/templates/docker/job-claude-code/entrypoint.sh +139 -0
  290. package/templates/docker/job-pi-coding-agent/Dockerfile +44 -0
  291. package/templates/docker/job-pi-coding-agent/entrypoint.sh +163 -0
  292. package/templates/docker-compose.yml +63 -0
  293. package/templates/instrumentation.js +6 -0
  294. package/templates/middleware.js +1 -0
  295. package/templates/next.config.mjs +3 -0
  296. package/templates/postcss.config.mjs +5 -0
  297. package/templates/skills/LICENSE +21 -0
  298. package/templates/skills/README.md +119 -0
  299. package/templates/skills/brave-search/SKILL.md +79 -0
  300. package/templates/skills/brave-search/content.js +86 -0
  301. package/templates/skills/brave-search/package-lock.json +621 -0
  302. package/templates/skills/brave-search/package.json +14 -0
  303. package/templates/skills/brave-search/search.js +199 -0
  304. package/templates/skills/browser-tools/SKILL.md +196 -0
  305. package/templates/skills/browser-tools/browser-content.js +103 -0
  306. package/templates/skills/browser-tools/browser-cookies.js +35 -0
  307. package/templates/skills/browser-tools/browser-eval.js +53 -0
  308. package/templates/skills/browser-tools/browser-hn-scraper.js +108 -0
  309. package/templates/skills/browser-tools/browser-nav.js +44 -0
  310. package/templates/skills/browser-tools/browser-pick.js +162 -0
  311. package/templates/skills/browser-tools/browser-screenshot.js +34 -0
  312. package/templates/skills/browser-tools/browser-start.js +87 -0
  313. package/templates/skills/browser-tools/package-lock.json +2556 -0
  314. package/templates/skills/browser-tools/package.json +19 -0
  315. package/templates/skills/llm-secrets/SKILL.md +34 -0
  316. package/templates/skills/llm-secrets/llm-secrets.js +33 -0
  317. 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,5 @@
1
+ {
2
+ "name": "env-sanitizer",
3
+ "private": true,
4
+ "type": "module"
5
+ }
@@ -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,7 @@
1
+ import { auth } from 'thepopebot/auth';
2
+ import { ChatsPage } from 'thepopebot/chat';
3
+
4
+ export default async function ChatsRoute() {
5
+ const session = await auth();
6
+ return <ChatsPage session={session} />;
7
+ }
@@ -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
+ }