@delt/claude-alarm 0.9.1 → 1.0.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 +24 -0
- package/dist/channel/server.js +41 -22
- package/dist/channel/server.js.map +1 -1
- package/dist/cli.js +354 -130
- package/dist/cli.js.map +1 -1
- package/dist/codex/main.js +778 -0
- package/dist/codex/main.js.map +1 -0
- package/dist/dashboard/index.html +18 -4
- package/dist/hub/server.js +155 -61
- package/dist/hub/server.js.map +1 -1
- package/dist/index.d.ts +24 -2
- package/dist/index.js +201 -88
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/dashboard/index.html +18 -4
package/dist/hub/server.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/hub/server.ts
|
|
4
4
|
import http from "http";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import fs5 from "fs";
|
|
6
|
+
import path5 from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
import { WebSocketServer, WebSocket } from "ws";
|
|
9
9
|
|
|
@@ -38,6 +38,8 @@ var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
|
38
38
|
var PID_FILE = path.join(CONFIG_DIR, "hub.pid");
|
|
39
39
|
var LOG_FILE = path.join(CONFIG_DIR, "hub.log");
|
|
40
40
|
var UPLOADS_DIR = path.join(CONFIG_DIR, "uploads");
|
|
41
|
+
var CODEX_PID_FILE = path.join(CONFIG_DIR, "codex.pid");
|
|
42
|
+
var CODEX_LOG_FILE = path.join(CONFIG_DIR, "codex.log");
|
|
41
43
|
var WS_PATH_CHANNEL = "/ws/channel";
|
|
42
44
|
var WS_PATH_DASHBOARD = "/ws/dashboard";
|
|
43
45
|
|
|
@@ -51,9 +53,9 @@ function isValidPeerName(name) {
|
|
|
51
53
|
var SessionManager = class {
|
|
52
54
|
sessions = /* @__PURE__ */ new Map();
|
|
53
55
|
register(session) {
|
|
54
|
-
const baseName =
|
|
56
|
+
const baseName = this.baseName(session);
|
|
55
57
|
const existing = Array.from(this.sessions.values()).filter(
|
|
56
|
-
(s) => s.id !== session.id &&
|
|
58
|
+
(s) => s.id !== session.id && this.baseName(s) === baseName
|
|
57
59
|
);
|
|
58
60
|
if (existing.length > 0) {
|
|
59
61
|
const usedNums = existing.map((s) => {
|
|
@@ -107,6 +109,9 @@ var SessionManager = class {
|
|
|
107
109
|
count() {
|
|
108
110
|
return this.sessions.size;
|
|
109
111
|
}
|
|
112
|
+
baseName(session) {
|
|
113
|
+
return session.title || session.cwd?.replace(/^.*[/\\]/, "") || session.name;
|
|
114
|
+
}
|
|
110
115
|
};
|
|
111
116
|
|
|
112
117
|
// src/hub/notifier.ts
|
|
@@ -127,7 +132,7 @@ var Notifier = class {
|
|
|
127
132
|
async notify(title, message, level = "info") {
|
|
128
133
|
await this.notifyWithSession(void 0, void 0, title, message, level);
|
|
129
134
|
}
|
|
130
|
-
async notifyWithSession(sessionId,
|
|
135
|
+
async notifyWithSession(sessionId, sessionLabel2, title, message, level = "info") {
|
|
131
136
|
const promises = [];
|
|
132
137
|
if (this.desktopEnabled) {
|
|
133
138
|
promises.push(this.sendDesktop(title, message, level));
|
|
@@ -135,8 +140,8 @@ var Notifier = class {
|
|
|
135
140
|
for (const webhook of this.webhooks) {
|
|
136
141
|
promises.push(this.sendWebhook(webhook, title, message, level));
|
|
137
142
|
}
|
|
138
|
-
if (this.telegramBot && sessionId &&
|
|
139
|
-
promises.push(this.telegramBot.sendNotification(sessionId,
|
|
143
|
+
if (this.telegramBot && sessionId && sessionLabel2) {
|
|
144
|
+
promises.push(this.telegramBot.sendNotification(sessionId, sessionLabel2, title, message));
|
|
140
145
|
}
|
|
141
146
|
await Promise.allSettled(promises);
|
|
142
147
|
}
|
|
@@ -222,6 +227,14 @@ var Notifier = class {
|
|
|
222
227
|
import fs from "fs";
|
|
223
228
|
import path2 from "path";
|
|
224
229
|
import { randomUUID } from "crypto";
|
|
230
|
+
|
|
231
|
+
// src/shared/session-label.ts
|
|
232
|
+
function sessionLabel(session) {
|
|
233
|
+
const base = session.displayName || session.cwd?.replace(/^.*[/\\]/, "") || session.name;
|
|
234
|
+
return session.agentKind === "codex" ? `Codex \xB7 ${base}` : base;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/hub/telegram.ts
|
|
225
238
|
var TELEGRAM_API = "https://api.telegram.org/bot";
|
|
226
239
|
var TelegramBot = class _TelegramBot {
|
|
227
240
|
config;
|
|
@@ -360,15 +373,14 @@ ${this.mdToHtml(message)}`;
|
|
|
360
373
|
const pending = this.pendingMessages.get(msg.chat.id);
|
|
361
374
|
if (pending) {
|
|
362
375
|
this.pendingMessages.delete(msg.chat.id);
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
if (idx >= 0 && idx < sessions2.length) {
|
|
376
|
+
const session = this.pendingSession(pending, parseInt(selectMatch[1], 10) - 1);
|
|
377
|
+
if (session) {
|
|
366
378
|
if (pending.photoFileId) {
|
|
367
|
-
await this.deliverPhotoToSessionByFileId(
|
|
379
|
+
await this.deliverPhotoToSessionByFileId(session.id, pending.photoFileId, pending.caption);
|
|
368
380
|
} else if (pending.text) {
|
|
369
|
-
this.deliverToSession(
|
|
381
|
+
this.deliverToSession(session.id, pending.text);
|
|
370
382
|
}
|
|
371
|
-
this.sendMessage(`Sent to [${this.getLabel(
|
|
383
|
+
this.sendMessage(`Sent to [${this.getLabel(session)}]`);
|
|
372
384
|
} else {
|
|
373
385
|
this.sendMessage("Invalid session number.");
|
|
374
386
|
}
|
|
@@ -389,11 +401,12 @@ ${this.mdToHtml(message)}`;
|
|
|
389
401
|
}
|
|
390
402
|
return;
|
|
391
403
|
}
|
|
404
|
+
const sessionIds = sessions.map((s) => s.id);
|
|
392
405
|
if (hasPhoto) {
|
|
393
406
|
const largest = msg.photo[msg.photo.length - 1];
|
|
394
|
-
this.pendingMessages.set(msg.chat.id, { photoFileId: largest.file_id, caption: text });
|
|
407
|
+
this.pendingMessages.set(msg.chat.id, { photoFileId: largest.file_id, caption: text, sessionIds });
|
|
395
408
|
} else {
|
|
396
|
-
this.pendingMessages.set(msg.chat.id, { text });
|
|
409
|
+
this.pendingMessages.set(msg.chat.id, { text, sessionIds });
|
|
397
410
|
}
|
|
398
411
|
const buttons = sessions.map((s, i) => ({
|
|
399
412
|
text: this.getLabel(s),
|
|
@@ -450,11 +463,16 @@ ${this.mdToHtml(message)}`;
|
|
|
450
463
|
logger.warn(`Telegram photo download failed: ${err.message}`);
|
|
451
464
|
}
|
|
452
465
|
}
|
|
466
|
+
// Buttons are numbered against the list shown when they were sent; Codex sessions come and go, so the live list may have shifted.
|
|
467
|
+
pendingSession(pending, idx) {
|
|
468
|
+
const id = pending.sessionIds[idx];
|
|
469
|
+
return id ? this.getSessions?.().find((s) => s.id === id) : void 0;
|
|
470
|
+
}
|
|
453
471
|
getLabel(session) {
|
|
454
|
-
return session
|
|
472
|
+
return sessionLabel(session);
|
|
455
473
|
}
|
|
456
474
|
/** Send a permission request with inline buttons */
|
|
457
|
-
async sendPermissionRequest(sessionId,
|
|
475
|
+
async sendPermissionRequest(sessionId, sessionLabel2, requestId, toolName, description, inputPreview) {
|
|
458
476
|
const isNotify = toolName.endsWith("__notify") || toolName === "notify";
|
|
459
477
|
let preview = inputPreview;
|
|
460
478
|
let kind = "code";
|
|
@@ -507,7 +525,7 @@ ${this.mdToHtml(message)}`;
|
|
|
507
525
|
const mcpMatch = toolName.match(/__([^_]+)$/);
|
|
508
526
|
if (mcpMatch) displayTool = mcpMatch[1].charAt(0).toUpperCase() + mcpMatch[1].slice(1);
|
|
509
527
|
}
|
|
510
|
-
const text = `\u26A0\uFE0F <b>Permission Request</b> \u2014 ${this.escHtml(
|
|
528
|
+
const text = `\u26A0\uFE0F <b>Permission Request</b> \u2014 ${this.escHtml(sessionLabel2)}
|
|
511
529
|
|
|
512
530
|
\u{1F527} <b>${this.escHtml(displayTool)}</b>
|
|
513
531
|
${previewHtml}${truncNote}`;
|
|
@@ -547,22 +565,18 @@ ${label}`);
|
|
|
547
565
|
const parts = query.data.split(":");
|
|
548
566
|
if (parts.length < 3) return;
|
|
549
567
|
const [, idxStr, chatIdStr] = parts;
|
|
550
|
-
const idx = parseInt(idxStr, 10);
|
|
551
568
|
const chatId = parseInt(chatIdStr, 10);
|
|
552
|
-
const
|
|
553
|
-
|
|
569
|
+
const pending = this.pendingMessages.get(chatId);
|
|
570
|
+
const session = pending ? this.pendingSession(pending, parseInt(idxStr, 10)) : void 0;
|
|
571
|
+
if (!pending || !session) {
|
|
554
572
|
await this.answerCallbackQuery(query.id, "Session not found");
|
|
555
573
|
return;
|
|
556
574
|
}
|
|
557
|
-
const session = sessions[idx];
|
|
558
|
-
const pending = this.pendingMessages.get(chatId);
|
|
559
575
|
this.pendingMessages.delete(chatId);
|
|
560
|
-
if (pending) {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
this.deliverToSession(session.id, pending.text);
|
|
565
|
-
}
|
|
576
|
+
if (pending.photoFileId) {
|
|
577
|
+
await this.deliverPhotoToSessionByFileId(session.id, pending.photoFileId, pending.caption);
|
|
578
|
+
} else if (pending.text) {
|
|
579
|
+
this.deliverToSession(session.id, pending.text);
|
|
566
580
|
}
|
|
567
581
|
await this.answerCallbackQuery(query.id, `Sent to ${this.getLabel(session)}`);
|
|
568
582
|
if (query.message) {
|
|
@@ -624,8 +638,8 @@ ${label}`);
|
|
|
624
638
|
css: "css",
|
|
625
639
|
scss: "scss"
|
|
626
640
|
};
|
|
627
|
-
langOf(
|
|
628
|
-
const m = /\.([a-z0-9]+)$/i.exec(
|
|
641
|
+
langOf(path6) {
|
|
642
|
+
const m = /\.([a-z0-9]+)$/i.exec(path6 || "");
|
|
629
643
|
return m ? _TelegramBot.LANG_BY_EXT[m[1].toLowerCase()] ?? "" : "";
|
|
630
644
|
}
|
|
631
645
|
/** Convert markdown to Telegram HTML (escape first, then apply formatting) */
|
|
@@ -668,9 +682,73 @@ ${bodyLines.join("\n")}`;
|
|
|
668
682
|
}
|
|
669
683
|
};
|
|
670
684
|
|
|
671
|
-
// src/
|
|
685
|
+
// src/hub/codex-supervisor.ts
|
|
686
|
+
import { spawn } from "child_process";
|
|
672
687
|
import fs2 from "fs";
|
|
673
688
|
import path3 from "path";
|
|
689
|
+
var HEALTHY_RUN_MS = 6e4;
|
|
690
|
+
function resolveAdapterScript(baseDir) {
|
|
691
|
+
return [path3.join(baseDir, "..", "codex", "main.js"), path3.join(baseDir, "codex", "main.js")].find((p) => fs2.existsSync(p));
|
|
692
|
+
}
|
|
693
|
+
var CodexSupervisor = class {
|
|
694
|
+
constructor(script, spawnFn = spawn, minDelayMs = 2e3, maxDelayMs = 6e4, stopGraceMs = 3e3) {
|
|
695
|
+
this.script = script;
|
|
696
|
+
this.spawnFn = spawnFn;
|
|
697
|
+
this.minDelayMs = minDelayMs;
|
|
698
|
+
this.maxDelayMs = maxDelayMs;
|
|
699
|
+
this.stopGraceMs = stopGraceMs;
|
|
700
|
+
this.delay = minDelayMs;
|
|
701
|
+
}
|
|
702
|
+
child;
|
|
703
|
+
timer;
|
|
704
|
+
stopped = true;
|
|
705
|
+
delay;
|
|
706
|
+
start() {
|
|
707
|
+
this.stopped = false;
|
|
708
|
+
this.launch();
|
|
709
|
+
}
|
|
710
|
+
stop() {
|
|
711
|
+
this.stopped = true;
|
|
712
|
+
if (this.timer) clearTimeout(this.timer);
|
|
713
|
+
this.timer = void 0;
|
|
714
|
+
const child = this.child;
|
|
715
|
+
this.child = void 0;
|
|
716
|
+
if (!child) return;
|
|
717
|
+
child.stdin?.end();
|
|
718
|
+
const force = setTimeout(() => child.kill(), this.stopGraceMs);
|
|
719
|
+
force.unref();
|
|
720
|
+
child.once("exit", () => clearTimeout(force));
|
|
721
|
+
}
|
|
722
|
+
launch() {
|
|
723
|
+
const startedAt = Date.now();
|
|
724
|
+
const child = this.spawnFn(process.execPath, [this.script, "--watch-stdin"], {
|
|
725
|
+
detached: true,
|
|
726
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
727
|
+
windowsHide: true
|
|
728
|
+
});
|
|
729
|
+
this.child = child;
|
|
730
|
+
child.stdout?.on("data", (d) => process.stdout.write(d));
|
|
731
|
+
child.stderr?.on("data", (d) => process.stderr.write(d));
|
|
732
|
+
child.stdin?.on("error", () => {
|
|
733
|
+
});
|
|
734
|
+
child.on("error", (err) => logger.warn(`Codex adapter failed to start: ${err.message}`));
|
|
735
|
+
child.on("exit", (code, signal) => {
|
|
736
|
+
if (this.child === child) this.child = void 0;
|
|
737
|
+
if (this.stopped || code === 0) return;
|
|
738
|
+
if (Date.now() - startedAt > HEALTHY_RUN_MS) this.delay = this.minDelayMs;
|
|
739
|
+
logger.warn(`Codex adapter exited (${signal ?? code}); restarting in ${this.delay}ms`);
|
|
740
|
+
this.timer = setTimeout(() => {
|
|
741
|
+
this.timer = void 0;
|
|
742
|
+
if (!this.stopped) this.launch();
|
|
743
|
+
}, this.delay);
|
|
744
|
+
this.delay = Math.min(this.delay * 2, this.maxDelayMs);
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
// src/shared/config.ts
|
|
750
|
+
import fs3 from "fs";
|
|
751
|
+
import path4 from "path";
|
|
674
752
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
675
753
|
var DEFAULT_CONFIG = {
|
|
676
754
|
hub: {
|
|
@@ -684,18 +762,18 @@ var DEFAULT_CONFIG = {
|
|
|
684
762
|
webhooks: []
|
|
685
763
|
};
|
|
686
764
|
function ensureConfigDir() {
|
|
687
|
-
if (!
|
|
688
|
-
|
|
765
|
+
if (!fs3.existsSync(CONFIG_DIR)) {
|
|
766
|
+
fs3.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
689
767
|
}
|
|
690
768
|
}
|
|
691
769
|
function loadConfig() {
|
|
692
770
|
ensureConfigDir();
|
|
693
771
|
let config;
|
|
694
|
-
if (!
|
|
772
|
+
if (!fs3.existsSync(CONFIG_FILE)) {
|
|
695
773
|
config = { ...DEFAULT_CONFIG, hub: { ...DEFAULT_CONFIG.hub } };
|
|
696
774
|
} else {
|
|
697
775
|
try {
|
|
698
|
-
const raw =
|
|
776
|
+
const raw = fs3.readFileSync(CONFIG_FILE, "utf-8");
|
|
699
777
|
const parsed = JSON.parse(raw);
|
|
700
778
|
config = { ...DEFAULT_CONFIG, ...parsed, hub: { ...DEFAULT_CONFIG.hub, ...parsed.hub }, ...parsed.telegram ? { telegram: parsed.telegram } : {} };
|
|
701
779
|
} catch {
|
|
@@ -710,17 +788,17 @@ function loadConfig() {
|
|
|
710
788
|
}
|
|
711
789
|
function saveConfig(config) {
|
|
712
790
|
ensureConfigDir();
|
|
713
|
-
|
|
791
|
+
fs3.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { encoding: "utf-8", mode: 384 });
|
|
714
792
|
}
|
|
715
793
|
|
|
716
794
|
// src/shared/crash-guard.ts
|
|
717
|
-
import
|
|
795
|
+
import fs4 from "fs";
|
|
718
796
|
var BURST_WINDOW_MS = 6e4;
|
|
719
797
|
var BURST_LIMIT = 10;
|
|
720
798
|
function appendToLog(text) {
|
|
721
799
|
try {
|
|
722
|
-
|
|
723
|
-
|
|
800
|
+
fs4.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
801
|
+
fs4.appendFileSync(LOG_FILE, text, "utf-8");
|
|
724
802
|
} catch {
|
|
725
803
|
}
|
|
726
804
|
}
|
|
@@ -819,7 +897,7 @@ function isSecureRequest(req) {
|
|
|
819
897
|
}
|
|
820
898
|
|
|
821
899
|
// src/hub/server.ts
|
|
822
|
-
var __dirname =
|
|
900
|
+
var __dirname = path5.dirname(fileURLToPath(import.meta.url));
|
|
823
901
|
var MAX_WS_PAYLOAD = 16 * 1024 * 1024;
|
|
824
902
|
var HubServer = class {
|
|
825
903
|
httpServer;
|
|
@@ -842,10 +920,13 @@ var HubServer = class {
|
|
|
842
920
|
host;
|
|
843
921
|
port;
|
|
844
922
|
token;
|
|
923
|
+
codexEnabled;
|
|
924
|
+
codexSupervisor;
|
|
845
925
|
constructor(config) {
|
|
846
926
|
this.host = config?.hub?.host ?? DEFAULT_HUB_HOST;
|
|
847
927
|
this.port = config?.hub?.port ?? DEFAULT_HUB_PORT;
|
|
848
928
|
this.token = config?.hub?.token;
|
|
929
|
+
this.codexEnabled = config?.codex?.enabled === true;
|
|
849
930
|
if (config?.notifications) {
|
|
850
931
|
this.notifier.configure({
|
|
851
932
|
desktop: config.notifications.desktop
|
|
@@ -903,12 +984,15 @@ var HubServer = class {
|
|
|
903
984
|
this.httpServer.on("error", (err) => logger.error(`HTTP server error: ${err.message}`));
|
|
904
985
|
const displayHost = this.host === "0.0.0.0" ? "127.0.0.1" : this.host;
|
|
905
986
|
logger.info(`Hub server listening on http://${displayHost}:${this.port}`);
|
|
987
|
+
this.startCodexAdapter();
|
|
906
988
|
resolve();
|
|
907
989
|
});
|
|
908
990
|
});
|
|
909
991
|
}
|
|
910
992
|
stop() {
|
|
911
993
|
return new Promise((resolve) => {
|
|
994
|
+
this.codexSupervisor?.stop();
|
|
995
|
+
this.codexSupervisor = void 0;
|
|
912
996
|
if (this.heartbeatInterval) clearInterval(this.heartbeatInterval);
|
|
913
997
|
if (this.telegramBot) this.telegramBot.stopPolling();
|
|
914
998
|
for (const ws of this.channelSockets.values()) ws.terminate();
|
|
@@ -1009,23 +1093,23 @@ var HubServer = class {
|
|
|
1009
1093
|
return;
|
|
1010
1094
|
}
|
|
1011
1095
|
const candidates = [
|
|
1012
|
-
|
|
1096
|
+
path5.join(__dirname, "..", "dashboard", "index.html"),
|
|
1013
1097
|
// from dist/hub/
|
|
1014
|
-
|
|
1098
|
+
path5.join(__dirname, "dashboard", "index.html"),
|
|
1015
1099
|
// from dist/ (bundled index.js)
|
|
1016
|
-
|
|
1100
|
+
path5.join(__dirname, "..", "..", "src", "dashboard", "index.html"),
|
|
1017
1101
|
// from dist/hub/ -> src/
|
|
1018
|
-
|
|
1102
|
+
path5.join(__dirname, "..", "src", "dashboard", "index.html"),
|
|
1019
1103
|
// from dist/ -> src/
|
|
1020
|
-
|
|
1104
|
+
path5.join(process.cwd(), "dist", "dashboard", "index.html"),
|
|
1021
1105
|
// from cwd
|
|
1022
|
-
|
|
1106
|
+
path5.join(process.cwd(), "src", "dashboard", "index.html")
|
|
1023
1107
|
// from cwd/src
|
|
1024
1108
|
];
|
|
1025
1109
|
logger.debug(`Dashboard candidates: ${JSON.stringify(candidates)}`);
|
|
1026
1110
|
for (const candidate of candidates) {
|
|
1027
|
-
if (
|
|
1028
|
-
const html =
|
|
1111
|
+
if (fs5.existsSync(candidate)) {
|
|
1112
|
+
const html = fs5.readFileSync(candidate, "utf-8");
|
|
1029
1113
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
1030
1114
|
res.end(html);
|
|
1031
1115
|
return;
|
|
@@ -1064,7 +1148,7 @@ var HubServer = class {
|
|
|
1064
1148
|
this.jsonResponse(res, 404, { error: "Session not connected" });
|
|
1065
1149
|
return;
|
|
1066
1150
|
}
|
|
1067
|
-
const msg = { type: "message_to_session", sessionId, content };
|
|
1151
|
+
const msg = { type: "message_to_session", sessionId, content, source: "api" };
|
|
1068
1152
|
ws.send(JSON.stringify(msg));
|
|
1069
1153
|
this.jsonResponse(res, 200, { ok: true });
|
|
1070
1154
|
}
|
|
@@ -1238,7 +1322,7 @@ var HubServer = class {
|
|
|
1238
1322
|
if (msg.type === "message_to_session") {
|
|
1239
1323
|
const channelWs = this.channelSockets.get(msg.sessionId);
|
|
1240
1324
|
if (channelWs?.readyState === WebSocket.OPEN) {
|
|
1241
|
-
channelWs.send(JSON.stringify(msg));
|
|
1325
|
+
channelWs.send(JSON.stringify({ ...msg, source: "dashboard" }));
|
|
1242
1326
|
}
|
|
1243
1327
|
} else if (msg.type === "image_upload") {
|
|
1244
1328
|
this.handleImageUpload(msg);
|
|
@@ -1283,11 +1367,11 @@ var HubServer = class {
|
|
|
1283
1367
|
logger.warn("Image upload rejected: exceeds 10MB");
|
|
1284
1368
|
return;
|
|
1285
1369
|
}
|
|
1286
|
-
|
|
1370
|
+
fs5.mkdirSync(UPLOADS_DIR, { recursive: true });
|
|
1287
1371
|
const ext = mimeType.split("/")[1] === "jpeg" ? "jpg" : mimeType.split("/")[1];
|
|
1288
1372
|
const filename = `${randomUUID3()}.${ext}`;
|
|
1289
|
-
const filePath =
|
|
1290
|
-
|
|
1373
|
+
const filePath = path5.join(UPLOADS_DIR, filename);
|
|
1374
|
+
fs5.writeFileSync(filePath, buffer);
|
|
1291
1375
|
const forwardMsg = {
|
|
1292
1376
|
type: "image_to_session",
|
|
1293
1377
|
sessionId,
|
|
@@ -1300,7 +1384,7 @@ var HubServer = class {
|
|
|
1300
1384
|
logger.info(`Image saved and forwarded: ${filename} (${buffer.length} bytes)`);
|
|
1301
1385
|
setTimeout(() => {
|
|
1302
1386
|
try {
|
|
1303
|
-
|
|
1387
|
+
fs5.unlinkSync(filePath);
|
|
1304
1388
|
} catch {
|
|
1305
1389
|
}
|
|
1306
1390
|
}, 5 * 60 * 1e3);
|
|
@@ -1328,7 +1412,7 @@ var HubServer = class {
|
|
|
1328
1412
|
this.telegramBot.onMessageToSession = (sessionId, content) => {
|
|
1329
1413
|
const channelWs = this.channelSockets.get(sessionId);
|
|
1330
1414
|
if (channelWs?.readyState === WebSocket.OPEN) {
|
|
1331
|
-
const msg = { type: "message_to_session", sessionId, content };
|
|
1415
|
+
const msg = { type: "message_to_session", sessionId, content, source: "telegram" };
|
|
1332
1416
|
channelWs.send(JSON.stringify(msg));
|
|
1333
1417
|
logger.info(`Telegram message forwarded to session: ${sessionId}`);
|
|
1334
1418
|
}
|
|
@@ -1455,6 +1539,17 @@ var HubServer = class {
|
|
|
1455
1539
|
this.jsonResponse(res, 500, { error: err.message });
|
|
1456
1540
|
}
|
|
1457
1541
|
}
|
|
1542
|
+
startCodexAdapter() {
|
|
1543
|
+
if (!this.codexEnabled) return;
|
|
1544
|
+
const script = resolveAdapterScript(__dirname);
|
|
1545
|
+
if (!script) {
|
|
1546
|
+
logger.warn("Codex adapter is enabled but codex/main.js was not found next to the hub");
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
this.codexSupervisor = new CodexSupervisor(script);
|
|
1550
|
+
this.codexSupervisor.start();
|
|
1551
|
+
logger.info("Codex adapter started");
|
|
1552
|
+
}
|
|
1458
1553
|
startHeartbeat() {
|
|
1459
1554
|
this.heartbeatInterval = setInterval(() => {
|
|
1460
1555
|
for (const [sessionId, ws] of this.channelSockets) {
|
|
@@ -1470,11 +1565,11 @@ var HubServer = class {
|
|
|
1470
1565
|
}
|
|
1471
1566
|
cleanupUploads() {
|
|
1472
1567
|
try {
|
|
1473
|
-
if (!
|
|
1474
|
-
const files =
|
|
1568
|
+
if (!fs5.existsSync(UPLOADS_DIR)) return;
|
|
1569
|
+
const files = fs5.readdirSync(UPLOADS_DIR);
|
|
1475
1570
|
for (const file of files) {
|
|
1476
1571
|
try {
|
|
1477
|
-
|
|
1572
|
+
fs5.unlinkSync(path5.join(UPLOADS_DIR, file));
|
|
1478
1573
|
} catch {
|
|
1479
1574
|
}
|
|
1480
1575
|
}
|
|
@@ -1483,8 +1578,7 @@ var HubServer = class {
|
|
|
1483
1578
|
}
|
|
1484
1579
|
}
|
|
1485
1580
|
getSessionLabel(session) {
|
|
1486
|
-
|
|
1487
|
-
return session.displayName || session.cwd?.replace(/^.*[/\\]/, "") || session.name;
|
|
1581
|
+
return session ? sessionLabel(session) : "unknown";
|
|
1488
1582
|
}
|
|
1489
1583
|
jsonResponse(res, status, body) {
|
|
1490
1584
|
res.writeHead(status, { "Content-Type": "application/json" });
|