thepopebot 1.2.20 → 1.2.21

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/api/CLAUDE.md ADDED
@@ -0,0 +1,19 @@
1
+ # /api — External API Routes
2
+
3
+ This directory contains the route handlers for all `/api/*` endpoints. These routes are for **external callers only** — GitHub Actions, Telegram, cURL, third-party webhooks.
4
+
5
+ ## Auth
6
+
7
+ All routes (except `/telegram/webhook` and `/github/webhook`, which use their own webhook secrets) require a valid API key passed via the `x-api-key` header. API keys are stored in the SQLite database and managed through the admin UI — they are NOT environment variables.
8
+
9
+ Auth flow: `x-api-key` header -> `verifyApiKey()` -> database lookup (hashed, timing-safe comparison).
10
+
11
+ ## Do NOT use these routes for browser UI
12
+
13
+ Browser-facing features must use **Server Actions** (`'use server'` functions) with `requireAuth()` session checks — never `/api` fetch calls. The only exception is chat streaming, which has its own dedicated route at `/stream/chat` with session auth.
14
+
15
+ | Caller | Mechanism | Auth |
16
+ |--------|-----------|------|
17
+ | External (cURL, GitHub Actions, Telegram) | `/api` route | `x-api-key` header |
18
+ | Browser UI (data/mutations) | Server Action | `requireAuth()` session |
19
+ | Browser UI (chat streaming) | `/stream/chat` | `auth()` session |
package/api/index.js CHANGED
@@ -30,7 +30,7 @@ function getFireTriggers() {
30
30
  }
31
31
 
32
32
  // Routes that have their own authentication
33
- const PUBLIC_ROUTES = ['/telegram/webhook', '/github/webhook'];
33
+ const PUBLIC_ROUTES = ['/telegram/webhook', '/github/webhook', '/ping'];
34
34
 
