@delt/claude-alarm 0.8.1 → 0.9.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/README.md +14 -0
- package/dist/channel/server.js +69 -5
- package/dist/channel/server.js.map +1 -1
- package/dist/cli.js +176 -38
- package/dist/cli.js.map +1 -1
- package/dist/dashboard/index.html +279 -26
- package/dist/hub/server.js +77 -9
- package/dist/hub/server.js.map +1 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +81 -11
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/src/dashboard/index.html +279 -26
package/dist/cli.js
CHANGED
|
@@ -190,11 +190,24 @@ var init_crash_guard = __esm({
|
|
|
190
190
|
}
|
|
191
191
|
});
|
|
192
192
|
|
|
193
|
+
// src/shared/peer-name.ts
|
|
194
|
+
function isValidPeerName(name) {
|
|
195
|
+
return typeof name === "string" && name.trim() !== "" && SAFE_PEER_NAME.test(name);
|
|
196
|
+
}
|
|
197
|
+
var SAFE_PEER_NAME;
|
|
198
|
+
var init_peer_name = __esm({
|
|
199
|
+
"src/shared/peer-name.ts"() {
|
|
200
|
+
"use strict";
|
|
201
|
+
SAFE_PEER_NAME = /^[^\u0000-\u001f\u007f"\\[\]]{1,128}$/;
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
193
205
|
// src/hub/session-manager.ts
|
|
194
206
|
var SessionManager;
|
|
195
207
|
var init_session_manager = __esm({
|
|
196
208
|
"src/hub/session-manager.ts"() {
|
|
197
209
|
"use strict";
|
|
210
|
+
init_peer_name();
|
|
198
211
|
SessionManager = class {
|
|
199
212
|
sessions = /* @__PURE__ */ new Map();
|
|
200
213
|
register(session) {
|
|
@@ -217,7 +230,7 @@ var init_session_manager = __esm({
|
|
|
217
230
|
} else {
|
|
218
231
|
session.displayName = baseName;
|
|
219
232
|
}
|
|
220
|
-
this.sessions.set(session.id, { ...session });
|
|
233
|
+
this.sessions.set(session.id, { ...session, peerName: isValidPeerName(session.peerName) ? session.peerName : void 0 });
|
|
221
234
|
}
|
|
222
235
|
unregister(sessionId2) {
|
|
223
236
|
const session = this.sessions.get(sessionId2);
|
|
@@ -232,6 +245,13 @@ var init_session_manager = __esm({
|
|
|
232
245
|
}
|
|
233
246
|
return session;
|
|
234
247
|
}
|
|
248
|
+
setPeerName(sessionId2, peerName2) {
|
|
249
|
+
const session = this.sessions.get(sessionId2);
|
|
250
|
+
if (session) {
|
|
251
|
+
session.peerName = isValidPeerName(peerName2) ? peerName2 : void 0;
|
|
252
|
+
}
|
|
253
|
+
return session;
|
|
254
|
+
}
|
|
235
255
|
updateActivity(sessionId2) {
|
|
236
256
|
const session = this.sessions.get(sessionId2);
|
|
237
257
|
if (session) {
|
|
@@ -378,7 +398,7 @@ var init_telegram = __esm({
|
|
|
378
398
|
init_logger();
|
|
379
399
|
init_constants();
|
|
380
400
|
TELEGRAM_API = "https://api.telegram.org/bot";
|
|
381
|
-
TelegramBot = class {
|
|
401
|
+
TelegramBot = class _TelegramBot {
|
|
382
402
|
config;
|
|
383
403
|
offset = 0;
|
|
384
404
|
polling = false;
|
|
@@ -610,33 +630,45 @@ ${this.mdToHtml(message)}`;
|
|
|
610
630
|
}
|
|
611
631
|
/** Send a permission request with inline buttons */
|
|
612
632
|
async sendPermissionRequest(sessionId2, sessionLabel, requestId, toolName, description, inputPreview) {
|
|
633
|
+
const isNotify = toolName.endsWith("__notify") || toolName === "notify";
|
|
613
634
|
let preview = inputPreview;
|
|
635
|
+
let kind = "code";
|
|
636
|
+
let lang = "";
|
|
614
637
|
let truncated = false;
|
|
615
638
|
try {
|
|
616
639
|
const p = JSON.parse(inputPreview);
|
|
617
|
-
if (p.command)
|
|
618
|
-
|
|
619
|
-
|
|
640
|
+
if (p.command) {
|
|
641
|
+
preview = `$ ${p.command}`;
|
|
642
|
+
lang = "bash";
|
|
643
|
+
} else if (p.title && p.message) {
|
|
644
|
+
preview = p.message;
|
|
645
|
+
kind = "prose";
|
|
646
|
+
} else if (p.file_path) {
|
|
620
647
|
preview = p.file_path;
|
|
648
|
+
lang = this.langOf(p.file_path);
|
|
621
649
|
if (p.content) {
|
|
622
650
|
preview += "\n" + p.content.slice(0, 3e3);
|
|
623
651
|
if (p.content.length > 500) truncated = true;
|
|
624
652
|
}
|
|
625
653
|
} else if (p.content && typeof p.content === "string") {
|
|
626
654
|
preview = p.content.slice(0, 3e3);
|
|
655
|
+
if (isNotify) kind = "prose";
|
|
627
656
|
if (p.content.length > 500) truncated = true;
|
|
628
657
|
}
|
|
629
658
|
} catch {
|
|
630
659
|
truncated = true;
|
|
631
660
|
const cmdMatch = inputPreview.match(/"command"\s*:\s*"((?:[^"\\]|\\.)*)"/);
|
|
632
661
|
const contentMatch = inputPreview.match(/"content"\s*:\s*"((?:[^"\\]|\\.)*)"/);
|
|
633
|
-
if (cmdMatch)
|
|
634
|
-
|
|
662
|
+
if (cmdMatch) {
|
|
663
|
+
preview = `$ ${cmdMatch[1]}`;
|
|
664
|
+
lang = "bash";
|
|
665
|
+
} else if (contentMatch) preview = contentMatch[1].slice(0, 3e3);
|
|
635
666
|
}
|
|
636
667
|
const truncNote = truncated ? "\n\n<i>...truncated</i>" : "";
|
|
637
668
|
const previewSlice = preview.slice(0, 3e3);
|
|
638
669
|
const isShort = !previewSlice.includes("\n") && previewSlice.length < 100;
|
|
639
|
-
const
|
|
670
|
+
const langAttr = lang ? ` class="language-${lang}"` : "";
|
|
671
|
+
const previewHtml = kind === "prose" ? this.mdToHtml(previewSlice) : isShort ? `<code>${this.escHtml(previewSlice)}</code>` : `<pre><code${langAttr}>${this.escHtml(previewSlice)}</code></pre>`;
|
|
640
672
|
let displayTool = toolName;
|
|
641
673
|
if ((toolName.endsWith("__notify") || toolName === "notify") && inputPreview) {
|
|
642
674
|
try {
|
|
@@ -736,6 +768,40 @@ ${label}`);
|
|
|
736
768
|
escHtml(s) {
|
|
737
769
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
738
770
|
}
|
|
771
|
+
static LANG_BY_EXT = {
|
|
772
|
+
js: "javascript",
|
|
773
|
+
mjs: "javascript",
|
|
774
|
+
cjs: "javascript",
|
|
775
|
+
jsx: "javascript",
|
|
776
|
+
ts: "typescript",
|
|
777
|
+
tsx: "typescript",
|
|
778
|
+
py: "python",
|
|
779
|
+
rb: "ruby",
|
|
780
|
+
go: "go",
|
|
781
|
+
java: "java",
|
|
782
|
+
kt: "kotlin",
|
|
783
|
+
rs: "rust",
|
|
784
|
+
php: "php",
|
|
785
|
+
cs: "csharp",
|
|
786
|
+
sh: "bash",
|
|
787
|
+
bash: "bash",
|
|
788
|
+
zsh: "bash",
|
|
789
|
+
ps1: "powershell",
|
|
790
|
+
json: "json",
|
|
791
|
+
yml: "yaml",
|
|
792
|
+
yaml: "yaml",
|
|
793
|
+
xml: "xml",
|
|
794
|
+
sql: "sql",
|
|
795
|
+
md: "markdown",
|
|
796
|
+
html: "html",
|
|
797
|
+
vue: "html",
|
|
798
|
+
css: "css",
|
|
799
|
+
scss: "scss"
|
|
800
|
+
};
|
|
801
|
+
langOf(path8) {
|
|
802
|
+
const m = /\.([a-z0-9]+)$/i.exec(path8 || "");
|
|
803
|
+
return m ? _TelegramBot.LANG_BY_EXT[m[1].toLowerCase()] ?? "" : "";
|
|
804
|
+
}
|
|
739
805
|
/** Convert markdown to Telegram HTML (escape first, then apply formatting) */
|
|
740
806
|
mdToHtml(s) {
|
|
741
807
|
let text = s.replace(
|
|
@@ -1093,7 +1159,7 @@ var init_server = __esm({
|
|
|
1093
1159
|
this.sessions.register(session);
|
|
1094
1160
|
this.channelSockets.set(session.id, ws);
|
|
1095
1161
|
if (isLocal) this.localChannels.add(session.id);
|
|
1096
|
-
logger.info(`Session registered: ${session.id} (${session.name}, channel: ${session.channelEnabled ?? false})`);
|
|
1162
|
+
logger.info(`Session registered: ${session.id} (${session.name}, channel: ${session.channelEnabled ?? false}, peer: ${session.peerName ?? "-"})`);
|
|
1097
1163
|
this.broadcastToDashboards({
|
|
1098
1164
|
type: isReregister ? "session_updated" : "session_connected",
|
|
1099
1165
|
session
|
|
@@ -1107,6 +1173,15 @@ var init_server = __esm({
|
|
|
1107
1173
|
}
|
|
1108
1174
|
break;
|
|
1109
1175
|
}
|
|
1176
|
+
case "peer_name": {
|
|
1177
|
+
if (this.channelSockets.get(msg.sessionId) !== ws) break;
|
|
1178
|
+
const updated = this.sessions.setPeerName(msg.sessionId, msg.peerName);
|
|
1179
|
+
if (updated) {
|
|
1180
|
+
logger.info(`Peer name for ${msg.sessionId}: ${msg.peerName ?? "-"}`);
|
|
1181
|
+
this.broadcastToDashboards({ type: "session_updated", session: updated });
|
|
1182
|
+
}
|
|
1183
|
+
break;
|
|
1184
|
+
}
|
|
1110
1185
|
case "notify": {
|
|
1111
1186
|
this.sessions.updateActivity(msg.sessionId);
|
|
1112
1187
|
const notifySession = this.sessions.get(msg.sessionId);
|
|
@@ -1481,12 +1556,13 @@ var init_hub_client = __esm({
|
|
|
1481
1556
|
init_logger();
|
|
1482
1557
|
init_constants();
|
|
1483
1558
|
HubClient = class {
|
|
1484
|
-
constructor(sessionId2, sessionName2, hubHost2 = DEFAULT_HUB_HOST, hubPort2 = DEFAULT_HUB_PORT, token) {
|
|
1559
|
+
constructor(sessionId2, sessionName2, hubHost2 = DEFAULT_HUB_HOST, hubPort2 = DEFAULT_HUB_PORT, token, getPeerName = () => void 0) {
|
|
1485
1560
|
this.sessionId = sessionId2;
|
|
1486
1561
|
this.sessionName = sessionName2;
|
|
1487
1562
|
this.hubHost = hubHost2;
|
|
1488
1563
|
this.hubPort = hubPort2;
|
|
1489
1564
|
this.token = token;
|
|
1565
|
+
this.getPeerName = getPeerName;
|
|
1490
1566
|
}
|
|
1491
1567
|
ws = null;
|
|
1492
1568
|
reconnectTimer = null;
|
|
@@ -1511,7 +1587,8 @@ var init_hub_client = __esm({
|
|
|
1511
1587
|
connectedAt: Date.now(),
|
|
1512
1588
|
lastActivity: Date.now(),
|
|
1513
1589
|
cwd: process.cwd(),
|
|
1514
|
-
channelEnabled: true
|
|
1590
|
+
channelEnabled: true,
|
|
1591
|
+
peerName: this.getPeerName()
|
|
1515
1592
|
}
|
|
1516
1593
|
};
|
|
1517
1594
|
this.ws.send(JSON.stringify(registration));
|
|
@@ -1579,6 +1656,53 @@ var init_hub_client = __esm({
|
|
|
1579
1656
|
}
|
|
1580
1657
|
});
|
|
1581
1658
|
|
|
1659
|
+
// src/channel/peer-name.ts
|
|
1660
|
+
import fs5 from "fs";
|
|
1661
|
+
import os2 from "os";
|
|
1662
|
+
import path5 from "path";
|
|
1663
|
+
function findPeerName(records, env) {
|
|
1664
|
+
const valid = records.filter((r) => typeof r === "object" && r !== null);
|
|
1665
|
+
const nameOf = (match) => {
|
|
1666
|
+
const rec = valid.find(match);
|
|
1667
|
+
return isValidPeerName(rec?.name) ? rec.name : void 0;
|
|
1668
|
+
};
|
|
1669
|
+
if (env.messagingSocket) {
|
|
1670
|
+
const bySocket = nameOf((r) => r.messagingSocketPath === env.messagingSocket);
|
|
1671
|
+
if (bySocket) return bySocket;
|
|
1672
|
+
}
|
|
1673
|
+
if (env.sessionId) return nameOf((r) => r.sessionId === env.sessionId);
|
|
1674
|
+
return void 0;
|
|
1675
|
+
}
|
|
1676
|
+
function readPeerName(env = process.env) {
|
|
1677
|
+
const lookup = {
|
|
1678
|
+
messagingSocket: env.CLAUDE_CODE_MESSAGING_SOCKET,
|
|
1679
|
+
sessionId: env.CLAUDE_CODE_SESSION_ID
|
|
1680
|
+
};
|
|
1681
|
+
if (!lookup.messagingSocket && !lookup.sessionId) return void 0;
|
|
1682
|
+
const configDir = env.CLAUDE_CONFIG_DIR || path5.join(os2.homedir(), ".claude");
|
|
1683
|
+
const dir = path5.join(configDir, "sessions");
|
|
1684
|
+
let files;
|
|
1685
|
+
try {
|
|
1686
|
+
files = fs5.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
1687
|
+
} catch {
|
|
1688
|
+
return void 0;
|
|
1689
|
+
}
|
|
1690
|
+
const records = [];
|
|
1691
|
+
for (const f of files) {
|
|
1692
|
+
try {
|
|
1693
|
+
records.push(JSON.parse(fs5.readFileSync(path5.join(dir, f), "utf8")));
|
|
1694
|
+
} catch {
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
return findPeerName(records, lookup);
|
|
1698
|
+
}
|
|
1699
|
+
var init_peer_name2 = __esm({
|
|
1700
|
+
"src/channel/peer-name.ts"() {
|
|
1701
|
+
"use strict";
|
|
1702
|
+
init_peer_name();
|
|
1703
|
+
}
|
|
1704
|
+
});
|
|
1705
|
+
|
|
1582
1706
|
// src/channel/server.ts
|
|
1583
1707
|
var server_exports2 = {};
|
|
1584
1708
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -1589,10 +1713,19 @@ import {
|
|
|
1589
1713
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
1590
1714
|
import { z } from "zod";
|
|
1591
1715
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
1592
|
-
import
|
|
1716
|
+
import path6 from "path";
|
|
1593
1717
|
async function main() {
|
|
1594
1718
|
logger.info(`Starting MCP channel server (session: ${sessionId})`);
|
|
1595
1719
|
hubClient.connect();
|
|
1720
|
+
const refreshPeerName = () => {
|
|
1721
|
+
const next = readPeerName();
|
|
1722
|
+
if (next === peerName) return;
|
|
1723
|
+
peerName = next;
|
|
1724
|
+
logger.info(`Peer name: ${next ?? "(none)"}`);
|
|
1725
|
+
hubClient.send({ type: "peer_name", sessionId, peerName: next });
|
|
1726
|
+
};
|
|
1727
|
+
setTimeout(refreshPeerName, 2e3).unref();
|
|
1728
|
+
setInterval(refreshPeerName, 3e4).unref();
|
|
1596
1729
|
hubClient.onMessage(async (msg) => {
|
|
1597
1730
|
if (msg.type === "message_to_session" && msg.sessionId === sessionId) {
|
|
1598
1731
|
logger.info(`Message from dashboard: ${msg.content}`);
|
|
@@ -1639,7 +1772,7 @@ ${msg.content}` : "";
|
|
|
1639
1772
|
process.exit(0);
|
|
1640
1773
|
});
|
|
1641
1774
|
}
|
|
1642
|
-
var sessionId, sessionName, server, config, hubHost, hubPort, hubToken, hubClient, PermissionRequestSchema;
|
|
1775
|
+
var sessionId, sessionName, peerName, server, config, hubHost, hubPort, hubToken, hubClient, PermissionRequestSchema;
|
|
1643
1776
|
var init_server2 = __esm({
|
|
1644
1777
|
"src/channel/server.ts"() {
|
|
1645
1778
|
"use strict";
|
|
@@ -1647,8 +1780,10 @@ var init_server2 = __esm({
|
|
|
1647
1780
|
init_constants();
|
|
1648
1781
|
init_config();
|
|
1649
1782
|
init_hub_client();
|
|
1783
|
+
init_peer_name2();
|
|
1650
1784
|
sessionId = randomUUID4();
|
|
1651
|
-
sessionName = process.env.CLAUDE_ALARM_SESSION_NAME ??
|
|
1785
|
+
sessionName = process.env.CLAUDE_ALARM_SESSION_NAME ?? path6.basename(process.cwd());
|
|
1786
|
+
peerName = readPeerName();
|
|
1652
1787
|
server = new Server(
|
|
1653
1788
|
{
|
|
1654
1789
|
name: CHANNEL_SERVER_NAME,
|
|
@@ -1671,6 +1806,8 @@ REPLYING:
|
|
|
1671
1806
|
|
|
1672
1807
|
IMAGES: If a channel message contains "[Image: ...] Read the file to view it: <path>", use the Read tool on that path before responding \u2014 otherwise the image is invisible to you.
|
|
1673
1808
|
|
|
1809
|
+
ROUTING: If a dashboard message has lines like [claude-alarm] @X = SendMessage to "Y", do what the message asks and deliver the result with the SendMessage tool to exactly "Y". Never guess other recipients. Then reply to the dashboard with what you sent.
|
|
1810
|
+
|
|
1674
1811
|
STATUS: Call status("working") before starting a long task, status("waiting_input") when blocked on user input, status("idle") when finished responding.
|
|
1675
1812
|
|
|
1676
1813
|
NOTIFICATIONS:
|
|
@@ -1687,7 +1824,8 @@ NOTIFICATIONS:
|
|
|
1687
1824
|
sessionName,
|
|
1688
1825
|
hubHost,
|
|
1689
1826
|
hubPort,
|
|
1690
|
-
hubToken
|
|
1827
|
+
hubToken,
|
|
1828
|
+
() => peerName
|
|
1691
1829
|
);
|
|
1692
1830
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
1693
1831
|
tools: [
|
|
@@ -1824,11 +1962,11 @@ init_constants();
|
|
|
1824
1962
|
init_logger();
|
|
1825
1963
|
init_crash_guard();
|
|
1826
1964
|
import { spawn } from "child_process";
|
|
1827
|
-
import
|
|
1828
|
-
import
|
|
1965
|
+
import fs6 from "fs";
|
|
1966
|
+
import path7 from "path";
|
|
1829
1967
|
import readline from "readline";
|
|
1830
1968
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1831
|
-
var __dirname2 =
|
|
1969
|
+
var __dirname2 = path7.dirname(fileURLToPath2(import.meta.url));
|
|
1832
1970
|
function printUsage() {
|
|
1833
1971
|
console.log(`
|
|
1834
1972
|
claude-alarm - Monitor Claude Code sessions with notifications
|
|
@@ -1849,8 +1987,8 @@ Quick start:
|
|
|
1849
1987
|
}
|
|
1850
1988
|
async function checkForUpdates() {
|
|
1851
1989
|
try {
|
|
1852
|
-
const pkgPath =
|
|
1853
|
-
const pkg = JSON.parse(
|
|
1990
|
+
const pkgPath = path7.join(__dirname2, "..", "package.json");
|
|
1991
|
+
const pkg = JSON.parse(fs6.readFileSync(pkgPath, "utf-8"));
|
|
1854
1992
|
const currentVersion = pkg.version;
|
|
1855
1993
|
const res = await fetch("https://registry.npmjs.org/@delt/claude-alarm/latest", {
|
|
1856
1994
|
signal: AbortSignal.timeout(3e3)
|
|
@@ -1873,25 +2011,25 @@ async function hubStart(daemon) {
|
|
|
1873
2011
|
const port = config2.hub.port ?? DEFAULT_HUB_PORT;
|
|
1874
2012
|
const displayHost = host === "0.0.0.0" ? "127.0.0.1" : host;
|
|
1875
2013
|
checkForUpdates();
|
|
1876
|
-
if (
|
|
1877
|
-
const pid = parseInt(
|
|
2014
|
+
if (fs6.existsSync(PID_FILE)) {
|
|
2015
|
+
const pid = parseInt(fs6.readFileSync(PID_FILE, "utf-8").trim(), 10);
|
|
1878
2016
|
if (isProcessRunning(pid)) {
|
|
1879
2017
|
console.log(`Hub is already running (PID: ${pid}) on http://${displayHost}:${port}`);
|
|
1880
2018
|
return;
|
|
1881
2019
|
}
|
|
1882
|
-
|
|
2020
|
+
fs6.unlinkSync(PID_FILE);
|
|
1883
2021
|
}
|
|
1884
2022
|
if (daemon) {
|
|
1885
2023
|
ensureConfigDir();
|
|
1886
|
-
const logFd =
|
|
1887
|
-
const hubScript =
|
|
2024
|
+
const logFd = fs6.openSync(LOG_FILE, "a");
|
|
2025
|
+
const hubScript = path7.join(__dirname2, "hub", "server.js");
|
|
1888
2026
|
const child = spawn(process.execPath, [hubScript], {
|
|
1889
2027
|
detached: true,
|
|
1890
2028
|
stdio: ["ignore", logFd, logFd],
|
|
1891
2029
|
env: { ...process.env }
|
|
1892
2030
|
});
|
|
1893
2031
|
if (child.pid) {
|
|
1894
|
-
|
|
2032
|
+
fs6.writeFileSync(PID_FILE, String(child.pid), "utf-8");
|
|
1895
2033
|
child.unref();
|
|
1896
2034
|
console.log(`Hub started as daemon (PID: ${child.pid})`);
|
|
1897
2035
|
console.log(`Dashboard: http://${displayHost}:${port}`);
|
|
@@ -1911,11 +2049,11 @@ async function hubStart(daemon) {
|
|
|
1911
2049
|
const hub = new HubServer2(config2);
|
|
1912
2050
|
await hub.start();
|
|
1913
2051
|
ensureConfigDir();
|
|
1914
|
-
|
|
2052
|
+
fs6.writeFileSync(PID_FILE, String(process.pid), "utf-8");
|
|
1915
2053
|
const shutdown = async () => {
|
|
1916
2054
|
console.log("\nShutting down...");
|
|
1917
2055
|
await hub.stop();
|
|
1918
|
-
if (
|
|
2056
|
+
if (fs6.existsSync(PID_FILE)) fs6.unlinkSync(PID_FILE);
|
|
1919
2057
|
process.exit(0);
|
|
1920
2058
|
};
|
|
1921
2059
|
process.on("SIGINT", shutdown);
|
|
@@ -1923,18 +2061,18 @@ async function hubStart(daemon) {
|
|
|
1923
2061
|
}
|
|
1924
2062
|
}
|
|
1925
2063
|
function hubStop() {
|
|
1926
|
-
if (!
|
|
2064
|
+
if (!fs6.existsSync(PID_FILE)) {
|
|
1927
2065
|
console.log("Hub is not running (no PID file found)");
|
|
1928
2066
|
return;
|
|
1929
2067
|
}
|
|
1930
|
-
const pid = parseInt(
|
|
2068
|
+
const pid = parseInt(fs6.readFileSync(PID_FILE, "utf-8").trim(), 10);
|
|
1931
2069
|
try {
|
|
1932
2070
|
process.kill(pid, "SIGTERM");
|
|
1933
2071
|
console.log(`Hub stopped (PID: ${pid})`);
|
|
1934
2072
|
} catch {
|
|
1935
2073
|
console.log("Hub process not found (may have already stopped)");
|
|
1936
2074
|
}
|
|
1937
|
-
|
|
2075
|
+
fs6.unlinkSync(PID_FILE);
|
|
1938
2076
|
}
|
|
1939
2077
|
async function hubStatus() {
|
|
1940
2078
|
const config2 = loadConfig();
|
|
@@ -1942,8 +2080,8 @@ async function hubStatus() {
|
|
|
1942
2080
|
const port = config2.hub.port ?? DEFAULT_HUB_PORT;
|
|
1943
2081
|
const displayHost = host === "0.0.0.0" ? "127.0.0.1" : host;
|
|
1944
2082
|
let pidInfo = "not running";
|
|
1945
|
-
if (
|
|
1946
|
-
const pid = parseInt(
|
|
2083
|
+
if (fs6.existsSync(PID_FILE)) {
|
|
2084
|
+
const pid = parseInt(fs6.readFileSync(PID_FILE, "utf-8").trim(), 10);
|
|
1947
2085
|
if (isProcessRunning(pid)) {
|
|
1948
2086
|
pidInfo = `running (PID: ${pid})`;
|
|
1949
2087
|
} else {
|
|
@@ -2015,7 +2153,7 @@ function ask(question) {
|
|
|
2015
2153
|
}
|
|
2016
2154
|
async function init() {
|
|
2017
2155
|
const dir = process.cwd();
|
|
2018
|
-
const projectName =
|
|
2156
|
+
const projectName = path7.basename(dir);
|
|
2019
2157
|
console.log(`
|
|
2020
2158
|
claude-alarm init for "${projectName}"
|
|
2021
2159
|
`);
|
|
@@ -2035,11 +2173,11 @@ claude-alarm init for "${projectName}"
|
|
|
2035
2173
|
if (port) env.CLAUDE_ALARM_HUB_PORT = port;
|
|
2036
2174
|
if (token) env.CLAUDE_ALARM_HUB_TOKEN = token;
|
|
2037
2175
|
}
|
|
2038
|
-
const mcpPath =
|
|
2176
|
+
const mcpPath = path7.join(dir, ".mcp.json");
|
|
2039
2177
|
let mcpConfig = {};
|
|
2040
|
-
if (
|
|
2178
|
+
if (fs6.existsSync(mcpPath)) {
|
|
2041
2179
|
try {
|
|
2042
|
-
mcpConfig = JSON.parse(
|
|
2180
|
+
mcpConfig = JSON.parse(fs6.readFileSync(mcpPath, "utf-8"));
|
|
2043
2181
|
} catch {
|
|
2044
2182
|
mcpConfig = {};
|
|
2045
2183
|
}
|
|
@@ -2050,7 +2188,7 @@ claude-alarm init for "${projectName}"
|
|
|
2050
2188
|
args: ["-y", "@delt/claude-alarm", "serve"],
|
|
2051
2189
|
env
|
|
2052
2190
|
};
|
|
2053
|
-
|
|
2191
|
+
fs6.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2), "utf-8");
|
|
2054
2192
|
console.log(`
|
|
2055
2193
|
\u2713 Created ${mcpPath}`);
|
|
2056
2194
|
if (remote.toLowerCase() !== "y") {
|