thepopebot 1.2.76-beta.2 → 1.2.76-beta.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.
Files changed (128) hide show
  1. package/README.md +3 -3
  2. package/api/CLAUDE.md +11 -4
  3. package/api/index.js +56 -18
  4. package/bin/CLAUDE.md +7 -4
  5. package/bin/cli.js +25 -45
  6. package/config/CLAUDE.md +23 -4
  7. package/drizzle/0021_coding_agent_workspace.sql +1 -0
  8. package/drizzle/0022_organic_apocalypse.sql +16 -0
  9. package/drizzle/0023_needy_ender_wiggin.sql +1 -0
  10. package/drizzle/meta/0021_snapshot.json +639 -0
  11. package/drizzle/meta/0022_snapshot.json +743 -0
  12. package/drizzle/meta/0023_snapshot.json +750 -0
  13. package/drizzle/meta/_journal.json +21 -0
  14. package/lib/CLAUDE.md +2 -2
  15. package/lib/actions.js +9 -1
  16. package/lib/ai/CLAUDE.md +72 -57
  17. package/lib/ai/helper-llm.js +108 -0
  18. package/lib/ai/index.js +308 -438
  19. package/lib/ai/line-mappers.js +42 -24
  20. package/lib/ai/scope.js +26 -0
  21. package/lib/ai/sdk-adapters/CLAUDE.md +114 -0
  22. package/lib/ai/sdk-adapters/claude-code.js +120 -8
  23. package/lib/ai/system-prompt.js +34 -0
  24. package/lib/ai/workspace-setup.js +19 -35
  25. package/lib/channels/CLAUDE.md +14 -4
  26. package/lib/channels/base.js +6 -2
  27. package/lib/channels/commands/index.js +42 -0
  28. package/lib/channels/commands/session.js +53 -0
  29. package/lib/channels/commands/verify.js +18 -0
  30. package/lib/channels/telegram.js +79 -28
  31. package/lib/chat/CLAUDE.md +4 -4
  32. package/lib/chat/actions.js +270 -49
  33. package/lib/chat/api.js +185 -31
  34. package/lib/chat/components/CLAUDE.md +6 -2
  35. package/lib/chat/components/chat-input.js +77 -47
  36. package/lib/chat/components/chat-input.jsx +77 -40
  37. package/lib/chat/components/chat-page.js +2 -0
  38. package/lib/chat/components/chat-page.jsx +3 -0
  39. package/lib/chat/components/chat.js +62 -14
  40. package/lib/chat/components/chat.jsx +68 -10
  41. package/lib/chat/components/code-mode-toggle.js +141 -22
  42. package/lib/chat/components/code-mode-toggle.jsx +129 -20
  43. package/lib/chat/components/containers-page.js +58 -40
  44. package/lib/chat/components/containers-page.jsx +64 -25
  45. package/lib/chat/components/crons-page.js +17 -3
  46. package/lib/chat/components/crons-page.jsx +34 -6
  47. package/lib/chat/components/index.js +2 -2
  48. package/lib/chat/components/message.js +18 -3
  49. package/lib/chat/components/message.jsx +18 -3
  50. package/lib/chat/components/profile-page.js +182 -4
  51. package/lib/chat/components/profile-page.jsx +196 -1
  52. package/lib/chat/components/scope-picker.js +21 -0
  53. package/lib/chat/components/scope-picker.jsx +27 -0
  54. package/lib/chat/components/settings-chat-page.js +11 -11
  55. package/lib/chat/components/settings-chat-page.jsx +14 -18
  56. package/lib/chat/components/settings-coding-agents-page.js +110 -16
  57. package/lib/chat/components/settings-coding-agents-page.jsx +87 -3
  58. package/lib/chat/components/settings-github-page.js +5 -0
  59. package/lib/chat/components/settings-github-page.jsx +5 -0
  60. package/lib/chat/components/settings-layout.js +3 -3
  61. package/lib/chat/components/settings-layout.jsx +3 -3
  62. package/lib/chat/components/settings-secrets-layout.js +1 -2
  63. package/lib/chat/components/settings-secrets-layout.jsx +1 -2
  64. package/lib/chat/components/settings-secrets-page.js +180 -75
  65. package/lib/chat/components/settings-secrets-page.jsx +212 -66
  66. package/lib/chat/components/triggers-page.js +17 -3
  67. package/lib/chat/components/triggers-page.jsx +34 -6
  68. package/lib/chat/components/ui/combobox.js +18 -2
  69. package/lib/chat/components/ui/combobox.jsx +17 -1
  70. package/lib/chat/components/ui/dropdown-menu.js +23 -2
  71. package/lib/chat/components/ui/dropdown-menu.jsx +27 -2
  72. package/lib/chat/telegram-profile.js +33 -0
  73. package/lib/cluster/CLAUDE.md +9 -3
  74. package/lib/code/CLAUDE.md +11 -3
  75. package/lib/code/actions.js +47 -8
  76. package/lib/code/terminal-view.js +31 -21
  77. package/lib/code/terminal-view.jsx +32 -23
  78. package/lib/config.js +15 -4
  79. package/lib/containers/CLAUDE.md +16 -6
  80. package/lib/db/CLAUDE.md +5 -2
  81. package/lib/db/chats.js +9 -17
  82. package/lib/db/code-workspaces.js +8 -3
  83. package/lib/db/config.js +0 -1
  84. package/lib/db/index.js +12 -0
  85. package/lib/db/schema.js +24 -1
  86. package/lib/db/user-channels.js +129 -0
  87. package/lib/llm-providers.js +8 -0
  88. package/lib/maintenance.js +31 -21
  89. package/lib/tools/CLAUDE.md +12 -3
  90. package/lib/tools/assemblyai.js +17 -0
  91. package/lib/tools/create-agent-job.js +12 -8
  92. package/lib/tools/docker.js +34 -10
  93. package/lib/tools/github.js +34 -0
  94. package/lib/tools/telegram.js +106 -0
  95. package/lib/utils/render-md.js +44 -18
  96. package/package.json +8 -8
  97. package/setup/CLAUDE.md +11 -5
  98. package/setup/lib/providers.mjs +2 -1
  99. package/setup/lib/targets.mjs +13 -16
  100. package/setup/lib/telegram.mjs +8 -69
  101. package/templates/.env.example +0 -7
  102. package/templates/.github/workflows/rebuild-event-handler.yml +1 -1
  103. package/templates/.gitignore.template +1 -3
  104. package/templates/CLAUDE.md +1 -1
  105. package/templates/CLAUDE.md.template +29 -7
  106. package/templates/agent-job/CLAUDE.md.template +5 -3
  107. package/templates/agent-job/CRONS.json +16 -0
  108. package/templates/agent-job/SYSTEM.md +16 -11
  109. package/templates/agents/CLAUDE.md.template +17 -17
  110. package/templates/coding-workspace/CLAUDE.md.template +7 -0
  111. package/templates/data/CLAUDE.md.template +1 -1
  112. package/templates/docker-compose.custom.yml +1 -0
  113. package/templates/docker-compose.yml +1 -0
  114. package/templates/event-handler/CLAUDE.md.template +79 -0
  115. package/templates/event-handler/TRIGGERS.json +18 -2
  116. package/templates/skills/CLAUDE.md.template +20 -22
  117. package/templates/skills/{library/agent-job-secrets → agent-job-secrets}/SKILL.md +2 -2
  118. package/lib/ai/agent.js +0 -65
  119. package/lib/ai/async-channel.js +0 -51
  120. package/lib/ai/model.js +0 -130
  121. package/lib/ai/tools.js +0 -164
  122. package/lib/tools/openai.js +0 -37
  123. package/setup/lib/telegram-verify.mjs +0 -63
  124. package/setup/setup-telegram.mjs +0 -260
  125. package/templates/agent-job/SOUL.md +0 -17
  126. /package/templates/{skills/active/.gitkeep → coding-workspace/SYSTEM.md} +0 -0
  127. /package/templates/skills/{library/agent-job-secrets → agent-job-secrets}/agent-job-secrets.js +0 -0
  128. /package/templates/skills/{library/playwright-cli → playwright-cli}/SKILL.md +0 -0
