@delt/claude-alarm 1.3.0 → 1.4.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/dist/cli.js +178 -49
- package/dist/cli.js.map +1 -1
- package/dist/codex/main.js +161 -32
- package/dist/codex/main.js.map +1 -1
- package/dist/dashboard/index.html +77 -46
- package/dist/hub/server.js +56 -31
- package/dist/hub/server.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.js +56 -31
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/dashboard/index.html +77 -46
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@delt/claude-alarm",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Monitor and get notifications from multiple Claude Code sessions via MCP Channels",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"dev": "tsup --watch",
|
|
29
29
|
"start:hub": "node dist/cli.js hub start",
|
|
30
30
|
"start:channel": "node dist/channel/server.js",
|
|
31
|
-
"test": "node --import tsx --import ./test/isolate-home.ts --test --test-timeout=
|
|
31
|
+
"test": "node --import tsx --import ./test/isolate-home.ts --test --test-timeout=60000 \"test/**/*.test.ts\""
|
|
32
32
|
},
|
|
33
33
|
"keywords": [
|
|
34
34
|
"claude",
|
package/src/dashboard/index.html
CHANGED
|
@@ -1286,13 +1286,20 @@
|
|
|
1286
1286
|
}
|
|
1287
1287
|
|
|
1288
1288
|
function showUploadRejected(msg) {
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1289
|
+
showNotDelivered(msg.sessionId, msg.withText ? 'Image and message not delivered' : 'Image not delivered', msg.reason);
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
function showMessageRejected(msg) {
|
|
1293
|
+
showNotDelivered(msg.sessionId, 'Message not delivered', msg.reason);
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
function showNotDelivered(sessionId, label, reason) {
|
|
1297
|
+
state.waitingReply[sessionId] = false;
|
|
1298
|
+
if (state.selectedSession === sessionId) {
|
|
1292
1299
|
renderMessages();
|
|
1293
|
-
showMentionError(`${label}: ${
|
|
1300
|
+
showMentionError(`${label}: ${reason}`);
|
|
1294
1301
|
}
|
|
1295
|
-
state.notifications.unshift({ sessionId
|
|
1302
|
+
state.notifications.unshift({ sessionId, title: label, message: reason, level: 'warning', time: Date.now() });
|
|
1296
1303
|
renderNotifications();
|
|
1297
1304
|
}
|
|
1298
1305
|
|
|
@@ -1462,32 +1469,15 @@
|
|
|
1462
1469
|
case 'upload_rejected':
|
|
1463
1470
|
showUploadRejected(msg);
|
|
1464
1471
|
break;
|
|
1472
|
+
case 'message_rejected':
|
|
1473
|
+
showMessageRejected(msg);
|
|
1474
|
+
break;
|
|
1465
1475
|
case 'notification':
|
|
1466
1476
|
state.notifications.unshift({ sessionId: msg.sessionId, title: msg.title, message: msg.message, level: msg.level || 'info', time: msg.timestamp });
|
|
1467
1477
|
renderNotifications();
|
|
1468
1478
|
break;
|
|
1469
1479
|
case 'permission_request':
|
|
1470
|
-
|
|
1471
|
-
state.permissionRequests[msg.sessionId].unshift({
|
|
1472
|
-
requestId: msg.requestId,
|
|
1473
|
-
toolName: msg.toolName,
|
|
1474
|
-
description: msg.description,
|
|
1475
|
-
inputPreview: msg.inputPreview,
|
|
1476
|
-
timestamp: msg.timestamp,
|
|
1477
|
-
resolved: false,
|
|
1478
|
-
behavior: null,
|
|
1479
|
-
choices: msg.choices || null,
|
|
1480
|
-
sent: null,
|
|
1481
|
-
outcome: null,
|
|
1482
|
-
});
|
|
1483
|
-
// Auto-select session if none selected
|
|
1484
|
-
if (!state.selectedSession && state.sessions[msg.sessionId]) selectSession(msg.sessionId);
|
|
1485
|
-
if (state.selectedSession === msg.sessionId) { renderMessages(); renderPermissionBar(); }
|
|
1486
|
-
// Also show in notifications with permission info
|
|
1487
|
-
state.notifications.unshift({ sessionId: msg.sessionId, title: 'Permission Request', message: `${msg.toolName}: ${msg.description}`, level: 'warning', time: msg.timestamp, permRequestId: msg.requestId });
|
|
1488
|
-
renderNotifications();
|
|
1489
|
-
// Play notification sound effect via title flash
|
|
1490
|
-
flashTitle('Permission Request');
|
|
1480
|
+
showPermissionRequest(msg);
|
|
1491
1481
|
break;
|
|
1492
1482
|
case 'permission_response':
|
|
1493
1483
|
// Another source (Telegram, other dashboard) resolved a permission request
|
|
@@ -1501,11 +1491,7 @@
|
|
|
1501
1491
|
}
|
|
1502
1492
|
break;
|
|
1503
1493
|
case 'permission_pending':
|
|
1504
|
-
|
|
1505
|
-
handleMessage({ type: 'permission_request', ...m });
|
|
1506
|
-
}
|
|
1507
|
-
renderPermissionBar();
|
|
1508
|
-
renderNotifications();
|
|
1494
|
+
restorePendingRequests(msg.requests || []);
|
|
1509
1495
|
break;
|
|
1510
1496
|
case 'codex_adapters': {
|
|
1511
1497
|
state.codexAdapters = msg.adapters || [];
|
|
@@ -2128,6 +2114,46 @@
|
|
|
2128
2114
|
return missing;
|
|
2129
2115
|
}
|
|
2130
2116
|
|
|
2117
|
+
function addPermissionRequest(msg) {
|
|
2118
|
+
if (!state.permissionRequests[msg.sessionId]) state.permissionRequests[msg.sessionId] = [];
|
|
2119
|
+
state.permissionRequests[msg.sessionId].unshift({
|
|
2120
|
+
requestId: msg.requestId,
|
|
2121
|
+
toolName: msg.toolName,
|
|
2122
|
+
description: msg.description,
|
|
2123
|
+
inputPreview: msg.inputPreview,
|
|
2124
|
+
timestamp: msg.timestamp,
|
|
2125
|
+
resolved: false,
|
|
2126
|
+
behavior: null,
|
|
2127
|
+
choices: msg.choices || null,
|
|
2128
|
+
sent: null,
|
|
2129
|
+
outcome: null,
|
|
2130
|
+
});
|
|
2131
|
+
if (!state.selectedSession && state.sessions[msg.sessionId]) selectSession(msg.sessionId);
|
|
2132
|
+
if (state.selectedSession === msg.sessionId) { renderMessages(); renderPermissionBar(); }
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
function showPermissionRequest(msg) {
|
|
2136
|
+
addPermissionRequest(msg);
|
|
2137
|
+
state.notifications.unshift({ sessionId: msg.sessionId, title: 'Permission Request', message: `${msg.toolName}: ${msg.description}`, level: 'warning', time: msg.timestamp, permRequestId: msg.requestId });
|
|
2138
|
+
renderNotifications();
|
|
2139
|
+
flashTitle('Permission Request');
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
// A reconnect can bring back many requests at once; one row and one flash keep them from burying the notifications.
|
|
2143
|
+
function restorePendingRequests(requests) {
|
|
2144
|
+
const restored = applyPendingChoices(state.permissionRequests, requests);
|
|
2145
|
+
for (const m of restored) addPermissionRequest(m);
|
|
2146
|
+
if (restored.length) {
|
|
2147
|
+
const label = `${restored.length} approval request(s) waiting`;
|
|
2148
|
+
const listed = restored.slice(0, 3).map(m => `${m.toolName}: ${m.description}`).join(' · ');
|
|
2149
|
+
const message = restored.length > 3 ? `${listed} …and ${restored.length - 3} more` : listed;
|
|
2150
|
+
state.notifications.unshift({ sessionId: restored[0].sessionId, title: label, message, level: 'warning', time: Date.now() });
|
|
2151
|
+
flashTitle(label);
|
|
2152
|
+
}
|
|
2153
|
+
renderPermissionBar();
|
|
2154
|
+
renderNotifications();
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2131
2157
|
function sendPermissionChoice(sessionId, requestId, choiceId) {
|
|
2132
2158
|
if (!state.ws || state.ws.readyState !== WebSocket.OPEN) return;
|
|
2133
2159
|
state.ws.send(JSON.stringify({ type: 'permission_response', sessionId, requestId, choiceId }));
|
|
@@ -2211,23 +2237,28 @@
|
|
|
2211
2237
|
}, true);
|
|
2212
2238
|
|
|
2213
2239
|
// Flash title for attention
|
|
2214
|
-
let
|
|
2240
|
+
let flash = null;
|
|
2215
2241
|
function flashTitle(msg) {
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
document.title = original;
|
|
2227
|
-
|
|
2242
|
+
// While flashing, document.title may hold the flash text, so only a flash that starts from rest reads the real title.
|
|
2243
|
+
if (flash) {
|
|
2244
|
+
flash.msg = msg;
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
2247
|
+
const current = { msg, original: document.title, on: true, interval: null, timeout: null };
|
|
2248
|
+
const stop = () => {
|
|
2249
|
+
clearInterval(current.interval);
|
|
2250
|
+
clearTimeout(current.timeout);
|
|
2251
|
+
window.removeEventListener('focus', stop);
|
|
2252
|
+
document.title = current.original;
|
|
2253
|
+
flash = null;
|
|
2228
2254
|
};
|
|
2229
|
-
|
|
2230
|
-
|
|
2255
|
+
current.interval = setInterval(() => {
|
|
2256
|
+
document.title = current.on ? `** ${current.msg} **` : current.original;
|
|
2257
|
+
current.on = !current.on;
|
|
2258
|
+
}, 500);
|
|
2259
|
+
current.timeout = setTimeout(stop, 30000);
|
|
2260
|
+
window.addEventListener('focus', stop);
|
|
2261
|
+
flash = current;
|
|
2231
2262
|
}
|
|
2232
2263
|
|
|
2233
2264
|
// Clear all notifications
|