@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,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
|
+
'
|