kryptheon 0.1.25 → 0.1.26

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.
@@ -459,6 +459,7 @@
459
459
  meta.appendChild(sevTag(f.severity));
460
460
  meta.appendChild(statusTag(f));
461
461
  meta.appendChild(el('span', 'tag ' + (CHECK_TONE[f.check] || ''), CHECK_NAME[f.check] || f.check));
462
+ if (f.from === 'nightly' || f.from === 'both') meta.appendChild(el('span', 'tag', f.from === 'both' ? 'also nightly' : 'nightly'));
462
463
  meta.appendChild(el('span', 'f-where', f.where));
463
464
  left.appendChild(meta);
464
465
  head.appendChild(left);
@@ -564,6 +565,9 @@
564
565
  st.appendChild(el('span', 'tag ' + (STATE_TONE[c.state] || ''), c.state));
565
566
  card.appendChild(st);
566
567
  if (c.what) card.appendChild(el('div', 'c-what', c.what));
568
+ // What the nightly run inside the database last said - its own answer,
569
+ // not this terminal's, so it gets its own line.
570
+ if (c.nightly) card.appendChild(el('div', 'c-what', c.nightly));
567
571
  if (c.runnable && c.enabled) {
568
572
  var run = button('sm', 'play', 'Run now', function () { runChecks([c.id], run); });
569
573
  run.setAttribute('data-action', 'run');
@@ -600,6 +604,7 @@
600
604
  ['test-results/', 'screenshots and traces from runs that broke'],
601
605
  ['code-findings.json', 'the last read of your frontend code'],
602
606
  ['night-last.json', 'the last database check'],
607
+ ['night-nightly.json', 'what the nightly run inside your database last said'],
603
608
  ['snapshot.json', 'what your files looked like at the last look'],
604
609
  ['changes.jsonl', 'every look: what changed, what broke, what got fixed'],
605
610
  ['fixes.jsonl', 'every re-check and its verdict'],
@@ -251,6 +251,28 @@ function nextAction(findings, steps) {
251
251
  return { kind: 'quiet', title: 'Nothing open' };
252
252
  }
253
253
 
254
+ /**
255
+ * One sentence about the nightly run inside the database, or null when
256
+ * kryptheon-night has never been asked. Its four states are four different
257
+ * answers, and none of the other three may read as "nothing found": not set
258
+ * up, set up and never run, set up with nothing scheduled, and stopped.
259
+ */
260
+ function nightlyLine(nightly) {
261
+ if (!nightly) return null;
262
+ const day = (iso) => String(iso || '').slice(0, 16).replace('T', ' ');
263
+ const read = ' (as of ' + day(nightly.readAt) + ')';
264
+ if (!nightly.installed) return 'Nightly run in your database: not set up. npx kryptheon-night install sets it up.' + read;
265
+ const idle = nightly.active ? '' : ' Nothing is scheduled, so it is not running - npx kryptheon-night install sets it up again.';
266
+ if (!nightly.ranAt) return 'Nightly run in your database: set up, and it has not run yet.' + idle + read;
267
+ const when = 'Nightly run in your database, ' + day(nightly.ranAt) + ': ';
268
+ if (nightly.stopped) return when + 'it could not check - ' + nightly.stopped + idle + read;
269
+ const n = (nightly.findings || []).length;
270
+ const skipped = (nightly.notChecked || []).length;
271
+ return when + (n ? n + (n === 1 ? ' problem found.' : ' problems found.')
272
+ : 'nothing got through ' + (nightly.attacksRun || 0) + ' attacks.') +
273
+ (skipped ? ' ' + skipped + ' ' + (skipped === 1 ? 'part was' : 'parts were') + ' not tested.' : '') + idle + read;
274
+ }
275
+
254
276
  /**
255
277
  * Everything the page shows, from the store alone. Findings from every check
256
278
  * are put into one list, each carrying the same fields - severity, status,
@@ -264,6 +286,10 @@ function buildState(root, env) {
264
286
  const baselines = readJson(paths.baselines) || {};
265
287
  const code = readJson(paths.codeFindings);
266
288
  const night = readJson(paths.nightLast);
289
+ // What the nightly run inside the database last said, kept by
290
+ // kryptheon-night whenever it connects. Not the same answer as night:
291
+ // that one is the scan last run from this terminal.
292
+ const nightly = readJson(paths.nightNightly);
267
293
 
268
294
  const findings = [];
269
295
 
@@ -331,6 +357,33 @@ function buildState(root, env) {
331
357
  }
332
358
  }
333
359
 
360
+ // The nightly run's findings, said as coming from it. One both answers
361
+ // share is shown once, with the newer date - the same problem found twice
362
+ // is still one problem to fix.
363
+ if (nightly && nightly.ranAt && config.enabled.data) {
364
+ for (const f of nightly.findings || []) {
365
+ const item = {
366
+ check: 'data',
367
+ from: 'nightly',
368
+ severity: f.severity,
369
+ status: f.status || 'confirmed',
370
+ evidence: (f.status || 'confirmed') === 'confirmed' ? 'runtime confirmed' : 'code analysis',
371
+ confidence: (f.status || 'confirmed') === 'confirmed' ? 'high' : 'medium',
372
+ where: f.table + (f.column ? '.' + f.column : ''),
373
+ headline: f.headline,
374
+ detail: f.body || '',
375
+ fixPrompt: f.fixPrompt || '',
376
+ when: nightly.ranAt,
377
+ };
378
+ const same = findings.find((o) => o.check === 'data' && changes.findingKey(o) === changes.findingKey(item));
379
+ if (!same) findings.push(item);
380
+ else {
381
+ same.from = 'both';
382
+ if (!same.when || item.when > same.when) same.when = item.when;
383
+ }
384
+ }
385
+ }
386
+
334
387
  const rank = { 'HIGH CRITICAL': 0, CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3 };
335
388
  findings.sort((a, b) => (rank[a.severity] ?? 9) - (rank[b.severity] ?? 9) ||
336
389
  (a.status === 'confirmed' ? 0 : 1) - (b.status === 'confirmed' ? 0 : 1));
@@ -359,9 +412,14 @@ function buildState(root, env) {
359
412
  else if (!config.enabled[c.id]) state = 'switched off';
360
413
  else if (c.id === 'regression') state = !recordings ? 'no recordings yet' : !lastRun ? 'never run' : lastRun.failed ? 'problems found' : 'last run held';
361
414
  else if (c.id === 'frontend') state = !code ? 'never run' : (code.findings || []).length ? 'things to check' : 'nothing found';
362
- else if (c.id === 'data') state = !night ? 'never run here' : (night.findings || []).length ? 'problems found' : 'last run held';
415
+ else if (c.id === 'data') {
416
+ const ranNightly = nightly && nightly.ranAt && !nightly.stopped;
417
+ state = !night && !ranNightly ? 'never run here'
418
+ : findings.some((f) => f.check === 'data') ? 'problems found' : 'last run held';
419
+ }
363
420
  return {
364
421
  id: c.id, label: c.label, available: c.available, enabled: config.enabled[c.id], state: state, what: c.what || '',
422
+ nightly: c.id === 'data' ? nightlyLine(nightly) : null,
365
423
  // Runnable from the page: what needs nothing the page cannot supply. The
366
424
  // database check needs a connection string, which stays in the terminal.
367
425
  runnable: c.available && (c.id === 'frontend' || (c.id === 'regression' && recordings > 0)),
@@ -372,7 +430,7 @@ function buildState(root, env) {
372
430
  codeAt: code ? code.checkedAt : null,
373
431
  recordings: recordings,
374
432
  lastRunAt: lastRun ? lastRun.runAt : null,
375
- nightAt: night ? nightWhen : null,
433
+ nightAt: [night ? nightWhen : null, nightly && nightly.ranAt].filter(Boolean).sort().pop() || null,
376
434
  });
377
435
 
378
436
  // Every look Kryptheon took - by `verify`, or by the dashboard's own watch -
@@ -45,6 +45,7 @@ const FILES = {
45
45
  history: 'history.jsonl',
46
46
  testResults: 'test-results',
47
47
  nightLast: 'night-last.json',
48
+ nightNightly: 'night-nightly.json',
48
49
  codeFindings: 'code-findings.json',
49
50
  config: 'config.json',
50
51
  snapshot: 'snapshot.json',
@@ -132,6 +133,7 @@ function pathsIn(dir) {
132
133
  history: path.join(dir, FILES.history),
133
134
  testResults: path.join(dir, FILES.testResults),
134
135
  nightLast: path.join(dir, FILES.nightLast),
136
+ nightNightly: path.join(dir, FILES.nightNightly),
135
137
  codeFindings: path.join(dir, FILES.codeFindings),
136
138
  config: path.join(dir, FILES.config),
137
139
  snapshot: path.join(dir, FILES.snapshot),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kryptheon",
3
- "version": "0.1.25",
3
+ "version": "0.1.26",
4
4
  "description": "Record what you do in your app and get plain-language reports when it breaks.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",