@delt/claude-alarm 1.2.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/README.md +2 -1
- package/dist/cli.js +574 -206
- package/dist/cli.js.map +1 -1
- package/dist/codex/main.js +459 -93
- package/dist/codex/main.js.map +1 -1
- package/dist/dashboard/index.html +86 -41
- package/dist/hub/server.js +189 -69
- package/dist/hub/server.js.map +1 -1
- package/dist/index.d.ts +19 -3
- package/dist/index.js +189 -69
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/dashboard/index.html +86 -41
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
|
@@ -1285,6 +1285,24 @@
|
|
|
1285
1285
|
el.classList.toggle('show', !!msg);
|
|
1286
1286
|
}
|
|
1287
1287
|
|
|
1288
|
+
function showUploadRejected(msg) {
|
|
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) {
|
|
1299
|
+
renderMessages();
|
|
1300
|
+
showMentionError(`${label}: ${reason}`);
|
|
1301
|
+
}
|
|
1302
|
+
state.notifications.unshift({ sessionId, title: label, message: reason, level: 'warning', time: Date.now() });
|
|
1303
|
+
renderNotifications();
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1288
1306
|
let mentionState = { open: false, items: [], index: 0, start: -1 };
|
|
1289
1307
|
|
|
1290
1308
|
function currentMentionQuery(input) {
|
|
@@ -1448,32 +1466,18 @@
|
|
|
1448
1466
|
state.notifications.unshift({ sessionId: msg.sessionId, title: 'Reply', message: msg.content.slice(0, 100), level: 'info', time: msg.timestamp });
|
|
1449
1467
|
renderNotifications();
|
|
1450
1468
|
break;
|
|
1469
|
+
case 'upload_rejected':
|
|
1470
|
+
showUploadRejected(msg);
|
|
1471
|
+
break;
|
|
1472
|
+
case 'message_rejected':
|
|
1473
|
+
showMessageRejected(msg);
|
|
1474
|
+
break;
|
|
1451
1475
|
case 'notification':
|
|
1452
1476
|
state.notifications.unshift({ sessionId: msg.sessionId, title: msg.title, message: msg.message, level: msg.level || 'info', time: msg.timestamp });
|
|
1453
1477
|
renderNotifications();
|
|
1454
1478
|
break;
|
|
1455
1479
|
case 'permission_request':
|
|
1456
|
-
|
|
1457
|
-
state.permissionRequests[msg.sessionId].unshift({
|
|
1458
|
-
requestId: msg.requestId,
|
|
1459
|
-
toolName: msg.toolName,
|
|
1460
|
-
description: msg.description,
|
|
1461
|
-
inputPreview: msg.inputPreview,
|
|
1462
|
-
timestamp: msg.timestamp,
|
|
1463
|
-
resolved: false,
|
|
1464
|
-
behavior: null,
|
|
1465
|
-
choices: msg.choices || null,
|
|
1466
|
-
sent: null,
|
|
1467
|
-
outcome: null,
|
|
1468
|
-
});
|
|
1469
|
-
// Auto-select session if none selected
|
|
1470
|
-
if (!state.selectedSession && state.sessions[msg.sessionId]) selectSession(msg.sessionId);
|
|
1471
|
-
if (state.selectedSession === msg.sessionId) { renderMessages(); renderPermissionBar(); }
|
|
1472
|
-
// Also show in notifications with permission info
|
|
1473
|
-
state.notifications.unshift({ sessionId: msg.sessionId, title: 'Permission Request', message: `${msg.toolName}: ${msg.description}`, level: 'warning', time: msg.timestamp, permRequestId: msg.requestId });
|
|
1474
|
-
renderNotifications();
|
|
1475
|
-
// Play notification sound effect via title flash
|
|
1476
|
-
flashTitle('Permission Request');
|
|
1480
|
+
showPermissionRequest(msg);
|
|
1477
1481
|
break;
|
|
1478
1482
|
case 'permission_response':
|
|
1479
1483
|
// Another source (Telegram, other dashboard) resolved a permission request
|
|
@@ -1487,11 +1491,7 @@
|
|
|
1487
1491
|
}
|
|
1488
1492
|
break;
|
|
1489
1493
|
case 'permission_pending':
|
|
1490
|
-
|
|
1491
|
-
handleMessage({ type: 'permission_request', ...m });
|
|
1492
|
-
}
|
|
1493
|
-
renderPermissionBar();
|
|
1494
|
-
renderNotifications();
|
|
1494
|
+
restorePendingRequests(msg.requests || []);
|
|
1495
1495
|
break;
|
|
1496
1496
|
case 'codex_adapters': {
|
|
1497
1497
|
state.codexAdapters = msg.adapters || [];
|
|
@@ -2114,6 +2114,46 @@
|
|
|
2114
2114
|
return missing;
|
|
2115
2115
|
}
|
|
2116
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
|
+
|
|
2117
2157
|
function sendPermissionChoice(sessionId, requestId, choiceId) {
|
|
2118
2158
|
if (!state.ws || state.ws.readyState !== WebSocket.OPEN) return;
|
|
2119
2159
|
state.ws.send(JSON.stringify({ type: 'permission_response', sessionId, requestId, choiceId }));
|
|
@@ -2197,23 +2237,28 @@
|
|
|
2197
2237
|
}, true);
|
|
2198
2238
|
|
|
2199
2239
|
// Flash title for attention
|
|
2200
|
-
let
|
|
2240
|
+
let flash = null;
|
|
2201
2241
|
function flashTitle(msg) {
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
document.title = original;
|
|
2213
|
-
|
|
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;
|
|
2214
2254
|
};
|
|
2215
|
-
|
|
2216
|
-
|
|
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;
|
|
2217
2262
|
}
|
|
2218
2263
|
|
|
2219
2264
|
// Clear all notifications
|