package/README.md CHANGED
@@ -104,7 +104,7 @@ The wizard walks you through everything:
104
104
  **That's it.** Visit your APP_URL when the wizard finishes.
105
105
 
106
106
  - **Web Chat**: Visit your APP_URL to chat with your agent, create jobs, upload files
107
- - **Telegram** (optional): Run `npm run setup-telegram` to connect a Telegram bot
107
+ - **Telegram** (optional): Connect a Telegram bot from `/admin/event-handler/telegram`
108
108
  - **Webhook**: Send a POST to `/api/create-agent-job` with your API key to create jobs programmatically
109
109
  - **Cron**: Edit `agent-job/CRONS.json` to schedule recurring jobs
110
110
 
@@ -143,9 +143,9 @@ See [Coding Agents](docs/CODING_AGENTS.md) for details on all five agent backend
143
143
  > ```bash
144
144
  > # Update .env and GitHub variable in one command:
145
145
  > npx thepopebot set-var APP_URL https://your-new-url.ngrok.io
146
- > # If Telegram is configured, re-register the webhook:
147
- > npm run setup-telegram
148
146
  > ```
147
+ >
148
+ > If Telegram is configured, click **Re-register webhook** at `/admin/event-handler/telegram` after the URL change.
149
149
 
150
150
  ---
