← back to Domain Sniper

inspect.js

63 lines

#!/usr/bin/env node
// inspect.js — pretty-prints the snipe-timing curve from data/honeypot-spotcheck.jsonl.
//
// Handles two record shapes:
//   1) legacy per-row : {ts, phase, domain, status}
//   2) per-batch tally: {spotcheckAt, batchTs, elapsedHours, sampleSize, sampleSlice, tally, items}
//
// Usage:  node inspect.js

const fs = require('fs');
const path = require('path');

const FILE = path.join(__dirname, 'data', 'honeypot-spotcheck.jsonl');
const lines = fs.readFileSync(FILE, 'utf8').split('\n').filter(Boolean);
const summaryOnly = process.argv.includes('--summary') || process.argv.includes('-s');

const rows = []; // {elapsedH, snipes, total, label}

// Bucket legacy per-row entries by phase
const legacy = {};
for (const ln of lines) {
  let r; try { r = JSON.parse(ln); } catch { continue; }
  if (r.phase) {
    const b = (legacy[r.phase] = legacy[r.phase] || { total: 0, snipes: 0, ts: r.ts });
    b.total++;
    if (r.status && /SNIP|TAKEN/i.test(r.status)) b.snipes++;
  } else if (r.elapsedHours != null && r.tally) {
    const snipes = Object.values(r.tally).reduce((a, b) => a + (b.sniped || 0), 0);
    const total = Object.values(r.tally).reduce((a, b) => a + (b.total || 0), 0);
    rows.push({ elapsedH: r.elapsedHours, snipes, total, label: `slice ${r.sampleSlice || '?'}` });
  }
}
for (const [phase, b] of Object.entries(legacy)) {
  rows.push({ elapsedH: phase.match(/(\d+(\.\d+)?)h/) ? +RegExp.$1 : 0, snipes: b.snipes, total: b.total, label: phase });
}
rows.sort((a, b) => a.elapsedH - b.elapsedH);

let cumSniped = 0, cumTotal = 0;
for (const r of rows) { cumSniped += r.snipes; cumTotal += r.total; }
const last = rows[rows.length - 1];

if (summaryOnly) {
  const tag = cumSniped > 0 ? '\x1b[31m' : '\x1b[32m';
  const end = '\x1b[0m';
  console.log(`  ${tag}${cumSniped}/${cumTotal}${end} sniped across ${rows.length} datapoints  |  last ${last ? `${last.elapsedH.toFixed(2)}h ${last.snipes}/${last.total} (${last.label})` : 'n/a'}`);
  process.exit(0);
}

console.log(`\n  honeypot snipe-timing curve  (${rows.length} datapoints, ${lines.length} raw rows)`);
console.log(`  ` + '-'.repeat(78));
console.log(`  elapsed   snipes/total   rate   label                          chart`);
console.log(`  ` + '-'.repeat(78));
for (const r of rows) {
  const rate = r.total ? `${Math.round((100 * r.snipes) / r.total)}%` : 'n/a';
  const bar = '█'.repeat(r.snipes) + '·'.repeat(Math.max(0, r.total - r.snipes));
  const tag = r.snipes > 0 ? '\x1b[31m' : '';
  const end = r.snipes > 0 ? '\x1b[0m' : '';
  console.log(`  ${r.elapsedH.toFixed(2).padStart(5)}h    ${tag}${String(r.snipes).padStart(2)}/${String(r.total).padEnd(3)}${end}        ${rate.padStart(4)}   ${r.label.padEnd(28)} ${bar}`);
}
console.log(`  ` + '-'.repeat(78));
console.log(`  cumulative: ${cumSniped} sniped of ${cumTotal} checks   (${cumTotal ? Math.round((100 * cumSniped) / cumTotal) : 0}%)`);
console.log(`\n  source: ${FILE}\n`);