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 +13 -4
- package/package.json +1 -1
- package/setup/lib/github.mjs +4 -2
- package/setup/lib/prerequisites.mjs +22 -2
- package/setup/lib/prompts.mjs +3 -3
- package/setup/setup.mjs +32 -12
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.
|
|
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
package/setup/lib/github.mjs
CHANGED
|
@@ -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 };
|
package/setup/lib/prompts.mjs
CHANGED
|
@@ -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? (
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|