thepopebot 1.2.70 → 1.2.71-beta.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/README.md CHANGED
@@ -64,10 +64,10 @@ You interact with your bot via the web chat interface or Telegram (optional). Th
64
64
  | **npm** | Included with Node.js |
65
65
  | **Git** | [git-scm.com](https://git-scm.com) |
66
66
  | **GitHub CLI** | [cli.github.com](https://cli.github.com) |
67
- | **Docker + Docker Compose** | [docker.com](https://docs.docker.com/get-docker/) |
68
- | **ngrok*** | [ngrok.com](https://ngrok.com/download) |
67
+ | **Docker + Docker Compose** | [docker.com](https://docs.docker.com/get-docker/) (installer requires admin password) |
68
+ | **ngrok*** | [ngrok.com](https://ngrok.com/download) (free account + authtoken required) |
69
69
 
70
- *\*ngrok is only required for local installs without port forwarding. VPS/cloud deployments don't need it.*
70
+ *\*ngrok is only required for local installs without port forwarding. VPS/cloud deployments don't need it. [Sign up](https://dashboard.ngrok.com/signup) for a free ngrok account, then run `ngrok config add-authtoken <YOUR_TOKEN>` before starting setup.*
71
71
 
72
72
  ### Three steps
73
73
 
@@ -106,7 +106,16 @@ docker compose up -d
106
106
  - **Webhook**: Send a POST to `/api/create-job` with your API key to create jobs programmatically
107
107
  - **Cron**: Edit `config/CRONS.json` to schedule recurring jobs
108
108
 
109
- > **Local installs**: Your server needs to be reachable from the internet for GitHub webhooks and Telegram. On a VPS/cloud server, your APP_URL is just your domain. For local development, use [ngrok](https://ngrok.com) (`ngrok http 80`) or port forwarding to expose your machine. If your ngrok URL changes, update APP_URL in `.env` and the GitHub repository variable, and re-run `npm run setup-telegram` if Telegram is configured.
109
+ > **Local installs**: Your server needs to be reachable from the internet for GitHub webhooks and Telegram. On a VPS/cloud server, your APP_URL is just your domain. For local development, use [ngrok](https://ngrok.com) (`ngrok http 80`) or port forwarding to expose your machine.
110
+ >
111
+ > **If your ngrok URL changes** (it changes every time you restart ngrok on the free plan), you must update APP_URL everywhere:
112
+ >
113
+ > ```bash
114
+ > # Update .env and GitHub variable in one command:
115
+ > npx thepopebot set-var APP_URL https://your-new-url.ngrok.io
116
+ > # If Telegram is configured, re-register the webhook:
117
+ > npm run setup-telegram
118
+ > ```
110
119
 
111
120
  ---
112
121
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thepopebot",
3
- "version": "1.2.70",
3
+ "version": "1.2.71-beta.0",
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": {
@@ -1,6 +1,7 @@
1
1
  import { execSync, exec } from 'child_process';
2
2
  import { promisify } from 'util';
3
3
  import { randomBytes } from 'crypto';
4
+ import { ghEnv } from './prerequisites.mjs';
4
5
 
5
6
  const execAsync = promisify(exec);
6
7
 
@@ -69,7 +70,7 @@ export async function setSecret(owner, repo, name, value) {
69
70
  // Use stdin to pass the secret value securely
70
71
  const { stdout, stderr } = await execAsync(
71
72
  `echo "${value.replace(/"/g, '\\"')}" | gh secret set ${name} --repo ${owner}/${repo}`,
72
- { encoding: 'utf-8' }
73
+ { encoding: 'utf-8', env: ghEnv() }
73
74
  );
74
75
  return { success: true };
75
76
  } catch (error) {
@@ -95,6 +96,7 @@ export async function listSecrets(owner, repo) {
95
96
  try {
96
97
  const { stdout } = await execAsync(`gh secret list --repo ${owner}/${repo}`, {
97
98
  encoding: 'utf-8',
99
+ env: ghEnv(),
98
100
  });
99
101
  const secrets = stdout
100
102
  .trim()
@@ -114,7 +116,7 @@ export async function setVariable(owner, repo, name, value) {
114
116
  try {
115
117
  const { stdout, stderr } = await execAsync(
116
118
  `echo "${value.replace(/"/g, '\\"')}" | gh variable set ${name} --repo ${owner}/${repo}`,
117
- { encoding: 'utf-8' }
119
+ { encoding: 'utf-8', env: ghEnv() }
118
120
  );
119
121
  return { success: true };
120
122
  } catch (error) {
@@ -3,6 +3,18 @@ import { promisify } from 'util';
3
3
 
4
4
  const execAsync = promisify(exec);
5
5
 
6
+ /**
7
+ * Return process.env without GITHUB_TOKEN and GH_TOKEN.
8
+ * The gh CLI auto-uses these env vars, which can shadow interactive login
9
+ * and cause secret/variable operations to fail with the wrong identity.
10
+ */
11
+ export function ghEnv() {
12
+ const env = { ...process.env };
13
+ delete env.GITHUB_TOKEN;
14
+ delete env.GH_TOKEN;
15
+ return env;
16
+ }
17
+
6
18
  /**
7
19
  * Check if a command exists
8
20
  */
@@ -11,6 +23,14 @@ function commandExists(cmd) {
11
23
  execSync(`which ${cmd}`, { stdio: 'ignore' });
12
24
  return true;
13
25
  } catch {
26
+ if (process.platform === 'win32') {
27
+ try {
28
+ execSync(`where ${cmd}`, { stdio: 'ignore' });
29
+ return true;
30
+ } catch {
31
+ return false;
32
+ }
33
+ }
14
34
  return false;
15
35
  }
16
36
  }
@@ -32,7 +52,7 @@ function getNodeVersion() {
32
52
  */
33
53
  async function isGhAuthenticated() {
34
54
  try {
35
- await execAsync('gh auth status');
55
+ await execAsync('gh auth status', { env: ghEnv() });
36
56
  return true;
37
57
  } catch {
38
58
  return false;
@@ -136,7 +156,7 @@ export async function installGlobalPackage(packageName) {
136
156
  */
137
157
  export async function runGhAuth() {
138
158
  // This needs to be interactive, so we use execSync
139
- execSync('gh auth login', { stdio: 'inherit' });
159
+ execSync('gh auth login', { stdio: 'inherit', env: ghEnv() });
140
160
  }
141
161
 
142
162
  export { commandExists, getGitRemoteInfo, getPackageManager };
@@ -189,18 +189,18 @@ export async function promptForCustomProvider() {
189
189
  */
190
190
  export async function promptForBraveKey() {
191
191
  const addKey = handleCancel(await clack.confirm({
192
- message: 'Add Brave Search API key? (free tier, greatly improves agent)',
192
+ message: 'Add Brave Search API key? (optional, greatly improves agent)',
193
193
  initialValue: true,
194
194
  }));
195
195
 
196
196
  if (!addKey) return null;
197
197
 
198
198
  clack.log.info(
199
- 'To get a free Brave Search API key:\n' +
199
+ 'To get a Brave Search API key:\n' +
200
200
  ' 1. Go to https://api-dashboard.search.brave.com/app/keys\n' +
201
201
  ' 2. Click "Get Started"\n' +
202
202
  ' 3. Create an account (or sign in)\n' +
203
- ' 4. Subscribe to the "Free" plan (2,000 queries/month)\n' +
203
+ ' 4. Subscribe to a plan (free credits may be available)\n' +
204
204
  ' 5. Copy your API key'
205
205
  );
206
206
 
package/setup/setup.mjs CHANGED
@@ -249,7 +249,11 @@ async function main() {
249
249
  clack.log.success('ngrok installed');
250
250
  } else {
251
251
  clack.log.warn('ngrok not installed (needed to expose local server)');
252
- clack.log.info('Install with: brew install ngrok/ngrok/ngrok');
252
+ clack.log.info(
253
+ 'Install with: brew install ngrok/ngrok/ngrok\n' +
254
+ ' Sign up for a free account at https://dashboard.ngrok.com/signup\n' +
255
+ ' Then run: ngrok config add-authtoken <YOUR_TOKEN>'
256
+ );
253
257
  }
254
258
 
255
259
  // ─── Step 2: GitHub PAT ──────────────────────────────────────────────
@@ -559,25 +563,41 @@ async function main() {
559
563
  // Server not reachable
560
564
  }
561
565
 
562
- if (serverAlreadyRunning) {
563
- clack.log.success('Server is already running');
564
- if (await confirm('Rebuild and restart anyway?', false)) {
565
- clack.log.info('Rebuilding Next.js...');
566
+ // Helper: run build with retry on failure
567
+ async function runBuildWithRetry() {
568
+ for (let attempt = 1; attempt <= 2; attempt++) {
566
569
  try {
567
570
  execSync('npm run build', { stdio: 'inherit' });
568
571
  clack.log.success('Build complete');
572
+ return true;
569
573
  } catch {
570
- clack.log.error('Build failed run npm run build manually');
574
+ if (attempt === 1) {
575
+ clack.log.error('Build failed.');
576
+ const retry = await confirm('Retry build?');
577
+ if (!retry) break;
578
+ } else {
579
+ clack.log.error('Build failed again.');
580
+ }
571
581
  }
572
582
  }
583
+ clack.log.error(
584
+ 'Cannot continue without a successful build.\n' +
585
+ ' Fix the error above, then run:\n\n' +
586
+ ' npm run build\n' +
587
+ ' docker compose up -d'
588
+ );
589
+ process.exit(1);
590
+ }
591
+
592
+ if (serverAlreadyRunning) {
593
+ clack.log.success('Server is already running');
594
+ if (await confirm('Rebuild and restart anyway?', false)) {
595
+ clack.log.info('Rebuilding Next.js...');
596
+ await runBuildWithRetry();
597
+ }
573
598
  } else {
574
599
  clack.log.info('Building Next.js...');
575
- try {
576
- execSync('npm run build', { stdio: 'inherit' });
577
- clack.log.success('Build complete');
578
- } catch {
579
- clack.log.error('Build failed — run npm run build manually');
580
- }
600
+ await runBuildWithRetry();
581
601
 
582
602
  clack.log.info('Start Docker in a new terminal window:\n\n docker compose up -d');
583
603