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.
Files changed (120) hide show
  1. package/README.md +1 -1
  2. package/api/CLAUDE.md +1 -1
  3. package/api/index.js +5 -12
  4. package/bin/CLAUDE.md +1 -1
  5. package/bin/cli.js +329 -14
  6. package/bin/docker-build.js +5 -0
  7. package/bin/managed-paths.js +0 -7
  8. package/bin/sync.js +84 -0
  9. package/config/CLAUDE.md +1 -29
  10. package/config/instrumentation.js +1 -1
  11. package/lib/CLAUDE.md +3 -3
  12. package/lib/ai/CLAUDE.md +24 -3
  13. package/lib/ai/agent.js +8 -5
  14. package/lib/ai/async-channel.js +51 -0
  15. package/lib/ai/headless-stream.js +3 -0
  16. package/lib/ai/index.js +149 -173
  17. package/lib/ai/line-mappers.js +72 -9
  18. package/lib/ai/tools.js +40 -28
  19. package/lib/chat/actions.js +34 -6
  20. package/lib/chat/api.js +17 -1
  21. package/lib/chat/components/chat-header.js +4 -0
  22. package/lib/chat/components/chat-header.jsx +4 -0
  23. package/lib/chat/components/chat-input.js +1 -0
  24. package/lib/chat/components/chat-input.jsx +1 -0
  25. package/lib/chat/components/chat.js +9 -1
  26. package/lib/chat/components/chat.jsx +15 -2
  27. package/lib/chat/components/chats-page.js +3 -3
  28. package/lib/chat/components/chats-page.jsx +4 -6
  29. package/lib/chat/components/crons-page.js +1 -1
  30. package/lib/chat/components/crons-page.jsx +1 -1
  31. package/lib/chat/components/message.js +12 -4
  32. package/lib/chat/components/message.jsx +17 -4
  33. package/lib/chat/components/settings-chat-page.js +2 -1
  34. package/lib/chat/components/settings-chat-page.jsx +4 -1
  35. package/lib/chat/components/settings-coding-agents-page.js +139 -1
  36. package/lib/chat/components/settings-coding-agents-page.jsx +160 -0
  37. package/lib/chat/components/settings-jobs-page.js +13 -2
  38. package/lib/chat/components/settings-jobs-page.jsx +15 -1
  39. package/lib/chat/components/settings-secrets-layout.js +1 -1
  40. package/lib/chat/components/settings-secrets-layout.jsx +1 -1
  41. package/lib/chat/components/sidebar-history-item.js +3 -3
  42. package/lib/chat/components/sidebar-history-item.jsx +4 -6
  43. package/lib/chat/components/triggers-page.js +1 -1
  44. package/lib/chat/components/triggers-page.jsx +1 -1
  45. package/lib/cluster/actions.js +4 -4
  46. package/lib/cluster/execute.js +3 -1
  47. package/lib/code/actions.js +34 -11
  48. package/lib/code/code-page.js +40 -40
  49. package/lib/code/code-page.jsx +36 -36
  50. package/lib/code/port-forwards.js +17 -3
  51. package/lib/code/terminal-view.js +16 -0
  52. package/lib/code/terminal-view.jsx +18 -0
  53. package/lib/config.js +4 -0
  54. package/lib/cron.js +3 -3
  55. package/lib/db/api-keys.js +22 -61
  56. package/lib/db/config.js +23 -0
  57. package/lib/db/index.js +3 -1
  58. package/lib/maintenance.js +34 -11
  59. package/lib/paths.js +1 -38
  60. package/lib/tools/create-agent-job.js +0 -4
  61. package/lib/tools/docker.js +23 -16
  62. package/lib/triggers.js +4 -3
  63. package/lib/utils/render-md.js +3 -1
  64. package/package.json +2 -1
  65. package/setup/setup-ssl.mjs +414 -0
  66. package/templates/.github/workflows/rebuild-event-handler.yml +3 -0
  67. package/templates/.github/workflows/upgrade-event-handler.yml +1 -1
  68. package/templates/.gitignore.template +7 -3
  69. package/templates/.tmp/CLAUDE.md.template +5 -0
  70. package/templates/CLAUDE.md +3 -2
  71. package/templates/CLAUDE.md.template +24 -357
  72. package/templates/agent-job/CLAUDE.md.template +57 -0
  73. package/templates/agent-job/CRONS.json +16 -0
  74. package/templates/{config/agent-job → agent-job}/SOUL.md +3 -3
  75. package/templates/agent-job/SYSTEM.md +60 -0
  76. package/templates/agents/CLAUDE.md.template +54 -0
  77. package/templates/data/CLAUDE.md.template +5 -0
  78. package/templates/docker-compose.custom.yml +41 -62
  79. package/templates/docker-compose.yml +14 -21
  80. package/templates/event-handler/CLAUDE.md.template +0 -0
  81. package/templates/logs/CLAUDE.md.template +5 -0
  82. package/templates/skills/CLAUDE.md.template +57 -32
  83. package/templates/skills/active/.gitkeep +0 -0
  84. package/templates/skills/library/agent-job-secrets/SKILL.md +23 -0
  85. package/templates/skills/library/agent-job-secrets/agent-job-secrets.js +62 -0
  86. package/templates/.pi/extensions/env-sanitizer/index.ts +0 -48
  87. package/templates/.pi/extensions/env-sanitizer/package.json +0 -5
  88. package/templates/README.md +0 -75
  89. package/templates/config/CLAUDE.md.template +0 -40
  90. package/templates/config/CRONS.json +0 -56
  91. package/templates/config/agent-job/AGENT_JOB.md +0 -30
  92. package/templates/cron/CLAUDE.md.template +0 -24
  93. package/templates/docker-compose.litellm.yml +0 -82
  94. package/templates/docs/CLAUDE.md.template +0 -12
  95. package/templates/docs/CLI.md +0 -59
  96. package/templates/docs/CLUSTERS.md +0 -151
  97. package/templates/docs/CONFIGURATION.md +0 -181
  98. package/templates/docs/CRONS_AND_TRIGGERS.md +0 -132
  99. package/templates/docs/GETTING_STARTED.md +0 -64
  100. package/templates/docs/SECURITY.md +0 -61
  101. package/templates/docs/SKILLS.md +0 -113
  102. package/templates/docs/UPGRADING.md +0 -92
  103. package/templates/skills/LICENSE +0 -21
  104. package/templates/skills/README.md +0 -117
  105. package/templates/skills/agent-job-secrets/SKILL.md +0 -25
  106. package/templates/skills/agent-job-secrets/agent-job-secrets.js +0 -66
  107. package/templates/traefik-dynamic.yml.example +0 -7
  108. package/templates/triggers/CLAUDE.md.template +0 -41
  109. /package/templates/{config → agent-job}/HEARTBEAT.md +0 -0
  110. /package/templates/{cron → data}/.gitkeep +0 -0
  111. /package/templates/{logs → data/clusters}/.gitkeep +0 -0
  112. /package/templates/{triggers → data/db}/.gitkeep +0 -0
  113. /package/templates/{config/agent-job → event-handler}/SUMMARY.md +0 -0
  114. /package/templates/{config → event-handler}/TRIGGERS.json +0 -0
  115. /package/templates/{config → event-handler}/agent-chat/SYSTEM.md +0 -0
  116. /package/templates/{config/cluster → event-handler/clusters}/ROLE.md +0 -0
  117. /package/templates/{config/cluster → event-handler/clusters}/SYSTEM.md +0 -0
  118. /package/templates/{config → event-handler}/code-chat/SYSTEM.md +0 -0
  119. /package/templates/{config → event-handler}/litellm/main.yaml +0 -0
  120. /package/templates/skills/{playwright-cli → library/playwright-cli}/SKILL.md +0 -0
