thepopebot 1.2.75-beta.2 → 1.2.75-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.
- package/README.md +1 -1
- package/api/CLAUDE.md +1 -1
- package/api/index.js +5 -12
- package/bin/CLAUDE.md +1 -1
- package/bin/cli.js +329 -14
- package/bin/docker-build.js +5 -0
- package/bin/managed-paths.js +0 -7
- package/bin/sync.js +84 -0
- package/config/CLAUDE.md +1 -29
- package/config/instrumentation.js +1 -1
- package/lib/CLAUDE.md +3 -3
- package/lib/ai/CLAUDE.md +24 -3
- package/lib/ai/agent.js +8 -5
- package/lib/ai/async-channel.js +51 -0
- package/lib/ai/headless-stream.js +3 -0
- package/lib/ai/index.js +149 -173
- package/lib/ai/line-mappers.js +72 -9
- package/lib/ai/tools.js +40 -28
- package/lib/chat/actions.js +34 -6
- package/lib/chat/api.js +17 -1
- package/lib/chat/components/chat-header.js +4 -0
- package/lib/chat/components/chat-header.jsx +4 -0
- package/lib/chat/components/chat-input.js +1 -0
- package/lib/chat/components/chat-input.jsx +1 -0
- package/lib/chat/components/chat.js +9 -1
- package/lib/chat/components/chat.jsx +15 -2
- package/lib/chat/components/chats-page.js +3 -3
- package/lib/chat/components/chats-page.jsx +4 -6
- package/lib/chat/components/crons-page.js +1 -1
- package/lib/chat/components/crons-page.jsx +1 -1
- package/lib/chat/components/message.js +12 -4
- package/lib/chat/components/message.jsx +17 -4
- package/lib/chat/components/settings-chat-page.js +2 -1
- package/lib/chat/components/settings-chat-page.jsx +4 -1
- package/lib/chat/components/settings-coding-agents-page.js +139 -1
- package/lib/chat/components/settings-coding-agents-page.jsx +160 -0
- package/lib/chat/components/settings-jobs-page.js +13 -2
- package/lib/chat/components/settings-jobs-page.jsx +15 -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/sidebar-history-item.js +3 -3
- package/lib/chat/components/sidebar-history-item.jsx +4 -6
- package/lib/chat/components/triggers-page.js +1 -1
- package/lib/chat/components/triggers-page.jsx +1 -1
- package/lib/cluster/actions.js +4 -4
- package/lib/cluster/execute.js +3 -1
- package/lib/code/actions.js +34 -11
- package/lib/code/code-page.js +40 -40
- package/lib/code/code-page.jsx +36 -36
- package/lib/code/port-forwards.js +17 -3
- package/lib/code/terminal-view.js +16 -0
- package/lib/code/terminal-view.jsx +18 -0
- package/lib/config.js +4 -0
- package/lib/cron.js +3 -3
- package/lib/db/api-keys.js +22 -61
- package/lib/db/config.js +23 -0
- package/lib/db/index.js +3 -1
- package/lib/maintenance.js +34 -11
- package/lib/paths.js +1 -38
- package/lib/tools/create-agent-job.js +0 -4
- package/lib/tools/docker.js +23 -16
- package/lib/triggers.js +4 -3
- package/lib/utils/render-md.js +3 -1
- package/package.json +2 -1
- package/setup/setup-ssl.mjs +414 -0
- package/templates/.github/workflows/rebuild-event-handler.yml +3 -0
- package/templates/.github/workflows/upgrade-event-handler.yml +1 -1
- package/templates/.gitignore.template +7 -3
- package/templates/.tmp/CLAUDE.md.template +5 -0
- package/templates/CLAUDE.md +3 -2
- package/templates/CLAUDE.md.template +24 -357
- package/templates/agent-job/CLAUDE.md.template +57 -0
- package/templates/agent-job/CRONS.json +16 -0
- package/templates/{config/agent-job → agent-job}/SOUL.md +3 -3
- package/templates/agent-job/SYSTEM.md +60 -0
- package/templates/agents/CLAUDE.md.template +54 -0
- package/templates/data/CLAUDE.md.template +5 -0
- package/templates/docker-compose.custom.yml +41 -62
- package/templates/docker-compose.yml +14 -21
- package/templates/event-handler/CLAUDE.md.template +0 -0
- package/templates/logs/CLAUDE.md.template +5 -0
- package/templates/skills/CLAUDE.md.template +57 -32
- package/templates/skills/active/.gitkeep +0 -0
- package/templates/skills/library/agent-job-secrets/SKILL.md +23 -0
- package/templates/skills/library/agent-job-secrets/agent-job-secrets.js +62 -0
- package/templates/.pi/extensions/env-sanitizer/index.ts +0 -48
- package/templates/.pi/extensions/env-sanitizer/package.json +0 -5
- package/templates/README.md +0 -75
- package/templates/config/CLAUDE.md.template +0 -40
- package/templates/config/CRONS.json +0 -56
- package/templates/config/agent-job/AGENT_JOB.md +0 -30
- package/templates/cron/CLAUDE.md.template +0 -24
- package/templates/docker-compose.litellm.yml +0 -82
- package/templates/docs/CLAUDE.md.template +0 -12
- package/templates/docs/CLI.md +0 -59
- package/templates/docs/CLUSTERS.md +0 -151
- package/templates/docs/CONFIGURATION.md +0 -181
- package/templates/docs/CRONS_AND_TRIGGERS.md +0 -132
- package/templates/docs/GETTING_STARTED.md +0 -64
- package/templates/docs/SECURITY.md +0 -61
- package/templates/docs/SKILLS.md +0 -113
- package/templates/docs/UPGRADING.md +0 -92
- package/templates/skills/LICENSE +0 -21
- package/templates/skills/README.md +0 -117
- package/templates/skills/agent-job-secrets/SKILL.md +0 -25
- package/templates/skills/agent-job-secrets/agent-job-secrets.js +0 -66
- package/templates/traefik-dynamic.yml.example +0 -7
- package/templates/triggers/CLAUDE.md.template +0 -41
- /package/templates/{config → agent-job}/HEARTBEAT.md +0 -0
- /package/templates/{cron → data}/.gitkeep +0 -0
- /package/templates/{logs → data/clusters}/.gitkeep +0 -0
- /package/templates/{triggers → data/db}/.gitkeep +0 -0
- /package/templates/{config/agent-job → event-handler}/SUMMARY.md +0 -0
- /package/templates/{config → event-handler}/TRIGGERS.json +0 -0
- /package/templates/{config → event-handler}/agent-chat/SYSTEM.md +0 -0
- /package/templates/{config/cluster → event-handler/clusters}/ROLE.md +0 -0
- /package/templates/{config/cluster → event-handler/clusters}/SYSTEM.md +0 -0
- /package/templates/{config → event-handler}/code-chat/SYSTEM.md +0 -0
- /package/templates/{config → event-handler}/litellm/main.yaml +0 -0
- /package/templates/skills/{playwright-cli → library/playwright-cli}/SKILL.md +0 -0
|
@@ -1,7 +1,19 @@
|
|
|
1
|
-
# Custom Docker Compose
|
|
1
|
+
# Custom Docker Compose — SSL with Let's Encrypt wildcard cert
|
|
2
2
|
# This file is NOT managed by thepopebot and won't be overwritten on upgrade.
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
#
|
|
4
|
+
# Activate: set COMPOSE_FILE=docker-compose.custom.yml in .env
|
|
5
|
+
# Setup: npx thepopebot setup-ssl
|
|
6
|
+
#
|
|
7
|
+
# Requires these .env vars (set by setup-ssl):
|
|
8
|
+
# SSL_DOMAIN — your domain (e.g., bot.example.com)
|
|
9
|
+
# SSL_EMAIL — email for Let's Encrypt notifications
|
|
10
|
+
# SSL_DNS_PROVIDER — Traefik DNS provider name (e.g., cloudflare)
|
|
11
|
+
#
|
|
12
|
+
# DNS provider API credentials are stored in .env.traefik (created by setup-ssl).
|
|
13
|
+
# Only this file is passed to the Traefik container — not the main .env.
|
|
14
|
+
#
|
|
15
|
+
# DNS: *.${SSL_DOMAIN} must resolve to this server (A record or CNAME).
|
|
16
|
+
# The setup-ssl command can create this record for you.
|
|
5
17
|
|
|
6
18
|
services:
|
|
7
19
|
traefik:
|
|
@@ -9,20 +21,22 @@ services:
|
|
|
9
21
|
command:
|
|
10
22
|
- --providers.docker=true
|
|
11
23
|
- --providers.docker.exposedByDefault=false
|
|
24
|
+
- --providers.file.directory=/traefik-config/
|
|
12
25
|
- --entrypoints.web.address=:80
|
|
13
26
|
- --entrypoints.websecure.address=:443
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
- --entrypoints.web.http.redirections.entrypoint.to=websecure
|
|
28
|
+
- --entrypoints.web.http.redirections.entrypoint.scheme=https
|
|
29
|
+
- --certificatesresolvers.letsencrypt.acme.email=${SSL_EMAIL}
|
|
30
|
+
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
|
|
31
|
+
- --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=${SSL_DNS_PROVIDER}
|
|
32
|
+
- --certificatesresolvers.letsencrypt.acme.dnschallenge.resolvers=8.8.8.8:53,8.8.4.4:53
|
|
33
|
+
env_file: .env.traefik
|
|
21
34
|
ports:
|
|
22
35
|
- "80:80"
|
|
23
36
|
- "443:443"
|
|
24
37
|
volumes:
|
|
25
38
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
39
|
+
- ./traefik-config:/traefik-config:ro
|
|
26
40
|
- traefik_certs:/letsencrypt
|
|
27
41
|
restart: unless-stopped
|
|
28
42
|
|
|
@@ -31,24 +45,24 @@ services:
|
|
|
31
45
|
image: ${EVENT_HANDLER_IMAGE_URL:-stephengpope/thepopebot:event-handler-${THEPOPEBOT_VERSION:-latest}}
|
|
32
46
|
volumes:
|
|
33
47
|
- .:/project
|
|
34
|
-
- ./
|
|
48
|
+
- ./agent-job:/app/agent-job
|
|
49
|
+
- ./event-handler:/app/event-handler
|
|
35
50
|
- ./skills:/app/skills
|
|
36
51
|
- ./.env:/app/.env
|
|
37
52
|
- ./data:/app/data
|
|
38
53
|
- ./logs:/app/logs
|
|
39
|
-
- ./
|
|
40
|
-
- ./triggers:/app/triggers
|
|
41
|
-
- ./CLAUDE.md:/app/CLAUDE.md
|
|
54
|
+
- ./traefik-config:/traefik-config
|
|
42
55
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
56
|
+
environment:
|
|
57
|
+
- TRAEFIK_CONFIG_DIR=/traefik-config
|
|
43
58
|
labels:
|
|
44
59
|
- traefik.enable=true
|
|
45
|
-
# Set APP_HOSTNAME in .env to the domain from APP_URL (e.g., mybot.example.com)
|
|
46
60
|
- traefik.http.routers.event-handler.rule=Host(`${APP_HOSTNAME}`)
|
|
47
|
-
- traefik.http.routers.event-handler.entrypoints=
|
|
61
|
+
- traefik.http.routers.event-handler.entrypoints=websecure
|
|
62
|
+
- traefik.http.routers.event-handler.tls.certresolver=letsencrypt
|
|
63
|
+
- traefik.http.routers.event-handler.tls.domains[0].main=${SSL_DOMAIN}
|
|
64
|
+
- traefik.http.routers.event-handler.tls.domains[0].sans=*.${SSL_DOMAIN}
|
|
48
65
|
- traefik.http.services.event-handler.loadbalancer.server.port=80
|
|
49
|
-
## Uncomment the following lines to enable TLS via Let's Encrypt:
|
|
50
|
-
# - traefik.http.routers.event-handler.entrypoints=websecure
|
|
51
|
-
# - traefik.http.routers.event-handler.tls.certresolver=letsencrypt
|
|
52
66
|
stop_grace_period: 120s
|
|
53
67
|
healthcheck:
|
|
54
68
|
test: ["CMD", "curl", "-f", "http://localhost:80/api/ping"]
|
|
@@ -58,6 +72,13 @@ services:
|
|
|
58
72
|
start_period: 30s
|
|
59
73
|
restart: unless-stopped
|
|
60
74
|
|
|
75
|
+
litellm:
|
|
76
|
+
image: ghcr.io/berriai/litellm:main-latest
|
|
77
|
+
command: --config /litellm/main.yaml --port 4000
|
|
78
|
+
volumes:
|
|
79
|
+
- ./event-handler/litellm:/litellm:ro
|
|
80
|
+
restart: unless-stopped
|
|
81
|
+
|
|
61
82
|
runner:
|
|
62
83
|
image: myoung34/github-runner:latest
|
|
63
84
|
deploy:
|
|
@@ -68,6 +89,7 @@ services:
|
|
|
68
89
|
RUNNER_SCOPE: repo
|
|
69
90
|
LABELS: self-hosted
|
|
70
91
|
EPHEMERAL: "1"
|
|
92
|
+
DISABLE_AUTO_UPDATE: "true"
|
|
71
93
|
volumes:
|
|
72
94
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
73
95
|
- .:/project
|
|
@@ -75,46 +97,3 @@ services:
|
|
|
75
97
|
|
|
76
98
|
volumes:
|
|
77
99
|
traefik_certs:
|
|
78
|
-
|
|
79
|
-
# ──────────────────────────────────────────────────────────────────────
|
|
80
|
-
# Tailscale TLS Example
|
|
81
|
-
# ──────────────────────────────────────────────────────────────────────
|
|
82
|
-
# To use Tailscale HTTPS certs instead of Let's Encrypt:
|
|
83
|
-
#
|
|
84
|
-
# 1. On your Tailscale machine, run:
|
|
85
|
-
# sudo tailscale cert <your-machine-name>.<tailnet>.ts.net
|
|
86
|
-
# This creates .crt and .key files in /var/lib/tailscale/certs/
|
|
87
|
-
#
|
|
88
|
-
# 2. Copy traefik-dynamic.yml.example to traefik-dynamic.yml and
|
|
89
|
-
# replace YOUR_HOSTNAME with your full Tailscale machine name.
|
|
90
|
-
#
|
|
91
|
-
# 3. Replace the traefik service above with:
|
|
92
|
-
#
|
|
93
|
-
# traefik:
|
|
94
|
-
# image: traefik:v3
|
|
95
|
-
# command:
|
|
96
|
-
# - --providers.docker=true
|
|
97
|
-
# - --providers.docker.exposedByDefault=false
|
|
98
|
-
# - --providers.file.filename=/etc/traefik/dynamic.yml
|
|
99
|
-
# - --entrypoints.web.address=:80
|
|
100
|
-
# - --entrypoints.web.http.redirections.entrypoint.to=websecure
|
|
101
|
-
# - --entrypoints.web.http.redirections.entrypoint.scheme=https
|
|
102
|
-
# - --entrypoints.websecure.address=:443
|
|
103
|
-
# ports:
|
|
104
|
-
# - "80:80"
|
|
105
|
-
# - "443:443"
|
|
106
|
-
# volumes:
|
|
107
|
-
# - /var/run/docker.sock:/var/run/docker.sock:ro
|
|
108
|
-
# - ./traefik-dynamic.yml:/etc/traefik/dynamic.yml:ro
|
|
109
|
-
# - /var/lib/tailscale/certs:/certs:ro
|
|
110
|
-
# restart: unless-stopped
|
|
111
|
-
#
|
|
112
|
-
# 4. Update the event-handler labels:
|
|
113
|
-
#
|
|
114
|
-
# event-handler:
|
|
115
|
-
# labels:
|
|
116
|
-
# - traefik.enable=true
|
|
117
|
-
# - traefik.http.routers.event-handler.rule=Host(`${APP_HOSTNAME}`)
|
|
118
|
-
# - traefik.http.routers.event-handler.entrypoints=websecure
|
|
119
|
-
# - traefik.http.routers.event-handler.tls=true
|
|
120
|
-
# - traefik.http.services.event-handler.loadbalancer.server.port=80
|
|
@@ -4,21 +4,13 @@ services:
|
|
|
4
4
|
command:
|
|
5
5
|
- --providers.docker=true
|
|
6
6
|
- --providers.docker.exposedByDefault=false
|
|
7
|
+
- --providers.file.directory=/traefik-config/
|
|
7
8
|
- --entrypoints.web.address=:80
|
|
8
|
-
- --entrypoints.websecure.address=:443
|
|
9
|
-
## Uncomment the following lines to enable TLS via Let's Encrypt
|
|
10
|
-
## (requires LETSENCRYPT_EMAIL in .env):
|
|
11
|
-
# - --entrypoints.web.http.redirections.entrypoint.to=websecure
|
|
12
|
-
# - --entrypoints.web.http.redirections.entrypoint.scheme=https
|
|
13
|
-
# - --certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL}
|
|
14
|
-
# - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
|
|
15
|
-
# - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
|
|
16
9
|
ports:
|
|
17
10
|
- "80:80"
|
|
18
|
-
- "443:443"
|
|
19
11
|
volumes:
|
|
20
12
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
21
|
-
-
|
|
13
|
+
- ./traefik-config:/traefik-config:ro
|
|
22
14
|
restart: unless-stopped
|
|
23
15
|
|
|
24
16
|
event-handler:
|
|
@@ -26,24 +18,21 @@ services:
|
|
|
26
18
|
image: ${EVENT_HANDLER_IMAGE_URL:-stephengpope/thepopebot:event-handler-${THEPOPEBOT_VERSION:-latest}}
|
|
27
19
|
volumes:
|
|
28
20
|
- .:/project
|
|
29
|
-
- ./
|
|
21
|
+
- ./agent-job:/app/agent-job
|
|
22
|
+
- ./event-handler:/app/event-handler
|
|
30
23
|
- ./skills:/app/skills
|
|
31
24
|
- ./.env:/app/.env
|
|
32
25
|
- ./data:/app/data
|
|
33
26
|
- ./logs:/app/logs
|
|
34
|
-
- ./
|
|
35
|
-
- ./triggers:/app/triggers
|
|
36
|
-
- ./CLAUDE.md:/app/CLAUDE.md
|
|
27
|
+
- ./traefik-config:/traefik-config
|
|
37
28
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
29
|
+
environment:
|
|
30
|
+
- TRAEFIK_CONFIG_DIR=/traefik-config
|
|
38
31
|
labels:
|
|
39
32
|
- traefik.enable=true
|
|
40
|
-
# Set APP_HOSTNAME in .env to the domain from APP_URL (e.g., mybot.example.com)
|
|
41
33
|
- traefik.http.routers.event-handler.rule=Host(`${APP_HOSTNAME}`)
|
|
42
34
|
- traefik.http.routers.event-handler.entrypoints=web
|
|
43
35
|
- traefik.http.services.event-handler.loadbalancer.server.port=80
|
|
44
|
-
## Uncomment the following lines to enable TLS via Let's Encrypt:
|
|
45
|
-
# - traefik.http.routers.event-handler.entrypoints=websecure
|
|
46
|
-
# - traefik.http.routers.event-handler.tls.certresolver=letsencrypt
|
|
47
36
|
stop_grace_period: 120s
|
|
48
37
|
healthcheck:
|
|
49
38
|
test: ["CMD", "curl", "-f", "http://localhost:80/api/ping"]
|
|
@@ -53,6 +42,13 @@ services:
|
|
|
53
42
|
start_period: 30s
|
|
54
43
|
restart: unless-stopped
|
|
55
44
|
|
|
45
|
+
litellm:
|
|
46
|
+
image: ghcr.io/berriai/litellm:main-latest
|
|
47
|
+
command: --config /litellm/main.yaml --port 4000
|
|
48
|
+
volumes:
|
|
49
|
+
- ./event-handler/litellm:/litellm:ro
|
|
50
|
+
restart: unless-stopped
|
|
51
|
+
|
|
56
52
|
runner:
|
|
57
53
|
image: myoung34/github-runner:latest
|
|
58
54
|
deploy:
|
|
@@ -68,6 +64,3 @@ services:
|
|
|
68
64
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
69
65
|
- .:/project
|
|
70
66
|
restart: unless-stopped
|
|
71
|
-
|
|
72
|
-
volumes:
|
|
73
|
-
traefik_certs:
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# skills/ — Agent Skills
|
|
2
2
|
|
|
3
|
-
Skills are lightweight plugins that extend agent abilities. Each skill lives in `skills/<skill-name>/` and is activated by symlinking into `skills/active/`.
|
|
3
|
+
Skills are lightweight plugins that extend agent abilities. Each skill lives in `skills/library/<skill-name>/` and is activated by symlinking into `skills/active/`.
|
|
4
4
|
|
|
5
5
|
## How Skills Work
|
|
6
6
|
|
|
@@ -10,7 +10,30 @@ Skills are lightweight plugins that extend agent abilities. Each skill lives in
|
|
|
10
10
|
|
|
11
11
|
Both Pi and Claude Code discover skills from the same `skills/active/` directory (via `.pi/skills` and `.claude/skills` symlink bridges).
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Conventions
|
|
14
|
+
|
|
15
|
+
### Language Preference
|
|
16
|
+
|
|
17
|
+
**Bash first.** Skills are glue code — API calls, data piping, file manipulation. Bash + curl + python3 (for JSON) handles nearly everything. No module systems, no dependency management, no surprises.
|
|
18
|
+
|
|
19
|
+
Use Node.js **only** when a required library has no alternative (e.g., `youtube-transcript-plus`). Never for new skills where bash + curl would work.
|
|
20
|
+
|
|
21
|
+
### Bash Script Standards
|
|
22
|
+
|
|
23
|
+
- Include `#!/bin/bash` and `set -euo pipefail` at the top
|
|
24
|
+
- `chmod +x` after creating
|
|
25
|
+
|
|
26
|
+
### Node.js Module Rules
|
|
27
|
+
|
|
28
|
+
The root `package.json` has `"type": "module"`, which forces **all** `.js` files in the project tree to be treated as ESM. This silently breaks any script using `require()`.
|
|
29
|
+
|
|
30
|
+
- **`.cjs`** — for CommonJS scripts (uses `require()`)
|
|
31
|
+
- **`.mjs`** — for ESM scripts (uses `import`)
|
|
32
|
+
- **Never use plain `.js`** for skill scripts. The behavior depends on the nearest `package.json` and will break unpredictably.
|
|
33
|
+
|
|
34
|
+
If you encounter a broken `.js` script in a skill, rename it to `.cjs` or `.mjs` as appropriate and update SKILL.md references.
|
|
35
|
+
|
|
36
|
+
### SKILL.md Format
|
|
14
37
|
|
|
15
38
|
Every skill must have a `SKILL.md` with YAML frontmatter:
|
|
16
39
|
|
|
@@ -25,28 +48,46 @@ description: One sentence describing what the skill does and when to use it.
|
|
|
25
48
|
## Usage
|
|
26
49
|
|
|
27
50
|
```bash
|
|
28
|
-
skills/skill-name/script.sh <args>
|
|
51
|
+
skills/library/skill-name/script.sh <args>
|
|
29
52
|
```
|
|
30
53
|
```
|
|
31
54
|
|
|
32
55
|
- The `description` field appears in the system prompt — keep it concise and action-oriented.
|
|
33
|
-
- Use project-root-relative paths in documentation (e.g., `skills/skill-name/script.sh`).
|
|
56
|
+
- Use project-root-relative paths in documentation (e.g., `skills/library/skill-name/script.sh`).
|
|
34
57
|
|
|
35
|
-
|
|
58
|
+
### Skill Structure
|
|
36
59
|
|
|
37
60
|
- **`SKILL.md`** (required) — YAML frontmatter + markdown documentation
|
|
38
|
-
- **Scripts**
|
|
61
|
+
- **Scripts** — bash (`.sh`) by default, `.cjs`/`.mjs` only when necessary
|
|
39
62
|
- **`package.json`** (optional) — only if Node.js dependencies are truly needed
|
|
40
63
|
|
|
64
|
+
### Credential Setup
|
|
65
|
+
|
|
66
|
+
If a skill needs an API key, add it via the admin UI (Settings > Agent Jobs > Secrets). The secret will be injected as an env var into Docker containers. The agent can discover available secrets via the `get-secret` skill.
|
|
67
|
+
|
|
68
|
+
### Activation & Deactivation
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Activate
|
|
72
|
+
ln -s ../library/skill-name skills/active/skill-name
|
|
73
|
+
|
|
74
|
+
# Deactivate
|
|
75
|
+
rm skills/active/skill-name
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The `skills/active/` directory is shared by both agent backends via symlink bridges:
|
|
79
|
+
- `.claude/skills → skills/active`
|
|
80
|
+
- `.pi/skills → skills/active`
|
|
81
|
+
|
|
41
82
|
## Creating a Skill
|
|
42
83
|
|
|
43
84
|
### Simple bash skill (most common)
|
|
44
85
|
|
|
45
86
|
```bash
|
|
46
|
-
mkdir skills/my-skill
|
|
87
|
+
mkdir skills/library/my-skill
|
|
47
88
|
```
|
|
48
89
|
|
|
49
|
-
**skills/my-skill/SKILL.md:**
|
|
90
|
+
**skills/library/my-skill/SKILL.md:**
|
|
50
91
|
```markdown
|
|
51
92
|
---
|
|
52
93
|
name: my-skill
|
|
@@ -60,13 +101,15 @@ Requires MY_API_KEY environment variable.
|
|
|
60
101
|
|
|
61
102
|
## Usage
|
|
62
103
|
```bash
|
|
63
|
-
skills/my-skill/run.sh <args>
|
|
104
|
+
skills/library/my-skill/run.sh <args>
|
|
64
105
|
```
|
|
65
106
|
```
|
|
66
107
|
|
|
67
|
-
**skills/my-skill/run.sh:**
|
|
108
|
+
**skills/library/my-skill/run.sh:**
|
|
68
109
|
```bash
|
|
69
110
|
#!/bin/bash
|
|
111
|
+
set -euo pipefail
|
|
112
|
+
|
|
70
113
|
if [ -z "$1" ]; then echo "Usage: run.sh <args>"; exit 1; fi
|
|
71
114
|
if [ -z "$MY_API_KEY" ]; then echo "Error: MY_API_KEY not set"; exit 1; fi
|
|
72
115
|
# ... skill logic
|
|
@@ -74,31 +117,13 @@ if [ -z "$MY_API_KEY" ]; then echo "Error: MY_API_KEY not set"; exit 1; fi
|
|
|
74
117
|
|
|
75
118
|
Then make it executable and activate:
|
|
76
119
|
```bash
|
|
77
|
-
chmod +x skills/my-skill/run.sh
|
|
78
|
-
ln -s ../my-skill skills/active/my-skill
|
|
120
|
+
chmod +x skills/library/my-skill/run.sh
|
|
121
|
+
ln -s ../library/my-skill skills/active/my-skill
|
|
79
122
|
```
|
|
80
123
|
|
|
81
124
|
### Node.js skill
|
|
82
125
|
|
|
83
|
-
Use
|
|
84
|
-
|
|
85
|
-
## Activation & Deactivation
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
# Activate
|
|
89
|
-
ln -s ../skill-name skills/active/skill-name
|
|
90
|
-
|
|
91
|
-
# Deactivate
|
|
92
|
-
rm skills/active/skill-name
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
The `skills/active/` directory is shared by both agent backends via symlink bridges:
|
|
96
|
-
- `.claude/skills → skills/active`
|
|
97
|
-
- `.pi/skills → skills/active`
|
|
98
|
-
|
|
99
|
-
## Credential Setup
|
|
100
|
-
|
|
101
|
-
If a skill needs an API key, add it via the admin UI (Settings > Agent Jobs > Secrets). The secret will be injected as an env var into Docker containers. The agent can discover available secrets via the `get-secret` skill.
|
|
126
|
+
Use only when a required library has no bash/curl alternative. Add a `package.json` with dependencies — they're installed automatically in Docker. Use `.cjs` for CommonJS or `.mjs` for ESM — never plain `.js`.
|
|
102
127
|
|
|
103
128
|
## Testing
|
|
104
129
|
|
|
@@ -106,4 +131,4 @@ Always build AND test a skill in the same job. Tell the agent to test with real
|
|
|
106
131
|
|
|
107
132
|
## Default Skills
|
|
108
133
|
|
|
109
|
-
Check `skills/` for available built-in skills. Activate any you need by symlinking into `skills/active/`.
|
|
134
|
+
Check `skills/library/` for available built-in skills. Activate any you need by symlinking into `skills/active/`.
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agent-job-secrets
|
|
3
|
+
description: List and retrieve agent secrets. Plain secrets are also available as env vars. OAuth credentials are auto-refreshed on every get call.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
# List available secret keys (fetches current list from server)
|
|
10
|
+
node skills/library/agent-job-secrets/agent-job-secrets.js
|
|
11
|
+
|
|
12
|
+
# Get a secret value (OAuth credentials are auto-refreshed)
|
|
13
|
+
node skills/library/agent-job-secrets/agent-job-secrets.js get MY_CREDENTIALS
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Notes
|
|
17
|
+
|
|
18
|
+
- `AGENT_JOB_TOKEN` and `APP_URL` are injected automatically — no setup required
|
|
19
|
+
- Plain (non-OAuth) secrets are also available directly as env vars (e.g. `echo $MY_KEY`)
|
|
20
|
+
- OAuth credentials must be fetched via `get` — they are not available as env vars
|
|
21
|
+
- `get` on an OAuth credential refreshes it server-side and returns a fresh access token
|
|
22
|
+
- If a fetched credential stops working (expired token, 401 error), call `get` again to obtain a fresh one
|
|
23
|
+
- `list` always fetches from the server, so it reflects secrets added after the container started
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const [cmd, key] = process.argv.slice(2);
|
|
4
|
+
|
|
5
|
+
const apiKey = process.env.AGENT_JOB_TOKEN;
|
|
6
|
+
const appUrl = process.env.APP_URL;
|
|
7
|
+
|
|
8
|
+
// Default to list
|
|
9
|
+
if (!cmd || cmd === 'list') {
|
|
10
|
+
if (!apiKey || !appUrl) {
|
|
11
|
+
console.log('No agent secrets available (missing AGENT_JOB_TOKEN or APP_URL).');
|
|
12
|
+
process.exit(0);
|
|
13
|
+
}
|
|
14
|
+
const url = `${appUrl}/api/agent-job-list-secrets`;
|
|
15
|
+
const res = await fetch(url, {
|
|
16
|
+
headers: { 'x-api-key': apiKey },
|
|
17
|
+
});
|
|
18
|
+
if (!res.ok) {
|
|
19
|
+
const body = await res.text();
|
|
20
|
+
console.error(`GET ${url} → ${res.status} ${body}`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const json = await res.json();
|
|
24
|
+
const secrets = json.secrets;
|
|
25
|
+
if (!secrets || secrets.length === 0) {
|
|
26
|
+
console.log('No agent secrets configured.');
|
|
27
|
+
} else {
|
|
28
|
+
console.log('Available secrets:');
|
|
29
|
+
secrets.forEach(s => {
|
|
30
|
+
const hint = s.secretType === 'oauth2' ? ' (OAuth — use get to fetch access token)'
|
|
31
|
+
: s.secretType === 'oauth_token' ? ' (OAuth token — use get to fetch)'
|
|
32
|
+
: '';
|
|
33
|
+
console.log(` - ${s.key}${hint}`);
|
|
34
|
+
});
|
|
35
|
+
console.log('\nUse: agent-job-secrets get KEY_NAME');
|
|
36
|
+
console.log('If a fetched value stops working, call get again for a fresh one.');
|
|
37
|
+
}
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!apiKey) { console.error('AGENT_JOB_TOKEN not available'); process.exit(1); }
|
|
42
|
+
if (!appUrl) { console.error('APP_URL not available'); process.exit(1); }
|
|
43
|
+
|
|
44
|
+
if (cmd === 'get') {
|
|
45
|
+
if (!key) { console.error('Usage: agent-job-secrets get KEY_NAME'); process.exit(1); }
|
|
46
|
+
const url = `${appUrl}/api/get-agent-job-secret?key=${encodeURIComponent(key)}`;
|
|
47
|
+
const res = await fetch(url, {
|
|
48
|
+
headers: { 'x-api-key': apiKey },
|
|
49
|
+
});
|
|
50
|
+
if (!res.ok) {
|
|
51
|
+
const body = await res.text();
|
|
52
|
+
console.error(`GET ${url} → ${res.status} ${body}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const json = await res.json();
|
|
56
|
+
console.log(json.value);
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.error(`Unknown command: ${cmd}`);
|
|
61
|
+
console.error('Available commands: list, get');
|
|
62
|
+
process.exit(1);
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Env Sanitizer Extension - Protects credentials from AI agent access
|
|
3
|
-
*
|
|
4
|
-
* Uses Pi's spawnHook to filter sensitive env vars from bash subprocess calls
|
|
5
|
-
* while keeping them available in the main process for:
|
|
6
|
-
* - Anthropic SDK (needs ANTHROPIC_API_KEY at init)
|
|
7
|
-
* - GitHub CLI (needs GH_TOKEN)
|
|
8
|
-
* - Other extensions that may need credentials
|
|
9
|
-
*
|
|
10
|
-
* Dynamically filters all keys defined in the SECRETS JSON env var.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
14
|
-
import { createBashTool } from "@mariozechner/pi-coding-agent";
|
|
15
|
-
|
|
16
|
-
// Parse SECRETS JSON to get list of keys to filter
|
|
17
|
-
function getSecretKeys(): string[] {
|
|
18
|
-
const keys: string[] = [];
|
|
19
|
-
if (process.env.SECRETS) {
|
|
20
|
-
try {
|
|
21
|
-
const secrets = JSON.parse(process.env.SECRETS);
|
|
22
|
-
keys.push(...Object.keys(secrets));
|
|
23
|
-
} catch {
|
|
24
|
-
// Invalid JSON, ignore
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
// Always filter SECRETS itself
|
|
28
|
-
keys.push("SECRETS");
|
|
29
|
-
return [...new Set(keys)]; // Dedupe
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export default function (pi: ExtensionAPI) {
|
|
33
|
-
const secretKeys = getSecretKeys();
|
|
34
|
-
|
|
35
|
-
// Override bash tool with filtered environment for subprocesses
|
|
36
|
-
const bashTool = createBashTool(process.cwd(), {
|
|
37
|
-
spawnHook: ({ command, cwd, env }) => {
|
|
38
|
-
// Filter all secret keys from subprocess environment
|
|
39
|
-
const filteredEnv = { ...env };
|
|
40
|
-
for (const key of secretKeys) {
|
|
41
|
-
delete filteredEnv[key];
|
|
42
|
-
}
|
|
43
|
-
return { command, cwd, env: filteredEnv };
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
pi.registerTool(bashTool);
|
|
48
|
-
}
|
package/templates/README.md
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# My Agent
|
|
2
|
-
|
|
3
|
-
An autonomous AI agent powered by [thepopebot](https://github.com/stephengpope/thepopebot).
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
1. **Open the web UI** — Visit your `APP_URL` in a browser
|
|
8
|
-
2. **Create an account** — First visit creates the admin account
|
|
9
|
-
3. **Start chatting** — The AI streams responses, supports file uploads and voice input
|
|
10
|
-
4. **Create a job** — Ask the AI to make code changes, or use the API
|
|
11
|
-
|
|
12
|
-
## What You Can Do
|
|
13
|
-
|
|
14
|
-
- **Chat** — Web chat with streaming responses, file uploads, voice input, and chat history
|
|
15
|
-
- **Telegram** — Chat on the go via a Telegram bot (`npm run setup-telegram`)
|
|
16
|
-
- **Jobs** — Autonomous coding tasks: the agent creates a branch, works in Docker, and opens a PR
|
|
17
|
-
- **Code Workspaces** — Browser-based interactive coding with Claude Code (`/code/{id}`)
|
|
18
|
-
- **Cluster Workspaces** — Multi-role agent teams with shared directories (`/clusters`)
|
|
19
|
-
- **Crons** — Scheduled recurring jobs (`config/CRONS.json`)
|
|
20
|
-
- **Triggers** — Webhook-activated automations (`config/TRIGGERS.json`)
|
|
21
|
-
- **Skills** — Extensible agent capabilities via `skills/active/`
|
|
22
|
-
|
|
23
|
-
## Config Files
|
|
24
|
-
|
|
25
|
-
| File | Purpose |
|
|
26
|
-
|------|---------|
|
|
27
|
-
| `config/agent-chat/SYSTEM.md` | Agent chat system prompt |
|
|
28
|
-
| `config/code-chat/SYSTEM.md` | Code workspace planning system prompt |
|
|
29
|
-
| `config/agent-job/SOUL.md` | Agent personality and identity |
|
|
30
|
-
| `config/agent-job/AGENT_JOB.md` | Agent runtime environment docs |
|
|
31
|
-
| `config/CRONS.json` | Scheduled job definitions |
|
|
32
|
-
| `config/TRIGGERS.json` | Webhook trigger definitions |
|
|
33
|
-
| `.env` | API keys, tokens, and settings |
|
|
34
|
-
|
|
35
|
-
See [docs/CONFIGURATION.md](docs/CONFIGURATION.md) for the complete list.
|
|
36
|
-
|
|
37
|
-
## Documentation
|
|
38
|
-
|
|
39
|
-
| Guide | Description |
|
|
40
|
-
|-------|-------------|
|
|
41
|
-
| [Getting Started](docs/GETTING_STARTED.md) | First steps: web UI, first chat, first job, Telegram |
|
|
42
|
-
| [Configuration](docs/CONFIGURATION.md) | All config files, env vars, LLM providers, GitHub setup |
|
|
43
|
-
| [Crons and Triggers](docs/CRONS_AND_TRIGGERS.md) | Action types, scheduling, webhooks, template tokens |
|
|
44
|
-
| [Skills](docs/SKILLS.md) | Bundled skills, activate/deactivate, build custom skills |
|
|
45
|
-
| [Clusters](docs/CLUSTERS.md) | Multi-role agent teams, triggers, shared directories |
|
|
46
|
-
| [CLI](docs/CLI.md) | All CLI commands reference |
|
|
47
|
-
| [Upgrading](docs/UPGRADING.md) | How to upgrade, managed files, recovery |
|
|
48
|
-
| [Security](docs/SECURITY.md) | API keys, secret filtering, auto-merge restrictions |
|
|
49
|
-
|
|
50
|
-
## Managed vs. User Files
|
|
51
|
-
|
|
52
|
-
**Managed files** are auto-updated by `thepopebot init` and `thepopebot upgrade` to stay in sync with the package version. Do not edit them — changes will be overwritten:
|
|
53
|
-
|
|
54
|
-
- `.github/workflows/`
|
|
55
|
-
- `docker-compose.yml`
|
|
56
|
-
- `.dockerignore`
|
|
57
|
-
- `.gitignore`
|
|
58
|
-
- `CLAUDE.md`
|
|
59
|
-
|
|
60
|
-
**Your files** (`config/`, `skills/`, `cron/`, `triggers/`, `docs/`, `README.md`) are yours to customize. They are scaffolded once and never overwritten.
|
|
61
|
-
|
|
62
|
-
To customize Docker Compose, set `COMPOSE_FILE=docker-compose.custom.yml` in `.env`.
|
|
63
|
-
|
|
64
|
-
## Updating
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
npx thepopebot upgrade
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Or trigger the **"Upgrade Event Handler"** workflow from the GitHub Actions tab.
|
|
71
|
-
|
|
72
|
-
## Help
|
|
73
|
-
|
|
74
|
-
- [thepopebot documentation](https://github.com/stephengpope/thepopebot)
|
|
75
|
-
- [Report an issue](https://github.com/stephengpope/thepopebot/issues)
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# config/ — Agent Configuration
|
|
2
|
-
|
|
3
|
-
This directory contains all user-editable configuration files. These files are **not managed** — your changes are preserved across upgrades.
|
|
4
|
-
|
|
5
|
-
## File Reference
|
|
6
|
-
|
|
7
|
-
### System Prompts
|
|
8
|
-
|
|
9
|
-
| File | Used By | Purpose |
|
|
10
|
-
|------|---------|---------|
|
|
11
|
-
| `agent-chat/SYSTEM.md` | Event handler | System prompt for the agent chat (job planning from web/Telegram). |
|
|
12
|
-
| `code-chat/SYSTEM.md` | Code workspaces | System prompt for the planning chat in interactive code workspaces. |
|
|
13
|
-
| `agent-job/SOUL.md` | Docker agent | Agent personality, identity, and values. Included in agent job system prompts. |
|
|
14
|
-
| `agent-job/AGENT_JOB.md` | Docker agent | Runtime environment documentation injected into the agent's context. |
|
|
15
|
-
| `agent-job/SUMMARY.md` | Docker agent | Prompt template for summarizing completed job results. |
|
|
16
|
-
| `cluster/SYSTEM.md` | Cluster workers | Shared system prompt for all cluster worker agents. |
|
|
17
|
-
| `cluster/ROLE.md` | Cluster workers | Per-role prompt template (receives role-specific variables). |
|
|
18
|
-
| `HEARTBEAT.md` | Heartbeat cron | Self-monitoring prompt for the agent's periodic heartbeat job. |
|
|
19
|
-
|
|
20
|
-
### Scheduling & Triggers
|
|
21
|
-
|
|
22
|
-
| File | Purpose |
|
|
23
|
-
|------|---------|
|
|
24
|
-
| `CRONS.json` | Scheduled job definitions — loaded at server startup by `node-cron`. |
|
|
25
|
-
| `TRIGGERS.json` | Webhook trigger definitions — loaded at server startup. |
|
|
26
|
-
|
|
27
|
-
## Template Variables
|
|
28
|
-
|
|
29
|
-
Config markdown files support includes and built-in variables (processed by `render-md.js` at runtime):
|
|
30
|
-
|
|
31
|
-
| Syntax | Description |
|
|
32
|
-
|--------|-------------|
|
|
33
|
-
| `{{ filepath.md }}` | Include another file (path relative to project root, recursive with circular detection) |
|
|
34
|
-
| `{{datetime}}` | Current ISO timestamp |
|
|
35
|
-
| `{{skills}}` | Dynamic bullet list of active skill descriptions from `skills/active/*/SKILL.md` frontmatter |
|
|
36
|
-
|
|
37
|
-
## When Changes Take Effect
|
|
38
|
-
|
|
39
|
-
- **Prompt files** (`.md`) — Changes take effect immediately on the next LLM call. No restart needed.
|
|
40
|
-
- **CRONS.json / TRIGGERS.json** — Require a server restart to reload schedules and trigger watchers.
|