@delt/claude-alarm 0.8.1 → 0.8.2
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 +53 -7
- package/dist/cli.js.map +1 -1
- package/dist/dashboard/index.html +66 -17
- package/dist/hub/server.js +53 -7
- package/dist/hub/server.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +53 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/dashboard/index.html +66 -17
|
@@ -617,12 +617,35 @@
|
|
|
617
617
|
border-radius: 5px;
|
|
618
618
|
padding: 6px 10px;
|
|
619
619
|
margin-top: 5px;
|
|
620
|
-
max-height:
|
|
621
|
-
overflow
|
|
620
|
+
max-height: 220px;
|
|
621
|
+
overflow: auto;
|
|
622
622
|
white-space: pre-wrap;
|
|
623
623
|
word-break: break-all;
|
|
624
624
|
line-height: 1.4;
|
|
625
625
|
}
|
|
626
|
+
.perm-preview pre {
|
|
627
|
+
margin: 0;
|
|
628
|
+
white-space: pre;
|
|
629
|
+
font-family: inherit;
|
|
630
|
+
font-size: inherit;
|
|
631
|
+
}
|
|
632
|
+
.perm-preview code { font-family: inherit; }
|
|
633
|
+
.perm-preview.prose {
|
|
634
|
+
font-family: inherit;
|
|
635
|
+
white-space: normal;
|
|
636
|
+
word-break: normal;
|
|
637
|
+
}
|
|
638
|
+
.perm-preview.prose pre,
|
|
639
|
+
.perm-preview.prose code { font-family: monospace; }
|
|
640
|
+
.perm-preview.prose ul { margin: 4px 0; padding-left: 20px; }
|
|
641
|
+
.perm-preview.prose li { margin: 2px 0; }
|
|
642
|
+
.perm-preview.prose strong { font-weight: 600; }
|
|
643
|
+
.perm-preview.prose h2,
|
|
644
|
+
.perm-preview.prose h3,
|
|
645
|
+
.perm-preview.prose h4 { font-size: 12px; font-weight: 600; margin: 4px 0; }
|
|
646
|
+
.perm-preview.prose table { border-collapse: collapse; margin: 4px 0; font-size: 11px; }
|
|
647
|
+
.perm-preview.prose th,
|
|
648
|
+
.perm-preview.prose td { border: 1px solid var(--border); padding: 3px 6px; text-align: left; }
|
|
626
649
|
.perm-actions {
|
|
627
650
|
display: flex;
|
|
628
651
|
gap: 10px;
|
|
@@ -1670,9 +1693,26 @@
|
|
|
1670
1693
|
return toolName;
|
|
1671
1694
|
}
|
|
1672
1695
|
|
|
1673
|
-
|
|
1696
|
+
const LANG_BY_EXT = {
|
|
1697
|
+
js: 'javascript', mjs: 'javascript', cjs: 'javascript', jsx: 'javascript',
|
|
1698
|
+
ts: 'typescript', tsx: 'typescript', py: 'python', rb: 'ruby', go: 'go',
|
|
1699
|
+
java: 'java', kt: 'kotlin', rs: 'rust', php: 'php', cs: 'csharp',
|
|
1700
|
+
sh: 'bash', bash: 'bash', zsh: 'bash', ps1: 'powershell',
|
|
1701
|
+
json: 'json', yml: 'yaml', yaml: 'yaml', xml: 'xml', sql: 'sql',
|
|
1702
|
+
md: 'markdown', html: 'html', vue: 'html', css: 'css', scss: 'scss',
|
|
1703
|
+
};
|
|
1704
|
+
function langOf(path) {
|
|
1705
|
+
const m = /\.([a-z0-9]+)$/i.exec(path || '');
|
|
1706
|
+
return m ? (LANG_BY_EXT[m[1].toLowerCase()] || '') : '';
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
// Returns { text, kind, lang } — dashboard shows full content, no truncation.
|
|
1710
|
+
// kind 'code' must stay byte-exact: markdown rendering would swallow the ** and `
|
|
1711
|
+
// characters that the approver needs to see to judge what actually gets executed.
|
|
1712
|
+
// Only notify-style messages (prose Claude wrote) are safe to render as markdown.
|
|
1674
1713
|
function formatPermPreview(toolName, raw) {
|
|
1675
|
-
if (!raw) return { text: '' };
|
|
1714
|
+
if (!raw) return { text: '', kind: 'code', lang: '' };
|
|
1715
|
+
const isNotify = toolName === 'notify' || toolName.endsWith('__notify');
|
|
1676
1716
|
let p = null;
|
|
1677
1717
|
try { p = JSON.parse(raw); } catch {
|
|
1678
1718
|
// Truncated JSON - extract known fields with regex (closing quote optional for truncated strings)
|
|
@@ -1682,43 +1722,52 @@
|
|
|
1682
1722
|
const contentMatch = raw.match(/"content"\s*:\s*"((?:[^"\\]|\\.)*)"?/);
|
|
1683
1723
|
const titleMatch = raw.match(/"title"\s*:\s*"((?:[^"\\]|\\.)*)"?/);
|
|
1684
1724
|
const msgMatch = raw.match(/"message"\s*:\s*"((?:[^"\\]|\\.)*)"?/);
|
|
1685
|
-
if (titleMatch && msgMatch) return { text: msgMatch[1] };
|
|
1686
|
-
if (cmdMatch) return { text: '$ ' + cmdMatch[1] };
|
|
1687
|
-
if (fileMatch) return { text: fileMatch[1] };
|
|
1688
|
-
if (patternMatch) return { text: patternMatch[1] };
|
|
1689
|
-
if (contentMatch) return { text: contentMatch[1] };
|
|
1690
|
-
return { text: raw };
|
|
1725
|
+
if (titleMatch && msgMatch) return { text: msgMatch[1], kind: 'prose', lang: '' };
|
|
1726
|
+
if (cmdMatch) return { text: '$ ' + cmdMatch[1], kind: 'code', lang: 'bash' };
|
|
1727
|
+
if (fileMatch) return { text: fileMatch[1], kind: 'code', lang: langOf(fileMatch[1]) };
|
|
1728
|
+
if (patternMatch) return { text: patternMatch[1], kind: 'code', lang: '' };
|
|
1729
|
+
if (contentMatch) return { text: contentMatch[1], kind: 'code', lang: '' };
|
|
1730
|
+
return { text: raw, kind: 'code', lang: '' };
|
|
1691
1731
|
}
|
|
1692
1732
|
let text;
|
|
1733
|
+
let kind = 'code';
|
|
1734
|
+
let lang = '';
|
|
1693
1735
|
switch (toolName) {
|
|
1694
1736
|
case 'Bash':
|
|
1695
|
-
text = p.command ? '$ ' + p.command : raw; break;
|
|
1737
|
+
text = p.command ? '$ ' + p.command : raw; lang = 'bash'; break;
|
|
1696
1738
|
case 'Write':
|
|
1697
1739
|
case 'Edit':
|
|
1698
1740
|
if (p.file_path) {
|
|
1699
1741
|
text = p.file_path;
|
|
1742
|
+
lang = langOf(p.file_path);
|
|
1700
1743
|
if (p.new_string) text += '\n+ ' + p.new_string;
|
|
1701
1744
|
else if (p.content) text += '\n' + p.content;
|
|
1702
1745
|
} else { text = raw; }
|
|
1703
1746
|
break;
|
|
1704
1747
|
case 'Read':
|
|
1705
|
-
text = p.file_path || raw; break;
|
|
1748
|
+
text = p.file_path || raw; lang = langOf(p.file_path); break;
|
|
1706
1749
|
case 'Glob':
|
|
1707
1750
|
text = p.pattern || raw; break;
|
|
1708
1751
|
case 'Grep':
|
|
1709
1752
|
text = (p.pattern || '') + (p.path ? ' in ' + p.path : ''); break;
|
|
1710
1753
|
default:
|
|
1711
1754
|
if (p.title && p.message) {
|
|
1712
|
-
text = p.message;
|
|
1755
|
+
text = p.message; kind = 'prose';
|
|
1713
1756
|
} else if (p.content && typeof p.content === 'string') {
|
|
1714
|
-
text = p.content;
|
|
1757
|
+
text = p.content; kind = isNotify ? 'prose' : 'code';
|
|
1715
1758
|
} else {
|
|
1716
1759
|
const entries = Object.entries(p);
|
|
1717
1760
|
if (entries.length <= 3) text = entries.map(([k, v]) => k + ': ' + String(v)).join('\n');
|
|
1718
1761
|
else text = JSON.stringify(p, null, 1);
|
|
1719
1762
|
}
|
|
1720
1763
|
}
|
|
1721
|
-
return { text: text || '' };
|
|
1764
|
+
return { text: text || '', kind, lang };
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
function renderPermPreview(text, kind, lang) {
|
|
1768
|
+
if (kind === 'prose') return `<div class="perm-preview prose">${renderMarkdown(text)}</div>`;
|
|
1769
|
+
const cls = lang ? ` class="lang-${esc(lang)}"` : '';
|
|
1770
|
+
return `<div class="perm-preview"><pre><code${cls}>${esc(text)}</code></pre></div>`;
|
|
1722
1771
|
}
|
|
1723
1772
|
|
|
1724
1773
|
function sendPermissionVerdict(sessionId, requestId, behavior) {
|
|
@@ -1742,7 +1791,7 @@
|
|
|
1742
1791
|
if (!reqs.length) { bar.classList.remove('active'); bar.innerHTML = ''; return; }
|
|
1743
1792
|
bar.classList.add('active');
|
|
1744
1793
|
bar.innerHTML = reqs.map(r => {
|
|
1745
|
-
const { text: previewText } = formatPermPreview(r.toolName, r.inputPreview);
|
|
1794
|
+
const { text: previewText, kind: previewKind, lang: previewLang } = formatPermPreview(r.toolName, r.inputPreview);
|
|
1746
1795
|
const displayName = formatToolName(r.toolName, r.inputPreview);
|
|
1747
1796
|
return `<div class="perm-item">
|
|
1748
1797
|
<div class="perm-icon">⚠</div>
|
|
@@ -1750,7 +1799,7 @@
|
|
|
1750
1799
|
<div class="perm-header">
|
|
1751
1800
|
<span class="perm-tool">${esc(displayName)}</span>
|
|
1752
1801
|
</div>
|
|
1753
|
-
${previewText ?
|
|
1802
|
+
${previewText ? renderPermPreview(previewText, previewKind, previewLang) : ''}
|
|
1754
1803
|
</div>
|
|
1755
1804
|
<div class="perm-actions">
|
|
1756
1805
|
<button class="perm-allow" data-req-id="${esc(r.requestId)}" data-session-id="${esc(sid)}">Allow <span class="perm-kbd">(Enter)</span></button>
|
package/dist/hub/server.js
CHANGED
|
@@ -210,7 +210,7 @@ import fs from "fs";
|
|
|
210
210
|
import path2 from "path";
|
|
211
211
|
import { randomUUID } from "crypto";
|
|
212
212
|
var TELEGRAM_API = "https://api.telegram.org/bot";
|
|
213
|
-
var TelegramBot = class {
|
|
213
|
+
var TelegramBot = class _TelegramBot {
|
|
214
214
|
config;
|
|
215
215
|
offset = 0;
|
|
216
216
|
polling = false;
|
|
@@ -442,33 +442,45 @@ ${this.mdToHtml(message)}`;
|
|
|
442
442
|
}
|
|
443
443
|
/** Send a permission request with inline buttons */
|
|
444
444
|
async sendPermissionRequest(sessionId, sessionLabel, requestId, toolName, description, inputPreview) {
|
|
445
|
+
const isNotify = toolName.endsWith("__notify") || toolName === "notify";
|
|
445
446
|
let preview = inputPreview;
|
|
447
|
+
let kind = "code";
|
|
448
|
+
let lang = "";
|
|
446
449
|
let truncated = false;
|
|
447
450
|
try {
|
|
448
451
|
const p = JSON.parse(inputPreview);
|
|
449
|
-
if (p.command)
|
|
450
|
-
|
|
451
|
-
|
|
452
|
+
if (p.command) {
|
|
453
|
+
preview = `$ ${p.command}`;
|
|
454
|
+
lang = "bash";
|
|
455
|
+
} else if (p.title && p.message) {
|
|
456
|
+
preview = p.message;
|
|
457
|
+
kind = "prose";
|
|
458
|
+
} else if (p.file_path) {
|
|
452
459
|
preview = p.file_path;
|
|
460
|
+
lang = this.langOf(p.file_path);
|
|
453
461
|
if (p.content) {
|
|
454
462
|
preview += "\n" + p.content.slice(0, 3e3);
|
|
455
463
|
if (p.content.length > 500) truncated = true;
|
|
456
464
|
}
|
|
457
465
|
} else if (p.content && typeof p.content === "string") {
|
|
458
466
|
preview = p.content.slice(0, 3e3);
|
|
467
|
+
if (isNotify) kind = "prose";
|
|
459
468
|
if (p.content.length > 500) truncated = true;
|
|
460
469
|
}
|
|
461
470
|
} catch {
|
|
462
471
|
truncated = true;
|
|
463
472
|
const cmdMatch = inputPreview.match(/"command"\s*:\s*"((?:[^"\\]|\\.)*)"/);
|
|
464
473
|
const contentMatch = inputPreview.match(/"content"\s*:\s*"((?:[^"\\]|\\.)*)"/);
|
|
465
|
-
if (cmdMatch)
|
|
466
|
-
|
|
474
|
+
if (cmdMatch) {
|
|
475
|
+
preview = `$ ${cmdMatch[1]}`;
|
|
476
|
+
lang = "bash";
|
|
477
|
+
} else if (contentMatch) preview = contentMatch[1].slice(0, 3e3);
|
|
467
478
|
}
|
|
468
479
|
const truncNote = truncated ? "\n\n<i>...truncated</i>" : "";
|
|
469
480
|
const previewSlice = preview.slice(0, 3e3);
|
|
470
481
|
const isShort = !previewSlice.includes("\n") && previewSlice.length < 100;
|
|
471
|
-
const
|
|
482
|
+
const langAttr = lang ? ` class="language-${lang}"` : "";
|
|
483
|
+
const previewHtml = kind === "prose" ? this.mdToHtml(previewSlice) : isShort ? `<code>${this.escHtml(previewSlice)}</code>` : `<pre><code${langAttr}>${this.escHtml(previewSlice)}</code></pre>`;
|
|
472
484
|
let displayTool = toolName;
|
|
473
485
|
if ((toolName.endsWith("__notify") || toolName === "notify") && inputPreview) {
|
|
474
486
|
try {
|
|
@@ -568,6 +580,40 @@ ${label}`);
|
|
|
568
580
|
escHtml(s) {
|
|
569
581
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
570
582
|
}
|
|
583
|
+
static LANG_BY_EXT = {
|
|
584
|
+
js: "javascript",
|
|
585
|
+
mjs: "javascript",
|
|
586
|
+
cjs: "javascript",
|
|
587
|
+
jsx: "javascript",
|
|
588
|
+
ts: "typescript",
|
|
589
|
+
tsx: "typescript",
|
|
590
|
+
py: "python",
|
|
591
|
+
rb: "ruby",
|
|
592
|
+
go: "go",
|
|
593
|
+
java: "java",
|
|
594
|
+
kt: "kotlin",
|
|
595
|
+
rs: "rust",
|
|
596
|
+
php: "php",
|
|
597
|
+
cs: "csharp",
|
|
598
|
+
sh: "bash",
|
|
599
|
+
bash: "bash",
|
|
600
|
+
zsh: "bash",
|
|
601
|
+
ps1: "powershell",
|
|
602
|
+
json: "json",
|
|
603
|
+
yml: "yaml",
|
|
604
|
+
yaml: "yaml",
|
|
605
|
+
xml: "xml",
|
|
606
|
+
sql: "sql",
|
|
607
|
+
md: "markdown",
|
|
608
|
+
html: "html",
|
|
609
|
+
vue: "html",
|
|
610
|
+
css: "css",
|
|
611
|
+
scss: "scss"
|
|
612
|
+
};
|
|
613
|
+
langOf(path5) {
|
|
614
|
+
const m = /\.([a-z0-9]+)$/i.exec(path5 || "");
|
|
615
|
+
return m ? _TelegramBot.LANG_BY_EXT[m[1].toLowerCase()] ?? "" : "";
|
|
616
|
+
}
|
|
571
617
|
/** Convert markdown to Telegram HTML (escape first, then apply formatting) */
|
|
572
618
|
mdToHtml(s) {
|
|
573
619
|
let text = s.replace(
|