@rel-time/mcp-server 1.2.12 → 1.2.14

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.
Files changed (3) hide show
  1. package/index.js +335 -27
  2. package/package.json +1 -1
  3. package/server.json +2 -2
package/index.js CHANGED
@@ -56,7 +56,7 @@ let LAST_INVOICE_PACK = null;
56
56
 
57
57
  const SERVER_INFO = {
58
58
  name: "rel-time-mcp-server",
59
- version: "1.2.12",
59
+ version: "1.2.14",
60
60
  };
61
61
 
62
62
  /**
@@ -341,6 +341,30 @@ function injectPngVerifyBuffer(pngBuffer, verifyUrl) {
341
341
  ]);
342
342
  }
343
343
 
344
+ /**
345
+ * Builds an OS-safe filename (<= 240 UTF-8 bytes) so macOS/Linux 255-byte limits
346
+ * (`ENAMETOOLONG`) never fail on long AI-prompt filenames.
347
+ */
348
+ function safeArtifactFilename(prefix, rawName, ext, hashHex = "") {
349
+ const candidate = `${prefix}${rawName}${ext}`;
350
+ if (Buffer.byteLength(candidate, "utf8") <= 240) {
351
+ return candidate;
352
+ }
353
+ const suffixTag = hashHex
354
+ ? `~${String(hashHex).slice(0, 8)}`
355
+ : `~${crypto.createHash("sha256").update(String(rawName), "utf8").digest("hex").slice(0, 8)}`;
356
+ const maxBodyBytes = Math.max(32, 240 - Buffer.byteLength(prefix, "utf8") - Buffer.byteLength(suffixTag, "utf8") - Buffer.byteLength(ext, "utf8"));
357
+ let trimmed = "";
358
+ let usedBytes = 0;
359
+ for (const ch of String(rawName)) {
360
+ const chBytes = Buffer.byteLength(ch, "utf8");
361
+ if (usedBytes + chBytes > maxBodyBytes) break;
362
+ trimmed += ch;
363
+ usedBytes += chBytes;
364
+ }
365
+ return `${prefix}${trimmed.trim()}${suffixTag}${ext}`;
366
+ }
367
+
344
368
  /**
345
369
  * Stamps the Option 1A (+14° Bitcoin Tilt, pure crisp 64-ray Sun-Stamp, 0% glare, no pill) onto a local Image or PDF.
346
370
  * Returns { stamped: true, output_path, format } or { stamped: false, reason }.
@@ -375,7 +399,9 @@ async function stampLocalFileOptionA(filePath, svgContent, customOutputPath = nu
375
399
  .replace(/<\/a>/gi, "");
376
400
 
377
401
  if (isImage) {
378
- const outPath = customOutputPath ? path.resolve(customOutputPath) : path.join(dir, `reltime-stamped-${baseName}.png`);
402
+ const outPath = customOutputPath
403
+ ? path.resolve(customOutputPath)
404
+ : path.join(dir, safeArtifactFilename("reltime-stamped-", baseName, ".png", verifyHash));
379
405
  const img = sharp(filePath);
380
406
  const meta = await img.metadata();
381
407
  const w = (meta.autoOrient && meta.autoOrient.width) || meta.width || 1200;
@@ -410,7 +436,9 @@ async function stampLocalFileOptionA(filePath, svgContent, customOutputPath = nu
410
436
  }
411
437
 
412
438
  if (isPdf) {
413
- const outPath = customOutputPath ? path.resolve(customOutputPath) : path.join(dir, `reltime-stamped-${baseName}.pdf`);
439
+ const outPath = customOutputPath
440
+ ? path.resolve(customOutputPath)
441
+ : path.join(dir, safeArtifactFilename("reltime-stamped-", baseName, ".pdf", verifyHash));
414
442
  const pdfBytes = fs.readFileSync(filePath);
415
443
  const pdfDoc = await PDFDocument.load(pdfBytes, { ignoreEncryption: true });
416
444
  const pages = pdfDoc.getPages();
@@ -639,6 +667,15 @@ function generateAuditReportHtml(audit) {
639
667
  }</span>
640
668
  </div>
641
669
  </div>
670
+ <div class="filter-bar no-print">
671
+ <div class="tab-group" id="localTabGroup">
672
+ <button class="filter-tab local-filter-tab active" onclick="setLocalPill(this, 'all')">All (${localCheck.summary.total_files_scanned})</button>
673
+ <button class="filter-tab local-filter-tab" onclick="setLocalPill(this, 'local-settled')">Settled (${localCheck.summary.verified_settled_count})</button>
674
+ <button class="filter-tab local-filter-tab" onclick="setLocalPill(this, 'local-pending')">Pending (${localCheck.summary.verified_pending_count})</button>
675
+ <button class="filter-tab local-filter-tab" onclick="setLocalPill(this, 'local-alert')">Unanchored / Modified (${localCheck.summary.unanchored_or_modified_count})</button>
676
+ </div>
677
+ <input id="localSearchInput" class="search-input" type="text" placeholder="QUERY FILE NAME / SHA-256 / PASSKEY / BLOCK..." oninput="resetAndFilterLocal()" />
678
+ </div>
642
679
  <div class="table-wrap">
643
680
  <table>
644
681
  <thead>
@@ -653,9 +690,16 @@ function generateAuditReportHtml(audit) {
653
690
  <th style="text-align:right">Receipt</th>
654
691
  </tr>
655
692
  </thead>
656
- <tbody>
693
+ <tbody id="localVerifyBody">
657
694
  ${localCheck.files
658
695
  .map((f, idx) => {
696
+ const localRowClass =
697
+ f.integrity_status === "VERIFIED_SETTLED"
698
+ ? "local-settled"
699
+ : f.integrity_status === "VERIFIED_PENDING"
700
+ ? "local-pending"
701
+ : "local-alert";
702
+
659
703
  const statusTag =
660
704
  f.integrity_status === "VERIFIED_SETTLED"
661
705
  ? `<span class="status-cell"><span class="dot dot-settled"></span><span class="mono" style="color:var(--text)">VERIFIED · SETTLED</span></span>`
@@ -682,7 +726,9 @@ function generateAuditReportHtml(audit) {
682
726
  : `<span class="mono muted">—</span>`;
683
727
 
684
728
  return `
685
- <tr>
729
+ <tr class="local-row ${localRowClass}" data-search="${escapeHtml(
730
+ `${f.file_name} ${f.local_sha256} ${f.signer_public_key || ""} ${f.block_height || ""} ${f.anchor_mode || ""}`.toLowerCase()
731
+ )}">
686
732
  <td class="mono muted">${String(idx + 1).padStart(2, "0")}</td>
687
733
  <td>
688
734
  <span style="font-weight:600;color:var(--text)">${escapeHtml(f.file_name)}</span>
@@ -702,6 +748,26 @@ function generateAuditReportHtml(audit) {
702
748
  </tbody>
703
749
  </table>
704
750
  </div>
751
+ <div class="pagination-bar no-print" id="localPaginationBar">
752
+ <div class="pagination-info mono">
753
+ <span id="localRangeLabel">SHOWING 1–25</span>
754
+ <span class="muted">·</span>
755
+ <label class="mono muted" for="localPageSizeSelect">ROWS:</label>
756
+ <select id="localPageSizeSelect" class="page-size-select mono" onchange="changeLocalPageSize(this.value)">
757
+ <option value="25" selected>25 / PAGE</option>
758
+ <option value="50">50 / PAGE</option>
759
+ <option value="100">100 / PAGE</option>
760
+ <option value="0">ALL (FULL LIST)</option>
761
+ </select>
762
+ </div>
763
+ <div class="pagination-controls">
764
+ <button type="button" class="page-btn mono" id="localFirstBtn" onclick="goLocalPage(1)">[ ⟪ FIRST ]</button>
765
+ <button type="button" class="page-btn mono" id="localPrevBtn" onclick="stepLocalPage(-1)">[ ‹ PREV ]</button>
766
+ <span class="page-indicator mono" id="localPageIndicator">PAGE 1 OF 1</span>
767
+ <button type="button" class="page-btn mono" id="localNextBtn" onclick="stepLocalPage(1)">[ NEXT › ]</button>
768
+ <button type="button" class="page-btn mono" id="localLastBtn" onclick="goLocalLastPage()">[ LAST ⟫ ]</button>
769
+ </div>
770
+ </div>
705
771
  </section>`
706
772
  : "";
707
773
 
@@ -1294,9 +1360,68 @@ function generateAuditReportHtml(audit) {
1294
1360
  font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
1295
1361
  font-size: 11.5px;
1296
1362
  }
1363
+ .pagination-bar {
1364
+ display: flex;
1365
+ justify-content: space-between;
1366
+ align-items: center;
1367
+ flex-wrap: wrap;
1368
+ gap: 12px;
1369
+ margin-top: 14px;
1370
+ padding-top: 12px;
1371
+ border-top: 1px solid var(--border);
1372
+ }
1373
+ .pagination-info {
1374
+ display: flex;
1375
+ align-items: center;
1376
+ gap: 8px;
1377
+ font-size: 11px;
1378
+ color: var(--text-secondary);
1379
+ }
1380
+ .page-size-select {
1381
+ background: var(--bg);
1382
+ color: var(--text);
1383
+ border: 1px solid var(--border-strong);
1384
+ padding: 4px 8px;
1385
+ font-size: 10.5px;
1386
+ cursor: pointer;
1387
+ }
1388
+ .pagination-controls {
1389
+ display: flex;
1390
+ align-items: center;
1391
+ gap: 6px;
1392
+ }
1393
+ .page-btn {
1394
+ background: var(--bg);
1395
+ color: var(--text-secondary);
1396
+ border: 1px solid var(--border-strong);
1397
+ padding: 5px 10px;
1398
+ font-size: 10.5px;
1399
+ font-weight: 600;
1400
+ cursor: pointer;
1401
+ transition: all 0.12s ease;
1402
+ }
1403
+ .page-btn:hover:not(:disabled) {
1404
+ border-color: var(--amber);
1405
+ color: var(--amber);
1406
+ }
1407
+ .page-btn:disabled {
1408
+ opacity: 0.35;
1409
+ cursor: not-allowed;
1410
+ }
1411
+ .page-indicator {
1412
+ font-size: 11px;
1413
+ font-weight: 600;
1414
+ color: var(--text);
1415
+ padding: 0 8px;
1416
+ }
1417
+ tr.filter-hidden,
1418
+ tr.page-hidden {
1419
+ display: none;
1420
+ }
1297
1421
  @media print {
1298
1422
  body { background: var(--bg) !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; padding: 10px; }
1299
1423
  .no-print { display: none !important; }
1424
+ tr.page-hidden { display: table-row !important; }
1300
1425
  }
1301
1426
  </style>
1302
1427
  </head>
@@ -1452,15 +1577,15 @@ function generateAuditReportHtml(audit) {
1452
1577
  </div>
1453
1578
  </div>
1454
1579
  <div class="filter-bar no-print">
1455
- <div class="tab-group">
1456
- <button class="filter-tab active" onclick="setPill(this, 'all')">All (${entries.length})</button>
1457
- <button class="filter-tab" onclick="setPill(this, 'status-settled')">Settled (${settledCount})</button>
1458
- <button class="filter-tab" onclick="setPill(this, 'status-pending')">Pending (${pendingCount})</button>
1459
- <button class="filter-tab" onclick="setPill(this, 'mode-biometric')">Passkey (${biometricCount})</button>
1460
- <button class="filter-tab" onclick="setPill(this, 'mode-temporal')">Temporal VDF (${temporalCount})</button>
1461
- <button class="filter-tab" onclick="setPill(this, 'mode-standard')">Standard (${standardCount})</button>
1580
+ <div class="tab-group" id="ledgerTabGroup">
1581
+ <button class="filter-tab ledger-filter-tab active" onclick="setPill(this, 'all')">All (${entries.length})</button>
1582
+ <button class="filter-tab ledger-filter-tab" onclick="setPill(this, 'status-settled')">Settled (${settledCount})</button>
1583
+ <button class="filter-tab ledger-filter-tab" onclick="setPill(this, 'status-pending')">Pending (${pendingCount})</button>
1584
+ <button class="filter-tab ledger-filter-tab" onclick="setPill(this, 'mode-biometric')">Passkey (${biometricCount})</button>
1585
+ <button class="filter-tab ledger-filter-tab" onclick="setPill(this, 'mode-temporal')">Temporal VDF (${temporalCount})</button>
1586
+ <button class="filter-tab ledger-filter-tab" onclick="setPill(this, 'mode-standard')">Standard (${standardCount})</button>
1462
1587
  </div>
1463
- <input id="searchInput" class="search-input" type="text" placeholder="QUERY SHA-256 / T_INDEX / PASSKEY / TXID / BLOCK..." oninput="filterLedger()" />
1588
+ <input id="searchInput" class="search-input" type="text" placeholder="QUERY SHA-256 / T_INDEX / PASSKEY / TXID / BLOCK..." oninput="resetAndFilterLedger()" />
1464
1589
  </div>
1465
1590
  <div class="table-wrap">
1466
1591
  <table>
@@ -1481,11 +1606,40 @@ function generateAuditReportHtml(audit) {
1481
1606
  </tbody>
1482
1607
  </table>
1483
1608
  </div>
1609
+ <div class="pagination-bar no-print" id="ledgerPaginationBar">
1610
+ <div class="pagination-info mono">
1611
+ <span id="ledgerRangeLabel">SHOWING 1–25</span>
1612
+ <span class="muted">·</span>
1613
+ <label class="mono muted" for="ledgerPageSizeSelect">ROWS:</label>
1614
+ <select id="ledgerPageSizeSelect" class="page-size-select mono" onchange="changeLedgerPageSize(this.value)">
1615
+ <option value="25" selected>25 / PAGE</option>
1616
+ <option value="50">50 / PAGE</option>
1617
+ <option value="100">100 / PAGE</option>
1618
+ <option value="0">ALL (FULL LIST)</option>
1619
+ </select>
1620
+ </div>
1621
+ <div class="pagination-controls">
1622
+ <button type="button" class="page-btn mono" id="ledgerFirstBtn" onclick="goLedgerPage(1)">[ ⟪ FIRST ]</button>
1623
+ <button type="button" class="page-btn mono" id="ledgerPrevBtn" onclick="stepLedgerPage(-1)">[ ‹ PREV ]</button>
1624
+ <span class="page-indicator mono" id="ledgerPageIndicator">PAGE 1 OF 1</span>
1625
+ <button type="button" class="page-btn mono" id="ledgerNextBtn" onclick="stepLedgerPage(1)">[ NEXT › ]</button>
1626
+ <button type="button" class="page-btn mono" id="ledgerLastBtn" onclick="goLedgerLastPage()">[ LAST ⟫ ]</button>
1627
+ </div>
1628
+ </div>
1484
1629
  </section>
1485
1630
  </div>
1486
1631
 
1487
1632
  <script>
1488
1633
  let currentFilter = 'all';
1634
+ let ledgerPage = 1;
1635
+ let ledgerPageSize = 25;
1636
+ let ledgerTotalPages = 1;
1637
+
1638
+ let currentLocalFilter = 'all';
1639
+ let localPage = 1;
1640
+ let localPageSize = 25;
1641
+ let localTotalPages = 1;
1642
+
1489
1643
  function applyTheme(theme) {
1490
1644
  const root = document.documentElement;
1491
1645
  const btn = document.getElementById('themeToggleBtn');
@@ -1508,20 +1662,158 @@ function generateAuditReportHtml(audit) {
1508
1662
  if (saved === 'light' || saved === 'dark') applyTheme(saved);
1509
1663
  } catch (e) {}
1510
1664
  })();
1665
+
1666
+ // Section 01: Local Directory Cross-Reference Pagination & Filtering
1667
+ function setLocalPill(btn, filter) {
1668
+ currentLocalFilter = filter;
1669
+ document.querySelectorAll('.local-filter-tab').forEach(el => el.classList.remove('active'));
1670
+ btn.classList.add('active');
1671
+ localPage = 1;
1672
+ filterLocalTable();
1673
+ }
1674
+ function resetAndFilterLocal() {
1675
+ localPage = 1;
1676
+ filterLocalTable();
1677
+ }
1678
+ function changeLocalPageSize(val) {
1679
+ localPageSize = Number(val) || 0;
1680
+ localPage = 1;
1681
+ filterLocalTable();
1682
+ }
1683
+ function goLocalPage(p) {
1684
+ localPage = Math.max(1, Math.min(localTotalPages, p));
1685
+ filterLocalTable();
1686
+ }
1687
+ function stepLocalPage(delta) {
1688
+ goLocalPage(localPage + delta);
1689
+ }
1690
+ function goLocalLastPage() {
1691
+ goLocalPage(localTotalPages);
1692
+ }
1693
+ function filterLocalTable() {
1694
+ const rows = Array.from(document.querySelectorAll('.local-row'));
1695
+ if (!rows.length) return;
1696
+ const qEl = document.getElementById('localSearchInput');
1697
+ const q = (qEl ? qEl.value : '').trim().toLowerCase();
1698
+ const matched = [];
1699
+ rows.forEach(row => {
1700
+ const matchesFilter = currentLocalFilter === 'all' || row.classList.contains(currentLocalFilter);
1701
+ const matchesQuery = !q || (row.getAttribute('data-search') || '').includes(q);
1702
+ if (matchesFilter && matchesQuery) {
1703
+ row.classList.remove('filter-hidden');
1704
+ matched.push(row);
1705
+ } else {
1706
+ row.classList.add('filter-hidden');
1707
+ row.classList.remove('page-hidden');
1708
+ }
1709
+ });
1710
+
1711
+ const total = matched.length;
1712
+ localTotalPages = localPageSize > 0 ? Math.max(1, Math.ceil(total / localPageSize)) : 1;
1713
+ if (localPage > localTotalPages) localPage = localTotalPages;
1714
+ const startIdx = localPageSize > 0 ? (localPage - 1) * localPageSize : 0;
1715
+ const endIdx = localPageSize > 0 ? Math.min(total, startIdx + localPageSize) : total;
1716
+
1717
+ matched.forEach((row, idx) => {
1718
+ if (localPageSize > 0 && (idx < startIdx || idx >= endIdx)) {
1719
+ row.classList.add('page-hidden');
1720
+ } else {
1721
+ row.classList.remove('page-hidden');
1722
+ }
1723
+ });
1724
+
1725
+ const rangeEl = document.getElementById('localRangeLabel');
1726
+ if (rangeEl) {
1727
+ rangeEl.textContent = total === 0 ? 'SHOWING 0 OF 0 FILES' : 'SHOWING ' + (startIdx + 1) + '–' + endIdx + ' OF ' + total.toLocaleString() + ' FILES';
1728
+ }
1729
+ const indEl = document.getElementById('localPageIndicator');
1730
+ if (indEl) indEl.textContent = 'PAGE ' + localPage + ' OF ' + localTotalPages;
1731
+ const firstBtn = document.getElementById('localFirstBtn');
1732
+ const prevBtn = document.getElementById('localPrevBtn');
1733
+ const nextBtn = document.getElementById('localNextBtn');
1734
+ const lastBtn = document.getElementById('localLastBtn');
1735
+ if (firstBtn) firstBtn.disabled = localPage <= 1;
1736
+ if (prevBtn) prevBtn.disabled = localPage <= 1;
1737
+ if (nextBtn) nextBtn.disabled = localPage >= localTotalPages;
1738
+ if (lastBtn) lastBtn.disabled = localPage >= localTotalPages;
1739
+ }
1740
+
1741
+ // Section 03: Chronological Timechain Ledger Pagination & Filtering
1511
1742
  function setPill(btn, filter) {
1512
1743
  currentFilter = filter;
1513
- document.querySelectorAll('.filter-tab').forEach(el => el.classList.remove('active'));
1744
+ document.querySelectorAll('.ledger-filter-tab').forEach(el => el.classList.remove('active'));
1514
1745
  btn.classList.add('active');
1746
+ ledgerPage = 1;
1747
+ filterLedger();
1748
+ }
1749
+ function resetAndFilterLedger() {
1750
+ ledgerPage = 1;
1751
+ filterLedger();
1752
+ }
1753
+ function changeLedgerPageSize(val) {
1754
+ ledgerPageSize = Number(val) || 0;
1755
+ ledgerPage = 1;
1515
1756
  filterLedger();
1516
1757
  }
1758
+ function goLedgerPage(p) {
1759
+ ledgerPage = Math.max(1, Math.min(ledgerTotalPages, p));
1760
+ filterLedger();
1761
+ }
1762
+ function stepLedgerPage(delta) {
1763
+ goLedgerPage(ledgerPage + delta);
1764
+ }
1765
+ function goLedgerLastPage() {
1766
+ goLedgerPage(ledgerTotalPages);
1767
+ }
1517
1768
  function filterLedger() {
1518
- const q = (document.getElementById('searchInput').value || '').trim().toLowerCase();
1519
- document.querySelectorAll('.ledger-row').forEach(row => {
1769
+ const rows = Array.from(document.querySelectorAll('.ledger-row'));
1770
+ const qEl = document.getElementById('searchInput');
1771
+ const q = (qEl ? qEl.value : '').trim().toLowerCase();
1772
+ const matched = [];
1773
+ rows.forEach(row => {
1520
1774
  const matchesFilter = currentFilter === 'all' || row.classList.contains(currentFilter);
1521
1775
  const matchesQuery = !q || (row.getAttribute('data-search') || '').includes(q);
1522
- row.style.display = (matchesFilter && matchesQuery) ? '' : 'none';
1776
+ if (matchesFilter && matchesQuery) {
1777
+ row.classList.remove('filter-hidden');
1778
+ matched.push(row);
1779
+ } else {
1780
+ row.classList.add('filter-hidden');
1781
+ row.classList.remove('page-hidden');
1782
+ }
1523
1783
  });
1524
- }
1784
+
1785
+ const total = matched.length;
1786
+ ledgerTotalPages = ledgerPageSize > 0 ? Math.max(1, Math.ceil(total / ledgerPageSize)) : 1;
1787
+ if (ledgerPage > ledgerTotalPages) ledgerPage = ledgerTotalPages;
1788
+ const startIdx = ledgerPageSize > 0 ? (ledgerPage - 1) * ledgerPageSize : 0;
1789
+ const endIdx = ledgerPageSize > 0 ? Math.min(total, startIdx + ledgerPageSize) : total;
1790
+
1791
+ matched.forEach((row, idx) => {
1792
+ if (ledgerPageSize > 0 && (idx < startIdx || idx >= endIdx)) {
1793
+ row.classList.add('page-hidden');
1794
+ } else {
1795
+ row.classList.remove('page-hidden');
1796
+ }
1797
+ });
1798
+
1799
+ const rangeEl = document.getElementById('ledgerRangeLabel');
1800
+ if (rangeEl) {
1801
+ rangeEl.textContent = total === 0 ? 'SHOWING 0 OF 0 RECORDS' : 'SHOWING ' + (startIdx + 1) + '–' + endIdx + ' OF ' + total.toLocaleString() + ' RECORDS';
1802
+ }
1803
+ const indEl = document.getElementById('ledgerPageIndicator');
1804
+ if (indEl) indEl.textContent = 'PAGE ' + ledgerPage + ' OF ' + ledgerTotalPages;
1805
+ const firstBtn = document.getElementById('ledgerFirstBtn');
1806
+ const prevBtn = document.getElementById('ledgerPrevBtn');
1807
+ const nextBtn = document.getElementById('ledgerNextBtn');
1808
+ const lastBtn = document.getElementById('ledgerLastBtn');
1809
+ if (firstBtn) firstBtn.disabled = ledgerPage <= 1;
1810
+ if (prevBtn) prevBtn.disabled = ledgerPage <= 1;
1811
+ if (nextBtn) nextBtn.disabled = ledgerPage >= ledgerTotalPages;
1812
+ if (lastBtn) lastBtn.disabled = ledgerPage >= ledgerTotalPages;
1813
+ }
1814
+
1815
+ filterLocalTable();
1816
+ filterLedger();
1525
1817
  </script>
1526
1818
  </body>
1527
1819
  </html>`;
@@ -2521,7 +2813,10 @@ async function executeTool(name, args = {}) {
2521
2813
  let savedSvgPath = null;
2522
2814
  if (args.save_sun_stamp_svg !== false && svgContent) {
2523
2815
  try {
2524
- savedSvgPath = path.join(path.dirname(filePath), `reltime-stamp-${path.basename(filePath)}.svg`);
2816
+ savedSvgPath = path.join(
2817
+ path.dirname(filePath),
2818
+ safeArtifactFilename("reltime-stamp-", path.basename(filePath), ".svg", clientHash)
2819
+ );
2525
2820
  fs.writeFileSync(savedSvgPath, svgContent, "utf8");
2526
2821
  } catch (e) {
2527
2822
  savedSvgPath = null;
@@ -2531,7 +2826,7 @@ async function executeTool(name, args = {}) {
2531
2826
  // Save initial Doomsday Proof JSON alongside the file
2532
2827
  const savedProofJsonPath = path.join(
2533
2828
  path.dirname(filePath),
2534
- `reltime-doomsday-proof-${path.basename(filePath)}.json`
2829
+ safeArtifactFilename("reltime-doomsday-proof-", path.basename(filePath), ".json", clientHash)
2535
2830
  );
2536
2831
  fs.writeFileSync(
2537
2832
  savedProofJsonPath,
@@ -2593,7 +2888,7 @@ async function executeTool(name, args = {}) {
2593
2888
  });
2594
2889
  savedCertificateHtmlPath = path.join(
2595
2890
  path.dirname(filePath),
2596
- `reltime-certificate-${path.basename(filePath)}.html`
2891
+ safeArtifactFilename("reltime-certificate-", path.basename(filePath), ".html", clientHash)
2597
2892
  );
2598
2893
  fs.writeFileSync(savedCertificateHtmlPath, certHtml, "utf8");
2599
2894
  if (args.open_certificate_in_browser) {
@@ -2705,12 +3000,24 @@ async function executeTool(name, args = {}) {
2705
3000
  for (const fPath of targetFiles) {
2706
3001
  const relPath = path.relative(folderPath, fPath);
2707
3002
  const baseName = path.basename(fPath);
2708
- const sidecarProof = path.join(path.dirname(fPath), `reltime-doomsday-proof-${baseName}.json`);
2709
- const subfolderProof = path.join(proofsDir, `reltime-doomsday-proof-${relPath.replace(/[\\/]/g, "__")}.json`);
2710
- if (existingManifest.anchored_files[relPath] || fs.existsSync(sidecarProof) || fs.existsSync(subfolderProof)) {
3003
+ const sidecarProof = path.join(
3004
+ path.dirname(fPath),
3005
+ safeArtifactFilename("reltime-doomsday-proof-", baseName, ".json")
3006
+ );
3007
+ const legacySidecarProof = path.join(path.dirname(fPath), `reltime-doomsday-proof-${baseName}.json`);
3008
+ const subfolderProof = path.join(
3009
+ proofsDir,
3010
+ safeArtifactFilename("reltime-doomsday-proof-", relPath.replace(/[\\/]/g, "__"), ".json")
3011
+ );
3012
+ if (
3013
+ existingManifest.anchored_files[relPath] ||
3014
+ fs.existsSync(sidecarProof) ||
3015
+ (Buffer.byteLength(`reltime-doomsday-proof-${baseName}.json`, "utf8") <= 255 && fs.existsSync(legacySidecarProof)) ||
3016
+ fs.existsSync(subfolderProof)
3017
+ ) {
2711
3018
  skipped.push(relPath);
2712
3019
  } else {
2713
- toProcess.push({ fPath, relPath, baseName, subfolderProof });
3020
+ toProcess.push({ fPath, relPath, baseName, subfolderProof, sidecarProof });
2714
3021
  }
2715
3022
  }
2716
3023
 
@@ -2779,8 +3086,7 @@ async function executeTool(name, args = {}) {
2779
3086
  if (artifactMode === "proofs_subfolder") {
2780
3087
  fs.writeFileSync(item.subfolderProof, JSON.stringify(proofRecord, null, 2), "utf8");
2781
3088
  } else if (artifactMode === "full") {
2782
- const sidecarPath = path.join(path.dirname(item.fPath), `reltime-doomsday-proof-${item.baseName}.json`);
2783
- fs.writeFileSync(sidecarPath, JSON.stringify(proofRecord, null, 2), "utf8");
3089
+ fs.writeFileSync(item.sidecarProof, JSON.stringify(proofRecord, null, 2), "utf8");
2784
3090
  }
2785
3091
 
2786
3092
  existingManifest.anchored_files[item.relPath] = proofRecord;
@@ -3232,10 +3538,12 @@ async function executeTool(name, args = {}) {
3232
3538
  if (item.startsWith(".")) continue;
3233
3539
  if (
3234
3540
  item.startsWith("reltime-stamp-") ||
3541
+ item.startsWith("reltime-stamped-") ||
3235
3542
  item.startsWith("reltime-doomsday-proof-") ||
3236
3543
  item.startsWith("reltime-proof-") ||
3237
3544
  item.startsWith("reltime-certificate-") ||
3238
3545
  item.startsWith("reltime-audit-report-") ||
3546
+ item.startsWith("reltime-audit-trail-") ||
3239
3547
  item === "reltime-batch-manifest.json" ||
3240
3548
  item === "_reltime_proofs"
3241
3549
  ) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rel-time/mcp-server",
3
- "version": "1.2.12",
3
+ "version": "1.2.14",
4
4
  "mcpName": "io.github.relsun369-dev/rel-time",
5
5
  "description": "Official Model Context Protocol (MCP) server for Rel-Time (rel-time.com) — Zero-Knowledge Bitcoin & VDF Provenance, Local File Hashing, and Sun-Stamp Generation for Claude, Cursor, Windsurf, and Antigravity.",
6
6
  "main": "index.js",
package/server.json CHANGED
@@ -8,13 +8,13 @@
8
8
  "url": "https://github.com/relsun369-dev/reltime-mcp",
9
9
  "source": "github"
10
10
  },
11
- "version": "1.2.12",
11
+ "version": "1.2.14",
12
12
  "packages": [
13
13
  {
14
14
  "registryType": "npm",
15
15
  "registryBaseUrl": "https://registry.npmjs.org",
16
16
  "identifier": "@rel-time/mcp-server",
17
- "version": "1.2.12",
17
+ "version": "1.2.14",
18
18
  "transport": {
19
19
  "type": "stdio"
20
20
  }