@delt/claude-alarm 0.5.2 → 0.5.4

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 CHANGED
@@ -293,11 +293,15 @@ var init_notifier = __esm({
293
293
  });
294
294
 
295
295
  // src/hub/telegram.ts
296
+ import fs2 from "fs";
297
+ import path3 from "path";
298
+ import { randomUUID as randomUUID2 } from "crypto";
296
299
  var TELEGRAM_API, TelegramBot;
297
300
  var init_telegram = __esm({
298
301
  "src/hub/telegram.ts"() {
299
302
  "use strict";
300
303
  init_logger();
304
+ init_constants();
301
305
  TELEGRAM_API = "https://api.telegram.org/bot";
302
306
  TelegramBot = class {
303
307
  config;
@@ -306,13 +310,15 @@ var init_telegram = __esm({
306
310
  pollTimer = null;
307
311
  // message_id -> sessionId mapping for reply-based routing
308
312
  messageSessionMap = /* @__PURE__ */ new Map();
309
- // Callback: when a message arrives from Telegram for a session
313
+ // Callback: when a text message arrives from Telegram for a session
310
314
  onMessageToSession;
315
+ // Callback: when an image arrives from Telegram for a session
316
+ onImageToSession;
311
317
  // Callback: get current sessions list
312
318
  getSessions;
313
- // Callback: when user needs to select a session (sends inline keyboard)
319
+ // Pending messages for session selection
314
320
  pendingMessages = /* @__PURE__ */ new Map();
315
- // chatId -> pending message text
321
+ // chatId -> pending
316
322
  constructor(config2) {
317
323
  this.config = config2;
318
324
  }
@@ -408,31 +414,42 @@ ${message}`;
408
414
  if (!this.polling) return;
409
415
  this.pollTimer = setTimeout(() => this.poll(), delay);
410
416
  }
411
- handleIncomingMessage(msg) {
412
- if (!msg.text) return;
417
+ async handleIncomingMessage(msg) {
413
418
  if (String(msg.chat.id) !== String(this.config.chatId)) return;
414
- const text = msg.text.trim();
419
+ const hasPhoto = msg.photo && msg.photo.length > 0;
420
+ const text = (msg.text || msg.caption || "").trim();
421
+ if (!text && !hasPhoto) return;
415
422
  if (msg.reply_to_message) {
416
423
  const sessionId2 = this.messageSessionMap.get(msg.reply_to_message.message_id);
417
424
  if (sessionId2) {
418
- this.deliverToSession(sessionId2, text);
425
+ if (hasPhoto) {
426
+ await this.deliverPhotoToSession(sessionId2, msg.photo, text);
427
+ } else {
428
+ this.deliverToSession(sessionId2, text);
429
+ }
419
430
  return;
420
431
  }
421
432
  }
422
- const selectMatch = text.match(/^\/s_(\d+)$/);
423
- if (selectMatch) {
424
- const pendingText = this.pendingMessages.get(msg.chat.id);
425
- if (pendingText) {
426
- this.pendingMessages.delete(msg.chat.id);
427
- const sessions2 = this.getSessions?.() ?? [];
428
- const idx = parseInt(selectMatch[1], 10) - 1;
429
- if (idx >= 0 && idx < sessions2.length) {
430
- this.deliverToSession(sessions2[idx].id, pendingText);
431
- this.sendMessage(`Sent to [${this.getLabel(sessions2[idx])}]`);
432
- } else {
433
- this.sendMessage("Invalid session number.");
433
+ if (text) {
434
+ const selectMatch = text.match(/^\/s_(\d+)$/);
435
+ if (selectMatch) {
436
+ const pending = this.pendingMessages.get(msg.chat.id);
437
+ if (pending) {
438
+ this.pendingMessages.delete(msg.chat.id);
439
+ const sessions2 = this.getSessions?.() ?? [];
440
+ const idx = parseInt(selectMatch[1], 10) - 1;
441
+ if (idx >= 0 && idx < sessions2.length) {
442
+ if (pending.photoFileId) {
443
+ await this.deliverPhotoToSessionByFileId(sessions2[idx].id, pending.photoFileId, pending.caption);
444
+ } else if (pending.text) {
445
+ this.deliverToSession(sessions2[idx].id, pending.text);
446
+ }
447
+ this.sendMessage(`Sent to [${this.getLabel(sessions2[idx])}]`);
448
+ } else {
449
+ this.sendMessage("Invalid session number.");
450
+ }
451
+ return;
434
452
  }
435
- return;
436
453
  }
437
454
  }
438
455
  const sessions = this.getSessions?.() ?? [];
@@ -441,10 +458,19 @@ ${message}`;
441
458
  return;
442
459
  }
443
460
  if (sessions.length === 1) {
444
- this.deliverToSession(sessions[0].id, text);
461
+ if (hasPhoto) {
462
+ await this.deliverPhotoToSession(sessions[0].id, msg.photo, text);
463
+ } else {
464
+ this.deliverToSession(sessions[0].id, text);
465
+ }
445
466
  return;
446
467
  }
447
- this.pendingMessages.set(msg.chat.id, text);
468
+ if (hasPhoto) {
469
+ const largest = msg.photo[msg.photo.length - 1];
470
+ this.pendingMessages.set(msg.chat.id, { photoFileId: largest.file_id, caption: text });
471
+ } else {
472
+ this.pendingMessages.set(msg.chat.id, { text });
473
+ }
448
474
  const sessionList = sessions.map((s, i) => `/s_${i + 1} - ${this.getLabel(s)}`).join("\n");
449
475
  this.sendMessage(`Multiple sessions active. Reply with a command to select:
450
476
 
@@ -455,6 +481,46 @@ ${sessionList}`);
455
481
  this.onMessageToSession(sessionId2, content);
456
482
  }
457
483
  }
484
+ async deliverPhotoToSession(sessionId2, photos, caption) {
485
+ const largest = photos[photos.length - 1];
486
+ await this.deliverPhotoToSessionByFileId(sessionId2, largest.file_id, caption);
487
+ }
488
+ async deliverPhotoToSessionByFileId(sessionId2, fileId, caption) {
489
+ try {
490
+ const fileRes = await fetch(`${this.apiUrl}/getFile?file_id=${fileId}`);
491
+ if (!fileRes.ok) {
492
+ logger.warn("Failed to get Telegram file info");
493
+ return;
494
+ }
495
+ const fileData = await fileRes.json();
496
+ if (!fileData.ok) return;
497
+ const downloadUrl = `https://api.telegram.org/file/bot${this.config.botToken}/${fileData.result.file_path}`;
498
+ const imgRes = await fetch(downloadUrl);
499
+ if (!imgRes.ok) {
500
+ logger.warn("Failed to download Telegram photo");
501
+ return;
502
+ }
503
+ const buffer = Buffer.from(await imgRes.arrayBuffer());
504
+ const ext = fileData.result.file_path.split(".").pop() || "jpg";
505
+ const mimeType = ext === "png" ? "image/png" : ext === "gif" ? "image/gif" : ext === "webp" ? "image/webp" : "image/jpeg";
506
+ fs2.mkdirSync(UPLOADS_DIR, { recursive: true });
507
+ const filename = `${randomUUID2()}.${ext}`;
508
+ const filePath = path3.join(UPLOADS_DIR, filename);
509
+ fs2.writeFileSync(filePath, buffer);
510
+ logger.info(`Telegram photo saved: ${filename} (${buffer.length} bytes)`);
511
+ if (this.onImageToSession) {
512
+ this.onImageToSession(sessionId2, filePath, mimeType, caption);
513
+ }
514
+ setTimeout(() => {
515
+ try {
516
+ fs2.unlinkSync(filePath);
517
+ } catch {
518
+ }
519
+ }, 5 * 60 * 1e3);
520
+ } catch (err) {
521
+ logger.warn(`Telegram photo download failed: ${err.message}`);
522
+ }
523
+ }
458
524
  getLabel(session) {
459
525
  return session.cwd?.replace(/^.*[/\\]/, "") || session.name;
460
526
  }
@@ -475,11 +541,11 @@ __export(server_exports, {
475
541
  HubServer: () => HubServer
476
542
  });
477
543
  import http from "http";
478
- import fs2 from "fs";
479
- import path3 from "path";
544
+ import fs3 from "fs";
545
+ import path4 from "path";
480
546
  import { fileURLToPath } from "url";
481
547
  import { WebSocketServer, WebSocket } from "ws";
482
- import { randomUUID as randomUUID2 } from "crypto";
548
+ import { randomUUID as randomUUID3 } from "crypto";
483
549
  var __dirname, HubServer;
484
550
  var init_server = __esm({
485
551
  "src/hub/server.ts"() {
@@ -490,7 +556,7 @@ var init_server = __esm({
490
556
  init_notifier();
491
557
  init_telegram();
492
558
  init_config();
493
- __dirname = path3.dirname(fileURLToPath(import.meta.url));
559
+ __dirname = path4.dirname(fileURLToPath(import.meta.url));
494
560
  HubServer = class {
495
561
  httpServer;
496
562
  wssChannel;
@@ -639,29 +705,31 @@ var init_server = __esm({
639
705
  this.handleTelegramSave(req, res);
640
706
  } else if (url.pathname === "/api/telegram/test" && req.method === "POST") {
641
707
  this.handleTelegramTest(req, res);
708
+ } else if (url.pathname === "/api/telegram/detect" && req.method === "POST") {
709
+ this.handleTelegramDetect(req, res);
642
710
  } else {
643
711
  this.jsonResponse(res, 404, { error: "Not found" });
644
712
  }
645
713
  }
646
714
  serveDashboard(res) {
647
715
  const candidates = [
648
- path3.join(__dirname, "..", "dashboard", "index.html"),
716
+ path4.join(__dirname, "..", "dashboard", "index.html"),
649
717
  // from dist/hub/
650
- path3.join(__dirname, "dashboard", "index.html"),
718
+ path4.join(__dirname, "dashboard", "index.html"),
651
719
  // from dist/ (bundled index.js)
652
- path3.join(__dirname, "..", "..", "src", "dashboard", "index.html"),
720
+ path4.join(__dirname, "..", "..", "src", "dashboard", "index.html"),
653
721
  // from dist/hub/ -> src/
654
- path3.join(__dirname, "..", "src", "dashboard", "index.html"),
722
+ path4.join(__dirname, "..", "src", "dashboard", "index.html"),
655
723
  // from dist/ -> src/
656
- path3.join(process.cwd(), "dist", "dashboard", "index.html"),
724
+ path4.join(process.cwd(), "dist", "dashboard", "index.html"),
657
725
  // from cwd
658
- path3.join(process.cwd(), "src", "dashboard", "index.html")
726
+ path4.join(process.cwd(), "src", "dashboard", "index.html")
659
727
  // from cwd/src
660
728
  ];
661
729
  logger.debug(`Dashboard candidates: ${JSON.stringify(candidates)}`);
662
730
  for (const candidate of candidates) {
663
- if (fs2.existsSync(candidate)) {
664
- const html = fs2.readFileSync(candidate, "utf-8");
731
+ if (fs3.existsSync(candidate)) {
732
+ const html = fs3.readFileSync(candidate, "utf-8");
665
733
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
666
734
  res.end(html);
667
735
  return;
@@ -839,11 +907,11 @@ var init_server = __esm({
839
907
  logger.warn("Image upload rejected: exceeds 10MB");
840
908
  return;
841
909
  }
842
- fs2.mkdirSync(UPLOADS_DIR, { recursive: true });
910
+ fs3.mkdirSync(UPLOADS_DIR, { recursive: true });
843
911
  const ext = mimeType.split("/")[1] === "jpeg" ? "jpg" : mimeType.split("/")[1];
844
- const filename = `${randomUUID2()}.${ext}`;
845
- const filePath = path3.join(UPLOADS_DIR, filename);
846
- fs2.writeFileSync(filePath, buffer);
912
+ const filename = `${randomUUID3()}.${ext}`;
913
+ const filePath = path4.join(UPLOADS_DIR, filename);
914
+ fs3.writeFileSync(filePath, buffer);
847
915
  const forwardMsg = {
848
916
  type: "image_to_session",
849
917
  sessionId: sessionId2,
@@ -856,7 +924,7 @@ var init_server = __esm({
856
924
  logger.info(`Image saved and forwarded: ${filename} (${buffer.length} bytes)`);
857
925
  setTimeout(() => {
858
926
  try {
859
- fs2.unlinkSync(filePath);
927
+ fs3.unlinkSync(filePath);
860
928
  } catch {
861
929
  }
862
930
  }, 5 * 60 * 1e3);
@@ -889,6 +957,14 @@ var init_server = __esm({
889
957
  logger.info(`Telegram message forwarded to session: ${sessionId2}`);
890
958
  }
891
959
  };
960
+ this.telegramBot.onImageToSession = (sessionId2, imagePath, mimeType, caption) => {
961
+ const channelWs = this.channelSockets.get(sessionId2);
962
+ if (channelWs?.readyState === WebSocket.OPEN) {
963
+ const msg = { type: "image_to_session", sessionId: sessionId2, imagePath, mimeType, content: caption };
964
+ channelWs.send(JSON.stringify(msg));
965
+ logger.info(`Telegram photo forwarded to session: ${sessionId2}`);
966
+ }
967
+ };
892
968
  this.notifier.configure({ telegramBot: this.telegramBot });
893
969
  this.telegramBot.startPolling();
894
970
  logger.info("Telegram bot initialized");
@@ -947,13 +1023,57 @@ var init_server = __esm({
947
1023
  this.jsonResponse(res, 500, { error: err.message });
948
1024
  }
949
1025
  }
1026
+ async handleTelegramDetect(req, res) {
1027
+ const body = await this.readBody(req);
1028
+ if (!body) {
1029
+ this.jsonResponse(res, 400, { error: "Invalid JSON" });
1030
+ return;
1031
+ }
1032
+ const { botToken } = body;
1033
+ if (!botToken) {
1034
+ this.jsonResponse(res, 400, { error: "botToken required" });
1035
+ return;
1036
+ }
1037
+ try {
1038
+ const detectRes = await fetch(`https://api.telegram.org/bot${botToken}/getUpdates?timeout=0&limit=10`, {
1039
+ signal: AbortSignal.timeout(1e4)
1040
+ });
1041
+ if (!detectRes.ok) {
1042
+ const err = await detectRes.json();
1043
+ this.jsonResponse(res, 400, { error: err.description || "Invalid bot token" });
1044
+ return;
1045
+ }
1046
+ const data = await detectRes.json();
1047
+ if (!data.ok || !data.result.length) {
1048
+ this.jsonResponse(res, 200, { ok: false, chats: [] });
1049
+ return;
1050
+ }
1051
+ const chatMap = /* @__PURE__ */ new Map();
1052
+ for (const update of data.result) {
1053
+ if (update.message?.chat) {
1054
+ const chat = update.message.chat;
1055
+ const id = String(chat.id);
1056
+ if (!chatMap.has(id)) {
1057
+ chatMap.set(id, {
1058
+ id,
1059
+ name: chat.title || chat.first_name || id,
1060
+ type: chat.type
1061
+ });
1062
+ }
1063
+ }
1064
+ }
1065
+ this.jsonResponse(res, 200, { ok: true, chats: [...chatMap.values()] });
1066
+ } catch (err) {
1067
+ this.jsonResponse(res, 500, { error: err.message });
1068
+ }
1069
+ }
950
1070
  cleanupUploads() {
951
1071
  try {
952
- if (!fs2.existsSync(UPLOADS_DIR)) return;
953
- const files = fs2.readdirSync(UPLOADS_DIR);
1072
+ if (!fs3.existsSync(UPLOADS_DIR)) return;
1073
+ const files = fs3.readdirSync(UPLOADS_DIR);
954
1074
  for (const file of files) {
955
1075
  try {
956
- fs2.unlinkSync(path3.join(UPLOADS_DIR, file));
1076
+ fs3.unlinkSync(path4.join(UPLOADS_DIR, file));
957
1077
  } catch {
958
1078
  }
959
1079
  }
@@ -1126,8 +1246,8 @@ import {
1126
1246
  CallToolRequestSchema,
1127
1247
  ListToolsRequestSchema
1128
1248
  } from "@modelcontextprotocol/sdk/types.js";
1129
- import { randomUUID as randomUUID3 } from "crypto";
1130
- import path4 from "path";
1249
+ import { randomUUID as randomUUID4 } from "crypto";
1250
+ import path5 from "path";
1131
1251
  async function main() {
1132
1252
  logger.info(`Starting MCP channel server (session: ${sessionId})`);
1133
1253
  hubClient.connect();
@@ -1166,8 +1286,8 @@ var init_server2 = __esm({
1166
1286
  init_constants();
1167
1287
  init_config();
1168
1288
  init_hub_client();
1169
- sessionId = randomUUID3();
1170
- sessionName = process.env.CLAUDE_ALARM_SESSION_NAME ?? path4.basename(process.cwd());
1289
+ sessionId = randomUUID4();
1290
+ sessionName = process.env.CLAUDE_ALARM_SESSION_NAME ?? path5.basename(process.cwd());
1171
1291
  server = new Server(
1172
1292
  {
1173
1293
  name: CHANNEL_SERVER_NAME,
@@ -1301,11 +1421,11 @@ init_config();
1301
1421
  init_constants();
1302
1422
  init_logger();
1303
1423
  import { spawn } from "child_process";
1304
- import fs3 from "fs";
1305
- import path5 from "path";
1424
+ import fs4 from "fs";
1425
+ import path6 from "path";
1306
1426
  import readline from "readline";
1307
1427
  import { fileURLToPath as fileURLToPath2 } from "url";
1308
- var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
1428
+ var __dirname2 = path6.dirname(fileURLToPath2(import.meta.url));
1309
1429
  function printUsage() {
1310
1430
  console.log(`
1311
1431
  claude-alarm - Monitor Claude Code sessions with notifications
@@ -1326,8 +1446,8 @@ Quick start:
1326
1446
  }
1327
1447
  async function checkForUpdates() {
1328
1448
  try {
1329
- const pkgPath = path5.join(__dirname2, "..", "package.json");
1330
- const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
1449
+ const pkgPath = path6.join(__dirname2, "..", "package.json");
1450
+ const pkg = JSON.parse(fs4.readFileSync(pkgPath, "utf-8"));
1331
1451
  const currentVersion = pkg.version;
1332
1452
  const res = await fetch("https://registry.npmjs.org/@delt/claude-alarm/latest", {
1333
1453
  signal: AbortSignal.timeout(3e3)
@@ -1350,25 +1470,25 @@ async function hubStart(daemon) {
1350
1470
  const port = config2.hub.port ?? DEFAULT_HUB_PORT;
1351
1471
  const displayHost = host === "0.0.0.0" ? "127.0.0.1" : host;
1352
1472
  checkForUpdates();
1353
- if (fs3.existsSync(PID_FILE)) {
1354
- const pid = parseInt(fs3.readFileSync(PID_FILE, "utf-8").trim(), 10);
1473
+ if (fs4.existsSync(PID_FILE)) {
1474
+ const pid = parseInt(fs4.readFileSync(PID_FILE, "utf-8").trim(), 10);
1355
1475
  if (isProcessRunning(pid)) {
1356
1476
  console.log(`Hub is already running (PID: ${pid}) on http://${displayHost}:${port}`);
1357
1477
  return;
1358
1478
  }
1359
- fs3.unlinkSync(PID_FILE);
1479
+ fs4.unlinkSync(PID_FILE);
1360
1480
  }
1361
1481
  if (daemon) {
1362
1482
  ensureConfigDir();
1363
- const logFd = fs3.openSync(LOG_FILE, "a");
1364
- const hubScript = path5.join(__dirname2, "hub", "server.js");
1483
+ const logFd = fs4.openSync(LOG_FILE, "a");
1484
+ const hubScript = path6.join(__dirname2, "hub", "server.js");
1365
1485
  const child = spawn(process.execPath, [hubScript], {
1366
1486
  detached: true,
1367
1487
  stdio: ["ignore", logFd, logFd],
1368
1488
  env: { ...process.env }
1369
1489
  });
1370
1490
  if (child.pid) {
1371
- fs3.writeFileSync(PID_FILE, String(child.pid), "utf-8");
1491
+ fs4.writeFileSync(PID_FILE, String(child.pid), "utf-8");
1372
1492
  child.unref();
1373
1493
  console.log(`Hub started as daemon (PID: ${child.pid})`);
1374
1494
  console.log(`Dashboard: http://${displayHost}:${port}`);
@@ -1385,11 +1505,11 @@ async function hubStart(daemon) {
1385
1505
  const hub = new HubServer2(config2);
1386
1506
  await hub.start();
1387
1507
  ensureConfigDir();
1388
- fs3.writeFileSync(PID_FILE, String(process.pid), "utf-8");
1508
+ fs4.writeFileSync(PID_FILE, String(process.pid), "utf-8");
1389
1509
  const shutdown = async () => {
1390
1510
  console.log("\nShutting down...");
1391
1511
  await hub.stop();
1392
- if (fs3.existsSync(PID_FILE)) fs3.unlinkSync(PID_FILE);
1512
+ if (fs4.existsSync(PID_FILE)) fs4.unlinkSync(PID_FILE);
1393
1513
  process.exit(0);
1394
1514
  };
1395
1515
  process.on("SIGINT", shutdown);
@@ -1397,18 +1517,18 @@ async function hubStart(daemon) {
1397
1517
  }
1398
1518
  }
1399
1519
  function hubStop() {
1400
- if (!fs3.existsSync(PID_FILE)) {
1520
+ if (!fs4.existsSync(PID_FILE)) {
1401
1521
  console.log("Hub is not running (no PID file found)");
1402
1522
  return;
1403
1523
  }
1404
- const pid = parseInt(fs3.readFileSync(PID_FILE, "utf-8").trim(), 10);
1524
+ const pid = parseInt(fs4.readFileSync(PID_FILE, "utf-8").trim(), 10);
1405
1525
  try {
1406
1526
  process.kill(pid, "SIGTERM");
1407
1527
  console.log(`Hub stopped (PID: ${pid})`);
1408
1528
  } catch {
1409
1529
  console.log("Hub process not found (may have already stopped)");
1410
1530
  }
1411
- fs3.unlinkSync(PID_FILE);
1531
+ fs4.unlinkSync(PID_FILE);
1412
1532
  }
1413
1533
  async function hubStatus() {
1414
1534
  const config2 = loadConfig();
@@ -1416,8 +1536,8 @@ async function hubStatus() {
1416
1536
  const port = config2.hub.port ?? DEFAULT_HUB_PORT;
1417
1537
  const displayHost = host === "0.0.0.0" ? "127.0.0.1" : host;
1418
1538
  let pidInfo = "not running";
1419
- if (fs3.existsSync(PID_FILE)) {
1420
- const pid = parseInt(fs3.readFileSync(PID_FILE, "utf-8").trim(), 10);
1539
+ if (fs4.existsSync(PID_FILE)) {
1540
+ const pid = parseInt(fs4.readFileSync(PID_FILE, "utf-8").trim(), 10);
1421
1541
  if (isProcessRunning(pid)) {
1422
1542
  pidInfo = `running (PID: ${pid})`;
1423
1543
  } else {
@@ -1489,7 +1609,7 @@ function ask(question) {
1489
1609
  }
1490
1610
  async function init() {
1491
1611
  const dir = process.cwd();
1492
- const projectName = path5.basename(dir);
1612
+ const projectName = path6.basename(dir);
1493
1613
  console.log(`
1494
1614
  claude-alarm init for "${projectName}"
1495
1615
  `);
@@ -1509,11 +1629,11 @@ claude-alarm init for "${projectName}"
1509
1629
  if (port) env.CLAUDE_ALARM_HUB_PORT = port;
1510
1630
  if (token) env.CLAUDE_ALARM_HUB_TOKEN = token;
1511
1631
  }
1512
- const mcpPath = path5.join(dir, ".mcp.json");
1632
+ const mcpPath = path6.join(dir, ".mcp.json");
1513
1633
  let mcpConfig = {};
1514
- if (fs3.existsSync(mcpPath)) {
1634
+ if (fs4.existsSync(mcpPath)) {
1515
1635
  try {
1516
- mcpConfig = JSON.parse(fs3.readFileSync(mcpPath, "utf-8"));
1636
+ mcpConfig = JSON.parse(fs4.readFileSync(mcpPath, "utf-8"));
1517
1637
  } catch {
1518
1638
  mcpConfig = {};
1519
1639
  }
@@ -1524,7 +1644,7 @@ claude-alarm init for "${projectName}"
1524
1644
  args: ["-y", "@delt/claude-alarm", "serve"],
1525
1645
  env
1526
1646
  };
1527
- fs3.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2), "utf-8");
1647
+ fs4.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2), "utf-8");
1528
1648
  console.log(`
1529
1649
  \u2713 Created ${mcpPath}`);
1530
1650
  if (remote.toLowerCase() !== "y") {