thepopebot 1.2.76-beta.20 → 1.2.76-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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): Run `npm run setup-telegram` to connect a Telegram bot
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;
@@ -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: "Default Coding Agent" }),
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">Default Coding Agent</h2>
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: "general", label: "General", href: "/admin/general", icon: SettingsIcon }
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: 'general', label: 'General', href: '/admin/general', icon: SettingsIcon },
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thepopebot",
3
- "version": "1.2.76-beta.20",
3
+ "version": "1.2.76-beta.21",
4
4
  "type": "module",
5
5
  "description": "Create autonomous AI agents with a two-layer architecture: Next.js Event Handler + Docker Agent.",
6
6
  "bin": {
@@ -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, secret: 'AGENT_GH_TOKEN' },
19
- ANTHROPIC_API_KEY: { dbSecret: true, secret: 'AGENT_ANTHROPIC_API_KEY' },
20
- OPENAI_API_KEY: { dbSecret: true, secret: 'AGENT_OPENAI_API_KEY' },
21
- GOOGLE_API_KEY: { dbSecret: true, secret: 'AGENT_GOOGLE_API_KEY' },
22
- CUSTOM_API_KEY: { dbSecret: true, secret: 'AGENT_CUSTOM_API_KEY' },
23
- MOONSHOT_API_KEY: { dbSecret: true, secret: 'AGENT_MOONSHOT_API_KEY' },
24
- CLAUDE_CODE_OAUTH_TOKEN: { dbSecret: true, secret: 'AGENT_CLAUDE_CODE_OAUTH_TOKEN' },
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, variable: true },
31
- LLM_MODEL: { db: true, variable: true },
32
- CUSTOM_OPENAI_BASE_URL: { db: true, variable: true },
33
- AGENT_BACKEND: { db: true, variable: 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-only
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 },
@@ -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
- });