thepopebot 1.2.71 → 1.2.72-beta.1

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
@@ -206,6 +206,20 @@ If you've made custom changes to managed files (e.g., added extra steps to a Git
206
206
  npx thepopebot init --no-managed
207
207
  ```
208
208
 
209
+ #### Template file conventions
210
+
211
+ The `templates/` directory contains files scaffolded into user projects by `thepopebot init`. Two naming conventions handle files that npm or AI tools would otherwise misinterpret:
212
+
213
+ **`.template` suffix** — Files ending in `.template` are scaffolded with the suffix stripped. This is used for files that npm mangles (`.gitignore`) or that AI tools would pick up as real project docs (`CLAUDE.md`).
214
+
215
+ | In `templates/` | Scaffolded as |
216
+ |-----------------|---------------|
217
+ | `.gitignore.template` | `.gitignore` |
218
+ | `CLAUDE.md.template` | `CLAUDE.md` |
219
+ | `api/CLAUDE.md.template` | `api/CLAUDE.md` |
220
+
221
+ **`CLAUDE.md` exclusion** — The scaffolding walker skips any file named `CLAUDE.md` (without the `.template` suffix). This is a safety net so a bare `CLAUDE.md` accidentally added to `templates/` never gets copied into user projects where AI tools would confuse it with real project instructions.
222
+
209
223
  ---
210
224
 
211
225
  ## CLI Commands
@@ -246,22 +260,6 @@ GitHub secrets use a prefix convention so the workflow can route them correctly:
246
260
 
247
261
  ---
248
262
 
249
- ## Template File Conventions
250
-
251
- The `templates/` directory contains files scaffolded into user projects by `thepopebot init`. Two naming conventions handle files that npm or AI tools would otherwise misinterpret:
252
-
253
- **`.template` suffix** — Files ending in `.template` are scaffolded with the suffix stripped. This is used for files that npm mangles (`.gitignore`) or that AI tools would pick up as real project docs (`CLAUDE.md`).
254
-
255
- | In `templates/` | Scaffolded as |
256
- |-----------------|---------------|
257
- | `.gitignore.template` | `.gitignore` |
258
- | `CLAUDE.md.template` | `CLAUDE.md` |
259
- | `api/CLAUDE.md.template` | `api/CLAUDE.md` |
260
-
261
- **`CLAUDE.md` exclusion** — The scaffolding walker skips any file named `CLAUDE.md` (without the `.template` suffix). This is a safety net so a bare `CLAUDE.md` accidentally added to `templates/` never gets copied into user projects where AI tools would confuse it with real project instructions.
262
-
263
- ---
264
-
265
263
  ## Security
266
264
 
267
265
  thepopebot includes API key authentication, webhook secret validation (fail-closed), session encryption, secret filtering in the Docker agent, and auto-merge path restrictions. However, all software carries risk — thepopebot is provided as-is, and you are responsible for securing your own infrastructure. If you're running locally with a tunnel (ngrok, Cloudflare Tunnel, port forwarding), be aware that your dev server endpoints are publicly accessible with no rate limiting and no TLS on the local hop.
@@ -270,85 +268,11 @@ See [docs/SECURITY.md](docs/SECURITY.md) for full details on what's exposed, the
270
268
 
271
269
  ---
272
270
 
273
- ## Running Different Models Per Job
274
-
275
- Every job your agent runs can use a different LLM. Use Claude for one cron, GPT-4o for another, or a local Ollama model for a third — just set `llm_provider` and `llm_model` on the entry.
276
-
277
- ### Per-job overrides
278
-
279
- Add `llm_provider` and `llm_model` to any agent-type entry in `config/CRONS.json` or any action in `config/TRIGGERS.json`. This overrides the default for just that one job:
280
-
281
- ```json
282
- {
283
- "name": "Code review",
284
- "schedule": "0 9 * * 1",
285
- "type": "agent",
286
- "job": "Review open PRs and leave comments",
287
- "llm_provider": "openai",
288
- "llm_model": "gpt-4o"
289
- }
290
- ```
291
-
292
- The matching API key must already exist as a GitHub secret (see the table below).
293
-
294
- > **Using `custom` on individual crons:** `llm_provider` and `llm_model` travel with the job, but `OPENAI_BASE_URL`, `RUNS_ON`, and `CUSTOM_API_KEY` are repo-level settings — they must be set as GitHub variables/secrets even if your default provider is something else. See [Using the `custom` provider](#using-the-custom-provider) below.
295
-
296
- ### Providers
297
-
298
- | Provider | What it is | Example model | GitHub secret needed |
299
- |----------|------------|---------------|----------------------|
300
- | `anthropic` | Anthropic (default) | `claude-sonnet-4-20250514` | `AGENT_ANTHROPIC_API_KEY` |
301
- | `openai` | OpenAI | `gpt-4o` | `AGENT_OPENAI_API_KEY` |
302
- | `google` | Google Gemini | `gemini-2.5-pro` | `AGENT_GOOGLE_API_KEY` |
303
- | `custom` | Any OpenAI-compatible API (DeepSeek, Ollama, Together AI, etc.) | `deepseek-chat` | `AGENT_CUSTOM_API_KEY` *(if required — see below)* |
304
-
305
- ### Changing the default for all jobs
306
-
307
- If you want every job to use the same non-Anthropic model, set `LLM_PROVIDER` and `LLM_MODEL` as GitHub repo variables:
308
-
309
- ```bash
310
- npx thepopebot set-var LLM_PROVIDER openai
311
- npx thepopebot set-var LLM_MODEL gpt-4o
312
- ```
313
-
314
- Per-job overrides in `CRONS.json` and `TRIGGERS.json` still take priority over these defaults.
315
-
316
- ### Using the `custom` provider
317
-
318
- `custom` means "any server that speaks the OpenAI chat-completions API." This covers two cases: cloud APIs and local models.
319
-
320
- #### Cloud custom (DeepSeek, Together AI, Fireworks, etc.)
321
-
322
- Point at the provider's endpoint and add your API key:
323
-
324
- ```bash
325
- npx thepopebot set-var LLM_PROVIDER custom
326
- npx thepopebot set-var LLM_MODEL deepseek-chat
327
- npx thepopebot set-var OPENAI_BASE_URL https://api.deepseek.com/v1
328
- ```
329
-
330
- Then set the API key as a GitHub secret:
331
-
332
- ```bash
333
- npx thepopebot set-agent-secret CUSTOM_API_KEY sk-...
334
- ```
335
-
336
- Cloud custom APIs are reachable from any runner — no other changes needed.
337
-
338
- #### Local custom (Ollama, LM Studio, vLLM, etc.)
339
-
340
- For a model running on your own machine you need a [self-hosted runner](https://docs.github.com/en/actions/hosting-your-own-runners) so the job executes on your hardware:
341
-
342
- ```bash
343
- npx thepopebot set-var RUNS_ON self-hosted
344
- npx thepopebot set-var LLM_PROVIDER custom
345
- npx thepopebot set-var LLM_MODEL qwen3:8b
346
- npx thepopebot set-var OPENAI_BASE_URL http://host.docker.internal:11434/v1
347
- ```
271
+ ## Running Different Models
348
272
 
349
- Most local servers don't need an API key. If yours does, set `AGENT_CUSTOM_API_KEY` as a GitHub secret.
273
+ The Event Handler (chat, Telegram, webhooks) and Jobs (Docker agent) are two independent layers — each can run a different LLM. Use Claude for interactive chat and a cheaper or local model for long-running jobs, mix providers per cron entry, or run everything on a single model.
350
274
 
351
- > **Important:** `RUNS_ON=self-hosted` is only needed when the model runs on your machine. Jobs run inside Docker, so use `host.docker.internal` to reach a model server on the host.
275
+ See [docs/RUNNING_DIFFERENT_MODELS.md](docs/RUNNING_DIFFERENT_MODELS.md) for the full guide: Event Handler config, job defaults, per-job overrides, provider table, and custom provider setup.
352
276
 
353
277
  ---
354
278
 
@@ -360,6 +284,7 @@ Most local servers don't need an API key. If yours does, set `AGENT_CUSTOM_API_K
360
284
  | [Configuration](docs/CONFIGURATION.md) | Environment variables, GitHub secrets, repo variables, ngrok, Telegram setup |
