telegram-approval-buttons 5.0.0 → 5.1.0

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/CHANGELOG.md CHANGED
@@ -5,27 +5,30 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [5.0.0] - 2026-02-24
8
+ ## [5.1.0] - 2026-03-31
9
+
10
+ ### Added
11
+ - **OpenClaw 2026 approval format support** — parser now handles native `Approval required`, `Full id:`, tool-result `(id ..., full ...)`, and `/approve ...` guidance formats (#4)
12
+ - **Async completion delivery** — captures post-approval exec output when the agent emits `NO_REPLY` and delivers results to Telegram users instead of silently dropping them
13
+ - **Duplicate approval suppression** — detects and cancels assistant fallback `/approve` messages when a native approval prompt already exists, preventing double prompts
14
+
15
+ ### Fixed
16
+ - Broadened duplicate fallback suppression to cover all actions (`allow-once|allow-always|deny`)
17
+ - `NO_REPLY` delivery paths now reuse shared `escapeHtml` helper instead of manual `.replace()` chains
18
+ - Added null-result handling + warning logs for failed Telegram `NO_REPLY` sends
19
+ - Normalized `NO_REPLY` user-facing strings to English for consistency
9
20
 
10
- ### ⚠️ Breaking Changes
21
+ ### Contributors
11
22
 
12
- - **Plugin ID renamed**: `telegram-approval-buttons` `approval-buttons`
23
+ Thanks to [@BlazzzPlay](https://github.com/BlazzzPlay) for contributing OpenClaw 2026 compatibility and async completion delivery in this release 🙏
13
24
 
14
- If you're upgrading from v4.x, update your `~/.openclaw/openclaw.json`:
25
+ ## [5.0.1] - 2026-03-01
15
26
 
16
- ```diff
17
- "plugins": {
18
- "entries": {
19
- - "telegram-approval-buttons": {
20
- + "approval-buttons": {
21
- "enabled": true,
22
- ...
23
- }
24
- }
25
- }
26
- ```
27
+ ### Fixed
28
+ - **Reverted plugin ID to `telegram-approval-buttons`** — v5.0.0 renamed it to `approval-buttons`, causing a config warning on every startup. The plugin ID now matches the npm package name again. No config changes needed for users upgrading from v4.x.
29
+ - Normalized all file headers to use consistent naming
27
30
 
28
- Then restart: `openclaw gateway restart`
31
+ ## [5.0.0] - 2026-02-24
29
32
 
30
33
  ### Added
31
34
  - **Slack inline button support** — approval buttons now work in Slack via Block Kit interactive messages
@@ -36,7 +39,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
36
39
 
37
40
  ### Changed
38
41
  - **Cleaner approval messages** — removed verbose internal fields (Security, Ask, Host) from both Telegram and Slack formats. Messages now focus on: command, CWD, agent, and expiry
39
- - Plugin ID and TAG shortened to `approval-buttons`
40
42
 
41
43
  ### Contributors
42
44
 
package/README.md CHANGED
@@ -1,3 +1,9 @@
1
+
2
+ --Closed #30695.
3
+
4
+
5
+
6
+
1
7
  # 🔐 Approval Buttons for OpenClaw
2
8
 
3
9
  > One-tap `exec` approvals in **Telegram** and **Slack** — no more typing `/approve <uuid> allow-once`.
@@ -75,7 +81,7 @@ Open your `~/.openclaw/openclaw.json` and add two things:
75
81
  },
76
82
  "plugins": {
77
83
  "entries": {
78
- "approval-buttons": {
84
+ "telegram-approval-buttons": {
79
85
  "enabled": true,
80
86
  "config": {
81
87
  "botToken": "<your_bot_token>",
@@ -110,7 +116,7 @@ Pending: 0 · Processed: 0
110
116
  Uptime: 1m
111
117
  ```
112
118
 
113
- > ⚠️ **If you see `DISABLED — missing config`**, the plugin can't find your bot token or chat ID. Double-check that `botToken` and `chatId` are set in `plugins.entries.approval-buttons.config` in your `~/.openclaw/openclaw.json`.
119
+ > ⚠️ **If you see `DISABLED — missing config`**, the plugin can't find your bot token or chat ID. Double-check that `botToken` and `chatId` are set in `plugins.entries.telegram-approval-buttons.config` in your `~/.openclaw/openclaw.json`.
114
120
 
115
121
  **That's it!** Next time the AI triggers an `exec` approval, you'll get inline buttons instead of text.
116
122
 
@@ -162,7 +168,7 @@ The plugin **auto-detects** `botToken` and `chatId` from your Telegram channel c
162
168
  {
163
169
  "plugins": {
164
170
  "entries": {
165
- "approval-buttons": {
171
+ "telegram-approval-buttons": {
166
172
  "enabled": true,
167
173
  "config": {
168
174
  "chatId": "123456789", // Telegram chat ID
@@ -202,36 +208,17 @@ A: Yes, but the bot needs to be an admin or it needs permission to edit its own
202
208
 
203
209
  | Problem | Fix |
204
210
  |---------|-----|
205
- | `DISABLED — missing config` in logs | Add `botToken` and `chatId` to `plugins.entries.approval-buttons.config` in your `~/.openclaw/openclaw.json`. See Step 2. |
206
- | `plugin not found: telegram-approval-buttons` | You upgraded from v4.x. Rename the key in your config from `telegram-approval-buttons` to `approval-buttons`. See [Upgrading from v4.x](#upgrading-from-v4x). |
211
+ | `DISABLED — missing config` in logs | Add `botToken` and `chatId` to `plugins.entries.telegram-approval-buttons.config` in your `~/.openclaw/openclaw.json`. See Step 2. |
207
212
  | Still getting old text approvals | Your `approvals.exec` config must target Telegram. See Step 2. |
208
213
  | `/approvalstatus` says "unknown command" | Plugin didn't load. Run `openclaw plugins install telegram-approval-buttons` and restart the gateway. |
209
214
  | No buttons appear | Check `tools.exec.ask` is not `"off"`. Run `/approvalstatus` to check config. |
210
215
  | Buttons show but nothing happens | Bot needs message editing permission. Use a private chat or make bot admin. |
211
216
  | Buttons say "expired" | Approval timed out before you tapped. Adjust `staleMins` if needed. |
212
217
 
213
- ## Upgrading from v4.x
214
-
215
- v5.0.0 renamed the plugin ID from `telegram-approval-buttons` to `approval-buttons`. Update your `~/.openclaw/openclaw.json`:
216
-
217
- ```diff
218
- "plugins": {
219
- "entries": {
220
- - "telegram-approval-buttons": {
221
- + "approval-buttons": {
222
- "enabled": true,
223
- ...
224
- }
225
- }
226
- }
227
- ```
228
-
229
- Then restart: `openclaw gateway restart`
230
-
231
218
  ## Architecture
232
219
 
233
220
  ```
234
- approval-buttons/
221
+ telegram-approval-buttons/
235
222
  ├── index.ts # Entry point — orchestration only
236
223
  ├── types.ts # Shared TypeScript interfaces
237
224
  ├── lib/
package/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // ─────────────────────────────────────────────────────────────────────────────
2
- // approval-buttons · index.ts (v5.0.0)
2
+ // telegram-approval-buttons · index.ts (v5.1.0)
3
3
  // Plugin entry point — orchestration only, all logic lives in lib/
4
4
  //
5
5
  // Adds inline keyboard/button approval messages to Telegram and Slack.
@@ -16,6 +16,7 @@ import { SlackApi } from "./lib/slack-api.js";
16
16
  import { ApprovalStore } from "./lib/approval-store.js";
17
17
  import { parseApprovalText, detectApprovalResult } from "./lib/approval-parser.js";
18
18
  import {
19
+ escapeHtml,
19
20
  formatApprovalRequest,
20
21
  formatApprovalResolved,
21
22
  formatApprovalExpired,
@@ -37,8 +38,31 @@ import {
37
38
 
38
39
  // ── Constants ───────────────────────────────────────────────────────────────
39
40
 
40
- const PLUGIN_VERSION = "5.0.0";
41
- const TAG = "approval-buttons";
41
+ const PLUGIN_VERSION = "5.1.0";
42
+ const TAG = "telegram-approval-buttons";
43
+
44
+ const RE_RICH_APPROVAL_HEADER = /^\s*🔐\s+<b>Exec Approval<\/b>/i;
45
+ const RE_ASSISTANT_APPROVE_FALLBACK = /\/approve\s+[a-f0-9-]{8,}\s+(allow-once|allow-always|deny)\b/i;
46
+
47
+ function approvalIdVariants(id: string): string[] {
48
+ const clean = id.trim();
49
+ const short = clean.slice(0, 8);
50
+ if (!short || short === clean) return [clean];
51
+ return [clean, short];
52
+ }
53
+
54
+ type CompletionSnapshot = { output: string; capturedAt: number };
55
+ type ResolvedSnapshot = { id: string; command: string; resolvedAt: number };
56
+
57
+ const RE_ASYNC_COMPLETION = /An async command the user already approved has completed\./i;
58
+ const RE_COMPLETION_OUTPUT = /Exact completion details:\r?\nExec finished[^\r\n]*\r?\n([\s\S]+?)\r?\n\r?\nReply to the user/i;
59
+
60
+ function parseAsyncCompletionOutput(text: string): string | null {
61
+ if (!RE_ASYNC_COMPLETION.test(text)) return null;
62
+ const match = text.match(RE_COMPLETION_OUTPUT);
63
+ const output = match?.[1]?.trim();
64
+ return output || null;
65
+ }
42
66
 
43
67
  // ── Plugin registration ─────────────────────────────────────────────────────
44
68
 
@@ -103,25 +127,28 @@ function register(api: any): void {
103
127
  config.telegram.chatId,
104
128
  entry.messageId,
105
129
  formatApprovalExpired(entry.info),
106
- ).catch(() => {});
130
+ ).catch(() => { });
107
131
  } else if (entry.channel === "slack" && slack && config.slack) {
108
132
  slack.updateMessage(
109
133
  config.slack.channelId,
110
134
  entry.slackTs,
111
135
  "Exec Approval Expired",
112
136
  formatSlackApprovalExpired(entry.info),
113
- ).catch(() => {});
137
+ ).catch(() => { });
114
138
  }
115
139
  },
116
140
  );
117
141
 
142
+ let lastCompletionGlobal: CompletionSnapshot | null = null;
143
+ let lastResolvedGlobal: ResolvedSnapshot | null = null;
144
+
118
145
  // ─── 4. Register background service (cleanup timer) ──────────────────
119
146
 
120
147
  api.registerService({
121
148
  id: `${TAG}-cleanup`,
122
149
  start: () => {
123
150
  store.start();
124
- runStartupChecks(tg, slack, log).catch(() => {});
151
+ runStartupChecks(tg, slack, log).catch(() => { });
125
152
  },
126
153
  stop: () => store.stop(),
127
154
  });
@@ -141,6 +168,24 @@ function register(api: any): void {
141
168
 
142
169
  // ─── 6. Register message_sending hook ────────────────────────────────
143
170
 
171
+ api.on(
172
+ "message_received",
173
+ async (
174
+ event: { content: string },
175
+ _ctx: { channelId: string },
176
+ ) => {
177
+ const output = parseAsyncCompletionOutput(event.content);
178
+ if (!output) return;
179
+ lastCompletionGlobal = {
180
+ output,
181
+ capturedAt: Date.now(),
182
+ };
183
+ if (config.verbose) {
184
+ log.info(`[${TAG}] captured async completion output (${output.length} chars)`);
185
+ }
186
+ },
187
+ );
188
+
144
189
  api.on(
145
190
  "message_sending",
146
191
  async (
@@ -149,7 +194,17 @@ function register(api: any): void {
149
194
  ) => {
150
195
  // ── Telegram ──────────────────────────────────────────────────
151
196
  if (ctx.channelId === "telegram" && tg && config.telegram) {
152
- return handleTelegram(event, config.telegram.chatId, tg, store, log);
197
+ return handleTelegram(
198
+ event,
199
+ config.telegram.chatId,
200
+ tg,
201
+ store,
202
+ log,
203
+ () => lastCompletionGlobal,
204
+ () => { lastCompletionGlobal = null; },
205
+ () => lastResolvedGlobal,
206
+ (next) => { lastResolvedGlobal = next; },
207
+ );
153
208
  }
154
209
 
155
210
  // ── Slack ─────────────────────────────────────────────────────
@@ -159,6 +214,44 @@ function register(api: any): void {
159
214
  },
160
215
  );
161
216
 
217
+ // Some OpenClaw builds deliver approval prompts before plugins can cancel.
218
+ // In that case, rewrite the already-sent native approval message in-place
219
+ // to avoid a second "button message".
220
+ api.on(
221
+ "message_sent",
222
+ async (
223
+ event: { content: string; messageId?: string; metadata?: Record<string, unknown> },
224
+ ctx: { channelId: string },
225
+ ) => {
226
+ if (ctx.channelId !== "telegram" || !tg || !config.telegram) return;
227
+ if (RE_RICH_APPROVAL_HEADER.test(event.content)) return;
228
+
229
+ const info = parseApprovalText(event.content);
230
+ if (!info) return;
231
+
232
+ const rawMsgId =
233
+ event.messageId
234
+ ?? (typeof event.metadata?.message_id === "string" ? event.metadata.message_id : undefined)
235
+ ?? (typeof event.metadata?.messageId === "string" ? event.metadata.messageId : undefined);
236
+
237
+ const messageId = rawMsgId ? Number(rawMsgId) : NaN;
238
+ if (!Number.isFinite(messageId)) return;
239
+
240
+ const edited = await tg.editMessageText(
241
+ config.telegram.chatId,
242
+ messageId,
243
+ formatApprovalRequest(info),
244
+ buildApprovalKeyboard(info.id),
245
+ );
246
+ if (!edited) return;
247
+
248
+ if (!store.has(info.id)) {
249
+ store.add(info.id, "telegram", { messageId }, info);
250
+ }
251
+ log.info(`[${TAG}] telegram upgraded native approval ${info.id.slice(0, 8)}… (msg=${messageId})`);
252
+ },
253
+ );
254
+
162
255
  // ─── Done ─────────────────────────────────────────────────────────────
163
256
 
164
257
  const channels = [config.telegram && "Telegram", config.slack && "Slack"]
@@ -175,13 +268,65 @@ async function handleTelegram(
175
268
  tg: TelegramApi,
176
269
  store: ApprovalStore,
177
270
  log: any,
271
+ getLastCompletion: () => CompletionSnapshot | null,
272
+ clearLastCompletion: () => void,
273
+ getLastResolved: () => ResolvedSnapshot | null,
274
+ setLastResolved: (next: ResolvedSnapshot | null) => void,
178
275
  ): Promise<{ cancel: true } | void> {
276
+ // If the agent emits NO_REPLY right after an async exec completion event,
277
+ // convert it into a short delivery so Telegram users still get the result.
278
+ if (event.content.trim() === "NO_REPLY") {
279
+ const completion = getLastCompletion();
280
+ if (completion && Date.now() - completion.capturedAt <= 120_000) {
281
+ const sentId = await tg.sendMessage(
282
+ chatId,
283
+ [
284
+ "✅ <b>Command completed</b>",
285
+ "",
286
+ `<pre>${escapeHtml(completion.output)}</pre>`,
287
+ ].join("\n"),
288
+ );
289
+ if (sentId === null) {
290
+ log.warn(`[${TAG}] telegram failed to deliver NO_REPLY completion output`);
291
+ }
292
+ clearLastCompletion();
293
+ setLastResolved(null);
294
+ return { cancel: true };
295
+ }
296
+
297
+ const resolved = getLastResolved();
298
+ if (resolved && Date.now() - resolved.resolvedAt <= 120_000) {
299
+ const sentId = await tg.sendMessage(
300
+ chatId,
301
+ [
302
+ "✅ <b>Command completed</b>",
303
+ "",
304
+ `<code>${escapeHtml(resolved.command)}</code>`,
305
+ "",
306
+ "Output was not forwarded by the agent (NO_REPLY).",
307
+ ].join("\n"),
308
+ );
309
+ if (sentId === null) {
310
+ log.warn(`[${TAG}] telegram failed to deliver NO_REPLY fallback notice`);
311
+ }
312
+ setLastResolved(null);
313
+ return { cancel: true };
314
+ }
315
+
316
+ if (log?.info) log.info(`[${TAG}] NO_REPLY without completion snapshot`);
317
+ }
318
+
179
319
  // Check for approval resolution
180
320
  const resolution = detectApprovalResult(event.content, store.entries());
181
321
  if (resolution) {
182
322
  const entry = store.resolve(resolution.id);
183
323
  if (entry && entry.channel === "telegram") {
184
324
  log.info(`[${TAG}] telegram resolved ${resolution.id.slice(0, 8)}… → ${resolution.action}`);
325
+ setLastResolved({
326
+ id: resolution.id,
327
+ command: entry.info.command,
328
+ resolvedAt: Date.now(),
329
+ });
185
330
  await tg.editMessageText(
186
331
  chatId,
187
332
  entry.messageId,
@@ -195,6 +340,27 @@ async function handleTelegram(
195
340
  const info = parseApprovalText(event.content);
196
341
  if (!info) return;
197
342
 
343
+ const looksLikeAssistantFallback =
344
+ RE_ASSISTANT_APPROVE_FALLBACK.test(event.content)
345
+ && !/Exec approval required/i.test(event.content)
346
+ && !/\bApproval required\b/i.test(event.content);
347
+
348
+ // Newer OpenClaw builds already send a native approval card/message.
349
+ // Suppress the assistant's plain `/approve ...` fallback so users don't
350
+ // receive a second approval prompt.
351
+ if (looksLikeAssistantFallback) return { cancel: true };
352
+
353
+ // If this is the model's fallback "/approve ..." guidance and we already
354
+ // converted the native approval prompt, suppress this duplicate text.
355
+ for (const variant of approvalIdVariants(info.id)) {
356
+ if (store.has(variant)) return { cancel: true };
357
+ for (const pendingId of store.entries().keys()) {
358
+ if (pendingId.startsWith(variant) || variant.startsWith(pendingId)) {
359
+ return { cancel: true };
360
+ }
361
+ }
362
+ }
363
+
198
364
  if (store.has(info.id)) return { cancel: true };
199
365
 
200
366
  log.info(`[${TAG}] telegram intercepting ${info.id.slice(0, 8)}…`);
@@ -265,8 +431,8 @@ async function handleSlack(
265
431
  // ─── Plugin export ──────────────────────────────────────────────────────────
266
432
 
267
433
  export default {
268
- id: "approval-buttons",
269
- name: "Approval Buttons",
434
+ id: "telegram-approval-buttons",
435
+ name: "Telegram Approval Buttons",
270
436
  description:
271
437
  "Adds inline buttons to exec approval messages in Telegram and Slack. " +
272
438
  "Tap to approve/deny without typing commands.",
@@ -1,5 +1,5 @@
1
1
  // ─────────────────────────────────────────────────────────────────────────────
2
- // approval-buttons · lib/approval-parser.ts
2
+ // telegram-approval-buttons · lib/approval-parser.ts
3
3
  // Parse OpenClaw's plain-text exec approval format into structured data
4
4
  // ─────────────────────────────────────────────────────────────────────────────
5
5
 
@@ -8,11 +8,15 @@ import type { ApprovalAction, ApprovalInfo, ApprovalResolution, SentApproval } f
8
8
  // ─── Regex patterns (compiled once) ─────────────────────────────────────────
9
9
 
10
10
  const RE_APPROVAL_MARKER = /Exec approval required/i;
11
+ const RE_NATIVE_APPROVAL_MARKER = /\bApproval required\b/i;
12
+ const RE_TOOLRESULT_APPROVAL = /Approval required\s*\(id\s+([a-f0-9-]{8,})\s*,\s*full\s+([a-f0-9-]{8,})\)/i;
11
13
  const RE_ID = /ID:\s*([a-f0-9-]+)/i;
14
+ const RE_FULL_ID = /Full id:\s*([a-f0-9-]{8,})/i;
15
+ const RE_APPROVE_CMD = /\/approve\s+([a-f0-9-]{8,})\s+(allow-once|allow-always|deny)\b/i;
12
16
  const RE_UUID = /([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/i;
13
17
  const RE_SHORT_HEX = /\b([a-f0-9]{8,})\b/i;
14
18
  const RE_GATEWAY_DENIAL = /Exec denied.*approval-timeout/i;
15
- const RE_COMMAND_BLOCK = /Command:\s*`{0,3}\n?(.+?)\n?`{0,3}(?:\n|$)/is;
19
+ const RE_COMMAND_BLOCK = /Command:\s*```(?:[a-z0-9_+-]+)?\n([\s\S]+?)\n```/i;
16
20
  const RE_COMMAND_INLINE = /Command:\s*(.+)/i;
17
21
  const RE_CWD = /CWD:\s*(.+)/i;
18
22
  const RE_HOST = /Host:\s*(.+)/i;
@@ -20,6 +24,7 @@ const RE_AGENT = /Agent:\s*(.+)/i;
20
24
  const RE_SECURITY = /Security:\s*(.+)/i;
21
25
  const RE_ASK = /Ask:\s*(.+)/i;
22
26
  const RE_EXPIRES = /Expires in:\s*(.+)/i;
27
+ const RE_PENDING_COMMAND = /Pending command:\s*`{0,3}\n?(.+?)\n?(?:`{0,3})(?:\n|$)/is;
23
28
 
24
29
  /**
25
30
  * Parse OpenClaw's plain-text approval message into an ApprovalInfo object.
@@ -29,14 +34,32 @@ const RE_EXPIRES = /Expires in:\s*(.+)/i;
29
34
  * falls back to sensible defaults for missing fields.
30
35
  */
31
36
  export function parseApprovalText(text: string): ApprovalInfo | null {
32
- if (!RE_APPROVAL_MARKER.test(text)) return null;
37
+ const hasLegacyMarker = RE_APPROVAL_MARKER.test(text);
38
+ const hasNativeMarker = RE_NATIVE_APPROVAL_MARKER.test(text);
39
+ const toolResultMatch = text.match(RE_TOOLRESULT_APPROVAL);
40
+
41
+ let id = hasLegacyMarker ? text.match(RE_ID)?.[1]?.trim() : undefined;
42
+
43
+ if (!id && toolResultMatch) {
44
+ id = toolResultMatch[2]?.trim() || toolResultMatch[1]?.trim();
45
+ }
46
+
47
+ if (!id && hasNativeMarker) {
48
+ id = text.match(RE_FULL_ID)?.[1]?.trim();
49
+ }
50
+
51
+ // OpenClaw 2026 format often delivers plain guidance text like:
52
+ // `/approve <id> allow-once` (with or without markdown backticks).
53
+ if (!id) {
54
+ id = text.match(RE_APPROVE_CMD)?.[1]?.trim();
55
+ }
33
56
 
34
- const id = text.match(RE_ID)?.[1]?.trim();
35
57
  if (!id) return null;
36
58
 
37
- // Try block format first (```command```), then inline
38
- let command = text.match(RE_COMMAND_BLOCK)?.[1]?.trim();
39
- if (!command) command = text.match(RE_COMMAND_INLINE)?.[1]?.trim() ?? "unknown";
59
+ const command = text.match(RE_COMMAND_BLOCK)?.[1]?.trim()
60
+ ?? text.match(RE_PENDING_COMMAND)?.[1]?.trim()
61
+ ?? text.match(RE_COMMAND_INLINE)?.[1]?.trim()
62
+ ?? "approval-pending";
40
63
 
41
64
  return {
42
65
  id,
@@ -1,5 +1,5 @@
1
1
  // ─────────────────────────────────────────────────────────────────────────────
2
- // approval-buttons · lib/approval-store.ts
2
+ // telegram-approval-buttons · lib/approval-store.ts
3
3
  // In-memory store for pending approvals with TTL-based cleanup
4
4
  // ─────────────────────────────────────────────────────────────────────────────
5
5
 
@@ -1,5 +1,5 @@
1
1
  // ─────────────────────────────────────────────────────────────────────────────
2
- // approval-buttons · lib/diagnostics.ts
2
+ // telegram-approval-buttons · lib/diagnostics.ts
3
3
  // Self-diagnostics: config validation, connectivity check, auto-repair
4
4
  // ─────────────────────────────────────────────────────────────────────────────
5
5
 
@@ -201,7 +201,7 @@ export function logStartupDiagnostics(
201
201
 
202
202
  log.info(
203
203
  `[diagnostics] Config OK → ${channels.join(", ")}, ` +
204
- `staleMins=${config.staleMins}, verbose=${config.verbose}`,
204
+ `staleMins=${config.staleMins}, verbose=${config.verbose}`,
205
205
  );
206
206
  }
207
207
 
@@ -1,5 +1,5 @@
1
1
  // ─────────────────────────────────────────────────────────────────────────────
2
- // approval-buttons · lib/message-formatter.ts
2
+ // telegram-approval-buttons · lib/message-formatter.ts
3
3
  // HTML message formatting for Telegram (approval requests & resolutions)
4
4
  // ─────────────────────────────────────────────────────────────────────────────
5
5
 
package/lib/slack-api.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // ─────────────────────────────────────────────────────────────────────────────
2
- // approval-buttons · lib/slack-api.ts
2
+ // telegram-approval-buttons · lib/slack-api.ts
3
3
  // Isolated Slack Web API wrapper — only depends on fetch (Node built-in)
4
4
  // ─────────────────────────────────────────────────────────────────────────────
5
5
 
@@ -63,7 +63,7 @@ export class SlackApi {
63
63
  constructor(
64
64
  private readonly token: string,
65
65
  private readonly log?: Logger,
66
- ) {}
66
+ ) { }
67
67
 
68
68
  // ── Connectivity ────────────────────────────────────────────────────────
69
69
 
@@ -1,5 +1,5 @@
1
1
  // ─────────────────────────────────────────────────────────────────────────────
2
- // approval-buttons · lib/slack-formatter.ts
2
+ // telegram-approval-buttons · lib/slack-formatter.ts
3
3
  // Block Kit message formatting for Slack (approval requests & resolutions)
4
4
  // ─────────────────────────────────────────────────────────────────────────────
5
5
 
@@ -1,5 +1,5 @@
1
1
  // ─────────────────────────────────────────────────────────────────────────────
2
- // approval-buttons · lib/telegram-api.ts
2
+ // telegram-approval-buttons · lib/telegram-api.ts
3
3
  // Isolated Telegram Bot API wrapper — only depends on fetch (Node built-in)
4
4
  // ─────────────────────────────────────────────────────────────────────────────
5
5
 
@@ -1,8 +1,8 @@
1
1
  {
2
- "id": "approval-buttons",
3
- "name": "Approval Buttons",
2
+ "id": "telegram-approval-buttons",
3
+ "name": "Telegram Approval Buttons",
4
4
  "description": "Adds inline buttons to exec approval messages in Telegram and Slack. Tap to approve/deny without typing commands.",
5
- "version": "5.0.0",
5
+ "version": "5.1.0",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
@@ -69,4 +69,4 @@
69
69
  "advanced": true
70
70
  }
71
71
  }
72
- }
72
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "telegram-approval-buttons",
3
- "version": "5.0.0",
3
+ "version": "5.1.0",
4
4
  "description": "Inline buttons for exec approval messages in Telegram and Slack — tap to approve/deny without typing commands",
5
5
  "type": "module",
6
6
  "main": "index.ts",
package/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // ─────────────────────────────────────────────────────────────────────────────
2
- // approval-buttons · types.ts
2
+ // telegram-approval-buttons · types.ts
3
3
  // Shared TypeScript interfaces for the plugin
4
4
  // ─────────────────────────────────────────────────────────────────────────────
5
5