@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,5 @@
1
+ node_modules
2
+ .env
3
+ data/
4
+ logs/
5
+ .git
@@ -0,0 +1,63 @@
1
+ # thepopebot Configuration
2
+ # Copy this file to .env and fill in your values
3
+ # NEVER commit the actual .env file with real secrets!
4
+
5
+ # Auth.js secret for session encryption (auto-generated by setup wizard)
6
+ # To generate manually: openssl rand -base64 32
7
+ AUTH_SECRET=
8
+
9
+ # Trust the host header when behind a reverse proxy (Traefik, ngrok, etc.)
10
+ AUTH_TRUST_HOST=true
11
+
12
+ # Public URL for webhooks, Telegram, and Traefik hostname
13
+ APP_URL=
14
+
15
+ # Hostname extracted from APP_URL (e.g., mybot.example.com) — used by docker-compose/Traefik
16
+ # Auto-set by setup wizard
17
+ APP_HOSTNAME=
18
+
19
+ # GitHub Personal Access Token (needs repo, workflow scopes)
20
+ GH_TOKEN=ghp_your_token_here
21
+
22
+ # Repository info
23
+ GH_OWNER=your_github_username
24
+ GH_REPO=your_repo_name
25
+
26
+ # Telegram bot token from @BotFather (optional)
27
+ TELEGRAM_BOT_TOKEN=
28
+
29
+ # Secret for validating Telegram webhooks (generate with: openssl rand -hex 32)
30
+ TELEGRAM_WEBHOOK_SECRET=
31
+
32
+ # Verification code for getting your chat ID (e.g., verify-abc12345)
33
+ TELEGRAM_VERIFICATION=
34
+
35
+ # Secret for GitHub Actions webhook auth (must match GH_WEBHOOK_SECRET secret)
36
+ GH_WEBHOOK_SECRET=
37
+
38
+ # Anthropic API key for Claude chat features
39
+ ANTHROPIC_API_KEY=sk-ant-your_key_here
40
+
41
+ # OpenAI API key for Whisper voice transcription (optional)
42
+ OPENAI_API_KEY=
43
+
44
+ # LLM provider: anthropic, openai, or google (default: anthropic)
45
+ # LLM_PROVIDER=anthropic
46
+
47
+ # LLM model name override (optional, provider-specific default if unset)
48
+ # LLM_MODEL=
49
+
50
+ # Default Telegram chat ID for scheduled notifications
51
+ # Get this by messaging your bot and checking the webhook logs, or use @userinfobot
52
+ TELEGRAM_CHAT_ID=
53
+
54
+ # Let's Encrypt email for automatic SSL (docker-compose only, optional)
55
+ # If not set, Traefik uses a self-signed certificate
56
+ # LETSENCRYPT_EMAIL=
57
+
58
+ # thepopebot version — auto-set by postinstall, used by docker-compose for image tags
59
+ # THEPOPEBOT_VERSION=
60
+
61
+ # Custom Docker images (optional, overrides the default stephengpope/thepopebot images)
62
+ # EVENT_HANDLER_IMAGE_URL=
63
+ # JOB_IMAGE_URL=
@@ -0,0 +1,117 @@
1
+ name: Auto-Merge Job PR
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened]
6
+ branches: [main]
7
+
8
+ jobs:
9
+ auto-merge:
10
+ runs-on: ${{ vars.RUNS_ON || 'ubuntu-latest' }}
11
+ if: startsWith(github.event.pull_request.head.ref, 'job/')
12
+ permissions:
13
+ contents: write
14
+ pull-requests: write
15
+
16
+ steps:
17
+ - name: Wait for merge check
18
+ id: merge-check
19
+ env:
20
+ GH_TOKEN: ${{ github.token }}
21
+ run: |
22
+ PR_NUMBER="${{ github.event.pull_request.number }}"
23
+ for i in $(seq 1 30); do
24
+ sleep 10
25
+ MERGEABLE=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json mergeable -q '.mergeable')
26
+ if [ "$MERGEABLE" != "" ] && [ "$MERGEABLE" != "UNKNOWN" ]; then
27
+ echo "mergeable=$MERGEABLE" >> "$GITHUB_OUTPUT"
28
+ echo "Mergeable: $MERGEABLE"
29
+ exit 0
30
+ fi
31
+ echo "Still computing... (attempt $i/30)"
32
+ done
33
+ echo "mergeable=UNKNOWN" >> "$GITHUB_OUTPUT"
34
+ echo "Timed out waiting for merge check"
35
+
36
+ - name: Check AUTO_MERGE setting
37
+ if: steps.merge-check.outputs.mergeable == 'MERGEABLE'
38
+ id: check-setting
39
+ run: |
40
+ AUTO_MERGE="${{ vars.AUTO_MERGE }}"
41
+ if [ "$AUTO_MERGE" = "false" ]; then
42
+ echo "Auto-merge disabled via AUTO_MERGE=false"
43
+ echo "enabled=false" >> "$GITHUB_OUTPUT"
44
+ else
45
+ echo "Auto-merge enabled"
46
+ echo "enabled=true" >> "$GITHUB_OUTPUT"
47
+ fi
48
+
49
+ - name: Check ALLOWED_PATHS
50
+ if: steps.merge-check.outputs.mergeable == 'MERGEABLE' && steps.check-setting.outputs.enabled == 'true'
51
+ id: check-paths
52
+ env:
53
+ GH_TOKEN: ${{ github.token }}
54
+ run: |
55
+ ALLOWED_PATHS="${{ vars.ALLOWED_PATHS }}"
56
+ PR_NUMBER="${{ github.event.pull_request.number }}"
57
+
58
+ # Default: only logs allowed
59
+ if [ -z "$ALLOWED_PATHS" ]; then
60
+ ALLOWED_PATHS="/logs"
61
+ fi
62
+
63
+ # Normalize: ensure each prefix starts with /
64
+ IFS=',' read -ra RAW_PREFIXES <<< "$ALLOWED_PATHS"
65
+ PREFIXES=()
66
+ for p in "${RAW_PREFIXES[@]}"; do
67
+ trimmed=$(echo "$p" | xargs)
68
+ [ -z "$trimmed" ] && continue
69
+ # Ensure leading /
70
+ [[ "$trimmed" != /* ]] && trimmed="/$trimmed"
71
+ PREFIXES+=("$trimmed")
72
+ done
73
+
74
+ # "/" means everything allowed
75
+ for p in "${PREFIXES[@]}"; do
76
+ if [ "$p" = "/" ]; then
77
+ echo "All paths allowed (ALLOWED_PATHS contains /)"
78
+ echo "allowed=true" >> "$GITHUB_OUTPUT"
79
+ exit 0
80
+ fi
81
+ done
82
+
83
+ # Get changed files
84
+ CHANGED_FILES=$(gh pr diff "$PR_NUMBER" --name-only --repo "${{ github.repository }}")
85
+ if [ -z "$CHANGED_FILES" ]; then
86
+ echo "No changed files"
87
+ echo "allowed=true" >> "$GITHUB_OUTPUT"
88
+ exit 0
89
+ fi
90
+
91
+ # Check each file against prefixes
92
+ # Strip leading / from prefixes for comparison (git paths are relative)
93
+ ALL_OK=true
94
+ while IFS= read -r file; do
95
+ [ -z "$file" ] && continue
96
+ FILE_OK=false
97
+ for prefix in "${PREFIXES[@]}"; do
98
+ compare="${prefix#/}"
99
+ if [[ "$file" == "$compare"* ]]; then
100
+ FILE_OK=true
101
+ break
102
+ fi
103
+ done
104
+ if [ "$FILE_OK" = "false" ]; then
105
+ echo "BLOCKED: $file"
106
+ ALL_OK=false
107
+ fi
108
+ done <<< "$CHANGED_FILES"
109
+
110
+ echo "allowed=$ALL_OK" >> "$GITHUB_OUTPUT"
111
+
112
+ - name: Merge PR
113
+ if: steps.merge-check.outputs.mergeable == 'MERGEABLE' && steps.check-setting.outputs.enabled == 'true' && steps.check-paths.outputs.allowed == 'true'
114
+ env:
115
+ GH_TOKEN: ${{ github.token }}
116
+ run: |
117
+ gh pr merge ${{ github.event.pull_request.number }} --squash --delete-branch --repo "${{ github.repository }}"
@@ -0,0 +1,36 @@
1
+ name: Build and Push Docker Image
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths: ['docker/job-pi-coding-agent/**']
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ${{ vars.RUNS_ON || 'ubuntu-latest' }}
12
+ if: vars.JOB_IMAGE_URL != '' && startsWith(vars.JOB_IMAGE_URL, 'ghcr.io/')
13
+ permissions:
14
+ contents: read
15
+ packages: write
16
+
17
+ steps:
18
+ - name: Checkout repository
19
+ uses: actions/checkout@v4
20
+
21
+ - name: Set up Docker Buildx
22
+ uses: docker/setup-buildx-action@v3
23
+
24
+ - name: Login to GHCR
25
+ uses: docker/login-action@v3
26
+ with:
27
+ registry: ghcr.io
28
+ username: ${{ github.actor }}
29
+ password: ${{ secrets.GITHUB_TOKEN }}
30
+
31
+ - name: Build and push
32
+ uses: docker/build-push-action@v6
33
+ with:
34
+ context: ./docker/job-pi-coding-agent
35
+ push: true
36
+ tags: ${{ vars.JOB_IMAGE_URL }}:latest
@@ -0,0 +1,64 @@
1
+ name: Notify Job Failed
2
+
3
+ on:
4
+ workflow_run:
5
+ workflows: ["Agent Job"]
6
+ types: [completed]
7
+
8
+ jobs:
9
+ notify-failure:
10
+ runs-on: ${{ vars.RUNS_ON || 'ubuntu-latest' }}
11
+ if: >
12
+ github.event.workflow_run.conclusion != 'success' &&
13
+ startsWith(github.event.workflow_run.head_branch, 'job/')
14
+ permissions:
15
+ contents: read
16
+
17
+ steps:
18
+ - name: Checkout job branch
19
+ uses: actions/checkout@v4
20
+ with:
21
+ ref: ${{ github.event.workflow_run.head_branch }}
22
+
23
+ - name: Send failure notification
24
+ env:
25
+ GH_TOKEN: ${{ github.token }}
26
+ run: |
27
+ BRANCH="${{ github.event.workflow_run.head_branch }}"
28
+ JOB_ID="${BRANCH#job/}"
29
+ RUN_ID="${{ github.event.workflow_run.id }}"
30
+ RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${RUN_ID}"
31
+
32
+ # Read job description from job.config.json (exists on branch from job creation)
33
+ JOB_CONTENT=""
34
+ if [ -f "logs/${JOB_ID}/job.config.json" ]; then
35
+ JOB_CONTENT=$(jq -r '.job // empty' "logs/${JOB_ID}/job.config.json")
36
+ fi
37
+
38
+ COMMIT_SHA=$(git rev-parse HEAD)
39
+ STATUS="${{ github.event.workflow_run.conclusion }}"
40
+
41
+ jq -n \
42
+ --arg job_id "$JOB_ID" \
43
+ --arg branch "$BRANCH" \
44
+ --arg status "$STATUS" \
45
+ --arg job "$JOB_CONTENT" \
46
+ --arg run_url "$RUN_URL" \
47
+ --arg commit_sha "$COMMIT_SHA" \
48
+ '{
49
+ job_id: $job_id,
50
+ branch: $branch,
51
+ status: $status,
52
+ job: $job,
53
+ run_url: $run_url,
54
+ pr_url: "",
55
+ changed_files: [],
56
+ commit_message: "",
57
+ commit_sha: $commit_sha,
58
+ merge_result: ""
59
+ }' > /tmp/payload.json
60
+
61
+ curl -s -X POST "${{ vars.APP_URL }}/api/github/webhook" \
62
+ -H "Content-Type: application/json" \
63
+ -H "X-GitHub-Webhook-Secret-Token: ${{ secrets.GH_WEBHOOK_SECRET }}" \
64
+ -d @/tmp/payload.json
@@ -0,0 +1,119 @@
1
+ name: Notify Event Handler
2
+
3
+ on:
4
+ workflow_run:
5
+ workflows: ["Auto-Merge Job PR"]
6
+ types: [completed]
7
+
8
+ jobs:
9
+ notify:
10
+ runs-on: ${{ vars.RUNS_ON || 'ubuntu-latest' }}
11
+ if: startsWith(github.event.workflow_run.head_branch, 'job/')
12
+ permissions:
13
+ contents: read
14
+ pull-requests: read
15
+
16
+ steps:
17
+ - name: Get PR number
18
+ id: pr
19
+ env:
20
+ GH_TOKEN: ${{ github.token }}
21
+ run: |
22
+ BRANCH="${{ github.event.workflow_run.head_branch }}"
23
+
24
+ # Try workflow_run.pull_requests first
25
+ PR_NUMBER=$(echo '${{ toJson(github.event.workflow_run.pull_requests) }}' | jq -r '.[0].number // empty')
26
+
27
+ # Fallback: look up PR by head branch
28
+ if [ -z "$PR_NUMBER" ]; then
29
+ PR_NUMBER=$(gh pr list --head "$BRANCH" --state all --repo "${{ github.repository }}" --json number -q '.[0].number')
30
+ fi
31
+
32
+ if [ -z "$PR_NUMBER" ]; then
33
+ echo "No PR found for branch $BRANCH"
34
+ exit 1
35
+ fi
36
+
37
+ PR_URL=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json url -q '.url')
38
+
39
+ echo "number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
40
+ echo "url=$PR_URL" >> "$GITHUB_OUTPUT"
41
+ echo "Found PR #$PR_NUMBER"
42
+
43
+ - name: Checkout PR branch
44
+ uses: actions/checkout@v4
45
+ with:
46
+ ref: ${{ github.event.workflow_run.head_sha }}
47
+
48
+ - name: Gather job results and notify
49
+ env:
50
+ GH_TOKEN: ${{ github.token }}
51
+ run: |
52
+ PR_NUMBER="${{ steps.pr.outputs.number }}"
53
+ BRANCH="${{ github.event.workflow_run.head_branch }}"
54
+ JOB_ID="${BRANCH#job/}"
55
+ RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}"
56
+
57
+ # Check actual PR merge state
58
+ PR_MERGED=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json mergedAt -q '.mergedAt')
59
+ if [ -n "$PR_MERGED" ] && [ "$PR_MERGED" != "null" ]; then
60
+ MERGE_RESULT="merged"
61
+ else
62
+ MERGE_RESULT="not_merged"
63
+ fi
64
+
65
+ # 1. Read job description from job.config.json (on disk after checkout)
66
+ JOB_CONTENT=""
67
+ if [ -f "logs/${JOB_ID}/job.config.json" ]; then
68
+ JOB_CONTENT=$(jq -r '.job // empty' "logs/${JOB_ID}/job.config.json")
69
+ fi
70
+
71
+ # 2. Get last commit message via gh CLI
72
+ COMMIT_MSG=$(gh pr view "$PR_NUMBER" --json commits --jq '.commits[-1].messageHeadline' --repo "${{ github.repository }}" 2>/dev/null || echo "")
73
+
74
+ # 3. Get changed files (names only)
75
+ CHANGED_FILES=$(gh pr diff "$PR_NUMBER" --name-only --repo "${{ github.repository }}" 2>/dev/null || echo "")
76
+
77
+ # 4. Get PR status
78
+ PR_STATUS=$(gh pr view "$PR_NUMBER" --json state --jq '.state' --repo "${{ github.repository }}" 2>/dev/null || echo "open")
79
+
80
+ # 5. Commit SHA for server-side log fetching
81
+ COMMIT_SHA="${{ github.event.workflow_run.head_sha }}"
82
+
83
+ # Determine status based on merge result and PR state
84
+ if [ "$MERGE_RESULT" = "merged" ]; then
85
+ STATUS="completed"
86
+ else
87
+ STATUS="$PR_STATUS"
88
+ fi
89
+
90
+ # 6. Build flat JSON payload
91
+ jq -n \
92
+ --arg job_id "$JOB_ID" \
93
+ --arg branch "$BRANCH" \
94
+ --arg status "$STATUS" \
95
+ --arg job "$JOB_CONTENT" \
96
+ --arg run_url "$RUN_URL" \
97
+ --arg pr_url "${{ steps.pr.outputs.url }}" \
98
+ --arg commit_message "$COMMIT_MSG" \
99
+ --arg changed_files "$CHANGED_FILES" \
100
+ --arg commit_sha "$COMMIT_SHA" \
101
+ --arg merge_result "$MERGE_RESULT" \
102
+ '{
103
+ job_id: $job_id,
104
+ branch: $branch,
105
+ status: $status,
106
+ job: $job,
107
+ run_url: $run_url,
108
+ pr_url: $pr_url,
109
+ changed_files: ($changed_files | split("\n") | map(select(length > 0))),
110
+ commit_message: $commit_message,
111
+ commit_sha: $commit_sha,
112
+ merge_result: $merge_result
113
+ }' > /tmp/payload.json
114
+
115
+ # 7. Send to event handler
116
+ curl -s -X POST "${{ vars.APP_URL }}/api/github/webhook" \
117
+ -H "Content-Type: application/json" \
118
+ -H "X-GitHub-Webhook-Secret-Token: ${{ secrets.GH_WEBHOOK_SECRET }}" \
119
+ -d @/tmp/payload.json
@@ -0,0 +1,121 @@
1
+ name: Rebuild Event Handler
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ concurrency:
8
+ group: event-handler-deploy
9
+ cancel-in-progress: false
10
+
11
+ jobs:
12
+ deploy:
13
+ runs-on: self-hosted
14
+ timeout-minutes: 20
15
+ steps:
16
+ - name: Pull latest and detect version change
17
+ id: pull
18
+ run: |
19
+ docker exec thepopebot-event-handler bash -c '
20
+ export GH_TOKEN=$(grep "^GH_TOKEN=" /app/.env | cut -d= -f2-)
21
+ echo "${GH_TOKEN}" | gh auth login --with-token
22
+ gh auth setup-git
23
+
24
+ git fetch origin main
25
+ CHANGED=$(git diff --name-only HEAD origin/main)
26
+ if ! echo "$CHANGED" | grep -qv "^logs/"; then
27
+ echo "SKIP" > /app/.rebuild-status
28
+ git reset --hard origin/main
29
+ exit 0
30
+ fi
31
+
32
+ # Detect thepopebot version change from package-lock.json in git
33
+ git show HEAD:package-lock.json > /tmp/old-lock.json 2>/dev/null || echo "{}" > /tmp/old-lock.json
34
+ git show origin/main:package-lock.json > /tmp/new-lock.json
35
+ OLD_TPB=$(node -p "try{require(\"/tmp/old-lock.json\").packages[\"node_modules/thepopebot\"]?.version||\"\"}catch(e){\"\"}")
36
+ NEW_TPB=$(node -p "try{require(\"/tmp/new-lock.json\").packages[\"node_modules/thepopebot\"]?.version||\"\"}catch(e){\"\"}")
37
+ rm -f /tmp/old-lock.json /tmp/new-lock.json
38
+
39
+ git reset --hard origin/main
40
+
41
+ if [ -n "$OLD_TPB" ] && [ "$OLD_TPB" != "$NEW_TPB" ]; then
42
+ # Version changed — run thepopebot init to scaffold new templates
43
+ if echo "$NEW_TPB" | grep -q "-"; then
44
+ npx --yes "thepopebot@${NEW_TPB}" init
45
+ else
46
+ npx --yes thepopebot@latest init
47
+ fi
48
+
49
+ # Commit any template changes from init
50
+ git add -A
51
+ if ! git diff --cached --quiet; then
52
+ git commit -m "chore: apply thepopebot init after upgrade"
53
+ git push origin main
54
+ fi
55
+
56
+ # Update THEPOPEBOT_VERSION in .env so docker compose pulls the right image
57
+ if grep -q "^THEPOPEBOT_VERSION=" /app/.env; then
58
+ sed -i "s/^THEPOPEBOT_VERSION=.*/THEPOPEBOT_VERSION=$NEW_TPB/" /app/.env
59
+ else
60
+ echo "THEPOPEBOT_VERSION=$NEW_TPB" >> /app/.env
61
+ fi
62
+ echo "VERSION_CHANGED" > /app/.rebuild-status
63
+ else
64
+ npm install --omit=dev
65
+ echo "REBUILD" > /app/.rebuild-status
66
+ fi
67
+ '
68
+
69
+ STATUS=$(docker exec thepopebot-event-handler cat /app/.rebuild-status)
70
+ docker exec thepopebot-event-handler rm -f /app/.rebuild-status
71
+ echo "status=$STATUS" >> $GITHUB_OUTPUT
72
+
73
+ - name: Rebuild (no version change)
74
+ if: steps.pull.outputs.status == 'REBUILD'
75
+ run: |
76
+ docker exec thepopebot-event-handler bash -c '
77
+ rm -rf .next-new .next-old
78
+ NEXT_BUILD_DIR=.next-new npm run build
79
+
80
+ mv .next .next-old 2>/dev/null || true
81
+ mv .next-new .next
82
+
83
+ echo "Rebuild complete, reloading Next.js..."
84
+ npx pm2 reload all
85
+
86
+ rm -rf .next-old
87
+ '
88
+
89
+ - name: Pull new image and restart container
90
+ if: steps.pull.outputs.status == 'VERSION_CHANGED'
91
+ run: |
92
+ HOST_DIR=$(docker inspect thepopebot-event-handler --format '{{index .Config.Labels "com.docker.compose.project.working_dir"}}')
93
+ docker compose -f /project/docker-compose.yml --project-directory "$HOST_DIR" --env-file /project/.env pull event-handler
94
+ docker stop thepopebot-event-handler || true
95
+ docker rm thepopebot-event-handler || true
96
+ docker compose -f /project/docker-compose.yml --project-directory "$HOST_DIR" --env-file /project/.env up -d event-handler
97
+
98
+ - name: Rebuild in new container
99
+ if: steps.pull.outputs.status == 'VERSION_CHANGED'
100
+ run: |
101
+ echo "Waiting for new container..."
102
+ for i in $(seq 1 30); do
103
+ if docker exec thepopebot-event-handler echo "ready" 2>/dev/null; then
104
+ break
105
+ fi
106
+ sleep 2
107
+ done
108
+
109
+ docker exec thepopebot-event-handler bash -c '
110
+ npm install --omit=dev
111
+
112
+ rm -rf .next-new .next-old
113
+ NEXT_BUILD_DIR=.next-new npm run build
114
+
115
+ mv .next .next-old 2>/dev/null || true
116
+ mv .next-new .next
117
+
118
+ npx pm2 restart all
119
+
120
+ rm -rf .next-old
121
+ '
@@ -0,0 +1,89 @@
1
+ name: Agent Job
2
+
3
+ on:
4
+ create:
5
+
6
+ jobs:
7
+ run-agent:
8
+ runs-on: ${{ vars.RUNS_ON || 'ubuntu-latest' }}
9
+ if: github.ref_type == 'branch' && startsWith(github.ref_name, 'job/')
10
+ permissions:
11
+ contents: write
12
+ packages: read
13
+
14
+ steps:
15
+ - name: Checkout
16
+ uses: actions/checkout@v4
17
+ with:
18
+ ref: ${{ github.ref_name }}
19
+ sparse-checkout: |
20
+ package-lock.json
21
+ logs/*/job.config.json
22
+ sparse-checkout-cone-mode: false
23
+
24
+ - name: Get thepopebot version
25
+ id: version
26
+ run: |
27
+ VERSION=$(jq -r '.packages["node_modules/thepopebot"].version // "latest"' package-lock.json)
28
+ echo "tag=$VERSION" >> $GITHUB_OUTPUT
29
+
30
+ - name: Read job config overrides
31
+ id: job-config
32
+ run: |
33
+ JOB_ID="${{ github.ref_name }}"
34
+ JOB_ID="${JOB_ID#job/}"
35
+ CONFIG_FILE="logs/${JOB_ID}/job.config.json"
36
+ if [ -f "$CONFIG_FILE" ]; then
37
+ echo "llm_provider=$(jq -r '.llm_provider // empty' "$CONFIG_FILE")" >> $GITHUB_OUTPUT
38
+ echo "llm_model=$(jq -r '.llm_model // empty' "$CONFIG_FILE")" >> $GITHUB_OUTPUT
39
+ echo "agent_backend=$(jq -r '.agent_backend // empty' "$CONFIG_FILE")" >> $GITHUB_OUTPUT
40
+ fi
41
+
42
+ - name: Login to GHCR
43
+ if: startsWith(vars.JOB_IMAGE_URL, 'ghcr.io/')
44
+ uses: docker/login-action@v3
45
+ with:
46
+ registry: ghcr.io
47
+ username: ${{ github.actor }}
48
+ password: ${{ secrets.GITHUB_TOKEN }}
49
+
50
+ - name: Run thepopebot Agent
51
+ env:
52
+ ALL_SECRETS: ${{ toJson(secrets) }}
53
+ JOB_IMAGE_URL: ${{ vars.JOB_IMAGE_URL }}
54
+ THEPOPEBOT_VERSION: ${{ steps.version.outputs.tag }}
55
+ LLM_MODEL: ${{ steps.job-config.outputs.llm_model || vars.LLM_MODEL }}
56
+ LLM_PROVIDER: ${{ steps.job-config.outputs.llm_provider || vars.LLM_PROVIDER }}
57
+ OPENAI_BASE_URL: ${{ vars.OPENAI_BASE_URL }}
58
+ AGENT_BACKEND: ${{ steps.job-config.outputs.agent_backend || vars.AGENT_BACKEND || '' }}
59
+ run: |
60
+ AGENT_BACKEND="${AGENT_BACKEND:-pi}"
61
+ if [ -n "$JOB_IMAGE_URL" ]; then
62
+ IMAGE="${JOB_IMAGE_URL}:latest"
63
+ elif [ "$AGENT_BACKEND" = "claude-code" ]; then
64
+ IMAGE="stephengpope/thepopebot:job-claude-code-${THEPOPEBOT_VERSION}"
65
+ else
66
+ IMAGE="stephengpope/thepopebot:job-${THEPOPEBOT_VERSION}"
67
+ fi
68
+ echo "Using image: $IMAGE"
69
+
70
+ # Build SECRETS JSON from AGENT_* (excluding AGENT_LLM_*) secrets
71
+ export SECRETS=$(echo "$ALL_SECRETS" | jq -c '
72
+ [to_entries[] | select(.key | startswith("AGENT_")) | select(.key | startswith("AGENT_LLM_") | not)]
73
+ | map({key: (.key | sub("^AGENT_"; "")), value: .value}) | from_entries')
74
+
75
+ # Build LLM_SECRETS JSON from AGENT_LLM_* secrets
76
+ export LLM_SECRETS=$(echo "$ALL_SECRETS" | jq -c '
77
+ [to_entries[] | select(.key | startswith("AGENT_LLM_"))]
78
+ | map({key: (.key | sub("^AGENT_LLM_"; "")), value: .value}) | from_entries')
79
+
80
+ docker run --rm \
81
+ -e REPO_URL="${{ github.server_url }}/${{ github.repository }}.git" \
82
+ -e BRANCH="${{ github.ref_name }}" \
83
+ -e SECRETS \
84
+ -e LLM_SECRETS \
85
+ -e LLM_MODEL="${LLM_MODEL}" \
86
+ -e LLM_PROVIDER="${LLM_PROVIDER}" \
87
+ -e OPENAI_BASE_URL="${OPENAI_BASE_URL}" \
88
+ -e AGENT_BACKEND="${AGENT_BACKEND}" \
89
+ "$IMAGE"
@@ -0,0 +1,62 @@
1
+ name: Upgrade Event Handler
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ target_version:
7
+ description: 'Target version to install (empty for latest stable)'
8
+ required: false
9
+ default: ''
10
+
11
+ concurrency:
12
+ group: event-handler-deploy
13
+ cancel-in-progress: false
14
+
15
+ jobs:
16
+ upgrade:
17
+ runs-on: self-hosted
18
+ timeout-minutes: 20
19
+ steps:
20
+ - name: Upgrade thepopebot
21
+ run: |
22
+ docker exec thepopebot-event-handler bash -c '
23
+ export GH_TOKEN=$(grep "^GH_TOKEN=" /app/.env | cut -d= -f2-)
24
+ echo "${GH_TOKEN}" | gh auth login --with-token
25
+ gh auth setup-git
26
+
27
+ REPO_URL=$(git -C /app remote get-url origin)
28
+ WORK_DIR=$(mktemp -d)
29
+ git clone --depth 1 "$REPO_URL" "$WORK_DIR"
30
+ cd "$WORK_DIR"
31
+
32
+ git config user.name "thepopebot"
33
+ git config user.email "thepopebot@users.noreply.github.com"
34
+
35
+ npm install --omit=dev
36
+ OLD_VERSION=$(node -p "require(\"./node_modules/thepopebot/package.json\").version")
37
+
38
+ TARGET="${{ github.event.inputs.target_version }}"
39
+ if [ -n "$TARGET" ] && echo "$TARGET" | grep -q "-"; then
40
+ # Beta: npm update cannot upgrade an exact-pinned dependency, must use install
41
+ npm install "thepopebot@${TARGET}" --prefer-online
42
+ else
43
+ # Stable: npm update resolves to latest within semver range
44
+ npm update thepopebot --prefer-online
45
+ fi
46
+
47
+ NEW_VERSION=$(node -p "require(\"./node_modules/thepopebot/package.json\").version")
48
+
49
+ if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
50
+ BRANCH="upgrade/thepopebot-${NEW_VERSION}-$(date +%s)"
51
+ git add -A
52
+ git checkout -b "$BRANCH"
53
+ git commit -m "chore: upgrade thepopebot to $NEW_VERSION"
54
+ git push origin "$BRANCH"
55
+ gh pr create --title "chore: upgrade thepopebot" --body "Automated upgrade via upgrade-event-handler workflow." --base main --head "$BRANCH"
56
+ gh pr merge "$BRANCH" --squash --auto --delete-branch
57
+ else
58
+ echo "No changes — thepopebot is already up to date."
59
+ fi
60
+
61
+ rm -rf "$WORK_DIR"
62
+ '