151
151
 
package/api/CLAUDE.md CHANGED
@@ -4,9 +4,14 @@ This directory contains the route handlers for all `/api/*` endpoints. These rou
4
4
 
5
5
  ## Auth
6
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.
7
+ Most routes 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
8
 
9
- Auth flow: `x-api-key` header -> `verifyApiKey()` -> database lookup (hashed, timing-safe comparison).
9
+ **Public routes** (no API key needed): `/ping`, `/telegram/webhook` (Telegram webhook secret), `/github/webhook` (GitHub webhook secret), `/oauth/callback` (validated via short-lived `state` token).
10
+
11
+ Auth flow: `x-api-key` header → `verifyApiKey()` → DB lookup (hashed, timing-safe comparison). Two key types exist:
12
+
13
+ - **User-owned API keys** — long-lived, created via the admin UI, used by external callers (cURL, GitHub Actions, Telegram register).
14
+ - **Per-job agent API keys** (`agent_job_api_key`) — short-lived, auto-created when an agent-job container launches (`createAgentJobApiKey()` in `lib/db/api-keys.js`), tied to the container name, and cleaned up by the maintenance cron after expiry. Only this key type is allowed to call `/api/get-agent-job-secret` (the route rejects other types).
10
15
 
11
16
  ## Do NOT use these routes for browser UI
12
17
 
@@ -25,10 +30,12 @@ Browser-facing data fetching uses **fetch route handlers** colocated with pages
25
30
  |--------|------|------|---------|
26
31
  | GET | `/api/ping` | None | Health check |
27
32
  | POST | `/api/create-agent-job` | `x-api-key` | Create agent job |
28
- | GET | `/api/get-agent-job-secret` | `x-api-key` | Get an agent job secret; oauth2 credentials return only the access_token (auto-refreshed) |
33
+ | GET | `/api/get-agent-job-secret` | `agent_job_api_key` only | Get an agent job secret. `oauth2` credentials return only the access_token (auto-refreshed under a lock; rotated refresh tokens are persisted back). Other secret types return the raw value. |
34
+ | POST | `/api/set-agent-job-secret` | `agent_job_api_key` only | Create or update an agent-job secret from inside the container (used by the `set-secret` skill). |
29
35
  | GET | `/api/agent-job-list-secrets` | `x-api-key` | List agent job secret keys (no values); returns `{secrets: [{key, isSet, updatedAt, secretType}]}` |
