thepopebot 1.1.2 → 1.2.3

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 (143) hide show
  1. package/README.md +1 -1
  2. package/api/index.js +72 -165
  3. package/bin/cli.js +36 -6
  4. package/bin/local.sh +31 -0
  5. package/bin/postinstall.js +6 -2
  6. package/config/index.js +2 -11
  7. package/config/instrumentation.js +17 -5
  8. package/lib/actions.js +7 -6
  9. package/lib/ai/agent.js +36 -0
  10. package/lib/ai/index.js +274 -0
  11. package/lib/ai/model.js +67 -0
  12. package/lib/ai/tools.js +49 -0
  13. package/lib/auth/actions.js +28 -0
  14. package/lib/auth/config.js +45 -0
  15. package/lib/auth/index.js +27 -0
  16. package/lib/auth/middleware.js +30 -0
  17. package/lib/channels/base.js +56 -0
  18. package/lib/channels/index.js +15 -0
  19. package/lib/channels/telegram.js +146 -0
  20. package/lib/chat/actions.js +239 -0
  21. package/lib/chat/api.js +103 -0
  22. package/lib/chat/components/app-sidebar.js +161 -0
  23. package/lib/chat/components/app-sidebar.jsx +214 -0
  24. package/lib/chat/components/chat-header.js +9 -0
  25. package/lib/chat/components/chat-header.jsx +14 -0
  26. package/lib/chat/components/chat-input.js +230 -0
  27. package/lib/chat/components/chat-input.jsx +232 -0
  28. package/lib/chat/components/chat-nav-context.js +11 -0
  29. package/lib/chat/components/chat-nav-context.jsx +11 -0
  30. package/lib/chat/components/chat-page.js +70 -0
  31. package/lib/chat/components/chat-page.jsx +89 -0
  32. package/lib/chat/components/chat.js +78 -0
  33. package/lib/chat/components/chat.jsx +91 -0
  34. package/lib/chat/components/chats-page.js +170 -0
  35. package/lib/chat/components/chats-page.jsx +203 -0
  36. package/lib/chat/components/crons-page.js +144 -0
  37. package/lib/chat/components/crons-page.jsx +204 -0
  38. package/lib/chat/components/greeting.js +11 -0
  39. package/lib/chat/components/greeting.jsx +14 -0
  40. package/lib/chat/components/icons.js +518 -0
  41. package/lib/chat/components/icons.jsx +482 -0
  42. package/lib/chat/components/index.js +19 -0
  43. package/lib/chat/components/message.js +66 -0
  44. package/lib/chat/components/message.jsx +92 -0
  45. package/lib/chat/components/messages.js +63 -0
  46. package/lib/chat/components/messages.jsx +72 -0
  47. package/lib/chat/components/notifications-page.js +54 -0
  48. package/lib/chat/components/notifications-page.jsx +83 -0
  49. package/lib/chat/components/page-layout.js +21 -0
  50. package/lib/chat/components/page-layout.jsx +28 -0
  51. package/lib/chat/components/settings-layout.js +37 -0
  52. package/lib/chat/components/settings-layout.jsx +51 -0
  53. package/lib/chat/components/settings-secrets-page.js +216 -0
  54. package/lib/chat/components/settings-secrets-page.jsx +264 -0
  55. package/lib/chat/components/sidebar-history-item.js +54 -0
  56. package/lib/chat/components/sidebar-history-item.jsx +50 -0
  57. package/lib/chat/components/sidebar-history.js +92 -0
  58. package/lib/chat/components/sidebar-history.jsx +132 -0
  59. package/lib/chat/components/sidebar-user-nav.js +59 -0
  60. package/lib/chat/components/sidebar-user-nav.jsx +69 -0
  61. package/lib/chat/components/swarm-page.js +250 -0
  62. package/lib/chat/components/swarm-page.jsx +356 -0
  63. package/lib/chat/components/triggers-page.js +121 -0
  64. package/lib/chat/components/triggers-page.jsx +177 -0
  65. package/lib/chat/components/ui/dropdown-menu.js +98 -0
  66. package/lib/chat/components/ui/dropdown-menu.jsx +116 -0
  67. package/lib/chat/components/ui/scroll-area.js +13 -0
  68. package/lib/chat/components/ui/scroll-area.jsx +17 -0
  69. package/lib/chat/components/ui/separator.js +21 -0
  70. package/lib/chat/components/ui/separator.jsx +18 -0
  71. package/lib/chat/components/ui/sheet.js +75 -0
  72. package/lib/chat/components/ui/sheet.jsx +95 -0
  73. package/lib/chat/components/ui/sidebar.js +227 -0
  74. package/lib/chat/components/ui/sidebar.jsx +245 -0
  75. package/lib/chat/components/ui/tooltip.js +56 -0
  76. package/lib/chat/components/ui/tooltip.jsx +66 -0
  77. package/lib/chat/utils.js +11 -0
  78. package/lib/cron.js +7 -8
  79. package/lib/db/api-keys.js +160 -0
  80. package/lib/db/chats.js +129 -0
  81. package/lib/db/index.js +106 -0
  82. package/lib/db/notifications.js +99 -0
  83. package/lib/db/schema.js +51 -0
  84. package/lib/db/users.js +89 -0
  85. package/lib/paths.js +23 -17
  86. package/lib/tools/create-job.js +3 -3
  87. package/lib/tools/github.js +145 -1
  88. package/lib/tools/openai.js +1 -1
  89. package/lib/tools/telegram.js +4 -3
  90. package/lib/triggers.js +6 -7
  91. package/lib/utils/render-md.js +6 -6
  92. package/package.json +43 -6
  93. package/setup/lib/auth.mjs +22 -9
  94. package/setup/lib/prerequisites.mjs +10 -3
  95. package/setup/lib/telegram-verify.mjs +3 -16
  96. package/setup/setup-telegram.mjs +31 -62
  97. package/setup/setup.mjs +58 -98
  98. package/templates/.dockerignore +5 -0
  99. package/templates/.env.example +18 -2
  100. package/templates/.github/workflows/auto-merge.yml +1 -1
  101. package/templates/.github/workflows/build-image.yml +6 -4
  102. package/templates/.github/workflows/notify-job-failed.yml +2 -2
  103. package/templates/.github/workflows/notify-pr-complete.yml +2 -2
  104. package/templates/.github/workflows/run-job.yml +24 -10
  105. package/templates/CLAUDE.md +5 -3
  106. package/templates/app/api/auth/[...nextauth]/route.js +1 -0
  107. package/templates/app/api/chat/route.js +1 -0
  108. package/templates/app/chat/[chatId]/page.js +8 -0
  109. package/templates/app/chats/page.js +7 -0
  110. package/templates/app/components/ascii-logo.jsx +10 -0
  111. package/templates/app/components/login-form.jsx +81 -0
  112. package/templates/app/components/setup-form.jsx +82 -0
  113. package/templates/app/components/theme-provider.jsx +11 -0
  114. package/templates/app/components/theme-toggle.jsx +38 -0
  115. package/templates/app/components/ui/button.jsx +21 -0
  116. package/templates/app/components/ui/card.jsx +23 -0
  117. package/templates/app/components/ui/input.jsx +10 -0
  118. package/templates/app/components/ui/label.jsx +10 -0
  119. package/templates/app/crons/page.js +7 -0
  120. package/templates/app/globals.css +66 -0
  121. package/templates/app/layout.js +9 -2
  122. package/templates/app/login/page.js +15 -0
  123. package/templates/app/notifications/page.js +7 -0
  124. package/templates/app/page.js +6 -30
  125. package/templates/app/settings/layout.js +7 -0
  126. package/templates/app/settings/page.js +5 -0
  127. package/templates/app/settings/secrets/page.js +5 -0
  128. package/templates/app/swarm/page.js +7 -0
  129. package/templates/app/triggers/page.js +7 -0
  130. package/templates/config/CRONS.json +2 -2
  131. package/templates/config/TRIGGERS.json +2 -2
  132. package/templates/docker/event_handler/Dockerfile +19 -0
  133. package/templates/docker/{entrypoint.sh → job/entrypoint.sh} +4 -4
  134. package/templates/docker/runner/Dockerfile +38 -0
  135. package/templates/docker/runner/entrypoint.sh +41 -0
  136. package/templates/docker-compose.yml +52 -0
  137. package/templates/instrumentation.js +6 -1
  138. package/templates/middleware.js +1 -0
  139. package/templates/postcss.config.mjs +5 -0
  140. package/lib/claude/conversation.js +0 -76
  141. package/lib/claude/index.js +0 -142
  142. package/lib/claude/tools.js +0 -54
  143. /package/templates/docker/{Dockerfile → job/Dockerfile} +0 -0
