@delt/claude-alarm 1.0.1 → 1.1.1
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 +6 -4
- package/dist/cli.js +165 -13
- package/dist/cli.js.map +1 -1
- package/dist/codex/main.js +147 -12
- package/dist/codex/main.js.map +1 -1
- package/dist/dashboard/index.html +101 -7
- package/dist/hub/server.js +157 -12
- package/dist/hub/server.js.map +1 -1
- package/dist/index.d.ts +37 -2
- package/dist/index.js +157 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/dashboard/index.html +101 -7
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
type SessionStatus = 'idle' | 'working' | 'waiting_input';
|
|
3
3
|
type AgentKind = 'claude' | 'codex';
|
|
4
4
|
type MessageSource = 'dashboard' | 'telegram' | 'api';
|
|
5
|
+
interface PermissionChoice {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
}
|
|
9
|
+
interface PendingChoiceRequest {
|
|
10
|
+
sessionId: string;
|
|
11
|
+
requestId: string;
|
|
12
|
+
toolName: string;
|
|
13
|
+
description: string;
|
|
14
|
+
inputPreview: string;
|
|
15
|
+
timestamp: number;
|
|
16
|
+
choices: PermissionChoice[];
|
|
17
|
+
}
|
|
5
18
|
/** Session info tracked by the hub */
|
|
6
19
|
interface SessionInfo {
|
|
7
20
|
id: string;
|
|
@@ -90,11 +103,21 @@ type ChannelMessage = {
|
|
|
90
103
|
description: string;
|
|
91
104
|
inputPreview: string;
|
|
92
105
|
timestamp: number;
|
|
106
|
+
choices?: PermissionChoice[];
|
|
93
107
|
} | {
|
|
94
108
|
type: 'permission_response';
|
|
95
109
|
sessionId: string;
|
|
96
110
|
requestId: string;
|
|
97
|
-
behavior
|
|
111
|
+
behavior?: 'allow' | 'deny';
|
|
112
|
+
choiceId?: string;
|
|
113
|
+
} | {
|
|
114
|
+
type: 'permission_resolved';
|
|
115
|
+
sessionId: string;
|
|
116
|
+
requestId: string;
|
|
117
|
+
state: 'resolved' | 'expired';
|
|
118
|
+
} | {
|
|
119
|
+
type: 'permission_pending';
|
|
120
|
+
requests: PendingChoiceRequest[];
|
|
98
121
|
} | {
|
|
99
122
|
type: 'error';
|
|
100
123
|
message: string;
|
|
@@ -155,6 +178,7 @@ declare class HubServer {
|
|
|
155
178
|
private heartbeatInterval?;
|
|
156
179
|
private channelAlive;
|
|
157
180
|
private socketOwners;
|
|
181
|
+
private choiceRequests;
|
|
158
182
|
private host;
|
|
159
183
|
private port;
|
|
160
184
|
private token?;
|
|
@@ -171,6 +195,9 @@ declare class HubServer {
|
|
|
171
195
|
private handleChannelConnection;
|
|
172
196
|
private handleChannelMessage;
|
|
173
197
|
private handleDashboardConnection;
|
|
198
|
+
private forwardPermissionResponse;
|
|
199
|
+
private resolveChoice;
|
|
200
|
+
private expireChoices;
|
|
174
201
|
private broadcastToDashboards;
|
|
175
202
|
private handleImageUpload;
|
|
176
203
|
private handleWebhookSave;
|
|
@@ -235,6 +262,9 @@ declare class TelegramBot {
|
|
|
235
262
|
onMessageToSession?: (sessionId: string, content: string) => void;
|
|
236
263
|
onImageToSession?: (sessionId: string, imagePath: string, mimeType: string, caption?: string) => void;
|
|
237
264
|
onPermissionVerdict?: (sessionId: string, requestId: string, behavior: 'allow' | 'deny') => void;
|
|
265
|
+
onChoiceVerdict?: (sessionId: string, requestId: string, choiceId: string) => void;
|
|
266
|
+
private choiceTokens;
|
|
267
|
+
private choiceMessages;
|
|
238
268
|
getSessions?: () => SessionInfo[];
|
|
239
269
|
private pendingMessages;
|
|
240
270
|
constructor(config: TelegramConfig);
|
|
@@ -257,6 +287,11 @@ declare class TelegramBot {
|
|
|
257
287
|
private getLabel;
|
|
258
288
|
/** Send a permission request with inline buttons */
|
|
259
289
|
sendPermissionRequest(sessionId: string, sessionLabel: string, requestId: string, toolName: string, description: string, inputPreview: string): Promise<void>;
|
|
290
|
+
sendChoiceRequest(sessionId: string, sessionLabel: string, requestId: string, toolName: string, description: string, inputPreview: string, choices: PermissionChoice[]): Promise<void>;
|
|
291
|
+
resolveChoiceRequest(sessionId: string, requestId: string, state: 'resolved' | 'expired'): Promise<void>;
|
|
292
|
+
private handleChoiceCallback;
|
|
293
|
+
private dropChoiceTokens;
|
|
294
|
+
private trimChoiceMessages;
|
|
260
295
|
private handleCallbackQuery;
|
|
261
296
|
private handleSessionSelectCallback;
|
|
262
297
|
private answerCallbackQuery;
|
|
@@ -323,4 +358,4 @@ declare const WS_PATH_DASHBOARD = "/ws/dashboard";
|
|
|
323
358
|
declare const CHANNEL_SERVER_NAME = "claude-alarm";
|
|
324
359
|
declare const CHANNEL_SERVER_VERSION = "0.1.0";
|
|
325
360
|
|
|
326
|
-
export { type AgentKind, type AppConfig, CHANNEL_SERVER_NAME, CHANNEL_SERVER_VERSION, CODEX_LOG_FILE, CODEX_PID_FILE, CONFIG_DIR, CONFIG_FILE, type ChannelMessage, type CodexConfig, DEFAULT_HUB_HOST, DEFAULT_HUB_PORT, HubClient, HubServer, type HubStatus, LOG_FILE, type MessageSource, Notifier, type NotifyLevel, PID_FILE, type SessionInfo, SessionManager, type SessionStatus, type TelegramConfig, UPLOADS_DIR, WS_PATH_CHANNEL, WS_PATH_DASHBOARD, type WebhookConfig, loadConfig, logger, saveConfig, setupMcpConfig };
|
|
361
|
+
export { type AgentKind, type AppConfig, CHANNEL_SERVER_NAME, CHANNEL_SERVER_VERSION, CODEX_LOG_FILE, CODEX_PID_FILE, CONFIG_DIR, CONFIG_FILE, type ChannelMessage, type CodexConfig, DEFAULT_HUB_HOST, DEFAULT_HUB_PORT, HubClient, HubServer, type HubStatus, LOG_FILE, type MessageSource, Notifier, type NotifyLevel, PID_FILE, type PendingChoiceRequest, type PermissionChoice, type SessionInfo, SessionManager, type SessionStatus, type TelegramConfig, UPLOADS_DIR, WS_PATH_CHANNEL, WS_PATH_DASHBOARD, type WebhookConfig, loadConfig, logger, saveConfig, setupMcpConfig };
|
package/dist/index.js
CHANGED
|
@@ -234,8 +234,15 @@ function sessionLabel(session) {
|
|
|
234
234
|
return session.agentKind === "codex" ? `Codex \xB7 ${base}` : base;
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
// src/shared/permission-key.ts
|
|
238
|
+
function permissionKey(sessionId, requestId) {
|
|
239
|
+
return `${sessionId}
|
|
240
|
+
${requestId}`;
|
|
241
|
+
}
|
|
242
|
+
|
|
237
243
|
// src/hub/telegram.ts
|
|
238
244
|
var TELEGRAM_API = "https://api.telegram.org/bot";
|
|
245
|
+
var MAX_CHOICE_MESSAGES = 200;
|
|
239
246
|
var TelegramBot = class _TelegramBot {
|
|
240
247
|
config;
|
|
241
248
|
offset = 0;
|
|
@@ -249,6 +256,11 @@ var TelegramBot = class _TelegramBot {
|
|
|
249
256
|
onImageToSession;
|
|
250
257
|
// Callback: when a permission verdict arrives from Telegram
|
|
251
258
|
onPermissionVerdict;
|
|
259
|
+
// Callback: when a Codex approval choice arrives from Telegram
|
|
260
|
+
onChoiceVerdict;
|
|
261
|
+
// Telegram caps callback_data at 64 bytes, so buttons carry a short token instead of the ids.
|
|
262
|
+
choiceTokens = /* @__PURE__ */ new Map();
|
|
263
|
+
choiceMessages = /* @__PURE__ */ new Map();
|
|
252
264
|
// Callback: get current sessions list
|
|
253
265
|
getSessions;
|
|
254
266
|
// Pending messages for session selection
|
|
@@ -537,6 +549,71 @@ ${previewHtml}${truncNote}`;
|
|
|
537
549
|
};
|
|
538
550
|
await this.sendMessage(text, void 0, replyMarkup);
|
|
539
551
|
}
|
|
552
|
+
async sendChoiceRequest(sessionId, sessionLabel2, requestId, toolName, description, inputPreview, choices) {
|
|
553
|
+
let preview = inputPreview;
|
|
554
|
+
try {
|
|
555
|
+
const p = JSON.parse(inputPreview);
|
|
556
|
+
if (typeof p.command === "string") preview = `$ ${p.command}`;
|
|
557
|
+
else if (typeof p.content === "string") preview = p.content;
|
|
558
|
+
} catch {
|
|
559
|
+
}
|
|
560
|
+
const slice = preview.slice(0, 3e3);
|
|
561
|
+
const html = `\u26A0\uFE0F <b>Permission Request</b> \u2014 ${this.escHtml(sessionLabel2)}
|
|
562
|
+
|
|
563
|
+
\u{1F527} <b>${this.escHtml(toolName)}</b>` + (description ? `
|
|
564
|
+
${this.escHtml(description)}` : "") + (slice ? `
|
|
565
|
+
<pre>${this.escHtml(slice)}</pre>` : "") + (preview.length > slice.length ? "\n<i>...truncated</i>" : "");
|
|
566
|
+
const entry = { html };
|
|
567
|
+
this.choiceMessages.set(permissionKey(sessionId, requestId), entry);
|
|
568
|
+
const rows = choices.map((c) => {
|
|
569
|
+
const token = randomUUID().replace(/-/g, "").slice(0, 16);
|
|
570
|
+
this.choiceTokens.set(token, { sessionId, requestId, choiceId: c.id, label: c.label });
|
|
571
|
+
return [{ text: c.label, callback_data: `pc:${token}` }];
|
|
572
|
+
});
|
|
573
|
+
this.trimChoiceMessages();
|
|
574
|
+
const sent = await this.sendMessage(html, void 0, { inline_keyboard: rows });
|
|
575
|
+
if (!sent) return;
|
|
576
|
+
entry.messageId = sent.message_id;
|
|
577
|
+
if (entry.outcome) await this.editMessageText(this.config.chatId, sent.message_id, html + entry.outcome);
|
|
578
|
+
}
|
|
579
|
+
async resolveChoiceRequest(sessionId, requestId, state) {
|
|
580
|
+
this.dropChoiceTokens(sessionId, requestId);
|
|
581
|
+
const key = permissionKey(sessionId, requestId);
|
|
582
|
+
const entry = this.choiceMessages.get(key);
|
|
583
|
+
if (!entry) return;
|
|
584
|
+
this.choiceMessages.delete(key);
|
|
585
|
+
entry.outcome = state === "resolved" ? "\n\n\u2705 <b>Resolved</b>" : "\n\n\u231B <b>Expired</b>";
|
|
586
|
+
if (entry.messageId !== void 0) await this.editMessageText(this.config.chatId, entry.messageId, entry.html + entry.outcome);
|
|
587
|
+
}
|
|
588
|
+
async handleChoiceCallback(query) {
|
|
589
|
+
const choice = this.choiceTokens.get(query.data.slice("pc:".length));
|
|
590
|
+
if (!choice) {
|
|
591
|
+
await this.answerCallbackQuery(query.id, "Expired");
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
this.dropChoiceTokens(choice.sessionId, choice.requestId);
|
|
595
|
+
this.onChoiceVerdict?.(choice.sessionId, choice.requestId, choice.choiceId);
|
|
596
|
+
await this.answerCallbackQuery(query.id, `Sent: ${choice.label}`);
|
|
597
|
+
const entry = this.choiceMessages.get(permissionKey(choice.sessionId, choice.requestId));
|
|
598
|
+
if (entry?.messageId !== void 0 && !entry.outcome) {
|
|
599
|
+
await this.editMessageText(this.config.chatId, entry.messageId, `${entry.html}
|
|
600
|
+
|
|
601
|
+
\u23F3 <b>Sent: ${this.escHtml(choice.label)}</b>`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
dropChoiceTokens(sessionId, requestId) {
|
|
605
|
+
for (const [token, c] of this.choiceTokens) {
|
|
606
|
+
if (c.sessionId === sessionId && c.requestId === requestId) this.choiceTokens.delete(token);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
trimChoiceMessages() {
|
|
610
|
+
while (this.choiceMessages.size > MAX_CHOICE_MESSAGES) {
|
|
611
|
+
const oldest = this.choiceMessages.keys().next().value;
|
|
612
|
+
this.choiceMessages.delete(oldest);
|
|
613
|
+
const [sessionId, requestId] = oldest.split("\n");
|
|
614
|
+
this.dropChoiceTokens(sessionId, requestId);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
540
617
|
async handleCallbackQuery(query) {
|
|
541
618
|
if (!query.data) return;
|
|
542
619
|
if (String(query.message?.chat.id) !== String(this.config.chatId)) return;
|
|
@@ -544,6 +621,10 @@ ${previewHtml}${truncNote}`;
|
|
|
544
621
|
await this.handleSessionSelectCallback(query);
|
|
545
622
|
return;
|
|
546
623
|
}
|
|
624
|
+
if (query.data.startsWith("pc:")) {
|
|
625
|
+
await this.handleChoiceCallback(query);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
547
628
|
if (!query.data.startsWith("perm:")) return;
|
|
548
629
|
const parts = query.data.split(":");
|
|
549
630
|
if (parts.length < 4) return;
|
|
@@ -923,6 +1004,11 @@ function isSecureRequest(req) {
|
|
|
923
1004
|
// src/hub/server.ts
|
|
924
1005
|
var __dirname = path5.dirname(fileURLToPath(import.meta.url));
|
|
925
1006
|
var MAX_WS_PAYLOAD = 16 * 1024 * 1024;
|
|
1007
|
+
function validChoices(choices) {
|
|
1008
|
+
if (!Array.isArray(choices)) return void 0;
|
|
1009
|
+
const valid = choices.filter((c) => typeof c?.id === "string" && typeof c?.label === "string");
|
|
1010
|
+
return valid.length ? valid : void 0;
|
|
1011
|
+
}
|
|
926
1012
|
var HubServer = class {
|
|
927
1013
|
httpServer;
|
|
928
1014
|
wssChannel;
|
|
@@ -941,6 +1027,8 @@ var HubServer = class {
|
|
|
941
1027
|
channelAlive = /* @__PURE__ */ new Map();
|
|
942
1028
|
// sessionId -> alive flag
|
|
943
1029
|
socketOwners = /* @__PURE__ */ new WeakMap();
|
|
1030
|
+
// Codex approvals carry their own choices; a response is forwarded only in the mode of the request it answers.
|
|
1031
|
+
choiceRequests = /* @__PURE__ */ new Map();
|
|
944
1032
|
host;
|
|
945
1033
|
port;
|
|
946
1034
|
token;
|
|
@@ -1078,6 +1166,7 @@ var HubServer = class {
|
|
|
1078
1166
|
this.channelSockets.delete(sessionId);
|
|
1079
1167
|
this.localChannels.delete(sessionId);
|
|
1080
1168
|
this.channelAlive.delete(sessionId);
|
|
1169
|
+
this.expireChoices(sessionId);
|
|
1081
1170
|
if (session) {
|
|
1082
1171
|
this.broadcastToDashboards({ type: "session_disconnected", sessionId });
|
|
1083
1172
|
this.jsonResponse(res, 200, { ok: true });
|
|
@@ -1221,6 +1310,7 @@ var HubServer = class {
|
|
|
1221
1310
|
this.channelSockets.delete(sessionId);
|
|
1222
1311
|
this.localChannels.delete(sessionId);
|
|
1223
1312
|
this.channelAlive.delete(sessionId);
|
|
1313
|
+
this.expireChoices(sessionId);
|
|
1224
1314
|
logger.info(`Channel disconnected: ${sessionId}`);
|
|
1225
1315
|
this.broadcastToDashboards({
|
|
1226
1316
|
type: "session_disconnected",
|
|
@@ -1308,7 +1398,22 @@ var HubServer = class {
|
|
|
1308
1398
|
}
|
|
1309
1399
|
case "permission_request": {
|
|
1310
1400
|
this.sessions.updateActivity(msg.sessionId);
|
|
1401
|
+
const choices = validChoices(msg.choices);
|
|
1311
1402
|
logger.info(`Permission request [${msg.requestId}] from ${msg.sessionId}: ${msg.toolName}`);
|
|
1403
|
+
if (choices) {
|
|
1404
|
+
this.choiceRequests.set(permissionKey(msg.sessionId, msg.requestId), {
|
|
1405
|
+
request: {
|
|
1406
|
+
sessionId: msg.sessionId,
|
|
1407
|
+
requestId: msg.requestId,
|
|
1408
|
+
toolName: msg.toolName,
|
|
1409
|
+
description: msg.description,
|
|
1410
|
+
inputPreview: msg.inputPreview,
|
|
1411
|
+
timestamp: msg.timestamp,
|
|
1412
|
+
choices
|
|
1413
|
+
},
|
|
1414
|
+
choiceIds: new Set(choices.map((c) => c.id))
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1312
1417
|
this.broadcastToDashboards({
|
|
1313
1418
|
type: "permission_request",
|
|
1314
1419
|
sessionId: msg.sessionId,
|
|
@@ -1316,15 +1421,23 @@ var HubServer = class {
|
|
|
1316
1421
|
toolName: msg.toolName,
|
|
1317
1422
|
description: msg.description,
|
|
1318
1423
|
inputPreview: msg.inputPreview,
|
|
1319
|
-
timestamp: msg.timestamp
|
|
1424
|
+
timestamp: msg.timestamp,
|
|
1425
|
+
...choices ? { choices } : {}
|
|
1320
1426
|
});
|
|
1321
1427
|
if (this.telegramBot) {
|
|
1322
|
-
const
|
|
1323
|
-
|
|
1324
|
-
|
|
1428
|
+
const label = this.getSessionLabel(this.sessions.get(msg.sessionId));
|
|
1429
|
+
if (choices) {
|
|
1430
|
+
void this.telegramBot.sendChoiceRequest(msg.sessionId, label, msg.requestId, msg.toolName, msg.description, msg.inputPreview, choices);
|
|
1431
|
+
} else {
|
|
1432
|
+
this.telegramBot.sendPermissionRequest(msg.sessionId, label, msg.requestId, msg.toolName, msg.description, msg.inputPreview);
|
|
1433
|
+
}
|
|
1325
1434
|
}
|
|
1326
1435
|
break;
|
|
1327
1436
|
}
|
|
1437
|
+
case "permission_resolved": {
|
|
1438
|
+
this.resolveChoice(msg.sessionId, msg.requestId, msg.state === "expired" ? "expired" : "resolved");
|
|
1439
|
+
break;
|
|
1440
|
+
}
|
|
1328
1441
|
}
|
|
1329
1442
|
}
|
|
1330
1443
|
// --- Dashboard WebSocket ---
|
|
@@ -1340,6 +1453,11 @@ var HubServer = class {
|
|
|
1340
1453
|
sessions: this.sessions.getAll()
|
|
1341
1454
|
};
|
|
1342
1455
|
ws.send(JSON.stringify(sessionsMsg));
|
|
1456
|
+
const pendingMsg = {
|
|
1457
|
+
type: "permission_pending",
|
|
1458
|
+
requests: [...this.choiceRequests.values()].map((c) => c.request)
|
|
1459
|
+
};
|
|
1460
|
+
ws.send(JSON.stringify(pendingMsg));
|
|
1343
1461
|
ws.on("message", (data) => {
|
|
1344
1462
|
try {
|
|
1345
1463
|
const msg = JSON.parse(data.toString());
|
|
@@ -1351,10 +1469,9 @@ var HubServer = class {
|
|
|
1351
1469
|
} else if (msg.type === "image_upload") {
|
|
1352
1470
|
this.handleImageUpload(msg);
|
|
1353
1471
|
} else if (msg.type === "permission_response") {
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
logger.info(`Permission verdict [${msg.requestId}]: ${msg.behavior} -> session ${msg.sessionId}`);
|
|
1472
|
+
if (this.forwardPermissionResponse(msg)) {
|
|
1473
|
+
const verdict = msg.choiceId !== void 0 ? `choice ${msg.choiceId}` : msg.behavior;
|
|
1474
|
+
logger.info(`Permission verdict [${msg.requestId}]: ${verdict} -> session ${msg.sessionId}`);
|
|
1358
1475
|
}
|
|
1359
1476
|
}
|
|
1360
1477
|
} catch {
|
|
@@ -1367,6 +1484,32 @@ var HubServer = class {
|
|
|
1367
1484
|
});
|
|
1368
1485
|
}
|
|
1369
1486
|
// --- Helpers ---
|
|
1487
|
+
forwardPermissionResponse(msg) {
|
|
1488
|
+
const pending = this.choiceRequests.get(permissionKey(msg.sessionId, msg.requestId));
|
|
1489
|
+
let out;
|
|
1490
|
+
if (pending) {
|
|
1491
|
+
if (typeof msg.choiceId !== "string" || !pending.choiceIds.has(msg.choiceId)) return false;
|
|
1492
|
+
out = { type: "permission_response", sessionId: msg.sessionId, requestId: msg.requestId, choiceId: msg.choiceId };
|
|
1493
|
+
} else {
|
|
1494
|
+
if (msg.behavior !== "allow" && msg.behavior !== "deny" || msg.choiceId !== void 0) return false;
|
|
1495
|
+
if (this.sessions.get(msg.sessionId)?.agentKind === "codex") return false;
|
|
1496
|
+
out = { type: "permission_response", sessionId: msg.sessionId, requestId: msg.requestId, behavior: msg.behavior };
|
|
1497
|
+
}
|
|
1498
|
+
const channelWs = this.channelSockets.get(msg.sessionId);
|
|
1499
|
+
if (channelWs?.readyState !== WebSocket.OPEN) return false;
|
|
1500
|
+
channelWs.send(JSON.stringify(out));
|
|
1501
|
+
return true;
|
|
1502
|
+
}
|
|
1503
|
+
resolveChoice(sessionId, requestId, state) {
|
|
1504
|
+
if (!this.choiceRequests.delete(permissionKey(sessionId, requestId))) return;
|
|
1505
|
+
this.broadcastToDashboards({ type: "permission_resolved", sessionId, requestId, state });
|
|
1506
|
+
void this.telegramBot?.resolveChoiceRequest(sessionId, requestId, state);
|
|
1507
|
+
}
|
|
1508
|
+
expireChoices(sessionId) {
|
|
1509
|
+
for (const pending of [...this.choiceRequests.values()]) {
|
|
1510
|
+
if (pending.request.sessionId === sessionId) this.resolveChoice(sessionId, pending.request.requestId, "expired");
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1370
1513
|
broadcastToDashboards(msg) {
|
|
1371
1514
|
const payload = JSON.stringify(msg);
|
|
1372
1515
|
for (const ws of this.dashboardSockets) {
|
|
@@ -1450,14 +1593,16 @@ var HubServer = class {
|
|
|
1450
1593
|
}
|
|
1451
1594
|
};
|
|
1452
1595
|
this.telegramBot.onPermissionVerdict = (sessionId, requestId, behavior) => {
|
|
1453
|
-
|
|
1454
|
-
if (channelWs?.readyState === WebSocket.OPEN) {
|
|
1455
|
-
const msg = { type: "permission_response", sessionId, requestId, behavior };
|
|
1456
|
-
channelWs.send(JSON.stringify(msg));
|
|
1596
|
+
if (this.forwardPermissionResponse({ sessionId, requestId, behavior })) {
|
|
1457
1597
|
logger.info(`Telegram permission verdict [${requestId}]: ${behavior} -> session ${sessionId}`);
|
|
1458
1598
|
}
|
|
1459
1599
|
this.broadcastToDashboards({ type: "permission_response", sessionId, requestId, behavior });
|
|
1460
1600
|
};
|
|
1601
|
+
this.telegramBot.onChoiceVerdict = (sessionId, requestId, choiceId) => {
|
|
1602
|
+
if (this.forwardPermissionResponse({ sessionId, requestId, choiceId })) {
|
|
1603
|
+
logger.info(`Telegram choice [${requestId}]: ${choiceId} -> session ${sessionId}`);
|
|
1604
|
+
}
|
|
1605
|
+
};
|
|
1461
1606
|
this.notifier.configure({ telegramBot: this.telegramBot });
|
|
1462
1607
|
this.telegramBot.startPolling();
|
|
1463
1608
|
logger.info("Telegram bot initialized");
|