thepopebot 1.2.70-beta.8 → 1.2.70

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 CHANGED
@@ -1,12 +1,4 @@
1
- # thepopebot
2
-
3
- **Autonomous AI agents. All the power. None of the leaked API keys.**
4
-
5
- ---
6
-
7
- ## Why thepopebot?
8
-
9
- **Secure by default** — Other frameworks hand credentials to the LLM and hope for the best. thepopebot is different: the AI literally cannot access your secrets, even if it tries. Secrets are filtered at the process level before the agent's shell even starts.
1
+ # Why thepopebot?
10
2
 
11
3
  **The repository IS the agent** — Every action your agent takes is a git commit. You can see exactly what it did, when, and why. If it screws up, revert it. Want to clone your agent? Fork the repo — code, personality, scheduled jobs, full history, all of it goes with your fork.
12
4
 
@@ -56,15 +48,9 @@ You interact with your bot via the web chat interface or Telegram (optional). Th
56
48
 
57
49
  ---
58
50
 
59
- ## Get FREE server time on Github!
60
-
61
- | | thepopebot | Other platforms |
62
- |---|---|---|
63
- | **Public repos** | Free. $0. GitHub Actions doesn't charge. | $20-100+/month |
64
- | **Private repos** | 2,000 free minutes/month (every GitHub plan, including free) | $20-100+/month |
65
- | **Infrastructure** | GitHub Actions (already included) | Dedicated servers |
51
+ ## Star History
66
52
 
