@delt/claude-alarm 1.0.1 → 1.1.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 +6 -4
- package/dist/cli.js +165 -13
- package/dist/cli.js.map +1 -1
- package/dist/codex/main.js +147 -12
- package/dist/codex/main.js.map +1 -1
- package/dist/dashboard/index.html +101 -7
- package/dist/hub/server.js +157 -12
- package/dist/hub/server.js.map +1 -1
- package/dist/index.d.ts +37 -2
- package/dist/index.js +157 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/dashboard/index.html +101 -7
package/package.json
CHANGED
package/src/dashboard/index.html
CHANGED
|
@@ -589,8 +589,23 @@
|
|
|
589
589
|
background: linear-gradient(180deg, rgba(245,158,11,0.08) 0%, transparent 100%);
|
|
590
590
|
padding: 0;
|
|
591
591
|
display: none;
|
|
592
|
+
flex-shrink: 0;
|
|
593
|
+
max-height: 45vh;
|
|
594
|
+
overflow-y: auto;
|
|
595
|
+
scrollbar-width: thin;
|
|
592
596
|
}
|
|
593
597
|
.permission-bar.active { display: block; }
|
|
598
|
+
.perm-count {
|
|
599
|
+
position: sticky;
|
|
600
|
+
top: 0;
|
|
601
|
+
z-index: 1;
|
|
602
|
+
padding: 6px 20px;
|
|
603
|
+
font-size: 12px;
|
|
604
|
+
font-weight: 600;
|
|
605
|
+
color: var(--yellow);
|
|
606
|
+
background: var(--surface);
|
|
607
|
+
border-bottom: 1px solid var(--border);
|
|
608
|
+
}
|
|
594
609
|
.perm-item {
|
|
595
610
|
display: flex;
|
|
596
611
|
align-items: center;
|
|
@@ -731,6 +746,18 @@
|
|
|
731
746
|
margin-left: 2px;
|
|
732
747
|
font-weight: 400;
|
|
733
748
|
}
|
|
749
|
+
.perm-actions.choices {
|
|
750
|
+
flex-direction: column;
|
|
751
|
+
gap: 6px;
|
|
752
|
+
}
|
|
753
|
+
.perm-actions .perm-choice {
|
|
754
|
+
padding: 7px 16px;
|
|
755
|
+
font-size: 13px;
|
|
756
|
+
background: var(--surface);
|
|
757
|
+
color: var(--text);
|
|
758
|
+
border: 1px solid var(--border);
|
|
759
|
+
}
|
|
760
|
+
.perm-actions .perm-choice:hover { border-color: var(--yellow); }
|
|
734
761
|
/* Permission in notification panel */
|
|
735
762
|
.notif-perm-actions {
|
|
736
763
|
display: flex;
|
|
@@ -1405,6 +1432,9 @@
|
|
|
1405
1432
|
timestamp: msg.timestamp,
|
|
1406
1433
|
resolved: false,
|
|
1407
1434
|
behavior: null,
|
|
1435
|
+
choices: msg.choices || null,
|
|
1436
|
+
sent: null,
|
|
1437
|
+
outcome: null,
|
|
1408
1438
|
});
|
|
1409
1439
|
// Auto-select session if none selected
|
|
1410
1440
|
if (!state.selectedSession && state.sessions[msg.sessionId]) selectSession(msg.sessionId);
|
|
@@ -1426,6 +1456,23 @@
|
|
|
1426
1456
|
renderNotifications();
|
|
1427
1457
|
}
|
|
1428
1458
|
break;
|
|
1459
|
+
case 'permission_pending':
|
|
1460
|
+
for (const m of applyPendingChoices(state.permissionRequests, msg.requests || [])) {
|
|
1461
|
+
handleMessage({ type: 'permission_request', ...m });
|
|
1462
|
+
}
|
|
1463
|
+
renderPermissionBar();
|
|
1464
|
+
renderNotifications();
|
|
1465
|
+
break;
|
|
1466
|
+
case 'permission_resolved': {
|
|
1467
|
+
const req = (state.permissionRequests[msg.sessionId] || []).find(r => r.requestId === msg.requestId);
|
|
1468
|
+
if (req && !req.resolved) {
|
|
1469
|
+
req.resolved = true;
|
|
1470
|
+
req.outcome = msg.state;
|
|
1471
|
+
renderPermissionBar();
|
|
1472
|
+
renderNotifications();
|
|
1473
|
+
}
|
|
1474
|
+
break;
|
|
1475
|
+
}
|
|
1429
1476
|
}
|
|
1430
1477
|
}
|
|
1431
1478
|
|
|
@@ -1664,7 +1711,10 @@
|
|
|
1664
1711
|
if (n.permRequestId) {
|
|
1665
1712
|
const reqs = state.permissionRequests[n.sessionId] || [];
|
|
1666
1713
|
const req = reqs.find(r => r.requestId === n.permRequestId);
|
|
1667
|
-
if (req &&
|
|
1714
|
+
if (req && req.choices) {
|
|
1715
|
+
if (req.resolved) permButtons = `<div class="perm-resolved-label">${req.outcome === 'expired' ? 'Expired' : 'Resolved'}</div>`;
|
|
1716
|
+
else if (req.sent != null) permButtons = '<div class="perm-resolved-label">Sent</div>';
|
|
1717
|
+
} else if (req && !req.resolved) {
|
|
1668
1718
|
permButtons = `<div class="notif-perm-actions">
|
|
1669
1719
|
<button class="notif-perm-allow" data-req-id="${esc(n.permRequestId)}" data-session-id="${esc(n.sessionId)}">Allow</button>
|
|
1670
1720
|
<button class="notif-perm-deny" data-req-id="${esc(n.permRequestId)}" data-session-id="${esc(n.sessionId)}">Deny</button>
|
|
@@ -1951,6 +2001,7 @@
|
|
|
1951
2001
|
let kind = 'code';
|
|
1952
2002
|
let lang = '';
|
|
1953
2003
|
switch (toolName) {
|
|
2004
|
+
case 'Command':
|
|
1954
2005
|
case 'Bash':
|
|
1955
2006
|
text = p.command ? '$ ' + p.command : raw; lang = 'bash'; break;
|
|
1956
2007
|
case 'Write':
|
|
@@ -2001,6 +2052,47 @@
|
|
|
2001
2052
|
renderNotifications();
|
|
2002
2053
|
}
|
|
2003
2054
|
|
|
2055
|
+
// After a reconnect the hub lists the choice requests still waiting; any other unresolved one died with the old connection.
|
|
2056
|
+
function applyPendingChoices(permissionRequests, pending) {
|
|
2057
|
+
const missing = pending.filter(p => !(permissionRequests[p.sessionId] || []).some(r => r.requestId === p.requestId));
|
|
2058
|
+
const live = new Set(pending.map(p => p.sessionId + '\n' + p.requestId));
|
|
2059
|
+
for (const [sid, reqs] of Object.entries(permissionRequests)) {
|
|
2060
|
+
for (const r of reqs) {
|
|
2061
|
+
if (!r.choices || r.resolved) continue;
|
|
2062
|
+
if (!live.has(sid + '\n' + r.requestId)) { r.resolved = true; r.outcome = 'expired'; }
|
|
2063
|
+
else r.sent = null;
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
return missing;
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
function sendPermissionChoice(sessionId, requestId, choiceId) {
|
|
2070
|
+
if (!state.ws || state.ws.readyState !== WebSocket.OPEN) return;
|
|
2071
|
+
state.ws.send(JSON.stringify({ type: 'permission_response', sessionId, requestId, choiceId }));
|
|
2072
|
+
const req = (state.permissionRequests[sessionId] || []).find(r => r.requestId === requestId);
|
|
2073
|
+
if (req) req.sent = choiceId;
|
|
2074
|
+
renderPermissionBar();
|
|
2075
|
+
renderNotifications();
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
function permissionActions(r, sid) {
|
|
2079
|
+
const ids = `data-req-id="${esc(r.requestId)}" data-session-id="${esc(sid)}"`;
|
|
2080
|
+
if (!r.choices) {
|
|
2081
|
+
return `<button class="perm-allow" ${ids}>Allow <span class="perm-kbd">(Enter)</span></button>
|
|
2082
|
+
<button class="perm-deny" ${ids}>Deny <span class="perm-kbd">(Esc)</span></button>`;
|
|
2083
|
+
}
|
|
2084
|
+
if (r.sent != null) {
|
|
2085
|
+
const chosen = r.choices.find(c => c.id === r.sent);
|
|
2086
|
+
return `<div class="perm-resolved-label">Sent: ${esc(chosen ? chosen.label : r.sent)}</div>`;
|
|
2087
|
+
}
|
|
2088
|
+
return r.choices.map(c => `<button class="perm-choice" ${ids} data-choice-id="${esc(c.id)}">${esc(c.label)}</button>`).join('');
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
// Codex choices have no safe default, so Enter/Esc stay with the message box for them.
|
|
2092
|
+
function shortcutRequest(reqs) {
|
|
2093
|
+
return reqs.find(r => !r.resolved && !r.choices);
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2004
2096
|
function renderPermissionBar() {
|
|
2005
2097
|
const bar = $('#permissionBar');
|
|
2006
2098
|
const sid = state.selectedSession;
|
|
@@ -2008,7 +2100,8 @@
|
|
|
2008
2100
|
const reqs = (state.permissionRequests[sid] || []).filter(r => !r.resolved);
|
|
2009
2101
|
if (!reqs.length) { bar.classList.remove('active'); bar.innerHTML = ''; return; }
|
|
2010
2102
|
bar.classList.add('active');
|
|
2011
|
-
|
|
2103
|
+
const count = reqs.length > 1 ? `<div class="perm-count">${reqs.length} permission requests waiting</div>` : '';
|
|
2104
|
+
bar.innerHTML = count + reqs.map(r => {
|
|
2012
2105
|
const { text: previewText, kind: previewKind, lang: previewLang } = formatPermPreview(r.toolName, r.inputPreview);
|
|
2013
2106
|
const displayName = formatToolName(r.toolName, r.inputPreview);
|
|
2014
2107
|
return `<div class="perm-item">
|
|
@@ -2017,12 +2110,10 @@
|
|
|
2017
2110
|
<div class="perm-header">
|
|
2018
2111
|
<span class="perm-tool">${esc(displayName)}</span>
|
|
2019
2112
|
</div>
|
|
2113
|
+
${r.choices && r.description ? `<div class="perm-desc">${esc(r.description)}</div>` : ''}
|
|
2020
2114
|
${previewText ? renderPermPreview(previewText, previewKind, previewLang) : ''}
|
|
2021
2115
|
</div>
|
|
2022
|
-
<div class="perm-actions">
|
|
2023
|
-
<button class="perm-allow" data-req-id="${esc(r.requestId)}" data-session-id="${esc(sid)}">Allow <span class="perm-kbd">(Enter)</span></button>
|
|
2024
|
-
<button class="perm-deny" data-req-id="${esc(r.requestId)}" data-session-id="${esc(sid)}">Deny <span class="perm-kbd">(Esc)</span></button>
|
|
2025
|
-
</div>
|
|
2116
|
+
<div class="perm-actions${r.choices ? ' choices' : ''}">${permissionActions(r, sid)}</div>
|
|
2026
2117
|
</div>`;
|
|
2027
2118
|
}).join('');
|
|
2028
2119
|
bindPermissionButtons(bar);
|
|
@@ -2035,6 +2126,9 @@
|
|
|
2035
2126
|
(container || document).querySelectorAll('.perm-deny, .notif-perm-deny').forEach(btn => {
|
|
2036
2127
|
btn.addEventListener('click', (e) => { e.stopPropagation(); sendPermissionVerdict(btn.dataset.sessionId, btn.dataset.reqId, 'deny'); });
|
|
2037
2128
|
});
|
|
2129
|
+
(container || document).querySelectorAll('.perm-choice').forEach(btn => {
|
|
2130
|
+
btn.addEventListener('click', (e) => { e.stopPropagation(); sendPermissionChoice(btn.dataset.sessionId, btn.dataset.reqId, btn.dataset.choiceId); });
|
|
2131
|
+
});
|
|
2038
2132
|
}
|
|
2039
2133
|
|
|
2040
2134
|
// Keyboard shortcuts: Enter=allow, Esc=deny — works even in textarea when permission is pending
|
|
@@ -2044,7 +2138,7 @@
|
|
|
2044
2138
|
const sid = state.selectedSession;
|
|
2045
2139
|
if (!sid) return;
|
|
2046
2140
|
const reqs = state.permissionRequests[sid] || [];
|
|
2047
|
-
const pending = reqs
|
|
2141
|
+
const pending = shortcutRequest(reqs);
|
|
2048
2142
|
if (!pending) return;
|
|
2049
2143
|
// Permission pending: intercept Enter/Esc everywhere (even in textarea)
|
|
2050
2144
|
// Shift+Enter in textarea still inserts newline
|