30
36
  | GET | `/api/agent-jobs/status` | `x-api-key` | Agent job status (query: `?agent_job_id=`) |
31
- | POST | `/api/telegram/webhook` | Telegram webhook secret | Telegram message handler |
37
+ | POST | `/api/telegram/webhook` | Telegram webhook secret | Telegram message handler (per-user routing via `user_channels`; verifies via `/verify <code>`, dispatches `/session` commands) |
32
38
  | POST | `/api/telegram/register` | `x-api-key` | Register bot token + webhook URL |
33
39
  | POST | `/api/github/webhook` | GitHub webhook secret | GitHub event handler |
34
40
  | POST | `/api/cluster/:clusterId/role/:roleId/webhook` | `x-api-key` | Trigger cluster role execution |
41
+ | GET/POST | `/api/oauth/callback` | `state` token | OAuth provider redirect target. Exchanges `code` for tokens, persists via `setAgentJobSecret(name, stored, 'oauth')`. |
package/api/index.js CHANGED
@@ -1,9 +1,11 @@
1
- import { createHash, timingSafeEqual } from 'crypto';
1
+ import { createHash, timingSafeEqual, randomUUID } from 'crypto';
2
2
  import { createAgentJob } from '../lib/tools/create-agent-job.js';
3
3
  import { setWebhook } from '../lib/tools/telegram.js';
4
4
  import { getAgentJobStatus, fetchAgentJobLog } from '../lib/tools/github.js';
5
5
  import { getTelegramAdapter } from '../lib/channels/index.js';
6
- import { chat, summarizeAgentJob } from '../lib/ai/index.js';
6
+ import { dispatchCommand, dispatchPreAuthCommand } from '../lib/channels/commands/index.js';
7
+ import { getByChannelChatId, setActiveThread } from '../lib/db/user-channels.js';
8
+ import { chat, chatStream, summarizeAgentJob } from '../lib/ai/index.js';
7
9
  import { createNotification } from '../lib/db/notifications.js';
8
10
  import { loadTriggers } from '../lib/triggers.js';
9
11
  import { verifyApiKey } from '../lib/db/api-keys.js';
@@ -209,29 +211,65 @@ async function handleTelegramWebhook(request) {
209
211
  }
210
212
 
211
213
  /**
212
- * Process a normalized message through the AI layer with channel UX.
213
- * Message persistence is handled centrally by the AI layer.
214
+ * Resolve the incoming channel message to a user, dispatch any slash command,
215
+ * and otherwise stream the message through the AI layer using the user's
216
+ * active session.
214
217
  */
