thepopebot 1.2.76-beta.20 → 1.2.76-beta.22
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 +3 -3
- package/bin/CLAUDE.md +0 -1
- package/bin/cli.js +0 -14
- package/lib/chat/actions.js +2 -1
- package/lib/chat/components/settings-coding-agents-page.js +2 -2
- package/lib/chat/components/settings-coding-agents-page.jsx +2 -2
- package/lib/chat/components/settings-github-page.js +5 -0
- package/lib/chat/components/settings-github-page.jsx +5 -0
- package/lib/chat/components/settings-layout.js +3 -3
- package/lib/chat/components/settings-layout.jsx +3 -3
- package/lib/chat/components/settings-secrets-layout.js +0 -1
- package/lib/chat/components/settings-secrets-layout.jsx +0 -1
- package/lib/code/terminal-view.js +26 -8
- package/lib/code/terminal-view.jsx +31 -8
- package/package.json +1 -1
- package/setup/lib/targets.mjs +12 -13
- package/setup/setup-telegram.mjs +0 -229
package/README.md
CHANGED
|
@@ -104,7 +104,7 @@ The wizard walks you through everything:
|
|
|
104
104
|
**That's it.** Visit your APP_URL when the wizard finishes.
|
|
105
105
|
|
|
106
106
|
- **Web Chat**: Visit your APP_URL to chat with your agent, create jobs, upload files
|
|
107
|
-
- **Telegram** (optional):
|
|
107
|
+
- **Telegram** (optional): Connect a Telegram bot from `/admin/event-handler/telegram`
|
|
108
108
|
- **Webhook**: Send a POST to `/api/create-agent-job` with your API key to create jobs programmatically
|
|
109
109
|
- **Cron**: Edit `agent-job/CRONS.json` to schedule recurring jobs
|
|
110
110
|
|
|
@@ -143,9 +143,9 @@ See [Coding Agents](docs/CODING_AGENTS.md) for details on all five agent backend
|
|
|
143
143
|
> ```bash
|
|
144
144
|
> # Update .env and GitHub variable in one command:
|
|
145
145
|
> npx thepopebot set-var APP_URL https://your-new-url.ngrok.io
|
|
146
|
-
> # If Telegram is configured, re-register the webhook:
|
|
147
|
-
> npm run setup-telegram
|
|
148
146
|
> ```
|
|
147
|
+
>
|
|
148
|
+
> If Telegram is configured, click **Re-register webhook** at `/admin/event-handler/telegram` after the URL change.
|
|
149
149
|
|
|
150
150
|
---
|
|
151
151
|
|
package/bin/CLAUDE.md
CHANGED
|
@@ -8,7 +8,6 @@ Entry point: `cli.js` (invoked via `npx thepopebot <command>`).
|
|
|
8
8
|
|---------|---------|
|
|
9
9
|
| `init [--no-managed] [--no-install]` | Scaffold project from templates, sync managed files, create `.env`, install deps |
|
|
10
10
|
| `setup` | Run interactive setup wizard (see `setup/CLAUDE.md`) |
|
|
11
|
-
| `setup-telegram` | Reconfigure Telegram webhook |
|
|
12
11
|
| `setup-ssl` | Configure SSL with Let's Encrypt wildcard cert |
|
|
13
12
|
| `upgrade [@beta\|version]` | Upgrade package, run init, rebuild, commit, push, restart Docker |
|
|
14
13
|
| `reset [file]` | Restore a template file to defaults |
|
package/bin/cli.js
CHANGED
|
@@ -53,7 +53,6 @@ Commands:
|
|
|
53
53
|
upgrade|update [@beta|version] Upgrade thepopebot (install, init, build, commit, push)
|
|
54
54
|
setup Run interactive setup wizard
|
|
55
55
|
setup-ssl Configure SSL with Let's Encrypt wildcard cert
|
|
56
|
-
setup-telegram Reconfigure Telegram webhook
|
|
57
56
|
reset-auth Regenerate AUTH_SECRET (invalidates all sessions)
|
|
58
57
|
reset [file] Restore a template file (or list available templates)
|
|
59
58
|
reset-all Nuclear reset — restore entire project to fresh init state
|
|
@@ -256,7 +255,6 @@ async function init(options = {}) {
|
|
|
256
255
|
type: 'module',
|
|
257
256
|
scripts: {
|
|
258
257
|
setup: 'thepopebot setup',
|
|
259
|
-
'setup-telegram': 'thepopebot setup-telegram',
|
|
260
258
|
'reset-auth': 'thepopebot reset-auth',
|
|
261
259
|
},
|
|
262
260
|
dependencies: {
|
|
@@ -783,15 +781,6 @@ function setupSsl() {
|
|
|
783
781
|
}
|
|
784
782
|
}
|
|
785
783
|
|
|
786
|
-
function setupTelegram() {
|
|
787
|
-
const setupScript = path.join(__dirname, '..', 'setup', 'setup-telegram.mjs');
|
|
788
|
-
try {
|
|
789
|
-
execFileSync(process.execPath, [setupScript], { stdio: 'inherit', cwd: process.cwd() });
|
|
790
|
-
} catch {
|
|
791
|
-
process.exit(1);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
|
|
795
784
|
async function resetAuth() {
|
|
796
785
|
const { randomBytes } = await import('crypto');
|
|
797
786
|
const { updateEnvVariable } = await import(path.join(__dirname, '..', 'setup', 'lib', 'auth.mjs'));
|
|
@@ -1066,9 +1055,6 @@ switch (command) {
|
|
|
1066
1055
|
case 'setup-ssl':
|
|
1067
1056
|
setupSsl();
|
|
1068
1057
|
break;
|
|
1069
|
-
case 'setup-telegram':
|
|
1070
|
-
setupTelegram();
|
|
1071
|
-
break;
|
|
1072
1058
|
case 'reset-auth':
|
|
1073
1059
|
await resetAuth();
|
|
1074
1060
|
break;
|
package/lib/chat/actions.js
CHANGED
|
@@ -1373,6 +1373,7 @@ export async function getGitHubConfig() {
|
|
|
1373
1373
|
}
|
|
1374
1374
|
|
|
1375
1375
|
// Build variables list: known names + any extras, with current value
|
|
1376
|
+
const variablesError = !Array.isArray(remoteVariables) ? (remoteVariables?.error || 'Failed to load variables') : null;
|
|
1376
1377
|
const remoteVarMap = new Map(
|
|
1377
1378
|
Array.isArray(remoteVariables) ? remoteVariables.map((v) => [v.name, v.value]) : []
|
|
1378
1379
|
);
|
|
@@ -1383,7 +1384,7 @@ export async function getGitHubConfig() {
|
|
|
1383
1384
|
}
|
|
1384
1385
|
}
|
|
1385
1386
|
|
|
1386
|
-
return { secrets, variables };
|
|
1387
|
+
return { secrets, variables, variablesError };
|
|
1387
1388
|
}
|
|
1388
1389
|
|
|
1389
1390
|
/**
|
|
@@ -68,12 +68,12 @@ function DefaultAgentSection({ settings, onReload }) {
|
|
|
68
68
|
};
|
|
69
69
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
70
70
|
/* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
|
|
71
|
-
/* @__PURE__ */ jsx("h2", { className: "text-base font-medium", children: "
|
|
71
|
+
/* @__PURE__ */ jsx("h2", { className: "text-base font-medium", children: "Coding Agent" }),
|
|
72
72
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Select which coding agent runs headless tasks and code workspaces." })
|
|
73
73
|
] }),
|
|
74
74
|
/* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4 space-y-3", children: [
|
|
75
75
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
76
|
-
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium shrink-0", children: "Agent" }),
|
|
76
|
+
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium shrink-0", children: "Default Coding Agent" }),
|
|
77
77
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
78
78
|
saving && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Saving..." }),
|
|
79
79
|
saved && /* @__PURE__ */ jsxs("span", { className: "text-xs text-green-500 inline-flex items-center gap-1", children: [
|
|
@@ -91,12 +91,12 @@ function DefaultAgentSection({ settings, onReload }) {
|
|
|
91
91
|
return (
|
|
92
92
|
<div>
|
|
93
93
|
<div className="mb-4">
|
|
94
|
-
<h2 className="text-base font-medium">
|
|
94
|
+
<h2 className="text-base font-medium">Coding Agent</h2>
|
|
95
95
|
<p className="text-sm text-muted-foreground">Select which coding agent runs headless tasks and code workspaces.</p>
|
|
96
96
|
</div>
|
|
97
97
|
<div className="rounded-lg border bg-card p-4 space-y-3">
|
|
98
98
|
<div className="flex items-center justify-between">
|
|
99
|
-
<label className="text-sm font-medium shrink-0">Agent</label>
|
|
99
|
+
<label className="text-sm font-medium shrink-0">Default Coding Agent</label>
|
|
100
100
|
<div className="flex items-center gap-3">
|
|
101
101
|
{saving && <span className="text-xs text-muted-foreground">Saving...</span>}
|
|
102
102
|
{saved && <span className="text-xs text-green-500 inline-flex items-center gap-1"><CheckIcon size={12} /> Saved</span>}
|
|
@@ -354,6 +354,11 @@ function GitHubVariablesPage() {
|
|
|
354
354
|
onCancel: () => setShowAdd(false)
|
|
355
355
|
}
|
|
356
356
|
),
|
|
357
|
+
data.variablesError && /* @__PURE__ */ jsxs("div", { className: "mb-4 rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2 text-xs text-destructive", children: [
|
|
358
|
+
"Couldn't read GitHub variables: ",
|
|
359
|
+
data.variablesError,
|
|
360
|
+
". Statuses below may be inaccurate. Check that the GitHub PAT has Variables: Read scope."
|
|
361
|
+
] }),
|
|
357
362
|
/* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-4", children: /* @__PURE__ */ jsx("div", { className: "divide-y divide-border", children: data.variables.map((v) => /* @__PURE__ */ jsx(VariableRow, { name: v.name, isSet: v.isSet, currentValue: v.value, onUpdate: handleUpdate, onDelete: handleDelete }, v.name)) }) })
|
|
358
363
|
] });
|
|
359
364
|
}
|
|
@@ -389,6 +389,11 @@ export function GitHubVariablesPage() {
|
|
|
389
389
|
onAdd={handleUpdate}
|
|
390
390
|
onCancel={() => setShowAdd(false)}
|
|
391
391
|
/>
|
|
392
|
+
{data.variablesError && (
|
|
393
|
+
<div className="mb-4 rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2 text-xs text-destructive">
|
|
394
|
+
Couldn't read GitHub variables: {data.variablesError}. Statuses below may be inaccurate. Check that the GitHub PAT has Variables: Read scope.
|
|
395
|
+
</div>
|
|
396
|
+
)}
|
|
392
397
|
<div className="rounded-lg border bg-card p-4">
|
|
393
398
|
<div className="divide-y divide-border">
|
|
394
399
|
{data.variables.map((v) => (
|
|
@@ -4,12 +4,12 @@ import { useState, useEffect } from "react";
|
|
|
4
4
|
import { PageLayout } from "./page-layout.js";
|
|
5
5
|
import { UserIcon, ClockIcon, ZapIcon, MessageIcon, GitBranchIcon, SettingsIcon } from "./icons.js";
|
|
6
6
|
const TABS = [
|
|
7
|
+
{ id: "general", label: "General", href: "/admin/general", icon: SettingsIcon },
|
|
7
8
|
{ id: "event-handler", label: "Event Handler", href: "/admin/event-handler", icon: MessageIcon },
|
|
8
|
-
{ id: "github", label: "GitHub", href: "/admin/github", icon: GitBranchIcon },
|
|
9
|
-
{ id: "users", label: "Users", href: "/admin/users", icon: UserIcon },
|
|
10
9
|
{ id: "crons", label: "Crons", href: "/admin/crons", icon: ClockIcon },
|
|
11
10
|
{ id: "triggers", label: "Triggers", href: "/admin/triggers", icon: ZapIcon },
|
|
12
|
-
{ id: "
|
|
11
|
+
{ id: "users", label: "Users", href: "/admin/users", icon: UserIcon },
|
|
12
|
+
{ id: "github", label: "GitHub", href: "/admin/github", icon: GitBranchIcon }
|
|
13
13
|
];
|
|
14
14
|
function SettingsLayout({ session, children }) {
|
|
15
15
|
const [activePath, setActivePath] = useState("");
|
|
@@ -5,12 +5,12 @@ import { PageLayout } from './page-layout.js';
|
|
|
5
5
|
import { UserIcon, ClockIcon, ZapIcon, MessageIcon, GitBranchIcon, SettingsIcon } from './icons.js';
|
|
6
6
|
|
|
7
7
|
const TABS = [
|
|
8
|
+
{ id: 'general', label: 'General', href: '/admin/general', icon: SettingsIcon },
|
|
8
9
|
{ id: 'event-handler', label: 'Event Handler', href: '/admin/event-handler', icon: MessageIcon },
|
|
9
|
-
{ id: 'github', label: 'GitHub', href: '/admin/github', icon: GitBranchIcon },
|
|
10
|
-
{ id: 'users', label: 'Users', href: '/admin/users', icon: UserIcon },
|
|
11
10
|
{ id: 'crons', label: 'Crons', href: '/admin/crons', icon: ClockIcon },
|
|
12
11
|
{ id: 'triggers', label: 'Triggers', href: '/admin/triggers', icon: ZapIcon },
|
|
13
|
-
{ id: '
|
|
12
|
+
{ id: 'users', label: 'Users', href: '/admin/users', icon: UserIcon },
|
|
13
|
+
{ id: 'github', label: 'GitHub', href: '/admin/github', icon: GitBranchIcon },
|
|
14
14
|
];
|
|
15
15
|
|
|
16
16
|
export function SettingsLayout({ session, children }) {
|
|
@@ -37,7 +37,6 @@ const EVENT_HANDLER_TABS = [
|
|
|
37
37
|
];
|
|
38
38
|
const GITHUB_TABS = [
|
|
39
39
|
{ id: "tokens", label: "Tokens", href: "/admin/github/tokens" },
|
|
40
|
-
{ id: "secrets", label: "Secrets", href: "/admin/github/secrets" },
|
|
41
40
|
{ id: "variables", label: "Variables", href: "/admin/github/variables" }
|
|
42
41
|
];
|
|
43
42
|
function ApiKeysLayout({ children }) {
|
|
@@ -60,7 +60,6 @@ const EVENT_HANDLER_TABS = [
|
|
|
60
60
|
|
|
61
61
|
const GITHUB_TABS = [
|
|
62
62
|
{ id: 'tokens', label: 'Tokens', href: '/admin/github/tokens' },
|
|
63
|
-
{ id: 'secrets', label: 'Secrets', href: '/admin/github/secrets' },
|
|
64
63
|
{ id: 'variables', label: 'Variables', href: '/admin/github/variables' },
|
|
65
64
|
];
|
|
66
65
|
|
|
@@ -780,32 +780,50 @@ function TerminalView({ codeWorkspaceId, wsPath, isActive = true, showToolbar =
|
|
|
780
780
|
] }) })
|
|
781
781
|
] });
|
|
782
782
|
}
|
|
783
|
-
const STORAGE_KEY = "thepopebot-workspace-command
|
|
783
|
+
const STORAGE_KEY = "thepopebot-workspace-command";
|
|
784
|
+
const FALLBACK_BY_MODE = { agent: "push", code: "create-pr" };
|
|
784
785
|
function ToolbarCommandButton({ codeWorkspaceId, diffStats, onDiffStatsRefresh, onShowDiff }) {
|
|
786
|
+
const [chatMode, setChatMode] = useState("code");
|
|
787
|
+
useEffect(() => {
|
|
788
|
+
if (!codeWorkspaceId) return;
|
|
789
|
+
let cancelled = false;
|
|
790
|
+
fetch(`/code/${codeWorkspaceId}/chat-data`).then((r) => r.json()).then((data) => {
|
|
791
|
+
if (cancelled) return;
|
|
792
|
+
if (data?.chatMode) setChatMode(data.chatMode);
|
|
793
|
+
}).catch(() => {
|
|
794
|
+
});
|
|
795
|
+
return () => {
|
|
796
|
+
cancelled = true;
|
|
797
|
+
};
|
|
798
|
+
}, [codeWorkspaceId]);
|
|
799
|
+
const storageKey = `${STORAGE_KEY}:${chatMode}`;
|
|
785
800
|
const [selectedCommand, setSelectedCommandState] = useState(() => {
|
|
786
801
|
try {
|
|
787
|
-
return localStorage.getItem(
|
|
802
|
+
return localStorage.getItem(storageKey) || FALLBACK_BY_MODE[chatMode] || "create-pr";
|
|
788
803
|
} catch {
|
|
789
|
-
return "create-pr";
|
|
804
|
+
return FALLBACK_BY_MODE[chatMode] || "create-pr";
|
|
790
805
|
}
|
|
791
806
|
});
|
|
792
807
|
const setSelectedCommand = (cmd) => {
|
|
793
808
|
setSelectedCommandState(cmd);
|
|
794
809
|
try {
|
|
795
|
-
localStorage.setItem(
|
|
810
|
+
localStorage.setItem(storageKey, cmd);
|
|
796
811
|
} catch {
|
|
797
812
|
}
|
|
798
813
|
};
|
|
799
814
|
useEffect(() => {
|
|
800
815
|
let stored = null;
|
|
801
816
|
try {
|
|
802
|
-
stored = localStorage.getItem(
|
|
817
|
+
stored = localStorage.getItem(storageKey);
|
|
803
818
|
} catch {
|
|
804
819
|
}
|
|
805
|
-
if (stored)
|
|
820
|
+
if (stored) {
|
|
821
|
+
setSelectedCommandState(stored);
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
806
824
|
let cancelled = false;
|
|
807
825
|
import("../chat/actions.js").then(({ getModeGitActionDefault }) => {
|
|
808
|
-
getModeGitActionDefault(
|
|
826
|
+
getModeGitActionDefault(chatMode).then((val) => {
|
|
809
827
|
if (cancelled || !val) return;
|
|
810
828
|
setSelectedCommandState(val);
|
|
811
829
|
}).catch(() => {
|
|
@@ -815,7 +833,7 @@ function ToolbarCommandButton({ codeWorkspaceId, diffStats, onDiffStatsRefresh,
|
|
|
815
833
|
return () => {
|
|
816
834
|
cancelled = true;
|
|
817
835
|
};
|
|
818
|
-
}, []);
|
|
836
|
+
}, [chatMode, storageKey]);
|
|
819
837
|
const [commandRunning, setCommandRunning] = useState(false);
|
|
820
838
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
821
839
|
const [commandOutput, setCommandOutput] = useState("");
|
|
@@ -842,31 +842,54 @@ export default function TerminalView({ codeWorkspaceId, wsPath, isActive = true,
|
|
|
842
842
|
);
|
|
843
843
|
}
|
|
844
844
|
|
|
845
|
-
const STORAGE_KEY = 'thepopebot-workspace-command
|
|
845
|
+
const STORAGE_KEY = 'thepopebot-workspace-command';
|
|
846
|
+
const FALLBACK_BY_MODE = { agent: 'push', code: 'create-pr' };
|
|
846
847
|
|
|
847
848
|
function ToolbarCommandButton({ codeWorkspaceId, diffStats, onDiffStatsRefresh, onShowDiff }) {
|
|
849
|
+
// Resolve chatMode from the workspace's chat (matches code-mode-toggle's per-mode behavior).
|
|
850
|
+
// Defaults to 'code' until the fetch resolves, since code workspaces are most often code-mode.
|
|
851
|
+
const [chatMode, setChatMode] = useState('code');
|
|
852
|
+
|
|
853
|
+
useEffect(() => {
|
|
854
|
+
if (!codeWorkspaceId) return;
|
|
855
|
+
let cancelled = false;
|
|
856
|
+
fetch(`/code/${codeWorkspaceId}/chat-data`)
|
|
857
|
+
.then((r) => r.json())
|
|
858
|
+
.then((data) => {
|
|
859
|
+
if (cancelled) return;
|
|
860
|
+
if (data?.chatMode) setChatMode(data.chatMode);
|
|
861
|
+
})
|
|
862
|
+
.catch(() => {});
|
|
863
|
+
return () => { cancelled = true; };
|
|
864
|
+
}, [codeWorkspaceId]);
|
|
865
|
+
|
|
866
|
+
const storageKey = `${STORAGE_KEY}:${chatMode}`;
|
|
848
867
|
const [selectedCommand, setSelectedCommandState] = useState(() => {
|
|
849
|
-
try { return localStorage.getItem(
|
|
868
|
+
try { return localStorage.getItem(storageKey) || FALLBACK_BY_MODE[chatMode] || 'create-pr'; }
|
|
869
|
+
catch { return FALLBACK_BY_MODE[chatMode] || 'create-pr'; }
|
|
850
870
|
});
|
|
851
871
|
const setSelectedCommand = (cmd) => {
|
|
852
872
|
setSelectedCommandState(cmd);
|
|
853
|
-
try { localStorage.setItem(
|
|
873
|
+
try { localStorage.setItem(storageKey, cmd); } catch {}
|
|
854
874
|
};
|
|
855
875
|
|
|
856
|
-
//
|
|
876
|
+
// When chatMode resolves (or changes), reload preference from its storage key, then seed from admin default if unset.
|
|
857
877
|
useEffect(() => {
|
|
858
878
|
let stored = null;
|
|
859
|
-
try { stored = localStorage.getItem(
|
|
860
|
-
if (stored)
|
|
879
|
+
try { stored = localStorage.getItem(storageKey); } catch {}
|
|
880
|
+
if (stored) {
|
|
881
|
+
setSelectedCommandState(stored);
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
861
884
|
let cancelled = false;
|
|
862
885
|
import('../chat/actions.js').then(({ getModeGitActionDefault }) => {
|
|
863
|
-
getModeGitActionDefault(
|
|
886
|
+
getModeGitActionDefault(chatMode).then((val) => {
|
|
864
887
|
if (cancelled || !val) return;
|
|
865
888
|
setSelectedCommandState(val);
|
|
866
889
|
}).catch(() => {});
|
|
867
890
|
}).catch(() => {});
|
|
868
891
|
return () => { cancelled = true; };
|
|
869
|
-
}, []);
|
|
892
|
+
}, [chatMode, storageKey]);
|
|
870
893
|
const [commandRunning, setCommandRunning] = useState(false);
|
|
871
894
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
872
895
|
const [commandOutput, setCommandOutput] = useState('');
|
package/package.json
CHANGED
package/setup/lib/targets.mjs
CHANGED
|
@@ -15,22 +15,22 @@
|
|
|
15
15
|
*/
|
|
16
16
|
export const CONFIG_TARGETS = {
|
|
17
17
|
// Secrets → DB encrypted (never .env)
|
|
18
|
-
GH_TOKEN: { env: true, dbSecret: true
|
|
19
|
-
ANTHROPIC_API_KEY: { dbSecret: true
|
|
20
|
-
OPENAI_API_KEY: { dbSecret: true
|
|
21
|
-
GOOGLE_API_KEY: { dbSecret: true
|
|
22
|
-
CUSTOM_API_KEY: { dbSecret: true
|
|
23
|
-
MOONSHOT_API_KEY: { dbSecret: true
|
|
24
|
-
CLAUDE_CODE_OAUTH_TOKEN: { dbSecret: true
|
|
18
|
+
GH_TOKEN: { env: true, dbSecret: true },
|
|
19
|
+
ANTHROPIC_API_KEY: { dbSecret: true },
|
|
20
|
+
OPENAI_API_KEY: { dbSecret: true },
|
|
21
|
+
GOOGLE_API_KEY: { dbSecret: true },
|
|
22
|
+
CUSTOM_API_KEY: { dbSecret: true },
|
|
23
|
+
MOONSHOT_API_KEY: { dbSecret: true },
|
|
24
|
+
CLAUDE_CODE_OAUTH_TOKEN: { dbSecret: true },
|
|
25
25
|
GH_WEBHOOK_SECRET: { dbSecret: true, secret: true },
|
|
26
26
|
TELEGRAM_BOT_TOKEN: { dbSecret: true },
|
|
27
27
|
TELEGRAM_WEBHOOK_SECRET: { dbSecret: true },
|
|
28
28
|
|
|
29
29
|
// Plain config → DB (not .env)
|
|
30
|
-
LLM_PROVIDER: { db: true
|
|
31
|
-
LLM_MODEL: { db: true
|
|
32
|
-
CUSTOM_OPENAI_BASE_URL: { db: true
|
|
33
|
-
AGENT_BACKEND: { db: true
|
|
30
|
+
LLM_PROVIDER: { db: true },
|
|
31
|
+
LLM_MODEL: { db: true },
|
|
32
|
+
CUSTOM_OPENAI_BASE_URL: { db: true },
|
|
33
|
+
AGENT_BACKEND: { db: true },
|
|
34
34
|
|
|
35
35
|
// Infrastructure → .env only (needed before DB is available)
|
|
36
36
|
GH_OWNER: { env: true },
|
|
@@ -38,8 +38,7 @@ export const CONFIG_TARGETS = {
|
|
|
38
38
|
APP_URL: { env: true, variable: true },
|
|
39
39
|
APP_HOSTNAME: { env: true },
|
|
40
40
|
|
|
41
|
-
// GitHub
|
|
42
|
-
BRAVE_API_KEY: { secret: 'AGENT_LLM_BRAVE_API_KEY' },
|
|
41
|
+
// GitHub variables consumed by scaffolded workflows
|
|
43
42
|
AUTO_MERGE: { variable: true, default: 'true', firstRunOnly: true },
|
|
44
43
|
ALLOWED_PATHS: { variable: true, default: '/logs,/agents', firstRunOnly: true },
|
|
45
44
|
RUNS_ON: { variable: true },
|
package/setup/setup-telegram.mjs
DELETED
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
import * as clack from '@clack/prompts';
|
|
5
|
-
|
|
6
|
-
import { checkPrerequisites } from './lib/prerequisites.mjs';
|
|
7
|
-
import { setVariables, setSecrets } from './lib/github.mjs';
|
|
8
|
-
import { setTelegramWebhook, validateBotToken, getBotFatherURL } from './lib/telegram.mjs';
|
|
9
|
-
import { confirm, keepOrReconfigure, generateTelegramWebhookSecret, promptForOptionalKey, maskSecret, openOrShowURL } from './lib/prompts.mjs';
|
|
10
|
-
import { updateEnvVariable } from './lib/auth.mjs';
|
|
11
|
-
import { loadEnvFile } from './lib/env.mjs';
|
|
12
|
-
|
|
13
|
-
const logo = `
|
|
14
|
-
_____ _ ____ ____ _
|
|
15
|
-
|_ _| |__ ___| _ \\ ___ _ __ ___| __ ) ___ | |_
|
|
16
|
-
| | | '_ \\ / _ \\ |_) / _ \\| '_ \\ / _ \\ _ \\ / _ \\| __|
|
|
17
|
-
| | | | | | __/ __/ (_) | |_) | __/ |_) | (_) | |_
|
|
18
|
-
|_| |_| |_|\\___|_| \\___/| .__/ \\___|____/ \\___/ \\__|
|
|
19
|
-
|_|
|
|
20
|
-
`;
|
|
21
|
-
|
|
22
|
-
async function main() {
|
|
23
|
-
console.log(chalk.cyan(logo));
|
|
24
|
-
clack.intro('Telegram Setup');
|
|
25
|
-
clack.log.info('Connect a Telegram bot to your agent. This wizard will walk you through creating a bot, registering a webhook, and linking your chat.');
|
|
26
|
-
|
|
27
|
-
const TOTAL_STEPS = 5;
|
|
28
|
-
let currentStep = 0;
|
|
29
|
-
|
|
30
|
-
// Track values for summary
|
|
31
|
-
let botUsername = null;
|
|
32
|
-
let webhookUrl = null;
|
|
33
|
-
|
|
34
|
-
// ─── Step 1: Prerequisites ──────────────────────────────────────────
|
|
35
|
-
clack.log.step(`[${++currentStep}/${TOTAL_STEPS}] Checking prerequisites`);
|
|
36
|
-
clack.log.info('Verifying git remote and loading existing configuration.');
|
|
37
|
-
|
|
38
|
-
const prereqs = await checkPrerequisites();
|
|
39
|
-
|
|
40
|
-
if (!prereqs.git.remoteInfo) {
|
|
41
|
-
clack.log.error('Could not detect GitHub repository from git remote.');
|
|
42
|
-
clack.cancel('Run setup first to initialize your repository.');
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const { owner, repo } = prereqs.git.remoteInfo;
|
|
47
|
-
clack.log.success(`Repository: ${owner}/${repo}`);
|
|
48
|
-
|
|
49
|
-
const env = loadEnvFile();
|
|
50
|
-
|
|
51
|
-
if (env) {
|
|
52
|
-
clack.log.info('Existing .env detected — previously configured values can be skipped.');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// ─── Step 2: App URL ────────────────────────────────────────────────
|
|
56
|
-
clack.log.step(`[${++currentStep}/${TOTAL_STEPS}] App URL`);
|
|
57
|
-
clack.log.info('Your bot needs a public HTTPS URL so Telegram can deliver messages to it via webhook.');
|
|
58
|
-
clack.log.warn('Make sure your server is running and publicly accessible.');
|
|
59
|
-
|
|
60
|
-
let appUrl = null;
|
|
61
|
-
|
|
62
|
-
if (await keepOrReconfigure('APP_URL', env?.APP_URL || null)) {
|
|
63
|
-
appUrl = env.APP_URL;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (!appUrl) {
|
|
67
|
-
clack.log.info(
|
|
68
|
-
'Enter the public URL where your agent is running.\n' +
|
|
69
|
-
' Examples:\n' +
|
|
70
|
-
' ngrok: https://abc123.ngrok.io\n' +
|
|
71
|
-
' VPS: https://mybot.example.com\n' +
|
|
72
|
-
' PaaS: https://mybot.vercel.app'
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
while (!appUrl) {
|
|
76
|
-
const url = await clack.text({
|
|
77
|
-
message: 'Enter your APP_URL (https://...):',
|
|
78
|
-
validate: (input) => {
|
|
79
|
-
if (!input) return 'URL is required';
|
|
80
|
-
if (!input.startsWith('https://')) return 'URL must start with https://';
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
if (clack.isCancel(url)) {
|
|
84
|
-
clack.cancel('Setup cancelled.');
|
|
85
|
-
process.exit(0);
|
|
86
|
-
}
|
|
87
|
-
appUrl = url.replace(/\/$/, '');
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Update APP_URL and APP_HOSTNAME in .env
|
|
92
|
-
const appHostname = new URL(appUrl).hostname;
|
|
93
|
-
updateEnvVariable('APP_URL', appUrl);
|
|
94
|
-
updateEnvVariable('APP_HOSTNAME', appHostname);
|
|
95
|
-
clack.log.success('APP_URL saved to .env');
|
|
96
|
-
|
|
97
|
-
// Set APP_URL variable on GitHub
|
|
98
|
-
const urlSpinner = clack.spinner();
|
|
99
|
-
urlSpinner.start('Updating APP_URL variable on GitHub...');
|
|
100
|
-
const urlResult = await setVariables(owner, repo, { APP_URL: appUrl });
|
|
101
|
-
if (urlResult.APP_URL.success) {
|
|
102
|
-
urlSpinner.stop('APP_URL variable updated on GitHub');
|
|
103
|
-
} else {
|
|
104
|
-
urlSpinner.stop(`Failed: ${urlResult.APP_URL.error}`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// ─── Step 3: Bot Token ──────────────────────────────────────────────
|
|
108
|
-
clack.log.step(`[${++currentStep}/${TOTAL_STEPS}] Telegram Bot Token`);
|
|
109
|
-
clack.log.info('Your agent needs a Telegram bot token from @BotFather to send and receive messages.');
|
|
110
|
-
|
|
111
|
-
let token = null;
|
|
112
|
-
|
|
113
|
-
if (await keepOrReconfigure('Telegram Bot', env?.TELEGRAM_BOT_TOKEN ? maskSecret(env.TELEGRAM_BOT_TOKEN) : null)) {
|
|
114
|
-
// Validate existing token
|
|
115
|
-
token = env.TELEGRAM_BOT_TOKEN;
|
|
116
|
-
const validateSpinner = clack.spinner();
|
|
117
|
-
validateSpinner.start('Validating existing bot token...');
|
|
118
|
-
const validation = await validateBotToken(token);
|
|
119
|
-
if (validation.valid) {
|
|
120
|
-
botUsername = validation.botInfo.username;
|
|
121
|
-
validateSpinner.stop(`Bot: @${botUsername}`);
|
|
122
|
-
} else {
|
|
123
|
-
validateSpinner.stop(`Invalid token in .env: ${validation.error}`);
|
|
124
|
-
clack.log.warn('Existing token is invalid — you\'ll need to enter a new one.');
|
|
125
|
-
token = null;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (!token) {
|
|
130
|
-
clack.log.info(
|
|
131
|
-
'Create a Telegram bot via @BotFather:\n' +
|
|
132
|
-
' 1. Open @BotFather in Telegram\n' +
|
|
133
|
-
' 2. Send /newbot and follow the prompts\n' +
|
|
134
|
-
' 3. Copy the bot token'
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
await openOrShowURL(getBotFatherURL(), 'Telegram BotFather');
|
|
138
|
-
|
|
139
|
-
let tokenValid = false;
|
|
140
|
-
while (!tokenValid) {
|
|
141
|
-
const inputToken = await clack.password({
|
|
142
|
-
message: 'Telegram bot token:',
|
|
143
|
-
validate: (input) => {
|
|
144
|
-
if (!input) return 'Token is required';
|
|
145
|
-
if (!/^\d+:[A-Za-z0-9_-]+$/.test(input)) {
|
|
146
|
-
return 'Invalid format. Should be like 123456789:ABC-DEF...';
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
});
|
|
150
|
-
if (clack.isCancel(inputToken)) {
|
|
151
|
-
clack.cancel('Setup cancelled.');
|
|
152
|
-
process.exit(0);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const validateSpinner = clack.spinner();
|
|
156
|
-
validateSpinner.start('Validating bot token...');
|
|
157
|
-
const validation = await validateBotToken(inputToken);
|
|
158
|
-
|
|
159
|
-
if (!validation.valid) {
|
|
160
|
-
validateSpinner.stop(`Invalid token: ${validation.error}`);
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
token = inputToken;
|
|
165
|
-
botUsername = validation.botInfo.username;
|
|
166
|
-
validateSpinner.stop(`Bot: @${botUsername}`);
|
|
167
|
-
tokenValid = true;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Bug fix #146: save token to .env
|
|
172
|
-
updateEnvVariable('TELEGRAM_BOT_TOKEN', token);
|
|
173
|
-
|
|
174
|
-
// ─── Step 4: Webhook ────────────────────────────────────────────────
|
|
175
|
-
clack.log.step(`[${++currentStep}/${TOTAL_STEPS}] Register Webhook`);
|
|
176
|
-
clack.log.info('Registering a webhook tells Telegram where to send messages for your bot.');
|
|
177
|
-
|
|
178
|
-
// Handle webhook secret
|
|
179
|
-
let webhookSecret = env?.TELEGRAM_WEBHOOK_SECRET;
|
|
180
|
-
if (webhookSecret) {
|
|
181
|
-
clack.log.success('Using existing webhook secret');
|
|
182
|
-
} else {
|
|
183
|
-
webhookSecret = await generateTelegramWebhookSecret();
|
|
184
|
-
updateEnvVariable('TELEGRAM_WEBHOOK_SECRET', webhookSecret);
|
|
185
|
-
clack.log.success('Generated webhook secret');
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Register Telegram webhook
|
|
189
|
-
webhookUrl = `${appUrl}/api/telegram/webhook`;
|
|
190
|
-
const tgSpinner = clack.spinner();
|
|
191
|
-
tgSpinner.start('Registering Telegram webhook...');
|
|
192
|
-
const tgResult = await setTelegramWebhook(token, webhookUrl, webhookSecret);
|
|
193
|
-
if (tgResult.ok) {
|
|
194
|
-
tgSpinner.stop('Telegram webhook registered');
|
|
195
|
-
} else {
|
|
196
|
-
tgSpinner.stop(`Failed: ${tgResult.description}`);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// ─── Step 5: Optional Keys ──────────────────────────────────────────
|
|
200
|
-
clack.log.step(`[${++currentStep}/${TOTAL_STEPS}] Optional Keys`);
|
|
201
|
-
clack.log.info('An OpenAI API key enables voice message transcription via Whisper.');
|
|
202
|
-
|
|
203
|
-
if (await keepOrReconfigure('OpenAI key', env?.OPENAI_API_KEY ? maskSecret(env.OPENAI_API_KEY) : null)) {
|
|
204
|
-
// Keep existing
|
|
205
|
-
} else {
|
|
206
|
-
const openaiKey = await promptForOptionalKey('openai', 'voice messages');
|
|
207
|
-
if (openaiKey) {
|
|
208
|
-
updateEnvVariable('OPENAI_API_KEY', openaiKey);
|
|
209
|
-
const s = clack.spinner();
|
|
210
|
-
s.start('Setting OpenAI secret on GitHub...');
|
|
211
|
-
await setSecrets(owner, repo, { AGENT_OPENAI_API_KEY: openaiKey });
|
|
212
|
-
s.stop('OpenAI secret set');
|
|
213
|
-
clack.log.success(`OpenAI key added for voice (${maskSecret(openaiKey)})`);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// ─── Summary ────────────────────────────────────────────────────────
|
|
218
|
-
let summary = '';
|
|
219
|
-
summary += `Bot: @${botUsername || '(unknown)'}\n`;
|
|
220
|
-
summary += `Webhook: ${webhookUrl}`;
|
|
221
|
-
clack.note(summary, 'Telegram Configuration');
|
|
222
|
-
|
|
223
|
-
clack.outro('Telegram setup complete! Link your personal chat at /profile/telegram.');
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
main().catch((error) => {
|
|
227
|
-
clack.log.error(`Failed: ${error.message}`);
|
|
228
|
-
process.exit(1);
|
|
229
|
-
});
|