@@ -1,7 +1,19 @@
1
- # Custom Docker Compose configuration
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
- # To activate: set COMPOSE_FILE=docker-compose.custom.yml in .env
4
- # Make your customizations here (TLS, ports, volumes, extra services, etc.)
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
- ## Uncomment the following lines to enable TLS via Let's Encrypt
15
- ## (requires LETSENCRYPT_EMAIL in .env):
16
- # - --entrypoints.web.http.redirections.entrypoint.to=websecure
17
- # - --entrypoints.web.http.redirections.entrypoint.scheme=https
18
- # - --certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL}
19
- # - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
20
- # - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
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
- - ./config:/app/config
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
- - ./cron:/app/cron
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=web
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
- - traefik_certs:/letsencrypt
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
- - ./config:/app/config
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
- - ./cron:/app/cron
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
@@ -0,0 +1,5 @@
1
+ # Logs Directory
2
+
3
+ Agent job logs, organized by job ID. Each subdirectory contains the system prompt, task prompt, and agent output for a single job run.
4
+
5
+ Not checked into git.
@@ -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
- ## SKILL.md Format
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
- ## Skill Structure
58
+ ### Skill Structure
36
59
 
37
60
  - **`SKILL.md`** (required) — YAML frontmatter + markdown documentation
38
- - **Scripts** (optional) prefer bash (`.sh`) for simplicity
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 this pattern only when bash + curl isn't sufficient (e.g., HTML parsing, complex data processing). Add a `package.json` with dependencies — they're installed automatically in Docker.
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
- }
@@ -1,5 +0,0 @@
1
- {
2
- "name": "env-sanitizer",
3
- "private": true,
4
- "type": "module"
5
- }
@@ -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.