361
285
  | [Customization](docs/CUSTOMIZATION.md) | Personality, skills, operating system files, using your bot, security details |
362
286
  | [Chat Integrations](docs/CHAT_INTEGRATIONS.md) | Web chat, Telegram, adding new channels |
287
+ | [Running Different Models](docs/RUNNING_DIFFERENT_MODELS.md) | Event Handler vs job model config, per-job overrides, providers, custom provider |
363
288
  | [Auto-Merge](docs/AUTO_MERGE.md) | Auto-merge controls, ALLOWED_PATHS configuration |
364
289
  | [Deployment](docs/DEPLOYMENT.md) | VPS setup, Docker Compose, HTTPS with Let's Encrypt |
365
290
  | [How to Use Pi](docs/HOW_TO_USE_PI.md) | Guide to the Pi coding agent |
package/bin/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { execSync } from 'child_process';
3
+ import { execSync, execFileSync } from 'child_process';
4
4
  import fs from 'fs';
5
5
  import path from 'path';
6
6
  import { fileURLToPath } from 'url';
@@ -18,6 +18,7 @@ const args = process.argv.slice(3);
18
18
  const MANAGED_PATHS = [
19
19
  '.github/workflows/',
20
20
  'docker/event-handler/',
21
+ 'docker/job/',
21
22
  'docker-compose.yml',
22
23
  '.dockerignore',
23
24
  ];