67
- You just bring your own [Anthropic API key](https://console.anthropic.com/).
53
+ [![Star History Chart](https://api.star-history.com/svg?repos=stephengpope/thepopebot&type=date&legend=top-left)](https://www.star-history.com/#stephengpope/thepopebot&type=date&legend=top-left)
68
54
 
69
55
  ---
70
56
 
@@ -124,12 +110,6 @@ docker compose up -d
124
110
 
125
111
  ---
126
112
 
127
- ## Star History
128
-
129
- [![Star History Chart](https://api.star-history.com/svg?repos=stephengpope/thepopebot&type=date&legend=top-left)](https://www.star-history.com/#stephengpope/thepopebot&type=date&legend=top-left)
130
-
131
- ---
132
-
133
113
  ## Manual Updating
134
114
 
135
115
  **1. Update the package**
@@ -144,7 +124,26 @@ npm install thepopebot@latest
144
124
  npx thepopebot init
145
125
  ```
146
126
 
147
- For most people, that's it — `init` handles everything. It updates your project files, runs `npm install`, and updates `THEPOPEBOT_VERSION` in your local `.env`.
127
+ For most people, that's it — `init` handles everything. It updates your project files, runs `npm install`, and updates `THEPOPEBOT_VERSION` in your local `.env`. See [Understanding `init`](#understanding-init) below for details on what this updates and how to handle custom changes.
128
+
129
+ **3. Rebuild for local dev**
130
+
131
+ ```bash
132
+ npm run build
133
+ ```
134
+
135
+ **4. Commit and push**
136
+
137
+ ```bash
138
+ git add -A && git commit -m "upgrade thepopebot to vX.X.X"
139
+ git push
140
+ ```
141
+
142
+ Pushing to `main` triggers the `rebuild-event-handler.yml` workflow on your server. It detects the version change, runs `thepopebot init`, updates `THEPOPEBOT_VERSION` in the server's `.env`, pulls the new Docker image, restarts the container, rebuilds `.next`, and reloads PM2 — no manual `docker compose` needed.
143
+
144
+ > **Upgrade failed?** See [Recovering from a Failed Upgrade](docs/UPGRADE.md#recovering-from-a-failed-upgrade).
145
+
146
+ ### Understanding `init`
148
147
 
149
148
  #### How your project is structured
150
149
 
@@ -154,7 +153,7 @@ When you ran `thepopebot init` the first time, it scaffolded a project folder wi
154
153
 
155
154
  | Files | What they do |
156
155
  |-------|-------------|
157
- | `config/SOUL.md`, `CHATBOT.md`, `AGENT.md`, etc. | Your agent's personality, behavior, and prompts |
156
+ | `config/SOUL.md`, `EVENT_HANDLER.md`, `AGENT.md`, etc. | Your agent's personality, behavior, and prompts |
158
157
  | `config/CRONS.json`, `TRIGGERS.json` | Your scheduled jobs and webhook triggers |
159
158
  | `app/` | Next.js pages and UI components |
160
159
  | `docker/job/` | The Dockerfile for your agent's job container |
@@ -198,21 +197,6 @@ If you've made custom changes to managed files (e.g., added extra steps to a Git
198
197
  npx thepopebot init --no-managed
199
198
  ```
200
199
 
201
- **3. Rebuild for local dev**
202
-
203
- ```bash
204
- npm run build
205
- ```
206
-
207
- **4. Commit and push**
208
-
209
- ```bash
210
- git add -A && git commit -m "upgrade thepopebot to vX.X.X"
211
- git push
212
- ```
213
-
214
- Pushing to `main` triggers the `rebuild-event-handler.yml` workflow on your server. It detects the version change, runs `thepopebot init`, updates `THEPOPEBOT_VERSION` in the server's `.env`, pulls the new Docker image, restarts the container, rebuilds `.next`, and reloads PM2 — no manual `docker compose` needed.
215
-
216
200
  ---
217
201
 
218
202
  ## CLI Commands
@@ -269,88 +253,11 @@ The `templates/` directory contains files scaffolded into user projects by `thep
269
253
 
270
254
  ---
271
255
 
272
- ## Production Deployment
273
-
274
- Deploy your agent to a cloud VPS with HTTPS.
275
-
276
- ### 1. Server prerequisites
277
-
278
- You need a VPS (any provider — Hetzner, DigitalOcean, AWS, etc.) with:
279
-
280
- - Docker + Docker Compose
281
- - Node.js 18+
282
- - Git
283
- - GitHub CLI (`gh`)
284
-
285
- Point a domain (e.g., `mybot.example.com`) to your server's IP address with a DNS A record.
286
-
287
- ### 2. Scaffold and configure
288
-
289
- SSH into your server and scaffold the project:
290
-
291
- ```bash
292
- mkdir my-agent && cd my-agent
293
- npx thepopebot@latest init
294
- npm run setup
295
- ```
296
-
297
- When the setup wizard asks for `APP_URL`, enter your production URL with `https://` (e.g., `https://mybot.example.com`).
298
-
299
- Set the `RUNS_ON` GitHub variable so workflows use your server's self-hosted runner instead of GitHub-hosted runners:
300
-
301
- ```bash
302
- gh variable set RUNS_ON --body "self-hosted" --repo OWNER/REPO
303
- ```
304
-
305
- ### 3. Enable HTTPS (Let's Encrypt)
306
-
307
- The `docker-compose.yml` has Let's Encrypt support built in but commented out. Three edits to enable it:
308
-
309
- **a) Add your email to `.env`:**
310
-
311
- ```
312
- LETSENCRYPT_EMAIL=you@example.com
313
- ```
314
-
315
- **b) In `docker-compose.yml`, remove the `#` from the TLS lines in the traefik service command:**
316
-
317
- ```yaml
318
- # Before (commented out):
319
- # - --entrypoints.web.http.redirections.entrypoint.to=websecure
320
- # ...
321
-
322
- # After (uncommented):
323
- - --entrypoints.web.http.redirections.entrypoint.to=websecure
324
- - --entrypoints.web.http.redirections.entrypoint.scheme=https
325
- - --certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL}
326
- - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
327
- - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
328
- ```
329
-
330
- **c) In the event-handler labels, switch from HTTP to HTTPS:**
331
-
332
- Add a `#` to comment out the HTTP entrypoint, and remove the `#` from the two HTTPS lines:
333
-
334
- ```yaml
335
- # Before:
336
- - traefik.http.routers.event-handler.entrypoints=web
337
- # - traefik.http.routers.event-handler.entrypoints=websecure
338
- # - traefik.http.routers.event-handler.tls.certresolver=letsencrypt
339
-
340
- # After:
341
- # - traefik.http.routers.event-handler.entrypoints=web
342
- - traefik.http.routers.event-handler.entrypoints=websecure
343
- - traefik.http.routers.event-handler.tls.certresolver=letsencrypt
344
- ```
345
-
346
- ### 4. Build and launch
256
+ ## Security
347
257
 
348
- ```bash
349
- npm run build
350
- docker compose up -d
351
- ```
258
+ thepopebot includes API key authentication, webhook secret validation (fail-closed), session encryption, secret filtering in the Docker agent, and auto-merge path restrictions. However, all software carries risk — thepopebot is provided as-is, and you are responsible for securing your own infrastructure. If you're running locally with a tunnel (ngrok, Cloudflare Tunnel, port forwarding), be aware that your dev server endpoints are publicly accessible with no rate limiting and no TLS on the local hop.
352
259
 
353
- Ports 80 and 443 must be open on your server. Port 80 is required even with HTTPS — Let's Encrypt uses it for the ACME HTTP challenge to verify domain ownership.
260
+ See [docs/SECURITY.md](docs/SECURITY.md) for full details on what's exposed, the risks, and recommendations.
354
261
 
355
262
  ---
356
263
 
@@ -363,7 +270,9 @@ Ports 80 and 443 must be open on your server. Port 80 is required even with HTTP
363
270
  | [Customization](docs/CUSTOMIZATION.md) | Personality, skills, operating system files, using your bot, security details |
364
271
  | [Chat Integrations](docs/CHAT_INTEGRATIONS.md) | Web chat, Telegram, adding new channels |
365
272
  | [Auto-Merge](docs/AUTO_MERGE.md) | Auto-merge controls, ALLOWED_PATHS configuration |
273
+ | [Deployment](docs/DEPLOYMENT.md) | VPS setup, Docker Compose, HTTPS with Let's Encrypt |
366
274
  | [How to Use Pi](docs/HOW_TO_USE_PI.md) | Guide to the Pi coding agent |
275
+ | [Pre-Release](docs/PRE_RELEASE.md) | Installing beta/alpha builds, going back to stable |
367
276
  | [Security](docs/SECURITY.md) | Security disclaimer, local development risks |
368
277
  | [Upgrading](docs/UPGRADE.md) | Automated upgrades, recovering from failed upgrades |
369
278
 
@@ -372,45 +281,3 @@ Ports 80 and 443 must be open on your server. Port 80 is required even with HTTP
372
281
  | Document | Description |
373
282
  |----------|-------------|
374
283
  | [NPM](docs/NPM.md) | Updating pi-skills, versioning, and publishing releases |
375
-
376
- ---
377
-
378
- ## Security
379
-
380
- thepopebot includes API key authentication, webhook secret validation (fail-closed), session encryption, secret filtering in the Docker agent, and auto-merge path restrictions. However, all software carries risk — thepopebot is provided as-is, and you are responsible for securing your own infrastructure. If you're running locally with a tunnel (ngrok, Cloudflare Tunnel, port forwarding), be aware that your dev server endpoints are publicly accessible with no rate limiting and no TLS on the local hop.
381
-
382
- See [docs/SECURITY.md](docs/SECURITY.md) for full details on what's exposed, the risks, and recommendations.
383
-
384
- ---
385
-
386
- ## Pre-Release Versions
387
-
388
- Pre-release builds (beta, alpha, rc) are published to separate npm dist-tags. They won't be installed by normal `npm update` or `thepopebot init` — you have to opt in explicitly.
389
-
390
- **Install the latest pre-release:**
391
-
392
- ```bash
393
- mkdir my-agent && cd my-agent
394
- npx thepopebot@beta init
395
- ```
396
-
397
- **Install a specific version:**
398
-
399
- ```bash
400
- npx thepopebot@1.3.0-beta.1 init
401
- ```
402
-
403
- **Check available versions:**
404
-
405
- ```bash
406
- npm info thepopebot
407
- ```
408
-
409
- **Go back to stable:**
410
-
411
- ```bash
412
- npm install thepopebot@latest
413
- npx thepopebot init
414
- ```
415
-
416
- Pre-releases may contain breaking changes or incomplete features. Use them for testing and feedback — not production agents.
package/lib/ai/agent.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { createReactAgent } from '@langchain/langgraph/prebuilt';
2
2
  import { SystemMessage } from '@langchain/core/messages';
3
3
  import { createModel } from './model.js';
4
- import { createJobTool, getJobStatusTool } from './tools.js';
4
+ import { createJobTool, getJobStatusTool, getSystemTechnicalSpecsTool, getPiSkillCreationGuideTool } from './tools.js';
5
5
  import { SqliteSaver } from '@langchain/langgraph-checkpoint-sqlite';
6
- import { chatbotMd, thepopebotDb } from '../paths.js';
6
+ import { eventHandlerMd, thepopebotDb } from '../paths.js';
7
7
  import { render_md } from '../utils/render-md.js';
8
8
 
9
9
  let _agent = null;
@@ -16,14 +16,14 @@ let _agent = null;
16
16
  export async function getAgent() {
17
17
  if (!_agent) {
18
18
  const model = await createModel();
19
- const tools = [createJobTool, getJobStatusTool];
19
+ const tools = [createJobTool, getJobStatusTool, getSystemTechnicalSpecsTool, getPiSkillCreationGuideTool];
20
20
  const checkpointer = SqliteSaver.fromConnString(thepopebotDb);
21
21
 
22
22
  _agent = createReactAgent({
23
23
  llm: model,
24
24
  tools,
25
25
  checkpointSaver: checkpointer,
26
- prompt: (state) => [new SystemMessage(render_md(chatbotMd)), ...state.messages],
26
+ prompt: (state) => [new SystemMessage(render_md(eventHandlerMd)), ...state.messages],
27
27
  });
28
28
  }
29
29
  return _agent;
package/lib/ai/index.js CHANGED
@@ -149,24 +149,45 @@ async function* chatStream(threadId, message, attachments = [], options = {}) {
149
149
  for await (const event of stream) {
150
150
  // streamMode: 'messages' yields [message, metadata] tuples
151
151
  const msg = Array.isArray(event) ? event[0] : event;
152
- const isAI = msg._getType?.() === 'ai';
153
- if (!isAI) continue;
154
-
155
- // Content can be a string or an array of content blocks
156
- let text = '';
157
- if (typeof msg.content === 'string') {
158
- text = msg.content;
159
- } else if (Array.isArray(msg.content)) {
160
- text = msg.content
161
- .filter((b) => b.type === 'text' && b.text)
162
- .map((b) => b.text)
163
- .join('');
164
- }
165
-
166
- if (text) {
167
- fullText += text;
168
- yield text;
152
+ const msgType = msg._getType?.();
153
+
154
+ if (msgType === 'ai') {
155
+ // Tool calls AIMessage.tool_calls is an array of { id, name, args }
156
+ if (msg.tool_calls?.length > 0) {
157
+ for (const tc of msg.tool_calls) {
158
+ yield {
159
+ type: 'tool-call',
160
+ toolCallId: tc.id,
161
+ toolName: tc.name,
162
+ args: tc.args,
163
+ };
164
+ }
165
+ }
166
+
167
+ // Text content (wrapped in structured object)
168
+ let text = '';
169
+ if (typeof msg.content === 'string') {
170
+ text = msg.content;
171
+ } else if (Array.isArray(msg.content)) {
172
+ text = msg.content
173
+ .filter((b) => b.type === 'text' && b.text)
174
+ .map((b) => b.text)
175
+ .join('');
176
+ }
177
+
178
+ if (text) {
179
+ fullText += text;
180
+ yield { type: 'text', text };
181
+ }
182
+ } else if (msgType === 'tool') {
183
+ // Tool result — ToolMessage has tool_call_id and content
184
+ yield {
185
+ type: 'tool-result',
186
+ toolCallId: msg.tool_call_id,
187
+ result: msg.content,
188
+ };
169
189
  }
190
+ // Skip other message types (human, system)
170
191
  }
171
192
 
172
193
  // Save assistant response to DB
@@ -192,7 +213,7 @@ async function autoTitle(threadId, firstMessage) {
192
213
  const chat = getChatById(threadId);
193
214
  if (!chat || chat.title !== 'New Chat') return;
194
215
 
195
- const model = await createModel();
216
+ const model = await createModel({ maxTokens: 250 });
196
217
  const response = await model.invoke([
197
218
  ['system', 'Generate a short (3-6 word) title for this chat based on the user\'s first message. Return ONLY the title, nothing else.'],
198
219
  ['human', firstMessage],
package/lib/ai/tools.js CHANGED
@@ -1,7 +1,9 @@
1
+ import fs from 'fs';
1
2
  import { tool } from '@langchain/core/tools';
2
3
  import { z } from 'zod';
3
4
  import { createJob } from '../tools/create-job.js';
4
5
  import { getJobStatus } from '../tools/github.js';
6
+ import { claudeMd, skillGuidePath } from '../paths.js';
5
7
 
6
8
  const createJobTool = tool(
7
9
  async ({ job_description }) => {
@@ -15,7 +17,7 @@ const createJobTool = tool(
15
17
  {
16
18
  name: 'create_job',
17
19
  description:
18
- 'Create an autonomous job for thepopebot to execute. Use this tool liberally - if the user asks for ANY task to be done, create a job. Jobs can handle code changes, file updates, research tasks, web scraping, data analysis, or anything requiring autonomous work. When the user explicitly asks for a job, ALWAYS use this tool. Returns the job ID and branch name.',
20
+ 'Create an autonomous job that runs a Docker agent in a container. The Docker agent has full filesystem access, web search, browser automation, and other abilities. The job description you provide becomes the Docker agent\'s task prompt. Returns the job ID and branch name.',
19
21
  schema: z.object({
20
22
  job_description: z
21
23
  .string()
@@ -46,4 +48,36 @@ const getJobStatusTool = tool(
46
48
  }
47
49
  );
48
50
 
49
- export { createJobTool, getJobStatusTool };
51
+ const getSystemTechnicalSpecsTool = tool(
52
+ async () => {
53
+ try {
54
+ return fs.readFileSync(claudeMd, 'utf8');
55
+ } catch {
56
+ return 'No technical documentation found (CLAUDE.md not present in project root).';
57
+ }
58
+ },
59
+ {
60
+ name: 'get_system_technical_specs',
61
+ description:
62
+ 'Read the system architecture and technical documentation (CLAUDE.md). Use this when you need to understand how the system itself works — the event handler, Docker agent, API routes, database, cron/trigger configuration, GitHub Actions, deployment, or file structure. Use this before planning jobs that modify system configuration or infrastructure. NOT for Pi skill creation (use get_pi_skill_creation_guide for that).',
63
+ schema: z.object({}),
64
+ }
65
+ );
66
+
67
+ const getPiSkillCreationGuideTool = tool(
68
+ async () => {
69
+ try {
70
+ return fs.readFileSync(skillGuidePath, 'utf8');
71
+ } catch {
72
+ return 'Skill guide not found.';
73
+ }
74
+ },
75
+ {
76
+ name: 'get_pi_skill_creation_guide',
77
+ description:
78
+ 'Load the guide for creating, modifying, and understanding Pi agent skills (pi-skills). Use this when the user wants to create a new skill, asks how skills work, wants to modify an existing skill, or when you need to understand the skill format (SKILL.md, {baseDir}, activation, testing). This is about Pi skills specifically — the lightweight bash/Node.js wrappers that extend what the Docker agent can do. NOT for understanding the system architecture (use get_system_technical_specs for that).',
79
+ schema: z.object({}),
80
+ }
81
+ );
82
+
83
+ export { createJobTool, getJobStatusTool, getSystemTechnicalSpecsTool, getPiSkillCreationGuideTool };
@@ -23,7 +23,27 @@ export const middleware = auth((req) => {
23
23
 
24
24
  // Everything else requires auth
25
25
  if (!req.auth) {
26
- return NextResponse.redirect(new URL('/login', req.url));
26
+ const response = NextResponse.redirect(new URL('/login', req.url));
27
+
28
+ // Clear stale session cookies that can't be decrypted (e.g. after AUTH_SECRET rotation
29
+ // or container restart). Auth.js clears these internally in route handlers via
30
+ // sessionStore.clean(), but NOT in middleware — so the bad cookie loops forever.
31
+ // Only session-token cookies are cleared; csrf-token and callback-url are left intact.
32
+ const cookieNames = Object.keys(req.cookies.getAll().reduce((acc, c) => { acc[c.name] = true; return acc; }, {}));
33
+ const staleSessionCookies = cookieNames.filter(name =>
34
+ name === 'authjs.session-token' ||
35
+ name === '__Secure-authjs.session-token' ||
36
+ /^authjs\.session-token\.\d+$/.test(name) ||
37
+ /^__Secure-authjs\.session-token\.\d+$/.test(name)
38
+ );
39
+
40
+ if (staleSessionCookies.length > 0) {
41
+ for (const name of staleSessionCookies) {
42
+ response.cookies.set(name, '', { maxAge: 0, path: '/' });
43
+ }
44
+ }
45
+
46
+ return response;
27
47
  }
28
48
  });
29
49
 
package/lib/chat/api.js CHANGED
@@ -81,17 +81,46 @@ export async function POST(request) {
81
81
  // Signal start of assistant message
82
82
  writer.write({ type: 'start' });
83
83
 
84
- const textId = crypto.randomUUID();
85
84
  let textStarted = false;
85
+ let textId = crypto.randomUUID();
86
86
 
87
87
  for await (const chunk of chunks) {
88
- if (!textStarted) {
89
- writer.write({ type: 'text-start', id: textId });
90
- textStarted = true;
88
+ if (chunk.type === 'text') {
89
+ if (!textStarted) {
90
+ textId = crypto.randomUUID();
91
+ writer.write({ type: 'text-start', id: textId });
92
+ textStarted = true;
93
+ }
94
+ writer.write({ type: 'text-delta', id: textId, delta: chunk.text });
95
+
96
+ } else if (chunk.type === 'tool-call') {
97
+ // Close any open text block before tool events
98
+ if (textStarted) {
99
+ writer.write({ type: 'text-end', id: textId });
100
+ textStarted = false;
101
+ }
102
+ writer.write({
103
+ type: 'tool-input-start',
104
+ toolCallId: chunk.toolCallId,
105
+ toolName: chunk.toolName,
106
+ });
107
+ writer.write({
108
+ type: 'tool-input-available',
109
+ toolCallId: chunk.toolCallId,
110
+ toolName: chunk.toolName,
111
+ input: chunk.args,
112
+ });
113
+
114
+ } else if (chunk.type === 'tool-result') {
115
+ writer.write({
116
+ type: 'tool-output-available',
117
+ toolCallId: chunk.toolCallId,
118
+ output: chunk.result,
119
+ });
91
120
  }
92
- writer.write({ type: 'text-delta', id: textId, delta: chunk });
93
121
  }
94
122
 
123
+ // Close final text block if still open
95
124
  if (textStarted) {
96
125
  writer.write({ type: 'text-end', id: textId });
97
126
  }
@@ -42,7 +42,7 @@ function AppSidebar({ user }) {
42
42
  /* @__PURE__ */ jsxs(SidebarHeader, { children: [
43
43
  /* @__PURE__ */ jsxs("div", { className: collapsed ? "flex justify-center" : "flex items-center justify-between", children: [
44
44
  !collapsed && /* @__PURE__ */ jsxs("span", { className: "px-2 font-semibold text-lg", children: [
45
- "The Pope Bot",
45
+ "ThePopeBot",
46
46
  version && /* @__PURE__ */ jsxs("span", { className: "text-[11px] font-normal text-muted-foreground", children: [
47
47
  " v",
48
48
  version
@@ -49,7 +49,7 @@ export function AppSidebar({ user }) {
49
49
  {/* Top row: brand name + toggle icon (open) or just toggle icon (collapsed) */}
50
50
  <div className={collapsed ? 'flex justify-center' : 'flex items-center justify-between'}>
51
51
  {!collapsed && (
52
- <span className="px-2 font-semibold text-lg">The Pope Bot{version && <span className="text-[11px] font-normal text-muted-foreground"> v{version}</span>}</span>
52
+ <span className="px-2 font-semibold text-lg">ThePopeBot{version && <span className="text-[11px] font-normal text-muted-foreground"> v{version}</span>}</span>
53
53
  )}
54
54
  <Tooltip>
55
55
  <TooltipTrigger asChild>
@@ -209,7 +209,7 @@ function PaperclipIcon({ size = 16 }) {
209
209
  }
210
210
  );
211
211
  }
212
- function XIcon({ size = 16 }) {
212
+ function XIcon({ size = 16, className = "" }) {
213
213
  return /* @__PURE__ */ jsxs(
214
214
  "svg",
215
215
  {
@@ -222,6 +222,7 @@ function XIcon({ size = 16 }) {
222
222
  strokeLinejoin: "round",
223
223
  width: size,
224
224
  height: size,
225
+ className,
225
226
  children: [
226
227
  /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
227
228
  /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
@@ -311,7 +312,7 @@ function RefreshIcon({ size = 16 }) {
311
312
  }
312
313
  );
313
314
  }
314
- function ChevronDownIcon({ size = 16 }) {
315
+ function ChevronDownIcon({ size = 16, className = "" }) {
315
316
  return /* @__PURE__ */ jsx(
316
317
  "svg",
317
318
  {
@@ -324,6 +325,7 @@ function ChevronDownIcon({ size = 16 }) {
324
325
  strokeLinejoin: "round",
325
326
  width: size,
326
327
  height: size,
328
+ className,
327
329
  children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
328
330
  }
329
331
  );
@@ -389,7 +391,7 @@ function CopyIcon({ size = 16 }) {
389
391
  }
390
392
  );
391
393
  }
392
- function CheckIcon({ size = 16 }) {
394
+ function CheckIcon({ size = 16, className = "" }) {
393
395
  return /* @__PURE__ */ jsx(
394
396
  "svg",
395
397
  {
@@ -402,6 +404,7 @@ function CheckIcon({ size = 16 }) {
402
404
  strokeLinejoin: "round",
403
405
  width: size,
404
406
  height: size,
407
+ className,
405
408
  children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" })
406
409
  }
407
410
  );
@@ -635,6 +638,24 @@ function LifeBuoyIcon({ size = 16 }) {
635
638
  }
636
639
  );
637
640
  }
641
+ function WrenchIcon({ size = 16, className = "" }) {
642
+ return /* @__PURE__ */ jsx(
643
+ "svg",
644
+ {
645
+ xmlns: "http://www.w3.org/2000/svg",
646
+ viewBox: "0 0 24 24",
647
+ fill: "none",
648
+ stroke: "currentColor",
649
+ strokeWidth: 2,
650
+ strokeLinecap: "round",
651
+ strokeLinejoin: "round",
652
+ width: size,
653
+ height: size,
654
+ className,
655
+ children: /* @__PURE__ */ jsx("path", { d: "M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z" })
656
+ }
657
+ );
658
+ }
638
659
  function LogOutIcon({ size = 16 }) {
639
660
  return /* @__PURE__ */ jsxs(
640
661
  "svg",
@@ -688,6 +709,7 @@ export {
688
709
  SunIcon,
689
710
  SwarmIcon,
690
711
  TrashIcon,
712
+ WrenchIcon,
691
713
  XIcon,
692
714
  ZapIcon
693
715
  };
@@ -205,7 +205,7 @@ export function PaperclipIcon({ size = 16 }) {
205
205
  );
206
206
  }
207
207
 
208
- export function XIcon({ size = 16 }) {
208
+ export function XIcon({ size = 16, className = '' }) {
209
209
  return (
210
210
  <svg
211
211
  xmlns="http://www.w3.org/2000/svg"
@@ -217,6 +217,7 @@ export function XIcon({ size = 16 }) {
217
217
  strokeLinejoin="round"
218
218
  width={size}
219
219
  height={size}
220
+ className={className}
220
221
  >
221
222
  <path d="M18 6 6 18" />
222
223
  <path d="m6 6 12 12" />
@@ -304,7 +305,7 @@ export function RefreshIcon({ size = 16 }) {
304
305
  );
305
306
  }
306
307
 
307
- export function ChevronDownIcon({ size = 16 }) {
308
+ export function ChevronDownIcon({ size = 16, className = '' }) {
308
309
  return (
309
310
  <svg
310
311
  xmlns="http://www.w3.org/2000/svg"
@@ -316,6 +317,7 @@ export function ChevronDownIcon({ size = 16 }) {
316
317
  strokeLinejoin="round"
317
318
  width={size}
318
319
  height={size}
320
+ className={className}
319
321
  >
320
322
  <path d="m6 9 6 6 6-6" />
321
323
  </svg>
@@ -380,7 +382,7 @@ export function CopyIcon({ size = 16 }) {
380
382
  );
381
383
  }
382
384
 
383
- export function CheckIcon({ size = 16 }) {
385
+ export function CheckIcon({ size = 16, className = '' }) {
384
386
  return (
385
387
  <svg
386
388
  xmlns="http://www.w3.org/2000/svg"
@@ -392,6 +394,7 @@ export function CheckIcon({ size = 16 }) {
392
394
  strokeLinejoin="round"
393
395
  width={size}
394
396
  height={size}
397
+ className={className}
395
398
  >
396
399
  <path d="M20 6 9 17l-5-5" />
397
400
  </svg>
@@ -626,6 +629,25 @@ export function LifeBuoyIcon({ size = 16 }) {
626
629
  );
627
630
  }
628
631
 
632
+ export function WrenchIcon({ size = 16, className = '' }) {
633
+ return (
634
+ <svg
635
+ xmlns="http://www.w3.org/2000/svg"
636
+ viewBox="0 0 24 24"
637
+ fill="none"
638
+ stroke="currentColor"
639
+ strokeWidth={2}
640
+ strokeLinecap="round"
641
+ strokeLinejoin="round"
642
+ width={size}
643
+ height={size}
644
+ className={className}
645
+ >
646
+ <path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z" />
647
+ </svg>
648
+ );
649
+ }
650
+
629
651
  export function LogOutIcon({ size = 16 }) {
630
652
  return (
631
653
  <svg