215
218
  async function processChannelMessage(adapter, normalized) {
216
- await adapter.acknowledge(normalized.metadata);
217
- const stopIndicator = adapter.startProcessingIndicator(normalized.metadata);
219
+ const { channel, channelChatId, metadata } = normalized;
220
+ const binding = getByChannelChatId(channel, channelChatId);
221
+
222
+ // Unbound chat → only /verify is accepted; everything else is silently ignored.
223
+ if (!binding || !binding.verifiedAt) {
224
+ const result = await dispatchPreAuthCommand(normalized, { channel, channelChatId });
225
+ if (result?.handled) {
226
+ await adapter.sendResponse(channelChatId, result.reply, metadata);
227
+ }
228
+ return;
229
+ }
230
+
231
+ const ctx = { channel, channelChatId, userId: binding.userId };
232
+
233
+ // Post-auth slash commands short-circuit the AI path.
234
+ const cmd = await dispatchCommand(normalized, ctx);
235
+ if (cmd?.handled) {
236
+ await adapter.sendResponse(channelChatId, cmd.reply, metadata);
237
+ return;
238
+ }
239
+
240
+ await adapter.acknowledge(metadata);
241
+ const stopIndicator = adapter.startProcessingIndicator(metadata);
218
242
 
219
243
  try {
220
- const response = await chat(
221
- normalized.threadId,
222
- normalized.text,
223
- normalized.attachments,
224
- { userId: 'telegram', chatTitle: 'Telegram' }
225
- );
226
- await adapter.sendResponse(normalized.threadId, response, normalized.metadata);
244
+ let threadId = binding.activeThreadId;
245
+ if (!threadId) {
246
+ threadId = randomUUID();
247
+ setActiveThread(binding.userId, channel, threadId);
248
+ }
249
+
250
+ const envRepo = process.env.GH_OWNER && process.env.GH_REPO
251
+ ? `${process.env.GH_OWNER}/${process.env.GH_REPO}`
252
+ : '';
253
+ const streamOptions = {
254
+ userId: binding.userId,
255
+ chatTitle: 'Telegram',
256
+ repo: envRepo,
257
+ branch: 'main',
258
+ codeMode: false,
259
+ codeModeType: 'code',
260
+ };
261
+
262
+ if (adapter.streamChatResponse) {
263
+ const chunks = chatStream(threadId, normalized.text, normalized.attachments, streamOptions);
264
+ await adapter.streamChatResponse(channelChatId, chunks);
265
+ } else {
266
+ const response = await chat(threadId, normalized.text, normalized.attachments, streamOptions);
267
+ await adapter.sendResponse(channelChatId, response, metadata);
268
+ }
227
269
  } catch (err) {
228
270
  console.error('Failed to process message with AI:', err);
229
271
  await adapter
230
- .sendResponse(
231
- normalized.threadId,
232
- 'Sorry, I encountered an error processing your message.',
233
- normalized.metadata
234
- )
272
+ .sendResponse(channelChatId, 'Sorry, I encountered an error processing your message.', metadata)
235
273
  .catch(() => {});
236
274
  } finally {
237
275
  stopIndicator();
package/bin/CLAUDE.md CHANGED
@@ -8,20 +8,23 @@ Entry point: `cli.js` (invoked via `npx thepopebot <command>`).
8
8
  |---------|---------|
9
9
  | `init [--no-managed] [--no-install]` | Scaffold project from templates, sync managed files, create `.env`, install deps |
10
10
  | `setup` | Run interactive setup wizard (see `setup/CLAUDE.md`) |
11
- | `setup-telegram` | Reconfigure Telegram webhook |
11
+ | `setup-ssl` | Configure SSL with Let's Encrypt wildcard cert |
12
12
  | `upgrade [@beta\|version]` | Upgrade package, run init, rebuild, commit, push, restart Docker |
13
13
  | `reset [file]` | Restore a template file to defaults |
14
+ | `reset-all` | Nuclear reset — restore entire project to fresh init state |
15
+ | `audit` | Show project state vs. package templates (modified / missing / unknown) |
14
16
  | `diff [file]` | Show diff between user file and package template |
15
17
  | `reset-auth` | Regenerate `AUTH_SECRET` (invalidates all sessions) |
16
- | `set-var <KEY> [VALUE]` | Set GitHub repository variable |
18
+ | `set-var <KEY> [VALUE]` | Set GitHub repository variable (also reads piped stdin) |
17
19
  | `user:password <email>` | Change user password |
18
- | `sync <path>` | Dev helper — sync local package to test install |
20
+ | `sync <path>` | Dev helper — pack, build, upload local package to test install |
21
+ | `sync --fast <path>` | Fast variant — copy source into the running container and rebuild `.next` only |
19
22
 
20
23
  ## Managed Paths System
21
24
 
22
25
  `managed-paths.js` defines files auto-synced by `init`. These are overwritten on every init/upgrade — users should not edit them.
23
26
 
24
- **Managed paths**: `.github/workflows/`, `docker-compose.yml`, `.dockerignore`, `.gitignore`, `agent-job/CLAUDE.md`, `event-handler/CLAUDE.md`, `skills/CLAUDE.md`.
27
+ **Managed paths**: `.github/workflows/`, `docker-compose.yml`, `.dockerignore`, `.gitignore`.
25
28
 
26
29
  `isManaged(relPath)` — returns true if a path is managed (exact match or directory prefix).
27
30
 
package/bin/cli.js CHANGED
@@ -53,7 +53,6 @@ Commands:
53
53
  upgrade|update [@beta|version] Upgrade thepopebot (install, init, build, commit, push)
54
54
  setup Run interactive setup wizard
55
55
  setup-ssl Configure SSL with Let's Encrypt wildcard cert
56
- setup-telegram Reconfigure Telegram webhook
57
56
  reset-auth Regenerate AUTH_SECRET (invalidates all sessions)
58
57
  reset [file] Restore a template file (or list available templates)
59
58
  reset-all Nuclear reset — restore entire project to fresh init state
@@ -86,12 +85,12 @@ function getTemplateFiles(templatesDir) {
86
85
  return files;
87
86
  }
88
87
 
89
- async function init() {
88
+ async function init(options = {}) {
90
89
  let cwd = process.cwd();
91
90
  const packageDir = path.join(__dirname, '..');
92
91
  const templatesDir = path.join(packageDir, 'templates');
93
- const noManaged = args.includes('--no-managed');
94
- const noInstall = args.includes('--no-install');
92
+ const noManaged = options.noManaged ?? args.includes('--no-managed');
93
+ const noInstall = options.noInstall ?? args.includes('--no-install');
95
94
 
96
95
  // Guard: warn if the directory is not empty (unless it's an existing thepopebot project)
97
96
  const entries = fs.readdirSync(cwd);
@@ -256,7 +255,6 @@ async function init() {
256
255
  type: 'module',
257
256
  scripts: {
258
257
  setup: 'thepopebot setup',
259
- 'setup-telegram': 'thepopebot setup-telegram',
260
258
  'reset-auth': 'thepopebot reset-auth',
261
259
  },
262
260
  dependencies: {
@@ -277,33 +275,23 @@ async function init() {
277
275
  }
278
276
  }
279
277
 
280
- // Create default skill activation symlinks
281
- const defaultSkills = [];
282
- const activeDir = path.join(cwd, 'skills', 'active');
283
- fs.mkdirSync(activeDir, { recursive: true });
284
- for (const skill of defaultSkills) {
285
- const symlink = path.join(activeDir, skill);
286
- if (!fs.existsSync(symlink)) {
287
- createDirLink(`../library/${skill}`, symlink);
288
- console.log(` Created skills/active/${skill} ../library/${skill}`);
278
+ // Create agent skill bridge symlinks (point to ../skills)
279
+ const skillBridges = [
280
+ { dir: '.pi', name: 'Pi' },
281
+ { dir: '.claude', name: 'Claude' },
282
+ { dir: '.codex', name: 'Codex' },
283
+ { dir: '.gemini', name: 'Gemini' },
284
+ { dir: '.kimi', name: 'Kimi' },
285
+ ];
286
+ for (const { dir, name } of skillBridges) {
287
+ const link = path.join(cwd, dir, 'skills');
288
+ if (!fs.existsSync(link)) {
289
+ fs.mkdirSync(path.dirname(link), { recursive: true });
290
+ createDirLink('../skills', link);
291
+ console.log(` Created ${dir}/skills → ../skills`);
289
292
  }
290
293
  }
291
294
 
292
- // Create .pi/skills → ../skills/active symlink
293
- const piSkillsLink = path.join(cwd, '.pi', 'skills');
294
- if (!fs.existsSync(piSkillsLink)) {
295
- fs.mkdirSync(path.dirname(piSkillsLink), { recursive: true });
296
- createDirLink('../skills/active', piSkillsLink);
297
- console.log(' Created .pi/skills → ../skills/active');
298
- }
299
-
300
- // Create .claude/skills → ../skills/active symlink
301
- const claudeSkillsLink = path.join(cwd, '.claude', 'skills');
302
- if (!fs.existsSync(claudeSkillsLink)) {
303
- fs.mkdirSync(path.dirname(claudeSkillsLink), { recursive: true });
304
- createDirLink('../skills/active', claudeSkillsLink);
305
- console.log(' Created .claude/skills → ../skills/active');
306
- }
307
295
 
308
296
  // Report backed-up files
309
297
  if (backedUp.length > 0) {
@@ -420,7 +408,7 @@ function reset(filePath) {
420
408
  console.log(` ${destPath(file)}`);
421
409
  }
422
410
  console.log('\nUsage: thepopebot reset <file>');
423
- console.log('Example: thepopebot reset agent-job/SOUL.md\n');
411
+ console.log('Example: thepopebot reset agent-job/SYSTEM.md\n');
424
412
  return;
425
413
  }
426
414
 
@@ -502,7 +490,7 @@ function diff(filePath) {
502
490
  console.log(' All files match package templates.');
503
491
  }
504
492
  console.log('\nUsage: thepopebot diff <file>');
505
- console.log('Example: thepopebot diff agent-job/SOUL.md\n');
493
+ console.log('Example: thepopebot diff agent-job/SYSTEM.md\n');
506
494
  return;
507
495
  }
508
496
 
@@ -656,8 +644,12 @@ const PROTECTED_PATHS = [
656
644
  'package.json',
657
645
  'docker-compose.custom.yml',
658
646
  '.claude/',
647
+ '.codex/',
648
+ '.gemini/',
649
+ '.kimi/',
659
650
  '.pi/',
660
651
  'skills/',
652
+ 'agents/',
661
653
  'node_modules/',
662
654
  ];
663
655
 
@@ -749,10 +741,10 @@ async function resetAll() {
749
741
 
750
742
  console.log(`\n Moved ${filesToMove.length} file(s) to .backups/${ts}/`);
751
743
 
752
- // Run init to rebuild from templates
744
+ // Run init to rebuild from templates (call directly to use the same package version)
753
745
  console.log('\n Running init to rebuild project...\n');
754
746
  try {
755
- execSync('npx thepopebot init --no-install', { stdio: 'inherit', cwd });
747
+ await init({ noInstall: true });
756
748
  } catch {
757
749
  console.error('\n Init failed. Your backup is at .backups/' + ts + '/\n');
758
750
  process.exit(1);
@@ -789,15 +781,6 @@ function setupSsl() {
789
781
  }
790
782
  }
791
783
 
792
- function setupTelegram() {
793
- const setupScript = path.join(__dirname, '..', 'setup', 'setup-telegram.mjs');
794
- try {
795
- execFileSync(process.execPath, [setupScript], { stdio: 'inherit', cwd: process.cwd() });
796
- } catch {
797
- process.exit(1);
798
- }
799
- }
800
-
801
784
  async function resetAuth() {
802
785
  const { randomBytes } = await import('crypto');
803
786
  const { updateEnvVariable } = await import(path.join(__dirname, '..', 'setup', 'lib', 'auth.mjs'));
@@ -1072,9 +1055,6 @@ switch (command) {
1072
1055
  case 'setup-ssl':
1073
1056
  setupSsl();
1074
1057
  break;
1075
- case 'setup-telegram':
1076
- setupTelegram();
1077
- break;
1078
1058
  case 'reset-auth':
1079
1059
  await resetAuth();
1080
1060
  break;
package/config/CLAUDE.md CHANGED
@@ -1,9 +1,28 @@
1
- # config/ — Next.js Config Wrapper
1
+ # config/ — Next.js Config + Server Bootstrap
2
2
 
3
3
  ## Next.js Config Wrapper (index.js)
4
4
 
5
- `withThepopebot()` wraps user's `next.config.mjs`. Adds `transpilePackages` and `serverExternalPackages` for the npm package's dependencies that need special bundling.
5
+ `withThepopebot()` wraps the user's `next.config.mjs`. Adds `transpilePackages` and `serverExternalPackages` for npm-package dependencies that need special bundling (Drizzle, better-sqlite3, etc.).
6
6
 
7
- ## Instrumentation (instrumentation.js)
7
+ ## Instrumentation Hook (instrumentation.js)
8
8
 
9
- Server startup hook loaded by Next.js. Sequence: loads `.env`, initializes database, starts cron scheduler, starts cluster runtime. Skipped during `next build` (checks `NEXT_PHASE`).
9
+ Loaded by Next.js once on server start. The user's project re-exports it from their own `instrumentation.js`:
10
+
11
+ ```js
12
+ export { register } from 'thepopebot/instrumentation';
13
+ ```
14
+
15
+ ### Boot sequence
16
+
17
+ 1. **Skip during `next build`** — checks `process.argv` for `'build'` to avoid keeping the event loop alive during build output.
18
+ 2. **Load `.env`** — `dotenv.config()` from project root.
19
+ 3. **Default `AUTH_URL` from `APP_URL`** — so NextAuth redirects to the correct host on sign-out.
20
+ 4. **Validate `AUTH_SECRET`** — throws if unset (required for session encryption).
21
+ 5. **`initDatabase()`** — `lib/db/index.js`. Opens SQLite, runs Drizzle migrations.
22
+ 6. **`migrateEnvToDb()`** — `lib/db/config.js`. Idempotent first-run migration of `.env` values into the settings table.
23
+ 7. **`loadCrons()`** — `lib/cron.js`. Reads `agent-job/CRONS.json`, schedules user-defined crons.
24
+ 8. **`startBuiltinCrons()`** — `lib/cron.js`. Starts internal crons (e.g., npm version check). Then warms the in-memory update flag from `lib/db/update-check.js`.
25
+ 9. **`startClusterRuntime()`** — `lib/cluster/runtime.js`. Registers cluster role triggers (cron + file watch + webhook).
26
+ 10. **`startMaintenanceCron()`** — `lib/maintenance.js`. Hourly cleanup of expired agent-job API keys and other housekeeping.
27
+
28
+ `initialized` is module-scoped so the sequence runs exactly once even if `register()` is called more than once.
@@ -0,0 +1 @@
1
+ ALTER TABLE `code_workspaces` ADD `coding_agent` text;
@@ -0,0 +1,16 @@
1
+ CREATE TABLE `user_channels` (
2
+ `id` text PRIMARY KEY NOT NULL,
3
+ `user_id` text NOT NULL,
4
+ `channel` text NOT NULL,
5
+ `channel_chat_id` text,
6
+ `code` text,
7
+ `code_expires_at` integer,
8
+ `verified_at` integer,
9
+ `active_thread_id` text,
10
+ `created_at` integer NOT NULL,
11
+ `updated_at` integer NOT NULL
12
+ );
13
+ --> statement-breakpoint
14
+ CREATE UNIQUE INDEX `user_channels_user_channel_unique` ON `user_channels` (`user_id`,`channel`);--> statement-breakpoint
15
+ CREATE UNIQUE INDEX `user_channels_channel_chat_id_unique` ON `user_channels` (`channel`,`channel_chat_id`);--> statement-breakpoint
16
+ CREATE INDEX `user_channels_code_lookup` ON `user_channels` (`code`);
@@ -0,0 +1 @@
1
+ ALTER TABLE `code_workspaces` ADD `scope` text;