@delt/claude-alarm 1.3.0 → 1.4.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/dist/cli.js +288 -60
- package/dist/cli.js.map +1 -1
- package/dist/codex/main.js +220 -46
- package/dist/codex/main.js.map +1 -1
- package/dist/dashboard/index.html +77 -46
- package/dist/hub/server.js +83 -40
- package/dist/hub/server.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.js +83 -40
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/dashboard/index.html +77 -46
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,7 @@ type ChannelMessage = {
|
|
|
49
49
|
title: string;
|
|
50
50
|
message: string;
|
|
51
51
|
level?: NotifyLevel;
|
|
52
|
+
to?: MessageSource;
|
|
52
53
|
} | {
|
|
53
54
|
type: 'reply';
|
|
54
55
|
sessionId: string;
|
|
@@ -78,6 +79,10 @@ type ChannelMessage = {
|
|
|
78
79
|
sessionId: string;
|
|
79
80
|
reason: string;
|
|
80
81
|
withText: boolean;
|
|
82
|
+
} | {
|
|
83
|
+
type: 'message_rejected';
|
|
84
|
+
sessionId: string;
|
|
85
|
+
reason: string;
|
|
81
86
|
} | {
|
|
82
87
|
type: 'sessions_list';
|
|
83
88
|
sessions: SessionInfo[];
|
|
@@ -321,8 +326,8 @@ declare class TelegramBot {
|
|
|
321
326
|
private polling;
|
|
322
327
|
private pollTimer;
|
|
323
328
|
private messageSessionMap;
|
|
324
|
-
onMessageToSession?: (sessionId: string, content: string) =>
|
|
325
|
-
onImageToSession?: (sessionId: string, imagePath: string, mimeType: string, caption?: string) =>
|
|
329
|
+
onMessageToSession?: (sessionId: string, content: string) => boolean;
|
|
330
|
+
onImageToSession?: (sessionId: string, imagePath: string, mimeType: string, caption?: string) => boolean;
|
|
326
331
|
onPermissionVerdict?: (sessionId: string, requestId: string, behavior: 'allow' | 'deny') => void;
|
|
327
332
|
onChoiceVerdict?: (sessionId: string, requestId: string, choiceId: string) => void;
|
|
328
333
|
private choiceTokens;
|
|
@@ -345,6 +350,7 @@ declare class TelegramBot {
|
|
|
345
350
|
private scheduleNextPoll;
|
|
346
351
|
private handleIncomingMessage;
|
|
347
352
|
private deliverToSession;
|
|
353
|
+
private deliverPending;
|
|
348
354
|
private deliverPhotoToSession;
|
|
349
355
|
private deliverPhotoToSessionByFileId;
|
|
350
356
|
private photoNotDelivered;
|
package/dist/index.js
CHANGED
|
@@ -249,6 +249,7 @@ var MAX_SELECTIONS = 20;
|
|
|
249
249
|
var MAX_VISIBLE_CHARS = 4e3;
|
|
250
250
|
var TRUNCATED = "\u2026(truncated)";
|
|
251
251
|
var MAX_PHOTO_BYTES = 10 * 1024 * 1024;
|
|
252
|
+
var NO_LONGER_CONNECTED = "the session is no longer connected";
|
|
252
253
|
function visibleLength(html) {
|
|
253
254
|
return html.replace(/<[^>]*>/g, "").replace(/&(?:amp|lt|gt);/g, "&").length;
|
|
254
255
|
}
|
|
@@ -422,13 +423,7 @@ ${TRUNCATED}`);
|
|
|
422
423
|
this.selections.delete(latest);
|
|
423
424
|
const session = this.pendingSession(pending, parseInt(selectMatch[1], 10) - 1);
|
|
424
425
|
if (session) {
|
|
425
|
-
|
|
426
|
-
if (pending.photoFileId) {
|
|
427
|
-
delivered = await this.deliverPhotoToSessionByFileId(session.id, pending.photoFileId, pending.caption);
|
|
428
|
-
} else if (pending.text) {
|
|
429
|
-
this.deliverToSession(session.id, pending.text);
|
|
430
|
-
}
|
|
431
|
-
if (delivered) this.sendMessage(`Sent to [${this.getLabel(session)}]`);
|
|
426
|
+
if (await this.deliverPending(session.id, pending)) this.sendMessage(`Sent to [${this.getLabel(session)}]`);
|
|
432
427
|
} else {
|
|
433
428
|
this.sendMessage("Invalid session number.");
|
|
434
429
|
}
|
|
@@ -470,9 +465,13 @@ ${TRUNCATED}`);
|
|
|
470
465
|
this.sendMessage("Multiple sessions active. Select one:", void 0, { inline_keyboard: rows });
|
|
471
466
|
}
|
|
472
467
|
deliverToSession(sessionId, content) {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
468
|
+
const delivered = this.onMessageToSession?.(sessionId, content) === true;
|
|
469
|
+
if (!delivered) void this.sendMessage(`Not delivered: ${NO_LONGER_CONNECTED}`);
|
|
470
|
+
return delivered;
|
|
471
|
+
}
|
|
472
|
+
async deliverPending(sessionId, pending) {
|
|
473
|
+
if (pending.photoFileId) return this.deliverPhotoToSessionByFileId(sessionId, pending.photoFileId, pending.caption);
|
|
474
|
+
return pending.text ? this.deliverToSession(sessionId, pending.text) : false;
|
|
476
475
|
}
|
|
477
476
|
async deliverPhotoToSession(sessionId, photos, caption) {
|
|
478
477
|
const largest = photos[photos.length - 1];
|
|
@@ -504,8 +503,19 @@ ${TRUNCATED}`);
|
|
|
504
503
|
const filePath = path2.join(UPLOADS_DIR, filename);
|
|
505
504
|
fs.writeFileSync(filePath, buffer);
|
|
506
505
|
logger.info(`Telegram photo saved: ${filename} (${buffer.length} bytes)`);
|
|
507
|
-
|
|
508
|
-
|
|
506
|
+
let accepted = false;
|
|
507
|
+
try {
|
|
508
|
+
accepted = this.onImageToSession?.(sessionId, filePath, mimeType, caption) === true;
|
|
509
|
+
} catch (err) {
|
|
510
|
+
logger.warn(`Telegram photo hand-off failed: ${err.message}`);
|
|
511
|
+
}
|
|
512
|
+
if (!accepted) {
|
|
513
|
+
try {
|
|
514
|
+
fs.unlinkSync(filePath);
|
|
515
|
+
} catch {
|
|
516
|
+
}
|
|
517
|
+
this.photoNotDelivered(NO_LONGER_CONNECTED);
|
|
518
|
+
return false;
|
|
509
519
|
}
|
|
510
520
|
setTimeout(() => {
|
|
511
521
|
try {
|
|
@@ -513,7 +523,7 @@ ${TRUNCATED}`);
|
|
|
513
523
|
} catch {
|
|
514
524
|
}
|
|
515
525
|
}, 5 * 60 * 1e3).unref();
|
|
516
|
-
return
|
|
526
|
+
return true;
|
|
517
527
|
} catch (err) {
|
|
518
528
|
logger.warn(`Telegram photo download failed: ${err.message}`);
|
|
519
529
|
this.photoNotDelivered("the download failed");
|
|
@@ -708,15 +718,10 @@ ${label}`);
|
|
|
708
718
|
return;
|
|
709
719
|
}
|
|
710
720
|
this.selections.delete(selectionId);
|
|
711
|
-
if (pending
|
|
712
|
-
|
|
713
|
-
if (
|
|
714
|
-
|
|
715
|
-
if (query.message) await this.removeButtons(query.message.chat.id, query.message.message_id);
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
718
|
-
} else if (pending.text) {
|
|
719
|
-
this.deliverToSession(session.id, pending.text);
|
|
721
|
+
if (!await this.deliverPending(session.id, pending)) {
|
|
722
|
+
await this.answerCallbackQuery(query.id, pending.photoFileId ? "Photo not delivered" : "Not delivered");
|
|
723
|
+
if (query.message) await this.removeButtons(query.message.chat.id, query.message.message_id);
|
|
724
|
+
return;
|
|
720
725
|
}
|
|
721
726
|
await this.answerCallbackQuery(query.id, `Sent to ${this.getLabel(session)}`);
|
|
722
727
|
if (query.message) {
|
|
@@ -841,6 +846,11 @@ ${bodyLines.join("\n")}`;
|
|
|
841
846
|
import { spawn } from "child_process";
|
|
842
847
|
import fs2 from "fs";
|
|
843
848
|
import path3 from "path";
|
|
849
|
+
|
|
850
|
+
// src/codex/shutdown.ts
|
|
851
|
+
var SUPERVISOR_STOP_GRACE_MS = 3e4;
|
|
852
|
+
|
|
853
|
+
// src/hub/codex-supervisor.ts
|
|
844
854
|
var HEALTHY_RUN_MS = 6e4;
|
|
845
855
|
function resolveAdapterScript(baseDir) {
|
|
846
856
|
return [path3.join(baseDir, "..", "codex", "main.js"), path3.join(baseDir, "codex", "main.js")].find((p) => fs2.existsSync(p));
|
|
@@ -856,7 +866,7 @@ var CodexSupervisor = class {
|
|
|
856
866
|
this.spawnFn = opts.spawnFn ?? spawn;
|
|
857
867
|
this.minDelayMs = opts.minDelayMs ?? 2e3;
|
|
858
868
|
this.maxDelayMs = opts.maxDelayMs ?? 6e4;
|
|
859
|
-
this.stopGraceMs = opts.stopGraceMs ??
|
|
869
|
+
this.stopGraceMs = opts.stopGraceMs ?? SUPERVISOR_STOP_GRACE_MS;
|
|
860
870
|
this.env = opts.env;
|
|
861
871
|
this.delay = this.minDelayMs;
|
|
862
872
|
}
|
|
@@ -879,11 +889,22 @@ var CodexSupervisor = class {
|
|
|
879
889
|
this.timer = void 0;
|
|
880
890
|
const child = this.child;
|
|
881
891
|
this.child = void 0;
|
|
882
|
-
if (!child) return;
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
892
|
+
if (!child) return Promise.resolve();
|
|
893
|
+
return new Promise((resolve) => {
|
|
894
|
+
const done = () => {
|
|
895
|
+
clearTimeout(force);
|
|
896
|
+
child.removeListener("exit", done);
|
|
897
|
+
resolve();
|
|
898
|
+
};
|
|
899
|
+
const force = setTimeout(() => {
|
|
900
|
+
logger.warn(`Codex adapter did not exit within ${this.stopGraceMs}ms of stop; killing it`);
|
|
901
|
+
child.kill();
|
|
902
|
+
done();
|
|
903
|
+
}, this.stopGraceMs);
|
|
904
|
+
force.unref();
|
|
905
|
+
child.once("exit", done);
|
|
906
|
+
child.stdin?.end();
|
|
907
|
+
});
|
|
887
908
|
}
|
|
888
909
|
launch() {
|
|
889
910
|
const startedAt = Date.now();
|
|
@@ -1217,9 +1238,9 @@ var HubServer = class {
|
|
|
1217
1238
|
});
|
|
1218
1239
|
}
|
|
1219
1240
|
stop() {
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1241
|
+
const adapterStopped = this.codexSupervisor?.stop();
|
|
1242
|
+
this.codexSupervisor = void 0;
|
|
1243
|
+
const serverStopped = new Promise((resolve) => {
|
|
1223
1244
|
if (this.heartbeatInterval) clearInterval(this.heartbeatInterval);
|
|
1224
1245
|
if (this.telegramBot) this.telegramBot.stopPolling();
|
|
1225
1246
|
for (const ws of this.channelSockets.values()) ws.terminate();
|
|
@@ -1239,6 +1260,8 @@ var HubServer = class {
|
|
|
1239
1260
|
resolve();
|
|
1240
1261
|
}, 3e3);
|
|
1241
1262
|
});
|
|
1263
|
+
return Promise.all([serverStopped, adapterStopped]).then(() => {
|
|
1264
|
+
});
|
|
1242
1265
|
}
|
|
1243
1266
|
// --- HTTP Handler ---
|
|
1244
1267
|
handleHttp(req, res) {
|
|
@@ -1495,7 +1518,16 @@ var HubServer = class {
|
|
|
1495
1518
|
this.sessions.updateActivity(msg.sessionId);
|
|
1496
1519
|
const notifySession = this.sessions.get(msg.sessionId);
|
|
1497
1520
|
const notifyLabel = this.getSessionLabel(notifySession);
|
|
1498
|
-
|
|
1521
|
+
const notifyTitle = `[${notifyLabel}] ${msg.title}`;
|
|
1522
|
+
if (msg.to === "api") {
|
|
1523
|
+
logger.info(`${notifyTitle}: ${msg.message}`);
|
|
1524
|
+
break;
|
|
1525
|
+
}
|
|
1526
|
+
if (msg.to === "telegram") {
|
|
1527
|
+
void this.telegramBot?.sendNotification(msg.sessionId, notifyLabel, notifyTitle, msg.message);
|
|
1528
|
+
break;
|
|
1529
|
+
}
|
|
1530
|
+
if (msg.to !== "dashboard") this.notifier.notifyWithSession(msg.sessionId, notifyLabel, notifyTitle, msg.message, msg.level ?? "info");
|
|
1499
1531
|
this.broadcastToDashboards({
|
|
1500
1532
|
type: "notification",
|
|
1501
1533
|
sessionId: msg.sessionId,
|
|
@@ -1697,6 +1729,11 @@ var HubServer = class {
|
|
|
1697
1729
|
const channelWs = this.channelSockets.get(msg.sessionId);
|
|
1698
1730
|
if (channelWs?.readyState === WebSocket.OPEN) {
|
|
1699
1731
|
channelWs.send(JSON.stringify({ ...msg, source: "dashboard" }));
|
|
1732
|
+
} else {
|
|
1733
|
+
const reason = "the session is not connected";
|
|
1734
|
+
logger.warn(`Message rejected for ${msg.sessionId}: ${reason}`);
|
|
1735
|
+
const rejected = { type: "message_rejected", sessionId: msg.sessionId, reason };
|
|
1736
|
+
if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify(rejected));
|
|
1700
1737
|
}
|
|
1701
1738
|
} else if (msg.type === "image_upload") {
|
|
1702
1739
|
this.handleImageUpload(ws, msg);
|
|
@@ -1822,19 +1859,25 @@ var HubServer = class {
|
|
|
1822
1859
|
this.telegramBot.getSessions = () => this.sessions.getAll();
|
|
1823
1860
|
this.telegramBot.onMessageToSession = (sessionId, content) => {
|
|
1824
1861
|
const channelWs = this.channelSockets.get(sessionId);
|
|
1825
|
-
if (channelWs?.readyState
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
logger.info(`Telegram message forwarded to session: ${sessionId}`);
|
|
1862
|
+
if (channelWs?.readyState !== WebSocket.OPEN) {
|
|
1863
|
+
logger.warn(`Telegram message not delivered to ${sessionId}: the session is not connected`);
|
|
1864
|
+
return false;
|
|
1829
1865
|
}
|
|
1866
|
+
const msg = { type: "message_to_session", sessionId, content, source: "telegram" };
|
|
1867
|
+
channelWs.send(JSON.stringify(msg));
|
|
1868
|
+
logger.info(`Telegram message forwarded to session: ${sessionId}`);
|
|
1869
|
+
return true;
|
|
1830
1870
|
};
|
|
1831
1871
|
this.telegramBot.onImageToSession = (sessionId, imagePath, mimeType, caption) => {
|
|
1832
1872
|
const channelWs = this.channelSockets.get(sessionId);
|
|
1833
|
-
if (channelWs?.readyState
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
logger.info(`Telegram photo forwarded to session: ${sessionId}`);
|
|
1873
|
+
if (channelWs?.readyState !== WebSocket.OPEN) {
|
|
1874
|
+
logger.warn(`Telegram photo not delivered to ${sessionId}: the session is not connected`);
|
|
1875
|
+
return false;
|
|
1837
1876
|
}
|
|
1877
|
+
const msg = { type: "image_to_session", sessionId, imagePath, mimeType, content: caption, source: "telegram" };
|
|
1878
|
+
channelWs.send(JSON.stringify(msg));
|
|
1879
|
+
logger.info(`Telegram photo forwarded to session: ${sessionId}`);
|
|
1880
|
+
return true;
|
|
1838
1881
|
};
|
|
1839
1882
|
this.telegramBot.onPermissionVerdict = (sessionId, requestId, behavior) => {
|
|
1840
1883
|
if (this.forwardPermissionResponse({ sessionId, requestId, behavior })) {
|