thepopebot 1.2.74-beta.9 → 1.2.74
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 +37 -38
- package/api/CLAUDE.md +6 -5
- package/api/index.js +93 -25
- package/bin/CLAUDE.md +37 -0
- package/bin/cli.js +9 -73
- package/bin/docker-build.js +94 -32
- package/bin/managed-paths.js +6 -0
- package/bin/sync.js +170 -76
- package/config/CLAUDE.md +19 -9
- package/config/instrumentation.js +4 -4
- package/drizzle/0016_majestic_metal_master.sql +1 -0
- package/drizzle/0017_smart_trauma.sql +1 -0
- package/drizzle/0018_aberrant_vertigo.sql +1 -0
- package/drizzle/0019_slimy_midnight.sql +1 -0
- package/drizzle/0020_natural_fabian_cortez.sql +1 -0
- package/drizzle/meta/0016_snapshot.json +616 -0
- package/drizzle/meta/0017_snapshot.json +624 -0
- package/drizzle/meta/0018_snapshot.json +632 -0
- package/drizzle/meta/0019_snapshot.json +640 -0
- package/drizzle/meta/0020_snapshot.json +632 -0
- package/drizzle/meta/_journal.json +35 -0
- package/lib/CLAUDE.md +2 -2
- package/lib/actions.js +4 -4
- package/lib/ai/CLAUDE.md +40 -17
- package/lib/ai/agent.js +33 -64
- package/lib/ai/headless-stream.js +55 -93
- package/lib/ai/index.js +116 -88
- package/lib/ai/line-mappers.js +393 -0
- package/lib/ai/model.js +43 -5
- package/lib/ai/tools.js +109 -309
- package/lib/auth/CLAUDE.md +8 -0
- package/lib/auth/components/login-form.js +1 -1
- package/lib/auth/components/login-form.jsx +1 -1
- package/lib/auth/components/setup-form.js +50 -1
- package/lib/auth/components/setup-form.jsx +57 -2
- package/lib/auth/middleware.js +1 -1
- package/lib/chat/CLAUDE.md +53 -0
- package/lib/chat/actions.js +468 -28
- package/lib/chat/api.js +340 -6
- package/lib/chat/components/CLAUDE.md +145 -0
- package/lib/chat/components/app-sidebar.js +11 -13
- package/lib/chat/components/app-sidebar.jsx +14 -17
- package/lib/chat/components/chat-header.js +15 -6
- package/lib/chat/components/chat-header.jsx +27 -8
- package/lib/chat/components/chat-input.js +140 -21
- package/lib/chat/components/chat-input.jsx +132 -25
- package/lib/chat/components/chat-page.js +10 -4
- package/lib/chat/components/chat-page.jsx +53 -45
- package/lib/chat/components/chat.js +254 -67
- package/lib/chat/components/chat.jsx +275 -81
- package/lib/chat/components/chats-page.js +32 -5
- package/lib/chat/components/chats-page.jsx +45 -6
- package/lib/chat/components/code-mode-toggle.js +311 -162
- package/lib/chat/components/code-mode-toggle.jsx +349 -198
- package/lib/chat/components/containers-page.js +542 -0
- package/lib/chat/components/containers-page.jsx +710 -0
- package/lib/chat/components/diff-viewer.js +138 -0
- package/lib/chat/components/diff-viewer.jsx +158 -0
- package/lib/chat/components/icons.js +88 -0
- package/lib/chat/components/icons.jsx +82 -0
- package/lib/chat/components/index.js +6 -4
- package/lib/chat/components/message.js +107 -27
- package/lib/chat/components/message.jsx +128 -30
- package/lib/chat/components/notifications-page.js +1 -1
- package/lib/chat/components/notifications-page.jsx +1 -1
- package/lib/chat/components/profile-page.js +1 -1
- package/lib/chat/components/profile-page.jsx +1 -1
- package/lib/chat/components/runners-page.js +1 -1
- package/lib/chat/components/runners-page.jsx +1 -1
- package/lib/chat/components/settings-chat-page.js +437 -294
- package/lib/chat/components/settings-chat-page.jsx +381 -243
- package/lib/chat/components/settings-coding-agents-page.js +767 -0
- package/lib/chat/components/settings-coding-agents-page.jsx +940 -0
- package/lib/chat/components/settings-general-page.js +189 -29
- package/lib/chat/components/settings-general-page.jsx +178 -14
- package/lib/chat/components/settings-github-page.js +103 -475
- package/lib/chat/components/settings-github-page.jsx +96 -462
- package/lib/chat/components/settings-jobs-page.js +527 -0
- package/lib/chat/components/settings-jobs-page.jsx +619 -0
- package/lib/chat/components/settings-layout.js +5 -6
- package/lib/chat/components/settings-layout.jsx +5 -6
- package/lib/chat/components/settings-secrets-layout.js +15 -6
- package/lib/chat/components/settings-secrets-layout.jsx +16 -6
- package/lib/chat/components/settings-secrets-page.js +23 -189
- package/lib/chat/components/settings-secrets-page.jsx +20 -195
- package/lib/chat/components/settings-shared.js +328 -0
- package/lib/chat/components/settings-shared.jsx +339 -0
- package/lib/chat/components/settings-users-page.js +22 -58
- package/lib/chat/components/settings-users-page.jsx +19 -57
- package/lib/chat/components/sidebar-history-item.js +5 -2
- package/lib/chat/components/sidebar-history-item.jsx +7 -2
- package/lib/chat/components/sidebar-history.js +9 -10
- package/lib/chat/components/sidebar-history.jsx +10 -10
- package/lib/chat/components/tool-call.js +2 -2
- package/lib/chat/components/tool-call.jsx +2 -2
- package/lib/chat/components/ui/combobox.js +12 -8
- package/lib/chat/components/ui/combobox.jsx +17 -9
- package/lib/chat/components/ui/dropdown-menu.js +52 -26
- package/lib/chat/components/ui/dropdown-menu.jsx +40 -18
- package/lib/chat/components/upgrade-dialog.js +3 -3
- package/lib/chat/components/upgrade-dialog.jsx +3 -3
- package/lib/chat/components/voice-bars.js +2 -2
- package/lib/chat/components/voice-bars.jsx +2 -2
- package/lib/cluster/CLAUDE.md +1 -1
- package/lib/cluster/actions.js +2 -2
- package/lib/cluster/components/cluster-console-page.js +3 -3
- package/lib/cluster/components/cluster-console-page.jsx +3 -3
- package/lib/cluster/components/cluster-logs-page.js +7 -37
- package/lib/cluster/components/cluster-logs-page.jsx +7 -40
- package/lib/cluster/components/cluster-page.js +82 -17
- package/lib/cluster/components/cluster-page.jsx +77 -16
- package/lib/cluster/components/clusters-page.js +13 -1
- package/lib/cluster/components/clusters-page.jsx +17 -3
- package/lib/cluster/execute.js +6 -3
- package/lib/cluster/runtime.js +2 -1
- package/lib/cluster/stream.js +22 -34
- package/lib/code/CLAUDE.md +13 -1
- package/lib/code/actions.js +741 -151
- package/lib/code/code-page.js +264 -104
- package/lib/code/code-page.jsx +279 -112
- package/lib/code/editor-view.js +662 -0
- package/lib/code/editor-view.jsx +749 -0
- package/lib/code/port-forwards.js +152 -0
- package/lib/code/terminal-sessions.js +4 -1
- package/lib/code/terminal-view.js +447 -35
- package/lib/code/terminal-view.jsx +434 -30
- package/lib/config.js +57 -15
- package/lib/containers/CLAUDE.md +9 -0
- package/lib/containers/logs.js +178 -0
- package/lib/containers/stream.js +72 -0
- package/lib/cron.js +1 -1
- package/lib/db/CLAUDE.md +36 -3
- package/lib/db/api-keys.js +38 -12
- package/lib/db/chats.js +3 -1
- package/lib/db/clusters.js +1 -0
- package/lib/db/code-workspaces.js +27 -3
- package/lib/db/config.js +136 -11
- package/lib/db/index.js +16 -6
- package/lib/db/oauth-tokens.js +157 -0
- package/lib/db/schema.js +4 -1
- package/lib/llm-providers.js +93 -10
- package/lib/oauth/helper.js +58 -0
- package/lib/oauth/providers.js +35 -0
- package/lib/paths.js +7 -9
- package/lib/tools/CLAUDE.md +7 -3
- package/lib/tools/create-agent-job.js +137 -0
- package/lib/tools/docker.js +541 -73
- package/lib/tools/github.js +40 -71
- package/lib/utils/random-name.js +58 -0
- package/lib/utils/render-md.js +2 -13
- package/lib/voice/use-voice-input.js +8 -3
- package/package.json +12 -1
- package/setup/CLAUDE.md +32 -0
- package/setup/lib/providers.mjs +11 -0
- package/setup/lib/targets.mjs +2 -1
- package/setup/setup.mjs +64 -460
- package/templates/.env.example +3 -0
- package/templates/.github/workflows/CLAUDE.md.template +16 -0
- package/templates/.github/workflows/auto-merge.yml +1 -1
- package/templates/.github/workflows/notify-pr-complete.yml +8 -8
- package/templates/.github/workflows/rebuild-event-handler.yml +9 -5
- package/templates/.github/workflows/upgrade-event-handler.yml +1 -1
- package/templates/.gitignore.template +9 -10
- package/templates/CLAUDE.md +1 -1
- package/templates/CLAUDE.md.template +130 -74
- package/templates/README.md +75 -0
- package/templates/config/CLAUDE.md.template +40 -0
- package/templates/config/agent-chat/SYSTEM.md +76 -0
- package/templates/config/code-chat/SYSTEM.md +10 -0
- package/templates/config/litellm/main.yaml +6 -0
- package/templates/cron/.gitkeep +0 -0
- package/templates/cron/CLAUDE.md.template +24 -0
- package/templates/docker-compose.custom.yml +1 -1
- package/templates/docker-compose.litellm.yml +81 -0
- package/templates/docker-compose.yml +1 -1
- package/templates/docs/CLAUDE.md.template +12 -0
- package/templates/docs/CLI.md +59 -0
- package/templates/docs/CLUSTERS.md +151 -0
- package/templates/docs/CONFIGURATION.md +181 -0
- package/templates/docs/CRONS_AND_TRIGGERS.md +132 -0
- package/templates/docs/GETTING_STARTED.md +64 -0
- package/templates/docs/SECURITY.md +61 -0
- package/templates/docs/SKILLS.md +113 -0
- package/templates/docs/UPGRADING.md +92 -0
- package/templates/logs/.gitkeep +0 -0
- package/templates/skills/CLAUDE.md.template +109 -0
- package/templates/skills/README.md +0 -2
- package/templates/skills/{llm-secrets → get-secret}/SKILL.md +2 -2
- package/templates/skills/{llm-secrets/llm-secrets.js → get-secret/get-secret.js} +7 -7
- package/templates/triggers/.gitkeep +0 -0
- package/templates/triggers/CLAUDE.md.template +41 -0
- package/lib/ai/web-search.js +0 -44
- package/lib/chat/components/features-context.js +0 -14
- package/lib/chat/components/features-context.jsx +0 -13
- package/lib/tools/create-job.js +0 -97
- package/templates/.github/workflows/notify-job-failed.yml +0 -64
- package/templates/.github/workflows/run-job.yml +0 -89
- package/templates/config/CODE_PLANNING.md +0 -14
- package/templates/config/JOB_PLANNING.md +0 -240
- package/templates/config/SKILL_BUILDING_GUIDE.md +0 -96
- package/templates/config/WEB_SEARCH_AVAILABLE.md +0 -5
- package/templates/config/WEB_SEARCH_UNAVAILABLE.md +0 -3
- package/templates/skills/modify-self/SKILL.md +0 -12
- /package/lib/{cluster → chat}/components/code-log-view.js +0 -0
- /package/lib/{cluster → chat}/components/code-log-view.jsx +0 -0
- /package/templates/config/{JOB_AGENT.md → agent-job/AGENT_JOB.md} +0 -0
- /package/templates/config/{SOUL.md → agent-job/SOUL.md} +0 -0
- /package/templates/config/{JOB_SUMMARY.md → agent-job/SUMMARY.md} +0 -0
- /package/templates/config/{CLUSTER_ROLE_PROMPT.md → cluster/ROLE.md} +0 -0
- /package/templates/config/{CLUSTER_SYSTEM_PROMPT.md → cluster/SYSTEM.md} +0 -0
package/README.md
CHANGED
|
@@ -22,36 +22,36 @@ Build autonomous AI agents that work for you 24/7, individually or in teams.
|
|
|
22
22
|
│ │
|
|
23
23
|
│ ┌─────────────────┐ ┌─────────────────┐ │
|
|
24
24
|
│ │ Event Handler │ ──1──► │ GitHub │ │
|
|
25
|
-
│ │
|
|
26
|
-
│ └────────▲────────┘
|
|
27
|
-
│ │
|
|
28
|
-
│ │
|
|
29
|
-
│ │
|
|
30
|
-
│
|
|
31
|
-
│
|
|
32
|
-
│
|
|
33
|
-
│
|
|
34
|
-
│
|
|
35
|
-
│ │
|
|
36
|
-
│ │
|
|
37
|
-
│ │
|
|
38
|
-
│
|
|
39
|
-
│
|
|
40
|
-
│
|
|
41
|
-
│
|
|
42
|
-
│
|
|
43
|
-
│ │
|
|
44
|
-
│ │
|
|
45
|
-
│ │
|
|
46
|
-
│ │
|
|
47
|
-
│ 5 (notify-pr-complete.yml
|
|
48
|
-
│ │
|
|
49
|
-
│
|
|
25
|
+
│ │ (creates branch)│ │(agent-job/* br) │ │
|
|
26
|
+
│ └────────▲────────┘ └─────────────────┘ │
|
|
27
|
+
│ │ │
|
|
28
|
+
│ │ 2 (launches Docker container locally) │
|
|
29
|
+
│ │ │
|
|
30
|
+
│ ▼ │
|
|
31
|
+
│ ┌─────────────────┐ │
|
|
32
|
+
│ │ Docker Agent │ │
|
|
33
|
+
│ │ (coding agent) │ │
|
|
34
|
+
│ └────────┬────────┘ │
|
|
35
|
+
│ │ │
|
|
36
|
+
│ │ 3 (commits, pushes, creates PR) │
|
|
37
|
+
│ │ │
|
|
38
|
+
│ ▼ │
|
|
39
|
+
│ ┌─────────────────┐ │
|
|
40
|
+
│ │ GitHub │ │
|
|
41
|
+
│ │ (PR opened) │ │
|
|
42
|
+
│ └────────┬────────┘ │
|
|
43
|
+
│ │ │
|
|
44
|
+
│ │ 4a (auto-merge.yml) │
|
|
45
|
+
│ │ 4b (rebuild-event-handler.yml) │
|
|
46
|
+
│ │ │
|
|
47
|
+
│ 5 (notify-pr-complete.yml → │
|
|
48
|
+
│ │ webhook to event handler) │
|
|
49
|
+
│ └──────────────────────────► Event Handler │
|
|
50
50
|
│ │
|
|
51
51
|
└──────────────────────────────────────────────────────────────────────┘
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
You interact with your bot via the web chat interface or Telegram (optional). The Event Handler creates
|
|
54
|
+
You interact with your bot via the web chat interface or Telegram (optional). The Event Handler creates an agent-job branch and launches a Docker container locally with the coding agent. The agent does the work, commits the results, pushes, and opens a PR. Auto-merge handles the rest. You get a notification when it's done.
|
|
55
55
|
|
|
56
56
|
---
|
|
57
57
|
|
|
@@ -94,19 +94,18 @@ npm run setup
|
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
The wizard walks you through everything:
|
|
97
|
-
- Checks prerequisites (Node.js, Git, GitHub CLI)
|
|
97
|
+
- Checks prerequisites (Node.js, Git, GitHub CLI, Docker)
|
|
98
98
|
- Creates a GitHub repository and pushes your initial commit
|
|
99
99
|
- Creates a GitHub Personal Access Token (scoped to your repo)
|
|
100
|
-
-
|
|
101
|
-
-
|
|
102
|
-
-
|
|
103
|
-
- Builds the project and starts Docker for you
|
|
100
|
+
- Configures your public URL and webhook secret
|
|
101
|
+
- Syncs settings to `.env`, database, and GitHub secrets/variables
|
|
102
|
+
- Starts Docker for you
|
|
104
103
|
|
|
105
104
|
**That's it.** Visit your APP_URL when the wizard finishes.
|
|
106
105
|
|
|
107
106
|
- **Web Chat**: Visit your APP_URL to chat with your agent, create jobs, upload files
|
|
108
107
|
- **Telegram** (optional): Run `npm run setup-telegram` to connect a Telegram bot
|
|
109
|
-
- **Webhook**: Send a POST to `/api/create-job` with your API key to create jobs programmatically
|
|
108
|
+
- **Webhook**: Send a POST to `/api/create-agent-job` with your API key to create jobs programmatically
|
|
110
109
|
- **Cron**: Edit `config/CRONS.json` to schedule recurring jobs
|
|
111
110
|
|
|
112
111
|
### Chat vs Agent LLM
|
|
@@ -135,7 +134,7 @@ claude setup-token
|
|
|
135
134
|
|
|
136
135
|
Paste the token (starts with `sk-ant-oat01-`) into the setup wizard. Your agent jobs will now run through your subscription. Note that usage counts toward your Claude.ai limits, and you still need an API key for the chat side.
|
|
137
136
|
|
|
138
|
-
See [
|
|
137
|
+
See [Coding Agents](docs/CODING_AGENTS.md) for details on all five agent backends.
|
|
139
138
|
|
|
140
139
|
> **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.
|
|
141
140
|
>
|
|
@@ -187,9 +186,9 @@ See [Security](docs/SECURITY.md) for full details on what's exposed, the risks,
|
|
|
187
186
|
|
|
188
187
|
## Different Models
|
|
189
188
|
|
|
190
|
-
|
|
189
|
+
thepopebot supports 9 built-in LLM providers (Anthropic, OpenAI, Google, DeepSeek, MiniMax, Mistral, xAI, Kimi, OpenRouter) plus custom OpenAI-compatible endpoints. The chat layer and coding agents are independent — use Claude for interactive chat and a different model for code tasks, or run everything on a single provider.
|
|
191
190
|
|
|
192
|
-
See [Different Models](docs/RUNNING_DIFFERENT_MODELS.md) for the full
|
|
191
|
+
See [Different Models](docs/RUNNING_DIFFERENT_MODELS.md) for the full provider reference, admin UI configuration, per-job overrides, and custom provider setup.
|
|
193
192
|
|
|
194
193
|
---
|
|
195
194
|
|
|
@@ -199,13 +198,13 @@ See [Different Models](docs/RUNNING_DIFFERENT_MODELS.md) for the full guide: Eve
|
|
|
199
198
|
|----------|-------------|
|
|
200
199
|
| [Architecture](docs/ARCHITECTURE.md) | Two-layer design, file structure, API endpoints, GitHub Actions, Docker agent |
|
|
201
200
|
| [CLI Reference](docs/CLI.md) | `init`, managed vs user files, template conventions, all CLI commands |
|
|
202
|
-
| [Configuration](docs/CONFIGURATION.md) |
|
|
201
|
+
| [Configuration](docs/CONFIGURATION.md) | Admin UI, DB-backed config, infrastructure variables, GitHub secrets, Docker Compose |
|
|
203
202
|
| [Customization](docs/CUSTOMIZATION.md) | Personality, skills, operating system files, using your bot, security details |
|
|
204
203
|
| [Chat Integrations](docs/CHAT_INTEGRATIONS.md) | Web chat, Telegram, adding new channels |
|
|
205
|
-
| [Different Models](docs/RUNNING_DIFFERENT_MODELS.md) |
|
|
204
|
+
| [Different Models](docs/RUNNING_DIFFERENT_MODELS.md) | 9 built-in LLM providers, chat vs coding agent config, per-job overrides, custom providers |
|
|
206
205
|
| [Auto-Merge](docs/AUTO_MERGE.md) | Auto-merge controls, ALLOWED_PATHS configuration |
|
|
207
206
|
| [Deployment](docs/DEPLOYMENT.md) | VPS setup, Docker Compose, HTTPS with Let's Encrypt |
|
|
208
|
-
| [
|
|
207
|
+
| [Coding Agents](docs/CODING_AGENTS.md) | 5 coding agent backends, OAuth tokens, LiteLLM proxy, per-agent config |
|
|
209
208
|
| [How to Build Skills](docs/HOW_TO_BUILD_SKILLS.md) | Guide to building and activating agent skills |
|
|
210
209
|
| [Pre-Release](docs/PRE_RELEASE.md) | Installing beta/alpha builds |
|
|
211
210
|
| [Code Workspaces](docs/CODE_WORKSPACES.md) | Interactive Docker containers with in-browser terminal |
|
package/api/CLAUDE.md
CHANGED
|
@@ -10,21 +10,22 @@ Auth flow: `x-api-key` header -> `verifyApiKey()` -> database lookup (hashed, ti
|
|
|
10
10
|
|
|
11
11
|
## Do NOT use these routes for browser UI
|
|
12
12
|
|
|
13
|
-
Browser-facing
|
|
13
|
+
Browser-facing data fetching uses **fetch route handlers** colocated with pages (`route.js` files in `web/app/`). These check `auth()` session — never use `/api` routes from the browser. Server actions (`'use server'`) are used only for **mutations** (rename, delete, star, config updates) — never for data fetching (causes page refresh issues). Handler implementations live in `lib/chat/api.js`; route files are thin re-exports.
|
|
14
14
|
|
|
15
15
|
| Caller | Mechanism | Auth |
|
|
16
16
|
|--------|-----------|------|
|
|
17
17
|
| External (cURL, GitHub Actions, Telegram) | `/api` route | `x-api-key` header |
|
|
18
|
-
| Browser UI (data
|
|
19
|
-
| Browser UI (
|
|
18
|
+
| Browser UI (data fetching) | Fetch route handler colocated with page | `auth()` session |
|
|
19
|
+
| Browser UI (mutations) | Server action | `requireAuth()` session |
|
|
20
|
+
| Browser UI (streaming) | `/stream/chat`, `/stream/containers`, `/stream/cluster/*/logs` | `auth()` session |
|
|
20
21
|
|
|
21
22
|
## Routes
|
|
22
23
|
|
|
23
24
|
| Method | Path | Auth | Handler |
|
|
24
25
|
|--------|------|------|---------|
|
|
25
26
|
| GET | `/api/ping` | None | Health check |
|
|
26
|
-
| POST | `/api/create-job` | `x-api-key` | Create agent job |
|
|
27
|
-
| GET | `/api/jobs/status` | `x-api-key` |
|
|
27
|
+
| POST | `/api/create-agent-job` | `x-api-key` | Create agent job |
|
|
28
|
+
| GET | `/api/agent-jobs/status` | `x-api-key` | Agent job status (query: `?agent_job_id=`) |
|
|
28
29
|
| POST | `/api/telegram/webhook` | Telegram webhook secret | Telegram message handler |
|
|
29
30
|
| POST | `/api/telegram/register` | `x-api-key` | Register bot token + webhook URL |
|
|
30
31
|
| POST | `/api/github/webhook` | GitHub webhook secret | GitHub event handler |
|
package/api/index.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { createHash, timingSafeEqual } from 'crypto';
|
|
2
|
-
import {
|
|
2
|
+
import { createAgentJob } from '../lib/tools/create-agent-job.js';
|
|
3
3
|
import { setWebhook } from '../lib/tools/telegram.js';
|
|
4
|
-
import {
|
|
4
|
+
import { getAgentJobStatus, fetchAgentJobLog } from '../lib/tools/github.js';
|
|
5
5
|
import { getTelegramAdapter } from '../lib/channels/index.js';
|
|
6
|
-
import { chat,
|
|
6
|
+
import { chat, summarizeAgentJob } from '../lib/ai/index.js';
|
|
7
7
|
import { createNotification } from '../lib/db/notifications.js';
|
|
8
8
|
import { loadTriggers } from '../lib/triggers.js';
|
|
9
9
|
import { verifyApiKey } from '../lib/db/api-keys.js';
|
|
10
10
|
import { getConfig } from '../lib/config.js';
|
|
11
|
+
import { parseOAuthState, exchangeCodeForToken } from '../lib/oauth/helper.js';
|
|
12
|
+
import { setAgentJobSecret } from '../lib/db/config.js';
|
|
11
13
|
|
|
12
14
|
// Bot token — resolved from DB/env, can be overridden by /telegram/register
|
|
13
15
|
let telegramBotToken = null;
|
|
@@ -31,7 +33,7 @@ function getFireTriggers() {
|
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
// Routes that have their own authentication
|
|
34
|
-
const PUBLIC_ROUTES = ['/telegram/webhook', '/github/webhook', '/ping'];
|
|
36
|
+
const PUBLIC_ROUTES = ['/telegram/webhook', '/github/webhook', '/ping', '/oauth/callback'];
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
39
|
* Timing-safe string comparison.
|
|
@@ -71,28 +73,34 @@ function checkAuth(routePath, request) {
|
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
/**
|
|
74
|
-
* Extract job ID from branch name (e.g., "job/abc123" -> "abc123")
|
|
76
|
+
* Extract agent job ID from branch name (e.g., "agent-job/abc123" -> "abc123")
|
|
75
77
|
*/
|
|
76
|
-
function
|
|
77
|
-
if (!branchName
|
|
78
|
-
return branchName.slice(
|
|
78
|
+
function extractAgentJobId(branchName) {
|
|
79
|
+
if (!branchName) return null;
|
|
80
|
+
if (branchName.startsWith('agent-job/')) return branchName.slice(10);
|
|
81
|
+
// Backwards compatibility with old job/ prefix
|
|
82
|
+
if (branchName.startsWith('job/')) return branchName.slice(4);
|
|
83
|
+
return null;
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
82
87
|
// Route handlers
|
|
83
88
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
84
89
|
|
|
85
|
-
async function
|
|
90
|
+
async function handleCreateAgentJob(request) {
|
|
86
91
|
const body = await request.json();
|
|
87
92
|
const { job } = body;
|
|
88
93
|
if (!job) return Response.json({ error: 'Missing job field' }, { status: 400 });
|
|
89
94
|
|
|
90
95
|
try {
|
|
91
|
-
const result = await
|
|
96
|
+
const result = await createAgentJob(job, {
|
|
97
|
+
llmModel: body.llm_model,
|
|
98
|
+
agentBackend: body.agent_backend,
|
|
99
|
+
});
|
|
92
100
|
return Response.json(result);
|
|
93
101
|
} catch (err) {
|
|
94
102
|
console.error(err);
|
|
95
|
-
return Response.json({ error: 'Failed to create job' }, { status: 500 });
|
|
103
|
+
return Response.json({ error: 'Failed to create agent job' }, { status: 500 });
|
|
96
104
|
}
|
|
97
105
|
}
|
|
98
106
|
|
|
@@ -168,14 +176,14 @@ async function handleGithubWebhook(request) {
|
|
|
168
176
|
}
|
|
169
177
|
|
|
170
178
|
const payload = await request.json();
|
|
171
|
-
const
|
|
172
|
-
if (!
|
|
179
|
+
const agentJobId = payload.agent_job_id || payload.job_id || extractAgentJobId(payload.branch);
|
|
180
|
+
if (!agentJobId) return Response.json({ ok: true, skipped: true, reason: 'not an agent job' });
|
|
173
181
|
|
|
174
182
|
try {
|
|
175
183
|
// Fetch log from repo via API (no longer sent in payload)
|
|
176
184
|
let log = payload.log || '';
|
|
177
185
|
if (!log) {
|
|
178
|
-
log = await
|
|
186
|
+
log = await fetchAgentJobLog(agentJobId, payload.commit_sha);
|
|
179
187
|
}
|
|
180
188
|
|
|
181
189
|
const results = {
|
|
@@ -189,10 +197,10 @@ async function handleGithubWebhook(request) {
|
|
|
189
197
|
commit_message: payload.commit_message || '',
|
|
190
198
|
};
|
|
191
199
|
|
|
192
|
-
const message = await
|
|
200
|
+
const message = await summarizeAgentJob(results);
|
|
193
201
|
await createNotification(message, payload);
|
|
194
202
|
|
|
195
|
-
console.log(`Notification saved for job ${
|
|
203
|
+
console.log(`Notification saved for agent-job ${agentJobId.slice(0, 8)}`);
|
|
196
204
|
|
|
197
205
|
return Response.json({ ok: true, notified: true });
|
|
198
206
|
} catch (err) {
|
|
@@ -201,18 +209,77 @@ async function handleGithubWebhook(request) {
|
|
|
201
209
|
}
|
|
202
210
|
}
|
|
203
211
|
|
|
204
|
-
async function
|
|
212
|
+
async function handleAgentJobStatus(request) {
|
|
205
213
|
try {
|
|
206
214
|
const url = new URL(request.url);
|
|
207
|
-
const
|
|
208
|
-
const result = await
|
|
215
|
+
const agentJobId = url.searchParams.get('agent_job_id') || url.searchParams.get('job_id');
|
|
216
|
+
const result = await getAgentJobStatus(agentJobId);
|
|
209
217
|
return Response.json(result);
|
|
210
218
|
} catch (err) {
|
|
211
|
-
console.error('Failed to get job status:', err);
|
|
212
|
-
return Response.json({ error: 'Failed to get job status' }, { status: 500 });
|
|
219
|
+
console.error('Failed to get agent job status:', err);
|
|
220
|
+
return Response.json({ error: 'Failed to get agent job status' }, { status: 500 });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function handleOAuthCallback(request) {
|
|
225
|
+
const url = new URL(request.url);
|
|
226
|
+
const code = url.searchParams.get('code');
|
|
227
|
+
const stateParam = url.searchParams.get('state');
|
|
228
|
+
const error = url.searchParams.get('error');
|
|
229
|
+
|
|
230
|
+
if (error) {
|
|
231
|
+
const desc = url.searchParams.get('error_description') || error;
|
|
232
|
+
return oauthResultPage(false, desc);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!code || !stateParam) {
|
|
236
|
+
return oauthResultPage(false, 'Missing code or state parameter.');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
const state = parseOAuthState(stateParam);
|
|
241
|
+
const redirectUri = `${process.env.AUTH_URL}/api/oauth/callback`;
|
|
242
|
+
|
|
243
|
+
const tokenData = await exchangeCodeForToken({
|
|
244
|
+
code,
|
|
245
|
+
clientId: state.clientId,
|
|
246
|
+
clientSecret: state.clientSecret,
|
|
247
|
+
tokenUrl: state.tokenUrl,
|
|
248
|
+
redirectUri,
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Save the full token JSON as the secret value
|
|
252
|
+
const tokenJson = JSON.stringify(tokenData);
|
|
253
|
+
setAgentJobSecret(state.secretName, tokenJson, 'oauth');
|
|
254
|
+
|
|
255
|
+
return oauthResultPage(true, state.secretName);
|
|
256
|
+
} catch (err) {
|
|
257
|
+
console.error('OAuth callback error:', err);
|
|
258
|
+
return oauthResultPage(false, err.message || 'Token exchange failed.');
|
|
213
259
|
}
|
|
214
260
|
}
|
|
215
261
|
|
|
262
|
+
function oauthResultPage(success, detail) {
|
|
263
|
+
const safe = String(detail).replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]);
|
|
264
|
+
const messagePayload = JSON.stringify({ type: success ? 'oauth-success' : 'oauth-error', detail: safe });
|
|
265
|
+
const fallback = success
|
|
266
|
+
? `Token saved as <strong>${safe}</strong>. You can close this tab and return to settings.`
|
|
267
|
+
: `Error: ${safe}`;
|
|
268
|
+
|
|
269
|
+
const html = `<!DOCTYPE html><html><head><title>OAuth ${success ? 'Success' : 'Error'}</title></head><body>
|
|
270
|
+
<script>
|
|
271
|
+
if (window.opener) {
|
|
272
|
+
window.opener.postMessage(${messagePayload}, window.location.origin);
|
|
273
|
+
window.close();
|
|
274
|
+
} else {
|
|
275
|
+
document.body.innerHTML = '<p style="font-family:sans-serif;padding:2rem;">${fallback.replace(/'/g, "\\'")}</p>';
|
|
276
|
+
}
|
|
277
|
+
</script>
|
|
278
|
+
<noscript><p style="font-family:sans-serif;padding:2rem;">${fallback}</p></noscript>
|
|
279
|
+
</body></html>`;
|
|
280
|
+
return new Response(html, { status: success ? 200 : 400, headers: { 'Content-Type': 'text/html' } });
|
|
281
|
+
}
|
|
282
|
+
|
|
216
283
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
217
284
|
// Next.js Route Handlers (catch-all)
|
|
218
285
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -247,7 +314,7 @@ async function POST(request) {
|
|
|
247
314
|
|
|
248
315
|
// Route to handler
|
|
249
316
|
switch (routePath) {
|
|
250
|
-
case '/create-job':
|
|
317
|
+
case '/create-agent-job': return handleCreateAgentJob(request);
|
|
251
318
|
case '/telegram/webhook': return handleTelegramWebhook(request);
|
|
252
319
|
case '/telegram/register': return handleTelegramRegister(request);
|
|
253
320
|
case '/github/webhook': return handleGithubWebhook(request);
|
|
@@ -264,9 +331,10 @@ async function GET(request) {
|
|
|
264
331
|
if (authError) return authError;
|
|
265
332
|
|
|
266
333
|
switch (routePath) {
|
|
267
|
-
case '/ping':
|
|
268
|
-
case '/jobs/status':
|
|
269
|
-
|
|
334
|
+
case '/ping': return Response.json({ message: 'Pong!' });
|
|
335
|
+
case '/agent-jobs/status': return handleAgentJobStatus(request);
|
|
336
|
+
case '/oauth/callback': return handleOAuthCallback(request);
|
|
337
|
+
default: return Response.json({ error: 'Not found' }, { status: 404 });
|
|
270
338
|
}
|
|
271
339
|
}
|
|
272
340
|
|
package/bin/CLAUDE.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# bin/ — CLI Tools
|
|
2
|
+
|
|
3
|
+
Entry point: `cli.js` (invoked via `npx thepopebot <command>`).
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
| Command | Purpose |
|
|
8
|
+
|---------|---------|
|
|
9
|
+
| `init [--no-managed] [--no-install]` | Scaffold project from templates, sync managed files, create `.env`, install deps |
|
|
10
|
+
| `setup` | Run interactive setup wizard (see `setup/CLAUDE.md`) |
|
|
11
|
+
| `setup-telegram` | Reconfigure Telegram webhook |
|
|
12
|
+
| `upgrade [@beta\|version]` | Upgrade package, run init, rebuild, commit, push, restart Docker |
|
|
13
|
+
| `reset [file]` | Restore a template file to defaults |
|
|
14
|
+
| `diff [file]` | Show diff between user file and package template |
|
|
15
|
+
| `reset-auth` | Regenerate `AUTH_SECRET` (invalidates all sessions) |
|
|
16
|
+
| `set-var <KEY> [VALUE]` | Set GitHub repository variable |
|
|
17
|
+
| `user:password <email>` | Change user password |
|
|
18
|
+
| `sync <path>` | Dev helper — sync local package to test install |
|
|
19
|
+
|
|
20
|
+
## Managed Paths System
|
|
21
|
+
|
|
22
|
+
`managed-paths.js` defines files auto-synced by `init`. These are overwritten on every init/upgrade — users should not edit them.
|
|
23
|
+
|
|
24
|
+
**Managed paths**: `.github/workflows/`, `docker-compose.yml`, `.dockerignore`, `.gitignore`, `CLAUDE.md`, `config/CLAUDE.md`, `skills/CLAUDE.md`, `cron/CLAUDE.md`, `triggers/CLAUDE.md`, `docs/CLAUDE.md`.
|
|
25
|
+
|
|
26
|
+
`isManaged(relPath)` — returns true if a path is managed (exact match or directory prefix).
|
|
27
|
+
|
|
28
|
+
## Template Processing
|
|
29
|
+
|
|
30
|
+
- Templates live in `templates/` with optional `.template` suffix
|
|
31
|
+
- `.template` suffix is stripped when copying to the user project (e.g., `CLAUDE.md.template` → `CLAUDE.md`)
|
|
32
|
+
- Managed files are deleted from user projects if removed from templates
|
|
33
|
+
- Non-managed template files are only created, never overwritten
|
|
34
|
+
|
|
35
|
+
## docker-build.js
|
|
36
|
+
|
|
37
|
+
Builds the event-handler Docker image locally. Used by `docker-compose.yml` build step. Bakes the npm package, `web/` source, and `.next` build output into the image.
|
package/bin/cli.js
CHANGED
|
@@ -57,8 +57,6 @@ Commands:
|
|
|
57
57
|
reset [file] Restore a template file (or list available templates)
|
|
58
58
|
diff [file] Show differences between project files and package templates
|
|
59
59
|
sync <path> Sync local package to a test install (build, pack, Docker)
|
|
60
|
-
set-agent-secret <KEY> [VALUE] Set a GitHub secret with AGENT_ prefix (also updates .env)
|
|
61
|
-
set-agent-llm-secret <KEY> [VALUE] Set a GitHub secret with AGENT_LLM_ prefix
|
|
62
60
|
set-var <KEY> [VALUE] Set a GitHub repository variable
|
|
63
61
|
user:password <email> Change a user's password
|
|
64
62
|
`);
|
|
@@ -89,6 +87,7 @@ async function init() {
|
|
|
89
87
|
const packageDir = path.join(__dirname, '..');
|
|
90
88
|
const templatesDir = path.join(packageDir, 'templates');
|
|
91
89
|
const noManaged = args.includes('--no-managed');
|
|
90
|
+
const noInstall = args.includes('--no-install');
|
|
92
91
|
|
|
93
92
|
// Guard: warn if the directory is not empty (unless it's an existing thepopebot project)
|
|
94
93
|
const entries = fs.readdirSync(cwd);
|
|
@@ -262,18 +261,8 @@ async function init() {
|
|
|
262
261
|
console.log(' Skipped package.json (already exists)');
|
|
263
262
|
}
|
|
264
263
|
|
|
265
|
-
// Create .gitkeep files for empty dirs
|
|
266
|
-
const gitkeepDirs = ['cron', 'triggers', 'logs', 'tmp', 'data', 'data/clusters'];
|
|
267
|
-
for (const dir of gitkeepDirs) {
|
|
268
|
-
const gitkeep = path.join(cwd, dir, '.gitkeep');
|
|
269
|
-
if (!fs.existsSync(gitkeep)) {
|
|
270
|
-
fs.mkdirSync(path.join(cwd, dir), { recursive: true });
|
|
271
|
-
fs.writeFileSync(gitkeep, '');
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
264
|
// Create default skill activation symlinks
|
|
276
|
-
const defaultSkills = ['
|
|
265
|
+
const defaultSkills = ['get-secret'];
|
|
277
266
|
const activeDir = path.join(cwd, 'skills', 'active');
|
|
278
267
|
fs.mkdirSync(activeDir, { recursive: true });
|
|
279
268
|
for (const skill of defaultSkills) {
|
|
@@ -325,9 +314,11 @@ async function init() {
|
|
|
325
314
|
console.log(' To reset to default: npx thepopebot reset <file>');
|
|
326
315
|
}
|
|
327
316
|
|
|
328
|
-
// Run npm install
|
|
329
|
-
|
|
330
|
-
|
|
317
|
+
// Run npm install to ensure lock file reflects installed dependencies
|
|
318
|
+
if (!noInstall) {
|
|
319
|
+
console.log('\nInstalling dependencies...\n');
|
|
320
|
+
execSync('npm install', { stdio: 'inherit', cwd });
|
|
321
|
+
}
|
|
331
322
|
|
|
332
323
|
// Create or update .env with auto-generated infrastructure values
|
|
333
324
|
const envPath = path.join(cwd, '.env');
|
|
@@ -343,6 +334,7 @@ async function init() {
|
|
|
343
334
|
|
|
344
335
|
AUTH_SECRET=${authSecret}
|
|
345
336
|
AUTH_TRUST_HOST=true
|
|
337
|
+
DATABASE_PATH=data/db/thepopebot.sqlite
|
|
346
338
|
THEPOPEBOT_VERSION=${version}
|
|
347
339
|
|
|
348
340
|
# Uncomment to use a custom docker-compose file that won't be overwritten by upgrades.
|
|
@@ -641,7 +633,7 @@ async function upgrade() {
|
|
|
641
633
|
const running = execSync('docker compose ps --status running -q', { encoding: 'utf8', cwd }).trim();
|
|
642
634
|
if (running) {
|
|
643
635
|
console.log(' Pulling new image and restarting Docker containers...\n');
|
|
644
|
-
execSync('docker compose pull event-handler && docker compose up -d', { stdio: 'inherit', cwd });
|
|
636
|
+
execSync('docker compose pull event-handler && docker compose up -d --force-recreate event-handler', { stdio: 'inherit', cwd });
|
|
645
637
|
}
|
|
646
638
|
} catch {
|
|
647
639
|
// Docker not available or not running — skip
|
|
@@ -717,56 +709,6 @@ async function promptForValue(key) {
|
|
|
717
709
|
return value;
|
|
718
710
|
}
|
|
719
711
|
|
|
720
|
-
async function setAgentSecret(key, value) {
|
|
721
|
-
if (!key) {
|
|
722
|
-
console.error('\n Usage: thepopebot set-agent-secret <KEY> [VALUE]\n');
|
|
723
|
-
console.error(' Example: thepopebot set-agent-secret ANTHROPIC_API_KEY\n');
|
|
724
|
-
process.exit(1);
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
if (!value) value = await promptForValue(key);
|
|
728
|
-
|
|
729
|
-
const { owner, repo } = loadRepoInfo();
|
|
730
|
-
const prefixedName = `AGENT_${key}`;
|
|
731
|
-
|
|
732
|
-
const { setSecret } = await import(path.join(__dirname, '..', 'setup', 'lib', 'github.mjs'));
|
|
733
|
-
const { updateEnvVariable } = await import(path.join(__dirname, '..', 'setup', 'lib', 'auth.mjs'));
|
|
734
|
-
|
|
735
|
-
const result = await setSecret(owner, repo, prefixedName, value);
|
|
736
|
-
if (result.success) {
|
|
737
|
-
console.log(`\n Set GitHub secret: ${prefixedName}`);
|
|
738
|
-
updateEnvVariable(key, value);
|
|
739
|
-
console.log(` Updated .env: ${key}`);
|
|
740
|
-
console.log('');
|
|
741
|
-
} else {
|
|
742
|
-
console.error(`\n Failed to set ${prefixedName}: ${result.error}\n`);
|
|
743
|
-
process.exit(1);
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
async function setAgentLlmSecret(key, value) {
|
|
748
|
-
if (!key) {
|
|
749
|
-
console.error('\n Usage: thepopebot set-agent-llm-secret <KEY> [VALUE]\n');
|
|
750
|
-
console.error(' Example: thepopebot set-agent-llm-secret BRAVE_API_KEY\n');
|
|
751
|
-
process.exit(1);
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
if (!value) value = await promptForValue(key);
|
|
755
|
-
|
|
756
|
-
const { owner, repo } = loadRepoInfo();
|
|
757
|
-
const prefixedName = `AGENT_LLM_${key}`;
|
|
758
|
-
|
|
759
|
-
const { setSecret } = await import(path.join(__dirname, '..', 'setup', 'lib', 'github.mjs'));
|
|
760
|
-
|
|
761
|
-
const result = await setSecret(owner, repo, prefixedName, value);
|
|
762
|
-
if (result.success) {
|
|
763
|
-
console.log(`\n Set GitHub secret: ${prefixedName}\n`);
|
|
764
|
-
} else {
|
|
765
|
-
console.error(`\n Failed to set ${prefixedName}: ${result.error}\n`);
|
|
766
|
-
process.exit(1);
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
|
|
770
712
|
async function setVar(key, value) {
|
|
771
713
|
if (!key) {
|
|
772
714
|
console.error('\n Usage: thepopebot set-var <KEY> [VALUE]\n');
|
|
@@ -849,12 +791,6 @@ switch (command) {
|
|
|
849
791
|
await sync(args[0]);
|
|
850
792
|
break;
|
|
851
793
|
}
|
|
852
|
-
case 'set-agent-secret':
|
|
853
|
-
await setAgentSecret(args[0], args[1]);
|
|
854
|
-
break;
|
|
855
|
-
case 'set-agent-llm-secret':
|
|
856
|
-
await setAgentLlmSecret(args[0], args[1]);
|
|
857
|
-
break;
|
|
858
794
|
case 'set-var':
|
|
859
795
|
await setVar(args[0], args[1]);
|
|
860
796
|
break;
|