@@ -400,7 +401,7 @@ function copyDirSyncForce(src, dest, templateRelBase = '') {
400
401
  function setup() {
401
402
  const setupScript = path.join(__dirname, '..', 'setup', 'setup.mjs');
402
403
  try {
403
- execSync(`node ${setupScript}`, { stdio: 'inherit', cwd: process.cwd() });
404
+ execFileSync(process.execPath, [setupScript], { stdio: 'inherit', cwd: process.cwd() });
404
405
  } catch {
405
406
  process.exit(1);
406
407
  }
@@ -409,7 +410,7 @@ function setup() {
409
410
  function setupTelegram() {
410
411
  const setupScript = path.join(__dirname, '..', 'setup', 'setup-telegram.mjs');
411
412
  try {
412
- execSync(`node ${setupScript}`, { stdio: 'inherit', cwd: process.cwd() });
413
+ execFileSync(process.execPath, [setupScript], { stdio: 'inherit', cwd: process.cwd() });
413
414
  } catch {
414
415
  process.exit(1);
415
416
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thepopebot",
3
- "version": "1.2.71",
3
+ "version": "1.2.72-beta.1",
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
@@ -237,12 +237,12 @@ async function main() {
237
237
 
238
238
  try {
239
239
  const url = remoteUrl.replace(/\/$/, '').replace(/\.git$/, '') + '.git';
240
- execSync(`git remote add origin ${url}`, { stdio: 'ignore' });
240
+ execSync(`git remote add origin "${url}"`, { stdio: 'ignore' });
241
241
  remoteAdded = true;
242
242
  } catch {
243
243
  try {
244
244
  const url = remoteUrl.replace(/\/$/, '').replace(/\.git$/, '') + '.git';
245
- execSync(`git remote set-url origin ${url}`, { stdio: 'ignore' });
245
+ execSync(`git remote set-url origin "${url}"`, { stdio: 'ignore' });
246
246
  remoteAdded = true;
247
247
  } catch {
248
248
  clack.log.error('Failed to set remote. Try again.');
@@ -355,7 +355,7 @@ async function main() {
355
355
  let pushed = false;
356
356
  while (!pushed) {
357
357
  const authedUrl = remote.replace('https://github.com/', `https://x-access-token:${pat}@github.com/`);
358
- execSync(`git remote set-url origin ${authedUrl}`, { stdio: 'ignore' });
358
+ execSync(`git remote set-url origin "${authedUrl}"`, { stdio: 'ignore' });
359
359
 
360
360
  const pushSpinner = clack.spinner();
361
361
  pushSpinner.start('Pushing to GitHub...');
@@ -368,7 +368,7 @@ async function main() {
368
368
  pushSpinner.stop('Failed to push');
369
369
  const output = (err.stdout || '') + (err.stderr || '');
370
370
  if (output) clack.log.error(output.trim());
371
- execSync(`git remote set-url origin ${remote}`, { stdio: 'ignore' });
371
+ execSync(`git remote set-url origin "${remote}"`, { stdio: 'ignore' });
372
372
  clack.log.info('Your PAT may not have write access to this repository.');
373
373
  pat = await promptForPAT();
374
374
  collected.GH_TOKEN = pat;
@@ -376,7 +376,7 @@ async function main() {
376
376
  }
377
377
 
378
378
  // Reset remote URL back to clean HTTPS (no token embedded)
379
- execSync(`git remote set-url origin ${remote}`, { stdio: 'ignore' });
379
+ execSync(`git remote set-url origin "${remote}"`, { stdio: 'ignore' });
380
380
  }
381
381
  }
382
382
 
@@ -12,13 +12,13 @@ echo "Job ID: ${JOB_ID}"
12
12
  # Export SECRETS (JSON) as flat env vars (GH_TOKEN, ANTHROPIC_API_KEY, etc.)
13
13
  # These are filtered from LLM's bash subprocess by env-sanitizer extension
14
14
  if [ -n "$SECRETS" ]; then
15
- eval $(echo "$SECRETS" | jq -r 'to_entries | .[] | "export \(.key)=\"\(.value)\""')
15
+ eval $(echo "$SECRETS" | jq -r 'to_entries | .[] | "export \(.key)=\(.value | @sh)"')
16
16
  fi
17
17
 
18
18
  # Export LLM_SECRETS (JSON) as flat env vars
19
19
  # These are NOT filtered - LLM can access these (browser logins, skill API keys, etc.)
20
20
  if [ -n "$LLM_SECRETS" ]; then
21
- eval $(echo "$LLM_SECRETS" | jq -r 'to_entries | .[] | "export \(.key)=\"\(.value)\""')
21
+ eval $(echo "$LLM_SECRETS" | jq -r 'to_entries | .[] | "export \(.key)=\(.value | @sh)"')
22
22
  fi
23
23
 
24
24
  # Git setup - derive identity from GitHub token