thepopebot 1.2.76-beta.2 → 1.2.76-beta.20
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 +11 -4
- package/api/index.js +56 -18
- package/bin/CLAUDE.md +7 -3
- package/bin/cli.js +25 -31
- package/config/CLAUDE.md +23 -4
- package/drizzle/0021_coding_agent_workspace.sql +1 -0
- package/drizzle/0022_organic_apocalypse.sql +16 -0
- package/drizzle/0023_needy_ender_wiggin.sql +1 -0
- package/drizzle/meta/0021_snapshot.json +639 -0
- package/drizzle/meta/0022_snapshot.json +743 -0
- package/drizzle/meta/0023_snapshot.json +750 -0
- package/drizzle/meta/_journal.json +21 -0
- package/lib/CLAUDE.md +2 -2
- package/lib/actions.js +9 -1
- package/lib/ai/CLAUDE.md +72 -57
- package/lib/ai/helper-llm.js +108 -0
- package/lib/ai/index.js +308 -438
- package/lib/ai/line-mappers.js +42 -24
- package/lib/ai/scope.js +26 -0
- package/lib/ai/sdk-adapters/CLAUDE.md +114 -0
- package/lib/ai/sdk-adapters/claude-code.js +120 -8
- package/lib/ai/system-prompt.js +34 -0
- package/lib/ai/workspace-setup.js +19 -35
- package/lib/channels/CLAUDE.md +14 -4
- package/lib/channels/base.js +6 -2
- package/lib/channels/commands/index.js +42 -0
- package/lib/channels/commands/session.js +53 -0
- package/lib/channels/commands/verify.js +18 -0
- package/lib/channels/telegram.js +79 -28
- package/lib/chat/CLAUDE.md +4 -4
- package/lib/chat/actions.js +268 -48
- package/lib/chat/api.js +185 -31
- package/lib/chat/components/CLAUDE.md +6 -2
- package/lib/chat/components/chat-input.js +77 -47
- package/lib/chat/components/chat-input.jsx +77 -40
- package/lib/chat/components/chat-page.js +2 -0
- package/lib/chat/components/chat-page.jsx +3 -0
- package/lib/chat/components/chat.js +62 -14
- package/lib/chat/components/chat.jsx +68 -10
- package/lib/chat/components/code-mode-toggle.js +141 -22
- package/lib/chat/components/code-mode-toggle.jsx +129 -20
- package/lib/chat/components/containers-page.js +58 -40
- package/lib/chat/components/containers-page.jsx +64 -25
- package/lib/chat/components/crons-page.js +17 -3
- package/lib/chat/components/crons-page.jsx +34 -6
- package/lib/chat/components/index.js +2 -2
- package/lib/chat/components/message.js +18 -3
- package/lib/chat/components/message.jsx +18 -3
- package/lib/chat/components/profile-page.js +182 -4
- package/lib/chat/components/profile-page.jsx +196 -1
- package/lib/chat/components/scope-picker.js +21 -0
- package/lib/chat/components/scope-picker.jsx +27 -0
- package/lib/chat/components/settings-chat-page.js +11 -11
- package/lib/chat/components/settings-chat-page.jsx +14 -18
- package/lib/chat/components/settings-coding-agents-page.js +109 -15
- package/lib/chat/components/settings-coding-agents-page.jsx +85 -1
- package/lib/chat/components/settings-secrets-layout.js +1 -1
- package/lib/chat/components/settings-secrets-layout.jsx +1 -1
- package/lib/chat/components/settings-secrets-page.js +180 -75
- package/lib/chat/components/settings-secrets-page.jsx +212 -66
- package/lib/chat/components/triggers-page.js +17 -3
- package/lib/chat/components/triggers-page.jsx +34 -6
- package/lib/chat/components/ui/combobox.js +18 -2
- package/lib/chat/components/ui/combobox.jsx +17 -1
- package/lib/chat/components/ui/dropdown-menu.js +23 -2
- package/lib/chat/components/ui/dropdown-menu.jsx +27 -2
- package/lib/chat/telegram-profile.js +33 -0
- package/lib/cluster/CLAUDE.md +9 -3
- package/lib/code/CLAUDE.md +11 -3
- package/lib/code/actions.js +47 -8
- package/lib/code/terminal-view.js +31 -21
- package/lib/code/terminal-view.jsx +32 -23
- package/lib/config.js +15 -4
- package/lib/containers/CLAUDE.md +16 -6
- package/lib/db/CLAUDE.md +5 -2
- package/lib/db/chats.js +9 -17
- package/lib/db/code-workspaces.js +8 -3
- package/lib/db/config.js +0 -1
- package/lib/db/index.js +12 -0
- package/lib/db/schema.js +24 -1
- package/lib/db/user-channels.js +129 -0
- package/lib/llm-providers.js +8 -0
- package/lib/maintenance.js +31 -21
- package/lib/tools/CLAUDE.md +12 -3
- package/lib/tools/assemblyai.js +17 -0
- package/lib/tools/create-agent-job.js +12 -8
- package/lib/tools/docker.js +34 -10
- package/lib/tools/github.js +34 -0
- package/lib/tools/telegram.js +106 -0
- package/lib/utils/render-md.js +44 -18
- package/package.json +8 -8
- package/setup/CLAUDE.md +11 -5
- package/setup/lib/providers.mjs +2 -1
- package/setup/lib/targets.mjs +1 -3
- package/setup/lib/telegram.mjs +8 -69
- package/setup/setup-telegram.mjs +5 -36
- package/templates/.env.example +0 -7
- package/templates/.github/workflows/rebuild-event-handler.yml +1 -1
- package/templates/.gitignore.template +1 -3
- package/templates/CLAUDE.md +1 -1
- package/templates/CLAUDE.md.template +29 -7
- package/templates/agent-job/CLAUDE.md.template +5 -3
- package/templates/agent-job/CRONS.json +16 -0
- package/templates/agent-job/SYSTEM.md +16 -11
- package/templates/agents/CLAUDE.md.template +17 -17
- package/templates/coding-workspace/CLAUDE.md.template +7 -0
- package/templates/data/CLAUDE.md.template +1 -1
- package/templates/docker-compose.custom.yml +1 -0
- package/templates/docker-compose.yml +1 -0
- package/templates/event-handler/CLAUDE.md.template +79 -0
- package/templates/event-handler/TRIGGERS.json +18 -2
- package/templates/skills/CLAUDE.md.template +20 -22
- package/templates/skills/{library/agent-job-secrets → agent-job-secrets}/SKILL.md +2 -2
- package/lib/ai/agent.js +0 -65
- package/lib/ai/async-channel.js +0 -51
- package/lib/ai/model.js +0 -130
- package/lib/ai/tools.js +0 -164
- package/lib/tools/openai.js +0 -37
- package/setup/lib/telegram-verify.mjs +0 -63
- package/templates/agent-job/SOUL.md +0 -17
- /package/templates/{skills/active/.gitkeep → coding-workspace/SYSTEM.md} +0 -0
- /package/templates/skills/{library/agent-job-secrets → agent-job-secrets}/agent-job-secrets.js +0 -0
- /package/templates/skills/{library/playwright-cli → playwright-cli}/SKILL.md +0 -0
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
|
-
|
|
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
|
-
|
|
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` | `
|
|
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 {
|
|
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
|
-
*
|
|
213
|
-
*
|
|
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
|
-
|
|
217
|
-
const
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
@@ -9,19 +9,23 @@ Entry point: `cli.js` (invoked via `npx thepopebot <command>`).
|
|
|
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
11
|
| `setup-telegram` | Reconfigure Telegram webhook |
|
|
12
|
+
| `setup-ssl` | Configure SSL with Let's Encrypt wildcard cert |
|
|
12
13
|
| `upgrade [@beta\|version]` | Upgrade package, run init, rebuild, commit, push, restart Docker |
|
|
13
14
|
| `reset [file]` | Restore a template file to defaults |
|
|
15
|
+
| `reset-all` | Nuclear reset — restore entire project to fresh init state |
|
|
16
|
+
| `audit` | Show project state vs. package templates (modified / missing / unknown) |
|
|
14
17
|
| `diff [file]` | Show diff between user file and package template |
|
|
15
18
|
| `reset-auth` | Regenerate `AUTH_SECRET` (invalidates all sessions) |
|
|
16
|
-
| `set-var <KEY> [VALUE]` | Set GitHub repository variable |
|
|
19
|
+
| `set-var <KEY> [VALUE]` | Set GitHub repository variable (also reads piped stdin) |
|
|
17
20
|
| `user:password <email>` | Change user password |
|
|
18
|
-
| `sync <path>` | Dev helper —
|
|
21
|
+
| `sync <path>` | Dev helper — pack, build, upload local package to test install |
|
|
22
|
+
| `sync --fast <path>` | Fast variant — copy source into the running container and rebuild `.next` only |
|
|
19
23
|
|
|
20
24
|
## Managed Paths System
|
|
21
25
|
|
|
22
26
|
`managed-paths.js` defines files auto-synced by `init`. These are overwritten on every init/upgrade — users should not edit them.
|
|
23
27
|
|
|
24
|
-
**Managed paths**: `.github/workflows/`, `docker-compose.yml`, `.dockerignore`, `.gitignore
|
|
28
|
+
**Managed paths**: `.github/workflows/`, `docker-compose.yml`, `.dockerignore`, `.gitignore`.
|
|
25
29
|
|
|
26
30
|
`isManaged(relPath)` — returns true if a path is managed (exact match or directory prefix).
|
|
27
31
|
|
package/bin/cli.js
CHANGED
|
@@ -86,12 +86,12 @@ function getTemplateFiles(templatesDir) {
|
|
|
86
86
|
return files;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
async function init() {
|
|
89
|
+
async function init(options = {}) {
|
|
90
90
|
let cwd = process.cwd();
|
|
91
91
|
const packageDir = path.join(__dirname, '..');
|
|
92
92
|
const templatesDir = path.join(packageDir, 'templates');
|
|
93
|
-
const noManaged = args.includes('--no-managed');
|
|
94
|
-
const noInstall = args.includes('--no-install');
|
|
93
|
+
const noManaged = options.noManaged ?? args.includes('--no-managed');
|
|
94
|
+
const noInstall = options.noInstall ?? args.includes('--no-install');
|
|
95
95
|
|
|
96
96
|
// Guard: warn if the directory is not empty (unless it's an existing thepopebot project)
|
|
97
97
|
const entries = fs.readdirSync(cwd);
|
|
@@ -277,33 +277,23 @@ async function init() {
|
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
// Create
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
280
|
+
// Create agent skill bridge symlinks (point to ../skills)
|
|
281
|
+
const skillBridges = [
|
|
282
|
+
{ dir: '.pi', name: 'Pi' },
|
|
283
|
+
{ dir: '.claude', name: 'Claude' },
|
|
284
|
+
{ dir: '.codex', name: 'Codex' },
|
|
285
|
+
{ dir: '.gemini', name: 'Gemini' },
|
|
286
|
+
{ dir: '.kimi', name: 'Kimi' },
|
|
287
|
+
];
|
|
288
|
+
for (const { dir, name } of skillBridges) {
|
|
289
|
+
const link = path.join(cwd, dir, 'skills');
|
|
290
|
+
if (!fs.existsSync(link)) {
|
|
291
|
+
fs.mkdirSync(path.dirname(link), { recursive: true });
|
|
292
|
+
createDirLink('../skills', link);
|
|
293
|
+
console.log(` Created ${dir}/skills → ../skills`);
|
|
289
294
|
}
|
|
290
295
|
}
|
|
291
296
|
|
|
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
297
|
|
|
308
298
|
// Report backed-up files
|
|
309
299
|
if (backedUp.length > 0) {
|
|
@@ -420,7 +410,7 @@ function reset(filePath) {
|
|
|
420
410
|
console.log(` ${destPath(file)}`);
|
|
421
411
|
}
|
|
422
412
|
console.log('\nUsage: thepopebot reset <file>');
|
|
423
|
-
console.log('Example: thepopebot reset agent-job/
|
|
413
|
+
console.log('Example: thepopebot reset agent-job/SYSTEM.md\n');
|
|
424
414
|
return;
|
|
425
415
|
}
|
|
426
416
|
|
|
@@ -502,7 +492,7 @@ function diff(filePath) {
|
|
|
502
492
|
console.log(' All files match package templates.');
|
|
503
493
|
}
|
|
504
494
|
console.log('\nUsage: thepopebot diff <file>');
|
|
505
|
-
console.log('Example: thepopebot diff agent-job/
|
|
495
|
+
console.log('Example: thepopebot diff agent-job/SYSTEM.md\n');
|
|
506
496
|
return;
|
|
507
497
|
}
|
|
508
498
|
|
|
@@ -656,8 +646,12 @@ const PROTECTED_PATHS = [
|
|
|
656
646
|
'package.json',
|
|
657
647
|
'docker-compose.custom.yml',
|
|
658
648
|
'.claude/',
|
|
649
|
+
'.codex/',
|
|
650
|
+
'.gemini/',
|
|
651
|
+
'.kimi/',
|
|
659
652
|
'.pi/',
|
|
660
653
|
'skills/',
|
|
654
|
+
'agents/',
|
|
661
655
|
'node_modules/',
|
|
662
656
|
];
|
|
663
657
|
|
|
@@ -749,10 +743,10 @@ async function resetAll() {
|
|
|
749
743
|
|
|
750
744
|
console.log(`\n Moved ${filesToMove.length} file(s) to .backups/${ts}/`);
|
|
751
745
|
|
|
752
|
-
// Run init to rebuild from templates
|
|
746
|
+
// Run init to rebuild from templates (call directly to use the same package version)
|
|
753
747
|
console.log('\n Running init to rebuild project...\n');
|
|
754
748
|
try {
|
|
755
|
-
|
|
749
|
+
await init({ noInstall: true });
|
|
756
750
|
} catch {
|
|
757
751
|
console.error('\n Init failed. Your backup is at .backups/' + ts + '/\n');
|
|
758
752
|
process.exit(1);
|
package/config/CLAUDE.md
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
|
-
# config/ — Next.js Config
|
|
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
|
|
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
|
-
|
|
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;
|