@@ -0,0 +1,82 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { useRouter } from 'next/navigation';
5
+ import { Button } from './ui/button';
6
+ import { Input } from './ui/input';
7
+ import { Label } from './ui/label';
8
+ import { Card, CardHeader, CardTitle, CardDescription, CardContent } from './ui/card';
9
+ import { setupAdmin } from 'thepopebot/auth/actions';
10
+
11
+ export function SetupForm() {
12
+ const router = useRouter();
13
+ const [email, setEmail] = useState('');
14
+ const [password, setPassword] = useState('');
15
+ const [error, setError] = useState('');
16
+ const [loading, setLoading] = useState(false);
17
+
18
+ async function handleSubmit(e) {
19
+ e.preventDefault();
20
+ setError('');
21
+ setLoading(true);
22
+
23
+ try {
24
+ const result = await setupAdmin(email, password);
25
+
26
+ if (result.error) {
27
+ setError(result.error);
28
+ setLoading(false);
29
+ return;
30
+ }
31
+
32
+ // Redirect to login — admin must authenticate through the normal flow
33
+ router.push('/login');
34
+ } catch {
35
+ setError('Something went wrong. Please try again.');
36
+ } finally {
37
+ setLoading(false);
38
+ }
39
+ }
40
+
41
+ return (
42
+ <Card className="w-full max-w-sm">
43
+ <CardHeader>
44
+ <CardTitle>Create Admin Account</CardTitle>
45
+ <CardDescription>Set up your first admin account to get started.</CardDescription>
46
+ </CardHeader>
47
+ <CardContent>
48
+ <form onSubmit={handleSubmit} className="space-y-4">
49
+ <div className="space-y-2">
50
+ <Label htmlFor="email">Email</Label>
51
+ <Input
52
+ id="email"
53
+ type="email"
54
+ placeholder="admin@example.com"
55
+ value={email}
56
+ onChange={(e) => setEmail(e.target.value)}
57
+ required
58
+ />
59
+ </div>
60
+ <div className="space-y-2">
61
+ <Label htmlFor="password">Password</Label>
62
+ <Input
63
+ id="password"
64
+ type="password"
65
+ placeholder="Min 8 characters"
66
+ value={password}
67
+ onChange={(e) => setPassword(e.target.value)}
68
+ required
69
+ minLength={8}
70
+ />
71
+ </div>
72
+ {error && (
73
+ <p className="text-sm text-destructive">{error}</p>
74
+ )}
75
+ <Button type="submit" className="w-full" disabled={loading}>
76
+ {loading ? 'Creating...' : 'Create Account'}
77
+ </Button>
78
+ </form>
79
+ </CardContent>
80
+ </Card>
81
+ );
82
+ }
@@ -0,0 +1,11 @@
1
+ 'use client';
2
+
3
+ import { ThemeProvider as NextThemesProvider } from 'next-themes';
4
+
5
+ export function ThemeProvider({ children }) {
6
+ return (
7
+ <NextThemesProvider attribute="class" defaultTheme="system" enableSystem>
8
+ {children}
9
+ </NextThemesProvider>
10
+ );
11
+ }
@@ -0,0 +1,38 @@
1
+ 'use client';
2
+
3
+ import { useTheme } from 'next-themes';
4
+ import { useEffect, useState } from 'react';
5
+
6
+ export function ThemeToggle() {
7
+ const { theme, setTheme } = useTheme();
8
+ const [mounted, setMounted] = useState(false);
9
+
10
+ useEffect(() => setMounted(true), []);
11
+
12
+ if (!mounted) return <div className="h-[34px] w-[106px]" />;
13
+
14
+ const options = [
15
+ { value: 'light', label: '☀️' },
16
+ { value: 'dark', label: '🌙' },
17
+ { value: 'system', label: '💻' },
18
+ ];
19
+
20
+ return (
21
+ <div className="flex items-center gap-1 rounded-md border border-border bg-muted p-1">
22
+ {options.map((opt) => (
23
+ <button
24
+ key={opt.value}
25
+ onClick={() => setTheme(opt.value)}
26
+ className={`rounded px-2 py-1 text-sm transition-colors ${
27
+ theme === opt.value
28
+ ? 'bg-background text-foreground shadow-sm'
29
+ : 'text-muted-foreground hover:text-foreground'
30
+ }`}
31
+ title={opt.value.charAt(0).toUpperCase() + opt.value.slice(1)}
32
+ >
33
+ {opt.label}
34
+ </button>
35
+ ))}
36
+ </div>
37
+ );
38
+ }
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+
3
+ export function Button({ children, variant = 'default', className = '', disabled, ...props }) {
4
+ const base = 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 px-4 py-2';
5
+
6
+ const variants = {
7
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
8
+ outline: 'border border-border bg-transparent text-foreground hover:bg-muted',
9
+ ghost: 'text-foreground hover:bg-muted',
10
+ };
11
+
12
+ return (
13
+ <button
14
+ className={`${base} ${variants[variant] || variants.default} ${className}`}
15
+ disabled={disabled}
16
+ {...props}
17
+ >
18
+ {children}
19
+ </button>
20
+ );
21
+ }
@@ -0,0 +1,23 @@
1
+ export function Card({ children, className = '' }) {
2
+ return (
3
+ <div className={`rounded-lg border border-border bg-muted p-6 ${className}`}>
4
+ {children}
5
+ </div>
6
+ );
7
+ }
8
+
9
+ export function CardHeader({ children, className = '' }) {
10
+ return <div className={`mb-4 ${className}`}>{children}</div>;
11
+ }
12
+
13
+ export function CardTitle({ children, className = '' }) {
14
+ return <h2 className={`text-lg font-semibold text-foreground ${className}`}>{children}</h2>;
15
+ }
16
+
17
+ export function CardDescription({ children, className = '' }) {
18
+ return <p className={`text-sm text-muted-foreground ${className}`}>{children}</p>;
19
+ }
20
+
21
+ export function CardContent({ children, className = '' }) {
22
+ return <div className={className}>{children}</div>;
23
+ }
@@ -0,0 +1,10 @@
1
+ 'use client';
2
+
3
+ export function Input({ className = '', ...props }) {
4
+ return (
5
+ <input
6
+ className={`flex h-10 w-full rounded-md border border-border bg-input px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${className}`}
7
+ {...props}
8
+ />
9
+ );
10
+ }
@@ -0,0 +1,10 @@
1
+ export function Label({ children, className = '', ...props }) {
2
+ return (
3
+ <label
4
+ className={`text-sm font-medium text-foreground ${className}`}
5
+ {...props}
6
+ >
7
+ {children}
8
+ </label>
9
+ );
10
+ }
@@ -0,0 +1,7 @@
1
+ import { auth } from 'thepopebot/auth';
2
+ import { CronsPage } from 'thepopebot/chat';
3
+
4
+ export default async function CronsRoute() {
5
+ const session = await auth();
6
+ return <CronsPage session={session} />;
7
+ }
@@ -0,0 +1,66 @@
1
+ @import "tailwindcss";
2
+ @import "streamdown/styles.css";
3
+ @source "../node_modules/thepopebot/lib/**/*.{js,jsx}";
4
+
5
+ :root {
6
+ --background: #ffffff;
7
+ --foreground: #171717;
8
+ --primary: #171717;
9
+ --primary-foreground: #ffffff;
10
+ --muted: #f5f5f5;
11
+ --muted-foreground: #737373;
12
+ --border: #e5e5e5;
13
+ --input: #f5f5f5;
14
+ --ring: #171717;
15
+ --destructive: #dc2626;
16
+ }
17
+
18
+ .dark {
19
+ --background: #0a0a0a;
20
+ --foreground: #ededed;
21
+ --primary: #ededed;
22
+ --primary-foreground: #0a0a0a;
23
+ --muted: #171717;
24
+ --muted-foreground: #a3a3a3;
25
+ --border: #262626;
26
+ --input: #171717;
27
+ --ring: #ededed;
28
+ --destructive: #ef4444;
29
+ }
30
+
31
+ @theme inline {
32
+ --color-background: var(--background);
33
+ --color-foreground: var(--foreground);
34
+ --color-primary: var(--primary);
35
+ --color-primary-foreground: var(--primary-foreground);
36
+ --color-muted: var(--muted);
37
+ --color-muted-foreground: var(--muted-foreground);
38
+ --color-border: var(--border);
39
+ --color-input: var(--input);
40
+ --color-ring: var(--ring);
41
+ --color-destructive: var(--destructive);
42
+ --font-sans: system-ui, -apple-system, sans-serif;
43
+ --font-mono: ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace;
44
+ --radius-sm: 0.375rem;
45
+ --radius-md: 0.5rem;
46
+ --radius-lg: 0.75rem;
47
+ }
48
+
49
+ /* Scrollbar styling */
50
+ .scrollbar-thin {
51
+ scrollbar-width: thin;
52
+ scrollbar-color: var(--border) transparent;
53
+ }
54
+
55
+ .scrollbar-thin::-webkit-scrollbar {
56
+ width: 6px;
57
+ }
58
+
59
+ .scrollbar-thin::-webkit-scrollbar-track {
60
+ background: transparent;
61
+ }
62
+
63
+ .scrollbar-thin::-webkit-scrollbar-thumb {
64
+ background-color: var(--border);
65
+ border-radius: 3px;
66
+ }
@@ -1,3 +1,6 @@
1
+ import './globals.css';
2
+ import { ThemeProvider } from './components/theme-provider';
3
+
1
4
  export const metadata = {
2
5
  title: 'thepopebot',
3
6
  description: 'AI Agent',
@@ -5,8 +8,12 @@ export const metadata = {
5
8
 
6
9
  export default function RootLayout({ children }) {
7
10
  return (
8
- <html lang="en">
9
- <body style={{ margin: 0, overflow: 'hidden' }}>{children}</body>
11
+ <html lang="en" suppressHydrationWarning>
12
+ <body className="min-h-screen bg-background text-foreground antialiased">
13
+ <ThemeProvider>
14
+ {children}
15
+ </ThemeProvider>
16
+ </body>
10
17
  </html>
11
18
  );
12
19
  }
@@ -0,0 +1,15 @@
1
+ import { getPageAuthState } from 'thepopebot/auth';
2
+ import { AsciiLogo } from '../components/ascii-logo';
3
+ import { SetupForm } from '../components/setup-form';
4
+ import { LoginForm } from '../components/login-form';
5
+
6
+ export default async function LoginPage() {
7
+ const { needsSetup } = await getPageAuthState();
8
+
9
+ return (
10
+ <main className="min-h-screen flex flex-col items-center justify-center p-8">
11
+ <AsciiLogo />
12
+ {needsSetup ? <SetupForm /> : <LoginForm />}
13
+ </main>
14
+ );
15
+ }
@@ -0,0 +1,7 @@
1
+ import { auth } from 'thepopebot/auth';
2
+ import { NotificationsPage } from 'thepopebot/chat';
3
+
4
+ export default async function NotificationsRoute() {
5
+ const session = await auth();
6
+ return <NotificationsPage session={session} />;
7
+ }
@@ -1,31 +1,7 @@
1
- export default function Home() {
2
- return (
3
- <main style={{
4
- minHeight: '100vh',
5
- backgroundColor: '#0a0a0a',
6
- color: '#e0e0e0',
7
- display: 'flex',
8
- flexDirection: 'column',
9
- alignItems: 'center',
10
- justifyContent: 'center',
11
- fontFamily: 'system-ui, sans-serif',
12
- padding: '2rem',
13
- }}>
14
- <pre style={{
15
- color: '#00e5ff',
16
- fontSize: 'clamp(0.45rem, 1.5vw, 0.85rem)',
17
- lineHeight: 1.3,
18
- textAlign: 'left',
19
- marginBottom: '2rem',
20
- }}>{` _____ _ ____ ____ _
21
- |_ _| |__ ___| _ \\ ___ _ __ ___| __ ) ___ | |_
22
- | | | '_ \\ / _ \\ |_) / _ \\| '_ \\ / _ \\ _ \\ / _ \\| __|
23
- | | | | | | __/ __/ (_) | |_) | __/ |_) | (_) | |_
24
- |_| |_| |_|\\___|_| \\___/| .__/ \\___|____/ \\___/ \\__|
25
- |_|`}</pre>
26
- <p style={{ color: '#888', fontSize: '1rem' }}>
27
- Your AI agent is running.
28
- </p>
29
- </main>
30
- );
1
+ import { auth } from 'thepopebot/auth';
2
+ import { ChatPage } from 'thepopebot/chat';
3
+
4
+ export default async function Home() {
5
+ const session = await auth();
6
+ return <ChatPage session={session} needsSetup={false} />;
31
7
  }
@@ -0,0 +1,7 @@
1
+ import { auth } from 'thepopebot/auth';
2
+ import { SettingsLayout } from 'thepopebot/chat';
3
+
4
+ export default async function Layout({ children }) {
5
+ const session = await auth();
6
+ return <SettingsLayout session={session}>{children}</SettingsLayout>;
7
+ }
@@ -0,0 +1,5 @@
1
+ import { redirect } from 'next/navigation';
2
+
3
+ export default function SettingsRoot() {
4
+ redirect('/settings/secrets');
5
+ }
@@ -0,0 +1,5 @@
1
+ import { SettingsSecretsPage } from 'thepopebot/chat';
2
+
3
+ export default function SecretsRoute() {
4
+ return <SettingsSecretsPage />;
5
+ }
@@ -0,0 +1,7 @@
1
+ import { auth } from 'thepopebot/auth';
2
+ import { SwarmPage } from 'thepopebot/chat';
3
+
4
+ export default async function SwarmRoute() {
5
+ const session = await auth();
6
+ return <SwarmPage session={session} />;
7
+ }
@@ -0,0 +1,7 @@
1
+ import { auth } from 'thepopebot/auth';
2
+ import { TriggersPage } from 'thepopebot/chat';
3
+
4
+ export default async function TriggersRoute() {
5
+ const session = await auth();
6
+ return <TriggersPage session={session} />;
7
+ }
@@ -30,7 +30,7 @@
30
30
  {
31
31
  "name": "ping-status",
32
32
  "schedule": "*/5 * * * *",
33
- "type": "http",
33
+ "type": "webhook",
34
34
  "url": "https://example.com/status",
35
35
  "method": "POST",
36
36
  "vars": { "source": "heartbeat" },
@@ -39,7 +39,7 @@
39
39
  {
40
40
  "name": "health-check",
41
41
  "schedule": "*/10 * * * *",
42
- "type": "http",
42
+ "type": "webhook",
43
43
  "url": "https://example.com/health",
44
44
  "method": "GET",
45
45
  "enabled": false
@@ -35,7 +35,7 @@
35
35
  "name": "forward-github",
36
36
  "watch_path": "/github/webhook",
37
37
  "actions": [
38
- { "type": "http", "url": "https://example.com/hook", "method": "POST", "vars": { "source": "github" } }
38
+ { "type": "webhook", "url": "https://example.com/hook", "method": "POST", "vars": { "source": "github" } }
39
39
  ],
40
40
  "enabled": false
41
41
  },
@@ -43,7 +43,7 @@
43
43
  "name": "forward-webhook",
44
44
  "watch_path": "/webhook",
45
45
  "actions": [
46
- { "type": "http", "url": "https://example.com/hook", "method": "POST", "vars": { "source": "webhook" } }
46
+ { "type": "webhook", "url": "https://example.com/hook", "method": "POST", "vars": { "source": "webhook" } }
47
47
  ],
48
48
  "enabled": false
49
49
  }
@@ -0,0 +1,19 @@
1
+ FROM node:22-bookworm-slim
2
+
3
+ RUN apt-get update && apt-get install -y \
4
+ curl \
5
+ && rm -rf /var/lib/apt/lists/*
6
+
7
+ WORKDIR /app
8
+
9
+ COPY package.json package-lock.json* ./
10
+
11
+ RUN npm install --omit=dev
12
+
13
+ COPY . .
14
+
15
+ RUN npm run build
16
+
17
+ EXPOSE 80
18
+
19
+ CMD ["npx", "next", "start", "-p", "80"]
@@ -73,11 +73,11 @@ PROMPT="
73
73
 
74
74
  $(cat /job/logs/${JOB_ID}/job.md)"
75
75
 
76
- PROVIDER="${PROVIDER:-anthropic}"
76
+ LLM_PROVIDER="${LLM_PROVIDER:-anthropic}"
77
77
 
78
- MODEL_FLAGS="--provider $PROVIDER"
79
- if [ -n "$MODEL" ]; then
80
- MODEL_FLAGS="$MODEL_FLAGS --model $MODEL"
78
+ MODEL_FLAGS="--provider $LLM_PROVIDER"
79
+ if [ -n "$LLM_MODEL" ]; then
80
+ MODEL_FLAGS="$MODEL_FLAGS --model $LLM_MODEL"
81
81
  fi
82
82
 
83
83
  # Copy custom models.json to PI's global config if present in repo
@@ -0,0 +1,38 @@
1
+ FROM ubuntu:24.04
2
+
3
+ ARG RUNNER_VERSION=2.321.0
4
+ ARG RUNNER_ARCH=x64
5
+
6
+ RUN apt-get update && apt-get install -y \
7
+ curl \
8
+ jq \
9
+ git \
10
+ ca-certificates \
11
+ gnupg \
12
+ && rm -rf /var/lib/apt/lists/*
13
+
14
+ # Install Docker CLI (talks to host daemon via socket)
15
+ RUN install -m 0755 -d /etc/apt/keyrings \
16
+ && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
17
+ && chmod a+r /etc/apt/keyrings/docker.gpg \
18
+ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu noble stable" > /etc/apt/sources.list.d/docker.list \
19
+ && apt-get update && apt-get install -y docker-ce-cli \
20
+ && rm -rf /var/lib/apt/lists/*
21
+
22
+ # Create non-root runner user
23
+ RUN useradd -m -s /bin/bash runner \
24
+ && usermod -aG root runner
25
+
26
+ # Download and extract GitHub Actions runner
27
+ WORKDIR /home/runner
28
+ RUN curl -fsSL "https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-${RUNNER_ARCH}-${RUNNER_VERSION}.tar.gz" \
29
+ | tar xz \
30
+ && ./bin/installdependencies.sh \
31
+ && chown -R runner:runner /home/runner
32
+
33
+ COPY entrypoint.sh /entrypoint.sh
34
+ RUN chmod +x /entrypoint.sh
35
+
36
+ USER runner
37
+
38
+ ENTRYPOINT ["/entrypoint.sh"]
@@ -0,0 +1,41 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ # Required environment variables
5
+ : "${REPO_URL:?REPO_URL is required}"
6
+ : "${ACCESS_TOKEN:?ACCESS_TOKEN is required}"
7
+
8
+ RUNNER_NAME="${RUNNER_NAME:-thepopebot-runner}"
9
+ LABELS="${LABELS:-self-hosted,thepopebot}"
10
+
11
+ # Generate a registration token from the PAT
12
+ REG_TOKEN=$(curl -s -X POST \
13
+ -H "Authorization: token ${ACCESS_TOKEN}" \
14
+ -H "Accept: application/vnd.github.v3+json" \
15
+ "${REPO_URL/github.com/api.github.com/repos}/actions/runners/registration-token" \
16
+ | jq -r '.token')
17
+
18
+ if [ "$REG_TOKEN" = "null" ] || [ -z "$REG_TOKEN" ]; then
19
+ echo "Failed to get registration token. Check ACCESS_TOKEN permissions."
20
+ exit 1
21
+ fi
22
+
23
+ # Configure the runner
24
+ ./config.sh \
25
+ --url "$REPO_URL" \
26
+ --token "$REG_TOKEN" \
27
+ --name "$RUNNER_NAME" \
28
+ --labels "$LABELS" \
29
+ --unattended \
30
+ --replace
31
+
32
+ # Deregister on shutdown
33
+ cleanup() {
34
+ echo "Removing runner..."
35
+ ./config.sh remove --token "$REG_TOKEN" 2>/dev/null || true
36
+ }
37
+ trap cleanup SIGTERM SIGINT
38
+
39
+ # Start the runner (blocks until stopped)
40
+ ./run.sh &
41
+ wait $!
@@ -0,0 +1,52 @@
1
+ services:
2
+ traefik:
3
+ image: traefik:v3
4
+ command:
5
+ - --providers.docker=true
6
+ - --providers.docker.exposedByDefault=false
7
+ - --entrypoints.web.address=:80
8
+ - --entrypoints.websecure.address=:443
9
+ - --entrypoints.web.http.redirections.entrypoint.to=websecure
10
+ - --entrypoints.web.http.redirections.entrypoint.scheme=https
11
+ - --certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL:-}
12
+ - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
13
+ - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
14
+ ports:
15
+ - "80:80"
16
+ - "443:443"
17
+ volumes:
18
+ - /var/run/docker.sock:/var/run/docker.sock:ro
19
+ - traefik_certs:/letsencrypt
20
+ restart: unless-stopped
21
+
22
+ event_handler:
23
+ image: ${EVENT_HANDLER_IMAGE_URL:-stephengpope/thepopebot-event-handler:latest}
24
+ volumes:
25
+ - ./.env:/app/.env
26
+ - ./config:/app/config
27
+ - ./data:/app/data
28
+ - ./cron:/app/cron
29
+ - ./triggers:/app/triggers
30
+ - ./logs:/app/logs
31
+ labels:
32
+ - traefik.enable=true
33
+ # Set APP_HOSTNAME in .env to the domain from APP_URL (e.g., mybot.example.com)
34
+ - traefik.http.routers.event_handler.rule=Host(`${APP_HOSTNAME}`)
35
+ - traefik.http.routers.event_handler.entrypoints=websecure
36
+ - traefik.http.routers.event_handler.tls.certresolver=letsencrypt
37
+ - traefik.http.services.event_handler.loadbalancer.server.port=80
38
+ restart: unless-stopped
39
+
40
+ runner:
41
+ image: ${RUNNER_IMAGE_URL:-stephengpope/thepopebot-runner:latest}
42
+ environment:
43
+ REPO_URL: https://github.com/${GH_OWNER}/${GH_REPO}
44
+ ACCESS_TOKEN: ${GH_TOKEN}
45
+ RUNNER_SCOPE: repo
46
+ LABELS: self-hosted,thepopebot
47
+ volumes:
48
+ - /var/run/docker.sock:/var/run/docker.sock
49
+ restart: unless-stopped
50
+
51
+ volumes:
52
+ traefik_certs:
@@ -1 +1,6 @@
1
- export { register } from 'thepopebot/instrumentation';
1
+ export async function register() {
2
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
3
+ const { register } = await import('thepopebot/instrumentation');
4
+ await register();
5
+ }
6
+ }
@@ -0,0 +1 @@
1
+ export { middleware, config } from 'thepopebot/middleware';
@@ -0,0 +1,5 @@
1
+ export default {
2
+ plugins: {
3
+ '@tailwindcss/postcss': {},
4
+ },
5
+ };