35
35
  /**
36
36
  * Timing-safe string comparison.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thepopebot",
3
- "version": "1.2.20",
3
+ "version": "1.2.21",
4
4
  "type": "module",
5
5
  "description": "Create autonomous AI agents with a two-layer architecture: Next.js Event Handler + Docker Agent.",
6
6
  "bin": {
package/setup/setup.mjs CHANGED
@@ -19,8 +19,6 @@ import {
19
19
  promptForOptionalKey,
20
20
  promptForCustomProvider,
21
21
  promptForBraveKey,
22
- promptForTelegramToken,
23
- generateTelegramWebhookSecret,
24
22
  confirm,
25
23
  pressEnter,
26
24
  maskSecret,
@@ -41,8 +39,6 @@ import {
41
39
  encodeLlmSecretsBase64,
42
40
  updateEnvVariable,
43
41
  } from './lib/auth.mjs';
44
- import { setTelegramWebhook, validateBotToken, generateVerificationCode } from './lib/telegram.mjs';
45
- import { runVerificationFlow, verifyRestart } from './lib/telegram-verify.mjs';
46
42
 
47
43
  const logo = `
48
44
  _____ _ ____ ____ _
@@ -81,7 +77,7 @@ function printInfo(message) {
81
77
  async function main() {
82
78
  printHeader();
83
79
 
84
- const TOTAL_STEPS = 8;
80
+ const TOTAL_STEPS = 7;
85
81
  let currentStep = 0;
86
82
 
87
83
  // Collected values
@@ -90,8 +86,6 @@ async function main() {
90
86
  let agentModel = null;
91
87
  const collectedKeys = {};
92
88
  let braveKey = null;
93
- let telegramToken = null;
94
- let telegramWebhookSecret = null;
95
89
  let webhookSecret = null;
96
90
  let owner = null;
97
91
  let repo = null;
@@ -488,36 +482,21 @@ async function main() {
488
482
  }
489
483
  }
490
484
 
491
- // Step 5: Telegram Setup
492
- printStep(++currentStep, TOTAL_STEPS, 'Telegram Setup');
485
+ // Chat Interfaces (informational)
486
+ console.log(chalk.dim('\n Your agent includes a web chat interface at your APP_URL.'));
487
+ console.log(chalk.dim(' You can also connect additional chat interfaces:\n'));
488
+ console.log(chalk.dim(' \u2022 Telegram: ') + chalk.cyan('npm run setup-telegram'));
489
+ console.log('');
493
490
 
494
- telegramToken = await promptForTelegramToken();
495
-
496
- if (telegramToken) {
497
- const validateSpinner = ora('Validating bot token...').start();
498
- const validation = await validateBotToken(telegramToken);
499
-
500
- if (!validation.valid) {
501
- validateSpinner.fail(`Invalid token: ${validation.error}`);
502
- telegramToken = null;
503
- } else {
504
- validateSpinner.succeed(`Bot: @${validation.botInfo.username}`);
505
- telegramWebhookSecret = await generateTelegramWebhookSecret();
506
- }
507
- } else {
508
- printInfo('Skipped Telegram setup');
509
- }
510
-
511
- // Write .env file (now at project root, not event_handler/)
512
- const telegramVerification = telegramToken ? generateVerificationCode() : null;
491
+ // Write .env file
513
492
  const providerConfig = agentProvider !== 'custom' ? PROVIDERS[agentProvider] : null;
514
493
  const providerEnvKey = providerConfig ? providerConfig.envKey : 'CUSTOM_API_KEY';
515
494
  const envPath = writeEnvFile({
516
495
  githubToken: pat,
517
496
  githubOwner: owner,
518
497
  githubRepo: repo,
519
- telegramBotToken: telegramToken,
520
- telegramWebhookSecret,
498
+ telegramBotToken: null,
499
+ telegramWebhookSecret: null,
521
500
  ghWebhookSecret: webhookSecret,
522
501
  llmProvider: agentProvider,
523
502
  llmModel: agentModel,
@@ -525,11 +504,11 @@ async function main() {
525
504
  providerApiKey: collectedKeys[providerEnvKey] || '',
526
505
  openaiApiKey: collectedKeys['OPENAI_API_KEY'] || '',
527
506
  telegramChatId: null,
528
- telegramVerification,
507
+ telegramVerification: null,
529
508
  });
530
509
  printSuccess(`Created ${envPath}`);
531
510
 
532
- // Step 6: Start Server
511
+ // Step 5: Start Server
533
512
  printStep(++currentStep, TOTAL_STEPS, 'Start Server');
534
513
 
535
514
  console.log(chalk.bold(' Start the dev server in a new terminal window:\n'));
@@ -552,10 +531,10 @@ async function main() {
552
531
  }
553
532
  }
554
533
 
555
- // Step 7: APP_URL
534
+ // Step 6: APP_URL
556
535
  printStep(++currentStep, TOTAL_STEPS, 'App URL');
557
536
 
558
- console.log(chalk.dim(' Your app needs a public URL for GitHub webhooks and Telegram.\n'));
537
+ console.log(chalk.dim(' Your app needs a public URL for GitHub webhooks.\n'));
559
538
  console.log(chalk.dim(' Examples:'));
560
539
  console.log(chalk.dim(' \u2022 ngrok: ') + chalk.cyan('https://abc123.ngrok.io'));
561
540
  console.log(chalk.dim(' \u2022 VPS: ') + chalk.cyan('https://mybot.example.com'));
@@ -598,45 +577,6 @@ async function main() {
598
577
  }
599
578
  }
600
579
 
601
- // Register Telegram webhook if configured
602
- if (telegramToken) {
603
- const webhookUrl = `${appUrl}/api/telegram/webhook`;
604
- let tgWebhookSet = false;
605
- while (!tgWebhookSet) {
606
- const tgSpinner = ora('Registering Telegram webhook...').start();
607
- const tgResult = await setTelegramWebhook(telegramToken, webhookUrl, telegramWebhookSecret);
608
- if (tgResult.ok) {
609
- tgSpinner.succeed(`Telegram webhook registered: ${webhookUrl}`);
610
- tgWebhookSet = true;
611
- } else {
612
- tgSpinner.fail(`Failed: ${tgResult.description}`);
613
- await pressEnter('Fix the issue, then press enter to retry');
614
- }
615
- }
616
-
617
- // Chat ID verification
618
- let chatVerified = false;
619
- while (!chatVerified) {
620
- const chatId = await runVerificationFlow(telegramVerification);
621
-
622
- if (chatId) {
623
- updateEnvVariable('TELEGRAM_CHAT_ID', chatId);
624
- printSuccess(`Chat ID saved: ${chatId}`);
625
-
626
- const verified = await verifyRestart(appUrl);
627
- if (verified) {
628
- printSuccess('Telegram bot is configured and working!');
629
- } else {
630
- printWarning('Could not verify bot. Check your configuration.');
631
- }
632
- chatVerified = true;
633
- } else {
634
- printWarning('Chat ID is required \u2014 the bot will not respond without it.');
635
- await pressEnter('Fix the issue, then press enter to retry');
636
- }
637
- }
638
- }
639
-
640
580
  // Step 7: Summary
641
581
  printStep(++currentStep, TOTAL_STEPS, 'Setup Complete!');
642
582
 
@@ -651,7 +591,6 @@ async function main() {
651
591
  console.log(` ${chalk.dim(`${envVar}:`)} ${maskSecret(value)}`);
652
592
  }
653
593
  if (braveKey) console.log(` ${chalk.dim('Brave Search:')} ${maskSecret(braveKey)}`);
654
- if (telegramToken) console.log(` ${chalk.dim('Telegram Bot:')} Webhook registered`);
655
594
 
656
595
  console.log(chalk.bold('\n GitHub Secrets Set:\n'));
657
596
  console.log(' \u2022 SECRETS');
@@ -667,11 +606,8 @@ async function main() {
667
606
 
668
607
  console.log(chalk.bold.green('\n You\'re all set!\n'));
669
608
 
670
- if (telegramToken) {
671
- console.log(chalk.cyan(' Message your Telegram bot to create your first job!'));
672
- } else {
673
- console.log(chalk.dim(' Use the /api/create-job endpoint to create jobs.'));
674
- }
609
+ console.log(chalk.dim(' Chat with your agent at ') + chalk.cyan(appUrl));
610
+ console.log(chalk.dim(' To connect Telegram: ') + chalk.cyan('npm run setup-telegram'));
675
611
 
676
612
  console.log('\n');
677
613
  }
@@ -0,0 +1,37 @@
1
+ name: Deploy Event Handler
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ concurrency:
8
+ group: deploy
9
+ cancel-in-progress: true
10
+
11
+ jobs:
12
+ deploy:
13
+ runs-on: self-hosted
14
+ steps:
15
+ - name: Deploy event handler
16
+ run: |
17
+ docker exec thepopebot-event-handler bash -c '
18
+ # Authenticate git via GitHub CLI
19
+ echo "${GH_TOKEN}" | gh auth login --with-token
20
+ gh auth setup-git
21
+
22
+ # Check if there are code changes (not just logs)
23
+ git fetch origin main
24
+ CHANGED=$(git diff --name-only HEAD origin/main)
25
+ if ! echo "$CHANGED" | grep -qv "^logs/"; then
26
+ echo "Logs-only change, skipping rebuild"
27
+ git reset --hard origin/main
28
+ exit 0
29
+ fi
30
+
31
+ # Pull, build, reload
32
+ git reset --hard origin/main
33
+ npm install --omit=dev
34
+ npm run build
35
+ echo "Rebuild complete, reloading Next.js..."
36
+ npx pm2 reload all
37
+ '
@@ -0,0 +1,19 @@
1
+ # /api — External API Routes
2
+
3
+ This directory contains the route handlers for all `/api/*` endpoints. These routes are for **external callers only** — GitHub Actions, Telegram, cURL, third-party webhooks.
4
+
5
+ ## Auth
6
+
7
+ All routes (except `/telegram/webhook` and `/github/webhook`, which use their own webhook secrets) require a valid API key passed via the `x-api-key` header. API keys are stored in the SQLite database and managed through the admin UI — they are NOT environment variables.
8
+
9
+ Auth flow: `x-api-key` header -> `verifyApiKey()` -> database lookup (hashed, timing-safe comparison).
10
+
11
+ ## Do NOT use these routes for browser UI
12
+
13
+ Browser-facing features must use **Server Actions** (`'use server'` functions) with `requireAuth()` session checks — never `/api` fetch calls. The only exception is chat streaming, which has its own dedicated route at `/stream/chat` with session auth.
14
+
15
+ | Caller | Mechanism | Auth |
16
+ |--------|-----------|------|
17
+ | External (cURL, GitHub Actions, Telegram) | `/api` route | `x-api-key` header |
18
+ | Browser UI (data/mutations) | Server Action | `requireAuth()` session |
19
+ | Browser UI (chat streaming) | `/stream/chat` | `auth()` session |
@@ -1,11 +1,20 @@
1
1
  FROM node:22-bookworm-slim
2
2
 
3
- RUN apt-get update && apt-get install -y curl python3 make g++ && rm -rf /var/lib/apt/lists/*
3
+ RUN apt-get update && apt-get install -y curl git python3 make g++ && \
4
+ curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
5
+ | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg && \
6
+ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
7
+ | tee /etc/apt/sources.list.d/github-cli.list > /dev/null && \
8
+ apt-get update && apt-get install -y gh && \
9
+ rm -rf /var/lib/apt/lists/*
10
+ RUN npm install -g pm2
4
11
 
5
12
  WORKDIR /app
6
13
  COPY package.json package-lock.json* ./
7
14
  RUN npm install --omit=dev && \
8
15
  npm install --no-save thepopebot@$(node -p "require('./package.json').version")
9
16
 
17
+ COPY ecosystem.config.cjs ./
18
+
10
19
  EXPOSE 80
11
- CMD ["node_modules/.bin/next", "start", "-p", "80"]
20
+ CMD ["pm2-runtime", "ecosystem.config.cjs"]
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ apps: [{
3
+ name: 'next',
4
+ script: 'node_modules/.bin/next',
5
+ args: 'start -p 80',
6
+ kill_timeout: 120000,
7
+ }]
8
+ };
@@ -21,7 +21,8 @@ services:
21
21
  - traefik_certs:/letsencrypt
22
22
  restart: unless-stopped
23
23
 
24
- event_handler:
24
+ event-handler:
25
+ container_name: thepopebot-event-handler
25
26
  image: ${EVENT_HANDLER_IMAGE_URL:-stephengpope/thepopebot:event-handler-${THEPOPEBOT_VERSION:-latest}}
26
27
  volumes:
27
28
  - .:/app
@@ -29,12 +30,19 @@ services:
29
30
  labels:
30
31
  - traefik.enable=true
31
32
  # Set APP_HOSTNAME in .env to the domain from APP_URL (e.g., mybot.example.com)
32
- - traefik.http.routers.event_handler.rule=Host(`${APP_HOSTNAME}`)
33
- - traefik.http.routers.event_handler.entrypoints=web
34
- - traefik.http.services.event_handler.loadbalancer.server.port=80
33
+ - traefik.http.routers.event-handler.rule=Host(`${APP_HOSTNAME}`)
34
+ - traefik.http.routers.event-handler.entrypoints=web
35
+ - traefik.http.services.event-handler.loadbalancer.server.port=80
35
36
  ## Uncomment the following lines to enable TLS via Let's Encrypt:
36
- # - traefik.http.routers.event_handler.entrypoints=websecure
37
- # - traefik.http.routers.event_handler.tls.certresolver=letsencrypt
37
+ # - traefik.http.routers.event-handler.entrypoints=websecure
38
+ # - traefik.http.routers.event-handler.tls.certresolver=letsencrypt
39
+ stop_grace_period: 120s
40
+ healthcheck:
41
+ test: ["CMD", "curl", "-f", "http://localhost:80/api/ping"]
42
+ interval: 30s
43
+ timeout: 10s
44
+ retries: 3
45
+ start_period: 30s
38
46
  restart: unless-stopped
39
47
 
40
48
  runner: